From 62f8d40ffc6d3a723f5f1d99fee7febfd47026b5 Mon Sep 17 00:00:00 2001 From: Madeleine Thompson Date: Tue, 26 Oct 2021 12:33:01 -0700 Subject: [PATCH 001/914] python client 0.11.0 beta release (#42) - Add `.isort.cfg` and fix imports to match. - Exit with status 1 instead of 0 on error in `openai`. - Support `openai.api_key_path` variable to make it easy to have long-running processes with auto-updated keys. - Drop support for unverified SSL connections. - Substantially simplify HTTP client code. Now that we use `requests` for everything, we do not need distinct `HTTPClient` and `APIRequestor` abstractions, and we can use the `requests` implementation for multipart data, rather than our own. - Drop vestigial code originally from the Stripe client. For example, there was a bunch of code related to an idempotency framework; OpenAI does not have an idempotency framework. - Drop support for `APIResource.delete` as an instance method; it is just a class method now. - Drop support for `APIResource.save`; use `APIResource.modify` instead. This substantially simplifies API resources, since they no longer need to track changed fields, and there's less risk of race conditions. And the only thing it could be used for is changing the number of replicas an engine had, which does not justify the complexity of the code. - Drop the request-metrics code. It is unused. --- .gitignore | 7 +- .isort.cfg | 6 + README.md | 2 +- bin/openai | 5 +- examples/codex/backtranslation.py | 6 +- examples/finetuning/answers-with-ft.py | 3 +- examples/semanticsearch/semanticsearch.py | 3 +- openai/__init__.py | 72 ++-- openai/api_requestor.py | 362 ++++++++---------- openai/api_resources/abstract/__init__.py | 4 +- openai/api_resources/abstract/api_resource.py | 14 +- .../abstract/createable_api_resource.py | 10 +- .../api_resources/abstract/custom_method.py | 40 -- .../abstract/deletable_api_resource.py | 10 +- .../abstract/engine_api_resource.py | 10 +- .../abstract/listable_api_resource.py | 7 +- .../abstract/nested_resource_class_methods.py | 6 +- .../abstract/singleton_api_resource.py | 24 -- .../abstract/updateable_api_resource.py | 13 - openai/api_resources/completion.py | 2 +- openai/api_resources/engine.py | 5 +- openai/api_resources/error_object.py | 9 +- openai/api_resources/file.py | 47 ++- openai/api_resources/fine_tune.py | 12 +- openai/api_resources/model.py | 5 +- openai/cli.py | 19 +- openai/error.py | 12 +- openai/gzip_stream.py | 83 ---- openai/http_client.py | 321 ---------------- openai/multipart_data_generator.py | 92 ----- openai/object_classes.py | 2 - openai/openai_object.py | 147 ++----- openai/openai_response.py | 31 +- openai/request_metrics.py | 13 - openai/tests/test_api_requestor.py | 27 ++ openai/tests/test_endpoints.py | 10 +- openai/tests/test_file_cli.py | 39 ++ openai/tests/test_util.py | 30 ++ openai/util.py | 102 +---- openai/validators.py | 6 +- openai/version.py | 2 +- setup.py | 4 +- 42 files changed, 470 insertions(+), 1154 deletions(-) create mode 100644 .isort.cfg delete mode 100644 openai/api_resources/abstract/custom_method.py delete mode 100644 openai/api_resources/abstract/singleton_api_resource.py delete mode 100644 openai/gzip_stream.py delete mode 100644 openai/http_client.py delete mode 100644 openai/multipart_data_generator.py delete mode 100644 openai/request_metrics.py create mode 100644 openai/tests/test_api_requestor.py create mode 100644 openai/tests/test_file_cli.py create mode 100644 openai/tests/test_util.py diff --git a/.gitignore b/.gitignore index a8b3cf4a28..b7c7a797a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.egg-info -__pycache__ -/public/dist .idea -.python-version \ No newline at end of file +.python-version +/public/dist +__pycache__ +build diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000000..1a11349890 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,6 @@ +[settings] +include_trailing_comma=True +line_length=88 +known_first_party= +multi_line_output=3 +py_version=36 diff --git a/README.md b/README.md index c4c68e2d18..a62958f652 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ openai api completions.create -e ada -p "Hello world" ## Requirements -- Python 3.7+ +- Python 3.7.1+ In general we want to support the versions of Python that our customers are using, so if you run into issues with any version diff --git a/bin/openai b/bin/openai index 8bfbe5ac1e..3c34b69347 100755 --- a/bin/openai +++ b/bin/openai @@ -4,8 +4,7 @@ import logging import sys import openai -from openai.cli import display_error -from openai.cli import api_register, tools_register +from openai.cli import api_register, display_error, tools_register logger = logging.getLogger() formatter = logging.Formatter("[%(asctime)s] %(message)s") @@ -62,8 +61,10 @@ def main(): args.func(args) except openai.error.OpenAIError as e: display_error(e) + return 1 except KeyboardInterrupt: sys.stderr.write("\n") + return 1 return 0 diff --git a/examples/codex/backtranslation.py b/examples/codex/backtranslation.py index ca0a3e6ccd..8289a73ade 100644 --- a/examples/codex/backtranslation.py +++ b/examples/codex/backtranslation.py @@ -1,7 +1,9 @@ -import openai -from smokey import Smokey from typing import List, Union +from smokey import Smokey + +import openai + def get_candidates( prompt: str, diff --git a/examples/finetuning/answers-with-ft.py b/examples/finetuning/answers-with-ft.py index 672f0e4b40..2ba22edb6f 100644 --- a/examples/finetuning/answers-with-ft.py +++ b/examples/finetuning/answers-with-ft.py @@ -1,6 +1,7 @@ -import openai import argparse +import openai + def create_context( question, search_file_id, max_len=1800, search_model="ada", max_rerank=10 diff --git a/examples/semanticsearch/semanticsearch.py b/examples/semanticsearch/semanticsearch.py index 9b94adfbb7..bceb4c55d2 100755 --- a/examples/semanticsearch/semanticsearch.py +++ b/examples/semanticsearch/semanticsearch.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -import openai import argparse import logging import sys from typing import List +import openai + logger = logging.getLogger() formatter = logging.Formatter("[%(asctime)s] [%(process)d] %(message)s") handler = logging.StreamHandler(sys.stderr) diff --git a/openai/__init__.py b/openai/__init__.py index 054cec3d4d..f9d601bcf8 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -1,31 +1,11 @@ -import os - # OpenAI Python bindings. # # Originally forked from the MIT-licensed Stripe Python bindings. -# Configuration variables - -api_key = os.environ.get("OPENAI_API_KEY") -organization = os.environ.get("OPENAI_ORGANIZATION") -client_id = None -api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/") -file_api_base = None -api_version = None -verify_ssl_certs = True -proxy = None -default_http_client = None -app_info = None -enable_telemetry = True -max_network_retries = 0 -ca_bundle_path = os.path.join(os.path.dirname(__file__), "data/ca-certificates.crt") -debug = False - -# Set to either 'debug' or 'info', controls console logging -log = None +import os +from typing import Optional -# API resources -from openai.api_resources import ( # noqa: E402,F401 +from openai.api_resources import ( Answer, Classification, Completion, @@ -36,4 +16,48 @@ Model, Search, ) -from openai.error import APIError, InvalidRequestError, OpenAIError # noqa: E402,F401 +from openai.error import APIError, InvalidRequestError, OpenAIError + +api_key = os.environ.get("OPENAI_API_KEY") +# Path of a file with an API key, whose contents can change. Supercedes +# `api_key` if set. The main use case is volume-mounted Kubernetes secrets, +# which are updated automatically. +api_key_path: Optional[str] = os.environ.get("OPENAI_API_KEY_PATH") + +organization = os.environ.get("OPENAI_ORGANIZATION") +api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/") +api_version = None +verify_ssl_certs = True # No effect. Certificates are always verified. +proxy = None +app_info = None +enable_telemetry = False # Ignored; the telemetry feature was removed. +ca_bundle_path = os.path.join(os.path.dirname(__file__), "data/ca-certificates.crt") +debug = False +log = None # Set to either 'debug' or 'info', controls console logging + +__all__ = [ + "APIError", + "Answer", + "Classification", + "Completion", + "Engine", + "ErrorObject", + "File", + "FineTune", + "InvalidRequestError", + "Model", + "OpenAIError", + "Search", + "api_base", + "api_key", + "api_key_path", + "api_version", + "app_info", + "ca_bundle_path", + "debug", + "enable_elemetry", + "log", + "organization", + "proxy", + "verify_ssl_certs", +] diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 5202a7d7d2..1923e9bac9 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -1,60 +1,22 @@ -import calendar -import datetime import json import platform -import time -import uuid +import threading import warnings -from io import BytesIO -from collections import OrderedDict +from json import JSONDecodeError +from typing import Dict, Iterator, Optional, Tuple, Union from urllib.parse import urlencode, urlsplit, urlunsplit +import requests + import openai -from openai import error, http_client, version, util -from openai.multipart_data_generator import MultipartDataGenerator +from openai import error, util, version from openai.openai_response import OpenAIResponse -from openai.upload_progress import BufferReader +TIMEOUT_SECS = 600 +MAX_CONNECTION_RETRIES = 2 -def _encode_datetime(dttime) -> int: - utc_timestamp: float - if dttime.tzinfo and dttime.tzinfo.utcoffset(dttime) is not None: - utc_timestamp = calendar.timegm(dttime.utctimetuple()) - else: - utc_timestamp = time.mktime(dttime.timetuple()) - - return int(utc_timestamp) - - -def _encode_nested_dict(key, data, fmt="%s[%s]"): - d = OrderedDict() - for subkey, subvalue in data.items(): - d[fmt % (key, subkey)] = subvalue - return d - - -def _api_encode(data): - for key, value in data.items(): - if value is None: - continue - elif hasattr(value, "openai_id"): - yield (key, value.openai_id) - elif isinstance(value, list) or isinstance(value, tuple): - for i, sv in enumerate(value): - if isinstance(sv, dict): - subdict = _encode_nested_dict("%s[%d]" % (key, i), sv) - for k, v in _api_encode(subdict): - yield (k, v) - else: - yield ("%s[%d]" % (key, i), sv) - elif isinstance(value, dict): - subdict = _encode_nested_dict(key, value) - for subkey, subvalue in _api_encode(subdict): - yield (subkey, subvalue) - elif isinstance(value, datetime.datetime): - yield (key, _encode_datetime(value)) - else: - yield (key, value) +# Has one attribute per thread, 'session'. +_thread_context = threading.local() def _build_api_url(http://github.com/url,%20query): @@ -66,6 +28,35 @@ def _build_api_url(http://github.com/url,%20query): return urlunsplit((scheme, netloc, path, query, fragment)) +def _requests_proxies_arg(proxy) -> Optional[Dict[str, str]]: + """Returns a value suitable for the 'proxies' argument to 'requests.request.""" + if proxy is None: + return None + elif isinstance(proxy, str): + return {"http": proxy, "https": proxy} + elif isinstance(proxy, dict): + return proxy.copy() + else: + raise ValueError( + "'openai.proxy' must be specified as either a string URL or a dict with string URL under the https and/or http keys." + ) + + +def _make_session() -> requests.Session: + if not openai.verify_ssl_certs: + warnings.warn("verify_ssl_certs is ignored; openai always verifies.") + s = requests.Session() + proxies = _requests_proxies_arg(openai.proxy) + if proxies: + s.proxies = proxies + s.verify = openai.ca_bundle_path + s.mount( + "https://", + requests.adapters.HTTPAdapter(max_retries=MAX_CONNECTION_RETRIES), + ) + return s + + def parse_stream(rbody): for line in rbody: if line: @@ -79,40 +70,12 @@ def parse_stream(rbody): class APIRequestor: - def __init__( - self, key=None, client=None, api_base=None, api_version=None, organization=None - ): + def __init__(self, key=None, api_base=None, api_version=None, organization=None): self.api_base = api_base or openai.api_base - self.api_key = key + self.api_key = key or util.default_api_key() self.api_version = api_version or openai.api_version self.organization = organization or openai.organization - self._default_proxy = None - - from openai import verify_ssl_certs as verify - from openai import proxy - - if client: - self._client = client - elif openai.default_http_client: - self._client = openai.default_http_client - if proxy != self._default_proxy: - warnings.warn( - "openai.proxy was updated after sending a " - "request - this is a no-op. To use a different proxy, " - "set openai.default_http_client to a new client " - "configured with the proxy." - ) - else: - # If the openai.default_http_client has not been set by the user - # yet, we'll set it here. This way, we aren't creating a new - # HttpClient for every request. - openai.default_http_client = http_client.new_default_http_client( - verify_ssl_certs=verify, proxy=proxy - ) - self._client = openai.default_http_client - self._default_proxy = proxy - @classmethod def format_app_info(cls, info): str = info["name"] @@ -122,12 +85,27 @@ def format_app_info(cls, info): str += " (%s)" % (info["url"],) return str - def request(self, method, url, params=None, headers=None, stream=False): - rbody, rcode, rheaders, stream, my_api_key = self.request_raw( - method.lower(), url, params, headers, stream=stream + def request( + self, + method, + url, + params=None, + headers=None, + files=None, + stream=False, + request_id: Optional[str] = None, + ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool, str]: + result = self.request_raw( + method.lower(), + url, + params, + headers, + files=files, + stream=stream, + request_id=request_id, ) - resp = self.interpret_response(rbody, rcode, rheaders, stream=stream) - return resp, stream, my_api_key + resp, got_stream = self._interpret_response(result, stream) + return resp, got_stream, self.api_key def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): try: @@ -159,20 +137,15 @@ def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False error_data.get("message"), rbody, rcode, resp, rheaders ) elif rcode in [400, 404, 415]: - if error_data.get("type") == "idempotency_error": - return error.IdempotencyError( - error_data.get("message"), rbody, rcode, resp, rheaders - ) - else: - return error.InvalidRequestError( - error_data.get("message"), - error_data.get("param"), - error_data.get("code"), - rbody, - rcode, - resp, - rheaders, - ) + return error.InvalidRequestError( + error_data.get("message"), + error_data.get("param"), + error_data.get("code"), + rbody, + rcode, + resp, + rheaders, + ) elif rcode == 401: return error.AuthenticationError( error_data.get("message"), rbody, rcode, resp, rheaders @@ -195,19 +168,24 @@ def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False error_data.get("message"), rbody, rcode, resp, rheaders ) - def request_headers(self, api_key, method, extra): + def request_headers( + self, method: str, extra, request_id: Optional[str] + ) -> Dict[str, str]: user_agent = "OpenAI/v1 PythonBindings/%s" % (version.VERSION,) if openai.app_info: user_agent += " " + self.format_app_info(openai.app_info) + uname_without_node = " ".join( + v for k, v in platform.uname()._asdict().items() if k != "node" + ) ua = { "bindings_version": version.VERSION, - "httplib": self._client.name, + "httplib": "requests", "lang": "python", "lang_version": platform.python_version(), "platform": platform.platform(), "publisher": "openai", - "uname": " ".join(platform.uname()), + "uname": uname_without_node, } if openai.app_info: ua["application"] = openai.app_info @@ -215,92 +193,48 @@ def request_headers(self, api_key, method, extra): headers = { "X-OpenAI-Client-User-Agent": json.dumps(ua), "User-Agent": user_agent, - "Authorization": "Bearer %s" % (api_key,), + "Authorization": "Bearer %s" % (self.api_key,), } if self.organization: headers["OpenAI-Organization"] = self.organization - if method in {"post", "put"}: - headers.setdefault("Idempotency-Key", str(uuid.uuid4())) - if self.api_version is not None: headers["OpenAI-Version"] = self.api_version - + if request_id is not None: + headers["X-Request-Id"] = request_id + if openai.debug: + headers["OpenAI-Debug"] = "true" headers.update(extra) return headers def request_raw( - self, method, url, params=None, supplied_headers=None, stream=False - ): - """ - Mechanism for issuing an API call - """ - - if self.api_key: - my_api_key = self.api_key - else: - from openai import api_key - - my_api_key = api_key - - if my_api_key is None: - raise error.AuthenticationError( - "No API key provided. (HINT: set your API key in code using " - '"openai.api_key = ", or you can set the environment variable OPENAI_API_KEY=). You can generate API keys ' - "in the OpenAI web interface. See https://onboard.openai.com " - "for details, or email support@openai.com if you have any " - "questions." - ) - + self, + method, + url, + params=None, + supplied_headers=None, + files=None, + stream=False, + request_id: Optional[str] = None, + ) -> requests.Response: abs_url = "%s%s" % (self.api_base, url) headers = {} - compress = None - progress_meter = False + data = None if method == "get" or method == "delete": if params: - encoded_params = url_encode_params(params) + encoded_params = urlencode( + [(k, v) for k, v in params.items() if v is not None] + ) abs_url = _build_api_url(http://github.com/abs_url,%20encoded_params) - else: - encoded_params = None - post_data = None elif method in {"post", "put"}: - if ( - supplied_headers is not None - and supplied_headers.get("Content-Type") == "multipart/form-data" - ): - generator = MultipartDataGenerator() - generator.add_params(params or {}) - post_data = generator.get_post_data() - content_type = "multipart/form-data; boundary=%s" % ( - generator.boundary, - ) - # We will overrite Content-Type - supplied_headers.pop("Content-Type") - progress_meter = True - # compress = "gzip" - compress = None - else: - post_data = json.dumps(params).encode() - content_type = "application/json" - - headers["Content-Type"] = content_type - - encoded_params = post_data - - if progress_meter: - post_data = BufferReader(post_data, desc="Upload progress") - - if compress == "gzip": - if not hasattr(post_data, "read"): - post_data = BytesIO(post_data) - headers["Content-Encoding"] = "gzip" - - from openai.gzip_stream import GZIPCompressedStream - - post_data = GZIPCompressedStream(post_data, compression_level=9) + if params and files: + raise ValueError("At most one of params and files may be specified.") + if params: + data = json.dumps(params).encode() + headers["Content-Type"] = "application/json" else: raise error.APIConnectionError( "Unrecognized HTTP method %r. This may indicate a bug in the " @@ -308,58 +242,75 @@ def request_raw( "assistance." % (method,) ) - headers = self.request_headers(my_api_key, method, headers) + headers = self.request_headers(method, headers, request_id) if supplied_headers is not None: - for key, value in supplied_headers.items(): - headers[key] = value + headers.update(supplied_headers) util.log_info("Request to OpenAI API", method=method, path=abs_url) - util.log_debug( - "Post details", post_data=encoded_params, api_version=self.api_version - ) - - rbody, rcode, rheaders, stream = self._client.request_with_retries( - method, abs_url, headers, post_data, stream=stream - ) + util.log_debug("Post details", data=data, api_version=self.api_version) + if not hasattr(_thread_context, "session"): + _thread_context.session = _make_session() + try: + result = _thread_context.session.request( + method, + abs_url, + headers=headers, + data=data, + files=files, + stream=stream, + timeout=TIMEOUT_SECS, + ) + except requests.exceptions.RequestException as e: + raise error.APIConnectionError("Error communicating with OpenAI") from e util.log_info( "OpenAI API response", path=abs_url, - response_code=rcode, - processing_ms=rheaders.get("OpenAI-Processing-Ms"), + response_code=result.status_code, + processing_ms=result.headers.get("OpenAI-Processing-Ms"), ) - util.log_debug("API response body", body=rbody, headers=rheaders) - - if "Request-Id" in rheaders: - request_id = rheaders["Request-Id"] + # Don't read the whole stream for debug logging unless necessary. + if openai.log == "debug": util.log_debug( - "Dashboard link for request", link=util.dashboard_link(request_id) + "API response body", body=result.content, headers=result.headers ) + return result - return rbody, rcode, rheaders, stream, my_api_key - - def interpret_response(self, rbody, rcode, rheaders, stream=False): - if stream: + def _interpret_response( + self, result: requests.Response, stream: bool + ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool]: + """Returns the response(s) and a bool indicating whether it is a stream.""" + if stream and "text/event-stream" in result.headers.get("Content-Type", ""): return ( - self.interpret_response_line(line, rcode, rheaders, stream) - for line in parse_stream(rbody) - ) + self._interpret_response_line( + line, result.status_code, result.headers, stream=True + ) + for line in parse_stream(result.iter_lines()) + ), True else: - return self.interpret_response_line(rbody, rcode, rheaders, stream) + return ( + self._interpret_response_line( + result.content, result.status_code, result.headers, stream=False + ), + False, + ) - def interpret_response_line(self, rbody, rcode, rheaders, stream=False): + def _interpret_response_line( + self, rbody, rcode, rheaders, stream: bool + ) -> OpenAIResponse: + if rcode == 503: + raise error.ServiceUnavailableError( + "The server is overloaded or not ready yet.", rbody, rcode, rheaders + ) try: if hasattr(rbody, "decode"): rbody = rbody.decode("utf-8") - resp = OpenAIResponse(rbody, rcode, rheaders) - except Exception: + data = json.loads(rbody) + except (JSONDecodeError, UnicodeDecodeError): raise error.APIError( - "Invalid response body from API: %s " - "(HTTP response code was %d)" % (rbody, rcode), - rbody, - rcode, - rheaders, + f"HTTP code {rcode} from API ({rbody})", rbody, rcode, rheaders ) + resp = OpenAIResponse(data, rheaders) # In the future, we might add a "status" parameter to errors # to better handle the "error while streaming" case. stream_error = stream and "error" in resp.data @@ -367,15 +318,4 @@ def interpret_response_line(self, rbody, rcode, rheaders, stream=False): raise self.handle_error_response( rbody, rcode, resp.data, rheaders, stream_error=stream_error ) - return resp - - -def url_encode_params(params): - encoded_params = urlencode(list(_api_encode(params or {}))) - - # Don't use strict form encoding by changing the square bracket control - # characters back to their literals. This is fine by the server, and - # makes these parameter strings easier to read. - encoded_params = encoded_params.replace("%5B", "[").replace("%5D", "]") - return encoded_params diff --git a/openai/api_resources/abstract/__init__.py b/openai/api_resources/abstract/__init__.py index 8b42e409b5..32830e273c 100644 --- a/openai/api_resources/abstract/__init__.py +++ b/openai/api_resources/abstract/__init__.py @@ -1,12 +1,10 @@ # flake8: noqa from openai.api_resources.abstract.api_resource import APIResource -from openai.api_resources.abstract.singleton_api_resource import SingletonAPIResource from openai.api_resources.abstract.createable_api_resource import CreateableAPIResource -from openai.api_resources.abstract.updateable_api_resource import UpdateableAPIResource from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource from openai.api_resources.abstract.listable_api_resource import ListableAPIResource -from openai.api_resources.abstract.custom_method import custom_method from openai.api_resources.abstract.nested_resource_class_methods import ( nested_resource_class_methods, ) +from openai.api_resources.abstract.updateable_api_resource import UpdateableAPIResource diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index 289363370c..3a27c66585 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -14,16 +14,16 @@ def retrieve(cls, id, api_key=None, request_id=None, **params): return instance def refresh(self, request_id=None): - headers = util.populate_headers(request_id=request_id) - self.refresh_from(self.request("get", self.instance_url(), headers=headers)) + self.refresh_from( + self.request("get", self.instance_url(), request_id=request_id) + ) return self @classmethod def class_url(http://github.com/cls): if cls == APIResource: raise NotImplementedError( - "APIResource is an abstract class. You should perform " - "actions on its subclasses (e.g. Charge, Customer)" + "APIResource is an abstract class. You should perform actions on its subclasses." ) # Namespaces are separated in object names with periods (.) and in URLs # with forward slashes (/), so replace the former with the latter. @@ -54,7 +54,6 @@ def _static_request( url_, api_key=None, api_base=None, - idempotency_key=None, request_id=None, api_version=None, organization=None, @@ -66,8 +65,9 @@ def _static_request( organization=organization, api_base=api_base, ) - headers = util.populate_headers(idempotency_key, request_id) - response, _, api_key = requestor.request(method_, url_, params, headers) + response, _, api_key = requestor.request( + method_, url_, params, request_id=request_id + ) return util.convert_to_openai_object( response, api_key, api_version, organization ) diff --git a/openai/api_resources/abstract/createable_api_resource.py b/openai/api_resources/abstract/createable_api_resource.py index 231e4e9ca7..1538cb3719 100644 --- a/openai/api_resources/abstract/createable_api_resource.py +++ b/openai/api_resources/abstract/createable_api_resource.py @@ -1,7 +1,5 @@ -from __future__ import absolute_import, division, print_function - -from openai.api_resources.abstract.api_resource import APIResource from openai import api_requestor, util +from openai.api_resources.abstract.api_resource import APIResource class CreateableAPIResource(APIResource): @@ -12,7 +10,6 @@ def create( cls, api_key=None, api_base=None, - idempotency_key=None, request_id=None, api_version=None, organization=None, @@ -25,8 +22,9 @@ def create( organization=organization, ) url = cls.class_url() - headers = util.populate_headers(idempotency_key, request_id) - response, _, api_key = requestor.request("post", url, params, headers) + response, _, api_key = requestor.request( + "post", url, params, request_id=request_id + ) return util.convert_to_openai_object( response, diff --git a/openai/api_resources/abstract/custom_method.py b/openai/api_resources/abstract/custom_method.py deleted file mode 100644 index 3c3eb8d3ce..0000000000 --- a/openai/api_resources/abstract/custom_method.py +++ /dev/null @@ -1,40 +0,0 @@ -from urllib.parse import quote_plus - -from openai import util - - -def custom_method(name, http_verb, http_path=None): - if http_verb not in ["get", "post", "delete"]: - raise ValueError( - "Invalid http_verb: %s. Must be one of 'get', 'post' or 'delete'" - % http_verb - ) - if http_path is None: - http_path = name - - def wrapper(cls): - def custom_method_request(cls, sid, **params): - url = "%s/%s/%s" % ( - cls.class_url(), - quote_plus(sid), - http_path, - ) - return cls._static_request(http_verb, url, **params) - - existing_method = getattr(cls, name, None) - if existing_method is None: - setattr(cls, name, classmethod(custom_method_request)) - else: - # If a method with the same name we want to use already exists on - # the class, we assume it's an instance method. In this case, the - # new class method is prefixed with `_cls_`, and the original - # instance method is decorated with `util.class_method_variant` so - # that the new class method is called when the original method is - # called as a class method. - setattr(cls, "_cls_" + name, classmethod(custom_method_request)) - instance_method = util.class_method_variant("_cls_" + name)(existing_method) - setattr(cls, name, instance_method) - - return cls - - return wrapper diff --git a/openai/api_resources/abstract/deletable_api_resource.py b/openai/api_resources/abstract/deletable_api_resource.py index 4bebe3ecb5..47111b153c 100644 --- a/openai/api_resources/abstract/deletable_api_resource.py +++ b/openai/api_resources/abstract/deletable_api_resource.py @@ -1,16 +1,12 @@ from urllib.parse import quote_plus -from openai import util from openai.api_resources.abstract.api_resource import APIResource class DeletableAPIResource(APIResource): @classmethod - def _cls_delete(cls, sid, **params): + def delete(cls, sid, **params): + if isinstance(cls, APIResource): + raise ValueError(".delete may only be called as a class method now.") url = "%s/%s" % (cls.class_url(), quote_plus(sid)) return cls._static_request("delete", url, **params) - - @util.class_method_variant("_cls_delete") - def delete(self, **params): - self.refresh_from(self.request("delete", self.instance_url(), params)) - return self diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index 21112685ae..e613e53814 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -4,6 +4,7 @@ from openai import api_requestor, error, util from openai.api_resources.abstract.api_resource import APIResource +from openai.openai_response import OpenAIResponse MAX_TIMEOUT = 20 @@ -31,7 +32,6 @@ def create( cls, api_key=None, api_base=None, - idempotency_key=None, request_id=None, api_version=None, organization=None, @@ -62,12 +62,12 @@ def create( organization=organization, ) url = cls.class_url(http://github.com/engine) - headers = util.populate_headers(idempotency_key, request_id) response, _, api_key = requestor.request( - "post", url, params, headers, stream=stream + "post", url, params, stream=stream, request_id=request_id ) if stream: + assert not isinstance(response, OpenAIResponse) # must be an iterator return ( util.convert_to_openai_object( line, @@ -99,9 +99,7 @@ def instance_url(http://github.com/self): if not isinstance(id, str): raise error.InvalidRequestError( - "Could not determine which URL to request: %s instance " - "has invalid ID: %r, %s. ID should be of type `str` (or" - " `unicode`)" % (type(self).__name__, id, type(id)), + f"Could not determine which URL to request: {type(self).__name__} instance has invalid ID: {id}, {type(id)}. ID should be of type str.", "id", ) diff --git a/openai/api_resources/abstract/listable_api_resource.py b/openai/api_resources/abstract/listable_api_resource.py index dfdd7a2d25..b9cf952a91 100644 --- a/openai/api_resources/abstract/listable_api_resource.py +++ b/openai/api_resources/abstract/listable_api_resource.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from openai import api_requestor, util from openai.api_resources.abstract.api_resource import APIResource @@ -19,7 +17,6 @@ def list( api_base=None, **params, ): - headers = util.populate_headers(request_id=request_id) requestor = api_requestor.APIRequestor( api_key, api_base=api_base or cls.api_base(), @@ -27,7 +24,9 @@ def list( organization=organization, ) url = cls.class_url() - response, _, api_key = requestor.request("get", url, params, headers) + response, _, api_key = requestor.request( + "get", url, params, request_id=request_id + ) openai_object = util.convert_to_openai_object( response, api_key, api_version, organization ) diff --git a/openai/api_resources/abstract/nested_resource_class_methods.py b/openai/api_resources/abstract/nested_resource_class_methods.py index 7655a996e6..c86e59fbf6 100644 --- a/openai/api_resources/abstract/nested_resource_class_methods.py +++ b/openai/api_resources/abstract/nested_resource_class_methods.py @@ -28,7 +28,6 @@ def nested_resource_request( method, url, api_key=None, - idempotency_key=None, request_id=None, api_version=None, organization=None, @@ -37,8 +36,9 @@ def nested_resource_request( requestor = api_requestor.APIRequestor( api_key, api_version=api_version, organization=organization ) - headers = util.populate_headers(idempotency_key, request_id) - response, _, api_key = requestor.request(method, url, params, headers) + response, _, api_key = requestor.request( + method, url, params, request_id=request_id + ) return util.convert_to_openai_object( response, api_key, api_version, organization ) diff --git a/openai/api_resources/abstract/singleton_api_resource.py b/openai/api_resources/abstract/singleton_api_resource.py deleted file mode 100644 index 0385c7066c..0000000000 --- a/openai/api_resources/abstract/singleton_api_resource.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import absolute_import, division, print_function - -from openai.api_resources.abstract.api_resource import APIResource - - -class SingletonAPIResource(APIResource): - @classmethod - def retrieve(cls, **params): - return super(SingletonAPIResource, cls).retrieve(None, **params) - - @classmethod - def class_url(http://github.com/cls): - if cls == SingletonAPIResource: - raise NotImplementedError( - "SingletonAPIResource is an abstract class. You should " - "perform actions on its subclasses (e.g. Balance)" - ) - # Namespaces are separated in object names with periods (.) and in URLs - # with forward slashes (/), so replace the former with the latter. - base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - return "/v1/%s" % (base,) - - def instance_url(http://github.com/self): - return self.class_url() diff --git a/openai/api_resources/abstract/updateable_api_resource.py b/openai/api_resources/abstract/updateable_api_resource.py index 9d2050f10a..e7289d12d3 100644 --- a/openai/api_resources/abstract/updateable_api_resource.py +++ b/openai/api_resources/abstract/updateable_api_resource.py @@ -1,6 +1,5 @@ from urllib.parse import quote_plus -from openai import util from openai.api_resources.abstract.api_resource import APIResource @@ -9,15 +8,3 @@ class UpdateableAPIResource(APIResource): def modify(cls, sid, **params): url = "%s/%s" % (cls.class_url(), quote_plus(sid)) return cls._static_request("post", url, **params) - - def save(self, idempotency_key=None, request_id=None): - updated_params = self.serialize(None) - headers = util.populate_headers(idempotency_key, request_id) - - if updated_params: - self.refresh_from( - self.request("post", self.instance_url(), updated_params, headers) - ) - else: - util.logger.debug("Trying to save already saved object %r", self) - return self diff --git a/openai/api_resources/completion.py b/openai/api_resources/completion.py index cd0d204370..5c4a1a34ea 100644 --- a/openai/api_resources/completion.py +++ b/openai/api_resources/completion.py @@ -3,7 +3,7 @@ from openai import util from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import TryAgain, InvalidRequestError +from openai.error import InvalidRequestError, TryAgain class Completion(EngineAPIResource, ListableAPIResource, DeletableAPIResource): diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py index d63bf2159d..8ace8c9f5f 100644 --- a/openai/api_resources/engine.py +++ b/openai/api_resources/engine.py @@ -1,10 +1,7 @@ import time from openai import util -from openai.api_resources.abstract import ( - ListableAPIResource, - UpdateableAPIResource, -) +from openai.api_resources.abstract import ListableAPIResource, UpdateableAPIResource from openai.error import TryAgain diff --git a/openai/api_resources/error_object.py b/openai/api_resources/error_object.py index f02de25e4a..38d8fbf16b 100644 --- a/openai/api_resources/error_object.py +++ b/openai/api_resources/error_object.py @@ -1,7 +1,7 @@ -from __future__ import absolute_import, division, print_function +from typing import Optional -from openai.util import merge_dicts from openai.openai_object import OpenAIObject +from openai.util import merge_dicts class ErrorObject(OpenAIObject): @@ -9,15 +9,14 @@ def refresh_from( self, values, api_key=None, - partial=False, api_version=None, organization=None, - last_response=None, + response_ms: Optional[int] = None, ): # Unlike most other API resources, the API will omit attributes in # error objects when they have a null value. We manually set default # values here to facilitate generic error handling. values = merge_dicts({"message": None, "type": None}, values) return super(ErrorObject, self).refresh_from( - values, api_key, partial, api_version, organization, last_response + values, api_key, api_version, organization, response_ms ) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 4a5feb92d9..f79242bfbf 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -1,14 +1,10 @@ -from __future__ import absolute_import, division, print_function - import json import os +from typing import cast import openai from openai import api_requestor, util -from openai.api_resources.abstract import ( - DeletableAPIResource, - ListableAPIResource, -) +from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource class File(ListableAPIResource, DeletableAPIResource): @@ -16,19 +12,30 @@ class File(ListableAPIResource, DeletableAPIResource): @classmethod def create( - cls, api_key=None, api_base=None, api_version=None, organization=None, **params + cls, + file, + purpose, + model=None, + api_key=None, + api_base=None, + api_version=None, + organization=None, ): + if purpose != "search" and model is not None: + raise ValueError("'model' is only meaningful if 'purpose' is 'search'") requestor = api_requestor.APIRequestor( api_key, - api_base=api_base or openai.file_api_base or openai.api_base, + api_base=api_base or openai.api_base, api_version=api_version, organization=organization, ) url = cls.class_url() - supplied_headers = {"Content-Type": "multipart/form-data"} - response, _, api_key = requestor.request( - "post", url, params=params, headers=supplied_headers - ) + # Set the filename on 'purpose' and 'model' to None so they are + # interpreted as form data. + files = [("file", file), ("purpose", (None, purpose))] + if model is not None: + files.append(("model", (None, model))) + response, _, api_key = requestor.request("post", url, files=files) return util.convert_to_openai_object( response, api_key, api_version, organization ) @@ -39,17 +46,21 @@ def download( ): requestor = api_requestor.APIRequestor( api_key, - api_base=api_base or openai.file_api_base or openai.api_base, + api_base=api_base or openai.api_base, api_version=api_version, organization=organization, ) url = f"{cls.class_url()}/{id}/content" - rbody, rcode, rheaders, _, _ = requestor.request_raw("get", url) - if not 200 <= rcode < 300: + result = requestor.request_raw("get", url) + if not 200 <= result.status_code < 300: raise requestor.handle_error_response( - rbody, rcode, json.loads(rbody), rheaders, stream_error=False + result.content, + result.status_code, + json.loads(cast(bytes, result.content)), + result.headers, + stream_error=False, ) - return rbody + return result.content @classmethod def find_matching_files( @@ -71,7 +82,7 @@ def find_matching_files( ) all_files = cls.list( api_key=api_key, - api_base=api_base or openai.file_api_base or openai.api_base, + api_base=api_base or openai.api_base, api_version=api_version, organization=organization, ).get("data", []) diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index 9597c2f4f3..c53671ae68 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -1,11 +1,12 @@ from urllib.parse import quote_plus +from openai import api_requestor, util from openai.api_resources.abstract import ( - ListableAPIResource, CreateableAPIResource, + ListableAPIResource, nested_resource_class_methods, ) -from openai import api_requestor, util +from openai.openai_response import OpenAIResponse @nested_resource_class_methods("event", operations=["list"]) @@ -18,8 +19,7 @@ def cancel(cls, id, api_key=None, request_id=None, **params): extn = quote_plus(id) url = "%s/%s/cancel" % (base, extn) instance = cls(id, api_key, **params) - headers = util.populate_headers(request_id=request_id) - return instance.request("post", url, headers=headers) + return instance.request("post", url, request_id=request_id) @classmethod def stream_events( @@ -42,11 +42,11 @@ def stream_events( organization=organization, ) url = "%s/%s/events?stream=true" % (base, extn) - headers = util.populate_headers(request_id=request_id) response, _, api_key = requestor.request( - "get", url, params, headers=headers, stream=True + "get", url, params, stream=True, request_id=request_id ) + assert not isinstance(response, OpenAIResponse) # must be an iterator return ( util.convert_to_openai_object( line, diff --git a/openai/api_resources/model.py b/openai/api_resources/model.py index 77d0322687..f0b123a974 100644 --- a/openai/api_resources/model.py +++ b/openai/api_resources/model.py @@ -1,7 +1,4 @@ -from openai.api_resources.abstract import ( - ListableAPIResource, - DeletableAPIResource, -) +from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource class Model(ListableAPIResource, DeletableAPIResource): diff --git a/openai/cli.py b/openai/cli.py index e625d1bdf6..872209f5bb 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -5,6 +5,7 @@ import warnings import openai +from openai.upload_progress import BufferReader from openai.validators import ( apply_necessary_remediation, apply_optional_remediation, @@ -60,9 +61,7 @@ def get(cls, args): @classmethod def update(cls, args): - engine = openai.Engine(id=args.id) - engine.replicas = args.replicas - engine.save() + engine = openai.Engine.modify(args.id, replicas=args.replicas) display(engine) @classmethod @@ -181,14 +180,12 @@ def create(cls, args): class Model: @classmethod def get(cls, args): - resp = openai.Model.retrieve( - id=args.id, - ) + resp = openai.Model.retrieve(id=args.id) print(resp) @classmethod def delete(cls, args): - model = openai.Model(id=args.id).delete() + model = openai.Model.delete(args.id) print(model) @classmethod @@ -200,10 +197,10 @@ def list(cls, args): class File: @classmethod def create(cls, args): + with open(args.file, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") resp = openai.File.create( - file=open(args.file), - purpose=args.purpose, - model=args.model, + file=buffer_reader, purpose=args.purpose, model=args.model ) print(resp) @@ -214,7 +211,7 @@ def get(cls, args): @classmethod def delete(cls, args): - file = openai.File(id=args.id).delete() + file = openai.File.delete(args.id) print(file) @classmethod diff --git a/openai/error.py b/openai/error.py index 9683f66124..1f0fa3e906 100644 --- a/openai/error.py +++ b/openai/error.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - import openai @@ -66,7 +64,7 @@ def construct_error_object(self): return None return openai.api_resources.error_object.ErrorObject.construct_from( - self.json_body["error"], openai.api_key + self.json_body["error"], key=None ) @@ -95,10 +93,6 @@ def __init__( self.should_retry = should_retry -class IdempotencyError(OpenAIError): - pass - - class InvalidRequestError(OpenAIError): def __init__( self, @@ -138,6 +132,10 @@ class RateLimitError(OpenAIError): pass +class ServiceUnavailableError(OpenAIError): + pass + + class SignatureVerificationError(OpenAIError): def __init__(self, message, sig_header, http_body=None): super(SignatureVerificationError, self).__init__(message, http_body) diff --git a/openai/gzip_stream.py b/openai/gzip_stream.py deleted file mode 100644 index ebe2e0571d..0000000000 --- a/openai/gzip_stream.py +++ /dev/null @@ -1,83 +0,0 @@ -# Vendored from https://github.com/leenr/gzip-stream -import gzip -import io - - -class GZIPCompressedStream(io.RawIOBase): - def __init__(self, stream, compression_level): - assert 1 <= compression_level <= 9 - - self._compression_level = compression_level - self._stream = stream - - self._compressed_stream = io.BytesIO() - self._compressor = gzip.GzipFile( - mode="wb", fileobj=self._compressed_stream, compresslevel=compression_level - ) - - # because of the GZIP header written by `GzipFile.__init__`: - self._compressed_stream.seek(0) - - @property - def compression_level(self): - return self._compression_level - - @property - def stream(self): - return self._stream - - def readable(self): - return True - - def _read_compressed_into(self, b): - buf = self._compressed_stream.read(len(b)) - b[: len(buf)] = buf - return len(buf) - - def readinto(self, b): - b = memoryview(b) - - offset = 0 - size = len(b) - while offset < size: - offset += self._read_compressed_into(b[offset:]) - if offset < size: - # self._compressed_buffer now empty - if self._compressor.closed: - # nothing to compress anymore - break - # compress next bytes - self._read_n_compress(size) - - return offset - - def _read_n_compress(self, size): - assert size > 0 - - data = self._stream.read(size) - - # rewind buffer to the start to free up memory - # (because anything currently in the buffer should be already - # streamed off the object) - self._compressed_stream.seek(0) - self._compressed_stream.truncate(0) - - if data: - self._compressor.write(data) - else: - # this will write final data (will flush zlib with Z_FINISH) - self._compressor.close() - - # rewind to the buffer start - self._compressed_stream.seek(0) - - def __repr__(self): - return ( - "{self.__class__.__name__}(" - "{self.stream!r}, " - "compression_level={self.compression_level!r}" - ")" - ).format(self=self) - - -__all__ = ("GZIPCompressedStream",) diff --git a/openai/http_client.py b/openai/http_client.py deleted file mode 100644 index bef8c48dd6..0000000000 --- a/openai/http_client.py +++ /dev/null @@ -1,321 +0,0 @@ -import abc -import json -import random -import textwrap -import threading -import time -from typing import Any, Dict -from urllib.parse import urlparse - -import requests - -import openai -from openai import error, util -from openai.request_metrics import RequestMetrics - - -def _now_ms(): - return int(round(time.time() * 1000)) - - -def new_default_http_client(*args, **kwargs): - return RequestsClient(*args, **kwargs) - - -class HTTPClient(abc.ABC): - MAX_DELAY = 2 - INITIAL_DELAY = 0.5 - MAX_RETRY_AFTER = 60 - - def __init__(self, verify_ssl_certs=True, proxy=None): - self._verify_ssl_certs = verify_ssl_certs - if proxy: - if isinstance(proxy, str): - proxy = {"http": proxy, "https": proxy} - if not isinstance(proxy, dict): - raise ValueError( - "Proxy(ies) must be specified as either a string " - "URL or a dict() with string URL under the" - " " - "https" - " and/or " - "http" - " keys." - ) - self._proxy = proxy.copy() if proxy else None - - self._thread_local = threading.local() - - def request_with_retries(self, method, url, headers, post_data=None, stream=False): - self._add_telemetry_header(headers) - - num_retries = 0 - - while True: - request_start = _now_ms() - - try: - response = self.request(method, url, headers, post_data, stream=stream) - connection_error = None - except error.APIConnectionError as e: - connection_error = e - response = None - - if self._should_retry(response, connection_error, num_retries): - if connection_error: - util.log_warn( - "Encountered a retryable error %s" - % connection_error.user_message - ) - num_retries += 1 - sleep_time = self._sleep_time_seconds(num_retries, response) - util.log_info( - ( - "Initiating retry %i for request %s %s after " - "sleeping %.2f seconds." - % (num_retries, method, url, sleep_time) - ) - ) - time.sleep(sleep_time) - else: - if response is not None: - self._record_request_metrics(response, request_start) - - return response - else: - assert connection_error is not None - raise connection_error - - def request(self, method, url, headers, post_data=None, stream=False): - raise NotImplementedError("HTTPClient subclasses must implement `request`") - - def _should_retry(self, response, api_connection_error, num_retries): - if num_retries >= self._max_network_retries(): - return False - - if response is None: - # We generally want to retry on timeout and connection - # exceptions, but defer this decision to underlying subclass - # implementations. They should evaluate the driver-specific - # errors worthy of retries, and set flag on the error returned. - return api_connection_error.should_retry - - _, status_code, rheaders, _ = response - - # The API may ask us not to retry (eg; if doing so would be a no-op) - # or advise us to retry (eg; in cases of lock timeouts); we defer to that. - # - # Note that we expect the headers object to be a CaseInsensitiveDict, as is the case with the requests library. - if rheaders is not None and "openai-should-retry" in rheaders: - if rheaders["openai-should-retry"] == "false": - return False - if rheaders["openai-should-retry"] == "true": - return True - - # Retry on conflict errors. - if status_code == 409: - return True - - # Retry on 500, 503, and other internal errors. - # - # Note that we expect the openai-should-retry header to be false - # in most cases when a 500 is returned, since our idempotency framework - # would typically replay it anyway. - if status_code >= 500: - return True - - return False - - def _max_network_retries(self): - from openai import max_network_retries - - # Configured retries, isolated here for tests - return max_network_retries - - def _retry_after_header(self, response=None): - if response is None: - return None - _, _, rheaders, _ = response - - try: - return int(rheaders["retry-after"]) - except (KeyError, ValueError): - return None - - def _sleep_time_seconds(self, num_retries, response=None): - # Apply exponential backoff with initial_network_retry_delay on the - # number of num_retries so far as inputs. - # Do not allow the number to exceed max_network_retry_delay. - sleep_seconds = min( - HTTPClient.INITIAL_DELAY * (2 ** (num_retries - 1)), HTTPClient.MAX_DELAY - ) - - sleep_seconds = self._add_jitter_time(sleep_seconds) - - # But never sleep less than the base sleep seconds. - sleep_seconds = max(HTTPClient.INITIAL_DELAY, sleep_seconds) - - # And never sleep less than the time the API asks us to wait, assuming it's a reasonable ask. - retry_after = self._retry_after_header(response) or 0 - if retry_after <= HTTPClient.MAX_RETRY_AFTER: - sleep_seconds = max(retry_after, sleep_seconds) - - return sleep_seconds - - def _add_jitter_time(self, sleep_seconds): - # Randomize the value in [(sleep_seconds/ 2) to (sleep_seconds)] - # Also separated method here to isolate randomness for tests - sleep_seconds *= 0.5 * (1 + random.uniform(0, 1)) - return sleep_seconds - - def _add_telemetry_header(self, headers): - last_request_metrics = getattr(self._thread_local, "last_request_metrics", None) - if openai.enable_telemetry and last_request_metrics: - telemetry = {"last_request_metrics": last_request_metrics.payload()} - headers["X-OpenAI-Client-Telemetry"] = json.dumps(telemetry) - - def _record_request_metrics(self, response, request_start): - _, _, rheaders, _ = response - if "Request-Id" in rheaders and openai.enable_telemetry: - request_id = rheaders["Request-Id"] - request_duration_ms = _now_ms() - request_start - self._thread_local.last_request_metrics = RequestMetrics( - request_id, request_duration_ms - ) - - @abc.abstractmethod - def close(self): - ... - - -class RequestsClient(HTTPClient): - name = "requests" - - def __init__(self, timeout=600, session=None, **kwargs): - super(RequestsClient, self).__init__(**kwargs) - self._session = session - self._timeout = timeout - - def request(self, method, url, headers, post_data=None, stream=False): - kwargs: Dict[str, Any] = {} - if self._verify_ssl_certs: - kwargs["verify"] = openai.ca_bundle_path - else: - kwargs["verify"] = False - - if self._proxy: - kwargs["proxies"] = self._proxy - - if getattr(self._thread_local, "session", None) is None: - self._thread_local.session = self._session or requests.Session() - - try: - try: - result = self._thread_local.session.request( - method, - url, - headers=headers, - data=post_data, - timeout=self._timeout, - stream=stream, - **kwargs, - ) - except TypeError as e: - raise TypeError( - "Warning: It looks like your installed version of the " - '"requests" library is not compatible with OpenAI\'s ' - "usage thereof. (HINT: The most likely cause is that " - 'your "requests" library is out of date. You can fix ' - 'that by running "pip install -U requests".) The ' - "underlying error was: %s" % (e,) - ) - - # This causes the content to actually be read, which could cause - # e.g. a socket timeout. TODO: The other fetch methods probably - # are susceptible to the same and should be updated. - if stream and "text/event-stream" in result.headers.get("Content-Type", ""): - content = result.iter_lines() - stream = True - else: - content = result.content - stream = False - status_code = result.status_code - except Exception as e: - # Would catch just requests.exceptions.RequestException, but can - # also raise ValueError, RuntimeError, etc. - self._handle_request_error(e) - return content, status_code, result.headers, stream - - def _handle_request_error(self, e): - # Catch SSL error first as it belongs to ConnectionError, - # but we don't want to retry, unless it is caused by dropped - # SSL connection - if isinstance(e, requests.exceptions.SSLError): - if "ECONNRESET" not in repr(e): - msg = ( - "Could not verify OpenAI's SSL certificate. Please make " - "sure that your network is not intercepting certificates. " - "If this problem persists, let us know at " - "support@openai.com." - ) - should_retry = False - else: - msg = "Detected ECONNRESET, indicates a dropped SSL connection." - should_retry = True - err = "%s: %s" % (type(e).__name__, str(e)) - # Retry only timeout and connect errors; similar to urllib3 Retry - elif isinstance( - e, - ( - requests.exceptions.Timeout, - requests.exceptions.ConnectionError, - requests.exceptions.ChunkedEncodingError, - ), - ): - msg = ( - "Unexpected error communicating with OpenAI. " - "If this problem persists, let us know at " - "support@openai.com." - ) - err = "%s: %s" % (type(e).__name__, str(e)) - should_retry = True - # Catch remaining request exceptions - elif isinstance(e, requests.exceptions.RequestException): - msg = ( - "Unexpected error communicating with OpenAI. " - "If this problem persists, let us know at " - "support@openai.com." - ) - err = "%s: %s" % (type(e).__name__, str(e)) - should_retry = False - else: - msg = ( - "Unexpected error communicating with OpenAI. " - "It looks like there's probably a configuration " - "issue locally. If this problem persists, let us " - "know at support@openai.com." - ) - err = "A %s was raised" % (type(e).__name__,) - if str(e): - err += " with error message %s" % (str(e),) - else: - err += " with no error message" - should_retry = False - - if isinstance(e, requests.RequestException): - request = e.request # type: requests.Request - if request is not None: - err += " (url=" + self._sanitized_url(http://github.com/request.url) + ")" - - msg = textwrap.fill(msg) + "\n\n(Network error: %s)" % (err,) - raise error.APIConnectionError(msg, should_retry=should_retry) - - @staticmethod - def _sanitized_http://github.com/url(url): - """for now just strip all query params from the url for privacy""" - url = urlparse(url) - return url.scheme + "://" + url.netloc + url.path - - def close(self): - if getattr(self._thread_local, "session", None) is not None: - self._thread_local.session.close() diff --git a/openai/multipart_data_generator.py b/openai/multipart_data_generator.py deleted file mode 100644 index 43c37c25f2..0000000000 --- a/openai/multipart_data_generator.py +++ /dev/null @@ -1,92 +0,0 @@ -from __future__ import absolute_import, division, print_function - -import random -import io - -import openai -import re - - -class MultipartDataGenerator(object): - def __init__(self, chunk_size=1028): - self.data = io.BytesIO() - self.line_break = "\r\n" - self.boundary = self._initialize_boundary() - self.chunk_size = chunk_size - - def _remove_array_element(self, input_string): - match = re.match(r"^(.*)\[.*\]$", input_string) - return match[1] if match else input_string - - def add_params(self, params): - # Flatten parameters first - params = dict(openai.api_requestor._api_encode(params)) - - for key, value in params.items(): - - # strip array elements if present from key - key = self._remove_array_element(key) - - if value is None: - continue - - self._write(self.param_header()) - self._write(self.line_break) - if hasattr(value, "read"): - filename = "blob" - if hasattr(value, "name"): - # Convert the filename to string, just in case it's not - # already one. E.g. `tempfile.TemporaryFile` has a `name` - # attribute but it's an `int`. - filename = str(value.name) - - self._write('Content-Disposition: form-data; name="') - self._write(key) - self._write('"; filename="') - self._write(filename) - self._write('"') - self._write(self.line_break) - self._write("Content-Type: application/octet-stream") - self._write(self.line_break) - self._write(self.line_break) - - self._write_file(value) - else: - self._write('Content-Disposition: form-data; name="') - self._write(key) - self._write('"') - self._write(self.line_break) - self._write(self.line_break) - self._write(str(value)) - - self._write(self.line_break) - - def param_header(self): - return "--%s" % self.boundary - - def get_post_data(self): - self._write("--%s--" % (self.boundary,)) - self._write(self.line_break) - return self.data.getvalue() - - def _write(self, value): - if isinstance(value, bytes): - array = bytearray(value) - elif isinstance(value, str): - array = bytearray(value, encoding="utf-8") - else: - raise TypeError( - "unexpected type: {value_type}".format(value_type=type(value)) - ) - - self.data.write(array) - - def _write_file(self, f): - while True: - file_contents = f.read(self.chunk_size) - if not file_contents: - break - self._write(file_contents) - - def _initialize_boundary(self): - return random.randint(0, 2 ** 63) diff --git a/openai/object_classes.py b/openai/object_classes.py index 93f3d929e3..76e8c0fe1b 100644 --- a/openai/object_classes.py +++ b/openai/object_classes.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from openai import api_resources from openai.api_resources.experimental.completion_config import CompletionConfig diff --git a/openai/openai_object.py b/openai/openai_object.py index 109741665f..9b56082d51 100644 --- a/openai/openai_object.py +++ b/openai/openai_object.py @@ -1,66 +1,32 @@ -from __future__ import absolute_import, division, print_function - -import datetime import json from copy import deepcopy +from typing import Optional -import openai from openai import api_requestor, util - - -def _compute_diff(current, previous): - if isinstance(current, dict): - previous = previous or {} - diff = current.copy() - for key in set(previous.keys()) - set(diff.keys()): - diff[key] = "" - return diff - return current if current is not None else "" - - -def _serialize_list(array, previous): - array = array or [] - previous = previous or [] - params = {} - - for i, v in enumerate(array): - previous_item = previous[i] if len(previous) > i else None - if hasattr(v, "serialize"): - params[str(i)] = v.serialize(previous_item) - else: - params[str(i)] = _compute_diff(v, previous_item) - - return params +from openai.openai_response import OpenAIResponse class OpenAIObject(dict): api_base_override = None - class ReprJSONEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, datetime.datetime): - return api_requestor._encode_datetime(obj) - return super(OpenAIObject.ReprJSONEncoder, self).default(obj) - def __init__( self, id=None, api_key=None, api_version=None, organization=None, - last_response=None, + response_ms: Optional[int] = None, api_base=None, engine=None, **params, ): super(OpenAIObject, self).__init__() - self._unsaved_values = set() - self._transient_values = set() - self._last_response = last_response + if response_ms is not None and not isinstance(response_ms, int): + raise TypeError(f"response_ms is a {type(response_ms).__name__}.") + self._response_ms = response_ms self._retrieve_params = params - self._previous = None object.__setattr__(self, "api_key", api_key) object.__setattr__(self, "api_version", api_version) @@ -72,14 +38,8 @@ def __init__( self["id"] = id @property - def last_response(self): - return self._last_response - - def update(self, update_dict): - for k in update_dict: - self._unsaved_values.add(k) - - return super(OpenAIObject, self).update(update_dict) + def response_ms(self) -> Optional[int]: + return self._response_ms def __setattr__(self, k, v): if k[0] == "_" or k in self.__dict__: @@ -91,7 +51,6 @@ def __setattr__(self, k, v): def __getattr__(self, k): if k[0] == "_": raise AttributeError(k) - try: return self[k] except KeyError as err: @@ -110,37 +69,10 @@ def __setitem__(self, k, v): "We interpret empty strings as None in requests." "You may set %s.%s = None to delete the property" % (k, str(self), k) ) - - # Allows for unpickling in Python 3.x - if not hasattr(self, "_unsaved_values"): - self._unsaved_values = set() - - self._unsaved_values.add(k) - super(OpenAIObject, self).__setitem__(k, v) - def __getitem__(self, k): - try: - return super(OpenAIObject, self).__getitem__(k) - except KeyError as err: - if k in self._transient_values: - raise KeyError( - "%r. HINT: The %r attribute was set in the past. " - "It was then wiped when refreshing the object with " - "the result returned by OpenAI's API, probably as a " - "result of a save(). The attributes currently " - "available on this object are: %s" - % (k, k, ", ".join(list(self.keys()))) - ) - else: - raise err - def __delitem__(self, k): - super(OpenAIObject, self).__delitem__(k) - - # Allows for unpickling in Python 3.x - if hasattr(self, "_unsaved_values") and k in self._unsaved_values: - self._unsaved_values.remove(k) + raise NotImplementedError("del is not supported") # Custom unpickling method that uses `update` to update the dictionary # without calling __setitem__, which would fail if any value is an empty @@ -172,7 +104,7 @@ def construct_from( api_version=None, organization=None, engine=None, - last_response=None, + response_ms: Optional[int] = None, ): instance = cls( values.get("id"), @@ -180,14 +112,14 @@ def construct_from( api_version=api_version, organization=organization, engine=engine, - last_response=last_response, + response_ms=response_ms, ) instance.refresh_from( values, api_key=key, api_version=api_version, organization=organization, - last_response=last_response, + response_ms=response_ms, ) return instance @@ -195,29 +127,17 @@ def refresh_from( self, values, api_key=None, - partial=False, api_version=None, organization=None, - last_response=None, + response_ms: Optional[int] = None, ): self.api_key = api_key or getattr(values, "api_key", None) self.api_version = api_version or getattr(values, "api_version", None) self.organization = organization or getattr(values, "organization", None) - self._last_response = last_response or getattr(values, "_last_response", None) - - # Wipe old state before setting new. This is useful for e.g. - # updating a customer, where there is no persistent card - # parameter. Mark those values which don't persist as transient - if partial: - self._unsaved_values = self._unsaved_values - set(values) - else: - removed = set(self.keys()) - set(values) - self._transient_values = self._transient_values | removed - self._unsaved_values = set() - self.clear() - - self._transient_values = self._transient_values - set(values) + self._response_ms = response_ms or getattr(values, "_response_ms", None) + # Wipe old state before setting new. + self.clear() for k, v in values.items(): super(OpenAIObject, self).__setitem__( k, util.convert_to_openai_object(v, api_key, api_version, organization) @@ -230,7 +150,14 @@ def api_base(cls): return None def request( - self, method, url, params=None, headers=None, stream=False, plain_old_data=False + self, + method, + url, + params=None, + headers=None, + stream=False, + plain_old_data=False, + request_id: Optional[str] = None, ): if params is None: params = self._retrieve_params @@ -241,10 +168,11 @@ def request( organization=self.organization, ) response, stream, api_key = requestor.request( - method, url, params, headers, stream=stream + method, url, params, stream=stream, headers=headers, request_id=request_id ) if stream: + assert not isinstance(response, OpenAIResponse) # must be an iterator return ( util.convert_to_openai_object( line, @@ -284,7 +212,7 @@ def __repr__(self): def __str__(self): obj = self.to_dict_recursive() - return json.dumps(obj, sort_keys=True, indent=2, cls=self.ReprJSONEncoder) + return json.dumps(obj, sort_keys=True, indent=2) def to_dict(self): return dict(self) @@ -305,27 +233,6 @@ def to_dict_recursive(self): def openai_id(self): return self.id - def serialize(self, previous): - params = {} - unsaved_keys = self._unsaved_values or set() - previous = previous or self._previous or {} - - for k, v in self.items(): - if k == "id" or (isinstance(k, str) and k.startswith("_")): - continue - elif isinstance(v, openai.api_resources.abstract.APIResource): - continue - elif hasattr(v, "serialize"): - child = v.serialize(previous.get(k, None)) - if child != {}: - params[k] = child - elif k in unsaved_keys: - params[k] = _compute_diff(v, previous.get(k, None)) - elif k == "additional_owners" and v is not None: - params[k] = _serialize_list(v, previous.get(k, None)) - - return params - # This class overrides __setitem__ to throw exceptions on inputs that it # doesn't like. This can cause problems when we try to copy an object # wholesale because some data that's returned from the API may not be valid diff --git a/openai/openai_response.py b/openai/openai_response.py index 4550c22ec0..aa0d3a2ba0 100644 --- a/openai/openai_response.py +++ b/openai/openai_response.py @@ -1,25 +1,20 @@ -from __future__ import absolute_import, division, print_function +from typing import Optional -import json +class OpenAIResponse: + def __init__(self, data, headers): + self._headers = headers + self.data = data -class OpenAIResponse(object): - def __init__(self, body, code, headers): - self.body = body - self.code = code - self.headers = headers - self.data = json.loads(body) + @property + def request_id(self) -> Optional[str]: + return self._headers.get("request-id") @property - def idempotency_key(self): - try: - return self.headers["idempotency-key"] - except KeyError: - return None + def organization(self) -> Optional[str]: + return self._headers.get("OpenAI-Organization") @property - def request_id(self): - try: - return self.headers["request-id"] - except KeyError: - return None + def response_ms(self) -> Optional[int]: + h = self._headers.get("Openai-Processing-Ms") + return None if h is None else int(h) diff --git a/openai/request_metrics.py b/openai/request_metrics.py deleted file mode 100644 index 3ed45a35bf..0000000000 --- a/openai/request_metrics.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import absolute_import, division, print_function - - -class RequestMetrics(object): - def __init__(self, request_id, request_duration_ms): - self.request_id = request_id - self.request_duration_ms = request_duration_ms - - def payload(self): - return { - "request_id": self.request_id, - "request_duration_ms": self.request_duration_ms, - } diff --git a/openai/tests/test_api_requestor.py b/openai/tests/test_api_requestor.py new file mode 100644 index 0000000000..2e2927386f --- /dev/null +++ b/openai/tests/test_api_requestor.py @@ -0,0 +1,27 @@ +import json + +import requests +from pytest_mock import MockerFixture + +from openai import Model + + +def test_requestor_sets_request_id(mocker: MockerFixture) -> None: + # Fake out 'requests' and confirm that the X-Request-Id header is set. + + got_headers = {} + + def fake_request(self, *args, **kwargs): + nonlocal got_headers + got_headers = kwargs["headers"] + r = requests.Response() + r.status_code = 200 + r.headers["content-type"] = "application/json" + r._content = json.dumps({}).encode("utf-8") + return r + + mocker.patch("requests.sessions.Session.request", fake_request) + fake_request_id = "1234" + Model.retrieve("xxx", request_id=fake_request_id) # arbitrary API resource + got_request_id = got_headers.get("X-Request-Id") + assert got_request_id == fake_request_id diff --git a/openai/tests/test_endpoints.py b/openai/tests/test_endpoints.py index b6fe681684..6ef4d2d373 100644 --- a/openai/tests/test_endpoints.py +++ b/openai/tests/test_endpoints.py @@ -1,7 +1,8 @@ -import openai import io import json +import openai + # FILE TESTS def test_file_upload(): @@ -12,15 +13,18 @@ def test_file_upload(): assert result.purpose == "search" assert "id" in result + result = openai.File.retrieve(id=result.id) + assert result.status == "uploaded" + # COMPLETION TESTS def test_completions(): - result = openai.Completion.create(prompt="This was a test", n=5, engine="davinci") + result = openai.Completion.create(prompt="This was a test", n=5, engine="ada") assert len(result.choices) == 5 def test_completions_multiple_prompts(): result = openai.Completion.create( - prompt=["This was a test", "This was another test"], n=5, engine="davinci" + prompt=["This was a test", "This was another test"], n=5, engine="ada" ) assert len(result.choices) == 10 diff --git a/openai/tests/test_file_cli.py b/openai/tests/test_file_cli.py new file mode 100644 index 0000000000..69ea29e2a0 --- /dev/null +++ b/openai/tests/test_file_cli.py @@ -0,0 +1,39 @@ +import json +import subprocess +import time +from tempfile import NamedTemporaryFile + +STILL_PROCESSING = "File is still processing. Check back later." + + +def test_file_cli() -> None: + contents = json.dumps({"prompt": "1 + 3 =", "completion": "4"}) + "\n" + with NamedTemporaryFile(suffix=".jsonl", mode="wb") as train_file: + train_file.write(contents.encode("utf-8")) + train_file.flush() + create_output = subprocess.check_output( + ["openai", "api", "files.create", "-f", train_file.name, "-p", "fine-tune"] + ) + file_obj = json.loads(create_output) + assert file_obj["bytes"] == len(contents) + file_id: str = file_obj["id"] + assert file_id.startswith("file-") + start_time = time.time() + while True: + delete_result = subprocess.run( + ["openai", "api", "files.delete", "-i", file_id], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + if delete_result.returncode == 0: + break + elif STILL_PROCESSING in delete_result.stderr: + time.sleep(0.5) + if start_time + 60 < time.time(): + raise RuntimeError("timed out waiting for file to become available") + continue + else: + raise RuntimeError( + f"delete failed: stdout={delete_result.stdout} stderr={delete_result.stderr}" + ) diff --git a/openai/tests/test_util.py b/openai/tests/test_util.py new file mode 100644 index 0000000000..d0ce0ac5c4 --- /dev/null +++ b/openai/tests/test_util.py @@ -0,0 +1,30 @@ +from tempfile import NamedTemporaryFile + +import pytest + +import openai +from openai import util + + +@pytest.fixture(scope="function") +def api_key_file(): + saved_path = openai.api_key_path + try: + with NamedTemporaryFile(prefix="openai-api-key", mode="wt") as tmp: + openai.api_key_path = tmp.name + yield tmp + finally: + openai.api_key_path = saved_path + + +def test_openai_api_key_path(api_key_file) -> None: + print("sk-foo", file=api_key_file) + api_key_file.flush() + assert util.default_api_key() == "sk-foo" + + +def test_openai_api_key_path_with_malformed_key(api_key_file) -> None: + print("malformed-api-key", file=api_key_file) + api_key_file.flush() + with pytest.raises(ValueError, match="Malformed API key"): + util.default_api_key() diff --git a/openai/util.py b/openai/util.py index 5e10292e95..3be1717034 100644 --- a/openai/util.py +++ b/openai/util.py @@ -1,36 +1,23 @@ -import functools -import hmac -import io import logging import os import re import sys -from urllib.parse import parse_qsl +from typing import Optional import openai - OPENAI_LOG = os.environ.get("OPENAI_LOG") logger = logging.getLogger("openai") __all__ = [ - "io", - "parse_qsl", "log_info", "log_debug", "log_warn", - "dashboard_link", "logfmt", ] -def is_appengine_dev(): - return "APPENGINE_RUNTIME" in os.environ and "Dev" in os.environ.get( - "SERVER_SOFTWARE", "" - ) - - def _console_log_level(): if openai.log in ["debug", "info"]: return openai.log @@ -60,21 +47,6 @@ def log_warn(message, **params): logger.warn(msg) -def _test_or_live_environment(): - if openai.api_key is None: - return - match = re.match(r"sk_(live|test)_", openai.api_key) - if match is None: - return - return match.groups()[0] - - -def dashboard_link(request_id): - return "https://dashboard.openai.com/%7Benv%7D/logs/%7Breqid%7D".format( - env=_test_or_live_environment() or "test", reqid=request_id - ) - - def logfmt(props): def fmt(key, val): # Handle case where val is a bytes or bytesarray @@ -93,10 +65,6 @@ def fmt(key, val): return " ".join([fmt(key, val) for key, val in sorted(props.items())]) -def secure_compare(val1, val2): - return hmac.compare_digest(val1, val2) - - def get_object_classes(): # This is here to avoid a circular dependency from openai.object_classes import OBJECT_CLASSES @@ -112,18 +80,13 @@ def convert_to_openai_object( engine=None, plain_old_data=False, ): - # If we get a OpenAIResponse, we'll want to return a - # OpenAIObject with the last_response field filled out with - # the raw API response information - openai_response = None + # If we get a OpenAIResponse, we'll want to return a OpenAIObject. + response_ms: Optional[int] = None if isinstance(resp, openai.openai_response.OpenAIResponse): - # TODO: move this elsewhere - openai_response = resp - resp = openai_response.data - organization = ( - openai_response.headers.get("OpenAI-Organization") or organization - ) + organization = resp.organization + response_ms = resp.response_ms + resp = resp.data if plain_old_data: return resp @@ -151,7 +114,7 @@ def convert_to_openai_object( api_key, api_version=api_version, organization=organization, - last_response=openai_response, + response_ms=response_ms, engine=engine, ) else: @@ -178,47 +141,22 @@ def convert_to_dict(obj): return obj -def populate_headers(idempotency_key=None, request_id=None): - headers = {} - if idempotency_key is not None: - headers["Idempotency-Key"] = idempotency_key - if request_id is not None: - headers["X-Request-Id"] = request_id - if openai.debug: - headers["OpenAI-Debug"] = "true" - - return headers - - def merge_dicts(x, y): z = x.copy() z.update(y) return z -class class_method_variant(object): - def __init__(self, class_method_name): - self.class_method_name = class_method_name - - def __call__(self, method): - self.method = method - return self - - def __get__(self, obj, objtype=None): - @functools.wraps(self.method) - def _wrapper(*args, **kwargs): - if obj is not None: - # Method was called as an instance method, e.g. - # instance.method(...) - return self.method(obj, *args, **kwargs) - elif len(args) > 0 and isinstance(args[0], objtype): - # Method was called as a class method with the instance as the - # first argument, e.g. Class.method(instance, ...) which in - # Python is the same thing as calling an instance method - return self.method(args[0], *args[1:], **kwargs) - else: - # Method was called as a class method, e.g. Class.method(...) - class_method = getattr(objtype, self.class_method_name) - return class_method(*args, **kwargs) - - return _wrapper +def default_api_key() -> str: + if openai.api_key_path: + with open(openai.api_key_path, "rt") as k: + api_key = k.read().strip() + if not api_key.startswith("sk-"): + raise ValueError(f"Malformed API key in {openai.api_key_path}.") + return api_key + elif openai.api_key is not None: + return openai.api_key + else: + raise openai.error.AuthenticationError( + "No API key provided. You can set your API key in code using 'openai.api_key = ', or you can set the environment variable OPENAI_API_KEY=). If your API key is stored in a file, you can point the openai module at it with 'openai.api_key_path = '. You can generate API keys in the OpenAI web interface. See https://onboard.openai.com for details, or email support@openai.com if you have any questions." + ) diff --git a/openai/validators.py b/openai/validators.py index 181aacb7dd..976bd6f714 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -1,9 +1,9 @@ import os import sys -import pandas as pd -import numpy as np +from typing import Any, Callable, NamedTuple, Optional -from typing import NamedTuple, Optional, Callable, Any +import numpy as np +import pandas as pd class Remediation(NamedTuple): diff --git a/openai/version.py b/openai/version.py index e5f9491ec9..056f0f4ba2 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.10.5" +VERSION = "0.11.0" diff --git a/setup.py b/setup.py index 21bb3fcbb2..deeb740c98 100644 --- a/setup.py +++ b/setup.py @@ -20,8 +20,8 @@ "pandas-stubs>=1.1.0.11", # Needed for type hints for mypy "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format ], - extras_require={"dev": ["black==20.8b1", "pytest==6.*"]}, - python_requires=">=3.6", + extras_require={"dev": ["black~=21.6b0", "pytest==6.*"]}, + python_requires=">=3.7.1", scripts=["bin/openai"], packages=find_packages(exclude=["tests", "tests.*"]), package_data={ From 1f324723ccac898a133fa8b4d69c0c356fcfac4e Mon Sep 17 00:00:00 2001 From: Boris Power <81998504+BorisPower@users.noreply.github.com> Date: Thu, 2 Dec 2021 21:03:04 +0000 Subject: [PATCH 002/914] Boris/embedding examples (#49) * add Olympics Q&A fine-tuning tutorial notebooks * fix batch calculation for very small datasets * Add embeddings tutorial notebooks --- examples/embeddings/Classification.ipynb | 130 +++ examples/embeddings/Clustering.ipynb | 262 ++++++ examples/embeddings/Code_search.ipynb | 396 +++++++++ examples/embeddings/Get_embeddings.ipynb | 107 +++ examples/embeddings/Obtain_dataset.ipynb | 192 +++++ examples/embeddings/Regression.ipynb | 109 +++ ...emantic_text_search_using_embeddings.ipynb | 185 +++++ .../User_and_product_embeddings.ipynb | 177 +++++ examples/embeddings/Visualize_in_2d.ipynb | 142 ++++ .../embeddings/Zero-shot_classification.ipynb | 226 ++++++ examples/embeddings/utils.py | 94 +++ ...{answers-with-ft.py => answers_with_ft.py} | 9 +- .../finetuning/olympics-1-collect-data.ipynb | 513 ++++++++++++ .../finetuning/olympics-2-create-qa.ipynb | 751 ++++++++++++++++++ examples/finetuning/olympics-3-train-qa.ipynb | 637 +++++++++++++++ openai/validators.py | 5 +- openai/version.py | 2 +- 17 files changed, 3933 insertions(+), 4 deletions(-) create mode 100644 examples/embeddings/Classification.ipynb create mode 100644 examples/embeddings/Clustering.ipynb create mode 100644 examples/embeddings/Code_search.ipynb create mode 100644 examples/embeddings/Get_embeddings.ipynb create mode 100644 examples/embeddings/Obtain_dataset.ipynb create mode 100644 examples/embeddings/Regression.ipynb create mode 100644 examples/embeddings/Semantic_text_search_using_embeddings.ipynb create mode 100644 examples/embeddings/User_and_product_embeddings.ipynb create mode 100644 examples/embeddings/Visualize_in_2d.ipynb create mode 100644 examples/embeddings/Zero-shot_classification.ipynb create mode 100644 examples/embeddings/utils.py rename examples/finetuning/{answers-with-ft.py => answers_with_ft.py} (92%) create mode 100644 examples/finetuning/olympics-1-collect-data.ipynb create mode 100644 examples/finetuning/olympics-2-create-qa.ipynb create mode 100644 examples/finetuning/olympics-3-train-qa.ipynb diff --git a/examples/embeddings/Classification.ipynb b/examples/embeddings/Classification.ipynb new file mode 100644 index 0000000000..482ba85910 --- /dev/null +++ b/examples/embeddings/Classification.ipynb @@ -0,0 +1,130 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Classification using the embeddings\n", + "\n", + "In the classification task we predict one of the predefined categories given an input. We will predict the score based on the embedding of the review's text, where the algorithm is correct only if it guesses the exact number of stars. We split the dataset into a training and a testing set for all the following tasks, so we can realistically evaluate performance on unseen data. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb).\n", + "\n", + "In the following example we're predicting the number of stars in a review, from 1 to 5." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " 1 0.82 0.67 0.74 21\n", + " 2 0.50 0.50 0.50 6\n", + " 3 1.00 0.46 0.63 13\n", + " 4 0.75 0.35 0.48 17\n", + " 5 0.88 1.00 0.93 143\n", + "\n", + " accuracy 0.86 200\n", + " macro avg 0.79 0.60 0.66 200\n", + "weighted avg 0.86 0.86 0.84 200\n", + "\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import classification_report, accuracy_score\n", + "\n", + "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", + "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", + "\n", + "X_train, X_test, y_train, y_test = train_test_split(list(df.babbage_similarity.values), df.Score, test_size = 0.2, random_state=42)\n", + "\n", + "clf = RandomForestClassifier(n_estimators=100)\n", + "clf.fit(X_train, y_train)\n", + "preds = clf.predict(X_test)\n", + "probas = clf.predict_proba(X_test)\n", + "\n", + "report = classification_report(y_test, preds)\n", + "print(report)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the model has learnt to distinguish between the categories decently. 5-star reviews show the best performance overall, and this is not too surprising, since they are the most common in the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "RandomForestClassifier() - Average precision score over all classes: 0.93\n" + ] + }, + { + "data": { + "image/png": "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I+9ynph431RPPvlk1qxZw1tvvcXixYv55S9/2ahNn3/+Oddccw0Ad911F/Pnt1+SKYPBELvohsNh/P5jgVc2m61W24ULF7Jq1Sq+++47LrzwQkATTL/5zW9i9tXkiy++4O233+a3v/0ts2bN4rbbbms3uzsDIQTUESQyECDw3VFCrnKtjcmEsXdv9JFhOEX3xGQykZGRQWlpKWVlZWSoOCiFIqFQnph2Zv/+/fTo0YOrr76aq666ii+++ILJkyfzn//8h+LiYkKhEM899xwzZsxgypQpbN68mc2bN7dawJxyyim8+OKLlETylUSHkwYMGMCmTZsAbZiqqQDFCy+8kOeff55Vq1axcOFCAObMmcOTTz6J2+0G4PDhwzGPTUpKCj/60Y9YsmQJX3yRnMXjavpigiUl+PLzNQEjBIacHMxDhigBowDAbDaTlpaGz+ejvLxcTTtWKBKILueJiTeffPIJ999/P0ajEbvdztNPP02vXr247777+MEPfoCUknnz5nH22Wcft68NGzawYMECysrKePPNN/n973/PN998U6tNXl4et956KzNmzECv13PiiSeyfPlyrr76as4++2zGjh3L3Llz63lf6vZRWVlJbm4uvXr1AuC0005j+/btTJs2DdACg//1r3+xa9culixZgk6nw2g08uijj7Zhb8UPodNETLi6mnB1NQD61FQMvXqhM5niaZoiAbFarYRCISoqKtDpdHH3+CoUCo0uUTtp+/btjBgxIk4WdTzRVPnRKsyKthOqqsK/Zw8AwmjE2KsXutTUDh8qUGUHmknh9kjZgeGxsgNR4nm+V1RU4Ha7SUlJwdlEeYlkZ8N3G7jivSuY2GMiT819Kt7mKBKdfZPBuwH6f96qwN5uXztJoWgpOosFXWoqmEyYcnJUtl1Fs3A4HAghqKysREpJmiozoVDEFSViFN0SodNh7NuXYDCIbGK2kkJRl9SIx66iogIpJenp6UrIKBRxQo1NKLot7VEMUtE9sdvtOJ1OvF4vpaWl6hhSKOKEEjGKbosQAp1Op/J/KFqFzWYjPT0dv99PSUmJOo4UijigREwSoDwGHYcQAiml2reKVmG1WklPTycQCCgho1DEASViuil6vT5W32nhwoVUVVW1uc/bbruN1atXN7p+2bJlPP30023eTnsSne0VvflEa0zt27ePUaNGNfiZgoICzjzzzM4xsB3x+XxceOGFDBkyhClTprBv374G2z300EOMGjWKvLw8Hnzwwdj7v/vd7xgzZgzjxo3jtNNO48iRIwC8+eabSZfwsD2xWCxkZGQQDAZjuaAUCkXnoERMAtORF0Or1crmzZvZunUrJpOJZcuW1VrfmvpTd911F6eeemqj66+99louu+yyFvd7PNpSK0sIEfPGNJe//OUvXH311a3eZnPoiKKD//znP0lPT2fXrl384he/aLAQ6datW3niiSdYv349X331FW+++Sa7du0CYMmSJWzZsoXNmzdz5plnctdddwEwb9483njjjXYRwsmK2WwmMzOTcDhMSUlJtywaqVDEgw4VMUKIuUKIb4UQu4QQSxtpc4EQYpsQ4hshxL870p6O5JxzzmHChAnk5eXx+OOPA5rnYcmSJbE2y5cv5/rrrwfgX//6F5MnT2bcuHFcc801McFit9v51a9+xdixY1m3bh133XUXkydP5sQTT+Taa6+N3Ww3bNgQ+1e8ZMmSmNcgFAqxZMmSWPXpxx577Li2T58+nV27dtWrcN1UX3/84x8ZPXo0Y8eOZelS7aetWTl76dKljBw5kjFjxnDTTTcBtStkb968malTpzJmzJhYQj+AmTNncvPNNzN58mSGDRsWqwJel5kzZ3LjjTcyceJEHnroITZt2sSMGTOYMGECc+bMoaCgAIBdu3Zx6qmnMnbsWMaPH8/u3btxu93MmjWL8ePHM3r0aN58803C4XCzhcxLL73E3LlzAc1jM336dMaPH8/48eP59NNPAS3pYU1vzfXXX8/y5csBGD5nDjffdhujR49m8uTJMZGwePFirr32WqZMmcKvf/3rBm1vC6+99hqLFi0C4Pzzz+fDDz+s9523b9/OlClTSElJwWAwMGPGDF5++WVAm14cxePxxIY5hRDMnDmTN998s032JTvREgVKyCgUnUg0HqC9H4Ae2A0MAkzAV8DIOm2GAl8C6ZHXOcfrd8KECbIu27Ztq/deZ1NSUiKllLKqqkrm5eXJ4uJiWVhYKAcPHhxrM3fuXLl27Vq5bds2eeaZZ0q/3y+llPK6666TK1askFJKCciVK1fW69fn88lLLrlEvv7661JKKfPy8uSnn34qpZTy5ptvlnl5eVJKKR977DH5+9//XkoppdfrlRMmTJB79uypZ6/NZpNSShkIBOT8+fPl3//+d/nxxx/LlJSUWPvG+nr77bfltGnTpMfjqWXjokWL5IsvviiLi4vlsGHDZDgcllJKWVZWJqWU8vbbb5f333+/lFLK0aNHy08++URKKeXvfvc7+fOf/1xKKeWMGTPkL3/5SymllG+99ZacNWtWg/t7xowZ8rrrrpNSSun3++W0adNkYWGhlFLK559/Xl5++eVSSiknT54sX375ZSmllNXV1dLj8chAICBdLpeUUsqioiI5ePBg6fV6ZSgUiu2XvXv3xvZpTfbs2SPHjx8fe+3xeGR1dbWUUsqdO3fK6PH58ccfy3nz5sXa/fSnP5VPPfWU9O3fL/v17i3v+u1vpZRSrlixItZu0aJFct68eTIYDDZqe12+//3vy7Fjx9Z7fPDBB/Xa5uXlyYMHD8ZeDxo0SBYVFdVqs23bNjl06FBZXFwsPR6PnDp1qrz++utj62+55RbZp08fmZeXF9vfUkr5r3/9q1a7NnN0m5SHv5DSX1VvVSKc703h9/vld999JwsKCqTP54u3Oa1ifcF6OWr5KLn4ncXxNkWRDOydJOV2pKz6vFUfBzbKVmqNjswTMxnYJaXcAyCEeB44G9hWo83VwN+klGURQVXY5q3u6KB8DcOb/pf+17/+lVdeeQWAgwcPkp+fz9SpUxk0aBCfffYZQ4cOZceOHZx00kn87W9/Y9OmTUyaNAmA6upqcnJyAC1W5bzzzov1+/HHH/OnP/0Jj8dDWVkZo0aNYvr06VRWVsZKAlx88cWxf8Hvv/8+W7ZsiXlEXC4X+fn5sarVUaqrqxk3bhygeWKuvPJKPv3001oVrhvra/Xq1Vx++eWkpKQA9StnO51OLBYLV155JWeeeWa9+BGXy0V5eTkzZswAYNGiRbGaTQDnnnsuABMmTGg0bgOIFav89ttv2bp1K7NnzwY0b1SvXr2orKzk8OHDLFiwANBiFwACgQC33HILa9asQafTxepCRUsuNEVBQQHZ2dmx14FAgOuvv57Nmzej1+vZuXPncfsAuOj88wH44Q9/yC9+8YvY+wsXLkSv1zdqe10a81S1lhEjRnDzzTdz2mmnYbPZGDduHPoaiQDvuece7rnnHv7whz/wyCOPcOeddwKQk5MTi5Hp7hiNRjIzMyktLaWkpASn0xk7VxQKRfvSkSImFzhY4/UhYEqdNsMAhBD/Q/Pc3CGlfLcDbeoQPvnkE1avXs26detISUlh5syZeL1eAC666CJeeOEFhg8fzoIFC2LxF4sWLeIPf/hDvb4sFkvspuH1evnJT37Cxo0b6dmzJ7///e9j/TaGlJKHH36YOXPmNNkuGhNTl5o1lhrr67333muyb4PBwPr16/nwww9ZtWoVjzzyCB999FGTn6mJ2WwGNEEXdclffvnlfPnll/Tu3Zu33367lq1SSvLy8li3bl2tfiorKxvs/9lnn6WoqIhNmzZhNBoZMGAAPp+vWcNJVqu11m/wf//3f/To0YOvvvqKcDgcExs1q4gD9X63mlK7ZqK0pmpcNURU0NblgQceqBeflJuby8GDB+nTpw/BYBCXy0VmZma9z1555ZVceeWVANxyyy306dOnXptLLrmEM844IyZivF4vVqu1XrvuisFgICsri7KyMsrLywkGg7EkeQqFov2Id2CvAW1IaSbwQ+AJIURa3UZCiB8LITYKITYWFRU13eNw2TGPJnC5XKSnp5OSksKOHTv47LPPYusWLFjAa6+9xnPPPcdFF10EwKxZs1i1ahWFhZrjqbS0lP3799frN3rjy8rKwu12x2IT0tLSSE1N5fPPPwfg+eefj31mzpw5PProo7Gq1Tt37sTj8TS9zxqhsb5mz57NU089FQvkjFbOjuJ2u3G5XJxxxhn83//9H1999VWt9U6nk/T09JgX4Zlnnol5ZRrjqaeeYvPmzTEBU5MTTjiBoqKimIgJBAJ88803pKam0qdPH1599VVAm51TVVWFy+UiJycHo9HIxx9/zP79+9HpdM0SMcOGDavlHXK5XPTq1QudTsczzzwTi23q378/27Zti1U+/vDDD2v180LEa7dy5cqYR60mjdlel7Vr18Yqodd8NBRgPX/+fFasWAHAqlWrOOWUUxq8qUaPywMHDvDyyy9z8cUXA5Cfnx9r89prrzF8+PDY6507dzY6m6u7otPpyMjIwGaz4Xa7KS0tVVOwFV2czj++O9ITcxjoW+N1n8h7NTkEfC6lDAB7hRA70UTNhpqNpJSPA4+DVgCywyxuJXPnzmXZsmWMGDGCE044galTp8bWpaenM2LECLZt28bkyVphrJEjR3L33Xdz2mmnEQ6HMRqN/O1vf6N///61+k1LS+Pqq69m1KhR9OjRgwkTJsTW/fOf/+Tqq69Gp9MxY8aMWFXdq666in379jF+/HiklGRnZ8duhC2l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bBEREcbtN954Q997771648aN+mc/+5meOHGi7tGjh3a5XPqRRx7Rqampunfv3nrZsmXGYxYtWqR79eqlU1JS9OOPP6611nrGjBn6ww8/1Fpr/fjjj+urrrpK9+rVSz/00ENaa62ffvpp/eKLL2qttf7+++/14MGDde/evfWUKVN0Tk6O1lrrkSNH6scee0wPHDhQ9+jRQ3/99ddVln/kyJH6gQce0AMGDNBLlizRO3fu1CNGjND9+/fX1157rT516pTWWuuDBw/qsWPH6pSUFN2vXz996NAhXVBQoMeMGaP79eune/XqpdesWVPpuBw9elQnJydXet6JEydqk8mk+/Tpo99//3199OhRPXr0aN27d289ZswYfezYMeNY3HPPPXrQoEHG3+/jcrn0ww8/rJOTk3Xv3r31q6++avxNO3bs0FprPXfuXD1gwADds2dPvWDBAuOxvuPau3dv/fDDD2uttV61apVOTk7WKSkpevjw4dW95JW4XC5tt9v12bNndefOnfXJkycDetwTTzyh33nnHeN+dWW99NJLdWZmptZa6x07duiRI0dqrb3vg9tuu00PGTJEd+/eXS9fvlxrrY3334033KC7X3qpLty7t8rjVFd//vOf9d13323cv/vuu/Wf//znarf3eDy6U6dO+sCBA1rr6t8TWmudk5NT7brm8P8eVHLStX46WuuXegX8kO2nt+teK3rpmetmNmDBqldaWqpzcnL0qVOn9MmTJ3VGRobOz8/XTqezScoj6knpD1r/GKn1PrTO/l9jMbBT1zETBPpz9nFgDvBv4B7gM+D3Fx+hWh6Xy8W6deu4/vrrAfjuu+/Ys2cPXbt2Zfny5cTExLBjxw7sdjvDhg3j2muvZf/+/axdu5Zt27YZE/P5y87OZvXq1ezbtw+Xy1XlbNJ33HEHS5cuZeTIkSxYsICFCxfy8ssvG2Xavn07n332GQsXLmTDhg1Vlt3hcLBz506cTicjR45k7dq1JCQk8MEHH/DUU0/xdtnkgfPmzWPq1KnGXE42m43Vq1cTHR1NVlYWQ4YMYdKkSQFVBX/yySdERkYav8YnTpzIjBkzmDFjBm+//Tb3338/a9asAbyzJG/ZsqVSjc3y5ctJT09n165dxgSQFT333HO0adMGt9vN2LFj2b17Nx07dmT16tXs378fpRS5ubmAtwZs/fr1dOzY0VgW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+jQAhw4dMobH79+/P4cPH6awsJCxY8fSv39/evfuzdq1awM+VpMmTaKwsJABAwbwwQcfkJ6ezpgxY0hJSWHs2LEcP37cOBZz585l8ODB5Wa2Bm8weOSRR+jVqxcpKSksXbq00vPce++9pKamkpyczNNPP20s9x3XlJQU40v2ww8/pFevXvTp06dSIKlJWloaGRkZXHvttdVu4xsvxr9Z6b333ivXBl1dWbt06UJWVhbgreb1TT3wzDPPcPvtt3P11VfTo0cP3nrLO9m87/03+T/+g/5TpgR0nC7E+vXrGTduHG3atCEuLo5x48bx+eefX9A+1qxZQ9euXUlOTi63fMqUKVWGRCGCgcViISIigri4ONq1a0dCQgIxMTHYbLZyNTVnzpwhJyeHwsJCnE6nNDXXgcTAeuZyuVi3bh3XX389AN999x179uyha9euLF++nJiYGHbs2IHdbmfYsGFce+217N+/n7Vr17Jt2zZjAkh/2dnZrF69mv379+PxeMjOzq70vHfccQdLly5l5MiRLFiwgIULF/Lyyy8bZdq+fTufffYZCxcurNRE5eNwONi5cydOp5ORI0eydu1aEhIS+OCDD3jqqad4++23mT59OvPmzWPq1KnGXE42m43Vq1cTHR1NVlYWQ4YMYdKkSQH12v/kk0+IjIw0arEmTpzIjBkzmDFjBm+//Tb333+/MQLliRMn2LJlS6Uam+XLl5Oens6uXbuMiQ0reu6552jTpg1ut5uxY8eye/duOnbsaBxXpZQx18qzzz7L+vXr6dixo7GsNh6Ph4cffpg//elP1R5fwJgywuVyGTVKmzdvLjevUFVlTUlJqfH5d+/ezbfffktRURH9+vXjhhtuALzvv91paXRwuXirLCTWdJwuZBbrkydP0rlzZ+N+p06dOHnyZJXle+211/jjH/9Iamoqv/3tb4mLi6OwsJDFixfzxRdfGGHcp1evXuzYsaPGv1mIYOEbPdg3q7zL5cLhcBgX36z0vr43votvXjFRvRYXYvZd2TBzqly1f1+N60tKSujbty/grYmZPXs2W7ZsYdCgQXTt2hXwzvK7e/duo3YlLy/PaIO88847jarIijMLx8TEEBoayuzZs5kwYYIRkHzy8vLIzc01Zq6eMWMG06ZNM9bfdNNNAAwYMID09PRq/4Zbb70VgB9//JE9e/YYIzO63W7at29P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deumDx8+rO12u05JSdE//PBDpe1uvvlmvWLFCq211l9++aW+7bbbtNZab968WQ8dOlS7XC7tcrn0kCFD9MaNG7XWWq9YsUL/+te/rvJ5m8P/e1DJSdf66WitX+rV1CWpF//vn/9P91rRS3984OOmLoqoq6JvtN6H1unDtNZaAzt1HTOB1MQ0gOHDh3Po0CE2bdrE8OHDmTRpEj179sTtdvPoo48ycOBAUlJSyg2/vnjxYnr37k2fPn2YN28eADNnzjRmvJ43bx49e/YkJSWFxx9/HPCOD7JkyRIAdu3axZAhQ0hJSWHq1KnGr9hRo0bx+OOPM2jQIC6//PJy89X4GzVqFA8++CCpqam88sorpKWlMXLkSAYMGMB1113H6dOnATh06BDXXHMNffr0oX///hw+fJjCwkLGjh1L//796d27N2vXrg34WE2aNInCwkIGDBjABx98QHp6OmPGjCElJYWxY8dy/Phx41jMnTuXwYMHl5vZGrydzB555BF69epFSkoKS5curfQ89957L6mpqSQnJ/P0008by/2Pq2/E2w8//JBevXrRp08fRowYEfDfUld//etfy81M/uyzzzJw4EB69erFPffcg8lkQillzE4NkJWVRZcuXQDvXE2TJ09m1KhR9OjRg4ULFwLe2q8rrriCGbNmkTp1Kj+dPl3l+6yutm/fTvfu3enWrRs2m43//M//rPK137t3L2PGjAFg9OjRxjZKKUpLS3E4HNjtdpxOpzFZ5KRJk/jLX/5yUeUTQrR80rG3nrlcLtatW2d8KX333Xfs2bOHrl27snz5cmJiYtixYwd2u51hw4Zx7bXXsn//ftauXcu2bduMCSD9ZWdns3r1avbv349SiqysrEp9Yu644w6WLl3KyJEjWbBgAQsXLuTll182yrR9+3Y+++wzFi5cWG0TlcPhYOfOnTidTkaOHMnatWtJSEjggw8+4KmnnuLtt99m+vTpzJs3j6lTp1JaWorH48Fms7F69Wqio6PJyspiyJAhTJo0KaBOyJ988gmRkZHGDOATJ05kxowZzJgxg7fffpv777+fNWvWAN5Zkrds2VKpo+vy5ctJT09n165dxsSGFT333HO0adMGt9vN2LFj2b17Nx07dix3XHNzcwFviFi/fj0dO3Y0lgWitLSU1NRULBYL8+bNY8qUKQE9bvPmzdx8883G/fvuu48FCxYAcPvtt/Ppp58aMz9X17S0fft29uzZQ3h4OAMHDuSGG24gPj6egwcP8s5bb9HviSf4+9atNb7PwDuR5Ysvvlhpeffu3Y1A7XPy5Ek6d+5s3O/UqRPbtm2r9Ng+ffrw8ccf88ADD7B69WoKCgrIzs7m6quvZvTo0bRv3x6tNffdd58xnUBcXBx2u53s7OxaJ98UQrReLS7E/G7uVw2y318uG1Pj+pKSEvr27Qt4a2Jmz57Nli1bGDRoEF27dgW8s/zu3r3b+DLIy8vj4MGDbNiwgTvvvNOY2KtiG2FMTAyhoaHMnj2bG2+8kfHjxxtVab795ObmGjNXz5gxg2nTphmPv+mmmwAYMGAA6enp1f4Nt956KwA//vgje/bsYdw479kAbreb9u3bU1BQwMmTJ5k6dSqAMVCS0+nkySef5Ouvv8ZkMnHy5EkyMjJo165djcesKlu3buXjjz8GvF/g/rUu06ZNq/JMnQ0bNjB37lzjdOSq2lhXrVrF8uXLcblcnD59mr1799KzZ89yx9UXFIYNG8bMmTO55ZZbjGMXiGPHjtGxY0eOHDnCmDFj6N27N5dddlmtjzt9+jQJCQnG/Y0bN/LCCy9QXFxMTk4OycnJTJw40RgAz+VyVQoy48aNM77sb7rpJv75z38yZcoULr30UoYMHoz94EG+2rq1xvcZwPTp05k+fXrAf3MglixZwn333ceKFSsYMWIEHTt2xGw2c+jQIfbt22f0kRk3bpwxEztAYmIip06dkhAjhKhWiwsxTSUsLMyoTfAXEXF+yGytNUuXLuW6664rt8369etr3LfFYmH79u18+eWXfPTRRyxdurTWx/jznQbnG38E4M477+T777+nQ4cOfPbZZ+XKqrUmOTmZrVvLj5zpP/W7v/fee4/MzEzS0tKwWq106dKF0tLSgMsXKP9jeSGOHj3KkiVL2LFjB3FxccycOZPS0tJKx/W1117jq6++YtmyZWzbto1PP/2UAQMGkJaWFtAXaceOHQHo1q0bo0aN4vvvvw8oxISFhRnHq7S0lP/6r/9i586ddO7cmWeeecZY5xv8TilFYWFhuX1UrPXy3b/QY3YhNTEdO3bkp59+Mu6fOHHCOAb+OnToYATTwsJC/vrXvxIbG8tbb73FkCFDjM7f48ePZ+vWrUaIKS0tLTdUuhBCVNTiQkxtNSZN6brrruONN95gzJgxWK1WDhw4QMeOHRk3bhzPPvss06dPN6r5/X8lFxYWUlxczIQJExg2bBjdunUrt9+YmBji4uKMX7HvvvuuUStTnXfeeafadVdccQWZmZls3bqVq6++GqfTyYEDB0hOTqZTp06sWbOGKVOmYLfbcbvd5OXlkZiYiNVqZePGjRw7dqzOx2jo0KG8//773H777bz33nvGF1pNxo0bx5tvvsno0aON5iT/45efn09ERAQxMTFkZGSwbt06Ro0aVe1xPXz4MIMHD2bw4MGsW7eOn376qdYQc+7cOcLDwwkJCSErK4vNmzdX6rtTnauuuopDhw4xatQoI7DEx8dTWFjIRx99ZDQ1denShe+++45BgwaxevVqAKNG5osvviAnJ4ewsDDWrFnD22+/Xel5xg4dyqKyJsGq3mdwYTUxAwcO5ODBgxw9epSOHTvy/vvv8+c//7nSdllZWbRp0waTycRvfvMbZs2aBcAll1zCW2+9xRNPPIHWmn/84x88+OCDgDdInzlzxuj3I4QQVWlxIaY5mzNnDunp6fTv3x+tNQkJCaxZs4brr7+eXbt2kZqais1mY8KECTz//PPG4woKCpg8eTKlpaVorfntb38LlJ8EcuXKlcydO5fi4mK6detWY0ipjc1m46OPPuL+++8nLy8Pl8vFgw8+SHJyMu+++y733HMPCxYswGq18uGHHzJ9+nQmTpxI7969SU1N5corr6zzcy9dupQ777yTF198kYSEhID+jjlz5nDgwAFSUlKwWq3cdddd3Hfffcb6Pn360K9fP6688ko6d+7MsGHDgMrH9aWXXgK8p3wfPHgQrTVjx46lT58+tZZh3759Ridcj8djdBgOxA033MCbb77JnDlziI2N5a677qJXr160a9eOgQMHGts98sgj3HLLLSxfvpwJEyYAGE1LgwYN4j/+4z84ceIEt912G6mpqZWaDq8dMYK9WVnVvs8ulMVi4bXXXuO6667D7XYza9YskpOTAe/wAKmpqUyaNIlNmzbxxBNPoJRixIgR/O53vwPg5ptv5quvvqJ3794opbj++uuZOHEiAGlpaQwZMkRGLBZC1EhV7CBarztX6nrgFcAM/F5rXeVAEkqp/wA+AgZqrWsc2CM1NVVXHPtj3759RofA1sLpdBrn34vg97Of/Yy//e1vxMbGBvwYXTavycqVK/n++++NcFCRx27H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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from utils import plot_multiclass_precision_recall\n", + "\n", + "plot_multiclass_precision_recall(probas, y_test, [1,2,3,4,5], clf)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unsurprisingly 5-star and 1-star reviews seem to be easier to predict. Perhaps with more data, the nuances between 2-4 stars could be better predicted, but there's also probably more subjectivity in how people use the inbetween scores." + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Clustering.ipynb b/examples/embeddings/Clustering.ipynb new file mode 100644 index 0000000000..ab5cf055ab --- /dev/null +++ b/examples/embeddings/Clustering.ipynb @@ -0,0 +1,262 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clustering\n", + "\n", + "We use a simple k-means algorithm to demonstrate how clustering can be done. Clustering can help discover valuable, hidden groupings within the data. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1000, 2048)" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "\n", + "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", + "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", + "matrix = np.vstack(df.babbage_similarity.values)\n", + "matrix.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Find the clusters using K-means" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We show the simplest use of K-means. You can pick the number of clusters that fits your use case best." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Cluster\n", + "2 2.543478\n", + "3 4.374046\n", + "0 4.709402\n", + "1 4.832099\n", + "Name: Score, dtype: float64" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.cluster import KMeans\n", + "\n", + "n_clusters = 4\n", + "\n", + "kmeans = KMeans(n_clusters = n_clusters,init='k-means++',random_state=42)\n", + "kmeans.fit(matrix)\n", + "labels = kmeans.labels_\n", + "df['Cluster'] = labels\n", + "\n", + "df.groupby('Cluster').Score.mean().sort_values()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It looks like cluster 2 focused on negative reviews, while cluster 0 and 1 focused on positive reviews." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Clusters identified visualized in language 2d using t-SNE')" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from sklearn.manifold import TSNE\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "\n", + "tsne = TSNE(n_components=2, perplexity=15, random_state=42, init='random', learning_rate=200)\n", + "vis_dims2 = tsne.fit_transform(matrix)\n", + "\n", + "x = [x for x,y in vis_dims2]\n", + "y = [y for x,y in vis_dims2]\n", + "\n", + "for category, color in enumerate(['purple', 'green', 'red', 'blue']):\n", + " xs = np.array(x)[df.Cluster==category]\n", + " ys = np.array(y)[df.Cluster==category]\n", + " plt.scatter(xs, ys, color=color, alpha=0.3)\n", + "\n", + " avg_x = xs.mean()\n", + " avg_y = ys.mean()\n", + " \n", + " plt.scatter(avg_x, avg_y, marker='x', color=color, s=100)\n", + "plt.title(\"Clusters identified visualized in language 2d using t-SNE\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Visualization of clusters in a 2d projection. The red cluster clearly represents negative reviews. The blue cluster seems quite different from the others. Let's see a few samples from each cluster." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Text samples in the clusters & naming the clusters\n", + "\n", + "Let's show random samples from each cluster. We'll use davinci-instruct-beta-v3 to name the clusters, based on a random sample of 6 reviews from that cluster." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cluster 0 Theme: All of the customer reviews mention the great flavor of the product.\n", + "5, French Vanilla Cappuccino: Great price. Really love the the flavor. No need to add anything to \n", + "5, great coffee: A bit pricey once you add the S & H but this is one of the best flavor\n", + "5, Love It: First let me say I'm new to drinking tea. So you're not getting a well\n", + "----------------------------------------------------------------------------------------------------\n", + "Cluster 1 Theme: All three reviews mention the quality of the product.\n", + "5, Beautiful: I don't plan to grind these, have plenty other peppers for that. I go\n", + "5, Awesome: I can't find this in the stores and thought I would like it. So I bou\n", + "5, Came as expected: It was tasty and fresh. The other one I bought was old and tasted mold\n", + "----------------------------------------------------------------------------------------------------\n", + "Cluster 2 Theme: All reviews are about customer's disappointment.\n", + "1, Disappointed...: I should read the fine print, I guess. I mostly went by the picture a\n", + "5, Excellent but Price?: I first heard about this on America's Test Kitchen where it won a blin\n", + "1, Disappointed: I received the offer from Amazon and had never tried this brand before\n", + "----------------------------------------------------------------------------------------------------\n", + "Cluster 3 Theme: The reviews for these products have in common that the customers' dogs love them.\n", + "5, My Dog's Favorite Snack!: I was first introduced to this snack at my dog's training classes at p\n", + "4, Fruitables Crunchy Dog Treats: My lab goes wild for these and I am almost tempted to have a go at som\n", + "5, Happy with the product: My dog was suffering with itchy skin. He had been eating Natural Choi\n", + "----------------------------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "import openai\n", + "\n", + "# Reading a review which belong to each group.\n", + "rev_per_cluster = 3\n", + "\n", + "for i in range(n_clusters):\n", + " print(f\"Cluster {i} Theme:\", end=\" \")\n", + " \n", + " reviews = \"\\n\".join(df[df.Cluster == i].combined.str.replace(\"Title: \", \"\").str.replace(\"\\n\\nContent: \", \": \").sample(rev_per_cluster, random_state=42).values)\n", + " response = openai.Completion.create(\n", + " engine=\"davinci-instruct-beta-v3\",\n", + " prompt=f\"What do the following customer reviews have in common?\\n\\nCustomer reviews:\\n\\\"\\\"\\\"\\n{reviews}\\n\\\"\\\"\\\"\\n\\nTheme:\",\n", + " temperature=0,\n", + " max_tokens=64,\n", + " top_p=1,\n", + " frequency_penalty=0,\n", + " presence_penalty=0\n", + " )\n", + " print(response[\"choices\"][0][\"text\"].replace('\\n',''))\n", + "\n", + " sample_cluster_rows = df[df.Cluster == i].sample(rev_per_cluster, random_state=42) \n", + " for j in range(rev_per_cluster):\n", + " print(sample_cluster_rows.Score.values[j], end=\", \")\n", + " print(sample_cluster_rows.Summary.values[j], end=\": \")\n", + " print(sample_cluster_rows.Text.str[:70].values[j])\n", + " \n", + " print(\"-\" * 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see based on the average ratings per cluster, that Cluster 2 contains mostly negative reviews. Cluster 0 and 1 contain mostly positive reviews, whilst Cluster 3 appears to contain reviews about dog products." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It's important to note that clusters will not necessarily match what you intend to use them for. A larger amount of clusters will focus on more specific patterns, whereas a small number of clusters will usually focus on largest discrepencies in the data." + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Code_search.ipynb b/examples/embeddings/Code_search.ipynb new file mode 100644 index 0000000000..14cbf81777 --- /dev/null +++ b/examples/embeddings/Code_search.ipynb @@ -0,0 +1,396 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code search\n", + "\n", + "We index our own openai-python code repository, and show how it can be searched. We implement a simple version of file parsing and extracting of functions from python files. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total number of py files: 40\n", + "Total number of functions extracted: 64\n" + ] + } + ], + "source": [ + "import os\n", + "from glob import glob\n", + "import pandas as pd\n", + "\n", + "def get_function_name(code):\n", + " \"\"\"\n", + " Extract function name from a line beginning with \"def \"\n", + " \"\"\"\n", + " assert code.startswith(\"def \")\n", + " return code[len(\"def \"): code.index(\"(\")]\n", + "\n", + "def get_until_no_space(all_lines, i) -> str:\n", + " \"\"\"\n", + " Get all lines until a line outside the function definition is found.\n", + " \"\"\"\n", + " ret = [all_lines[i]]\n", + " for j in range(i + 1, i + 10000):\n", + " if j < len(all_lines):\n", + " if len(all_lines[j]) == 0 or all_lines[j][0] in [\" \", \"\\t\", \")\"]:\n", + " ret.append(all_lines[j])\n", + " else:\n", + " break\n", + " return \"\\n\".join(ret)\n", + "\n", + "def get_functions(filepath):\n", + " \"\"\"\n", + " Get all functions in a Python file.\n", + " \"\"\"\n", + " whole_code = open(filepath).read().replace(\"\\r\", \"\\n\")\n", + " all_lines = whole_code.split(\"\\n\")\n", + " for i, l in enumerate(all_lines):\n", + " if l.startswith(\"def \"):\n", + " code = get_until_no_space(all_lines, i)\n", + " function_name = get_function_name(code)\n", + " yield {\"code\": code, \"function_name\": function_name, \"filepath\": filepath}\n", + "\n", + "\n", + "# get user root directory\n", + "root_dir = os.path.expanduser(\"~\")\n", + "\n", + "# path to code repository directory\n", + "code_root = root_dir + \"/openai-python\"\n", + "code_files = [y for x in os.walk(code_root) for y in glob(os.path.join(x[0], '*.py'))]\n", + "print(\"Total number of py files:\", len(code_files))\n", + "all_funcs = []\n", + "for code_file in code_files:\n", + " funcs = list(get_functions(code_file))\n", + " for func in funcs:\n", + " all_funcs.append(func)\n", + "\n", + "print(\"Total number of functions extracted:\", len(all_funcs))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For code search models we use babbage-code-search-code to obtain embeddings for code snippets, and code-search-text to embed natural language queries." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
codefunction_namefilepathcode_embedding
0def semantic_search(engine, query, documents):...semantic_search/examples/semanticsearch/semanticsearch.py[-0.038976121693849564, -0.0031428150832653046...
1def main():\\n parser = argparse.ArgumentPar...main/examples/semanticsearch/semanticsearch.py[-0.024289356544613838, -0.017748363316059113,...
2def get_candidates(\\n prompt: str,\\n sto...get_candidates/examples/codex/backtranslation.py[-0.04161201789975166, -0.0169310811907053, 0....
3def rindex(lst: List, value: str) -> int:\\n ...rindex/examples/codex/backtranslation.py[-0.027255680412054062, -0.007931121625006199,...
4def eval_candidate(\\n candidate_answer: str...eval_candidate/examples/codex/backtranslation.py[-0.00999179296195507, -0.01640152558684349, 0...
\n", + "
" + ], + "text/plain": [ + " code function_name \\\n", + "0 def semantic_search(engine, query, documents):... semantic_search \n", + "1 def main():\\n parser = argparse.ArgumentPar... main \n", + "2 def get_candidates(\\n prompt: str,\\n sto... get_candidates \n", + "3 def rindex(lst: List, value: str) -> int:\\n ... rindex \n", + "4 def eval_candidate(\\n candidate_answer: str... eval_candidate \n", + "\n", + " filepath \\\n", + "0 /examples/semanticsearch/semanticsearch.py \n", + "1 /examples/semanticsearch/semanticsearch.py \n", + "2 /examples/codex/backtranslation.py \n", + "3 /examples/codex/backtranslation.py \n", + "4 /examples/codex/backtranslation.py \n", + "\n", + " code_embedding \n", + "0 [-0.038976121693849564, -0.0031428150832653046... \n", + "1 [-0.024289356544613838, -0.017748363316059113,... \n", + "2 [-0.04161201789975166, -0.0169310811907053, 0.... \n", + "3 [-0.027255680412054062, -0.007931121625006199,... \n", + "4 [-0.00999179296195507, -0.01640152558684349, 0... " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from utils import get_embedding\n", + "\n", + "df = pd.DataFrame(all_funcs)\n", + "df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, engine='babbage-code-search-code'))\n", + "df['filepath'] = df['filepath'].apply(lambda x: x.replace(code_root, \"\"))\n", + "df.to_csv(\"output/code_search_openai-python.csv\", index=False)\n", + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/openai/tests/test_endpoints.py:test_completions_multiple_prompts score=0.681\n", + "def test_completions_multiple_prompts():\n", + " result = openai.Completion.create(\n", + " prompt=[\"This was a test\", \"This was another test\"], n=5, engine=\"ada\"\n", + " )\n", + " assert len(result.choices) == 10\n", + "\n", + "----------------------------------------------------------------------\n", + "/openai/tests/test_endpoints.py:test_completions score=0.675\n", + "def test_completions():\n", + " result = openai.Completion.create(prompt=\"This was a test\", n=5, engine=\"ada\")\n", + " assert len(result.choices) == 5\n", + "\n", + "\n", + "----------------------------------------------------------------------\n", + "/openai/tests/test_api_requestor.py:test_requestor_sets_request_id score=0.635\n", + "def test_requestor_sets_request_id(mocker: MockerFixture) -> None:\n", + " # Fake out 'requests' and confirm that the X-Request-Id header is set.\n", + "\n", + " got_headers = {}\n", + "\n", + " def fake_request(self, *args, **kwargs):\n", + " nonlocal got_headers\n", + "----------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "from utils import cosine_similarity\n", + "\n", + "def search_functions(df, code_query, n=3, pprint=True, n_lines=7):\n", + " embedding = get_embedding(code_query, engine='babbage-code-search-text')\n", + " df['similarities'] = df.code_embedding.apply(lambda x: cosine_similarity(x, embedding))\n", + "\n", + " res = df.sort_values('similarities', ascending=False).head(n)\n", + " if pprint:\n", + " for r in res.iterrows():\n", + " print(r[1].filepath+\":\"+r[1].function_name + \" score=\" + str(round(r[1].similarities, 3)))\n", + " print(\"\\n\".join(r[1].code.split(\"\\n\")[:n_lines]))\n", + " print('-'*70)\n", + " return res\n", + "res = search_functions(df, 'Completions API tests', n=3)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/openai/validators.py:format_inferrer_validator score=0.655\n", + "def format_inferrer_validator(df):\n", + " \"\"\"\n", + " This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification.\n", + " It will also suggest to use ada, --no_packing and explain train/validation split benefits.\n", + " \"\"\"\n", + " ft_type = infer_task_type(df)\n", + " immediate_msg = None\n", + "----------------------------------------------------------------------\n", + "/openai/validators.py:long_examples_validator score=0.649\n", + "def long_examples_validator(df):\n", + " \"\"\"\n", + " This validator will suggest to the user to remove examples that are too long.\n", + " \"\"\"\n", + " immediate_msg = None\n", + " optional_msg = None\n", + " optional_fn = None\n", + "----------------------------------------------------------------------\n", + "/openai/validators.py:non_empty_completion_validator score=0.646\n", + "def non_empty_completion_validator(df):\n", + " \"\"\"\n", + " This validator will ensure that no completion is empty.\n", + " \"\"\"\n", + " necessary_msg = None\n", + " necessary_fn = None\n", + " immediate_msg = None\n", + "----------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "res = search_functions(df, 'fine-tuning input data validation logic', n=3)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/openai/validators.py:common_completion_suffix_validator score=0.665\n", + "def common_completion_suffix_validator(df):\n", + " \"\"\"\n", + " This validator will suggest to add a common suffix to the completion if one doesn't already exist in case of classification or conditional generation.\n", + " \"\"\"\n", + " error_msg = None\n", + " immediate_msg = None\n", + " optional_msg = None\n", + " optional_fn = None\n", + "\n", + " ft_type = infer_task_type(df)\n", + "----------------------------------------------------------------------\n", + "/openai/validators.py:get_outfnames score=0.66\n", + "def get_outfnames(fname, split):\n", + " suffixes = [\"_train\", \"_valid\"] if split else [\"\"]\n", + " i = 0\n", + " while True:\n", + " index_suffix = f\" ({i})\" if i > 0 else \"\"\n", + " candidate_fnames = [\n", + " fname.split(\".\")[0] + \"_prepared\" + suffix + index_suffix + \".jsonl\"\n", + " for suffix in suffixes\n", + " ]\n", + " if not any(os.path.isfile(f) for f in candidate_fnames):\n", + "----------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "res = search_functions(df, 'find common suffix', n=2, n_lines=10)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/openai/cli.py:tools_register score=0.651\n", + "def tools_register(parser):\n", + " subparsers = parser.add_subparsers(\n", + " title=\"Tools\", help=\"Convenience client side tools\"\n", + " )\n", + "\n", + " def help(args):\n", + " parser.print_help()\n", + "\n", + " parser.set_defaults(func=help)\n", + "\n", + " sub = subparsers.add_parser(\"fine_tunes.prepare_data\")\n", + " sub.add_argument(\n", + " \"-f\",\n", + " \"--file\",\n", + " required=True,\n", + " help=\"JSONL, JSON, CSV, TSV, TXT or XLSX file containing prompt-completion examples to be analyzed.\"\n", + " \"This should be the local file path.\",\n", + " )\n", + " sub.add_argument(\n", + " \"-q\",\n", + "----------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "res = search_functions(df, 'Command line interface for fine-tuning', n=1, n_lines=20)" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Get_embeddings.ipynb b/examples/embeddings/Get_embeddings.ipynb new file mode 100644 index 0000000000..fb8a986f41 --- /dev/null +++ b/examples/embeddings/Get_embeddings.ipynb @@ -0,0 +1,107 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get embeddings\n", + "\n", + "The function `get_embedding` will give us an embedding for an input text." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "12288" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import openai\n", + "\n", + "embedding = openai.Engine(id=\"davinci-similarity\").embeddings(input=\"Sample document text goes here\")['data'][0]['embedding']\n", + "len(embedding)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1024\n" + ] + } + ], + "source": [ + "import openai\n", + "from tenacity import retry, wait_random_exponential, stop_after_attempt\n", + "\n", + "@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))\n", + "def get_embedding(text, engine=\"davinci-similarity\"):\n", + "\n", + " # replace newlines, which can negatively affect performance.\n", + " text = text.replace(\"\\n\", \" \")\n", + "\n", + " return openai.Engine(id=engine).embeddings(input = [text])['data'][0]['embedding']\n", + "\n", + "embedding = get_embedding(\"Sample query text goes here\", engine=\"ada-search-query\")\n", + "print(len(embedding))" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1024\n" + ] + } + ], + "source": [ + "embedding = get_embedding(\"Sample document text goes here\", engine=\"ada-search-document\")\n", + "print(len(embedding))" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Obtain_dataset.ipynb b/examples/embeddings/Obtain_dataset.ipynb new file mode 100644 index 0000000000..76bb7b8427 --- /dev/null +++ b/examples/embeddings/Obtain_dataset.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Load the dataset\n", + "\n", + "The dataset used in this example is [fine-food reviews](https://www.kaggle.com/snap/amazon-fine-food-reviews) from Amazon. The dataset contains a total of 568,454 food reviews Amazon users left up to October 2012. We will use a subset of this dataset, consisting of 1,000 most recent reviews for illustration purposes. The reviews are in English and tend to be positive or negative. Each review has a ProductId, UserId, Score, review title (Summary) and review body (Text).\n", + "\n", + "We will combine the review summary and review text into a single combined text. The model will encode this combined text and it will output a single vector embedding." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TimeProductIdUserIdScoreSummaryTextcombined
Id
11303862400B001E4KFG0A3SGXH7AUHU8GW5Good Quality Dog FoodI have bought several of the Vitality canned d...Title: Good Quality Dog Food; Content: I have ...
21346976000B00813GRG4A1D87F6ZCVE5NK1Not as AdvertisedProduct arrived labeled as Jumbo Salted Peanut...Title: Not as Advertised; Content: Product arr...
\n", + "
" + ], + "text/plain": [ + " Time ProductId UserId Score Summary \\\n", + "Id \n", + "1 1303862400 B001E4KFG0 A3SGXH7AUHU8GW 5 Good Quality Dog Food \n", + "2 1346976000 B00813GRG4 A1D87F6ZCVE5NK 1 Not as Advertised \n", + "\n", + " Text \\\n", + "Id \n", + "1 I have bought several of the Vitality canned d... \n", + "2 Product arrived labeled as Jumbo Salted Peanut... \n", + "\n", + " combined \n", + "Id \n", + "1 Title: Good Quality Dog Food; Content: I have ... \n", + "2 Title: Not as Advertised; Content: Product arr... " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "df = pd.read_csv('input/Reviews.csv', index_col=0)\n", + "df = df[['Time', 'ProductId', 'UserId', 'Score', 'Summary', 'Text']]\n", + "df = df.dropna()\n", + "df['combined'] = \"Title: \" + df.Summary.str.strip() + \"; Content: \" + df.Text.str.strip()\n", + "df.head(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1000" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# subsample to 1k most recent reviews and remove samples that are too long\n", + "df = df.sort_values('Time').tail(1_100)\n", + "df.drop('Time', axis=1, inplace=True)\n", + "\n", + "from transformers import GPT2TokenizerFast\n", + "tokenizer = GPT2TokenizerFast.from_pretrained(\"gpt2\")\n", + "\n", + "# remove reviews that are too long\n", + "df['n_tokens'] = df.combined.apply(lambda x: len(tokenizer.encode(x)))\n", + "df = df[df.n_tokens<2000].tail(1_000)\n", + "len(df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Get embeddings and save them for future reuse" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from utils import get_embedding\n", + "\n", + "# This will take just under 10 minutes\n", + "df['babbage_similarity'] = df.combined.apply(lambda x: get_embedding(x, engine='babbage-similarity'))\n", + "df['babbage_search'] = df.combined.apply(lambda x: get_embedding(x, engine='babbage-search-document'))\n", + "df.to_csv('output/embedded_1k_reviews.csv')" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Regression.ipynb b/examples/embeddings/Regression.ipynb new file mode 100644 index 0000000000..cf99894aa7 --- /dev/null +++ b/examples/embeddings/Regression.ipynb @@ -0,0 +1,109 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Regression using the embeddings\n", + "\n", + "Regression means predicting a number, rather than one of the categories. We will predict the score based on the embedding of the review's text. We split the dataset into a training and a testing set for all of the following tasks, so we can realistically evaluate performance on unseen data. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb).\n", + "\n", + "We're predicting the score of the review, which is a number between 1 and 5 (1-star being negative and 5-star positive)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Babbage similarity embedding performance on 1k Amazon reviews: mse=0.38, mae=0.39\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "from sklearn.ensemble import RandomForestRegressor\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import mean_squared_error, mean_absolute_error\n", + "\n", + "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", + "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", + "\n", + "X_train, X_test, y_train, y_test = train_test_split(list(df.babbage_similarity.values), df.Score, test_size = 0.2, random_state=42)\n", + "\n", + "rfr = RandomForestRegressor(n_estimators=100)\n", + "rfr.fit(X_train, y_train)\n", + "preds = rfr.predict(X_test)\n", + "\n", + "\n", + "mse = mean_squared_error(y_test, preds)\n", + "mae = mean_absolute_error(y_test, preds)\n", + "\n", + "print(f\"Babbage similarity embedding performance on 1k Amazon reviews: mse={mse:.2f}, mae={mae:.2f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dummy mean prediction performance on Amazon reviews: mse=1.77, mae=1.04\n" + ] + } + ], + "source": [ + "bmse = mean_squared_error(y_test, np.repeat(y_test.mean(), len(y_test)))\n", + "bmae = mean_absolute_error(y_test, np.repeat(y_test.mean(), len(y_test)))\n", + "print(f\"Dummy mean prediction performance on Amazon reviews: mse={bmse:.2f}, mae={bmae:.2f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the embeddings are able to predict the scores with an average error of 0.39 per score prediction. This is roughly equivalent to predicting 2 out of 3 reviews perfectly, and 1 out of three reviews by a one star error." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You could also train a classifier to predict the label, or use the embeddings within an existing ML model to encode free text features." + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb new file mode 100644 index 0000000000..e83d4db5e1 --- /dev/null +++ b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb @@ -0,0 +1,185 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Semantic text search using embeddings\n", + "\n", + "We can search through all our reviews semantically in a very efficient manner and at very low cost, by simply embedding our search query, and then finding the most similar reviews. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "\n", + "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", + "df['babbage_search'] = df.babbage_search.apply(eval).apply(np.array)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Remember to use the documents embedding engine for documents (in this case reviews), and query embedding engine for queries. Note that here we just compare the cosine similarity of the embeddings of the query and the documents, and show top_n best matches." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jamaican Blue beans: Excellent coffee bean for roasting. Our family just purchased another 5 pounds for more roasting. Plenty of flavor and mild on acidity when roasted to a dark brown bean and befor\n", + "\n", + "Good Buy: I liked the beans. They were vacuum sealed, plump and moist. Would recommend them for any use. I personally split and stuck them in some vodka to make vanilla extract. Yum!\n", + "\n", + "Fantastic Instant Refried beans: Fantastic Instant Refried Beans have been a staple for my family now for nearly 20 years. All 7 of us love it and my grown kids are passing on the tradition.\n", + "\n" + ] + } + ], + "source": [ + "from utils import get_embedding, cosine_similarity\n", + "\n", + "# search through the reviews for a specific product\n", + "def search_reviews(df, product_description, n=3, pprint=True):\n", + " embedding = get_embedding(product_description, engine='babbage-search-query')\n", + " df['similarities'] = df.babbage_search.apply(lambda x: cosine_similarity(x, embedding))\n", + "\n", + " res = df.sort_values('similarities', ascending=False).head(n).combined.str.replace('Title: ','').str.replace('; Content:', ': ')\n", + " if pprint:\n", + " for r in res:\n", + " print(r[:200])\n", + " print()\n", + " return res\n", + "res = search_reviews(df, 'delicious beans', n=3)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rustichella ROCKS!: Anything this company makes is worthwhile eating! My favorite is their Trenne.
Their whole wheat pasta is the best I have ever had.\n", + "\n", + "sooo good: tastes so good. Worth the money. My boyfriend hates wheat pasta and LOVES this. cooks fast tastes great.I love this brand and started buying more of their pastas. Bulk is best.\n", + "\n", + "Wonderful: Came quickly. Was plentiful and delicious and cheaper than in the store. You will enjoy it if you like thick pasta.\n", + "\n" + ] + } + ], + "source": [ + "res = search_reviews(df, 'whole wheat pasta', n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can search through these reviews easily. To speed up computation, we can use a special algorithm, aimed at faster search through embeddings." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "great product, poor delivery: The coffee is excellent and I am a repeat buyer. Problem this time was with the UPS delivery. They left the box in front of my garage door in the middle of the drivewa\n", + "\n" + ] + } + ], + "source": [ + "res = search_reviews(df, 'bad delivery', n=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see, this can immediately deliver a lot of value. In this example we show being able to quickly find the examples of delivery failures." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Extremely dissapointed: Hi,
I am very disappointed with the past shipment I received of the ONE coconut water. 3 of the boxes were leaking and the coconut water was spoiled.

Thanks." + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import statsmodels.api as sm\n", + "\n", + "\n", + "\n", + "\n", + "correlation = X_test[['percentile_cosine_similarity', 'Score']].corr().values[0,1]\n", + "print('Correlation between user&vector similarity percentile metric and review number of stars (score): %.2f%%' % (100*correlation))\n", + "\n", + "\n", + "# boxplot of cosine similarity for each score\n", + "X_test.boxplot(column='percentile_cosine_similarity', by='Score')\n", + "plt.title('')\n", + "plt.show()\n", + "plt.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can observe a weak trend, showing that the higher the similarity score between the user and the product embedding, the higher the review score. Therefore, the user and product embeddings can weakly predict the review score - even before the user receives the product!\n", + "\n", + "Because this signal works in a different way than the more commonly used collaborative filtering, it can act as an additional feature to slightly improve the performance on existing problems." + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Visualize_in_2d.ipynb b/examples/embeddings/Visualize_in_2d.ipynb new file mode 100644 index 0000000000..acb6b886b7 --- /dev/null +++ b/examples/embeddings/Visualize_in_2d.ipynb @@ -0,0 +1,142 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualizing the embeddings in 2D\n", + "\n", + "We will use t-SNE to reduce the dimensionality of the embeddings from 2048 to 2. Once the embeddings are reduced to two dimensions, we can plot them in a 2D scatter plot. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Reduce dimensionality\n", + "\n", + "We reduce the dimensionality to 2 dimensions using t-SNE decomposition." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1000, 2)" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "from sklearn.manifold import TSNE\n", + "\n", + "# Load the embeddings\n", + "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", + "\n", + "# Convert to a list of lists of floats\n", + "matrix = df.babbage_similarity.apply(eval).to_list()\n", + "\n", + "# Create a t-SNE model and transform the data\n", + "tsne = TSNE(n_components=2, perplexity=15, random_state=42, init='random', learning_rate=200)\n", + "vis_dims = tsne.fit_transform(matrix)\n", + "vis_dims.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Plotting the embeddings\n", + "\n", + "We colour each review by its star rating, ranging from red to green." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can observe a decent data separation even in the reduced 2 dimensions. There seems to be a cluster of mostly negative reviews." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Amazon ratings visualized in language using t-SNE')" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import matplotlib\n", + "import numpy as np\n", + "\n", + "colors = [\"red\", \"darkorange\", \"gold\", \"turquoise\", \"darkgreen\"]\n", + "x = [x for x,y in vis_dims]\n", + "y = [y for x,y in vis_dims]\n", + "color_indices = df.Score.values - 1\n", + "\n", + "colormap = matplotlib.colors.ListedColormap(colors)\n", + "plt.scatter(x, y, c=color_indices, cmap=colormap, alpha=0.3)\n", + "for score in [0,1,2,3,4]:\n", + " avg_x = np.array(x)[df.Score-1==score].mean()\n", + " avg_y = np.array(y)[df.Score-1==score].mean()\n", + " color = colors[score]\n", + " plt.scatter(avg_x, avg_y, marker='x', color=color, s=100)\n", + "plt.title(\"Amazon ratings visualized in language using t-SNE\")" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Zero-shot_classification.ipynb b/examples/embeddings/Zero-shot_classification.ipynb new file mode 100644 index 0000000000..95789287a6 --- /dev/null +++ b/examples/embeddings/Zero-shot_classification.ipynb @@ -0,0 +1,226 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zero-shot classification using the embeddings\n", + "\n", + "In this notebook we will classify the sentiment of reviews using embeddings and zero labeled data! The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb).\n", + "\n", + "We'll define positive sentiment to be 4 and 5-star reviews, and negative sentiment to be 1 and 2-star reviews. 3-star reviews are considered neutral and we won't use them for this example.\n", + "\n", + "We will perform zero-shot classification by embedding descriptions of each class and then comparing new samples to those class embeddings." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import classification_report, accuracy_score\n", + "\n", + "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", + "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", + "df['babbage_search'] = df.babbage_search.apply(eval).apply(np.array)\n", + "\n", + "df= df[df.Score!=3]\n", + "df['sentiment'] = df.Score.replace({1:'negative', 2:'negative', 4:'positive', 5:'positive'})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Zero-Shot Classification\n", + "To perform zero shot classification, we want to predict labels for our samples without any training. To do this, we can simply embed short descriptions of each label, such as positive and negative, and then compare the cosine distance between embeddings of samples and label descriptions. \n", + "\n", + "The highest similarity label to the sample input is the predicted label. We can also define a prediction score to be the difference between the cosine distance to the positive and to the negative label. This score can be used for plotting a precision-recall curve, which can be used to select a different tradeoff between precision and recall, by selecting a different threshold." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " negative 0.67 0.88 0.76 136\n", + " positive 0.98 0.93 0.95 789\n", + "\n", + " accuracy 0.92 925\n", + " macro avg 0.82 0.90 0.86 925\n", + "weighted avg 0.93 0.92 0.92 925\n", + "\n" + ] + }, + { + "data": { + "image/png": "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FzscsyqVfP0tAHO/WcZWrVrW+PkbdBPT0qVLad26tZODZSaJ1q1bu9Zp9cI7M+YCMGzylwU5f4NOEICTg601/5ux+uLRc/cp6PkbfIIwM2uoWjQr7Ee4E0SBff7555xyyil06dKFPffck6OOOoopU6Ywc+ZMdt111xq7Tv/+/XnttdcAGDlyJN26daNHjx6UlJRw0kknrde5I4KDDjqIhQsXrlr37LPPIokPP/xw1bqZM2ey4YYb0qNHD7p27cpFF11EWVnZel37zTffZI899qBZs2YMHjy40v3GjBnDD37wA3bccUcuv/xyIpJZcefNm8ehhx7KTjvtxKGHHsrXX38NwIsvvkj//v3XKzazhs4JooAiguOPP57evXszffp0xowZw0033cQXX3xR49caMGAAhxxyCACPPfYY1157LePGjaN9+/ZVfrBWtHLlmg/Nv/zyy3Tv3p1NN/3u2chBgwax//77M2jQoNX27dKlC+PGjWPChAl88MEHPPvss+tWoFTHjh156KGHOO2006rc7+KLL+Yvf/kLU6dOZerUqbz66qsA3HzzzRx88MFMnTqVgw8+mJtvvhmAPn368MILL7BkyZL1is+sIWvQdzHluuGFSXzw2cLqd1wLXbfdlF8d3a3S7cOHD6d58+ZcdNFFq9Z1794dSL5tl5s5cyZnnnkm33yTTLdx11130atXL2bPns3JJ5/MwoULWblyJffccw+9evXi3HPPZfTo0UjinHPO4Re/+AX9+vWjb9++zJ8/n6eeeoohQ4bwyiuvcOONN9K3b18mTpxIaWkp11xzDSNGjGDZsmVceumlXHjhhYwYMYLrr7+eLbbYgg8//JApU6asVo7HHnuMCy64YNXy4sWLeeuttxg+fDhHH300N9xwwxplb9asGb169WLatGnr9N6W69SpEwBNmlT+XWb27NksXLiQH/7whwCcddZZPPvssxx55JE899xzjBgxAoCzzz6b3r17c8sttyCJ3r178+KLL/LTn/50vWI0a6gaTYIohokTJ7LnnntWu99WW23F0KFDadmyJVOnTuXUU09l9OjRPP744xx++OFcd911lJaWsmTJEsaNG0dJSQkTJ04EYP78+aud67zzzuOtt96ib9++nHTSSaslogceeIDNNtuMUaNGsWzZMvbbbz8OO+wwAMaOHcvEiRPz3tr59ttvc999961afu655zjiiCPYeeedad26NWPGjFmjnEuWLGHYsGEMGDBgjfMdcMABLFq0aI31t91226pa0NooKSmhQ4cOq5Y7dOhASUkJAF988QXt2rUDYJtttlmt9tazZ09GjhzpBGFWiUaTIKr6pl9sK1as4LLLLmPcuHE0bdp01Tf4vfbai3POOYcVK1Zw3HHH0aNHD3bYYQdmzJjBz3/+c/r06bPqAz6Lf/zjH0yYMGFVk9OCBQuYOnUqLVq0YO+99670vv958+bRqlWrVcuDBg3iiiuuAOCUU05h0KBBqxLE9OnT6dGjB5I49thjOfLII9c438iRIzPHXJMkrXaH0lZbbcVnn31WlFjM6oNGkyCKoVu3bpna///whz+w9dZbM378eMrKymjZMnng5Uc/+hFvvvkmL730Ev369ePKK6/krLPOYvz48QwZMoR7772Xp556igcffDBTPBHBn/70Jw4//PDV1o8YMYKNN9640uOaNWtGWVkZTZo0Yd68ebz++uu8//77SKK0tBRJ3HrrrcB3fRBVqekaRPv27Zk1a9aq5VmzZtG+fXsAtt56a2bPnk27du2YPXs2W2211ar9li5dyoYbbrjW1zNrLNxJXUAHHXQQy5Yt4/7771+1bsKECWt8g16wYAHt2rWjSZMmPPLII5SWlgLw8ccfs/XWW3P++edz3nnnMXbsWL766ivKyso48cQT+e1vf8vYsWMzx3P44Ydzzz33sGJFMpL7lClTVvV7VOV73/seM2bMAGDw4MGceeaZfPzxx8ycOZNPP/2Uzp07r1WtYOTIkYwbN26Nn3VJDgDt2rVj00035d133yUiePjhhzn22GMBOOaYYxg4cCAAAwcOXLUekvLX5J1kZsXyzbLCzMjgBFFAkvj73//Oa6+9RpcuXejWrRvXXnst22yzzWr7XXLJJQwcOJDu3bvz4Ycfrvo2P2LECLp3787uu+/Ok08+yRVXXEFJSQm9e/emR48enHHGGdx0002Z4znvvPPo2rUre+yxB7vuuisXXnhh3ruWKurTp8+qjt5BgwZx/PHHr7b9xBNPXONuppoyatQoOnTowNNPP82FF15It27fNRX26NFj1eu7776b8847jx133JEuXbqsatq65pprGDp0KDvttBOvvfYa11xzzapjhg8fTp8+fQoSt1ltWLI8+f/725cmF+T8Kr9fvL7r2bNnjB49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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from utils import cosine_similarity, get_embedding\n", + "from sklearn.metrics import PrecisionRecallDisplay\n", + "\n", + "def evaluate_emeddings_approach(\n", + " labels = ['negative', 'positive'], \n", + " engine = 'babbage-similarity',\n", + "):\n", + " label_embeddings = [get_embedding(label, engine=engine) for label in labels]\n", + "\n", + " def label_score(review_embedding, label_embeddings):\n", + " return cosine_similarity(review_embedding, label_embeddings[1]) - cosine_similarity(review_embedding, label_embeddings[0])\n", + "\n", + " engine_col_name = engine.replace('-','_').replace('_query','')\n", + " probas = df[engine_col_name].apply(lambda x: label_score(x, label_embeddings))\n", + " preds = probas.apply(lambda x: 'positive' if x>0 else 'negative')\n", + "\n", + " report = classification_report(df.sentiment, preds)\n", + " print(report)\n", + "\n", + " display = PrecisionRecallDisplay.from_predictions(df.sentiment, probas, pos_label='positive')\n", + " _ = display.ax_.set_title(\"2-class Precision-Recall curve\")\n", + "\n", + "evaluate_emeddings_approach(labels=['negative', 'positive'], engine='babbage-similarity')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that this classifier already performs extremely well. We used similarity embeddings, and the simplest possible label name. Let's try to improve on this by using more descriptive label names, and search embeddings." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " negative 0.65 0.93 0.76 136\n", + " positive 0.99 0.91 0.95 789\n", + "\n", + " accuracy 0.92 925\n", + " macro avg 0.82 0.92 0.86 925\n", + "weighted avg 0.94 0.92 0.92 925\n", + "\n" + ] + }, + { + "data": { + "image/png": "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3fn+97/Pe++9x+zZs9lpp53q7DxXXnklzzzzDADjx4+nT58+9OvXj3nz5nH88cfX6tgRwf7778/SpUsryh5++GEk8c4771SUzZ49m9atW9OvXz969+7NueeeS1nZN+Z5WicvvPACu+22G82bN2fUqKpHfp84cSI777wz2267LRdeeCERyay4Cxcu5KCDDmK77bbjoIMOYtGiRQA8/vjjXHnllVUez8ycIAoqIjjmmGMYNGgQs2bNYuLEiVxzzTV88skndX6u4cOHc+CBBwJw7733ctlllzF58mS6dOlS7QdrZWvWfPOh+SeffJK+ffvStu3Xz0aOHDmSfffdl5EjR661b69evZg8eTJTp07lrbfe4uGHH16/CqW6devGXXfdxcknn1ztfueddx5/+ctfmDFjBjNmzODpp58G4Nprr+WAAw5gxowZHHDAAVx77bUADB48mMcee4zly5fXKj6zpqxJ38WU6+rHpvPWR0tr3nEd9N6qLb88ok+V28eOHUuLFi0499xzK8r69u0LJN+2y82ePZtTTz2VL79Mptu4+eabGTBgAPPnz+fEE09k6dKlrFmzhltuuYUBAwbwox/9iAkTJiCJM888k5/97GcMHTqUww8/nMWLF/PAAw8wevRonnrqKX7zm99w+OGHM23aNEpLS7n00ksZN24cq1at4ic/+QnnnHMO48aN44orrmCzzTbjnXfe4b333lurHvfeey9nn312xfqyZct48cUXGTt2LEcccQRXX331N+revHlzBgwYwMyZM9frvS3Xo0cPAJo1q/q7zPz581m6dCl77bUXAKeddhoPP/wwhx12GI888gjjxo0D4PTTT2fQoEFcd911SGLQoEE8/vjjnHDCCbWK0ayp2mASRDFMmzaN3Xffvcb9tthiC8aMGUOrVq2YMWMGQ4YMYcKECfztb3/jkEMO4fLLL6e0tJTly5czefJk5s2bx7Rp0wBYvHjxWsc666yzePHFFzn88MM5/vjj10pEd9xxB+3ateP1119n1apV7LPPPhx88MEATJo0iWnTpuW9tfOll17itttuq1h/5JFHOPTQQ9l+++3p0KEDEydO/EY9ly9fzrPPPsvw4cO/cbyBAwfyxRdffKP8hhtuqLgKWhfz5s2ja9euFetdu3Zl3rxkdttPPvmEzp07A7DllluudfXWv39/xo8f7wRhVoUNJkFU902/2FavXs3555/P5MmTKSkpqfgGv8cee3DmmWeyevVqjj76aPr168c222zD+++/zwUXXMDgwYMrPuCz+Oc//8nUqVMrmpyWLFnCjBkzaNmyJXvuuWeV9/0vXLiQTTfdtGJ95MiR/PSnPwXgpJNOYuTIkRUJYtasWfTr1w9JHHXUURx22GHfON748eMzx1yXJK11h9IWW2zBRx99VJRYzBqDDSZBFEOfPn0ytf//4Q9/oFOnTkyZMoWysjJatWoFwHe/+11eeOEFnnjiCYYOHcpFF13EaaedxpQpUxg9ejS33norDzzwAHfeeWemeCKCP/7xjxxyyCFrlY8bN442bdpU+brmzZtTVlZGs2bNWLhwIc899xxvvvkmkigtLUUS119/PfB1H0R16voKokuXLsydO7dife7cuXTp0gWATp06MX/+fDp37sz8+fPZYostKvZbuXIlrVu3XufzmTUUf/vXHADGvvMpB/buVOfHdyd1Ae2///6sWrWK22+/vaJs6tSp3/gGvWTJEjp37kyzZs245557KC0tBeCDDz6gU6dODBs2jLPOOotJkybx2WefUVZWxnHHHcevf/1rJk2alDmeQw45hFtuuYXVq5OR3N97772Kfo/qfPvb3+b9998HYNSoUZx66ql88MEHzJ49mw8//JCePXuu01XB+PHjmTx58jd+1ic5AHTu3Jm2bdvy6quvEhHcfffdHHXUUQAceeSRjBgxAoARI0ZUlENS/7q8k8ysvp2//7YAzP685v/H68MJooAk8Y9//INnnnmGXr160adPHy677DK23HLLtfb78Y9/zIgRI+jbty/vvPNOxbf5cePG0bdvX3bddVfuv/9+fvrTnzJv3jwGDRpEv379OOWUU7jmmmsyx3PWWWfRu3dvdtttN3baaSfOOeecvHctVTZ48OCKjt6RI0dyzDHHrLX9uOOO+8bdTHXl9ddfp2vXrjz44IOcc8459OnzdVNhv379Kpb//Oc/c9ZZZ7HtttvSq1eviqatSy+9lDFjxrDddtvxzDPPcOmll1a8ZuzYsQwePLggcZvVhwG9OgDw6yfeLsjxVX6/eGPXv3//mDBhwlplb7/9NjvuuGORImo65s+fz2mnncaYMWOKHUqd+eSTTzj55JN59tln8273vx1rDMrKgm3+60mGDezJ5YN7r9cxJE2MiP75trkPwmrUuXNnhg0bxtKlS9d6FqIxmzNnDjfe6GnWrXFr1kzMvrZwV8FOEJZJU7sVdI899ih2CGYNXpPvg2gqTWhWf/xvxizRpBNEq1at+Pzzz/0f3jIrnw+i/FZjsw1Zk25i6tq1K3PnzmXBggXFDsUakfIZ5cw2dE06QbRo0cKzgpmZracm3cRkZmbrzwnCzMzycoIwM7O8msyT1JIWAB/U4hCbA5/VUTiNxYZW5w2tvuA6byhqU+fuEdEx34YmkyBqS9KEqh43b6o2tDpvaPUF13lDUag6u4nJzMzycoIwM7O8nCC+dnvNuzQ5G1qdN7T6guu8oShInd0HYWZmefkKwszM8nKCMDOzvDaoBCHpUEnvSpop6dI82zeSdH+6/V+SehQhzDqVoc4XSXpL0lRJz0rqXow461JNdc7Z7zhJIanR3xKZpc6STkj/1tMl/a2+Y6xrGf5td5M0VtIb6b/v7xcjzroi6U5Jn0qaVsV2SbopfT+mStqt1ieNiA3iBygBZgHbAC2BKUDvSvv8GLg1XT4JuL/YcddDnb8HbJwun7ch1Dndb1PgBeBVoH+x466Hv/N2wBvAZun6FsWOux7qfDtwXrrcG5hd7LhrWefvArsB06rY/n3gKUDAXsC/anvODekKYk9gZkS8HxFfAfcBR1Xa5yhgRLo8CjhAkuoxxrpWY50jYmxELE9XXwUa+zjXWf7OAL8CrgNW1mdwBZKlzsOAP0XEIoCI+LSeY6xrWeocQPkcue2Aj+oxvjoXES8AC6vZ5Sjg7ki8CrSX1Lk259yQEkQX4MOc9blpWd59ImINsAToUC/RFUaWOuf6Eck3kMasxjqnl95bR8QT9RlYAWX5O28PbC/pJUmvSjq03qIrjCx1vgo4RdJc4EnggvoJrWjW9f97jZr0fBCWnaRTgP7AfsWOpZAkNQN+Dwwtcij1rTlJM9MgkqvEFyTtHBGLixlUgQ0B7oqIGyXtDdwjaaeIKCt2YI3FhnQFMQ/YOme9a1qWdx9JzUkuSz+vl+gKI0udkXQgcDlwZESsqqfYCqWmOm8K7ASMkzSbpK320UbeUZ3l7zwXeDQiVkfEv4H3SBJGY5Wlzj8CHgCIiFeAViSD2jVVmf6/r4sNKUG8DmwnqaekliSd0I9W2udR4PR0+XjguUh7fxqpGussaVfgNpLk0NjbpaGGOkfEkojYPCJ6REQPkn6XIyNiQnHCrRNZ/m0/THL1gKTNSZqc3q/HGOtaljrPAQ4AkLQjSYJoyvMPPwqclt7NtBewJCLm1+aAG0wTU0SskXQ+MJrkDog7I2K6pOHAhIh4FLiD5DJ0Jkln0EnFi7j2Mtb5emAT4MG0P35ORBxZtKBrKWOdm5SMdR4NHCzpLaAUuCQiGu3VccY6/wfwF0k/I+mwHtqYv/BJGkmS5DdP+1V+CbQAiIhbSfpZvg/MBJYDZ9T6nI34/TIzswLakJqYzMxsHThBmJlZXk4QZmaWlxOEmZnl5QRhZmZ5OUHYepNUKmmypGmSHpPUvo6PPzu9Zx9Jy6rYp7Wk5yWVSOohaUUa01uSbk2fnF6Xc/aXdFO6PEjSgJxt50o6rTZ1So9zlaSLa9jnLknHr8Mxe1Q1ymddk3Rk+eipko6W1Dtn2/D0wcv1Oe59khrzw3tNjhOE1caKiOgXETuRPDfykyLEcCbwUESUpuuzIqIfsAvJCJ5Hr8vBImJCRFyYrg4CBuRsuzUi7q5twI1dRDwaEdemq0eTvM/l266MiGfW89C3AD+vZXhWh5wgrK68QjowmKRekp6WNFHSeEk7pOWdJP1D0pT0Z0Ba/nC673RJZ6/jeX8IPFK5MB1s8WVg2/Tb9XP6es6Lbul5f5Be/UyR9EJaNkjS40rmAjkX+Fl6RTKw/Ju/pB0kvVZ+rvT4b6bLu6dXNBMljVYNo2lKGibp9TSGv0vaOGfzgZImSHpP0uHp/iWSrk9fM1XSOevyZklaJukP6Xv9rKSOaXk/JYP4TU3/Rpul5Rfq6/lC7kvLhkq6Of37HQlcn75HvcqvfJTM1fBgznkHSXo8XT5Y0iuSJkl6UNIm6W7j0zpvMA/wNnROEFZrkkpIhjQof0r5duCCiNgduBj4c1p+E/B8RPQlGdd+elp+Zrpvf+BCSZlG0FUyxMI2ETE7z7aN05jeBP4IjIiIXYB70zgArgQOSeNZ6+nx9Ji3An9Ir5LG52x7B2gpqWdadCJwv6QW6bmOT+tzJ/CbGqrxUETskcbwNsn4QeV6kAxrPRi4VVKrdPuSiNgD2AMYlhNHed23kvRkFedrQ/KkcR/geZKncQHuBv4zfY/ezCm/FNg1LT+30nv0Msnf/JL0PZqVs/kZ4DuS2qTrJwL3KWky/AVwYETsBkwALkqPV0byFHDfqt8uq09OEFYbrSVNBj4GOgFj0m+DA0iG7phMMs5T+bfo/UmaEYiI0ohYkpZfKGkKybhIW5N9ELnNgcWVynql530JeCIingL2BspnULsH2Dddfgm4S9IwkuEa1sUDJB96pL/vB75NMhDgmDSGX1Dz/Bo7pVdZb5JcDfXJPUdElEXEDJJxk3YADiYZb2cy8C+S4ejXer8i4qOIqGr2tLI0VoC/AvtKage0j4jn0/IRJJPTAEwF7lUy2u+aGuqSG8Ma4GngiPSKYDDJld5eJE1SL6V1OB3IncXwU2CrrOexwvKlnNXGiojol35bH03SB3EXsDjtB6iRpEHAgcDeEbFc0jiSQdUynT/PvrOynjsizpX0HZIPr4mSds94Xkg+ZB+U9FByqJghaWdgekTsvQ7HuQs4OiKmSBpKOqBeeYiVQyaZLeyCiBidu0HrPz1uTWPtDCZJFkcAl6d1zOo+4HyS/qkJEfGFJAFjImJIFa9pRfJ3tQbAVxBWa+mMdBeSDI62HPi3pB9AxTy55U0Gz5JMa1relt6OZEj1RWly2IHkG2bW8y4CStKml+q8zNcDL/6QpK0bSb0i4l8RcSXJKJ9bV3rdFyTDg+c79yySQe+u4Otv5O8CHZXMPYCkFpL65Ht9jk2B+Wnz1A8rbfuBpGaSepFMrfkuSSI+L90fSdvnNONk0YxkpGKAk4EX0yu5RZIGpuWnAs8ruQNs64gYC/wnyd9qk0rHq/I9ImnC2o1kNrv70rJXgX0kbZvG30bS9jmv2R6ol7uxrGZOEFYnIuINkuaIISQfdD9Km42m8/VUkD8Fvpc2p0wkaWp4Gmgu6W3gWpIPkHXxT75uMqrKBcAZkqaSfPj9NC2/XtKbSm4PfZlkXuNcjwHHpB2wA/mm+4FT+HrOga9IPnyvS+s+mZy7oKpwBUlT0UvAO5W2zQFeI5nl79yIWAn8L/AWMCmN+zYqtQTU0AfxJbBn+tr9geFp+ekk78dUoF9aXgL8Nf17vQHclGeCofuASyS9kSayCumdZY8Dh6W/iYgFJJM1jUzP9QpJ0xmSOpFclX5cRexWzzyaqzVqSqYP/VlEnFrsWBoDScsiovJVQIOgZFjupRFxR7FjsYSvIKxRi4hJwNj0Tipr3BaTdJBbA+ErCDMzy8tXEGZmlpcThJmZ5eUEYWZmeTlBmJlZXk4QZmaW1/8HwFhzzjh8/JsAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "evaluate_emeddings_approach(labels=['An Amazon review with a negative sentiment.', 'An Amazon review with a positive sentiment.'], engine='babbage-similarity')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using the search embeddings and descriptive names leads to an additional improvement in performance." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " negative 0.77 0.79 0.78 136\n", + " positive 0.96 0.96 0.96 789\n", + "\n", + " accuracy 0.94 925\n", + " macro avg 0.87 0.88 0.87 925\n", + "weighted avg 0.94 0.94 0.94 925\n", + "\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAEWCAYAAAB8LwAVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Z1A+gAAAACXBIWXMAAAsTAAALEwEAmpwYAAAsFUlEQVR4nO3deZhU1bnv8e+PQVEBNUwiqCBqDCgQRaIYI3FWcDbOGmdxvvGYHL25TmiixiEnMSZqjkQ0hmiIUVQUUUAxiVFQQHAARFQGFWVSmeG9f+zdbdFUd22gq6uH3+d5+mFPtfe7qpt6a62191qKCMzMzCpqVOoAzMysdnKCMDOzvJwgzMwsLycIMzPLywnCzMzycoIwM7O8nCCsWkk6S9IrpY6jOkk6TdLzGY67V9K1NRFTTZA0U9JB6fINkv5c6pisZjlBGJI2lfSApA8lfSlpgqTDSx1XFumH2FJJX0n6VNKDkppX5zUi4pGIOCTDcQMi4qbqvHYZSSHp67ScsyXdJalxMa5lVsYJwgCaAB8D+wNbAv8PeExSp1IGtR6OjIjmwB5AL5L41yKpSY1HVf16pOXcHzgJOKfE8VSrevI7qlecIIyI+DoiboiImRGxJiKeBj4A9qzsNZK2k/S4pHmSvpD0u0qO+42kjyUtljRe0n45+3pLGpfu+1TSXen2ZpL+nJ53oaTXJbXLUI7ZwLPAbul5QtIlkqYB09Jt/dMa0kJJ/5LUvVCZcpvNlPi1pM/SuN+SVHa9ByXdnHO+8yVNlzRf0jBJ2+bsC0kDJE1LY7lHkgqVMS3ndOCfQM+c821IubpIGpVu+1zSI5K2yhJDRZKOTq+/WNL7kg5Lt5c3U6Xr5U1Vkjql78O5kj4CRkl6VtKlFc49UdJx6fKukkam7+l7kk7ckHgtGycIW0f6YbwLMKWS/Y2Bp4EPgU5AB+CvlZzudZIPsm8BfwH+JqlZuu83wG8ioiXQBXgs3f5jkprMdkArYACwNEPc2wFHAG/mbD4G+B7QVdJ3gUHAhel57wOGpU1sWct0CPADkvdnS+BE4Is8sRwA3JLub5+et+L5+gN7Ad3T4w4tVMb03LsC+wHT0/UNLZfSGLcFvkPyft+QJYYK8fQGHgJ+CmxF8v7MXI9T7J9e/1BgCHBKzrm7AjsAz0jaAhhJ8nfUFjgZ+H16jBWBE4StRVJT4BFgcES8W8lhvUk+VH6a1j6WRUTejumI+HNEfBERqyLiTmBT4Nvp7pXATpJaR8RXEfFqzvZWwE4RsToixkfE4irCfkLSQuAV4CXglzn7bomI+RGxFLgAuC8i/pOedzCwHNh7Pcq0EmgB7AooIt6JiLl5jjsNGBQRb0TEcuAaYJ8KzXa3RsTCiPgIGE1OjaASb0j6GngHGAP8Pt2+QeWKiOkRMTIilkfEPOAukg/r9XVuWtaRaQ10dhV/O/nckMa2FPgH0FPSDum+04DH0/ewPzAzIv6U/j29Cfwd+NEGxGwZOEFYOUmNgIeBFcClOdufVdI5+pWk00i+aX4YEasynPMqSe9IWpR+iG8JtE53n0vyTfzdtBmpf7r9YWAE8FdJcyT9Kk1clTkmIraKiB0i4uL0g6bMxznLOwD/lTbDLEzj2Y7kAzRTmSJiFPA74B7gM0n3S2qZ59BtSb61l73uK5KaRoecYz7JWV4CNAeQNCXn/d4v55g90mNOIqkVbbEx5ZLUTtJflXR6Lwb+zDe/m/WxHfD+BryuTPnvKCK+BJ4hqR1AUpt4JF3eAfhehXKeBmyzEde2KjhBGJC0rQMPAO2A4yNiZdm+iDg8IpqnP4+Q/IfeXgU6FdMPt5+RNJ9sHRFbAYtImjaIiGkRcQpJc8FtwFBJW0TEyoi4MSK6An1IvjmeuYFFyx2u+GPgF2kyKfvZPCKGZC1TGvdvI2JPoCtJgvtpnsPmkHygAZA2j7QCZmc4f7ec93tshX0REY8B/wau28hy/ZLk/dk9beY7nfR3s54+JmkizOdrYPOc9Xwf5hWHlB4CnCJpH6AZSe2q7DovVShn84i4aANitgycIKzMH0jagY+s8A08n9eAucCtkrZQ0qm8b57jWgCrgHlAE0nXAeXftiWdLqlNRKwBFqab10j6oaTd0/bzxSTNOms2pnCpPwIDJH1PiS0k9ZPUImuZJO2Vvr4pyYffskpiGwKcLamnpE1JPoz/ExEzq6EcALcC50vaZiPK1QL4ClgkqQP5E10WD5CU9UBJjSR1SPtJACYAJ0tqKqkXcEKG8w0nSa4DgUfTvw9I+lJ2kXRGer6m6e/jOxsYtxXgBGGk7b0XkrSBf1KhOWkdEbEaOBLYCfgImEXS7FHRCOA5YCpJc8sy1m7yOQyYIukrkg7rk9PktA0wlCQ5vEPSr/DwRhaTiBgHnE/SRLSApJP3rPUsU0uSD+QFaZm+AG7Pc60XgGtJ2sjnknzDPrnicRtRlreAl0n6Fja0XDeSNFstImnWeXwDY3kNOBv4dXqul/im9nQtSdkXpNf7S4bzLU9jOSj3+LT56RCS93EOSRPdbST9WlYE8oRBZmaWj2sQZmaWlxOEmZnl5QRhZmZ5OUGYmVle9WZwrNatW0enTp1KHYaZWZ0yfvz4zyOiTb599SZBdOrUiXHjxpU6DDOzOkXSh5XtcxOTmZnl5QRhZmZ5OUGYmVleThBmZpaXE4SZmeVVtAQhaZCSaRknV7Jfkn6rZErGSZL2yNn3YyVTMU6T9ONixWhmZpUrZg3iQZLROitzOLBz+nMByXDTSPoWcD3JhCi9geslbV3EOM3MLI+iPQcRES9XmF6xoqOBhyIZTvZVSVtJag/0BUZGxHwASSNJEs2QYsS5ZMUq7h2zMZNhmZll9+1tWtKve/tSh5FJKR+U68DacwPMSrdVtn0dki4gqX2w/fbbb1AQS1es5u7R0zfotWZm6yMCWjRr4gRREyLifuB+gF69em3QxBatmm/KB7f0q9a4zMzyufnptxny2kelDiOzUt7FNJtksvMyHdNtlW03M6vTPvj8a75esZpfDn+Hu0ZOZemK1aUOqUqlTBDDgDPTu5n2BhZFxFySaSoPkbR12jl9SLrNzKxOW5PO4Pngv2by2xen8eZHC0ocUdWK1sQkaQhJh3NrSbNI7kxqChAR95JMTH4Eyfy5S0jmtCUi5ku6CXg9PdXAsg5rM7O67E9n9wbg9Znz+dG9/2ZNLZ/xuZh3MZ1SYH8Al1SybxAwqBhxmZmVWlnT0pWPTeB7O7aiSSPx34ftyjZbNitxZGvzk9RmZjVs262SRPDZl8t548MF/OPN2fzngy9KHNW66vRdTGZmddFObVsw89bk7skZ877igDtfKnFE+bkGYWZmeTlBmJlZXk4QZmaWlxOEmVkJLV2Z3NF096jaN+SPE4SZWQl9u10LAFatXlPiSNblu5jMzEqoSeNG7NVpa5o2rn3f12tfRGZmVis4QZiZWV5OEGZmlpcThJmZ5eUEYWZmeTlBmJlZXk4QZmaWlxOEmZnl5QRhZlZi733yJf96/wv+Of3zUoeyFicIM7MSW7xsFQA/GzqpxJGszQnCzKzEpt58OP27t2fVmto1HpMThJlZiW3SpBFvz13Mp4uXs2jJylKHU84JwsysFli+Mqk9vPnxghJH8g0nCDOzWuDuU79b6hDW4QRhZmZ5OUGYmVleThBmZpaXE4SZmeXlBGFmZnk5QZiZWV5NshwkqS2wL7AtsBSYDIyLiNr12J+ZmVWbKhOEpB8CVwPfAt4EPgOaAccAXSQNBe6MiMVFjtPMzGpYoRrEEcD5EfFRxR2SmgD9gYOBvxchNjMzK6EqE0RE/LSKfauAJ6o7IDMzqx02uJNa0tnVGYiZmdUuG3MX043VFoWZmdU6hTqpK5u9QkC76g/HzMxqi0Kd1O2AQ4GK488K+Fehk0s6DPgN0Bj434i4tcL+HYBBQBtgPnB6RMxK9/0K6EdSyxkJXBERUeiaZmZWPQo1MT0NNI+IDyv8zATGVPVCSY2Be4DDga7AKZK6VjjsDuChiOgODARuSV/bh+S5i+7AbsBewP7rUzAzM9s4VSaIiDg3Il6pZN+pBc7dG5geETMiYgXwV+DoCsd0BUaly6Nz9gfJ8xabAJsCTYFPC1zPzMyqUTGH2ugAfJyzPivdlmsicFy6fCzQQlKriPg3ScKYm/6MiIh3Kl5A0gWSxkkaN2/evGovgJlZQ1bqsZiuAvaX9CZJE9JsYLWknYDvAB1JksoBkvar+OKIuD8iekVErzZt2tRk3GZm1Wrel8sBuH3EeyWO5BuZxmLaQLOB7XLWO6bbykXEHNIahKTmwPERsVDS+cCrEfFVuu9ZYB9gbBHjNTMrmQN2bQtA6+abljiSbxSzBvE6sLOkzpI2AU4GhuUeIKm1pLIYriG5owngI5KaRRNJTUlqF+s0MZmZ1RdNGzdi57bN2WLTxqUOpVzmBCHp/qrWK0qH4rgUGEHy4f5YREyRNFDSUelhfYH3JE0luaX2F+n2ocD7wFsk/RQTI+KprLGamdnGW58mpvsKrK8jIoYDwytsuy5neShJMqj4utXAhesRm5mZVbPMNYiIGF/VupmZ1S+Fhtp4iuSZhLwi4qjK9pmZWd1WqInpjhqJwszMap1C80G8VLYsaTNg+4ioPTfpmplZ0WTqg5B0JDABeC5d7ylpWJUvMjOzOi1rJ/UNJGMrLQSIiAlA56JEZGZmtULWBLEyIhZV2Oaht83M6rGsz0FMkXQq0FjSzsDlZJgPwszM6q6sNYjLgG7AcmAIsBj4P0WKyczMaoFMNYiIWAL8XNJtyWp8WdywzMys1LLexbSXpLeAScBbkiZK2rO4oZmZWSll7YN4ALg4IsYCSPo+8CeSKUHNzKweytoHsbosOQCk05CuKk5IZmZWGxQai2mPdPElSfeRdFAHcBIwprihmZlZKRVqYrqzwvr1Oct+DsLMrBpN++wrpn32Fa99MJ/enb9V6nAKjsX0w5oKxMysoWskWBPw1uxFtT9B5JLUj+RZiGZl2yJiYDGCMjNriN687hB63Ph8qcMol/U213tJ+h0uAwT8CNihiHGZmVmJZb2LqU9EnAksiIgbgX2AXYoXlpmZlVrWBLE0/XeJpG2BlUD74oRkZma1QdY+iKclbQXcDrxBcgfT/xYrKDMzK72sYzHdlC7+XdLTQLM8w3+bmVk9UuhBueOq2EdEPF79IZmZWW1QqAZxZBX7AnCCMDOrpwo9KHd2TQViZma1S9a7mMzMrIFxgjAzq4UigojSDnnnBGFmVsvc9PTbdL5mON2uH8HiZStLFkfWoTY2l3StpD+m6ztL6l/c0MzMGpYmjbTW+pIVq1nw9YoSRZO9BvEnYDnJEBsAs4GbixKRmVkDtcWmTRj1X/vz7k2HcdPR3QDY//Yx9Lp5JC9NnVfj8WRNEF0i4lckQ2wQEUtIBu0zM7NqtGOb5jRr2pjPvlxevu3zr1bw3ieLazyWrENtrJC0GekkQZK6kNQozMysCK48eBdO33sHmm/ahG7XjyhJDFkTxA3Ac8B2kh4B9gXOKlJMZmYNniTatWzG18tXlSyGrGMxPS9pPLA3SdPSFRHxeVEjMzOzksqUICQ9BfwFGBYRXxc3JDMzqw2ydlLfAewHvC1pqKQTJDUr9CJJh0l6T9J0SVfn2b+DpBclTZI0RlLHnH3bS3pe0juS3pbUKWuhzMxs42VKEBHxUkRcDOwI3AecCHxW1WskNQbuAQ4HugKnSOpa4bA7gIciojswELglZ99DwO0R8R2gd6HrmZlZ9cr8JHV6F9PxwABgL2BwgZf0BqZHxIyIWAH8FTi6wjFdgVHp8uiy/WkiaRIRIwEi4qv01lozM6shWZ+kfgx4BzgA+B3JcxGXFXhZB+DjnPVZ6bZcE4GyOSeOBVpIakUy3/VCSY9LelPS7WmNpGJcF0gaJ2ncvHk1/xCJmVl9lrUG8QBJUhgQEaMjYk01Xf8qYH9JbwL7kzyhvZqk83y/dP9eJE1bZ1V8cUTcHxG9IqJXmzZtqikkM7PaZ9S7Nd/KXmhGuQMiYhSwBXC0tPbD0wVmlJsNbJez3jHdlvv6OaQ1CEnNgeMjYqGkWcCEiJiR7nuC5BbbBzKUycys3mjSOPncfXXG/Jq/doH9+5P0EeSbWa7QjHKvAztL6kySGE4GTs09QFJrYH5aI7kGGJTz2q0ktYmIeSRNW+MKxGpmVu9s2qQx/bq35925tWyojYi4Pl0cGBEf5O5LP/ireu0qSZcCI4DGwKCImCJpIDAuIoYBfYFbJAXwMnBJ+trVkq4CXlRSbRkP/HG9S2dmZhss61Abfwf2qLBtKLBnVS+KiOHA8ArbrstZHpqeJ99rRwLdM8ZnZmbVrFAfxK5AN2BLScfl7GoJFHxQzszM6q5CNYhvA/2BrVi7H+JL4PwixWRmZrVAoT6IJ4EnJe0TEf+uoZjMzCzHZ4uX8f68rzny7leQoFnTxtx1Yg86br15Ua9bqInpZ+lEQadKOqXi/oi4vGiRmZkZAK/PXADAnIVL6dx6C177YD5vz1lc2gRB8vQ0+BZTM7OS6de9Pc9MmsvY//4hM+Z9Tf+7X6mR6xZqYnoq/bd83CVJjYDmEVHzN+WamTVA95y6B/ecWvi46pZ1LKa/SGopaQtgMsmw3z8tbmhmZlZKWcdi6prWGI4BngU6A2cUKygzMyu9rAmiqaSmJAliWESsJBlqw8zM6qmsCeI+YCbJoH0vS9oBcB+EmVmJLF62qujXyDqj3G8jokNEHBGJD4EfFjk2MzOrYPGylQBc/fdJRb9W1k7qLSXdVTY5j6Q7SWoTZmZWg/p0aQ3A93duXfRrZW1iGkQyvMaJ6c9i4E/FCsrMzCrXbduWNGmkwgdupKyjuXaJiONz1m+UNKEI8ZiZWS2RtQaxVNL3y1Yk7QssLU5IZmZWldkLlzJp1qKiXydrDWIA8JCkLdP1BcCPixOSmZlVZeGSpKP6y2UradGsadGuUzBBSOoJ7EQyZehsAA+zYWZWOv27t+fpSXNZsWpNUa9TZROTpOuAx4DjgWeAk5wczMxKq3fnb9XIdQrVIE4CekbEEkmtgOfw3NBmZg1CoU7q5RGxBCAivshwvJmZ1ROFahA7ShqWLgvokrNORBxVtMjMzKykCiWIoyus31GsQMzMrHYpNGHQSzUViJmZ1S6F7mJ6StKR6VDfFfftKGmgpHOKF56ZmZVKoSam84Ergf+RNB+YBzQDOgHvA7+LiCeLGqGZmZVEoSamT4CfAT+T1AloTzLExtSyu5vMzKx+yjrUBhExk2TSIDMzawD8XIOZmeXlBGFmZnk5QZiZWV6Z+iDS+R9uAHZIXyMgImLH4oVmZmallLWT+gHgJ8B4YHXxwjEzs9oia4JYFBHPFjUSMzOrVbImiNGSbgceB5aXbYyIN4oSlZmZlVzWBPG99N9eOdsCOKB6wzEzs9oiU4KIiB9uyMklHQb8BmgM/G9E3Fph/w7AIKANMB84PSJm5exvCbwNPBERl25IDGZmtmEy3eYqaUtJd0kal/7cKWnLAq9pDNwDHA50BU6R1LXCYXcAD0VEd2AgcEuF/TcBL2eJ0czMqlfW5yAGAV8CJ6Y/i4E/FXhNb2B6RMyIiBXAX1l3fomuwKh0eXTufkl7Au2A5zPGaGZm1ShrgugSEdenH/YzIuJGoNAzEB2Aj3PWZ6Xbck0EjkuXjwVaSGolqRFwJ3BVVReQdEFZrWbevHkZi2JmZllkTRBLJX2/bCV9cG5pNVz/KmB/SW8C+wOzSZ6zuBgYntsfkU9E3B8RvSKiV5s2baohHDMzK5P1LqaLgMFpv4NIOpTPKvCa2cB2Oesd023lImIOaQ1CUnPg+IhYKGkfYD9JFwPNgU0kfRURV2eM18zMNlLWu5gmAD3Su4qIiMUZXvY6sLOkziSJ4WTg1NwDJLUG5kfEGuAakr4OIuK0nGPOAno5OZiZ1awqE4Sk0yPiz5KurLAdgIi4q7LXRsQqSZcCI0hucx0UEVMkDQTGRcQwoC9wi6QguVvpko0pjJmZVZ9CNYgt0n9bbMjJI2I4MLzCtutylocCQwuc40HgwQ25vpmZbbhCU47el/57Y82EY2ZmtUXWB+V+JamlpKaSXpQ0T9LpxQ7OzMxKJ+ttroekHdP9Seal3gn4abGCMjOz0suaIMqaovoBf4uIRUWKx8zMaomsz0E8LeldkofjLpLUBlhWvLDMzKzUMtUg0mcQ+pA8j7AS+Jp1x1UyM7N6pNBzEAdExChJx+Vsyz3k8WIFZmZmpVWoiWl/ktFWj8yzL3CCMDOrtwo9B3F9+u/ZNROOmZnVFlmfg/ilpK1y1reWdHPRojIzs5LLepvr4RGxsGwlIhYARxQlIjMzqxWyJojGkjYtW5G0GbBpFcebmVkdl/U5iEeAFyWVTTN6NjC4OCGZmVltkHU+iNskTQQOSjfdFBEjiheWmZmVWtYaBMA7wKqIeEHS5pJaRMSXxQrMzMyqNuHjhRywa9uKz6dVm6x3MZ1PMm/DfemmDsATRYnIzMyq9OqMLwA4d/A4pszJMsHnhsnaSX0JsC+wGCAipgFtixWUmZlVrn/3bcuXl6xYXbTrZE0QyyNiRdmKpCYkT1KbmVkNO2L39jxy3veKfp2sCeIlSf8X2EzSwcDfgKeKF5aZmZVa1gTx38A84C3gQpJ5pv9fsYIyM7PSK3gXk6TGwJSI2BX4Y/FDMjOz2qBgDSIiVgPvSdq+BuIxM7NaIutzEFsDUyS9RjJZEAARcVRRojIzs5LLmiCuLWoUZmZW6xSaUa4ZMADYiaSD+oGIWFUTgZmZWWkV6oMYDPQiSQ6HA3cWPSIzM6sVCjUxdY2I3QEkPQC8VvyQzMysNihUg1hZtuCmJTOzhqVQDaKHpLKRoETyJPXidDkiomVRozMzs5KpMkFEROOaCsTMzGqXrENtmJlZA+MEYWZmeTlBmJlZXk4QZmaWV1EThKTDJL0nabqkq/Ps30HSi5ImSRojqWO6vaekf0uaku47qZhxmpnZuoqWINJhwu8heQK7K3CKpK4VDrsDeCgiugMDgVvS7UuAMyOiG3AY8D+StipWrGZmtq5i1iB6A9MjYkY6XelfgaMrHNMVGJUujy7bHxFT03mviYg5wGdAmyLGamZWp3z4xRIA7nvp/aJdo5gJogPwcc76rHRbronAcenysUALSa1yD5DUG9gEKN67YGZWx/xgl9YArFi9pmjXKHUn9VXA/pLeBPYHZgOry3ZKag88DJwdEeu8C5IukDRO0rh58+bVVMxmZiXXcevN6bDVZrRt0axo1yhmgpgNbJez3jHdVi4i5kTEcRHxXeDn6baFAJJaAs8AP4+IV/NdICLuj4heEdGrTRu3QJmZVadiJojXgZ0ldZa0CXAyMCz3AEmtJZXFcA0wKN2+CfAPkg7soUWM0czMKlG0BJGO/nopMAJ4B3gsIqZIGiipbKrSviTzXU8F2gG/SLefCPwAOEvShPSnZ7FiNTOzdWWdcnSDRMRwYHiFbdflLA8F1qkhRMSfgT8XMzYzM6taqTupzcyslnKCMDOzvJwgzMwsLycIMzPLq6id1KW2cuVKZs2axbJly0oditUhzZo1o2PHjjRt2rTUoZhVafbCpYx+77Oinb9eJ4hZs2bRokULOnXqhKRSh2N1QETwxRdfMGvWLDp37lzqcMwKmv/1ClavCRo3qv7PuHrdxLRs2TJatWrl5GCZSaJVq1audVqdcMCubYt6/nqdIAAnB1tv/puxuqLndlsV9fz1PkGYmdmGcYIosk8++YSTTz6ZLl26sOeee3LEEUcwdepUZs6cyW677VZt17nuuut44YUXABg7dizdunWjZ8+ezJ49mxNOOGGjzh0RHHDAASxevLh82xNPPIEk3n333fJtM2fOZLPNNqNnz5507dqVAQMGsGbNxg1F/PLLL7PHHnvQpEkThg6tfFiu8ePHs/vuu7PTTjtx+eWXExEAzJ8/n4MPPpidd96Zgw8+mAULFgDw9NNPc91111V6PjNzgiiqiODYY4+lb9++vP/++4wfP55bbrmFTz/9tNqvNXDgQA466CAAHnnkEa655homTJhAhw4dqvxgrWjVqlXrbBs+fDg9evSgZcuW5duGDBnC97//fYYMGbLWsV26dGHChAlMmjSJt99+myeeeGLDCpTafvvtefDBBzn11FOrPO6iiy7ij3/8I9OmTWPatGk899xzANx6660ceOCBTJs2jQMPPJBbb70VgH79+vHUU0+xZMmSjYrPrD6r13cx5brxqSm8PWdx4QPXQ9dtW3L9kd0q3T969GiaNm3KgAEDyrf16NEDSL5tl5k5cyZnnHEGX3/9NQC/+93v6NOnD3PnzuWkk05i8eLFrFq1ij/84Q/06dOHc889l3HjxiGJc845h5/85CecddZZ9O/fn4ULF/LYY48xYsQInn32WX7xi1/Qv39/Jk+ezOrVq7n66qsZM2YMy5cv55JLLuHCCy9kzJgxXHvttWy99da8++67TJ06da1yPPLII1xwwQXl61999RWvvPIKo0eP5sgjj+TGG29cp+xNmjShT58+TJ8+fYPe2zKdOnUCoFGjyr/LzJ07l8WLF7P33nsDcOaZZ/LEE09w+OGH8+STTzJmzBgAfvzjH9O3b19uu+02JNG3b1+efvppTjzxxI2K0ay+ajAJohQmT57MnnvuWfC4tm3bMnLkSJo1a8a0adM45ZRTGDduHH/5y1849NBD+fnPf87q1atZsmQJEyZMYPbs2UyePBmAhQsXrnWu8847j1deeYX+/ftzwgknrJWIHnjgAbbccktef/11li9fzr777sshhxwCwBtvvMHkyZPz3tr5z3/+k/vuu698/cknn+Swww5jl112oVWrVowfP36dci5ZsoQXX3yRgQMHrnO+/fbbjy+//HKd7XfccUd5LWh9zJ49m44dO5avd+zYkdmzk6lHPv30U9q3bw/ANttss1btrVevXowdO9YJwqwSDSZBVPVNv9RWrlzJpZdeyoQJE2jcuHH5N/i99tqLc845h5UrV3LMMcfQs2dPdtxxR2bMmMFll11Gv379yj/gs3j++eeZNGlSeZPTokWLmDZtGptssgm9e/eu9L7/+fPn06JFi/L1IUOGcMUVVwBw8sknM2TIkPIE8f7779OzZ08kcfTRR3P44Yevc76xY8dmjrk6SVrrDqW2bdsyZ86cksRiVhc0mARRCt26dcvU/v/rX/+adu3aMXHiRNasWUOzZskUgj/4wQ94+eWXeeaZZzjrrLO48sorOfPMM5k4cSIjRozg3nvv5bHHHmPQoEGZ4okI7r77bg499NC1to8ZM4Ytttii0tc1adKENWvW0KhRI+bPn8+oUaN46623kMTq1auRxO233w580wdRlequQXTo0IFZs2aVr8+aNYsOHZLpz9u1a8fcuXNp3749c+fOpW3bb+4bX7ZsGZttttl6X8+soXAndREdcMABLF++nPvvv79826RJk9b5Br1o0SLat29Po0aNePjhh1m9OpmW+8MPP6Rdu3acf/75nHfeebzxxht8/vnnrFmzhuOPP56bb76ZN954I3M8hx56KH/4wx9YuXIlAFOnTi3v96jKt7/9bWbMmAHA0KFDOeOMM/jwww+ZOXMmH3/8MZ07d16vWsHYsWOZMGHCOj8bkhwA2rdvT8uWLXn11VeJCB566CGOPvpoAI466igGDx4MwODBg8u3Q1L+6ryTzKxUxn+4oCjndYIoIkn84x//4IUXXqBLly5069aNa665hm222Wat4y6++GIGDx5Mjx49ePfdd8u/zY8ZM4YePXrw3e9+l0cffZQrrriC2bNn07dvX3r27Mnpp5/OLbfckjme8847j65du7LHHnuw2267ceGFF+a9a6mifv36lXf0DhkyhGOPPXat/ccff/w6dzNVl9dff52OHTvyt7/9jQsvvJBu3b5pKuzZs2f58u9//3vOO+88dtppJ7p06VLetHX11VczcuRIdt55Z1544QWuvvrq8teM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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "evaluate_emeddings_approach(labels=['An Amazon review with a negative sentiment.', 'An Amazon review with a positive sentiment.'], engine='babbage-search-query')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As shown above, zero-shot classification with embeddings can lead to great results, especially when the labels are more descriptive than just simple words." + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/utils.py b/examples/embeddings/utils.py new file mode 100644 index 0000000000..f7877147fd --- /dev/null +++ b/examples/embeddings/utils.py @@ -0,0 +1,94 @@ +import openai +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt + +from tenacity import retry, wait_random_exponential, stop_after_attempt +from sklearn.metrics import precision_recall_curve +from sklearn.metrics import average_precision_score + + +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) +def get_embedding(text, engine="davinci-similarity"): + + # replace newlines, which can negatively affect performance. + text = text.replace("\n", " ") + + return openai.Engine(id=engine).embeddings(input = [text])['data'][0]['embedding'] + + +def cosine_similarity(a, b): + return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) + + +def plot_multiclass_precision_recall( + y_score, y_true_untransformed, class_list, classifier_name +): + """ + Precision-Recall plotting for a multiclass problem. It plots average precision-recall, per class precision recall and reference f1 contours. + + Code slightly modified, but heavily based on https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html + """ + n_classes = len(class_list) + y_true = pd.concat( + [(y_true_untransformed == class_list[i]) for i in range(n_classes)], axis=1 + ).values + + # For each class + precision = dict() + recall = dict() + average_precision = dict() + for i in range(n_classes): + precision[i], recall[i], _ = precision_recall_curve(y_true[:, i], y_score[:, i]) + average_precision[i] = average_precision_score(y_true[:, i], y_score[:, i]) + + # A "micro-average": quantifying score on all classes jointly + precision["micro"], recall["micro"], _ = precision_recall_curve( + y_true.ravel(), y_score.ravel() + ) + average_precision["micro"] = average_precision_score( + y_true, y_score, average="micro" + ) + print( + str(classifier_name) + + " - Average precision score over all classes: {0:0.2f}".format( + average_precision["micro"] + ) + ) + + # setup plot details + plt.figure(figsize=(9, 10)) + f_scores = np.linspace(0.2, 0.8, num=4) + lines = [] + labels = [] + for f_score in f_scores: + x = np.linspace(0.01, 1) + y = f_score * x / (2 * x - f_score) + (l,) = plt.plot(x[y >= 0], y[y >= 0], color="gray", alpha=0.2) + plt.annotate("f1={0:0.1f}".format(f_score), xy=(0.9, y[45] + 0.02)) + + lines.append(l) + labels.append("iso-f1 curves") + (l,) = plt.plot(recall["micro"], precision["micro"], color="gold", lw=2) + lines.append(l) + labels.append( + "average Precision-recall (auprc = {0:0.2f})" + "".format(average_precision["micro"]) + ) + + for i in range(n_classes): + (l,) = plt.plot(recall[i], precision[i], lw=2) + lines.append(l) + labels.append( + "Precision-recall for class `{0}` (auprc = {1:0.2f})" + "".format(class_list[i], average_precision[i]) + ) + + fig = plt.gcf() + fig.subplots_adjust(bottom=0.25) + plt.xlim([0.0, 1.0]) + plt.ylim([0.0, 1.05]) + plt.xlabel("Recall") + plt.ylabel("Precision") + plt.title(f"{classifier_name}: Precision-Recall curve for each class") + plt.legend(lines, labels) \ No newline at end of file diff --git a/examples/finetuning/answers-with-ft.py b/examples/finetuning/answers_with_ft.py similarity index 92% rename from examples/finetuning/answers-with-ft.py rename to examples/finetuning/answers_with_ft.py index 2ba22edb6f..32507e82ff 100644 --- a/examples/finetuning/answers-with-ft.py +++ b/examples/finetuning/answers_with_ft.py @@ -67,8 +67,14 @@ def answer_question( print("Context:\n" + context) print("\n\n") try: + # fine-tuned models requires model parameter, whereas other models require engine parameter + model_param = ( + {"model": fine_tuned_qa_model} + if ":" in fine_tuned_qa_model + and fine_tuned_qa_model.split(":")[1].startswith("ft") + else {"engine": fine_tuned_qa_model} + ) response = openai.Completion.create( - model=fine_tuned_qa_model, prompt=f"Answer the question based on the context below\n\nText: {context}\n\n---\n\nQuestion: {question}\nAnswer:", temperature=0, max_tokens=max_tokens, @@ -76,6 +82,7 @@ def answer_question( frequency_penalty=0, presence_penalty=0, stop=stop_sequence, + **model_param, ) return response["choices"][0]["text"] except Exception as e: diff --git a/examples/finetuning/olympics-1-collect-data.ipynb b/examples/finetuning/olympics-1-collect-data.ipynb new file mode 100644 index 0000000000..7a88051bbf --- /dev/null +++ b/examples/finetuning/olympics-1-collect-data.ipynb @@ -0,0 +1,513 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1. Collect Wikipedia data about Olympic Games 2020\n", + "\n", + "The idea of this project is to create a question answering model, based on a few paragraphs of provided text. Base GPT-3 models do a good job at answering questions when the answer is contained within the paragraph, however if the answer isn't contained, the base models tend to try their best to answer anyway, often leading to confabulated answers. \n", + "\n", + "To create a model which answers questions only if there is sufficient context for doing so, we first create a dataset of questions and answers based on paragraphs of text. In order to train the model to answer only when the answer is present, we also add adversarial examples, where the question doesn't match the context. In those cases, we ask the model to output \"No sufficient context for answering the question\". \n", + "\n", + "We will perform this task in three notebooks:\n", + "1. The first (this) notebook focuses on collecting recent data, which GPT-3 didn't see during it's pre-training. We picked the topic of Olympic Games 2020 (which actually took place in the summer of 2021), and downloaded 713 unique pages. We organized the dataset by individual sections, which will serve as context for asking and answering the questions.\n", + "2. The [second notebook](olympics-2-create-qa.ipynb) will utilize Davinci-instruct to ask a few questions based on a Wikipedia section, as well as answer those questions, based on that section.\n", + "3. The [third notebook](olympics-3-train-qa.ipynb) will utilize the dataset of context, question and answer pairs to additionally create adversarial questions and context pairs, where the question was not generated on that context. In those cases the model will be prompted to answer \"No sufficient context for answering the question\". We will also train a discriminator model, which predicts whether the question can be answered based on the context or not." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1.1 Data extraction using the wikipedia API\n", + "Extracting the data will take about half an hour, and processing will likely take about as much." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "909" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "import wikipedia\n", + "\n", + "\n", + "def filter_olympic_2020_titles(titles):\n", + " \"\"\"\n", + " Get the titles which are related to Olympic games hosted in 2020, given a list of titles\n", + " \"\"\"\n", + " titles = [title for title in titles if '2020' in title and 'olympi' in title.lower()]\n", + " \n", + " return titles\n", + "\n", + "def get_wiki_page(title):\n", + " \"\"\"\n", + " Get the wikipedia page given a title\n", + " \"\"\"\n", + " try:\n", + " return wikipedia.page(title)\n", + " except wikipedia.exceptions.DisambiguationError as e:\n", + " return wikipedia.page(e.options[0])\n", + " except wikipedia.exceptions.PageError as e:\n", + " return None\n", + "\n", + "def recursively_find_all_pages(titles, titles_so_far=set()):\n", + " \"\"\"\n", + " Recursively find all the pages that are linked to the Wikipedia titles in the list\n", + " \"\"\"\n", + " all_pages = []\n", + " \n", + " titles = list(set(titles) - titles_so_far)\n", + " titles = filter_olympic_2020_titles(titles)\n", + " titles_so_far.update(titles)\n", + " for title in titles:\n", + " page = get_wiki_page(title)\n", + " if page is None:\n", + " continue\n", + " all_pages.append(page)\n", + "\n", + " new_pages = recursively_find_all_pages(page.links, titles_so_far)\n", + " for pg in new_pages:\n", + " if pg.title not in [p.title for p in all_pages]:\n", + " all_pages.append(pg)\n", + " titles_so_far.update(page.links)\n", + " return all_pages\n", + "\n", + "\n", + "pages = recursively_find_all_pages([\"2020 Summer Olympics\"])\n", + "len(pages)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1.2 Filtering the Wikipedia pages and splitting them into sections by headings\n", + "We remove sections unlikely to contain textual information, and ensure that each section is not longer than the token limit" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('Bermuda at the 2020 Summer Olympics',\n", + " 'Equestrian',\n", + " \"Bermuda entered one dressage rider into the Olympic competition by finishing in the top four, outside the group selection, of the individual FEI Olympic Rankings for Groups D and E (North, Central, and South America), marking the country's recurrence to the sport after an eight-year absence. The quota was later withdrawn, following an injury of Annabelle Collins' main horse Joyero and a failure to obtain minimum eligibility requirements (MER) aboard a new horse Chuppy Checker.\",\n", + " 104)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "import re\n", + "from typing import Set\n", + "from transformers import GPT2TokenizerFast\n", + "\n", + "import numpy as np\n", + "from nltk.tokenize import sent_tokenize\n", + "\n", + "tokenizer = GPT2TokenizerFast.from_pretrained(\"gpt2\")\n", + "\n", + "def count_tokens(text: str) -> int:\n", + " \"\"\"count the number of tokens in a string\"\"\"\n", + " return len(tokenizer.encode(text))\n", + "\n", + "def reduce_long(\n", + " long_text: str, long_text_tokens: bool = False, max_len: int = 590\n", + ") -> str:\n", + " \"\"\"\n", + " Reduce a long text to a maximum of `max_len` tokens by potentially cutting at a sentence end\n", + " \"\"\"\n", + " if not long_text_tokens:\n", + " long_text_tokens = count_tokens(long_text)\n", + " if long_text_tokens > max_len:\n", + " sentences = sent_tokenize(long_text.replace(\"\\n\", \" \"))\n", + " ntokens = 0\n", + " for i, sentence in enumerate(sentences):\n", + " ntokens += 1 + count_tokens(sentence)\n", + " if ntokens > max_len:\n", + " return \". \".join(sentences[:i][:-1]) + \".\"\n", + "\n", + " return long_text\n", + "\n", + "discard_categories = ['See also', 'References', 'External links', 'Further reading', \"Footnotes\",\n", + " \"Bibliography\", \"Sources\", \"Citations\", \"Literature\", \"Footnotes\", \"Notes and references\",\n", + " \"Photo gallery\", \"Works cited\", \"Photos\", \"Gallery\", \"Notes\", \"References and sources\",\n", + " \"References and notes\",]\n", + "\n", + "\n", + "def extract_sections(\n", + " wiki_text: str,\n", + " title: str,\n", + " max_len: int = 1500,\n", + " discard_categories: Set[str] = discard_categories,\n", + ") -> str:\n", + " \"\"\"\n", + " Extract the sections of a Wikipedia page, discarding the the references and other low information sections\n", + " \"\"\"\n", + " if len(wiki_text) == 0:\n", + " return []\n", + "\n", + " # find all headings and the coresponding contents\n", + " headings = re.findall(\"==+ .* ==+\", wiki_text)\n", + " for heading in headings:\n", + " wiki_text = wiki_text.replace(heading, \"==+ !! ==+\")\n", + " contents = wiki_text.split(\"==+ !! ==+\")\n", + " contents = [c.strip() for c in contents]\n", + " assert len(headings) == len(contents) - 1\n", + "\n", + " cont = contents.pop(0).strip()\n", + " outputs = [(title, \"Summary\", cont, count_tokens(cont)+4)]\n", + " \n", + " # discard the discard categories, accounting for a tree structure\n", + " max_level = 100\n", + " keep_group_level = max_level\n", + " remove_group_level = max_level\n", + " nheadings, ncontents = [], []\n", + " for heading, content in zip(headings, contents):\n", + " plain_heading = \" \".join(heading.split(\" \")[1:-1])\n", + " num_equals = len(heading.split(\" \")[0])\n", + " if num_equals <= keep_group_level:\n", + " keep_group_level = max_level\n", + "\n", + " if num_equals > remove_group_level:\n", + " if (\n", + " num_equals <= keep_group_level\n", + " ):\n", + " continue\n", + " keep_group_level = max_level\n", + " if plain_heading in discard_categories:\n", + " remove_group_level = num_equals\n", + " keep_group_level = max_level\n", + " continue\n", + " nheadings.append(heading.replace(\"=\", \"\").strip())\n", + " ncontents.append(content)\n", + " remove_group_level = max_level\n", + "\n", + " # count the tokens of each section\n", + " ncontent_ntokens = [\n", + " count_tokens(c)\n", + " + 3\n", + " + count_tokens(\" \".join(h.split(\" \")[1:-1]))\n", + " - (1 if len(c) == 0 else 0)\n", + " for h, c in zip(nheadings, ncontents)\n", + " ]\n", + "\n", + " # Create a tuple of (title, section_name, content, number of tokens)\n", + " outputs += [(title, h, c, t) if t 1024). Running this sequence through the model will result in indexing errors\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
titleheadingcontenttokens
02020 Summer OlympicsSummaryThe 2020 Summer Olympics (Japanese: 2020年夏季オリン...713
12020 Summer OlympicsHost city selectionThe International Olympic Committee (IOC) vote...126
22020 Summer OlympicsImpact of the COVID-19 pandemicIn January 2020, concerns were raised about th...369
32020 Summer OlympicsQualifying event cancellation and postponementConcerns about the pandemic began to affect qu...298
42020 Summer OlympicsEffect on doping testsMandatory doping tests were being severely res...163
\n", + "
" + ], + "text/plain": [ + " title heading \\\n", + "0 2020 Summer Olympics Summary \n", + "1 2020 Summer Olympics Host city selection \n", + "2 2020 Summer Olympics Impact of the COVID-19 pandemic \n", + "3 2020 Summer Olympics Qualifying event cancellation and postponement \n", + "4 2020 Summer Olympics Effect on doping tests \n", + "\n", + " content tokens \n", + "0 The 2020 Summer Olympics (Japanese: 2020年夏季オリン... 713 \n", + "1 The International Olympic Committee (IOC) vote... 126 \n", + "2 In January 2020, concerns were raised about th... 369 \n", + "3 Concerns about the pandemic began to affect qu... 298 \n", + "4 Mandatory doping tests were being severely res... 163 " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res = []\n", + "for page in pages:\n", + " res += extract_sections(page.content, page.title)\n", + "df = pd.DataFrame(res, columns=[\"title\", \"heading\", \"content\", \"tokens\"])\n", + "df = df[df.tokens>40]\n", + "df = df.drop_duplicates(['title','heading'])\n", + "df = df.reset_index().drop('index',axis=1) # reset index\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Save the section dataset\n", + "We will save the section dataset, for the [next notebook](olympics-2-create-qa.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "df.to_csv('olympics-data/olympics_sections.csv', index=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1.3 (Optional) Exploring the data " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Concerns and controversies at the 2020 Summer Olympics 51\n", + "United States at the 2020 Summer Olympics 46\n", + "Great Britain at the 2020 Summer Olympics 42\n", + "Canada at the 2020 Summer Olympics 39\n", + "Olympic Games 39\n", + "Name: title, dtype: int64" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.title.value_counts().head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There appear to be winter and summer Olympics 2020. We chose to leave a little ambiguity and noise in the dataset, even though we were interested in only Summer Olympics 2020." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True 3567\n", + "False 305\n", + "Name: title, dtype: int64" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.title.str.contains('Summer').value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False 3774\n", + "True 98\n", + "Name: title, dtype: int64" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.title.str.contains('Winter').value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYsAAAEWCAYAAACXGLsWAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Z1A+gAAAACXBIWXMAAAsTAAALEwEAmpwYAAAr20lEQVR4nO3deZwcVbn/8c+XsCdI2MwNEAibelEuCBHxojgBZRfUHyhcwABRREFB8UrABRQRXABFEERZXQiIC7sIXCIqsgWBsEqAsIRNIIQEBAl5fn+c06Sm6emumUzNFJnv+/Xq11SdU8vTNdX1dNU5XaWIwMzMrJ3FBjsAMzOrPycLMzPryMnCzMw6crIwM7OOnCzMzKwjJwszM+vIyaJA0qmSvtZPy1pD0lxJw/L4FEmf7I9l5+VdLmlCfy2vF+v9lqSnJT0x0OtuiqNL0qODuP6PSHok/4/f2Q/LC0nr9kdsfVh3t3214nW99hlr9z+U9D5J91YUw1mSvlX1evqDpMMl/Wyw44AhlCwkzZD0L0lzJD0n6TpJ+0t6bRtExP4RcVTJZX2g3TQR8XBEjIiIV/sh9iMl/aJp+dtFxNkLu+xexrEGcAiwfkT8x0Cuu4a+DxyY/8d/b64czIN/by3MvirpXkkfL4xvnt97c9kcSYuX/YxFxJ8j4q29jae3Bmo9ZbRKnhHx7Yjoty+ZC2PIJIvsQxGxHLAmcCxwKHB6f69E0uL9vcyaWAN4JiKeGuxA+lMf/19rAnf2dyxvQNcCWxTGtwDuaVH2t4iYN5CBWT+LiCHxAmYAH2gq2xSYD7wjj58FfCsPrwxcAjwHPAv8mZRcf57n+RcwF/gyMBYIYCLwMOkD1ChbPC9vCnAMcCPwPHAhsGKu6wIebRUvsC3wb+CVvL7bCsv7ZB5eDPgq8BDwFHAOsHyua8QxIcf2NPCVNttp+Tz/P/PyvpqX/4H8nufnOM5qMW8X8Cjp7OMp4HFgn0L9azHn8b2BvxTGA/gscB8wBzgKWAe4Lm+z84Elm9Z1eH5PM4A9CstaivTt/2HgSeBUYJmmeQ8FngB+3uK9tNymeblzc6wvAPe3mPfaQv1c4OO5/FPAdNL+dBGwatN7XzcPvxd4BOjK4/sCdwOzgCuANZvm2z9vs+eAkwHlunWBPwGz8zY6r4f/eWMfKe6rRwF/zf+HPwIr9zDvXsC0wvhl+f/aXPbVFp+xLgr7PfB54C5g9RZ1M4DDcv0s4Exg6UL9jsCteRtcB/xXoe6dwC35vZwHTG4TwyTg/jztXcBH2nxWNgVuJu2bTwLHF+o2y3E8B9zW+F/muhVz/I/l9/J7YDjdP19zgVWBI4FfFObdifQl5bn8f/rPpm30JeD2/D8/r7GN6OF41qtjaJUH6Dq9aJEscvnDwGda7MjHkA4wS+TX+1jwIey2LBZ82M7J//RlaP0BnAm8I0/zm8ZO0LzDNq+jeYcpLK+RLPYlHYTWBkYAvyUfAAtx/DTHtSHwcnEna1ruOaREtlye9x/AxJ7ibJq3C5gHfDNvs+2BF4EVmmPO43vz+mRxIfAm4O05zqvz+1qe9OGd0LSu40kH8PeTDs5vzfUnkA7IK+b3cjFwTNO838nzLtPivfS4TQuxrttmW3SrB7YkHbA3zuv8EXBt8/SkLwePAJvm8p1zHP8JLE5KYNc1zXcJMJJ05vdPYNtcdy7wFVLiWxp4bw+xNvaR4r56P/CWvM9MAY7tYd41SQe4FfN6nsrzPFIomw1s0eIz1kXen4Cvkw7oq7Ta10ifhzuAMXm5fy0s5515ve8GhpG+GM3I23lJUsL/Ammf3IX0xaunZLEr6SC9GPBx0j41uof3/jdgrzw8AtgsD68GPEPa/xcDPpjHG+/tUtKBfIUc0/vbHAeOZMFx4i05ng/m+b5M2jeWLGyjG3P8K5K+YOzf6XhW9jXULkO18hhpwzZ7BRhN+hb3SqRrm9FhWUdGxAsR8a8e6n8eEXdExAvA14CP9VOj4h6kbzUPRMRc0jew3Zour3wjIv4VEbeRvuls2LyQHMtuwGERMSciZgDHkb49lvUK8M28zS4jfUPqzTXh70bE8xFxJ+ng8Mf8vmYDl5MODEVfi4iXI+JPpA/hxyQJ2A/4QkQ8GxFzgG/n99YwHzgiz9vq/1Vmm/bGHsAZEXFLRLycl/ceSWML0+wK/ATYLiJuzGX7k5Lc3ZEu43wb2EjSmoX5jo2I5yLiYeAaYKNc/grpYL5qRLwUEX/pRbxnRsQ/8rY5v7DMbiLiIdIXrveR9qn78jx/LZQtCdzQw3ok6Xhga2B8RPyzTUwnRcQjEfEscDSwey7fD/hJRNwQEa9Gast7mfTtfjPSwfEHeZ+8ALippxVExK8j4rGImB8R55HO2DbtYfJXgHUlrRwRcyPi+ly+J3BZRFyWl3Ml6Qxke0mjge1IB/FZOaY/tXnPRR8HLo2IKyPiFdKZ8zLAfxemOTHH/yzpC9JGhVh7ezzrxskifQt4tkX590hZ+4+SHpA0qcSyHulF/UOknXjlUlG2t2peXnHZiwOjCmXF3ksvkr4JNVs5x9S8rNV6Ecsz0f3adE/r6smTheF/tRgvLmtWTrwND5G2xSrAssDU3JnhOeAPubzhnxHxUps4ymzT3ui2vJyAnqH7tj0YOD8i7iiUrQn8sPA+ngXUNF9P/9sv52lvlHSnpH17EW+Z/aWh0W6xBenyBsBfCmU35gTZykjSwf6Y/IWgnebPz6p5eE3gkMY2yttpTK5fFZjZdGAs/l+7kfQJSbcWlvMOev6MTiR9279H0k2SdizEs2tTPO8lHazHAM9GxKwO77WV5n1oPmmblNkX+nI862ZIJwtJ7yJt6Nd948rfrA+JiLVJ1wm/KGmrRnUPi+yUqccUhtcgZfunSaeWyxbiGkb3A1un5T5G2kGLy55H9wNtGU+z4NtocVkze7mcnnR7n8DC9qhaQdLwwvgapG3xNCmxvD0iRubX8hFRPOAN1DZtubwc90p037a7Ah+WdFCh7BHg04X3MTIilomI6zqtMCKeiIhPRcSqwKeBH1fUQ6uRLN7HgmTx50LZtW3mnUVqbzhT0uYd1tP8+XksDz8CHN20jZaNiHNJ7War5bPN4ryvk8/WfgocCKwUESNJZ7dqNX1E3BcRuwNvJl3SvCD/Xx8hXUUoxjM8Io7NdStKGtlqkR3ef/M+JNI26fj57HA8K2VIJgtJb8rfAiaTrgdOazHNjpLWzf+Q2cCrpEsXkA4Ya/dh1XtKWl/SsqTr+hdE6q74D2BpSTtIWoJ0XXqpwnxPAmOL3XybnAt8QdJakkaQLlWcF73sfZJjOR84WtJy+cPzReAX7ecs7Vbgo5KWzQetif2wzG9IWlLS+0gHnV/nb1w/BU6Q9GYASatJ2qYXy13Ybdq8j5wL7CNpI0lL5eXdkC/1NTwGbAUcJOkzuexU4DBJb8/vY3lJu5YJQNKuklbPo7NIB6P5bWbpq2tJlwe3IF1+ApgGrAWMp32yICKmkC7T/VZST5d8AA6QtLqkFUltMefl8p8C+0t6t5Lh+bO0HKldYR7weUlLSPooPV9WGk7aRv8EkLQP6cyiJUl7Slol72/P5eL5pM/LhyRtI2mYpKVzt9jVI+Jx0uXUH0taIcfU6Dn2JLCSpOV7WOX5wA6StsrHiUNIl9s6fnHocDwrZagli4slzSFl96+QGkf36WHa9YCrSNfc/wb8OCKuyXXHAF/Np5hf6sX6f05q4HuC1OD4eYB8+v1Z4GekbwkvkHrrNPw6/31G0i0tlntGXva1wIPAS8DnehFX0efy+h8gnXH9Ki+/P5xA6tn1JHA28MuFXN4TpIPgY3lZ+0fEPbnuUNJp9/WSnif9L3vTdrKw2/RI4Oy8j3wsIq4itVP9hvRtdx26t6EA6TcPpIQxSdInI+J3pG+tk/P7uIN0zbuMdwE3SJpLauw/KCIe6MV7KCUi/kE6wD4REc/lsvmkxtY3UeJglq/r70v6jG7cw2S/IvXMeoDUAP+tPO/NpJ5mJ5H2h+mkzhNExL+Bj+bxZ0nX/X/bQwx3kdro/kbaRzdgQfJrZVvgzrx9fwjsFqld8BFSx4TDSdvlEeB/WXC83Yt0Bn8PqWH+4Lz+e0hfKh7I+82qhXUREfeS2kN+RDp7/hDp5wD/bhNjQ7vjWSmN3j1mZrUlaQapJ91Vgx3LUDXUzizMzKwPnCzMzKwjX4YyM7OOfGZhZmYdLZI3vFt55ZVj7NixLeteeOEFhg8f3rKubhxrNRxrNRxrNQYy1qlTpz4dEau0rIyFvOdSHV+bbLJJ9OSaa67psa5uHGs1HGs1HGs1BjJW4ObwvaHMzKyvnCzMzKwjJwszM+vIycLMzDpysjAzs46cLMzMrCMnCzMz68jJwszMOnKyMDOzjhbJ230srLGTLh2U9c44dodBWa+ZWSc+szAzs46cLMzMrCMnCzMz68jJwszMOnKyMDOzjpwszMysIycLMzPrqLJkIWlpSTdKuk3SnZK+kcvXknSDpOmSzpO0ZC5fKo9Pz/VjC8s6LJffK2mbqmI2M7PWqjyzeBnYMiI2BDYCtpW0GfAd4ISIWBeYBUzM008EZuXyE/J0SFof2A14O7At8GNJwyqM28zMmlSWLPIjXefm0SXyK4AtgQty+dnAh/PwznmcXL+VJOXyyRHxckQ8CEwHNq0qbjMze71K2ywkDZN0K/AUcCVwP/BcRMzLkzwKrJaHVwMeAcj1s4GViuUt5jEzswFQ6b2hIuJVYCNJI4HfAW+ral2S9gP2Axg1ahRTpkxpOd3cuXN7rGs4ZIN5beur0hxXmVjrwrFWw7FWw7H23oDcSDAinpN0DfAeYKSkxfPZw+rAzDzZTGAM8KikxYHlgWcK5Q3FeYrrOA04DWDcuHHR1dXVMpYpU6bQU13D3oN1I8E9urqNl4m1LhxrNRxrNRxr71XZG2qVfEaBpGWADwJ3A9cAu+TJJgAX5uGL8ji5/v8iInL5brm31FrAesCNVcVtZmavV+WZxWjg7NxzaTHg/Ii4RNJdwGRJ3wL+Dpyepz8d+Lmk6cCzpB5QRMSdks4H7gLmAQfky1tmZjZAKksWEXE78M4W5Q/QojdTRLwE7NrDso4Gju7vGM3MrBz/gtvMzDpysjAzs46cLMzMrCMnCzMz68jJwszMOnKyMDOzjpwszMysIycLMzPrqGOykLS5pOF5eE9Jx0tas/rQzMysLsqcWZwCvChpQ+AQ0m3Gz6k0KjMzq5UyyWJevqHfzsBJEXEysFy1YZmZWZ2UuTfUHEmHAXsCW0hajPTUOzMzGyLKnFl8nPQ87YkR8QTpeRLfqzQqMzOrlY5nFjlBHF8Yfxi3WZiZDSllekN9VNJ9kmZLel7SHEnPD0RwZmZWD2XaLL4LfCgi7q46GDMzq6cybRZPOlGYmQ1tZc4sbpZ0HvB7UkM3ABHx26qCMjOzeimTLN4EvAhsXSgLwMnCzGyIKNMbap+BCMTMzOqrTG+o1SX9TtJT+fUbSasPRHBmZlYPZRq4zwQuAlbNr4tzmZmZDRFlksUqEXFmRMzLr7OAVSqOy8zMaqRMsngm35p8WH7tCTxTdWBmZlYfZZLFvsDHgCeAx4FdgI6N3pLGSLpG0l2S7pR0UC4/UtJMSbfm1/aFeQ6TNF3SvZK2KZRvm8umS5rU2zdpZmYLp0xvqIeAnfqw7HnAIRFxi6TlgKmSrsx1J0TE94sTS1of2A14O6lt5CpJb8nVJwMfBB4FbpJ0UUTc1YeYzMysD3pMFpK+HBHflfQj0u8quomIz7dbcEQ8TjoTISLmSLobWK3NLDsDkyPiZeBBSdOBTXPd9Ih4IMc1OU/rZGFmNkCUnmvUokL6UERcLGlCq/qIOLv0SqSxwLXAO4AvAnsDzwM3k84+Zkk6Cbg+In6R5zkduDwvYtuI+GQu3wt4d0Qc2LSO/YD9AEaNGrXJ5MmTW8Yyd+5cRowY0TbeaTNnl31r/WqD1ZbvNl4m1rpwrNVwrNVwrK2NHz9+akSMa1XX45lFRFycB1+MiF8X6yTtWnblkkYAvwEOjojnJZ0CHEU6WzkKOI7ULrJQIuI04DSAcePGRVdXV8vppkyZQk91DXtPunRhw+mTGXt0dRsvE2tdONZqONZqONbeK9PAfVjJsteRtAQpUfyycS+piHgyIl6NiPnAT1lwqWkmMKYw++q5rKdyMzMbIO3aLLYDtgdWk3RioepNpMbrtiQJOB24OyKOL5SPzu0ZAB8B7sjDFwG/knQ8qYF7PeBGQMB6ktYiJYndgP8p9/bMzKw/tOsN9RipTWEnYGqhfA7whRLL3hzYC5gm6dZcdjiwu6SNSJehZgCfBoiIOyWdT2q4ngccEBGvAkg6ELgCGAacERF3lli/mZn1k3ZtFrcBt0n6HfBC4cA9DFiq04Ij4i+ks4Jml7WZ52jg6Bbll7Wbz8zMqlWmzeKPwDKF8WWAq6oJx8zM6qhMslg6IuY2RvLwstWFZGZmdVMmWbwgaePGiKRNgH9VF5KZmdVNmSflHQz8WtJjpDaI/wA+XmVQZmZWL2XuDXWTpLcBb81F90bEK9WGZWZmdVLmSXnLAocCB0XEHcBYSTtWHpmZmdVG2Sfl/Rt4Tx6fCXyrsojMzKx2yiSLdSLiu8ArABHxIq1/P2FmZouoMsni35KWId+mXNI6wMuVRmVmZrVSpjfUEcAfgDGSfkm6jcfeVQZlZmb1UqY31JWSbgE2I11+Oiginq48MjMzq40yvaE2B16KiEuBkcDhktasOjAzM6uPMm0WpwAvStqQ9JS7+4FzKo3KzMxqpUyymBfp2as7AydHxMnActWGZWZmdVKmgXuOpMOAPYEtJC0GLFFtWGZmVidlziw+TuoqOzEiniA91vR7lUZlZma1UqY31BPA8YXxh3GbhZnZkFLmzMLMzIY4JwszM+vIycLMzDrq2GYhaT3gGGB9YOlGeUSsXWFcZmZWI2VvUX4KMA8YT2rc/kWVQZmZWb2USRbLRMTVgCLioYg4Etih2rDMzKxOyiSLl/MP8e6TdKCkjwAjOs0kaYykayTdJelOSQfl8hUlXSnpvvx3hVwuSSdKmi7pdkkbF5Y1IU9/n6QJfXyvZmbWR2WSxUHAssDngU2AvYAyB+x5wCERsT7pjrUHSFofmARcHRHrAVfncYDtgPXyaz/SpS8krUi6Tfq7gU2BIxoJxszMBkaZH+XdlAfnAvuUXXBEPA48nofnSLobWI10j6muPNnZwBTSM753Bs7J96G6XtJISaPztFdGxLMAkq4EtgXOLRuLmZktnB6ThaQfRMTBki4mPyWvKCJ2KrsSSWOBdwI3AKNyIgF4AhiVh1cDHinM9mgu66nczMwGSLszi5/nv99fmBVIGgH8Bjg4Ip6XFjy+OyJC0usSUR/Xsx/p8hWjRo1iypQpLaebO3duj3UNh2wwrz9C6rXmuMrEWheOtRqOtRqOtfd6TBYRMTX//VNfFy5pCVKi+GVE/DYXPylpdEQ8ni8zPZXLZwJjCrOvnstmsuCyVaN8Sot4TwNOAxg3blx0dXU1TwKkA3JPdQ17T7q0bX1VZuzR1W28TKx14Vir4Vir4Vh7r8cGbknTcq+klq9OC1Y6hTgduDsiji9UXcSCBvIJwIWF8k/kXlGbAbPz5aorgK0lrZAbtrfOZWZmNkDaXYbaMf89IP9tXJbakxZtGC1sTuo5NU3SrbnscOBY4HxJE4GHgI/lusuA7YHpwIvkxvSIeFbSUUCjof2bjcZuMzMbGO0uQz0EIOmDEfHOQtWhkm5hQZfXnub/C6AeqrdqMX2wIDE1150BnNFufWZmVp0yv7OQpM0LI/9dcj4zM1tElHms6kTgDEnL5/HngH0ri8jMzGqnzI/ypgIbNpJFRMyuPCozM6uVjpeTJI2SdDowOSJmS1o/N06bmdkQUabt4SxSV9VV8/g/gIMrisfMzGqoTLJYOSLOB+YDRMQ84NVKozIzs1opkyxekLQS+bcVjR/MVRqVmZnVSpneUF8k/bp6HUl/BVYBdqk0KjMzq5UyvaFukfR+4K2kH9ndGxGvVB6ZmZnVRsdkIWlp4LPAe0mXov4s6dSIeKnq4MzMrB7KXIY6B5gD/CiP/w/pPlG7VhWUmZnVS5lk8Y78aNSGayTdVVVAZmZWP2V6Q92Se0ABIOndwM3VhWRmZnVT5sxiE+A6SQ/n8TWAeyVNI90s9r8qi87MzGqhTLLYtvIozMys1npMFpLeFBHPkxq3X8cPIDIzGzranVn8ivS0vKmkLrPFBxkFsHaFcZmZWY20e1LejvnvWs11+fnaZmY2RJS5Rfk3m8YXA35RWURmZlY7ZbrOjpF0GICkpYDfAfdVGpWZmdVKmWSxL7BBThgXA9dExJGVRmVmZrXSrjfUxoXRHwI/Af4KXCtp44i4pergzMysHtr1hjquaXwWsH4uD2DLqoIyM7N6adcbavxABmJmZvXVY5uFpD3z3y+2enVasKQzJD0l6Y5C2ZGSZkq6Nb+2L9QdJmm6pHslbVMo3zaXTZc0qe9v1czM+qrdZajh+e9yfVz2WcBJpFucF50QEd8vFkhaH9gNeDuwKnCVpLfk6pOBDwKPAjdJuigifNdbM7MB1C5ZPCnpzRHxjb4sOCKulTS25OQ7A5Mj4mXgQUnTgU1z3fSIeABA0uQ8rZOFmdkAUkS0rpAuAN4DvAhcR+oJdV1E3NFyhtbLGAtcEhHvyONHAnsDz5Nuc35IRMySdBJwfUT8Ik93OnB5Xsy2EfHJXL4X8O6IOLDFuvYD9gMYNWrUJpMnT24Z09y5cxkxYkTbuKfNnF32LfarDVZbvtt4mVjrwrFWw7FWw7G2Nn78+KkRMa5VXbsG7l0AJK1FShr/DXxa0hrATRGxfU/ztnEKcBSpN9VRpJ5V+/ZhOa3iPQ04DWDcuHHR1dXVcropU6bQU13D3pMu7Y+Qem3GHl3dxsvEWheOtRqOtRqOtfc63qI8Ih7Mv9xeJr+Wzn97LSKebAxL+ilwSR6dCYwpTLp6LqNNuZmZDZB2vaEOl3SxpOuBw4AlSQ3W/9XXbrWSRhdGPwI0LmldBOwmaal8JrMecCNwE7CepLUkLUlqBL+oL+s2M7O+a3dm8QngBdItPq4DboiI0hfzJZ0LdAErS3oUOALokrQR6TLUDODTABFxp6TzSQ3X84ADIuLVvJwDgSuAYcAZEXFnL96fmZn1g3ZtFm+TtCKpraILmCRpBHAbqaH7zHYLjojdWxSf3mb6o4GjW5RfBlzWbl1mZlattm0W+Wl4l0j6A+lZ3FuQzgb2BdomCzMzW3S0u5HgTqSzis1JP5a7k9R99hDSZSkzMxsi2p1Z7E1KDl8GpkbEvwckIjMzq512bRYfHchAzMysvso8/MjMzIY4JwszM+uo3Y/yrs5/vzNw4ZiZWR21a+AeLem/gZ3y3V5VrPRjVc3Mho52yeLrwNdI92M6vqnOj1U1MxtC2vWGugC4QNLXIuKoAYzJzMxqpsxdZ4/KP9DbIhdNiYhL2s1jZmaLlo69oSQdAxxEusnfXcBBkr5ddWBmZlYfHc8sgB2AjSJiPoCks4G/A4dXGZiZmdVH2d9ZjCwML9/TRGZmtmgqc2ZxDPB3SdeQus9uAUyqNCozM6uVMg3c50qaArwrFx0aEU9UGpWZmdVKmTMLIuJx/DhTM7Mhy/eGMjOzjpwszMyso7bJQtIwSfcMVDBmZlZPbZNFRLwK3CtpjQGKx8zMaqhMA/cKwJ2SbgReaBRGxE6VRWVmZrVSJll8rfIozMys1sr8zuJPktYE1ouIqyQtCwyrPjQzM6uLMjcS/BRwAfCTXLQa8PsS850h6SlJdxTKVpR0paT78t8VcrkknShpuqTbJW1cmGdCnv4+SRN6+f7MzKwflOk6ewCwOfA8QETcB7y5xHxnAds2lU0Cro6I9YCrWXDbkO2A9fJrP+AUSMkFOAJ4N7ApcEQjwZiZ2cApkyxejoh/N0YkLU56Ul5bEXEt8GxT8c7A2Xn4bODDhfJzIrkeGClpNLANcGVEPBsRs4AreX0CMjOziimi/XFf0neB54BPAJ8DPgvcFRFf6bhwaSxwSUS8I48/FxEj87CAWRExUtIlwLER8ZdcdzVwKNAFLB0R38rlXwP+FRHfb7Gu/UhnJYwaNWqTyZMnt4xp7ty5jBgxom3c02bO7vTWKrHBat1v6Fsm1rpwrNVwrNVwrK2NHz9+akSMa1VXpjfUJGAiMA34NHAZ8LOFDSoiQlLHM5ReLO804DSAcePGRVdXV8vppkyZQk91DXtPurS/wuqVGXt0dRsvE2tdONZqONZqONbeK9Mban5+4NENpMtP90an05GePSlpdEQ8ni8zPZXLZwJjCtOtnstmks4uiuVT+rhuMzProzK9oXYA7gdOBE4Cpkvaro/ruwho9GiaAFxYKP9E7hW1GTA73+n2CmBrSSvkhu2tc5mZmQ2gMpehjgPGR8R0AEnrAJcCl7ebSdK5pLOClSU9SurVdCxwvqSJwEPAx/LklwHbA9OBF4F9ACLiWUlHATfl6b4ZEc2N5mZmVrEyyWJOI1FkDwBzOs0UEbv3ULVVi2mD1EW31XLOAM4oEaeZmVWkx2Qh6aN58GZJlwHnk9osdmXBN30zMxsC2p1ZfKgw/CTw/jz8T2CZyiIyM7Pa6TFZRMQ+AxmImZnVV8c2C0lrkX6MN7Y4vW9RbmY2dJRp4P49cDpwMTC/0mjMzKyWyiSLlyLixMojMTOz2iqTLH4o6Qjgj8DLjcKIuKWyqMzMrFbKJIsNgL2ALVlwGSryuJmZDQFlksWuwNrF25SbmdnQUuZ5FncAIyuOw8zMaqzMmcVI4B5JN9G9zcJdZ83MhogyyeKIyqMwM7NaK/M8iz8NRCBmZlZfZX7BPYcFz9xeElgCeCEi3lRlYGZmVh9lziyWawzn52bvDGxWZVBmZlYvZXpDvSaS3wPbVBOOmZnVUZnLUB8tjC4GjANeqiwiMzOrnTK9oYrPtZgHzCBdijIzsyGiTJuFn2thZjbEtXus6tfbzBcRcVQF8ZiZWQ21O7N4oUXZcGAisBLgZGFmNkS0e6zqcY1hScsBBwH7AJOB43qaz8zMFj1t2ywkrQh8EdgDOBvYOCJmDURgZmZWHz3+zkLS94CbgDnABhFxZH8lCkkzJE2TdKukm3PZipKulHRf/rtCLpekEyVNl3S7pI37IwYzMyuv3Y/yDgFWBb4KPCbp+fyaI+n5flj3+IjYKCLG5fFJwNURsR5wdR4H2A5YL7/2A07ph3WbmVkvtGuz6NWvu/vBzkBXHj4bmAIcmsvPiYgArpc0UtLoiHh8gOMzMxuylI7BA7xS6UFgFukGhT+JiNMkPRcRI3O9gFkRMVLSJcCxEfGXXHc1cGhE3Ny0zP1IZx6MGjVqk8mTJ7dc99y5cxkxYkTb+KbNnL0wb6/PNlht+W7jZWKtC8daDcdaDcfa2vjx46cWrvZ0U+YX3FV4b0TMlPRm4EpJ9xQrIyIk9SqLRcRpwGkA48aNi66urpbTTZkyhZ7qGvaedGlvVt1vZuzR1W28TKx14Vir4Vir4Vh7b6AvNQEQETPz36eA3wGbAk9KGg2Q/z6VJ58JjCnMvnouMzOzATLgyULS8Py7DSQNB7YmPef7ImBCnmwCcGEevgj4RO4VtRkw2+0VZmYDazAuQ40CfpeaJVgc+FVE/CE/4/t8SROBh4CP5ekvA7YHpgMvkn4YaGZmA2jAk0VEPABs2KL8GWCrFuUBHDAAoQ26sU1tJYdsMG/A2k9mHLvDgKzHzN6YBqXNwszM3licLMzMrCMnCzMz68jJwszMOnKyMDOzjpwszMysIycLMzPryMnCzMw6crIwM7OOnCzMzKwjJwszM+vIycLMzDpysjAzs46cLMzMrCMnCzMz68jJwszMOnKyMDOzjgbjsapWQ81P6eutvj7Vz0/oM3tj8JmFmZl15GRhZmYdOVmYmVlHThZmZtaRk4WZmXXkZGFmZh29YbrOStoW+CEwDPhZRBw7yCFZP1jYLrt90dduvv3F3YXtjegNcWYhaRhwMrAdsD6wu6T1BzcqM7Oh441yZrEpMD0iHgCQNBnYGbhrUKMy64PenE0N9llQb7SL1WdTb3yKiMGOoSNJuwDbRsQn8/hewLsj4sDCNPsB++XRtwL39rC4lYGnKwy3PznWajjWajjWagxkrGtGxCqtKt4oZxYdRcRpwGmdppN0c0SMG4CQFppjrYZjrYZjrUZdYn1DtFkAM4ExhfHVc5mZmQ2AN0qyuAlYT9JakpYEdgMuGuSYzMyGjDfEZaiImCfpQOAKUtfZMyLizj4uruOlqhpxrNVwrNVwrNWoRaxviAZuMzMbXG+Uy1BmZjaInCzMzKyjIZMsJG0r6V5J0yVNqkE8YyRdI+kuSXdKOiiXryjpSkn35b8r5HJJOjHHf7ukjQch5mGS/i7pkjy+lqQbckzn5c4HSFoqj0/P9WMHOM6Rki6QdI+kuyW9p67bVdIX8v//DknnSlq6LttV0hmSnpJ0R6Gs19tR0oQ8/X2SJgxgrN/L+8Dtkn4naWSh7rAc672StimUV36caBVroe4QSSFp5Tw+qNu1m4hY5F+kRvH7gbWBJYHbgPUHOabRwMZ5eDngH6RbmXwXmJTLJwHfycPbA5cDAjYDbhiEmL8I/Aq4JI+fD+yWh08FPpOHPwucmod3A84b4DjPBj6Zh5cERtZxuwKrAQ8CyxS259512a7AFsDGwB2Fsl5tR2BF4IH8d4U8vMIAxbo1sHge/k4h1vXzMWApYK18bBg2UMeJVrHm8jGkTjwPASvXYbt2i6/qD0QdXsB7gCsK44cBhw12XE0xXgh8kPTL89G5bDRwbx7+CbB7YfrXphug+FYHrga2BC7JO+/ThQ/ja9s47/DvycOL5+k0QHEunw/Aaiqv3XYlJYtH8gd+8bxdt6nTdgXGNh2Ae7Udgd2BnxTKu01XZaxNdR8BfpmHu33+G9t1II8TrWIFLgA2BGawIFkM+nZtvIbKZajGh7Lh0VxWC/lywjuBG4BREfF4rnoCGJWHB/s9/AD4MjA/j68EPBcR81rE81qsuX52nn4grAX8EzgzXzL7maTh1HC7RsRM4PvAw8DjpO00lXpu14bebsfB3m8b9iV9Q4caxippZ2BmRNzWVFWbWIdKsqgtSSOA3wAHR8TzxbpIXxkGvW+zpB2BpyJi6mDHUsLipFP8UyLincALpMslr6nRdl2BdEPMtYBVgeHAtoMaVC/UZTt2IukrwDzgl4MdSyuSlgUOB74+2LG0M1SSRS1vFyJpCVKi+GVE/DYXPylpdK4fDTyVywfzPWwO7CRpBjCZdCnqh8BISY0fdhbjeS3WXL888MwAxfoo8GhE3JDHLyAljzpu1w8AD0bEPyPiFeC3pG1dx+3a0NvtOKifPUl7AzsCe+TkRpuYBivWdUhfGG7Ln7HVgVsk/UedYh0qyaJ2twuRJOB04O6IOL5QdRHQ6NkwgdSW0Sj/RO4dsRkwu3A5oFIRcVhErB4RY0nb7v8iYg/gGmCXHmJtvIdd8vQD8g00Ip4AHpH01ly0FelW9rXbrqTLT5tJWjbvD41Ya7ddC3q7Ha8Atpa0Qj6T2jqXVU7pgWlfBnaKiBeb3sNuuXfZWsB6wI0M0nEiIqZFxJsjYmz+jD1K6vzyBHXarlU2iNTpRepV8A9Sb4ev1CCe95JO4W8Hbs2v7UnXoK8G7gOuAlbM04v0AKj7gWnAuEGKu4sFvaHWJn3IpgO/BpbK5Uvn8em5fu0BjnEj4Oa8bX9P6i1Sy+0KfAO4B7gD+Dmph04ttitwLqkt5RXSAWxiX7Yjqb1gen7tM4CxTidd1298vk4tTP+VHOu9wHaF8sqPE61ibaqfwYIG7kHdrsWXb/dhZmYdDZXLUGZmthCcLMzMrCMnCzMz68jJwszMOnKyMDOzjpws7A0v36XzuML4lyQd2U/LPkvSLp2nXOj17Kp0h9xrmsrHSvqfEvPvLemk6iK0oc7JwhYFLwMfbdzWuS4Kv8IuYyLwqYgY31Q+FuiYLMyq5mRhi4J5pOcUf6G5ovnMQNLc/LdL0p8kXSjpAUnHStpD0o2Spklap7CYD0i6WdI/8n2yGs/2+J6km/JzBj5dWO6fJV1E+jV2czy75+XfIek7uezrpB9pni7pe02zHAu8T9KtSs++WFrSmXkZf5fUnFyQtIOkv0laWdLWefgWSb/O9yJD0gxJ38jl0yS9LZe/P6/r1rz85cr/G2xR5mRhi4qTgT0kLd+LeTYE9gf+E9gLeEtEbAr8DPhcYbqxwKbADsCpkpYmnQnMjoh3Ae8CPpVvHQHpXlQHRcRbiiuTtCrpuQpbkn5l/i5JH46Ib5J+cb5HRPxvU4yTgD9HxEYRcQJwAOkefhuQblN9do6nsY6P5Hm2z0VfBT4QERvndXyxsOync/kpwJdy2ZeAAyJiI+B9wL/ab0IbKpwsbJEQ6Y695wCf78VsN0XE4xHxMul2Cn/M5dNICaLh/IiYHxH3kR4y8zbSvXg+IelW0q3lVyLdYwjgxoh4sMX63gVMiXTjwMZdULfoRbyQzkB+ARAR95AelNNISlsChwI7RMQs0sNy1gf+muOcAKxZWFbj5pVTC+/3r8Dxkj4PjIwFt0q3Ic7JwhYlPyB94x9eKJtH3s8lLUZ6AlrDy4Xh+YXx+aRbnTc03xMnSPfs+Vz+xr9RRKwVEY1k88LCvImFcD/pqYuN5CHgykKM60fExML0jff7Kvn9RsSxwCeBZUhJ5m0DE7rVnZOFLTIi4lnSI0mLB8QZwCZ5eCdgiT4seldJi+V2jLVJN5+7AviM0m3mkfQWpYcstXMj8P7cljCMdBnpTx3mmUNKAA1/BvZorBNYI8cD6Szj/wHnSHo7cD2wuaR18/TD8zw9krROpLugfod0F1YnCwOcLGzRcxxQ7BX1U9IB+jbSYzP78q3/YdKB/nJg/4h4idSucRfpuQN3kB5r2bb3U6RbS08i3YL8NmBqRFzYbh7SnXNflXSbpC8APwYWkzQNOA/YO19Ga6zjHlIy+TXwJtIzvc+VdDvwNzof/A/Oje+3k+6KenmH6W2I8F1nzcysI59ZmJlZR04WZmbWkZOFmZl15GRhZmYdOVmYmVlHThZmZtaRk4WZmXX0/wFZfduL32Si2AAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import pandas as pd\n", + "from matplotlib import pyplot as plt\n", + "\n", + "df = pd.read_csv('olympics-data/olympics_sections.csv')\n", + "df[['tokens']].hist()\n", + "# add axis descriptions and title\n", + "plt.xlabel('Number of tokens')\n", + "plt.ylabel('Number of Wikipedia sections')\n", + "plt.title('Distribution of number of tokens in Wikipedia sections')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the majority of section are fairly short (less than 500 tokens)." + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/finetuning/olympics-2-create-qa.ipynb b/examples/finetuning/olympics-2-create-qa.ipynb new file mode 100644 index 0000000000..9834cec85b --- /dev/null +++ b/examples/finetuning/olympics-2-create-qa.ipynb @@ -0,0 +1,751 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 2. Creating a synthetic Q&A dataset\n", + "We use [`davinci-instruct-beta-v2`](https://beta.openai.com/docs/engines/instruct-series-beta), a model specialized in following instructions, to create questions based on the given context. Then we also use [`davinci-instruct-beta-v2`](https://beta.openai.com/docs/engines/instruct-series-beta) to answer those questions, given the same context. \n", + "\n", + "This is expensive, and will also take a long time, as we call the davinci engine for each section. You can simply download the final dataset instead.\n", + "\n", + "We're using the dataset created using the [previous notebook](olympics-1-collect-data.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.1 Read in the data, and create a context\n", + "Create a context by concatenating the title, the heading and the content of that section" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
titleheadingcontenttokenscontext
02020 Summer OlympicsSummaryThe 2020 Summer Olympics (Japanese: 2020年夏季オリン...7132020 Summer Olympics\\nSummary\\n\\nThe 2020 Summ...
12020 Summer OlympicsHost city selectionThe International Olympic Committee (IOC) vote...1262020 Summer Olympics\\nHost city selection\\n\\nT...
22020 Summer OlympicsImpact of the COVID-19 pandemicIn January 2020, concerns were raised about th...3692020 Summer Olympics\\nImpact of the COVID-19 p...
32020 Summer OlympicsQualifying event cancellation and postponementConcerns about the pandemic began to affect qu...2982020 Summer Olympics\\nQualifying event cancell...
42020 Summer OlympicsEffect on doping testsMandatory doping tests were being severely res...1632020 Summer Olympics\\nEffect on doping tests\\n...
\n", + "
" + ], + "text/plain": [ + " title heading \\\n", + "0 2020 Summer Olympics Summary \n", + "1 2020 Summer Olympics Host city selection \n", + "2 2020 Summer Olympics Impact of the COVID-19 pandemic \n", + "3 2020 Summer Olympics Qualifying event cancellation and postponement \n", + "4 2020 Summer Olympics Effect on doping tests \n", + "\n", + " content tokens \\\n", + "0 The 2020 Summer Olympics (Japanese: 2020年夏季オリン... 713 \n", + "1 The International Olympic Committee (IOC) vote... 126 \n", + "2 In January 2020, concerns were raised about th... 369 \n", + "3 Concerns about the pandemic began to affect qu... 298 \n", + "4 Mandatory doping tests were being severely res... 163 \n", + "\n", + " context \n", + "0 2020 Summer Olympics\\nSummary\\n\\nThe 2020 Summ... \n", + "1 2020 Summer Olympics\\nHost city selection\\n\\nT... \n", + "2 2020 Summer Olympics\\nImpact of the COVID-19 p... \n", + "3 2020 Summer Olympics\\nQualifying event cancell... \n", + "4 2020 Summer Olympics\\nEffect on doping tests\\n... " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "df = pd.read_csv('olympics-data/olympics_sections.csv')\n", + "df['context'] = df.title + \"\\n\" + df.heading + \"\\n\\n\" + df.content\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.2 Create questions based on the context\n", + "Use davinci-instruct to generate a number of plausible questions relating to the Wikipedia section contents.\n", + "\n", + "Note: We have used temperature=0, but it may be beneficial to experiment with a higher temperature to get a higher diversity of questions.\n", + "\n", + "**WARNING: This step will last a long time, and consume a lot of tokens, as it calls davinci-instruct for every section to generate a number of questions.**" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. What is the 2020 Summer Olympics?\n", + "2. When did the 2020 Summer Olympics take place?\n", + "3. Who won the most medals at the 2020 Summer Olympics?\n", + "4. Who won the most gold medals at the 2020 Summer Olympics?\n", + "5. Who won the most medals at the 2020 Summer Olympics?\n" + ] + } + ], + "source": [ + "import openai\n", + "\n", + "def get_questions(context):\n", + " try:\n", + " response = openai.Completion.create(\n", + " engine=\"davinci-instruct-beta-v2\",\n", + " prompt=f\"Write questions based on the text below\\n\\nText: {context}\\n\\nQuestions:\\n1.\",\n", + " temperature=0,\n", + " max_tokens=257,\n", + " top_p=1,\n", + " frequency_penalty=0,\n", + " presence_penalty=0,\n", + " stop=[\"\\n\\n\"]\n", + " )\n", + " return response['choices'][0]['text']\n", + " except:\n", + " return \"\"\n", + "\n", + "\n", + "df['questions']= df.context.apply(get_questions)\n", + "df['questions'] = \"1.\" + df.questions\n", + "print(df[['questions']].values[0][0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The prompt is designed to generate a number of questions. Example questions above were generated based on the summary section of the 2020 Summer Olympics page.\n", + "\n", + "We can observe that the questions 3 and 5 above repeat. Sometimes the generated questions could be ambiguous without the context. We will show that even despite these limitations we can create a successful model." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The 2020 Summer Olympics (Japanese: 2020年夏季オリンピック, Hepburn: Nisen Nijū-nen Kaki Orinpikku), officially the Games of the XXXII Olympiad (第三十二回オリンピック競技大会, Dai Sanjūni-kai Orinpikku Kyōgi Taikai) and branded as Tokyo 2020 (東京2020, Tōkyō Nii Zero Nii Zero), was an international multi-sport event held from 23 July to 8 August 2021 in Tokyo, Japan, with some preliminary events that began on 21 July.\n", + "Tokyo was selected as the host city during the 125th IOC Session in Buenos Aires, Argentina, on 7 September 2013. Originally scheduled to take place from 24 July to 9 August 2020, the event was postponed to 2021 in March 2020 as a result of the COVID-19 pandemic, the first such instance in the history of the Olympic Games (previous games had been cancelled but not rescheduled). However, the event retained the Tokyo 2020 name for marketing and branding purposes. It was largely held behind closed doors with no public spectators permitted due to the declaration of a state of emergency in the Greater Tokyo Area in response to the pandemic. The Summer Paralympics were held between 24 August and 5 September 2021, 16 days after the completion of the Olympics.The 2020 Games were the fourth Olympic Games to be held in Japan, following the Tokyo 1964 (Summer), Sapporo 1972 (Winter) and Nagano 1998 (Winter) games. Tokyo is the first city in Asia to hold the Summer Games twice. The 2020 Games were the second of three consecutive Olympics to be held in East Asia, following the 2018 Winter Olympics in Pyeongchang, South Korea and preceding the 2022 Winter Olympics in Beijing, China.\n", + "New events were introduced in existing sports for 2020, including 3x3 basketball, freestyle BMX and mixed gender team events in a number of existing sports, as well as the return of madison cycling for men and an introduction of the same event for women. New IOC policies also allowed the host organizing committee to add new sports to the Olympic program for just one Games. The disciplines added by the Japanese Olympic Committee were baseball and softball, karate, sport climbing, surfing and skateboarding, the last four of which made their Olympic debuts, and the last three of which will remain on the Olympic program.The United States topped the medal count by both total golds (39) and total medals (113), with China finishing second by both respects (38 and 88). Host nation Japan finished third, setting a record for the most gold medals and total medals ever won by their delegation at an Olympic Games with 27 and 58. Great Britain finished fourth, with a total of 22 gold and 65 medals, becoming the first nation at the Summer Olympics to increase or equal their total medals won in the two Games subsequent to hosting them. The Russian delegation competing as the ROC (not to be confused with the Republic of China (Taiwan) which competed as Chinese Taipei, not ROC) finished fifth with 20 gold medals and third in the overall medal count, with 71 medals. Bermuda, the Philippines and Qatar won their first-ever Olympic gold medals. Burkina Faso, San Marino and Turkmenistan won their first-ever Olympic medals.\n" + ] + } + ], + "source": [ + "print(df.content.values[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.3 Create answers based on the context\n", + "Use davinci-instruct to answer the questions given the relevant Wikipedia section contents\n", + "\n", + "Note: We have used temperature=0, but it may be beneficial to experiment with a higher temperature to get a higher diversity of questions.\n", + "\n", + "**WARNING: This step will last a long time, and consume a lot of tokens, as it calls davinci-instruct for every section to answer all the questions.**" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. The 2020 Summer Olympics is an international multi-sport event held from 23 July to 8 August 2021 in Tokyo, Japan.\n", + "2. The 2020 Summer Olympics took place from 23 July to 8 August 2021.\n", + "3. The United States topped the medal count by both total golds (39) and total medals (113), with China finishing second by both respects (38 and 88).\n", + "4. The United States topped the medal count by both total golds (39) and total medals (113), with China finishing second by both respects (38 and 88).\n", + "5. The United States topped the medal count by both total golds (39) and total medals (113), with China finishing second by both respects (38 and 88).\n" + ] + } + ], + "source": [ + "def get_answers(row):\n", + " try:\n", + " response = openai.Completion.create(\n", + " engine=\"davinci-instruct-beta-v2\",\n", + " prompt=f\"Write questions based on the text below\\n\\nText: {row.context}\\n\\nQuestions:\\n{row.questions}\\n\\nAnswers:\\n1.\",\n", + " temperature=0,\n", + " max_tokens=257,\n", + " top_p=1,\n", + " frequency_penalty=0,\n", + " presence_penalty=0\n", + " )\n", + " return response['choices'][0]['text']\n", + " except Exception as e:\n", + " print (e)\n", + " return \"\"\n", + "\n", + "\n", + "df['answers']= df.apply(get_answers, axis=1)\n", + "df['answers'] = \"1.\" + df.answers\n", + "df = df.dropna().reset_index().drop('index',axis=1)\n", + "print(df[['answers']].values[0][0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These are the answers to the questions above based on the context around the host city selection. \n", + "\n", + "We can see that answers 3-5 contain the correct answer, but instead of answering the question directly, the answer is a verbatim extraction. Despite these occasional lower quality answers, we will show that the model can learn the task reasonably well, given a high number of examples." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.4 Save the Olympics Q&A dataset based on Wikipedia sections\n", + "We save the file for use in the [next notebook](olympics-3-train-qa.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "df.to_csv('olympics-data/olympics_qa.csv', index=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.5 Search file\n", + "We create a search file ([API reference](https://beta.openai.com/docs/api-reference/files/list)), which can be used to retrieve the relevant context when a question is asked.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "df = df[df.tokens<2000]\n", + "df[['context', 'tokens']].rename(columns={'context':'text','tokens':'metadata'}).to_json('olympics-data/olympics_search.jsonl', orient='records', lines=True)\n", + "\n", + "search_file = openai.File.create(\n", + " file=open(\"olympics-data/olympics_search.jsonl\"),\n", + " purpose='search'\n", + ")\n", + "olympics_search_fileid = search_file['id']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.6 Answer questions based on the context provided\n", + "\n", + "We will use a simple implementation of the answers endpoint. This works by simply using the [/search endpoint](https://beta.openai.com/docs/api-reference/searches), which searches over an indexed file to obtain the relevant sections which can be included in the context, following by a question and answering prompt given a specified model." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Athletics at the 2020 Summer Olympics – Women's 4 × 100 metres relay\n", + "Summary\n", + "\n", + "The women's 4 × 100 metres relay event at the 2020 Summer Olympics took place on 5 and 6 August 2021 at the Japan National Stadium. There were 16 competing relay teams, with each team having 5 members from which 4 were selected in each round.\n", + "\n", + "###\n", + "\n", + "Athletics at the 2020 Summer Olympics – Men's 4 × 100 metres relay\n", + "Qualification\n", + "\n", + "National Olympic Committees (NOCs) could qualify one relay team in one of three following ways:\n", + "The top 8 NOCs at the 2019 World Athletics Championships qualified a relay team.\n", + "The top 8 NOCs at the 2021 World Athletics Relays qualified a relay team.\n", + "Where an NOC placed in the top 8 at both the 2019 World Championships and the 2021 World Relays, the quota place was allocated to the world top list as of 29 June 2021. In this case, 4 teams did so, so there are 4 places available through the world rankings.A total of five athletes may be entered for a relay team. Should a NOC have also entered individual athletes in the corresponding individual event (100 m), the entered individual athletes must be included in the total of five (5) athletes entered for the relay event. In addition of five, NOCs can nominate a maximum of one alternate athlete for each team.\n", + "The qualifying period was originally from 1 May 2019 to 29 June 2020. Due to the COVID-19 pandemic, the period was suspended from 6 April 2020 to 30 November 2020, with the end date extended to 29 June 2021. The qualifying time standards could be obtained in various meets during the given period that have the approval of the IAAF. Both indoor and outdoor meets are eligible. The most recent Area Championships may be counted in the ranking, even if not during the qualifying period.\n" + ] + } + ], + "source": [ + "from answers_with_ft import create_context, answer_question\n", + "print(create_context(\"Where did women's 4 x 100 metres relay event take place during the 2020 Summer Olympics?\", olympics_search_fileid, max_len=400))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "' Japan National Stadium'" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "answer_question(olympics_search_fileid, \"davinci-instruct-beta-v2\", \n", + " \"Where did women's 4 x 100 metres relay event take place during the 2020 Summer Olympics?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After we fine-tune the model for Q&A we'll be able to use it instead of [`davinci-instruct-beta-v2`](https://beta.openai.com/docs/engines/instruct-series-beta), to obtain better answers when the question can't be answered based on the context. We see a downside of [`davinci-instruct-beta-v2`](https://beta.openai.com/docs/engines/instruct-series-beta), which always attempts to answer the question, regardless of the relevant context being present or not. (Note the second question is asking about a future event, set in 2024.)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "' Japan National Stadium'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "answer_question(olympics_search_fileid, \"davinci-instruct-beta-v2\", \n", + " \"Where did women's 4 x 100 metres relay event take place during the 2048 Summer Olympics?\", max_len=1000)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that davinci has a tendency to answer the question, even if the question can't be answered given the context provided. Note the question asked regarding 2048 Summer Olympics, which didn't happen yet, and the retrieved content has only returned results for 2020." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.7 (Optional) Investigation into how likely the search endpoint is to return the relevant context" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, 58)\n" + ] + } + ], + "source": [ + "def check_context(title, heading, question, max_len=1800, search_model='ada', max_rerank=10):\n", + " \"\"\"\n", + " Evaluate the performance of the search model in retrieving the correct context\n", + "\n", + " Parameters\n", + " ----------\n", + " title: str\n", + " The title of the Wikipedia page\n", + " heading: str\n", + " The heading of the Wikipedia section\n", + " qusetion: str\n", + " The question\n", + " max_len: int\n", + " The maximum length of the context\n", + " search_model: str\n", + " The search model to use - `ada` is most cost effective\n", + " max_rerank: int\n", + " The maximum number of reranking documents to use the search model on\n", + "\n", + " Returns\n", + " -------\n", + " rank: int\n", + " The rank of the correct context\n", + " token_length: int\n", + " The number of tokens needed to obtain the correct context\n", + " \"\"\"\n", + " \n", + " try:\n", + " results = openai.Engine(search_model).search(\n", + " search_model=search_model, \n", + " query=question, \n", + " max_rerank=max_rerank,\n", + " file=olympics_search_fileid,\n", + " return_metadata=True\n", + " )\n", + " index=-1\n", + " returns = []\n", + " cur_len = 0\n", + " for result in results['data']:\n", + " cur_len += int(result['metadata']) + 4 # we add 4 tokens for the separator `\\n\\n###\\n\\n`\n", + " if cur_len > max_len:\n", + " break\n", + " returns.append(result['text'])\n", + " res = result['text'].split('\\n')\n", + " if res[0] == title and res[1] == heading:\n", + " index = len(returns) - 1\n", + " break\n", + " return index, cur_len\n", + " except Exception as e:\n", + " #print (e)\n", + " return []\n", + "print(check_context(\"Athletics at the 2020 Summer Olympics – Women's 4 × 100 metres relay\", \"Summary\", \"Where did women's 4 x 100 metres relay event take place during the 2020 Summer Olympics?\", max_len=10000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We utilize the generated questions based on context to estimate how often we can retrieve the original context. These questions are noisy, so this is not a perfect estimate.\n", + "\n", + "Our questions and answers are prefixed with numbered bullet points, however due to the way they were generated, they are missing the first number, hence we add \"1.\" to the list of questions (and answers).\n", + "\n", + "We calculate the rank of the section retrieved using ada search, and the number of tokens in the context needed to retrieve the relevant section in full." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 [(132, 27104), (-1, 22939), (8, 2151), (2, 121...\n", + "1 [(4, 1737), (0, 130), (8, 744), (96, 17208), (...\n", + "2 [(0, 373), (0, 373), (-1, 40610), (1, 570)]\n", + "3 [(0, 302), (0, 302), (5, 968), (8, 1425)]\n", + "4 [(0, 167), (0, 167), (2, 1442)]\n", + "Name: ada, dtype: object" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ada_results = df.apply(lambda x: [\n", + " check_context( x.title, \n", + " x.heading, \n", + " q[3:], # remove the number prefix\n", + " max_len=1000000, # set a large number to get the full context \n", + " search_model='ada', \n", + " max_rerank=200,\n", + " ) \n", + " for q in (x.questions).split('\\n') # split the questions\n", + " if len(q) >10 # remove the empty questions\n", + " ], axis=1)\n", + "ada_results.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "out = pd.concat([ada_results], axis=1)\n", + "out.columns = ['ada']\n", + "out.to_csv('olympics-data/search_engine_results.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "def expand_lists(out):\n", + " \"\"\"\n", + " Expand a pandas series containing lists into a series, where each list element becomes a value on its own\n", + "\n", + " Input is a row per paragraph, which has multiple questions\n", + " Output is a row per question\n", + " \"\"\"\n", + " cols = [pd.DataFrame(out[name].tolist()).stack().reset_index(level=1, drop=True).rename(name) for name in out.columns] \n", + " return pd.concat(cols, axis=1)\n", + "\n", + "out_expanded = expand_lists(out)\n", + "out_expanded['rank'] = out_expanded.ada.apply(lambda x: x[0] if x != [] else -2)\n", + "out_expanded['tokens'] = out_expanded.ada.apply(lambda x: x[1] if x != [] else -2)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "74.3% of relevant paragraphs are retrieved within the first 2k tokens\n" + ] + } + ], + "source": [ + "within_2k = (out_expanded.tokens < 2000).mean()\n", + "print(f\"{within_2k*100:.1f}% of relevant paragraphs are retrieved within the first 2k tokens\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The relevant context can be obtained 74% of the time on this dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7.4% of relevant paragraphs are not retrieved within the first 200 results\n" + ] + } + ], + "source": [ + "outside_200 = (out_expanded['rank'] == -1).mean()\n", + "print(f\"{outside_200*100:.1f}% of relevant paragraphs are not retrieved within the first 200 results\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "7.4% of the time, this is due to the keyword search part of the search algorithm not retrieving the relevant context within the first 200 results.\n", + "18.3% of the time this is due to the semantic search not placing the relevant context within the first 2000 tokens." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# plot a histogram, and add axis descriptions and title\n", + "out_expanded[(out_expanded['rank'] >=0)&(out_expanded['rank'] <30)]['rank'].hist(bins=29)\n", + "plt.xlabel('rank')\n", + "plt.ylabel('count')\n", + "plt.title('Histogram of ranks of retrieved paragraphs')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "out_expanded[(out_expanded.tokens>=0)&(out_expanded.tokens < 2000)]['tokens'].hist(bins=29)\n", + "plt.xlabel('tokens')\n", + "plt.ylabel('count')\n", + "plt.title('Histogram of the number of minimum tokens needed')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can observe that the context is most likely to be returned as one of the first results, and most likely to be returned within the first 200-500 tokens." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-2 0.000063\n", + "-1 0.074428\n", + " 0 0.453420\n", + " 1 0.089515\n", + " 2 0.047146\n", + " 3 0.032437\n", + " 4 0.024139\n", + " 5 0.019676\n", + " 6 0.015967\n", + " 7 0.013452\n", + " 8 0.011189\n", + " 9 0.009869\n", + " 10 0.009178\n", + "Name: rank, dtype: float64" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# normalized value_counts\n", + "out_expanded['rank'].value_counts(normalize=True).sort_index()[:13]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "probabilities of the relevant context being returned at each rank. (-2 means a processing error, -1 means the rank is >200)" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/finetuning/olympics-3-train-qa.ipynb b/examples/finetuning/olympics-3-train-qa.ipynb new file mode 100644 index 0000000000..ebf89a5c9c --- /dev/null +++ b/examples/finetuning/olympics-3-train-qa.ipynb @@ -0,0 +1,637 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3. Train a fine-tuning model specialized for Q&A\n", + "This notebook will utilize the dataset of context, question and answer pairs to additionally create adversarial questions and context pairs, where the question was not generated on that context. In those cases the model will be prompted to answer \"No sufficient context for answering the question\". We will also train a discriminator model, which predicts whether the question can be answered based on the context or not.\n", + "\n", + "We will add hard adversarial examples as well, which will be based either on semantically similar sections, or neighbouring sections, originating from the same article." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
titleheadingcontenttokenscontextquestionsanswers
02020 Summer OlympicsSummaryThe 2020 Summer Olympics (Japanese: 2020年夏季オリン...7132020 Summer Olympics\\nSummary\\n\\nThe 2020 Summ...1. What is the 2020 Summer Olympics?\\n2. When ...1. The 2020 Summer Olympics is an internationa...
12020 Summer OlympicsHost city selectionThe International Olympic Committee (IOC) vote...1262020 Summer Olympics\\nHost city selection\\n\\nT...1. \\n2. \\n3. \\n4.1. What is the International Olympic Committee...
22020 Summer OlympicsImpact of the COVID-19 pandemicIn January 2020, concerns were raised about th...3692020 Summer Olympics\\nImpact of the COVID-19 p...1. What was the COVID-19 pandemic?\\n2. How did...1. The COVID-19 pandemic was a pandemic that o...
32020 Summer OlympicsQualifying event cancellation and postponementConcerns about the pandemic began to affect qu...2982020 Summer Olympics\\nQualifying event cancell...1. What was the original location of the Asia ...1. The original location of the Asia & Oceania...
42020 Summer OlympicsEffect on doping testsMandatory doping tests were being severely res...1632020 Summer Olympics\\nEffect on doping tests\\n...1. What was the COVID-19 pandemic?\\n2. What di...1. The COVID-19 pandemic was a pandemic that o...
\n", + "
" + ], + "text/plain": [ + " title heading \\\n", + "0 2020 Summer Olympics Summary \n", + "1 2020 Summer Olympics Host city selection \n", + "2 2020 Summer Olympics Impact of the COVID-19 pandemic \n", + "3 2020 Summer Olympics Qualifying event cancellation and postponement \n", + "4 2020 Summer Olympics Effect on doping tests \n", + "\n", + " content tokens \\\n", + "0 The 2020 Summer Olympics (Japanese: 2020年夏季オリン... 713 \n", + "1 The International Olympic Committee (IOC) vote... 126 \n", + "2 In January 2020, concerns were raised about th... 369 \n", + "3 Concerns about the pandemic began to affect qu... 298 \n", + "4 Mandatory doping tests were being severely res... 163 \n", + "\n", + " context \\\n", + "0 2020 Summer Olympics\\nSummary\\n\\nThe 2020 Summ... \n", + "1 2020 Summer Olympics\\nHost city selection\\n\\nT... \n", + "2 2020 Summer Olympics\\nImpact of the COVID-19 p... \n", + "3 2020 Summer Olympics\\nQualifying event cancell... \n", + "4 2020 Summer Olympics\\nEffect on doping tests\\n... \n", + "\n", + " questions \\\n", + "0 1. What is the 2020 Summer Olympics?\\n2. When ... \n", + "1 1. \\n2. \\n3. \\n4. \n", + "2 1. What was the COVID-19 pandemic?\\n2. How did... \n", + "3 1. What was the original location of the Asia ... \n", + "4 1. What was the COVID-19 pandemic?\\n2. What di... \n", + "\n", + " answers \n", + "0 1. The 2020 Summer Olympics is an internationa... \n", + "1 1. What is the International Olympic Committee... \n", + "2 1. The COVID-19 pandemic was a pandemic that o... \n", + "3 1. The original location of the Asia & Oceania... \n", + "4 1. The COVID-19 pandemic was a pandemic that o... " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import openai\n", + "import pandas as pd\n", + "df = pd.read_csv('olympics-data/olympics_qa.csv')\n", + "olympics_search_fileid = \"file-c3shd8wqF3vSCKaukW4Jr1TT\"\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Split the sections into a training and testing set" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(3014, 754)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "train_df, test_df = train_test_split(df, test_size=0.2, random_state=42)\n", + "len(train_df), len(test_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "we check that he separator we intend to use isn't present within the contexts" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.context.str.contains('->').sum()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3.1 Create the fine-tuning datasets for Q&A and discriminator models\n", + "The fine-tuning dataset is created in the following way. For every corresponding question, answer and context pair we create:\n", + "- Positive example: correct question, answer, context pair\n", + "- Negative examples:\n", + " - random negative example, where the random context is paired with the question \n", + " - two hard negative examples\n", + " - one originating from the same wikipedia article\n", + " - another, which is most similar to the correct context\n", + "\n", + "This process is noisy, as sometimes the question might be answerable given a different context, but on average we hope this won't affect the peformance too much.\n", + "\n", + "We apply the same process of dataset creation for both the discriminator, and the Q&A answering model. We apply the process separately for the training and testing set, to ensure that the examples from the traing set don't feature within the test set." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "def get_random_similar_contexts(question, context, file_id=olympics_search_fileid, search_model='ada', max_rerank=10):\n", + " \"\"\"\n", + " Find similar contexts to the given context using the search file\n", + " \"\"\"\n", + " try:\n", + " results = openai.Engine(search_model).search(\n", + " search_model=search_model, \n", + " query=question, \n", + " max_rerank=max_rerank,\n", + " file=file_id\n", + " )\n", + " candidates = []\n", + " for result in results['data'][:3]:\n", + " if result['text'] == context:\n", + " continue\n", + " candidates.append(result['text'])\n", + " random_candidate = random.choice(candidates)\n", + " return random_candidate\n", + " except Exception as e:\n", + " print(e)\n", + " return \"\"\n", + "\n", + "def create_fine_tuning_dataset(df, discriminator=False, n_negative=1, add_related=False):\n", + " \"\"\"\n", + " Create a dataset for fine tuning the OpenAI model; either for a discriminator model, \n", + " or a model specializing in Q&A, where it says if no relevant context is found.\n", + "\n", + " Parameters\n", + " ----------\n", + " df: pd.DataFrame\n", + " The dataframe containing the question, answer and context pairs\n", + " discriminator: bool\n", + " Whether to create a dataset for the discriminator\n", + " n_negative: int\n", + " The number of random negative samples to add (using a random context)\n", + " add_related: bool\n", + " Whether to add the related contexts to the correct context. These are hard negative examples\n", + "\n", + " Returns\n", + " -------\n", + " pd.DataFrame\n", + " The dataframe containing the prompts and completions, ready for fine-tuning\n", + " \"\"\"\n", + " rows = []\n", + " for i, row in df.iterrows():\n", + " for q, a in zip((\"1.\" + row.questions).split('\\n'), (\"1.\" + row.answers).split('\\n')):\n", + " if len(q) >10 and len(a) >10:\n", + " if discriminator:\n", + " rows.append({\"prompt\":f\"{row.context}\\nQuestion: {q[2:].strip()}\\n Related:\", \"completion\":f\" yes\"})\n", + " else:\n", + " rows.append({\"prompt\":f\"{row.context}\\nQuestion: {q[2:].strip()}\\nAnswer:\", \"completion\":f\" {a[2:].strip()}\"})\n", + "\n", + " for i, row in df.iterrows():\n", + " for q in (\"1.\" + row.questions).split('\\n'):\n", + " if len(q) >10:\n", + " for j in range(n_negative + (2 if add_related else 0)):\n", + " random_context = \"\"\n", + " if j == 0 and add_related:\n", + " # add the related contexts based on originating from the same wikipedia page\n", + " subset = df[(df.title == row.title) & (df.context != row.context)]\n", + " \n", + " if len(subset) < 1:\n", + " continue\n", + " random_context = subset.sample(1).iloc[0].context\n", + " if j == 1 and add_related:\n", + " # add the related contexts based on the most similar contexts according to the search\n", + " random_context = get_random_similar_contexts(q[2:].strip(), row.context, search_model='ada', max_rerank=10)\n", + " else:\n", + " while True:\n", + " # add random context, which isn't the correct context\n", + " random_context = df.sample(1).iloc[0].context\n", + " if random_context != row.context:\n", + " break\n", + " if discriminator:\n", + " rows.append({\"prompt\":f\"{random_context}\\nQuestion: {q[2:].strip()}\\n Related:\", \"completion\":f\" no\"})\n", + " else:\n", + " rows.append({\"prompt\":f\"{random_context}\\nQuestion: {q[2:].strip()}\\nAnswer:\", \"completion\":f\" No appropriate context found to answer the question.\"})\n", + "\n", + " return pd.DataFrame(rows) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We apply the same process of dataset creation for both the discriminator, and the Q&A answering model. We apply the process separately for the training and testing set, to ensure that the examples from the traing set don't feature within the test set." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for name, is_disc in [('discriminator', True), ('qa', False)]:\n", + " for train_test, dt in [('train', train_df), ('test', test_df)]:\n", + " ft = create_fine_tuning_dataset(dt, discriminator=is_disc, n_negative=1, add_related=True)\n", + " ft.to_json(f'{name}_{train_test}.jsonl', orient='records', lines=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We formatted the data according to the recommendations from the fine-tuning tool, which is available using\n", + "> openai tools fine_tunes.prepare_data -f qa_train.jsonl\n", + "\n", + "We highly recommend that you use this tool, which suggests improvements in your data formatting for fine-tuning.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3.2 Submit the datasets for fine-tuning" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "!openai api fine_tunes.create -t \"olympics-data/discriminator_train.jsonl\" -v \"olympics-data/discriminator_test.jsonl\" --no_packing --batch_size 16 --compute_classification_metrics --classification_positive_class \" yes\" --model ada" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "!openai api fine_tunes.create -t \"olympics-data/qa_train.jsonl\" -v \"olympics-data/qa_test.jsonl\" --no_packing --batch_size 16" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3.3 Using the fine-tuned models\n", + "\n", + "We will now use the fine-tuned discriminator and the fine-tuned Q&A model. By requesting logprobs, we can see how certain the discriminator is in a `yes` vs `no` answer." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[ JSON: {\n", + " \" no\": -10.819577,\n", + " \" yes\": -2.045765e-05\n", + " }]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ft_discriminator = \"curie:ft-openai-internal-2021-08-23-23-58-57\"\n", + "ft_qa = \"curie:ft-openai-internal-2021-08-23-17-54-10\"\n", + "\n", + "def apply_ft_discriminator(context, question, discriminator_model):\n", + " \"\"\"\n", + " Apply the fine tuned discriminator to a question, to assess whether it can be answered from the context.\n", + " \"\"\"\n", + " prompt = f\"{context}\\nQuestion: {question}\\n Related:\"\n", + " result = openai.Completion.create(model=discriminator_model, prompt=prompt, max_tokens=1, temperature=0, top_p=1, n=1, logprobs=2)\n", + " return result['choices'][0]['logprobs']['top_logprobs']\n", + "\n", + "apply_ft_discriminator('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.', \n", + " 'What was the first human-made object in space?', ft_discriminator)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the model can generalize well to different contexts and questions. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "' The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def apply_ft_qa_answer(context, question, answering_model):\n", + " \"\"\"\n", + " Apply the fine tuned discriminator to a question\n", + " \"\"\"\n", + " prompt = f\"{context}\\nQuestion: {question}\\nAnswer:\"\n", + " result = openai.Completion.create(model=answering_model, prompt=prompt, max_tokens=30, temperature=0, top_p=1, n=1, stop=['.','\\n'])\n", + " return result['choices'][0]['text']\n", + "\n", + "apply_ft_qa_answer('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.', \n", + " 'What was the first human-made object in space?', ft_qa)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the model can answer the question, when the context is appropriate." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "' The Soviet Union was the first country to successfully launch a satellite into space'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "apply_ft_qa_answer('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.',\n", + " 'What is impressive about the Soviet Union?', ft_qa)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "' No appropriate context found to answer the question'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "apply_ft_qa_answer('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.',\n", + " 'How many cars were produced in the Soviet Union in 1970?', ft_qa)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the model knows when to answer the question, and when to say that insufficient context is present to answer the question." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also combine a discriminator and a base model, or a fine-tuned Q&A model. Discriminator can essentially serve as a decision whether the question can be answered given the context or not." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "' Weather could cause a sport event to have no crowd'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def answer_question_conditionally(answering_model, discriminator_model, context, question, discriminator_logprob_yes_modifier=0):\n", + " logprobs = apply_ft_discriminator(context, question, discriminator_model)\n", + " yes_logprob = logprobs[' yes'] if ' yes' in logprobs else -100\n", + " no_logprob = logprobs[' no'] if ' no' in logprobs else -100\n", + " if yes_logprob + discriminator_logprob_yes_modifier < no_logprob:\n", + " return \" No appropriate context found to answer the question based on the discriminator.\"\n", + " return apply_ft_qa_answer(context, question, answering_model)\n", + "answer_question_conditionally(ft_qa, ft_discriminator, \n", + " \"Crowdless games are a rare although not unheard-of occurrence in sports. \\\n", + " When they do occur, it is usually the result of events beyond the control \\\n", + " of the teams or fans, such as weather-related concerns, public health concerns, \\\n", + " or wider civil disturbances unrelated to the game. For instance, \\\n", + " the COVID-19 pandemic caused many sports leagues around the world \\\n", + " to be played behind closed doors.\",\n", + " \"Could weather cause a sport event to have no crowd?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The above function illustrates how to potentially combine a discriminator and a fine-tuned Q&A model. This gives a more fine-grained control over how certain we want the model to be before it answers the question.\n", + "\n", + "We'll now take a look on how answers endpoint works - combining search to retrieve the relevant context from a knowledge base, and then using the fine-tuned Q&A model to answer the question." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3.4 Answering the question based on a knowledge base\n", + "Finally we can use a logic similar to the [/answers](https://beta.openai.com/docs/api-reference/answers) endpoint, where we first search for the relevant context, and then ask a Q&A model to answer the question given that context. If you'd like to see the implementation details, check out the [`answers_with_ft.py`](answers_with_ft.py) file." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\" Canada won the Women's football tournament at the 2020 Olympic games\"" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from answers_with_ft import answer_question\n", + "answer_question(olympics_search_fileid, ft_qa, \"Which country won the Women's football tournament at the 2020 Olympic games?\")" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" + }, + "kernelspec": { + "display_name": "Python 3.7.3 64-bit ('base': conda)", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/openai/validators.py b/openai/validators.py index 976bd6f714..ba4b92c2b0 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -654,7 +654,8 @@ def get_batch_size_suggestion(df, no_packing): batch_size = BATCH_SIZE_TO_N_EXAMPLES_RATIO * n_examples else: batch_size = BATCH_SIZE_TO_N_CHARACTERS_RATIO * n_characters - batch_size = 2 ** int(np.log2(batch_size)) + + batch_size = max(1, int(2 ** np.ceil(np.log2(batch_size)))) batch_size_suggestion = f" --batch_size {batch_size}" return batch_size_suggestion @@ -694,7 +695,7 @@ def write_out_file(df, fname, any_remediations, auto_accept): input_text = "\n\nYour data will be written to a new JSONL file. Proceed [Y/n]: " - if not any_remediations: + if not any_remediations and not split: sys.stdout.write( f'\nYou can use your file for fine-tuning:\n> openai api fine_tunes.create -t "{fname}"{additional_params}\n\nAfter you’ve fine-tuned a model, remember that your prompt has to end with the indicator string `{common_prompt_suffix_new_line_handled}` for the model to start generating completions, rather than continuing with the prompt.{optional_ending_string}\n' ) diff --git a/openai/version.py b/openai/version.py index 056f0f4ba2..b5ce99b561 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.11.0" +VERSION = "0.11.1" From f4f6f90cd6004beeab923dbe306fda305a7ebb0c Mon Sep 17 00:00:00 2001 From: Rachel Lim Date: Fri, 3 Dec 2021 16:31:43 -0800 Subject: [PATCH 003/914] [fine-tuning] accept file URLs as train & validation files (#50) also a few fixes: setting the correct filename for file uploads using files.create, reinstating the progress meter for uploading files in conjunction with the fine-tuning endpoint, standardizing punctuation on FT help strings --- openai/api_resources/file.py | 30 ++++--- openai/cli.py | 151 ++++++++++++++++++++++++----------- openai/version.py | 2 +- 3 files changed, 118 insertions(+), 65 deletions(-) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index f79242bfbf..dbe387b157 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -20,6 +20,7 @@ def create( api_base=None, api_version=None, organization=None, + user_provided_filename=None, ): if purpose != "search" and model is not None: raise ValueError("'model' is only meaningful if 'purpose' is 'search'") @@ -32,9 +33,13 @@ def create( url = cls.class_url() # Set the filename on 'purpose' and 'model' to None so they are # interpreted as form data. - files = [("file", file), ("purpose", (None, purpose))] + files = [("purpose", (None, purpose))] if model is not None: files.append(("model", (None, model))) + if user_provided_filename is not None: + files.append(("file", (user_provided_filename, file))) + else: + files.append(("file", file)) response, _, api_key = requestor.request("post", url, files=files) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -65,21 +70,15 @@ def download( @classmethod def find_matching_files( cls, + name, + bytes, + purpose, api_key=None, api_base=None, api_version=None, organization=None, - file=None, - purpose=None, ): - if file is None: - raise openai.error.InvalidRequestError( - "'file' is a required property", "file" - ) - if purpose is None: - raise openai.error.InvalidRequestError( - "'purpose' is a required property", "purpose" - ) + """Find already uploaded files with the same name, size, and purpose.""" all_files = cls.list( api_key=api_key, api_base=api_base or openai.api_base, @@ -87,15 +86,14 @@ def find_matching_files( organization=organization, ).get("data", []) matching_files = [] + basename = os.path.basename(name) for f in all_files: if f["purpose"] != purpose: continue - if not hasattr(file, "name") or f["filename"] != file.name: + file_basename = os.path.basename(f["filename"]) + if file_basename != basename: continue - file.seek(0, os.SEEK_END) - if f["bytes"] != file.tell(): - file.seek(0) + if f["bytes"] != bytes: continue - file.seek(0) matching_files.append(f) return matching_files diff --git a/openai/cli.py b/openai/cli.py index 872209f5bb..8130d2fb42 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -4,6 +4,8 @@ import sys import warnings +import requests + import openai from openai.upload_progress import BufferReader from openai.validators import ( @@ -200,7 +202,10 @@ def create(cls, args): with open(args.file, "rb") as file_reader: buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") resp = openai.File.create( - file=buffer_reader, purpose=args.purpose, model=args.model + file=buffer_reader, + purpose=args.purpose, + model=args.model, + user_provided_filename=args.file, ) print(resp) @@ -238,52 +243,102 @@ def list(cls, args): print(resp) @classmethod - def _get_or_upload(cls, file, check_if_file_exists=True): - try: - openai.File.retrieve(file) - except openai.error.InvalidRequestError as e: - if e.http_status == 404 and os.path.isfile(file): - matching_files = openai.File.find_matching_files( - file=open(file), purpose="fine-tune" + def _is_url(http://github.com/cls,%20file:%20str): + return file.lower().startswith("http") + + @classmethod + def _download_file_from_public_url(http://github.com/cls,%20url:%20str) -> Optional[bytes]: + resp = requests.get(url) + if resp.status_code == 200: + return resp.content + else: + return None + + @classmethod + def _maybe_upload_file( + cls, + file: Optional[str] = None, + content: Optional[bytes] = None, + user_provided_file: Optional[str] = None, + check_if_file_exists: bool = True, + ): + # Exactly one of `file` or `content` must be provided + if (file is None) == (content is None): + raise ValueError("Exactly one of `file` or `content` must be provided") + + if content is None: + assert file is not None + with open(file, "rb") as f: + content = f.read() + + if check_if_file_exists: + bytes = len(content) + matching_files = openai.File.find_matching_files( + name=user_provided_file or f.name, bytes=bytes, purpose="fine-tune" + ) + if len(matching_files) > 0: + file_ids = [f["id"] for f in matching_files] + sys.stdout.write( + "Found potentially duplicated files with name '{name}', purpose 'fine-tune' and size {size} bytes\n".format( + name=os.path.basename(matching_files[0]["filename"]), + size=matching_files[0]["bytes"], + ) ) - if len(matching_files) > 0 and check_if_file_exists: - file_ids = [f["id"] for f in matching_files] + sys.stdout.write("\n".join(file_ids)) + while True: sys.stdout.write( - "Found potentially duplicated files with name '{name}', purpose 'fine-tune' and size {size} bytes\n".format( - name=matching_files[0]["filename"], - size=matching_files[0]["bytes"], - ) + "\nEnter file ID to reuse an already uploaded file, or an empty string to upload this file anyway: " ) - sys.stdout.write("\n".join(file_ids)) - while True: + inp = sys.stdin.readline().strip() + if inp in file_ids: sys.stdout.write( - "\nEnter file ID to reuse an already uploaded file, or an empty string to upload this file anyway: " + "Reusing already uploaded file: {id}\n".format(id=inp) ) - inp = sys.stdin.readline().strip() - if inp in file_ids: - sys.stdout.write( - "Using your file {file}: {id}\n".format( - file=file, id=inp - ) - ) - return inp - elif inp == "": - break - else: - sys.stdout.write( - "File id '{id}' is not among the IDs of the potentially duplicated files\n".format( - id=inp - ) + return inp + elif inp == "": + break + else: + sys.stdout.write( + "File id '{id}' is not among the IDs of the potentially duplicated files\n".format( + id=inp ) + ) - resp = openai.File.create( - file=open(file), - purpose="fine-tune", - ) - sys.stdout.write( - "Uploaded file from {file}: {id}\n".format(file=file, id=resp["id"]) + buffer_reader = BufferReader(content, desc="Upload progress") + resp = openai.File.create( + file=buffer_reader, + purpose="fine-tune", + user_provided_filename=user_provided_file or file, + ) + sys.stdout.write( + "Uploaded file from {file}: {id}\n".format( + file=user_provided_file or file, id=resp["id"] + ) + ) + return resp["id"] + + @classmethod + def _get_or_upload(cls, file, check_if_file_exists=True): + try: + # 1. If it's a valid file, use it + openai.File.retrieve(file) + return file + except openai.error.InvalidRequestError: + pass + if os.path.isfile(file): + # 2. If it's a file on the filesystem, upload it + return cls._maybe_upload_file( + file=file, check_if_file_exists=check_if_file_exists + ) + if cls._is_url(http://github.com/file): + # 3. If it's a URL, download it temporarily + content = cls._download_file_from_public_url(http://github.com/file) + if content is not None: + return cls._maybe_upload_file( + content=content, + check_if_file_exists=check_if_file_exists, + user_provided_file=file, ) - return resp["id"] return file @classmethod @@ -737,15 +792,15 @@ def help(args): "--training_file", required=True, help="JSONL file containing prompt-completion examples for training. This can " - "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345) " - "or a local file path.", + "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " + 'a local file path, or a URL that starts with "http".', ) sub.add_argument( "-v", "--validation_file", help="JSONL file containing prompt-completion examples for validation. This can " - "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345) " - "or a local file path.", + "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " + 'a local file path, or a URL that starts with "http".', ) sub.add_argument( "--no_check_if_files_exist", @@ -780,7 +835,7 @@ def help(args): type=float, help="The learning rate multiplier to use for training. The fine-tuning " "learning rate is determined by the original learning rate used for " - "pretraining multiplied by this value", + "pretraining multiplied by this value.", ) sub.add_argument( "--use_packing", @@ -796,7 +851,7 @@ def help(args): "--no_packing", action="http://github.com/store_false", dest="use_packing", - help="Disables the packing flag (see --use_packing for description)", + help="Disables the packing flag (see --use_packing for description).", ) sub.set_defaults(use_packing=None) sub.add_argument( @@ -804,7 +859,7 @@ def help(args): type=float, help="The weight to use for the prompt loss. The optimum value here depends " "depends on your use case. This determines how much the model prioritizes " - "learning from prompt tokens vs learning from completion tokens", + "learning from prompt tokens vs learning from completion tokens.", ) sub.add_argument( "--compute_classification_metrics", @@ -817,13 +872,13 @@ def help(args): "--classification_n_classes", type=int, help="The number of classes in a classification task. This parameter is " - "required for multiclass classification", + "required for multiclass classification.", ) sub.add_argument( "--classification_positive_class", help="The positive class in binary classification. This parameter is needed " "to generate precision, recall and F-1 metrics when doing binary " - "classification", + "classification.", ) sub.add_argument( "--classification_betas", diff --git a/openai/version.py b/openai/version.py index b5ce99b561..2073d43ee6 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.11.1" +VERSION = "0.11.2" From 88bbe08947bceb10845b335d7f4cfb5ff406d948 Mon Sep 17 00:00:00 2001 From: Rachel Lim Date: Fri, 3 Dec 2021 18:23:02 -0800 Subject: [PATCH 004/914] Add necessary import (#51) * Add necessary import * version --- openai/cli.py | 1 + openai/version.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/openai/cli.py b/openai/cli.py index 8130d2fb42..842b0a290c 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -3,6 +3,7 @@ import signal import sys import warnings +from typing import Optional import requests diff --git a/openai/version.py b/openai/version.py index 2073d43ee6..46898a964b 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.11.2" +VERSION = "0.11.3" From b39bddd99dc243bf09bec60f4866e2fb542d0e8f Mon Sep 17 00:00:00 2001 From: hallacy Date: Mon, 13 Dec 2021 19:32:47 -0500 Subject: [PATCH 005/914] Hallacy/11 4 release (#54) * Make embeddings_utils be importable (#104) * Make embeddings_utils be importable * Small tweaks to dicts for typing * Remove default api_prefix and move v1 prefix to default api_base (#95) * make construct_from key argument optional (#92) * Split search.prepare_data into answers/classifications/search versions (#93) * Break out prepare_data into answers, classifications, and search * And cleaned up CLI * Validate search files (#69) * Add validators for search files * Clean up fields Co-authored-by: kennyhsu5 <1762087+kennyhsu5@users.noreply.github.com> Co-authored-by: Madeleine Thompson --- examples/embeddings/Classification.ipynb | 2 +- examples/embeddings/Code_search.ipynb | 4 +- examples/embeddings/Obtain_dataset.ipynb | 2 +- ...emantic_text_search_using_embeddings.ipynb | 2 +- .../User_and_product_embeddings.ipynb | 2 +- .../embeddings/Zero-shot_classification.ipynb | 2 +- openai/__init__.py | 2 +- openai/api_resources/abstract/api_resource.py | 6 +- .../abstract/engine_api_resource.py | 4 +- openai/api_resources/answer.py | 8 +- openai/api_resources/classification.py | 8 +- openai/api_resources/search.py | 7 +- openai/cli.py | 139 ++++++++++++------ .../utils.py => openai/embeddings_utils.py | 28 ++-- openai/error.py | 2 +- openai/openai_object.py | 6 +- openai/util.py | 2 +- openai/validators.py | 133 ++++++++++++++--- openai/version.py | 2 +- 19 files changed, 246 insertions(+), 115 deletions(-) rename examples/embeddings/utils.py => openai/embeddings_utils.py (78%) diff --git a/examples/embeddings/Classification.ipynb b/examples/embeddings/Classification.ipynb index 482ba85910..54ad13fe89 100644 --- a/examples/embeddings/Classification.ipynb +++ b/examples/embeddings/Classification.ipynb @@ -90,7 +90,7 @@ } ], "source": [ - "from utils import plot_multiclass_precision_recall\n", + "from openai.embeddings_utils import plot_multiclass_precision_recall\n", "\n", "plot_multiclass_precision_recall(probas, y_test, [1,2,3,4,5], clf)" ] diff --git a/examples/embeddings/Code_search.ipynb b/examples/embeddings/Code_search.ipynb index 14cbf81777..d440161493 100644 --- a/examples/embeddings/Code_search.ipynb +++ b/examples/embeddings/Code_search.ipynb @@ -185,7 +185,7 @@ } ], "source": [ - "from utils import get_embedding\n", + "from openai.embeddings_utils import get_embedding\n", "\n", "df = pd.DataFrame(all_funcs)\n", "df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, engine='babbage-code-search-code'))\n", @@ -231,7 +231,7 @@ } ], "source": [ - "from utils import cosine_similarity\n", + "from openai.embeddings_utils import cosine_similarity\n", "\n", "def search_functions(df, code_query, n=3, pprint=True, n_lines=7):\n", " embedding = get_embedding(code_query, engine='babbage-code-search-text')\n", diff --git a/examples/embeddings/Obtain_dataset.ipynb b/examples/embeddings/Obtain_dataset.ipynb index 76bb7b8427..61c5775c46 100644 --- a/examples/embeddings/Obtain_dataset.ipynb +++ b/examples/embeddings/Obtain_dataset.ipynb @@ -156,7 +156,7 @@ "metadata": {}, "outputs": [], "source": [ - "from utils import get_embedding\n", + "from openai.embeddings_utils import get_embedding\n", "\n", "# This will take just under 10 minutes\n", "df['babbage_similarity'] = df.combined.apply(lambda x: get_embedding(x, engine='babbage-similarity'))\n", diff --git a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb index e83d4db5e1..58a41439c4 100644 --- a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb +++ b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb @@ -49,7 +49,7 @@ } ], "source": [ - "from utils import get_embedding, cosine_similarity\n", + "from openai.embeddings_utils import get_embedding, cosine_similarity\n", "\n", "# search through the reviews for a specific product\n", "def search_reviews(df, product_description, n=3, pprint=True):\n", diff --git a/examples/embeddings/User_and_product_embeddings.ipynb b/examples/embeddings/User_and_product_embeddings.ipynb index ca74d6bdc6..ea0ef2f81c 100644 --- a/examples/embeddings/User_and_product_embeddings.ipynb +++ b/examples/embeddings/User_and_product_embeddings.ipynb @@ -70,7 +70,7 @@ "metadata": {}, "outputs": [], "source": [ - "from utils import cosine_similarity\n", + "from openai.embeddings_utils import cosine_similarity\n", "\n", "# evaluate embeddings as recommendations on X_test\n", "def evaluate_single_match(row):\n", diff --git a/examples/embeddings/Zero-shot_classification.ipynb b/examples/embeddings/Zero-shot_classification.ipynb index 95789287a6..d4fc1b38ff 100644 --- a/examples/embeddings/Zero-shot_classification.ipynb +++ b/examples/embeddings/Zero-shot_classification.ipynb @@ -78,7 +78,7 @@ } ], "source": [ - "from utils import cosine_similarity, get_embedding\n", + "from openai.embeddings_utils import cosine_similarity, get_embedding\n", "from sklearn.metrics import PrecisionRecallDisplay\n", "\n", "def evaluate_emeddings_approach(\n", diff --git a/openai/__init__.py b/openai/__init__.py index f9d601bcf8..1574cc0b38 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -25,7 +25,7 @@ api_key_path: Optional[str] = os.environ.get("OPENAI_API_KEY_PATH") organization = os.environ.get("OPENAI_ORGANIZATION") -api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/") +api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") api_version = None verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index 3a27c66585..40350dfe4a 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -5,7 +5,7 @@ class APIResource(OpenAIObject): - api_prefix = "v1" + api_prefix = "" @classmethod def retrieve(cls, id, api_key=None, request_id=None, **params): @@ -28,7 +28,9 @@ def class_url(http://github.com/cls): # Namespaces are separated in object names with periods (.) and in URLs # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - return "/%s/%ss" % (cls.api_prefix, base) + if cls.api_prefix: + return "/%s/%ss" % (cls.api_prefix, base) + return "/%ss" % (base) def instance_url(http://github.com/self): id = self.get("id") diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index e613e53814..aab1ae8663 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -22,10 +22,10 @@ def class_url(http://github.com/cls,%20engine:%20Optional[str]%20=%20None): # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore if engine is None: - return "/%s/%ss" % (cls.api_prefix, base) + return "/%ss" % (base) extn = quote_plus(engine) - return "/%s/engines/%s/%ss" % (cls.api_prefix, extn, base) + return "/engines/%s/%ss" % (extn, base) @classmethod def create( diff --git a/openai/api_resources/answer.py b/openai/api_resources/answer.py index 8dd3e84d23..33de3cb7e9 100644 --- a/openai/api_resources/answer.py +++ b/openai/api_resources/answer.py @@ -2,13 +2,11 @@ class Answer(OpenAIObject): - api_prefix = "v1" - @classmethod - def get_url(http://github.com/self,%20base): - return "/%s/%s" % (self.api_prefix, base) + def get_url(http://github.com/self): + return "/answers" @classmethod def create(cls, **params): instance = cls() - return instance.request("post", cls.get_url("http://github.com/answers"), params) + return instance.request("post", cls.get_url(), params) diff --git a/openai/api_resources/classification.py b/openai/api_resources/classification.py index b659164e5a..6423c6946a 100644 --- a/openai/api_resources/classification.py +++ b/openai/api_resources/classification.py @@ -2,13 +2,11 @@ class Classification(OpenAIObject): - api_prefix = "v1" - @classmethod - def get_url(http://github.com/self,%20base): - return "/%s/%s" % (self.api_prefix, base) + def get_url(http://github.com/self): + return "/classifications" @classmethod def create(cls, **params): instance = cls() - return instance.request("post", cls.get_url("http://github.com/classifications"), params) + return instance.request("post", cls.get_url(), params) diff --git a/openai/api_resources/search.py b/openai/api_resources/search.py index 4a6e9a4b46..fc7c4326f6 100644 --- a/openai/api_resources/search.py +++ b/openai/api_resources/search.py @@ -2,14 +2,11 @@ class Search(APIResource): - api_prefix = "v1" - OBJECT_NAME = "search_indices" - @classmethod def class_url(http://github.com/cls): - return "/%s/%s" % (cls.api_prefix, cls.OBJECT_NAME) + return "/search_indices/search" @classmethod def create_alpha(cls, **params): instance = cls() - return instance.request("post", f"{cls.class_url()}/search", params) + return instance.request("post", cls.class_url(), params) diff --git a/openai/cli.py b/openai/cli.py index 842b0a290c..1beca23d17 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -3,6 +3,7 @@ import signal import sys import warnings +from functools import partial from typing import Optional import requests @@ -11,10 +12,12 @@ from openai.upload_progress import BufferReader from openai.validators import ( apply_necessary_remediation, - apply_optional_remediation, + apply_validators, + get_search_validators, get_validators, read_any_format, write_out_file, + write_out_search_file, ) @@ -227,6 +230,40 @@ def list(cls, args): class Search: + @classmethod + def prepare_data(cls, args, purpose): + + sys.stdout.write("Analyzing...\n") + fname = args.file + auto_accept = args.quiet + + optional_fields = ["metadata"] + + if purpose == "classifications": + required_fields = ["text", "labels"] + else: + required_fields = ["text"] + + df, remediation = read_any_format( + fname, fields=required_fields + optional_fields + ) + + if "metadata" not in df: + df["metadata"] = None + + apply_necessary_remediation(None, remediation) + validators = get_search_validators(required_fields, optional_fields) + + write_out_file_func = partial( + write_out_search_file, + purpose=purpose, + fields=required_fields + optional_fields, + ) + + apply_validators( + df, fname, remediation, validators, auto_accept, write_out_file_func + ) + @classmethod def create_alpha(cls, args): resp = openai.Search.create_alpha( @@ -489,49 +526,14 @@ def prepare_data(cls, args): validators = get_validators() - optional_remediations = [] - if remediation is not None: - optional_remediations.append(remediation) - for validator in validators: - remediation = validator(df) - if remediation is not None: - optional_remediations.append(remediation) - df = apply_necessary_remediation(df, remediation) - - any_optional_or_necessary_remediations = any( - [ - remediation - for remediation in optional_remediations - if remediation.optional_msg is not None - or remediation.necessary_msg is not None - ] + apply_validators( + df, + fname, + remediation, + validators, + auto_accept, + write_out_file_func=write_out_file, ) - any_necessary_applied = any( - [ - remediation - for remediation in optional_remediations - if remediation.necessary_msg is not None - ] - ) - any_optional_applied = False - - if any_optional_or_necessary_remediations: - sys.stdout.write( - "\n\nBased on the analysis we will perform the following actions:\n" - ) - for remediation in optional_remediations: - df, optional_applied = apply_optional_remediation( - df, remediation, auto_accept - ) - any_optional_applied = any_optional_applied or optional_applied - else: - sys.stdout.write("\n\nNo remediations found.\n") - - any_optional_or_necessary_applied = ( - any_optional_applied or any_necessary_applied - ) - - write_out_file(df, fname, any_optional_or_necessary_applied, auto_accept) def tools_register(parser): @@ -561,6 +563,57 @@ def help(args): ) sub.set_defaults(func=FineTune.prepare_data) + sub = subparsers.add_parser("search.prepare_data") + sub.add_argument( + "-f", + "--file", + required=True, + help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing text examples to be analyzed." + "This should be the local file path.", + ) + sub.add_argument( + "-q", + "--quiet", + required=False, + action="http://github.com/store_true", + help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", + ) + sub.set_defaults(func=partial(Search.prepare_data, purpose="search")) + + sub = subparsers.add_parser("classifications.prepare_data") + sub.add_argument( + "-f", + "--file", + required=True, + help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing text-label examples to be analyzed." + "This should be the local file path.", + ) + sub.add_argument( + "-q", + "--quiet", + required=False, + action="http://github.com/store_true", + help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", + ) + sub.set_defaults(func=partial(Search.prepare_data, purpose="classification")) + + sub = subparsers.add_parser("answers.prepare_data") + sub.add_argument( + "-f", + "--file", + required=True, + help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing text examples to be analyzed." + "This should be the local file path.", + ) + sub.add_argument( + "-q", + "--quiet", + required=False, + action="http://github.com/store_true", + help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", + ) + sub.set_defaults(func=partial(Search.prepare_data, purpose="answer")) + def api_register(parser): # Engine management diff --git a/examples/embeddings/utils.py b/openai/embeddings_utils.py similarity index 78% rename from examples/embeddings/utils.py rename to openai/embeddings_utils.py index f7877147fd..d846ede42f 100644 --- a/examples/embeddings/utils.py +++ b/openai/embeddings_utils.py @@ -1,11 +1,10 @@ -import openai -import pandas as pd -import numpy as np import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from sklearn.metrics import average_precision_score, precision_recall_curve +from tenacity import retry, stop_after_attempt, wait_random_exponential -from tenacity import retry, wait_random_exponential, stop_after_attempt -from sklearn.metrics import precision_recall_curve -from sklearn.metrics import average_precision_score +import openai @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) @@ -14,7 +13,7 @@ def get_embedding(text, engine="davinci-similarity"): # replace newlines, which can negatively affect performance. text = text.replace("\n", " ") - return openai.Engine(id=engine).embeddings(input = [text])['data'][0]['embedding'] + return openai.Engine(id=engine).embeddings(input=[text])["data"][0]["embedding"] def cosine_similarity(a, b): @@ -43,16 +42,14 @@ def plot_multiclass_precision_recall( average_precision[i] = average_precision_score(y_true[:, i], y_score[:, i]) # A "micro-average": quantifying score on all classes jointly - precision["micro"], recall["micro"], _ = precision_recall_curve( + precision_micro, recall_micro, _ = precision_recall_curve( y_true.ravel(), y_score.ravel() ) - average_precision["micro"] = average_precision_score( - y_true, y_score, average="micro" - ) + average_precision_micro = average_precision_score(y_true, y_score, average="micro") print( str(classifier_name) + " - Average precision score over all classes: {0:0.2f}".format( - average_precision["micro"] + average_precision_micro ) ) @@ -69,11 +66,10 @@ def plot_multiclass_precision_recall( lines.append(l) labels.append("iso-f1 curves") - (l,) = plt.plot(recall["micro"], precision["micro"], color="gold", lw=2) + (l,) = plt.plot(recall_micro, precision_micro, color="gold", lw=2) lines.append(l) labels.append( - "average Precision-recall (auprc = {0:0.2f})" - "".format(average_precision["micro"]) + "average Precision-recall (auprc = {0:0.2f})" "".format(average_precision_micro) ) for i in range(n_classes): @@ -91,4 +87,4 @@ def plot_multiclass_precision_recall( plt.xlabel("Recall") plt.ylabel("Precision") plt.title(f"{classifier_name}: Precision-Recall curve for each class") - plt.legend(lines, labels) \ No newline at end of file + plt.legend(lines, labels) diff --git a/openai/error.py b/openai/error.py index 1f0fa3e906..f2ce82c2e2 100644 --- a/openai/error.py +++ b/openai/error.py @@ -64,7 +64,7 @@ def construct_error_object(self): return None return openai.api_resources.error_object.ErrorObject.construct_from( - self.json_body["error"], key=None + self.json_body["error"] ) diff --git a/openai/openai_object.py b/openai/openai_object.py index 9b56082d51..f87ff29f34 100644 --- a/openai/openai_object.py +++ b/openai/openai_object.py @@ -100,7 +100,7 @@ def __reduce__(self): def construct_from( cls, values, - key, + api_key: Optional[str] = None, api_version=None, organization=None, engine=None, @@ -108,7 +108,7 @@ def construct_from( ): instance = cls( values.get("id"), - api_key=key, + api_key=api_key, api_version=api_version, organization=organization, engine=engine, @@ -116,7 +116,7 @@ def construct_from( ) instance.refresh_from( values, - api_key=key, + api_key=api_key, api_version=api_version, organization=organization, response_ms=response_ms, diff --git a/openai/util.py b/openai/util.py index 3be1717034..1b87ac893f 100644 --- a/openai/util.py +++ b/openai/util.py @@ -111,7 +111,7 @@ def convert_to_openai_object( return klass.construct_from( resp, - api_key, + api_key=api_key, api_version=api_version, organization=organization, response_ms=response_ms, diff --git a/openai/validators.py b/openai/validators.py index ba4b92c2b0..356f461506 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -70,7 +70,7 @@ def lower_case_column_creator(df): ) -def additional_column_validator(df): +def additional_column_validator(df, fields=["prompt", "completion"]): """ This validator will remove additional columns from the dataframe. """ @@ -79,9 +79,7 @@ def additional_column_validator(df): immediate_msg = None necessary_fn = None if len(df.columns) > 2: - additional_columns = [ - c for c in df.columns if c not in ["prompt", "completion"] - ] + additional_columns = [c for c in df.columns if c not in fields] warn_message = "" for ac in additional_columns: dups = [c for c in additional_columns if ac in c] @@ -91,7 +89,7 @@ def additional_column_validator(df): necessary_msg = f"Remove additional columns/keys: {additional_columns}" def necessary_fn(x): - return x[["prompt", "completion"]] + return x[fields] return Remediation( name="additional_column", @@ -101,7 +99,7 @@ def necessary_fn(x): ) -def non_empty_completion_validator(df): +def non_empty_field_validator(df, field="completion"): """ This validator will ensure that no completion is empty. """ @@ -109,42 +107,39 @@ def non_empty_completion_validator(df): necessary_fn = None immediate_msg = None - if ( - df["completion"].apply(lambda x: x == "").any() - or df["completion"].isnull().any() - ): - empty_rows = (df["completion"] == "") | (df["completion"].isnull()) + if df[field].apply(lambda x: x == "").any() or df[field].isnull().any(): + empty_rows = (df[field] == "") | (df[field].isnull()) empty_indexes = df.reset_index().index[empty_rows].tolist() - immediate_msg = f"\n- `completion` column/key should not contain empty strings. These are rows: {empty_indexes}" + immediate_msg = f"\n- `{field}` column/key should not contain empty strings. These are rows: {empty_indexes}" def necessary_fn(x): - return x[x["completion"] != ""].dropna(subset=["completion"]) + return x[x[field] != ""].dropna(subset=[field]) - necessary_msg = f"Remove {len(empty_indexes)} rows with empty completions" + necessary_msg = f"Remove {len(empty_indexes)} rows with empty {field}s" return Remediation( - name="empty_completion", + name=f"empty_{field}", immediate_msg=immediate_msg, necessary_msg=necessary_msg, necessary_fn=necessary_fn, ) -def duplicated_rows_validator(df): +def duplicated_rows_validator(df, fields=["prompt", "completion"]): """ This validator will suggest to the user to remove duplicate rows if they exist. """ - duplicated_rows = df.duplicated(subset=["prompt", "completion"]) + duplicated_rows = df.duplicated(subset=fields) duplicated_indexes = df.reset_index().index[duplicated_rows].tolist() immediate_msg = None optional_msg = None optional_fn = None if len(duplicated_indexes) > 0: - immediate_msg = f"\n- There are {len(duplicated_indexes)} duplicated prompt-completion pairs. These are rows: {duplicated_indexes}" + immediate_msg = f"\n- There are {len(duplicated_indexes)} duplicated {'-'.join(fields)} sets. These are rows: {duplicated_indexes}" optional_msg = f"Remove {len(duplicated_indexes)} duplicate rows" def optional_fn(x): - return x.drop_duplicates(subset=["prompt", "completion"]) + return x.drop_duplicates(subset=fields) return Remediation( name="duplicated_rows", @@ -467,7 +462,7 @@ def lower_case(x): ) -def read_any_format(fname): +def read_any_format(fname, fields=["prompt", "completion"]): """ This function will read a file saved in .csv, .json, .txt, .xlsx or .tsv format using pandas. - for .xlsx it will read the first sheet @@ -502,7 +497,7 @@ def read_any_format(fname): content = f.read() df = pd.DataFrame( [["", line] for line in content.split("\n")], - columns=["prompt", "completion"], + columns=fields, dtype=str, ) if fname.lower().endswith("jsonl") or fname.lower().endswith("json"): @@ -623,7 +618,7 @@ def get_outfnames(fname, split): while True: index_suffix = f" ({i})" if i > 0 else "" candidate_fnames = [ - fname.split(".")[0] + "_prepared" + suffix + index_suffix + ".jsonl" + os.path.splitext(fname)[0] + "_prepared" + suffix + index_suffix + ".jsonl" for suffix in suffixes ] if not any(os.path.isfile(f) for f in candidate_fnames): @@ -744,6 +739,30 @@ def write_out_file(df, fname, any_remediations, auto_accept): sys.stdout.write("Aborting... did not write the file\n") +def write_out_search_file(df, fname, any_remediations, auto_accept, fields, purpose): + """ + This function will write out a dataframe to a file, if the user would like to proceed. + """ + input_text = "\n\nYour data will be written to a new JSONL file. Proceed [Y/n]: " + + if not any_remediations: + sys.stdout.write( + f'\nYou can upload your file:\n> openai api files.create -f "{fname}" -p {purpose}' + ) + + elif accept_suggestion(input_text, auto_accept): + fnames = get_outfnames(fname, split=False) + + assert len(fnames) == 1 + df[fields].to_json(fnames[0], lines=True, orient="records", force_ascii=False) + + sys.stdout.write( + f'\nWrote modified file to {fnames[0]}`\nFeel free to take a look!\n\nNow upload that file:\n> openai api files.create -f "{fnames[0]}" -p {purpose}' + ) + else: + sys.stdout.write("Aborting... did not write the file\n") + + def infer_task_type(df): """ Infer the likely fine-tuning task type from the data @@ -788,7 +807,7 @@ def get_validators(): lambda x: necessary_column_validator(x, "prompt"), lambda x: necessary_column_validator(x, "completion"), additional_column_validator, - non_empty_completion_validator, + non_empty_field_validator, format_inferrer_validator, duplicated_rows_validator, long_examples_validator, @@ -800,3 +819,71 @@ def get_validators(): common_completion_suffix_validator, completions_space_start_validator, ] + + +def get_search_validators(required_fields, optional_fields): + validators = [ + lambda x: necessary_column_validator(x, field) for field in required_fields + ] + validators += [ + lambda x: non_empty_field_validator(x, field) for field in required_fields + ] + validators += [lambda x: duplicated_rows_validator(x, required_fields)] + validators += [ + lambda x: additional_column_validator( + x, fields=required_fields + optional_fields + ), + ] + + return validators + + +def apply_validators( + df, + fname, + remediation, + validators, + auto_accept, + write_out_file_func, +): + optional_remediations = [] + if remediation is not None: + optional_remediations.append(remediation) + for validator in validators: + remediation = validator(df) + if remediation is not None: + optional_remediations.append(remediation) + df = apply_necessary_remediation(df, remediation) + + any_optional_or_necessary_remediations = any( + [ + remediation + for remediation in optional_remediations + if remediation.optional_msg is not None + or remediation.necessary_msg is not None + ] + ) + any_necessary_applied = any( + [ + remediation + for remediation in optional_remediations + if remediation.necessary_msg is not None + ] + ) + any_optional_applied = False + + if any_optional_or_necessary_remediations: + sys.stdout.write( + "\n\nBased on the analysis we will perform the following actions:\n" + ) + for remediation in optional_remediations: + df, optional_applied = apply_optional_remediation( + df, remediation, auto_accept + ) + any_optional_applied = any_optional_applied or optional_applied + else: + sys.stdout.write("\n\nNo remediations found.\n") + + any_optional_or_necessary_applied = any_optional_applied or any_necessary_applied + + write_out_file_func(df, fname, any_optional_or_necessary_applied, auto_accept) diff --git a/openai/version.py b/openai/version.py index 46898a964b..d0dcdac6c8 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.11.3" +VERSION = "0.11.4" From 0e21703ef4cb42eac8a9334f05697d49f9ed05dd Mon Sep 17 00:00:00 2001 From: Ted Sanders <95656834+ted-at-openai@users.noreply.github.com> Date: Fri, 17 Dec 2021 10:05:41 -0800 Subject: [PATCH 006/914] adds examples to openai python readme (#55) --- README.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a62958f652..9ba8426716 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ This library additionally provides an `openai` command-line utility which makes it easy to interact with the API from your terminal. Run `openai api -h` for usage. -``` +```sh # list engines openai api engines.list @@ -66,6 +66,57 @@ openai api engines.list openai api completions.create -e ada -p "Hello world" ``` +## Example code + +Examples of how to use [embeddings](https://github.com/openai/openai-python/tree/main/examples/embeddings), [fine tuning](https://github.com/openai/openai-python/tree/main/examples/finetuning), [semantic search](https://github.com/openai/openai-python/tree/main/examples/semanticsearch), and [codex](https://github.com/openai/openai-python/tree/main/examples/codex) can be found in the [examples folder](https://github.com/openai/openai-python/tree/main/examples). + +### Embeddings + +In the OpenAI Python library, an embedding represents a text string as a fixed-length vector of floating point numbers. Embeddings are designed to measure the similarity or relevance between text strings. + +To get an embedding for a text string, you can use the embeddings method as follows in Python: + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +# choose text to embed +text_string = "sample text" + +# choose an embedding +model_id = "davinci-similarity" + +# compute the embedding of the text +embedding = openai.Engine(id=model_id).embeddings(input=text_string)['data'][0]['embedding'] +``` + +An example of how to call the embeddings method is shown in the [get embeddings notebook](https://github.com/openai/openai-python/blob/main/examples/embeddings/Get_embeddings.ipynb). + +Examples of how to use embeddings are shared in the following Jupyter notebooks: + +- [Classification using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Classification.ipynb) +- [Clustering using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Clustering.ipynb) +- [Code search using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Code_search.ipynb) +- [Semantic text search using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Semantic_text_search_using_embeddings.ipynb) +- [User and product embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/User_and_product_embeddings.ipynb) +- [Zero-shot classification using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Zero-shot_classification.ipynb) + +For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://beta.openai.com/docs/guides/embeddings) in the OpenAI documentation. + +### Fine tuning + +Fine tuning a model on training data can both improve the results (by giving the model more examples to learn from) and reduce the cost & latency of API calls (by reducing the need to include training examples in prompts). + +Examples of fine tuning are shared in the following Jupyter notebooks: + +- [Classification with fine tuning](https://github.com/openai/openai-python/blob/main/examples/finetuning/finetuning-classification.ipynb) (a simple notebook that shows the steps required for fine tuning) +- Fine tuning a model that answers questions about the 2020 Olympics + - [Step 1: Collecting data](https://github.com/openai/openai-python/blob/main/examples/finetuning/olympics-1-collect-data.ipynb) + - [Step 2: Creating a synthetic Q&A dataset](https://github.com/openai/openai-python/blob/main/examples/finetuning/olympics-2-create-qa.ipynb) + - [Step 3: Train a fine-tuning model specialized for Q&A](https://github.com/openai/openai-python/blob/main/examples/finetuning/olympics-3-train-qa.ipynb) + +For more information on fine tuning, read the [fine-tuning guide](https://beta.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. + ## Requirements - Python 3.7.1+ From 26fbacb7ff699ed35997010edde3c0aee6599e5b Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 21 Dec 2021 12:24:14 -0800 Subject: [PATCH 007/914] Remove packing and default batch size from FT cli (#60) --- examples/embeddings/Code_search.ipynb | 2 +- .../finetuning-classification.ipynb | 10 +++--- examples/finetuning/olympics-3-train-qa.ipynb | 4 +-- openai/cli.py | 18 ---------- openai/validators.py | 33 ++----------------- openai/version.py | 2 +- 6 files changed, 11 insertions(+), 58 deletions(-) diff --git a/examples/embeddings/Code_search.ipynb b/examples/embeddings/Code_search.ipynb index d440161493..ca60566a7a 100644 --- a/examples/embeddings/Code_search.ipynb +++ b/examples/embeddings/Code_search.ipynb @@ -260,7 +260,7 @@ "def format_inferrer_validator(df):\n", " \"\"\"\n", " This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification.\n", - " It will also suggest to use ada, --no_packing and explain train/validation split benefits.\n", + " It will also suggest to use ada and explain train/validation split benefits.\n", " \"\"\"\n", " ft_type = infer_task_type(df)\n", " immediate_msg = None\n", diff --git a/examples/finetuning/finetuning-classification.ipynb b/examples/finetuning/finetuning-classification.ipynb index ff576ba35a..60b8896ecc 100644 --- a/examples/finetuning/finetuning-classification.ipynb +++ b/examples/finetuning/finetuning-classification.ipynb @@ -257,7 +257,7 @@ "\n", "- Your file contains 1197 prompt-completion pairs\n", "- Based on your data it seems like you're trying to fine-tune a model for classification\n", - "- For classification, we recommend you try one of the faster and cheaper models, such as `ada`. You should also set the `--no_packing` parameter when fine-tuning\n", + "- For classification, we recommend you try one of the faster and cheaper models, such as `ada`\n", "- For classification, you can estimate the expected model performance by keeping a held out dataset, which is not used for training\n", "- There are 11 examples that are very long. These are rows: [134, 200, 281, 320, 404, 595, 704, 838, 1113, 1139, 1174]\n", "For conditional generation, and for classification the examples shouldn't be longer than 2048 tokens.\n", @@ -277,7 +277,7 @@ "Feel free to take a look!\n", "\n", "Now use that file when fine-tuning:\n", - "> openai api fine_tunes.create -t \"sport2_prepared_train.jsonl\" -v \"sport2_prepared_valid.jsonl\" --no_packing --compute_classification_metrics --classification_positive_class \" baseball\"\n", + "> openai api fine_tunes.create -t \"sport2_prepared_train.jsonl\" -v \"sport2_prepared_valid.jsonl\" --compute_classification_metrics --classification_positive_class \" baseball\"\n", "\n", "After you’ve fine-tuned a model, remember that your prompt has to end with the indicator string `\\n\\n###\\n\\n` for the model to start generating completions, rather than continuing with the prompt.\n", "Once your model starts training, it'll approximately take 30.8 minutes to train a `curie` model, and less for `ada` and `babbage`. Queue will approximately take half an hour per job ahead of you.\n" @@ -301,7 +301,7 @@ "cell_type": "markdown", "source": [ "## Fine-tuning\n", - "The tool suggests we run the following command to train the dataset. Since this is a classification task, we would like to know what the generalization performance on the provided validation set is for our classification use case. The tool suggests to add `--compute_classification_metrics --classification_positive_class \" baseball\"` in order to compute the classification metrics. Classification performs better with a hyperparameter `--no_packing`.\n", + "The tool suggests we run the following command to train the dataset. Since this is a classification task, we would like to know what the generalization performance on the provided validation set is for our classification use case. The tool suggests to add `--compute_classification_metrics --classification_positive_class \" baseball\"` in order to compute the classification metrics.\n", "\n", "We can simply copy the suggested command from the CLI tool. We specifically add `-m ada` to fine-tune a cheaper and faster ada model, which is usually comperable in performance to slower and more expensive models on classification use cases. " ], @@ -311,7 +311,7 @@ "cell_type": "code", "execution_count": 9, "source": [ - "!openai api fine_tunes.create -t \"sport2_prepared_train.jsonl\" -v \"sport2_prepared_valid.jsonl\" --no_packing --compute_classification_metrics --classification_positive_class \" baseball\" -m ada" + "!openai api fine_tunes.create -t \"sport2_prepared_train.jsonl\" -v \"sport2_prepared_valid.jsonl\" --compute_classification_metrics --classification_positive_class \" baseball\" -m ada" ], "outputs": [ { @@ -737,4 +737,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/examples/finetuning/olympics-3-train-qa.ipynb b/examples/finetuning/olympics-3-train-qa.ipynb index ebf89a5c9c..76fb3f3ca8 100644 --- a/examples/finetuning/olympics-3-train-qa.ipynb +++ b/examples/finetuning/olympics-3-train-qa.ipynb @@ -373,7 +373,7 @@ } ], "source": [ - "!openai api fine_tunes.create -t \"olympics-data/discriminator_train.jsonl\" -v \"olympics-data/discriminator_test.jsonl\" --no_packing --batch_size 16 --compute_classification_metrics --classification_positive_class \" yes\" --model ada" + "!openai api fine_tunes.create -t \"olympics-data/discriminator_train.jsonl\" -v \"olympics-data/discriminator_test.jsonl\" --batch_size 16 --compute_classification_metrics --classification_positive_class \" yes\" --model ada" ] }, { @@ -391,7 +391,7 @@ } ], "source": [ - "!openai api fine_tunes.create -t \"olympics-data/qa_train.jsonl\" -v \"olympics-data/qa_test.jsonl\" --no_packing --batch_size 16" + "!openai api fine_tunes.create -t \"olympics-data/qa_train.jsonl\" -v \"olympics-data/qa_test.jsonl\" --batch_size 16" ] }, { diff --git a/openai/cli.py b/openai/cli.py index 1beca23d17..6c07b49135 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -397,7 +397,6 @@ def create(cls, args): "batch_size", "learning_rate_multiplier", "prompt_loss_weight", - "use_packing", "compute_classification_metrics", "classification_n_classes", "classification_positive_class", @@ -891,23 +890,6 @@ def help(args): "learning rate is determined by the original learning rate used for " "pretraining multiplied by this value.", ) - sub.add_argument( - "--use_packing", - action="http://github.com/store_true", - dest="use_packing", - help="On classification tasks, we recommend not setting this flag. " - "On all other tasks, we recommend setting it. " - "When set, we pack as many prompt-completion pairs as possible into each " - "training example. This greatly increases the speed of a fine-tuning job, " - "often without negatively affecting model performance.", - ) - sub.add_argument( - "--no_packing", - action="http://github.com/store_false", - dest="use_packing", - help="Disables the packing flag (see --use_packing for description).", - ) - sub.set_defaults(use_packing=None) sub.add_argument( "--prompt_loss_weight", type=float, diff --git a/openai/validators.py b/openai/validators.py index 356f461506..0d4d85d4f2 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -2,7 +2,6 @@ import sys from typing import Any, Callable, NamedTuple, Optional -import numpy as np import pandas as pd @@ -535,12 +534,12 @@ def read_any_format(fname, fields=["prompt", "completion"]): def format_inferrer_validator(df): """ This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification. - It will also suggest to use ada, --no_packing and explain train/validation split benefits. + It will also suggest to use ada and explain train/validation split benefits. """ ft_type = infer_task_type(df) immediate_msg = None if ft_type == "classification": - immediate_msg = f"\n- Based on your data it seems like you're trying to fine-tune a model for {ft_type}\n- For classification, we recommend you try one of the faster and cheaper models, such as `ada`. You should also set the `--no_packing` parameter when fine-tuning\n- For classification, you can estimate the expected model performance by keeping a held out dataset, which is not used for training" + immediate_msg = f"\n- Based on your data it seems like you're trying to fine-tune a model for {ft_type}\n- For classification, we recommend you try one of the faster and cheaper models, such as `ada`\n- For classification, you can estimate the expected model performance by keeping a held out dataset, which is not used for training" return Remediation(name="num_examples", immediate_msg=immediate_msg) @@ -634,27 +633,6 @@ def get_classification_hyperparams(df): return n_classes, pos_class -def get_batch_size_suggestion(df, no_packing): - """ - Suggest the batch size based on the number of examples after packing optionally is applied. - """ - n_examples, n_characters = ( - len(df), - df.completion.str.len().sum() + df.prompt.str.len().sum(), - ) - BATCH_SIZE_TO_N_EXAMPLES_RATIO = 0.002 - BATCH_SIZE_TO_N_CHARACTERS_RATIO = BATCH_SIZE_TO_N_EXAMPLES_RATIO / 10_000 - - if no_packing: - batch_size = BATCH_SIZE_TO_N_EXAMPLES_RATIO * n_examples - else: - batch_size = BATCH_SIZE_TO_N_CHARACTERS_RATIO * n_characters - - batch_size = max(1, int(2 ** np.ceil(np.log2(batch_size)))) - batch_size_suggestion = f" --batch_size {batch_size}" - return batch_size_suggestion - - def write_out_file(df, fname, any_remediations, auto_accept): """ This function will write out a dataframe to a file, if the user would like to proceed, and also offer a fine-tuning command with the newly created file. @@ -670,14 +648,7 @@ def write_out_file(df, fname, any_remediations, auto_accept): if accept_suggestion(input_text, auto_accept): split = True - no_packing = ft_format == "classification" or ( - ft_format == "conditional generation" and len(df) < 1000 - ) additional_params = "" - if no_packing: - additional_params = " --no_packing" - additional_params += get_batch_size_suggestion(df, no_packing) - common_prompt_suffix_new_line_handled = common_prompt_suffix.replace("\n", "\\n") common_completion_suffix_new_line_handled = common_completion_suffix.replace( "\n", "\\n" diff --git a/openai/version.py b/openai/version.py index d0dcdac6c8..bc927384a8 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.11.4" +VERSION = "0.11.5" From 0c4e7f0323e5e845031a83faa943afc674fef943 Mon Sep 17 00:00:00 2001 From: Filipe de Avila Belbute-Peres <10751509+filipeabperes@users.noreply.github.com> Date: Mon, 3 Jan 2022 15:52:53 -0300 Subject: [PATCH 008/914] Fix CLI tool for generating JSONL for classifications (#62) Edited line 597 to match condition in line 242, "classifications". Also edited line 243 to match documentation ("label", not "labels"). --- openai/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/cli.py b/openai/cli.py index 6c07b49135..4c5c8559cf 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -240,7 +240,7 @@ def prepare_data(cls, args, purpose): optional_fields = ["metadata"] if purpose == "classifications": - required_fields = ["text", "labels"] + required_fields = ["text", "label"] else: required_fields = ["text"] @@ -594,7 +594,7 @@ def help(args): action="http://github.com/store_true", help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", ) - sub.set_defaults(func=partial(Search.prepare_data, purpose="classification")) + sub.set_defaults(func=partial(Search.prepare_data, purpose="classifications")) sub = subparsers.add_parser("answers.prepare_data") sub.add_argument( From 25b681a18715aa7b92ffc261491aed10068fb3ae Mon Sep 17 00:00:00 2001 From: Lilian Date: Thu, 13 Jan 2022 13:57:38 -0800 Subject: [PATCH 009/914] Add embedding visualization in 3D notebook example (#108) (#63) * Add get_embeddings() * Add Visualize_in_3d notebook * Add dbpedia_samples.jsonl * black reformat openai/embeddings_utils.py * resolve comments * Remove .ipynb_checkpoints * fix build --- .gitignore | 1 + examples/embeddings/Visualize_in_3d.ipynb | 268 ++++++++++++++++++++++ examples/embeddings/dbpedia_samples.jsonl | 200 ++++++++++++++++ openai/embeddings_utils.py | 18 +- 4 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 examples/embeddings/Visualize_in_3d.ipynb create mode 100644 examples/embeddings/dbpedia_samples.jsonl diff --git a/.gitignore b/.gitignore index b7c7a797a3..47a4cda142 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /public/dist __pycache__ build +.ipynb_checkpoints diff --git a/examples/embeddings/Visualize_in_3d.ipynb b/examples/embeddings/Visualize_in_3d.ipynb new file mode 100644 index 0000000000..990c8edb07 --- /dev/null +++ b/examples/embeddings/Visualize_in_3d.ipynb @@ -0,0 +1,268 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "983ef639-fbf4-4912-b593-9cf08aeb11cd", + "metadata": {}, + "source": [ + "# Visualizing the embeddings in 3D" + ] + }, + { + "cell_type": "markdown", + "id": "9c9ea9a8-675d-4e3a-a8f7-6f4563df84ad", + "metadata": {}, + "source": [ + "The example uses [PCA](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html) to reduce the dimensionality fo the embeddings from 2048 to 3. Then we can visualize the data points in a 3D plot. The small dataset `dbpedia_samples.jsonl` is curated by randomly sampling 200 samples from [DBpedia validation dataset](https://www.kaggle.com/danofer/dbpedia-classes?select=DBPEDIA_val.csv)." + ] + }, + { + "cell_type": "markdown", + "id": "8df5f2c3-ddbb-4cc4-9205-4c0af1670562", + "metadata": {}, + "source": [ + "### 1. Load the dataset and query embeddings" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "133dfc2a-9dbd-4a5a-96fa-477272f7af5a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Categories of DBpedia samples: Artist 21\n", + "Film 19\n", + "Plant 19\n", + "OfficeHolder 18\n", + "Company 17\n", + "NaturalPlace 16\n", + "Athlete 16\n", + "Village 12\n", + "WrittenWork 11\n", + "Building 11\n", + "Album 11\n", + "Animal 11\n", + "EducationalInstitution 10\n", + "MeanOfTransportation 8\n", + "Name: category, dtype: int64\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textcategory
0Morada Limited is a textile company based in ...Company
1The Armenian Mirror-Spectator is a newspaper ...WrittenWork
2Mt. Kinka (金華山 Kinka-zan) also known as Kinka...NaturalPlace
3Planning the Play of a Bridge Hand is a book ...WrittenWork
4Wang Yuanping (born 8 December 1976) is a ret...Athlete
\n", + "
" + ], + "text/plain": [ + " text category\n", + "0 Morada Limited is a textile company based in ... Company\n", + "1 The Armenian Mirror-Spectator is a newspaper ... WrittenWork\n", + "2 Mt. Kinka (金華山 Kinka-zan) also known as Kinka... NaturalPlace\n", + "3 Planning the Play of a Bridge Hand is a book ... WrittenWork\n", + "4 Wang Yuanping (born 8 December 1976) is a ret... Athlete" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "samples = pd.read_json(\"dbpedia_samples.jsonl\", lines=True)\n", + "categories = sorted(samples[\"category\"].unique())\n", + "print(\"Categories of DBpedia samples:\", samples[\"category\"].value_counts())\n", + "samples.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "19874e3e-a216-48cc-a27b-acb73854d832", + "metadata": {}, + "outputs": [], + "source": [ + "from openai.embeddings_utils import get_embeddings\n", + "# NOTE: The following code will send a query of batch size 200 to /embeddings, cost about $0.2\n", + "matrix = get_embeddings(samples[\"text\"].to_list(), engine=\"babbage-similarity\")" + ] + }, + { + "cell_type": "markdown", + "id": "d410c268-d8a7-4979-887c-45b1d382dda9", + "metadata": {}, + "source": [ + "### 2. Reduce the embedding dimensionality" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f5410068-f3da-490c-8576-48e84a8728de", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.decomposition import PCA\n", + "pca = PCA(n_components=3)\n", + "vis_dims = pca.fit_transform(matrix)\n", + "samples[\"embed_vis\"] = vis_dims.tolist()" + ] + }, + { + "cell_type": "markdown", + "id": "b6565f57-59c6-4d36-a094-3cbbd9ddeb4c", + "metadata": {}, + "source": [ + "### 3. Plot the embeddings of lower dimensionality" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b17caad3-f0de-4115-83eb-55434a132acc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "864488447fdd46b4ae1f338d3b0afded", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib widget\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "fig = plt.figure(figsize=(10, 5))\n", + "ax = fig.add_subplot(projection='3d')\n", + "cmap = plt.get_cmap(\"tab20\")\n", + "\n", + "# Plot each sample category individually such that we can set label name.\n", + "for i, cat in enumerate(categories):\n", + " sub_matrix = np.array(samples[samples[\"category\"] == cat][\"embed_vis\"].to_list())\n", + " x=sub_matrix[:, 0]\n", + " y=sub_matrix[:, 1]\n", + " z=sub_matrix[:, 2]\n", + " colors = [cmap(i/len(categories))] * len(sub_matrix)\n", + " ax.scatter(x, y, zs=z, zdir='z', c=colors, label=cat)\n", + "\n", + "ax.set_xlabel('x')\n", + "ax.set_ylabel('y')\n", + "ax.set_zlabel('z')\n", + "ax.legend(bbox_to_anchor=(1.1, 1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8868043-9889-4a0b-b23d-79bb3823bdc7", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/embeddings/dbpedia_samples.jsonl b/examples/embeddings/dbpedia_samples.jsonl new file mode 100644 index 0000000000..0e022faa1b --- /dev/null +++ b/examples/embeddings/dbpedia_samples.jsonl @@ -0,0 +1,200 @@ +{"text": " Morada Limited is a textile company based in Altham Lancashire. Morada specializes in curtains.", "category": "Company"} +{"text": " The Armenian Mirror-Spectator is a newspaper published by the Baikar Association in Watertown Massachusetts.", "category": "WrittenWork"} +{"text": " Mt. Kinka (\u91d1\u83ef\u5c71 Kinka-zan) also known as Kinkazan is located in the heart of the city of Gifu Gifu Prefecture Japan and rises to a height of 329 m (1079 ft). Previously called Mt. Inaba (\u7a32\u8449\u5c71 Inaba-yama) it has long served as the representative symbol of Gifu. It stands along the Nagara River creating bountiful nature within the city. Though it is the most famous mountain in the city Mount Dodo to the north is the tallest.", "category": "NaturalPlace"} +{"text": " Planning the Play of a Bridge Hand is a book on contract bridge co-written by Canadian teacher and author Barbara Seagram and British author David Bird. It was published by Master Point Press in 2009.The book teaches novice bridge players some basic techniques of declarer play including suit establishment ruffing losers and the finesse.", "category": "WrittenWork"} +{"text": " Wang Yuanping (born 8 December 1976) is a retired Chinese athlete who specialised in the 800 metres. She won several medals at the regional level.Her personal bests in the event are 2:00.63 seconds outdoors (Jinzhou 2000) and 2:03.41 seconds indoors (Yokohama 2004).", "category": "Athlete"} +{"text": " The Incorporated VIllage of Westhampton Beach is an incorporated village in the Town of Southampton Suffolk County New York United States. As of the 2010 census the village population was 1721.", "category": "Village"} +{"text": " Andersons Creek is a creek in Warrandyte and Park Orchards east of Melbourne Victoria Australia. It is a tributary of the Yarra River.", "category": "NaturalPlace"} +{"text": " The Three Horseshoes is a public house in Drybridge Street in the Overmonnow area of Monmouth Wales. The pub has also been used as an Inn and also known as The Three Horse Shoes Inn. The building has been a Grade II Listed building since 15 August 1974. 19th century 2 storeys roughcast as stone with a hooded doorway", "category": "Building"} +{"text": " The Brewer's Art is a Baltimore Maryland brewpub and restaurant. Opened on Friday September 13 1996. In 2008 it was named by Esquire magazine as the #1 Best Bar in America.", "category": "Company"} +{"text": " The P\u00e2r\u00e2ul S\u0103r\u0103\u021bii is a tributary of the Cibin River in Romania.", "category": "NaturalPlace"} +{"text": " Jean-Fran\u00e7ois Imbernon (born October 17 1951 in Perpignan France is a retired French international rugby union player.He played as a Lock for USA Perpignan. He earned his first cap with the French national team on 7 February 1976 against Ireland at Parc des Princes.", "category": "Athlete"} +{"text": " Le Cadeau released in Italy as Il regalo is a 1982 French and Italian film. It stars Claudia Cardinale.", "category": "Film"} +{"text": " Mykola Kanevets (Ukrainian: \u041c\u0438\u043a\u043e\u043b\u0430 \u041a\u0430\u043d\u0456\u0432\u0435\u0446\u044c) is the Artistic Director and Ballet Master of the Cheremosh Ukrainian Dance Company in Edmonton Alberta Canada.A native of Kiev Ukraine Mykola attended the National University of Culture and Performing Arts in Kiev Ukraine where he graduated from the Faculty of Choreography with the distinction of Ballet Master and Choreographer.", "category": "Artist"} +{"text": " Jenna Rose Swerdlow (born September 28 1998) is an American teenage singer who gained media attention as a pre-teen with her single My Jeans. After the video went viral on YouTube and received 14 million views Swerdlow is considered a semi-viral star.", "category": "Artist"} +{"text": " The Spice of Life is a smooth jazz studio album by Earl Klugh released in April 2008. The album received a Grammy nomination for Best Pop Instrumental Album at the 51st Grammy Awards in 2009.", "category": "Album"} +{"text": " Lomatium macrocarpum is a perennial flowering plant in the carrot family known by the common names bigseed lomatium biscuit root or even bigseed biscuitroot. It is native to much of western North America where it can be found in various types of habitat including the grasslands of the Great Plains. It is spreading or erect perennial herb growing up to about half a meter long with hairy gray-green herbage.", "category": "Plant"} +{"text": " Physena is the sole genus of the flowering plant family Physenaceae. It contains two species of shrubs and small trees which are endemic to Madagascar. The APG II system of 2003 (unchanged from the APG system of 1998) does recognize this family and assigns it to the order Caryophyllales in the clade core eudicots.", "category": "Plant"} +{"text": " David John Weatherley (born 1 March 1939) is a New Zealander actor known for his roles as Spencer the butler and the voice of Benglo the Fearcat in Power Rangers Operation Overdrive and Barliman Butterbur in The Lord of the Rings: The Fellowship of the Ring.Weatherley was born in London England and moved to Canada for a military career. He eventually moved to New Zealand to engage in a theatre acting career.", "category": "Artist"} +{"text": " Draba incrassata is an uncommon species of flowering plant in the mustard family known by the common name Sweetwater Mountains draba. It is endemic to California where it is known mainly from the Sweetwater Mountains of Mono County. It grows in alpine rock fields on the barren high mountain peaks. Draba incrassata is a small perennial herb forming mats of thick oval-shaped leaves.", "category": "Plant"} +{"text": " Pimelea ferruginea is a small shrub native to southwest Western Australia. It was described by Labillardiere in 1805.", "category": "Plant"} +{"text": " Lindsay Ell is a country music singer songwriter and guitarist from Calgary Alberta.She performed at the South by Southwest music festival held in Austin Texas in March 2009 the welcome reception of the 2009 Juno Awards held in Vancouver British Columbia and was also a featured artist at the 2010 Winter Olympics.", "category": "Artist"} +{"text": " Scopula fuscata is a moth of the Geometridae family. It is found from south-western Saskatchewan west to British Columbia and south to California and Arizona. The habitat consists of montane areas including foothills.The wingspan is 24-28 mm. The wings and body are light tan sprinkled with darker yellow-brown or grey-brown scales. There is one generation per year with adults on wing in late June and early July in the northern part of the range.", "category": "Animal"} +{"text": " Oxmoor Center is a Louisville Kentucky shopping mall located at 7900 Shelbyville Road in eastern Louisville.", "category": "Building"} +{"text": " Ghostquake (also known as Haunted High) is a 2012 American made-for-television horror film produced by Syfy. The film was directed by Jeffrey Lando and written by Paul A. Birkett and Anthony C. Ferrante. The film stars Danny Trejo and MC Gainey. It follows a group of high school students trying to escape the wrath of a few ghastly spirits following an earthquake at their school Holloman High School.", "category": "Film"} +{"text": " The Masonic Temple in Great Falls Montana is a building from 1914. It was listed on the National Register of Historic Places in 2000.Address is 821 Central Avenue Great Falls Motana 59401 Phone number is 453-9080.Cascade No. 34 meets 2nd and 4th Tuesdays at 7:30pm Sept-June.Euclid No. 58 meets year-round 1st and 3rd Tuesdays at 7:30pm Sept-May 3rd Tuesdays at 7:30pm June-Aug. Delta No. 128 meets 2nd Wednesdays at 7:30pm Sept-June.", "category": "Building"} +{"text": " Harold Frederick Weaver Hawkins (1893-1977) was an English painter who specialized in ambitious sometimes mural-sized modernist allegories of morality for an age of atomic warfare and global over-population.", "category": "Artist"} +{"text": " Robert Murray Waddington (24 October 1927 \u2013 15 March 2007) was Dean of Manchester in the last quarter of the 20th century.Born in Bognor Regis on 24 October 1927 he was educated at Dulwich College Selwyn College Cambridge and Ely Theological College. Ordained in 1954 he began his career at St John\u2019s Bethnal Green. Afterwards he was Chaplain at Slade School in Warwick Queensland. He returned to England in 1959 to join the Oratory of the Good Shepherd an order of celibate priests.", "category": "OfficeHolder"} +{"text": " Jason Gary King (born 13 April 1985 in Maidstone England) is a speedway rider who was formerly the club captain of Newcastle Diamonds in the British Premier League. His brother Daniel is also a speedway rider.", "category": "Athlete"} +{"text": " The African Queen is a 1951 adventure film adapted from the 1935 novel of the same name by C. S. Forester. The film was directed by John Huston and produced by Sam Spiegel and John Woolf. The screenplay was adapted by James Agee John Huston John Collier and Peter Viertel. It was photographed in Technicolor by Jack Cardiff and had a music score by Allan Gray.", "category": "Film"} +{"text": " The Fiat Barchetta (Italian pronunciation: [\u02c8fiat bar\u02c8ketta]) (Type 183) is a roadster produced by the Italian manufacturer Fiat from 1995 to 2005 (though production was paused between May 2002 and 2004). Barchetta in Italian means 'little boat'.", "category": "MeanOfTransportation"} +{"text": " Sardar Vallabhbhai Patel National Memorial is a museum and exhibition centre dedicated to Sardar Vallabhbhai Patel at Moti Shahi Mahal located in Shahibaug Ahmedabad Gujarat. Moti Shahi Mahal was constructed by Mughal emperor Shahjahan between 1618 and 1622. It is surrounded by a garden.", "category": "Building"} +{"text": " Under Cover 2 is the 5th solo album of Joe Lynn Turner released in 1999. Just like Under Cover the album consists mainly of covers of Turner's favourite artists.", "category": "Album"} +{"text": " The Atakora River is a tributary of Lake Volta in Ghana it flows about 60 km east to the Lake Volta. Its entire course is in south Ghana.", "category": "NaturalPlace"} +{"text": " Death from Above is a 2011 horror film by director Bruce Koehler. The film features professional wrestling stars Kurt Angle Sid Eudy James Storm Matt Morgan Terry Gerin and Jessica Kresa.", "category": "Film"} +{"text": " Portraits of Cuba is an album by Cuban musician Paquito D'Rivera released through Chesky Records in 1996. In 1997 the album won D'Rivera the Grammy Award for Best Latin Jazz Performance.", "category": "Album"} +{"text": " Jimmy Cross (17 November 1938 - 8 October 1978) was an American radio producer and singer who attained a minor Billboard Hot 100 hit with the novelty song I Want My Baby Back in 1965. He was born in Dothan Alabama[citation needed] and became the producer of the syndicated radio series Country Concert.I Want My Baby Back was originally issued on the Tollie label and reached #92 on the Billboard Hot 100 in February 1965.", "category": "Artist"} +{"text": " Timothy Floyd Tim Burchett (born August 25 1964) is an American Republican politician currently the mayor of Knox County Tennessee. He previously served in Tennessee General Assembly first in the Tennessee House of Representatives and later in the Tennessee State Senate in which he represented Tennessee's District 7 part of Knox County. On August 5 2010 Burchett was elected mayor of Knox County replacing Mike Ragsdale.", "category": "OfficeHolder"} +{"text": " Daniel Lawrence Dan Whitney (born February 17 1963) best known by his stage name and character Larry the Cable Guy is an American stand-up comedian actor voice actor and former radio personality.", "category": "Artist"} +{"text": " Renealmia is a plant genus in the family Zingiberaceae. Species include: Renealmia alpinia Renealmia aurantifera Renealmia cernua Renealmia dolichocalyx Renealmia oligotricha Renealmia sessilifolia Renealmia thrysoidesE.g. Alpinia nutans was formerly placed herein too.", "category": "Plant"} +{"text": " Jeff Chapman (born July 17 1969 in Brunswick Georgia) is the bass singer for the Kingdom Heirs. He has been a member of the group since 2002. He has previously traveled with Bob Wills The Sound The Anchormen and The Blackwoods.He has twice been nominated as favorite bass in the Singing News fan awards.Chapman has a wife Angie two sons Justin and Sean and daughter Taylor.", "category": "Artist"} +{"text": " Arenaria ursina is a species of flowering plant in the pink family known by the common name Bear Valley sandwort.", "category": "Plant"} +{"text": " Living Fossil is a classic science fiction story on the concepts of human extinction and future evolution by L. Sprague de Camp. It was first published in the magazine Astounding Science-Fiction for February 1939. It first appeared in book form in the anthology A Treasury of Science Fiction (Crown Publishers 1948); it later appeared in the anthologies Gates to Tomorrow (Atheneum 1973) and The SFWA Grand Masters Volume 1 (Tor Books 1999).", "category": "WrittenWork"} +{"text": " Brachyglottis huntii commonly called rautini or Chatham Island Christmas tree is a species in the Asteraceae family and is found only on the Chatham Islands in New Zealand.", "category": "Plant"} +{"text": " Luktvatnet is a lake that lies in the northern part of the municipality of Vefsn in Nordland county Norway. The lake lies between the mountains Korgfjellet and Lukttinden about 5 kilometres (3.1 mi) south of Elsfjord. The European route E06 highway passes along the northern shore of the lake.", "category": "NaturalPlace"} +{"text": " The IAR 79 is a bi-engine bomber military reconnaissance aircraft with a wood and metal structure that saw service in World War II built under licence in Brasov Romania by Industria Aeronautic\u0103 Rom\u00e2n\u0103", "category": "MeanOfTransportation"} +{"text": " Enrico Perucconi (born 4 January 1925 in Morazzone Varese Italy) was an Italian athlete who competed mainly in the 100 metres.", "category": "Athlete"} +{"text": " Central National-Gottesman Inc. is one of the world's largest distributors of pulp paper paperboard and newsprint. The firm's products are sold in over 75 countries through a network of 43 offices located in the United States and abroad. With annual revenues exceeding $3 billion Forbes ranked Central National-Gottesman 137th in its annual list of The Largest Private Companies.", "category": "Company"} +{"text": " The Kout Food Group is a Kuwaiti-based conglomerate founded in 1982.In Kuwait it operates franchises of Burger King Pizza Hut and Taco Bell.Its UK arm Kout Food Group Restaurants UK it operates under brands such as Burger King KFC and Maison Blanc. In August 2013 it acquired the Little Chef chain for \u00a315 million.", "category": "Company"} +{"text": " Fab Five: The Texas Cheerleader Scandal is a Lifetime Television made-for-TV drama film starring Jenna Dewan Ashley Benson and Tatum O'Neal and directed by Tom McLoughlin. The film premiered on August 2 2008. It is based on a true story which occurred at McKinney North High School in McKinney Texas in 2006 in which five teenage cheerleaders became notorious for bullying truancies violations of the school dress code and general disrespect to the school community and authority.", "category": "Film"} +{"text": " Qadi Mahalleh (Persian: \u0642\u0627\u062f\u064a \u0645\u062d\u0644\u0647\u200e also Romanized as Q\u0101d\u012b Ma\u1e29alleh) is a village in Pazevar Rural District Rudbast District Babolsar County Mazandaran Province Iran. At the 2006 census its population was 228 in 59 families.", "category": "Village"} +{"text": " Eungella Dam is one of Queensland's more established freshwater fisheries. Eungella has made a name for producing extra oversized Sooty grunter and more recently Barramundi.Eungella Dam was constructed in 1969 to meet the requirements of a thermal power station at Collinsville and the town water requirement of Collinsville and Scottsville.", "category": "NaturalPlace"} +{"text": " The American Motor Car Company was a short-lived company in the automotive industry founded in 1906 lasting until 1913. It was based in Indianapolis Indiana United States. The American Motor Car Company pioneered the underslung design.", "category": "Company"} +{"text": " Hawkeye & Mockingbird was a comic book ongoing series published by Marvel Comics starring superheroes Hawkeye and Mockingbird.", "category": "WrittenWork"} +{"text": " Margaret Anderson Kelliher (born March 11 1968) is a Minnesota politician and a former member of the Minnesota House of Representatives. A member of the Minnesota Democratic\u2013Farmer\u2013Labor Party she represented District 60A which includes portions of the city of Minneapolis in Hennepin County located in the Twin Cities metropolitan area. First elected in 1999 she served until 2011 also serving as the Speaker from 2007 to 2011.", "category": "OfficeHolder"} +{"text": " John Whitlow Wyatt (September 27 1907 \u2013 July 16 1999) was a professional baseball pitcher. He played all or part of sixteen seasons in Major League Baseball for the Detroit Tigers (1929\u201333) Chicago White Sox (1933\u201336) Cleveland Indians (1937) Brooklyn Dodgers (1939\u201344) and Philadelphia Phillies (1945). While injuries sidetracked much of Wyatt's early career he is most famous for his performance in 1941 when his team (the Dodgers) won the National League pennant.", "category": "Athlete"} +{"text": " William Thomas Burton (31 January 1878 in Black Rock St Michael Barbados \u2013 22 August 1946 St Michael Barbados) was a coloured West Indian cricketer best known as a member of the 1900 and 1906 West Indian tourists to England. He is generally known as Tommie Burton.He was the son of a black mother and a white father. He was brought up in Barbados and served for some years there as a practice bowler and in trial matches.", "category": "Athlete"} +{"text": " Tulemalu Lake is a lake in Kivalliq Region Nunavut Canada.", "category": "NaturalPlace"} +{"text": " Sten Stjernqvist is a Swedish former footballer who played as a forward.", "category": "Athlete"} +{"text": " David Parlett (born 1939) is a games scholar from South London who has studied both card games and board games. His published works include many popular books on games and the more academic volumes The Oxford Guide to Card Games and The Oxford History of Board Games both now out of print. Parlett also invented a number of board games the most successful of which is Hare and Tortoise (1974). The German edition was awarded Spiel des Jahres (Game of the Year) in 1979.", "category": "Artist"} +{"text": " Karl Nabersberg (sometimes written as Carl Nabersberg) was a German youth leader.Nabersberg was the son of a Crefeld shopkeeper. In 1923 he joined the Jugendorganisation the forerunner of the Hitler Youth in his home town. On 28 December 1925 he was admitted as a member of the National Socialist German Workers' Party (member number 26269) and as a member of the Sturmabteilung.", "category": "OfficeHolder"} +{"text": " \u0160etonje is a village situated in Petrovac na Mlavi municipality in Serbia.", "category": "Village"} +{"text": " Dr. Joseph de Graft-Johnson (1933\u20131999) was an engineer academic and politician. He became the Vice-President of Ghana between 1979 and 1981.", "category": "OfficeHolder"} +{"text": " Patties Foods (previously Patties Bakery) is an Australian food manufacturing company that produces meat pies baked goods frozen fruits and pre-made desserts. Headquartered in Bairnsdale Victoria Australia Patties Foods is represented in the Australian market by the Four'N Twenty Patties Herbert Adams Creative Gourmet Nanna's and Chefs Pride brands. Patties is the largest meat pie producing company in Australia and the world.", "category": "Company"} +{"text": " Double Butte is the 2579-foot (786 m) mountain summit distinguished by two buttes (the other at abou 2480 feet or 756 metres) in Riverside County California. It is the western most summit of a mountain range north of Winchester California east of Perris Valley and west of the San Jacinto Valley. The eastern ridge is composed primarily of metamorphic rock of the Triassic - Jurassic French Valley formation.", "category": "NaturalPlace"} +{"text": " Mount Carmel \u2013 Blytheswood Public School is an elementary school in the north end of Leamington Ontario Canada. It is part of the Greater Essex County District School Board and serves students from JK to Grade 8 from the communities of Blytheswood and Mount Carmel and surrounding areas.", "category": "EducationalInstitution"} +{"text": " La combi asesina (The Killer Combination) is a 1982 Mexican film. It was directed by Gustavo Alatriste.", "category": "Film"} +{"text": " Halimium ocymoides (basil-leaved rock rose) syn. Cistus algarvensis is a species of flowering plant in the family Cistaceae native to southern Portugal and southern Spain. It is an erect evergreen shrub growing to 60 cm (24 in) tall by 100 cm (3 ft) wide with woolly grey-green leaves and bright yellow flowers in spring. The flowers may have a dark brown blotch at the base of each petal.In cultivation this plant requires a sandy soil and full sun.", "category": "Plant"} +{"text": " Kaala Patthar (English: Black Stone) is a 1979 Indian Bollywood action/drama film. It was produced and directed by Yash Chopra. The story was written by Salim-Javed. This film is the fourth collaboration between Amitabh Bachchan Shashi Kapoor and director Yash Chopra after the hugely successful Deewaar (1975) Kabhie Kabhie (1976) and Trishul (1978). However this film did average business at the box office. It was nominated for Filmfare awards.", "category": "Film"} +{"text": " Martin G.S. Mansergh (born 31 December 1946) is a former Irish Fianna F\u00e1il politician and historian. He was a Teachta D\u00e1la (TD) for the Tipperary South constituency from 2007 until 2011. He was previously a Senator from 2002 to 2007. He played a leading role in formulating Fianna F\u00e1il policy on Northern Ireland.", "category": "OfficeHolder"} +{"text": " Shriniwas Ganesh Sardesai (1907-1996) popularly known as S.G. Sardesai was an Indian freedom fighter from Maharashtra and one of the great communist leaders produced by the communist movement in India. He is author of the book Progress and conservatism in ancient India famous for his profound theoretical analysis. He was the Central Executive Committee of pre-split Communist Party of India during the Indo-china conflict.", "category": "OfficeHolder"} +{"text": " USS Tuluran (AG-46) \u2013 also known as USS Lake Superior (ID-2995) \u2013 was a commercial cargo ship acquired by the U.S. Navy for service during both World War I when she was known as USS Lake Superior and also during World War II when she was known as USS Tuluran.", "category": "MeanOfTransportation"} +{"text": " The American Journal of Gastroenterology is a peer-reviewed medical journal published for the American College of Gastroenterology by the Nature Publishing Group.", "category": "WrittenWork"} +{"text": " William Lindsay (September 4 1835 \u2013 October 15 1909) was a Democratic U.S. Senator from Kentucky from 1893 to 1901.Born near Lexington Virginia Lindsay attended the common schools and settled in Clinton Kentucky in 1854. There he taught school and studied law. He was admitted to the bar and commenced practice in Clinton in 1858.", "category": "OfficeHolder"} +{"text": " Brian Schroeder (a.k.a. Pushead) is an artist record label owner and writer within the hardcore punk and heavy metal field. He has created artwork for many bands artists and athletes including Metallica The Misfits Dr. Dre Travis Barker Craig Johnson and Kool Keith. He has designed many record covers T-shirts skateboards and a pair of Nike SB Dunks. His record label Pusmort Records has released albums by Negative Gain Poison Idea and Final Conflict.", "category": "Artist"} +{"text": " Panicum anceps is a species of grass known by the common name beaked panicgrass. It is native to the southeastern United States where it occurs as far north as New Jersey and as far west as Kansas and Texas.This species is a rhizomatous perennial grass with stems growing up to 1.3 meters tall. The leaves have erect blades up to half a meter tall. The inflorescence is a panicle up to 40 centimeters long bearing pale green or yellowish spikelets. The grass produces an abundance of seed.", "category": "Plant"} +{"text": " Shukan ST is a weekly newspaper published by The Japan Times for learners of English language. It is originally titled as Student Times but changed to Shukan ST since a significant portion of its readers are not students. It has articles on news movie lifestyle in English-speaking countries opinions and other kinds attracting learners of English and helping them with notes on terms.", "category": "Company"} +{"text": " The Tiger Hotel is a hotel in Columbia Missouri. Built as a hotel in 1928 the building later housed a retirement home and banquet center. In 2012 the building was fully restored and reopened as a boutique hotel. It was listed on the National Register of Historic Places in 1980.", "category": "Building"} +{"text": " Emi Motoi (\u672c\u4e95 \u3048\u307f Motoi Emi born October 11 in Kanagawa) is a Japanese voice actress.", "category": "Artist"} +{"text": " The Hudson River is a 49.5-mile-long (79.7 km) tributary of the Broad River in the U.S. state of Georgia. Via the Broad River it is part of the Savannah River watershed.The headwaters are in Banks County near the city of Homer. Grove Creek feeds into the Hudson near the Franklin County line. The river then constitutes most of the southern border of Franklin County separating it from Madison County.", "category": "NaturalPlace"} +{"text": " This article details Car Nos. 10\u201313 of the Manx Electric Railway on the Isle of Man.This was the third batch of motorcars delivered to the railway in 1895 at the same time as the cars for the new Snaefell Mountain Railway were delivered. They were constructed to a very similar design to those provided for the mountain line.", "category": "MeanOfTransportation"} +{"text": " Catharanthus roseus commonly known as the Madagascar rosy periwinkle is a species of Catharanthus native and endemic to Madagascar. Other English names occasionally used include Cape periwinkle rose periwinkle rosy periwinkle and old-maid.", "category": "Plant"} +{"text": " Thapanzeik is a village in Homalin Township Hkamti District in the Sagaing Region of northwestern Burma.", "category": "Village"} +{"text": " USS Spiegel Grove (LSD-32) was a Thomaston-class dock landing ship of the United States Navy. She was named for Spiegel Grove the home and estate in Fremont Ohio of Rutherford B. Hayes the 19th President of the United States.", "category": "MeanOfTransportation"} +{"text": " Acmella is a genus of thirty species of plants in the aster family Asteraceae. It is native to the Americas and has been introduced to Asia Africa the Pacific islands and Australia.One familiar species is Acmella oleracea which has been widely cultivated for centuries. It is used for food and medicine and as an insecticide and an ornamental plant.", "category": "Plant"} +{"text": " Mirbelia is a plant genus belonging to the Fabaceae family. It is endemic to Australia occurring in every mainland state except South Australia.", "category": "Plant"} +{"text": " Nigma puella is a species of spider belonging to the family Dictynidae. It is found in Europe Azores Madeira Canary Islands and parts of North Africa.Like most members of the family this is a small spider but the female is striking with a light green abdomen marked with a bold maroon blotch and a variable amount of barring in the same colour. The male is reddish-brown. This species makes a horizontal web over the top surface of a leaf.", "category": "Animal"} +{"text": " The Madrisa (or Madrisahorn) is a mountain in the R\u00e4tikon mountain range overlooking Klosters in the Swiss canton of Graub\u00fcnden. Its summit (2826 metres) is located near the Austrian border.The Madrisa is constituted by several secondary summits notably the Gargeller Madrisa (2770 metres) overlooking Gargellen in Austria.Ski lifts up to 2600 metres are located on the Klosters side.", "category": "NaturalPlace"} +{"text": " Temporary Temple is a live album by Psychic TV. The album was recorded on July 28 1984 in London and released on 12 vinyl. It was later coupled with another concert and released on CD as Temporary Temple & Atonal.", "category": "Album"} +{"text": " La hija de Juan Sim\u00f3n (Juan Sim\u00f3n's Daughter) is a musical play by Nemesio M. Sobrevila which has been made into two Spanish films. It is also the name of the title track and the song has been recorded by numerous artists such as Leonardo Favio.The first film directed by Jos\u00e9 Luis S\u00e1enz de Heredia was released in 1935 and starred Angelillo Pilar Mu\u00f1oz and Manuel Arb\u00f3. Luis Bu\u00f1uel was the executive producer for Film\u00f3fono and had a small role as an actor.", "category": "Film"} +{"text": " Book Of Matches is a poetry book written by Simon Armitage first published in 1993 by Faber and Faber. Several poems featured in the book are studied as part of the GCSE English Literature examination in the UK.The book is written in three sections the first (Book of Matches) containing 30 short sonnets. Each is meant to be read within 20 seconds the amount of time it would take for a match to be lit and burn out.", "category": "WrittenWork"} +{"text": " The Last Supper is the fourth album released by American stand-up comedian Jim Gaffigan. It focuses largely on his love of food.", "category": "Album"} +{"text": " The Miami Center is a skyscraper in downtown Miami Florida. Although Miami Center is not the city's tallest building it is a symbol of early downtown. Built in 1983 it is older compared with most of the taller buildings in Miami which have been built in the last decade. In addition the Miami Center is immediately adjacent to Bayfront Park and is unobstructed when looking at the skyline from Miami Beach to the east. The building is 484 ft (148 m) tall and has 34 floors.", "category": "Building"} +{"text": " Duboisia hopwoodii is a shrub native to the arid interior region of Australia. Common names include pituri pitchuri thornapple or pitcheri. It has an erect habit usually growing to between 1 and 3 metres in height and has long narrow leaves. Flowers are white and bell-shaped with violet-striped throats. These appear between June and November in the species native range followed by purple-black rounded berries which are 3 to 6 mm in diameter.", "category": "Plant"} +{"text": " Jelenin svet (Jelena's World) is a 2008 independent documentary film written and directed by Tanja Brzakovi\u0107 about former World No. 1 female tennis player Jelena Jankovi\u0107.", "category": "Film"} +{"text": " Jay Cashman Inc. is an American heavy-construction company based in Quincy Massachusetts with satellite offices in Boston Jupiter Florida and Staten Island New York. As of 2006 the company has about 1000 employees. The company was one of the major contractors on the Boston's Central Artery/Tunnel Project. In 2004 Jay Cashman Inc.", "category": "Company"} +{"text": " Hashemanli (Persian: \u0647\u0627\u0634\u0645\u0646\u0644\u064a\u200e also Romanized as H\u0101shemanl\u012b; also known as H\u0101shem El\u00e1) is a village in Jafarbay-ye Jonubi Rural District in the Central District of Torkaman County Golestan Province Iran. At the 2006 census its population was 135 in 27 families.", "category": "Village"} +{"text": " Rani Kasula Rangamma is a Telugu film starring Chiranjeevi.", "category": "Film"} +{"text": " The 20/20 Experience \u2013 The Complete Experience is a compilation album by American singer-songwriter Justin Timberlake. It was released on September 27 2013 by RCA Records.", "category": "Album"} +{"text": " R.C. Bigelow Inc better known as the Bigelow Tea Company is an American tea company based in Fairfield Connecticut. The company was founded by Ruth C. Bigelow in the late 1940s based on a recipe she marketed as Constant Comment tea. Bigelow is still a 100% family-owned business that markets over 50 varieties of tea including black and green as well as herbal teas all of which are still blended in Fairfield. They also own America's only tea plantation in Charleston South Carolina.", "category": "Company"} +{"text": " Thomas Eyre(fl. 1890s) was a footballer who made 65 appearances in the Football League playing for Lincoln City. He played at left back. Either side of Lincoln he played for Ashfield and Hamilton Academical in Scotland.", "category": "Athlete"} +{"text": " Malleable Iron Range Company was a company that existed from 1896 to 1985 and primarily produced kitchen ranges made of malleable iron but also produced a variety of other related products. The company's primary trademark was 'Monarch' and was colloquially often referred to as the Monarch Company or just Monarch.", "category": "Company"} +{"text": " The Chiltern School is a coeducational special school located over two sites in Dunstable and Houghton Regis in Bedfordshire England. The school accepts pupils from all over the Central Bedfordshire area.The school was formed in 2012 from the merger of Glenwood School in Dunstable and Hillcrest School in Houghton Regis.", "category": "EducationalInstitution"} +{"text": " Kim Dae-Eun (born September 17 1984) is a South Korean gymnast. He is the 2004 Olympic All-around silver medalist. He won the gold medal on the parallel bars at the 2007 World Artistic Gymnastics Championships.Kim was part of the South Korean team that won the bronze medal in the team event at the 2006 Asian Games.", "category": "Athlete"} +{"text": " Arayik Vladimirovich Harutyunyan (Armenian: \u0531\u0580\u0561\u0575\u056b\u056f \u0540\u0561\u0580\u0578\u0582\u0569\u0575\u0578\u0582\u0576\u0575\u0561\u0576 Russian: \u0410\u0440\u0430\u0438\u043a \u0410\u0440\u0443\u0442\u044e\u043d\u044f\u043d) (born 14 December 1973) is the current Prime Minister of the Nagorno-Karabakh Republic. He was suggested by the President of Nagorno-Karabakh Bako Sahakyan and was unanimously approved by the Parliament of Karabakh on 14 September 2007 by 32 out of 32 present parliamentarians.", "category": "OfficeHolder"} +{"text": " Shelton Hank Williams also known as Hank Williams III and Hank 3 (born December 12 1972) is an American musician singer and multi-instrumentalist including guitar bass drums banjo and vocals. In addition to his honky tonk recordings Williams' style alternates between country punk and metal.", "category": "Artist"} +{"text": " Helicella orzai is a species of air-breathing land snails terrestrial pulmonate gastropod mollusks in the family Hygromiidae the hairy snails and their allies. This species is endemic to Spain.", "category": "Animal"} +{"text": " Gro\u00dfe Schmalenau is a river of North Rhine-Westphalia Germany.", "category": "NaturalPlace"} +{"text": " The Tupolev ANT-29 (military designation DIP \u2013 Dvukhmotorny istrebitel pushechny twin-engined cannon fighter) was a 1930s twin-engined cannon-armed fighter designed by Alexander Arkhangelsky and built by Tupolev.Design work started in 1932 on a twin-engined aircraft capable of carrying two APK-100 cannons. The resulting design was the ANT-29 and it first flew in February 1935. A monoplane with a tall and narrow fuselage and powered by two Hispano-Suiza 12Ybrs engines.", "category": "MeanOfTransportation"} +{"text": " Charles Corm (1894-1963) was a Lebanese writer businessman and philanthropist. He is considered to be the leader of the Phoenicianism movement in Lebanon which ignited a surge of nationalism that led to Lebanon's independence. In a country torn by sectarian conflicts Corm's intention was to find a common root shared by all Lebanese beyond their religious beliefs (the Phoenicians were pagans).", "category": "Artist"} +{"text": " Joseph Hubert Ruetz (October 21 1916 \u2013 January 2 2003) was a professional football player in the All-America Football Conference for the Chicago Rockets in 1946 and 1948. Prior to that he played at the collegiate level while attending the University of Notre Dame. He played guard for the Irish with the exception of playing one season at quarterback. In 1938 he graduated from Notre Dame with cum laude honors.", "category": "Athlete"} +{"text": " The Reef House is a historic house located at 411 S. Poplar St. in Carbondale Illinois. William A. Reef built the house for his family circa 1892. The Queen Anne-style cottage may have been designed by local carpenter A. M. Etherton though records of its designer do not exist. The house features fishscale shingle siding on its second floor and clapboard siding on its first; the clapboard siding is adorned with stickwork.", "category": "Building"} +{"text": " MAKO Surgical Corp. (Stryker Medical) is a publicly traded medical device company based in Florida. On September 25 2013 the Board of Directors of Mako Surgical accepted a deal to merge with Stryker Medical for $1.65B subject to shareholder approval.", "category": "Company"} +{"text": " Pop Carn is a 2003 Indian Tamil film written and directed by actor-cum-director Nassar and starring Mohanlal and Simran Bagga in lead roles and introducing newcomers Kunal Shah and Jyothi Nawal. The film which had music scored by Yuvan Shankar Raja was released on 30 January 2003 but flopped at the box office. Nonetheless the film was dubbed into Malayalam and released in 2007 under the same name.", "category": "Film"} +{"text": " USNS Mount Baker (T-AE-34) is the seventh of eight Kilauea-class ammunition ships to serve with the Military Sealift Command. She is the second U.S. Navy ship to bear the name and is named for Mount Baker a 10781-foot volcano in the Cascade Range of Washington. Ammunition ships operated by Military Sealift Command provide logistic support to US Navy ships at sea.Mount Baker was built by Ingalls Shipbuilding Pascagoula Mississippi.", "category": "MeanOfTransportation"} +{"text": " Dansere is an album by Jan Garbarek. The album was recorded in November 1975 and features the Bobo Stenson Quartet.", "category": "Album"} +{"text": " Divraz (Persian: \u062f\u064a\u0648\u0631\u0632\u200e also Romanized as D\u012bvraz) is a village in Bala Khiyaban-e Litkuh Rural District in the Central District of Amol County Mazandaran Province Iran. At the 2006 census its population was 393 in 95 families.", "category": "Village"} +{"text": " The D\u0103ih\u0103\u021ba\u0219u River is a tributary of the Dumbr\u0103vanu River in Romania.", "category": "NaturalPlace"} +{"text": " Zeisters also known as Fat Guy Goes Nutzoid is a 1986 comedy film produced by Troma Entertainment. Troma was originally set to title the film Fat Boy Goes Nutzoid but at the request of the lawyers of the hip-hop group The Fat Boys it was changed to Fat Guy.", "category": "Film"} +{"text": " Paul Gobeil (born March 1 1942 in Saint-R\u00e9mi-de-Tingwick Quebec) is a former Canadian politician and businessman.From 1985 to 1989 Mr. Gobeil was a Liberal member of the National Assembly for the riding of Verdun and served as Minister assigned to Administration President of the Treasury Board and as Minister of International Affairs for the Government of Quebec.", "category": "OfficeHolder"} +{"text": " Ruff Ryders: Past Present Future is the fifth compilation album from American hip hop record label Ruff Ryders Entertainment released on November 21 2011.", "category": "Album"} +{"text": " Ridi Viharaya (Sinhala: \u0dbb\u0dd2\u0daf\u0dd3 \u0dc0\u0dd2\u0dc4\u0dcf\u0dbb\u0dba) or Silver Temple is a 2nd-century BCE Theravada Buddhist temple in the village of Ridigama Sri Lanka. Built during the reign of Dutthagamani of Anuradhapura the temple is considered as the place where the silver ore which provided silver to complete Ruwanwelisaya; one of the largest stupa in Sri Lanka was discovered.", "category": "Building"} +{"text": " Grand Canyon Preparatory Academy is a public charter college preparatory school in Tempe Arizona.", "category": "EducationalInstitution"} +{"text": " Aricoceras is an extinct genus of the Adrianitidae family. They are an extinct group of ammonoid which are shelled cephalopods related to squids belemnites octopuses and cuttlefish and more distantly to the nautiloids.", "category": "Animal"} +{"text": " Blackburn High School is a public secondary school for girls and boys in years 7 to 12 in Blackburn a suburb of Melbourne Victoria Australia. Blackburn High School is an outstanding secondary school for aspiring young men and women. It aims to educate tomorrow's minds today. Started in 1956 the school has a proud tradition of academic excellence and exceptional music achievement.The school is nationally recognised as a leading educational institution for music education.", "category": "EducationalInstitution"} +{"text": " Chris Nieratko (born February 19 1976) is an American humorist and author. Nieratko is a past editor of Big Brother Magazine and currently reviews pornographic films for Vice magazine as well as being the author of the related Skinema book. He also appeared on MTV's Jackass.", "category": "Artist"} +{"text": " Warlock is a 1959 film released by Twentieth Century Fox and shot in DeLuxe Color and CinemaScope. It is a Western adapted from the novel by Oakley Hall (screenplay written by Robert Alan Aurthur).", "category": "Film"} +{"text": " Sieniawa [\u0255e\u02c8\u0272ava] is a village in the administrative district of Gmina Raba Wy\u017cna within Nowy Targ County Lesser Poland Voivodeship in southern Poland. It lies approximately 5 kilometres (3 mi) south-east of Raba Wy\u017cna 10 km (6 mi) north-west of Nowy Targ and 59 km (37 mi) south of the regional capital Krak\u00f3w.The village has a population of 1900.", "category": "Village"} +{"text": " Michael Adam (born 9 December 1984) is a German politician. He has been District Administrator (Landrat) of Regen since 2011.", "category": "OfficeHolder"} +{"text": " Thunderbird High School is a public high school located in northwestern Phoenix Arizona. The school is a part of the Glendale Union High School District.", "category": "EducationalInstitution"} +{"text": " Nayef Al Khater (born May 10 1978) is a Qatari football player. He currently plays for Al Wakrah as a defender.", "category": "Athlete"} +{"text": " Black Cobra Woman (Italian: Eva nera) also known as Black Cobra is an Italian 1976 exploitation movie written and directed by Joe D'Amato.", "category": "Film"} +{"text": " Joe Cuba a.k.a Sonny (April 22 1931 \u2013 February 15 2009) was a musician of Puerto Rican descent who was known as the Father of Latin Boogaloo.", "category": "Artist"} +{"text": " Jacob LeBlanc (born February 2 1981 in Auburn California) is an American retired professional soccer player.", "category": "Athlete"} +{"text": " Kevin B. Kamenetz is the 12th and current County Executive of Baltimore County Maryland serving since 2010. He is a member of the Democratic Party. He previously served as a four-term County Councilman representing the Second District of Baltimore County.", "category": "OfficeHolder"} +{"text": " Thomas Frederick Fred Peart Baron Peart PC (30 April 1914 \u2013 26 August 1988) was a British Labour politician who served in the Labour governments of the 1960s and 1970s and was a candidate for Deputy Leader of the Party.", "category": "OfficeHolder"} +{"text": " Grand Lake is a large lake in the interior of Newfoundland of the Canadian province of Newfoundland and Labrador. It has an area of 534 km\u00b2 making it the largest lake on Newfoundland. Consequently it is one of if not the deepest.", "category": "NaturalPlace"} +{"text": " A Colossal Failure of Common Sense: The Inside Story of the Collapse of Lehman Brothers is a 2009 non-fiction book written by Lawrence G. McDonald and Patrick Robinson which chronicles the events surrounding the bankruptcy of Lehman Brothers in the context of the financial crisis of 2007\u20132010 and the subprime mortgage crisis.", "category": "WrittenWork"} +{"text": " Thatching (31 May 1975 \u2013 1999) was an Irish Thoroughbred racehorse and sire. The horse's early career was delayed and disrupted by injury and he did not show his best form until switched to sprinting distances in the spring of 1979 when he won the Duke of York Stakes. He improved further when equipped with blinkers that summer recording impressive victories in both the Cork and Orrery Stakes and the July Cup.", "category": "Animal"} +{"text": " Ya\u015far Kurt (Armenian: \u0545\u0561\u0577\u0561\u0580 \u053f\u0578\u0582\u0580\u0569 b.August 16 1968 in Istanbul Turkey) is a Turkish-Armenian rock artist.", "category": "Artist"} +{"text": " Then and Now is a historical novel by W. Somerset Maugham. Set in Florence Italy during the Renaissance the story focuses on three months in the life of Niccolo Machiavelli the Florentine politician diplomat philosopher and writer in the early years of the 16th century. The book was first published by Heinemann in 1946.", "category": "WrittenWork"} +{"text": " Abdollah Masud-e Sofla (Persian: \u0639\u0628\u062f\u0627\u0644\u0647 \u0645\u0633\u0639\u0648\u062f\u0633\u0641\u0644\u064a\u200e also Romanized as \u2018Abdoll\u0101h Mas\u2018\u016bd-e Sofl\u00e1; also known as Abdollah Mas\u2019ood \u2018Abdoll\u0101h Mas\u2018\u016bd and Abdull\u0101h Mas\u016bd) is a village in Hesar-e Valiyeasr Rural District Avaj District Buin Zahra County Qazvin Province Iran. At the 2006 census its population was 72 in 15 families.", "category": "Village"} +{"text": " Springhill High School (SHS) is a secondary school in Springhill Nova Scotia Canada. SHS is part of the Chignecto-Central Regional School Board and is the only high school in the town of Springhill. The school is home to many sports teams and clubs. These include: basketball soccer badminton track and field softball Students Against Destructive Decisions drama club homework club and book club.", "category": "EducationalInstitution"} +{"text": " Charniele L. Herring (/\u0283\u0251r\u02c8n\u025bl \u02c8h\u025br\u026a\u014b/ shar-NEL HERR-ing; born September 25 1969) is an American politician. She has served in the Virginia House of Delegates since 2009 representing the 46th district made up the city of Alexandria and part of Fairfax County near Washington D.C. Herring is a member of the Democratic Party. She has been the House minority whip since 2012 and in December 2012 she was the first African-American to be elected chair of the Democratic Party of Virginia.", "category": "OfficeHolder"} +{"text": " Symmoca dodecatella is a moth of the Symmocidae family. It is found in Portugal and Spain.The wingspan is about 18\u201319 mm. The forewings are grey sprinkled with black mainly along the margin. The hindwings are grey.", "category": "Animal"} +{"text": " Ali Abbasov Mammad oglu (Azerbaijani: \u018fli Abbasov M\u0259mm\u0259d o\u011flu) (born 1953 in Azerbaijan) is the current Minister of Communications and Information Technologies of the Republic of Azerbaijan.", "category": "OfficeHolder"} +{"text": " Worlds Beyond was an American digest magazine of science fiction and fantasy fiction in 1950 and 1951. The magazine only issued three monthly issues from December 1950 to February 1951 but is notable for having printed stories by Cyril M. Kornbluth Jack Vance Mack Reynolds Graham Greene John Christopher Lester del Rey Judith Merril and others.Worlds Beyond was published by Hillman Periodicals and was edited by Damon Knight.", "category": "Company"} +{"text": " The Daily News Journal commonly abbreviated to DNJ is a newspaper serving Murfreesboro Tennessee Rutherford County and surrounding communities. Published in Murfreesboro it serves as the primary local newspaper with competition from The Murfreesboro Post and other publications. The newspaper is not in competition with The Tennessean of Nashville as both are owned by Gannett Company.The roots the DNJ date back to 1849 and the founding of Murfreesboro News.", "category": "WrittenWork"} +{"text": " Echinocereus fendleri is a species of cactus known by the common names pinkflower hedgehog cactus and Fendler's hedgehog cactus. It grows in deserts and woodlands in the Southwestern United States and Northeastern Mexico. It is most common in New Mexico.The taxonomy of the species is uncertain with authors recognizing up to eight varieties.", "category": "Plant"} +{"text": " Michael F. Kitt Snr (13 September 1914 \u2013 24 December 1974) was an Irish Fianna F\u00e1il politician and long-serving Teachta D\u00e1la (TD).He was elected to D\u00e1il \u00c9ireann for the first time at the 1948 general election for the Galway North constituency but lost his seat at the 1951 general election and failed to be elected again at the 1954 general election.", "category": "OfficeHolder"} +{"text": " Epidendrum mancum is an epiphytic orchid that grows in the tropical low elfin cloud forests of Ecuador and Amazonas Peru at altitudes of 2\u20143 km .", "category": "Plant"} +{"text": " Salempur Masanda is a village in Jalandhar District near the Jalandhar Cantonment in Punjab India.", "category": "Village"} +{"text": " Yaleh Gonbad (Persian: \u064a\u0644\u0647 \u06af\u0646\u0628\u062f\u200e; also known as Em\u0101mz\u0101deh Imamzade-Ele-Geumbez and Im\u0101mz\u0101deh) is a village in Ilat-e Qaqazan-e Gharbi Rural District Kuhin District Qazvin County Qazvin Province Iran. At the 2006 census its population was 429 in 105 families.", "category": "Village"} +{"text": " Popeyes Louisiana Kitchen is an American chain of fried chicken fast food restaurants founded in 1972 in New Orleans Louisiana. Often referred to as Popeyes and sometimes as Popeyes Chicken & Biscuits or Popeyes Chicken & Seafood[citation needed] it was acquired by Sandy Springs Georgia-based AFC Enterprises originally America's Favorite Chicken Company in 1993.", "category": "Company"} +{"text": " The White Umfolozi River originates just south of Vryheid KwaZulu-Natal South Africa and joins the Black Umfolozi River at 28\u00b020\u203258\u2033S 31\u00b058\u203246\u2033E to form the Umfolozi River before it flows east towards the Indian Ocean.", "category": "NaturalPlace"} +{"text": " The Albatros L 74 was a two-seated German training biplane produced by Albatros Flugzeugwerke. Only two were produced.", "category": "MeanOfTransportation"} +{"text": " The University of Nevada School of Medicine is an academic division of the University of Nevada Reno and grants the Doctor of Medicine (MD) degree. The School of Medicine was founded in 1969 as the first medical school in the state of Nevada. More than 1500 MDs have graduated from the School of Medicine. The pre-clinical campus is located in Reno but the third and fourth years can be spent in hospitals and clinics throughout Nevada.", "category": "EducationalInstitution"} +{"text": " Leon Kroll (December 6 1884 \u2013 October 25 1974) was an American painter and lithographer. Known as a figurative artist Life Magazine described him as the dean of U.S. nude painters yet he was an exceptional landscape painter and also produced an exceptional body of still life compositions.Born into a musical family on lower Second Avenue in New York City Kroll's father was a violinist and his cousin was William Kroll.", "category": "Artist"} +{"text": " Michael E. DeBakey High School for Health Professions at Qatar (DHSHP@Q in short) is a private international middle and secondary school in Doha Qatar. The school is a branch campus of Michael E. DeBakey High School for Health Professions of Houston Texas United States. Charlesetta Deason is the CEO and President.Named after Michael E. DeBakey the school opened in September 2008 with grades 8 through 10 with 100 students per grade; the school will ultimately cover grades 7-12.", "category": "EducationalInstitution"} +{"text": " The Richleighs of Tantamount is a children\u2019s historical novel written by British author Barbara Willard. It was originally published in the United Kingdom in 1966 by the publishers Constable before being published in the United States by Harcourt Brace & World in June 1967. C. Walter Hodges drew the line illustrations and painted the cover portrait for the original edition.", "category": "WrittenWork"} +{"text": " Ennea is a genus of air-breathing land snails terrestrial pulmonate gastropod mollusks in the family Streptaxidae.Ennea is the type genus of the subfamily Enneinae.", "category": "Animal"} +{"text": " Come Live with Me is a 1941 American romantic comedy film produced and directed by Clarence Brown and starring James Stewart and Hedy Lamarr. Based on a story by Virginia Van Upp the film is about a beautiful Viennese refugee seeking United States citizenship who arranges a marriage of convenience with a struggling writer.", "category": "Film"} +{"text": " St. Thomas Episcopal Church is a parish church in the Episcopal Diocese of Iowa. The church is located in Sioux City Iowa United States at 1200 Douglas Street. The church building is listed on the National Register of Historic Places.", "category": "Building"} +{"text": " Nuno Daniel Costeira Valente (born 22 November 1991 in Ada\u00fafe - Braga ) is a Portuguese footballer who plays for Vizela on loan from S.C. Braga as a midfielder.", "category": "Athlete"} +{"text": " Jaagoo (Bengali: \u099c\u09be\u0997\u09cb) is a Bangladeshi sports based romantic Movies. Its writer and director Khijir Hayat Khan. Adnan Karim sponsored youth football game built this film was released in 2010. The film is produced by Sharjeel Karim and Adnan Karim and directed by Khijir Hayat Khan who has written the story screenplay and dialogues of the film. The film features Ferdous Ahmed and Afsana Ara Bindu in lead roles and with supporting Arefin Shuvo Tariq Anam Ronok Hasaan and many more.", "category": "Film"} +{"text": " John Edward Hatton AO (born 29 May 1933) is former Australian politician and an National Trust of Australia nominated Australian Living Treasure. He was the independent member of the Legislative Assembly of the New South Wales parliament for the seat of South Coast from 1973 to 1995. Notably the allegations about police corruption Hatton raised in Parliament resulted in the Wood Royal Commission. He is currently a social activist in his local community.", "category": "OfficeHolder"} +{"text": " Trichoptilus subtilis is a moth of the Pterophoridae family that is known from South Africa.", "category": "Animal"} +{"text": " Sin\u00e9ad Madden (born in Galway Ireland) is an Irish singer-songwriter and fiddle player best known as a member of the Moya Brennan band. She also teaches at Waltons New School of Music in Dublin.", "category": "Artist"} +{"text": " Philip Sprint is a German footballer who currently plays for Hertha BSC.Sprint made his professional debut for Hertha BSC on 12 August 2012 in a 2. Bundesliga match against FSV Frankfurt coming on in the 50th minute for Marvin Knoll after starting goalkeeper Sascha Burchert had been sent off.", "category": "Athlete"} +{"text": " River Roads Mall was an enclosed shopping mall located in the city of Jennings a suburb of St. Louis Missouri United States. Opened in 1962 as one of the nation's first shopping malls the mall declined in the 1990s becoming a dead mall and eventually being shuttered in 1995. Demolition of the long-vacant mall began in 2006.", "category": "Building"} +{"text": " The Brown-patched Kangaroo lizard (Otocryptis wiegmanni) also called Wiegmann's Agama or Sri Lankan Kangaroo Lizard is a small ground dwelling agamid lizard endemic to the wet zone forests and lower mountain forests (up to 1300 metres) of Sri Lanka. It is commonly seen in the leaf litter of shady rain forests. When perceiving danger it spurts away quickly on its large hind legs and might eventually climb up a sapling or tree.", "category": "Animal"} +{"text": " Shiho Kawaragi (\u6cb3\u539f\u6728 \u5fd7\u7a42 Kawaragi Shiho born April 29 1976 in Tokyo) is a Japanese voice actress who works for Kenyu-Office. When voicing adult games and hentai OVAs she is also known as Kaname Yuzuki (\u67da\u6728\u304b\u306a\u3081 Yuzuki Kaname) She is currently married since March 2012.", "category": "Artist"} +{"text": " Down in the Shacks Where the Satellite Dishes Grow is the second album by the Judybats released in 1992.", "category": "Album"} +{"text": " Turn of Faith is a 2001 film directed by Charles Jarrott. It stars Ray Mancini and Mia Sara.", "category": "Film"} +{"text": " Frederick William Seward (July 8 1830 \u2013 April 25 1915) was the Assistant Secretary of State during the American Civil War serving in Abraham Lincoln's administration as well as under Andrew Johnson during Reconstruction and for over two years under Rutherford B. Hayes.", "category": "OfficeHolder"} +{"text": " Ivoprop Corporation founded in 1984 by Ivo Zdarsky is an American manufacturer of composite propellers for homebuilt and ultralight aircraft as well as airboats. The company headquarters is located in Bellflower California.Zdarsky started the company after carving his own propeller for a homebuilt ultralight trike that he flew from Cold War Czechoslovakia over the Iron Curtain to Vienna in 1984.", "category": "Company"} +{"text": " Wave Broadband is a provider of residential business and enterprise class cable TV broadband internet and telephone services on the West Coast currently serving about 400000 customers within communities in western Washington state Oregon Sacramento California and the San Francisco Bay Area. Wave Broadband provides services via their fiber-optic network and uses Northwest Open Access Network as the backbone for most of their service areas in Washington.", "category": "Company"} +{"text": " Andie Tong is a comic book artist known for his work on books such as Spectacular Spider-Man UK The Batman Strikes! and Tangent: Superman's Reign.", "category": "Artist"} +{"text": " Merdani is a village in the municipality of Busova\u010da Bosnia and Herzegovina.", "category": "Village"} +{"text": " Kamam (Persian: \u0643\u0627\u0645\u0645\u200e also Romanized as K\u0101mam) is a village in Mangur-e Sharqi Rural District Khalifan District Mahabad County West Azerbaijan Province Iran. At the 2006 census its population was 98 in 16 families.", "category": "Village"} +{"text": " Ficus greiffiana is a species of plant in the Moraceae family. It is found in Argentina Brazil Colombia and Guyana.", "category": "Plant"} +{"text": " Toni Amboaje is a Spanish singer who currently works for metal band Sauze which formed on early 2008.", "category": "Artist"} +{"text": " Mount Whittier is a mountain in Carroll County New Hampshire in the northern Ossipee Mountains. Named after John Greenleaf Whittier the peak is not to be confused with nearby Nickerson Mountain which was once known as Mount Whittier.There are no hiking trails on Mount Whittier. There was once a CCC alpine ski trail on the northern face.", "category": "NaturalPlace"} +{"text": " El Rompe Discoteka: The Mix Album is a 2007 album by Hector El Father.", "category": "Album"} +{"text": " e-Spirit is a commercial software company that develops and markets the FirstSpirit CMS Web content management system. The company was founded in 1999 in Dortmund Germany and established a US presence in 2011. The company's FirstSpirit CMS is a Java-based offering now in its fifth major release.[citation needed]", "category": "Company"} +{"text": " The Valley is the first novel by Barry Pilton published in 2005 by Bloomsbury. It is a humorous account of the effect of outsiders on the rural status quo in a fictional mid-Wales valley during the 1980s and is being adapted for television.", "category": "WrittenWork"} +{"text": " Sema Group plc was an Anglo-French IT services company. It was listed on the London Stock Exchange and was a constituent of the FTSE 100 Index but was acquired by Schlumberger in 2001.", "category": "Company"} +{"text": " Bent Hansen (born 1954) is a retired Danish ice hockey forward. He played for 18 years in Denmark for the R\u00f8dovre SIK and KSF. He also competed for the Danish national team. His son Jannik Hansen also played for the R\u00f8dovre team and was drafted into the NHL by the Vancouver Canucks in 2004. During his hockey career Hansen also worked as a carpenter.", "category": "Athlete"} +{"text": " Behind the Sun is a 2004 album by Dive.", "category": "Album"} +{"text": " Mungaru Male (English: Pre Monsoon Rain) is a 2006 Kannada language movie directed by Yograj Bhat and produced by E Krishnappa. The film stars Ganesh Pooja Gandhi Anant Nag Padmaja Rao in lead roles.", "category": "Film"} +{"text": " Megachile perihirta commonly known as the Western leafcutting bee is a bee in the genus Megachile. The bee is native to western North America ranging from Nebraska to Texas and Mexico west to California and north to British Columbia and Alberta and often inhabits meadows and orchards. The bee is black with long whitish-yellow hair more so below the thorax and abdomen. The abdomen however is mostly bare although each segment has scattered whitish hair.", "category": "Animal"} +{"text": " Sukeban Deka The Movie (\u30b9\u30b1\u30d0\u30f3\u5211\u4e8b) is a live action Japanese film that was released in 1987. The movie closely follows a TV and manga series Sukeban Deka written and illustrated by Shinji Wada. The movie stars Yoko Minamino and Yui Asaka who were also in the TV series. The movie was followed by Sukeban Deka II in 1988.", "category": "Film"} +{"text": " The Maple School District is a public school district in Douglas County Wisconsin United States based in Maple Wisconsin.", "category": "EducationalInstitution"} +{"text": " Mount Waverley Secondary College is a public secondary school located in the Melbourne suburb of Mount Waverley. The school consists of roughly 1900 students and is one of the largest in the state.The school consists of two campuses (Junior & Senior) both situated on Stephensons Road in Mount Waverley. The Junior site holds years 7 and 8 with year levels 9 to 12 at the Senior Campus. The campuses are a short walking distance apart.", "category": "EducationalInstitution"} +{"text": " Jon-Paul Roger JP Pietersen (born 12 July 1986 in Stellenbosch South Africa) is a South African rugby union footballer. He generally plays fullback or wing for the Sharks (in the Super Rugby competition) and the Natal Sharks in the Currie Cup. He played in more than 50 tests for the Springboks.", "category": "Athlete"} +{"text": " Deltocolpodes is a genus of beetles in the family Carabidae containing the following species: Deltocolpodes brendelli Morvan 1992 Deltocolpodes championi Morvan 1992 Deltocolpodes duluchus Morvan 1992 Deltocolpodes heinigeri Morvan 1992 Deltocolpodes jalepensis Morvan 1992 Deltocolpodes kirschenhoferi Morvan 1992 Deltocolpodes nepalensis Morvan 1992 Deltocolpodes perreaui Deuve 1985 Deltocolpodes rectangulus Morvan 1992 Deltocolpodes rolex Morvan 1992 Deltocolpodes salpensis Deuve 1985 Deltocolpodes sikkimensis Morvan 1992\u2191", "category": "Animal"} +{"text": " Stanhopea martiana is a species of orchid endemic to southwestern Mexico.", "category": "Plant"} +{"text": " Yawarmayu (Quechua yawar blood mayu river blood river hispanicized spelling Yahuarmayo) is a river in Peru located in the Puno Region Carabaya Province Ayapata District. It originates near the border of the districts Ayapata and Coasa. Its direction is mainly to the northwest where it meets Inambari River as a right affluent. The confluence is north of the village Yawarmayu (Yahuarmayo).", "category": "NaturalPlace"} +{"text": " The Charles Granke House at 406 S. Seventh St. in Hamilton Montana is a historic house that was built in 1906. It includes Colonial Revival and Queen Anne architecture. It was listed on the National Register of Historic Places in 1988. The listing included two contributing buildings.It was built in approximately 1906 by the Anaconda Copper Mining Company as a worker cottage for workers at the sawmill that operated in Hamilton until 1915. Charles W.", "category": "Building"} +{"text": " Passiflora monadelpha is a species of plant in the Passifloraceae family. It is endemic to Ecuador.", "category": "Plant"} +{"text": " Mangifera persiciformis or Peach Mango is a species of plant in the Anacardiaceae family. It is endemic to China.", "category": "Plant"} diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index d846ede42f..4f1812bd6e 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -1,3 +1,5 @@ +from typing import List + import matplotlib.pyplot as plt import numpy as np import pandas as pd @@ -8,7 +10,7 @@ @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) -def get_embedding(text, engine="davinci-similarity"): +def get_embedding(text: str, engine="davinci-similarity") -> List[float]: # replace newlines, which can negatively affect performance. text = text.replace("\n", " ") @@ -16,6 +18,20 @@ def get_embedding(text, engine="davinci-similarity"): return openai.Engine(id=engine).embeddings(input=[text])["data"][0]["embedding"] +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) +def get_embeddings( + list_of_text: List[str], engine="babbage-similarity" +) -> List[List[float]]: + assert len(list_of_text) < 2048, "The batch size should not be larger than 2048." + + # replace newlines, which can negatively affect performance. + list_of_text = [text.replace("\n", " ") for text in list_of_text] + + data = openai.Engine(id=engine).embeddings(input=list_of_text).data + data = sorted(data, key=lambda x: x["index"]) # maintain the same order as input. + return [d["embedding"] for d in data] + + def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) From 7ea5dde96e580785aa843cd64ca5fac0cb9e8133 Mon Sep 17 00:00:00 2001 From: Max Schaefer <54907921+max-schaefer@users.noreply.github.com> Date: Wed, 19 Jan 2022 22:24:27 +0000 Subject: [PATCH 010/914] Fix recording of reponse headers in exceptions. (#44) These were passed as positional arguments, but in the wrong position, which meant they were passed into the `json_body` parameter of the `OpenAIError` constructor. --- openai/api_requestor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 1923e9bac9..a79f8e0450 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -300,7 +300,7 @@ def _interpret_response_line( ) -> OpenAIResponse: if rcode == 503: raise error.ServiceUnavailableError( - "The server is overloaded or not ready yet.", rbody, rcode, rheaders + "The server is overloaded or not ready yet.", rbody, rcode, headers=rheaders ) try: if hasattr(rbody, "decode"): @@ -308,7 +308,7 @@ def _interpret_response_line( data = json.loads(rbody) except (JSONDecodeError, UnicodeDecodeError): raise error.APIError( - f"HTTP code {rcode} from API ({rbody})", rbody, rcode, rheaders + f"HTTP code {rcode} from API ({rbody})", rbody, rcode, headers=rheaders ) resp = OpenAIResponse(data, rheaders) # In the future, we might add a "status" parameter to errors From f4be8f2f57ab8e96af1460f1c25a130640b167a7 Mon Sep 17 00:00:00 2001 From: hallacy Date: Thu, 20 Jan 2022 19:34:35 -0500 Subject: [PATCH 011/914] Version 11.6 diff (#66) * Hallacy/pickleable exceptions (#109) * Exceptions in python are hard to pickle. Make Exceptions pickleable * Remove debug * Added tests * nit * Change embeddings call to openai.Embedding.create (#110) * Change embeddings call to openai.Embedding.create * And update model names * Untweak notebook * Support encoded embeddings response (#111) * Support encoded embeddings response * Removed imports * Add a couple of comments * Typo * Remove CA bundle (#112) * Remove CA bundle * Removed some references --- README.md | 4 +- examples/embeddings/Get_embeddings.ipynb | 11 +- openai/__init__.py | 4 +- openai/api_requestor.py | 1 - openai/api_resources/__init__.py | 1 + openai/api_resources/embedding.py | 58 + openai/api_resources/engine.py | 4 + openai/data/ca-certificates.crt | 3240 ---------------------- openai/embeddings_utils.py | 8 +- openai/error.py | 18 + openai/tests/test_exceptions.py | 39 + openai/version.py | 2 +- setup.py | 1 - 13 files changed, 136 insertions(+), 3255 deletions(-) create mode 100644 openai/api_resources/embedding.py delete mode 100644 openai/data/ca-certificates.crt create mode 100644 openai/tests/test_exceptions.py diff --git a/README.md b/README.md index 9ba8426716..4238f02210 100644 --- a/README.md +++ b/README.md @@ -84,10 +84,10 @@ openai.api_key = "sk-..." # supply your API key however you choose text_string = "sample text" # choose an embedding -model_id = "davinci-similarity" +model_id = "text-similarity-davinci-001" # compute the embedding of the text -embedding = openai.Engine(id=model_id).embeddings(input=text_string)['data'][0]['embedding'] +embedding = openai.Embedding.create(input=text_string, engine=model_id)['data'][0]['embedding'] ``` An example of how to call the embeddings method is shown in the [get embeddings notebook](https://github.com/openai/openai-python/blob/main/examples/embeddings/Get_embeddings.ipynb). diff --git a/examples/embeddings/Get_embeddings.ipynb b/examples/embeddings/Get_embeddings.ipynb index fb8a986f41..dd60ef5230 100644 --- a/examples/embeddings/Get_embeddings.ipynb +++ b/examples/embeddings/Get_embeddings.ipynb @@ -28,7 +28,7 @@ "source": [ "import openai\n", "\n", - "embedding = openai.Engine(id=\"davinci-similarity\").embeddings(input=\"Sample document text goes here\")['data'][0]['embedding']\n", + "embedding = openai.Embedding.create(input=\"Sample document text goes here\", engine=\"text-similarity-davinci-001\")['data'][0]['embedding']\n", "len(embedding)" ] }, @@ -50,14 +50,15 @@ "from tenacity import retry, wait_random_exponential, stop_after_attempt\n", "\n", "@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))\n", - "def get_embedding(text, engine=\"davinci-similarity\"):\n", + "def get_embedding(text, engine=\"text-similarity-davinci-001\"):\n", + "\n", "\n", " # replace newlines, which can negatively affect performance.\n", " text = text.replace(\"\\n\", \" \")\n", "\n", - " return openai.Engine(id=engine).embeddings(input = [text])['data'][0]['embedding']\n", + " return openai.Embedding.create(input=[text], engine=engine)['data'][0]['embedding']\n", "\n", - "embedding = get_embedding(\"Sample query text goes here\", engine=\"ada-search-query\")\n", + "embedding = get_embedding(\"Sample query text goes here\", engine=\"text-search-ada-query-001\")\n", "print(len(embedding))" ] }, @@ -75,7 +76,7 @@ } ], "source": [ - "embedding = get_embedding(\"Sample document text goes here\", engine=\"ada-search-document\")\n", + "embedding = get_embedding(\"Sample document text goes here\", engine=\"text-search-ada-doc-001\")\n", "print(len(embedding))" ] } diff --git a/openai/__init__.py b/openai/__init__.py index 1574cc0b38..5ca63e8c25 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -9,6 +9,7 @@ Answer, Classification, Completion, + Embedding, Engine, ErrorObject, File, @@ -31,7 +32,7 @@ proxy = None app_info = None enable_telemetry = False # Ignored; the telemetry feature was removed. -ca_bundle_path = os.path.join(os.path.dirname(__file__), "data/ca-certificates.crt") +ca_bundle_path = None # No longer used, feature was removed debug = False log = None # Set to either 'debug' or 'info', controls console logging @@ -40,6 +41,7 @@ "Answer", "Classification", "Completion", + "Embedding", "Engine", "ErrorObject", "File", diff --git a/openai/api_requestor.py b/openai/api_requestor.py index a79f8e0450..d09175e4bf 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -49,7 +49,6 @@ def _make_session() -> requests.Session: proxies = _requests_proxies_arg(openai.proxy) if proxies: s.proxies = proxies - s.verify = openai.ca_bundle_path s.mount( "https://", requests.adapters.HTTPAdapter(max_retries=MAX_CONNECTION_RETRIES), diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index 6d2cbe67cc..7f4731bacf 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -1,6 +1,7 @@ from openai.api_resources.answer import Answer # noqa: F401 from openai.api_resources.classification import Classification # noqa: F401 from openai.api_resources.completion import Completion # noqa: F401 +from openai.api_resources.embedding import Embedding # noqa: F401 from openai.api_resources.engine import Engine # noqa: F401 from openai.api_resources.error_object import ErrorObject # noqa: F401 from openai.api_resources.file import File # noqa: F401 diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py new file mode 100644 index 0000000000..5bbd8e39d4 --- /dev/null +++ b/openai/api_resources/embedding.py @@ -0,0 +1,58 @@ +import base64 +import time + +import numpy as np + +from openai import util +from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource +from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.error import InvalidRequestError, TryAgain + + +class Embedding(EngineAPIResource, ListableAPIResource, DeletableAPIResource): + engine_required = True + OBJECT_NAME = "embedding" + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new embedding for the provided input and parameters. + + See https://beta.openai.com/docs/api-reference/embeddings for a list + of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + if kwargs.get("model", None) is None and kwargs.get("engine", None) is None: + raise InvalidRequestError( + "Must provide an 'engine' or 'model' parameter to create an Embedding.", + param="engine", + ) + + user_provided_encoding_format = kwargs.get("encoding_format", None) + + # If encoding format was not explicitly specified, we opaquely use base64 for performance + if not user_provided_encoding_format: + kwargs["encoding_format"] = "base64" + + while True: + try: + response = super().create(*args, **kwargs) + + # If a user specifies base64, we'll just return the encoded string. + # This is only for the default case. + if not user_provided_encoding_format: + for data in response.data: + + # If an engine isn't using this optimization, don't do anything + if type(data["embedding"]) == str: + data["embedding"] = np.frombuffer( + base64.b64decode(data["embedding"]), dtype="float32" + ).tolist() + + return response + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py index 8ace8c9f5f..685812e72c 100644 --- a/openai/api_resources/engine.py +++ b/openai/api_resources/engine.py @@ -1,4 +1,5 @@ import time +import warnings from openai import util from openai.api_resources.abstract import ListableAPIResource, UpdateableAPIResource @@ -29,4 +30,7 @@ def search(self, **params): return self.request("post", self.instance_url() + "/search", params) def embeddings(self, **params): + warnings.warn( + "Engine.embeddings is deprecated, use Embedding.create", DeprecationWarning + ) return self.request("post", self.instance_url() + "/embeddings", params) diff --git a/openai/data/ca-certificates.crt b/openai/data/ca-certificates.crt deleted file mode 100644 index 0cb1d00752..0000000000 --- a/openai/data/ca-certificates.crt +++ /dev/null @@ -1,3240 +0,0 @@ -## -## Bundle of CA Root Certificates -## -## Certificate data from Mozilla as of: Wed Oct 17 03:12:10 2018 GMT -## -## This is a bundle of X.509 certificates of public Certificate Authorities -## (CA). These were automatically extracted from Mozilla's root certificates -## file (certdata.txt). This file can be found in the mozilla source tree: -## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt -## -## It contains the certificates in PEM format and therefore -## can be directly used with curl / libcurl / php_curl, or with -## an Apache+mod_ssl webserver for SSL client authentication. -## Just configure this file as the SSLCACertificateFile. -## -## Conversion done with mk-ca-bundle.pl version 1.27. -## SHA256: 3f875d87fee4ce3d966c69f1d6c111aa95c0143ade59e4fa24882c582bb5f0ca -## - - -GlobalSign Root CA -================== ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx -GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds -b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV -BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD -VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa -DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc -THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb -Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP -c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX -gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF -AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj -Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG -j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH -hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC -X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- - -GlobalSign Root CA - R2 -======================= ------BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv -YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh -bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT -aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln -bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 -ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp -s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN -S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL -TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C -ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i -YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN -BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp -9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu -01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 -9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 -TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== ------END CERTIFICATE----- - -Verisign Class 3 Public Primary Certification Authority - G3 -============================================================ ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv -cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy -dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 -EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc -cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw -EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj -055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA -ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f -j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC -/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 -xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa -t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== ------END CERTIFICATE----- - -Entrust.net Premium 2048 Secure Server CA -========================================= ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u -ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp -bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV -BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx -NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 -d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl -MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u -ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL -Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr -hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW -nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi -VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ -KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy -T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf -zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT -J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e -nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= ------END CERTIFICATE----- - -Baltimore CyberTrust Root -========================= ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE -ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li -ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC -SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs -dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME -uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB -UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C -G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 -XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr -l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI -VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB -BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh -cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 -hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa -Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H -RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE----- - -AddTrust External Root -====================== ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD -VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw -NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU -cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg -Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 -+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw -Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo -aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy -2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 -7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL -VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk -VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB -IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl -j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 -e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u -G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- - -Entrust Root Certification Authority -==================================== ------BEGIN CERTIFICATE----- -MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV -BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw -b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG -A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 -MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu -MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu -Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v -dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz -A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww -Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 -j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN -rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw -DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 -MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH -hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA -A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM -Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa -v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS -W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 -tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 ------END CERTIFICATE----- - -GeoTrust Global CA -================== ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK -Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw -MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j -LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo -BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet -8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc -T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU -vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk -DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q -zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 -d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 -mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p -XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm -Mw== ------END CERTIFICATE----- - -GeoTrust Universal CA -===================== ------BEGIN CERTIFICATE----- -MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN -R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 -MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu -Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t -JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e -RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs -7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d -8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V -qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga -Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB -Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu -KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 -ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 -XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB -hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc -aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 -qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL -oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK -xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF -KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 -DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK -xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU -p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI -P/rmMuGNG2+k5o7Y+SlIis5z/iw= ------END CERTIFICATE----- - -GeoTrust Universal CA 2 -======================= ------BEGIN CERTIFICATE----- -MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN -R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 -MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg -SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 -DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 -j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q -JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a -QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 -WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP -20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn -ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC -SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG -8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 -+/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E -BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z -dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ -4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ -mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq -A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg -Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP -pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d -FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp -gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm -X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS ------END CERTIFICATE----- - -Visa eCommerce Root -=================== ------BEGIN CERTIFICATE----- -MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG -EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug -QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 -WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm -VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv -bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL -F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b -RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 -TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI -/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs -GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG -MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc -CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW -YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz -zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu -YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt -398znM/jra6O1I7mT1GvFpLgXPYHDw== ------END CERTIFICATE----- - -Comodo AAA Services root -======================== ------BEGIN CERTIFICATE----- -MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS -R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg -TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw -MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl -c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV -BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG -C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs -i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW -Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH -Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK -Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f -BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl -cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz -LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm -7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz -Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z -8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C -12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== ------END CERTIFICATE----- - -QuoVadis Root CA -================ ------BEGIN CERTIFICATE----- -MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE -ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz -MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp -cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD -EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk -J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL -F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL -YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen -AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w -PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y -ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 -MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj -YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs -ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh -Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW -Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu -BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw -FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 -tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo -fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul -LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x -gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi -5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi -5nrQNiOKSnQ2+Q== ------END CERTIFICATE----- - -QuoVadis Root CA 2 -================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -QuoVadis Root CA 3 -================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -Security Communication Root CA -============================== ------BEGIN CERTIFICATE----- -MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP -U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw -HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP -U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw -8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM -DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX -5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd -DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 -JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw -DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g -0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a -mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ -s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ -6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi -FL39vmwLAw== ------END CERTIFICATE----- - -Sonera Class 2 Root CA -====================== ------BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG -U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw -NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh -IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 -/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT -dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG -f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P -tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH -nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT -XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt -0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI -cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph -Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx -EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH -llpwrN9M ------END CERTIFICATE----- - -XRamp Global CA Root -==================== ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE -BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj -dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx -HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg -U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu -IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx -foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE -zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs -AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry -xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud -EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap -oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC -AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc -/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt -qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n -nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz -8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= ------END CERTIFICATE----- - -Go Daddy Class 2 CA -=================== ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY -VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG -A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g -RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD -ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv -2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 -qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j -YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY -vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O -BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o -atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu -MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG -A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim -PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt -I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ -HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI -Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b -vZ8= ------END CERTIFICATE----- - -Starfield Class 2 CA -==================== ------BEGIN CERTIFICATE----- -MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc -U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo -MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG -A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG -SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY -bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ -JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm -epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN -F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF -MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f -hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo -bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g -QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs -afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM -PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl -xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD -KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 -QBFGmh95DmK/D5fs4C8fF5Q= ------END CERTIFICATE----- - -Taiwan GRCA -=========== ------BEGIN CERTIFICATE----- -MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG -EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X -DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv -dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN -w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 -BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O -1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO -htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov -J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 -Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t -B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB -O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 -lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV -HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 -09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ -TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj -Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 -Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU -D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz -DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk -Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk -7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ -CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy -+fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS ------END CERTIFICATE----- - -DigiCert Assured ID Root CA -=========================== ------BEGIN CERTIFICATE----- -MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw -IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx -MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL -ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO -9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy -UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW -/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy -oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf -GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF -66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq -hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc -EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn -SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i -8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe -+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== ------END CERTIFICATE----- - -DigiCert Global Root CA -======================= ------BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw -HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw -MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 -dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn -TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 -BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H -4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y -7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB -o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm -8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF -BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr -EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt -tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 -UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- - -DigiCert High Assurance EV Root CA -================================== ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw -KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw -MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ -MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu -Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t -Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS -OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 -MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ -NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe -h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB -Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY -JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ -V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp -myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK -mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K ------END CERTIFICATE----- - -Certplus Class 2 Primary CA -=========================== ------BEGIN CERTIFICATE----- -MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE -BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN -OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy -dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR -5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ -Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO -YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e -e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME -CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ -YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t -L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD -P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R -TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ -7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW -//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 -l7+ijrRU ------END CERTIFICATE----- - -DST Root CA X3 -============== ------BEGIN CERTIFICATE----- -MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK -ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X -DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 -cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT -rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 -UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy -xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d -utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ -MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug -dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE -GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw -RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS -fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ ------END CERTIFICATE----- - -SwissSign Gold CA - G2 -====================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -SwissSign Silver CA - G2 -======================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -GeoTrust Primary Certification Authority -======================================== ------BEGIN CERTIFICATE----- -MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx -CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ -cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN -b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 -nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge -RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt -tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI -hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K -Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN -NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa -Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG -1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= ------END CERTIFICATE----- - -thawte Primary Root CA -====================== ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE -BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 -aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 -MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg -SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv -KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT -FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs -oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ -1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc -q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K -aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p -afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD -VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF -AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE -uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX -xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 -jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH -z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== ------END CERTIFICATE----- - -VeriSign Class 3 Public Primary Certification Authority - G5 -============================================================ ------BEGIN CERTIFICATE----- -MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE -BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO -ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk -IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB -yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln -biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh -dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt -YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz -j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD -Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ -Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r -fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ -BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv -Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy -aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG -SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ -X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE -KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC -Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE -ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq ------END CERTIFICATE----- - -SecureTrust CA -============== ------BEGIN CERTIFICATE----- -MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG -EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy -dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe -BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX -OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t -DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH -GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b -01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH -ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj -aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ -KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu -SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf -mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ -nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR -3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= ------END CERTIFICATE----- - -Secure Global CA -================ ------BEGIN CERTIFICATE----- -MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG -EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH -bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg -MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg -Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx -YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ -bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g -8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV -HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi -0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud -EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn -oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA -MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ -OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn -CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 -3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc -f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW ------END CERTIFICATE----- - -COMODO Certification Authority -============================== ------BEGIN CERTIFICATE----- -MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE -BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG -A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 -dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb -MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD -T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH -+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww -xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV -4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA -1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI -rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k -b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC -AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP -OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ -RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc -IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN -+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== ------END CERTIFICATE----- - -Network Solutions Certificate Authority -======================================= ------BEGIN CERTIFICATE----- -MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG -EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr -IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx -MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu -MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx -jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT -aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT -crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc -/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB -AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv -bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA -A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q -4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ -GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv -wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD -ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey ------END CERTIFICATE----- - -COMODO ECC Certification Authority -================================== ------BEGIN CERTIFICATE----- -MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC -R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE -ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix -GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR -Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo -b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X -4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni -wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG -FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA -U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= ------END CERTIFICATE----- - -OISTE WISeKey Global Root GA CA -=============================== ------BEGIN CERTIFICATE----- -MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE -BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG -A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH -bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD -VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw -IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 -IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 -Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg -Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD -d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ -/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R -LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ -KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm -MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 -+vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa -hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY -okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= ------END CERTIFICATE----- - -Certigna -======== ------BEGIN CERTIFICATE----- -MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw -EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 -MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI -Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q -XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH -GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p -ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg -DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf -Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ -tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ -BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J -SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA -hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ -ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu -PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY -1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw -WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== ------END CERTIFICATE----- - -Deutsche Telekom Root CA 2 -========================== ------BEGIN CERTIFICATE----- -MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT -RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG -A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 -MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G -A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS -b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 -bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI -KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY -AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK -Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV -jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV -HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr -E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy -zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 -rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G -dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU -Cm26OWMohpLzGITY+9HPBVZkVw== ------END CERTIFICATE----- - -Cybertrust Global Root -====================== ------BEGIN CERTIFICATE----- -MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li -ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 -MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD -ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -+Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW -0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL -AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin -89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT -8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 -MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G -A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO -lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi -5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 -hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T -X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW -WL1WMRJOEcgh4LMRkWXbtKaIOM5V ------END CERTIFICATE----- - -ePKI Root Certification Authority -================================= ------BEGIN CERTIFICATE----- -MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG -EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg -Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx -MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq -MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs -IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi -lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv -qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX -12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O -WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ -ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao -lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ -vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi -Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi -MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH -ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 -1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq -KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV -xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP -NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r -GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE -xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx -gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy -sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD -BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= ------END CERTIFICATE----- - -certSIGN ROOT CA -================ ------BEGIN CERTIFICATE----- -MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD -VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa -Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE -CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I -JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH -rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 -ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD -0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 -AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B -Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB -AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 -SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 -x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt -vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz -TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD ------END CERTIFICATE----- - -GeoTrust Primary Certification Authority - G3 -============================================= ------BEGIN CERTIFICATE----- -MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE -BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 -IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy -eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz -NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo -YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT -LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j -K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE -c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C -IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu -dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr -2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 -cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE -Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD -AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s -t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt ------END CERTIFICATE----- - -thawte Primary Root CA - G2 -=========================== ------BEGIN CERTIFICATE----- -MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC -VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu -IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg -Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV -MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG -b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt -IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS -LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 -8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU -mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN -G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K -rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== ------END CERTIFICATE----- - -thawte Primary Root CA - G3 -=========================== ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE -BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 -aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w -ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh -d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD -VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG -A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At -P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC -+BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY -7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW -vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ -KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK -A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu -t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC -8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm -er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= ------END CERTIFICATE----- - -GeoTrust Primary Certification Authority - G2 -============================================= ------BEGIN CERTIFICATE----- -MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu -Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 -OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg -MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl -b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG -BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc -KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ -EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m -ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 -npaqBA+K ------END CERTIFICATE----- - -VeriSign Universal Root Certification Authority -=============================================== ------BEGIN CERTIFICATE----- -MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE -BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO -ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk -IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u -IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv -cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj -1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP -MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 -9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I -AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR -tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G -CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O -a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud -DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 -Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx -Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx -P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P -wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 -mJO37M2CYfE45k+XmCpajQ== ------END CERTIFICATE----- - -VeriSign Class 3 Public Primary Certification Authority - G4 -============================================================ ------BEGIN CERTIFICATE----- -MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC -VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 -b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz -ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU -cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo -b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 -IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 -Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz -rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw -HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u -Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD -A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx -AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== ------END CERTIFICATE----- - -NetLock Arany (Class Gold) Főtanúsítvány -======================================== ------BEGIN CERTIFICATE----- -MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G -A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 -dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB -cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx -MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO -ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv -biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 -c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu -0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw -/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk -H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw -fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 -neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW -qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta -YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC -bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna -NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu -dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= ------END CERTIFICATE----- - -Staat der Nederlanden Root CA - G2 -================================== ------BEGIN CERTIFICATE----- -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 -66+KAQ== ------END CERTIFICATE----- - -Hongkong Post Root CA 1 -======================= ------BEGIN CERTIFICATE----- -MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT -DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx -NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n -IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 -ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr -auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh -qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY -V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV -HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i -h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio -l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei -IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps -T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT -c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== ------END CERTIFICATE----- - -SecureSign RootCA11 -=================== ------BEGIN CERTIFICATE----- -MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi -SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS -b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw -KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 -cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL -TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO -wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq -g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP -O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA -bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX -t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh -OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r -bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ -Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 -y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 -lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= ------END CERTIFICATE----- - -Microsec e-Szigno Root CA 2009 -============================== ------BEGIN CERTIFICATE----- -MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER -MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv -c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o -dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE -BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt -U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA -fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG -0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA -pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm -1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC -AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf -QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE -FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o -lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX -I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 -tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 -yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi -LXpUq3DDfSJlgnCW ------END CERTIFICATE----- - -GlobalSign Root CA - R3 -======================= ------BEGIN CERTIFICATE----- -MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv -YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh -bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT -aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln -bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt -iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ -0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 -rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl -OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 -xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE -FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 -lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 -EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E -bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 -YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r -kpeDMdmztcpHWD9f ------END CERTIFICATE----- - -Autoridad de Certificacion Firmaprofesional CIF A62634068 -========================================================= ------BEGIN CERTIFICATE----- -MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA -BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 -MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw -QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB -NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD -Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P -B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY -7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH -ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI -plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX -MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX -LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK -bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU -vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud -EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH -DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp -cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA -bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx -ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx -51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk -R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP -T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f -Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl -osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR -crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR -saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD -KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi -6Et8Vcad+qMUu2WFbm5PEn4KPJ2V ------END CERTIFICATE----- - -Izenpe.com -========== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -Chambers of Commerce Root - 2008 -================================ ------BEGIN CERTIFICATE----- -MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD -MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv -bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu -QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy -Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl -ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF -EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl -cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA -XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj -h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ -ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk -NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g -D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 -lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ -0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj -ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 -EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI -G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ -BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh -bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh -bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC -CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH -AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 -wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH -3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU -RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 -M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 -YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF -9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK -zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG -nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg -OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ ------END CERTIFICATE----- - -Global Chambersign Root - 2008 -============================== ------BEGIN CERTIFICATE----- -MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD -MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv -bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu -QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx -NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg -Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ -QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD -aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf -VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf -XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 -ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB -/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA -TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M -H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe -Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF -HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh -wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB -AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT -BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE -BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm -aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm -aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp -1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 -dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG -/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 -ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s -dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg -9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH -foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du -qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr -P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq -c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z -09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B ------END CERTIFICATE----- - -Go Daddy Root Certificate Authority - G2 -======================================== ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT -B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu -MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 -MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G -A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq -9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD -+qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd -fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl -NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 -BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac -vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r -5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV -N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO -LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 ------END CERTIFICATE----- - -Starfield Root Certificate Authority - G2 -========================================= ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT -B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s -b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 -eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw -DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg -VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB -dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv -W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs -bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk -N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf -ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU -JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol -TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx -4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw -F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K -pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ -c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE----- - -Starfield Services Root Certificate Authority - G2 -================================================== ------BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT -B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s -b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl -IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV -BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT -dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg -Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 -h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa -hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP -LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB -rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw -AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG -SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP -E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy -xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd -iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza -YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 ------END CERTIFICATE----- - -AffirmTrust Commercial -====================== ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS -BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw -MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly -bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb -DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV -C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 -BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww -MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV -HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG -hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi -qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv -0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh -sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE----- - -AffirmTrust Networking -====================== ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS -BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw -MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly -bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE -Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI -dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 -/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb -h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV -HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu -UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 -12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 -WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 -/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE----- - -AffirmTrust Premium -=================== ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS -BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy -OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy -dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn -BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV -5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs -+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd -GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R -p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI -S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 -6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 -/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo -+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv -MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg -Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC -6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S -L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK -+4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV -BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg -IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 -g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb -zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== ------END CERTIFICATE----- - -AffirmTrust Premium ECC -======================= ------BEGIN CERTIFICATE----- -MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV -BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx -MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U -cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ -N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW -BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK -BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X -57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM -eQ== ------END CERTIFICATE----- - -Certum Trusted Network CA -========================= ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK -ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy -MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU -ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC -l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J -J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 -fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 -cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB -Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw -DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj -jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 -mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj -Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI -03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= ------END CERTIFICATE----- - -TWCA Root Certification Authority -================================= ------BEGIN CERTIFICATE----- -MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ -VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG -EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB -IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx -QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC -oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP -4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r -y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG -9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC -mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW -QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY -T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny -Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== ------END CERTIFICATE----- - -Security Communication RootCA2 -============================== ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc -U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh -dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC -SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy -aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ -+T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R -3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV -spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K -EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 -QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB -CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj -u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk -3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q -tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 -mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 ------END CERTIFICATE----- - -EC-ACC -====== ------BEGIN CERTIFICATE----- -MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE -BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w -ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD -VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE -CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT -BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 -MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt -SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl -Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh -cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK -w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT -ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 -HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a -E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw -0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD -VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 -Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l -dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ -lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa -Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe -l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 -E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D -5EI= ------END CERTIFICATE----- - -Hellenic Academic and Research Institutions RootCA 2011 -======================================================= ------BEGIN CERTIFICATE----- -MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT -O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y -aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z -IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT -AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z -IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo -IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI -1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa -71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u -8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH -3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ -MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 -MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu -b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt -XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 -TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD -/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N -7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 ------END CERTIFICATE----- - -Actalis Authentication Root CA -============================== ------BEGIN CERTIFICATE----- -MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM -BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE -AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky -MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz -IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 -IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ -wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa -by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 -zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f -YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 -oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l -EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 -hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 -EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 -jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY -iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt -ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI -WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 -JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx -K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ -Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC -4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo -2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz -lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem -OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 -vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== ------END CERTIFICATE----- - -Trustis FPS Root CA -=================== ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG -EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 -IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV -BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ -RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk -H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa -cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt -o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA -AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd -BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c -GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC -yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P -8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV -l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl -iB6XzCGcKQENZetX2fNXlrtIzYE= ------END CERTIFICATE----- - -Buypass Class 2 Root CA -======================= ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU -QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X -DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 -eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw -DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 -g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn -9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b -/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU -CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff -awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI -zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn -Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX -Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs -M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF -AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s -A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI -osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S -aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd -DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD -LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 -oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC -wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS -CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN -rJgWVqA= ------END CERTIFICATE----- - -Buypass Class 3 Root CA -======================= ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU -QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X -DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 -eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw -DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH -sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR -5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh -7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ -ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH -2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV -/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ -RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA -Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq -j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF -AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV -cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G -uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG -Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 -ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 -KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz -6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug -UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe -eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi -Cp/HuZc= ------END CERTIFICATE----- - -T-TeleSec GlobalRoot Class 3 -============================ ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM -IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU -cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx -MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz -dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD -ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK -9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU -NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF -iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W -0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr -AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb -fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT -ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h -P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml -e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== ------END CERTIFICATE----- - -EE Certification Centre Root CA -=============================== ------BEGIN CERTIFICATE----- -MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG -EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy -dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw -MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB -UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy -ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM -TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 -rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw -93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN -P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ -MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF -BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj -xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM -lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u -uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU -3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM -dcGWxZ0= ------END CERTIFICATE----- - -D-TRUST Root Class 3 CA 2 2009 -============================== ------BEGIN CERTIFICATE----- -MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK -DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe -Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE -LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD -ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA -BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv -KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z -p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC -AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ -4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y -eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw -MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G -PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw -OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm -2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 -o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV -dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph -X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= ------END CERTIFICATE----- - -D-TRUST Root Class 3 CA 2 EV 2009 -================================= ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK -DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw -OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK -DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw -OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS -egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh -zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T -7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 -sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 -11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv -cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v -ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El -MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp -b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh -c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ -PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 -nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX -ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA -NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv -w9y4AyHqnxbxLFS1 ------END CERTIFICATE----- - -CA Disig Root R2 -================ ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw -EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp -ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx -EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp -c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC -w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia -xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 -A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S -GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV -g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa -5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE -koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A -Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i -Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u -Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM -tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV -sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je -dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 -1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx -mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 -utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 -sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg -UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV -7+ZtsH8tZ/3zbBt1RqPlShfppNcL ------END CERTIFICATE----- - -ACCVRAIZ1 -========= ------BEGIN CERTIFICATE----- -MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB -SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 -MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH -UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM -jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 -RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD -aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ -0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG -WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 -8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR -5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J -9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK -Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw -Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu -Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 -VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM -Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA -QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh -AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA -YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj -AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA -IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk -aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 -dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 -MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI -hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E -R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN -YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 -nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ -TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 -sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h -I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg -Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd -3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p -EfbRD0tVNEYqi4Y7 ------END CERTIFICATE----- - -TWCA Global Root CA -=================== ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT -CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD -QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK -EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg -Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C -nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV -r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR -Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV -tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W -KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 -sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p -yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn -kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI -zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC -AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g -cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn -LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M -8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg -/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg -lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP -A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m -i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 -EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 -zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= ------END CERTIFICATE----- - -TeliaSonera Root CA v1 -====================== ------BEGIN CERTIFICATE----- -MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE -CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 -MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW -VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ -6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA -3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k -B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn -Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH -oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 -F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ -oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 -gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc -TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB -AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW -DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm -zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx -0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW -pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV -G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc -c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT -JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 -qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 -Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems -WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= ------END CERTIFICATE----- - -E-Tugra Certification Authority -=============================== ------BEGIN CERTIFICATE----- -MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w -DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls -ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN -ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw -NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx -QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl -cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD -DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd -hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K -CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g -ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ -BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 -E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz -rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq -jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn -rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 -dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB -/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG -MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK -kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO -XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 -VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo -a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc -dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV -KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT -Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 -8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G -C7TbO6Orb1wdtn7os4I07QZcJA== ------END CERTIFICATE----- - -T-TeleSec GlobalRoot Class 2 -============================ ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM -IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU -cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx -MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz -dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD -ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ -SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F -vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 -2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV -WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy -YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 -r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf -vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR -3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN -9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== ------END CERTIFICATE----- - -Atos TrustedRoot 2011 -===================== ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU -cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 -MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG -A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV -hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr -54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ -DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 -HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR -z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R -l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ -bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB -CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h -k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh -TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 -61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G -3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed ------END CERTIFICATE----- - -QuoVadis Root CA 1 G3 -===================== ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG -A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv -b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN -MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg -RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE -PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm -PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 -Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN -ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l -g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV -7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX -9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f -iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg -t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI -hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC -MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 -GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct -Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP -+V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh -3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa -wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 -O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 -FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV -hMJKzRwuJIczYOXD ------END CERTIFICATE----- - -QuoVadis Root CA 2 G3 -===================== ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG -A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv -b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN -MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg -RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh -ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY -NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t -oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o -MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l -V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo -L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ -sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD -6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh -lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI -hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 -AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K -pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 -x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz -dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X -U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw -mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD -zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN -JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr -O3jtZsSOeWmD3n+M ------END CERTIFICATE----- - -QuoVadis Root CA 3 G3 -===================== ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG -A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv -b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN -MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg -RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 -IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL -Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe -6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 -I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U -VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 -5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi -Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM -dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt -rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI -hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px -KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS -t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ -TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du -DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib -Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD -hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX -0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW -dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 -PpxxVJkES/1Y+Zj0 ------END CERTIFICATE----- - -DigiCert Assured ID Root G2 -=========================== ------BEGIN CERTIFICATE----- -MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw -IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw -MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL -ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH -35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq -bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw -VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP -YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn -lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO -w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv -0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz -d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW -hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M -jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo -IhNzbM8m9Yop5w== ------END CERTIFICATE----- - -DigiCert Assured ID Root G3 -=========================== ------BEGIN CERTIFICATE----- -MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD -VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 -MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ -BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb -RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs -KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF -UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy -YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy -1vUhZscv6pZjamVFkpUBtA== ------END CERTIFICATE----- - -DigiCert Global Root G2 -======================= ------BEGIN CERTIFICATE----- -MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw -HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx -MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 -dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ -kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO -3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV -BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM -UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB -o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu -5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr -F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U -WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH -QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ -iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl -MrY= ------END CERTIFICATE----- - -DigiCert Global Root G3 -======================= ------BEGIN CERTIFICATE----- -MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD -VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw -MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k -aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C -AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O -YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp -Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y -3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 -VOKa5Vt8sycX ------END CERTIFICATE----- - -DigiCert Trusted Root G4 -======================== ------BEGIN CERTIFICATE----- -MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw -HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 -MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G -CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp -pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o -k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa -vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY -QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 -MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm -mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 -f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH -dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 -oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud -DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD -ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY -ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr -yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy -7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah -ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN -5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb -/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa -5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK -G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP -82Z+ ------END CERTIFICATE----- - -COMODO RSA Certification Authority -================================== ------BEGIN CERTIFICATE----- -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 -LaZRfyHBNVOFBkpdn627G190 ------END CERTIFICATE----- - -USERTrust RSA Certification Authority -===================================== ------BEGIN CERTIFICATE----- -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 -Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 ------END CERTIFICATE----- - -USERTrust ECC Certification Authority -===================================== ------BEGIN CERTIFICATE----- -MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC -VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU -aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC -VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU -aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 -0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez -nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV -HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB -HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu -9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= ------END CERTIFICATE----- - -GlobalSign ECC Root CA - R4 -=========================== ------BEGIN CERTIFICATE----- -MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb -R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD -EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb -R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD -EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl -OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P -AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV -MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF -JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q= ------END CERTIFICATE----- - -GlobalSign ECC Root CA - R5 -=========================== ------BEGIN CERTIFICATE----- -MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb -R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD -EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb -R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD -EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 -SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS -h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd -BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx -uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 -yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 ------END CERTIFICATE----- - -Staat der Nederlanden Root CA - G3 -================================== ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE -CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g -Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC -TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l -ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y -olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t -x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy -EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K -Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur -mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5 -1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp -07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo -FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE -41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB -AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu -yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD -U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq -KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1 -v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA -8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b -8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r -mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq -1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI -JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV -tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk= ------END CERTIFICATE----- - -Staat der Nederlanden EV Root CA -================================ ------BEGIN CERTIFICATE----- -MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE -CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g -RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M -MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl -cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk -SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW -O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r -0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8 -Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV -XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr -08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV -0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd -74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx -fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa -ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI -eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu -c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq -5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN -b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN -f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi -5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4 -WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK -DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy -eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg== ------END CERTIFICATE----- - -IdenTrust Commercial Root CA 1 -============================== ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG -EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS -b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES -MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB -IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld -hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ -mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi -1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C -XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl -3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy -NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV -WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg -xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix -uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC -AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI -hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH -6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg -ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt -ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV -YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX -feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro -kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe -2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz -Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R -cGzM7vRX+Bi6hG6H ------END CERTIFICATE----- - -IdenTrust Public Sector Root CA 1 -================================= ------BEGIN CERTIFICATE----- -MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG -EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv -ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV -UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS -b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy -P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 -Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI -rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf -qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS -mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn -ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh -LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v -iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL -4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B -Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw -DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj -t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A -mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt -GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt -m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx -NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 -Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI -ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC -ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ -3Wl9af0AVqW3rLatt8o+Ae+c ------END CERTIFICATE----- - -Entrust Root Certification Authority - G2 -========================================= ------BEGIN CERTIFICATE----- -MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV -BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy -bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug -b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw -HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT -DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx -OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s -eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP -/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz -HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU -s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y -TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx -AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 -0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z -iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ -Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi -nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ -vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO -e4pIb4tF9g== ------END CERTIFICATE----- - -Entrust Root Certification Authority - EC1 -========================================== ------BEGIN CERTIFICATE----- -MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx -FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn -YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl -ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw -FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs -LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg -dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt -IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy -AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef -9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE -FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h -vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 -kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G ------END CERTIFICATE----- - -CFCA EV ROOT -============ ------BEGIN CERTIFICATE----- -MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE -CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB -IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw -MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD -DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV -BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD -7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN -uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW -ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 -xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f -py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K -gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol -hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ -tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf -BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB -/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB -ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q -ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua -4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG -E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX -BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn -aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy -PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX -kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C -ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su ------END CERTIFICATE----- - -Certinomis - Root CA -==================== ------BEGIN CERTIFICATE----- -MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjETMBEGA1UEChMK -Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAbBgNVBAMTFENlcnRpbm9taXMg -LSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMzMTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIx -EzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRD -ZXJ0aW5vbWlzIC0gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQos -P5L2fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJflLieY6pOo -d5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQVWZUKxkd8aRi5pwP5ynap -z8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDFTKWrteoB4owuZH9kb/2jJZOLyKIOSY00 -8B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09x -RLWtwHkziOC/7aOgFLScCbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE -6OXWk6RiwsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJwx3t -FvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SGm/lg0h9tkQPTYKbV -PZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4F2iw4lNVYC2vPsKD2NkJK/DAZNuH -i5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZngWVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGj -YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I -6tNxIqSSaHh02TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF -AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/0KGRHCwPT5iV -WVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWwF6YSjNRieOpWauwK0kDDPAUw -Pk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZSg081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAX -lCOotQqSD7J6wWAsOMwaplv/8gzjqh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJ -y29SWwNyhlCVCNSNh4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9 -Iff/ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8Vbtaw5Bng -DwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwjY/M50n92Uaf0yKHxDHYi -I0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nM -cyrDflOR1m749fPH0FFNjkulW+YZFzvWgQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVr -hkIGuUE= ------END CERTIFICATE----- - -OISTE WISeKey Global Root GB CA -=============================== ------BEGIN CERTIFICATE----- -MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG -EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl -ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw -MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD -VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds -b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX -scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP -rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk -9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o -Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg -GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI -hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD -dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 -VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui -HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic -Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= ------END CERTIFICATE----- - -SZAFIR ROOT CA2 -=============== ------BEGIN CERTIFICATE----- -MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG -A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV -BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ -BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD -VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q -qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK -DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE -2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ -ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi -ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P -AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC -AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 -O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 -oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul -4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 -+/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== ------END CERTIFICATE----- - -Certum Trusted Network CA 2 -=========================== ------BEGIN CERTIFICATE----- -MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE -BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 -bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y -ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ -TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl -cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB -IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 -7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o -CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b -Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p -uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 -GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ -9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB -Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye -hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM -BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI -hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW -Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA -L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo -clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM -pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb -w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo -J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm -ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX -is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 -zAYspsbiDrW5viSP ------END CERTIFICATE----- - -Hellenic Academic and Research Institutions RootCA 2015 -======================================================= ------BEGIN CERTIFICATE----- -MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT -BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 -aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl -YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx -MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg -QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV -BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw -MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv -bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh -iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ -6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd -FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr -i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F -GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 -fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu -iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc -Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI -hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ -D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM -d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y -d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn -82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb -davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F -Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt -J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa -JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q -p/UsQu0yrbYhnr68 ------END CERTIFICATE----- - -Hellenic Academic and Research Institutions ECC RootCA 2015 -=========================================================== ------BEGIN CERTIFICATE----- -MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 -aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u -cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj -aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw -MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj -IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD -VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 -Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP -dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK -Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O -BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA -GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn -dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR ------END CERTIFICATE----- - -ISRG Root X1 -============ ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE -BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD -EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG -EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT -DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r -Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 -3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K -b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN -Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ -4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf -1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu -hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH -usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r -OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G -A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY -9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV -0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt -hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw -TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx -e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA -JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD -YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n -JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ -m+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- - -AC RAIZ FNMT-RCM -================ ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT -AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw -MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD -TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC -ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf -qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr -btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL -j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou -08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw -WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT -tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ -47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC -ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa -i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o -dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD -nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s -D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ -j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT -Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW -+YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 -Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d -8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm -5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG -rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= ------END CERTIFICATE----- - -Amazon Root CA 1 -================ ------BEGIN CERTIFICATE----- -MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD -VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 -MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv -bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH -FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ -gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t -dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce -VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 -DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM -CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy -8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa -2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 -xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE----- - -Amazon Root CA 2 -================ ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD -VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 -MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv -bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC -ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 -kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp -N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 -AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd -fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx -kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS -btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 -Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN -c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ -3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw -DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA -A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY -+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE -YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW -xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ -gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW -aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV -Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 -KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi -JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= ------END CERTIFICATE----- - -Amazon Root CA 3 -================ ------BEGIN CERTIFICATE----- -MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG -EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy -NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ -MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB -f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr -Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 -rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc -eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== ------END CERTIFICATE----- - -Amazon Root CA 4 -================ ------BEGIN CERTIFICATE----- -MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG -EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy -NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ -MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN -/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri -83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV -HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA -MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 -AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== ------END CERTIFICATE----- - -LuxTrust Global Root 2 -====================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 -============================================= ------BEGIN CERTIFICATE----- -MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT -D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr -IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g -TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp -ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD -VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt -c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth -bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 -IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 -6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc -wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 -3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 -WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU -ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ -KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh -AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc -lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R -e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j -q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= ------END CERTIFICATE----- - -GDCA TrustAUTH R5 ROOT -====================== ------BEGIN CERTIFICATE----- -MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw -BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD -DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow -YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ -IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs -AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p -OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr -pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ -9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ -xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM -R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ -D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 -oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx -9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg -p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 -H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 -6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd -+PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ -HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD -F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ -8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv -/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT -aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== ------END CERTIFICATE----- - -TrustCor RootCert CA-1 -====================== ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP -MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig -U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp -dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx -MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu -YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe -VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy -dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq -jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4 -pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0 -JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h -gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw -/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j -BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5 -mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf -ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C -qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P -3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk= ------END CERTIFICATE----- - -TrustCor RootCert CA-2 -====================== ------BEGIN CERTIFICATE----- -MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w -DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT -eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0 -eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy -MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h -bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U -cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0 -IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb -ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk -RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1 -oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb -XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1 -/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q -jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP -eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg -rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh -8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU -2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h -Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp -kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv -2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3 -S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw -PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv -DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU -RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE -xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX -RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ ------END CERTIFICATE----- - -TrustCor ECA-1 -============== ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP -MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig -U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp -dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw -N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5 -MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y -IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR -MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23 -xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc -p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+ -fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj -YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL -f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF -AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u -/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F -hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs -J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC -jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g== ------END CERTIFICATE----- - -SSL.com Root Certification Authority RSA -======================================== ------BEGIN CERTIFICATE----- -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 -WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= ------END CERTIFICATE----- - -SSL.com Root Certification Authority ECC -======================================== ------BEGIN CERTIFICATE----- -MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV -BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv -BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy -MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO -BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv -bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA -BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ -8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR -hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT -jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW -e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z -5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl ------END CERTIFICATE----- - -SSL.com EV Root Certification Authority RSA R2 -============================================== ------BEGIN CERTIFICATE----- -MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w -DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u -MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy -MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI -DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD -VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN -BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh -hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w -cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO -Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ -B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh -CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim -9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto -RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm -JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 -+qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV -HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp -qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 -++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx -Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G -guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz -OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 -CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq -lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR -rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 -hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX -9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== ------END CERTIFICATE----- - -SSL.com EV Root Certification Authority ECC -=========================================== ------BEGIN CERTIFICATE----- -MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV -BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy -BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw -MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx -EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM -LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB -BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy -3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O -BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe -5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ -N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm -m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== ------END CERTIFICATE----- - -GlobalSign Root CA - R6 -======================= ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX -R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds -b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i -YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs -U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss -grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE -3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF -vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM -PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ -azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O -WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy -CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP -0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN -b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE -AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV -HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN -nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 -lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY -BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym -Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr -3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 -0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T -uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK -oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t -JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= ------END CERTIFICATE----- - -OISTE WISeKey Global Root GC CA -=============================== ------BEGIN CERTIFICATE----- -MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD -SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo -MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa -Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL -ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh -bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr -VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab -NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd -BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E -AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk -AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 ------END CERTIFICATE----- diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index 4f1812bd6e..e8b29a946b 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -10,24 +10,24 @@ @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) -def get_embedding(text: str, engine="davinci-similarity") -> List[float]: +def get_embedding(text: str, engine="text-similarity-davinci-001") -> List[float]: # replace newlines, which can negatively affect performance. text = text.replace("\n", " ") - return openai.Engine(id=engine).embeddings(input=[text])["data"][0]["embedding"] + return openai.Embedding.create(input=[text], engine=engine)["data"][0]["embedding"] @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) def get_embeddings( - list_of_text: List[str], engine="babbage-similarity" + list_of_text: List[str], engine="text-similarity-babbage-001" ) -> List[List[float]]: assert len(list_of_text) < 2048, "The batch size should not be larger than 2048." # replace newlines, which can negatively affect performance. list_of_text = [text.replace("\n", " ") for text in list_of_text] - data = openai.Engine(id=engine).embeddings(input=list_of_text).data + data = openai.Embedding.create(input=list_of_text, engine=engine).data data = sorted(data, key=lambda x: x["index"]) # maintain the same order as input. return [d["embedding"] for d in data] diff --git a/openai/error.py b/openai/error.py index f2ce82c2e2..fd436dd077 100644 --- a/openai/error.py +++ b/openai/error.py @@ -119,6 +119,17 @@ def __repr__(self): self.request_id, ) + def __reduce__(self): + return type(self), ( + self._message, + self.param, + self.code, + self.http_body, + self.http_status, + self.json_body, + self.headers, + ) + class AuthenticationError(OpenAIError): pass @@ -140,3 +151,10 @@ class SignatureVerificationError(OpenAIError): def __init__(self, message, sig_header, http_body=None): super(SignatureVerificationError, self).__init__(message, http_body) self.sig_header = sig_header + + def __reduce__(self): + return type(self), ( + self._message, + self.sig_header, + self.http_body, + ) diff --git a/openai/tests/test_exceptions.py b/openai/tests/test_exceptions.py new file mode 100644 index 0000000000..e97b4cb386 --- /dev/null +++ b/openai/tests/test_exceptions.py @@ -0,0 +1,39 @@ +import pickle + +import pytest + +import openai + +EXCEPTION_TEST_CASES = [ + openai.InvalidRequestError( + "message", + "param", + code=400, + http_body={"test": "test1"}, + http_status="fail", + json_body={"text": "iono some text"}, + headers={"request-id": "asasd"}, + ), + openai.error.AuthenticationError(), + openai.error.PermissionError(), + openai.error.RateLimitError(), + openai.error.ServiceUnavailableError(), + openai.error.SignatureVerificationError("message", "sig_header?"), + openai.error.APIConnectionError("message!", should_retry=True), + openai.error.TryAgain(), + openai.error.APIError( + message="message", + code=400, + http_body={"test": "test1"}, + http_status="fail", + json_body={"text": "iono some text"}, + headers={"request-id": "asasd"}, + ), + openai.error.OpenAIError(), +] + + +class TestExceptions: + @pytest.mark.parametrize("error", EXCEPTION_TEST_CASES) + def test_exceptions_are_pickleable(self, error) -> None: + assert error.__repr__() == pickle.loads(pickle.dumps(error)).__repr__() diff --git a/openai/version.py b/openai/version.py index bc927384a8..27696fb246 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.11.5" +VERSION = "0.11.6" diff --git a/setup.py b/setup.py index deeb740c98..1c468d17f0 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,6 @@ packages=find_packages(exclude=["tests", "tests.*"]), package_data={ "openai": [ - "data/ca-certificates.crt", "py.typed", ] }, From c93af95bf94ecfd3236daccb40ebee404965f4cf Mon Sep 17 00:00:00 2001 From: Sorin Suciu Date: Sat, 22 Jan 2022 01:46:27 +0100 Subject: [PATCH 012/914] Add an option to use Azure endpoints for the /completions & /search operations. (#45) * Add an option to use Azure endpoints for the /completions operation. * Add the azure endpoints option for the /search operation + small fixes. * errata * Adressed CR comments --- .gitignore | 4 +- openai/__init__.py | 4 +- openai/api_requestor.py | 10 +- openai/api_resources/abstract/api_resource.py | 27 +++- .../abstract/engine_api_resource.py | 58 ++++++-- openai/api_resources/engine.py | 10 +- openai/error.py | 3 + openai/openai_object.py | 13 ++ openai/openai_response.py | 2 +- openai/tests/test_api_requestor.py | 27 +++- openai/tests/test_url_composition.py | 124 ++++++++++++++++++ openai/util.py | 16 +++ openai/version.py | 2 +- pytest.ini | 4 + 14 files changed, 279 insertions(+), 25 deletions(-) create mode 100644 openai/tests/test_url_composition.py create mode 100644 pytest.ini diff --git a/.gitignore b/.gitignore index 47a4cda142..6af1d15f0a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ /public/dist __pycache__ build -.ipynb_checkpoints +*.egg +.vscode/settings.json +.ipynb_checkpoints \ No newline at end of file diff --git a/openai/__init__.py b/openai/__init__.py index 5ca63e8c25..3137717cce 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -27,7 +27,8 @@ organization = os.environ.get("OPENAI_ORGANIZATION") api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") -api_version = None +api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") +api_version = '2021-11-01-preview' if api_type == "azure" else None verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None app_info = None @@ -52,6 +53,7 @@ "Search", "api_base", "api_key", + "api_type", "api_key_path", "api_version", "app_info", diff --git a/openai/api_requestor.py b/openai/api_requestor.py index d09175e4bf..cac62f5544 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -1,3 +1,4 @@ +from email import header import json import platform import threading @@ -11,6 +12,7 @@ import openai from openai import error, util, version from openai.openai_response import OpenAIResponse +from openai.util import ApiType TIMEOUT_SECS = 600 MAX_CONNECTION_RETRIES = 2 @@ -69,9 +71,10 @@ def parse_stream(rbody): class APIRequestor: - def __init__(self, key=None, api_base=None, api_version=None, organization=None): + def __init__(self, key=None, api_base=None, api_type=None, api_version=None, organization=None): self.api_base = api_base or openai.api_base self.api_key = key or util.default_api_key() + self.api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) self.api_version = api_version or openai.api_version self.organization = organization or openai.organization @@ -192,13 +195,14 @@ def request_headers( headers = { "X-OpenAI-Client-User-Agent": json.dumps(ua), "User-Agent": user_agent, - "Authorization": "Bearer %s" % (self.api_key,), } + headers.update(util.api_key_to_header(self.api_type, self.api_key)) + if self.organization: headers["OpenAI-Organization"] = self.organization - if self.api_version is not None: + if self.api_version is not None and self.api_type == ApiType.OPEN_AI: headers["OpenAI-Version"] = self.api_version if request_id is not None: headers["X-Request-Id"] = request_id diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index 40350dfe4a..c25aafd656 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -1,11 +1,14 @@ from urllib.parse import quote_plus from openai import api_requestor, error, util +import openai from openai.openai_object import OpenAIObject +from openai.util import ApiType class APIResource(OpenAIObject): api_prefix = "" + azure_api_prefix = 'openai/deployments' @classmethod def retrieve(cls, id, api_key=None, request_id=None, **params): @@ -32,7 +35,7 @@ def class_url(http://github.com/cls): return "/%s/%ss" % (cls.api_prefix, base) return "/%ss" % (base) - def instance_url(http://github.com/self): + def instance_url(http://github.com/self,%20operation=None): id = self.get("id") if not isinstance(id, str): @@ -42,10 +45,26 @@ def instance_url(http://github.com/self): " `unicode`)" % (type(self).__name__, id, type(id)), "id", ) + api_version = self.api_version or openai.api_version - base = self.class_url() - extn = quote_plus(id) - return "%s/%s" % (base, extn) + if self.typed_api_type == ApiType.AZURE: + if not api_version: + raise error.InvalidRequestError("An API version is required for the Azure API type.") + if not operation: + raise error.InvalidRequestError( + "The request needs an operation (eg: 'search') for the Azure OpenAI API type." + ) + extn = quote_plus(id) + return "/%s/%s/%s?api-version=%s" % (self.azure_api_prefix, extn, operation, api_version) + + elif self.typed_api_type == ApiType.OPEN_AI: + base = self.class_url() + extn = quote_plus(id) + return "%s/%s" % (base, extn) + + else: + raise error.InvalidAPIType('Unsupported API type %s' % self.api_type) + # The `method_` and `url_` arguments are suffixed with an underscore to # avoid conflicting with actual request parameters in `params`. diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index aab1ae8663..84904e1f80 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -1,10 +1,13 @@ +from pydoc import apropos import time from typing import Optional from urllib.parse import quote_plus +import openai from openai import api_requestor, error, util from openai.api_resources.abstract.api_resource import APIResource from openai.openai_response import OpenAIResponse +from openai.util import ApiType MAX_TIMEOUT = 20 @@ -12,26 +15,46 @@ class EngineAPIResource(APIResource): engine_required = True plain_old_data = False + azure_api_prefix = 'openai/deployments' def __init__(self, engine: Optional[str] = None, **kwargs): super().__init__(engine=engine, **kwargs) @classmethod - def class_url(http://github.com/cls,%20engine:%20Optional[str]%20=%20None): + def class_url(http://github.com/cls,%20engine:%20Optional[str]%20=%20None,%20api_type%20:%20Optional[str]%20=%20None,%20api_version:%20Optional[str]%20=%20None): # Namespaces are separated in object names with periods (.) and in URLs # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - if engine is None: - return "/%ss" % (base) + typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) + api_version = api_version or openai.api_version + + if typed_api_type == ApiType.AZURE: + if not api_version: + raise error.InvalidRequestError("An API version is required for the Azure API type.") + if engine is None: + raise error.InvalidRequestError( + "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service" + ) + extn = quote_plus(engine) + return "/%s/%s/%ss?api-version=%s" % (cls.azure_api_prefix, extn, base, api_version) + + elif typed_api_type == ApiType.OPEN_AI: + if engine is None: + return "/%ss" % (base) + + extn = quote_plus(engine) + return "/engines/%s/%ss" % (extn, base) + + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) - extn = quote_plus(engine) - return "/engines/%s/%ss" % (extn, base) @classmethod def create( cls, api_key=None, api_base=None, + api_type=None, request_id=None, api_version=None, organization=None, @@ -58,10 +81,11 @@ def create( requestor = api_requestor.APIRequestor( api_key, api_base=api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = cls.class_url(http://github.com/engine) + url = cls.class_url(http://github.com/engine,%20api_type,%20api_version) response, _, api_key = requestor.request( "post", url, params, stream=stream, request_id=request_id ) @@ -103,14 +127,28 @@ def instance_url(http://github.com/self): "id", ) - base = self.class_url(http://github.com/self.engine) - extn = quote_plus(id) - url = "%s/%s" % (base, extn) + params_connector = '?' + if self.typed_api_type == ApiType.AZURE: + api_version = self.api_version or openai.api_version + if not api_version: + raise error.InvalidRequestError("An API version is required for the Azure API type.") + extn = quote_plus(id) + base = self.OBJECT_NAME.replace(".", "/") + url = "/%s/%s/%ss/%s?api-version=%s" % (self.azure_api_prefix, self.engine, base, extn, api_version) + params_connector = '&' + + elif self.typed_api_type == ApiType.OPEN_AI: + base = self.class_url(http://github.com/self.engine,%20self.api_type,%20self.api_version) + extn = quote_plus(id) + url = "%s/%s" % (base, extn) + + else: + raise error.InvalidAPIType('Unsupported API type %s' % self.api_type) timeout = self.get("timeout") if timeout is not None: timeout = quote_plus(str(timeout)) - url += "?timeout={}".format(timeout) + url += params_connector + "timeout={}".format(timeout) return url def wait(self, timeout=None): diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py index 685812e72c..4a7b03cfbf 100644 --- a/openai/api_resources/engine.py +++ b/openai/api_resources/engine.py @@ -3,7 +3,8 @@ from openai import util from openai.api_resources.abstract import ListableAPIResource, UpdateableAPIResource -from openai.error import TryAgain +from openai.error import InvalidAPIType, TryAgain +from openai.util import ApiType class Engine(ListableAPIResource, UpdateableAPIResource): @@ -27,7 +28,12 @@ def generate(self, timeout=None, **params): util.log_info("Waiting for model to warm up", error=e) def search(self, **params): - return self.request("post", self.instance_url() + "/search", params) + if self.typed_api_type == ApiType.AZURE: + return self.request("post", self.instance_url("http://github.com/search"), params) + elif self.typed_api_type == ApiType.OPEN_AI: + return self.request("post", self.instance_url() + "/search", params) + else: + raise InvalidAPIType('Unsupported API type %s' % self.api_type) def embeddings(self, **params): warnings.warn( diff --git a/openai/error.py b/openai/error.py index fd436dd077..b357093e7e 100644 --- a/openai/error.py +++ b/openai/error.py @@ -146,6 +146,9 @@ class RateLimitError(OpenAIError): class ServiceUnavailableError(OpenAIError): pass +class InvalidAPIType(OpenAIError): + pass + class SignatureVerificationError(OpenAIError): def __init__(self, message, sig_header, http_body=None): diff --git a/openai/openai_object.py b/openai/openai_object.py index f87ff29f34..3dbd2a8ed7 100644 --- a/openai/openai_object.py +++ b/openai/openai_object.py @@ -2,8 +2,10 @@ from copy import deepcopy from typing import Optional +import openai from openai import api_requestor, util from openai.openai_response import OpenAIResponse +from openai.util import ApiType class OpenAIObject(dict): @@ -14,6 +16,7 @@ def __init__( id=None, api_key=None, api_version=None, + api_type=None, organization=None, response_ms: Optional[int] = None, api_base=None, @@ -30,6 +33,7 @@ def __init__( object.__setattr__(self, "api_key", api_key) object.__setattr__(self, "api_version", api_version) + object.__setattr__(self, "api_type", api_type) object.__setattr__(self, "organization", organization) object.__setattr__(self, "api_base_override", api_base) object.__setattr__(self, "engine", engine) @@ -90,6 +94,7 @@ def __reduce__(self): self.get("id", None), self.api_key, self.api_version, + self.api_type, self.organization, ), dict(self), # state @@ -128,11 +133,13 @@ def refresh_from( values, api_key=None, api_version=None, + api_type=None, organization=None, response_ms: Optional[int] = None, ): self.api_key = api_key or getattr(values, "api_key", None) self.api_version = api_version or getattr(values, "api_version", None) + self.api_type = api_type or getattr(values, "api_type", None) self.organization = organization or getattr(values, "organization", None) self._response_ms = response_ms or getattr(values, "_response_ms", None) @@ -164,6 +171,7 @@ def request( requestor = api_requestor.APIRequestor( key=self.api_key, api_base=self.api_base_override or self.api_base(), + api_type=self.api_type, api_version=self.api_version, organization=self.organization, ) @@ -233,6 +241,10 @@ def to_dict_recursive(self): def openai_id(self): return self.id + @property + def typed_api_type(self): + return ApiType.from_str(self.api_type) if self.api_type else ApiType.from_str(openai.api_type) + # This class overrides __setitem__ to throw exceptions on inputs that it # doesn't like. This can cause problems when we try to copy an object # wholesale because some data that's returned from the API may not be valid @@ -243,6 +255,7 @@ def __copy__(self): self.get("id"), self.api_key, api_version=self.api_version, + api_type=self.api_type, organization=self.organization, ) diff --git a/openai/openai_response.py b/openai/openai_response.py index aa0d3a2ba0..9954247319 100644 --- a/openai/openai_response.py +++ b/openai/openai_response.py @@ -17,4 +17,4 @@ def organization(self) -> Optional[str]: @property def response_ms(self) -> Optional[int]: h = self._headers.get("Openai-Processing-Ms") - return None if h is None else int(h) + return None if h is None else round(float(h)) diff --git a/openai/tests/test_api_requestor.py b/openai/tests/test_api_requestor.py index 2e2927386f..039dd6c09a 100644 --- a/openai/tests/test_api_requestor.py +++ b/openai/tests/test_api_requestor.py @@ -1,11 +1,12 @@ import json - +import pytest import requests from pytest_mock import MockerFixture from openai import Model +from openai.api_requestor import APIRequestor - +@pytest.mark.requestor def test_requestor_sets_request_id(mocker: MockerFixture) -> None: # Fake out 'requests' and confirm that the X-Request-Id header is set. @@ -25,3 +26,25 @@ def fake_request(self, *args, **kwargs): Model.retrieve("xxx", request_id=fake_request_id) # arbitrary API resource got_request_id = got_headers.get("X-Request-Id") assert got_request_id == fake_request_id + +@pytest.mark.requestor +def test_requestor_open_ai_headers() -> None: + api_requestor = APIRequestor(key="test_key", api_type="open_ai") + headers = {"Test_Header": "Unit_Test_Header"} + headers = api_requestor.request_headers(method="get", extra=headers, request_id="test_id") + print(headers) + assert "Test_Header"in headers + assert headers["Test_Header"] == "Unit_Test_Header" + assert "Authorization"in headers + assert headers["Authorization"] == "Bearer test_key" + +@pytest.mark.requestor +def test_requestor_azure_headers() -> None: + api_requestor = APIRequestor(key="test_key", api_type="azure") + headers = {"Test_Header": "Unit_Test_Header"} + headers = api_requestor.request_headers(method="get", extra=headers, request_id="test_id") + print(headers) + assert "Test_Header"in headers + assert headers["Test_Header"] == "Unit_Test_Header" + assert "api-key"in headers + assert headers["api-key"] == "test_key" diff --git a/openai/tests/test_url_composition.py b/openai/tests/test_url_composition.py new file mode 100644 index 0000000000..7298fe8ec1 --- /dev/null +++ b/openai/tests/test_url_composition.py @@ -0,0 +1,124 @@ +from sys import api_version +import pytest + +from openai import Completion +from openai import Engine +from openai.util import ApiType + +@pytest.mark.url +def test_completions_url_composition_azure() -> None: + url = Completion.class_url("http://github.com/test_engine%22,%20%22azure%22,%20'2021-11-01-preview') + assert url == '/openai/deployments/test_engine/completions?api-version=2021-11-01-preview' + +@pytest.mark.url +def test_completions_url_composition_default() -> None: + url = Completion.class_url("http://github.com/test_engine") + assert url == '/engines/test_engine/completions' + +@pytest.mark.url +def test_completions_url_composition_open_ai() -> None: + url = Completion.class_url("http://github.com/test_engine%22,%20%22open_ai") + assert url == '/engines/test_engine/completions' + +@pytest.mark.url +def test_completions_url_composition_invalid_type() -> None: + with pytest.raises(Exception): + url = Completion.class_url("http://github.com/test_engine%22,%20%22invalid") + +@pytest.mark.url +def test_completions_url_composition_instance_url_azure() -> None: + completion = Completion(id="test_id", engine="test_engine", api_type="azure", api_version='2021-11-01-preview') + url = completion.instance_url() + assert url == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview" + +@pytest.mark.url +def test_completions_url_composition_instance_url_azure_no_version() -> None: + completion = Completion(id="test_id", engine="test_engine", api_type="azure", api_version=None) + with pytest.raises(Exception): + completion.instance_url() + +@pytest.mark.url +def test_completions_url_composition_instance_url_default() -> None: + completion = Completion(id="test_id", engine="test_engine") + url = completion.instance_url() + assert url == "/engines/test_engine/completions/test_id" + +@pytest.mark.url +def test_completions_url_composition_instance_url_open_ai() -> None: + completion = Completion(id="test_id", engine="test_engine", api_type="open_ai", api_version='2021-11-01-preview') + url = completion.instance_url() + assert url == "/engines/test_engine/completions/test_id" + +@pytest.mark.url +def test_completions_url_composition_instance_url_invalid() -> None: + completion = Completion(id="test_id", engine="test_engine", api_type="invalid") + with pytest.raises(Exception): + url = completion.instance_url() + +@pytest.mark.url +def test_completions_url_composition_instance_url_timeout_azure() -> None: + completion = Completion(id="test_id", engine="test_engine", api_type="azure", api_version='2021-11-01-preview') + completion["timeout"] = 12 + url = completion.instance_url() + assert url == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview&timeout=12" + +@pytest.mark.url +def test_completions_url_composition_instance_url_timeout_openai() -> None: + completion = Completion(id="test_id", engine="test_engine", api_type="open_ai" ) + completion["timeout"] = 12 + url = completion.instance_url() + assert url == "/engines/test_engine/completions/test_id?timeout=12" + +@pytest.mark.url +def test_engine_search_url_composition_azure() -> None: + engine = Engine(id="test_id", api_type="azure", api_version='2021-11-01-preview') + assert engine.api_type == "azure" + assert engine.typed_api_type == ApiType.AZURE + url = engine.instance_url("http://github.com/test_operation") + assert url == '/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview' + +@pytest.mark.url +def test_engine_search_url_composition_azure_no_version() -> None: + engine = Engine(id="test_id", api_type="azure", api_version=None) + assert engine.api_type == "azure" + assert engine.typed_api_type == ApiType.AZURE + with pytest.raises(Exception): + engine.instance_url("http://github.com/test_operation") + +@pytest.mark.url +def test_engine_search_url_composition_azure_no_operation() -> None: + engine = Engine(id="test_id", api_type="azure", api_version='2021-11-01-preview') + assert engine.api_type == "azure" + assert engine.typed_api_type == ApiType.AZURE + with pytest.raises(Exception): + engine.instance_url() + +@pytest.mark.url +def test_engine_search_url_composition_default() -> None: + engine = Engine(id="test_id") + assert engine.api_type == None + assert engine.typed_api_type == ApiType.OPEN_AI + url = engine.instance_url() + assert url == '/engines/test_id' + +@pytest.mark.url +def test_engine_search_url_composition_open_ai() -> None: + engine = Engine(id="test_id", api_type="open_ai") + assert engine.api_type == "open_ai" + assert engine.typed_api_type == ApiType.OPEN_AI + url = engine.instance_url() + assert url == '/engines/test_id' + +@pytest.mark.url +def test_engine_search_url_composition_invalid_type() -> None: + engine = Engine(id="test_id", api_type="invalid") + assert engine.api_type == "invalid" + with pytest.raises(Exception): + assert engine.typed_api_type == ApiType.OPEN_AI + +@pytest.mark.url +def test_engine_search_url_composition_invalid_search() -> None: + engine = Engine(id="test_id", api_type="invalid") + assert engine.api_type == "invalid" + with pytest.raises(Exception): + engine.search() diff --git a/openai/util.py b/openai/util.py index 1b87ac893f..b57cd5ea5a 100644 --- a/openai/util.py +++ b/openai/util.py @@ -3,6 +3,7 @@ import re import sys from typing import Optional +from enum import Enum import openai @@ -17,6 +18,21 @@ "logfmt", ] +api_key_to_header = lambda api, key: {"Authorization": f"Bearer {key}"} if api == ApiType.OPEN_AI else {"api-key": f"{key}"} + +class ApiType(Enum): + AZURE = 1 + OPEN_AI = 2 + + @staticmethod + def from_str(label): + if label.lower() == 'azure': + return ApiType.AZURE + elif label.lower() in ('open_ai', 'openai'): + return ApiType.OPEN_AI + else: + raise openai.error.InvalidAPIType("The API type provided in invalid. Please select one of the supported API types: 'azure', 'open_ai'") + def _console_log_level(): if openai.log in ["debug", "info"]: diff --git a/openai/version.py b/openai/version.py index 27696fb246..c1830e4131 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.11.6" +VERSION = "0.12.0" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000000..5b78d87c16 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +markers = + url: mark a test as part of the url composition tests. + requestor: mark test as part of the api_requestor tests. From eabf01f0f4aa514fa1c494c2ca9aef7c17a6a1cc Mon Sep 17 00:00:00 2001 From: Ted Sanders <95656834+ted-at-openai@users.noreply.github.com> Date: Mon, 24 Jan 2022 16:20:48 -0800 Subject: [PATCH 013/914] ted-at-openai/update-embedding-examples (#67) * updates embeddings examples * updates README with new example notebook using embeddings for recommendation --- README.md | 1 + examples/embeddings/Clustering.ipynb | 4 +- examples/embeddings/Code_search.ipynb | 6 +- examples/embeddings/Get_embeddings.ipynb | 5 +- examples/embeddings/Obtain_dataset.ipynb | 6 +- examples/embeddings/Recommendation.ipynb | 33446 ++++++++++++++++ ...emantic_text_search_using_embeddings.ipynb | 2 +- .../User_and_product_embeddings.ipynb | 5 +- examples/embeddings/Visualize_in_3d.ipynb | 2 +- .../embeddings/Zero-shot_classification.ipynb | 12 +- openai/embeddings_utils.py | 123 +- openai/version.py | 2 +- 12 files changed, 33588 insertions(+), 26 deletions(-) create mode 100644 examples/embeddings/Recommendation.ipynb diff --git a/README.md b/README.md index 4238f02210..cb688fde7a 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Examples of how to use embeddings are shared in the following Jupyter notebooks: - [Semantic text search using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Semantic_text_search_using_embeddings.ipynb) - [User and product embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/User_and_product_embeddings.ipynb) - [Zero-shot classification using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Zero-shot_classification.ipynb) +- [Recommendation using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Recommendation.ipynb) For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://beta.openai.com/docs/guides/embeddings) in the OpenAI documentation. diff --git a/examples/embeddings/Clustering.ipynb b/examples/embeddings/Clustering.ipynb index ab5cf055ab..a03eec5357 100644 --- a/examples/embeddings/Clustering.ipynb +++ b/examples/embeddings/Clustering.ipynb @@ -31,7 +31,7 @@ "\n", "\n", "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", - "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", + "df['text-similarity-babbage-001'] = df.babbage_similarity.apply(eval).apply(np.array)\n", "matrix = np.vstack(df.babbage_similarity.values)\n", "matrix.shape" ] @@ -253,7 +253,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, "orig_nbformat": 4 }, diff --git a/examples/embeddings/Code_search.ipynb b/examples/embeddings/Code_search.ipynb index ca60566a7a..02aa162068 100644 --- a/examples/embeddings/Code_search.ipynb +++ b/examples/embeddings/Code_search.ipynb @@ -81,7 +81,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For code search models we use babbage-code-search-code to obtain embeddings for code snippets, and code-search-text to embed natural language queries." + "For code search models we use code-search-{model}-code to obtain embeddings for code snippets, and code-search-{model}-text to embed natural language queries." ] }, { @@ -188,7 +188,7 @@ "from openai.embeddings_utils import get_embedding\n", "\n", "df = pd.DataFrame(all_funcs)\n", - "df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, engine='babbage-code-search-code'))\n", + "df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, engine='code-search-babbage-code-001'))\n", "df['filepath'] = df['filepath'].apply(lambda x: x.replace(code_root, \"\"))\n", "df.to_csv(\"output/code_search_openai-python.csv\", index=False)\n", "df.head()" @@ -234,7 +234,7 @@ "from openai.embeddings_utils import cosine_similarity\n", "\n", "def search_functions(df, code_query, n=3, pprint=True, n_lines=7):\n", - " embedding = get_embedding(code_query, engine='babbage-code-search-text')\n", + " embedding = get_embedding(code_query, engine='code-search-babbage-text-001')\n", " df['similarities'] = df.code_embedding.apply(lambda x: cosine_similarity(x, embedding))\n", "\n", " res = df.sort_values('similarities', ascending=False).head(n)\n", diff --git a/examples/embeddings/Get_embeddings.ipynb b/examples/embeddings/Get_embeddings.ipynb index dd60ef5230..e1b17327e2 100644 --- a/examples/embeddings/Get_embeddings.ipynb +++ b/examples/embeddings/Get_embeddings.ipynb @@ -50,13 +50,12 @@ "from tenacity import retry, wait_random_exponential, stop_after_attempt\n", "\n", "@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))\n", - "def get_embedding(text, engine=\"text-similarity-davinci-001\"):\n", - "\n", + "def get_embedding(text: str, engine=\"text-similarity-davinci-001\") -> List[float]:\n", "\n", " # replace newlines, which can negatively affect performance.\n", " text = text.replace(\"\\n\", \" \")\n", "\n", - " return openai.Embedding.create(input=[text], engine=engine)['data'][0]['embedding']\n", + " return openai.Embedding.create(input=[text], engine=engine)[\"data\"][0][\"embedding\"]\n", "\n", "embedding = get_embedding(\"Sample query text goes here\", engine=\"text-search-ada-query-001\")\n", "print(len(embedding))" diff --git a/examples/embeddings/Obtain_dataset.ipynb b/examples/embeddings/Obtain_dataset.ipynb index 61c5775c46..80d1db33a9 100644 --- a/examples/embeddings/Obtain_dataset.ipynb +++ b/examples/embeddings/Obtain_dataset.ipynb @@ -159,8 +159,8 @@ "from openai.embeddings_utils import get_embedding\n", "\n", "# This will take just under 10 minutes\n", - "df['babbage_similarity'] = df.combined.apply(lambda x: get_embedding(x, engine='babbage-similarity'))\n", - "df['babbage_search'] = df.combined.apply(lambda x: get_embedding(x, engine='babbage-search-document'))\n", + "df['babbage_similarity'] = df.combined.apply(lambda x: get_embedding(x, engine='text-similarity-babbage-001'))\n", + "df['babbage_search'] = df.combined.apply(lambda x: get_embedding(x, engine='text-search-babbage-doc-001'))\n", "df.to_csv('output/embedded_1k_reviews.csv')" ] } @@ -183,7 +183,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, "orig_nbformat": 4 }, diff --git a/examples/embeddings/Recommendation.ipynb b/examples/embeddings/Recommendation.ipynb new file mode 100644 index 0000000000..9a2cce82c0 --- /dev/null +++ b/examples/embeddings/Recommendation.ipynb @@ -0,0 +1,33446 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Recommendation using embeddings and nearest neighbor search\n", + "\n", + "Recommendations are widespread across the web.\n", + "\n", + "- 'Bought that item? Try these similar items.'\n", + "- 'Enjoy that book? Try these similar titles.'\n", + "- 'Not the help page you were looking for? Try these similar pages.'\n", + "\n", + "This notebook demonstrates how to use embeddings to find similar items to recommend. In particular, we use [AG's corpus of news articles](http://groups.di.unipi.it/~gulli/AG_corpus_of_news_articles.html) as our dataset.\n", + "\n", + "Our model will answer the question: given an article, what are the articles most similar to it?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Imports\n", + "\n", + "First, let's import the packages and functions we'll need for later. If you don't have these, you'll need to install them. You can install them via your terminal by running `pip install {package_name}`, e.g. `pip install pandas`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "from typing import List\n", + "\n", + "import pandas as pd\n", + "import pickle\n", + "\n", + "from openai.embeddings_utils import (\n", + " get_embedding,\n", + " distances_from_embeddings,\n", + " tsne_components_from_embeddings,\n", + " chart_from_components,\n", + " indices_of_nearest_neighbors_from_distances,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Load data\n", + "\n", + "Next, let's load the AG news data and see what it looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
titledescriptionlabel_intlabel
0World BriefingsBRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime M...1World
1Nvidia Puts a Firewall on a Motherboard (PC Wo...PC World - Upcoming chip set will include buil...4Sci/Tech
2Olympic joy in Greek, Chinese pressNewspapers in Greece reflect a mixture of exhi...2Sports
3U2 Can iPod with PicturesSAN JOSE, Calif. -- Apple Computer (Quote, Cha...4Sci/Tech
4The Dream FactoryAny product, any shape, any size -- manufactur...4Sci/Tech
\n", + "
" + ], + "text/plain": [ + " title \\\n", + "0 World Briefings \n", + "1 Nvidia Puts a Firewall on a Motherboard (PC Wo... \n", + "2 Olympic joy in Greek, Chinese press \n", + "3 U2 Can iPod with Pictures \n", + "4 The Dream Factory \n", + "\n", + " description label_int label \n", + "0 BRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime M... 1 World \n", + "1 PC World - Upcoming chip set will include buil... 4 Sci/Tech \n", + "2 Newspapers in Greece reflect a mixture of exhi... 2 Sports \n", + "3 SAN JOSE, Calif. -- Apple Computer (Quote, Cha... 4 Sci/Tech \n", + "4 Any product, any shape, any size -- manufactur... 4 Sci/Tech " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# load data\n", + "dataset_path = \"https://cdn.openai.com/API/examples/data/AG_news_samples.csv/"\n", + "df = pd.read_csv(dataset_path)\n", + "\n", + "# print dataframe\n", + "n_examples = 5\n", + "df.head(n_examples)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's take a look at those same examples, but not truncated by ellipses." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Title: World Briefings\n", + "Description: BRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime Minister Tony Blair urged the international community to consider global warming a dire threat and agree on a plan of action to curb the quot;alarming quot; growth of greenhouse gases.\n", + "Label: World\n", + "\n", + "Title: Nvidia Puts a Firewall on a Motherboard (PC World)\n", + "Description: PC World - Upcoming chip set will include built-in security features for your PC.\n", + "Label: Sci/Tech\n", + "\n", + "Title: Olympic joy in Greek, Chinese press\n", + "Description: Newspapers in Greece reflect a mixture of exhilaration that the Athens Olympics proved successful, and relief that they passed off without any major setback.\n", + "Label: Sports\n", + "\n", + "Title: U2 Can iPod with Pictures\n", + "Description: SAN JOSE, Calif. -- Apple Computer (Quote, Chart) unveiled a batch of new iPods, iTunes software and promos designed to keep it atop the heap of digital music players.\n", + "Label: Sci/Tech\n", + "\n", + "Title: The Dream Factory\n", + "Description: Any product, any shape, any size -- manufactured on your desktop! The future is the fabricator. By Bruce Sterling from Wired magazine.\n", + "Label: Sci/Tech\n" + ] + } + ], + "source": [ + "# print the title, description, and label of each example\n", + "for idx, row in df.head(n_examples).iterrows():\n", + " print(\"\")\n", + " print(f\"Title: {row['title']}\")\n", + " print(f\"Description: {row['description']}\")\n", + " print(f\"Label: {row['label']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. Build cache to save embeddings\n", + "\n", + "Before getting embeddings for these articles, let's set up a cache to save the embeddings we generate. In general, it's a good idea to save your embeddings so you can re-use them later. If you don't save them, you'll pay again each time you compute them again.\n", + "\n", + "To save you the expense of computing the embeddings needed for this demo, we've provided a pre-filled cache via the URL below. The cache is a dictionary that maps tuples of `(text, engine)` to a `list of floats` embedding. The cache is saved as a Python pickle file." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# establish a cache of embeddings to avoid recomputing\n", + "# cache is a dict of tuples (text, engine) -> embedding, saved as a pickle file\n", + "\n", + "# set path to embedding cache\n", + "embedding_cache_path_to_load = \"https://cdn.openai.com/API/examples/data/example_embeddings_cache.pkl/"\n", + "embedding_cache_path_to_save = \"example_embeddings_cache.pkl\"\n", + "\n", + "# load the cache if it exists, and save a copy to disk\n", + "try:\n", + " embedding_cache = pd.read_pickle(embedding_cache_path_to_load)\n", + "except FileNotFoundError:\n", + " embedding_cache = {}\n", + "with open(embedding_cache_path_to_save, \"wb\") as embedding_cache_file:\n", + " pickle.dump(embedding_cache, embedding_cache_file)\n", + "\n", + "# define a function to retrieve embeddings from the cache if present, and otherwise request via the API\n", + "def embedding_from_string(\n", + " string: str,\n", + " engine: str = \"text-similarity-babbage-001\",\n", + " embedding_cache=embedding_cache\n", + ") -> List:\n", + " \"\"\"Return embedding of given string, using a cache to avoid recomputing.\"\"\"\n", + " if (string, engine) not in embedding_cache.keys():\n", + " embedding_cache[(string, engine)] = get_embedding(string, engine)\n", + " print('NOT FOUND')\n", + " with open(embedding_cache_path_to_save, \"wb\") as embedding_cache_file:\n", + " pickle.dump(embedding_cache, embedding_cache_file)\n", + " return embedding_cache[(string, engine)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's check that it works by getting an embedding." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Example string: BRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime Minister Tony Blair urged the international community to consider global warming a dire threat and agree on a plan of action to curb the quot;alarming quot; growth of greenhouse gases.\n", + "\n", + "Example embedding: [-0.029093433171510696, 0.007570988964289427, -0.011933144181966782, -0.016499919816851616, 0.026675179600715637, -0.016704540699720383, -0.019439026713371277, 0.015421006828546524, -0.009700911119580269, -0.02580088935792446]...\n" + ] + } + ], + "source": [ + "# as an example, take the first description from the dataset\n", + "example_string = df[\"description\"].values[0]\n", + "print(f\"\\nExample string: {example_string}\")\n", + "\n", + "# print the first 10 dimensions of the embedding\n", + "example_embedding = embedding_from_string(example_string, engine=\"text-similarity-babbage-001\")\n", + "print(f\"\\nExample embedding: {example_embedding[:10]}...\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4. Recommend similar articles based on embeddings\n", + "\n", + "To find similar articles, let's follow a three-step plan:\n", + "1. Get the similarity embeddings of all the article descriptions\n", + "2. Calculate the distance between a source title and all other articles\n", + "3. Print out the other articles closest to the source title" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def print_recommendations_from_strings(\n", + " strings: List[str],\n", + " index_of_source_string: int,\n", + " k_nearest_neighbors: int = 1,\n", + " engine=\"text-similarity-babbage-001\",\n", + ") -> List[int]:\n", + " \"\"\"Print out the k nearest neighbors of a given string.\"\"\"\n", + " # get embeddings for all strings\n", + " embeddings = [embedding_from_string(string, engine=engine) for string in strings]\n", + " # get the embedding of the source string\n", + " query_embedding = embeddings[index_of_source_string]\n", + " # get distances between the source embedding and other embeddings (function from embeddings_utils.py)\n", + " distances = distances_from_embeddings(query_embedding, embeddings, distance_metric=\"cosine\")\n", + " # get indices of nearest neighbors (function from embeddings_utils.py)\n", + " indices_of_nearest_neighbors = indices_of_nearest_neighbors_from_distances(distances)\n", + "\n", + " # print out source string\n", + " query_string = strings[index_of_source_string]\n", + " print(f\"Source string: {query_string}\")\n", + " # print out its k nearest neighbors\n", + " k_counter = 0\n", + " for i in indices_of_nearest_neighbors:\n", + " # skip any strings that are identical matches to the starting string\n", + " if query_string == strings[i]:\n", + " continue\n", + " # stop after printing out k articles\n", + " if k_counter >= k_nearest_neighbors:\n", + " break\n", + " k_counter += 1\n", + "\n", + " # print out the similar strings and their distances\n", + " print(\n", + " f\"\"\"\n", + " --- Recommendation #{k_counter} (nearest neighbor {k_counter} of {k_nearest_neighbors}) ---\n", + " String: {strings[i]}\n", + " Distance: {distances[i]:0.3f}\"\"\"\n", + " )\n", + "\n", + " return indices_of_nearest_neighbors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 5. Example recommendations\n", + "\n", + "Let's look for articles similar to first one, which was about Tony Blair." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Source string: BRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime Minister Tony Blair urged the international community to consider global warming a dire threat and agree on a plan of action to curb the quot;alarming quot; growth of greenhouse gases.\n", + "\n", + " --- Recommendation #1 (nearest neighbor 1 of 5) ---\n", + " String: THE re-election of British Prime Minister Tony Blair would be seen as an endorsement of the military action in Iraq, Prime Minister John Howard said today.\n", + " Distance: 0.164\n", + "\n", + " --- Recommendation #2 (nearest neighbor 2 of 5) ---\n", + " String: Israel is prepared to back a Middle East conference convened by Tony Blair early next year despite having expressed fears that the British plans were over-ambitious and designed \n", + " Distance: 0.169\n", + "\n", + " --- Recommendation #3 (nearest neighbor 3 of 5) ---\n", + " String: WASHINGTON (Reuters) - President Bush on Friday set a four-year goal of seeing a Palestinian state established and he and British Prime Minister Tony Blair vowed to mobilize international support to help make it happen now that Yasser Arafat is dead.\n", + " Distance: 0.174\n", + "\n", + " --- Recommendation #4 (nearest neighbor 4 of 5) ---\n", + " String: AP - President Bush declared Friday that charges of voter fraud have cast doubt on the Ukrainian election, and warned that any European-negotiated pact on Iran's nuclear program must ensure the world can verify Tehran's compliance.\n", + " Distance: 0.179\n", + "\n", + " --- Recommendation #5 (nearest neighbor 5 of 5) ---\n", + " String: AFP - A battle group of British troops rolled out of southern Iraq on a US-requested mission to deadlier areas near Baghdad, in a major political gamble for British Prime Minister Tony Blair.\n", + " Distance: 0.182\n" + ] + } + ], + "source": [ + "article_descriptions = df[\"description\"].tolist()\n", + "\n", + "tony_blair_articles = print_recommendations_from_strings(\n", + " strings=article_descriptions, # let's base similarity off of the article description\n", + " index_of_source_string=0, # let's look at articles similar to the first one about Tony Blair\n", + " k_nearest_neighbors=5, # let's look at the 5 most similar articles\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pretty good! All 5 of the recommendations look similar to the original article about Tony Blair. Interestingly, note that #4 doesn't mention the words Tony Blair, but is nonetheless recommended by the model, presumably because the model understands that Tony Blair tends to be related to President Bush or European pacts over Iran's nuclear program. This illustrates the potential power of using embeddings rather than basic string matching; our models understand what topics are related to one another, even when their words don't overlap." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see how our recommender does on the second example article about NVIDIA's new chipset with more security." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Source string: PC World - Upcoming chip set will include built-in security features for your PC.\n", + "\n", + " --- Recommendation #1 (nearest neighbor 1 of 5) ---\n", + " String: PC World - Updated antivirus software for businesses adds intrusion prevention features.\n", + " Distance: 0.108\n", + "\n", + " --- Recommendation #2 (nearest neighbor 2 of 5) ---\n", + " String: PC World - Send your video throughout your house--wirelessly--with new gateways and media adapters.\n", + " Distance: 0.160\n", + "\n", + " --- Recommendation #3 (nearest neighbor 3 of 5) ---\n", + " String: PC World - The one-time World Class Product of the Year PDA gets a much-needed upgrade.\n", + " Distance: 0.161\n", + "\n", + " --- Recommendation #4 (nearest neighbor 4 of 5) ---\n", + " String: PC World - Symantec, McAfee hope raising virus-definition fees will move users to\\ suites.\n", + " Distance: 0.166\n", + "\n", + " --- Recommendation #5 (nearest neighbor 5 of 5) ---\n", + " String: Ziff Davis - The company this week will unveil more programs and technologies designed to ease users of its high-end servers onto its Integrity line, which uses Intel's 64-bit Itanium processor.\n", + " Distance: 0.193\n" + ] + } + ], + "source": [ + "chipset_security_articles = print_recommendations_from_strings(\n", + " strings=article_descriptions, # let's base similarity off of the article description\n", + " index_of_source_string=1, # let's look at articles similar to the second one about a more secure chipset\n", + " k_nearest_neighbors=5, # let's look at the 5 most similar articles\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From the printed distances, you can see that the #1 recommendation is much closer than all the others (0.108 vs 0.160+). And the #1 recommendation looks very similar to the starting article - it's another article from PC World about increasing computer security. Pretty good! " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Appendix: Using embeddings in more sophisticated recommenders\n", + "\n", + "A more sophisticated way to build a recommender system is to train a machine learning model that takes in tens or hundreds of signals, such as item popularity or user click data. Even in this system, embeddings can be a very useful signal into the recommender, especially for items that are being 'cold started' with no user data yet (e.g., a brand new product added to the catalog without any clicks yet)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Appendix: Using embeddings to visualize similar articles" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To get a sense of what our nearest neighbor recommender is doing, let's visualize the article embeddings. Although we can't plot the 2048 dimensions of each embedding vector, we can use techniques like [t-SNE](https://en.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding) or [PCA](https://en.wikipedia.org/wiki/Principal_component_analysis) to compress the embeddings down into 2 or 3 dimensions, which we can chart.\n", + "\n", + "Before visualizing the nearest neighbors, let's visualize all of the article descriptions using t-SNE. Note that t-SNE is not deterministic, meaning that results may vary from run to run." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/ted/.virtualenvs/openai/lib/python3.9/site-packages/sklearn/manifold/_t_sne.py:982: FutureWarning: The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly/" + }, + "data": [ + { + "customdata": [ + [ + "BRITAIN: BLAIR WARNS OF
CLIMATE THREAT Prime Minister
Tony Blair urged the
international community to
consider global warming a dire
threat and agree on a plan of
action to curb the
quot;alarming quot; growth of
greenhouse gases." + ], + [ + "KABUL, Sept 22 (AFP): Three US
soldiers were killed and 14
wounded in a series of fierce
clashes with suspected Taliban
fighters in south and eastern
Afghanistan this week, the US
military said Wednesday." + ], + [ + "AUSTRALIAN journalist John
Martinkus is lucky to be alive
after spending 24 hours in the
hands of Iraqi militants at
the weekend. Martinkus was in
Baghdad working for the SBS
Dateline TV current affairs
program" + ], + [ + " GAZA (Reuters) - An Israeli
helicopter fired a missile
into a town in the southern
Gaza Strip late on Wednesday,
witnesses said, hours after a
Palestinian suicide bomber
blew herself up in Jerusalem,
killing two Israeli border
policemen." + ], + [ + "RIYADH, Saudi Arabia -- Saudi
police are seeking two young
men in the killing of a Briton
in a Riyadh parking lot, the
Interior Ministry said today,
and the British ambassador
called it a terrorist attack." + ], + [ + "A gas explosion at a coal mine
in northern China killed 33
workers in the 10th deadly
mine blast reported in three
months. The explosion occurred
yesterday at 4:20 pm at Nanlou
township" + ], + [ + "Reuters - Palestinian leader
Mahmoud Abbas called\\Israel
\"the Zionist enemy\" Tuesday,
unprecedented language for\\the
relative moderate who is
expected to succeed Yasser
Arafat." + ], + [ + "Nasser al-Qidwa, Palestinian
representative at the United
Nations and nephew of late
leader Yasser Arafat, handed
Arafat #39;s death report to
the Palestinian National
Authority (PNA) on Saturday." + ], + [ + "CAIRO, Egypt - France's
foreign minister appealed
Monday for the release of two
French journalists abducted in
Baghdad, saying the French
respect all religions. He did
not rule out traveling to
Baghdad..." + ], + [ + "United Arab Emirates President
and ruler of Abu Dhabi Sheik
Zayed bin Sultan al-Nayhan
died Tuesday, official
television reports. He was 86." + ], + [ + "PALESTINIAN leader Yasser
Arafat today issued an urgent
call for the immediate release
of two French journalists
taken hostage in Iraq." + ], + [ + "The al-Qaida terrorist network
spent less than \\$50,000 on
each of its major attacks
except for the Sept. 11, 2001,
suicide hijackings, and one of
its hallmarks is using" + ], + [ + "A FATHER who scaled the walls
of a Cardiff court dressed as
superhero Robin said the
Buckingham Palace protester
posed no threat. Fathers 4
Justice activist Jim Gibson,
who earlier this year staged
an eye-catching" + ], + [ + "Julia Gillard has reportedly
bowed out of the race to
become shadow treasurer,
taking enormous pressure off
Opposition Leader Mark Latham." + ], + [ + "AFP - Maybe it's something to
do with the fact that the
playing area is so vast that
you need a good pair of
binoculars to see the action
if it's not taking place right
in front of the stands." + ], + [ + "Egypt #39;s release of accused
Israeli spy Azzam Azzam in an
apparent swap for six Egyptian
students held on suspicion of
terrorism is expected to melt
the ice and perhaps result" + ], + [ + "GAZA CITY, Gaza Strip: Hamas
militants killed an Israeli
soldier and wounded four with
an explosion in a booby-
trapped chicken coop on
Tuesday, in what the Islamic
group said was an elaborate
scheme to lure troops to the
area with the help of a double" + ], + [ + "AP - The 300 men filling out
forms in the offices of an
Iranian aid group were offered
three choices: Train for
suicide attacks against U.S.
troops in Iraq, for suicide
attacks against Israelis or to
assassinate British author
Salman Rushdie." + ], + [ + "ATHENS, Greece - Gail Devers,
the most talented yet star-
crossed hurdler of her
generation, was unable to
complete even one hurdle in
100-meter event Sunday -
failing once again to win an
Olympic hurdling medal.
Devers, 37, who has three
world championships in the
hurdles but has always flopped
at the Olympics, pulled up
short and screamed as she slid
under the first hurdle..." + ], + [ + " NAIROBI (Reuters) - The
Sudanese government and its
southern rebel opponents have
agreed to sign a pledge in the
Kenyan capital on Friday to
formally end a brutal 21-year-
old civil war, with U.N.
Security Council ambassadors
as witnesses." + ], + [ + "AP - Former Guatemalan
President Alfonso Portillo
#151; suspected of corruption
at home #151; is living and
working part-time in the same
Mexican city he fled two
decades ago to avoid arrest on
murder charges, his close
associates told The Associated
Press on Sunday." + ], + [ + "washingtonpost.com - BRUSSELS,
Aug. 26 -- The United States
will have to wait until next
year to see its fight with the
European Union over biotech
foods resolved, as the World
Trade Organization agreed to
an E.U. request to bring
scientists into the debate,
officials said Thursday." + ], + [ + "Insisting that Hurriyat
Conference is the real
representative of Kashmiris,
Pakistan has claimed that
India is not ready to accept
ground realities in Kashmir." + ], + [ + "VIENNA -- After two years of
investigating Iran's atomic
program, the UN nuclear
watchdog still cannot rule out
that Tehran has a secret atom
bomb project as Washington
insists, the agency's chief
said yesterday." + ], + [ + "AFP - US Secretary of State
Colin Powell wrapped up a
three-nation tour of Asia
after winning pledges from
Japan, China and South Korea
to press North Korea to resume
stalled talks on its nuclear
weapons programs." + ], + [ + "CAIRO, Egypt An Egyptian
company says one of its four
workers who had been kidnapped
in Iraq has been freed. It
says it can #39;t give the
status of the others being
held hostage but says it is
quot;doing its best to secure
quot; their release." + ], + [ + "AFP - Hosts India braced
themselves for a harrowing
chase on a wearing wicket in
the first Test after Australia
declined to enforce the
follow-on here." + ], + [ + "Prime Minister Paul Martin of
Canada urged Haitian leaders
on Sunday to allow the
political party of the deposed
president, Jean-Bertrand
Aristide, to take part in new
elections." + ], + [ + "Hostage takers holding up to
240 people at a school in
southern Russia have refused
to talk with a top Islamic
leader and demanded to meet
with regional leaders instead,
ITAR-TASS reported on
Wednesday." + ], + [ + "Three children from a care
home are missing on the
Lancashire moors after they
are separated from a group." + ], + [ + "Diabetics should test their
blood sugar levels more
regularly to reduce the risk
of cardiovascular disease, a
study says." + ], + [ + "Iraq's interim Prime Minister
Ayad Allawi announced that
proceedings would begin
against former Baath Party
leaders." + ], + [ + "A toxic batch of home-brewed
alcohol has killed 31 people
in several towns in central
Pakistan, police and hospital
officials say." + ], + [ + " BEIJING (Reuters) - North
Korea is committed to holding
six-party talks aimed at
resolving the crisis over its
nuclear weapons program, but
has not indicated when, a top
British official said on
Tuesday." + ], + [ + " BAGHDAD (Reuters) - Iraq's
interim government extended
the closure of Baghdad
international airport
indefinitely on Saturday
under emergency rule imposed
ahead of this week's U.S.-led
offensive on Falluja." + ], + [ + "Rivaling Bush vs. Kerry for
bitterness, doctors and trial
lawyers are squaring off this
fall in an unprecedented four-
state struggle over limiting
malpractice awards..." + ], + [ + "AP - Hundreds of tribesmen
gathered Tuesday near the area
where suspected al-Qaida-
linked militants are holding
two Chinese engineers and
demanding safe passage to
their reputed leader, a former
U.S. prisoner from Guantanamo
Bay, Cuba, officials and
residents said." + ], + [ + "In an alarming development,
high-precision equipment and
materials which could be used
for making nuclear bombs have
disappeared from some Iraqi
facilities, the United Nations
watchdog agency has said." + ], + [ + "A US airman dies and two are
hurt as a helicopter crashes
due to technical problems in
western Afghanistan." + ], + [ + "Jacques Chirac has ruled out
any withdrawal of French
troops from Ivory Coast,
despite unrest and anti-French
attacks, which have forced the
evacuation of thousands of
Westerners." + ], + [ + "Japanese Prime Minister
Junichiro Koizumi reshuffled
his cabinet yesterday,
replacing several top
ministers in an effort to
boost his popularity,
consolidate political support
and quicken the pace of
reforms in the world #39;s
second-largest economy." + ], + [ + "TBILISI (Reuters) - At least
two Georgian soldiers were
killed and five wounded in
artillery fire with
separatists in the breakaway
region of South Ossetia,
Georgian officials said on
Wednesday." + ], + [ + "Laksamana.Net - Two Indonesian
female migrant workers freed
by militants in Iraq are
expected to arrive home within
a day or two, the Foreign
Affairs Ministry said
Wednesday (6/10/04)." + ], + [ + "A bus was hijacked today and
shots were fired at police who
surrounded it on the outskirts
of Athens. Police did not know
how many passengers were
aboard the bus." + ], + [ + "AP - President Bashar Assad
shuffled his Cabinet on
Monday, just weeks after the
United States and the United
Nations challenged Syria over
its military presence in
Lebanon and the security
situation along its border
with Iraq." + ], + [ + "AP - President Vladimir Putin
has signed a bill confirming
Russia's ratification of the
Kyoto Protocol, the Kremlin
said Friday, clearing the way
for the global climate pact to
come into force early next
year." + ], + [ + "AP - The authenticity of newly
unearthed memos stating that
George W. Bush failed to meet
standards of the Texas Air
National Guard during the
Vietnam War was questioned
Thursday by the son of the
late officer who reportedly
wrote the memos." + ], + [ + "Canadian Press - OAKVILLE,
Ont. (CP) - The body of a
missing autistic man was
pulled from a creek Monday,
just metres from where a key
piece of evidence was
uncovered but originally
overlooked because searchers
had the wrong information." + ], + [ + "AFP - German Chancellor
Gerhard Schroeder arrived in
Libya for an official visit
during which he is to hold
talks with Libyan leader
Moamer Kadhafi." + ], + [ + "The government will examine
claims 100,000 Iraqi civilians
have been killed since the US-
led invasion, Jack Straw says." + ], + [ + "Eton College and Clarence
House joined forces yesterday
to deny allegations due to be
made at an employment tribunal
today by a former art teacher
that she improperly helped
Prince Harry secure an A-level
pass in art two years ago." + ], + [ + "AFP - Great Britain's chances
of qualifying for the World
Group of the Davis Cup were
evaporating rapidly after
Austria moved into a 2-1 lead
following the doubles." + ], + [ + "Asia-Pacific leaders meet in
Australia to discuss how to
keep nuclear weapons out of
the hands of extremists." + ], + [ + " TALL AFAR, Iraq -- A three-
foot-high coil of razor wire,
21-ton armored vehicles and
American soldiers with black
M-4 assault rifles stood
between tens of thousands of
people and their homes last
week." + ], + [ + "LAKE GEORGE, N.Y. - Even
though he's facing double hip
replacement surgery, Bill
Smith is more than happy to
struggle out the door each
morning, limp past his brand
new P.T..." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
poured cold water on Tuesday
on recent international
efforts to restart stalled
peace talks with Syria, saying
there was \"no possibility\" of
returning to previous
discussions." + ], + [ + "Dutch smugness was slapped
hard during the past
fortnight. The rude awakening
began with the barbaric
slaying of controversial
filmmaker Theo van Gogh on
November 2. Then followed a
reciprocal cycle of some" + ], + [ + "pee writes quot;A passenger
on a commuter plane in
northern Norway attacked both
pilots and at least one
passenger with an axe as the
aircraft was coming in to
land." + ], + [ + "Prime Minister Ariel Sharon
pledged Sunday to escalate a
broad Israeli offensive in
northern Gaza, saying troops
will remain until Palestinian
rocket attacks are halted.
Israeli officials said the
offensive -- in which 58
Palestinians and three
Israelis have been killed --
will help clear the way for an
Israeli withdrawal." + ], + [ + "NEW YORK - Wall Street
professionals know to keep
their expectations in check in
September, historically the
worst month of the year for
stocks. As summertime draws to
a close, money managers are
getting back to business,
cleaning house, and often
sending the market lower in
the process..." + ], + [ + "A group linked to al Qaeda
ally Abu Musab al-Zarqawi said
it had tried to kill Iraq
#39;s environment minister on
Tuesday and warned it would
not miss next time, according
to an Internet statement." + ], + [ + "The Israeli military killed
four Palestinian militants on
Wednesday as troops in tanks
and armored vehicles pushed
into another town in the
northern Gaza Strip, extending" + ], + [ + "KIRKUK, Iraq - A suicide
attacker detonated a car bomb
Saturday outside a police
academy in the northern Iraqi
city of Kirkuk as hundreds of
trainees and civilians were
leaving for the day, killing
at least 20 people and
wounding 36, authorities said.
Separately, U.S and Iraqi
forces clashed with insurgents
in another part of northern
Iraq after launching an
operation to destroy an
alleged militant cell in the
town of Tal Afar, the U.S..." + ], + [ + "AP - Many states are facing
legal challenges over possible
voting problems Nov. 2. A look
at some of the developments
Thursday:" + ], + [ + "Israeli troops withdrew from
the southern Gaza Strip town
of Khan Yunis on Tuesday
morning, following a 30-hour
operation that left 17
Palestinians dead." + ], + [ + "PM-designate Omar Karameh
forms a new 30-member cabinet
which includes women for the
first time." + ], + [ + "Bahrain #39;s king pardoned a
human rights activist who
convicted of inciting hatred
of the government and
sentenced to one year in
prison Sunday in a case linked
to criticism of the prime
minister." + ], + [ + "Leaders from 38 Asian and
European nations are gathering
in Vietnam for a summit of the
Asia-Europe Meeting, know as
ASEM. One thousand delegates
are to discuss global trade
and regional politics during
the two-day forum." + ], + [ + "A US soldier has pleaded
guilty to murdering a wounded
16-year-old Iraqi boy. Staff
Sergeant Johnny Horne was
convicted Friday of the
unpremeditated murder" + ], + [ + "Guinea-Bissau #39;s army chief
of staff and former interim
president, General Verissimo
Correia Seabra, was killed
Wednesday during unrest by
mutinous soldiers in the
former Portuguese" + ], + [ + "31 October 2004 -- Exit polls
show that Prime Minister
Viktor Yanukovich and
challenger Viktor Yushchenko
finished on top in Ukraine
#39;s presidential election
today and will face each other
in a run-off next month." + ], + [ + "Rock singer Bono pledges to
spend the rest of his life
trying to eradicate extreme
poverty around the world." + ], + [ + "AP - Just when tourists
thought it was safe to go back
to the Princess Diana memorial
fountain, the mud has struck." + ], + [ + "AP - Three times a week, The
Associated Press picks an
issue and asks President Bush
and Democratic presidential
candidate John Kerry a
question about it. Today's
question and their responses:" + ], + [ + "In an apparent damage control
exercise, Russian President
Vladimir Putin on Saturday
said he favored veto rights
for India as new permanent
member of the UN Security
Council." + ], + [ + "AP - Nigeria's Senate has
ordered a subsidiary of
petroleum giant Royal/Dutch
Shell to pay a Nigerian ethnic
group #36;1.5 billion for oil
spills in their homelands, but
the legislative body can't
enforce the resolution, an
official said Wednesday." + ], + [ + "Australian troops in Baghdad
came under attack today for
the first time since the end
of the Iraq war when a car
bomb exploded injuring three
soldiers and damaging an
Australian armoured convoy." + ], + [ + "Pakistans decision to refuse
the International Atomic
Energy Agency to have direct
access to Dr AQ Khan is
correct on both legal and
political counts." + ], + [ + "MANILA, 4 December 2004 - With
floods receding, rescuers
raced to deliver food to
famished survivors in
northeastern Philippine
villages isolated by back-to-
back storms that left more
than 650 people dead and
almost 400 missing." + ], + [ + "Talks on where to build the
world #39;s first nuclear
fusion reactor ended without a
deal on Tuesday but the
European Union said Japan and
the United States no longer
firmly opposed its bid to put
the plant in France." + ], + [ + "CLEVELAND - The White House
said Vice President Dick
Cheney faces a \"master
litigator\" when he debates
Sen. John Edwards Tuesday
night, a backhanded compliment
issued as the Republican
administration defended itself
against criticism that it has
not acknowledged errors in
waging war in Iraq..." + ], + [ + "SEOUL (Reuters) - The chairman
of South Korea #39;s ruling
Uri Party resigned on Thursday
after saying his father had
served as a military police
officer during Japan #39;s
1910-1945 colonial rule on the
peninsula." + ], + [ + "ALERE, Uganda -- Kasmiro
Bongonyinge remembers sitting
up suddenly in his bed. It was
just after sunrise on a summer
morning two years ago, and the
old man, 87 years old and
blind, knew something was
wrong." + ], + [ + "JAKARTA - Official results
have confirmed former army
general Susilo Bambang
Yudhoyono as the winner of
Indonesia #39;s first direct
presidential election, while
incumbent Megawati
Sukarnoputri urged her nation
Thursday to wait for the
official announcement" + ], + [ + "Reuters - A ragged band of
children\\emerges ghost-like
from mists in Ethiopia's
highlands,\\thrusting bunches
of carrots at a car full of
foreigners." + ], + [ + "AP - A U.N. human rights
expert criticized the U.S.-led
coalition forces in
Afghanistan for violating
international law by allegedly
beating Afghans to death and
forcing some to remove their
clothes or wear hoods." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
said on Thursday Yasser
Arafat's death could be a
turning point for peacemaking
but he would pursue a
unilateral plan that would
strip Palestinians of some
land they want for a state." + ], + [ + " AL-ASAD AIRBASE, Iraq
(Reuters) - Defense Secretary
Donald Rumsfeld swept into an
airbase in Iraq's western
desert Sunday to make a
first-hand evaluation of
operations to quell a raging
Iraqi insurgency in his first
such visit in five months." + ], + [ + "WASHINGTON - Democrat John
Kerry accused President Bush
on Monday of sending U.S.
troops to the \"wrong war in
the wrong place at the wrong
time\" and said he'd try to
bring them all home in four
years..." + ], + [ + "More lorry drivers are
bringing supplies to Nepal's
capital in defiance of an
indefinite blockade by Maoist
rebels." + ], + [ + " BEIJING (Reuters) - Floods
and landslides have killed 76
people in southwest China in
the past four days and washed
away homes and roads, knocked
down power lines and cut off
at least one city, state
media said on Monday." + ], + [ + "AP - Victims of the Sept. 11
attacks were mourned worldwide
Saturday, but in the Middle
East, amid sympathy for the
dead, Arabs said Washington's
support for Israel and the war
on terror launched in the
aftermath of the World Trade
Center's collapse have only
fueled anger and violence." + ], + [ + "SEATTLE - Ichiro Suzuki set
the major league record for
hits in a season with 258,
breaking George Sisler's
84-year-old mark with a pair
of singles Friday night. The
Seattle star chopped a leadoff
single in the first inning,
then made history with a
grounder up the middle in the
third..." + ], + [ + "The intruder who entered
British Queen Elizabeth II
#39;s official Scottish
residence and caused a
security scare was a reporter
from the London-based Sunday
Times newspaper, local media
reported Friday." + ], + [ + "Canadian Press - FREDERICTON
(CP) - A New Brunswick truck
driver arrested in Ontario
this week has been accused by
police of stealing 50,000 cans
of Moosehead beer." + ], + [ + "Chinese authorities detained a
prominent, U.S.-based Buddhist
leader in connection with his
plans to reopen an ancient
temple complex in the Chinese
province of Inner Mongolia
last week and have forced
dozens of his American
followers to leave the region,
local officials said
Wednesday." + ], + [ + "Adorned with Turkish and EU
flags, Turkey #39;s newspapers
hailed Thursday an official EU
report recommending the
country start talks to join
the bloc, while largely
ignoring the stringent
conditions attached to the
announcement." + ], + [ + "Thailand's prime minister
visits the southern town where
scores of Muslims died in army
custody after a rally." + ], + [ + "Beijing: At least 170 miners
were trapped underground after
a gas explosion on Sunday
ignited a fire in a coalmine
in north-west China #39;s
Shaanxi province, reports
said." + ], + [ + "SAMARRA (Iraq): With renewe d
wave of skirmishes between the
Iraqi insurgents and the US-
led coalition marines, several
people including top police
officers were put to death on
Saturday." + ], + [ + "AFP - Like most US Latinos,
members of the extended
Rodriguez family say they will
cast their votes for Democrat
John Kerry in next month's
presidential polls." + ], + [ + "FALLUJAH, Iraq -- Four Iraqi
fighters huddled in a trench,
firing rocket-propelled
grenades at Lieutenant Eric
Gregory's Bradley Fighting
Vehicle and the US tanks and
Humvees that were lumbering
through tight streets between
boxlike beige houses." + ], + [ + "AP - Several thousand
Christians who packed a
cathedral compound in the
Egyptian capital hurled stones
at riot police Wednesday to
protest a woman's alleged
forced conversion to Islam. At
least 30 people were injured." + ], + [ + "A group of Saudi religious
scholars have signed an open
letter urging Iraqis to
support jihad against US-led
forces. quot;Fighting the
occupiers is a duty for all
those who are able, quot; they
said in a statement posted on
the internet at the weekend." + ], + [ + "Mountaineers retrieve three
bodies believed to have been
buried for 22 years on an
Indian glacier." + ], + [ + "President Thabo Mbeki met with
Ivory Coast Prime Minister
Seydou Diarra for three hours
yesterday as part of talks
aimed at bringing peace to the
conflict-wracked Ivory Coast." + ], + [ + " KATHMANDU (Reuters) - Nepal's
Maoist rebels have
temporarily suspended a
crippling economic blockade of
the capital from Wednesday,
saying the move was in
response to popular appeals." + ], + [ + "Reuters - An Algerian
suspected of being a leader\\of
the Madrid train bombers has
been identified as one of
seven\\people who blew
themselves up in April to
avoid arrest, Spain's\\Interior
Ministry said on Friday." + ], + [ + "KABUL: An Afghan man was found
guilty on Saturday of killing
four journalists in 2001,
including two from Reuters,
and sentenced to death." + ], + [ + "Yasser Arafat, the leader for
decades of a fight for
Palestinian independence from
Israel, has died at a military
hospital in Paris, according
to news reports." + ], + [ + " JABALYA, Gaza Strip (Reuters)
- Israel pulled most of its
forces out of the northern
Gaza Strip Saturday after a
four-day incursion it said
was staged to halt Palestinian
rocket attacks on southern
Israeli towns." + ], + [ + "THE Turkish embassy in Baghdad
was investigating a television
report that two Turkish
hostages had been killed in
Iraq, but no confirmation was
available so far, a senior
Turkish diplomat said today." + ], + [ + "Reuters - Thousands of
supporters of
Ukraine's\\opposition leader,
Viktor Yushchenko, celebrated
on the streets\\in the early
hours on Monday after an exit
poll showed him\\winner of a
bitterly fought presidential
election." + ], + [ + "LONDON : The United States
faced rare criticism over
human rights from close ally
Britain, with an official
British government report
taking Washington to task over
concerns about Iraq and the
Guantanamo Bay jail." + ], + [ + "LONDON - A bomb threat that
mentioned Iraq forced a New
York-bound Greek airliner to
make an emergency landing
Sunday at London's Stansted
Airport escorted by military
jets, authorities said. An
airport spokeswoman said an
Athens newspaper had received
a phone call saying there was
a bomb on board the Olympic
Airlines plane..." + ], + [ + "ATHENS, Greece - Sheryl
Swoopes made three big plays
at the end - two baskets and
another on defense - to help
the United States squeeze out
a 66-62 semifinal victory over
Russia on Friday. Now, only
one game stands between the
U.S..." + ], + [ + "Scientists are developing a
device which could improve the
lives of kidney dialysis
patients." + ], + [ + "KABUL, Afghanistan The Afghan
government is blaming drug
smugglers for yesterday #39;s
attack on the leading vice
presidential candidate ." + ], + [ + "One of the leading figures in
the Greek Orthodox Church, the
Patriarch of Alexandria Peter
VII, has been killed in a
helicopter crash in the Aegean
Sea." + ], + [ + "CANBERRA, Australia -- The
sweat-stained felt hats worn
by Australian cowboys, as much
a part of the Outback as
kangaroos and sun-baked soil,
may be heading for the history
books. They fail modern
industrial safety standards." + ], + [ + "A London-to-Washington flight
is diverted after a security
alert involving the singer
formerly known as Cat Stevens." + ], + [ + "AP - President Bush declared
Friday that charges of voter
fraud have cast doubt on the
Ukrainian election, and warned
that any European-negotiated
pact on Iran's nuclear program
must ensure the world can
verify Tehran's compliance." + ], + [ + "TheSpaceShipOne team is handed
the \\$10m cheque and trophy it
won for claiming the Ansari
X-Prize." + ], + [ + "Security officials have
identified six of the
militants who seized a school
in southern Russia as being
from Chechnya, drawing a
strong connection to the
Chechen insurgents who have
been fighting Russian forces
for years." + ], + [ + "SEOUL -- North Korea set three
conditions yesterday to be met
before it would consider
returning to six-party talks
on its nuclear programs." + ], + [ + "US-backed Iraqi commandos were
poised Friday to storm rebel
strongholds in the northern
city of Mosul, as US military
commanders said they had
quot;broken the back quot; of
the insurgency with their
assault on the former rebel
bastion of Fallujah." + ], + [ + "JERUSALEM (Reuters) - Prime
Minister Ariel Sharon, facing
a party mutiny over his plan
to quit the Gaza Strip, has
approved 1,000 more Israeli
settler homes in the West Bank
in a move that drew a cautious
response on Tuesday from ..." + ], + [ + "GHAZNI, Afghanistan, 6 October
2004 - Wartime security was
rolled out for Afghanistans
interim President Hamid Karzai
as he addressed his first
election campaign rally
outside the capital yesterday
amid spiraling violence." + ], + [ + "China has confirmed that it
found a deadly strain of bird
flu in pigs as early as two
years ago. China #39;s
Agriculture Ministry said two
cases had been discovered, but
it did not say exactly where
the samples had been taken." + ], + [ + "AP - Ten years after the Irish
Republican Army's momentous
cease-fire, negotiations
resumed Wednesday in hope of
reviving a Catholic-Protestant
administration, an elusive
goal of Northern Ireland's
hard-fought peace process." + ], + [ + " SANTO DOMINGO, Dominican
Republic, Sept. 18 -- Tropical
Storm Jeanne headed for the
Bahamas on Saturday after an
assault on the Dominican
Republic that killed 10
people, destroyed hundreds of
houses and forced thousands
from their homes." + ], + [ + "An explosion tore apart a car
in Gaza City Monday, killing
at least one person,
Palestinian witnesses said.
They said Israeli warplanes
were circling overhead at the
time of the blast, indicating
a possible missile strike." + ], + [ + "Beijing, Oct. 25 (PTI): China
and the US today agreed to
work jointly to re-energise
the six-party talks mechanism
aimed at dismantling North
Korea #39;s nuclear programmes
while Washington urged Beijing
to resume" + ], + [ + "AFP - Sporadic gunfire and
shelling took place overnight
in the disputed Georgian
region of South Ossetia in
violation of a fragile
ceasefire, wounding seven
Georgian servicemen." + ], + [ + " FALLUJA, Iraq (Reuters) -
U.S. forces hit Iraq's rebel
stronghold of Falluja with the
fiercest air and ground
bombardment in months, as
insurgents struck back on
Saturday with attacks that
killed up to 37 people in
Samarra." + ], + [ + " NAJAF, Iraq (Reuters) - The
fate of a radical Shi'ite
rebellion in the holy city of
Najaf was uncertain Friday
amid disputed reports that
Iraqi police had gained
control of the Imam Ali
Mosque." + ], + [ + "Until this week, only a few
things about the strange,
long-ago disappearance of
Charles Robert Jenkins were
known beyond a doubt. In the
bitter cold of Jan. 5, 1965,
the 24-year-old US Army
sergeant was leading" + ], + [ + "The United States on Tuesday
modified slightly a threat of
sanctions on Sudan #39;s oil
industry in a revised text of
its UN resolution on
atrocities in the country
#39;s Darfur region." + ], + [ + "AP - France intensified
efforts Tuesday to save the
lives of two journalists held
hostage in Iraq, and the Arab
League said the militants'
deadline for France to revoke
a ban on Islamic headscarves
in schools had been extended." + ], + [ + "At least 12 people die in an
explosion at a fuel pipeline
on the outskirts of Nigeria's
biggest city, Lagos." + ], + [ + "Volkswagen demanded a two-year
wage freeze for the
170,000-strong workforce at
Europe #39;s biggest car maker
yesterday, provoking union
warnings of imminent conflict
at key pay and conditions
negotiations." + ], + [ + "Citing security concerns, the
U.S. Embassy on Thursday
banned its employees from
using the highway linking the
embassy area to the
international airport, a
10-mile stretch of road
plagued by frequent suicide
car-bomb attacks." + ], + [ + "AP - Tom Daschle bade his
fellow Senate Democrats
farewell Tuesday with a plea
that they seek common ground
with Republicans yet continue
to fight for the less
fortunate." + ], + [ + "AP - Police defused a bomb in
a town near Prime Minister
Silvio Berlusconi's villa on
the island of Sardinia on
Wednesday shortly after
British Prime Minister Tony
Blair finished a visit there
with the Italian leader." + ], + [ + "The coffin of Yasser Arafat,
draped with the Palestinian
flag, was bound for Ramallah
in the West Bank Friday,
following a formal funeral on
a military compound near
Cairo." + ], + [ + "US Ambassador to the United
Nations John Danforth resigned
on Thursday after serving in
the post for less than six
months. Danforth, 68, said in
a letter released Thursday" + ], + [ + "ISLAMABAD, Pakistan -- Photos
were published yesterday in
newspapers across Pakistan of
six terror suspects, including
a senior Al Qaeda operative,
the government says were
behind attempts to assassinate
the nation's president." + ], + [ + " ATHENS (Reuters) - The Athens
Paralympics canceled
celebrations at its closing
ceremony after seven
schoolchildren traveling to
watch the event died in a bus
crash on Monday." + ], + [ + "DUBAI : An Islamist group has
threatened to kill two Italian
women held hostage in Iraq if
Rome does not withdraw its
troops from the war-torn
country within 24 hours,
according to an internet
statement." + ], + [ + "A heavy quake rocked Indonesia
#39;s Papua province killing
at least 11 people and
wounding 75. The quake
destroyed 150 buildings,
including churches, mosques
and schools." + ], + [ + "Reuters - A small group of
suspected\\gunmen stormed
Uganda's Water Ministry
Wednesday and took
three\\people hostage to
protest against proposals to
allow President\\Yoweri
Museveni for a third
term.\\Police and soldiers with
assault rifles cordoned off
the\\three-story building, just
328 feet from Uganda's
parliament\\building in the
capital Kampala." + ], + [ + "Venezuela suggested Friday
that exiles living in Florida
may have masterminded the
assassination of a prosecutor
investigating a short-lived
coup against leftist President
Hugo Chvez" + ], + [ + "Facing a popular outcry at
home and stern warnings from
Europe, the Turkish government
discreetly stepped back
Tuesday from a plan to
introduce a motion into a
crucial penal reform bill to
make adultery a crime
punishable by prison." + ], + [ + "North-west Norfolk MP Henry
Bellingham has called for the
release of an old college
friend accused of plotting a
coup in Equatorial Guinea." + ], + [ + "AFP - Want to buy a castle?
Head for the former East
Germany." + ], + [ + "AFP - Steven Gerrard has moved
to allay Liverpool fans' fears
that he could be out until
Christmas after breaking a
metatarsal bone in his left
foot." + ], + [ + "MINSK - Legislative elections
in Belarus held at the same
time as a referendum on
whether President Alexander
Lukashenko should be allowed
to seek a third term fell
significantly short of
democratic standards, foreign
observers said here Monday." + ], + [ + "An Olympic sailor is charged
with the manslaughter of a
Briton who died after being
hit by a car in Athens." + ], + [ + "AP - Secretary of State Colin
Powell on Friday praised the
peace deal that ended fighting
in Iraq's holy city of Najaf
and said the presence of U.S.
forces in the area helped make
it possible." + ], + [ + "26 August 2004 -- Iraq #39;s
top Shi #39;ite cleric, Grand
Ayatollah Ali al-Sistani,
arrived in the city of Al-
Najaf today in a bid to end a
weeks-long conflict between US
forces and militiamen loyal to
Shi #39;ite cleric Muqtada al-
Sadr." + ], + [ + "PARIS : French trade unions
called on workers at France
Telecom to stage a 24-hour
strike September 7 to protest
government plans to privatize
the public telecommunications
operator, union sources said." + ], + [ + "The Indonesian tourism
industry has so far not been
affected by last week #39;s
bombing outside the Australian
embassy in Jakarta and
officials said they do not
expect a significant drop in
visitor numbers as a result of
the attack." + ], + [ + "MARK Thatcher will have to
wait until at least next April
to face trial on allegations
he helped bankroll a coup
attempt in oil-rich Equatorial
Guinea." + ], + [ + "NEW YORK - A drop in oil
prices and upbeat outlooks
from Wal-Mart and Lowe's
helped send stocks sharply
higher Monday on Wall Street,
with the swing exaggerated by
thin late summer trading. The
Dow Jones industrials surged
nearly 130 points..." + ], + [ + "ROSTOV-ON-DON, Russia --
Hundreds of protesters
ransacked and occupied the
regional administration
building in a southern Russian
province Tuesday, demanding
the resignation of the region
#39;s president, whose former
son-in-law has been linked to
a multiple" + ], + [ + "AFP - Iraqi Foreign Minister
Hoshyar Zebari arrived
unexpectedly in the holy city
of Mecca Wednesday where he
met Crown Prince Abdullah bin
Abdul Aziz, the official SPA
news agency reported." + ], + [ + "Haitian police and UN troops
moved into a slum neighborhood
on Sunday and cleared street
barricades that paralyzed a
part of the capital." + ], + [ + "withdrawal of troops and
settlers from occupied Gaza
next year. Militants seek to
claim any pullout as a
victory. quot;Islamic Jihad
will not be broken by this
martyrdom, quot; said Khaled
al-Batsh, a senior political
leader in Gaza." + ], + [ + "The U.S. military has found
nearly 20 houses where
intelligence officers believe
hostages were tortured or
killed in this city, including
the house with the cage that
held a British contractor who
was beheaded last month." + ], + [ + "AFP - Opponents of the Lao
government may be plotting
bomb attacks in Vientiane and
other areas of Laos timed to
coincide with a summit of
Southeast Asian leaders the
country is hosting next month,
the United States said." + ], + [ + "AP - Russia agreed Thursday to
send warships to help NATO
naval patrols that monitor
suspicious vessels in the
Mediterranean, part of a push
for closer counterterrorism
cooperation between Moscow and
the western alliance." + ], + [ + "A military plane crashed in
the mountains near Caracas,
killing all 16 persons on
board, including two high-
ranking military officers,
officials said." + ], + [ + "A voice recording said to be
that of suspected Al Qaeda
commander Abu Mussab al-
Zarqawi, claims Iraq #39;s
Prime Minister Iyad Allawi is
the militant network #39;s
number one target." + ], + [ + "BEIJING -- More than a year
after becoming China's
president, Hu Jintao was
handed the full reins of power
yesterday when his
predecessor, Jiang Zemin, gave
up the nation's most powerful
military post." + ], + [ + "AP - Greenpeace activists
scaled the walls of Ford Motor
Co.'s Norwegian headquarters
Tuesday to protest plans to
destroy hundreds of non-
polluting electric cars." + ], + [ + "AFP - The chances of Rupert
Murdoch's News Corp relocating
from Australia to the United
States have increased after
one of its biggest
institutional investors has
chosen to abstain from a vote
next week on the move." + ], + [ + "AFP - An Indian minister said
a school text-book used in the
violence-prone western state
of Gujarat portrayed Adolf
Hitler as a role model." + ], + [ + "DOVER, N.H. (AP) -- Democrat
John Kerry is seizing on the
Bush administration's failure
to secure hundreds of tons of
explosives now missing in
Iraq." + ], + [ + "United Nations officials
report security breaches in
internally displaced people
and refugee camps in Sudan
#39;s embattled Darfur region
and neighboring Chad." + ], + [ + "KINGSTON, Jamaica - Hurricane
Ivan's deadly winds and
monstrous waves bore down on
Jamaica on Friday, threatening
a direct hit on its densely
populated capital after
ravaging Grenada and killing
at least 33 people. The
Jamaican government ordered
the evacuation of half a
million people from coastal
areas, where rains on Ivan's
outer edges were already
flooding roads..." + ], + [ + "North Korea has denounced as
quot;wicked terrorists quot;
the South Korean officials who
orchestrated last month #39;s
airlift to Seoul of 468 North
Korean defectors." + ], + [ + "The Black Watch regiment has
returned to its base in Basra
in southern Iraq after a
month-long mission standing in
for US troops in a more
violent part of the country,
the Ministry of Defence says." + ], + [ + "AP - A senior Congolese
official said Tuesday his
nation had been invaded by
neighboring Rwanda, and U.N.
officials said they were
investigating claims of
Rwandan forces clashing with
militias in the east." + ], + [ + "UNITED NATIONS - The United
Nations #39; nuclear agency
says it is concerned about the
disappearance of equipment and
materials from Iraq that could
be used to make nuclear
weapons." + ], + [ + " BRUSSELS (Reuters) - The EU's
historic deal with Turkey to
open entry talks with the vast
Muslim country was hailed by
supporters as a bridge builder
between Europe and the Islamic
world." + ], + [ + "Iraqi President Ghazi al-
Yawar, who was due in Paris on
Sunday to start a European
tour, has postponed his visit
to France due to the ongoing
hostage drama involving two
French journalists, Arab
diplomats said Friday." + ], + [ + " SAO PAULO, Brazil (Reuters) -
President Luiz Inacio Lula da
Silva's Workers' Party (PT)
won the mayoralty of six state
capitals in Sunday's municipal
vote but was forced into a
run-off to defend its hold on
the race's biggest prize, the
city of Sao Paulo." + ], + [ + "ATHENS, Greece - They are
America's newest golden girls
- powerful and just a shade
from perfection. The U.S..." + ], + [ + "AMMAN, Sept. 15. - The owner
of a Jordanian truck company
announced today that he had
ordered its Iraq operations
stopped in a bid to save the
life of a driver held hostage
by a militant group." + ], + [ + "Israel is prepared to back a
Middle East conference
convened by Tony Blair early
next year despite having
expressed fears that the
British plans were over-
ambitious and designed" + ], + [ + "AP - U.S. State Department
officials learned that seven
American children had been
abandoned at a Nigerian
orphanage but waited more than
a week to check on the youths,
who were suffering from
malnutrition, malaria and
typhoid, a newspaper reported
Saturday." + ], + [ + "\\Angry mobs in Ivory Coast's
main city, Abidjan, marched on
the airport, hours after it
came under French control." + ], + [ + "Several workers are believed
to have been killed and others
injured after a contruction
site collapsed at Dubai
airport. The workers were
trapped under rubble at the
site of a \\$4." + ], + [ + "Talks between Sudan #39;s
government and two rebel
groups to resolve the nearly
two-year battle resume Friday.
By Abraham McLaughlin Staff
writer of The Christian
Science Monitor." + ], + [ + "Stansted airport is the
designated emergency landing
ground for planes in British
airspace hit by in-flight
security alerts. Emergency
services at Stansted have
successfully dealt" + ], + [ + "The massive military operation
to retake Fallujah has been
quot;accomplished quot;, a
senior Iraqi official said.
Fierce fighting continued in
the war-torn city where
pockets of resistance were
still holding out against US
forces." + ], + [ + "There are some signs of
progress in resolving the
Nigerian conflict that is
riling global oil markets. The
leader of militia fighters
threatening to widen a battle
for control of Nigeria #39;s
oil-rich south has" + ], + [ + "A strong earthquake hit Taiwan
on Monday, shaking buildings
in the capital Taipei for
several seconds. No casualties
were reported." + ], + [ + "A policeman ran amok at a
security camp in Indian-
controlled Kashmir after an
argument and shot dead seven
colleagues before he was
gunned down, police said on
Sunday." + ], + [ + "New York police have developed
a pre-emptive strike policy,
cutting off demonstrations
before they grow large." + ], + [ + "Bulgaria has started its first
co-mission with the EU in
Bosnia and Herzegovina, along
with some 30 countries,
including Canada and Turkey." + ], + [ + "AP - The pileup of events in
the city next week, including
the Republican National
Convention, will add to the
security challenge for the New
York Police Department, but
commissioner Ray Kelly says,
\"With a big, experienced
police force, we can do it.\"" + ], + [ + "LONDON, Dec 11 (IranMania) -
Iraqi Vice-President Ibrahim
al-Jaafari refused to believe
in remarks published Friday
that Iran was attempting to
influence Iraqi polls with the
aim of creating a
quot;crescent quot; dominated
by Shiites in the region." + ], + [ + "The late Princess Dianas
former bodyguard, Ken Wharfe,
dismisses her suspicions that
one of her lovers was bumped
off. Princess Diana had an
affair with Barry Mannakee, a
policeman who was assigned to
protect her." + ], + [ + "Long considered beyond the
reach of mainland mores, the
Florida city is trying to
limit blatant displays of
sexual behavior." + ], + [ + "Senator John Kerry said today
that the war in Iraq was a
\"profound diversion\" from the
war on terror and Osama bin
Laden." + ], + [ + "A group claiming to have
captured two Indonesian women
in Iraq has said it will
release them if Jakarta frees
Muslim cleric Abu Bakar Bashir
being held for alleged
terrorist links." + ], + [ + "Indonesian police said
yesterday that DNA tests had
identified a suicide bomber
involved in a deadly attack
this month on the Australian
embassy in Jakarta." + ], + [ + "NEW YORK - Wal-Mart Stores
Inc.'s warning of
disappointing sales sent
stocks fluctuating Monday as
investors' concerns about a
slowing economy offset their
relief over a drop in oil
prices. October contracts
for a barrel of light crude
were quoted at \\$46.48, down
24 cents, on the New York
Mercantile Exchange..." + ], + [ + "Iraq #39;s top Shi #39;ite
cleric made a sudden return to
the country on Wednesday and
said he had a plan to end an
uprising in the quot;burning
city quot; of Najaf, where
fighting is creeping ever
closer to its holiest shrine." + ], + [ + "KABUL, Afghanistan Aug. 22,
2004 - US soldiers sprayed a
pickup truck with bullets
after it failed to stop at a
roadblock in central
Afghanistan, killing two women
and a man and critically
wounding two other" + ], + [ + "SYDNEY -- Prime Minister John
Howard of Australia, a key US
ally and supporter of the Iraq
war, celebrated his election
win over opposition Labor
after voters enjoying the
fruits of a strong economy
gave him another term." + ], + [ + "BAGHDAD, Iraq - Two rockets
hit a downtown Baghdad hotel
housing foreigners and
journalists Thursday, and
gunfire erupted in the
neighborhood across the Tigris
River from the U.S. Embassy
compound..." + ], + [ + "The Prevention of Terrorism
Act 2002 (Pota) polarised the
country, not just by the
manner in which it was pushed
through by the NDA government
through a joint session of
Parliament but by the shabby
and often biased manner in
which it was enforced." + ], + [ + "The US military says marines
in Fallujah shot and killed an
insurgent who engaged them as
he was faking being dead, a
week after footage of a marine
killing an apparently unarmed
and wounded Iraqi caused a
stir in the region." + ], + [ + "Description: NPR #39;s Alex
Chadwick talks to Colin Brown,
deputy political editor for
the United Kingdom #39;s
Independent newspaper,
currently covering the British
Labour Party Conference." + ], + [ + "Hamas vowed revenge yesterday
after an Israeli airstrike in
Gaza killed one of its senior
commanders - the latest
assassination to have weakened
the militant group." + ], + [ + "A senior member of the
Palestinian resistance group
Hamas has been released from
an Israeli prison after
completing a two-year
sentence." + ], + [ + "MPs have announced a new
inquiry into family courts and
whether parents are treated
fairly over issues such as
custody or contact with their
children." + ], + [ + "Canadian Press - MELBOURNE,
Australia (AP) - A 36-year-old
businesswoman was believed to
be the first woman to walk
around Australia on Friday
after striding into her
hometown of Melbourne to
complete her 16,700-kilometre
trek in 365 days." + ], + [ + "Most remaining Pakistani
prisoners held at the US
Guantanamo Bay prison camp are
freed, officials say." + ], + [ + "French police are
investigating an arson-caused
fire at a Jewish Social Center
that might have killed dozens
without the quick response of
firefighters." + ], + [ + "Rodney King, whose videotaped
beating led to riots in Los
Angeles in 1992, is out of
jail now and talking frankly
for the first time about the
riots, himself and the
American way of life." + ], + [ + "AFP - Radical Islamic cleric
Abu Hamza al-Masri was set to
learn Thursday whether he
would be charged under
Britain's anti-terrorism law,
thus delaying his possible
extradition to the United
States to face terrorism-
related charges." + ], + [ + "Louisen Louis, 30, walked
Monday in the middle of a
street that resembled a small
river with brown rivulets and
waves. He wore sandals and had
a cut on one of his big toes." + ], + [ + "A car bomb exploded outside
the main hospital in Chechny
#39;s capital, Grozny, on
Sunday, injuring 17 people in
an attack apparently targeting
members of a Chechen security
force bringing in wounded from
an earlier explosion" + ], + [ + "AP - Gay marriage is emerging
as a big enough issue in
several states to influence
races both for Congress and
the presidency." + ], + [ + "More than 30 aid workers have
been airlifted to safety from
a town in Sudan #39;s troubled
Darfur region after fighting
broke out and their base was
bombed, a British charity
says." + ], + [ + "It #39;s the mildest of mild
winters down here in the south
of Italy and, last weekend at
Bcoli, a pretty suburb by the
seaside west of Naples, the
customers of Pizzeria quot;Da
Enrico quot; were making the
most of it." + ], + [ + "WASHINGTON - A spotty job
market and stagnant paychecks
cloud this Labor Day holiday
for many workers, highlighting
the importance of pocketbook
issues in the presidential
election. \"Working harder
and enjoying it less,\" said
economist Ken Mayland,
president of ClearView
Economics, summing up the
state of working America..." + ], + [ + "Canadian Press - MONTREAL (CP)
- A 19-year-old man charged in
a firebombing at a Jewish
elementary school pleaded
guilty Thursday to arson." + ], + [ + " quot;Resuming uranium
enrichment is not in our
agenda. We are still committed
to the suspension, quot;
Foreign Ministry spokesman
Hamid Reza." + ], + [ + "The U.S. military presence in
Iraq will grow to 150,000
troops by next month, the
highest level since the
invasion last year." + ], + [ + "UPDATE, SUN 9PM: More than a
million people have left their
homes in Cuba, as Hurricane
Ivan approaches. The ferocious
storm is headed that way,
after ripping through the
Cayman Islands, tearing off
roofs, flooding homes and
causing general havoc." + ], + [ + "German Chancellor Gerhard
Schroeder said Sunday that
there was quot;no problem
quot; with Germany #39;s
support to the start of
negotiations on Turkey #39;s
entrance into EU." + ], + [ + "MANILA Fernando Poe Jr., the
popular actor who challenged
President Gloria Macapagal
Arroyo in the presidential
elections this year, died
early Tuesday." + ], + [ + "AMSTERDAM, NETHERLANDS - A
Dutch filmmaker who outraged
members of the Muslim
community by making a film
critical of the mistreatment
of women in Islamic society
was gunned down and stabbed to
death Tuesday on an Amsterdam
street." + ], + [ + "Zimbabwe #39;s most persecuted
white MP began a year of hard
labour last night after
parliament voted to jail him
for shoving the Justice
Minister during a debate over
land seizures." + ], + [ + "A smashing blow is being dealt
to thousands of future
pensioners by a law that has
just been brought into force
by the Federal Government." + ], + [ + "AP - An Israeli helicopter
fired two missiles in Gaza
City after nightfall
Wednesday, one at a building
in the Zeitoun neighborhood,
witnesses said, setting a
fire." + ], + [ + "The Philippines put the toll
at more than 1,000 dead or
missing in four storms in two
weeks but, even with a break
in the weather on Saturday" + ], + [ + "Reuters - Four explosions were
reported at petrol\\stations in
the Madrid area on Friday,
Spanish radio stations\\said,
following a phone warning in
the name of the armed
Basque\\separatist group ETA to
a Basque newspaper." + ], + [ + "WEST PALM BEACH, Fla. -
Hurricane Jeanne got stronger,
bigger and faster as it
battered the Bahamas and bore
down on Florida Saturday,
sending huge waves crashing
onto beaches and forcing
thousands into shelters just
weeks after Frances ravaged
this area..." + ], + [ + "NEW YORK - Elena Dementieva
shook off a subpar serve that
produced 15 double-faults, an
aching left thigh and an upset
stomach to advance to the
semifinals at the U.S. Open
with a 4-6, 6-4, 7-6 (1)
victory Tuesday over Amelie
Mauresmo..." + ], + [ + "Prime Minister Dr Manmohan
Singh inaugurated a research
centre in the Capital on
Thursday to mark 400 years of
compilation of Sikh holy book
the Guru Granth Sahib." + ], + [ + "THE re-election of British
Prime Minister Tony Blair
would be seen as an
endorsement of the military
action in Iraq, Prime Minister
John Howard said today." + ], + [ + "President George W. Bush
pledged Friday to spend some
of the political capital from
his re-election trying to
secure a lasting Middle East
peace, and he envisioned the
establishment" + ], + [ + "NEW DELHI - A bomb exploded
during an Independence Day
parade in India's remote
northeast on Sunday, killing
at least 15 people, officials
said, just an hour after Prime
Minister Manmohan Singh
pledged to fight terrorism.
The outlawed United Liberation
Front of Asom was suspected of
being behind the attack in
Assam state and a second one
later in the area, said Assam
Inspector General of Police
Khagen Sharma..." + ], + [ + "A UN envoy to Sudan will visit
Darfur tomorrow to check on
the government #39;s claim
that some 70,000 people
displaced by conflict there
have voluntarily returned to
their homes, a spokesman said." + ], + [ + "AP - Most of the presidential
election provisional ballots
rejected so far in Ohio came
from people who were not even
registered to vote, election
officials said after spending
nearly two weeks poring over
thousands of disputed votes." + ], + [ + "AP - Rival inmates fought each
other with knives and sticks
Wednesday at a San Salvador
prison, leaving at least 31
people dead and two dozen
injured, officials said." + ], + [ + "BAGHDAD - Two Egyptian
employees of a mobile phone
company were seized when
gunmen stormed into their
Baghdad office, the latest in
a series of kidnappings in the
country." + ], + [ + "BRISBANE, Australia - The body
of a whale resembling a giant
dolphin that washed up on an
eastern Australian beach has
intrigued local scientists,
who agreed Wednesday that it
is rare but are not sure just
how rare." + ], + [ + "President Bush aims to
highlight American drug-
fighting aid in Colombia and
boost a conservative Latin
American leader with a stop in
the Andean nation where
thousands of security forces
are deployed to safeguard his
brief stay." + ], + [ + "Dubai - Former Palestinian
security minister Mohammed
Dahlan said on Monday that a
quot;gang of mercenaries quot;
known to the Palestinian
police were behind the
shooting that resulted in two
deaths in a mourning tent for
Yasser Arafat in Gaza." + ], + [ + "A Frenchman working for Thales
SA, Europe #39;s biggest maker
of military electronics, was
shot dead while driving home
at night in the Saudi Arabian
city of Jeddah." + ], + [ + "China will take tough measures
this winter to improve the
country #39;s coal mine safety
and prevent accidents. State
Councilor Hua Jianmin said
Thursday the industry should
take" + ], + [ + "BAGHDAD (Iraq): As the
intensity of skirmishes
swelled on the soils of Iraq,
dozens of people were put to
death with toxic shots by the
US helicopter gunship, which
targeted the civilians,
milling around a burning
American vehicle in a Baghdad
street on" + ], + [ + "Reuters - A key Iranian
nuclear facility which
the\\U.N.'s nuclear watchdog
has urged Tehran to shut down
is\\nearing completion, a
senior Iranian nuclear
official said on\\Sunday." + ], + [ + "Spain's Football Federation
launches an investigation into
racist comments made by
national coach Luis Aragones." + ], + [ + "Bricks and plaster blew inward
from the wall, as the windows
all shattered and I fell to
the floorwhether from the
shock wave, or just fright, it
wasn #39;t clear." + ], + [ + "Surfersvillage Global Surf
News, 13 September 2004: - -
Hurricane Ivan, one of the
most powerful storms to ever
hit the Caribbean, killed at
least 16 people in Jamaica,
where it wrecked houses and
washed away roads on Saturday,
but appears to have spared" + ], + [ + "LONDON, England -- A US
scientist is reported to have
observed a surprising jump in
the amount of carbon dioxide,
the main greenhouse gas." + ], + [ + "Zimbabwe #39;s ruling Zanu-PF
old guard has emerged on top
after a bitter power struggle
in the deeply divided party
during its five-yearly
congress, which ended
yesterday." + ], + [ + "Reuters - Thousands of
demonstrators pressing
to\\install Ukraine's
opposition leader as president
after a\\disputed election
launched fresh street rallies
in the capital\\for the third
day Wednesday." + ], + [ + "Michael Jackson wishes he had
fought previous child
molestation claims instead of
trying to \"buy peace\", his
lawyer says." + ], + [ + "North Korea says it will not
abandon its weapons programme
after the South admitted
nuclear activities." + ], + [ + "While there is growing
attention to ongoing genocide
in Darfur, this has not
translated into either a
meaningful international
response or an accurate
rendering of the scale and
evident course of the
catastrophe." + ], + [ + "THE prosecution on terrorism
charges of extremist Islamic
cleric and accused Jemaah
Islamiah leader Abu Bakar
Bashir will rely heavily on
the potentially tainted
testimony of at least two
convicted Bali bombers, his
lawyers have said." + ], + [ + "Clashes between US troops and
Sadr militiamen escalated
Thursday, as the US surrounded
Najaf for possible siege." + ], + [ + "AFP - A battle group of
British troops rolled out of
southern Iraq on a US-
requested mission to deadlier
areas near Baghdad, in a major
political gamble for British
Prime Minister Tony Blair." + ], + [ + "over half the children in the
world - suffer extreme
deprivation because of war,
HIV/AIDS or poverty, according
to a report released yesterday
by the United Nations Children
#39;s Fund." + ], + [ + "Reuters - Philippine rescue
teams\\evacuated thousands of
people from the worst flooding
in the\\central Luzon region
since the 1970s as hungry
victims hunted\\rats and birds
for food." + ], + [ + "The Afghan president expresses
deep concern after a bomb
attack which left at least
seven people dead." + ], + [ + "Gardez (Afghanistan), Sept. 16
(Reuters): Afghan President
Hamid Karzai escaped an
assassination bid today when a
rocket was fired at his US
military helicopter as it was
landing in the southeastern
town of Gardez." + ], + [ + "The Jets came up with four
turnovers by Dolphins
quarterback Jay Fiedler in the
second half, including an
interception returned 66 yards
for a touchdown." + ], + [ + "DUBLIN -- Prime Minister
Bertie Ahern urged Irish
Republican Army commanders
yesterday to meet what he
acknowledged was ''a heavy
burden quot;: disarming and
disbanding their organization
in support of Northern
Ireland's 1998 peace accord." + ], + [ + "While reproductive planning
and women #39;s equality have
improved substantially over
the past decade, says a United
Nations report, world
population will increase from
6.4 billion today to 8.9
billion by 2050, with the 50
poorest countries tripling in" + ], + [ + "BAR's Anthony Davidson and
Jenson Button set the pace at
the first Chinese Grand Prix." + ], + [ + "WASHINGTON - Contradicting the
main argument for a war that
has cost more than 1,000
American lives, the top U.S.
arms inspector reported
Wednesday that he found no
evidence that Iraq produced
any weapons of mass
destruction after 1991..." + ], + [ + "AFP - Style mavens will be
scanning the catwalks in Paris
this week for next spring's
must-have handbag, as a
sweeping exhibition at the
French capital's fashion and
textile museum reveals the bag
in all its forms." + ], + [ + "Canadian Press - SAINT-
QUENTIN, N.B. (CP) - A major
highway in northern New
Brunswick remained closed to
almost all traffic Monday, as
local residents protested
planned health care cuts." + ], + [ + " NAJAF, Iraq (Reuters) - A
radical Iraqi cleric leading a
Shi'ite uprising agreed on
Wednesday to disarm his
militia and leave one of the
country's holiest Islamic
shrines after warnings of an
onslaught by government
forces." + ], + [ + "Saudi security forces have
killed a wanted militant near
the scene of a deadly shootout
Thursday. Officials say the
militant was killed in a
gunbattle Friday in the
northern town of Buraida,
hours after one" + ], + [ + "Two South Africans acquitted
by a Zimbabwean court of
charges related to the alleged
coup plot in Equatorial Guinea
are to be questioned today by
the South African authorities." + ], + [ + "MOSCOW (CP) - Russia mourned
89 victims of a double air
disaster today as debate
intensified over whether the
two passenger liners could
have plunged almost
simultaneously from the sky by
accident." + ], + [ + "Australia #39;s prime minister
says a body found in Fallujah
is likely that of kidnapped
aid worker Margaret Hassan.
John Howard told Parliament a
videotape of an Iraqi
terrorist group executing a
Western woman appears to have
been genuine." + ], + [ + "AP - Their first debate less
than a week away, President
Bush and Democrat John Kerry
kept their public schedules
clear on Saturday and began to
focus on their prime-time
showdown." + ], + [ + "PARIS Getting to the bottom of
what killed Yassar Arafat
could shape up to be an ugly
family tug-of-war. Arafat
#39;s half-brother and nephew
want copies of Arafat #39;s
medical records from the
suburban Paris hospital" + ], + [ + " THE HAGUE (Reuters) - Former
Yugoslav President Slobodan
Milosevic condemned his war
crimes trial as a \"pure farce\"
on Wednesday in a defiant
finish to his opening defense
statement against charges of
ethnic cleansing in the
Balkans." + ], + [ + " GUWAHATI, India (Reuters) -
People braved a steady drizzle
to come out to vote in a
remote northeast Indian state
on Thursday, as troops
guarded polling stations in an
election being held under the
shadow of violence." + ], + [ + "AFP - Three of the nine
Canadian sailors injured when
their newly-delivered,
British-built submarine caught
fire in the North Atlantic
were airlifted Wednesday to
hospital in northwest Ireland,
officials said." + ], + [ + "BAGHDAD, Iraq - A series of
strong explosions shook
central Baghdad near dawn
Sunday, and columns of thick
black smoke rose from the
Green Zone where U.S. and
Iraqi government offices are
located..." + ], + [ + "Sven-Goran Eriksson may gamble
by playing goalkeeper Paul
Robinson and striker Jermain
Defoe in Poland." + ], + [ + "Foreign Secretary Jack Straw
has flown to Khartoum on a
mission to pile the pressure
on the Sudanese government to
tackle the humanitarian
catastrophe in Darfur." + ], + [ + "Reuters - A senior U.S.
official said on
Wednesday\\deals should not be
done with hostage-takers ahead
of the\\latest deadline set by
Afghan Islamic militants who
have\\threatened to kill three
kidnapped U.N. workers." + ], + [ + "Sinn Fein leader Gerry Adams
has put the pressure for the
success or failure of the
Northern Ireland assembly
talks firmly on the shoulders
of Ian Paisley." + ], + [ + " JAKARTA (Reuters) - President
Megawati Sukarnoputri urged
Indonesians on Thursday to
accept the results of the
country's first direct
election of a leader, but
stopped short of conceding
defeat." + ], + [ + "ISLAMABAD: Pakistan early
Monday test-fired its
indigenously developed short-
range nuclear-capable Ghaznavi
missile, the Inter Services
Public Relations (ISPR) said
in a statement." + ], + [ + "The trial of a man accused of
murdering York backpacker
Caroline Stuttle begins in
Australia." + ], + [ + "BRUSSELS: The EU sought
Wednesday to keep pressure on
Turkey over its bid to start
talks on joining the bloc, as
last-minute haggling seemed
set to go down to the wire at
a summit poised to give a
green light to Ankara." + ], + [ + "AP - J. Cofer Black, the State
Department official in charge
of counterterrorism, is
leaving government in the next
few weeks." + ], + [ + "AFP - The United States
presented a draft UN
resolution that steps up the
pressure on Sudan over the
crisis in Darfur, including
possible international
sanctions against its oil
sector." + ], + [ + "AFP - At least 33 people were
killed and dozens others
wounded when two bombs ripped
through a congregation of
Sunni Muslims in Pakistan's
central city of Multan, police
said." + ], + [ + "AFP - A series of torchlight
rallies and vigils were held
after darkness fell on this
central Indian city as victims
and activists jointly
commemorated a night of horror
20 years ago when lethal gas
leaked from a pesticide plant
and killed thousands." + ], + [ + "A Zimbabwe court Friday
convicted a British man
accused of leading a coup plot
against the government of oil-
rich Equatorial Guinea on
weapons charges, but acquitted
most of the 69 other men held
with him." + ], + [ + "Canadian Press - TORONTO (CP)
- The fatal stabbing of a
young man trying to eject
unwanted party guests from his
family home, the third such
knifing in just weeks, has
police worried about a
potentially fatal holiday
recipe: teens, alcohol and
knives." + ], + [ + "MOSCOW - A female suicide
bomber set off a shrapnel-
filled explosive device
outside a busy Moscow subway
station on Tuesday night,
officials said, killing 10
people and injuring more than
50." + ], + [ + "ABIDJAN (AFP) - Two Ivory
Coast military aircraft
carried out a second raid on
Bouake, the stronghold of the
former rebel New Forces (FN)
in the divided west African
country, a French military
source told AFP." + ], + [ + "AFP - A state of civil
emergency in the rebellion-hit
Indonesian province of Aceh
has been formally extended by
six month, as the country's
president pledged to end
violence there without foreign
help." + ], + [ + "US Secretary of State Colin
Powell on Monday said he had
spoken to both Indian Foreign
Minister K Natwar Singh and
his Pakistani counterpart
Khurshid Mahmud Kasuri late
last week before the two met
in New Delhi this week for
talks." + ], + [ + "NEW YORK - Victor Diaz hit a
tying, three-run homer with
two outs in the ninth inning,
and Craig Brazell's first
major league home run in the
11th gave the New York Mets a
stunning 4-3 victory over the
Chicago Cubs on Saturday.
The Cubs had much on the
line..." + ], + [ + "AFP - At least 54 people have
died and more than a million
have fled their homes as
torrential rains lashed parts
of India and Bangladesh,
officials said." + ], + [ + "LOS ANGELES - California has
adopted the world's first
rules to reduce greenhouse
emissions for autos, taking
what supporters see as a
dramatic step toward cleaning
up the environment but also
ensuring higher costs for
drivers. The rules may lead
to sweeping changes in
vehicles nationwide,
especially if other states opt
to follow California's
example..." + ], + [ + "AFP - Republican and
Democratic leaders each
declared victory after the
first head-to-head sparring
match between President George
W. Bush and Democratic
presidential hopeful John
Kerry." + ], + [ + "Last night in New York, the UN
secretary-general was given a
standing ovation - a robust
response to a series of
attacks in past weeks." + ], + [ + "JOHANNESBURG -- Meeting in
Nigeria four years ago,
African leaders set a goal
that 60 percent of children
and pregnant women in malaria-
affected areas around the
continent would be sleeping
under bed nets by the end of
2005." + ], + [ + "AP - Duke Bainum outspent Mufi
Hannemann in Honolulu's most
expensive mayoral race, but
apparently failed to garner
enough votes in Saturday's
primary to claim the office
outright." + ], + [ + "POLITICIANS and aid agencies
yesterday stressed the
importance of the media in
keeping the spotlight on the
appalling human rights abuses
taking place in the Darfur
region of Sudan." + ], + [ + "\\Children who have a poor diet
are more likely to become
aggressive and anti-social, US
researchers believe." + ], + [ + "Canadian Press - OTTAWA (CP) -
Contrary to Immigration
Department claims, there is no
shortage of native-borne
exotic dancers in Canada, says
a University of Toronto law
professor who has studied the
strip club business." + ], + [ + "The European Union presidency
yesterday expressed optimism
that a deal could be struck
over Turkey #39;s refusal to
recognize Cyprus in the lead-
up to next weekend #39;s EU
summit, which will decide
whether to give Ankara a date
for the start of accession
talks." + ], + [ + " WASHINGTON (Reuters) -
President Bush on Friday set a
four-year goal of seeing a
Palestinian state established
and he and British Prime
Minister Tony Blair vowed to
mobilize international
support to help make it happen
now that Yasser Arafat is
dead." + ], + [ + " KHARTOUM (Reuters) - Sudan on
Saturday questioned U.N.
estimates that up to 70,000
people have died from hunger
and disease in its remote
Darfur region since a
rebellion began 20 months
ago." + ], + [ + "AFP - At least four Georgian
soldiers were killed and five
wounded in overnight clashes
in Georgia's separatist, pro-
Russian region of South
Ossetia, Georgian officers
near the frontline with
Ossetian forces said early
Thursday." + ], + [ + "The European Commission is
expected later this week to
recommend EU membership talks
with Turkey. Meanwhile, German
Chancellor Gerhard Schroeder
and Turkish Prime Minister
Tayyip Erdogan are
anticipating a quot;positive
report." + ], + [ + "The Canadian government
signalled its intention
yesterday to reintroduce
legislation to decriminalise
the possession of small
amounts of marijuana." + ], + [ + "A screensaver targeting spam-
related websites appears to
have been too successful." + ], + [ + " ABIDJAN (Reuters) - Ivory
Coast warplanes killed nine
French soldiers on Saturday in
a bombing raid during the
fiercest clashes with rebels
for 18 months and France hit
back by destroying most of
the West African country's
small airforce." + ], + [ + "GAZA CITY, Gaza Strip --
Islamic militant groups behind
many suicide bombings
dismissed yesterday a call
from Mahmoud Abbas, the
interim Palestinian leader, to
halt attacks in the run-up to
a Jan. 9 election to replace
Yasser Arafat." + ], + [ + "Secretary of State Colin
Powell is wrapping up an East
Asia trip focused on prodding
North Korea to resume talks
aimed at ending its nuclear-
weapons program." + ], + [ + "The risk of intestinal damage
from common painkillers may be
higher than thought, research
suggests." + ], + [ + "AN earthquake measuring 7.3 on
the Richter Scale hit western
Japan this morning, just hours
after another strong quake
rocked the area." + ], + [ + "Britain #39;s Prince Harry,
struggling to shed a growing
quot;wild child quot; image,
won #39;t apologize to a
photographer he scuffled with
outside an exclusive London
nightclub, a royal spokesman
said on Saturday." + ], + [ + "Pakistan #39;s interim Prime
Minister Chaudhry Shaujaat
Hussain has announced his
resignation, paving the way
for his successor Shauket
Aziz." + ], + [ + "A previously unknown group
calling itself Jamaat Ansar
al-Jihad al-Islamiya says it
set fire to a Jewish soup
kitchen in Paris, according to
an Internet statement." + ], + [ + "The deadliest attack on
Americans in Iraq since May
came as Iraqi officials
announced that Saddam
Hussein's deputy had not been
captured on Sunday." + ], + [ + "A fundraising concert will be
held in London in memory of
the hundreds of victims of the
Beslan school siege." + ], + [ + "AFP - The landmark trial of a
Rwandan Roman Catholic priest
accused of supervising the
massacre of 2,000 of his Tutsi
parishioners during the
central African country's 1994
genocide opens at a UN court
in Tanzania." + ], + [ + "The Irish government has
stepped up its efforts to free
the British hostage in Iraq,
Ken Bigley, whose mother is
from Ireland, by talking to
diplomats from Iran and
Jordan." + ], + [ + "AP - Republican Sen. Lincoln
Chafee, who flirted with
changing political parties in
the wake of President Bush's
re-election victory, says he
will stay in the GOP." + ], + [ + "South Korean President Roh
Moo-hyun pays a surprise visit
to troops in Iraq, after his
government decided to extend
their mandate." + ], + [ + "As the European Union
approaches a contentious
decision - whether to let
Turkey join the club - the
Continent #39;s rulers seem to
have left their citizens
behind." + ], + [ + " BAGHDAD (Reuters) - A
deadline set by militants who
have threatened to kill two
Americans and a Briton seized
in Iraq was due to expire
Monday, and more than two
dozen other hostages were
also facing death unless rebel
demands were met." + ], + [ + "Some Venezuelan television
channels began altering their
programs Thursday, citing
fears of penalties under a new
law restricting violence and
sexual content over the
airwaves." + ], + [ + "afrol News, 4 October -
Hundred years of conservation
efforts have lifted the
southern black rhino
population from about hundred
to 11,000 animals." + ], + [ + "THE death of Philippine movie
star and defeated presidential
candidate Fernando Poe has
drawn some political backlash,
with some people seeking to
use his sudden demise as a
platform to attack President
Gloria Arroyo." + ], + [ + "VIENTIANE, Laos China moved
yet another step closer in
cementing its economic and
diplomatic relationships with
Southeast Asia today when
Prime Minister Wen Jiabao
signed a trade accord at a
regional summit that calls for
zero tariffs on a wide range
of" + ], + [ + "British judges in London
Tuesday ordered radical Muslim
imam Abu Hamza to stand trial
for soliciting murder and
inciting racial hatred." + ], + [ + "SARASOTA, Fla. - The
devastation brought on by
Hurricane Charley has been
especially painful for an
elderly population that is
among the largest in the
nation..." + ], + [ + " quot;He is charged for having
a part in the Bali incident,
quot; state prosecutor Andi
Herman told Reuters on
Saturday. bombing attack at
the US-run JW." + ], + [ + "The United Nations called on
Monday for an immediate
ceasefire in eastern Congo as
fighting between rival army
factions flared for a third
day." + ], + [ + "Allowing dozens of casinos to
be built in the UK would bring
investment and thousands of
jobs, Tony Blair says." + ], + [ + "The shock here was not just
from the awful fact itself,
that two vibrant young Italian
women were kidnapped in Iraq,
dragged from their office by
attackers who, it seems, knew
their names." + ], + [ + "Tehran/Vianna, Sept. 19 (NNN):
Iran on Sunday rejected the
International Atomic Energy
Agency (IAEA) call to suspend
of all its nuclear activities,
saying that it will not agree
to halt uranium enrichment." + ], + [ + "A 15-year-old Argentine
student opened fire at his
classmates on Tuesday in a
middle school in the south of
the Buenos Aires province,
leaving at least four dead and
five others wounded, police
said." + ], + [ + "NEW YORK - A cable pay-per-
view company has decided not
to show a three-hour election
eve special with filmmaker
Michael Moore that included a
showing of his documentary
\"Fahrenheit 9/11,\" which is
sharply critical of President
Bush. The company, iN
DEMAND, said Friday that its
decision is due to \"legitimate
business and legal concerns.\"
A spokesman would not
elaborate..." + ], + [ + "Democracy candidates picked up
at least one more seat in
parliament, according to exit
polls." + ], + [ + "The IOC wants suspended
Olympic member Ivan Slavkov to
be thrown out of the
organisation." + ], + [ + " BANGKOK (Reuters) - The
ouster of Myanmar's prime
minister, architect of a
tentative \"roadmap to
democracy,\" has dashed faint
hopes for an end to military
rule and leaves Southeast
Asia's policy of constructive
engagement in tatters." + ], + [ + "The anguish of hostage Kenneth
Bigley in Iraq hangs over
Prime Minister Tony Blair
today as he faces the twin
test of a local election and a
debate by his Labour Party
about the divisive war." + ], + [ + " quot;There were 16 people
travelling aboard. ... It
crashed into a mountain, quot;
Col. Antonio Rivero, head of
the Civil Protection service,
told." + ], + [ + "President Bush is reveling in
winning the popular vote and
feels he can no longer be
considered a one-term accident
of history." + ], + [ + "Canadian Press - TORONTO (CP)
- Thousands of Royal Bank
clerks are being asked to
display rainbow stickers at
their desks and cubicles to
promote a safe work
environment for gays,
lesbians, and bisexuals." + ], + [ + " BAGHDAD (Reuters) - A car
bomb killed two American
soldiers and wounded eight
when it exploded in Baghdad on
Saturday, the U.S. military
said in a statement." + ], + [ + "Sudanese authorities have
moved hundreds of pro-
government fighters from the
crisis-torn Darfur region to
other parts of the country to
keep them out of sight of
foreign military observers
demanding the militia #39;s
disarmament, a rebel leader
charged" + ], + [ + "BAGHDAD, Iraq - A car bomb
Tuesday ripped through a busy
market near a Baghdad police
headquarters where Iraqis were
waiting to apply for jobs on
the force, and gunmen opened
fire on a van carrying police
home from work in Baqouba,
killing at least 59 people
total and wounding at least
114. The attacks were the
latest attempts by militants
to wreck the building of a
strong Iraqi security force, a
keystone of the U.S..." + ], + [ + "The Israeli prime minister
said today that he wanted to
begin withdrawing settlers
from Gaza next May or June." + ], + [ + "Queen Elizabeth II stopped
short of apologizing for the
Allies #39; bombing of Dresden
during her first state visit
to Germany in 12 years and
instead acknowledged quot;the
appalling suffering of war on
both sides." + ], + [ + "AP - Fugitive Taliban leader
Mullah Mohammed Omar has
fallen out with some of his
lieutenants, who blame him for
the rebels' failure to disrupt
the landmark Afghan
presidential election, the
U.S. military said Wednesday." + ], + [ + "HAVANA -- Cuban President
Fidel Castro's advancing age
-- and ultimately his
mortality -- were brought home
yesterday, a day after he
fractured a knee and arm when
he tripped and fell at a
public event." + ], + [ + " BRASILIA, Brazil (Reuters) -
The United States and Brazil
predicted on Tuesday Latin
America's largest country
would resolve a dispute with
the U.N. nuclear watchdog over
inspections of a uranium
enrichment plant." + ], + [ + "Two bombs exploded today near
a tea shop and wounded 20
people in southern Thailand,
police said, as violence
continued unabated in the
Muslim-majority region where
residents are seething over
the deaths of 78 detainees
while in military custody." + ], + [ + "Prime Minister Junichiro
Koizumi, back in Tokyo after
an 11-day diplomatic mission
to the Americas, hunkered down
with senior ruling party
officials on Friday to focus
on a major reshuffle of
cabinet and top party posts." + ], + [ + "NEW YORK - A sluggish gross
domestic product reading was
nonetheless better than
expected, prompting investors
to send stocks slightly higher
Friday on hopes that the
economic slowdown would not be
as bad as first thought.
The 2.8 percent GDP growth in
the second quarter, a revision
from the 3 percent preliminary
figure reported in July, is a
far cry from the 4.5 percent
growth in the first quarter..." + ], + [ + " SEOUL (Reuters) - A huge
explosion in North Korea last
week may have been due to a
combination of demolition work
for a power plant and
atmospheric clouds, South
Korea's spy agency said on
Wednesday." + ], + [ + "AP - Sudan's U.N. ambassador
challenged the United States
to send troops to the Darfur
region if it really believes a
genocide is taking place as
the U.S. Congress and
President Bush's
administration have
determined." + ], + [ + "AFP - A junior British
minister said that people
living in camps after fleeing
their villages because of
conflict in Sudan's Darfur
region lived in fear of
leaving their temporary homes
despite a greater presence now
of aid workers." + ], + [ + "US Deputy Secretary of State
Richard Armitage (L) shakes
hands with Pakistani Foreign
Minister Khurshid Kasuri prior
to their meeting in Islamabad,
09 November 2004." + ], + [ + "AP - Democrat John Edwards
kept up a long-distance debate
over his \"two Americas\"
campaign theme with Vice
President Dick Cheney on
Tuesday, saying it was no
illusion to thousands of laid-
off workers in Ohio." + ], + [ + "A group of 76 Eritreans on a
repatriation flight from Libya
Friday forced their plane to
change course and land in the
Sudanese capital, Khartoum,
where they" + ], + [ + "AP - They say that opposites
attract, and in the case of
Sen. John Kerry and Teresa
Heinz Kerry, that may be true
#151; at least in their public
personas." + ], + [ + "AP - Impoverished North Korea
might resort to selling
weapons-grade plutonium to
terrorists for much-needed
cash, and that would be
\"disastrous for the world,\"
the top U.S. military
commander in South Korea said
Friday." + ], + [ + "Dubai: Osama bin Laden on
Thursday called on his
fighters to strike Gulf oil
supplies and warned Saudi
leaders they risked a popular
uprising in an audio message
said to be by the Western
world #39;s most wanted terror
mastermind." + ], + [ + "BEIJING -- Chinese authorities
have arrested or reprimanded
more than 750 officials in
recent months in connection
with billions of dollars in
financial irregularities
ranging from unpaid taxes to
embezzlement, according to a
report made public yesterday." + ], + [ + "Canadian Press - GAUHATI,
India (AP) - Residents of
northeastern India were
bracing for more violence
Friday, a day after bombs
ripped through two buses and a
grenade was hurled into a
crowded market in attacks that
killed four people and wounded
54." + ], + [ + "Some 28 people are charged
with trying to overthrow
Sudan's President Bashir,
reports say." + ], + [ + "Hong Kong #39;s Beijing-backed
chief executive yesterday
ruled out any early moves to
pass a controversial national
security law which last year
sparked a street protest by
half a million people." + ], + [ + "Beer consumption has doubled
over the past five years,
prompting legislators to
implement new rules." + ], + [ + "At least 79 people were killed
and 74 were missing after some
of the worst rainstorms in
recent years triggered
landslides and flash floods in
southwest China, disaster
relief officials said
yesterday." + ], + [ + "BANGKOK Thai military aircraft
dropped 100 million paper
birds over southern Thailand
on Sunday in a gesture
intended to promote peace in
mainly Muslim provinces, where
more than 500 people have died
this year in attacks by
separatist militants and" + ], + [ + "Insurgents threatened on
Saturday to cut the throats of
two Americans and a Briton
seized in Baghdad, and
launched a suicide car bomb
attack on Iraqi security
forces in Kirkuk that killed
at least 23 people." + ], + [ + " BEIJING (Reuters) - Secretary
of State Colin Powell will
urge China Monday to exert its
influence over North Korea to
resume six-party negotiations
on scrapping its suspected
nuclear weapons program." + ], + [ + "Reuters - An Israeli missile
strike killed at least\\two
Hamas gunmen in Gaza City
Monday, a day after a
top\\commander of the Islamic
militant group was killed in a
similar\\attack, Palestinian
witnesses and security sources
said." + ], + [ + "AP - The Pentagon has restored
access to a Web site that
assists soldiers and other
Americans living overseas in
voting, after receiving
complaints that its security
measures were preventing
legitimate voters from using
it." + ], + [ + "Montreal - The British-built
Canadian submarine HMCS
Chicoutimi, crippled since a
fire at sea that killed one of
its crew, is under tow and is
expected in Faslane, Scotland,
early next week, Canadian
naval commander Tyrone Pile
said on Friday." + ], + [ + "Thirty-five Pakistanis freed
from the US Guantanamo Bay
prison camp arrived home on
Saturday and were taken
straight to prison for further
interrogation, the interior
minister said." + ], + [ + "Witnesses in the trial of a US
soldier charged with abusing
prisoners at Abu Ghraib have
told the court that the CIA
sometimes directed abuse and
orders were received from
military command to toughen
interrogations." + ], + [ + "AP - Italian and Lebanese
authorities have arrested 10
suspected terrorists who
planned to blow up the Italian
Embassy in Beirut, an Italian
news agency and the Defense
Ministry said Tuesday." + ], + [ + "CORAL GABLES, Fla. -- John F.
Kerry and President Bush
clashed sharply over the war
in Iraq last night during the
first debate of the
presidential campaign season,
with the senator from
Massachusetts accusing" + ], + [ + "GONAIVES, Haiti -- While
desperately hungry flood
victims wander the streets of
Gonaives searching for help,
tons of food aid is stacking
up in a warehouse guarded by
United Nations peacekeepers." + ], + [ + "AFP - The resignation of
disgraced Fiji Vice-President
Ratu Jope Seniloli failed to
quell anger among opposition
leaders and the military over
his surprise release from
prison after serving just
three months of a four-year
jail term for his role in a
failed 2000 coup." + ], + [ + "Sourav Ganguly files an appeal
against a two-match ban
imposed for time-wasting." + ], + [ + "Greek police surrounded a bus
full of passengers seized by
armed hijackers along a
highway from an Athens suburb
Wednesday, police said." + ], + [ + "BAGHDAD: A suicide car bomber
attacked a police convoy in
Baghdad yesterday as guerillas
kept pressure on Iraq #39;s
security forces despite a
bloody rout of insurgents in
Fallujah." + ], + [ + "Fixtures and fittings from
Damien Hirst's restaurant
Pharmacy sell for 11.1, 8m
more than expected." + ], + [ + "Five years after catastrophic
floods and mudslides killed
thousands along Venezuela's
Caribbean coast, survivors in
this town still see the signs
of destruction - shattered
concrete walls and tall weeds
growing atop streets covered
in dried mud." + ], + [ + "The UN nuclear watchdog
confirmed Monday that nearly
400 tons of powerful
explosives that could be used
in conventional or nuclear
missiles disappeared from an
unguarded military
installation in Iraq." + ], + [ + "Militants in Iraq have killed
the second of two US civilians
they were holding hostage,
according to a statement on an
Islamist website." + ], + [ + "More than 4,000 American and
Iraqi soldiers mounted a
military assault on the
insurgent-held city of
Samarra." + ], + [ + " MEXICO CITY (Reuters) -
Mexican President Vicente Fox
said on Monday he hoped
President Bush's re-election
meant a bilateral accord on
migration would be reached
before his own term runs out
at the end of 2006." + ], + [ + " BRUSSELS (Reuters) - French
President Jacques Chirac said
on Friday he had not refused
to meet Iraqi interim Prime
Minister Iyad Allawi after
reports he was snubbing the
Iraqi leader by leaving an EU
meeting in Brussels early." + ], + [ + "NEW YORK - Former President
Bill Clinton was in good
spirits Saturday, walking
around his hospital room in
street clothes and buoyed by
thousands of get-well messages
as he awaited heart bypass
surgery early this coming
week, people close to the
family said. Clinton was
expected to undergo surgery as
early as Monday but probably
Tuesday, said Democratic Party
Chairman Terry McAuliffe, who
said the former president was
\"upbeat\" when he spoke to him
by phone Friday..." + ], + [ + "Poland will cut its troops in
Iraq early next year and won
#39;t stay in the country
quot;an hour longer quot; than
needed, the country #39;s
prime minister said Friday." + ], + [ + "A car bomb exploded at a US
military convoy in the
northern Iraqi city of Mosul,
causing several casualties,
the army and Iraqi officials
said." + ], + [ + "ATHENS, Greece - Michael
Phelps took care of qualifying
for the Olympic 200-meter
freestyle semifinals Sunday,
and then found out he had been
added to the American team for
the evening's 400 freestyle
relay final. Phelps' rivals
Ian Thorpe and Pieter van den
Hoogenband and teammate Klete
Keller were faster than the
teenager in the 200 free
preliminaries..." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
intends to present a timetable
for withdrawal from Gaza to
lawmakers from his Likud Party
Tuesday despite a mutiny in
the right-wing bloc over the
plan." + ], + [ + " KABUL (Reuters) - Three U.N.
workers held by militants in
Afghanistan were in their
third week of captivity on
Friday after calls from both
sides for the crisis to be
resolved ahead of this
weekend's Muslim festival of
Eid al-Fitr." + ], + [ + "ROME, Oct 29 (AFP) - French
President Jacques Chirac urged
the head of the incoming
European Commission Friday to
take quot;the appropriate
decisions quot; to resolve a
row over his EU executive team
which has left the EU in
limbo." + ], + [ + "Russia's parliament will
launch an inquiry into a
school siege that killed over
300 hostages, President
Vladimir Putin said on Friday,
but analysts doubt it will
satisfy those who blame the
carnage on security services." + ], + [ + "Tony Blair talks to business
leaders about new proposals
for a major shake-up of the
English exam system." + ], + [ + "KUALA LUMPUR, Malaysia A
Malaysian woman has claimed a
new world record after living
with over six-thousand
scorpions for 36 days
straight." + ], + [ + "could take drastic steps if
the talks did not proceed as
Tehran wants. Mehr news agency
quoted him as saying on
Wednesday. that could be used" + ], + [ + "Israeli authorities have
launched an investigation into
death threats against Israeli
Prime Minister Ariel Sharon
and other officials supporting
his disengagement plan from
Gaza and parts of the West
Bank, Jerusalem police said
Tuesday." + ], + [ + "It was a carefully scripted
moment when Russian President
Vladimir Putin began quoting
Taras Shevchenko, this country
#39;s 19th-century bard,
during a live television" + ], + [ + "China says it welcomes Russia
#39;s ratification of the
Kyoto Protocol that aims to
stem global warming by
reducing greenhouse-gas
emissions." + ], + [ + "The Metropolitan
Transportation Authority is
proposing a tax increase to
raise \\$900 million a year to
help pay for a five-year
rebuilding program." + ], + [ + "AFP - The Canadian armed
forces chief of staff on was
elected to take over as head
of NATO's Military Committee,
the alliance's highest
military authority, military
and diplomatic sources said." + ], + [ + "AFP - Two proposed resolutions
condemning widespread rights
abuses in Sudan and Zimbabwe
failed to pass a UN committee,
mired in debate between
African and western nations." + ], + [ + "The return of noted reformer
Nabil Amr to Palestinian
politics comes at a crucial
juncture." + ], + [ + "AP - After two debates, voters
have seen President Bush look
peevish and heard him pass the
buck. They've watched Sen.
John Kerry deny he's a flip-
flopper and then argue that
Saddam Hussein was a threat
#151; and wasn't. It's no
wonder so few minds have
changed." + ], + [ + "The United States says the
Lebanese parliament #39;s
decision Friday to extend the
term of pro-Syrian President
Emile Lahoud made a
quot;crude mockery of
democratic principles." + ], + [ + "Canadian Press - OTTAWA (CP) -
The prime minister says he's
been assured by President
George W. Bush that the U.S.
missile defence plan does not
necessarily involve the
weaponization of space." + ], + [ + "Fifty-six miners are dead and
another 92 are still stranded
underground after a gas blast
at the Daping coal mine in
Central China #39;s Henan
Province, safety officials
said Thursday." + ], + [ + "BEIRUT, Lebanon - Three
Lebanese men and their Iraqi
driver have been kidnapped in
Iraq, the Lebanese Foreign
Ministry said Sunday, as
Iraq's prime minister said his
government was working for the
release of two Americans and a
Briton also being held
hostage. Gunmen snatched
the three Lebanese, who worked
for a travel agency with a
branch in Baghdad, as they
drove on the highway between
the capital and Fallujah on
Friday night, a ministry
official said..." + ], + [ + "PORTLAND, Ore. - It's been
almost a year since singer-
songwriter Elliott Smith
committed suicide, and fans
and friends will be looking
for answers as the posthumous
\"From a Basement on the Hill\"
is released..." + ], + [ + " GAZA (Reuters) - Israel
expanded its military
offensive in northern Gaza,
launching two air strikes
early on Monday that killed
at least three Palestinians
and wounded two, including a
senior Hamas leader, witnesses
and medics said." + ], + [ + "Reuters - Sudan's government
resumed\\talks with rebels in
the oil-producing south on
Thursday while\\the United
Nations set up a panel to
investigate charges
of\\genocide in the west of
Africa's largest country." + ], + [ + "NEW YORK - Optimism that the
embattled technology sector
was due for a recovery sent
stocks modestly higher Monday
despite a new revenue warning
from semiconductor company
Broadcom Inc. While
Broadcom, which makes chips
for television set-top boxes
and other electronics, said
high inventories resulted in
delayed shipments, investors
were encouraged as it said
future quarters looked
brighter..." + ], + [ + "Carlos Barrios Orta squeezed
himself into his rubber diving
suit, pulled on an 18-pound
helmet that made him look like
an astronaut, then lowered
himself into the sewer. He
disappeared into the filthy
water, which looked like some
cauldron of rancid beef stew,
until the only sign of him was
air bubbles breaking the
surface." + ], + [ + "The mutilated body of a woman
of about sixty years,
amputated of arms and legs and
with the sliced throat, has
been discovered this morning
in a street of the south of
Faluya by the Marines,
according to a statement by
AFP photographer." + ], + [ + "(SH) - In Afghanistan, Hamid
Karzai defeated a raft of
candidates to win his historic
election. In Iraq, more than
200 political parties have
registered for next month
#39;s elections." + ], + [ + "Margaret Hassan, who works for
charity Care International,
was taken hostage while on her
way to work in Baghdad. Here
are the main events since her
kidnapping." + ], + [ + "Reuters - The United States
modified its\\call for U.N.
sanctions against Sudan on
Tuesday but still kept\\up the
threat of punitive measures if
Khartoum did not
stop\\atrocities in its Darfur
region." + ], + [ + "Human rights and environmental
activists have hailed the
award of the 2004 Nobel Peace
Prize to Wangari Maathai of
Kenya as fitting recognition
of the growing role of civil
society" + ], + [ + "An Al Qaeda-linked militant
group beheaded an American
hostage in Iraq and threatened
last night to kill another two
Westerners in 24 hours unless
women prisoners were freed
from Iraqi jails." + ], + [ + "Argentina, Denmark, Greece,
Japan and Tanzania on Friday
won coveted two-year terms on
the UN Security Council at a
time when pressure is mounting
to expand the powerful
15-nation body." + ], + [ + "ISLAMABAD: Pakistan and India
agreed on Friday to re-open
the Khokhropar-Munabao railway
link, which was severed nearly
40 years ago." + ], + [ + "NEW YORK - Stocks moved higher
Friday as a stronger than
expected retail sales report
showed that higher oil prices
aren't scaring consumers away
from spending. Federal Reserve
Chairman Alan Greenspan's
positive comments on oil
prices also encouraged
investors..." + ], + [ + "Saddam Hussein lives in an
air-conditioned 10-by-13 foot
cell on the grounds of one of
his former palaces, tending
plants and proclaiming himself
Iraq's lawful ruler." + ], + [ + "AP - Brazilian U.N.
peacekeepers will remain in
Haiti until presidential
elections are held in that
Caribbean nation sometime next
year, President Luiz Inacio
Lula da Silva said Monday." + ], + [ + "Iraq is quot;working 24 hours
a day to ... stop the
terrorists, quot; interim
Iraqi Prime Minister Ayad
Allawi said Monday. Iraqis are
pushing ahead with reforms and
improvements, Allawi told" + ], + [ + "AP - Musicians, composers and
authors were among the more
than two dozen people
Wednesday honored with
National Medal of Arts and
National Humanities awards at
the White House." + ], + [ + "Wynton Marsalis, the trumpet-
playing star and artistic
director of Jazz at Lincoln
Center, has become an entity
above and around the daily
jazz world." + ], + [ + "BUENOS AIRES: Pakistan has
ratified the Kyoto Protocol on
Climatic Change, Environment
Minister Malik Khan said on
Thursday. He said that
Islamabad has notified UN
authorities of ratification,
which formally comes into
effect in February 2005." + ], + [ + "Staff Sgt. Johnny Horne, Jr.,
and Staff Sgt. Cardenas Alban
have been charged with murder
in the death of an Iraqi, the
1st Cavalry Division announced
Monday." + ], + [ + "President Vladimir Putin makes
a speech as he hosts Russia
#39;s Olympic athletes at a
Kremlin banquet in Moscow,
Thursday, Nov. 4, 2004." + ], + [ + "AFP - Hundreds of Buddhists in
southern Russia marched
through snow to see and hear
the Dalai Lama as he continued
a long-awaited visit to the
country in spite of Chinese
protests." + ], + [ + "British officials were on
diplomatic tenterhooks as they
awaited the arrival on
Thursday of French President
Jacques Chirac for a two-day
state visit to Britain." + ], + [ + " SYDNEY (Reuters) - A group of
women on Pitcairn Island in
the South Pacific are standing
by their men, who face
underage sex charges, saying
having sex at age 12 is a
tradition dating back to 18th
century mutineers who settled
on the island." + ], + [ + "Two aircraft are flying out
from the UK on Sunday to
deliver vital aid and supplies
to Haiti, which has been
devastated by tropical storm
Jeanne." + ], + [ + "A scare triggered by a
vibrating sex toy shut down a
major Australian regional
airport for almost an hour
Monday, police said. The
vibrating object was
discovered Monday morning" + ], + [ + "AP - An Indiana congressional
candidate abruptly walked off
the set of a debate because
she got stage fright." + ], + [ + "Reuters - Having reached out
to Kashmiris during a two-day
visit to the region, the prime
minister heads this weekend to
the country's volatile
northeast, where public anger
is high over alleged abuses by
Indian soldiers." + ], + [ + "SAN JUAN IXTAYOPAN, Mexico --
A pair of wooden crosses
outside the elementary school
are all that mark the macabre
site where, just weeks ago, an
angry mob captured two federal
police officers, beat them
unconscious, and set them on
fire." + ], + [ + "DUBLIN : An Olympic Airlines
plane diverted to Ireland
following a bomb alert, the
second faced by the Greek
carrier in four days, resumed
its journey to New York after
no device was found on board,
airport officials said." + ], + [ + " WASHINGTON (Reuters) - The
United States said on Friday
it is preparing a new U.N.
resolution on Darfur and that
Secretary of State Colin
Powell might address next week
whether the violence in
western Sudan constitutes
genocide." + ], + [ + "NAJAF, Iraq : Iraq #39;s top
Shiite Muslim clerics, back in
control of Najaf #39;s Imam
Ali shrine after a four-month
militia occupation, met amid
the ruins of the city as life
spluttered back to normality." + ], + [ + "Reuters - House of
Representatives
Majority\\Leader Tom DeLay,
admonished twice in six days
by his chamber's\\ethics
committee, withstood calls on
Thursday by rival\\Democrats
and citizen groups that he
step aside." + ], + [ + "AFP - Australia has accounted
for all its nationals known to
be working in Iraq following a
claim by a radical Islamic
group to have kidnapped two
Australians, Foreign Affairs
Minister Alexander Downer
said." + ], + [ + "The hurricane appeared to be
strengthening and forecasters
warned of an increased threat
of serious flooding and wind
damage." + ], + [ + "Four homeless men were
bludgeoned to death and six
were in critical condition on
Friday following early morning
attacks by unknown assailants
in downtown streets of Sao
Paulo, a police spokesman
said." + ], + [ + "Phone companies are not doing
enough to warn customers about
internet \"rogue-dialling\"
scams, watchdog Icstis warns." + ], + [ + "India's main opposition party
takes action against senior
party member Uma Bharti after
a public row." + ], + [ + "The Russian military yesterday
extended its offer of a \\$10
million reward for information
leading to the capture of two
separatist leaders who, the
Kremlin claims, were behind
the Beslan massacre." + ], + [ + "Israels Shin Bet security
service has tightened
protection of the prime
minister, MPs and parliament
ahead of next weeks crucial
vote on a Gaza withdrawal." + ], + [ + "Reuters - Iraq's interim
defense minister
accused\\neighbors Iran and
Syria on Wednesday of aiding
al Qaeda\\Islamist Abu Musab
al-Zarqawi and former agents
of Saddam\\Hussein to promote a
\"terrorist\" insurgency in
Iraq." + ], + [ + "AP - The Senate race in
Kentucky stayed at fever pitch
on Thursday as Democratic
challenger Daniel Mongiardo
stressed his opposition to gay
marriage while accusing
Republican incumbent Jim
Bunning of fueling personal
attacks that seemed to suggest
Mongiardo is gay." + ], + [ + " PORT LOUIS, Aug. 17
(Xinhuanet) -- Southern
African countries Tuesday
pledged better trade and
investment relations with
China as well as India in the
final communique released at
the end of their two-day
summit." + ], + [ + "BAGHDAD - A militant group has
released a video saying it
kidnapped a missing journalist
in Iraq and would kill him
unless US forces left Najaf
within 48 hours." + ], + [ + "18 August 2004 -- There has
been renewed fighting in the
Iraqi city of Al-Najaf between
US and Iraqi troops and Shi
#39;a militiamen loyal to
radical cleric Muqtada al-
Sadr." + ], + [ + "Australia tighten their grip
on the third Test and the
series after dominating India
on day two in Nagpur." + ], + [ + " SEOUL (Reuters) - North
Korea's two-year-old nuclear
crisis has taxed the world's
patience, the chief United
Nations nuclear regulator
said on Wednesday, urging
communist Pyongyang to return
to its disarmament treaty
obligations." + ], + [ + "GROZNY, Russia - The Russian
government's choice for
president of war-ravaged
Chechnya appeared to be the
victor Sunday in an election
tainted by charges of fraud
and shadowed by last week's
terrorist destruction of two
airliners. Little more than
two hours after polls closed,
acting Chechen president
Sergei Abramov said
preliminary results showed
Maj..." + ], + [ + "BAGHDAD, Sept 5 (AFP) - Izzat
Ibrahim al-Duri, Saddam
Hussein #39;s deputy whose
capture was announced Sunday,
is 62 and riddled with cancer,
but was public enemy number
two in Iraq for the world
#39;s most powerful military." + ], + [ + "AP - An explosion targeted the
Baghdad governor's convoy as
he was traveling through the
capital Tuesday, killing two
people but leaving him
uninjured, the Interior
Ministry said." + ], + [ + "GENEVA: Rescuers have found
the bodies of five Swiss
firemen who died after the
ceiling of an underground car
park collapsed during a fire,
a police spokesman said last
night." + ], + [ + "John Kerry has held 10 \"front
porch visit\" events an actual
front porch is optional where
perhaps 100 people ask
questions in a low-key
campaigning style." + ], + [ + "The right-win opposition
Conservative Party and Liberal
Center Union won 43 seats in
the 141-member Lithuanian
parliament, after more than 99
percent of the votes were
counted" + ], + [ + "KHARTOUM, Aug 18 (Reuters) -
The United Nations said on
Wednesday it was very
concerned by Sudan #39;s lack
of practical progress in
bringing security to Darfur,
where more than a million
people have fled their homes
for fear of militia ..." + ], + [ + "AFP - With less than two
months until the November 2
election, President George W.
Bush is working to shore up
support among his staunchest
supporters even as he courts
undecided voters." + ], + [ + "Chile's government says it
will build a prison for
officers convicted of human
rights abuses in the Pinochet
era." + ], + [ + "Palestinian leader Mahmoud
Abbas reiterated calls for his
people to drop their weapons
in the struggle for a state. a
clear change of strategy for
peace with Israel after Yasser
Arafat #39;s death." + ], + [ + " BAGHDAD (Reuters) - Iraq's
U.S.-backed government said on
Tuesday that \"major neglect\"
by its American-led military
allies led to a massacre of 49
army recruits at the weekend." + ], + [ + "BEIJING -- Police have
detained a man accused of
slashing as many as nine boys
to death as they slept in
their high school dormitory in
central China, state media
reported today." + ], + [ + "Israeli Prime Minister Ariel
Sharon #39;s Likud party
agreed on Thursday to a
possible alliance with
opposition Labour in a vote
that averted a snap election
and strengthened his Gaza
withdrawal plan." + ], + [ + "While the American forces are
preparing to launch a large-
scale attack against Falluja
and al-Ramadi, one of the
chieftains of Falluja said
that contacts are still
continuous yesterday between
members representing the city
#39;s delegation and the
interim" + ], + [ + "UN Security Council
ambassadors were still
quibbling over how best to
pressure Sudan and rebels to
end two different wars in the
country even as they left for
Kenya on Tuesday for a meeting
on the crisis." + ], + [ + "HENDALA, Sri Lanka -- Day
after day, locked in a cement
room somewhere in Iraq, the
hooded men beat him. They told
him he would be beheaded.
''Ameriqi! quot; they shouted,
even though he comes from this
poor Sri Lankan fishing
village." + ], + [ + "THE kidnappers of British aid
worker Margaret Hassan last
night threatened to turn her
over to the group which
beheaded Ken Bigley if the
British Government refuses to
pull its troops out of Iraq." + ], + [ + "<p></p><p>
BOGOTA, Colombia (Reuters) -
Ten Colombian police
officerswere killed on Tuesday
in an ambush by the National
LiberationArmy, or ELN, in the
worst attack by the Marxist
group inyears, authorities
told Reuters.</p>" + ], + [ + "AFP - US President George W.
Bush called his Philippines
counterpart Gloria Arroyo and
said their countries should
keep strong ties, a spokesman
said after a spat over
Arroyo's handling of an Iraq
kidnapping." + ], + [ + "A scathing judgment from the
UK #39;s highest court
condemning the UK government
#39;s indefinite detention of
foreign terror suspects as a
threat to the life of the
nation, left anti-terrorist
laws in the UK in tatters on
Thursday." + ], + [ + "Reuters - Australia's
conservative Prime
Minister\\John Howard, handed
the most powerful mandate in a
generation,\\got down to work
on Monday with reform of
telecommunications,\\labor and
media laws high on his agenda." + ], + [ + " TOKYO (Reuters) - Typhoon
Megi killed one person as it
slammed ashore in northern
Japan on Friday, bringing the
death toll to at least 13 and
cutting power to thousands of
homes before heading out into
the Pacific." + ], + [ + "Cairo: Egyptian President
Hosni Mubarak held telephone
talks with Palestinian leader
Yasser Arafat about Israel
#39;s plan to pull troops and
the 8,000 Jewish settlers out
of the Gaza Strip next year,
the Egyptian news agency Mena
said." + ], + [ + "The first American military
intelligence soldier to be
court-martialed over the Abu
Ghraib abuse scandal was
sentenced Saturday to eight
months in jail, a reduction in
rank and a bad-conduct
discharge." + ], + [ + " TOKYO (Reuters) - Japan will
seek an explanation at weekend
talks with North Korea on
activity indicating Pyongyang
may be preparing a missile
test, although Tokyo does not
think a launch is imminent,
Japan's top government
spokesman said." + ], + [ + " BEIJING (Reuters) - Iran will
never be prepared to
dismantle its nuclear program
entirely but remains committed
to the non-proliferation
treaty (NPT), its chief
delegate to the International
Atomic Energy Agency said on
Wednesday." + ], + [ + "<p></p><p>
SANTIAGO, Chile (Reuters) -
President Bush on
Saturdayreached into a throng
of squabbling bodyguards and
pulled aSecret Service agent
away from Chilean security
officers afterthey stopped the
U.S. agent from accompanying
the president ata
dinner.</p>" + ], + [ + "Iranian deputy foreign
minister Gholamali Khoshrou
denied Tuesday that his
country #39;s top leaders were
at odds over whether nuclear
weapons were un-Islamic,
insisting that it will
quot;never quot; make the
bomb." + ], + [ + " quot;Israel mercenaries
assisting the Ivory Coast army
operated unmanned aircraft
that aided aerial bombings of
a French base in the country,
quot; claimed" + ], + [ + "AFP - SAP, the world's leading
maker of business software,
may need an extra year to
achieve its medium-term profit
target of an operating margin
of 30 percent, its chief
financial officer said." + ], + [ + "AFP - The airline Swiss said
it had managed to cut its
first-half net loss by about
90 percent but warned that
spiralling fuel costs were
hampering a turnaround despite
increasing passenger travel." + ], + [ + "Vulnerable youngsters expelled
from schools in England are
being let down by the system,
say inspectors." + ], + [ + "Yasser Arafat was undergoing
tests for possible leukaemia
at a military hospital outside
Paris last night after being
airlifted from his Ramallah
headquarters to an anxious
farewell from Palestinian
well-wishers." + ], + [ + "Nepal #39;s main opposition
party urged the government on
Monday to call a unilateral
ceasefire with Maoist rebels
and seek peace talks to end a
road blockade that has cut the
capital off from the rest of
the country." + ], + [ + "BOSTON - The New York Yankees
and Boston were tied 4-4 after
13 innings Monday night with
the Red Sox trying to stay
alive in the AL championship
series. Boston tied the
game with two runs in the
eighth inning on David Ortiz's
solo homer, a walk to Kevin
Millar, a single by Trot Nixon
and a sacrifice fly by Jason
Varitek..." + ], + [ + "Reuters - The country may be
more\\or less evenly divided
along partisan lines when it
comes to\\the presidential
race, but the Republican Party
prevailed in\\the Nielsen
polling of this summer's
nominating conventions." + ], + [ + " In Vice President Cheney's
final push before next
Tuesday's election, talk of
nuclear annihilation and
escalating war rhetoric have
blended with balloon drops,
confetti cannons and the other
trappings of modern
campaigning with such ferocity
that it is sometimes tough to
tell just who the enemy is." + ] + ], + "hovertemplate": "label=World
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "World", + "marker": { + "color": "#636efa", + "size": 5, + "symbol": "circle" + }, + "mode": "markers", + "name": "World", + "showlegend": true, + "type": "scattergl", + "x": [ + 9.787297, + 17.206654, + 28.167477, + 14.671119, + 21.907679, + 11.003371, + 12.786125, + 28.192938, + 27.511831, + 31.907536, + 27.685726, + -0.48236108, + 24.631432, + 19.87161, + 42.217842, + 23.463217, + 15.450646, + 22.48473, + 47.958393, + 7.788443, + 26.455547, + -4.8219795, + 3.2403526, + 0.8158772, + 7.324227, + 24.88585, + 38.79396, + 13.661437, + 30.355759, + 3.1252713, + -17.98721, + 18.820461, + 13.827141, + 1.4320391, + 12.191131, + -2.3659778, + 25.89013, + -1.0874362, + 15.950565, + 11.055151, + 14.368044, + 15.940281, + 28.371725, + 17.925577, + 15.851762, + 1.6593695, + 18.05479, + 28.471008, + 10.705277, + 18.345266, + 31.360231, + 50.538635, + 4.6381655, + 16.084637, + 51.287056, + 12.821454, + 24.011929, + 17.79019, + 15.120078, + -39.450546, + 19.99747, + 15.529904, + 19.791918, + -9.525661, + 12.873272, + 16.33122, + 22.366423, + 2.4414933, + 25.421625, + 20.893494, + 19.51864, + 11.30494, + 2.9794881, + 12.285144, + 3.3651476, + -16.802534, + 18.079544, + 2.7036908, + 6.7110343, + -1.3755705, + 10.5324745, + 18.966919, + 31.810251, + 17.243034, + 4.507162, + 21.953882, + 12.895756, + 13.899155, + 9.645002, + 8.84193, + 7.766448, + 10.753302, + 60.835865, + 29.961258, + 31.980536, + 23.273722, + 3.1031818, + 12.880273, + 11.016033, + 17.860275, + 12.732019, + 16.701687, + 14.009928, + 18.774673, + 2.979671, + 8.863162, + 9.040379, + 24.042429, + 25.076736, + 30.519775, + 12.896586, + 25.85091, + 19.948092, + 20.974108, + 18.678154, + 49.229675, + -13.887877, + 19.741331, + 31.022472, + -11.186273, + 18.250782, + 4.836007, + -9.537627, + 24.104692, + 1.9518297, + 15.377659, + 14.915583, + 19.173527, + -7.4475813, + 8.212411, + 5.134725, + 14.638772, + 1.3949758, + 15.7138605, + 17.170551, + 17.347712, + 24.028929, + 5.1724906, + 27.314432, + 12.80685, + -26.112938, + 12.646676, + 9.909096, + 22.181377, + 29.485273, + -1.1384851, + 24.128727, + 16.221085, + 25.097984, + 8.574884, + 21.295237, + 25.800558, + 5.8968763, + 24.184378, + -54.980534, + 46.068615, + 20.787077, + 29.313784, + 8.255305, + 15.709119, + -26.129557, + 10.710161, + 23.318872, + -46.75248, + 20.918581, + 11.446291, + 10.624376, + 13.654009, + 23.009165, + 0.9160498, + 1.6248351, + 15.268804, + 20.570063, + 16.519796, + 1.7450867, + -16.392036, + 19.578056, + 9.273592, + 5.769484, + 5.3805246, + 2.333401, + 14.209792, + 7.33986, + -1.2716205, + 2.7853732, + 26.978062, + 18.276062, + 20.191584, + 25.01299, + 10.248553, + 4.6009235, + 9.839586, + 11.750173, + 7.9382405, + 17.778656, + 15.492881, + 7.7321296, + 8.952756, + 20.371246, + -9.127035, + 1.8769449, + 12.356418, + 4.2263513, + 31.053217, + -9.149289, + 9.723743, + 27.65755, + 26.112234, + -39.176956, + 16.072603, + 17.410772, + 12.662249, + 18.940567, + 20.549877, + 19.506048, + 8.936648, + 14.902041, + 22.10423, + -2.6893454, + 51.905163, + 21.657618, + 13.691204, + 28.035511, + 23.045893, + 30.591192, + 17.440117, + -9.929121, + 7.212067, + 14.5572, + -33.03146, + 26.660809, + 2.4019308, + 14.605348, + 5.080946, + 4.331932, + 32.7655, + 23.97021, + 22.242004, + -28.60194, + 14.265632, + 7.0784307, + 20.74278, + 4.454403, + 51.55675, + 14.978659, + 11.338411, + 11.213355, + 15.526145, + 9.335806, + -8.953644, + 18.62211, + 23.033293, + -5.5465455, + 10.529721, + 13.965547, + 21.258057, + 10.181149, + 17.598188, + 1.3672223, + 32.536667, + 10.24864, + 5.8206697, + -10.62197, + 21.86292, + 20.400032, + 27.48848, + 2.2426317, + 5.0682087, + 27.914957, + 16.509165, + 14.155432, + 2.808305, + 6.707939, + 19.51436, + 18.935217, + 46.280994, + 8.379798, + 2.4920332, + 47.18665, + -0.99369574, + -28.31348, + 2.79127, + 16.340908, + 20.619595, + 23.400663, + 14.445518, + 26.900372, + 12.496445, + 28.753572, + 21.319304, + 11.094263, + 7.11425, + 18.546873, + 37.20064, + 9.897873, + 24.480858, + 8.527567, + 16.88663, + 13.06387, + 29.025854, + 3.984353, + -1.1919637, + 5.320594, + 14.373686, + 11.930037, + 23.310764, + 18.960714, + 17.941885, + 10.820086, + 11.278602, + 8.342169, + 58.012913, + 7.6059, + -7.4377956, + 12.515653, + 8.195707, + 1.8771796, + 22.029692, + 4.4929285, + -17.537066, + -2.2965894, + 3.6962993, + 11.67213, + 5.52023, + 15.926025, + 4.0713243, + -3.7340183, + -15.154654, + 11.040503, + 13.273838, + 7.288217, + -18.065994, + 8.75742, + 29.14241, + 17.28087, + 13.877883, + 21.1013, + 13.981468, + 10.2113, + 28.748735, + 10.333969, + 12.468114, + 3.0369818, + 24.300056, + -8.906268, + -0.9226989, + 32.861256, + 1.5139743, + 22.857521, + 4.3284388, + 26.680521, + 8.132189, + 11.17274, + 22.79567, + 2.0400486, + 19.547178, + -4.5817585, + 17.717125, + 32.523216, + 19.772266, + 10.665481, + 15.195456, + 11.543438, + -18.300999, + 18.401154, + 7.0795293, + 19.923655, + 14.908174, + 15.951407, + 20.179276, + 31.14273, + 0.258186, + 14.35011, + 14.368874, + -42.005432, + 9.892005, + 6.150114, + 6.031961, + 8.659726, + 10.755605, + 8.7123785, + 12.619259, + 0.080250725, + 20.854275, + -11.675889, + 14.897927, + 8.117931, + 6.539579, + -52.00866, + 7.7298007, + 12.733178, + 23.083542, + 6.8278956, + 14.610548, + -19.3257, + 7.079915, + 21.593582, + 22.709345, + 23.351994, + 10.662044, + 6.3287606, + 20.248484, + 34.33151, + 17.894573, + 18.915913, + -56.14451, + 5.4172053, + -0.9005222, + 24.29299, + 15.59622, + 10.643132, + 5.560217, + 24.635311, + 13.113659, + 18.401346, + 46.27362, + 14.899461, + 24.252523, + 5.36986, + 31.625498, + 10.970106, + 51.867313, + 2.5174408, + 15.975605, + 27.581682, + 1.6741688, + -27.687584, + 16.295555, + 3.6958573, + 17.794357, + 10.886147, + 18.907486, + 3.8529873, + 10.995691, + 23.637348, + 33.6961, + 14.72486, + 7.9172506, + -48.755344, + -3.3571925, + 19.580015, + 19.054085, + 26.428055, + 4.8372564, + 2.4959075, + 24.6171, + -3.8125634, + -0.46443436, + -45.570045, + 25.599476, + 14.019283, + 15.602155, + -13.399857, + 62.178337, + 1.1374025, + 25.336575, + 27.973917, + 23.369677, + 6.171623, + 26.484608, + 7.0410976, + 19.369898, + 20.71207, + 12.614038, + 17.452501, + 18.572897, + 6.0333753, + 16.012442, + 9.572269, + 26.459038, + 2.9941561, + 17.785444, + -16.35767, + 20.736986, + 24.382677, + 16.1644, + 19.621206, + 8.682678, + 1.4400622, + 25.263475, + 16.929596, + 39.76294, + 0.50536364, + 19.154146, + 26.26559, + 19.381176, + 12.624142, + 13.547113, + 17.735638, + 5.6944747, + 11.9049635, + 19.661798, + 12.937629, + 18.126286, + 19.819803, + 14.775703, + 16.772926, + 3.6852949, + 25.670559, + 26.078526, + 17.1552, + 9.536311, + 21.02507, + 12.836097, + 6.9077144, + 13.885367, + 25.294838, + 0.9785223, + 1.8828108, + 10.278181, + 3.350846, + 10.675423, + -27.676836, + -31.987688, + 2.7699895, + 29.804829, + 8.834031, + 57.51465, + 14.13684, + 10.008406 + ], + "xaxis": "x", + "y": [ + -0.25818, + 22.728033, + 23.284092, + 21.800117, + 22.159718, + 29.896206, + 6.133116, + 8.674217, + 25.905638, + 9.192532, + 25.749458, + 39.722248, + 7.8999453, + -7.791385, + -27.949192, + 10.707973, + 21.421028, + 23.633503, + 9.072081, + 11.001149, + 6.058426, + 4.199548, + 8.901868, + 7.6804514, + 4.9480743, + 26.755693, + -2.3146381, + 2.532362, + 23.373753, + 22.522005, + 24.106895, + 9.316687, + 27.62886, + 5.951754, + 15.638516, + -17.482075, + 20.813839, + 12.0553255, + 28.830767, + 14.3665, + 0.061168052, + 23.813591, + 21.227903, + 31.043804, + 3.844936, + 1.4175098, + 3.6453905, + 16.828537, + 9.330847, + 9.5765, + -10.817816, + 5.1117396, + 3.6004014, + 18.804445, + -9.840589, + 4.881303, + 30.134317, + 32.50385, + 8.090675, + 21.478413, + 14.554468, + 20.755419, + 23.0764, + 8.462659, + 18.15951, + 2.4064643, + 8.999294, + -6.154228, + 14.864804, + 19.460798, + -2.3412774, + 2.4203362, + 32.717518, + -7.624435, + 2.3350112, + -16.211517, + 22.352716, + 8.7813, + 27.98141, + 5.231712, + -9.940092, + 2.9951952, + 7.513018, + -2.4976819, + 22.716187, + 15.819128, + 5.0620914, + 12.743932, + -8.023369, + 16.329193, + 30.292624, + 23.7153, + -17.214022, + 15.711783, + 0.59863055, + 0.31460643, + -1.605775, + 10.840164, + 30.278105, + 19.883846, + -6.269737, + 20.470428, + 25.027699, + 15.167711, + 23.27041, + 9.704817, + 14.702273, + 18.21779, + 16.7961, + 9.027207, + 18.049044, + 25.408955, + -1.7761685, + 7.837817, + 34.420277, + 6.988793, + 24.185543, + 17.168627, + 10.645888, + 33.268223, + 35.533974, + 5.3443413, + 16.755838, + 19.649885, + 5.488912, + 15.933173, + 5.761818, + 0.9805258, + 34.912987, + 2.2579987, + 31.233051, + 22.297411, + 3.5171926, + 23.886812, + 19.465944, + 16.774218, + 14.815331, + 11.881365, + 25.418554, + 28.923544, + 5.0646152, + 23.290081, + -5.3597302, + 18.798145, + 8.9016, + -22.157974, + 18.377977, + 31.100603, + 23.13797, + 31.66899, + 25.328583, + 5.658062, + 8.72019, + 4.320076, + 0.7318651, + -11.081378, + -2.6716044, + 13.364902, + 5.9486156, + 12.414623, + 5.5806613, + 16.740986, + 5.6076837, + 19.352674, + -0.68818355, + 9.428179, + 18.622755, + 7.8728004, + 21.211613, + 10.388335, + -0.20468314, + 29.903488, + 14.083497, + 0.6598771, + 26.250034, + -24.852484, + 12.103588, + -8.026257, + 16.791052, + 31.885904, + 11.005908, + 15.028398, + 14.654819, + 11.900147, + -1.3390135, + 26.11797, + -2.478072, + -23.535704, + 27.143415, + 0.81385136, + 17.844543, + 19.694197, + 30.822157, + 11.223421, + 34.832695, + 15.818021, + 12.122141, + 33.150494, + 20.72354, + 3.162032, + -0.9915625, + -15.606873, + 7.1253057, + 15.600531, + 3.0372694, + -7.6383777, + 21.02248, + 17.865675, + 22.459995, + 12.360714, + 21.917862, + -2.2529974, + 23.902458, + 7.067429, + 19.686275, + -2.4079158, + 20.084156, + 9.865102, + -7.1564, + 14.4235935, + 10.5956135, + 33.509598, + 2.4050539, + 7.209693, + 5.5682783, + 24.32622, + 8.181132, + 18.989775, + -25.712864, + 24.787573, + 14.609039, + 7.1217036, + 16.848389, + 32.03336, + -1.7420386, + 10.520301, + 30.076416, + 2.9773757, + 17.441216, + 22.073168, + 29.535501, + 31.019588, + 31.29178, + 7.783574, + -1.0848922, + -1.632514, + 3.1787782, + 26.127491, + 11.578919, + 8.806574, + 26.605755, + 25.579552, + 27.044067, + 7.423554, + 19.89922, + 21.111221, + 29.565565, + 20.825033, + 8.250312, + -4.315795, + 33.91667, + 31.587502, + 22.03959, + 2.5191355, + -1.2537154, + 1.041188, + 6.271001, + 15.563097, + 19.476908, + 17.528534, + 14.279812, + 19.227066, + 27.98984, + 17.888885, + 20.995552, + -25.321373, + 2.030001, + 19.098873, + -1.3566388, + 8.210962, + -34.972008, + 26.949068, + 12.173473, + 20.502365, + 4.4826207, + 30.730364, + 22.801989, + -7.8275642, + 9.649646, + 5.474195, + 26.200584, + 23.3236, + 23.742764, + -9.009804, + 10.729193, + 24.293411, + 1.2037258, + -2.6479704, + 21.060795, + 13.542582, + -0.5990197, + -23.021431, + 11.98244, + 26.565365, + 27.739674, + 4.583181, + 28.793531, + 25.030203, + 20.863323, + 15.091036, + 4.7823358, + -19.877468, + 29.76316, + -3.8284235, + -8.098414, + 6.9839473, + 18.062141, + -3.4012072, + 15.937399, + 24.769993, + 8.2281, + -1.0408515, + 3.6773765, + 16.484713, + 23.824167, + -1.6157911, + -5.807004, + 5.4682074, + 20.873947, + 7.471235, + 4.855366, + 24.15315, + 33.191727, + -1.2212269, + 1.353517, + 33.686493, + 17.499424, + 10.638852, + 12.36343, + 25.982975, + -5.359385, + 10.390779, + -2.7448454, + 23.544054, + 1.2731425, + 20.49504, + 11.026453, + -5.233647, + 7.5924697, + 30.057226, + 17.865553, + 14.572172, + -0.43766883, + 26.446459, + 7.797492, + 27.607801, + -10.88375, + -5.4497714, + -3.2008982, + 3.516546, + -2.0853994, + 29.915886, + -4.8032465, + 10.539988, + 22.650063, + 16.676228, + 23.31772, + 6.885112, + -3.024608, + 16.557335, + 8.043718, + 4.2089057, + 26.167639, + 0.031842478, + 23.264338, + 28.108557, + 13.463569, + 17.450424, + 4.5370917, + -9.992426, + 19.615667, + -8.944074, + 10.486003, + 13.904057, + 0.17712176, + 26.276295, + 18.80954, + 8.85504, + 6.502574, + 30.14757, + 25.445856, + 23.137957, + 4.7606173, + 20.943676, + 34.946663, + 23.354967, + 10.895949, + 16.164768, + 18.747564, + -8.708449, + 26.564816, + 3.6623113, + -2.406363, + 30.990358, + 22.291674, + 8.649531, + 29.637974, + 12.135396, + 22.34238, + 17.654528, + 3.530811, + 1.305536, + -3.4917607, + 15.667845, + 22.695467, + 3.3096304, + 5.8128743, + 24.534492, + 0.7944149, + 23.025293, + -0.33986145, + 14.641378, + 4.667992, + 6.4794717, + -3.0957053, + 1.5328475, + 14.641849, + 0.75382006, + 13.353416, + 7.6756034, + -8.807442, + 5.574528, + 5.459471, + 30.176495, + 25.41137, + 12.340705, + 20.984993, + 11.320027, + 19.095402, + 26.357058, + 25.323908, + 0.40740138, + 23.981928, + 12.158624, + 15.439734, + 22.680363, + -0.7899231, + 0.6461262, + 21.006203, + 10.719515, + 2.1415112, + 15.075585, + 19.439365, + -7.721521, + 1.3289208, + 15.136754, + -3.098876, + 0.043326903, + -0.730605, + 20.569399, + 20.47704, + 34.56152, + -9.076381, + 11.381456, + 27.552359, + 34.52153, + 12.712044, + 13.092032, + -5.358728, + 21.975595, + 31.648344, + 27.402872, + 7.268004, + -9.072612, + 21.008837, + 6.308118, + 14.5252905, + -10.20566, + -5.336508, + 22.627249, + 17.18417, + -2.677447, + 6.842084, + -2.1064568, + 11.057577, + 21.760345, + 31.105818, + -7.2484465, + -5.4786696, + 16.441422, + -5.9703918, + 9.63625, + 6.1655693, + 18.591246, + 27.819729, + 5.3510475, + 15.152627, + 13.393224, + 19.411095, + 23.425772, + 26.30866, + 5.3038287, + 7.526191, + -2.2162335, + 31.378555, + 6.302167, + 14.10122, + 5.90295, + 7.223655, + 6.634295, + 7.232844, + 21.119562, + 12.128642, + 9.936496, + 21.916615, + 9.071596, + 14.497267, + -22.694798, + -8.824205, + -10.619046 + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "PC World - Upcoming chip set
will include built-in security
features for your PC." + ], + [ + "SAN JOSE, Calif. -- Apple
Computer (Quote, Chart)
unveiled a batch of new iPods,
iTunes software and promos
designed to keep it atop the
heap of digital music players." + ], + [ + "Any product, any shape, any
size -- manufactured on your
desktop! The future is the
fabricator. By Bruce Sterling
from Wired magazine." + ], + [ + "The Microsoft CEO says one way
to stem growing piracy of
Windows and Office in emerging
markets is to offer low-cost
computers." + ], + [ + "PalmSource surprised the
mobile vendor community today
with the announcement that it
will acquire China MobileSoft
(CMS), ostensibly to leverage
that company's expertise in
building a mobile version of
the Linux operating system." + ], + [ + "JEFFERSON CITY - An election
security expert has raised
questions about Missouri
Secretary of State Matt Blunt
#39;s plan to let soldiers at
remote duty stations or in
combat areas cast their
ballots with the help of
e-mail." + ], + [ + "A new, optional log-on service
from America Online that makes
it harder for scammers to
access a persons online
account will not be available
for Macintosh" + ], + [ + "It #39;s official. Microsoft
recently stated
definitivelyand contrary to
rumorsthat there will be no
new versions of Internet
Explorer for users of older
versions of Windows." + ], + [ + "But fresh antitrust suit is in
the envelope, says Novell" + ], + [ + "Chips that help a computer's
main microprocessors perform
specific types of math
problems are becoming a big
business once again.\\" + ], + [ + "A rocket carrying two Russian
cosmonauts and an American
astronaut to the international
space station streaked into
orbit on Thursday, the latest
flight of a Russian space
vehicle to fill in for
grounded US shuttles." + ], + [ + "OCTOBER 12, 2004
(COMPUTERWORLD) - Microsoft
Corp. #39;s monthly patch
release cycle may be making it
more predictable for users to
deploy software updates, but
there appears to be little
letup in the number of holes
being discovered in the
company #39;s software" + ], + [ + "Wearable tech goes mainstream
as the Gap introduces jacket
with built-in radio.\\" + ], + [ + "Funding round of \\$105 million
brings broadband Internet
telephony company's total haul
to \\$208 million." + ], + [ + "washingtonpost.com - Anthony
Casalena was 17 when he got
his first job as a programmer
for a start-up called
HyperOffice, a software
company that makes e-mail and
contact management
applications for the Web.
Hired as an intern, he became
a regular programmer after two
weeks and rewrote the main
product line." + ], + [ + "The fossilized neck bones of a
230-million-year-old sea
creature have features
suggesting that the animal
#39;s snakelike throat could
flare open and create suction
that would pull in prey." + ], + [ + "IBM late on Tuesday announced
the biggest update of its
popular WebSphere business
software in two years, adding
features such as automatically
detecting and fixing problems." + ], + [ + "AP - British entrepreneur
Richard Branson said Monday
that his company plans to
launch commercial space
flights over the next few
years." + ], + [ + "A group of Internet security
and instant messaging
providers have teamed up to
detect and thwart the growing
threat of IM and peer-to-peer
viruses and worms, they said
this week." + ], + [ + "His first space flight was in
1965 when he piloted the first
manned Gemini mission. Later
he made two trips to the moon
-- orbiting during a 1969
flight and then walking on the
lunar surface during a mission
in 1972." + ], + [ + "By RACHEL KONRAD SAN
FRANCISCO (AP) -- EBay Inc.,
which has been aggressively
expanding in Asia, plans to
increase its stake in South
Korea's largest online auction
company. The Internet
auction giant said Tuesday
night that it would purchase
nearly 3 million publicly held
shares of Internet Auction
Co..." + ], + [ + "AP - China's biggest computer
maker, Lenovo Group, said
Wednesday it has acquired a
majority stake in
International Business
Machines Corp.'s personal
computer business for
#36;1.75 billion, one of the
biggest Chinese overseas
acquisitions ever." + ], + [ + "Big evolutionary insights
sometimes come in little
packages. Witness the
startling discovery, in a cave
on the eastern Indonesian
island of Flores, of the
partial skeleton of a half-
size Homo species that" + ], + [ + "Luke Skywalker and Darth Vader
may get all the glory, but a
new Star Wars video game
finally gives credit to the
everyday grunts who couldn
#39;t summon the Force for
help." + ], + [ + " LOS ANGELES (Reuters) -
Federal authorities raided
three Washington, D.C.-area
video game stores and arrested
two people for modifying
video game consoles to play
pirated video games, a video
game industry group said on
Wednesday." + ], + [ + "Thornbugs communicate by
vibrating the branches they
live on. Now scientists are
discovering just what the bugs
are \"saying.\"" + ], + [ + "Federal prosecutors cracked a
global cartel that had
illegally fixed prices of
memory chips in personal
computers and servers for
three years." + ], + [ + "AP - Oracle Corp. CEO Larry
Ellison reiterated his
determination to prevail in a
long-running takeover battle
with rival business software
maker PeopleSoft Inc.,
predicting the proposed deal
will create a more competitive
company with improved customer
service." + ], + [ + "AP - Scientists warned Tuesday
that a long-term increase in
global temperature of 3.5
degrees could threaten Latin
American water supplies,
reduce food yields in Asia and
result in a rise in extreme
weather conditions in the
Caribbean." + ], + [ + "AP - Further proof New York's
real estate market is
inflated: The city plans to
sell space on top of lampposts
to wireless phone companies
for #36;21.6 million a year." + ], + [ + "The rollout of new servers and
networking switches in stores
is part of a five-year
agreement supporting 7-Eleven
#39;s Retail Information
System." + ], + [ + "Top Hollywood studios have
launched a wave of court cases
against internet users who
illegally download film files.
The Motion Picture Association
of America, which acts as the
representative of major film" + ], + [ + "AUSTRALIANS went into a
television-buying frenzy the
run-up to the Athens Olympics,
suggesting that as a nation we
could easily have scored a
gold medal for TV purchasing." + ], + [ + "The next time you are in your
bedroom with your PC plus
Webcam switched on, don #39;t
think that your privacy is all
intact. If you have a Webcam
plugged into an infected
computer, there is a
possibility that" + ], + [ + "AP - Shawn Fanning's Napster
software enabled countless
music fans to swap songs on
the Internet for free, turning
him into the recording
industry's enemy No. 1 in the
process." + ], + [ + "Reuters - Walt Disney Co.
is\\increasing investment in
video games for hand-held
devices and\\plans to look for
acquisitions of small game
publishers and\\developers,
Disney consumer products
Chairman Andy Mooney said\\on
Monday." + ], + [ + "BANGKOK - A UN conference last
week banned commercial trade
in the rare Irrawaddy dolphin,
a move environmentalists said
was needed to save the
threatened species." + ], + [ + "PC World - Updated antivirus
software for businesses adds
intrusion prevention features." + ], + [ + "Reuters - Online media company
Yahoo Inc.\\ late on Monday
rolled out tests of redesigned
start\\pages for its popular
Yahoo.com and My.Yahoo.com
sites." + ], + [ + "The Sun may have captured
thousands or even millions of
asteroids from another
planetary system during an
encounter more than four
billion years ago, astronomers
are reporting." + ], + [ + "Thumb through the book - then
buy a clean copy from Amazon" + ], + [ + "Reuters - High-definition
television can\\show the sweat
beading on an athlete's brow,
but the cost of\\all the
necessary electronic equipment
can get a shopper's own\\pulse
racing." + ], + [ + "MacCentral - Apple Computer
Inc. on Monday released an
update for Apple Remote
Desktop (ARD), the company's
software solution to assist
Mac system administrators and
computer managers with asset
management, software
distribution and help desk
support. ARD 2.1 includes
several enhancements and bug
fixes." + ], + [ + "The fastest-swiveling space
science observatory ever built
rocketed into orbit on
Saturday to scan the universe
for celestial explosions." + ], + [ + "Copernic Unleashes Desktop
Search Tool\\\\Copernic
Technologies Inc. today
announced Copernic Desktop
Search(TM) (CDS(TM)), \"The
Search Engine For Your PC
(TM).\" Copernic has used the
experience gained from over 30
million downloads of its
Windows-based Web search
software to develop CDS, a
desktop search product that
users are saying is far ..." + ], + [ + "The DVD Forum moved a step
further toward the advent of
HD DVD media and drives with
the approval of key physical
specifications at a meeting of
the organisations steering
committee last week." + ], + [ + "Often pigeonholed as just a
seller of televisions and DVD
players, Royal Philips
Electronics said third-quarter
profit surged despite a slide
into the red by its consumer
electronics division." + ], + [ + "AP - Google, the Internet
search engine, has done
something that law enforcement
officials and their computer
tools could not: Identify a
man who died in an apparent
hit-and-run accident 11 years
ago in this small town outside
Yakima." + ], + [ + "We are all used to IE getting
a monthly new security bug
found, but Winamp? In fact,
this is not the first security
flaw found in the application." + ], + [ + "The Apache Software Foundation
and the Debian Project said
they won't support the Sender
ID e-mail authentication
standard in their products." + ], + [ + "USATODAY.com - The newly
restored THX 1138 arrives on
DVD today with Star Wars
creator George Lucas' vision
of a Brave New World-like
future." + ], + [ + "The chances of scientists
making any one of five
discoveries by 2010 have been
hugely underestimated,
according to bookmakers." + ], + [ + "Deepening its commitment to
help corporate users create
SOAs (service-oriented
architectures) through the use
of Web services, IBM's Global
Services unit on Thursday
announced the formation of an
SOA Management Practice." + ], + [ + "Toshiba Corp. #39;s new
desktop-replacement multimedia
notebooks, introduced on
Tuesday, are further evidence
that US consumers still have
yet to embrace the mobility
offered by Intel Corp." + ], + [ + "Aregular Amazon customer,
Yvette Thompson has found
shopping online to be mostly
convenient and trouble-free.
But last month, after ordering
two CDs on Amazon.com, the
Silver Spring reader
discovered on her bank
statement that she was double-
charged for the \\$26.98 order.
And there was a \\$25 charge
that was a mystery." + ], + [ + "Finnish mobile giant Nokia has
described its new Preminet
solution, which it launched
Monday (Oct. 25), as a
quot;major worldwide
initiative." + ], + [ + " SAN FRANCISCO (Reuters) - At
virtually every turn, Intel
Corp. executives are heaping
praise on an emerging long-
range wireless technology
known as WiMAX, which can
blanket entire cities with
high-speed Internet access." + ], + [ + "Phishing is one of the
fastest-growing forms of
personal fraud in the world.
While consumers are the most
obvious victims, the damage
spreads far wider--hurting
companies #39; finances and
reputations and potentially" + ], + [ + "Reuters - The U.S. Interior
Department on\\Friday gave
final approval to a plan by
ConocoPhillips and\\partner
Anadarko Petroleum Corp. to
develop five tracts around\\the
oil-rich Alpine field on
Alaska's North Slope." + ], + [ + "Atari has opened the initial
sign-up phase of the closed
beta for its Dungeons amp;
Dragons real-time-strategy
title, Dragonshard." + ], + [ + "Big Blue adds features, beefs
up training efforts in China;
rival Unisys debuts new line
and pricing plan." + ], + [ + "South Korea's Hynix
Semiconductor Inc. and Swiss-
based STMicroelectronics NV
signed a joint-venture
agreement on Tuesday to
construct a memory chip
manufacturing plant in Wuxi,
about 100 kilometers west of
Shanghai, in China." + ], + [ + "Well, Intel did say -
dismissively of course - that
wasn #39;t going to try to
match AMD #39;s little dual-
core Opteron demo coup of last
week and show off a dual-core
Xeon at the Intel Developer
Forum this week and - as good
as its word - it didn #39;t." + ], + [ + "IT #39;S BEEN a heck of an
interesting two days here in
Iceland. I #39;ve seen some
interesting technology, heard
some inventive speeches and
met some people with different
ideas." + ], + [ + "Reuters - Two clients of
Germany's Postbank\\(DPBGn.DE)
fell for an e-mail fraud that
led them to reveal\\money
transfer codes to a bogus Web
site -- the first case of\\this
scam in German, prosecutors
said on Thursday." + ], + [ + "US spending on information
technology goods, services,
and staff will grow seven
percent in 2005 and continue
at a similar pace through
2008, according to a study
released Monday by Forrester
Research." + ], + [ + "Look, Ma, no hands! The U.S.
space agency's latest
spacecraft can run an entire
mission by itself. By Amit
Asaravala." + ], + [ + "Joining America Online,
EarthLink and Yahoo against
spamming, Microsoft Corp.
today announced the filing of
three new anti-Spam lawsuits
under the CAN-SPAM federal law
as part of its initiative in
solving the Spam problem for
Internet users worldwide." + ], + [ + "A nationwide inspection shows
Internet users are not as safe
online as they believe. The
inspections found most
consumers have no firewall
protection, outdated antivirus
software and dozens of spyware
programs secretly running on
their computers." + ], + [ + "More than three out of four
(76 percent) consumers are
experiencing an increase in
spoofing and phishing
incidents, and 35 percent
receive fake e-mails at least
once a week, according to a
recent national study." + ], + [ + "AP - Deep in the Atlantic
Ocean, undersea explorers are
living a safer life thanks to
germ-fighting clothing made in
Kinston." + ], + [ + "Computer Associates Monday
announced the general
availability of three
Unicenter performance
management products for
mainframe data management." + ], + [ + "Reuters - The European Union
approved on\\Wednesday the
first biotech seeds for
planting and sale across\\EU
territory, flying in the face
of widespread
consumer\\resistance to
genetically modified (GMO)
crops and foods." + ], + [ + "p2pnet.net News:- A Microsoft
UK quot;WEIGHING THE COST OF
LINUX VS. WINDOWS? LET #39;S
REVIEW THE FACTS quot;
magazine ad has been nailed as
misleading by Britain #39;s
Advertising Standards
Authority (ASA)." + ], + [ + "The retail sector overall may
be reporting a sluggish start
to the season, but holiday
shoppers are scooping up tech
goods at a brisk pace -- and
they're scouring the Web for
bargains more than ever.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\" color=\"#666666\">&
lt;B>-washingtonpost.com<
;/B></FONT>" + ], + [ + "The team behind the Beagle 2
mission has unveiled its
design for a successor to the
British Mars lander." + ], + [ + "Survey points to popularity in
Europe, the Middle East and
Asia of receivers that skip
the pay TV and focus on free
programming." + ], + [ + "Linux publisher Red Hat Inc.
said Tuesday that information-
technology consulting firm
Unisys Corp. will begin
offering a business version of
the company #39;s open-source
operating system on its
servers." + ], + [ + "IBM's p5-575, a specialized
server geared for high-
performance computing, has
eight 1.9GHz Power5
processors." + ], + [ + "Reuters - British police said
on Monday they had\\charged a
man with sending hoax emails
to relatives of people\\missing
since the Asian tsunami,
saying their loved ones
had\\been confirmed dead." + ], + [ + "LOS ANGELES - On Sept. 1,
former secretary of
Agriculture Dan Glickman
replaced the legendary Jack
Valenti as president and CEO
of Hollywood #39;s trade
group, the Motion Picture
Association of America." + ], + [ + "The director of the National
Hurricane Center stays calm in
the midst of a storm, but
wants everyone in hurricane-
prone areas to get the message
from his media advisories:
Respect the storm's power and
make proper response plans." + ], + [ + "SAN FRANCISCO Several
California cities and
counties, including Los
Angeles and San Francisco, are
suing Microsoft for what could
amount to billions of dollars." + ], + [ + "Google plans to release a
version of its desktop search
tool for computers that run
Apple Computer #39;s Mac
operating system, Google #39;s
chief executive, Eric Schmidt,
said Friday." + ], + [ + "AMD : sicurezza e prestazioni
ottimali con il nuovo
processore mobile per notebook
leggeri e sottili; Acer Inc.
preme sull #39;acceleratore
con il nuovo notebook a
marchio Ferrari." + ], + [ + "Firefox use around the world
climbed 34 percent in the last
month, according to a report
published by Web analytics
company WebSideStory Monday." + ], + [ + "If a plastic card that gives
you credit for something you
don't want isn't your idea of
a great gift, you can put it
up for sale or swap." + ], + [ + "WASHINGTON Aug. 17, 2004
Scientists are planning to
take the pulse of the planet
and more in an effort to
improve weather forecasts,
predict energy needs months in
advance, anticipate disease
outbreaks and even tell
fishermen where the catch will
be ..." + ], + [ + "New communications technology
could spawn future products.
Could your purse tell you to
bring an umbrella?" + ], + [ + "SAP has won a \\$35 million
contract to install its human
resources software for the US
Postal Service. The NetWeaver-
based system will replace the
Post Office #39;s current
25-year-old legacy application" + ], + [ + "NewsFactor - For years,
companies large and small have
been convinced that if they
want the sophisticated
functionality of enterprise-
class software like ERP and
CRM systems, they must buy
pre-packaged applications.
And, to a large extent, that
remains true." + ], + [ + "Following in the footsteps of
the RIAA, the MPAA (Motion
Picture Association of
America) announced that they
have began filing lawsuits
against people who use peer-
to-peer software to download
copyrighted movies off the
Internet." + ], + [ + "MacCentral - At a special
music event featuring Bono and
The Edge from rock group U2
held on Tuesday, Apple took
the wraps off the iPod Photo,
a color iPod available in 40GB
or 60GB storage capacities.
The company also introduced
the iPod U2, a special edition
of Apple's 20GB player clad in
black, equipped with a red
Click Wheel and featuring
engraved U2 band member
signatures. The iPod Photo is
available immediately, and
Apple expects the iPod U2 to
ship in mid-November." + ], + [ + "Reuters - Oracle Corp is
likely to win clearance\\from
the European Commission for
its hostile #36;7.7
billion\\takeover of rival
software firm PeopleSoft Inc.,
a source close\\to the
situation said on Friday." + ], + [ + "IBM (Quote, Chart) said it
would spend a quarter of a
billion dollars over the next
year and a half to grow its
RFID (define) business." + ], + [ + "SPACE.com - NASA released one
of the best pictures ever made
of Saturn's moon Titan as the
Cassini spacecraft begins a
close-up inspection of the
satellite today. Cassini is
making the nearest flyby ever
of the smog-shrouded moon." + ], + [ + "ANNAPOLIS ROYAL, NS - Nova
Scotia Power officials
continued to keep the sluice
gates open at one of the
utility #39;s hydroelectric
plants Wednesday in hopes a
wayward whale would leave the
area and head for the open
waters of the Bay of Fundy." + ], + [ + "In fact, Larry Ellison
compares himself to the
warlord, according to
PeopleSoft's former CEO,
defending previous remarks he
made." + ], + [ + "You can empty your pockets of
change, take off your belt and
shoes and stick your keys in
the little tray. But if you've
had radiation therapy
recently, you still might set
off Homeland Security alarms." + ], + [ + "SEOUL, Oct 19 Asia Pulse -
LG.Philips LCD Co.
(KSE:034220), the world #39;s
second-largest maker of liquid
crystal display (LCD), said
Tuesday it has developed the
world #39;s largest organic
light emitting diode" + ], + [ + "Security experts warn of
banner ads with a bad attitude
--and a link to malicious
code. Also: Phishers, be gone." + ], + [ + "com October 15, 2004, 5:11 AM
PT. Wood paneling and chrome
made your dad #39;s station
wagon look like a million
bucks, and they might also be
just the ticket for Microsoft
#39;s fledgling" + ], + [ + "Computer Associates
International yesterday
reported a 6 increase in
revenue during its second
fiscal quarter, but posted a
\\$94 million loss after paying
to settle government
investigations into the
company, it said yesterday." + ], + [ + "PC World - Send your video
throughout your house--
wirelessly--with new gateways
and media adapters." + ], + [ + "Links to this week's topics
from search engine forums
across the web: New MSN Search
Goes LIVE in Beta - Microsoft
To Launch New Search Engine -
Google Launches 'Google
Advertising Professionals' -
Organic vs Paid Traffic ROI? -
Making Money With AdWords? -
Link Building 101" + ], + [ + "Eight conservation groups are
fighting the US government
over a plan to poison
thousands of prairie dogs in
the grasslands of South
Dakota, saying wildlife should
not take a backseat to
ranching interests." + ], + [ + "Siding with chip makers,
Microsoft said it won't charge
double for its per-processor
licenses when dual-core chips
come to market next year." + ], + [ + "IBM and the Spanish government
have introduced a new
supercomputer they hope will
be the most powerful in
Europe, and one of the 10 most
powerful in the world." + ], + [ + "Mozilla's new web browser is
smart, fast and user-friendly
while offering a slew of
advanced, customizable
functions. By Michelle Delio." + ], + [ + "originally offered on notebook
PCs -- to its Opteron 32- and
64-bit x86 processors for
server applications. The
technology will help servers
to run" + ], + [ + "MacCentral - After Apple
unveiled the iMac G5 in Paris
this week, Vice President of
Hardware Product Marketing
Greg Joswiak gave Macworld
editors a guided tour of the
desktop's new design. Among
the topics of conversation:
the iMac's cooling system, why
pre-installed Bluetooth
functionality and FireWire 800
were left out, and how this
new model fits in with Apple's
objectives." + ], + [ + "We #39;ve known about
quot;strained silicon quot;
for a while--but now there
#39;s a better way to do it.
Straining silicon improves
chip performance." + ], + [ + "This week, Sir Richard Branson
announced his new company,
Virgin Galactic, has the
rights to the first commercial
flights into space." + ], + [ + "71-inch HDTV comes with a home
stereo system and components
painted in 24-karat gold." + ], + [ + "MacCentral - RealNetworks Inc.
said on Tuesday that it has
sold more than a million songs
at its online music store
since slashing prices last
week as part of a limited-time
sale aimed at growing the user
base of its new digital media
software." + ], + [ + "With the presidential election
less than six weeks away,
activists and security experts
are ratcheting up concern over
the use of touch-screen
machines to cast votes.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-washingtonpost.com&l
t;/B></FONT>" + ], + [ + "NEW YORK, September 14 (New
Ratings) - Yahoo! Inc
(YHOO.NAS) has agreed to
acquire Musicmatch Inc, a
privately held digital music
software company, for about
\\$160 million in cash." + ], + [ + "Gov. Rod Blagojevich plans to
propose a ban Thursday on the
sale of violent and sexually
explicit video games to
minors, something other states
have tried with little
success." + ], + [ + "A cable channel plans to
resurrect each of the 1,230
regular-season games listed on
the league's defunct 2004-2005
schedule by setting them in
motion on a video game
console." + ], + [ + "SiliconValley.com - When
\"Halo\" became a smash video
game hit following Microsoft's
launch of the Xbox console in
2001, it was a no-brainer that
there would be a sequel to the
science fiction shoot-em-up." + ], + [ + "PARIS -- The city of Paris
intends to reduce its
dependence on software
suppliers with \"de facto
monopolies,\" but considers an
immediate switch of its 17,000
desktops to open source
software too costly, it said
Wednesday." + ], + [ + "The vast majority of consumers
are unaware that an Apple iPod
digital music player only
plays proprietary iTunes
files, while a smaller
majority agree that it is
within RealNetworks #39;
rights to develop a program
that will make its music files
compatible" + ], + [ + " PROVIDENCE, R.I. (Reuters) -
You change the oil in your car
every 5,000 miles or so. You
clean your house every week or
two. Your PC needs regular
maintenance as well --
especially if you're using
Windows and you spend a lot of
time on the Internet." + ], + [ + "The Motley Fool - Here's
something you don't see every
day -- the continuing brouhaha
between Oracle (Nasdaq: ORCL -
News) and PeopleSoft (Nasdaq:
PSFT - News) being a notable
exception. South Africa's
Harmony Gold Mining Company
(NYSE: HMY - News) has
announced a hostile takeover
bid to acquire fellow South
African miner Gold Fields
Limited (NYSE: GFI - News).
The transaction, if it takes
place, would be an all-stock
acquisition, with Harmony
issuing 1.275 new shares in
payment for each share of Gold
Fields. The deal would value
Gold Fields at more than
#36;8 billion. ..." + ], + [ + "SPACE.com - NASA's Mars \\rover
Opportunity nbsp;will back its
\\way out of a nbsp;crater it
has spent four months
exploring after reaching
terrain nbsp;that appears \\too
treacherous to tread. nbsp;" + ], + [ + "Sony Corp. announced Tuesday a
new 20 gigabyte digital music
player with MP3 support that
will be available in Great
Britain and Japan before
Christmas and elsewhere in
Europe in early 2005." + ], + [ + "TOKYO (AP) -- The electronics
and entertainment giant Sony
Corp. (SNE) is talking with
Wal-Mart Stores Inc..." + ], + [ + "After an unprecedented span of
just five days, SpaceShipOne
is ready for a return trip to
space on Monday, its final
flight to clinch a \\$10
million prize." + ], + [ + "Hi-tech monitoring of
livestock at pig farms could
help improve the animal growth
process and reduce costs." + ], + [ + "Cable amp; Wireless plc
(NYSE: CWP - message board) is
significantly ramping up its
investment in local loop
unbundling (LLU) in the UK,
and it plans to spend up to 85
million (\\$152." + ], + [ + "AFP - Microsoft said that it
had launched a new desktop
search tool that allows
personal computer users to
find documents or messages on
their PCs." + ], + [ + "The three largest computer
makers spearheaded a program
today designed to standardize
working conditions for their
non-US workers." + ], + [ + "Description: Illinois Gov. Rod
Blagojevich is backing state
legislation that would ban
sales or rentals of video
games with graphic sexual or
violent content to children
under 18." + ], + [ + "Are you bidding on keywords
through Overture's Precision
Match, Google's AdWords or
another pay-for-placement
service? If so, you're
eligible to participate in
their contextual advertising
programs." + ], + [ + "Nobel Laureate Wilkins, 87,
played an important role in
the discovery of the double
helix structure of DNA, the
molecule that carries our
quot;life code quot;,
Kazinform refers to BBC News." + ], + [ + "A number of signs point to
increasing demand for tech
workers, but not all the
clouds have been driven away." + ], + [ + "Microsoft Corp. (MSFT.O:
Quote, Profile, Research)
filed nine new lawsuits
against spammers who send
unsolicited e-mail, including
an e-mail marketing Web
hosting company, the world
#39;s largest software maker
said on Thursday." + ], + [ + "The rocket plane SpaceShipOne
is just one flight away from
claiming the Ansari X-Prize, a
\\$10m award designed to kick-
start private space travel." + ], + [ + "Reuters - Three American
scientists won the\\2004 Nobel
physics prize on Tuesday for
showing how tiny
quark\\particles interact,
helping to explain everything
from how a\\coin spins to how
the universe was built." + ], + [ + "AP - Sharp Electronics Corp.
plans to stop selling its
Linux-based handheld computer
in the United States, another
sign of the slowing market for
personal digital assistants." + ], + [ + "Reuters - Glaciers once held
up by a floating\\ice shelf off
Antarctica are now sliding off
into the sea --\\and they are
going fast, scientists said on
Tuesday." + ], + [ + "AP - Warning lights flashed
atop four police cars as the
caravan wound its way up the
driveway in a procession fit
for a presidential candidate.
At long last, Azy and Indah
had arrived. They even flew
through a hurricane to get
here." + ], + [ + "\\Female undergraduates work
harder and are more open-
minded than males, leading to
better results, say
scientists." + ], + [ + "The United Nations annual
World Robotics Survey predicts
the use of robots around the
home will surge seven-fold by
2007. The boom is expected to
be seen in robots that can mow
lawns and vacuum floors, among
other chores." + ], + [ + "Want to dive deep -- really
deep -- into the technical
literature about search
engines? Here's a road map to
some of the best web
information retrieval
resources available online." + ], + [ + "Reuters - Ancel Keys, a
pioneer in public health\\best
known for identifying the
connection between
a\\cholesterol-rich diet and
heart disease, has died." + ], + [ + "Reuters - T-Mobile USA, the
U.S. wireless unit\\of Deutsche
Telekom AG (DTEGn.DE), does
not expect to offer\\broadband
mobile data services for at
least the next two years,\\its
chief executive said on
Thursday." + ], + [ + "Verizon Communications is
stepping further into video as
a way to compete against cable
companies." + ], + [ + "Oracle Corp. plans to release
the latest version of its CRM
(customer relationship
management) applications
within the next two months, as
part of an ongoing update of
its E-Business Suite." + ], + [ + "Hosted CRM service provider
Salesforce.com took another
step forward last week in its
strategy to build an online
ecosystem of vendors that
offer software as a service." + ], + [ + "washingtonpost.com -
Technology giants IBM and
Hewlett-Packard are injecting
hundreds of millions of
dollars into radio-frequency
identification technology,
which aims to advance the
tracking of items from ho-hum
bar codes to smart tags packed
with data." + ], + [ + "The Norfolk Broads are on
their way to getting a clean
bill of ecological health
after a century of stagnation." + ], + [ + "For the second time this year,
an alliance of major Internet
providers - including Atlanta-
based EarthLink -iled a
coordinated group of lawsuits
aimed at stemming the flood of
online junk mail." + ], + [ + "Via Technologies has released
a version of the open-source
Xine media player that is
designed to take advantage of
hardware digital video
acceleration capabilities in
two of the company #39;s PC
chipsets, the CN400 and
CLE266." + ], + [ + "SCO Group has a plan to keep
itself fit enough to continue
its legal battles against
Linux and to develop its Unix-
on-Intel operating systems." + ], + [ + "New software helps corporate
travel managers track down
business travelers who
overspend. But it also poses a
dilemma for honest travelers
who are only trying to save
money." + ], + [ + "NATO Secretary-General Jaap de
Hoop Scheffer has called a
meeting of NATO states and
Russia on Tuesday to discuss
the siege of a school by
Chechen separatists in which
more than 335 people died, a
NATO spokesman said." + ], + [ + "AFP - Senior executives at
business software group
PeopleSoft unanimously
recommended that its
shareholders reject a 8.8
billion dollar takeover bid
from Oracle Corp, PeopleSoft
said in a statement Wednesday." + ], + [ + "Reuters - Neolithic people in
China may have\\been the first
in the world to make wine,
according to\\scientists who
have found the earliest
evidence of winemaking\\from
pottery shards dating from
7,000 BC in northern China." + ], + [ + " TOKYO (Reuters) - Electronics
conglomerate Sony Corp.
unveiled eight new flat-screen
televisions on Thursday in a
product push it hopes will
help it secure a leading 35
percent of the domestic
market in the key month of
December." + ], + [ + "Donald Halsted, one target of
a class-action suit alleging
financial improprieties at
bankrupt Polaroid, officially
becomes CFO." + ], + [ + "The 7710 model features a
touch screen, pen input, a
digital camera, an Internet
browser, a radio, video
playback and streaming and
recording capabilities, the
company said." + ], + [ + "A top Red Hat executive has
attacked the open-source
credentials of its sometime
business partner Sun
Microsystems. In a Web log
posting Thursday, Michael
Tiemann, Red Hat #39;s vice
president of open-source
affairs" + ], + [ + "MySQL developers turn to an
unlikely source for database
tool: Microsoft. Also: SGI
visualizes Linux, and the
return of Java veteran Kim
Polese." + ], + [ + "Dell Inc. said its profit
surged 25 percent in the third
quarter as the world's largest
personal computer maker posted
record sales due to rising
technology spending in the
corporate and government
sectors in the United States
and abroad." + ], + [ + "Reuters - SBC Communications
said on Monday it\\would offer
a television set-top box that
can handle music,\\photos and
Internet downloads, part of
SBC's efforts to expand\\into
home entertainment." + ], + [ + " WASHINGTON (Reuters) - Hopes
-- and worries -- that U.S.
regulators will soon end the
ban on using wireless phones
during U.S. commercial flights
are likely at least a year or
two early, government
officials and analysts say." + ], + [ + "After a year of pilots and
trials, Siebel Systems jumped
with both feet into the SMB
market Tuesday, announcing a
new approach to offer Siebel
Professional CRM applications
to SMBs (small and midsize
businesses) -- companies with
revenues up to about \\$500
million." + ], + [ + "Intel won't release a 4-GHz
version of its flagship
Pentium 4 product, having
decided instead to realign its
engineers around the company's
new design priorities, an
Intel spokesman said today.
\\\\" + ], + [ + "AP - A Soyuz spacecraft
carrying two Russians and an
American rocketed closer
Friday to its docking with the
international space station,
where the current three-man
crew made final departure
preparations." + ], + [ + "Scientists have manipulated
carbon atoms to create a
material that could be used to
create light-based, versus
electronic, switches. The
material could lead to a
supercharged Internet based
entirely on light, scientists
say." + ], + [ + "LOS ANGELES (Reuters)
Qualcomm has dropped an \\$18
million claim for monetary
damages from rival Texas
Instruments for publicly
discussing terms of a
licensing pact, a TI
spokeswoman confirmed Tuesday." + ], + [ + "Hewlett-Packard is the latest
IT vendor to try blogging. But
analysts wonder if the weblog
trend is the 21st century
equivalent of CB radios, which
made a big splash in the 1970s
before fading." + ], + [ + "The scientists behind Dolly
the sheep apply for a license
to clone human embryos. They
want to take stem cells from
the embryos to study Lou
Gehrig's disease." + ], + [ + "Some of the silly tunes
Japanese pay to download to
use as the ring tone for their
mobile phones sure have their
knockers, but it #39;s for
precisely that reason that a
well-known counselor is raking
it in at the moment, according
to Shukan Gendai (10/2)." + ], + [ + "IBM announced yesterday that
it will invest US\\$250 million
(S\\$425 million) over the next
five years and employ 1,000
people in a new business unit
to support products and
services related to sensor
networks." + ], + [ + "AP - Microsoft Corp. goes into
round two Friday of its battle
to get the European Union's
sweeping antitrust ruling
lifted having told a judge
that it had been prepared
during settlement talks to
share more software code with
its rivals than the EU
ultimately demanded." + ], + [ + "AP - Sears, Roebuck and Co.,
which has successfully sold
its tools and appliances on
the Web, is counting on having
the same magic with bedspreads
and sweaters, thanks in part
to expertise gained by its
purchase of Lands' End Inc." + ], + [ + "Austin police are working with
overseas officials to bring
charges against an English man
for sexual assault of a child,
a second-degree felony." + ], + [ + "San Francisco
developer/publisher lands
coveted Paramount sci-fi
license, \\$6.5 million in
funding on same day. Although
it is less than two years old,
Perpetual Entertainment has
acquired one of the most
coveted sci-fi licenses on the
market." + ], + [ + "By KELLY WIESE JEFFERSON
CITY, Mo. (AP) -- Missouri
will allow members of the
military stationed overseas to
return absentee ballots via
e-mail, raising concerns from
Internet security experts
about fraud and ballot
secrecy..." + ], + [ + "Avis Europe PLC has dumped a
new ERP system based on
software from PeopleSoft Inc.
before it was even rolled out,
citing substantial delays and
higher-than-expected costs." + ], + [ + "Yahoo #39;s (Quote, Chart)
public embrace of the RSS
content syndication format
took a major leap forward with
the release of a revamped My
Yahoo portal seeking to
introduce the technology to
mainstream consumers." + ], + [ + "TOKYO (AP) -- Japanese
electronics and entertainment
giant Sony Corp. (SNE) plans
to begin selling a camcorder
designed for consumers that
takes video at digital high-
definition quality and is
being priced at about
\\$3,600..." + ], + [ + "In a meeting of the cinematic
with the scientific, Hollywood
helicopter stunt pilots will
try to snatch a returning NASA
space probe out of the air
before it hits the ground." + ], + [ + "Legend has it (incorrectly, it
seems) that infamous bank
robber Willie Sutton, when
asked why banks were his
favorite target, responded,
quot;Because that #39;s where
the money is." + ], + [ + "Pfizer, GlaxoSmithKline and
Purdue Pharma are the first
drugmakers willing to take the
plunge and use radio frequency
identification technology to
protect their US drug supply
chains from counterfeiters." + ], + [ + "Search any fee-based digital
music service for the best-
loved musical artists of the
20th century and most of the
expected names show up." + ], + [ + "America Online Inc. is
packaging new features to
combat viruses, spam and
spyware in response to growing
online security threats.
Subscribers will be able to
get the free tools" + ], + [ + "CAPE CANAVERAL-- NASA aims to
launch its first post-Columbia
shuttle mission during a
shortened nine-day window
March, and failure to do so
likely would delay a planned
return to flight until at
least May." + ], + [ + "The Chinese city of Beijing
has cancelled an order for
Microsoft software, apparently
bowing to protectionist
sentiment. The deal has come
under fire in China, which is
trying to build a domestic
software industry." + ], + [ + "Apple says it will deliver its
iTunes music service to more
European countries next month.
Corroborating several reports
in recent months, Reuters is
reporting today that Apple
Computer is planning the next" + ], + [ + "Reuters - Motorola Inc., the
world's\\second-largest mobile
phone maker, said on Tuesday
it expects\\to sustain strong
sales growth in the second
half of 2004\\thanks to new
handsets with innovative
designs and features." + ], + [ + "PRAGUE, Czech Republic --
Eugene Cernan, the last man to
walk on the moon during the
final Apollo landing, said
Thursday he doesn't expect
space tourism to become
reality in the near future,
despite a strong demand.
Cernan, now 70, who was
commander of NASA's Apollo 17
mission and set foot on the
lunar surface in December 1972
during his third space flight,
acknowledged that \"there are
many people interested in
space tourism.\" But the
former astronaut said he
believed \"we are a long way
away from the day when we can
send a bus of tourists to the
moon.\" He spoke to reporters
before being awarded a medal
by the Czech Academy of
Sciences for his contribution
to science..." + ], + [ + "Never shy about entering a
market late, Microsoft Corp.
is planning to open the
virtual doors of its long-
planned Internet music store
next week. <FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</B></FONT>" + ], + [ + "AP - On his first birthday
Thursday, giant panda cub Mei
Sheng delighted visitors by
playing for the first time in
snow delivered to him at the
San Diego Zoo. He also sat on
his ice cake, wrestled with
his mom, got his coat
incredibly dirty, and didn't
read any of the more than 700
birthday wishes sent him via
e-mail from as far away as
Ireland and Argentina." + ], + [ + "AP - Researchers put a
satellite tracking device on a
15-foot shark that appeared to
be lost in shallow water off
Cape Cod, the first time a
great white has been tagged
that way in the Atlantic." + ], + [ + "The Windows Future Storage
(WinFS) technology that got
cut out of Windows
quot;Longhorn quot; is in
serious trouble, and not just
the hot water a feature might
encounter for missing its
intended production vehicle." + ], + [ + "washingtonpost.com - Let the
games begin. Not the Olympics
again, but the all-out battle
between video game giants Sony
Corp. and Nintendo Co. Ltd.
The two Japanese companies are
rolling out new gaming
consoles, but Nintendo has
beaten Sony to the punch by
announcing an earlier launch
date for its new hand-held
game player." + ], + [ + "AP - Echoing what NASA
officials said a day earlier,
a Russian space official on
Friday said the two-man crew
on the international space
station could be forced to
return to Earth if a planned
resupply flight cannot reach
them with food supplies later
this month." + ], + [ + "InfoWorld - SANTA CLARA,
CALIF. -- Accommodating large
patch sets in Linux is
expected to mean forking off
of the 2.7 version of the
platform to accommodate these
changes, according to Andrew
Morton, lead maintainer of the
Linux kernel for Open Source
Development Labs (OSDL)." + ], + [ + "AMSTERDAM The mobile phone
giants Vodafone and Nokia
teamed up on Thursday to
simplify cellphone software
written with the Java computer
language." + ], + [ + "The overall Linux market is
far larger than previous
estimates show, a new study
says. In an analysis of the
Linux market released late
Tuesday, market research firm
IDC estimated that the Linux
market -- including" + ], + [ + "By PAUL ELIAS SAN FRANCISCO
(AP) -- Several California
cities and counties, including
San Francisco and Los Angeles,
sued Microsoft Corp. (MSFT) on
Friday, accusing the software
giant of illegally charging
inflated prices for its
products because of monopoly
control of the personal
computer operating system
market..." + ], + [ + "Public opinion of the database
giant sinks to 12-year low, a
new report indicates." + ], + [ + "For spammers, it #39;s been a
summer of love. Two newly
issued reports tracking the
circulation of unsolicited
e-mails say pornographic spam
dominated this summer, nearly
all of it originating from
Internet addresses in North
America." + ], + [ + "Microsoft portrayed its
Longhorn decision as a
necessary winnowing to hit the
2006 timetable. The
announcement on Friday,
Microsoft executives insisted,
did not point to a setback in
software" + ], + [ + "A problem in the Service Pack
2 update for Windows XP may
keep owners of AMD-based
computers from using the long-
awaited security package,
according to Microsoft." + ], + [ + "Five years ago, running a
telephone company was an
immensely profitable
proposition. Since then, those
profits have inexorably
declined, and now that decline
has taken another gut-
wrenching dip." + ], + [ + "NEW YORK - The litigious
Recording Industry Association
of America (RIAA) is involved
in another legal dispute with
a P-to-P (peer-to-peer)
technology maker, but this
time, the RIAA is on defense.
Altnet Inc. filed a lawsuit
Wednesday accusing the RIAA
and several of its partners of
infringing an Altnet patent
covering technology for
identifying requested files on
a P-to-P network." + ], + [ + "For two weeks before MTV
debuted U2 #39;s video for the
new single quot;Vertigo,
quot; fans had a chance to see
the band perform the song on
TV -- in an iPod commercial." + ], + [ + "Oct. 26, 2004 - The US-
European spacecraft Cassini-
Huygens on Tuesday made a
historic flyby of Titan,
Saturn #39;s largest moon,
passing so low as to almost
touch the fringes of its
atmosphere." + ], + [ + "Reuters - A volcano in central
Japan sent smoke and\\ash high
into the sky and spat out
molten rock as it erupted\\for
a fourth straight day on
Friday, but experts said the
peak\\appeared to be quieting
slightly." + ], + [ + "Shares of Google Inc. made
their market debut on Thursday
and quickly traded up 19
percent at \\$101.28. The Web
search company #39;s initial
public offering priced at \\$85" + ], + [ + "Reuters - Global warming is
melting\\Ecuador's cherished
mountain glaciers and could
cause several\\of them to
disappear over the next two
decades, Ecuadorean and\\French
scientists said on Wednesday." + ], + [ + "Rather than tell you, Dan
Kranzler chooses instead to
show you how he turned Mforma
into a worldwide publisher of
video games, ringtones and
other hot downloads for mobile
phones." + ], + [ + "Not being part of a culture
with a highly developed
language, could limit your
thoughts, at least as far as
numbers are concerned, reveals
a new study conducted by a
psychologist at the Columbia
University in New York." + ], + [ + "CAMBRIDGE, Mass. A native of
Red Oak, Iowa, who was a
pioneer in astronomy who
proposed the quot;dirty
snowball quot; theory for the
substance of comets, has died." + ], + [ + "A Portuguese-sounding version
of the virus has appeared in
the wild. Be wary of mail from
Manaus." + ], + [ + "Reuters - Hunters soon may be
able to sit at\\their computers
and blast away at animals on a
Texas ranch via\\the Internet,
a prospect that has state
wildlife officials up\\in arms." + ], + [ + "The Bedminster-based company
yesterday said it was pushing
into 21 new markets with the
service, AT amp;T CallVantage,
and extending an introductory
rate offer until Sept. 30. In
addition, the company is
offering in-home installation
of up to five ..." + ], + [ + "Samsung Electronics Co., Ltd.
has developed a new LCD
(liquid crystal display)
technology that builds a touch
screen into the display, a
development that could lead to
thinner and cheaper display
panels for mobile phones, the
company said Tuesday." + ], + [ + "The message against illegally
copying CDs for uses such as
in file-sharing over the
Internet has widely sunk in,
said the company in it #39;s
recent announcement to drop
the Copy-Control program." + ], + [ + "Reuters - California will
become hotter and\\drier by the
end of the century, menacing
the valuable wine and\\dairy
industries, even if dramatic
steps are taken to curb\\global
warming, researchers said on
Monday." + ], + [ + "IBM said Monday that it won a
500 million (AUD\\$1.25
billion), seven-year services
contract to help move UK bank
Lloyds TBS from its
traditional voice
infrastructure to a converged
voice and data network." + ], + [ + "Space shuttle astronauts will
fly next year without the
ability to repair in orbit the
type of damage that destroyed
the Columbia vehicle in
February 2003." + ], + [ + "By cutting WinFS from Longhorn
and indefinitely delaying the
storage system, Microsoft
Corp. has also again delayed
the Microsoft Business
Framework (MBF), a new Windows
programming layer that is
closely tied to WinFS." + ], + [ + "Samsung's new SPH-V5400 mobile
phone sports a built-in
1-inch, 1.5-gigabyte hard disk
that can store about 15 times
more data than conventional
handsets, Samsung said." + ], + [ + "Vendor says it #39;s
developing standards-based
servers in various form
factors for the telecom
market. By Darrell Dunn.
Hewlett-Packard on Thursday
unveiled plans to create a
portfolio of products and
services" + ], + [ + "Ziff Davis - The company this
week will unveil more programs
and technologies designed to
ease users of its high-end
servers onto its Integrity
line, which uses Intel's
64-bit Itanium processor." + ], + [ + "The Mac maker says it will
replace about 28,000 batteries
in one model of PowerBook G4
and tells people to stop using
the notebook." + ], + [ + "Millions of casual US anglers
are having are larger than
appreciated impact on sea fish
stocks, scientists claim." + ], + [ + "Microsoft Xbox Live traffic on
service provider networks
quadrupled following the
November 9th launch of Halo-II
-- which set entertainment
industry records by selling
2.4-million units in the US
and Canada on the first day of
availability, driving cash" + ], + [ + "Lawyers in a California class
action suit against Microsoft
will get less than half the
payout they had hoped for. A
judge in San Francisco ruled
that the attorneys will
collect only \\$112." + ], + [ + "Google Browser May Become
Reality\\\\There has been much
fanfare in the Mozilla fan
camps about the possibility of
Google using Mozilla browser
technology to produce a
GBrowser - the Google Browser.
Over the past two weeks, the
news and speculation has
escalated to the point where
even Google itself is ..." + ], + [ + "Hewlett-Packard has joined
with Brocade to integrate
Brocade #39;s storage area
network switching technology
into HP Bladesystem servers to
reduce the amount of fabric
infrastructure needed in a
datacentre." + ], + [ + "The US Senate Commerce
Committee on Wednesday
approved a measure that would
provide up to \\$1 billion to
ensure consumers can still
watch television when
broadcasters switch to new,
crisp digital signals." + ], + [ + "Reuters - Internet stocks
are\\as volatile as ever, with
growth-starved investors
flocking to\\the sector in the
hope they've bought shares in
the next online\\blue chip." + ], + [ + "NASA has released an inventory
of the scientific devices to
be put on board the Mars
Science Laboratory rover
scheduled to land on the
surface of Mars in 2009, NASAs
news release reads." + ], + [ + "The U.S. Congress needs to
invest more in the U.S.
education system and do more
to encourage broadband
adoption, the chief executive
of Cisco said Wednesday.<p&
gt;ADVERTISEMENT</p><
p><img src=\"http://ad.do
ubleclick.net/ad/idg.us.ifw.ge
neral/sbcspotrssfeed;sz=1x1;or
d=200301151450?\" width=\"1\"
height=\"1\"
border=\"0\"/><a href=\"htt
p://ad.doubleclick.net/clk;922
8975;9651165;a?http://www.info
world.com/spotlights/sbc/main.
html?lpid0103035400730000idlp\"
>SBC Case Study: Crate Ba
rrel</a><br/>What
sold them on improving their
network? A system that could
cut management costs from the
get-go. Find out
more.</p>" + ], + [ + "Thirty-two countries and
regions will participate the
Fifth China International
Aviation and Aerospace
Exhibition, opening Nov. 1 in
Zhuhai, a city in south China
#39;s Guangdong Province." + ], + [ + "p2pnet.net News:- Virgin
Electronics has joined the mp3
race with a \\$250, five gig
player which also handles
Microsoft #39;s WMA format." + ], + [ + "Microsoft has suspended the
beta testing of the next
version of its MSN Messenger
client because of a potential
security problem, a company
spokeswoman said Wednesday." + ], + [ + "AP - Business software maker
Oracle Corp. attacked the
credibility and motives of
PeopleSoft Inc.'s board of
directors Monday, hoping to
rally investor support as the
17-month takeover battle
between the bitter business
software rivals nears a
climactic showdown." + ], + [ + "A new worm has been discovered
in the wild that #39;s not
just settling for invading
users #39; PCs--it wants to
invade their homes too." + ], + [ + "Researchers have for the first
time established the existence
of odd-parity superconductors,
materials that can carry
electric current without any
resistance." + ], + [ + "BRUSSELS, Belgium (AP) --
European antitrust regulators
said Monday they have extended
their review of a deal between
Microsoft Corp. (MSFT) and
Time Warner Inc..." + ], + [ + "Fans who can't get enough of
\"The Apprentice\" can visit a
new companion Web site each
week and watch an extra 40
minutes of video not broadcast
on the Thursday
show.<br><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</b></font>" + ], + [ + "An adult Web site publisher is
suing Google, saying the
search engine company made it
easier for users to see the
site #39;s copyrighted nude
photographs without paying or
gaining access through the
proper channels." + ], + [ + "A Washington-based public
opinion firm has released the
results of an election day
survey of Nevada voters
showing 81 support for the
issuance of paper receipts
when votes are cast
electronically." + ], + [ + "NAPSTER creator SHAWN FANNING
has revealed his plans for a
new licensed file-sharing
service with an almost
unlimited selection of tracks." + ], + [ + "Two separate studies by U.S.
researchers find that super
drug-resistant strains of
tuberculosis are at the
tipping point of a global
epidemic, and only small
changes could help them spread
quickly." + ], + [ + "Dell cut prices on some
servers and PCs by as much as
22 percent because it #39;s
paying less for parts. The
company will pass the savings
on components such as memory
and liquid crystal displays" + ], + [ + "WASHINGTON: The European-
American Cassini-Huygens space
probe has detected traces of
ice flowing on the surface of
Saturn #39;s largest moon,
Titan, suggesting the
existence of an ice volcano,
NASA said Tuesday." + ], + [ + "All ISS systems continue to
function nominally, except
those noted previously or
below. Day 7 of joint
Exp.9/Exp.10 operations and
last full day before 8S
undocking." + ], + [ + " quot;Magic can happen. quot;
Sirius Satellite Radio
(nasdaq: SIRI - news - people
) may have signed Howard Stern
and the men #39;s NCAA
basketball tournaments, but XM
Satellite Radio (nasdaq: XMSR
- news - people ) has its
sights on your cell phone." + ], + [ + "Trick-or-treaters can expect
an early Halloween treat on
Wednesday night, when a total
lunar eclipse makes the moon
look like a glowing pumpkin." + ], + [ + "Sifting through millions of
documents to locate a valuable
few is tedious enough, but
what happens when those files
are scattered across different
repositories?" + ], + [ + "NASA #39;s Mars rovers have
uncovered more tantalizing
evidence of a watery past on
the Red Planet, scientists
said Wednesday. And the
rovers, Spirit and
Opportunity, are continuing to
do their jobs months after
they were expected to ..." + ], + [ + "Intel Chief Technology Officer
Pat Gelsinger said on
Thursday, Sept. 9, that the
Internet needed to be upgraded
in order to deal with problems
that will become real issues
soon." + ], + [ + " LONDON (Reuters) - Television
junkies of the world, get
ready for \"Friends,\" \"Big
Brother\" and \"The Simpsons\" to
phone home." + ], + [ + "I #39;M FEELING a little bit
better about the hundreds of
junk e-mails I get every day
now that I #39;ve read that
someone else has much bigger
e-mail troubles." + ], + [ + "Microsoft's antispam Sender ID
technology continues to get
the cold shoulder. Now AOL
adds its voice to a growing
chorus of businesses and
organizations shunning the
proprietary e-mail
authentication system." + ], + [ + "Through the World Community
Grid, your computer could help
address the world's health and
social problems." + ], + [ + "A planned component for
Microsoft #39;s next version
of Windows is causing
consternation among antivirus
experts, who say that the new
module, a scripting platform
called Microsoft Shell, could
give birth to a whole new
generation of viruses and
remotely" + ], + [ + "SAN JOSE, California Yahoo
will likely have a tough time
getting American courts to
intervene in a dispute over
the sale of Nazi memorabilia
in France after a US appeals
court ruling." + ], + [ + "eBay Style director Constance
White joins Post fashion
editor Robin Givhan and host
Janet Bennett to discuss how
to find trends and bargains
and pull together a wardrobe
online." + ], + [ + "With a sudden shudder, the
ground collapsed and the pipe
pushed upward, buckling into a
humped shape as Cornell
University scientists produced
the first simulated earthquake" + ], + [ + "Microsoft (Quote, Chart) has
fired another salvo in its
ongoing spam battle, this time
against porn peddlers who don
#39;t keep their smut inside
the digital equivalent of a
quot;Brown Paper Wrapper." + ], + [ + " SEATTLE (Reuters) - The next
version of the Windows
operating system, Microsoft
Corp.'s <A HREF=\"http://www
.reuters.co.uk/financeQuoteLoo
kup.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
flagship product, will ship
in 2006, the world's largest
software maker said on
Friday." + ], + [ + "Fossil remains of the oldest
and smallest known ancestor of
Tyrannosaurus rex, the world
#39;s favorite ferocious
dinosaur, have been discovered
in China with evidence that
its body was cloaked in downy
quot;protofeathers." + ], + [ + "People fishing for sport are
doing far more damage to US
marine fish stocks than anyone
thought, accounting for nearly
a quarter of the" + ], + [ + "This particular index is
produced by the University of
Michigan Business School, in
partnership with the American
Society for Quality and CFI
Group, and is supported in
part by ForeSee Results" + ], + [ + "PC World - Symantec, McAfee
hope raising virus-definition
fees will move users to\\
suites." + ], + [ + "By byron kho. A consortium of
movie and record companies
joined forces on Friday to
request that the US Supreme
Court take another look at
peer-to-peer file-sharing
programs." + ], + [ + "LONDON - Wild capuchin monkeys
can understand cause and
effect well enough to use
rocks to dig for food,
scientists have found.
Capuchin monkeys often use
tools and solve problems in
captivity and sometimes" + ], + [ + "The key to hidden treasure
lies in your handheld GPS
unit. GPS-based \"geocaching\"
is a high-tech sport being
played by thousands of people
across the globe." + ], + [ + "The U.S. information tech
sector lost 403,300 jobs
between March 2001 and April
2004, and the market for tech
workers remains bleak,
according to a new report." + ], + [ + "com September 30, 2004, 11:11
AM PT. SanDisk announced
Thursday increased capacities
for several different flash
memory cards. The Sunnyvale,
Calif." + ], + [ + "roundup Plus: Tech firms rally
against copyright bill...Apple
.Mac customers suffer e-mail
glitches...Alvarion expands
wireless broadband in China." + ], + [ + "Red Hat is acquiring security
and authentication tools from
Netscape Security Solutions to
bolster its software arsenal.
Red Hat #39;s CEO and chairman
Matthew Szulik spoke about the
future strategy of the Linux
supplier." + ], + [ + "Global Web portal Yahoo! Inc.
Wednesday night made available
a beta version of a new search
service for videos. Called
Yahoo! Video Search, the
search engine crawls the Web
for different types of media
files" + ], + [ + "Interactive posters at 25
underground stations are
helping Londoners travel
safely over Christmas." + ], + [ + "According to Swiss
authorities, history was made
Sunday when 2723 people in
four communities in canton
Geneva, Switzerland, voted
online in a national federal
referendum." + ], + [ + " Nextel was the big story in
telecommunications yesterday,
thanks to the Reston company's
mega-merger with Sprint, but
the future of wireless may be
percolating in dozens of
Washington area start-ups." + ], + [ + "I have been anticipating this
day like a child waits for
Christmas. Today, PalmOne
introduces the Treo 650, the
answer to my quot;what smart
phone will I buy?" + ], + [ + "Wikipedia has surprised Web
watchers by growing fast and
maturing into one of the most
popular reference sites." + ], + [ + "It only takes 20 minutes on
the Internet for an
unprotected computer running
Microsoft Windows to be taken
over by a hacker. Any personal
or financial information
stored" + ], + [ + "Prices for flash memory cards
-- the little modules used by
digital cameras, handheld
organizers, MP3 players and
cell phones to store pictures,
music and other data -- are
headed down -- way down. Past
trends suggest that prices
will drop 35 percent a year,
but industry analysts think
that rate will be more like 40
or 50 percent this year and
next, due to more
manufacturers entering the
market." + ], + [ + "While the US software giant
Microsoft has achieved almost
sweeping victories in
government procurement
projects in several Chinese
provinces and municipalities,
the process" + ], + [ + "Microsoft Corp. has delayed
automated distribution of a
major security upgrade to its
Windows XP Professional
operating system, citing a
desire to give companies more
time to test it." + ], + [ + "Gateway computers will be more
widely available at Office
Depot, in the PC maker #39;s
latest move to broaden
distribution at retail stores
since acquiring rival
eMachines this year." + ], + [ + "Intel Corp. is refreshing its
64-bit Itanium 2 processor
line with six new chips based
on the Madison core. The new
processors represent the last
single-core Itanium chips that
the Santa Clara, Calif." + ], + [ + "For the first time, broadband
connections are reaching more
than half (51 percent) of the
American online population at
home, according to measurement
taken in July by
Nielsen/NetRatings, a
Milpitas-based Internet
audience measurement and
research ..." + ], + [ + "Consider the New World of
Information - stuff that,
unlike the paper days of the
past, doesn't always
physically exist. You've got
notes, scrawlings and
snippets, Web graphics, photos
and sounds. Stuff needs to be
cut, pasted, highlighted,
annotated, crossed out,
dragged away. And, as Ross
Perot used to say (or maybe it
was Dana Carvey impersonating
him), don't forget the graphs
and charts." + ], + [ + "Major Hollywood studios on
Tuesday announced scores of
lawsuits against computer
server operators worldwide,
including eDonkey, BitTorrent
and DirectConnect networks,
for allowing trading of
pirated movies." + ], + [ + "But will Wi-Fi, high-
definition broadcasts, mobile
messaging and other
enhancements improve the game,
or wreck it?\\<br />
Photos of tech-friendly parks\\" + ], + [ + "On-demand viewing isn't just
for TiVo owners anymore.
Television over internet
protocol, or TVIP, offers
custom programming over
standard copper wires." + ], + [ + "PalmSource #39;s European
developer conference is going
on now in Germany, and this
company is using this
opportunity to show off Palm
OS Cobalt 6.1, the latest
version of its operating
system." + ], + [ + "Speaking to members of the
Massachusetts Software
Council, Microsoft CEO Steve
Ballmer touted a bright future
for technology but warned his
listeners to think twice
before adopting open-source
products like Linux." + ], + [ + "MIAMI - The Trillian instant
messaging (IM) application
will feature several
enhancements in its upcoming
version 3.0, including new
video and audio chat
capabilities, enhanced IM
session logs and integration
with the Wikipedia online
encyclopedia, according to
information posted Friday on
the product developer's Web
site." + ], + [ + "Reuters - Online DVD rental
service Netflix Inc.\\and TiVo
Inc., maker of a digital video
recorder, on Thursday\\said
they have agreed to develop a
joint entertainment\\offering,
driving shares of both
companies higher." + ], + [ + "THIS YULE is all about console
supply and there #39;s
precious little units around,
it has emerged. Nintendo has
announced that it is going to
ship another 400,000 units of
its DS console to the United
States to meet the shortfall
there." + ], + [ + "Annual global semiconductor
sales growth will probably
fall by half in 2005 and
memory chip sales could
collapse as a supply glut saps
prices, world-leading memory
chip maker Samsung Electronics
said on Monday." + ], + [ + "NEW YORK - Traditional phone
systems may be going the way
of the Pony Express. Voice-
over-Internet Protocol,
technology that allows users
to make and receive phone
calls using the Internet, is
giving the old circuit-
switched system a run for its
money." + ], + [ + "AP - The first U.S. cases of
the fungus soybean rust, which
hinders plant growth and
drastically cuts crop
production, were found at two
research sites in Louisiana,
officials said Wednesday." + ], + [ + " quot;There #39;s no way
anyone would hire them to
fight viruses, quot; said
Sophos security analyst Gregg
Mastoras. quot;For one, no
security firm could maintain
its reputation by employing
hackers." + ], + [ + "Symantec has revoked its
decision to blacklist a
program that allows Web
surfers in China to browse
government-blocked Web sites.
The move follows reports that
the firm labelled the Freegate
program, which" + ], + [ + "Microsoft has signed a pact to
work with the United Nations
Educational, Scientific and
Cultural Organization (UNESCO)
to increase computer use,
Internet access and teacher
training in developing
countries." + ], + [ + "roundup Plus: Microsoft tests
Windows Marketplace...Nortel
delays financials
again...Microsoft updates
SharePoint." + ], + [ + "OPEN SOURCE champion Microsoft
is expanding its programme to
give government organisations
some of its source code. In a
communique from the lair of
the Vole, in Redmond,
spinsters have said that
Microsoft" + ], + [ + "WASHINGTON Can you always tell
when somebody #39;s lying? If
so, you might be a wizard of
the fib. A California
psychology professor says
there #39;s a tiny subculture
of people that can pick out a
lie nearly every time they
hear one." + ], + [ + "Type design was once the
province of skilled artisans.
With the help of new computer
programs, neophytes have
flooded the Internet with
their creations." + ], + [ + "RCN Inc., co-owner of
Starpower Communications LLC,
the Washington area
television, telephone and
Internet provider, filed a
plan of reorganization
yesterday that it said puts
the company" + ], + [ + "<strong>Letters</stro
ng> Reports of demise
premature" + ], + [ + "Intel, the world #39;s largest
chip maker, scrapped a plan
Thursday to enter the digital
television chip business,
marking a major retreat from
its push into consumer
electronics." + ], + [ + "The US is the originator of
over 42 of the worlds
unsolicited commercial e-mail,
making it the worst offender
in a league table of the top
12 spam producing countries
published yesterday by anti-
virus firm Sophos." + ], + [ + "Intel is drawing the curtain
on some of its future research
projects to continue making
transistors smaller, faster,
and less power-hungry out as
far as 2020." + ], + [ + "Plus: Experts fear Check 21
could lead to massive bank
fraud." + ], + [ + "By SIOBHAN McDONOUGH
WASHINGTON (AP) -- Fewer
American youths are using
marijuana, LSD and Ecstasy,
but more are abusing
prescription drugs, says a
government report released
Thursday. The 2003 National
Survey on Drug Use and Health
also found youths and young
adults are more aware of the
risks of using pot once a
month or more frequently..." + ], + [ + "A TNO engineer prepares to
start capturing images for the
world's biggest digital photo." + ], + [ + "PalmOne is aiming to sharpen
up its image with the launch
of the Treo 650 on Monday. As
previously reported, the smart
phone update has a higher-
resolution screen and a faster
processor than the previous
top-of-the-line model, the
Treo 600." + ], + [ + "kinrowan writes quot;MIT,
inventor of Kerberos, has
announced a pair of
vulnerabities in the software
that will allow an attacker to
either execute a DOS attack or
execute code on the machine." + ], + [ + "Reuters - Former Pink Floyd
mainman Roger\\Waters released
two new songs, both inspired
by the U.S.-led\\invasion of
Iraq, via online download
outlets Tuesday." + ], + [ + "NASA has been working on a
test flight project for the
last few years involving
hypersonic flight. Hypersonic
flight is fight at speeds
greater than Mach 5, or five
times the speed of sound." + ], + [ + "AP - Microsoft Corp. announced
Wednesday that it would offer
a low-cost, localized version
of its Windows XP operating
system in India to tap the
large market potential in this
country of 1 billion people,
most of whom do not speak
English." + ], + [ + "AP - Utah State University has
secured a #36;40 million
contract with NASA to build an
orbiting telescope that will
examine galaxies and try to
find new stars." + ], + [ + "I spend anywhere from three to
eight hours every week
sweating along with a motley
crew of local misfits,
shelving, sorting, and hauling
ton after ton of written
matter in a rowhouse basement
in Baltimore. We have no heat
nor air conditioning, but
still, every week, we come and
work. Volunteer night is
Wednesday, but many of us also
work on the weekends, when
we're open to the public.
There are times when we're
freezing and we have to wear
coats and gloves inside,
making handling books somewhat
tricky; other times, we're all
soaked with sweat, since it's
90 degrees out and the
basement is thick with bodies.
One learns to forget about
personal space when working at
The Book Thing, since you can
scarcely breathe without
bumping into someone, and we
are all so accustomed to
having to scrape by each other
that most of us no longer
bother to say \"excuse me\"
unless some particularly
dramatic brushing occurs." + ], + [ + "Alarmed by software glitches,
security threats and computer
crashes with ATM-like voting
machines, officials from
Washington, D.C., to
California are considering an
alternative from an unlikely
place: Nevada." + ], + [ + "More Americans are quitting
their jobs and taking the risk
of starting a business despite
a still-lackluster job market." + ], + [ + "SPACE.com - With nbsp;food
stores nbsp;running low, the
two \\astronauts living aboard
the International Space
Station (ISS) are cutting back
\\their meal intake and
awaiting a critical cargo
nbsp;delivery expected to
arrive \\on Dec. 25." + ], + [ + "Virgin Electronics hopes its
slim Virgin Player, which
debuts today and is smaller
than a deck of cards, will
rise as a lead competitor to
Apple's iPod." + ], + [ + "Researchers train a monkey to
feed itself by guiding a
mechanical arm with its mind.
It could be a big step forward
for prosthetics. By David
Cohn." + ], + [ + "AP - Scientists in 17
countries will scout waterways
to locate and study the
world's largest freshwater
fish species, many of which
are declining in numbers,
hoping to learn how to better
protect them, researchers
announced Thursday." + ], + [ + "AP - Google Inc.'s plans to
move ahead with its initial
public stock offering ran into
a roadblock when the
Securities and Exchange
Commission didn't approve the
Internet search giant's
regulatory paperwork as
requested." + ], + [ + "Citing security risks, a state
university is urging students
to drop Internet Explorer in
favor of alternative Web
browsers such as Firefox and
Safari." + ], + [ + "Despite being ranked eleventh
in the world in broadband
penetration, the United States
is rolling out high-speed
services on a quot;reasonable
and timely basis to all
Americans, quot; according to
a new report narrowly approved
today by the Federal
Communications" + ], + [ + "NewsFactor - Taking an
innovative approach to the
marketing of high-performance
\\computing, Sun Microsystems
(Nasdaq: SUNW) is offering its
N1 Grid program in a pay-for-
use pricing model that mimics
the way such commodities as
electricity and wireless phone
plans are sold." + ], + [ + "Reuters - Madonna and m-Qube
have\\made it possible for the
star's North American fans to
download\\polyphonic ring tones
and other licensed mobile
content from\\her official Web
site, across most major
carriers and without\\the need
for a credit card." + ], + [ + "The chipmaker is back on a
buying spree, having scooped
up five other companies this
year." + ], + [ + "The company hopes to lure
software partners by promising
to save them from
infrastructure headaches." + ], + [ + "Call it the Maximus factor.
Archaeologists working at the
site of an old Roman temple
near Guy #39;s hospital in
London have uncovered a pot of
cosmetic cream dating back to
AD2." + ], + [ + "The deal comes as Cisco pushes
to develop a market for CRS-1,
a new line of routers aimed at
telephone, wireless and cable
companies." + ], + [ + "SEPTEMBER 14, 2004 (IDG NEWS
SERVICE) - Sun Microsystems
Inc. and Microsoft Corp. next
month plan to provide more
details on the work they are
doing to make their products
interoperable, a Sun executive
said yesterday." + ], + [ + "AP - Astronomy buffs and
amateur stargazers turned out
to watch a total lunar eclipse
Wednesday night #151; the
last one Earth will get for
nearly two and a half years." + ], + [ + "The compact disc has at least
another five years as the most
popular music format before
online downloads chip away at
its dominance, a new study
said on Tuesday." + ], + [ + "Does Your Site Need a Custom
Search Engine
Toolbar?\\\\Today's surfers
aren't always too comfortable
installing software on their
computers. Especially free
software that they don't
necessarily understand. With
all the horror stories of
viruses, spyware, and adware
that make the front page these
days, it's no wonder. So is
there ..." + ], + [ + "Spammers aren't ducking
antispam laws by operating
offshore, they're just
ignoring it." + ], + [ + "AP - Biologists plan to use
large nets and traps this week
in Chicago's Burnham Harbor to
search for the northern
snakehead #151; a type of
fish known for its voracious
appetite and ability to wreak
havoc on freshwater
ecosystems." + ], + [ + "BAE Systems PLC and Northrop
Grumman Corp. won \\$45 million
contracts yesterday to develop
prototypes of anti-missile
technology that could protect
commercial aircraft from
shoulder-fired missiles." + ], + [ + "Users of Microsoft #39;s
Hotmail, most of whom are
accustomed to getting regular
sales pitches for premium
e-mail accounts, got a
pleasant surprise in their
inboxes this week: extra
storage for free." + ], + [ + "IT services provider
Electronic Data Systems
yesterday reported a net loss
of \\$153 million for the third
quarter, with earnings hit in
part by an asset impairment
charge of \\$375 million
connected with EDS's N/MCI
project." + ], + [ + "International Business
Machines Corp. said Wednesday
it had agreed to settle most
of the issues in a suit over
changes in its pension plans
on terms that allow the
company to continue to appeal
a key question while capping
its liability at \\$1.7
billion. <BR><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-The Washington
Post</B></FONT>" + ], + [ + "Edward Kozel, Cisco's former
chief technology officer,
joins the board of Linux
seller Red Hat." + ], + [ + "Reuters - International
Business Machines\\Corp. late
on Wednesday rolled out a new
version of its\\database
software aimed at users of
Linux and Unix
operating\\systems that it
hopes will help the company
take away market\\share from
market leader Oracle Corp. ." + ], + [ + "NASA #39;s Mars Odyssey
mission, originally scheduled
to end on Tuesday, has been
granted a stay of execution
until at least September 2006,
reveal NASA scientists." + ], + [ + "BusinessWeek Online - The
jubilation that swept East
Germany after the fall of the
Berlin Wall in 1989 long ago
gave way to the sober reality
of globalization and market
forces. Now a decade of
resentment seems to be boiling
over. In Eastern cities such
as Leipzig or Chemnitz,
thousands have taken to the
streets since July to protest
cuts in unemployment benefits,
the main source of livelihood
for 1.6 million East Germans.
Discontent among
reunification's losers fueled
big gains by the far left and
far right in Brandenburg and
Saxony state elections Sept.
19. ..." + ], + [ + "In a discovery sure to set off
a firestorm of debate over
human migration to the western
hemisphere, archaeologists in
South Carolina say they have
uncovered evidence that people
lived in eastern North America
at least 50,000 years ago -
far earlier than" + ], + [ + "AP - Former president Bill
Clinton on Monday helped
launch a new Internet search
company backed by the Chinese
government which says its
technology uses artificial
intelligence to produce better
results than Google Inc." + ], + [ + "Sun Microsystems plans to
release its Sun Studio 10
development tool in the fourth
quarter of this year,
featuring support for 64-bit
applications running on AMD
Opteron and Nocona processors,
Sun officials said on Tuesday." + ], + [ + "Most IT Managers won #39;t
question the importance of
security, but this priority
has been sliding between the
third and fourth most
important focus for companies." + ], + [ + "AP - The Federal Trade
Commission on Thursday filed
the first case in the country
against software companies
accused of infecting computers
with intrusive \"spyware\" and
then trying to sell people the
solution." + ], + [ + "While shares of Apple have
climbed more than 10 percent
this week, reaching 52-week
highs, two research firms told
investors Friday they continue
to remain highly bullish about
the stock." + ], + [ + "States are now barred from
imposing telecommunications
regulations on Net phone
providers." + ], + [ + "Strong sales of new mobile
phone models boosts profits at
Carphone Warehouse but the
retailer's shares fall on
concerns at a decline in
profits from pre-paid phones." + ], + [ + " NEW YORK (Reuters) - IBM and
top scientific research
organizations are joining
forces in a humanitarian
effort to tap the unused
power of millions of computers
and help solve complex social
problems." + ], + [ + "AP - Grizzly and black bears
killed a majority of elk
calves in northern Yellowstone
National Park for the second
year in a row, preliminary
study results show." + ], + [ + "PC World - The one-time World
Class Product of the Year PDA
gets a much-needed upgrade." + ], + [ + "As part of its much-touted new
MSN Music offering, Microsoft
Corp. (MSFT) is testing a Web-
based radio service that
mimics nearly 1,000 local
radio stations, allowing users
to hear a version of their
favorite radio station with
far fewer interruptions." + ], + [ + "Ziff Davis - A quick
resolution to the Mambo open-
source copyright dispute seems
unlikely now that one of the
parties has rejected an offer
for mediation." + ], + [ + "Students take note - endless
journeys to the library could
become a thing of the past
thanks to a new multimillion-
pound scheme to make classic
texts available at the click
of a mouse." + ], + [ + "Moscow - The next crew of the
International Space Station
(ISS) is to contribute to the
Russian search for a vaccine
against Aids, Russian
cosmonaut Salijan Sharipovthe
said on Thursday." + ], + [ + "Locked away in fossils is
evidence of a sudden solar
cooling. Kate Ravilious meets
the experts who say it could
explain a 3,000-year-old mass
migration - and today #39;s
global warming." + ], + [ + "By ANICK JESDANUN NEW YORK
(AP) -- Taran Rampersad didn't
complain when he failed to
find anything on his hometown
in the online encyclopedia
Wikipedia. Instead, he simply
wrote his own entry for San
Fernando, Trinidad and
Tobago..." + ], + [ + "How do you top a battle
between Marines and an alien
religious cult fighting to the
death on a giant corona in
outer space? The next logical
step is to take that battle to
Earth, which is exactly what
Microsoft" + ], + [ + "Four U.S. agencies yesterday
announced a coordinated attack
to stem the global trade in
counterfeit merchandise and
pirated music and movies, an
underground industry that law-
enforcement officials estimate
to be worth \\$500 billion each
year." + ], + [ + "Half of all U.S. Secret
Service agents are dedicated
to protecting President
Washington #151;and all the
other Presidents on U.S.
currency #151;from
counterfeiters." + ], + [ + "Maxime Faget conceived and
proposed the development of
the one-man spacecraft used in
Project Mercury, which put the
first American astronauts into
suborbital flight, then
orbital flight" + ], + [ + "The patch fixes a flaw in the
e-mail server software that
could be used to get access to
in-boxes and information." + ], + [ + "AP - Being the biggest dog may
pay off at feeding time, but
species that grow too large
may be more vulnerable to
extinction, new research
suggests." + ], + [ + "Newest Efficeon processor also
offers higher frequency using
less power." + ], + [ + "AP - In what it calls a first
in international air travel,
Finnair says it will let its
frequent fliers check in using
text messages on mobile
phones." + ], + [ + "Search Engine for Programming
Code\\\\An article at Newsforge
pointed me to Koders (
http://www.koders.com ) a
search engine for finding
programming code. Nifty.\\\\The
front page allows you to
specify keywords, sixteen
languages (from ASP to VB.NET)
and sixteen licenses (from AFL
to ZPL -- fortunately there's
an information page to ..." + ], + [ + " #147;Apple once again was the
star of the show at the annual
MacUser awards, #148; reports
MacUser, #147;taking away six
Maxine statues including
Product of the Year for the
iTunes Music Store. #148;
Other Apple award winners:
Power Mac G5, AirPort Express,
20-inch Apple Cinema Display,
GarageBand and DVD Studio Pro
3. Nov 22" + ], + [ + "AP - A sweeping wildlife
preserve in southwestern
Arizona is among the nation's
10 most endangered refuges,
due in large part to illegal
drug and immigrant traffic and
Border Patrol operations, a
conservation group said
Friday." + ], + [ + "SEPTEMBER 20, 2004
(COMPUTERWORLD) - At
PeopleSoft Inc. #39;s Connect
2004 conference in San
Francisco this week, the
software vendor is expected to
face questions from users
about its ability to fend off
Oracle Corp." + ], + [ + "PBS's Charlie Rose quizzes Sun
co-founder Bill Joy,
Monster.com chief Jeff Taylor,
and venture capitalist John
Doerr." + ], + [ + "Skype Technologies is teaming
with Siemens to offer cordless
phone users the ability to
make Internet telephony calls,
in addition to traditional
calls, from their handsets." + ], + [ + "AP - After years of
resistance, the U.S. trucking
industry says it will not try
to impede or delay a new
federal rule aimed at cutting
diesel pollution." + ], + [ + "Umesh Patel, a 36-year old
software engineer from
California, debated until the
last minute." + ], + [ + "AP - From now until the start
of winter, male tarantulas are
roaming around, searching for
female mates, an ideal time to
find out where the spiders
flourish in Arkansas." + ], + [ + "NewsFactor - While a U.S.
District Court continues to
weigh the legality of
\\Oracle's (Nasdaq: ORCL)
attempted takeover of
\\PeopleSoft (Nasdaq: PSFT),
Oracle has taken the necessary
steps to ensure the offer does
not die on the vine with
PeopleSoft's shareholders." + ], + [ + "Analysts said the smartphone
enhancements hold the
potential to bring PalmSource
into an expanding market that
still has room despite early
inroads by Symbian and
Microsoft." + ], + [ + "TOKYO (AFP) - Japan #39;s
Fujitsu and Cisco Systems of
the US said they have agreed
to form a strategic alliance
focusing on routers and
switches that will enable
businesses to build advanced
Internet Protocol (IP)
networks." + ], + [ + "It #39;s a new internet
browser. The first full
version, Firefox 1.0, was
launched earlier this month.
In the sense it #39;s a
browser, yes, but the
differences are larger than
the similarities." + ], + [ + "Microsoft will accelerate SP2
distribution to meet goal of
100 million downloads in two
months." + ], + [ + "International Business
Machines Corp., taken to court
by workers over changes it
made to its traditional
pension plan, has decided to
stop offering any such plan to
new employees." + ], + [ + "NEW YORK - With four of its
executives pleading guilty to
price-fixing charges today,
Infineon will have a hard time
arguing that it didn #39;t fix
prices in its ongoing
litigation with Rambus." + ], + [ + "A report indicates that many
giants of the industry have
been able to capture lasting
feelings of customer loyalty." + ], + [ + "It is the news that Internet
users do not want to hear: the
worldwide web is in danger of
collapsing around us. Patrick
Gelsinger, the chief
technology officer for
computer chip maker Intel,
told a conference" + ], + [ + "Cisco Systems CEO John
Chambers said today that his
company plans to offer twice
as many new products this year
as ever before." + ], + [ + "Every day, somewhere in the
universe, there #39;s an
explosion that puts the power
of the Sun in the shade. Steve
Connor investigates the riddle
of gamma-ray bursts." + ], + [ + "Ten months after NASA #39;s
twin rovers landed on Mars,
scientists reported this week
that both robotic vehicles are
still navigating their rock-
studded landscapes with all
instruments operating" + ], + [ + "British scientists say they
have found a new, greener way
to power cars and homes using
sunflower oil, a commodity
more commonly used for cooking
fries." + ], + [ + "The United States #39; top
computer-security official has
resigned after a little more
than a year on the job, the US
Department of Homeland
Security said on Friday." + ], + [ + " SAN FRANCISCO (Reuters) -
Intel Corp. <A HREF=\"http:/
/www.reuters.co.uk/financeQuot
eLookup.jhtml?ticker=INTC.O
qtype=sym infotype=info
qcat=news\">INTC.O</A>
on Thursday outlined its
vision of the Internet of the
future, one in which millions
of computer servers would
analyze and direct network
traffic to make the Web safer
and more efficient." + ], + [ + "Check out new gadgets as
holiday season approaches." + ], + [ + "TOKYO, Sep 08, 2004 (AFX-UK
via COMTEX) -- Sony Corp will
launch its popular projection
televisions with large liquid
crystal display (LCD) screens
in China early next year, a
company spokeswoman said." + ], + [ + "AFP - Outgoing EU competition
commissioner Mario Monti wants
to reach a decision on
Oracle's proposed takeover of
business software rival
PeopleSoft by the end of next
month, an official said." + ], + [ + " WASHINGTON (Reuters) -
Satellite companies would be
able to retransmit
broadcasters' television
signals for another five
years but would have to offer
those signals on a single
dish, under legislation
approved by Congress on
Saturday." + ], + [ + "Sven Jaschan, who may face
five years in prison for
spreading the Netsky and
Sasser worms, is now working
in IT security. Photo: AFP." + ], + [ + "TechWeb - An Indian Institute
of Technology professor--and
open-source evangelist--
discusses the role of Linux
and open source in India." + ], + [ + "Here are some of the latest
health and medical news
developments, compiled by
editors of HealthDay: -----
Contaminated Fish in Many U.S.
Lakes and Rivers Fish
that may be contaminated with
dioxin, mercury, PCBs and
pesticides are swimming in
more than one-third of the
United States' lakes and
nearly one-quarter of its
rivers, according to a list of
advisories released by the
Environmental Protection
Agency..." + ], + [ + "Bill Gates is giving his big
speech right now at Microsofts
big Digital Entertainment
Anywhere event in Los Angeles.
Well have a proper report for
you soon, but in the meantime
we figured wed" + ], + [ + "Spinal and non-spinal
fractures are reduced by
almost a third in women age 80
or older who take a drug
called strontium ranelate,
European investigators
announced" + ], + [ + "BERLIN: Apple Computer Inc is
planning the next wave of
expansion for its popular
iTunes online music store with
a multi-country European
launch in October, the
services chief architect said
on Wednesday." + ], + [ + "Calls to 13 other countries
will be blocked to thwart
auto-dialer software." + ], + [ + "com September 27, 2004, 5:00
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." + ], + [ + "SPACE.com - The Zero Gravity
Corporation \\ has been given
the thumbs up by the Federal
Aviation Administration (FAA)
to \\ conduct quot;weightless
flights quot; for the general
public, providing the \\
sensation of floating in
space." + ], + [ + "A 32-year-old woman in Belgium
has become the first woman
ever to give birth after
having ovarian tissue removed,
frozen and then implanted back
in her body." + ], + [ + "No sweat, says the new CEO,
who's imported from IBM. He
also knows this will be the
challenge of a lifetime." + ], + [ + "AP - Their discoveries may be
hard for many to comprehend,
but this year's Nobel Prize
winners still have to explain
what they did and how they did
it." + ], + [ + "More than 15 million homes in
the UK will be able to get on-
demand movies by 2008, say
analysts." + ], + [ + "A well-researched study by the
National Academy of Sciences
makes a persuasive case for
refurbishing the Hubble Space
Telescope. NASA, which is
reluctant to take this
mission, should rethink its
position." + ], + [ + "LONDON -- In a deal that
appears to buck the growing
trend among governments to
adopt open-source
alternatives, the U.K.'s
Office of Government Commerce
(OGC) is negotiating a renewal
of a three-year agreement with
Microsoft Corp." + ], + [ + "WASHINGTON - A Pennsylvania
law requiring Internet service
providers (ISPs) to block Web
sites the state's prosecuting
attorneys deem to be child
pornography has been reversed
by a U.S. federal court, with
the judge ruling the law
violated free speech rights." + ], + [ + "The Microsoft Corp. chairman
receives four million e-mails
a day, but practically an
entire department at the
company he founded is
dedicated to ensuring that
nothing unwanted gets into his
inbox, the company #39;s chief
executive said Thursday." + ], + [ + "The 7100t has a mobile phone,
e-mail, instant messaging, Web
browsing and functions as an
organiser. The device looks
like a mobile phone and has
the features of the other
BlackBerry models, with a
large screen" + ], + [ + "Apple Computer has unveiled
two new versions of its hugely
successful iPod: the iPod
Photo and the U2 iPod. Apple
also has expanded" + ], + [ + "AP - This year's hurricanes
spread citrus canker to at
least 11,000 trees in
Charlotte County, one of the
largest outbreaks of the
fruit-damaging infection to
ever affect Florida's citrus
industry, state officials
said." + ], + [ + "IBM moved back into the iSCSI
(Internet SCSI) market Friday
with a new array priced at
US\\$3,000 and aimed at the
small and midsize business
market." + ], + [ + "American Technology Research
analyst Shaw Wu has initiated
coverage of Apple Computer
(AAPL) with a #39;buy #39;
recommendation, and a 12-month
target of US\\$78 per share." + ], + [ + "washingtonpost.com - Cell
phone shoppers looking for new
deals and features didn't find
them yesterday, a day after
Sprint Corp. and Nextel
Communications Inc. announced
a merger that the phone
companies promised would shake
up the wireless business." + ], + [ + "JAPANESE GIANT Sharp has
pulled the plug on its Linux-
based PDAs in the United
States as no-one seems to want
them. The company said that it
will continue to sell them in
Japan where they sell like hot
cakes" + ], + [ + "New NavOne offers handy PDA
and Pocket PC connectivity,
but fails to impress on
everything else." + ], + [ + "The Marvel deal calls for
Mforma to work with the comic
book giant to develop a
variety of mobile applications
based on Marvel content." + ], + [ + "washingtonpost.com - The
Portable Media Center -- a
new, Microsoft-conceived
handheld device that presents
video and photos as well as
music -- would be a decent
idea if there weren't such a
thing as lampposts. Or street
signs. Or trees. Or other
cars." + ], + [ + "The Air Force Reserve #39;s
Hurricane Hunters, those
fearless crews who dive into
the eyewalls of hurricanes to
relay critical data on
tropical systems, were chased
from their base on the
Mississippi Gulf Coast by
Hurricane Ivan." + ], + [ + "Embargo or not, Fidel Castro's
socialist paradise has quietly
become a pharmaceutical
powerhouse. (They're still
working on the capitalism
thing.) By Douglas Starr from
Wired magazine." + ], + [ + "A new worm can spy on users by
hijacking their Web cameras, a
security firm warned Monday.
The Rbot.gr worm -- the latest
in a long line of similar
worms; one security firm
estimates that more than 4,000
variations" + ], + [ + "The separation of PalmOne and
PalmSource will be complete
with Eric Benhamou's
resignation as the latter's
chairman." + ], + [ + "\\\\\"(CNN) -- A longtime
associate of al Qaeda leader
Osama bin Laden surrendered
to\\Saudi Arabian officials
Tuesday, a Saudi Interior
Ministry official said.\"\\\\\"But
it is unclear what role, if
any, Khaled al-Harbi may have
had in any terror\\attacks
because no public charges have
been filed against him.\"\\\\\"The
Saudi government -- in a
statement released by its
embassy in Washington
--\\called al-Harbi's surrender
\"the latest direct result\" of
its limited, one-month\\offer
of leniency to terror
suspects.\"\\\\This is great! I
hope this really starts to pay
off. Creative solutions
to\\terrorism that don't
involve violence. \\\\How
refreshing! \\\\Are you paying
attention Bush
administration?\\\\" + ], + [ + "AT T Corp. is cutting 7,400
more jobs and slashing the
book value of its assets by
\\$11.4 billion, drastic moves
prompted by the company's plan
to retreat from the
traditional consumer telephone
business following a lost
court battle." + ], + [ + "Celerons form the basis of
Intel #39;s entry-level
platform which includes
integrated/value motherboards
as well. The Celeron 335D is
the fastest - until the
Celeron 340D - 2.93GHz -
becomes mainstream - of the" + ], + [ + "The entertainment industry
asks the Supreme Court to
reverse the Grokster decision,
which held that peer-to-peer
networks are not liable for
copyright abuses of their
users. By Michael Grebb." + ], + [ + "Hewlett-Packard will shell out
\\$16.1 billion for chips in
2005, but Dell's wallet is
wide open, too." + ], + [ + "A remote attacker could take
complete control over
computers running many
versions of Microsoft software
by inserting malicious code in
a JPEG image that executes
through an unchecked buffer" + ], + [ + "The lawsuit claims the
companies use a patented
Honeywell technology for
brightening images and
reducing interference on
displays." + ], + [ + "The co-president of Oracle
testified that her company was
serious about its takeover
offer for PeopleSoft and was
not trying to scare off its
customers." + ], + [ + "An extremely rare Hawaiian
bird dies in captivity,
possibly marking the
extinction of its entire
species only 31 years after it
was first discovered." + ], + [ + "Does Geico's trademark lawsuit
against Google have merit? How
will the case be argued?
What's the likely outcome of
the trial? A mock court of
trademark experts weighs in
with their verdict." + ], + [ + "Treo 650 boasts a high-res
display, an improved keyboard
and camera, a removable
battery, and more. PalmOne
this week is announcing the
Treo 650, a hybrid PDA/cell-
phone device that addresses
many of the shortcomings" + ], + [ + "International Business
Machines Corp.'s possible exit
from the personal computer
business would be the latest
move in what amounts to a long
goodbye from a field it
pioneered and revolutionized." + ], + [ + "Leipzig Game Convention in
Germany, the stage for price-
slash revelations. Sony has
announced that it #39;s
slashing the cost of PS2 in
the UK and Europe to 104.99
GBP." + ], + [ + "In yet another devastating
body blow to the company,
Intel (Nasdaq: INTC) announced
it would be canceling its
4-GHz Pentium chip. The
semiconductor bellwether said
it was switching" + ], + [ + "Seagate #39;s native SATA
interface technology with
Native Command Queuing (NCQ)
allows the Barracuda 7200.8 to
match the performance of
10,000-rpm SATA drives without
sacrificing capacity" + ], + [ + "You #39;re probably already
familiar with one of the most
common questions we hear at
iPodlounge: quot;how can I
load my iPod up with free
music?" + ], + [ + "Vice chairman of the office of
the chief executive officer in
Novell Inc, Chris Stone is
leaving the company to pursue
other opportunities in life." + ], + [ + "washingtonpost.com - Microsoft
is going to Tinseltown today
to announce plans for its
revamped Windows XP Media
Center, part of an aggressive
push to get ahead in the
digital entertainment race." + ], + [ + "AP - Most of the turkeys
gracing the nation's dinner
tables Thursday have been
selectively bred for their
white meat for so many
generations that simply
walking can be a problem for
many of the big-breasted birds
and sex is no longer possible." + ], + [ + " SEATTLE (Reuters) - Microsoft
Corp. <A HREF=\"http://www.r
euters.co.uk/financeQuoteLooku
p.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
is making a renewed push this
week to get its software into
living rooms with the launch
of a new version of its
Windows XP Media Center, a
personal computer designed for
viewing movies, listening to
music and scrolling through
digital pictures." + ], + [ + "The new software is designed
to simplify the process of
knitting together back-office
business applications." + ], + [ + "FRANKFURT, GERMANY -- The
German subsidiaries of
Hewlett-Packard Co. (HP) and
Novell Inc. are teaming to
offer Linux-based products to
the country's huge public
sector." + ], + [ + "SiliconValley.com - \"I'm
back,\" declared Apple
Computer's Steve Jobs on
Thursday morning in his first
public appearance before
reporters since cancer surgery
in late July." + ], + [ + "Health India: London, Nov 4 :
Cosmetic face cream used by
fashionable Roman women was
discovered at an ongoing
archaeological dig in London,
in a metal container, complete
with the lid and contents." + ], + [ + "MacCentral - Microsoft's
Macintosh Business Unit on
Tuesday issued a patch for
Virtual PC 7 that fixes a
problem that occurred when
running the software on Power
Mac G5 computers with more
than 2GB of RAM installed.
Previously, Virtual PC 7 would
not run on those computers,
causing a fatal error that
crashed the application.
Microsoft also noted that
Virtual PC 7.0.1 also offers
stability improvements,
although it wasn't more
specific than that -- some
users have reported problems
with using USB devices and
other issues." + ], + [ + "Don't bother buying Star Wars:
Battlefront if you're looking
for a first-class shooter --
there are far better games out
there. But if you're a Star
Wars freak and need a fix,
this title will suffice. Lore
Sjberg reviews Battlefront." + ], + [ + "More than by brain size or
tool-making ability, the human
species was set apart from its
ancestors by the ability to
jog mile after lung-stabbing
mile with greater endurance
than any other primate,
according to research
published today in the journal" + ], + [ + "Woodland Hills-based Brilliant
Digital Entertainment and its
subsidiary Altnet announced
yesterday that they have filed
a patent infringement suit
against the Recording Industry
Association of America (RIAA)." + ], + [ + "Sony Ericsson and Cingular
provide Z500a phones and
service for military families
to stay connected on today
#39;s quot;Dr. Phil Show
quot;." + ], + [ + "Officials at EarthLink #39;s
(Quote, Chart) R amp;D
facility have quietly released
a proof-of-concept file-
sharing application based on
the Session Initiated Protocol
(define)." + ], + [ + " quot;It #39;s your mail,
quot; the Google Web site
said. quot;You should be able
to choose how and where you
read it. You can even switch
to other e-mail services
without having to worry about
losing access to your
messages." + ], + [ + "The world #39;s only captive
great white shark made history
this week when she ate several
salmon fillets, marking the
first time that a white shark
in captivity" + ], + [ + "Communications aggregator
iPass said Monday that it is
adding in-flight Internet
access to its access
portfolio. Specifically, iPass
said it will add access
offered by Connexion by Boeing
to its list of access
providers." + ], + [ + "Company launches free test
version of service that
fosters popular Internet
activity." + ], + [ + "Outdated computer systems are
hampering the work of
inspectors, says the UN
nuclear agency." + ], + [ + "MacCentral - You Software Inc.
announced on Tuesday the
availability of You Control:
iTunes, a free\\download that
places iTunes controls in the
Mac OS X menu bar.
Without\\leaving the current
application, you can pause,
play, rewind or skip songs,\\as
well as control iTunes' volume
and even browse your entire
music library\\by album, artist
or genre. Each time a new song
plays, You Control:
iTunes\\also pops up a window
that displays the artist and
song name and the
album\\artwork, if it's in the
library. System requirements
call for Mac OS X\\v10.2.6 and
10MB free hard drive space.
..." + ], + [ + "The US Secret Service Thursday
announced arrests in eight
states and six foreign
countries of 28 suspected
cybercrime gangsters on
charges of identity theft,
computer fraud, credit-card
fraud, and conspiracy." + ] + ], + "hovertemplate": "label=Sci/Tech
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Sci/Tech", + "marker": { + "color": "#EF553B", + "size": 5, + "symbol": "diamond" + }, + "mode": "markers", + "name": "Sci/Tech", + "showlegend": true, + "type": "scattergl", + "x": [ + -50.70537, + -47.977715, + -51.65402, + -37.6373, + -36.581165, + -19.791555, + -20.928211, + -22.567608, + -19.96729, + -49.124306, + -7.3474283, + -22.541676, + -52.717, + -28.148346, + -42.825386, + -5.8827424, + -35.965088, + -10.623615, + -18.956379, + -8.153443, + -29.768171, + -31.347673, + -6.4607854, + -41.868053, + -13.634508, + -11.594272, + -13.830222, + -26.233013, + -10.061741, + -32.693943, + -38.45829, + -15.162608, + 16.015558, + -18.354656, + -46.83205, + -38.63723, + -7.0522246, + -50.323666, + -44.325756, + -7.6315646, + -48.104885, + -56.60167, + -40.07204, + -8.970166, + -44.30361, + -36.139923, + -48.951195, + 29.55601, + -21.136202, + -21.265085, + -41.619125, + -16.407732, + -33.503048, + -40.584736, + -24.227406, + -41.094902, + -41.256504, + -18.041924, + -18.929783, + -45.789707, + -35.619858, + -29.50228, + -36.813686, + 2.6427522, + -23.344019, + -44.347576, + -8.361857, + -17.157036, + -18.672327, + -18.132378, + -8.266737, + -34.572502, + -5.21674, + -21.22969, + -42.24456, + -7.7231383, + -46.522186, + -36.61156, + -37.112934, + 27.892096, + -5.4085217, + 2.6259706, + -16.563955, + -43.12336, + -57.728573, + -48.13403, + -27.2014, + -10.165841, + -53.29632, + -31.430248, + -42.180077, + -15.3164215, + -47.9015, + -23.828165, + -31.504562, + -6.3850884, + -3.119768, + -26.526276, + -22.841238, + -51.962658, + -15.730567, + -44.145668, + -30.795404, + -50.141148, + -43.173016, + -4.780475, + -36.60279, + -34.644447, + -47.198746, + -37.63518, + -46.367523, + -47.68558, + -10.65396, + -49.790844, + -45.67376, + -20.76551, + -29.445877, + 4.37751, + -45.453674, + -44.030884, + -24.544922, + -50.093754, + -19.335144, + -25.326218, + -6.78382, + -50.14104, + -33.647213, + -9.488163, + -14.337521, + -29.272839, + -43.886417, + -32.545635, + 4.3835373, + -43.219402, + 17.87465, + -31.277052, + -16.921326, + -9.602789, + -13.009839, + -35.251965, + -9.096614, + 2.5471764, + -16.914656, + -44.31037, + -42.23089, + 33.389534, + -37.813118, + -45.167847, + -36.155823, + -33.989525, + -30.086702, + -1.0110728, + -17.443365, + -42.189774, + -35.002357, + -24.72687, + 31.629358, + -25.166414, + -7.6725793, + -51.84964, + -6.9841313, + -50.175316, + -35.0245, + -35.842033, + -48.693672, + -44.228165, + -31.057253, + -33.267147, + -36.05716, + -7.2311153, + -12.550289, + -20.162544, + -39.967007, + -13.788036, + -31.409056, + -31.830273, + -22.6749, + -31.353453, + 27.913883, + -28.206575, + -19.676825, + -27.176733, + -44.44146, + -51.24567, + -10.997008, + 21.737896, + -29.750324, + -51.056225, + -19.130821, + -5.5860677, + -24.740665, + -47.064148, + -54.69983, + -7.06102, + -43.44754, + 24.795843, + -4.7836714, + -24.956768, + -53.22971, + -6.0662427, + -37.926384, + -41.1264, + -37.36757, + -16.533398, + -30.736622, + -15.802876, + -25.432766, + -22.672857, + -35.77931, + -16.137821, + -48.2029, + -6.2585354, + 7.9719267, + -53.251343, + -9.297528, + -44.267094, + -19.017382, + 33.992294, + -15.977553, + -15.548051, + -44.87483, + -51.507732, + -22.362936, + -10.216267, + -31.37848, + -5.704888, + -24.823088, + -50.370255, + -38.724506, + -38.19549, + -21.799944, + -13.132145, + -44.289158, + -16.34018, + -42.824986, + -38.334126, + -35.730442, + -36.50344, + -7.2380333, + -40.239014, + 2.5284033, + -49.050774, + -22.21815, + -25.737848, + -17.621637, + -12.631271, + -22.281115, + -43.594814, + -15.313636, + -21.579958, + -46.839928, + -8.932405, + -38.67932, + -6.674851, + -6.503544, + -36.837105, + -4.2590623, + -42.01228, + -6.2482433, + -39.613857, + -46.561874, + -45.638084, + -21.206648, + -34.547794, + -18.876629, + -17.48441, + -27.667318, + 9.352251, + -16.641712, + -41.143227, + -6.392582, + -13.137335, + -30.403149, + -50.138874, + -14.373312, + -11.489995, + -48.586517, + -32.614243, + -40.497093, + -34.05016, + -35.82806, + -44.020023, + -23.90992, + 17.625916, + -27.855904, + -52.830154, + -48.07056, + -19.067713, + -40.71425, + -25.576069, + -23.095638, + -40.87854, + -38.34742, + -50.887096, + -42.132465, + -14.928126, + -42.33495, + -45.8806, + -51.873066, + -35.644184, + -43.46856, + -35.958366, + -54.239532, + -39.886326, + -43.70516, + -8.241421, + -12.890557, + -23.360226, + -33.456993, + -34.14632, + -30.420895, + -19.05618, + -48.503033, + -18.99317, + -14.034199, + -35.493626, + -16.14875, + -36.11573, + -16.320389, + -19.673914, + -9.829185, + -52.1362, + -20.01322, + 18.142248, + -10.860374, + -37.78618, + -8.867661, + 5.742987, + -21.27348, + -30.542126, + -5.9578004, + -48.91647, + -11.771681, + -9.854177, + -23.889015, + -22.090435, + -50.986782, + -38.59416, + -45.269844, + -49.752243, + -37.106304, + -2.1609035, + -39.611637, + -38.19811, + -4.4758306, + -50.613956, + -20.649567, + -15.612729, + -9.729161, + -28.362564, + -45.534595, + -30.753082, + -15.496077, + -35.993237, + -36.425526, + -5.6656046, + -31.978811, + -7.6462755, + -31.936037, + -38.00677, + -42.961407, + -16.082205, + -50.200237, + -10.404932, + -38.39936, + -34.192577, + -7.734347, + -50.261745, + -44.1901, + -21.206255, + -41.132812, + -7.490298, + -6.7789235, + 21.208382, + -44.169758, + -12.397968, + -11.5951185, + -8.326396, + -20.166492, + -15.611658, + -38.040787, + -30.962019, + -45.082096, + -47.16861, + -4.7970433, + -26.904469, + -17.731619, + -43.073074, + -6.7949033, + 35.39813, + -11.253943, + -24.282412, + -53.5042, + -38.4294, + -46.690304, + -36.175987, + -33.290382, + -15.035485, + -49.55056, + -39.571285, + -51.21703, + -8.731081, + -6.0078135, + -11.813869, + -1.1245881, + -40.870914, + -52.076916, + -51.801567, + -23.981367, + -35.73722, + 0.08228831, + -36.47172, + -11.448383, + -40.73366, + -19.412077, + -47.05097, + -10.298156, + -43.35723, + -10.978807, + 51.953743, + -8.411927, + -13.247851, + -47.515026, + -4.658773, + -29.961985, + -12.003402, + -17.52331, + -50.23115, + -48.06655, + -7.6838555, + -33.914284, + -50.991158, + -38.40225, + -35.325897, + -51.80827, + -41.270184, + -49.167038, + 5.044943, + -27.684992, + -17.990955, + -2.3985894, + 17.726252, + -34.57765, + -39.0015, + -14.16722, + -50.34398, + -19.314493, + -17.94723, + -24.707636, + -5.6782784, + -13.584895, + -52.063236, + -33.76177, + -50.93683, + -35.708652, + -41.237144, + -50.64484, + -2.0928724, + -41.631573, + -15.368924, + -41.6564, + -37.111736, + -37.09859, + -41.025192, + -2.1707618, + -40.202778, + -41.903214, + -9.049681, + -16.408047, + -42.958614, + -42.372177, + -45.431026, + -4.7174063, + -30.901245, + -46.568104, + -2.3207862, + -45.59506, + -13.1443 + ], + "xaxis": "x", + "y": [ + -10.254759, + -22.326504, + -8.559602, + -20.467894, + -16.367886, + 34.67725, + 0.20705454, + -2.1101115, + -5.9476533, + -0.62792206, + 17.02401, + 2.5558534, + -10.392384, + -18.170088, + -29.326565, + 26.266212, + -12.614066, + 14.819815, + -0.6994232, + 14.150794, + -22.118223, + -19.087698, + 25.554472, + -32.54127, + -2.2139447, + 26.281322, + -1.2898456, + -8.973769, + 32.03464, + -26.55557, + -18.176107, + -3.6315196, + -19.35426, + 4.681029, + -27.456821, + -24.490698, + 29.56973, + -10.1600275, + -10.852377, + 22.864084, + -10.734537, + -6.189112, + -13.330445, + 20.16529, + -9.1425705, + -23.877768, + 11.228575, + 17.663988, + 2.542931, + -1.3406546, + -23.981632, + 30.063469, + -14.152756, + -8.409088, + 19.963877, + -15.70016, + -19.558147, + 6.959669, + -17.884281, + -12.053112, + -12.727203, + -25.217607, + -0.7906725, + -10.548126, + 20.132593, + 10.375427, + 18.302557, + -1.274212, + 5.588625, + 7.1431947, + 27.65378, + -12.216588, + 3.5037541, + -4.302394, + 16.477135, + 19.197943, + -14.683218, + -14.726069, + -8.98979, + 16.493795, + -23.418943, + 31.197924, + -7.310227, + -8.874298, + -13.5169735, + -7.2581453, + 22.63779, + 28.57203, + -10.379873, + -16.003376, + 3.1463404, + -3.7059593, + -23.257317, + -9.050367, + -14.212201, + 21.597916, + 28.315454, + -9.711501, + 24.548212, + -17.763275, + 5.0752234, + -0.48519766, + 11.854107, + -11.487356, + -6.806543, + 32.15406, + -2.8677607, + -9.217092, + -7.9709535, + -9.329842, + -23.302309, + -0.9053779, + 14.969123, + -12.578425, + -21.124685, + 33.794212, + -20.977274, + -15.128482, + -16.33133, + -30.116133, + -4.7339125, + -24.043522, + 5.9628015, + -11.766295, + 20.015493, + -20.486948, + -21.596968, + 16.84247, + 24.66648, + -17.758942, + -8.986503, + -7.5245304, + -15.130549, + -6.4981833, + -11.591567, + 21.901451, + -0.6368593, + 16.802742, + 20.371767, + 0.94949424, + 31.862827, + 35.12017, + 24.827017, + 9.816621, + -5.2393093, + 9.180699, + 3.9763682, + -14.759153, + -11.865506, + -13.755454, + -13.679028, + 20.333702, + -1.8922254, + -13.595177, + -15.84722, + 25.674028, + 23.005444, + -7.935221, + 24.642124, + -19.237648, + -20.576859, + -14.856947, + -17.656506, + -14.13704, + 11.748135, + -17.733555, + -2.6014824, + -13.317306, + -0.7534798, + 16.867065, + 22.19843, + -8.660773, + -7.2729115, + 23.76637, + -30.203144, + -14.535694, + -6.199936, + -22.042368, + 14.838855, + -23.169117, + 34.738625, + 10.819606, + -10.898081, + -19.525257, + 17.761076, + 13.325627, + -13.576142, + -25.388409, + -0.5336322, + 17.181808, + -4.1373553, + -20.077517, + -16.791988, + 13.435741, + -22.73552, + -0.6918705, + 27.578976, + 0.41720697, + -21.062717, + 15.710144, + -13.556541, + -15.989742, + -13.601137, + -7.148113, + 19.323282, + 1.1989386, + -0.47444645, + 0.94497335, + 9.556583, + -4.4569497, + -24.076357, + 21.659195, + 33.719093, + 19.704102, + 31.99901, + -26.121319, + 26.534893, + 9.6071, + 3.7444665, + 21.501694, + -17.915682, + -17.60881, + -2.6888435, + 32.299854, + -15.889842, + 16.942707, + 0.2748501, + -16.57776, + -16.080286, + -10.464009, + 11.700205, + 30.462563, + -29.86582, + -8.282391, + -8.46684, + -16.238358, + -25.85015, + 19.385956, + 19.32553, + -19.83609, + -6.5198464, + -20.836182, + 0.45974386, + -8.057026, + 3.8825731, + 22.033895, + -5.582006, + -23.06022, + -5.6566067, + 33.256733, + -27.384535, + 34.042473, + -3.056115, + 21.529955, + 17.78518, + -29.090658, + 21.886444, + -3.69288, + 20.052608, + -0.2840251, + -15.78091, + -4.7497973, + -1.3183376, + -8.057265, + 3.0869732, + -7.959283, + -34.817635, + 34.081158, + -0.1847365, + -21.634756, + 25.563747, + 30.48236, + -11.662108, + -9.688497, + -4.4100275, + 24.713762, + -15.716439, + 21.239998, + -10.980467, + -5.3119135, + -16.683647, + -10.756376, + 27.087646, + -6.474749, + -16.08439, + -14.420636, + -7.351985, + 4.601816, + 10.600249, + -4.315942, + 0.85024196, + -8.745571, + -10.188763, + -5.876716, + -3.0804467, + -3.2769468, + -19.281757, + -15.235338, + -15.53886, + -19.303434, + -12.302218, + -23.051016, + -20.775913, + 11.0288925, + -15.161179, + 35.796543, + 4.53065, + -3.197111, + -9.126152, + -5.3693423, + -8.645808, + 26.929934, + -3.2705798, + -22.339233, + 6.1994753, + -0.03827765, + 1.0350281, + -2.2386749, + 10.1421995, + 21.500256, + 20.323536, + -14.895687, + 3.4454656, + 13.368094, + 17.025469, + -20.511335, + 20.105099, + 39.494793, + 33.45021, + 21.672586, + 15.536683, + -21.343506, + 24.513098, + 28.953205, + -1.2875158, + -0.14630945, + -5.8219385, + -14.570528, + -19.750566, + -0.25747964, + -6.1142654, + 24.168753, + -17.26478, + -12.292187, + 21.782167, + -22.266432, + 5.911049, + 7.3130083, + 29.060263, + -1.2906566, + -5.6858554, + 11.88096, + -14.269513, + -17.597988, + -13.744734, + 19.081799, + 25.979095, + 24.452019, + -11.330729, + -11.392148, + 1.1835473, + -1.922125, + 18.76773, + -5.1183553, + 14.371391, + -9.079416, + 32.181087, + -10.61583, + -20.358099, + -7.6430187, + -5.257486, + 16.290205, + 24.053005, + -5.443865, + -30.894657, + -1.7249132, + -1.4501517, + 14.297588, + 1.5236746, + 26.836803, + -7.542444, + -4.085233, + -9.243302, + -22.380308, + 32.18937, + -7.3226933, + -32.17766, + -15.654366, + -3.2708795, + 0.07473384, + 26.989523, + -8.599584, + -15.78256, + -16.991213, + -8.202672, + -10.86935, + 0.538039, + -11.617886, + 3.5003624, + -0.27149853, + -1.4504046, + 21.914633, + 19.832611, + 22.365917, + -22.19336, + -19.892523, + -11.016326, + -18.943878, + -9.76076, + -25.935007, + -21.395668, + -19.286484, + 30.605867, + -25.124516, + 19.076454, + -20.217596, + -4.427436, + 0.124456935, + 15.850318, + 14.106509, + -25.880474, + 20.326845, + -18.178284, + 18.546848, + -8.462077, + -6.078381, + 0.24295494, + -14.893564, + -22.965818, + 36.82579, + -12.61784, + 18.366398, + 4.072649, + 1.2205616, + -13.276994, + -17.09381, + -18.344118, + 33.12794, + -12.617298, + 3.6332028, + -24.02265, + 11.337199, + 6.917111, + -9.203751, + -4.5927486, + 0.687024, + 4.003382, + -5.714998, + -7.6352305, + 28.259352, + -7.4210625, + -14.774667, + 0.21726441, + -20.630865, + -0.4162142, + -12.0516205, + -24.923342, + -23.75025, + -23.006275, + 27.442217, + -22.516329, + -6.229835, + -15.101339, + -25.983875, + 24.170658, + -13.358003, + -32.53302, + 24.710947, + -4.510418, + -15.999681, + -10.241354, + -6.066459, + 27.73088, + -4.162361, + -11.81379, + 11.597373, + -21.738821, + -1.3630763 + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "Newspapers in Greece reflect a
mixture of exhilaration that
the Athens Olympics proved
successful, and relief that
they passed off without any
major setback." + ], + [ + "Loudon, NH -- As the rain
began falling late Friday
afternoon at New Hampshire
International Speedway, the
rich in the Nextel Cup garage
got richer." + ], + [ + "AP - Ottawa Senators right
wing Marian Hossa is switching
European teams during the NHL
lockout." + ], + [ + "Chelsea terminated Romania
striker Adrian Mutu #39;s
contract, citing gross
misconduct after the player
failed a doping test for
cocaine and admitted taking
the drug, the English soccer
club said in a statement." + ], + [ + "The success of Paris #39; bid
for Olympic Games 2012 would
bring an exceptional
development for France for at
least 6 years, said Jean-
Francois Lamour, French
minister for Youth and Sports
on Tuesday." + ], + [ + "Madison, WI (Sports Network) -
Anthony Davis ran for 122
yards and two touchdowns to
lead No. 6 Wisconsin over
Northwestern, 24-12, to
celebrate Homecoming weekend
at Camp Randall Stadium." + ], + [ + "LaVar Arrington participated
in his first practice since
Oct. 25, when he aggravated a
knee injury that sidelined him
for 10 games." + ], + [ + " NEW YORK (Reuters) - Billie
Jean King cut her final tie
with the U.S. Fed Cup team
Tuesday when she retired as
coach." + ], + [ + "The Miami Dolphins arrived for
their final exhibition game
last night in New Orleans
short nine players." + ], + [ + "AP - From LSU at No. 1 to Ohio
State at No. 10, The AP
women's basketball poll had no
changes Monday." + ], + [ + "nother stage victory for race
leader Petter Solberg, his
fifth since the start of the
rally. The Subaru driver is
not pulling away at a fast
pace from Gronholm and Loeb
but the gap is nonetheless
increasing steadily." + ], + [ + "April 1980 -- Players strike
the last eight days of spring
training. Ninety-two
exhibition games are canceled.
June 1981 -- Players stage
first midseason strike in
history." + ], + [ + "On Sunday, the day after Ohio
State dropped to 0-3 in the
Big Ten with a 33-7 loss at
Iowa, the Columbus Dispatch
ran a single word above its
game story on the Buckeyes:
quot;Embarrassing." + ], + [ + "BOSTON - Curt Schilling got
his 20th win on the eve of
Boston #39;s big series with
the New York Yankees. Now he
wants much more. quot;In a
couple of weeks, hopefully, it
will get a lot better, quot;
he said after becoming" + ], + [ + "Shooed the ghosts of the
Bambino and the Iron Horse and
the Yankee Clipper and the
Mighty Mick, on his 73rd
birthday, no less, and turned
Yankee Stadium into a morgue." + ], + [ + "Gold medal-winning Marlon
Devonish says the men #39;s
4x100m Olympic relay triumph
puts British sprinting back on
the map. Devonish, Darren
Campbell, Jason Gardener and
Mark Lewis-Francis edged out
the American" + ], + [ + "he difficult road conditions
created a few incidents in the
first run of the Epynt stage.
Francois Duval takes his
second stage victory since the
start of the rally, nine
tenths better than Sebastien
Loeb #39;s performance in
second position." + ], + [ + "Tallahassee, FL (Sports
Network) - Florida State head
coach Bobby Bowden has
suspended senior wide receiver
Craphonso Thorpe for the
Seminoles #39; game against
fellow Atlantic Coast
Conference member Duke on
Saturday." + ], + [ + "Former champion Lleyton Hewitt
bristled, battled and
eventually blossomed as he
took another step towards a
second US Open title with a
second-round victory over
Moroccan Hicham Arazi on
Friday." + ], + [ + "As the Mets round out their
search for a new manager, the
club is giving a last-minute
nod to its past. Wally
Backman, an infielder for the
Mets from 1980-88 who played
second base on the 1986" + ], + [ + "AMSTERDAM, Aug 18 (Reuters) -
Midfielder Edgar Davids #39;s
leadership qualities and
never-say-die attitude have
earned him the captaincy of
the Netherlands under new
coach Marco van Basten." + ], + [ + "COUNTY KILKENNY, Ireland (PA)
-- Hurricane Jeanne has led to
world No. 1 Vijay Singh
pulling out of this week #39;s
WGC-American Express
Championship at Mount Juliet." + ], + [ + "Green Bay, WI (Sports Network)
- The Green Bay Packers will
be without the services of Pro
Bowl center Mike Flanagan for
the remainder of the season,
as he will undergo left knee
surgery." + ], + [ + "COLUMBUS, Ohio -- NCAA
investigators will return to
Ohio State University Monday
to take another look at the
football program after the
latest round of allegations
made by former players,
according to the Akron Beacon
Journal." + ], + [ + "Manchester United gave Alex
Ferguson a 1,000th game
anniversary present by
reaching the last 16 of the
Champions League yesterday,
while four-time winners Bayern
Munich romped into the second
round with a 5-1 beating of
Maccabi Tel Aviv." + ], + [ + "The last time the Angels
played the Texas Rangers, they
dropped two consecutive
shutouts at home in their most
agonizing lost weekend of the
season." + ], + [ + "SEATTLE - Chasing a nearly
forgotten ghost of the game,
Ichiro Suzuki broke one of
baseball #39;s oldest records
Friday night, smoking a single
up the middle for his 258th
hit of the year and breaking
George Sisler #39;s record for
the most hits in a season" + ], + [ + "Grace Park, her caddie - and
fans - were poking around in
the desert brush alongside the
18th fairway desperately
looking for her ball." + ], + [ + "Washington Redskins kicker
John Hall strained his right
groin during practice
Thursday, his third leg injury
of the season. Hall will be
held out of practice Friday
and is questionable for Sunday
#39;s game against the Chicago
Bears." + ], + [ + "The Florida Gators and
Arkansas Razorbacks meet for
just the sixth time Saturday.
The Gators hold a 4-1
advantage in the previous five
meetings, including last year
#39;s 33-28 win." + ], + [ + "AP - 1941 #151; Brooklyn
catcher Mickey Owen dropped a
third strike on Tommy Henrich
of what would have been the
last out of a Dodgers victory
against the New York Yankees.
Given the second chance, the
Yankees scored four runs for a
7-4 victory and a 3-1 lead in
the World Series, which they
ended up winning." + ], + [ + "Braves shortstop Rafael Furcal
was arrested on drunken
driving charges Friday, his
second D.U.I. arrest in four
years." + ], + [ + " ATHENS (Reuters) - Natalie
Coughlin's run of bad luck
finally took a turn for the
better when she won the gold
medal in the women's 100
meters backstroke at the
Athens Olympics Monday." + ], + [ + "AP - Pedro Feliz hit a
tiebreaking grand slam with
two outs in the eighth inning
for his fourth hit of the day,
and the Giants helped their
playoff chances with a 9-5
victory over the Los Angeles
Dodgers on Saturday." + ], + [ + "LEVERKUSEN/ROME, Dec 7 (SW) -
Dynamo Kiev, Bayer Leverkusen,
and Real Madrid all have a
good chance of qualifying for
the Champions League Round of
16 if they can get the right
results in Group F on
Wednesday night." + ], + [ + "Ed Hinkel made a diving,
fingertip catch for a key
touchdown and No. 16 Iowa
stiffened on defense when it
needed to most to beat Iowa
State 17-10 Saturday." + ], + [ + "During last Sunday #39;s
Nextel Cup race, amid the
ongoing furor over Dale
Earnhardt Jr. #39;s salty
language, NBC ran a commercial
for a show coming on later
that night called quot;Law
amp; Order: Criminal Intent." + ], + [ + "AP - After playing in hail,
fog and chill, top-ranked
Southern California finishes
its season in familiar
comfort. The Trojans (9-0, 6-0
Pacific-10) have two games at
home #151; against Arizona on
Saturday and Notre Dame on
Nov. 27 #151; before their
rivalry game at UCLA." + ], + [ + "The remnants of Hurricane
Jeanne rained out Monday's
game between the Mets and the
Atlanta Braves. It will be
made up Tuesday as part of a
doubleheader." + ], + [ + "AP - NASCAR is not expecting
any immediate changes to its
top-tier racing series
following the merger between
telecommunications giant
Sprint Corp. and Nextel
Communications Inc." + ], + [ + "Like wide-open races? You
#39;ll love the Big 12 North.
Here #39;s a quick morning
line of the Big 12 North as it
opens conference play this
weekend." + ], + [ + "Taquan Dean scored 22 points,
Francisco Garcia added 19 and
No. 13 Louisville withstood a
late rally to beat Florida
74-70 Saturday." + ], + [ + "NEW YORK -- This was all about
Athens, about redemption for
one and validation for the
other. Britain's Paula
Radcliffe, the fastest female
marathoner in history, failed
to finish either of her
Olympic races last summer.
South Africa's Hendrik Ramaala
was a five-ringed dropout,
too, reinforcing his
reputation as a man who could
go only half the distance." + ], + [ + "LONDON -- Ernie Els has set
his sights on an improved
putting display this week at
the World Golf Championships
#39; NEC Invitational in
Akron, Ohio, after the
disappointment of tying for
fourth place at the PGA
Championship last Sunday." + ], + [ + "Fiji #39;s Vijay Singh
replaced Tiger Woods as the
world #39;s No.1 ranked golfer
today by winning the PGA
Deutsche Bank Championship." + ], + [ + "LEIPZIG, Germany : Jurgen
Klinsmann enjoyed his first
home win as German manager
with his team defeating ten-
man Cameroon 3-0 in a friendly
match." + ], + [ + "AP - Kevin Brown had a chance
to claim a place in Yankees
postseason history with his
start in Game 7 of the AL
championship series." + ], + [ + "HOMESTEAD, Fla. -- Kurt Busch
got his first big break in
NASCAR by winning a 1999
talent audition nicknamed
quot;The Gong Show. quot; He
was selected from dozens of
unknown, young race drivers to
work for one of the sport
#39;s most famous team owners,
Jack Roush." + ], + [ + "Zurich, Switzerland (Sports
Network) - Former world No. 1
Venus Williams advanced on
Thursday and will now meet
Wimbledon champion Maria
Sharapova in the quarterfinals
at the \\$1." + ], + [ + "INDIA #39;S cricket chiefs
began a frenetic search today
for a broadcaster to show next
month #39;s home series
against world champion
Australia after cancelling a
controversial \\$US308 million
(\\$440 million) television
deal." + ], + [ + "The International Olympic
Committee (IOC) has urged
Beijing to ensure the city is
ready to host the 2008 Games
well in advance, an official
said on Wednesday." + ], + [ + "Virginia Tech scores 24 points
off four first-half turnovers
in a 55-6 wipeout of Maryland
on Thursday to remain alone
atop the ACC." + ], + [ + "AP - Martina Navratilova's
long, illustrious career will
end without an Olympic medal.
The 47-year-old Navratilova
and Lisa Raymond lost 6-4,
4-6, 6-4 on Thursday night to
fifth-seeded Shinobu Asagoe
and Ai Sugiyama of Japan in
the quarterfinals, one step
shy of the medal round." + ], + [ + "PSV Eindhoven re-established
their five-point lead at the
top of the Dutch Eredivisie
today with a 2-0 win at
Vitesse Arnhem. Former
Sheffield Wednesday striker
Gerald Sibon put PSV ahead in
the 56th minute" + ], + [ + "TODAY AUTO RACING 3 p.m. --
NASCAR Nextel Cup Sylvania 300
qualifying at N.H.
International Speedway,
Loudon, N.H., TNT PRO BASEBALL
7 p.m. -- Red Sox at New York
Yankees, Ch. 38, WEEI (850)
(on cable systems where Ch. 38
is not available, the game
will air on NESN); Chicago
Cubs at Cincinnati, ESPN 6
p.m. -- Eastern League finals:
..." + ], + [ + "MUMBAI, SEPTEMBER 21: The
Board of Control for Cricket
in India (BCCI) today informed
the Bombay High Court that it
was cancelling the entire
tender process regarding
cricket telecast rights as
also the conditional deal with
Zee TV." + ], + [ + "JEJU, South Korea : Grace Park
of South Korea won an LPGA
Tour tournament, firing a
seven-under-par 65 in the
final round of the
1.35-million dollar CJ Nine
Bridges Classic." + ], + [ + "AP - The NHL will lock out its
players Thursday, starting a
work stoppage that threatens
to keep the sport off the ice
for the entire 2004-05 season." + ], + [ + "When Paula Radcliffe dropped
out of the Olympic marathon
miles from the finish, she
sobbed uncontrollably.
Margaret Okayo knew the
feeling." + ], + [ + "First baseman Richie Sexson
agrees to a four-year contract
with the Seattle Mariners on
Wednesday." + ], + [ + "Notes and quotes from various
drivers following California
Speedway #39;s Pop Secret 500.
Jeff Gordon slipped to second
in points following an engine
failure while Jimmie Johnson
moved back into first." + ], + [ + " MEMPHIS, Tenn. (Sports
Network) - The Memphis
Grizzlies signed All-Star
forward Pau Gasol to a multi-
year contract on Friday.
Terms of the deal were not
announced." + ], + [ + "Andre Agassi brushed past
Jonas Bjorkman 6-3 6-4 at the
Stockholm Open on Thursday to
set up a quarter-final meeting
with Spanish sixth seed
Fernando Verdasco." + ], + [ + " BOSTON (Reuters) - Boston was
tingling with anticipation on
Saturday as the Red Sox
prepared to host Game One of
the World Series against the
St. Louis Cardinals and take a
step toward ridding
themselves of a hex that has
hung over the team for eight
decades." + ], + [ + "FOR the first time since his
appointment as Newcastle
United manager, Graeme Souness
has been required to display
the strong-arm disciplinary
qualities that, to Newcastle
directors, made" + ], + [ + "WHY IT HAPPENED Tom Coughlin
won his first game as Giants
coach and immediately
announced a fine amnesty for
all Giants. Just kidding." + ], + [ + "A second-place finish in his
first tournament since getting
married was good enough to
take Tiger Woods from third to
second in the world rankings." + ], + [ + " COLORADO SPRINGS, Colorado
(Reuters) - World 400 meters
champion Jerome Young has been
given a lifetime ban from
athletics for a second doping
offense, the U.S. Anti-Doping
Agency (USADA) said Wednesday." + ], + [ + "LONDON - In two years, Arsenal
will play their home matches
in the Emirates stadium. That
is what their new stadium at
Ashburton Grove will be called
after the Premiership
champions yesterday agreed to
the" + ], + [ + "KNOXVILLE, Tenn. -- Jason
Campbell threw for 252 yards
and two touchdowns, and No. 8
Auburn proved itself as a
national title contender by
overwhelming No. 10 Tennessee,
34-10, last night." + ], + [ + "WASHINGTON -- Another
Revolution season concluded
with an overtime elimination.
Last night, the Revolution
thrice rallied from deficits
for a 3-3 tie with D.C. United
in the Eastern Conference
final, then lost in the first-
ever Major League Soccer match
decided by penalty kicks." + ], + [ + "Dwyane Wade calls himself a
quot;sidekick, quot; gladly
accepting the role Kobe Bryant
never wanted in Los Angeles.
And not only does second-year
Heat point" + ], + [ + "World number one golfer Vijay
Singh of Fiji has captured his
eighth PGA Tour event of the
year with a win at the 84
Lumber Classic in Farmington,
Pennsylvania." + ], + [ + "The noise was deafening and
potentially unsettling in the
minutes before the start of
the men #39;s Olympic
200-meter final. The Olympic
Stadium crowd chanted
quot;Hellas!" + ], + [ + "Brazilian forward Ronaldinho
scored a sensational goal for
Barcelona against Milan,
making for a last-gasp 2-1.
The 24-year-old #39;s
brilliant left-foot hit at the
Nou Camp Wednesday night sent
Barcelona atop of Group F." + ], + [ + "Bee Staff Writer. SANTA CLARA
- Andre Carter #39;s back
injury has kept him out of the
49ers #39; lineup since Week
1. It #39;s also interfering
with him rooting on his alma
mater in person." + ], + [ + "AP - Kellen Winslow Jr. ended
his second NFL holdout Friday." + ], + [ + "HOUSTON - With only a few
seconds left in a certain
victory for Miami, Peyton
Manning threw a meaningless
6-yard touchdown pass to
Marvin Harrison to cut the
score to 24-15." + ], + [ + "DEADLY SCORER: Manchester
United #39;s Wayne Rooney
celebrating his three goals
against Fenerbahce this week
at Old Trafford. (AP)." + ], + [ + "You can feel the confidence
level, not just with Team
Canada but with all of Canada.
There #39;s every expectation,
from one end of the bench to
the other, that Martin Brodeur
is going to hold the fort." + ], + [ + "Heading into the first game of
a new season, every team has
question marks. But in 2004,
the Denver Broncos seemed to
have more than normal." + ], + [ + "Anaheim, Calif. - There is a
decidedly right lean to the
three-time champions of the
American League Central. In a
span of 26 days, the Minnesota
Twins lost the left side of
their infield to free agency." + ], + [ + "With the NFL trading deadline
set for 4 p.m. Tuesday,
Patriots coach Bill Belichick
said there didn't seem to be
much happening on the trade
front around the league." + ], + [ + "AP - David Beckham broke his
rib moments after scoring
England's second goal in
Saturday's 2-0 win over Wales
in a World Cup qualifying
game." + ], + [ + "Nothing changed at the top of
Serie A as all top teams won
their games to keep the
distance between one another
unaltered. Juventus came back
from behind against Lazio to
win thanks to another goal by
Ibrahimovic" + ], + [ + "RICHMOND, Va. Jeremy Mayfield
won his first race in over
four years, taking the
Chevrolet 400 at Richmond
International Raceway after
leader Kurt Busch ran out of
gas eight laps from the
finish." + ], + [ + "AP - Track star Marion Jones
filed a defamation lawsuit
Wednesday against the man
whose company is at the center
of a federal investigation
into illegal steroid use among
some of the nation's top
athletes." + ], + [ + "England #39;s players hit out
at cricket #39;s authorities
tonight and claimed they had
been used as quot;political
pawns quot; after the Zimbabwe
government produced a
spectacular U-turn to ensure
the controversial one-day
series will go ahead." + ], + [ + "AP - The Japanese won the
pregame home run derby. Then
the game started and the major
league All-Stars put their
bats to work. Back-to-back
home runs by Moises Alou and
Vernon Wells in the fourth
inning and by Johnny Estrada
and Brad Wilkerson in the
ninth powered the major
leaguers past the Japanese
stars 7-3 Sunday for a 3-0
lead in the eight-game series." + ], + [ + "With Chelsea losing their
unbeaten record and Manchester
United failing yet again to
win, William Hill now make
Arsenal red-hot 2/5 favourites
to retain the title." + ], + [ + "Theresa special bookcase in Al
Grohs office completely full
of game plans from his days in
the NFL. Green ones are from
the Jets." + ], + [ + "Newcastle ensured their place
as top seeds in Friday #39;s
third round UEFA Cup draw
after holding Sporting Lisbon
to a 1-1 draw at St James #39;
Park." + ], + [ + "CBC SPORTS ONLINE - Bode
Miller continued his
impressive 2004-05 World Cup
skiing season by winning a
night slalom race in
Sestriere, Italy on Monday." + ], + [ + "Damien Rhodes scored on a
2-yard run in the second
overtime, then Syracuse's
defense stopped Pittsburgh on
fourth and 1, sending the
Orange to a 38-31 victory
yesterday in Syracuse, N.Y." + ], + [ + "AP - Anthony Harris scored 18
of his career-high 23 points
in the second half to help
Miami upset No. 19 Florida
72-65 Saturday and give first-
year coach Frank Haith his
biggest victory." + ], + [ + "AP - The five cities looking
to host the 2012 Summer Games
submitted bids to the
International Olympic
Committee on Monday, entering
the final stage of a long
process in hopes of landing
one of the biggest prizes in
sports." + ], + [ + "The FIA has already cancelled
todays activities at Suzuka as
Super Typhoon Ma-On heads
towards the 5.807km circuit.
Saturday practice has been
cancelled altogether while
pre-qualifying and final
qualifying" + ], + [ + " GRAND PRAIRIE, Texas
(Reuters) - Betting on horses
was banned in Texas until as
recently as 1987. Times have
changed rapidly since.
Saturday, Lone Star Park race
track hosts the \\$14 million
Breeders Cup, global racing's
end-of-season extravaganza." + ], + [ + "It might be a stay of
execution for Coach P, or it
might just be a Christmas
miracle come early. SU #39;s
upset win over BC has given
hope to the Orange playing in
a post season Bowl game." + ], + [ + "PHIL Neville insists
Manchester United don #39;t
fear anyone in the Champions
League last 16 and declared:
quot;Bring on the Italians." + ], + [ + "TORONTO -- Toronto Raptors
point guard Alvin Williams
will miss the rest of the
season after undergoing
surgery on his right knee
Monday." + ], + [ + "AP - Tennessee's two freshmen
quarterbacks have Volunteers
fans fantasizing about the
next four years. Brent
Schaeffer and Erik Ainge
surprised many with the nearly
seamless way they rotated
throughout a 42-17 victory
over UNLV on Sunday night." + ], + [ + "MADRID, Aug 18 (Reuters) -
Portugal captain Luis Figo
said on Wednesday he was
taking an indefinite break
from international football,
but would not confirm whether
his decision was final." + ], + [ + "AP - Shaun Rogers is in the
backfield as often as some
running backs. Whether teams
dare to block Detroit's star
defensive tackle with one
player or follow the trend of
double-teaming him, he often
rips through offensive lines
with a rare combination of
size, speed, strength and
nimble footwork." + ], + [ + "The Lions and Eagles entered
Sunday #39;s game at Ford
Field in the same place --
atop their respective
divisions -- and with
identical 2-0 records." + ], + [ + "After Marcos Moreno threw four
more interceptions in last
week's 14-13 overtime loss at
N.C. A T, Bison Coach Ray
Petty will start Antoine
Hartfield against Norfolk
State on Saturday." + ], + [ + "SOUTH WILLIAMSPORT, Pa. --
Looking ahead to the US
championship game almost cost
Conejo Valley in the
semifinals of the Little
League World Series." + ], + [ + "The Cubs didn #39;t need to
fly anywhere near Florida to
be in the eye of the storm.
For a team that is going on
100 years since last winning a
championship, the only thing
they never are at a loss for
is controversy." + ], + [ + "KETTERING, Ohio Oct. 12, 2004
- Cincinnati Bengals defensive
end Justin Smith pleaded not
guilty to a driving under the
influence charge." + ], + [ + "MINNEAPOLIS -- For much of the
2004 season, Twins pitcher
Johan Santana didn #39;t just
beat opposing hitters. Often,
he overwhelmed and owned them
in impressive fashion." + ], + [ + "At a charity auction in New
Jersey last weekend, baseball
memorabilia dealer Warren
Heller was approached by a man
with an unusual but topical
request." + ], + [ + "INTER Milan coach Roberto
Mancini believes the club
#39;s lavish (northern) summer
signings will enable them to
mount a serious Serie A
challenge this season." + ], + [ + "AP - Brad Ott shot an 8-under
64 on Sunday to win the
Nationwide Tour's Price Cutter
Charity Championship for his
first Nationwide victory." + ], + [ + "AP - New York Jets running
back Curtis Martin passed Eric
Dickerson and Jerome Bettis on
the NFL career rushing list
Sunday against the St. Louis
Rams, moving to fourth all-
time." + ], + [ + "Instead of standing for ante
meridian and post meridian,
though, fans will remember the
time periods of pre-Mia and
after-Mia. After playing for
18 years and shattering nearly
every record" + ], + [ + "Stephon Marbury, concerned
about his lousy shooting in
Athens, used an off day to go
to the gym and work on his
shot. By finding his range, he
saved the United States #39;
hopes for a basketball gold
medal." + ], + [ + "AP - Randy Moss is expected to
play a meaningful role for the
Minnesota Vikings this weekend
against the Giants, even
without a fully healed right
hamstring." + ], + [ + "Pros: Fits the recent profile
(44, past PGA champion, fiery
Ryder Cup player); the job is
his if he wants it. Cons:
Might be too young to be
willing to burn two years of
play on tour." + ], + [ + "Elmer Santos scored in the
second half, lifting East
Boston to a 1-0 win over
Brighton yesterday afternoon
and giving the Jets an early
leg up in what is shaping up
to be a tight Boston City
League race." + ], + [ + "Saints special teams captain
Steve Gleason expects to be
fined by the league after
being ejected from Sunday's
game against the Carolina
Panthers for throwing a punch." + ], + [ + "Play has begun in the
Australian Masters at
Huntingdale in Melbourne with
around half the field of 120
players completing their first
rounds." + ], + [ + "LOUISVILLE, Ky. - Louisville
men #39;s basketball head
coach Rick Pitino and senior
forward Ellis Myles met with
members of the media on Friday
to preview the Cardinals #39;
home game against rival
Kentucky on Satursday." + ], + [ + "AP - Sounds like David
Letterman is as big a \"Pops\"
fan as most everyone else." + ], + [ + "Arsenal was held to a 1-1 tie
by struggling West Bromwich
Albion on Saturday, failing to
pick up a Premier League
victory when Rob Earnshaw
scored with 11 minutes left." + ], + [ + "Baseball #39;s executive vice
president Sandy Alderson
insisted last month that the
Cubs, disciplined for an
assortment of run-ins with
umpires, would not be targeted
the rest of the season by
umpires who might hold a
grudge." + ], + [ + "As Superman and Batman would
no doubt reflect during their
cigarette breaks, the really
draining thing about being a
hero was that you have to keep
riding to the rescue." + ], + [ + "AP - Eric Hinske and Vernon
Wells homered, and the Toronto
Blue Jays completed a three-
game sweep of the Baltimore
Orioles with an 8-5 victory
Sunday." + ], + [ + " NEW YORK (Reuters) - Todd
Walker homered, had three hits
and drove in four runs to lead
the Chicago Cubs to a 12-5 win
over the Cincinnati Reds in
National League play at
Wrigley Field on Monday." + ], + [ + "MIAMI (Sports Network) -
Shaquille O #39;Neal made his
home debut, but once again it
was Dwyane Wade stealing the
show with 28 points as the
Miami Heat downed the
Cleveland Cavaliers, 92-86, in
front of a record crowd at
AmericanAirlines Arena." + ], + [ + "AP - The San Diego Chargers
looked sharp #151; and played
the same way. Wearing their
powder-blue throwback jerseys
and white helmets from the
1960s, the Chargers did almost
everything right in beating
the Jacksonville Jaguars 34-21
on Sunday." + ], + [ + "NERVES - no problem. That
#39;s the verdict of Jose
Mourinho today after his
Chelsea side gave a resolute
display of character at
Highbury." + ], + [ + "AP - The latest low point in
Ron Zook's tenure at Florida
even has the coach wondering
what went wrong. Meanwhile,
Sylvester Croom's first big
win at Mississippi State has
given the Bulldogs and their
fans a reason to believe in
their first-year leader.
Jerious Norwood's 37-yard
touchdown run with 32 seconds
remaining lifted the Bulldogs
to a 38-31 upset of the 20th-
ranked Gators on Saturday." + ], + [ + "Someone forgot to inform the
US Olympic basketball team
that it was sent to Athens to
try to win a gold medal, not
to embarrass its country." + ], + [ + "Ten-man Paris St Germain
clinched their seventh
consecutive victory over arch-
rivals Olympique Marseille
with a 2-1 triumph in Ligue 1
on Sunday thanks to a second-
half winner by substitute
Edouard Cisse." + ], + [ + "Roy Oswalt wasn #39;t
surprised to hear the Astros
were flying Sunday night
through the remnants of a
tropical depression that
dumped several inches of rain
in Louisiana and could bring
showers today in Atlanta." + ], + [ + "AP - This hardly seemed
possible when Pitt needed
frantic rallies to overcome
Division I-AA Furman or Big
East cellar dweller Temple. Or
when the Panthers could barely
move the ball against Ohio
#151; not Ohio State, but Ohio
U." + ], + [ + "Everyone is moaning about the
fallout from last weekend but
they keep on reporting the
aftermath. #39;The fall-out
from the so-called
quot;Battle of Old Trafford
quot; continues to settle over
the nation and the debate" + ], + [ + "American Lindsay Davenport
regained the No. 1 ranking in
the world for the first time
since early 2002 by defeating
Dinara Safina of Russia 6-4,
6-2 in the second round of the
Kremlin Cup on Thursday." + ], + [ + "Freshman Jeremy Ito kicked
four field goals and Ryan
Neill scored on a 31-yard
interception return to lead
improving Rutgers to a 19-14
victory on Saturday over
visiting Michigan State." + ], + [ + "Third-seeded Guillermo Canas
defeated Guillermo Garcia-
Lopez of Spain 7-6 (1), 6-3
Monday on the first day of the
Shanghai Open on Monday." + ], + [ + "But to play as feebly as it
did for about 35 minutes last
night in Game 1 of the WNBA
Finals and lose by only four
points -- on the road, no less
-- has to be the best
confidence builder since Cindy
St." + ], + [ + "With yesterday #39;s report on
its athletic department
violations completed, the
University of Washington says
it is pleased to be able to
move forward." + ], + [ + "Sammy Sosa was fined \\$87,400
-- one day's salary -- for
arriving late to the Cubs'
regular-season finale at
Wrigley Field and leaving the
game early. The slugger's
agent, Adam Katz , said
yesterday Sosa most likely
will file a grievance. Sosa
arrived 70 minutes before
Sunday's first pitch, and he
apparently left 15 minutes
after the game started without
..." + ], + [ + "Is the Oklahoma defense a
notch below its predecessors?
Is Texas #39; offense a step-
ahead? Why is Texas Tech
feeling good about itself
despite its recent loss?" + ], + [ + "New York gets 57 combined
points from its starting
backcourt of Jamal Crawford
and Stephon Marbury and tops
Denver, 107-96." + ], + [ + "AP - Shawn Marion had a
season-high 33 points and 15
rebounds, leading the Phoenix
Suns on a fourth-quarter
comeback despite the absence
of Steve Nash in a 95-86 win
over the New Orleans Hornets
on Friday night." + ], + [ + "Messina upset defending
champion AC Milan 2-1
Wednesday, while Juventus won
its third straight game to
stay alone atop the Italian
league standings." + ], + [ + "AP - Padraig Harrington
rallied to a three-stroke
victory in the German Masters
on a windy Sunday, closing
with a 2-under-par 70 and
giving his game a big boost
before the Ryder Cup." + ], + [ + "Ironically it was the first
regular season game for the
Carolina Panthers that not
only began the history of the
franchise, but also saw the
beginning of a rivalry that
goes on to this day." + ], + [ + "Baltimore Ravens running back
Jamal Lewis did not appear at
his arraignment Friday, but
his lawyers entered a not
guilty plea on charges in an
expanded drug conspiracy
indictment." + ], + [ + "After serving a five-game
suspension, Milton Bradley
worked out with the Dodgers as
they prepared for Tuesday's
opener against the St. Louis
Cardinals." + ], + [ + "AP - Prime Time won't be
playing in prime time this
time. Deion Sanders was on the
inactive list and missed a
chance to strut his stuff on
\"Monday Night Football.\"" + ], + [ + "The man who delivered the
knockout punch was picked up
from the Seattle scrap heap
just after the All-Star Game.
Before that, John Olerud
certainly hadn't figured on
facing Pedro Martinez in
Yankee Stadium in October." + ], + [ + "LONDON : A consortium,
including former world
champion Nigel Mansell, claims
it has agreed terms to ensure
Silverstone remains one of the
venues for the 2005 Formula
One world championship." + ], + [ + " BATON ROUGE, La. (Sports
Network) - LSU has named Les
Miles its new head football
coach, replacing Nick Saban." + ], + [ + "AP - The Chicago Blackhawks
re-signed goaltender Michael
Leighton to a one-year
contract Wednesday." + ], + [ + "ATHENS -- She won her first
Olympic gold medal in kayaking
when she was 18, the youngest
paddler to do so in Games
history. Yesterday, at 42,
Germany #39;s golden girl
Birgit Fischer won her eighth
Olympic gold in the four-woman
500-metre kayak race." + ], + [ + "England boss Sven Goran
Eriksson has defended
goalkeeper David James after
last night #39;s 2-2 draw in
Austria. James allowed Andreas
Ivanschitz #39;s shot to slip
through his fingers to
complete Austria comeback from
two goals down." + ], + [ + "The quot;future quot; is
getting a chance to revive the
presently struggling New York
Giants. Two other teams also
decided it was time for a
change at quarterback, but the
Buffalo Bills are not one of
them." + ], + [ + "Flushing Meadows, NY (Sports
Network) - The men #39;s
semifinals at the 2004 US Open
will be staged on Saturday,
with three of the tournament
#39;s top-five seeds ready for
action at the USTA National
Tennis Center." + ], + [ + "Given nearly a week to examine
the security issues raised by
the now-infamous brawl between
players and fans in Auburn
Hills, Mich., Nov. 19, the
Celtics returned to the
FleetCenter last night with
two losses and few concerns
about their on-court safety." + ], + [ + "Arsenals Thierry Henry today
missed out on the European
Footballer of the Year award
as Andriy Shevchenko took the
honour. AC Milan frontman
Shevchenko held off
competition from Barcelona
pair Deco and" + ], + [ + "Neil Mellor #39;s sensational
late winner for Liverpool
against Arsenal on Sunday has
earned the back-up striker the
chance to salvage a career
that had appeared to be
drifting irrevocably towards
the lower divisions." + ], + [ + "ABOUT 70,000 people were
forced to evacuate Real Madrid
#39;s Santiago Bernabeu
stadium minutes before the end
of a Primera Liga match
yesterday after a bomb threat
in the name of ETA Basque
separatist guerillas." + ], + [ + "The team learned on Monday
that full-back Jon Ritchie
will miss the rest of the
season with a torn anterior
cruciate ligament in his left
knee." + ], + [ + "Venus Williams barely kept
alive her hopes of qualifying
for next week #39;s WTA Tour
Championships. Williams,
seeded fifth, survived a
third-set tiebreaker to
outlast Yuilana Fedak of the
Ukraine, 6-4 2-6 7-6" + ], + [ + " SYDNEY (Reuters) - Arnold
Palmer has taken a swing at
America's top players,
criticizing their increasing
reluctance to travel abroad
to play in tournaments." + ], + [ + "Although he was well-beaten by
Retief Goosen in Sunday #39;s
final round of The Tour
Championship in Atlanta, there
has been some compensation for
the former world number one,
Tiger Woods." + ], + [ + "WAYNE Rooney and Henrik
Larsson are among the players
nominated for FIFAs
prestigious World Player of
the Year award. Rooney is one
of four Manchester United
players on a list which is
heavily influenced by the
Premiership." + ], + [ + "It didn #39;t look good when
it happened on the field, and
it looked worse in the
clubhouse. Angels second
baseman Adam Kennedy left the
Angels #39; 5-2 win over the
Seattle Mariners" + ], + [ + "Freshman Darius Walker ran for
115 yards and scored two
touchdowns, helping revive an
Irish offense that had managed
just one touchdown in the
season's first six quarters." + ], + [ + "AP - NBC is adding a 5-second
delay to its NASCAR telecasts
after Dale Earnhardt Jr. used
a vulgarity during a postrace
TV interview last weekend." + ], + [ + " BOSTON (Sports Network) - The
New York Yankees will start
Orlando \"El Duque\" Hernandez
in Game 4 of the American
League Championship Series on
Saturday against the Boston
Red Sox." + ], + [ + "The future of Sven-Goran
Eriksson as England coach is
the subject of intense
discussion after the draw in
Austria. Has the Swede lost
the confidence of the nation
or does he remain the best man
for the job?" + ], + [ + "Spain begin their third final
in five seasons at the Olympic
stadium hoping to secure their
second title since their first
in Barcelona against Australia
in 2000." + ], + [ + "Second-seeded David Nalbandian
of Argentina lost at the Japan
Open on Thursday, beaten by
Gilles Muller of Luxembourg
7-6 (4), 3-6, 6-4 in the third
round." + ], + [ + "Thursday #39;s unexpected
resignation of Memphis
Grizzlies coach Hubie Brown
left a lot of questions
unanswered. In his unique way
of putting things, the
71-year-old Brown seemed to
indicate he was burned out and
had some health concerns." + ], + [ + "RUDI Voller had quit as coach
of Roma after a 3-1 defeat
away to Bologna, the Serie A
club said today. Under the
former Germany coach, Roma had
taken just four league points
from a possible 12." + ], + [ + "ONE by one, the players #39;
faces had flashed up on the
giant Ibrox screens offering
season #39;s greetings to the
Rangers fans. But the main
presents were reserved for
Auxerre." + ], + [ + "South Korea #39;s Grace Park
shot a seven-under-par 65 to
triumph at the CJ Nine Bridges
Classic on Sunday. Park #39;s
victory made up her final-
round collapse at the Samsung
World Championship two weeks
ago." + ], + [ + "Titans QB Steve McNair was
released from a Nashville
hospital after a two-night
stay for treatment of a
bruised sternum. McNair was
injured during the fourth
quarter of the Titans #39;
15-12 loss to Jacksonville on
Sunday." + ], + [ + "Keith Miller, Australia #39;s
most prolific all-rounder in
Test cricket, died today at a
nursing home, Cricket
Australia said. He was 84." + ], + [ + "TORONTO Former Toronto pitcher
John Cerutti (seh-ROO
#39;-tee) was found dead in
his hotel room today,
according to the team. He was
44." + ], + [ + "Defense: Ken Lucas. His
biggest play was his first
one. The fourth-year
cornerback intercepted a Ken
Dorsey pass that kissed off
the hands of wide receiver
Rashaun Woods and returned it
25 yards to set up the
Seahawks #39; first score." + ], + [ + "The powerful St. Louis trio of
Albert Pujols, Scott Rolen and
Jim Edmonds is 4 for 23 with
one RBI in the series and with
runners on base, they are 1
for 13." + ], + [ + "The Jets signed 33-year-old
cornerback Terrell Buckley,
who was released by New
England on Sunday, after
putting nickel back Ray
Mickens on season-ending
injured reserve yesterday with
a torn ACL in his left knee." + ], + [ + "WEST INDIES thrilling victory
yesterday in the International
Cricket Council Champions
Trophy meant the world to the
five million people of the
Caribbean." + ], + [ + "Scotland manager Berti Vogts
insists he is expecting
nothing but victory against
Moldova on Wednesday. The game
certainly is a must-win affair
if the Scots are to have any
chance of qualifying for the
2006 World Cup finals." + ], + [ + " INDIANAPOLIS (Reuters) -
Jenny Thompson will take the
spotlight from injured U.S.
team mate Michael Phelps at
the world short course
championships Saturday as she
brings down the curtain on a
spectacular swimming career." + ], + [ + "Martin Brodeur made 27 saves,
and Brad Richards, Kris Draper
and Joe Sakic scored goals to
help Canada beat Russia 3-1
last night in the World Cup of
Hockey, giving the Canadians a
3-0 record in round-robin
play." + ], + [ + "ST. LOUIS -- Mike Martz #39;s
week of anger was no empty
display. He saw the defending
NFC West champions slipping
and thought taking potshots at
his players might be his best
shot at turning things around." + ], + [ + "Romanian soccer star Adrian
Mutu as he arrives at the
British Football Association
in London, ahead of his
disciplinary hearing, Thursday
Nov. 4, 2004." + ], + [ + "Australia completed an
emphatic Test series sweep
over New Zealand with a
213-run win Tuesday, prompting
a caution from Black Caps
skipper Stephen Fleming for
other cricket captains around
the globe." + ], + [ + "Liverpool, England (Sports
Network) - Former English
international and Liverpool
great Emlyn Hughes passed away
Tuesday from a brain tumor." + ], + [ + "JACKSONVILLE, Fla. -- They
were singing in the Colts #39;
locker room today, singing
like a bunch of wounded
songbirds. Never mind that
Marcus Pollard, Dallas Clark
and Ben Hartsock won #39;t be
recording a remake of Kenny
Chesney #39;s song,
quot;Young, quot; any time
soon." + ], + [ + "As the close-knit NASCAR
community mourns the loss of
team owner Rick Hendrick #39;s
son, brother, twin nieces and
six others in a plane crash
Sunday, perhaps no one outside
of the immediate family
grieves more deeply than
Darrell Waltrip." + ], + [ + "AP - Purdue quarterback Kyle
Orton has no trouble
remembering how he felt after
last year's game at Michigan." + ], + [ + "Brown is a second year player
from Memphis and has spent the
2004 season on the Steelers
#39; practice squad. He played
in two games last year." + ], + [ + "Barret Jackman, the last of
the Blues left to sign before
the league #39;s probable
lockout on Wednesday,
finalized a deal Monday that
is rare in the current
economic climate but fitting
for him." + ], + [ + "AP - In the tumult of the
visitors' clubhouse at Yankee
Stadium, champagne pouring all
around him, Theo Epstein held
a beer. \"I came in and there
was no champagne left,\" he
said this week. \"I said, 'I'll
have champagne if we win it
all.'\" Get ready to pour a
glass of bubbly for Epstein.
No I.D. necessary." + ], + [ + "Barcelona held on from an
early Deco goal to edge game
local rivals Espanyol 1-0 and
carve out a five point
tabletop cushion. Earlier,
Ronaldo rescued a point for
Real Madrid, who continued
their middling form with a 1-1
draw at Real Betis." + ], + [ + "MONTREAL (CP) - The Expos may
be history, but their demise
has heated up the market for
team memorabilia. Vintage
1970s and 1980s shirts are
already sold out, but
everything from caps, beer
glasses and key-chains to
dolls of mascot Youppi!" + ], + [ + "A 76th minute goal from
European Footballer of the
Year Pavel Nedved gave
Juventus a 1-0 win over Bayern
Munich on Tuesday handing the
Italians clear control at the
top of Champions League Group
C." + ], + [ + "ANN ARBOR, Mich. -- Some NHL
players who took part in a
charity hockey game at the
University of Michigan on
Thursday were hopeful the news
that the NHL and the players
association will resume talks
next week" + ], + [ + "BOULDER, Colo. -- Vernand
Morency ran for 165 yards and
two touchdowns and Donovan
Woods threw for three more
scores, lifting No. 22
Oklahoma State to a 42-14
victory over Colorado
yesterday." + ], + [ + "PULLMAN - Last week, in
studying USC game film, Cougar
coaches thought they found a
chink in the national
champions armor. And not just
any chink - one with the
potential, right from the get
go, to" + ], + [ + "AP - Matt Leinart was quite a
baseball prospect growing up,
showing so much promise as a
left-handed pitcher that
scouts took notice before high
school." + ], + [ + "LSU will stick with a two-
quarterback rotation Saturday
at Auburn, according to Tigers
coach Nick Saban, who seemed
to have some fun telling the
media what he will and won
#39;t discuss Monday." + ], + [ + "Seattle -- - Not so long ago,
the 49ers were inflicting on
other teams the kind of pain
and embarrassment they felt in
their 34-0 loss to the
Seahawks on Sunday." + ], + [ + "London - Manchester City held
fierce crosstown rivals
Manchester United to a 0-0
draw on Sunday, keeping the
Red Devils eleven points
behind leaders Chelsea." + ], + [ + "New Ole Miss head coach Ed
Orgeron, speaking for the
first time since his hiring,
made clear the goal of his
football program. quot;The
goal of this program will be
to go to the Sugar Bowl, quot;
Orgeron said." + ], + [ + "It is much too easy to call
Pedro Martinez the selfish
one, to say he is walking out
on the Red Sox, his baseball
family, for the extra year of
the Mets #39; crazy money." + ], + [ + "It is impossible for young
tennis players today to know
what it was like to be Althea
Gibson and not to be able to
quot;walk in the front door,
quot; Garrison said." + ], + [ + "Here #39;s an obvious word of
advice to Florida athletic
director Jeremy Foley as he
kicks off another search for
the Gators football coach: Get
Steve Spurrier on board." + ], + [ + "Amid the stormy gloom in
Gotham, the rain-idled Yankees
last night had plenty of time
to gather in front of their
televisions and watch the Red
Sox Express roar toward them.
The national telecast might
have been enough to send a
jittery Boss Steinbrenner
searching his Bartlett's
Familiar Quotations for some
quot;Little Engine That Could
quot; metaphor." + ], + [ + "FULHAM fans would have been
singing the late Elvis #39;
hit #39;The wonder of you
#39; to their player Elvis
Hammond. If not for Frank
Lampard spoiling the party,
with his dedication to his
late grandfather." + ], + [ + "A bird #39;s eye view of the
circuit at Shanghai shows what
an event Sunday #39;s Chinese
Grand Prix will be. The course
is arguably one of the best
there is, and so it should be
considering the amount of
money that has been spent on
it." + ], + [ + "AP - Sirius Satellite Radio
signed a deal to air the men's
NCAA basketball tournament
through 2007, the latest move
made in an attempt to draw
customers through sports
programming." + ], + [ + "(Sports Network) - The
inconsistent San Diego Padres
will try for consecutive wins
for the first time since
August 28-29 tonight, when
they begin a huge four-game
set against the Los Angeles
Dodgers at Dodger Stadium." + ], + [ + " NEW YORK (Reuters) - Top seed
Roger Federer survived a
stirring comeback from twice
champion Andre Agassi to reach
the semifinals of the U.S.
Open for the first time on
Thursday, squeezing through
6-3, 2-6, 7-5, 3-6, 6-3." + ], + [ + "SAN FRANCISCO - What Babe Ruth
was to the first half of the
20th century and Hank Aaron
was to the second, Barry Bonds
has become for the home run
generation." + ], + [ + "Birgit Fischer settled for
silver, leaving the 42-year-
old Olympian with two medals
in two days against decidedly
younger competition." + ], + [ + "There was no mystery ... no
secret strategy ... no baited
trap that snapped shut and
changed the course of history
#39;s most lucrative non-
heavyweight fight." + ], + [ + "Notre Dame accepted an
invitation Sunday to play in
the Insight Bowl in Phoenix
against a Pac-10 team on Dec.
28. The Irish (6-5) accepted
the bid a day after losing to
Southern California" + ], + [ + "Greg Anderson has so dominated
Pro Stock this season that his
championship quest has evolved
into a pursuit of NHRA
history. By Bob Hesser, Racers
Edge Photography." + ], + [ + "Michael Powell, chairman of
the FCC, said Wednesday he was
disappointed with ABC for
airing a sexually suggestive
opening to \"Monday Night
Football.\"" + ], + [ + "Former Washington football
coach Rick Neuheisel looked
forward to a return to
coaching Wednesday after being
cleared by the NCAA of
wrongdoing related to his
gambling on basketball games." + ], + [ + "AP - Rutgers basketball player
Shalicia Hurns was suspended
from the team after pleading
guilty to punching and tying
up her roommate during a
dispute over painkilling
drugs." + ], + [ + "This was the event Michael
Phelps didn't really need to
compete in if his goal was to
win eight golds. He probably
would have had a better chance
somewhere else." + ], + [ + "Gabe Kapler became the first
player to leave the World
Series champion Boston Red
Sox, agreeing to a one-year
contract with the Yomiuri
Giants in Tokyo." + ], + [ + "BALI, Indonesia - Svetlana
Kuznetsova, fresh off her
championship at the US Open,
defeated Australian qualifier
Samantha Stosur 6-4, 6-4
Thursday to reach the
quarterfinals of the Wismilak
International." + ], + [ + "AP - Just like the old days in
Dallas, Emmitt Smith made life
miserable for the New York
Giants on Sunday." + ], + [ + "TAMPA, Fla. - Chris Simms
first NFL start lasted 19
plays, and it might be a while
before he plays again for the
Tampa Bay Buccaneers." + ], + [ + "Jarno Trulli made the most of
the conditions in qualifying
to claim pole ahead of Michael
Schumacher, while Fernando
finished third." + ], + [ + "AP - Authorities are
investigating whether bettors
at New York's top thoroughbred
tracks were properly informed
when jockeys came in
overweight at races, a source
familiar with the probe told
The Associated Press." + ], + [ + "Michael Owen scored his first
goal for Real Madrid in a 1-0
home victory over Dynamo Kiev
in the Champions League. The
England striker toe-poked home
Ronaldo #39;s cross in the
35th minute to join the
Russians" + ], + [ + "Jason Giambi has returned to
the New York Yankees'
clubhouse but is still
clueless as to when he will be
able to play again." + ], + [ + "Seventy-five National Hockey
League players met with union
leaders yesterday to get an
update on a lockout that shows
no sign of ending." + ], + [ + "Howard, at 6-4 overall and 3-3
in the Mid-Eastern Athletic
Conference, can clinch a
winning record in the MEAC
with a win over Delaware State
on Saturday." + ], + [ + " ATHENS (Reuters) - Top-ranked
Argentina booked their berth
in the women's hockey semi-
finals at the Athens Olympics
on Friday but defending
champions Australia now face
an obstacle course to qualify
for the medal matches." + ], + [ + "The Notre Dame message boards
are no longer discussing
whether Tyrone Willingham
should be fired. Theyre
already arguing about whether
the next coach should be Barry
Alvarez or Steve Spurrier." + ], + [ + " THOMASTOWN, Ireland (Reuters)
- World number three Ernie
Els overcame difficult weather
conditions to fire a sparkling
eight-under-par 64 and move
two shots clear after two
rounds of the WGC-American
Express Championship Friday." + ], + [ + "Lamar Odom supplemented 20
points with 13 rebounds and
Kobe Bryant added 19 points to
overcome a big night from Yao
Ming as the Los Angeles Lakers
ground out an 84-79 win over
the Rockets in Houston
Saturday." + ], + [ + "It rained Sunday, of course,
and but another soppy, sloppy
gray day at Westside Tennis
Club did nothing to deter
Roger Federer from his
appointed rounds." + ], + [ + "Amelie Mauresmo was handed a
place in the Advanta
Championships final after
Maria Sharapova withdrew from
their semi-final because of
injury." + ], + [ + " EAST RUTHERFORD, New Jersey
(Sports Network) - Retired NBA
center and seven-time All-
Star Alonzo Mourning is going
to give his playing career
one more shot." + ], + [ + "Jordan have confirmed that
Timo Glock will replace
Giorgio Pantano for this
weekend #39;s Chinese GP as
the team has terminated its
contract with Pantano." + ], + [ + "The continuing heartache of
Wake Forest #39;s ACC football
season was best described by
fifth-ranked Florida State
coach Bobby Bowden, after his
Seminoles had edged the
Deacons 20-17 Saturday at
Groves Stadium." + ], + [ + "THE glory days have returned
to White Hart Lane. When Spurs
new first-team coach Martin
Jol promised a return to the
traditions of the 1960s,
nobody could have believed he
was so determined to act so
quickly and so literally." + ], + [ + "AP - When Paula Radcliffe
dropped out of the Olympic
marathon miles from the
finish, she sobbed
uncontrollably. Margaret Okayo
knew the feeling. Okayo pulled
out of the marathon at the
15th mile with a left leg
injury, and she cried, too.
When she watched Radcliffe
quit, Okayo thought, \"Let's
cry together.\"" + ], + [ + " ATLANTA (Sports Network) -
The Atlanta Hawks signed free
agent Kevin Willis on
Wednesday, nearly a decade
after the veteran big man
ended an 11- year stint with
the team." + ], + [ + "When his right-front tire went
flying off early in the Ford
400, the final race of the
NASCAR Nextel Cup Series
season, Kurt Busch, it seemed,
was destined to spend his
offseason" + ], + [ + "Brandon Backe and Woody
Williams pitched well last
night even though neither
earned a win. But their
outings will show up in the
record books." + ], + [ + "The Football Association today
decided not to charge David
Beckham with bringing the game
into disrepute. The FA made
the surprise announcement
after their compliance unit
ruled" + ], + [ + " CRANS-SUR-SIERRE, Switzerland
(Reuters) - World number
three Ernie Els says he feels
a failure after narrowly
missing out on three of the
year's four major
championships." + ], + [ + "Striker Bonaventure Kalou
netted twice to send AJ
Auxerre through to the first
knockout round of the UEFA Cup
at the expense of Rangers on
Wednesday." + ], + [ + "THIS weekend sees the
quot;other quot; showdown
between New York and New
England as the Jets and
Patriots clash in a battle of
the unbeaten teams." + ], + [ + "Nicolas Anelka is fit for
Manchester City #39;s
Premiership encounter against
Tottenham at Eastlands, but
the 13million striker will
have to be content with a
place on the bench." + ], + [ + " PITTSBURGH (Reuters) - Ben
Roethlisberger passed for 183
yards and two touchdowns,
Hines Ward scored twice and
the Pittsburgh Steelers
rolled to a convincing 27-3
victory over Philadelphia on
Sunday for their second
straight win against an
undefeated opponent." + ], + [ + " ATHENS (Reuters) - The U.S.
men's basketball team got
their first comfortable win
at the Olympic basketball
tournament Monday, routing
winless Angola 89-53 in their
final preliminary round game." + ], + [ + "Often, the older a pitcher
becomes, the less effective he
is on the mound. Roger Clemens
apparently didn #39;t get that
memo. On Tuesday, the 42-year-
old Clemens won an
unprecedented" + ], + [ + "PSV Eindhoven faces Arsenal at
Highbury tomorrow night on the
back of a free-scoring start
to the season. Despite losing
Mateja Kezman to Chelsea in
the summer, the Dutch side has
scored 12 goals in the first" + ], + [ + "PLAYER OF THE GAME: Playing
with a broken nose, Seattle
point guard Sue Bird set a
WNBA playoff record for
assists with 14, also pumping
in 10 points as the Storm
claimed the Western Conference
title last night." + ], + [ + "Frankfurt - World Cup winners
Brazil were on Monday drawn to
meet European champions
Greece, Gold Cup winners
Mexico and Asian champions
Japan at the 2005
Confederations Cup." + ], + [ + "Third baseman Vinny Castilla
said he fits fine with the
Colorado youth movement, even
though he #39;ll turn 38 next
season and the Rockies are
coming off the second-worst" + ], + [ + "(Sports Network) - Two of the
top teams in the American
League tangle in a possible
American League Division
Series preview tonight, as the
West-leading Oakland Athletics
host the wild card-leading
Boston Red Sox for the first
of a three-game set at the" + ], + [ + "Inverness Caledonian Thistle
appointed Craig Brewster as
its new manager-player
Thursday although he #39;s
unable to play for the team
until January." + ], + [ + "ATHENS, Greece -- Alan Shearer
converted an 87th-minute
penalty to give Newcastle a
1-0 win over Panionios in
their UEFA Cup Group D match." + ], + [ + "Derek Jeter turned a season
that started with a terrible
slump into one of the best in
his accomplished 10-year
career. quot;I don #39;t
think there is any question,
quot; the New York Yankees
manager said." + ], + [ + "China's Guo Jingjing easily
won the women's 3-meter
springboard last night, and Wu
Minxia made it a 1-2 finish
for the world's diving
superpower, taking the silver." + ], + [ + "GREEN BAY, Wisconsin (Ticker)
-- Brett Favre will be hoping
his 200th consecutive start
turns out better than his last
two have against the St." + ], + [ + "When an NFL team opens with a
prolonged winning streak,
former Miami Dolphins coach
Don Shula and his players from
the 17-0 team of 1972 root
unabashedly for the next
opponent." + ], + [ + "MIANNE Bagger, the transsexual
golfer who prompted a change
in the rules to allow her to
compete on the professional
circuit, made history
yesterday by qualifying to
play full-time on the Ladies
European Tour." + ], + [ + "Great Britain #39;s gold medal
tally now stands at five after
Leslie Law was handed the
individual three day eventing
title - in a courtroom." + ], + [ + "Mayor Tom Menino must be
proud. His Boston Red Sox just
won their first World Series
in 86 years and his Hyde Park
Blue Stars yesterday clinched
their first Super Bowl berth
in 32 years, defeating
O'Bryant, 14-0. Who would have
thought?" + ], + [ + "South Korea have appealed to
sport #39;s supreme legal body
in an attempt to award Yang
Tae-young the Olympic
gymnastics all-round gold
medal after a scoring error
robbed him of the title in
Athens." + ], + [ + "AP - Johan Santana had an
early lead and was well on his
way to his 10th straight win
when the rain started to fall." + ], + [ + "ATHENS-In one of the biggest
shocks in Olympic judo
history, defending champion
Kosei Inoue was defeated by
Dutchman Elco van der Geest in
the men #39;s 100-kilogram
category Thursday." + ], + [ + "NBC is adding a 5-second delay
to its Nascar telecasts after
Dale Earnhardt Jr. used a
vulgarity during a postrace
interview last weekend." + ], + [ + "San Francisco Giants
outfielder Barry Bonds, who
became the third player in
Major League Baseball history
to hit 700 career home runs,
won the National League Most
Valuable Player Award" + ], + [ + "Portsmouth chairman Milan
Mandaric said on Tuesday that
Harry Redknapp, who resigned
as manager last week, was
innocent of any wrong-doing
over agent or transfer fees." + ], + [ + "This record is for all the
little guys, for all the
players who have to leg out
every hit instead of taking a
relaxing trot around the
bases, for all the batters
whose muscles aren #39;t" + ], + [ + "Charlie Hodgson #39;s record-
equalling performance against
South Africa was praised by
coach Andy Robinson after the
Sale flyhalf scored 27 points
in England #39;s 32-16 victory
here at Twickenham on
Saturday." + ], + [ + "BASEBALL Atlanta (NL):
Optioned P Roman Colon to
Greenville (Southern);
recalled OF Dewayne Wise from
Richmond (IL). Boston (AL):
Purchased C Sandy Martinez
from Cleveland (AL) and
assigned him to Pawtucket
(IL). Cleveland (AL): Recalled
OF Ryan Ludwick from Buffalo
(IL). Chicago (NL): Acquired
OF Ben Grieve from Milwaukee
(NL) for player to be named
and cash; acquired C Mike ..." + ], + [ + "BRONX, New York (Ticker) --
Kelvim Escobar was the latest
Anaheim Angels #39; pitcher to
subdue the New York Yankees.
Escobar pitched seven strong
innings and Bengie Molina tied
a career-high with four hits,
including" + ], + [ + "Sep 08 - Vijay Singh revelled
in his status as the new world
number one after winning the
Deutsche Bank Championship by
three shots in Boston on
Monday." + ], + [ + "Many people in golf are asking
that today. He certainly wasn
#39;t A-list and he wasn #39;t
Larry Nelson either. But you
couldn #39;t find a more solid
guy to lead the United States
into Ireland for the 2006
Ryder Cup Matches." + ], + [ + "Australian Stuart Appleby, who
was the joint second-round
leader, returned a two-over 74
to drop to third at three-
under while American Chris
DiMarco moved into fourth with
a round of 69." + ], + [ + "With a doubleheader sweep of
the Minnesota Twins, the New
York Yankees moved to the
verge of clinching their
seventh straight AL East
title." + ], + [ + "Athens, Greece (Sports
Network) - The first official
track event took place this
morning and Italy #39;s Ivano
Brugnetti won the men #39;s
20km walk at the Summer
Olympics in Athens." + ], + [ + "Champions Arsenal opened a
five-point lead at the top of
the Premier League after a 4-0
thrashing of Charlton Athletic
at Highbury Saturday." + ], + [ + "The Redskins and Browns have
traded field goals and are
tied, 3-3, in the first
quarter in Cleveland." + ], + [ + "Forget September call-ups. The
Red Sox may tap their minor
league system for an extra
player or two when the rules
allow them to expand their
25-man roster Wednesday, but
any help from the farm is
likely to pale against the
abundance of talent they gain
from the return of numerous
players, including Trot Nixon
, from the disabled list." + ], + [ + "THOUSAND OAKS -- Anonymity is
only a problem if you want it
to be, and it is obvious Vijay
Singh doesn #39;t want it to
be. Let others chase fame." + ], + [ + "David Coulthard #39;s season-
long search for a Formula One
drive next year is almost
over. Negotiations between Red
Bull Racing and Coulthard, who
tested for the Austrian team
for the first time" + ], + [ + "AP - Southern California
tailback LenDale White
remembers Justin Holland from
high school. The Colorado
State quarterback made quite
an impression." + ], + [ + "TIM HENMAN last night admitted
all of his energy has been
drained away as he bowed out
of the Madrid Masters. The top
seed, who had a blood test on
Wednesday to get to the bottom
of his fatigue, went down" + ], + [ + "ATHENS -- US sailors needed a
big day to bring home gold and
bronze medals from the sailing
finale here yesterday. But
rolling the dice on windshifts
and starting tactics backfired
both in Star and Tornado
classes, and the Americans had
to settle for a single silver
medal." + ], + [ + "AP - Cavaliers forward Luke
Jackson was activated
Wednesday after missing five
games because of tendinitis in
his right knee. Cleveland also
placed forward Sasha Pavlovic
on the injured list." + ], + [ + "The Brisbane Lions #39;
football manager stepped out
of the changerooms just before
six o #39;clock last night and
handed one of the milling
supporters a six-pack of beer." + ], + [ + "Cavaliers owner Gordon Gund is
in quot;serious quot;
negotiations to sell the NBA
franchise, which has enjoyed a
dramatic financial turnaround
since the arrival of star
LeBron James." + ], + [ + "After two days of gloom, China
was back on the winning rails
on Thursday with Liu Chunhong
winning a weightlifting title
on her record-shattering binge
and its shuttlers contributing
two golds in the cliff-hanging
finals." + ], + [ + "One question that arises
whenever a player is linked to
steroids is, \"What would he
have done without them?\"
Baseball history whispers an
answer." + ], + [ + "The second round of the
Canadian Open golf tournament
continues Saturday Glenn Abbey
Golf Club in Oakville,
Ontario, after play was
suspended late Friday due to
darkness." + ], + [ + "A massive plan to attract the
2012 Summer Olympics to New
York, touting the city's
diversity, financial and media
power, was revealed Wednesday." + ], + [ + "NICK Heidfeld #39;s test with
Williams has been brought
forward after BAR blocked
plans for Anthony Davidson to
drive its Formula One rival
#39;s car." + ], + [ + "Grace Park closed with an
eagle and two birdies for a
7-under-par 65 and a two-
stroke lead after three rounds
of the Wachovia LPGA Classic
on Saturday." + ], + [ + "Carlos Beltran drives in five
runs to carry the Astros to a
12-3 rout of the Braves in
Game 5 of their first-round NL
playoff series." + ], + [ + "Since Lennox Lewis #39;s
retirement, the heavyweight
division has been knocked for
having more quantity than
quality. Eight heavyweights on
Saturday night #39;s card at
Madison Square Garden hope to
change that perception, at
least for one night." + ], + [ + "Tim Duncan had 17 points and
10 rebounds, helping the San
Antonio Spurs to a 99-81
victory over the New York
Kicks. This was the Spurs
fourth straight win this
season." + ], + [ + "Nagpur: India suffered a
double blow even before the
first ball was bowled in the
crucial third cricket Test
against Australia on Tuesday
when captain Sourav Ganguly
and off spinner Harbhajan
Singh were ruled out of the
match." + ], + [ + "AP - Former New York Yankees
hitting coach Rick Down was
hired for the same job by the
Mets on Friday, reuniting him
with new manager Willie
Randolph." + ], + [ + "FILDERSTADT (Germany) - Amelie
Mauresmo and Lindsay Davenport
took their battle for the No.
1 ranking and Porsche Grand
Prix title into the semi-
finals with straight-sets
victories on Friday." + ], + [ + "Carter returned, but it was
running back Curtis Martin and
the offensive line that put
the Jets ahead. Martin rushed
for all but 10 yards of a
45-yard drive that stalled at
the Cardinals 10." + ], + [ + "DENVER (Ticker) -- Jake
Plummer more than made up for
a lack of a running game.
Plummer passed for 294 yards
and two touchdowns as the
Denver Broncos posted a 23-13
victory over the San Diego
Chargers in a battle of AFC
West Division rivals." + ], + [ + "It took all of about five
minutes of an introductory
press conference Wednesday at
Heritage Hall for USC
basketball to gain something
it never really had before." + ], + [ + "AP - The Boston Red Sox looked
at the out-of-town scoreboard
and could hardly believe what
they saw. The New York Yankees
were trailing big at home
against the Cleveland Indians
in what would be the worst
loss in the 101-year history
of the storied franchise." + ], + [ + "The Red Sox will either
complete an amazing comeback
as the first team to rebound
from a 3-0 deficit in
postseason history, or the
Yankees will stop them." + ], + [ + "The Red Sox have reached
agreement with free agent
pitcher Matt Clement yesterday
on a three-year deal that will
pay him around \\$25 million,
his agent confirmed yesterday." + ], + [ + "HEN Manny Ramirez and David
Ortiz hit consecutive home
runs Sunday night in Chicago
to put the Red Sox ahead,
there was dancing in the
streets in Boston." + ], + [ + "MIAMI -- Bryan Randall grabbed
a set of Mardi Gras beads and
waved them aloft, while his
teammates exalted in the
prospect of a trip to New
Orleans." + ], + [ + "TORONTO (CP) - With an injured
Vince Carter on the bench, the
Toronto Raptors dropped their
sixth straight game Friday,
101-87 to the Denver Nuggets." + ], + [ + "While not quite a return to
glory, Monday represents the
Redskins' return to the
national consciousness." + ], + [ + "Vijay Singh has won the US PGA
Tour player of the year award
for the first time, ending
Tiger Woods #39;s five-year
hold on the honour." + ], + [ + "England got strikes from
sparkling debut starter
Jermain Defoe and Michael Owen
to defeat Poland in a Uefa
World Cup qualifier in
Chorzow." + ], + [ + "Titleholder Ernie Els moved
within sight of a record sixth
World Match Play title on
Saturday by solving a putting
problem to overcome injured
Irishman Padraig Harrington 5
and 4." + ], + [ + "If the Washington Nationals
never win a pennant, they have
no reason to ever doubt that
DC loves them. Yesterday, the
District City Council
tentatively approved a tab for
a publicly financed ballpark
that could amount to as much
as \\$630 million." + ], + [ + "Defensive back Brandon
Johnson, who had two
interceptions for Tennessee at
Mississippi, was suspended
indefinitely Monday for
violation of team rules." + ], + [ + "Points leader Kurt Busch spun
out and ran out of fuel, and
his misfortune was one of the
reasons crew chief Jimmy
Fennig elected not to pit with
20 laps to go." + ], + [ + "(CP) - The NHL all-star game
hasn #39;t been cancelled
after all. It #39;s just been
moved to Russia. The agent for
New York Rangers winger
Jaromir Jagr confirmed Monday
that the Czech star had joined
Omsk Avangard" + ], + [ + "HERE in the land of myth, that
familiar god of sports --
karma -- threw a bolt of
lightning into the Olympic
stadium yesterday. Marion
Jones lunged desperately with
her baton in the 4 x 100m
relay final, but couldn #39;t
reach her target." + ], + [ + "AP - Kenny Rogers lost at the
Coliseum for the first time in
more than 10 years, with Bobby
Crosby's three-run double in
the fifth inning leading the
Athletics to a 5-4 win over
the Texas Rangers on Thursday." + ], + [ + "Dambulla, Sri Lanka - Kumar
Sangakkara and Avishka
Gunawardene slammed impressive
half-centuries to help an
under-strength Sri Lanka crush
South Africa by seven wickets
in the fourth one-day
international here on
Saturday." + ], + [ + "Fresh off being the worst team
in baseball, the Arizona
Diamondbacks set a new record
this week: fastest team to
both hire and fire a manager." + ], + [ + "Nebraska head coach Bill
Callahan runs off the field at
halftime of the game against
Baylor in Lincoln, Neb.,
Saturday, Oct. 16, 2004." + ], + [ + " EAST RUTHERFORD, N.J. (Sports
Network) - The Toronto
Raptors have traded All-Star
swingman Vince Carter to the
New Jersey Nets in exchange
for center Alonzo Mourning,
forward Eric Williams,
center/forward Aaron Williams
and two first- round draft
picks." + ], + [ + "What riot? quot;An Argentine
friend of mine was a little
derisive of the Pacers-Pistons
eruption, quot; says reader
Mike Gaynes. quot;He snorted,
#39;Is that what Americans
call a riot?" + ], + [ + "All season, Chris Barnicle
seemed prepared for just about
everything, but the Newton
North senior was not ready for
the hot weather he encountered
yesterday in San Diego at the
Footlocker Cross-Country
National Championships. Racing
in humid conditions with
temperatures in the 70s, the
Massachusetts Division 1 state
champion finished sixth in 15
minutes 34 seconds in the
5-kilometer race. ..." + ], + [ + "AP - Coach Tyrone Willingham
was fired by Notre Dame on
Tuesday after three seasons in
which he failed to return one
of the nation's most storied
football programs to
prominence." + ], + [ + "COLLEGE PARK, Md. -- Joel
Statham completed 18 of 25
passes for 268 yards and two
touchdowns in No. 23
Maryland's 45-22 victory over
Temple last night, the
Terrapins' 12th straight win
at Byrd Stadium." + ], + [ + "Manchester United boss Sir
Alex Ferguson wants the FA to
punish Arsenal good guy Dennis
Bergkamp for taking a swing at
Alan Smith last Sunday." + ], + [ + "Published reports say Barry
Bonds has testified that he
used a clear substance and a
cream given to him by a
trainer who was indicted in a
steroid-distribution ring." + ], + [ + " ATHENS (Reuters) - Christos
Angourakis added his name to
Greece's list of Paralympic
medal winners when he claimed
a bronze in the T53 shot put
competition Thursday." + ], + [ + "Jay Fiedler threw for one
touchdown, Sage Rosenfels
threw for another and the
Miami Dolphins got a victory
in a game they did not want to
play, beating the New Orleans
Saints 20-19 Friday night." + ], + [ + " NEW YORK (Reuters) - Terrell
Owens scored three touchdowns
and the Philadelphia Eagles
amassed 35 first-half points
on the way to a 49-21
drubbing of the Dallas Cowboys
in Irving, Texas, Monday." + ], + [ + "A late strike by Salomon Kalou
sealed a 2-1 win for Feyenoord
over NEC Nijmegen, while
second placed AZ Alkmaar
defeated ADO Den Haag 2-0 in
the Dutch first division on
Sunday." + ], + [ + "What a disgrace Ron Artest has
become. And the worst part is,
the Indiana Pacers guard just
doesn #39;t get it. Four days
after fueling one of the
biggest brawls in the history
of pro sports, Artest was on
national" + ], + [ + "Jenson Button has revealed
dissatisfaction with the way
his management handled a
fouled switch to Williams. Not
only did the move not come
off, his reputation may have
been irreparably damaged amid
news headline" + ], + [ + "Redknapp and his No2 Jim Smith
resigned from Portsmouth
yesterday, leaving
controversial new director
Velimir Zajec in temporary
control." + ], + [ + "As the season winds down for
the Frederick Keys, Manager
Tom Lawless is starting to
enjoy the progress his
pitching staff has made this
season." + ], + [ + "Britain #39;s Bradley Wiggins
won the gold medal in men
#39;s individual pursuit
Saturday, finishing the
4,000-meter final in 4:16." + ], + [ + "And when David Akers #39;
50-yard field goal cleared the
crossbar in overtime, they did
just that. They escaped a
raucous Cleveland Browns
Stadium relieved but not
broken, tested but not
cracked." + ], + [ + "San Antonio, TX (Sports
Network) - Dean Wilson shot a
five-under 65 on Friday to
move into the lead at the
halfway point of the Texas
Open." + ], + [ + "Now that Chelsea have added
Newcastle United to the list
of clubs that they have given
what for lately, what price
Jose Mourinho covering the
Russian-funded aristocrats of
west London in glittering
glory to the tune of four
trophies?" + ], + [ + "AP - Former Seattle Seahawks
running back Chris Warren has
been arrested in Virginia on a
federal warrant, accused of
failing to pay #36;137,147 in
child support for his two
daughters in Washington state." + ], + [ + "It #39;s official: US Open had
never gone into the third
round with only two American
men, including the defending
champion, Andy Roddick." + ], + [ + "WASHINGTON, Aug. 17
(Xinhuanet) -- England coach
Sven-Goran Eriksson has urged
the international soccer
authorities to preserve the
health of the world superstar
footballers for major
tournaments, who expressed his
will in Slaley of England on
Tuesday ..." + ], + [ + "Juventus coach Fabio Capello
has ordered his players not to
kick the ball out of play when
an opponent falls to the
ground apparently hurt because
he believes some players fake
injury to stop the match." + ], + [ + "You #39;re angry. You want to
lash out. The Red Sox are
doing it to you again. They
#39;re blowing a playoff
series, and to the Yankees no
less." + ], + [ + "TORONTO -- There is no
mystique to it anymore,
because after all, the
Russians have become commoners
in today's National Hockey
League, and Finns, Czechs,
Slovaks, and Swedes also have
been entrenched in the
Original 30 long enough to
turn the ongoing World Cup of
Hockey into a protracted
trailer for the NHL season." + ], + [ + "Indianapolis, IN (Sports
Network) - The Indiana Pacers
try to win their second
straight game tonight, as they
host Kevin Garnett and the
Minnesota Timberwolves in the
third of a four-game homestand
at Conseco Fieldhouse." + ], + [ + "It is a team game, this Ryder
Cup stuff that will commence
Friday at Oakland Hills
Country Club. So what are the
teams? For the Americans,
captain Hal Sutton isn't
saying." + ], + [ + "MANCHESTER United today
dramatically rejected the
advances of Malcolm Glazer,
the US sports boss who is
mulling an 825m bid for the
football club." + ], + [ + "As usual, the Big Ten coaches
were out in full force at
today #39;s Big Ten
Teleconference. Both OSU head
coach Jim Tressel and Iowa
head coach Kirk Ferentz
offered some thoughts on the
upcoming game between OSU" + ], + [ + "New York Knicks #39; Stephon
Marbury (3) fires over New
Orleans Hornets #39; Dan
Dickau (2) during the second
half in New Orleans Wednesday
night, Dec. 8, 2004." + ], + [ + "At the very moment when the
Red Sox desperately need
someone slightly larger than
life to rally around, they
suddenly have the man for the
job: Thrilling Schilling." + ], + [ + "Dale Earnhardt Jr, right,
talks with Matt Kenseth, left,
during a break in practice at
Lowe #39;s Motor Speedway in
Concord, NC, Thursday Oct. 14,
2004 before qualifying for
Saturday #39;s UAW-GM Quality
500 NASCAR Nextel Cup race." + ], + [ + "Several starting spots may
have been usurped or at least
threatened after relatively
solid understudy showings
Sunday, but few players
welcome the kind of shot
delivered to Oakland" + ], + [ + "TEMPE, Ariz. -- Neil Rackers
kicked four field goals and
the Arizona Cardinals stifled
rookie Chris Simms and the
rest of the Tampa Bay offense
for a 12-7 victory yesterday
in a matchup of two sputtering
teams out of playoff
contention. Coach Jon Gruden's
team lost its fourth in a row
to finish 5-11, Tampa Bay's
worst record since going ..." + ], + [ + "ATHENS France, Britain and the
United States issued a joint
challenge Thursday to Germany
#39;s gold medal in equestrian
team three-day eventing." + ], + [ + "ATLANTA - Who could have
imagined Tommy Tuberville in
this position? Yet there he
was Friday, standing alongside
the SEC championship trophy,
posing for pictures and
undoubtedly chuckling a bit on
the inside." + ], + [ + "John Thomson threw shutout
ball for seven innings
Wednesday night in carrying
Atlanta to a 2-0 blanking of
the New York Mets. New York
lost for the 21st time in 25
games on the" + ], + [ + "Vikram Solanki beat the rain
clouds to register his second
one-day international century
as England won the third one-
day international to wrap up a
series victory." + ], + [ + "A three-touchdown point spread
and a recent history of late-
season collapses had many
thinking the UCLA football
team would provide little
opposition to rival USC #39;s
march to the BCS-championship
game at the Orange Bowl." + ], + [ + " PHOENIX (Sports Network) -
Free agent third baseman Troy
Glaus is reportedly headed to
the Arizona Diamondbacks." + ], + [ + "Third-string tailback Chris
Markey ran for 131 yards to
lead UCLA to a 34-26 victory
over Oregon on Saturday.
Markey, playing because of an
injury to starter Maurice
Drew, also caught five passes
for 84 yards" + ], + [ + "Jay Payton #39;s three-run
homer led the San Diego Padres
to a 5-1 win over the San
Francisco Giants in National
League play Saturday, despite
a 701st career blast from
Barry Bonds." + ], + [ + "The optimists among Rutgers
fans were delighted, but the
Scarlet Knights still gave the
pessimists something to worry
about." + ], + [ + "Perhaps the sight of Maria
Sharapova opposite her tonight
will jog Serena Williams #39;
memory. Wimbledon. The final.
You and Maria." + ], + [ + "Five days after making the
putt that won the Ryder Cup,
Colin Montgomerie looked set
to miss the cut at a European
PGA tour event." + ], + [ + "Karachi - Captain Inzamam ul-
Haq and coach Bob Woolmer came
under fire on Thursday for
choosing to bat first on a
tricky pitch after Pakistan
#39;s humiliating defeat in
the ICC Champions Trophy semi-
final." + ], + [ + "Scottish champions Celtic saw
their three-year unbeaten home
record in Europe broken
Tuesday as they lost 3-1 to
Barcelona in the Champions
League Group F opener." + ], + [ + "AP - Andy Roddick searched out
Carlos Moya in the throng of
jumping, screaming Spanish
tennis players, hoping to
shake hands." + ], + [ + "ENGLAND captain and Real
Madrid midfielder David
Beckham has played down
speculation that his club are
moving for England manager
Sven-Goran Eriksson." + ], + [ + "AFP - National Basketball
Association players trying to
win a fourth consecutive
Olympic gold medal for the
United States have gotten the
wake-up call that the \"Dream
Team\" days are done even if
supporters have not." + ], + [ + "England will be seeking their
third clean sweep of the
summer when the NatWest
Challenge against India
concludes at Lord #39;s. After
beating New Zealand and West
Indies 3-0 and 4-0 in Tests,
they have come alive" + ], + [ + "AP - Mark Richt knows he'll
have to get a little creative
when he divvies up playing
time for Georgia's running
backs next season. Not so on
Saturday. Thomas Brown is the
undisputed starter for the
biggest game of the season." + ], + [ + "Michael Phelps, the six-time
Olympic champion, issued an
apology yesterday after being
arrested and charged with
drunken driving in the United
States." + ], + [ + "ATHENS Larry Brown, the US
coach, leaned back against the
scorer #39;s table, searching
for support on a sinking ship.
His best player, Tim Duncan,
had just fouled out, and the
options for an American team
that" + ], + [ + "Spain have named an unchanged
team for the Davis Cup final
against the United States in
Seville on 3-5 December.
Carlos Moya, Juan Carlos
Ferrero, Rafael Nadal and
Tommy Robredo will take on the
US in front of 22,000 fans at
the converted Olympic stadium." + ], + [ + "Last Tuesday night, Harvard
knocked off rival Boston
College, which was ranked No.
1 in the country. Last night,
the Crimson knocked off
another local foe, Boston
University, 2-1, at Walter
Brown Arena, which marked the
first time since 1999 that
Harvard had beaten them both
in the same season." + ], + [ + "About 500 prospective jurors
will be in an Eagle, Colorado,
courtroom Friday, answering an
82-item questionnaire in
preparation for the Kobe
Bryant sexual assault trial." + ], + [ + "BEIJING The NBA has reached
booming, basketball-crazy
China _ but the league doesn
#39;t expect to make any money
soon. The NBA flew more than
100 people halfway around the
world for its first games in
China, featuring" + ], + [ + " NEW YORK (Reuters) - Curt
Schilling pitched 6 2/3
innings and Manny Ramirez hit
a three-run homer in a seven-
run fourth frame to lead the
Boston Red Sox to a 9-3 win
over the host Anaheim Angels
in their American League
Divisional Series opener
Tuesday." + ], + [ + "AP - The game between the
Minnesota Twins and the New
York Yankees on Tuesday night
was postponed by rain." + ], + [ + "PARIS The verdict is in: The
world #39;s greatest race car
driver, the champion of
champions - all disciplines
combined - is Heikki
Kovalainen." + ], + [ + "Pakistan won the toss and
unsurprisingly chose to bowl
first as they and West Indies
did battle at the Rose Bowl
today for a place in the ICC
Champions Trophy final against
hosts England." + ], + [ + "AP - Boston Red Sox center
fielder Johnny Damon is having
a recurrence of migraine
headaches that first bothered
him after a collision in last
year's playoffs." + ], + [ + "Felix Cardenas of Colombia won
the 17th stage of the Spanish
Vuelta cycling race Wednesday,
while defending champion
Roberto Heras held onto the
overall leader #39;s jersey
for the sixth day in a row." + ], + [ + "Established star Landon
Donovan and rising sensation
Eddie Johnson carried the
United States into the
regional qualifying finals for
the 2006 World Cup in emphatic
fashion Wednesday night." + ], + [ + "Wizards coach Eddie Jordan
says the team is making a
statement that immaturity will
not be tolerated by suspending
Kwame Brown one game for not
taking part in a team huddle
during a loss to Denver." + ], + [ + "AP - Andy Roddick has yet to
face a challenge in his U.S.
Open title defense. He beat
No. 18 Tommy Robredo of Spain
6-3, 6-2, 6-4 Tuesday night to
move into the quarterfinals
without having lost a set." + ], + [ + "Now that hell froze over in
Boston, New England braces for
its rarest season -- a winter
of content. Red Sox fans are
adrift from the familiar
torture of the past." + ], + [ + "THE South Africans have called
the Wallabies scrum cheats as
a fresh round of verbal
warfare opened in the Republic
last night." + ], + [ + "An overwhelming majority of
NHL players who expressed
their opinion in a poll said
they would not support a
salary cap even if it meant
saving a season that was
supposed to have started Oct.
13." + ], + [ + "Tony Eury Sr. oversees Dale
Earnhardt Jr. on the
racetrack, but Sunday he
extended his domain to Victory
Lane. Earnhardt was unbuckling
to crawl out of the No." + ], + [ + "(Sports Network) - The New
York Yankees try to move one
step closer to a division
title when they conclude their
critical series with the
Boston Red Sox at Fenway Park." + ], + [ + "Kurt Busch claimed a stake in
the points lead in the NASCAR
Chase for the Nextel Cup
yesterday, winning the
Sylvania 300 at New Hampshire
International Speedway." + ], + [ + "LOUDON, NH - As this
newfangled stretch drive for
the Nextel Cup championship
ensues, Jeff Gordon has to be
considered the favorite for a
fifth title." + ], + [ + "By nick giongco. YOU KNOW you
have reached the status of a
boxing star when ring
announcer extraordinaire
Michael Buffer calls out your
name in his trademark booming
voice during a high-profile
event like yesterday" + ], + [ + "AP - Even with a big lead,
Eric Gagne wanted to pitch in
front of his hometown fans one
last time." + ], + [ + "ATHENS, Greece -- Larry Brown
was despondent, the head of
the US selection committee was
defensive and an irritated
Allen Iverson was hanging up
on callers who asked what went
wrong." + ], + [ + "AP - Courtney Brown refuses to
surrender to injuries. He's
already planning another
comeback." + ], + [ + "It took an off-the-cuff
reference to a serial
murderer/cannibal to punctuate
the Robby Gordon storyline.
Gordon has been vilified by
his peers and put on probation" + ], + [ + "By BOBBY ROSS JR. Associated
Press Writer. play the Atlanta
Hawks. They will be treated to
free food and drink and have.
their pictures taken with
Mavericks players, dancers and
team officials." + ], + [ + "ARSENAL #39;S Brazilian World
Cup winning midfielder
Gilberto Silva is set to be
out for at least a month with
a back injury, the Premiership
leaders said." + ], + [ + "INDIANAPOLIS -- With a package
of academic reforms in place,
the NCAA #39;s next crusade
will address what its
president calls a dangerous
drift toward professionalism
and sports entertainment." + ], + [ + "AP - It was difficult for
Southern California's Pete
Carroll and Oklahoma's Bob
Stoops to keep from repeating
each other when the two
coaches met Thursday." + ], + [ + "Andy Roddick, along with
Olympic silver medalist Mardy
Fish and the doubles pair of
twins Bob and Mike Bryan will
make up the US team to compete
with Belarus in the Davis Cup,
reported CRIENGLISH." + ], + [ + "While the world #39;s best
athletes fight the noble
Olympic battle in stadiums and
pools, their fans storm the
streets of Athens, turning the
Greek capital into a huge
international party every
night." + ], + [ + "EVERTON showed they would not
be bullied into selling Wayne
Rooney last night by rejecting
a 23.5million bid from
Newcastle - as Manchester
United gamble on Goodison
#39;s resolve to keep the
striker." + ], + [ + "After months of legal
wrangling, the case of
<em>People v. Kobe Bean
Bryant</em> commences on
Friday in Eagle, Colo., with
testimony set to begin next
month." + ], + [ + "Lyon coach Paul le Guen has
admitted his side would be
happy with a draw at Old
Trafford on Tuesday night. The
three-times French champions
have assured themselves of
qualification for the
Champions League" + ], + [ + "Reuters - Barry Bonds failed
to collect a hit in\\his bid to
join the 700-homer club, but
he did score a run to\\help the
San Francisco Giants edge the
host Milwaukee Brewers\\3-2 in
National League action on
Tuesday." + ], + [ + "After waiting an entire summer
for the snow to fall and
Beaver Creek to finally open,
skiers from around the planet
are coming to check out the
Birds of Prey World Cup action
Dec. 1 - 5. Although everyones" + ], + [ + "I confess that I am a complete
ignoramus when it comes to
women #39;s beach volleyball.
In fact, I know only one thing
about the sport - that it is
the supreme aesthetic
experience available on planet
Earth." + ], + [ + "Manchester United Plc may
offer US billionaire Malcolm
Glazer a seat on its board if
he agrees to drop a takeover
bid for a year, the Observer
said, citing an unidentified
person in the soccer industry." + ], + [ + "For a guy who spent most of
his first four professional
seasons on the disabled list,
Houston Astros reliever Brad
Lidge has developed into quite
the ironman these past two
days." + ], + [ + "Former Bengal Corey Dillon
found his stride Sunday in his
second game for the New
England Patriots. Dillon
gained 158 yards on 32 carries
as the Patriots beat the
Arizona Cardinals, 23-12, for
their 17th victory in a row." + ], + [ + "If legend is to be believed,
the end of a victorious war
was behind Pheidippides #39;
trek from Marathon to Athens
2,500 years ago." + ], + [ + " BEIJING (Reuters) - Wimbledon
champion Maria Sharapova
demolished fellow Russian
Tatiana Panova 6-1, 6-1 to
advance to the quarter-finals
of the China Open on
Wednesday." + ], + [ + "Padres general manager Kevin
Towers called Expos general
manager Omar Minaya on
Thursday afternoon and told
him he needed a shortstop
because Khalil Greene had
broken his" + ], + [ + "Motorsport.com. Nine of the
ten Formula One teams have
united to propose cost-cutting
measures for the future, with
the notable exception of
Ferrari." + ], + [ + "Steve Francis and Shaquille O
#39;Neal enjoyed big debuts
with their new teams. Kobe
Bryant found out he can #39;t
carry the Lakers all by
himself." + ], + [ + "Australia, by winning the
third Test at Nagpur on
Friday, also won the four-
match series 2-0 with one
match to go. Australia had
last won a Test series in
India way back in December
1969 when Bill Lawry #39;s
team beat Nawab Pataudi #39;s
Indian team 3-1." + ], + [ + "Final Score: Connecticut 61,
New York 51 New York, NY
(Sports Network) - Nykesha
Sales scored 15 points to lead
Connecticut to a 61-51 win
over New York in Game 1 of
their best-of-three Eastern
Conference Finals series at
Madison Square Garden." + ], + [ + "Charlotte, NC -- LeBron James
poured in a game-high 19
points and Jeff McInnis scored
18 as the Cleveland Cavaliers
routed the Charlotte Bobcats,
106-89, at the Charlotte
Coliseum." + ], + [ + "Lehmann, who was at fault in
two matches in the tournament
last season, was blundering
again with the German set to
take the rap for both Greek
goals." + ], + [ + "Jeju Island, South Korea
(Sports Network) - Grace Park
and Carin Koch posted matching
rounds of six-under-par 66 on
Friday to share the lead after
the first round of the CJ Nine
Bridges Classic." + ], + [ + "Benfica and Real Madrid set
the standard for soccer
success in Europe in the late
1950s and early #39;60s. The
clubs have evolved much
differently, but both have
struggled" + ], + [ + "Pakistan are closing in fast
on Sri Lanka #39;s first
innings total after impressing
with both ball and bat on the
second day of the opening Test
in Faisalabad." + ], + [ + "Ron Artest has been hit with a
season long suspension,
unprecedented for the NBA
outside doping cases; Stephen
Jackson banned for 30 games;
Jermaine O #39;Neal for 25
games and Anthony Johnson for
five." + ], + [ + "Three shots behind Grace Park
with five holes to go, six-
time LPGA player of the year
Sorenstam rallied with an
eagle, birdie and three pars
to win her fourth Samsung
World Championship by three
shots over Park on Sunday." + ], + [ + "NSW Rugby CEO Fraser Neill
believes Waratah star Mat
Rogers has learned his lesson
after he was fined and ordered
to do community service
following his controversial
comments about the club rugby
competition." + ], + [ + "ATHENS: China, the dominant
force in world diving for the
best part of 20 years, won six
out of eight Olympic titles in
Athens and prompted
speculation about a clean
sweep when they stage the
Games in Beijing in 2008." + ], + [ + "The Houston Astros won their
19th straight game at home and
are one game from winning
their first playoff series in
42 years." + ], + [ + "The eighth-seeded American
fell to sixth-seeded Elena
Dementieva of Russia, 0-6 6-2
7-6 (7-5), on Friday - despite
being up a break on four
occasions in the third set." + ], + [ + "TUCSON, Arizona (Ticker) --
No. 20 Arizona State tries to
post its first three-game
winning streak over Pac-10
Conference rival Arizona in 26
years when they meet Friday." + ], + [ + "AP - Phillip Fulmer kept his
cool when starting center
Jason Respert drove off in the
coach's golf cart at practice." + ], + [ + "GOALS from Wayne Rooney and
Ruud van Nistelrooy gave
Manchester United the win at
Newcastle. Alan Shearer
briefly levelled matters for
the Magpies but United managed
to scrape through." + ], + [ + "AP - Two-time U.S. Open
doubles champion Max Mirnyi
will lead the Belarus team
that faces the United States
in the Davis Cup semifinals in
Charleston later this month." + ], + [ + "AP - New York Jets safety Erik
Coleman got his souvenir
football from the equipment
manager and held it tightly." + ], + [ + "Ivan Hlinka coached the Czech
Republic to the hockey gold
medal at the 1998 Nagano
Olympics and became the coach
of the Pittsburgh Penguins two
years later." + ], + [ + "AUBURN - Ah, easy street. No
game this week. Light
practices. And now Auburn is
being touted as the No. 3 team
in the Bowl Championship
Series standings." + ], + [ + "Portsmouth #39;s Harry
Redknapp has been named as the
Barclays manager of the month
for October. Redknapp #39;s
side were unbeaten during the
month and maintained an
impressive climb to ninth in
the Premiership." + ], + [ + "Three directors of Manchester
United have been ousted from
the board after US tycoon
Malcolm Glazer, who is
attempting to buy the club,
voted against their re-
election." + ], + [ + "AP - Manny Ramirez singled and
scored before leaving with a
bruised knee, and the
streaking Boston Red Sox beat
the Detroit Tigers 5-3 Friday
night for their 10th victory
in 11 games." + ], + [ + "The news comes fast and
furious. Pedro Martinez goes
to Tampa to visit George
Steinbrenner. Theo Epstein and
John Henry go to Florida for
their turn with Pedro. Carl
Pavano comes to Boston to
visit Curt Schilling. Jason
Varitek says he's not a goner.
Derek Lowe is a goner, but he
says he wishes it could be
different. Orlando Cabrera ..." + ], + [ + "FRED Hale Sr, documented as
the worlds oldest man, has
died at the age of 113. Hale
died in his sleep on Friday at
a hospital in Syracuse, New
York, while trying to recover
from a bout of pneumonia, his
grandson, Fred Hale III said." + ], + [ + "The Oakland Raiders have
traded Jerry Rice to the
Seattle Seahawks in a move
expected to grant the most
prolific receiver in National
Football League history his
wish to get more playing time." + ], + [ + "AP - Florida coach Ron Zook
was fired Monday but will be
allowed to finish the season,
athletic director Jeremy Foley
told The Gainesville Sun." + ], + [ + "Troy Brown has had to make a
lot of adjustments while
playing both sides of the
football. quot;You always
want to score when you get the
ball -- offense or defense" + ], + [ + "Jenson Button will tomorrow
discover whether he is allowed
to quit BAR and move to
Williams for 2005. The
Englishman has signed
contracts with both teams but
prefers a switch to Williams,
where he began his Formula One
career in 2000." + ], + [ + "ARSENAL boss Arsene Wenger
last night suffered a
Champions League setback as
Brazilian midfielder Gilberto
Silva (above) was left facing
a long-term injury absence." + ], + [ + "American improves to 3-1 on
the season with a hard-fought
overtime win, 74-63, against
Loyala at Bender Arena on
Friday night." + ], + [ + "Bee Staff Writers. SAN
FRANCISCO - As Eric Johnson
drove to the stadium Sunday
morning, his bruised ribs were
so sore, he wasn #39;t sure he
#39;d be able to suit up for
the game." + ], + [ + "The New England Patriots are
so single-minded in pursuing
their third Super Bowl triumph
in four years that they almost
have no room for any other
history." + ], + [ + "Because, while the Eagles are
certain to stumble at some
point during the regular
season, it seems inconceivable
that they will falter against
a team with as many offensive
problems as Baltimore has
right now." + ], + [ + "AP - J.J. Arrington ran for 84
of his 121 yards in the second
half and Aaron Rodgers shook
off a slow start to throw two
touchdown passes to help No. 5
California beat Washington
42-12 on Saturday." + ], + [ + "SHANGHAI, China The Houston
Rockets have arrived in
Shanghai with hometown
favorite Yao Ming declaring
himself quot;here on
business." + ], + [ + "Charleston, SC (Sports
Network) - Andy Roddick and
Mardy Fish will play singles
for the United States in this
weekend #39;s Davis Cup
semifinal matchup against
Belarus." + ], + [ + "RICKY PONTING believes the
game #39;s watchers have
fallen for the quot;myth
quot; that New Zealand know
how to rattle Australia." + ], + [ + "MILWAUKEE (SportsTicker) -
Barry Bonds tries to go where
just two players have gone
before when the San Francisco
Giants visit the Milwaukee
Brewers on Tuesday." + ], + [ + "AP - With Tom Brady as their
quarterback and a stingy,
opportunistic defense, it's
difficult to imagine when the
New England Patriots might
lose again. Brady and
defensive end Richard Seymour
combined to secure the
Patriots' record-tying 18th
straight victory, 31-17 over
the Buffalo Bills on Sunday." + ], + [ + "Approaching Hurricane Ivan has
led to postponement of the
game Thursday night between
10th-ranked California and
Southern Mississippi in
Hattiesburg, Cal #39;s
athletic director said Monday." + ], + [ + "SAN DIEGO (Ticker) - The San
Diego Padres lacked speed and
an experienced bench last
season, things veteran
infielder Eric Young is
capable of providing." + ], + [ + "This is an eye chart,
reprinted as a public service
to the New York Mets so they
may see from what they suffer:
myopia. Has ever a baseball
franchise been so shortsighted
for so long?" + ], + [ + " LONDON (Reuters) - A medical
product used to treat both
male hair loss and prostate
problems has been added to the
list of banned drugs for
athletes." + ], + [ + "AP - Curtis Martin and Jerome
Bettis have the opportunity to
go over 13,000 career yards
rushing in the same game when
Bettis and the Pittsburgh
Steelers play Martin and the
New York Jets in a big AFC
matchup Sunday." + ], + [ + "Michigan Stadium was mostly
filled with empty seats. The
only cheers were coming from
near one end zone -he Iowa
section. By Carlos Osorio, AP." + ], + [ + "The International Rugby Board
today confirmed that three
countries have expressed an
interest in hosting the 2011
World Cup. New Zealand, South
Africa and Japan are leading
the race to host rugby union
#39;s global spectacular in
seven years #39; time." + ], + [ + "MIAMI (Ticker) -- In its first
season in the Atlantic Coast
Conference, No. 11 Virginia
Tech is headed to the BCS.
Bryan Randall threw two
touchdown passes and the
Virginia Tech defense came up
big all day as the Hokies
knocked off No." + ], + [ + "(CP) - Somehow, in the span of
half an hour, the Detroit
Tigers #39; pitching went from
brutal to brilliant. Shortly
after being on the wrong end
of several records in a 26-5
thrashing from to the Kansas
City" + ], + [ + "With the Huskies reeling at
0-4 - the only member of a
Bowl Championship Series
conference left without a win
-an Jose State suddenly looms
as the only team left on the
schedule that UW will be
favored to beat." + ], + [ + "Darryl Sutter, who coached the
Calgary Flames to the Stanley
Cup finals last season, had an
emergency appendectomy and was
recovering Friday." + ], + [ + "Athens, Greece (Sports
Network) - For the second
straight day a Briton captured
gold at the Olympic Velodrome.
Bradley Wiggins won the men
#39;s individual 4,000-meter
pursuit Saturday, one day
after teammate" + ], + [ + "A Steffen Iversen penalty was
sufficient to secure the
points for Norway at Hampden
on Saturday. James McFadden
was ordered off after 53
minutes for deliberate
handball as he punched Claus
Lundekvam #39;s header off the
line." + ], + [ + "AP - Ailing St. Louis reliever
Steve Kline was unavailable
for Game 3 of the NL
championship series on
Saturday, but Cardinals
manager Tony LaRussa hopes the
left-hander will pitch later
this postseason." + ], + [ + "MADRID: A stunning first-half
free kick from David Beckham
gave Real Madrid a 1-0 win
over newly promoted Numancia
at the Bernabeu last night." + ], + [ + "Favourites Argentina beat
Italy 3-0 this morning to
claim their place in the final
of the men #39;s Olympic
football tournament. Goals by
leading goalscorer Carlos
Tevez, with a smart volley
after 16 minutes, and" + ], + [ + "Shortly after Steve Spurrier
arrived at Florida in 1990,
the Gators were placed on NCAA
probation for a year stemming
from a child-support payment
former coach Galen Hall made
for a player." + ] + ], + "hovertemplate": "label=Sports
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Sports", + "marker": { + "color": "#00cc96", + "size": 5, + "symbol": "square" + }, + "mode": "markers", + "name": "Sports", + "showlegend": true, + "type": "scattergl", + "x": [ + 16.660423, + 49.959976, + 54.445854, + 30.389194, + 13.9476, + 47.226242, + 51.068775, + 50.124294, + 44.945942, + 46.406788, + 48.53827, + 31.687206, + 40.537342, + 63.37852, + 56.515934, + 45.10189, + 48.401085, + 36.802402, + 54.15107, + 58.71805, + 38.756367, + 59.531124, + 47.890396, + 36.084118, + 39.42093, + 55.4732, + 60.896523, + 56.4989, + 48.16754, + 47.840588, + 60.467564, + 31.210938, + 49.345882, + 58.657722, + 37.406326, + 45.95487, + 27.38124, + 46.569256, + 40.19783, + -38.585472, + 48.277, + 48.961315, + 46.0185, + 59.281708, + 58.10119, + 41.329796, + 60.929493, + 49.080627, + 52.442997, + 25.193996, + 13.627039, + 49.169395, + 50.209198, + 38.928047, + 57.835648, + 25.167212, + 54.942364, + 31.13371, + 45.565693, + 56.076347, + 47.481384, + 54.362545, + 53.08307, + 55.395947, + 32.21779, + 36.722115, + 58.705086, + 30.618706, + 23.218187, + 48.1655, + 39.944633, + 51.72719, + 57.632236, + 18.106745, + 40.575005, + 49.25058, + 52.102192, + 45.8365, + 41.15665, + 45.06367, + 50.470036, + 55.406406, + 34.643066, + 44.60481, + 40.65631, + 49.13915, + 29.803865, + 25.779589, + 56.531708, + 37.571487, + 65.59185, + 38.63354, + 46.481388, + 46.68557, + 49.08016, + 14.032702, + 23.928396, + -6.620774, + 45.88543, + 35.342182, + 47.523766, + 47.32294, + 34.786186, + 43.796356, + 49.697426, + 37.340225, + 54.74676, + 41.444393, + 30.604837, + 60.995483, + 66.21081, + 35.24333, + 57.111607, + 48.019886, + 52.86177, + 52.435543, + 51.07264, + 61.549442, + 42.282997, + 33.828228, + 56.65421, + 43.26525, + 62.865932, + 39.05222, + 41.410755, + 38.42368, + 56.985188, + 57.739395, + 50.66318, + 48.850945, + 36.300686, + 39.32889, + 31.211935, + 39.84183, + 40.939545, + 48.37629, + 25.452175, + 51.559708, + 46.41191, + 52.77315, + 55.190277, + 38.03023, + 59.759964, + 48.908253, + 49.40172, + 49.71215, + 40.702366, + 57.916637, + 51.67792, + 30.202019, + 57.654526, + 34.585587, + 61.278408, + 23.422081, + 39.786133, + 54.46313, + 48.63991, + 35.595135, + 50.553566, + 54.101334, + 51.79524, + 42.513103, + 39.504147, + 20.738512, + 47.29459, + 51.208797, + 61.06654, + 59.059566, + 41.391323, + 56.262905, + 46.51155, + 27.989025, + 57.75085, + 35.379726, + 40.129883, + 51.36741, + 44.124233, + 38.459312, + 42.09723, + 54.85633, + 48.645084, + 33.95719, + 34.440483, + 45.367435, + 57.216434, + 49.150986, + 40.020714, + 36.398586, + 49.763367, + 44.87128, + 34.063007, + 29.130596, + 40.65309, + 33.885105, + 42.74865, + 46.39128, + 63.1983, + 49.79338, + 54.410885, + 63.605362, + 39.934895, + 17.519335, + 40.63617, + 30.811249, + 47.465935, + 47.272655, + 63.177612, + 41.66488, + 51.543095, + 38.820065, + 40.088383, + 60.45724, + 52.433907, + 38.667847, + 57.766346, + 42.41823, + 19.677572, + -36.831997, + 56.381645, + 53.579098, + 59.620773, + 48.37667, + 35.445507, + 46.157833, + 49.838924, + 29.182852, + 38.542347, + 30.625587, + 43.841885, + 56.74711, + 51.738125, + 51.774147, + 43.796253, + 47.406204, + -6.8207917, + 41.08887, + 62.321815, + 30.915781, + 49.839912, + 48.11404, + 37.93922, + 58.07306, + 50.09836, + 54.922394, + 52.352432, + 53.529724, + 25.636118, + 38.338932, + 37.103165, + 45.54423, + 53.86991, + 47.012283, + 60.093002, + 32.56739, + 60.606064, + 39.772743, + 48.95457, + 44.68764, + 48.906673, + 47.63983, + 62.788, + 38.030407, + 56.60054, + 40.073948, + 60.232296, + 56.806816, + 45.64992, + 39.266872, + 62.91453, + 50.306213, + 53.805332, + 50.443317, + 53.03957, + 44.309578, + 54.42772, + 34.023724, + 61.473892, + 50.651646, + 27.97626, + 59.39454, + 32.39917, + 59.94177, + 42.23158, + 56.222717, + 59.36177, + 57.550297, + 60.801098, + 57.966537, + 55.964886, + 45.870426, + 38.638237, + 49.729176, + 58.080826, + 58.289707, + 25.88273, + 63.36231, + 50.890648, + 45.88053, + 50.78542, + 43.456127, + 28.731657, + 50.339493, + 61.357586, + 56.755314, + 13.779188, + 25.783823, + 55.462612, + 58.314003, + 53.49091, + 49.449314, + 37.930157, + 57.86416, + 52.26376, + 47.072803, + 48.17513, + 44.906193, + 56.61765, + 56.60834, + 56.066643, + 56.162464, + 53.77512, + 47.758217, + 50.96032, + 58.05466, + 41.446438, + 58.41072, + 11.970405, + 35.8429, + 47.047108, + 54.494556, + 47.79968, + 59.32574, + 40.920834, + 55.53207, + 44.559975, + 53.77833, + 32.079, + 47.351242, + 38.30059, + 48.4094, + 33.499294, + 29.347134, + 45.721344, + 45.999187, + 49.048878, + 39.29256, + 32.81585, + 26.376568, + 32.12724, + 59.9222, + 45.969334, + 47.935875, + 57.007023, + 34.043217, + 30.42877, + 53.714108, + 34.008293, + 33.648438, + 59.613667, + 31.749115, + 52.849945, + 61.664543, + 30.838337, + 43.08786, + 49.605602, + 63.165108, + 47.894882, + 51.314476, + 44.60029, + 44.015842, + 39.41502, + 59.81665, + 41.209156, + 47.579025, + 56.48261, + 46.025146, + 58.74744, + 46.206974, + 53.145702, + 60.687424, + 38.102413, + 39.646805, + 52.38563, + 34.14185, + 31.320894, + 40.573475, + 41.94092, + 30.75048, + 39.690254, + 53.560726, + 50.696335, + 0.011293956, + 29.16581, + 58.507614, + 40.03086, + 48.50645, + 38.79774, + 62.77733, + 47.435825, + 43.003845, + 34.368248, + 53.752186, + 55.804855, + 26.508045, + 30.6338, + 47.806313, + 56.805237, + 49.249695, + 49.522606, + 61.757652, + 61.630924, + 39.668633, + 51.79226, + 28.194004, + 53.427227, + 45.15016, + 36.015182, + 43.020264, + 54.577732, + 18.002365, + 31.860546, + -0.10420398, + 37.094307, + 58.735332, + 12.898047, + 35.940807, + 30.280912, + 63.004868, + 47.676117, + 43.803253, + 52.02361, + 57.569775, + -31.23627, + 51.3331, + 40.77166, + 48.259228, + 49.969627, + 36.618427, + 55.361397, + 40.71827, + 39.666965, + 33.43409, + 55.207115, + 32.183567, + 16.695406, + 55.5641, + 51.27627, + 47.339336, + 63.347527, + 39.062187, + 54.67712, + 65.51466, + 45.10427, + 47.36583, + 35.328148, + 30.716692, + 58.147617, + 64.86492, + 32.677113, + 55.061314, + 38.546684, + 43.151165, + 26.161028, + 44.980778, + 47.840103, + 49.251343, + 52.69105, + 49.939175, + 46.95092, + 28.887644, + 54.523384, + 27.097654, + 58.47151, + 50.23622, + 39.812546, + 59.636997, + 57.34273, + -18.721113, + 48.26851, + 42.70037, + 15.134995, + 49.12623, + 59.67211, + 47.292377, + 47.805153, + 46.018627, + 37.074707, + 64.22567, + 40.90623, + 42.02211, + 36.001675 + ], + "xaxis": "x", + "y": [ + -20.803589, + -3.7095485, + -10.627376, + -3.9541492, + -17.816854, + -21.783358, + -12.137919, + 8.247171, + -27.218586, + -35.47235, + 0.2254613, + -32.542274, + -29.048313, + -18.953293, + -16.318848, + 1.29799, + 0.27214336, + -22.427275, + 1.5617585, + -13.859794, + -10.261337, + 2.3437028, + -11.51763, + -28.920532, + -14.537146, + -28.30168, + -17.185091, + 10.214211, + -11.397742, + -29.872732, + -19.64737, + 0.16790108, + 9.72588, + -19.213398, + -17.097263, + -23.540205, + -22.659904, + -30.161833, + -32.789925, + 4.0007343, + -30.553509, + -19.611755, + 8.52158, + 4.1424494, + 1.4751459, + -12.852704, + -20.392239, + -4.5837173, + 8.166061, + -11.337284, + -16.43065, + -22.28117, + 7.9826016, + -13.418971, + -24.45379, + -11.355663, + 9.545558, + -32.61165, + 11.230936, + -13.468946, + -5.462048, + -13.069021, + 1.5735915, + -23.937035, + -15.694869, + -25.943512, + 0.82049704, + -3.9954906, + -18.380714, + -21.956186, + -17.928478, + -17.166925, + 1.8213869, + -20.784616, + -11.329836, + -8.567718, + -11.798271, + -25.911339, + -10.770047, + -15.446808, + -32.171726, + -29.32608, + -35.429436, + -11.357406, + -14.503349, + -3.407611, + -5.3719177, + -9.659326, + -19.531103, + -14.720505, + -27.277906, + -15.416589, + 0.7872089, + -23.089176, + -19.599855, + -17.141865, + -14.515779, + -8.936446, + -31.523523, + -15.298813, + -11.721406, + -20.626392, + -8.267473, + -22.223913, + -28.966488, + -21.779205, + -27.218397, + -36.186253, + 0.31401816, + -22.044197, + -25.348227, + -15.303513, + 2.799256, + -27.043676, + -29.502745, + -6.9711366, + -13.013833, + 2.553211, + -14.678428, + -19.333553, + 5.2738123, + -30.191147, + -8.254407, + -16.734713, + -36.29533, + -0.7015533, + -19.59588, + -19.748474, + -18.429428, + -24.985508, + -14.398376, + -23.032887, + -27.591204, + -13.656402, + -33.948692, + -33.920197, + -9.174384, + 8.925708, + -22.970503, + 1.9238061, + -5.8668604, + -27.786598, + -11.159512, + -32.18803, + -16.650103, + -18.147429, + -14.823587, + 3.2132275, + -28.899687, + 0.73946625, + -14.675726, + -24.362509, + -18.907366, + -18.148087, + -24.660992, + -12.368451, + 10.51185, + -10.045945, + -30.512154, + -0.48769227, + -22.191689, + -9.914337, + -10.982109, + 31.096636, + -11.43558, + 6.894826, + 4.1693807, + 1.2161766, + -9.937122, + -27.618727, + -22.007416, + -22.37126, + -22.028944, + -10.037108, + -7.7222157, + 3.5807598, + -6.6086307, + -19.699232, + -17.251148, + 9.637636, + -10.8705435, + 8.272561, + 8.240764, + -22.69967, + -29.479254, + -11.955685, + -4.9588523, + -16.469437, + 9.750696, + -14.905879, + -20.787516, + -3.1344242, + -3.9499974, + 8.201109, + -19.819767, + -4.076858, + -24.730019, + -13.261404, + -11.716383, + -18.290712, + -15.495678, + -32.85601, + -13.886067, + -33.77542, + -22.37821, + -33.790863, + -23.158052, + -23.911886, + -28.11098, + -15.843517, + -24.879805, + -27.03218, + 5.8135962, + -25.135891, + -25.89945, + -18.168903, + -15.80312, + -29.042156, + -23.639217, + 1.8415234, + -16.338673, + 10.431047, + -34.98855, + -30.50168, + -1.8533841, + -23.127438, + -26.898434, + -6.0696855, + -0.83113074, + -13.937987, + 8.4994335, + -25.814455, + -25.507462, + -1.2937344, + -9.251707, + -11.8820095, + -13.837845, + -33.480656, + -20.987251, + 6.7093077, + -24.921698, + 3.5488389, + -18.068623, + 3.0699391, + 9.083556, + -13.918425, + -16.273378, + -22.65874, + -12.477235, + 11.250292, + -13.376665, + -5.0115275, + -23.036457, + -11.463062, + 3.7194152, + -12.564382, + -29.44567, + -9.352621, + -23.756565, + -17.759132, + -15.3389635, + -13.819872, + -6.09211, + -7.863438, + -13.616495, + -23.899826, + -9.426962, + -12.24037, + -18.85435, + 11.024011, + -19.68378, + -26.103676, + 10.497953, + 3.9857144, + -22.706993, + -2.4754145, + -21.39815, + 3.600532, + -22.364601, + -16.769764, + -12.030829, + -28.317688, + -4.760545, + -17.78651, + -20.541088, + 1.3681566, + 1.0861593, + 4.390221, + -22.447065, + 2.2553952, + -14.250181, + -28.855429, + -23.47392, + -0.6379835, + -17.019722, + -23.794706, + 2.3137836, + 6.832896, + -11.976225, + -16.869408, + -14.887161, + 11.149261, + -15.141055, + 5.0789285, + -17.239506, + -16.723783, + 9.832679, + -19.392942, + -24.406261, + -17.971964, + -2.6890767, + -13.708067, + 9.420364, + -26.4286, + -23.904675, + -33.521553, + -27.414234, + -24.713099, + -13.032957, + -20.86936, + -17.725012, + -13.713904, + -27.97785, + 1.1649175, + -12.003306, + 3.4183521, + -18.329607, + -22.500238, + -5.1817036, + -10.172258, + 8.929348, + -18.92016, + -4.0155163, + -29.874123, + -23.89452, + -14.478729, + -21.707514, + 2.8463974, + -24.179169, + -22.502762, + -18.470171, + -5.5552483, + 2.6354103, + -25.625107, + -23.603718, + -13.1784725, + -21.927172, + -17.776453, + -12.744574, + -24.39855, + 1.6557639, + -25.33089, + 3.7044208, + -14.088412, + 1.8123101, + 3.1115727, + -9.5224, + -8.527657, + -27.493273, + -28.8183, + -21.120987, + -0.42459357, + -13.964472, + -30.554207, + -16.260057, + -20.409258, + -3.838907, + -30.899261, + -25.502863, + 4.312004, + -26.893, + -20.63535, + -4.0243726, + -33.28943, + -13.433018, + -21.37861, + -17.676962, + -33.109673, + 7.7211857, + 2.9930232, + -3.4584122, + -17.335155, + 0.4309157, + -9.979049, + -27.767008, + -2.7953665, + -23.63617, + -0.20407373, + 2.833431, + -1.6160171, + -22.09123, + -15.144995, + -27.617838, + -20.576097, + -32.521618, + -1.5771652, + -3.4706712, + -13.110925, + 1.27955, + -13.123537, + -21.404385, + 2.485261, + -26.038076, + -8.591754, + -32.257572, + -3.6816385, + -23.705658, + -3.3590631, + -2.241037, + -7.3185177, + -20.510658, + 2.8498745, + -14.110134, + -21.078281, + -16.38932, + -10.101326, + -29.059853, + -31.21759, + -1.3346295, + -20.799906, + -14.345478, + -15.090428, + -16.226871, + -17.027992, + -20.647305, + -34.179035, + -14.075991, + -15.682211, + -23.77744, + 2.101532, + 8.422051, + -12.298222, + 2.824297, + -18.204716, + -2.6403008, + -17.935425, + -18.721956, + -6.343975, + 9.154357, + -16.127396, + -2.973772, + -22.44099, + 10.113919, + -16.923988, + -18.502573, + -22.337847, + 5.892835, + -30.008844, + -26.583797, + -12.331805, + -1.2270886, + -26.34871, + -13.808859, + -32.725826, + -12.638194, + -13.887938, + -20.714098, + -18.954786, + 8.2712965, + -14.246153, + -24.174063, + -22.63233, + -17.627256, + -10.120339, + -18.194794, + -8.593113, + -27.35188, + -31.873516, + -21.917208, + -27.548603, + -0.95101047, + -8.804195, + -16.590578, + -25.044327, + -32.0242, + -14.339118, + -28.126497, + 17.26326, + -27.410538, + -27.716919, + -16.625145, + -21.870625, + -21.870728, + -32.103767, + -10.273103, + 1.9282136, + -10.849964, + -15.895552, + -12.564632, + -13.048038, + -23.010983 + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "The EU and US moved closer to
an aviation trade war after
failing to reach agreement
during talks Thursday on
subsidies to Airbus Industrie." + ], + [ + " SAN FRANCISCO (Reuters) -
Nike Inc. <A HREF=\"http://w
ww.investor.reuters.com/FullQu
ote.aspx?ticker=NKE.N target=/
stocks/quickinfo/fullquote\">
;NKE.N</A>, the world's
largest athletic shoe company,
on Monday reported a 25
percent rise in quarterly
profit, beating analysts'
estimates, on strong demand
for high-end running and
basketball shoes in the
United States." + ], + [ + "NEW YORK (CBS.MW) - US stocks
traded mixed Thurday as Merck
shares lost a quarter of their
value, dragging blue chips
lower, but the Nasdaq moved
higher, buoyed by gains in the
semiconductor sector." + ], + [ + "Bankrupt ATA Airlines #39;
withdrawal from Chicago #39;s
Midway International Airport
presents a juicy opportunity
for another airline to beef up
service in the Midwest" + ], + [ + "The Instinet Group, owner of
one of the largest electronic
stock trading systems, is for
sale, executives briefed on
the plan say." + ], + [ + "The Auburn Hills-based
Chrysler Group made a profit
of \\$269 million in the third
quarter, even though worldwide
sales and revenues declined,
contributing to a \\$1." + ], + [ + "SAN FRANCISCO (CBS.MW) -- UAL
Corp., parent of United
Airlines, said Wednesday it
will overhaul its route
structure to reduce costs and
offset rising fuel costs." + ], + [ + "Annual economic growth in
China has slowed for the third
quarter in a row, falling to
9.1 per cent, third-quarter
data shows. The slowdown shows
the economy is responding to
Beijing #39;s efforts to rein
in break-neck investment and
lending." + ], + [ + "THE All-India Motor Transport
Congress (AIMTC) on Saturday
called off its seven-day
strike after finalising an
agreement with the Government
on the contentious issue of
service tax and the various
demands including tax deducted
at source (TDS), scrapping" + ], + [ + "AP - The euro-zone economy
grew by 0.5 percent in the
second quarter of 2004, a
touch slower than in the first
three months of the year,
according to initial estimates
released Tuesday by the
European Union." + ], + [ + "A few years ago, casinos
across the United States were
closing their poker rooms to
make space for more popular
and lucrative slot machines." + ], + [ + "WASHINGTON -- Consumers were
tightfisted amid soaring
gasoline costs in August and
hurricane-related disruptions
last week sent applications
for jobless benefits to their
highest level in seven months." + ], + [ + "Talking kitchens and vanities.
Musical jump ropes and potty
seats. Blusterous miniature
leaf blowers and vacuum
cleaners -- almost as loud as
the real things." + ], + [ + "Online merchants in the United
States have become better at
weeding out fraudulent credit
card orders, a new survey
indicates. But shipping
overseas remains a risky
venture. By Joanna Glasner." + ], + [ + "Popping a gadget into a cradle
to recharge it used to mean
downtime, but these days
chargers are doing double
duty, keeping your portable
devices playing even when
they're charging." + ], + [ + " SAN FRANCISCO (Reuters) -
Texas Instruments Inc. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=T
XN.N target=/stocks/quickinfo/
fullquote\">TXN.N</A>,
the largest maker of chips for
cellular phones, on Monday
said record demand for its
handset and television chips
boosted quarterly profit by
26 percent, even as it
struggles with a nagging
inventory problem." + ], + [ + "LONDON ARM Holdings, a British
semiconductor designer, said
on Monday that it would buy
Artisan Components for \\$913
million to broaden its product
range." + ], + [ + "MELBOURNE: Global shopping
mall owner Westfield Group
will team up with Australian
developer Multiplex Group to
bid 585mil (US\\$1." + ], + [ + "Reuters - Delta Air Lines Inc.
, which is\\racing to cut costs
to avoid bankruptcy, on
Wednesday reported\\a wider
quarterly loss amid soaring
fuel prices and weak\\domestic
airfares." + ], + [ + "Energy utility TXU Corp. on
Monday more than quadrupled
its quarterly dividend payment
and raised its projected
earnings for 2004 and 2005
after a companywide
performance review." + ], + [ + "Northwest Airlines Corp., the
fourth- largest US airline,
and its pilots union reached
tentative agreement on a
contract that would cut pay
and benefits, saving the
company \\$265 million a year." + ], + [ + "Microsoft Corp. MSFT.O and
cable television provider
Comcast Corp. CMCSA.O said on
Monday that they would begin
deploying set-top boxes
powered by Microsoft software
starting next week." + ], + [ + "Philippines mobile phone
operator Smart Communications
Inc. is in talks with
Singapore #39;s Mobile One for
a possible tie-up,
BusinessWorld reported Monday." + ], + [ + "Airline warns it may file for
bankruptcy if too many senior
pilots take early retirement
option. Delta Air LInes #39;
CEO says it faces bankruptcy
if it can #39;t slow the pace
of pilots taking early
retirement." + ], + [ + "Kodak Versamark #39;s parent
company, Eastman Kodak Co.,
reported Tuesday it plans to
eliminate almost 900 jobs this
year in a restructuring of its
overseas operations." + ], + [ + "A top official of the US Food
and Drug Administration said
Friday that he and his
colleagues quot;categorically
reject quot; earlier
Congressional testimony that
the agency has failed to
protect the public against
dangerous drugs." + ], + [ + "AFP - British retailer Marks
and Spencer announced a major
management shake-up as part of
efforts to revive its
fortunes, saying trading has
become tougher ahead of the
crucial Christmas period." + ], + [ + " ATLANTA (Reuters) - Home
Depot Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=HD.N target=/
stocks/quickinfo/fullquote\">
;HD.N</A>, the top home
improvement retailer, on
Tuesday reported a 15 percent
rise in third-quarter profit,
topping estimates, aided by
installed services and sales
to contractors." + ], + [ + " LONDON (Reuters) - The dollar
fought back from one-month
lows against the euro and
Swiss franc on Wednesday as
investors viewed its sell-off
in the wake of the Federal
Reserve's verdict on interest
rates as overdone." + ], + [ + "Boston Scientific Corp said on
Friday it has recalled an ear
implant the company acquired
as part of its purchase of
Advanced Bionics in June." + ], + [ + "MarketWatch.com. Richemont
sees significant H1 lift,
unclear on FY (2:53 AM ET)
LONDON (CBS.MW) -- Swiss
luxury goods maker
Richemont(zz:ZZ:001273145:
news, chart, profile), which
also is a significant" + ], + [ + "Crude oil climbed more than
\\$1 in New York on the re-
election of President George
W. Bush, who has been filling
the US Strategic Petroleum
Reserve." + ], + [ + "Yukos #39; largest oil-
producing unit regained power
supplies from Tyumenenergo, a
Siberia-based electricity
generator, Friday after the
subsidiary pledged to pay 440
million rubles (\\$15 million)
in debt by Oct. 3." + ], + [ + "US STOCKS vacillated yesterday
as rising oil prices muted
Wall Streets excitement over
strong results from Lehman
Brothers and Sprints \\$35
billion acquisition of Nextel
Communications." + ], + [ + "At the head of the class,
Rosabeth Moss Kanter is an
intellectual whirlwind: loud,
expansive, in constant motion." + ], + [ + "A bitter row between America
and the European Union over
alleged subsidies to rival
aircraft makers Boeing and
Airbus intensified yesterday." + ], + [ + "Amsterdam (pts) - Dutch
electronics company Philips
http://www.philips.com has
announced today, Friday, that
it has cut its stake in Atos
Origin by more than a half." + ], + [ + "TORONTO (CP) - Two-thirds of
banks around the world have
reported an increase in the
volume of suspicious
activities that they report to
police, a new report by KPMG
suggests." + ], + [ + "The Atkins diet frenzy slowed
growth briefly, but the
sandwich business is booming,
with \\$105 billion in sales
last year." + ], + [ + "Luxury carmaker Jaguar said
Friday it was stopping
production at a factory in
central England, resulting in
a loss of 1,100 jobs,
following poor sales in the
key US market." + ], + [ + "John Gibson said Friday that
he decided to resign as chief
executive officer of
Halliburton Energy Services
when it became apparent he
couldn #39;t become the CEO of
the entire corporation, after
getting a taste of the No." + ], + [ + "NEW YORK (Reuters) - Outback
Steakhouse Inc. said Tuesday
it lost about 130 operating
days and up to \\$2 million in
revenue because it had to
close some restaurants in the
South due to Hurricane
Charley." + ], + [ + "State insurance commissioners
from across the country have
proposed new rules governing
insurance brokerage fees,
winning praise from an
industry group and criticism
from" + ], + [ + "SOFTWARE giant Oracle #39;s
stalled \\$7.7bn (4.2bn) bid to
take over competitor
PeopleSoft has received a huge
boost after a US judge threw
out an anti-trust lawsuit
filed by the Department of
Justice to block the
acquisition." + ], + [ + "Office Depot Inc. (ODP.N:
Quote, Profile, Research) on
Tuesday warned of weaker-than-
expected profits for the rest
of the year because of
disruptions from the string of
hurricanes" + ], + [ + "THE photo-equipment maker
Kodak yesterday announced
plans to axe 600 jobs in the
UK and close a factory in
Nottinghamshire, in a move in
line with the giants global
restructuring strategy
unveiled last January." + ], + [ + "China's central bank on
Thursday raised interest rates
for the first time in nearly a
decade, signaling deepening
unease with the breakneck pace
of development and an intent
to reign in a construction
boom now sowing fears of
runaway inflation." + ], + [ + "CHICAGO - Delta Air Lines
(DAL) said Wednesday it plans
to eliminate between 6,000 and
6,900 jobs during the next 18
months, implement a 10 across-
the-board pay reduction and
reduce employee benefits." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks were likely to open
flat on Wednesday, with high
oil prices and profit warnings
weighing on the market before
earnings reports start and key
jobs data is released this
week." + ], + [ + "Best known for its popular
search engine, Google is
rapidly rolling out new
products and muscling into
Microsoft's stronghold: the
computer desktop. The
competition means consumers
get more choices and better
products." + ], + [ + " MOSCOW (Reuters) - Russia's
Gazprom said on Tuesday it
will bid for embattled oil
firm YUKOS' main unit next
month, as the Kremlin seeks
to turn the world's biggest
gas producer into a major oil
player." + ], + [ + "Federal Reserve officials
raised a key short-term
interest rate Tuesday for the
fifth time this year, and
indicated they will gradually
move rates higher next year to
keep inflation under control
as the economy expands." + ], + [ + "Canadians are paying more to
borrow money for homes, cars
and other purchases today
after a quarter-point
interest-rate increase by the
Bank of Canada yesterday was
quickly matched by the
chartered banks." + ], + [ + "Delta Air Lines is to issue
millions of new shares without
shareholder consent as part of
moves to ensure its survival." + ], + [ + "Genta (GNTA:Nasdaq - news -
research) is never boring!
Monday night, the company
announced that its phase III
Genasense study in chronic
lymphocytic leukemia (CLL) met
its primary endpoint, which
was tumor shrinkage." + ], + [ + "While the entire airline
industry #39;s finances are
under water, ATA Airlines will
have to hold its breath longer
than its competitors to keep
from going belly up." + ], + [ + "One day after ousting its
chief executive, the nation's
largest insurance broker said
it will tell clients exactly
how much they are paying for
services and renounce back-
door payments from carriers." + ], + [ + "LONDON (CBS.MW) -- Outlining
an expectation for higher oil
prices and increasing demand,
Royal Dutch/Shell on Wednesday
said it #39;s lifting project
spending to \\$45 billion over
the next three years." + ], + [ + "Tuesday #39;s meeting could
hold clues to whether it
#39;ll be a November or
December pause in rate hikes.
By Chris Isidore, CNN/Money
senior writer." + ], + [ + "The dollar may fall against
the euro for a third week in
four on concern near-record
crude oil prices will temper
the pace of expansion in the
US economy, a survey by
Bloomberg News indicates." + ], + [ + "The battle for the British-
based Chelsfield plc hotted up
at the weekend, with reports
from London that the property
giant #39;s management was
working on its own bid to
thwart the 585 million (\\$A1." + ], + [ + "SAN DIEGO --(Business Wire)--
Oct. 11, 2004 -- Breakthrough
PeopleSoft EnterpriseOne 8.11
Applications Enable
Manufacturers to Become
Demand-Driven." + ], + [ + "Reuters - Oil prices rose on
Friday as tight\\supplies of
distillate fuel, including
heating oil, ahead of\\the
northern hemisphere winter
spurred buying." + ], + [ + "Nov. 18, 2004 - An FDA
scientist says the FDA, which
is charged with protecting
America #39;s prescription
drug supply, is incapable of
doing so." + ], + [ + "The UK's inflation rate fell
in September, thanks in part
to a fall in the price of
airfares, increasing the
chance that interest rates
will be kept on hold." + ], + [ + " HONG KONG/SAN FRANCISCO
(Reuters) - IBM is selling its
PC-making business to China's
largest personal computer
company, Lenovo Group Ltd.,
for \\$1.25 billion, marking
the U.S. firm's retreat from
an industry it helped pioneer
in 1981." + ], + [ + "Nordstrom reported a strong
second-quarter profit as it
continued to select more
relevant inventory and sell
more items at full price." + ], + [ + "The Bank of England is set to
keep interest rates on hold
following the latest meeting
of the its Monetary Policy
Committee." + ], + [ + "The Bush administration upheld
yesterday the imposition of
penalty tariffs on shrimp
imports from China and
Vietnam, handing a victory to
beleaguered US shrimp
producers." + ], + [ + "House prices rose 0.2 percent
in September compared with the
month before to stand up 17.8
percent on a year ago, the
Nationwide Building Society
says." + ], + [ + "Nortel Networks says it will
again delay the release of its
restated financial results.
The Canadian telecom vendor
originally promised to release
the restated results in
September." + ], + [ + " CHICAGO (Reuters) - At first
glance, paying \\$13 or \\$14
for a hard-wired Internet
laptop connection in a hotel
room might seem expensive." + ], + [ + "Reuters - An investigation
into U.S. insurers\\and brokers
rattled insurance industry
stocks for a second day\\on
Friday as investors, shaken
further by subpoenas
delivered\\to the top U.S. life
insurer, struggled to gauge
how deep the\\probe might
reach." + ], + [ + "The Dow Jones Industrial
Average failed three times
this year to exceed its
previous high and fell to
about 10,000 each time, most
recently a week ago." + ], + [ + " SINGAPORE (Reuters) - Asian
share markets staged a broad-
based retreat on Wednesday,
led by steelmakers amid
warnings of price declines,
but also enveloping technology
and financial stocks on
worries that earnings may
disappoint." + ], + [ + "NEW YORK - CNN has a new boss
for the second time in 14
months: former CBS News
executive Jonathan Klein, who
will oversee programming and
editorial direction at the
second-ranked cable news
network." + ], + [ + "Cut-price carrier Virgin Blue
said Tuesday the cost of using
Australian airports is
spiraling upward and asked the
government to review the
deregulated system of charges." + ], + [ + "Saudi Arabia, Kuwait and the
United Arab Emirates, which
account for almost half of
OPEC #39;s oil output, said
they #39;re committed to
boosting capacity to meet
soaring demand that has driven
prices to a record." + ], + [ + "The US Commerce Department
said Thursday personal income
posted its biggest increase in
three months in August. The
government agency also said
personal spending was
unchanged after rising
strongly in July." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei average opened up 0.54
percent on Monday with banks
and exporters leading the way
as a stronger finish on Wall
Street and declining oil
prices soothed worries over
the global economic outlook." + ], + [ + "Bruce Wasserstein, head of
Lazard LLC, is asking partners
to take a one-third pay cut as
he readies the world #39;s
largest closely held
investment bank for a share
sale, people familiar with the
situation said." + ], + [ + "The Lemon Bay Manta Rays were
not going to let a hurricane
get in the way of football. On
Friday, they headed to the
practice field for the first
time in eight" + ], + [ + "Microsoft Corp. Chairman Bill
Gates has donated \\$400,000 to
a campaign in California
trying to win approval of a
measure calling for the state
to sell \\$3 billion in bonds
to fund stem-cell research." + ], + [ + "Newspaper publisher Pulitzer
Inc. said Sunday that company
officials are considering a
possible sale of the firm to
boost shareholder value." + ], + [ + "Shares of Merck amp; Co.
plunged almost 10 percent
yesterday after a media report
said that documents show the
pharmaceutical giant hid or
denied" + ], + [ + "Reuters - Wall Street was
expected to dip at\\Thursday's
opening, but shares of Texas
Instruments Inc.\\, may climb
after it gave upbeat earnings
guidance." + ], + [ + "Late in August, Boeing #39;s
top sales execs flew to
Singapore for a crucial sales
pitch. They were close to
persuading Singapore Airlines,
one of the world #39;s leading
airlines, to buy the American
company #39;s new jet, the
mid-sized 7E7." + ], + [ + "SBC Communications and
BellSouth will acquire
YellowPages.com with the goal
of building the site into a
nationwide online business
index, the companies said
Thursday." + ], + [ + "The sounds of tinkling bells
could be heard above the
bustle of the Farmers Market
on the Long Beach Promenade,
leading shoppers to a row of
bright red tin kettles dotting
a pathway Friday." + ], + [ + "LONDON Santander Central
Hispano of Spain looked
certain to clinch its bid for
the British mortgage lender
Abbey National, after HBOS,
Britain #39;s biggest home-
loan company, said Wednesday
it would not counterbid, and
after the European Commission
cleared" + ], + [ + " WASHINGTON (Reuters) - The
Justice Department is
investigating possible
accounting fraud at Fannie
Mae, bringing greater
government scrutiny to bear on
the mortgage finance company,
already facing a parallel
inquiry by the SEC, a source
close to the matter said on
Thursday." + ], + [ + "Indian industrial group Tata
agrees to invest \\$2bn in
Bangladesh, the biggest single
deal agreed by a firm in the
south Asian country." + ], + [ + "The steel tubing company
reports sharply higher
earnings, but the stock is
falling." + ], + [ + "Playboy Enterprises, the adult
entertainment company, has
announced plans to open a
private members club in
Shanghai even though the
company #39;s flagship men
#39;s magazine is still banned
in China." + ], + [ + "TORONTO (CP) - Earnings
warnings from Celestica and
Coca-Cola along with a
slowdown in US industrial
production sent stock markets
lower Wednesday." + ], + [ + "Eastman Kodak Co., the world
#39;s largest maker of
photographic film, said
Wednesday it expects sales of
digital products and services
to grow at an annual rate of
36 percent between 2003 and
2007, above prior growth rate
estimates of 26 percent
between 2002" + ], + [ + "AFP - The Iraqi government
plans to phase out slowly
subsidies on basic products,
such as oil and electricity,
which comprise 50 percent of
public spending, equal to 15
billion dollars, the planning
minister said." + ], + [ + "The federal agency that
insures pension plans said
that its deficit, already at
the highest in its history,
had doubled in its last fiscal
year, to \\$23.3 billion." + ], + [ + "A Milan judge on Tuesday opens
hearings into whether to put
on trial 32 executives and
financial institutions over
the collapse of international
food group Parmalat in one of
Europe #39;s biggest fraud
cases." + ], + [ + "The Bank of England on
Thursday left its benchmark
interest rate unchanged, at
4.75 percent, as policy makers
assessed whether borrowing
costs, already the highest in
the Group of Seven, are
constraining consumer demand." + ], + [ + "Fashion retailers Austin Reed
and Ted Baker have reported
contrasting fortunes on the
High Street. Austin Reed
reported interim losses of 2." + ], + [ + " NEW YORK (Reuters) - A
federal judge on Friday
approved Citigroup Inc.'s
\\$2.6 billion settlement with
WorldCom Inc. investors who
lost billions when an
accounting scandal plunged
the telecommunications company
into bankruptcy protection." + ], + [ + "An unspecified number of
cochlear implants to help
people with severe hearing
loss are being recalled
because they may malfunction
due to ear moisture, the US
Food and Drug Administration
announced." + ], + [ + "Profits triple at McDonald's
Japan after the fast-food
chain starts selling larger
burgers." + ], + [ + "Britain #39;s inflation rate
fell in August further below
its 2.0 percent government-set
upper limit target with
clothing and footwear prices
actually falling, official
data showed on Tuesday." + ], + [ + " LONDON (Reuters) - European
shares shrugged off a spike in
the euro to a fresh all-time
high Wednesday, with telecoms
again leading the way higher
after interim profits at
Britain's mm02 beat
expectations." + ], + [ + "WASHINGTON - Weighed down by
high energy prices, the US
economy grew slower than the
government estimated in the
April-June quarter, as higher
oil prices limited consumer
spending and contributed to a
record trade deficit." + ], + [ + "CHICAGO United Airlines says
it will need even more labor
cuts than anticipated to get
out of bankruptcy. United told
a bankruptcy court judge in
Chicago today that it intends
to start talks with unions
next month on a new round of
cost savings." + ], + [ + "The University of California,
Berkeley, has signed an
agreement with the Samoan
government to isolate, from a
tree, the gene for a promising
anti- Aids drug and to share
any royalties from the sale of
a gene-derived drug with the
people of Samoa." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei average jumped 2.5
percent by mid-afternoon on
Monday as semiconductor-
related stocks such as
Advantest Corp. mirrored a
rally by their U.S. peers
while banks and brokerages
extended last week's gains." + ], + [ + "General Motors (GM) plans to
announce a massive
restructuring Thursday that
will eliminate as many as
12,000 jobs in Europe in a
move to stem the five-year
flow of red ink from its auto
operations in the region." + ], + [ + " LONDON (Reuters) - Oil prices
held firm on Wednesday as
Hurricane Ivan closed off
crude output and shut
refineries in the Gulf of
Mexico, while OPEC's Gulf
producers tried to reassure
traders by recommending an
output hike." + ], + [ + "State-owned, running a
monopoly on imports of jet
fuel to China #39;s fast-
growing aviation industry and
a prized member of Singapore
#39;s Stock Exchange." + ], + [ + "Google has won a trade mark
dispute, with a District Court
judge finding that the search
engines sale of sponsored
search terms Geico and Geico
Direct did not breach car
insurance firm GEICOs rights
in the trade marked terms." + ], + [ + "Wall Street bounded higher for
the second straight day
yesterday as investors reveled
in sharply falling oil prices
and the probusiness agenda of
the second Bush
administration. The Dow Jones
industrials gained more than
177 points for its best day of
2004, while the Standard amp;
Poor's 500 closed at its
highest level since early
2002." + ], + [ + "Key factors help determine if
outsourcing benefits or hurts
Americans." + ], + [ + "The US Trade Representative on
Monday rejected the European
Union #39;s assertion that its
ban on beef from hormone-
treated cattle is now
justified by science and that
US and Canadian retaliatory
sanctions should be lifted." + ], + [ + "NEW YORK -- Wall Street's
fourth-quarter rally gave
stock mutual funds a solid
performance for 2004, with
small-cap equity funds and
real estate funds scoring some
of the biggest returns. Large-
cap growth equities and
technology-focused funds had
the slimmest gains." + ], + [ + "Big Food Group Plc, the UK
owner of the Iceland grocery
chain, said second-quarter
sales at stores open at least
a year dropped 3.3 percent,
the second consecutive
decline, after competitors cut
prices." + ], + [ + " WASHINGTON (Reuters) - The
first case of soybean rust has
been found on the mainland
United States and could affect
U.S. crops for the near
future, costing farmers
millions of dollars, the
Agriculture Department said on
Wednesday." + ], + [ + "The Supreme Court today
overturned a five-figure
damage award to an Alexandria
man for a local auto dealer
#39;s alleged loan scam,
ruling that a Richmond-based
federal appeals court had
wrongly" + ], + [ + "Official figures show the
12-nation eurozone economy
continues to grow, but there
are warnings it may slow down
later in the year." + ], + [ + "In upholding a lower court
#39;s ruling, the Supreme
Court rejected arguments that
the Do Not Call list violates
telemarketers #39; First
Amendment rights." + ], + [ + "Infineon Technologies, the
second-largest chip maker in
Europe, said Wednesday that it
planned to invest about \\$1
billion in a new factory in
Malaysia to expand its
automotive chip business and
be closer to customers in the
region." + ], + [ + " NEW YORK (Reuters) -
Washington Post Co. <A HREF
=\"http://www.investor.reuters.
com/FullQuote.aspx?ticker=WPO.
N target=/stocks/quickinfo/ful
lquote\">WPO.N</A>
said on Friday that quarterly
profit jumped, beating
analysts' forecasts, boosted
by results at its Kaplan
education unit and television
broadcasting operations." + ], + [ + "New orders for US-made durable
goods increased 0.2pc in
September, held back by a big
drop in orders for
transportation goods, the US
Commerce Department said
today." + ], + [ + "Siblings are the first ever to
be convicted for sending
boatloads of junk e-mail
pushing bogus products. Also:
Microsoft takes MSN music
download on a Euro trip....
Nokia begins legal battle
against European
counterparts.... and more." + ], + [ + "I always get a kick out of the
annual list published by
Forbes singling out the
richest people in the country.
It #39;s almost as amusing as
those on the list bickering
over their placement." + ], + [ + "Williams-Sonoma Inc., operator
of home furnishing chains
including Pottery Barn, said
third-quarter earnings rose 19
percent, boosted by store
openings and catalog sales." + ], + [ + "TOKYO - Mitsubishi Heavy
Industries said today it #39;s
in talks to buy a plot of land
in central Japan #39;s Nagoya
city from Mitsubishi Motors
for building aircraft parts." + ], + [ + "Japan #39;s Sumitomo Mitsui
Financial Group Inc. said
Tuesday it proposed to UFJ
Holdings Inc. that the two
banks merge on an equal basis
in its latest attempt to woo
UFJ away from a rival suitor." + ], + [ + "Oil futures prices were little
changed Thursday as traders
anxiously watched for
indications that the supply or
demand picture would change in
some way to add pressure to
the market or take some away." + ], + [ + " CHICAGO (Reuters) - Delta Air
Lines Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=DAL.N target=
/stocks/quickinfo/fullquote\"&g
t;DAL.N</A> said on
Tuesday it will cut wages by
10 percent and its chief
executive will go unpaid for
the rest of the year, but it
still warned of bankruptcy
within weeks unless more cuts
are made." + ], + [ + " WASHINGTON (Reuters) - A
former Fannie Mae <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=FNM.N
target=/stocks/quickinfo/fullq
uote\">FNM.N</A>
employee who gave U.S.
officials information about
what he saw as accounting
irregularities will not
testify as planned before a
congressional hearing next
week, a House committee said
on Friday." + ], + [ + "PARIS, Nov 4 (AFP) - The
European Aeronautic Defence
and Space Company reported
Thursday that its nine-month
net profit more than doubled,
thanks largely to sales of
Airbus aircraft, and raised
its full-year forecast." + ], + [ + "The number of people claiming
unemployment benefit last
month fell by 6,100 to
830,200, according to the
Office for National
Statistics." + ], + [ + "Tyler airlines are gearing up
for the beginning of holiday
travel, as officials offer
tips to help travelers secure
tickets and pass through
checkpoints with ease." + ], + [ + "A criminal trial scheduled to
start Monday involving former
Enron Corp. executives may
shine a rare and potentially
harsh spotlight on the inner
workings" + ], + [ + "Wal-Mart Stores Inc. #39;s
Asda, the UK #39;s second
biggest supermarket chain,
surpassed Marks amp; Spencer
Group Plc as Britain #39;s
largest clothing retailer in
the last three months,
according to the Sunday
Telegraph." + ], + [ + "Oil supply concerns and broker
downgrades of blue-chip
companies left stocks mixed
yesterday, raising doubts that
Wall Street #39;s year-end
rally would continue." + ], + [ + "Genentech Inc. said the
marketing of Rituxan, a cancer
drug that is the company #39;s
best-selling product, is the
subject of a US criminal
investigation." + ], + [ + " The world's No. 2 soft drink
company said on Thursday
quarterly profit rose due to
tax benefits." + ], + [ + "USATODAY.com - Personal
finance software programs are
the computer industry's
version of veggies: Everyone
knows they're good for you,
but it's just hard to get
anyone excited about them." + ], + [ + " NEW YORK (Reuters) - The
dollar rebounded on Monday
after last week's heavy
selloff, but analysts were
uncertain if the rally would
hold after fresh economic data
suggested the December U.S.
jobs report due Friday might
not live up to expectations." + ], + [ + " NEW YORK (Reuters) - U.S.
stock futures pointed to a
lower open on Wall Street on
Thursday, extending the
previous session's sharp
fall, with rising energy
prices feeding investor
concerns about corporate
profits and slower growth." + ], + [ + "MILAN General Motors and Fiat
on Wednesday edged closer to
initiating a legal battle that
could pit the two carmakers
against each other in a New
York City court room as early
as next month." + ], + [ + "Two of the Ford Motor Company
#39;s most senior executives
retired on Thursday in a sign
that the company #39;s deep
financial crisis has abated,
though serious challenges
remain." + ], + [ + " LONDON (Reuters) - Wall
Street was expected to start
little changed on Friday as
investors continue to fret
over the impact of high oil
prices on earnings, while
Boeing <A HREF=\"http://www.
investor.reuters.com/FullQuote
.aspx?ticker=BA.N target=/stoc
ks/quickinfo/fullquote\">BA.
N</A> will be eyed
after it reiterated its
earnings forecast." + ], + [ + "Having an always-on, fast net
connection is changing the way
Britons use the internet,
research suggests." + ], + [ + "Crude oil futures prices
dropped below \\$51 a barrel
yesterday as supply concerns
ahead of the Northern
Hemisphere winter eased after
an unexpectedly high rise in
US inventories." + ], + [ + "By Lilly Vitorovich Of DOW
JONES NEWSWIRES SYDNEY (Dow
Jones)--Rupert Murdoch has
seven weeks to convince News
Corp. (NWS) shareholders a
move to the US will make the
media conglomerate more
attractive to" + ], + [ + "The long-term economic health
of the United States is
threatened by \\$53 trillion in
government debts and
liabilities that start to come
due in four years when baby
boomers begin to retire." + ], + [ + "The Moscow Arbitration Court
ruled on Monday that the YUKOS
oil company must pay RUR
39.113bn (about \\$1.34bn) as
part of its back tax claim for
2001." + ], + [ + "NOVEMBER 11, 2004 -- Bankrupt
US Airways this morning said
it had reached agreements with
lenders and lessors to
continue operating nearly all
of its mainline and US Airways
Express fleets." + ], + [ + "The US government asks the
World Trade Organisation to
step in to stop EU member
states from \"subsidising\"
planemaker Airbus." + ], + [ + "Boston Scientific Corp.
(BSX.N: Quote, Profile,
Research) said on Wednesday it
received US regulatory
approval for a device to treat
complications that arise in
patients with end-stage kidney
disease who need dialysis." + ], + [ + "With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." + ], + [ + "Toyota Motor Corp. #39;s
shares fell for a second day,
after the world #39;s second-
biggest automaker had an
unexpected quarterly profit
drop." + ], + [ + "Britain-based HBOS says it
will file a complaint to the
European Commission against
Spanish bank Santander Central
Hispano (SCH) in connection
with SCH #39;s bid to acquire
British bank Abbey National" + ], + [ + "Verizon Wireless on Thursday
announced an agreement to
acquire all the PCS spectrum
licenses of NextWave Telecom
Inc. in 23 markets for \\$3
billion." + ], + [ + " WASHINGTON (Reuters) - The
PIMCO mutual fund group has
agreed to pay \\$50 million to
settle fraud charges involving
improper rapid dealing in
mutual fund shares, the U.S.
Securities and Exchange
Commission said on Monday." + ], + [ + "The Conference Board reported
Thursday that the Leading
Economic Index fell for a
third consecutive month in
August, suggesting slower
economic growth ahead amid
rising oil prices." + ], + [ + " SAN FRANCISCO (Reuters) -
Software maker Adobe Systems
Inc.<A HREF=\"http://www.inv
estor.reuters.com/FullQuote.as
px?ticker=ADBE.O target=/stock
s/quickinfo/fullquote\">ADBE
.O</A> on Thursday
posted a quarterly profit that
rose more than one-third from
a year ago, but shares fell 3
percent after the maker of
Photoshop and Acrobat software
did not raise forecasts for
fiscal 2005." + ], + [ + "William Morrison Supermarkets
has agreed to sell 114 small
Safeway stores and a
distribution centre for 260.2
million pounds. Morrison
bought these stores as part of
its 3 billion pound" + ], + [ + "Pepsi pushes a blue version of
Mountain Dew only at Taco
Bell. Is this a winning
strategy?" + ], + [ + "As the election approaches,
Congress abandons all pretense
of fiscal responsibility,
voting tax cuts that would
drive 10-year deficits past
\\$3 trillion." + ], + [ + "ServiceMaster profitably
bundles services and pays a
healthy 3.5 dividend." + ], + [ + "\\$222.5 million -- in an
ongoing securities class
action lawsuit against Enron
Corp. The settlement,
announced Friday and" + ], + [ + " NEW YORK (Reuters) -
Lifestyle guru Martha Stewart
said on Wednesday she wants
to start serving her prison
sentence for lying about a
suspicious stock sale as soon
as possible, so she can put
her \"nightmare\" behind her." + ], + [ + "Apple Computer's iPod remains
the king of digital music
players, but robust pretenders
to the throne have begun to
emerge in the Windows
universe. One of them is the
Zen Touch, from Creative Labs." + ], + [ + "SAN FRANCISCO (CBS.MW) --
Crude futures closed under
\\$46 a barrel Wednesday for
the first time since late
September and heating-oil and
unleaded gasoline prices
dropped more than 6 percent
following an across-the-board
climb in US petroleum
inventories." + ], + [ + "The University of Iowa #39;s
market for US presidential
futures, founded 16-years ago,
has been overtaken by a
Dublin-based exchange that is
now 25 times larger." + ], + [ + "President Bush #39;s drive to
deploy a multibillion-dollar
shield against ballistic
missiles was set back on
Wednesday by what critics
called a stunning failure of
its first full flight test in
two years." + ], + [ + "Air travelers moved one step
closer to being able to talk
on cell phones and surf the
Internet from laptops while in
flight, thanks to votes by the
Federal Communications
Commission yesterday." + ], + [ + "DESPITE the budget deficit,
continued increases in oil and
consumer prices, the economy,
as measured by gross domestic
product, grew by 6.3 percent
in the third" + ], + [ + "Consumers who cut it close by
paying bills from their
checking accounts a couple of
days before depositing funds
will be out of luck under a
new law that takes effect Oct.
28." + ], + [ + "Component problems meant
Brillian's new big screens
missed the NFL's kickoff
party." + ], + [ + "A Russian court on Thursday
rejected an appeal by the
Yukos oil company seeking to
overturn a freeze on the
accounts of the struggling oil
giant #39;s core subsidiaries." + ], + [ + "Switzerland #39;s struggling
national airline reported a
second-quarter profit of 45
million Swiss francs (\\$35.6
million) Tuesday, although its
figures were boosted by a
legal settlement in France." + ], + [ + "SIPTU has said it is strongly
opposed to any privatisation
of Aer Lingus as pressure
mounts on the Government to
make a decision on the future
funding of the airline." + ], + [ + "Molson Inc. Chief Executive
Officer Daniel O #39;Neill
said he #39;ll provide
investors with a positive #39;
#39; response to their
concerns over the company
#39;s plan to let stock-
option holders vote on its
planned merger with Adolph
Coors Co." + ], + [ + " NEW YORK (Reuters) - The
world's largest gold producer,
Newmont Mining Corp. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=NEM
.N target=/stocks/quickinfo/fu
llquote\">NEM.N</A>,
on Wednesday said higher gold
prices drove up quarterly
profit by 12.5 percent, even
though it sold less of the
precious metal." + ], + [ + " WASHINGTON (Reuters) - Fannie
Mae executives and their
regulator squared off on
Wednesday, with executives
denying any accounting
irregularity and the regulator
saying the housing finance
company's management may need
to go." + ], + [ + "As the first criminal trial
stemming from the financial
deals at Enron opened in
Houston on Monday, it is
notable as much for who is not
among the six defendants as
who is - and for how little
money was involved compared
with how much in other Enron" + ], + [ + "LONDON (CBS.MW) -- British
bank Barclays on Thursday said
it is in talks to buy a
majority stake in South
African bank ABSA. Free!" + ], + [ + "Investors sent stocks sharply
lower yesterday as oil prices
continued their climb higher
and new questions about the
safety of arthritis drugs
pressured pharmaceutical
stocks." + ], + [ + "Reuters - The head of UAL
Corp.'s United\\Airlines said
on Thursday the airline's
restructuring plan\\would lead
to a significant number of job
losses, but it was\\not clear
how many." + ], + [ + "com September 14, 2004, 9:12
AM PT. With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." + ], + [ + " NEW YORK (Reuters) -
Children's Place Retail Stores
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=PLCE.O target=/sto
cks/quickinfo/fullquote\">PL
CE.O</A> said on
Wednesday it will buy 313
retail stores from Walt
Disney Co., and its stock rose
more than 14 percent in early
morning trade." + ], + [ + "ALBANY, N.Y. -- A California-
based company that brokers
life, accident, and disability
policies for leading US
companies pocketed millions of
dollars a year in hidden
payments from insurers and
from charges on clients'
unsuspecting workers, New York
Attorney General Eliot Spitzer
charged yesterday." + ], + [ + "NORTEL Networks plans to slash
its workforce by 3500, or ten
per cent, as it struggles to
recover from an accounting
scandal that toppled three top
executives and led to a
criminal investigation and
lawsuits." + ], + [ + "Ebay Inc. (EBAY.O: Quote,
Profile, Research) said on
Friday it would buy Rent.com,
an Internet housing rental
listing service, for \\$415
million in a deal that gives
it access to a new segment of
the online real estate market." + ], + [ + "Noranda Inc., Canada #39;s
biggest mining company, began
exclusive talks on a takeover
proposal from China Minmetals
Corp. that would lead to the
spinoff of Noranda #39;s
aluminum business to
shareholders." + ], + [ + "Google warned Thursday that
increased competition and the
maturing of the company would
result in an quot;inevitable
quot; slowing of its growth." + ], + [ + " TOKYO (Reuters) - The Nikkei
average rose 0.55 percent by
midsession on Wednesday as
some techs including Advantest
Corp. gained ground after
Wall Street reacted positively
to results from Intel Corp.
released after the U.S. market
close." + ], + [ + " ATLANTA (Reuters) - Soft
drink giant Coca-Cola Co.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=KO.N target=/stocks/quic
kinfo/fullquote\">KO.N</A
>, stung by a prolonged
downturn in North America,
Germany and other major
markets, on Thursday lowered
its key long-term earnings
and sales targets." + ], + [ + "A Canadian court approved Air
Canada #39;s (AC.TO: Quote,
Profile, Research) plan of
arrangement with creditors on
Monday, clearing the way for
the world #39;s 11th largest
airline to emerge from
bankruptcy protection at the
end of next month" + ], + [ + " LONDON (Reuters) - Oil prices
eased on Monday after rebels
in Nigeria withdrew a threat
to target oil operations, but
lingering concerns over
stretched supplies ahead of
winter kept prices close to
\\$50." + ], + [ + " LONDON (Reuters) - Oil prices
climbed above \\$42 a barrel on
Wednesday, rising for the
third day in a row as cold
weather gripped the U.S.
Northeast, the world's biggest
heating fuel market." + ], + [ + "Travelers headed home for
Thanksgiving were greeted
Wednesday with snow-covered
highways in the Midwest, heavy
rain and tornadoes in parts of
the South, and long security
lines at some of the nation
#39;s airports." + ], + [ + "The union representing flight
attendants on Friday said it
mailed more than 5,000 strike
authorization ballots to its
members employed by US Airways
as both sides continued talks
that are expected to stretch
through the weekend." + ], + [ + "LOS ANGELES (CBS.MW) - The US
Securities and Exchange
Commission is probing
transactions between Delphi
Corp and EDS, which supplies
the automotive parts and
components giant with
technology services, Delphi
said late Wednesday." + ], + [ + "MONTREAL (CP) - Molson Inc.
and Adolph Coors Co. are
sweetening their brewery
merger plan with a special
dividend to Molson
shareholders worth \\$381
million." + ], + [ + "WELLINGTON: National carrier
Air New Zealand said yesterday
the Australian Competition
Tribunal has approved a
proposed alliance with Qantas
Airways Ltd, despite its
rejection in New Zealand." + ], + [ + "\"Everyone's nervous,\" Acting
Undersecretary of Defense
Michael W. Wynne warned in a
confidential e-mail to Air
Force Secretary James G. Roche
on July 8, 2003." + ], + [ + "Reuters - Alpharma Inc. on
Friday began\\selling a cheaper
generic version of Pfizer
Inc.'s #36;3\\billion a year
epilepsy drug Neurontin
without waiting for a\\court
ruling on Pfizer's request to
block the copycat medicine." + ], + [ + "Opinion: Privacy hysterics
bring old whine in new bottles
to the Internet party. The
desktop search beta from this
Web search leader doesn #39;t
do anything you can #39;t do
already." + ], + [ + "The Nordics fared well because
of their long-held ideals of
keeping corruption clamped
down and respect for
contracts, rule of law and
dedication to one-on-one
business relationships." + ], + [ + "Don't bother with the small
stuff. Here's what really
matters to your lender." + ], + [ + "UK interest rates have been
kept on hold at 4.75 following
the latest meeting of the Bank
of England #39;s rate-setting
committee." + ], + [ + "Resurgent oil prices paused
for breath as the United
States prepared to draw on its
emergency reserves to ease
supply strains caused by
Hurricane Ivan." + ], + [ + "President Bush, who credits
three years of tax relief
programs with helping
strengthen the slow economy,
said Saturday he would sign
into law the Working Families
Tax Relief Act to preserve tax
cuts." + ], + [ + "HEN investors consider the
bond market these days, the
low level of interest rates
should be more cause for worry
than for gratitude." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks rallied on Monday after
software maker PeopleSoft Inc.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=PSFT.O target=/stocks/qu
ickinfo/fullquote\">PSFT.O&l
t;/A> accepted a sweetened
\\$10.3 billion buyout by rival
Oracle Corp.'s <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=ORCL.O ta
rget=/stocks/quickinfo/fullquo
te\">ORCL.O</A> and
other big deals raised
expectations of more
takeovers." + ], + [ + "The New Jersey-based Accoona
Corporation, an industry
pioneer in artificial
intelligence search
technology, announced on
Monday the launch of Accoona." + ], + [ + "Goldman Sachs Group Inc. on
Thursday said fourth-quarter
profit rose as its fixed-
income, currency and
commodities business soared
while a rebounding stock
market boosted investment
banking." + ], + [ + " NEW YORK (Reuters) - The
dollar rose on Monday in a
retracement from last week's
steep losses, but dealers said
the bias toward a weaker
greenback remained intact." + ], + [ + "Moscow - Russia plans to
combine Gazprom, the world
#39;s biggest natural gas
producer, with state-owned oil
producer Rosneft, easing rules
for trading Gazprom shares and
creating a company that may
dominate the country #39;s
energy industry." + ], + [ + "Diversified manufacturer
Honeywell International Inc.
(HON.N: Quote, Profile,
Research) posted a rise in
quarterly profit as strong
demand for aerospace equipment
and automobile components" + ], + [ + "Reuters - U.S. housing starts
jumped a\\larger-than-expected
6.4 percent in October to the
busiest pace\\since December as
buyers took advantage of low
mortgage rates,\\a government
report showed on Wednesday." + ], + [ + "The Securities and Exchange
Commission ordered mutual
funds to stop paying higher
commissions to brokers who
promote the companies' funds
and required portfolio
managers to reveal investments
in funds they supervise." + ], + [ + "Sumitomo Mitsui Financial
Group (SMFG), Japans second
largest bank, today put
forward a 3,200 billion (\\$29
billion) takeover bid for
United Financial Group (UFJ),
the countrys fourth biggest
lender, in an effort to regain
initiative in its bidding" + ], + [ + " NEW YORK (Reuters) - U.S.
chain store retail sales
slipped during the
Thanksgiving holiday week, as
consumers took advantage of
discounted merchandise, a
retail report said on
Tuesday." + ], + [ + "By George Chamberlin , Daily
Transcript Financial
Correspondent. Concerns about
oil production leading into
the winter months sent shivers
through the stock market
Wednesday." + ], + [ + "Airbus has withdrawn a filing
that gave support for
Microsoft in an antitrust case
before the European Union
#39;s Court of First Instance,
a source close to the
situation said on Friday." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks rose on Wednesday
lifted by a merger between
retailers Kmart and Sears,
better-than-expected earnings
from Hewlett-Packard and data
showing a slight rise in core
inflation." + ], + [ + "European Commission president
Romano Prodi has unveiled
proposals to loosen the
deficit rules under the EU
Stability Pact. The loosening
was drafted by monetary
affairs commissioner Joaquin
Almunia, who stood beside the
president at the announcement." + ], + [ + "Retail sales in Britain saw
the fastest growth in
September since January,
casting doubts on the view
that the economy is slowing
down, according to official
figures released Thursday." + ], + [ + " NEW YORK (Reuters) -
Interstate Bakeries Corp.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=IBC.N target=/stocks/qui
ckinfo/fullquote\">IBC.N<
/A>, maker of Hostess
Twinkies and Wonder Bread,
filed for bankruptcy on
Wednesday after struggling
with more than \\$1.3 billion
in debt and high costs." + ], + [ + "Delta Air Lines (DAL.N: Quote,
Profile, Research) on Thursday
said it reached a deal with
FedEx Express to sell eight
McDonnell Douglas MD11
aircraft and four spare
engines for delivery in 2004." + ], + [ + "Leading OPEC producer Saudi
Arabia said on Monday in
Vienna, Austria, that it had
made a renewed effort to
deflate record high world oil
prices by upping crude output
again." + ], + [ + "The founders of the Pilgrim
Baxter amp; Associates money-
management firm agreed
yesterday to personally fork
over \\$160 million to settle
charges they allowed a friend
to" + ], + [ + "IBM Corp. Tuesday announced
plans to acquire software
vendor Systemcorp ALG for an
undisclosed amount. Systemcorp
of Montreal makes project
portfolio management software
aimed at helping companies
better manage their IT
projects." + ], + [ + "Forbes.com - By now you
probably know that earnings of
Section 529 college savings
accounts are free of federal
tax if used for higher
education. But taxes are only
part of the problem. What if
your investments tank? Just
ask Laurence and Margo
Williams of Alexandria, Va. In
2000 they put #36;45,000 into
the Virginia Education Savings
Trust to open accounts for
daughters Lea, now 5, and
Anne, now 3. Since then their
investment has shrunk 5 while
the average private college
tuition has climbed 18 to
#36;18,300." + ], + [ + "Coca-Cola Amatil Ltd.,
Australia #39;s biggest soft-
drink maker, offered A\\$500
million (\\$382 million) in
cash and stock for fruit
canner SPC Ardmona Ltd." + ], + [ + "US technology shares tumbled
on Friday after technology
bellwether Intel Corp.
(INTC.O: Quote, Profile,
Research) slashed its revenue
forecast, but blue chips were
only moderately lower as drug
and industrial stocks made
solid gains." + ], + [ + " WASHINGTON (Reuters) - Final
U.S. government tests on an
animal suspected of having mad
cow disease were not yet
complete, the U.S. Agriculture
Department said, with no
announcement on the results
expected on Monday." + ], + [ + "Metro, Germany's biggest
retailer, turns in weaker-
than-expected profits as sales
at its core supermarkets
division dip lower." + ], + [ + "BOSTON (CBS.MW) -- First
Command has reached a \\$12
million settlement with
federal regulators for making
misleading statements and
omitting important information
when selling mutual funds to
US military personnel." + ], + [ + "The federal government, banks
and aircraft lenders are
putting the clamps on
airlines, particularly those
operating under bankruptcy
protection." + ], + [ + "EURO DISNEY, the financially
crippled French theme park
operator, has admitted that
its annual losses more than
doubled last financial year as
it was hit by a surge in
costs." + ], + [ + "WASHINGTON The idea of a no-
bid contract for maintaining
airport security equipment has
turned into a non-starter for
the Transportation Security
Administration." + ], + [ + "Eyetech (EYET:Nasdaq - news -
research) did not open for
trading Friday because a Food
and Drug Administration
advisory committee is meeting
to review the small New York-
based biotech #39;s
experimental eye disease drug." + ], + [ + "On September 13, 2001, most
Americans were still reeling
from the shock of the
terrorist attacks on New York
and the Pentagon two days
before." + ], + [ + "Domestic air travelers could
be surfing the Web by 2006
with government-approved
technology that allows people
access to high-speed Internet
connections while they fly." + ], + [ + "GENEVA: Cross-border
investment is set to bounce in
2004 after three years of deep
decline, reflecting a stronger
world economy and more
international merger activity,
the United Nations (UN) said
overnight." + ], + [ + "Chewing gum giant Wm. Wrigley
Jr. Co. on Thursday said it
plans to phase out production
of its Eclipse breath strips
at a plant in Phoenix, Arizona
and shift manufacturing to
Poznan, Poland." + ], + [ + "com September 16, 2004, 7:58
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." + ], + [ + "Tightness in the labour market
notwithstanding, the prospects
for hiring in the third
quarter are down from the
second quarter, according to
the new Manpower Employment
Outlook Survey." + ], + [ + " NEW YORK (Reuters) - The
dollar rose on Friday, after a
U.S. report showed consumer
prices in line with
expections, reminding
investors that the Federal
Reserve was likely to
continue raising interest
rates, analysts said." + ], + [ + "Last year some election
watchers made a bold
prediction that this
presidential election would
set a record: the first half
billion dollar campaign in
hard money alone." + ], + [ + "It #39;s one more blow to
patients who suffer from
arthritis. Pfizer, the maker
of Celebrex, says it #39;s
painkiller poses an increased
risk of heart attacks to
patients using the drugs." + ], + [ + "The economic growth rate in
the July-September period was
revised slightly downward from
an already weak preliminary
report, the government said
Wednesday." + ], + [ + "A drug company executive who
spoke out in support of
Montgomery County's proposal
to import drugs from Canada
and similar legislation before
Congress said that his company
has launched an investigation
into his political activities." + ], + [ + "SYDNEY (AFP) - Australia #39;s
commodity exports are forecast
to increase by 15 percent to a
record 95 billion dollars (71
million US), the government
#39;s key economic forecaster
said." + ], + [ + "Google won a major legal
victory when a federal judge
ruled that the search engines
advertising policy does not
violate federal trademark
laws." + ], + [ + "AT amp;T Corp. on Thursday
said it is reducing one fifth
of its workforce this year and
will record a non-cash charge
of approximately \\$11." + ], + [ + "A rift appeared within Canada
#39;s music industry yesterday
as prominent artists called on
the CRTC to embrace satellite
radio and the industry warned
of lost revenue and job
losses." + ], + [ + "NEW DELHI: India and Pakistan
agreed on Monday to step up
cooperation in the energy
sector, which could lead to
Pakistan importing large
amounts of diesel fuel from
its neighbour, according to
Pakistani Foreign Minister
Khurshid Mehmood Kasuri." + ], + [ + "TORONTO (CP) - Glamis Gold of
Reno, Nev., is planning a
takeover bid for Goldcorp Inc.
of Toronto - but only if
Goldcorp drops its
\\$2.4-billion-Cdn offer for
another Canadian firm, made in
early December." + ], + [ + "This week will see the release
of October new and existing
home sales, a measure of
strength in the housing
industry. But the short
holiday week will also leave
investors looking ahead to the
holiday travel season." + ], + [ + " BETHESDA, Md. (Reuters) - The
use of some antidepressant
drugs appears linked to an
increase in suicidal behavior
in some children and teen-
agers, a U.S. advisory panel
concluded on Tuesday." + ], + [ + " NEW YORK (Reuters) - U.S.
technology stocks opened lower
on Thursday after a sales
warning from Applied Materials
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AMAT.O target=/sto
cks/quickinfo/fullquote\">AM
AT.O</A>, while weekly
jobless claims data met Wall
Street's expectations,
leaving the Dow and S P 500
market measures little
changed." + ], + [ + "CHICAGO : Interstate Bakeries
Corp., the maker of popular,
old-style snacks Twinkies and
Hostess Cakes, filed for
bankruptcy, citing rising
costs and falling sales." + ], + [ + "Delta Air Lines (DAL:NYSE -
commentary - research) will
cut employees and benefits but
give a bigger-than-expected
role to Song, its low-cost
unit, in a widely anticipated
but still unannounced
overhaul, TheStreet.com has
learned." + ], + [ + "Instead of the skinny black
line, showing a hurricane
#39;s forecast track,
forecasters have drafted a
couple of alternative graphics
to depict where the storms
might go -- and they want your
opinion." + ], + [ + "BERLIN - Volkswagen AG #39;s
announcement this week that it
has forged a new partnership
deal with Malaysian carmaker
Proton comes as a strong euro
and Europe #39;s weak economic
performance triggers a fresh
wave of German investment in
Asia." + ], + [ + "Consumers in Dublin pay more
for basic goods and services
that people elsewhere in the
country, according to figures
released today by the Central
Statistics Office." + ], + [ + "LIBERTY Media #39;s move last
week to grab up to 17.1 per
cent of News Corporation
voting stock has prompted the
launch of a defensive
shareholder rights plan." + ], + [ + "The blue-chip Hang Seng Index
rose 171.88 points, or 1.22
percent, to 14,066.91. On
Friday, the index had slipped
31.58 points, or 0.2 percent." + ], + [ + "Shares plunge after company
says its vein graft treatment
failed to show benefit in
late-stage test. CHICAGO
(Reuters) - Biotechnology
company Corgentech Inc." + ], + [ + "US blue-chip stocks rose
slightly on Friday as
government data showed better-
than-expected demand in August
for durable goods other than
transportation equipment, but
climbing oil prices limited
gains." + ], + [ + "Business software maker
PeopleSoft Inc. said Monday
that it expects third-quarter
revenue to range between \\$680
million and \\$695 million,
above average Wall Street
estimates of \\$651." + ], + [ + "Reuters - Enron Corp. ,
desperate to\\meet profit
targets, \"parked\" unwanted
power generating barges\\at
Merrill Lynch in a sham sale
designed to be reversed,
a\\prosecutor said on Tuesday
in the first criminal trial
of\\former executives at the
fallen energy company." + ], + [ + " NEW YORK (Reuters) - The
dollar rebounded on Monday
after a heavy selloff last
week, but analysts were
uncertain if the rally could
hold as the drumbeat of
expectation began for to the
December U.S. jobs report due
Friday." + ], + [ + "Coles Myer Ltd. Australia
#39;s biggest retailer,
increased second-half profit
by 26 percent after opening
fuel and convenience stores,
selling more-profitable
groceries and cutting costs." + ], + [ + "MOSCOW: Us oil major
ConocoPhillips is seeking to
buy up to 25 in Russian oil
giant Lukoil to add billions
of barrels of reserves to its
books, an industry source
familiar with the matter said
on Friday." + ], + [ + "US Airways Group (otc: UAIRQ -
news - people ) on Thursday
said it #39;ll seek a court
injunction to prohibit a
strike by disaffected unions." + ], + [ + "Shares in Unilever fall after
the Anglo-Dutch consumer goods
giant issued a surprise
profits warning." + ], + [ + "SAN FRANCISCO (CBS.MW) - The
Canadian government will sell
its 19 percent stake in Petro-
Canada for \\$2.49 billion,
according to the final
prospectus filed with the US
Securities and Exchange
Commission Thursday." + ], + [ + " HYDERABAD, India (Reuters) -
Microsoft Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=MSFT.O t
arget=/stocks/quickinfo/fullqu
ote\">MSFT.O</A> will
hire several hundred new staff
at its new Indian campus in
the next year, its chief
executive said on Monday, in a
move aimed at strengthening
its presence in Asia's fourth-
biggest economy." + ], + [ + "Alitalia SpA, Italy #39;s
largest airline, reached an
agreement with its flight
attendants #39; unions to cut
900 jobs, qualifying the
company for a government
bailout that will keep it in
business for another six
months." + ], + [ + "P amp;Os cutbacks announced
today are the result of the
waves of troubles that have
swamped the ferry industry of
late. Some would say the
company has done well to
weather the storms for as long
as it has." + ], + [ + "TORONTO (CP) - Russia #39;s
Severstal has made an offer to
buy Stelco Inc., in what #39;s
believed to be one of several
competing offers emerging for
the restructuring but
profitable Hamilton steel
producer." + ], + [ + "Walt Disney Co. #39;s
directors nominated Michael
Ovitz to serve on its board
for another three years at a
meeting just weeks before
forcing him out of his job as" + ], + [ + "The European Union, Japan,
Brazil and five other
countries won World Trade
Organization approval to
impose tariffs worth more than
\\$150 million a year on
imports from the United" + ], + [ + "Industrial conglomerate
Honeywell International on
Wednesday said it has filed a
lawsuit against 34 electronics
companies including Apple
Computer and Eastman Kodak,
claiming patent infringement
of its liquid crystal display
technology." + ], + [ + "Australia #39;s Computershare
has agreed to buy EquiServe of
the United States for US\\$292
million (\\$423 million),
making it the largest US share
registrar and driving its
shares up by a third." + ], + [ + "Interest rates on short-term
Treasury securities were mixed
in yesterday's auction. The
Treasury Department sold \\$18
billion in three-month bills
at a discount rate of 1.640
percent, up from 1.635 percent
last week. An additional \\$16
billion was sold in six-month
bills at a rate of 1.840
percent, down from 1.860
percent." + ], + [ + "Two top executives of scandal-
tarred insurance firm Marsh
Inc. were ousted yesterday,
the company said, the latest
casualties of an industry
probe by New York's attorney
general." + ], + [ + "USDA #39;s Animal Plant Health
Inspection Service (APHIS)
this morning announced it has
confirmed a detection of
soybean rust from two test
plots at Louisiana State
University near Baton Rouge,
Louisiana." + ], + [ + "Mexican Cemex, being the third
largest cement maker in the
world, agreed to buy its
British competitor - RMC Group
- for \\$5.8 billion, as well
as their debts in order to
expand their activity on the
building materials market of
the USA and Europe." + ], + [ + "The world's largest insurance
group pays \\$126m in fines as
part of a settlement with US
regulators over its dealings
with two firms." + ], + [ + "The tobacco firm John Player
amp; Sons has announced plans
to lay off 90 workers at its
cigarette factory in Dublin.
The company said it was
planning a phased closure of
the factory between now and
February as part of a review
of its global operations." + ], + [ + "AP - Consumers borrowed more
freely in September,
especially when it came to
racking up charges on their
credit cards, the Federal
Reserve reported Friday." + ], + [ + "Bold, innovative solutions are
key to addressing the rapidly
rising costs of higher
education and the steady
reduction in government-
subsidized help to finance
such education." + ], + [ + "Just as the PhD crowd emerge
with different interpretations
of today's economy, everyday
Americans battling to balance
the checkbook hold diverse
opinions about where things
stand now and in the future." + ], + [ + "Authorities here are always
eager to show off their
accomplishments, so when
Beijing hosted the World
Toilet Organization conference
last week, delegates were
given a grand tour of the
city's toilets." + ], + [ + "WASHINGTON Trying to break a
deadlock on energy policy, a
diverse group of
environmentalists, academics
and former government
officials were to publish a
report on Wednesday that
presents strategies for making
the United States cleaner,
more competitive" + ], + [ + "A consortium led by Royal
Dutch/Shell Group that is
developing gas reserves off
Russia #39;s Sakhalin Island
said Thursday it has struck a
US\\$6 billion (euro4." + ], + [ + "An audit by international
observers supported official
elections results that gave
President Hugo Chavez a
victory over a recall vote
against him, the secretary-
general of the Organisation of
American States announced." + ], + [ + "Apple is recalling 28,000
faulty batteries for its
15-inch Powerbook G4 laptops." + ], + [ + "The former Chief Executive
Officer of Computer Associates
was indicted by a federal
grand jury in New York
Wednesday for allegedly
participating in a massive
fraud conspiracy and an
elaborate cover up of a scheme
that cost investors" + ], + [ + "Honeywell International Inc.,
the world #39;s largest
supplier of building controls,
agreed to buy Novar Plc for
798 million pounds (\\$1.53
billion) to expand its
security, fire and
ventilation-systems business
in Europe." + ], + [ + "San Francisco investment bank
Thomas Weisel Partners on
Thursday agreed to pay \\$12.5
million to settle allegations
that some of the stock
research the bank published
during the Internet boom was
tainted by conflicts of
interest." + ], + [ + "Forbes.com - Peter Frankling
tapped an unusual source to
fund his new business, which
makes hot-dog-shaped ice cream
treats known as Cool Dogs: Two
investors, one a friend and
the other a professional
venture capitalist, put in
more than #36;100,000 each
from their Individual
Retirement Accounts. Later
Franklin added #36;150,000
from his own IRA." + ], + [ + "A San Diego insurance
brokerage has been sued by New
York Attorney General Elliot
Spitzer for allegedly
soliciting payoffs in exchange
for steering business to
preferred insurance companies." + ], + [ + "The European Union agreed
Monday to lift penalties that
have cost American exporters
\\$300 million, following the
repeal of a US corporate tax
break deemed illegal under
global trade rules." + ], + [ + " LONDON (Reuters) - European
stock markets scaled
near-2-1/2 year highs on
Friday as oil prices held
below \\$48 a barrel, and the
euro held off from mounting
another assault on \\$1.30 but
hovered near record highs
against the dollar." + ], + [ + "Reuters - The company behind
the Atkins Diet\\on Friday
shrugged off a recent decline
in interest in low-carb\\diets
as a seasonal blip, and its
marketing chief said\\consumers
would cut out starchy foods
again after picking up\\pounds
over the holidays." + ], + [ + "There #39;s something to be
said for being the quot;first
mover quot; in an industry
trend. Those years of extra
experience in tinkering with a
new idea can be invaluable in
helping the first" + ], + [ + " NEW YORK (Reuters) - Shares
of Chiron Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=CHIR.O t
arget=/stocks/quickinfo/fullqu
ote\">CHIR.O</A> fell
7 percent before the market
open on Friday, a day after
the biopharmaceutical company
said it is delaying shipment
of its flu vaccine, Fluvirin,
because lots containing 4
million vaccines do not meet
product sterility standards." + ], + [ + "The nation's top
telecommunications regulator
said yesterday he will push --
before the next president is
inaugurated -- to protect
fledgling Internet telephone
services from getting taxed
and heavily regulated by the
50 state governments." + ], + [ + "DALLAS -- Belo Corp. said
yesterday that it would cut
250 jobs, more than half of
them at its flagship
newspaper, The Dallas Morning
News, and that an internal
investigation into circulation
overstatements" + ], + [ + "The Federal Reserve still has
some way to go to restore US
interest rates to more normal
levels, Philadelphia Federal
Reserve President Anthony
Santomero said on Monday." + ], + [ + "Delta Air Lines (DAL.N: Quote,
Profile, Research) said on
Wednesday its auditors have
expressed doubt about the
airline #39;s financial
viability." + ], + [ + "Takeover target Ronin Property
Group said it would respond to
an offer by Multiplex Group
for all the securities in the
company in about three weeks." + ], + [ + "Google Inc. is trying to
establish an online reading
room for five major libraries
by scanning stacks of hard-to-
find books into its widely
used Internet search engine." + ], + [ + "HOUSTON--(BUSINESS
WIRE)--Sept. 1, 2004-- L
#39;operazione crea una
centrale globale per l
#39;analisi strategica el
#39;approfondimento del
settore energetico IHS Energy,
fonte globale leader di
software, analisi e
informazioni" + ], + [ + "The US airline industry,
riddled with excess supply,
will see a significant drop in
capacity, or far fewer seats,
as a result of at least one
airline liquidating in the
next year, according to
AirTran Airways Chief
Executive Joe Leonard." + ], + [ + "Boeing (nyse: BA - news -
people ) Chief Executive Harry
Stonecipher is keeping the
faith. On Monday, the head of
the aerospace and military
contractor insists he #39;s
confident his firm will
ultimately win out" + ], + [ + "Australia #39;s biggest
supplier of fresh milk,
National Foods, has posted a
net profit of \\$68.7 million,
an increase of 14 per cent on
last financial year." + ], + [ + "Lawyers for customers suing
Merck amp; Co. want to
question CEO Raymond Gilmartin
about what he knew about the
dangers of Vioxx before the
company withdrew the drug from
the market because of health
hazards." + ], + [ + "New York; September 23, 2004 -
The Department of Justice
(DoJ), FBI and US Attorney
#39;s Office handed down a
10-count indictment against
former Computer Associates
(CA) chairman and CEO Sanjay
Kumar and Stephen Richards,
former CA head of worldwide
sales." + ], + [ + "PACIFIC Hydro shares yesterday
caught an updraught that sent
them more than 20 per cent
higher after the wind farmer
moved to flush out a bidder." + ], + [ + " NEW YORK (Reuters) - U.S.
consumer confidence retreated
in August while Chicago-area
business activity slowed,
according to reports on
Tuesday that added to worries
the economy's patch of slow
growth may last beyond the
summer." + ], + [ + "Unilever has reported a three
percent rise in third-quarter
earnings but warned it is
reviewing its targets up to
2010, after issuing a shock
profits warning last month." + ], + [ + " LONDON (Reuters) - Oil prices
extended recent heavy losses
on Wednesday ahead of weekly
U.S. data expected to show
fuel stocks rising in time
for peak winter demand." + ], + [ + "Vodafone has increased the
competition ahead of Christmas
with plans to launch 10
handsets before the festive
season. The Newbury-based
group said it will begin
selling the phones in
November." + ], + [ + "A former assistant treasurer
at Enron Corp. (ENRNQ.PK:
Quote, Profile, Research)
agreed to plead guilty to
conspiracy to commit
securities fraud on Tuesday
and will cooperate with" + ], + [ + "UK house prices fell by 1.1 in
October, confirming a
softening of the housing
market, Halifax has said. The
UK #39;s biggest mortgage
lender said prices rose 18." + ], + [ + "More than six newspaper
companies have received
letters from the Securities
and Exchange Commission
seeking information about
their circulation practices." + ], + [ + "THE 64,000 dollar -
correction, make that 500
million dollar -uestion
hanging over Shire
Pharmaceuticals is whether the
5 per cent jump in the
companys shares yesterday
reflects relief that US
regulators have finally
approved its drug for" + ], + [ + "Businesses saw inventories
rise in July and sales picked
up, the government reported
Wednesday. The Commerce
Department said that stocks of
unsold goods increased 0.9 in
July, down from a 1.1 rise in
June." + ], + [ + "Shares of Genta Inc. (GNTA.O:
Quote, Profile, Research)
soared nearly 50 percent on
Monday after the biotechnology
company presented promising
data on an experimental
treatment for blood cancers." + ], + [ + "SBC Communications Inc. plans
to cut at least 10,000 jobs,
or 6 percent of its work
force, by the end of next year
to compensate for a drop in
the number of local-telephone
customers." + ], + [ + " WASHINGTON (Reuters) - Major
cigarette makers go on trial
on Tuesday in the U.S.
government's \\$280 billion
racketeering case that
charges the tobacco industry
with deliberately deceiving
the public about the risks of
smoking since the 1950s." + ], + [ + "Federal Reserve policy-makers
raised the benchmark US
interest rate a quarter point
to 2.25 per cent and restated
a plan to carry out" + ], + [ + " DETROIT (Reuters) - A
Canadian law firm on Tuesday
said it had filed a lawsuit
against Ford Motor Co. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=F
.N target=/stocks/quickinfo/fu
llquote\">F.N</A> over
what it claims are defective
door latches on about 400,000
of the automaker's popular
pickup trucks and SUVs." + ], + [ + "AstraZeneca Plc suffered its
third setback in two months on
Friday as lung cancer drug
Iressa failed to help patients
live longer" + ], + [ + "Bruce Wasserstein, the
combative chief executive of
investment bank Lazard, is
expected to agree this week
that he will quit the group
unless he can pull off a
successful" + ], + [ + "Dr. David J. Graham, the FDA
drug safety reviewer who
sounded warnings over five
drugs he felt could become the
next Vioxx has turned to a
Whistleblower protection group
for legal help." + ], + [ + "The Kmart purchase of Sears,
Roebuck may be the ultimate
expression of that old saying
in real estate: location,
location, location." + ], + [ + "Sprint Corp. (FON.N: Quote,
Profile, Research) on Friday
said it plans to cut up to 700
jobs as it realigns its
business to focus on wireless
and Internet services and
takes a non-cash network
impairment charge." + ], + [ + "Says that amount would have
been earned for the first 9
months of 2004, before AT
amp;T purchase. LOS ANGELES,
(Reuters) - Cingular Wireless
would have posted a net profit
of \\$650 million for the first
nine months" + ], + [ + " CHICAGO (Reuters) - Goodyear
Tire Rubber Co. <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=GT.N t
arget=/stocks/quickinfo/fullqu
ote\">GT.N</A> said
on Friday it will cut 340 jobs
in its engineered products and
chemical units as part of its
cost-reduction efforts,
resulting in a third-quarter
charge." + ], + [ + "Reuters - Shares of long-
distance phone\\companies AT T
Corp. and MCI Inc. have
plunged\\about 20 percent this
year, but potential buyers
seem to be\\holding out for
clearance sales." + ], + [ + "Russian oil giant Yukos files
for bankruptcy protection in
the US in a last ditch effort
to stop the Kremlin auctioning
its main production unit." + ], + [ + "British Airways #39; (BA)
chief executive Rod Eddington
has admitted that the company
quot;got it wrong quot; after
staff shortages led to three
days of travel chaos for
passengers." + ], + [ + "The issue of drug advertising
directly aimed at consumers is
becoming political." + ], + [ + "AP - China's economic boom is
still roaring despite efforts
to cool sizzling growth, with
the gross domestic product
climbing 9.5 percent in the
first three quarters of this
year, the government reported
Friday." + ], + [ + "Soaring petroleum prices
pushed the cost of goods
imported into the U.S. much
higher than expected in
August, the government said
today." + ], + [ + "Anheuser-Busch teams up with
Vietnam's largest brewer,
laying the groundwork for
future growth in the region." + ], + [ + "CHARLOTTE, NC - Shares of
Krispy Kreme Doughnuts Inc.
#39;s fell sharply Monday as a
79 percent plunge in third-
quarter earnings and an
intensifying accounting
investigation overshadowed the
pastrymaker #39;s statement
that the low-carb craze might
be easing." + ], + [ + "OXNARD - A leak of explosive
natural gas forced dozens of
workers to evacuate an
offshore oil platform for
hours Thursday but no damage
or injuries were reported." + ], + [ + "AP - Assets of the nation's
retail money market mutual
funds rose by #36;2.85
billion in the latest week to
#36;845.69 billion, the
Investment Company Institute
said Thursday." + ], + [ + "Peter Mandelson provoked fresh
Labour in-fighting yesterday
with an implied attack on
Gordon Brown #39;s
quot;exaggerated gloating
quot; about the health of the
British economy." + ], + [ + "JC Penney said yesterday that
Allen I. Questrom, the chief
executive who has restyled the
once-beleaguered chain into a
sleeker and more profitable
entity, would be succeeded by
Myron E. Ullman III, another
longtime retail executive." + ], + [ + " In the cosmetics department
at Hecht's in downtown
Washington, construction crews
have ripped out the
traditional glass display
cases, replacing them with a
system of open shelves stacked
high with fragrances from
Chanel, Burberry and Armani,
now easily within arm's reach
of the impulse buyer." + ], + [ + " LONDON (Reuters) - Oil prices
slid from record highs above
\\$50 a barrel Wednesday as the
U.S. government reported a
surprise increase in crude
stocks and rebels in Nigeria's
oil-rich delta region agreed
to a preliminary cease-fire." + ], + [ + "Rocky Shoes and Boots makes an
accretive acquisition -- and
gets Dickies and John Deere as
part of the deal." + ], + [ + "BONN: Deutsche Telekom is
bidding 2.9 bn for the 26 it
does not own in T-Online
International, pulling the
internet service back into the
fold four years after selling
stock to the public." + ], + [ + "Motorola Inc. says it #39;s
ready to make inroads in the
cell-phone market after
posting a third straight
strong quarter and rolling out
a series of new handsets in
time for the holiday selling
season." + ], + [ + "Costs of employer-sponsored
health plans are expected to
climb an average of 8 percent
in 2005, the first time in
five years increases have been
in single digits." + ], + [ + "After again posting record
earnings for the third
quarter, Taiwan Semiconductor
Manufacturing Company (TSMC)
expects to see its first
sequential drop in fourth-
quarter revenues, coupled with
a sharp drop in capacity
utilization rates." + ], + [ + "Many people who have never
bounced a check in their life
could soon bounce their first
check if they write checks to
pay bills a couple of days
before their paycheck is
deposited into their checking
account." + ], + [ + " LUXEMBOURG (Reuters) -
Microsoft Corp told a judge on
Thursday that the European
Commission must be stopped
from ordering it to give up
secret technology to
competitors." + ], + [ + "Dow Jones Industrial Average
futures declined amid concern
an upcoming report on
manufacturing may point to
slowing economic growth." + ], + [ + "Reuters - U.S. industrial
output advanced in\\July, as
American factories operated at
their highest capacity\\in more
than three years, a Federal
Reserve report on
Tuesday\\showed." + ], + [ + "Sir Martin Sorrell, chief
executive of WPP, yesterday
declared he was quot;very
impressed quot; with Grey
Global, stoking speculation
WPP will bid for the US
advertising company." + ], + [ + "Like introductory credit card
rates and superior customer
service, some promises just
aren #39;t built to last. And
so it is that Bank of America
- mere months after its pledge
to preserve" + ], + [ + "Reuters - Accounting firm KPMG
will pay #36;10\\million to
settle charges of improper
professional conduct\\while
acting as auditor for Gemstar-
TV Guide International Inc.\\,
the U.S. Securities and
Exchange Commission said
on\\Wednesday." + ], + [ + "Family matters made public: As
eager cousins wait for a slice
of the \\$15 billion cake that
is the Pritzker Empire,
Circuit Court Judge David
Donnersberger has ruled that
the case will be conducted in
open court." + ], + [ + "The weekly survey from
mortgage company Freddie Mac
had rates on 30-year fixed-
rate mortgages inching higher
this week, up to an average
5.82 percent from last week
#39;s 5.81 percent." + ], + [ + "The classic power struggle
between Walt Disney Co. CEO
Michael Eisner and former
feared talent agent Michael
Ovitz makes for high drama in
the courtroom - and apparently
on cable." + ], + [ + "LONDON (CBS.MW) -- Elan
(UK:ELA) (ELN) and partner
Biogen (BIIB) said the FDA has
approved new drug Tysabri to
treat relapsing forms of
multiple sclerosis." + ], + [ + "Bank of New Zealand has frozen
all accounts held in the name
of Access Brokerage, which was
yesterday placed in
liquidation after a client
fund shortfall of around \\$5
million was discovered." + ], + [ + "ZDNet #39;s survey of IT
professionals in August kept
Wired amp; Wireless on top
for the 18th month in a row.
Telecommunications equipment
maker Motorola said Tuesday
that it would cut 1,000 jobs
and take related" + ], + [ + "BRITAIN #39;S largest
financial institutions are
being urged to take lead roles
in lawsuits seeking hundreds
of millions of dollars from
the scandal-struck US
insurance industry." + ], + [ + "Bankrupt UAL Corp. (UALAQ.OB:
Quote, Profile, Research) on
Thursday reported a narrower
third-quarter net loss. The
parent of United Airlines
posted a loss of \\$274
million, or" + ], + [ + "The European Union #39;s head
office issued a bleak economic
report Tuesday, warning that
the sharp rise in oil prices
will quot;take its toll quot;
on economic growth next year
while the euro #39;s renewed
climb could threaten crucial
exports." + ], + [ + "Though Howard Stern's
defection from broadcast to
satellite radio is still 16
months off, the industry is
already trying to figure out
what will fill the crater in
ad revenue and listenership
that he is expected to leave
behind." + ], + [ + " WASHINGTON (Reuters) - The
United States set final anti-
dumping duties of up to 112.81
percent on shrimp imported
from China and up to 25.76
percent on shrimp from Vietnam
to offset unfair pricing, the
Commerce Department said on
Tuesday." + ], + [ + "NEW YORK -- When Office Depot
Inc. stores ran an electronics
recycling drive last summer
that accepted everything from
cellphones to televisions,
some stores were overwhelmed
by the amount of e-trash they
received." + ], + [ + "The International Monetary
Fund expressed concern Tuesday
about the impact of the
troubles besetting oil major
Yukos on Russia #39;s standing
as a place to invest." + ], + [ + "LinuxWorld Conference amp;
Expo will come to Boston for
the first time in February,
underscoring the area's
standing as a hub for the
open-source software being
adopted by thousands of
businesses." + ], + [ + "Interest rates on short-term
Treasury securities rose in
yesterday's auction. The
Treasury Department sold \\$19
billion in three-month bills
at a discount rate of 1.710
percent, up from 1.685 percent
last week. An additional \\$17
billion was sold in six-month
bills at a rate of 1.950
percent, up from 1.870
percent." + ], + [ + "Moody #39;s Investors Service
on Wednesday said it may cut
its bond ratings on HCA Inc.
(HCA.N: Quote, Profile,
Research) deeper into junk,
citing the hospital operator
#39;s plan to buy back about
\\$2." + ], + [ + "Telekom Austria AG, the
country #39;s biggest phone
operator, won the right to buy
Bulgaria #39;s largest mobile
phone company, MobilTel EAD,
for 1.6 billion euros (\\$2.1
billion), an acquisition that
would add 3 million
subscribers." + ], + [ + "Shares of ID Biomedical jumped
after the company reported
Monday that it signed long-
term agreements with the three
largest flu vaccine
wholesalers in the United
States in light of the
shortage of vaccine for the
current flu season." + ], + [ + "The federal government closed
its window on the oil industry
Thursday, saying that it is
selling its last 19 per cent
stake in Calgary-based Petro-
Canada." + ], + [ + " NEW YORK, Nov. 11 -- The 40
percent share price slide in
Merck #38; Co. in the five
weeks after it pulled the
painkiller Vioxx off the
market highlighted larger
problems in the pharmaceutical
industry that may depress
performance for years,
according to academics and
stock analysts who follow the
sector." + ], + [ + "Low-fare carrier Southwest
Airlines Co. said Thursday
that its third-quarter profit
rose 12.3 percent to beat Wall
Street expectations despite
higher fuel costs." + ], + [ + "Another shock hit the drug
sector Friday when
pharmaceutical giant Pfizer
Inc. announced that it found
an increased heart risk to
patients for its blockbuster
arthritis drug Celebrex." + ], + [ + "The number of US information
technology workers rose 2
percent to 10.5 million in the
first quarter of this year,
but demand for them is
dropping, according to a new
report." + ], + [ + "Insurance firm says its board
now consists of its new CEO
Michael Cherkasky and 10
outside members. NEW YORK
(Reuters) - Marsh amp;
McLennan Cos." + ], + [ + "Slot machine maker
International Game Technology
(IGT.N: Quote, Profile,
Research) on Tuesday posted
better-than-expected quarterly
earnings, as casinos bought" + ], + [ + "Venezuelan election officials
say they expect to announce
Saturday, results of a partial
audit of last Sunday #39;s
presidential recall
referendum." + ], + [ + "Sony Ericsson Mobile
Communications Ltd., the
mobile-phone venture owned by
Sony Corp. and Ericsson AB,
said third-quarter profit rose
45 percent on camera phone
demand and forecast this
quarter will be its strongest." + ], + [ + "NEW YORK, September 17
(newratings.com) - Alcatel
(ALA.NYS) has expanded its
operations and presence in the
core North American
telecommunication market with
two separate acquisitions for
about \\$277 million." + ], + [ + "Oil giant Shell swept aside
nearly 100 years of history
today when it unveiled plans
to merge its UK and Dutch
parent companies. Shell said
scrapping its twin-board
structure" + ], + [ + "Caesars Entertainment Inc. on
Thursday posted a rise in
third-quarter profit as Las
Vegas hotels filled up and
Atlantic City properties
squeaked out a profit that was
unexpected by the company." + ], + [ + "One of India #39;s leading
telecommunications providers
will use Cisco Systems #39;
gear to build its new
Ethernet-based broadband
network." + ], + [ + "Shares of Beacon Roofing
Suppler Inc. shot up as much
as 26 percent in its trading
debut Thursday, edging out
bank holding company Valley
Bancorp as the biggest gainer
among a handful of new stocks
that went public this week." + ], + [ + "The first weekend of holiday
shopping went from red-hot to
dead white, as a storm that
delivered freezing, snowy
weather across Colorado kept
consumers at home." + ], + [ + " LONDON (Reuters) - The dollar
fell within half a cent of
last week's record low against
the euro on Thursday after
capital inflows data added to
worries the United States may
struggle to fund its current
account deficit." + ], + [ + "China has pledged to invest
\\$20 billion in Argentina in
the next 10 years, La Nacion
reported Wednesday. The
announcement came during the
first day of a two-day visit" + ], + [ + "Toyota Motor Corp., the world
#39;s biggest carmaker by
value, will invest 3.8 billion
yuan (\\$461 million) with its
partner Guangzhou Automobile
Group to boost manufacturing
capacity in" + ], + [ + "BAE Systems has launched a
search for a senior American
businessman to become a non-
executive director. The high-
profile appointment is
designed to strengthen the
board at a time when the
defence giant is" + ], + [ + "Computer Associates
International is expected to
announce that its new chief
executive will be John
Swainson, an I.B.M. executive
with strong technical and
sales credentials." + ], + [ + "STOCKHOLM (Dow
Jones)--Expectations for
Telefon AB LM Ericsson #39;s
(ERICY) third-quarter
performance imply that while
sales of mobile telephony
equipment are expected to have
dipped, the company" + ], + [ + "The Anglo-Dutch oil giant
Shell today sought to draw a
line under its reserves
scandal by announcing plans to
spend \\$15bn (8.4bn) a year to
replenish reserves and develop
production in its oil and gas
business." + ], + [ + "Circulation declined at most
major US newspapers in the
last half year, the latest
blow for an industry already
rocked by a scandal involving
circulation misstatements that
has undermined the confidence
of investors and advertisers." + ], + [ + "INDIANAPOLIS - ATA Airlines
has accepted a \\$117 million
offer from Southwest Airlines
that would forge close ties
between two of the largest US
discount carriers." + ], + [ + "EVERETT Fire investigators
are still trying to determine
what caused a two-alarm that
destroyed a portion of a South
Everett shopping center this
morning." + ], + [ + "Sure, the PeopleSoft board
told shareholders to just say
no. This battle will go down
to the wire, and even
afterward Ellison could
prevail." + ], + [ + "Investors cheered by falling
oil prices and an improving
job picture sent stocks higher
Tuesday, hoping that the news
signals a renewal of economic
strength and a fall rally in
stocks." + ], + [ + " CHICAGO (Reuters) - Wal-Mart
Stores Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=WMT.N target
=/stocks/quickinfo/fullquote\"&
gt;WMT.N</A>, the
world's largest retailer, said
on Saturday it still
anticipates September U.S.
sales to be up 2 percent to 4
percent at stores open at
least a year." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei stock average opened
down 0.15 percent on
Wednesday as investors took a
breather from the market's
recent rises and sold shares
of gainers such as Sharp
Corp." + ], + [ + "NEW YORK : World oil prices
fell, capping a drop of more
than 14 percent in a two-and-
a-half-week slide triggered by
a perception of growing US
crude oil inventories." + ], + [ + "SUFFOLK -- Virginia Tech
scientists are preparing to
protect the state #39;s
largest crop from a disease
with strong potential to do
damage." + ], + [ + " NEW YORK (Reuters) -
Citigroup Inc. <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=C.N targe
t=/stocks/quickinfo/fullquote\"
>C.N</A> the world's
largest financial services
company, said on Tuesday it
will acquire First American
Bank in the quickly-growing
Texas market." + ], + [ + " SINGAPORE (Reuters) - U.S.
oil prices hovered just below
\\$50 a barrel on Tuesday,
holding recent gains on a rash
of crude supply outages and
fears over thin heating oil
tanks." + ], + [ + "US retail sales fell 0.3 in
August as rising energy costs
and bad weather persuaded
shoppers to reduce their
spending." + ], + [ + "GEORGETOWN, Del., Oct. 28 --
Plaintiffs in a shareholder
lawsuit over former Walt
Disney Co. president Michael
Ovitz's \\$140 million
severance package attempted
Thursday to portray Ovitz as a
dishonest bumbler who botched
the hiring of a major
television executive and
pushed the release of a movie
that angered the Chinese
government, damaging Disney's
business prospects in the
country." + ], + [ + "US stocks gained ground in
early trading Thursday after
tame inflation reports and
better than expected jobless
news. Oil prices held steady
as Hurricane Ivan battered the
Gulf coast, where oil
operations have halted." + ], + [ + "PepsiCo. Inc., the world #39;s
No. 2 soft- drink maker, plans
to buy General Mills Inc.
#39;s stake in their European
joint venture for \\$750
million in cash, giving it
complete ownership of Europe
#39;s largest snack-food
company." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks opened little changed
on Friday, after third-
quarter gross domestic product
data showed the U.S. economy
grew at a slower-than-expected
pace." + ], + [ + " NEW YORK (Reuters) - Limited
Brands Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=LTD.N target
=/stocks/quickinfo/fullquote\"&
gt;LTD.N</A> on
Thursday reported higher
quarterly operating profit as
cost controls and strong
lingerie sales offset poor
results at the retailer's
Express apparel stores." + ], + [ + "General Motors and
DaimlerChrysler are
collaborating on development
of fuel- saving hybrid engines
in hopes of cashing in on an
expanding market dominated by
hybrid leaders Toyota and
Honda." + ], + [ + " TOKYO (Reuters) - Nintendo
Co. Ltd. raised its 2004
shipment target for its DS
handheld video game device by
40 percent to 2.8 million
units on Thursday after many
stores in Japan and the
United States sold out in the
first week of sales." + ], + [ + "Autodesk this week unwrapped
an updated version of its
hosted project collaboration
service targeted at the
construction and manufacturing
industries. Autodesk Buzzsaw
lets multiple, dispersed
project participants --
including building owners,
developers, architects,
construction teams, and
facility managers -- share and
manage data throughout the
life of a project, according
to Autodesk officials." + ], + [ + " WASHINGTON (Reuters) - U.S.
employers hired just 96,000
workers in September, the
government said on Friday in a
weak jobs snapshot, the final
one ahead of presidential
elections that also fueled
speculation about a pause in
interest-rate rises." + ], + [ + "SAN FRANCISCO (CBS.MW) -- The
magazine known for evaluating
cars and electronics is
setting its sights on finding
the best value and quality of
prescription drugs on the
market." + ], + [ + "A mouse, a house, and your
tax-planning spouse all factor
huge in the week of earnings
that lies ahead." + ], + [ + "DALLAS (CBS.MW) -- Royal
Dutch/Shell Group will pay a
\\$120 million penalty to
settle a Securities and
Exchange Commission
investigation of its
overstatement of nearly 4.5
billion barrels of proven
reserves, the federal agency
said Tuesday." + ], + [ + "PHOENIX America West Airlines
has backed away from a
potential bidding war for
bankrupt ATA Airlines, paving
the way for AirTran to take
over ATA operations." + ], + [ + "US stock-index futures
declined. Dow Jones Industrial
Average shares including
General Electric Co. slipped
in Europe. Citigroup Inc." + ], + [ + "Federal Reserve Chairman Alan
Greenspan has done it again.
For at least the fourth time
this year, he has touched the
electrified third rail of
American politics - Social
Security." + ], + [ + "Description: Scientists say
the arthritis drug Bextra may
pose increased risk of
cardiovascular troubles.
Bextra is related to Vioxx,
which was pulled off the
market in September for the
same reason." + ], + [ + "The trial of a lawsuit by Walt
Disney Co. shareholders who
accuse the board of directors
of rubberstamping a deal to
hire Michael Ovitz" + ], + [ + "LOS ANGELES Grocery giant
Albertsons says it has
purchased Bristol Farms, which
operates eleven upscale stores
in Southern California." + ], + [ + "The figures from a survey
released today are likely to
throw more people into the
ranks of the uninsured,
analysts said." + ], + [ + "JB Oxford Holdings Inc., a
Beverly Hills-based discount
brokerage firm, was sued by
the Securities and Exchange
Commission for allegedly
allowing thousands of improper
trades in more than 600 mutual
funds." + ], + [ + "Goldman Sachs reported strong
fourth quarter and full year
earnings of \\$1.19bn, up 36
per cent on the previous
quarter. Full year profit rose
52 per cent from the previous
year to \\$4.55bn." + ], + [ + "Argosy Gaming (AGY:NYSE - news
- research) jumped in early
trading Thursday, after the
company agreed to be acquired
by Penn National Gaming
(PENN:Nasdaq - news -
research) in a \\$1." + ], + [ + "The DuPont Co. has agreed to
pay up to \\$340 million to
settle a lawsuit that it
contaminated water supplies in
West Virginia and Ohio with a
chemical used to make Teflon,
one of its best-known brands." + ], + [ + "Reuters - Jeffrey Greenberg,
chairman and chief\\executive
of embattled insurance broker
Marsh McLennan Cos.\\, is
expected to step down within
hours, a newspaper\\reported on
Friday, citing people close to
the discussions." + ], + [ + "A state regulatory board
yesterday handed a five-year
suspension to a Lawrence
funeral director accused of
unprofessional conduct and
deceptive practices, including
one case where he refused to
complete funeral arrangements
for a client because she had
purchased a lower-priced
casket elsewhere." + ], + [ + "The California Public
Utilities Commission on
Thursday upheld a \\$12.1
million fine against Cingular
Wireless, related to a two-
year investigation into the
cellular telephone company
#39;s business practices." + ], + [ + "Barclays, the British bank
that left South Africa in 1986
after apartheid protests, may
soon resume retail operations
in the nation." + ], + [ + "Italian-based Parmalat is
suing its former auditors --
Grant Thornton International
and Deloitte Touche Tohmatsu
-- for billions of dollars in
damages. Parmalat blames its
demise on the two companies
#39; mismanagement of its
finances." + ], + [ + "update An alliance of
technology workers on Tuesday
accused conglomerate Honeywell
International of planning to
move thousands of jobs to low-
cost regions over the next
five years--a charge that
Honeywell denies." + ], + [ + " NEW YORK (Reuters) - Northrop
Grumman Corp. <A HREF=\"http
://www.investor.reuters.com/Fu
llQuote.aspx?ticker=NOC.N targ
et=/stocks/quickinfo/fullquote
\">NOC.N</A> reported
higher third-quarter earnings
on Wednesday and an 11
percent increase in sales on
strength in its mission
systems, integrated systems,
ships and space technology
businesses." + ], + [ + "TORONTO (CP) - Shares of
Iamgold fell more than 10 per
cent after its proposed merger
with Gold Fields Ltd. was
thrown into doubt Monday as
South Africa #39;s Harmony
Gold Mining Company Ltd." + ], + [ + " LONDON (Reuters) - Oil prices
tumbled again on Monday to an
8-week low under \\$46 a
barrel, as growing fuel stocks
in the United States eased
fears of a winter supply
crunch." + ], + [ + "Alcoa Inc., one of the world
#39;s top producers of
aluminum, said Monday that it
received an unsolicited
quot;mini-tender quot; offer
from Toronto-based TRC Capital
Corp." + ], + [ + "The European Commission is to
warn Greece about publishing
false information about its
public finances." + ], + [ + " LONDON (Reuters) - U.S.
shares were expected to open
lower on Wednesday after
crude oil pushed to a fresh
high overnight, while Web
search engine Google Inc.
dented sentiment as it
slashed the price range on its
initial public offering." + ], + [ + "The telemarketer at the other
end of Orlando Castelblanco
#39;s line promised to reduce
the consumer #39;s credit card
debt by at least \\$2,500 and
get his 20 percent -- and
growing -- interest rates down
to single digits." + ], + [ + " NEW YORK (Reuters) - Blue-
chip stocks fell slightly on
Monday after No. 1 retailer
Wal-Mart Stores Inc. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=WMT
.N target=/stocks/quickinfo/fu
llquote\">WMT.N</A>
reported lower-than-expected
Thanksgiving sales, while
technology shares were lifted
by a rally in Apple Computer
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AAPL.O target=/sto
cks/quickinfo/fullquote\">AA
PL.O</A>." + ], + [ + "WPP Group Inc., the world
#39;s second- largest
marketing and advertising
company, said it won the
bidding for Grey Global Group
Inc." + ], + [ + " NEW YORK (Reuters) - Stocks
were slightly lower on
Tuesday, as concerns about
higher oil prices cutting into
corporate profits and
consumer demand weighed on
sentiment, while retail sales
posted a larger-than-expected
decline in August." + ], + [ + " SINGAPORE (Reuters) - Oil
prices climbed above \\$42 a
barrel on Wednesday, rising
for the third day in a row as
the heavy consuming U.S.
Northeast feels the first
chills of winter." + ], + [ + " NEW YORK (Reuters) - Merck
Co Inc. <A HREF=\"http://www
.investor.reuters.com/FullQuot
e.aspx?ticker=MRK.N target=/st
ocks/quickinfo/fullquote\">M
RK.N</A> on Thursday
pulled its arthritis drug
Vioxx off the market after a
study showed it doubled the
risk of heart attack and
stroke, a move that sent its
shares plunging and erased
\\$25 billion from its market
value." + ], + [ + "The government on Wednesday
defended its decision to
radically revise the country
#39;s deficit figures, ahead
of a European Commission
meeting to consider possible
disciplinary action against
Greece for submitting faulty
figures." + ], + [ + "OPEC ministers yesterday
agreed to increase their
ceiling for oil production to
help bring down stubbornly
high prices in a decision that
traders and analysts dismissed
as symbolic because the cartel
already is pumping more than
its new target." + ], + [ + "Williams-Sonoma Inc. said
second- quarter profit rose 55
percent, boosted by the
addition of Pottery Barn
stores and sale of outdoor
furniture." + ], + [ + " HONG KONG/SAN FRANCISCO
(Reuters) - China's largest
personal computer maker,
Lenovo Group Ltd., said on
Tuesday it was in acquisition
talks with a major technology
company, which a source
familiar with the situation
said was IBM." + ], + [ + "Reuters - Oil prices stayed
close to #36;49 a\\barrel on
Thursday, supported by a
forecast for an early
cold\\snap in the United States
that could put a strain on a
thin\\supply cushion of winter
heating fuel." + ], + [ + "WASHINGTON (CBS.MW) --
President Bush announced
Monday that Kellogg chief
executive Carlos Gutierrez
would replace Don Evans as
Commerce secretary, naming the
first of many expected changes
to his economic team." + ], + [ + "Iron Mountain moved further
into the backup and recovery
space Tuesday with the
acquisition of Connected Corp.
for \\$117 million. Connected
backs up desktop data for more
than 600 corporations, with
more than" + ], + [ + "Montgomery County (website -
news) is a big step closer to
shopping for prescription
drugs north of the border. On
a 7-2 vote, the County Council
is approving a plan that would
give county" + ], + [ + "The disclosure this week that
a Singapore-listed company
controlled by a Chinese state-
owned enterprise lost \\$550
million in derivatives
transactions" + ], + [ + "VANCOUVER - A Vancouver-based
firm won #39;t sell 1.2
million doses of influenza
vaccine to the United States
after all, announcing Tuesday
that it will sell the doses
within Canada instead." + ], + [ + "consortium led by the Sony
Corporation of America reached
a tentative agreement today to
buy Metro-Goldwyn-Mayer, the
Hollywood studio famous for
James Bond and the Pink
Panther, for" + ], + [ + "The country-cooking restaurant
chain has agreed to pay \\$8.7
million over allegations that
it segregated black customers,
subjected them to racial slurs
and gave black workers
inferior jobs." + ], + [ + " SINGAPORE (Reuters) -
Investors bought shares in
Asian exporters and
electronics firms such as
Fujitsu Ltd. on Tuesday,
buoyed by a favorable outlook
from U.S. technology
bellwethers and a slide in oil
prices." + ], + [ + "Ace Ltd. will stop paying
brokers for steering business
its way, becoming the third
company to make concessions in
the five days since New York
Attorney General Eliot Spitzer
unveiled a probe of the
insurance industry." + ], + [ + "Wm. Wrigley Jr. Co., the world
#39;s largest maker of chewing
gum, agreed to buy candy
businesses including Altoids
mints and Life Savers from
Kraft Foods Inc." + ], + [ + " #39;Reaching a preliminary
pilot agreement is the single
most important hurdle they
have to clear, but certainly
not the only one." + ], + [ + "TORONTO (CP) - Canada #39;s
big banks are increasing
mortgage rates following a
decision by the Bank of Canada
to raise its overnight rate by
one-quarter of a percentage
point to 2.25 per cent." + ], + [ + "OTTAWA (CP) - The economy
created another 43,000 jobs
last month, pushing the
unemployment rate down to 7.1
per cent from 7.2 per cent in
August, Statistics Canada said
Friday." + ], + [ + " TOKYO (Reuters) - Japan's
Nikkei average rose 0.39
percent by midsession on
Friday, bolstered by solid
gains in stocks dependent on
domestic business such as Kao
Corp. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=4452.T target=/sto
cks/quickinfo/fullquote\">44
52.T</A>." + ], + [ + " FRANKFURT (Reuters) -
DaimlerChrysler and General
Motors will jointly develop
new hybrid motors to compete
against Japanese rivals on
the fuel-saving technology
that reduces harmful
emissions, the companies said
on Monday." + ], + [ + "The bass should be in your
face. That's what Matt Kelly,
of Boston's popular punk rock
band Dropkick Murphys, thinks
is the mark of a great stereo
system. And he should know.
Kelly, 29, is the drummer for
the band that likes to think
of itself as a bit of an Irish
lucky charm for the Red Sox." + ], + [ + "Slumping corporate spending
and exports caused the economy
to slow to a crawl in the
July-September period, with
real gross domestic product
expanding just 0.1 percent
from the previous quarter,
Cabinet Office data showed
Friday." + ], + [ + "US President George W. Bush
signed into law a bill
replacing an export tax
subsidy that violated
international trade rules with
a \\$145 billion package of new
corporate tax cuts and a
buyout for tobacco farmers." + ], + [ + "The Nikkei average was up 0.37
percent in mid-morning trade
on Thursday as a recovery in
the dollar helped auto makers
among other exporters, but
trade was slow as investors
waited for important Japanese
economic data." + ], + [ + "Jennifer Canada knew she was
entering a boy's club when she
enrolled in Southern Methodist
University's Guildhall school
of video-game making." + ], + [ + "SYDNEY (Dow Jones)--Colorado
Group Ltd. (CDO.AU), an
Australian footwear and
clothing retailer, said Monday
it expects net profit for the
fiscal year ending Jan. 29 to
be over 30 higher than that of
a year earlier." + ], + [ + "NEW YORK - What are the odds
that a tiny nation like
Antigua and Barbuda could take
on the United States in an
international dispute and win?" + ], + [ + "SBC Communications expects to
cut 10,000 or more jobs by the
end of next year through
layoffs and attrition. That
#39;s about six percent of the
San Antonio-based company
#39;s work force." + ], + [ + "Another United Airlines union
is seeking to oust senior
management at the troubled
airline, saying its strategies
are reckless and incompetent." + ], + [ + "Global oil prices boomed on
Wednesday, spreading fear that
energy prices will restrain
economic activity, as traders
worried about a heating oil
supply crunch in the American
winter." + ], + [ + "Custom-designed imported
furniture was once an
exclusive realm. Now, it's the
economical alternative for
commercial developers and
designers needing everything
from seats to beds to desks
for their projects." + ], + [ + "Short-term interest rate
futures struggled on Thursday
after a government report
showing US core inflation for
August below market
expectations failed to alter
views on Federal Reserve rate
policy." + ], + [ + "Samsung is now the world #39;s
second-largest mobile phone
maker, behind Nokia. According
to market watcher Gartner, the
South Korean company has
finally knocked Motorola into
third place." + ], + [ + " TOKYO (Reuters) - Tokyo
stocks climbed to a two week
high on Friday after Tokyo
Electron Ltd. and other chip-
related stocks were boosted
by a bullish revenue outlook
from industry leader Intel
Corp." + ], + [ + " NEW YORK (Reuters) - Adobe
Systems Inc. <A HREF=\"http:
//www.investor.reuters.com/Ful
lQuote.aspx?ticker=ADBE.O targ
et=/stocks/quickinfo/fullquote
\">ADBE.O</A> on
Monday reported a sharp rise
in quarterly profit, driven by
robust demand for its
Photoshop and document-sharing
software." + ], + [ + "Dow Jones amp; Co., publisher
of The Wall Street Journal,
has agreed to buy online
financial news provider
MarketWatch Inc. for about
\\$463 million in a bid to
boost its revenue from the
fast-growing Internet
advertising market." + ], + [ + "Low-fare airline ATA has
announced plans to lay off
hundreds of employees and to
drop most of its flights out
of Midway Airport in Chicago." + ], + [ + " WASHINGTON (Reuters) -
Germany's Bayer AG <A HREF=
\"http://www.investor.reuters.c
om/FullQuote.aspx?ticker=BAYG.
DE target=/stocks/quickinfo/fu
llquote\">BAYG.DE</A>
has agreed to plead guilty
and pay a \\$4.7 million fine
for taking part in a
conspiracy to fix the prices
of synthetic rubber, the U.S.
Justice Department said on
Wednesday." + ], + [ + "The US oil giant got a good
price, Russia #39;s No. 1 oil
company acquired a savvy
partner, and Putin polished
Russia #39;s image." + ], + [ + "The maker of Hostess Twinkies,
a cake bar and a piece of
Americana children have
snacked on for almost 75
years, yesterday raised
concerns about the company
#39;s ability to stay in
business." + ], + [ + "Tenet Healthcare Corp., the
second- largest US hospital
chain, said fourth-quarter
charges may exceed \\$1 billion
and its loss from continuing
operations will widen from the
third quarter #39;s because of
increased bad debt." + ], + [ + "The London-based brokerage
Collins Stewart Tullett placed
55m of new shares yesterday to
help fund the 69.5m purchase
of the money and futures
broker Prebon." + ], + [ + " PARIS (Reuters) - European
equities flirted with 5-month
peaks as hopes that economic
growth was sustainable and a
small dip in oil prices
helped lure investors back to
recent underperformers such
as technology and insurance
stocks." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks looked to open higher
on Friday, as the fourth
quarter begins on Wall Street
with oil prices holding below
\\$50 a barrel." + ], + [ + "LONDON : World oil prices
stormed above 54 US dollars
for the first time Tuesday as
strikes in Nigeria and Norway
raised worries about possible
supply shortages during the
northern hemisphere winter." + ], + [ + "US stocks were little changed
on Thursday as an upbeat
earnings report from chip
maker National Semiconductor
Corp. (NSM) sparked some
buying, but higher oil prices
limited gains." + ] + ], + "hovertemplate": "label=Business
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Business", + "marker": { + "color": "#ab63fa", + "size": 5, + "symbol": "x" + }, + "mode": "markers", + "name": "Business", + "showlegend": true, + "type": "scattergl", + "x": [ + -23.452963, + -53.210773, + -39.491558, + -28.231606, + -19.31264, + -47.410145, + -29.877638, + -40.76841, + -5.107883, + -42.131943, + -32.52056, + -39.654697, + 12.912951, + -18.497515, + -48.55776, + -53.367012, + -27.265852, + -25.208595, + -30.356327, + -49.554066, + -29.197483, + -43.75864, + -33.627903, + -28.459074, + -33.894855, + -5.484721, + -36.616104, + -53.822376, + -44.628685, + -19.861258, + -44.997097, + -37.276833, + -19.60824, + -38.214413, + 20.328302, + -23.330465, + -35.855865, + -18.20647, + -53.65577, + -33.435104, + -1.9387143, + -32.65333, + -11.705121, + -23.656853, + -34.370987, + -33.846046, + -41.55955, + -31.4103, + -41.654114, + -43.58004, + -20.22472, + -41.80841, + -43.47724, + -28.54648, + -55.67927, + -27.870377, + -11.352515, + -46.313496, + -46.637367, + -38.786, + -18.44551, + -34.186226, + -37.5602, + -5.632542, + -45.209023, + -31.500145, + -51.390118, + -42.6501, + -6.2927465, + -46.257027, + -23.71305, + -55.86017, + -34.78124, + -35.732872, + -39.5287, + -6.6976542, + -26.256298, + -34.82042, + -45.549995, + -48.911743, + -10.257471, + 45.230713, + -16.951935, + -19.065458, + -22.693665, + -44.68482, + -29.42919, + -30.674974, + 13.034079, + -15.232134, + -7.2577205, + -27.285727, + -37.857563, + -30.674488, + -37.931133, + -47.091602, + -16.122215, + -28.833778, + -7.503495, + -42.6552, + -37.64758, + -14.742917, + -19.793585, + -53.133896, + -44.98284, + -47.514927, + -40.143944, + -29.305222, + -19.33182, + -49.376717, + -32.70729, + -34.81799, + -22.524883, + -12.82885, + -46.24378, + -29.501112, + -5.0456986, + -47.141144, + -36.80437, + -8.335074, + -11.501718, + -42.394825, + -11.57566, + -29.24355, + -53.3065, + -42.896793, + -15.898649, + 1.5646982, + -51.349506, + -27.663815, + -23.031208, + -37.19868, + -31.336668, + -6.1798644, + -50.869877, + -44.016224, + -24.092436, + -6.3253975, + -49.846096, + -37.92231, + -20.485857, + -52.251915, + -42.127716, + -45.652252, + -39.891853, + -24.499039, + -9.765382, + -42.07617, + -46.420776, + -36.030876, + -16.348925, + -28.402678, + -18.734745, + -28.94602, + -23.825924, + -22.952112, + -45.96224, + -37.039566, + -15.194927, + -28.049438, + -12.567652, + -40.062756, + -52.438923, + -24.236567, + -54.033554, + -27.801344, + -52.71393, + -13.981046, + -10.861444, + -48.87469, + -35.826897, + -31.892145, + -3.1801224, + -31.190088, + -44.238426, + -24.869604, + 34.078384, + -19.088882, + -32.190376, + -25.71062, + -22.265373, + -52.42964, + -7.045784, + -6.436385, + -21.87981, + -37.63322, + -30.145031, + -45.900715, + -54.722725, + -9.91315, + -33.41616, + -29.682985, + -21.902475, + -38.86974, + -49.362682, + -38.69257, + -29.136082, + -35.14978, + -37.452244, + 1.1558152, + -27.990711, + -7.9959974, + -23.705156, + -27.350927, + 7.0387945, + -22.092422, + -44.570576, + -49.361073, + -28.812906, + -42.660313, + -34.603184, + 7.4309235, + -35.853523, + -52.502518, + -32.07786, + -48.87661, + -45.17925, + -20.502497, + -50.661304, + -47.05539, + -11.594388, + -23.063238, + -41.108536, + -36.380856, + -22.186844, + -47.564026, + -0.203547, + -45.418346, + -32.97913, + -29.1495, + -34.859055, + -12.614871, + -33.125538, + -30.265446, + -24.302145, + -38.971054, + -6.6933794, + -37.11219, + -12.324585, + -27.018137, + -30.541101, + -23.530384, + -23.313187, + 9.910433, + -31.606474, + -46.921032, + -34.711258, + -43.354763, + -33.235416, + -45.437263, + -27.061964, + -20.385843, + -41.675705, + -3.9804885, + -48.024345, + -12.530503, + -34.855972, + -17.363302, + -0.31910038, + -20.422543, + -48.115654, + -19.632275, + -41.17226, + -33.03263, + -30.970875, + 1.7612854, + -28.959257, + -42.69843, + -18.173891, + -50.46992, + -22.095016, + -43.79571, + -48.04673, + -10.504851, + -45.725693, + -49.58257, + -20.070473, + -27.236769, + -37.01328, + -19.117485, + -39.115543, + -29.740028, + -34.482994, + -21.63138, + -4.696033, + -4.852559, + -17.729515, + -29.865084, + -46.45139, + -9.0318775, + -8.188348, + -26.325634, + -13.60535, + -34.409237, + -46.677288, + -45.882183, + -32.426746, + 2.8495433, + -2.2277532, + -18.868166, + 19.120626, + -21.790516, + -8.934254, + -26.787775, + -12.61641, + -12.760466, + -9.973649, + -5.3010283, + -46.84005, + -40.78544, + -46.046387, + -23.913462, + -10.10402, + -34.200912, + -40.844006, + -29.91782, + -19.92304, + -41.66086, + -57.68235, + -29.327738, + -27.266224, + -49.10616, + -4.6537275, + -8.695738, + -52.898724, + -40.76337, + -36.72977, + -37.2606, + -54.428818, + -9.159126, + -46.200623, + -13.831901, + -21.605253, + -45.533035, + -55.4472, + -34.02309, + -5.918376, + -42.396126, + -16.095537, + -21.678917, + -10.15981, + -4.0862207, + -54.596252, + -34.252632, + -16.827753, + -32.974155, + -36.174, + -19.309122, + -28.692097, + -10.5037985, + -44.20961, + -39.52826, + -27.136961, + -34.181965, + 10.556734, + -48.20029, + 9.239984, + -7.4418893, + -28.779457, + -35.19683, + -54.1994, + -26.758255, + -54.634964, + -43.92253, + -39.611347, + -24.87918, + -22.471472, + -36.996124, + -46.204945, + -24.678703, + -36.588448, + -12.811854, + -0.94040644, + -44.44866, + -4.227074, + -22.860474, + -10.390649, + -36.567635, + -12.951042, + -30.48289, + -38.7589, + -36.49073, + -6.355229, + -43.13706, + -19.532629, + -36.42381, + -46.434704, + -29.59413, + -27.002981, + -20.752146, + -18.822731, + -23.879477, + -49.73623, + -20.585733, + -32.563313, + -8.220228, + -50.147823, + 19.135786, + -47.881756, + -28.388693, + -17.01861, + -49.997864, + -38.691113, + -53.174366, + 0.79674417, + -44.02579, + 0.5841107, + -28.108793, + -8.663238, + -8.084352, + -39.750137, + -16.89367, + -34.483925, + -27.989662, + 12.8087, + -25.524998, + -46.526306, + -54.849133, + -40.10975, + -36.69884, + -9.612953, + -28.30938, + -35.854397, + -40.808376, + -5.0891867, + -45.114822, + -25.18432, + -41.517033, + -54.139446, + -25.219833, + -53.989174, + -34.2908, + -40.278057, + -28.372087, + -28.535112, + -16.299625, + -27.921608, + -36.991142, + 6.9097495, + -20.261177, + -4.775729, + -24.145273, + -29.613533, + -11.283554, + -48.772636, + -53.829735, + -15.053305, + -8.963649, + -9.277499, + -15.22937, + -21.86168, + -7.6405187, + -31.572515, + -52.86785, + -35.83179, + -36.710674, + -21.823997, + -1.3200667, + -40.638805, + -12.411886, + -41.362526, + -24.7866, + -40.258007, + -37.48171, + -23.877874, + -0.9994553, + -34.842422, + -51.40557, + -31.760511, + -36.74732, + -5.775708, + -32.659416, + -3.8920984, + -10.574449, + -20.651194, + -26.427645, + -14.214475, + -48.196377, + -10.817454, + -25.303522, + -23.521646, + -43.41168, + -44.33141, + -49.86946, + -25.189764, + 64.17483, + -40.866665, + 7.287593, + -48.861267, + 22.103119, + -49.37285, + -0.5269812, + -33.893284, + -27.523653, + -37.509644, + -48.43532, + -37.623196, + -55.58907, + -49.322422, + -52.688995, + -29.522964, + -30.956533, + -15.489649, + -21.36634, + -32.821945, + -30.13521, + -20.815348, + -47.19078, + -45.82224, + -36.344696, + -43.422806 + ], + "xaxis": "x", + "y": [ + 7.8286805, + 13.600263, + 18.938295, + 3.4177885, + -23.29184, + 12.92886, + 7.4292397, + 25.03953, + -3.3929446, + 24.970749, + 0.9335098, + 26.929127, + -26.440922, + 9.1828, + -14.938796, + 13.022359, + -21.471975, + -25.318462, + 9.6741905, + 12.820546, + 4.9088416, + -17.468342, + -20.446613, + 8.161042, + 5.007877, + 6.2455893, + 12.575957, + 13.993413, + 22.678474, + 14.391562, + 17.073126, + 30.481924, + -16.22469, + 20.519714, + -24.165535, + 7.7406225, + 5.442065, + 9.661537, + 7.6243353, + 4.249151, + -21.713743, + 12.867583, + -10.440891, + -8.8150835, + 12.972686, + 4.730411, + 30.71818, + 6.520791, + 21.164257, + -8.373115, + -17.390278, + 31.114712, + 30.646704, + 6.923768, + 20.141148, + 8.499566, + -10.837732, + 7.830664, + 24.00407, + 24.8098, + -24.985989, + -11.249722, + 27.957365, + 6.4599543, + 28.99686, + -18.920874, + 9.491719, + 33.583965, + -0.7140172, + 28.585188, + 1.3020672, + -5.6699047, + 18.828894, + 20.908062, + 19.798025, + -23.965172, + 8.796663, + 32.09736, + 26.441679, + 21.586815, + -22.324871, + -27.750652, + -28.93897, + -23.277813, + 16.425425, + 18.54026, + -0.26035935, + -21.057713, + -26.390093, + -21.929888, + -11.895842, + -26.575148, + 15.54324, + -29.652391, + 18.134205, + 10.717227, + -19.211506, + 16.122732, + -14.575271, + 33.287487, + 12.847039, + -13.995945, + 14.344031, + 7.419209, + 28.721743, + 21.439281, + 25.772839, + 5.9104095, + -11.143075, + 20.674139, + 5.2307787, + 28.700588, + -16.839674, + -6.9957857, + 19.155857, + 22.57425, + 4.2664466, + 17.716654, + 13.592724, + 35.79897, + -7.505608, + 25.960653, + -6.362675, + -25.562366, + 14.799318, + 24.331854, + -1.1096494, + -27.81828, + 10.189425, + -25.194714, + -19.704374, + 23.483368, + 8.0363655, + -11.6483135, + 12.337329, + 27.538383, + 26.484413, + -16.154268, + 7.6020126, + 20.629124, + 16.143784, + 7.7047353, + 3.5172246, + 23.261137, + 21.07103, + -14.940109, + -20.06512, + 20.645449, + -13.735753, + 26.788364, + -24.88111, + 16.572128, + -15.456796, + 1.9859632, + 7.721218, + -12.5096655, + 2.0307107, + 16.297318, + -21.899122, + -19.075409, + -14.551722, + 24.951237, + 15.502925, + -26.033178, + 5.509095, + 16.660208, + 9.362999, + -14.059228, + -17.933233, + -22.175861, + 26.15921, + -23.924995, + 15.090964, + -3.5818095, + 25.846468, + 23.278618, + -25.306404, + -15.10728, + 10.148522, + 7.5768538, + -23.194473, + 13.976609, + -11.945698, + -16.428364, + -28.303225, + 19.381159, + 7.8694215, + 2.0052595, + 15.873175, + -11.668809, + 7.235856, + -21.42294, + -21.904455, + 9.315685, + 20.726511, + 17.144817, + 2.2092547, + 27.541485, + 28.821224, + 34.996464, + 4.8909636, + -12.007193, + -23.275118, + 1.5220636, + -0.8274632, + -11.567605, + -7.7168093, + 3.9182017, + 22.896002, + 33.52397, + 29.210163, + -4.9220414, + 22.216219, + 16.870352, + -11.452592, + 13.475318, + 22.79436, + -17.442066, + 12.352939, + 26.730667, + -12.461162, + -19.692043, + 16.529331, + 24.65294, + -5.132745, + 19.01329, + -2.7055879, + 27.475252, + 14.475491, + 0.96339697, + 31.858027, + -16.143095, + -15.854709, + 16.484713, + -23.097334, + 18.896671, + 34.8398, + 13.576438, + -14.182415, + 8.168441, + 13.999631, + 8.972529, + 14.504229, + 24.289896, + -3.5055225, + 15.380986, + 3.7059414, + 0.16963075, + 21.913166, + 22.36791, + 16.85544, + 17.925854, + 25.043688, + -13.892162, + 8.306711, + -6.695091, + 6.3890157, + -3.9792998, + 0.70931256, + -20.58928, + 26.520325, + 21.436638, + 18.802984, + 14.603634, + 7.0568976, + 31.796051, + -26.096392, + 28.98958, + -24.192507, + 21.682661, + 16.40951, + 20.87726, + 12.556309, + -15.915366, + 23.323511, + 8.230685, + -17.60057, + 6.01643, + 14.907442, + -19.740395, + -21.907415, + 5.0985155, + 10.961509, + -21.606695, + -19.460848, + -1.0193497, + -5.6577854, + -19.941338, + 32.26841, + -20.533716, + 35.702885, + -22.034695, + -13.390235, + 4.2575808, + 27.501059, + -1.931646, + 24.216267, + -8.099275, + 2.2845645, + -17.30228, + -3.5722063, + 11.71164, + -16.890417, + -21.589676, + -14.588415, + -17.137983, + -11.65064, + -1.327813, + 21.326263, + 15.523962, + -1.839738, + 15.938968, + -5.59304, + 8.384921, + 32.041183, + 9.530565, + -23.740261, + -5.3864436, + -13.534582, + 8.064646, + -8.887762, + 8.281391, + -14.383507, + -17.226963, + 21.001068, + 23.757103, + 14.622503, + 26.616285, + -17.779476, + -16.79903, + 29.02862, + -9.61028, + -10.290435, + 26.314108, + 20.082417, + 7.00291, + -14.920918, + 31.43023, + -10.685339, + 16.79463, + -22.31949, + -14.08771, + 1.3350589, + 6.380316, + -12.914869, + 6.286185, + 18.577192, + -14.996935, + 9.808406, + 7.8065777, + 25.720367, + 27.335323, + -27.719013, + 15.222469, + 31.183216, + 16.581377, + -1.3270321, + -24.535004, + -35.037914, + 27.32407, + 2.2356973, + -19.28855, + -16.932995, + 11.079847, + 11.247658, + 23.26528, + -5.613793, + 21.349566, + 26.02513, + -20.807219, + 9.496942, + -15.057436, + -14.524883, + 30.993462, + -18.399357, + -12.452006, + -14.193346, + 7.1376367, + -11.71116, + 10.333615, + 25.406893, + -30.53585, + -0.6449607, + 13.889341, + -14.508683, + -15.281551, + 32.249325, + 12.763572, + -19.383118, + -11.329933, + -19.62514, + 17.039936, + 12.198028, + 17.744976, + 21.359537, + -21.386267, + 14.2712755, + -3.9283407, + 10.8514185, + -19.329512, + -17.759117, + 14.102587, + -17.441105, + 19.873198, + 35.126564, + 22.92706, + -5.8584995, + -25.70212, + -24.459936, + -24.834877, + 13.346765, + -17.298498, + 9.108203, + 1.7839518, + 33.032204, + -8.874142, + 19.918457, + 13.791234, + 19.88417, + 26.08512, + 35.79187, + -20.758772, + 27.588259, + 26.69215, + -18.813177, + 20.353582, + -22.663652, + 22.6146, + 14.793362, + -15.245933, + -20.736963, + -14.522557, + 23.530079, + -12.780764, + 24.898293, + -16.473738, + 1.9298484, + 20.942158, + -5.986609, + 18.014528, + -18.893454, + -26.197563, + 19.50926, + -13.34786, + 13.471852, + 18.979578, + -15.592323, + -21.118057, + -10.412807, + -13.279965, + -28.35139, + -14.49968, + 4.322983, + 14.2359915, + 16.888266, + 26.655972, + -21.796993, + -2.695157, + 20.795307, + -25.788994, + 18.527788, + -20.973848, + 21.49163, + 28.852188, + 16.580437, + -2.6463892, + 31.668863, + 10.09489, + -19.010218, + 27.96568, + -24.00849, + -16.797096, + -5.864986, + -14.093458, + -11.445573, + -23.317041, + -15.430166, + 20.119074, + -11.128392, + -22.75563, + 5.6336102, + 30.831476, + 27.198599, + 20.937487, + -15.238694, + -20.764137, + 25.318214, + -4.7924676, + 22.43602, + -29.857277, + 9.825396, + 3.6917143, + 7.166533, + 6.549803, + 27.198914, + -3.37324, + 22.221628, + -16.78678, + 20.027725, + 15.184932, + -21.060045, + 6.1585164, + -11.634907, + -17.1438, + 15.137199, + 12.653392, + -25.22734, + 20.774977, + 20.526009, + 28.81767, + 19.897066 + ], + "yaxis": "y" + } + ], + "layout": { + "height": 500, + "legend": { + "title": { + "text": "label" + }, + "tracegroupgap": 0 + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "t-SNE components of article descriptions" + }, + "width": 600, + "xaxis": { + "anchor": "y", + "domain": [ + 0, + 1 + ], + "title": { + "text": "Component 0" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0, + 1 + ], + "title": { + "text": "Component 1" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# get embeddings for all article descriptions\n", + "embeddings = [embedding_from_string(string) for string in article_descriptions]\n", + "# compress the 2048-dimensional embeddings into 2 dimensions using t-SNE\n", + "tsne_components = tsne_components_from_embeddings(embeddings)\n", + "# get the article labels for coloring the chart\n", + "labels = df[\"label\"].tolist()\n", + "\n", + "chart_from_components(\n", + " components=tsne_components,\n", + " labels=labels,\n", + " strings=article_descriptions,\n", + " width=600,\n", + " height=500,\n", + " title=\"t-SNE components of article descriptions\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see in the chart above, even the highly compressed embeddings do a good job of clustering article descriptions by category. And it's worth emphasizing: this clustering is done with no knowledge of the labels themselves!\n", + "\n", + "Also, if you look closely at the most egregious outliers, they are often due to mislabeling rather than poor embedding. For example, the majority of the blue World points in the green Sports cluster appear to be Sports stories." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, let's recolor the points by whether they are a source article, its nearest neighbors, or other." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# create labels for the recommended articles\n", + "def nearest_neighbor_labels(\n", + " list_of_indices: list[int],\n", + " k_nearest_neighbors: int = 5\n", + ") -> list[str]:\n", + " \"\"\"Return a list of labels to color the k nearest neighbors.\"\"\"\n", + " labels = [\"Other\" for _ in list_of_indices]\n", + " source_index = list_of_indices[0]\n", + " labels[source_index] = \"Source\"\n", + " for i in range(k_nearest_neighbors):\n", + " nearest_neighbor_index = list_of_indices[i + 1]\n", + " labels[nearest_neighbor_index] = f\"Nearest neighbor (top {k_nearest_neighbors})\"\n", + " return labels\n", + "\n", + "\n", + "tony_blair_labels = nearest_neighbor_labels(tony_blair_articles, k_nearest_neighbors=5)\n", + "chipset_security_labels = nearest_neighbor_labels(chipset_security_articles, k_nearest_neighbors=5\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly/" + }, + "data": [ + { + "customdata": [ + [ + "PC World - Upcoming chip set
will include built-in security
features for your PC." + ], + [ + "Newspapers in Greece reflect a
mixture of exhilaration that
the Athens Olympics proved
successful, and relief that
they passed off without any
major setback." + ], + [ + "SAN JOSE, Calif. -- Apple
Computer (Quote, Chart)
unveiled a batch of new iPods,
iTunes software and promos
designed to keep it atop the
heap of digital music players." + ], + [ + "Any product, any shape, any
size -- manufactured on your
desktop! The future is the
fabricator. By Bruce Sterling
from Wired magazine." + ], + [ + "KABUL, Sept 22 (AFP): Three US
soldiers were killed and 14
wounded in a series of fierce
clashes with suspected Taliban
fighters in south and eastern
Afghanistan this week, the US
military said Wednesday." + ], + [ + "The EU and US moved closer to
an aviation trade war after
failing to reach agreement
during talks Thursday on
subsidies to Airbus Industrie." + ], + [ + "AUSTRALIAN journalist John
Martinkus is lucky to be alive
after spending 24 hours in the
hands of Iraqi militants at
the weekend. Martinkus was in
Baghdad working for the SBS
Dateline TV current affairs
program" + ], + [ + " GAZA (Reuters) - An Israeli
helicopter fired a missile
into a town in the southern
Gaza Strip late on Wednesday,
witnesses said, hours after a
Palestinian suicide bomber
blew herself up in Jerusalem,
killing two Israeli border
policemen." + ], + [ + "The Microsoft CEO says one way
to stem growing piracy of
Windows and Office in emerging
markets is to offer low-cost
computers." + ], + [ + "RIYADH, Saudi Arabia -- Saudi
police are seeking two young
men in the killing of a Briton
in a Riyadh parking lot, the
Interior Ministry said today,
and the British ambassador
called it a terrorist attack." + ], + [ + "Loudon, NH -- As the rain
began falling late Friday
afternoon at New Hampshire
International Speedway, the
rich in the Nextel Cup garage
got richer." + ], + [ + "PalmSource surprised the
mobile vendor community today
with the announcement that it
will acquire China MobileSoft
(CMS), ostensibly to leverage
that company's expertise in
building a mobile version of
the Linux operating system." + ], + [ + "JEFFERSON CITY - An election
security expert has raised
questions about Missouri
Secretary of State Matt Blunt
#39;s plan to let soldiers at
remote duty stations or in
combat areas cast their
ballots with the help of
e-mail." + ], + [ + "A gas explosion at a coal mine
in northern China killed 33
workers in the 10th deadly
mine blast reported in three
months. The explosion occurred
yesterday at 4:20 pm at Nanlou
township" + ], + [ + "Reuters - Palestinian leader
Mahmoud Abbas called\\Israel
\"the Zionist enemy\" Tuesday,
unprecedented language for\\the
relative moderate who is
expected to succeed Yasser
Arafat." + ], + [ + "AP - Ottawa Senators right
wing Marian Hossa is switching
European teams during the NHL
lockout." + ], + [ + "A new, optional log-on service
from America Online that makes
it harder for scammers to
access a persons online
account will not be available
for Macintosh" + ], + [ + "Nasser al-Qidwa, Palestinian
representative at the United
Nations and nephew of late
leader Yasser Arafat, handed
Arafat #39;s death report to
the Palestinian National
Authority (PNA) on Saturday." + ], + [ + "CAIRO, Egypt - France's
foreign minister appealed
Monday for the release of two
French journalists abducted in
Baghdad, saying the French
respect all religions. He did
not rule out traveling to
Baghdad..." + ], + [ + "Chelsea terminated Romania
striker Adrian Mutu #39;s
contract, citing gross
misconduct after the player
failed a doping test for
cocaine and admitted taking
the drug, the English soccer
club said in a statement." + ], + [ + "United Arab Emirates President
and ruler of Abu Dhabi Sheik
Zayed bin Sultan al-Nayhan
died Tuesday, official
television reports. He was 86." + ], + [ + "PALESTINIAN leader Yasser
Arafat today issued an urgent
call for the immediate release
of two French journalists
taken hostage in Iraq." + ], + [ + "The al-Qaida terrorist network
spent less than \\$50,000 on
each of its major attacks
except for the Sept. 11, 2001,
suicide hijackings, and one of
its hallmarks is using" + ], + [ + " SAN FRANCISCO (Reuters) -
Nike Inc. <A HREF=\"http://w
ww.investor.reuters.com/FullQu
ote.aspx?ticker=NKE.N target=/
stocks/quickinfo/fullquote\">
;NKE.N</A>, the world's
largest athletic shoe company,
on Monday reported a 25
percent rise in quarterly
profit, beating analysts'
estimates, on strong demand
for high-end running and
basketball shoes in the
United States." + ], + [ + "A FATHER who scaled the walls
of a Cardiff court dressed as
superhero Robin said the
Buckingham Palace protester
posed no threat. Fathers 4
Justice activist Jim Gibson,
who earlier this year staged
an eye-catching" + ], + [ + "NEW YORK (CBS.MW) - US stocks
traded mixed Thurday as Merck
shares lost a quarter of their
value, dragging blue chips
lower, but the Nasdaq moved
higher, buoyed by gains in the
semiconductor sector." + ], + [ + "Julia Gillard has reportedly
bowed out of the race to
become shadow treasurer,
taking enormous pressure off
Opposition Leader Mark Latham." + ], + [ + "It #39;s official. Microsoft
recently stated
definitivelyand contrary to
rumorsthat there will be no
new versions of Internet
Explorer for users of older
versions of Windows." + ], + [ + "The success of Paris #39; bid
for Olympic Games 2012 would
bring an exceptional
development for France for at
least 6 years, said Jean-
Francois Lamour, French
minister for Youth and Sports
on Tuesday." + ], + [ + "AFP - Maybe it's something to
do with the fact that the
playing area is so vast that
you need a good pair of
binoculars to see the action
if it's not taking place right
in front of the stands." + ], + [ + "Egypt #39;s release of accused
Israeli spy Azzam Azzam in an
apparent swap for six Egyptian
students held on suspicion of
terrorism is expected to melt
the ice and perhaps result" + ], + [ + "But fresh antitrust suit is in
the envelope, says Novell" + ], + [ + "Chips that help a computer's
main microprocessors perform
specific types of math
problems are becoming a big
business once again.\\" + ], + [ + "GAZA CITY, Gaza Strip: Hamas
militants killed an Israeli
soldier and wounded four with
an explosion in a booby-
trapped chicken coop on
Tuesday, in what the Islamic
group said was an elaborate
scheme to lure troops to the
area with the help of a double" + ], + [ + "A rocket carrying two Russian
cosmonauts and an American
astronaut to the international
space station streaked into
orbit on Thursday, the latest
flight of a Russian space
vehicle to fill in for
grounded US shuttles." + ], + [ + "Bankrupt ATA Airlines #39;
withdrawal from Chicago #39;s
Midway International Airport
presents a juicy opportunity
for another airline to beef up
service in the Midwest" + ], + [ + "AP - The 300 men filling out
forms in the offices of an
Iranian aid group were offered
three choices: Train for
suicide attacks against U.S.
troops in Iraq, for suicide
attacks against Israelis or to
assassinate British author
Salman Rushdie." + ], + [ + "ATHENS, Greece - Gail Devers,
the most talented yet star-
crossed hurdler of her
generation, was unable to
complete even one hurdle in
100-meter event Sunday -
failing once again to win an
Olympic hurdling medal.
Devers, 37, who has three
world championships in the
hurdles but has always flopped
at the Olympics, pulled up
short and screamed as she slid
under the first hurdle..." + ], + [ + "OCTOBER 12, 2004
(COMPUTERWORLD) - Microsoft
Corp. #39;s monthly patch
release cycle may be making it
more predictable for users to
deploy software updates, but
there appears to be little
letup in the number of holes
being discovered in the
company #39;s software" + ], + [ + "Wearable tech goes mainstream
as the Gap introduces jacket
with built-in radio.\\" + ], + [ + "Madison, WI (Sports Network) -
Anthony Davis ran for 122
yards and two touchdowns to
lead No. 6 Wisconsin over
Northwestern, 24-12, to
celebrate Homecoming weekend
at Camp Randall Stadium." + ], + [ + "LaVar Arrington participated
in his first practice since
Oct. 25, when he aggravated a
knee injury that sidelined him
for 10 games." + ], + [ + " NEW YORK (Reuters) - Billie
Jean King cut her final tie
with the U.S. Fed Cup team
Tuesday when she retired as
coach." + ], + [ + "The Instinet Group, owner of
one of the largest electronic
stock trading systems, is for
sale, executives briefed on
the plan say." + ], + [ + "Funding round of \\$105 million
brings broadband Internet
telephony company's total haul
to \\$208 million." + ], + [ + "The Miami Dolphins arrived for
their final exhibition game
last night in New Orleans
short nine players." + ], + [ + "washingtonpost.com - Anthony
Casalena was 17 when he got
his first job as a programmer
for a start-up called
HyperOffice, a software
company that makes e-mail and
contact management
applications for the Web.
Hired as an intern, he became
a regular programmer after two
weeks and rewrote the main
product line." + ], + [ + "The Auburn Hills-based
Chrysler Group made a profit
of \\$269 million in the third
quarter, even though worldwide
sales and revenues declined,
contributing to a \\$1." + ], + [ + "SAN FRANCISCO (CBS.MW) -- UAL
Corp., parent of United
Airlines, said Wednesday it
will overhaul its route
structure to reduce costs and
offset rising fuel costs." + ], + [ + " NAIROBI (Reuters) - The
Sudanese government and its
southern rebel opponents have
agreed to sign a pledge in the
Kenyan capital on Friday to
formally end a brutal 21-year-
old civil war, with U.N.
Security Council ambassadors
as witnesses." + ], + [ + "AP - From LSU at No. 1 to Ohio
State at No. 10, The AP
women's basketball poll had no
changes Monday." + ], + [ + "nother stage victory for race
leader Petter Solberg, his
fifth since the start of the
rally. The Subaru driver is
not pulling away at a fast
pace from Gronholm and Loeb
but the gap is nonetheless
increasing steadily." + ], + [ + "The fossilized neck bones of a
230-million-year-old sea
creature have features
suggesting that the animal
#39;s snakelike throat could
flare open and create suction
that would pull in prey." + ], + [ + "IBM late on Tuesday announced
the biggest update of its
popular WebSphere business
software in two years, adding
features such as automatically
detecting and fixing problems." + ], + [ + "April 1980 -- Players strike
the last eight days of spring
training. Ninety-two
exhibition games are canceled.
June 1981 -- Players stage
first midseason strike in
history." + ], + [ + "AP - Former Guatemalan
President Alfonso Portillo
#151; suspected of corruption
at home #151; is living and
working part-time in the same
Mexican city he fled two
decades ago to avoid arrest on
murder charges, his close
associates told The Associated
Press on Sunday." + ], + [ + "AP - British entrepreneur
Richard Branson said Monday
that his company plans to
launch commercial space
flights over the next few
years." + ], + [ + "Annual economic growth in
China has slowed for the third
quarter in a row, falling to
9.1 per cent, third-quarter
data shows. The slowdown shows
the economy is responding to
Beijing #39;s efforts to rein
in break-neck investment and
lending." + ], + [ + "washingtonpost.com - BRUSSELS,
Aug. 26 -- The United States
will have to wait until next
year to see its fight with the
European Union over biotech
foods resolved, as the World
Trade Organization agreed to
an E.U. request to bring
scientists into the debate,
officials said Thursday." + ], + [ + "A group of Internet security
and instant messaging
providers have teamed up to
detect and thwart the growing
threat of IM and peer-to-peer
viruses and worms, they said
this week." + ], + [ + "On Sunday, the day after Ohio
State dropped to 0-3 in the
Big Ten with a 33-7 loss at
Iowa, the Columbus Dispatch
ran a single word above its
game story on the Buckeyes:
quot;Embarrassing." + ], + [ + "Insisting that Hurriyat
Conference is the real
representative of Kashmiris,
Pakistan has claimed that
India is not ready to accept
ground realities in Kashmir." + ], + [ + "THE All-India Motor Transport
Congress (AIMTC) on Saturday
called off its seven-day
strike after finalising an
agreement with the Government
on the contentious issue of
service tax and the various
demands including tax deducted
at source (TDS), scrapping" + ], + [ + "BOSTON - Curt Schilling got
his 20th win on the eve of
Boston #39;s big series with
the New York Yankees. Now he
wants much more. quot;In a
couple of weeks, hopefully, it
will get a lot better, quot;
he said after becoming" + ], + [ + "Shooed the ghosts of the
Bambino and the Iron Horse and
the Yankee Clipper and the
Mighty Mick, on his 73rd
birthday, no less, and turned
Yankee Stadium into a morgue." + ], + [ + "Gold medal-winning Marlon
Devonish says the men #39;s
4x100m Olympic relay triumph
puts British sprinting back on
the map. Devonish, Darren
Campbell, Jason Gardener and
Mark Lewis-Francis edged out
the American" + ], + [ + "AP - The euro-zone economy
grew by 0.5 percent in the
second quarter of 2004, a
touch slower than in the first
three months of the year,
according to initial estimates
released Tuesday by the
European Union." + ], + [ + "His first space flight was in
1965 when he piloted the first
manned Gemini mission. Later
he made two trips to the moon
-- orbiting during a 1969
flight and then walking on the
lunar surface during a mission
in 1972." + ], + [ + "he difficult road conditions
created a few incidents in the
first run of the Epynt stage.
Francois Duval takes his
second stage victory since the
start of the rally, nine
tenths better than Sebastien
Loeb #39;s performance in
second position." + ], + [ + "VIENNA -- After two years of
investigating Iran's atomic
program, the UN nuclear
watchdog still cannot rule out
that Tehran has a secret atom
bomb project as Washington
insists, the agency's chief
said yesterday." + ], + [ + "By RACHEL KONRAD SAN
FRANCISCO (AP) -- EBay Inc.,
which has been aggressively
expanding in Asia, plans to
increase its stake in South
Korea's largest online auction
company. The Internet
auction giant said Tuesday
night that it would purchase
nearly 3 million publicly held
shares of Internet Auction
Co..." + ], + [ + "AFP - US Secretary of State
Colin Powell wrapped up a
three-nation tour of Asia
after winning pledges from
Japan, China and South Korea
to press North Korea to resume
stalled talks on its nuclear
weapons programs." + ], + [ + "Tallahassee, FL (Sports
Network) - Florida State head
coach Bobby Bowden has
suspended senior wide receiver
Craphonso Thorpe for the
Seminoles #39; game against
fellow Atlantic Coast
Conference member Duke on
Saturday." + ], + [ + "A few years ago, casinos
across the United States were
closing their poker rooms to
make space for more popular
and lucrative slot machines." + ], + [ + "CAIRO, Egypt An Egyptian
company says one of its four
workers who had been kidnapped
in Iraq has been freed. It
says it can #39;t give the
status of the others being
held hostage but says it is
quot;doing its best to secure
quot; their release." + ], + [ + "WASHINGTON -- Consumers were
tightfisted amid soaring
gasoline costs in August and
hurricane-related disruptions
last week sent applications
for jobless benefits to their
highest level in seven months." + ], + [ + "Talking kitchens and vanities.
Musical jump ropes and potty
seats. Blusterous miniature
leaf blowers and vacuum
cleaners -- almost as loud as
the real things." + ], + [ + "Online merchants in the United
States have become better at
weeding out fraudulent credit
card orders, a new survey
indicates. But shipping
overseas remains a risky
venture. By Joanna Glasner." + ], + [ + "Former champion Lleyton Hewitt
bristled, battled and
eventually blossomed as he
took another step towards a
second US Open title with a
second-round victory over
Moroccan Hicham Arazi on
Friday." + ], + [ + "AP - China's biggest computer
maker, Lenovo Group, said
Wednesday it has acquired a
majority stake in
International Business
Machines Corp.'s personal
computer business for
#36;1.75 billion, one of the
biggest Chinese overseas
acquisitions ever." + ], + [ + "Popping a gadget into a cradle
to recharge it used to mean
downtime, but these days
chargers are doing double
duty, keeping your portable
devices playing even when
they're charging." + ], + [ + "AFP - Hosts India braced
themselves for a harrowing
chase on a wearing wicket in
the first Test after Australia
declined to enforce the
follow-on here." + ], + [ + " SAN FRANCISCO (Reuters) -
Texas Instruments Inc. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=T
XN.N target=/stocks/quickinfo/
fullquote\">TXN.N</A>,
the largest maker of chips for
cellular phones, on Monday
said record demand for its
handset and television chips
boosted quarterly profit by
26 percent, even as it
struggles with a nagging
inventory problem." + ], + [ + "LONDON ARM Holdings, a British
semiconductor designer, said
on Monday that it would buy
Artisan Components for \\$913
million to broaden its product
range." + ], + [ + "Big evolutionary insights
sometimes come in little
packages. Witness the
startling discovery, in a cave
on the eastern Indonesian
island of Flores, of the
partial skeleton of a half-
size Homo species that" + ], + [ + "Prime Minister Paul Martin of
Canada urged Haitian leaders
on Sunday to allow the
political party of the deposed
president, Jean-Bertrand
Aristide, to take part in new
elections." + ], + [ + "Hostage takers holding up to
240 people at a school in
southern Russia have refused
to talk with a top Islamic
leader and demanded to meet
with regional leaders instead,
ITAR-TASS reported on
Wednesday." + ], + [ + "As the Mets round out their
search for a new manager, the
club is giving a last-minute
nod to its past. Wally
Backman, an infielder for the
Mets from 1980-88 who played
second base on the 1986" + ], + [ + "MELBOURNE: Global shopping
mall owner Westfield Group
will team up with Australian
developer Multiplex Group to
bid 585mil (US\\$1." + ], + [ + "Three children from a care
home are missing on the
Lancashire moors after they
are separated from a group." + ], + [ + "Luke Skywalker and Darth Vader
may get all the glory, but a
new Star Wars video game
finally gives credit to the
everyday grunts who couldn
#39;t summon the Force for
help." + ], + [ + "AMSTERDAM, Aug 18 (Reuters) -
Midfielder Edgar Davids #39;s
leadership qualities and
never-say-die attitude have
earned him the captaincy of
the Netherlands under new
coach Marco van Basten." + ], + [ + "COUNTY KILKENNY, Ireland (PA)
-- Hurricane Jeanne has led to
world No. 1 Vijay Singh
pulling out of this week #39;s
WGC-American Express
Championship at Mount Juliet." + ], + [ + "Green Bay, WI (Sports Network)
- The Green Bay Packers will
be without the services of Pro
Bowl center Mike Flanagan for
the remainder of the season,
as he will undergo left knee
surgery." + ], + [ + "Diabetics should test their
blood sugar levels more
regularly to reduce the risk
of cardiovascular disease, a
study says." + ], + [ + "COLUMBUS, Ohio -- NCAA
investigators will return to
Ohio State University Monday
to take another look at the
football program after the
latest round of allegations
made by former players,
according to the Akron Beacon
Journal." + ], + [ + " LOS ANGELES (Reuters) -
Federal authorities raided
three Washington, D.C.-area
video game stores and arrested
two people for modifying
video game consoles to play
pirated video games, a video
game industry group said on
Wednesday." + ], + [ + "Manchester United gave Alex
Ferguson a 1,000th game
anniversary present by
reaching the last 16 of the
Champions League yesterday,
while four-time winners Bayern
Munich romped into the second
round with a 5-1 beating of
Maccabi Tel Aviv." + ], + [ + "Iraq's interim Prime Minister
Ayad Allawi announced that
proceedings would begin
against former Baath Party
leaders." + ], + [ + "Reuters - Delta Air Lines Inc.
, which is\\racing to cut costs
to avoid bankruptcy, on
Wednesday reported\\a wider
quarterly loss amid soaring
fuel prices and weak\\domestic
airfares." + ], + [ + "Energy utility TXU Corp. on
Monday more than quadrupled
its quarterly dividend payment
and raised its projected
earnings for 2004 and 2005
after a companywide
performance review." + ], + [ + "Northwest Airlines Corp., the
fourth- largest US airline,
and its pilots union reached
tentative agreement on a
contract that would cut pay
and benefits, saving the
company \\$265 million a year." + ], + [ + "The last time the Angels
played the Texas Rangers, they
dropped two consecutive
shutouts at home in their most
agonizing lost weekend of the
season." + ], + [ + "Microsoft Corp. MSFT.O and
cable television provider
Comcast Corp. CMCSA.O said on
Monday that they would begin
deploying set-top boxes
powered by Microsoft software
starting next week." + ], + [ + "SEATTLE - Chasing a nearly
forgotten ghost of the game,
Ichiro Suzuki broke one of
baseball #39;s oldest records
Friday night, smoking a single
up the middle for his 258th
hit of the year and breaking
George Sisler #39;s record for
the most hits in a season" + ], + [ + "Grace Park, her caddie - and
fans - were poking around in
the desert brush alongside the
18th fairway desperately
looking for her ball." + ], + [ + "Philippines mobile phone
operator Smart Communications
Inc. is in talks with
Singapore #39;s Mobile One for
a possible tie-up,
BusinessWorld reported Monday." + ], + [ + "Washington Redskins kicker
John Hall strained his right
groin during practice
Thursday, his third leg injury
of the season. Hall will be
held out of practice Friday
and is questionable for Sunday
#39;s game against the Chicago
Bears." + ], + [ + "Airline warns it may file for
bankruptcy if too many senior
pilots take early retirement
option. Delta Air LInes #39;
CEO says it faces bankruptcy
if it can #39;t slow the pace
of pilots taking early
retirement." + ], + [ + "A toxic batch of home-brewed
alcohol has killed 31 people
in several towns in central
Pakistan, police and hospital
officials say." + ], + [ + "Thornbugs communicate by
vibrating the branches they
live on. Now scientists are
discovering just what the bugs
are \"saying.\"" + ], + [ + "The Florida Gators and
Arkansas Razorbacks meet for
just the sixth time Saturday.
The Gators hold a 4-1
advantage in the previous five
meetings, including last year
#39;s 33-28 win." + ], + [ + "Kodak Versamark #39;s parent
company, Eastman Kodak Co.,
reported Tuesday it plans to
eliminate almost 900 jobs this
year in a restructuring of its
overseas operations." + ], + [ + "A top official of the US Food
and Drug Administration said
Friday that he and his
colleagues quot;categorically
reject quot; earlier
Congressional testimony that
the agency has failed to
protect the public against
dangerous drugs." + ], + [ + " BEIJING (Reuters) - North
Korea is committed to holding
six-party talks aimed at
resolving the crisis over its
nuclear weapons program, but
has not indicated when, a top
British official said on
Tuesday." + ], + [ + "AP - 1941 #151; Brooklyn
catcher Mickey Owen dropped a
third strike on Tommy Henrich
of what would have been the
last out of a Dodgers victory
against the New York Yankees.
Given the second chance, the
Yankees scored four runs for a
7-4 victory and a 3-1 lead in
the World Series, which they
ended up winning." + ], + [ + "Federal prosecutors cracked a
global cartel that had
illegally fixed prices of
memory chips in personal
computers and servers for
three years." + ], + [ + "AP - Oracle Corp. CEO Larry
Ellison reiterated his
determination to prevail in a
long-running takeover battle
with rival business software
maker PeopleSoft Inc.,
predicting the proposed deal
will create a more competitive
company with improved customer
service." + ], + [ + "Braves shortstop Rafael Furcal
was arrested on drunken
driving charges Friday, his
second D.U.I. arrest in four
years." + ], + [ + "AFP - British retailer Marks
and Spencer announced a major
management shake-up as part of
efforts to revive its
fortunes, saying trading has
become tougher ahead of the
crucial Christmas period." + ], + [ + " BAGHDAD (Reuters) - Iraq's
interim government extended
the closure of Baghdad
international airport
indefinitely on Saturday
under emergency rule imposed
ahead of this week's U.S.-led
offensive on Falluja." + ], + [ + " ATHENS (Reuters) - Natalie
Coughlin's run of bad luck
finally took a turn for the
better when she won the gold
medal in the women's 100
meters backstroke at the
Athens Olympics Monday." + ], + [ + " ATLANTA (Reuters) - Home
Depot Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=HD.N target=/
stocks/quickinfo/fullquote\">
;HD.N</A>, the top home
improvement retailer, on
Tuesday reported a 15 percent
rise in third-quarter profit,
topping estimates, aided by
installed services and sales
to contractors." + ], + [ + " LONDON (Reuters) - The dollar
fought back from one-month
lows against the euro and
Swiss franc on Wednesday as
investors viewed its sell-off
in the wake of the Federal
Reserve's verdict on interest
rates as overdone." + ], + [ + "Rivaling Bush vs. Kerry for
bitterness, doctors and trial
lawyers are squaring off this
fall in an unprecedented four-
state struggle over limiting
malpractice awards..." + ], + [ + "Boston Scientific Corp said on
Friday it has recalled an ear
implant the company acquired
as part of its purchase of
Advanced Bionics in June." + ], + [ + "AP - Pedro Feliz hit a
tiebreaking grand slam with
two outs in the eighth inning
for his fourth hit of the day,
and the Giants helped their
playoff chances with a 9-5
victory over the Los Angeles
Dodgers on Saturday." + ], + [ + "MarketWatch.com. Richemont
sees significant H1 lift,
unclear on FY (2:53 AM ET)
LONDON (CBS.MW) -- Swiss
luxury goods maker
Richemont(zz:ZZ:001273145:
news, chart, profile), which
also is a significant" + ], + [ + "Crude oil climbed more than
\\$1 in New York on the re-
election of President George
W. Bush, who has been filling
the US Strategic Petroleum
Reserve." + ], + [ + "AP - Hundreds of tribesmen
gathered Tuesday near the area
where suspected al-Qaida-
linked militants are holding
two Chinese engineers and
demanding safe passage to
their reputed leader, a former
U.S. prisoner from Guantanamo
Bay, Cuba, officials and
residents said." + ], + [ + "AP - Scientists warned Tuesday
that a long-term increase in
global temperature of 3.5
degrees could threaten Latin
American water supplies,
reduce food yields in Asia and
result in a rise in extreme
weather conditions in the
Caribbean." + ], + [ + "AP - Further proof New York's
real estate market is
inflated: The city plans to
sell space on top of lampposts
to wireless phone companies
for #36;21.6 million a year." + ], + [ + "In an alarming development,
high-precision equipment and
materials which could be used
for making nuclear bombs have
disappeared from some Iraqi
facilities, the United Nations
watchdog agency has said." + ], + [ + "Yukos #39; largest oil-
producing unit regained power
supplies from Tyumenenergo, a
Siberia-based electricity
generator, Friday after the
subsidiary pledged to pay 440
million rubles (\\$15 million)
in debt by Oct. 3." + ], + [ + "The rollout of new servers and
networking switches in stores
is part of a five-year
agreement supporting 7-Eleven
#39;s Retail Information
System." + ], + [ + "Top Hollywood studios have
launched a wave of court cases
against internet users who
illegally download film files.
The Motion Picture Association
of America, which acts as the
representative of major film" + ], + [ + "AUSTRALIANS went into a
television-buying frenzy the
run-up to the Athens Olympics,
suggesting that as a nation we
could easily have scored a
gold medal for TV purchasing." + ], + [ + "US STOCKS vacillated yesterday
as rising oil prices muted
Wall Streets excitement over
strong results from Lehman
Brothers and Sprints \\$35
billion acquisition of Nextel
Communications." + ], + [ + "The next time you are in your
bedroom with your PC plus
Webcam switched on, don #39;t
think that your privacy is all
intact. If you have a Webcam
plugged into an infected
computer, there is a
possibility that" + ], + [ + "At the head of the class,
Rosabeth Moss Kanter is an
intellectual whirlwind: loud,
expansive, in constant motion." + ], + [ + "LEVERKUSEN/ROME, Dec 7 (SW) -
Dynamo Kiev, Bayer Leverkusen,
and Real Madrid all have a
good chance of qualifying for
the Champions League Round of
16 if they can get the right
results in Group F on
Wednesday night." + ], + [ + "Ed Hinkel made a diving,
fingertip catch for a key
touchdown and No. 16 Iowa
stiffened on defense when it
needed to most to beat Iowa
State 17-10 Saturday." + ], + [ + "During last Sunday #39;s
Nextel Cup race, amid the
ongoing furor over Dale
Earnhardt Jr. #39;s salty
language, NBC ran a commercial
for a show coming on later
that night called quot;Law
amp; Order: Criminal Intent." + ], + [ + "AP - After playing in hail,
fog and chill, top-ranked
Southern California finishes
its season in familiar
comfort. The Trojans (9-0, 6-0
Pacific-10) have two games at
home #151; against Arizona on
Saturday and Notre Dame on
Nov. 27 #151; before their
rivalry game at UCLA." + ], + [ + "A US airman dies and two are
hurt as a helicopter crashes
due to technical problems in
western Afghanistan." + ], + [ + "Jacques Chirac has ruled out
any withdrawal of French
troops from Ivory Coast,
despite unrest and anti-French
attacks, which have forced the
evacuation of thousands of
Westerners." + ], + [ + "Japanese Prime Minister
Junichiro Koizumi reshuffled
his cabinet yesterday,
replacing several top
ministers in an effort to
boost his popularity,
consolidate political support
and quicken the pace of
reforms in the world #39;s
second-largest economy." + ], + [ + "The remnants of Hurricane
Jeanne rained out Monday's
game between the Mets and the
Atlanta Braves. It will be
made up Tuesday as part of a
doubleheader." + ], + [ + "AP - NASCAR is not expecting
any immediate changes to its
top-tier racing series
following the merger between
telecommunications giant
Sprint Corp. and Nextel
Communications Inc." + ], + [ + "AP - Shawn Fanning's Napster
software enabled countless
music fans to swap songs on
the Internet for free, turning
him into the recording
industry's enemy No. 1 in the
process." + ], + [ + "TBILISI (Reuters) - At least
two Georgian soldiers were
killed and five wounded in
artillery fire with
separatists in the breakaway
region of South Ossetia,
Georgian officials said on
Wednesday." + ], + [ + "Like wide-open races? You
#39;ll love the Big 12 North.
Here #39;s a quick morning
line of the Big 12 North as it
opens conference play this
weekend." + ], + [ + "Reuters - Walt Disney Co.
is\\increasing investment in
video games for hand-held
devices and\\plans to look for
acquisitions of small game
publishers and\\developers,
Disney consumer products
Chairman Andy Mooney said\\on
Monday." + ], + [ + "Taquan Dean scored 22 points,
Francisco Garcia added 19 and
No. 13 Louisville withstood a
late rally to beat Florida
74-70 Saturday." + ], + [ + "BANGKOK - A UN conference last
week banned commercial trade
in the rare Irrawaddy dolphin,
a move environmentalists said
was needed to save the
threatened species." + ], + [ + "Laksamana.Net - Two Indonesian
female migrant workers freed
by militants in Iraq are
expected to arrive home within
a day or two, the Foreign
Affairs Ministry said
Wednesday (6/10/04)." + ], + [ + "A bitter row between America
and the European Union over
alleged subsidies to rival
aircraft makers Boeing and
Airbus intensified yesterday." + ], + [ + "PC World - Updated antivirus
software for businesses adds
intrusion prevention features." + ], + [ + "NEW YORK -- This was all about
Athens, about redemption for
one and validation for the
other. Britain's Paula
Radcliffe, the fastest female
marathoner in history, failed
to finish either of her
Olympic races last summer.
South Africa's Hendrik Ramaala
was a five-ringed dropout,
too, reinforcing his
reputation as a man who could
go only half the distance." + ], + [ + "Reuters - Online media company
Yahoo Inc.\\ late on Monday
rolled out tests of redesigned
start\\pages for its popular
Yahoo.com and My.Yahoo.com
sites." + ], + [ + "Amsterdam (pts) - Dutch
electronics company Philips
http://www.philips.com has
announced today, Friday, that
it has cut its stake in Atos
Origin by more than a half." + ], + [ + "TORONTO (CP) - Two-thirds of
banks around the world have
reported an increase in the
volume of suspicious
activities that they report to
police, a new report by KPMG
suggests." + ], + [ + "The Sun may have captured
thousands or even millions of
asteroids from another
planetary system during an
encounter more than four
billion years ago, astronomers
are reporting." + ], + [ + "LONDON -- Ernie Els has set
his sights on an improved
putting display this week at
the World Golf Championships
#39; NEC Invitational in
Akron, Ohio, after the
disappointment of tying for
fourth place at the PGA
Championship last Sunday." + ], + [ + "The Atkins diet frenzy slowed
growth briefly, but the
sandwich business is booming,
with \\$105 billion in sales
last year." + ], + [ + "Luxury carmaker Jaguar said
Friday it was stopping
production at a factory in
central England, resulting in
a loss of 1,100 jobs,
following poor sales in the
key US market." + ], + [ + "A bus was hijacked today and
shots were fired at police who
surrounded it on the outskirts
of Athens. Police did not know
how many passengers were
aboard the bus." + ], + [ + "Thumb through the book - then
buy a clean copy from Amazon" + ], + [ + "AP - President Bashar Assad
shuffled his Cabinet on
Monday, just weeks after the
United States and the United
Nations challenged Syria over
its military presence in
Lebanon and the security
situation along its border
with Iraq." + ], + [ + "Fiji #39;s Vijay Singh
replaced Tiger Woods as the
world #39;s No.1 ranked golfer
today by winning the PGA
Deutsche Bank Championship." + ], + [ + "LEIPZIG, Germany : Jurgen
Klinsmann enjoyed his first
home win as German manager
with his team defeating ten-
man Cameroon 3-0 in a friendly
match." + ], + [ + "AP - Kevin Brown had a chance
to claim a place in Yankees
postseason history with his
start in Game 7 of the AL
championship series." + ], + [ + "Reuters - High-definition
television can\\show the sweat
beading on an athlete's brow,
but the cost of\\all the
necessary electronic equipment
can get a shopper's own\\pulse
racing." + ], + [ + "HOMESTEAD, Fla. -- Kurt Busch
got his first big break in
NASCAR by winning a 1999
talent audition nicknamed
quot;The Gong Show. quot; He
was selected from dozens of
unknown, young race drivers to
work for one of the sport
#39;s most famous team owners,
Jack Roush." + ], + [ + "AP - President Vladimir Putin
has signed a bill confirming
Russia's ratification of the
Kyoto Protocol, the Kremlin
said Friday, clearing the way
for the global climate pact to
come into force early next
year." + ], + [ + "John Gibson said Friday that
he decided to resign as chief
executive officer of
Halliburton Energy Services
when it became apparent he
couldn #39;t become the CEO of
the entire corporation, after
getting a taste of the No." + ], + [ + "MacCentral - Apple Computer
Inc. on Monday released an
update for Apple Remote
Desktop (ARD), the company's
software solution to assist
Mac system administrators and
computer managers with asset
management, software
distribution and help desk
support. ARD 2.1 includes
several enhancements and bug
fixes." + ], + [ + "NEW YORK (Reuters) - Outback
Steakhouse Inc. said Tuesday
it lost about 130 operating
days and up to \\$2 million in
revenue because it had to
close some restaurants in the
South due to Hurricane
Charley." + ], + [ + "State insurance commissioners
from across the country have
proposed new rules governing
insurance brokerage fees,
winning praise from an
industry group and criticism
from" + ], + [ + "AP - The authenticity of newly
unearthed memos stating that
George W. Bush failed to meet
standards of the Texas Air
National Guard during the
Vietnam War was questioned
Thursday by the son of the
late officer who reportedly
wrote the memos." + ], + [ + "Zurich, Switzerland (Sports
Network) - Former world No. 1
Venus Williams advanced on
Thursday and will now meet
Wimbledon champion Maria
Sharapova in the quarterfinals
at the \\$1." + ], + [ + "INDIA #39;S cricket chiefs
began a frenetic search today
for a broadcaster to show next
month #39;s home series
against world champion
Australia after cancelling a
controversial \\$US308 million
(\\$440 million) television
deal." + ], + [ + "Canadian Press - OAKVILLE,
Ont. (CP) - The body of a
missing autistic man was
pulled from a creek Monday,
just metres from where a key
piece of evidence was
uncovered but originally
overlooked because searchers
had the wrong information." + ], + [ + "SOFTWARE giant Oracle #39;s
stalled \\$7.7bn (4.2bn) bid to
take over competitor
PeopleSoft has received a huge
boost after a US judge threw
out an anti-trust lawsuit
filed by the Department of
Justice to block the
acquisition." + ], + [ + "The International Olympic
Committee (IOC) has urged
Beijing to ensure the city is
ready to host the 2008 Games
well in advance, an official
said on Wednesday." + ], + [ + "AFP - German Chancellor
Gerhard Schroeder arrived in
Libya for an official visit
during which he is to hold
talks with Libyan leader
Moamer Kadhafi." + ], + [ + "The fastest-swiveling space
science observatory ever built
rocketed into orbit on
Saturday to scan the universe
for celestial explosions." + ], + [ + "The government will examine
claims 100,000 Iraqi civilians
have been killed since the US-
led invasion, Jack Straw says." + ], + [ + "Virginia Tech scores 24 points
off four first-half turnovers
in a 55-6 wipeout of Maryland
on Thursday to remain alone
atop the ACC." + ], + [ + "Copernic Unleashes Desktop
Search Tool\\\\Copernic
Technologies Inc. today
announced Copernic Desktop
Search(TM) (CDS(TM)), \"The
Search Engine For Your PC
(TM).\" Copernic has used the
experience gained from over 30
million downloads of its
Windows-based Web search
software to develop CDS, a
desktop search product that
users are saying is far ..." + ], + [ + "The DVD Forum moved a step
further toward the advent of
HD DVD media and drives with
the approval of key physical
specifications at a meeting of
the organisations steering
committee last week." + ], + [ + "Eton College and Clarence
House joined forces yesterday
to deny allegations due to be
made at an employment tribunal
today by a former art teacher
that she improperly helped
Prince Harry secure an A-level
pass in art two years ago." + ], + [ + "AFP - Great Britain's chances
of qualifying for the World
Group of the Davis Cup were
evaporating rapidly after
Austria moved into a 2-1 lead
following the doubles." + ], + [ + "AP - Martina Navratilova's
long, illustrious career will
end without an Olympic medal.
The 47-year-old Navratilova
and Lisa Raymond lost 6-4,
4-6, 6-4 on Thursday night to
fifth-seeded Shinobu Asagoe
and Ai Sugiyama of Japan in
the quarterfinals, one step
shy of the medal round." + ], + [ + "Often pigeonholed as just a
seller of televisions and DVD
players, Royal Philips
Electronics said third-quarter
profit surged despite a slide
into the red by its consumer
electronics division." + ], + [ + "AP - Google, the Internet
search engine, has done
something that law enforcement
officials and their computer
tools could not: Identify a
man who died in an apparent
hit-and-run accident 11 years
ago in this small town outside
Yakima." + ], + [ + "We are all used to IE getting
a monthly new security bug
found, but Winamp? In fact,
this is not the first security
flaw found in the application." + ], + [ + "The Apache Software Foundation
and the Debian Project said
they won't support the Sender
ID e-mail authentication
standard in their products." + ], + [ + "USATODAY.com - The newly
restored THX 1138 arrives on
DVD today with Star Wars
creator George Lucas' vision
of a Brave New World-like
future." + ], + [ + "Office Depot Inc. (ODP.N:
Quote, Profile, Research) on
Tuesday warned of weaker-than-
expected profits for the rest
of the year because of
disruptions from the string of
hurricanes" + ], + [ + "THE photo-equipment maker
Kodak yesterday announced
plans to axe 600 jobs in the
UK and close a factory in
Nottinghamshire, in a move in
line with the giants global
restructuring strategy
unveiled last January." + ], + [ + "The chances of scientists
making any one of five
discoveries by 2010 have been
hugely underestimated,
according to bookmakers." + ], + [ + "Asia-Pacific leaders meet in
Australia to discuss how to
keep nuclear weapons out of
the hands of extremists." + ], + [ + " TALL AFAR, Iraq -- A three-
foot-high coil of razor wire,
21-ton armored vehicles and
American soldiers with black
M-4 assault rifles stood
between tens of thousands of
people and their homes last
week." + ], + [ + "PSV Eindhoven re-established
their five-point lead at the
top of the Dutch Eredivisie
today with a 2-0 win at
Vitesse Arnhem. Former
Sheffield Wednesday striker
Gerald Sibon put PSV ahead in
the 56th minute" + ], + [ + "China's central bank on
Thursday raised interest rates
for the first time in nearly a
decade, signaling deepening
unease with the breakneck pace
of development and an intent
to reign in a construction
boom now sowing fears of
runaway inflation." + ], + [ + "Deepening its commitment to
help corporate users create
SOAs (service-oriented
architectures) through the use
of Web services, IBM's Global
Services unit on Thursday
announced the formation of an
SOA Management Practice." + ], + [ + "TODAY AUTO RACING 3 p.m. --
NASCAR Nextel Cup Sylvania 300
qualifying at N.H.
International Speedway,
Loudon, N.H., TNT PRO BASEBALL
7 p.m. -- Red Sox at New York
Yankees, Ch. 38, WEEI (850)
(on cable systems where Ch. 38
is not available, the game
will air on NESN); Chicago
Cubs at Cincinnati, ESPN 6
p.m. -- Eastern League finals:
..." + ], + [ + "MUMBAI, SEPTEMBER 21: The
Board of Control for Cricket
in India (BCCI) today informed
the Bombay High Court that it
was cancelling the entire
tender process regarding
cricket telecast rights as
also the conditional deal with
Zee TV." + ], + [ + "CHICAGO - Delta Air Lines
(DAL) said Wednesday it plans
to eliminate between 6,000 and
6,900 jobs during the next 18
months, implement a 10 across-
the-board pay reduction and
reduce employee benefits." + ], + [ + "LAKE GEORGE, N.Y. - Even
though he's facing double hip
replacement surgery, Bill
Smith is more than happy to
struggle out the door each
morning, limp past his brand
new P.T..." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks were likely to open
flat on Wednesday, with high
oil prices and profit warnings
weighing on the market before
earnings reports start and key
jobs data is released this
week." + ], + [ + "Best known for its popular
search engine, Google is
rapidly rolling out new
products and muscling into
Microsoft's stronghold: the
computer desktop. The
competition means consumers
get more choices and better
products." + ], + [ + "Toshiba Corp. #39;s new
desktop-replacement multimedia
notebooks, introduced on
Tuesday, are further evidence
that US consumers still have
yet to embrace the mobility
offered by Intel Corp." + ], + [ + "JEJU, South Korea : Grace Park
of South Korea won an LPGA
Tour tournament, firing a
seven-under-par 65 in the
final round of the
1.35-million dollar CJ Nine
Bridges Classic." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
poured cold water on Tuesday
on recent international
efforts to restart stalled
peace talks with Syria, saying
there was \"no possibility\" of
returning to previous
discussions." + ], + [ + "Dutch smugness was slapped
hard during the past
fortnight. The rude awakening
began with the barbaric
slaying of controversial
filmmaker Theo van Gogh on
November 2. Then followed a
reciprocal cycle of some" + ], + [ + "AP - The NHL will lock out its
players Thursday, starting a
work stoppage that threatens
to keep the sport off the ice
for the entire 2004-05 season." + ], + [ + " MOSCOW (Reuters) - Russia's
Gazprom said on Tuesday it
will bid for embattled oil
firm YUKOS' main unit next
month, as the Kremlin seeks
to turn the world's biggest
gas producer into a major oil
player." + ], + [ + "pee writes quot;A passenger
on a commuter plane in
northern Norway attacked both
pilots and at least one
passenger with an axe as the
aircraft was coming in to
land." + ], + [ + "Aregular Amazon customer,
Yvette Thompson has found
shopping online to be mostly
convenient and trouble-free.
But last month, after ordering
two CDs on Amazon.com, the
Silver Spring reader
discovered on her bank
statement that she was double-
charged for the \\$26.98 order.
And there was a \\$25 charge
that was a mystery." + ], + [ + "Prime Minister Ariel Sharon
pledged Sunday to escalate a
broad Israeli offensive in
northern Gaza, saying troops
will remain until Palestinian
rocket attacks are halted.
Israeli officials said the
offensive -- in which 58
Palestinians and three
Israelis have been killed --
will help clear the way for an
Israeli withdrawal." + ], + [ + "Federal Reserve officials
raised a key short-term
interest rate Tuesday for the
fifth time this year, and
indicated they will gradually
move rates higher next year to
keep inflation under control
as the economy expands." + ], + [ + "Canadians are paying more to
borrow money for homes, cars
and other purchases today
after a quarter-point
interest-rate increase by the
Bank of Canada yesterday was
quickly matched by the
chartered banks." + ], + [ + "NEW YORK - Wall Street
professionals know to keep
their expectations in check in
September, historically the
worst month of the year for
stocks. As summertime draws to
a close, money managers are
getting back to business,
cleaning house, and often
sending the market lower in
the process..." + ], + [ + "A group linked to al Qaeda
ally Abu Musab al-Zarqawi said
it had tried to kill Iraq
#39;s environment minister on
Tuesday and warned it would
not miss next time, according
to an Internet statement." + ], + [ + "The Israeli military killed
four Palestinian militants on
Wednesday as troops in tanks
and armored vehicles pushed
into another town in the
northern Gaza Strip, extending" + ], + [ + "When Paula Radcliffe dropped
out of the Olympic marathon
miles from the finish, she
sobbed uncontrollably.
Margaret Okayo knew the
feeling." + ], + [ + "Delta Air Lines is to issue
millions of new shares without
shareholder consent as part of
moves to ensure its survival." + ], + [ + "First baseman Richie Sexson
agrees to a four-year contract
with the Seattle Mariners on
Wednesday." + ], + [ + "KIRKUK, Iraq - A suicide
attacker detonated a car bomb
Saturday outside a police
academy in the northern Iraqi
city of Kirkuk as hundreds of
trainees and civilians were
leaving for the day, killing
at least 20 people and
wounding 36, authorities said.
Separately, U.S and Iraqi
forces clashed with insurgents
in another part of northern
Iraq after launching an
operation to destroy an
alleged militant cell in the
town of Tal Afar, the U.S..." + ], + [ + "Genta (GNTA:Nasdaq - news -
research) is never boring!
Monday night, the company
announced that its phase III
Genasense study in chronic
lymphocytic leukemia (CLL) met
its primary endpoint, which
was tumor shrinkage." + ], + [ + "Finnish mobile giant Nokia has
described its new Preminet
solution, which it launched
Monday (Oct. 25), as a
quot;major worldwide
initiative." + ], + [ + "While the entire airline
industry #39;s finances are
under water, ATA Airlines will
have to hold its breath longer
than its competitors to keep
from going belly up." + ], + [ + " SAN FRANCISCO (Reuters) - At
virtually every turn, Intel
Corp. executives are heaping
praise on an emerging long-
range wireless technology
known as WiMAX, which can
blanket entire cities with
high-speed Internet access." + ], + [ + "One day after ousting its
chief executive, the nation's
largest insurance broker said
it will tell clients exactly
how much they are paying for
services and renounce back-
door payments from carriers." + ], + [ + "LONDON (CBS.MW) -- Outlining
an expectation for higher oil
prices and increasing demand,
Royal Dutch/Shell on Wednesday
said it #39;s lifting project
spending to \\$45 billion over
the next three years." + ], + [ + "Tuesday #39;s meeting could
hold clues to whether it
#39;ll be a November or
December pause in rate hikes.
By Chris Isidore, CNN/Money
senior writer." + ], + [ + "Phishing is one of the
fastest-growing forms of
personal fraud in the world.
While consumers are the most
obvious victims, the damage
spreads far wider--hurting
companies #39; finances and
reputations and potentially" + ], + [ + "Reuters - The U.S. Interior
Department on\\Friday gave
final approval to a plan by
ConocoPhillips and\\partner
Anadarko Petroleum Corp. to
develop five tracts around\\the
oil-rich Alpine field on
Alaska's North Slope." + ], + [ + "The dollar may fall against
the euro for a third week in
four on concern near-record
crude oil prices will temper
the pace of expansion in the
US economy, a survey by
Bloomberg News indicates." + ], + [ + "The battle for the British-
based Chelsfield plc hotted up
at the weekend, with reports
from London that the property
giant #39;s management was
working on its own bid to
thwart the 585 million (\\$A1." + ], + [ + "Atari has opened the initial
sign-up phase of the closed
beta for its Dungeons amp;
Dragons real-time-strategy
title, Dragonshard." + ], + [ + "AP - Many states are facing
legal challenges over possible
voting problems Nov. 2. A look
at some of the developments
Thursday:" + ], + [ + "Israeli troops withdrew from
the southern Gaza Strip town
of Khan Yunis on Tuesday
morning, following a 30-hour
operation that left 17
Palestinians dead." + ], + [ + "Notes and quotes from various
drivers following California
Speedway #39;s Pop Secret 500.
Jeff Gordon slipped to second
in points following an engine
failure while Jimmie Johnson
moved back into first." + ], + [ + "PM-designate Omar Karameh
forms a new 30-member cabinet
which includes women for the
first time." + ], + [ + "Bahrain #39;s king pardoned a
human rights activist who
convicted of inciting hatred
of the government and
sentenced to one year in
prison Sunday in a case linked
to criticism of the prime
minister." + ], + [ + "Big Blue adds features, beefs
up training efforts in China;
rival Unisys debuts new line
and pricing plan." + ], + [ + " MEMPHIS, Tenn. (Sports
Network) - The Memphis
Grizzlies signed All-Star
forward Pau Gasol to a multi-
year contract on Friday.
Terms of the deal were not
announced." + ], + [ + "Leaders from 38 Asian and
European nations are gathering
in Vietnam for a summit of the
Asia-Europe Meeting, know as
ASEM. One thousand delegates
are to discuss global trade
and regional politics during
the two-day forum." + ], + [ + "A US soldier has pleaded
guilty to murdering a wounded
16-year-old Iraqi boy. Staff
Sergeant Johnny Horne was
convicted Friday of the
unpremeditated murder" + ], + [ + "Andre Agassi brushed past
Jonas Bjorkman 6-3 6-4 at the
Stockholm Open on Thursday to
set up a quarter-final meeting
with Spanish sixth seed
Fernando Verdasco." + ], + [ + "South Korea's Hynix
Semiconductor Inc. and Swiss-
based STMicroelectronics NV
signed a joint-venture
agreement on Tuesday to
construct a memory chip
manufacturing plant in Wuxi,
about 100 kilometers west of
Shanghai, in China." + ], + [ + "SAN DIEGO --(Business Wire)--
Oct. 11, 2004 -- Breakthrough
PeopleSoft EnterpriseOne 8.11
Applications Enable
Manufacturers to Become
Demand-Driven." + ], + [ + "Reuters - Oil prices rose on
Friday as tight\\supplies of
distillate fuel, including
heating oil, ahead of\\the
northern hemisphere winter
spurred buying." + ], + [ + "Well, Intel did say -
dismissively of course - that
wasn #39;t going to try to
match AMD #39;s little dual-
core Opteron demo coup of last
week and show off a dual-core
Xeon at the Intel Developer
Forum this week and - as good
as its word - it didn #39;t." + ], + [ + "Guinea-Bissau #39;s army chief
of staff and former interim
president, General Verissimo
Correia Seabra, was killed
Wednesday during unrest by
mutinous soldiers in the
former Portuguese" + ], + [ + "31 October 2004 -- Exit polls
show that Prime Minister
Viktor Yanukovich and
challenger Viktor Yushchenko
finished on top in Ukraine
#39;s presidential election
today and will face each other
in a run-off next month." + ], + [ + "Nov. 18, 2004 - An FDA
scientist says the FDA, which
is charged with protecting
America #39;s prescription
drug supply, is incapable of
doing so." + ], + [ + "Rock singer Bono pledges to
spend the rest of his life
trying to eradicate extreme
poverty around the world." + ], + [ + "AP - Just when tourists
thought it was safe to go back
to the Princess Diana memorial
fountain, the mud has struck." + ], + [ + "The UK's inflation rate fell
in September, thanks in part
to a fall in the price of
airfares, increasing the
chance that interest rates
will be kept on hold." + ], + [ + " HONG KONG/SAN FRANCISCO
(Reuters) - IBM is selling its
PC-making business to China's
largest personal computer
company, Lenovo Group Ltd.,
for \\$1.25 billion, marking
the U.S. firm's retreat from
an industry it helped pioneer
in 1981." + ], + [ + "AP - Three times a week, The
Associated Press picks an
issue and asks President Bush
and Democratic presidential
candidate John Kerry a
question about it. Today's
question and their responses:" + ], + [ + " BOSTON (Reuters) - Boston was
tingling with anticipation on
Saturday as the Red Sox
prepared to host Game One of
the World Series against the
St. Louis Cardinals and take a
step toward ridding
themselves of a hex that has
hung over the team for eight
decades." + ], + [ + "FOR the first time since his
appointment as Newcastle
United manager, Graeme Souness
has been required to display
the strong-arm disciplinary
qualities that, to Newcastle
directors, made" + ], + [ + "In an apparent damage control
exercise, Russian President
Vladimir Putin on Saturday
said he favored veto rights
for India as new permanent
member of the UN Security
Council." + ], + [ + "Nordstrom reported a strong
second-quarter profit as it
continued to select more
relevant inventory and sell
more items at full price." + ], + [ + "WHY IT HAPPENED Tom Coughlin
won his first game as Giants
coach and immediately
announced a fine amnesty for
all Giants. Just kidding." + ], + [ + "A second-place finish in his
first tournament since getting
married was good enough to
take Tiger Woods from third to
second in the world rankings." + ], + [ + " COLORADO SPRINGS, Colorado
(Reuters) - World 400 meters
champion Jerome Young has been
given a lifetime ban from
athletics for a second doping
offense, the U.S. Anti-Doping
Agency (USADA) said Wednesday." + ], + [ + "AP - Nigeria's Senate has
ordered a subsidiary of
petroleum giant Royal/Dutch
Shell to pay a Nigerian ethnic
group #36;1.5 billion for oil
spills in their homelands, but
the legislative body can't
enforce the resolution, an
official said Wednesday." + ], + [ + "IT #39;S BEEN a heck of an
interesting two days here in
Iceland. I #39;ve seen some
interesting technology, heard
some inventive speeches and
met some people with different
ideas." + ], + [ + "The Bank of England is set to
keep interest rates on hold
following the latest meeting
of the its Monetary Policy
Committee." + ], + [ + "Australian troops in Baghdad
came under attack today for
the first time since the end
of the Iraq war when a car
bomb exploded injuring three
soldiers and damaging an
Australian armoured convoy." + ], + [ + "The Bush administration upheld
yesterday the imposition of
penalty tariffs on shrimp
imports from China and
Vietnam, handing a victory to
beleaguered US shrimp
producers." + ], + [ + "House prices rose 0.2 percent
in September compared with the
month before to stand up 17.8
percent on a year ago, the
Nationwide Building Society
says." + ], + [ + "Reuters - Two clients of
Germany's Postbank\\(DPBGn.DE)
fell for an e-mail fraud that
led them to reveal\\money
transfer codes to a bogus Web
site -- the first case of\\this
scam in German, prosecutors
said on Thursday." + ], + [ + "US spending on information
technology goods, services,
and staff will grow seven
percent in 2005 and continue
at a similar pace through
2008, according to a study
released Monday by Forrester
Research." + ], + [ + "LONDON - In two years, Arsenal
will play their home matches
in the Emirates stadium. That
is what their new stadium at
Ashburton Grove will be called
after the Premiership
champions yesterday agreed to
the" + ], + [ + "KNOXVILLE, Tenn. -- Jason
Campbell threw for 252 yards
and two touchdowns, and No. 8
Auburn proved itself as a
national title contender by
overwhelming No. 10 Tennessee,
34-10, last night." + ], + [ + "Look, Ma, no hands! The U.S.
space agency's latest
spacecraft can run an entire
mission by itself. By Amit
Asaravala." + ], + [ + "Pakistans decision to refuse
the International Atomic
Energy Agency to have direct
access to Dr AQ Khan is
correct on both legal and
political counts." + ], + [ + "MANILA, 4 December 2004 - With
floods receding, rescuers
raced to deliver food to
famished survivors in
northeastern Philippine
villages isolated by back-to-
back storms that left more
than 650 people dead and
almost 400 missing." + ], + [ + "Talks on where to build the
world #39;s first nuclear
fusion reactor ended without a
deal on Tuesday but the
European Union said Japan and
the United States no longer
firmly opposed its bid to put
the plant in France." + ], + [ + "Joining America Online,
EarthLink and Yahoo against
spamming, Microsoft Corp.
today announced the filing of
three new anti-Spam lawsuits
under the CAN-SPAM federal law
as part of its initiative in
solving the Spam problem for
Internet users worldwide." + ], + [ + "WASHINGTON -- Another
Revolution season concluded
with an overtime elimination.
Last night, the Revolution
thrice rallied from deficits
for a 3-3 tie with D.C. United
in the Eastern Conference
final, then lost in the first-
ever Major League Soccer match
decided by penalty kicks." + ], + [ + "Dwyane Wade calls himself a
quot;sidekick, quot; gladly
accepting the role Kobe Bryant
never wanted in Los Angeles.
And not only does second-year
Heat point" + ], + [ + "A nationwide inspection shows
Internet users are not as safe
online as they believe. The
inspections found most
consumers have no firewall
protection, outdated antivirus
software and dozens of spyware
programs secretly running on
their computers." + ], + [ + "World number one golfer Vijay
Singh of Fiji has captured his
eighth PGA Tour event of the
year with a win at the 84
Lumber Classic in Farmington,
Pennsylvania." + ], + [ + "The noise was deafening and
potentially unsettling in the
minutes before the start of
the men #39;s Olympic
200-meter final. The Olympic
Stadium crowd chanted
quot;Hellas!" + ], + [ + "CLEVELAND - The White House
said Vice President Dick
Cheney faces a \"master
litigator\" when he debates
Sen. John Edwards Tuesday
night, a backhanded compliment
issued as the Republican
administration defended itself
against criticism that it has
not acknowledged errors in
waging war in Iraq..." + ], + [ + "Brazilian forward Ronaldinho
scored a sensational goal for
Barcelona against Milan,
making for a last-gasp 2-1.
The 24-year-old #39;s
brilliant left-foot hit at the
Nou Camp Wednesday night sent
Barcelona atop of Group F." + ], + [ + "Nortel Networks says it will
again delay the release of its
restated financial results.
The Canadian telecom vendor
originally promised to release
the restated results in
September." + ], + [ + " CHICAGO (Reuters) - At first
glance, paying \\$13 or \\$14
for a hard-wired Internet
laptop connection in a hotel
room might seem expensive." + ], + [ + "SEOUL (Reuters) - The chairman
of South Korea #39;s ruling
Uri Party resigned on Thursday
after saying his father had
served as a military police
officer during Japan #39;s
1910-1945 colonial rule on the
peninsula." + ], + [ + "ALERE, Uganda -- Kasmiro
Bongonyinge remembers sitting
up suddenly in his bed. It was
just after sunrise on a summer
morning two years ago, and the
old man, 87 years old and
blind, knew something was
wrong." + ], + [ + "Reuters - An investigation
into U.S. insurers\\and brokers
rattled insurance industry
stocks for a second day\\on
Friday as investors, shaken
further by subpoenas
delivered\\to the top U.S. life
insurer, struggled to gauge
how deep the\\probe might
reach." + ], + [ + "Bee Staff Writer. SANTA CLARA
- Andre Carter #39;s back
injury has kept him out of the
49ers #39; lineup since Week
1. It #39;s also interfering
with him rooting on his alma
mater in person." + ], + [ + "AP - Kellen Winslow Jr. ended
his second NFL holdout Friday." + ], + [ + "JAKARTA - Official results
have confirmed former army
general Susilo Bambang
Yudhoyono as the winner of
Indonesia #39;s first direct
presidential election, while
incumbent Megawati
Sukarnoputri urged her nation
Thursday to wait for the
official announcement" + ], + [ + "HOUSTON - With only a few
seconds left in a certain
victory for Miami, Peyton
Manning threw a meaningless
6-yard touchdown pass to
Marvin Harrison to cut the
score to 24-15." + ], + [ + "Reuters - A ragged band of
children\\emerges ghost-like
from mists in Ethiopia's
highlands,\\thrusting bunches
of carrots at a car full of
foreigners." + ], + [ + "DEADLY SCORER: Manchester
United #39;s Wayne Rooney
celebrating his three goals
against Fenerbahce this week
at Old Trafford. (AP)." + ], + [ + "AP - A U.N. human rights
expert criticized the U.S.-led
coalition forces in
Afghanistan for violating
international law by allegedly
beating Afghans to death and
forcing some to remove their
clothes or wear hoods." + ], + [ + "You can feel the confidence
level, not just with Team
Canada but with all of Canada.
There #39;s every expectation,
from one end of the bench to
the other, that Martin Brodeur
is going to hold the fort." + ], + [ + "Heading into the first game of
a new season, every team has
question marks. But in 2004,
the Denver Broncos seemed to
have more than normal." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
said on Thursday Yasser
Arafat's death could be a
turning point for peacemaking
but he would pursue a
unilateral plan that would
strip Palestinians of some
land they want for a state." + ], + [ + " AL-ASAD AIRBASE, Iraq
(Reuters) - Defense Secretary
Donald Rumsfeld swept into an
airbase in Iraq's western
desert Sunday to make a
first-hand evaluation of
operations to quell a raging
Iraqi insurgency in his first
such visit in five months." + ], + [ + "More than three out of four
(76 percent) consumers are
experiencing an increase in
spoofing and phishing
incidents, and 35 percent
receive fake e-mails at least
once a week, according to a
recent national study." + ], + [ + "The Dow Jones Industrial
Average failed three times
this year to exceed its
previous high and fell to
about 10,000 each time, most
recently a week ago." + ], + [ + "AP - Deep in the Atlantic
Ocean, undersea explorers are
living a safer life thanks to
germ-fighting clothing made in
Kinston." + ], + [ + "Anaheim, Calif. - There is a
decidedly right lean to the
three-time champions of the
American League Central. In a
span of 26 days, the Minnesota
Twins lost the left side of
their infield to free agency." + ], + [ + "Computer Associates Monday
announced the general
availability of three
Unicenter performance
management products for
mainframe data management." + ], + [ + "Reuters - The European Union
approved on\\Wednesday the
first biotech seeds for
planting and sale across\\EU
territory, flying in the face
of widespread
consumer\\resistance to
genetically modified (GMO)
crops and foods." + ], + [ + "With the NFL trading deadline
set for 4 p.m. Tuesday,
Patriots coach Bill Belichick
said there didn't seem to be
much happening on the trade
front around the league." + ], + [ + "WASHINGTON - Democrat John
Kerry accused President Bush
on Monday of sending U.S.
troops to the \"wrong war in
the wrong place at the wrong
time\" and said he'd try to
bring them all home in four
years..." + ], + [ + " SINGAPORE (Reuters) - Asian
share markets staged a broad-
based retreat on Wednesday,
led by steelmakers amid
warnings of price declines,
but also enveloping technology
and financial stocks on
worries that earnings may
disappoint." + ], + [ + "p2pnet.net News:- A Microsoft
UK quot;WEIGHING THE COST OF
LINUX VS. WINDOWS? LET #39;S
REVIEW THE FACTS quot;
magazine ad has been nailed as
misleading by Britain #39;s
Advertising Standards
Authority (ASA)." + ], + [ + "More lorry drivers are
bringing supplies to Nepal's
capital in defiance of an
indefinite blockade by Maoist
rebels." + ], + [ + "NEW YORK - CNN has a new boss
for the second time in 14
months: former CBS News
executive Jonathan Klein, who
will oversee programming and
editorial direction at the
second-ranked cable news
network." + ], + [ + "Cut-price carrier Virgin Blue
said Tuesday the cost of using
Australian airports is
spiraling upward and asked the
government to review the
deregulated system of charges." + ], + [ + "The retail sector overall may
be reporting a sluggish start
to the season, but holiday
shoppers are scooping up tech
goods at a brisk pace -- and
they're scouring the Web for
bargains more than ever.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\" color=\"#666666\">&
lt;B>-washingtonpost.com<
;/B></FONT>" + ], + [ + "AP - David Beckham broke his
rib moments after scoring
England's second goal in
Saturday's 2-0 win over Wales
in a World Cup qualifying
game." + ], + [ + "Saudi Arabia, Kuwait and the
United Arab Emirates, which
account for almost half of
OPEC #39;s oil output, said
they #39;re committed to
boosting capacity to meet
soaring demand that has driven
prices to a record." + ], + [ + "The US Commerce Department
said Thursday personal income
posted its biggest increase in
three months in August. The
government agency also said
personal spending was
unchanged after rising
strongly in July." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei average opened up 0.54
percent on Monday with banks
and exporters leading the way
as a stronger finish on Wall
Street and declining oil
prices soothed worries over
the global economic outlook." + ], + [ + " BEIJING (Reuters) - Floods
and landslides have killed 76
people in southwest China in
the past four days and washed
away homes and roads, knocked
down power lines and cut off
at least one city, state
media said on Monday." + ], + [ + "Nothing changed at the top of
Serie A as all top teams won
their games to keep the
distance between one another
unaltered. Juventus came back
from behind against Lazio to
win thanks to another goal by
Ibrahimovic" + ], + [ + "The team behind the Beagle 2
mission has unveiled its
design for a successor to the
British Mars lander." + ], + [ + "Survey points to popularity in
Europe, the Middle East and
Asia of receivers that skip
the pay TV and focus on free
programming." + ], + [ + "RICHMOND, Va. Jeremy Mayfield
won his first race in over
four years, taking the
Chevrolet 400 at Richmond
International Raceway after
leader Kurt Busch ran out of
gas eight laps from the
finish." + ], + [ + "AP - Victims of the Sept. 11
attacks were mourned worldwide
Saturday, but in the Middle
East, amid sympathy for the
dead, Arabs said Washington's
support for Israel and the war
on terror launched in the
aftermath of the World Trade
Center's collapse have only
fueled anger and violence." + ], + [ + "Linux publisher Red Hat Inc.
said Tuesday that information-
technology consulting firm
Unisys Corp. will begin
offering a business version of
the company #39;s open-source
operating system on its
servers." + ], + [ + "SEATTLE - Ichiro Suzuki set
the major league record for
hits in a season with 258,
breaking George Sisler's
84-year-old mark with a pair
of singles Friday night. The
Seattle star chopped a leadoff
single in the first inning,
then made history with a
grounder up the middle in the
third..." + ], + [ + "The intruder who entered
British Queen Elizabeth II
#39;s official Scottish
residence and caused a
security scare was a reporter
from the London-based Sunday
Times newspaper, local media
reported Friday." + ], + [ + "IBM's p5-575, a specialized
server geared for high-
performance computing, has
eight 1.9GHz Power5
processors." + ], + [ + "Bruce Wasserstein, head of
Lazard LLC, is asking partners
to take a one-third pay cut as
he readies the world #39;s
largest closely held
investment bank for a share
sale, people familiar with the
situation said." + ], + [ + "Canadian Press - FREDERICTON
(CP) - A New Brunswick truck
driver arrested in Ontario
this week has been accused by
police of stealing 50,000 cans
of Moosehead beer." + ], + [ + "Reuters - British police said
on Monday they had\\charged a
man with sending hoax emails
to relatives of people\\missing
since the Asian tsunami,
saying their loved ones
had\\been confirmed dead." + ], + [ + "The Lemon Bay Manta Rays were
not going to let a hurricane
get in the way of football. On
Friday, they headed to the
practice field for the first
time in eight" + ], + [ + "Microsoft Corp. Chairman Bill
Gates has donated \\$400,000 to
a campaign in California
trying to win approval of a
measure calling for the state
to sell \\$3 billion in bonds
to fund stem-cell research." + ], + [ + "AP - Track star Marion Jones
filed a defamation lawsuit
Wednesday against the man
whose company is at the center
of a federal investigation
into illegal steroid use among
some of the nation's top
athletes." + ], + [ + "LOS ANGELES - On Sept. 1,
former secretary of
Agriculture Dan Glickman
replaced the legendary Jack
Valenti as president and CEO
of Hollywood #39;s trade
group, the Motion Picture
Association of America." + ], + [ + "England #39;s players hit out
at cricket #39;s authorities
tonight and claimed they had
been used as quot;political
pawns quot; after the Zimbabwe
government produced a
spectacular U-turn to ensure
the controversial one-day
series will go ahead." + ], + [ + "Newspaper publisher Pulitzer
Inc. said Sunday that company
officials are considering a
possible sale of the firm to
boost shareholder value." + ], + [ + "Shares of Merck amp; Co.
plunged almost 10 percent
yesterday after a media report
said that documents show the
pharmaceutical giant hid or
denied" + ], + [ + "AP - The Japanese won the
pregame home run derby. Then
the game started and the major
league All-Stars put their
bats to work. Back-to-back
home runs by Moises Alou and
Vernon Wells in the fourth
inning and by Johnny Estrada
and Brad Wilkerson in the
ninth powered the major
leaguers past the Japanese
stars 7-3 Sunday for a 3-0
lead in the eight-game series." + ], + [ + "Reuters - Wall Street was
expected to dip at\\Thursday's
opening, but shares of Texas
Instruments Inc.\\, may climb
after it gave upbeat earnings
guidance." + ], + [ + "Chinese authorities detained a
prominent, U.S.-based Buddhist
leader in connection with his
plans to reopen an ancient
temple complex in the Chinese
province of Inner Mongolia
last week and have forced
dozens of his American
followers to leave the region,
local officials said
Wednesday." + ], + [ + "The director of the National
Hurricane Center stays calm in
the midst of a storm, but
wants everyone in hurricane-
prone areas to get the message
from his media advisories:
Respect the storm's power and
make proper response plans." + ], + [ + "With Chelsea losing their
unbeaten record and Manchester
United failing yet again to
win, William Hill now make
Arsenal red-hot 2/5 favourites
to retain the title." + ], + [ + "Late in August, Boeing #39;s
top sales execs flew to
Singapore for a crucial sales
pitch. They were close to
persuading Singapore Airlines,
one of the world #39;s leading
airlines, to buy the American
company #39;s new jet, the
mid-sized 7E7." + ], + [ + "SBC Communications and
BellSouth will acquire
YellowPages.com with the goal
of building the site into a
nationwide online business
index, the companies said
Thursday." + ], + [ + "Theresa special bookcase in Al
Grohs office completely full
of game plans from his days in
the NFL. Green ones are from
the Jets." + ], + [ + "SAN FRANCISCO Several
California cities and
counties, including Los
Angeles and San Francisco, are
suing Microsoft for what could
amount to billions of dollars." + ], + [ + "Newcastle ensured their place
as top seeds in Friday #39;s
third round UEFA Cup draw
after holding Sporting Lisbon
to a 1-1 draw at St James #39;
Park." + ], + [ + "Adorned with Turkish and EU
flags, Turkey #39;s newspapers
hailed Thursday an official EU
report recommending the
country start talks to join
the bloc, while largely
ignoring the stringent
conditions attached to the
announcement." + ], + [ + "Google plans to release a
version of its desktop search
tool for computers that run
Apple Computer #39;s Mac
operating system, Google #39;s
chief executive, Eric Schmidt,
said Friday." + ], + [ + "AMD : sicurezza e prestazioni
ottimali con il nuovo
processore mobile per notebook
leggeri e sottili; Acer Inc.
preme sull #39;acceleratore
con il nuovo notebook a
marchio Ferrari." + ], + [ + "The sounds of tinkling bells
could be heard above the
bustle of the Farmers Market
on the Long Beach Promenade,
leading shoppers to a row of
bright red tin kettles dotting
a pathway Friday." + ], + [ + "CBC SPORTS ONLINE - Bode
Miller continued his
impressive 2004-05 World Cup
skiing season by winning a
night slalom race in
Sestriere, Italy on Monday." + ], + [ + "Firefox use around the world
climbed 34 percent in the last
month, according to a report
published by Web analytics
company WebSideStory Monday." + ], + [ + "If a plastic card that gives
you credit for something you
don't want isn't your idea of
a great gift, you can put it
up for sale or swap." + ], + [ + "WASHINGTON Aug. 17, 2004
Scientists are planning to
take the pulse of the planet
and more in an effort to
improve weather forecasts,
predict energy needs months in
advance, anticipate disease
outbreaks and even tell
fishermen where the catch will
be ..." + ], + [ + "Damien Rhodes scored on a
2-yard run in the second
overtime, then Syracuse's
defense stopped Pittsburgh on
fourth and 1, sending the
Orange to a 38-31 victory
yesterday in Syracuse, N.Y." + ], + [ + "AP - Anthony Harris scored 18
of his career-high 23 points
in the second half to help
Miami upset No. 19 Florida
72-65 Saturday and give first-
year coach Frank Haith his
biggest victory." + ], + [ + "LONDON Santander Central
Hispano of Spain looked
certain to clinch its bid for
the British mortgage lender
Abbey National, after HBOS,
Britain #39;s biggest home-
loan company, said Wednesday
it would not counterbid, and
after the European Commission
cleared" + ], + [ + "New communications technology
could spawn future products.
Could your purse tell you to
bring an umbrella?" + ], + [ + " WASHINGTON (Reuters) - The
Justice Department is
investigating possible
accounting fraud at Fannie
Mae, bringing greater
government scrutiny to bear on
the mortgage finance company,
already facing a parallel
inquiry by the SEC, a source
close to the matter said on
Thursday." + ], + [ + "AP - The five cities looking
to host the 2012 Summer Games
submitted bids to the
International Olympic
Committee on Monday, entering
the final stage of a long
process in hopes of landing
one of the biggest prizes in
sports." + ], + [ + "SAP has won a \\$35 million
contract to install its human
resources software for the US
Postal Service. The NetWeaver-
based system will replace the
Post Office #39;s current
25-year-old legacy application" + ], + [ + "The FIA has already cancelled
todays activities at Suzuka as
Super Typhoon Ma-On heads
towards the 5.807km circuit.
Saturday practice has been
cancelled altogether while
pre-qualifying and final
qualifying" + ], + [ + "Thailand's prime minister
visits the southern town where
scores of Muslims died in army
custody after a rally." + ], + [ + "Indian industrial group Tata
agrees to invest \\$2bn in
Bangladesh, the biggest single
deal agreed by a firm in the
south Asian country." + ], + [ + "NewsFactor - For years,
companies large and small have
been convinced that if they
want the sophisticated
functionality of enterprise-
class software like ERP and
CRM systems, they must buy
pre-packaged applications.
And, to a large extent, that
remains true." + ], + [ + "Following in the footsteps of
the RIAA, the MPAA (Motion
Picture Association of
America) announced that they
have began filing lawsuits
against people who use peer-
to-peer software to download
copyrighted movies off the
Internet." + ], + [ + " GRAND PRAIRIE, Texas
(Reuters) - Betting on horses
was banned in Texas until as
recently as 1987. Times have
changed rapidly since.
Saturday, Lone Star Park race
track hosts the \\$14 million
Breeders Cup, global racing's
end-of-season extravaganza." + ], + [ + "MacCentral - At a special
music event featuring Bono and
The Edge from rock group U2
held on Tuesday, Apple took
the wraps off the iPod Photo,
a color iPod available in 40GB
or 60GB storage capacities.
The company also introduced
the iPod U2, a special edition
of Apple's 20GB player clad in
black, equipped with a red
Click Wheel and featuring
engraved U2 band member
signatures. The iPod Photo is
available immediately, and
Apple expects the iPod U2 to
ship in mid-November." + ], + [ + "Beijing: At least 170 miners
were trapped underground after
a gas explosion on Sunday
ignited a fire in a coalmine
in north-west China #39;s
Shaanxi province, reports
said." + ], + [ + "The steel tubing company
reports sharply higher
earnings, but the stock is
falling." + ], + [ + "It might be a stay of
execution for Coach P, or it
might just be a Christmas
miracle come early. SU #39;s
upset win over BC has given
hope to the Orange playing in
a post season Bowl game." + ], + [ + "PHIL Neville insists
Manchester United don #39;t
fear anyone in the Champions
League last 16 and declared:
quot;Bring on the Italians." + ], + [ + "Playboy Enterprises, the adult
entertainment company, has
announced plans to open a
private members club in
Shanghai even though the
company #39;s flagship men
#39;s magazine is still banned
in China." + ], + [ + "Reuters - Oracle Corp is
likely to win clearance\\from
the European Commission for
its hostile #36;7.7
billion\\takeover of rival
software firm PeopleSoft Inc.,
a source close\\to the
situation said on Friday." + ], + [ + "TORONTO (CP) - Earnings
warnings from Celestica and
Coca-Cola along with a
slowdown in US industrial
production sent stock markets
lower Wednesday." + ], + [ + "IBM (Quote, Chart) said it
would spend a quarter of a
billion dollars over the next
year and a half to grow its
RFID (define) business." + ], + [ + "Eastman Kodak Co., the world
#39;s largest maker of
photographic film, said
Wednesday it expects sales of
digital products and services
to grow at an annual rate of
36 percent between 2003 and
2007, above prior growth rate
estimates of 26 percent
between 2002" + ], + [ + "SAMARRA (Iraq): With renewe d
wave of skirmishes between the
Iraqi insurgents and the US-
led coalition marines, several
people including top police
officers were put to death on
Saturday." + ], + [ + "SPACE.com - NASA released one
of the best pictures ever made
of Saturn's moon Titan as the
Cassini spacecraft begins a
close-up inspection of the
satellite today. Cassini is
making the nearest flyby ever
of the smog-shrouded moon." + ], + [ + "AFP - The Iraqi government
plans to phase out slowly
subsidies on basic products,
such as oil and electricity,
which comprise 50 percent of
public spending, equal to 15
billion dollars, the planning
minister said." + ], + [ + "ANNAPOLIS ROYAL, NS - Nova
Scotia Power officials
continued to keep the sluice
gates open at one of the
utility #39;s hydroelectric
plants Wednesday in hopes a
wayward whale would leave the
area and head for the open
waters of the Bay of Fundy." + ], + [ + "TORONTO -- Toronto Raptors
point guard Alvin Williams
will miss the rest of the
season after undergoing
surgery on his right knee
Monday." + ], + [ + "The federal agency that
insures pension plans said
that its deficit, already at
the highest in its history,
had doubled in its last fiscal
year, to \\$23.3 billion." + ], + [ + "AFP - Like most US Latinos,
members of the extended
Rodriguez family say they will
cast their votes for Democrat
John Kerry in next month's
presidential polls." + ], + [ + "A Milan judge on Tuesday opens
hearings into whether to put
on trial 32 executives and
financial institutions over
the collapse of international
food group Parmalat in one of
Europe #39;s biggest fraud
cases." + ], + [ + "AP - Tennessee's two freshmen
quarterbacks have Volunteers
fans fantasizing about the
next four years. Brent
Schaeffer and Erik Ainge
surprised many with the nearly
seamless way they rotated
throughout a 42-17 victory
over UNLV on Sunday night." + ], + [ + "In fact, Larry Ellison
compares himself to the
warlord, according to
PeopleSoft's former CEO,
defending previous remarks he
made." + ], + [ + "FALLUJAH, Iraq -- Four Iraqi
fighters huddled in a trench,
firing rocket-propelled
grenades at Lieutenant Eric
Gregory's Bradley Fighting
Vehicle and the US tanks and
Humvees that were lumbering
through tight streets between
boxlike beige houses." + ], + [ + "MADRID, Aug 18 (Reuters) -
Portugal captain Luis Figo
said on Wednesday he was
taking an indefinite break
from international football,
but would not confirm whether
his decision was final." + ], + [ + "The Bank of England on
Thursday left its benchmark
interest rate unchanged, at
4.75 percent, as policy makers
assessed whether borrowing
costs, already the highest in
the Group of Seven, are
constraining consumer demand." + ], + [ + "AP - Several thousand
Christians who packed a
cathedral compound in the
Egyptian capital hurled stones
at riot police Wednesday to
protest a woman's alleged
forced conversion to Islam. At
least 30 people were injured." + ], + [ + "A group of Saudi religious
scholars have signed an open
letter urging Iraqis to
support jihad against US-led
forces. quot;Fighting the
occupiers is a duty for all
those who are able, quot; they
said in a statement posted on
the internet at the weekend." + ], + [ + "Fashion retailers Austin Reed
and Ted Baker have reported
contrasting fortunes on the
High Street. Austin Reed
reported interim losses of 2." + ], + [ + "AP - Shaun Rogers is in the
backfield as often as some
running backs. Whether teams
dare to block Detroit's star
defensive tackle with one
player or follow the trend of
double-teaming him, he often
rips through offensive lines
with a rare combination of
size, speed, strength and
nimble footwork." + ], + [ + " NEW YORK (Reuters) - A
federal judge on Friday
approved Citigroup Inc.'s
\\$2.6 billion settlement with
WorldCom Inc. investors who
lost billions when an
accounting scandal plunged
the telecommunications company
into bankruptcy protection." + ], + [ + "The Lions and Eagles entered
Sunday #39;s game at Ford
Field in the same place --
atop their respective
divisions -- and with
identical 2-0 records." + ], + [ + "An unspecified number of
cochlear implants to help
people with severe hearing
loss are being recalled
because they may malfunction
due to ear moisture, the US
Food and Drug Administration
announced." + ], + [ + "Profits triple at McDonald's
Japan after the fast-food
chain starts selling larger
burgers." + ], + [ + "After Marcos Moreno threw four
more interceptions in last
week's 14-13 overtime loss at
N.C. A T, Bison Coach Ray
Petty will start Antoine
Hartfield against Norfolk
State on Saturday." + ], + [ + "You can empty your pockets of
change, take off your belt and
shoes and stick your keys in
the little tray. But if you've
had radiation therapy
recently, you still might set
off Homeland Security alarms." + ], + [ + "Mountaineers retrieve three
bodies believed to have been
buried for 22 years on an
Indian glacier." + ], + [ + "SEOUL, Oct 19 Asia Pulse -
LG.Philips LCD Co.
(KSE:034220), the world #39;s
second-largest maker of liquid
crystal display (LCD), said
Tuesday it has developed the
world #39;s largest organic
light emitting diode" + ], + [ + "SOUTH WILLIAMSPORT, Pa. --
Looking ahead to the US
championship game almost cost
Conejo Valley in the
semifinals of the Little
League World Series." + ], + [ + "The Cubs didn #39;t need to
fly anywhere near Florida to
be in the eye of the storm.
For a team that is going on
100 years since last winning a
championship, the only thing
they never are at a loss for
is controversy." + ], + [ + "Security experts warn of
banner ads with a bad attitude
--and a link to malicious
code. Also: Phishers, be gone." + ], + [ + "KETTERING, Ohio Oct. 12, 2004
- Cincinnati Bengals defensive
end Justin Smith pleaded not
guilty to a driving under the
influence charge." + ], + [ + "com October 15, 2004, 5:11 AM
PT. Wood paneling and chrome
made your dad #39;s station
wagon look like a million
bucks, and they might also be
just the ticket for Microsoft
#39;s fledgling" + ], + [ + "President Thabo Mbeki met with
Ivory Coast Prime Minister
Seydou Diarra for three hours
yesterday as part of talks
aimed at bringing peace to the
conflict-wracked Ivory Coast." + ], + [ + "MINNEAPOLIS -- For much of the
2004 season, Twins pitcher
Johan Santana didn #39;t just
beat opposing hitters. Often,
he overwhelmed and owned them
in impressive fashion." + ], + [ + "Britain #39;s inflation rate
fell in August further below
its 2.0 percent government-set
upper limit target with
clothing and footwear prices
actually falling, official
data showed on Tuesday." + ], + [ + " KATHMANDU (Reuters) - Nepal's
Maoist rebels have
temporarily suspended a
crippling economic blockade of
the capital from Wednesday,
saying the move was in
response to popular appeals." + ], + [ + "Reuters - An Algerian
suspected of being a leader\\of
the Madrid train bombers has
been identified as one of
seven\\people who blew
themselves up in April to
avoid arrest, Spain's\\Interior
Ministry said on Friday." + ], + [ + "KABUL: An Afghan man was found
guilty on Saturday of killing
four journalists in 2001,
including two from Reuters,
and sentenced to death." + ], + [ + "Yasser Arafat, the leader for
decades of a fight for
Palestinian independence from
Israel, has died at a military
hospital in Paris, according
to news reports." + ], + [ + " LONDON (Reuters) - European
shares shrugged off a spike in
the euro to a fresh all-time
high Wednesday, with telecoms
again leading the way higher
after interim profits at
Britain's mm02 beat
expectations." + ], + [ + "WASHINGTON - Weighed down by
high energy prices, the US
economy grew slower than the
government estimated in the
April-June quarter, as higher
oil prices limited consumer
spending and contributed to a
record trade deficit." + ], + [ + "CHICAGO United Airlines says
it will need even more labor
cuts than anticipated to get
out of bankruptcy. United told
a bankruptcy court judge in
Chicago today that it intends
to start talks with unions
next month on a new round of
cost savings." + ], + [ + " JABALYA, Gaza Strip (Reuters)
- Israel pulled most of its
forces out of the northern
Gaza Strip Saturday after a
four-day incursion it said
was staged to halt Palestinian
rocket attacks on southern
Israeli towns." + ], + [ + "Computer Associates
International yesterday
reported a 6 increase in
revenue during its second
fiscal quarter, but posted a
\\$94 million loss after paying
to settle government
investigations into the
company, it said yesterday." + ], + [ + "THE Turkish embassy in Baghdad
was investigating a television
report that two Turkish
hostages had been killed in
Iraq, but no confirmation was
available so far, a senior
Turkish diplomat said today." + ], + [ + "Reuters - Thousands of
supporters of
Ukraine's\\opposition leader,
Viktor Yushchenko, celebrated
on the streets\\in the early
hours on Monday after an exit
poll showed him\\winner of a
bitterly fought presidential
election." + ], + [ + "LONDON : The United States
faced rare criticism over
human rights from close ally
Britain, with an official
British government report
taking Washington to task over
concerns about Iraq and the
Guantanamo Bay jail." + ], + [ + "The University of California,
Berkeley, has signed an
agreement with the Samoan
government to isolate, from a
tree, the gene for a promising
anti- Aids drug and to share
any royalties from the sale of
a gene-derived drug with the
people of Samoa." + ], + [ + "PC World - Send your video
throughout your house--
wirelessly--with new gateways
and media adapters." + ], + [ + "At a charity auction in New
Jersey last weekend, baseball
memorabilia dealer Warren
Heller was approached by a man
with an unusual but topical
request." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei average jumped 2.5
percent by mid-afternoon on
Monday as semiconductor-
related stocks such as
Advantest Corp. mirrored a
rally by their U.S. peers
while banks and brokerages
extended last week's gains." + ], + [ + "INTER Milan coach Roberto
Mancini believes the club
#39;s lavish (northern) summer
signings will enable them to
mount a serious Serie A
challenge this season." + ], + [ + "LONDON - A bomb threat that
mentioned Iraq forced a New
York-bound Greek airliner to
make an emergency landing
Sunday at London's Stansted
Airport escorted by military
jets, authorities said. An
airport spokeswoman said an
Athens newspaper had received
a phone call saying there was
a bomb on board the Olympic
Airlines plane..." + ], + [ + "Links to this week's topics
from search engine forums
across the web: New MSN Search
Goes LIVE in Beta - Microsoft
To Launch New Search Engine -
Google Launches 'Google
Advertising Professionals' -
Organic vs Paid Traffic ROI? -
Making Money With AdWords? -
Link Building 101" + ], + [ + "AP - Brad Ott shot an 8-under
64 on Sunday to win the
Nationwide Tour's Price Cutter
Charity Championship for his
first Nationwide victory." + ], + [ + "AP - New York Jets running
back Curtis Martin passed Eric
Dickerson and Jerome Bettis on
the NFL career rushing list
Sunday against the St. Louis
Rams, moving to fourth all-
time." + ], + [ + "Eight conservation groups are
fighting the US government
over a plan to poison
thousands of prairie dogs in
the grasslands of South
Dakota, saying wildlife should
not take a backseat to
ranching interests." + ], + [ + "ATHENS, Greece - Sheryl
Swoopes made three big plays
at the end - two baskets and
another on defense - to help
the United States squeeze out
a 66-62 semifinal victory over
Russia on Friday. Now, only
one game stands between the
U.S..." + ], + [ + "Instead of standing for ante
meridian and post meridian,
though, fans will remember the
time periods of pre-Mia and
after-Mia. After playing for
18 years and shattering nearly
every record" + ], + [ + "General Motors (GM) plans to
announce a massive
restructuring Thursday that
will eliminate as many as
12,000 jobs in Europe in a
move to stem the five-year
flow of red ink from its auto
operations in the region." + ], + [ + "Scientists are developing a
device which could improve the
lives of kidney dialysis
patients." + ], + [ + "KABUL, Afghanistan The Afghan
government is blaming drug
smugglers for yesterday #39;s
attack on the leading vice
presidential candidate ." + ], + [ + "Stephon Marbury, concerned
about his lousy shooting in
Athens, used an off day to go
to the gym and work on his
shot. By finding his range, he
saved the United States #39;
hopes for a basketball gold
medal." + ], + [ + " LONDON (Reuters) - Oil prices
held firm on Wednesday as
Hurricane Ivan closed off
crude output and shut
refineries in the Gulf of
Mexico, while OPEC's Gulf
producers tried to reassure
traders by recommending an
output hike." + ], + [ + "State-owned, running a
monopoly on imports of jet
fuel to China #39;s fast-
growing aviation industry and
a prized member of Singapore
#39;s Stock Exchange." + ], + [ + "Google has won a trade mark
dispute, with a District Court
judge finding that the search
engines sale of sponsored
search terms Geico and Geico
Direct did not breach car
insurance firm GEICOs rights
in the trade marked terms." + ], + [ + "Wall Street bounded higher for
the second straight day
yesterday as investors reveled
in sharply falling oil prices
and the probusiness agenda of
the second Bush
administration. The Dow Jones
industrials gained more than
177 points for its best day of
2004, while the Standard amp;
Poor's 500 closed at its
highest level since early
2002." + ], + [ + "Key factors help determine if
outsourcing benefits or hurts
Americans." + ], + [ + "The US Trade Representative on
Monday rejected the European
Union #39;s assertion that its
ban on beef from hormone-
treated cattle is now
justified by science and that
US and Canadian retaliatory
sanctions should be lifted." + ], + [ + "One of the leading figures in
the Greek Orthodox Church, the
Patriarch of Alexandria Peter
VII, has been killed in a
helicopter crash in the Aegean
Sea." + ], + [ + "Siding with chip makers,
Microsoft said it won't charge
double for its per-processor
licenses when dual-core chips
come to market next year." + ], + [ + "NEW YORK -- Wall Street's
fourth-quarter rally gave
stock mutual funds a solid
performance for 2004, with
small-cap equity funds and
real estate funds scoring some
of the biggest returns. Large-
cap growth equities and
technology-focused funds had
the slimmest gains." + ], + [ + "CANBERRA, Australia -- The
sweat-stained felt hats worn
by Australian cowboys, as much
a part of the Outback as
kangaroos and sun-baked soil,
may be heading for the history
books. They fail modern
industrial safety standards." + ], + [ + "Big Food Group Plc, the UK
owner of the Iceland grocery
chain, said second-quarter
sales at stores open at least
a year dropped 3.3 percent,
the second consecutive
decline, after competitors cut
prices." + ], + [ + "A London-to-Washington flight
is diverted after a security
alert involving the singer
formerly known as Cat Stevens." + ], + [ + " WASHINGTON (Reuters) - The
first case of soybean rust has
been found on the mainland
United States and could affect
U.S. crops for the near
future, costing farmers
millions of dollars, the
Agriculture Department said on
Wednesday." + ], + [ + "IBM and the Spanish government
have introduced a new
supercomputer they hope will
be the most powerful in
Europe, and one of the 10 most
powerful in the world." + ], + [ + "The Supreme Court today
overturned a five-figure
damage award to an Alexandria
man for a local auto dealer
#39;s alleged loan scam,
ruling that a Richmond-based
federal appeals court had
wrongly" + ], + [ + "TheSpaceShipOne team is handed
the \\$10m cheque and trophy it
won for claiming the Ansari
X-Prize." + ], + [ + "Security officials have
identified six of the
militants who seized a school
in southern Russia as being
from Chechnya, drawing a
strong connection to the
Chechen insurgents who have
been fighting Russian forces
for years." + ], + [ + "AP - Randy Moss is expected to
play a meaningful role for the
Minnesota Vikings this weekend
against the Giants, even
without a fully healed right
hamstring." + ], + [ + "Pros: Fits the recent profile
(44, past PGA champion, fiery
Ryder Cup player); the job is
his if he wants it. Cons:
Might be too young to be
willing to burn two years of
play on tour." + ], + [ + "SEOUL -- North Korea set three
conditions yesterday to be met
before it would consider
returning to six-party talks
on its nuclear programs." + ], + [ + "Official figures show the
12-nation eurozone economy
continues to grow, but there
are warnings it may slow down
later in the year." + ], + [ + "Elmer Santos scored in the
second half, lifting East
Boston to a 1-0 win over
Brighton yesterday afternoon
and giving the Jets an early
leg up in what is shaping up
to be a tight Boston City
League race." + ], + [ + "In upholding a lower court
#39;s ruling, the Supreme
Court rejected arguments that
the Do Not Call list violates
telemarketers #39; First
Amendment rights." + ], + [ + "US-backed Iraqi commandos were
poised Friday to storm rebel
strongholds in the northern
city of Mosul, as US military
commanders said they had
quot;broken the back quot; of
the insurgency with their
assault on the former rebel
bastion of Fallujah." + ], + [ + "Infineon Technologies, the
second-largest chip maker in
Europe, said Wednesday that it
planned to invest about \\$1
billion in a new factory in
Malaysia to expand its
automotive chip business and
be closer to customers in the
region." + ], + [ + "Mozilla's new web browser is
smart, fast and user-friendly
while offering a slew of
advanced, customizable
functions. By Michelle Delio." + ], + [ + "Saints special teams captain
Steve Gleason expects to be
fined by the league after
being ejected from Sunday's
game against the Carolina
Panthers for throwing a punch." + ], + [ + "JERUSALEM (Reuters) - Prime
Minister Ariel Sharon, facing
a party mutiny over his plan
to quit the Gaza Strip, has
approved 1,000 more Israeli
settler homes in the West Bank
in a move that drew a cautious
response on Tuesday from ..." + ], + [ + "Play has begun in the
Australian Masters at
Huntingdale in Melbourne with
around half the field of 120
players completing their first
rounds." + ], + [ + " NEW YORK (Reuters) -
Washington Post Co. <A HREF
=\"http://www.investor.reuters.
com/FullQuote.aspx?ticker=WPO.
N target=/stocks/quickinfo/ful
lquote\">WPO.N</A>
said on Friday that quarterly
profit jumped, beating
analysts' forecasts, boosted
by results at its Kaplan
education unit and television
broadcasting operations." + ], + [ + "GHAZNI, Afghanistan, 6 October
2004 - Wartime security was
rolled out for Afghanistans
interim President Hamid Karzai
as he addressed his first
election campaign rally
outside the capital yesterday
amid spiraling violence." + ], + [ + "LOUISVILLE, Ky. - Louisville
men #39;s basketball head
coach Rick Pitino and senior
forward Ellis Myles met with
members of the media on Friday
to preview the Cardinals #39;
home game against rival
Kentucky on Satursday." + ], + [ + "AP - Sounds like David
Letterman is as big a \"Pops\"
fan as most everyone else." + ], + [ + "originally offered on notebook
PCs -- to its Opteron 32- and
64-bit x86 processors for
server applications. The
technology will help servers
to run" + ], + [ + "New orders for US-made durable
goods increased 0.2pc in
September, held back by a big
drop in orders for
transportation goods, the US
Commerce Department said
today." + ], + [ + "Siblings are the first ever to
be convicted for sending
boatloads of junk e-mail
pushing bogus products. Also:
Microsoft takes MSN music
download on a Euro trip....
Nokia begins legal battle
against European
counterparts.... and more." + ], + [ + "I always get a kick out of the
annual list published by
Forbes singling out the
richest people in the country.
It #39;s almost as amusing as
those on the list bickering
over their placement." + ], + [ + "MacCentral - After Apple
unveiled the iMac G5 in Paris
this week, Vice President of
Hardware Product Marketing
Greg Joswiak gave Macworld
editors a guided tour of the
desktop's new design. Among
the topics of conversation:
the iMac's cooling system, why
pre-installed Bluetooth
functionality and FireWire 800
were left out, and how this
new model fits in with Apple's
objectives." + ], + [ + "Williams-Sonoma Inc., operator
of home furnishing chains
including Pottery Barn, said
third-quarter earnings rose 19
percent, boosted by store
openings and catalog sales." + ], + [ + "We #39;ve known about
quot;strained silicon quot;
for a while--but now there
#39;s a better way to do it.
Straining silicon improves
chip performance." + ], + [ + "This week, Sir Richard Branson
announced his new company,
Virgin Galactic, has the
rights to the first commercial
flights into space." + ], + [ + "71-inch HDTV comes with a home
stereo system and components
painted in 24-karat gold." + ], + [ + "Arsenal was held to a 1-1 tie
by struggling West Bromwich
Albion on Saturday, failing to
pick up a Premier League
victory when Rob Earnshaw
scored with 11 minutes left." + ], + [ + "TOKYO - Mitsubishi Heavy
Industries said today it #39;s
in talks to buy a plot of land
in central Japan #39;s Nagoya
city from Mitsubishi Motors
for building aircraft parts." + ], + [ + "China has confirmed that it
found a deadly strain of bird
flu in pigs as early as two
years ago. China #39;s
Agriculture Ministry said two
cases had been discovered, but
it did not say exactly where
the samples had been taken." + ], + [ + "Baseball #39;s executive vice
president Sandy Alderson
insisted last month that the
Cubs, disciplined for an
assortment of run-ins with
umpires, would not be targeted
the rest of the season by
umpires who might hold a
grudge." + ], + [ + "As Superman and Batman would
no doubt reflect during their
cigarette breaks, the really
draining thing about being a
hero was that you have to keep
riding to the rescue." + ], + [ + "MacCentral - RealNetworks Inc.
said on Tuesday that it has
sold more than a million songs
at its online music store
since slashing prices last
week as part of a limited-time
sale aimed at growing the user
base of its new digital media
software." + ], + [ + "With the presidential election
less than six weeks away,
activists and security experts
are ratcheting up concern over
the use of touch-screen
machines to cast votes.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-washingtonpost.com&l
t;/B></FONT>" + ], + [ + "NEW YORK, September 14 (New
Ratings) - Yahoo! Inc
(YHOO.NAS) has agreed to
acquire Musicmatch Inc, a
privately held digital music
software company, for about
\\$160 million in cash." + ], + [ + "Japan #39;s Sumitomo Mitsui
Financial Group Inc. said
Tuesday it proposed to UFJ
Holdings Inc. that the two
banks merge on an equal basis
in its latest attempt to woo
UFJ away from a rival suitor." + ], + [ + "Oil futures prices were little
changed Thursday as traders
anxiously watched for
indications that the supply or
demand picture would change in
some way to add pressure to
the market or take some away." + ], + [ + "Gov. Rod Blagojevich plans to
propose a ban Thursday on the
sale of violent and sexually
explicit video games to
minors, something other states
have tried with little
success." + ], + [ + " CHICAGO (Reuters) - Delta Air
Lines Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=DAL.N target=
/stocks/quickinfo/fullquote\"&g
t;DAL.N</A> said on
Tuesday it will cut wages by
10 percent and its chief
executive will go unpaid for
the rest of the year, but it
still warned of bankruptcy
within weeks unless more cuts
are made." + ], + [ + "AP - Ten years after the Irish
Republican Army's momentous
cease-fire, negotiations
resumed Wednesday in hope of
reviving a Catholic-Protestant
administration, an elusive
goal of Northern Ireland's
hard-fought peace process." + ], + [ + "A cable channel plans to
resurrect each of the 1,230
regular-season games listed on
the league's defunct 2004-2005
schedule by setting them in
motion on a video game
console." + ], + [ + " SANTO DOMINGO, Dominican
Republic, Sept. 18 -- Tropical
Storm Jeanne headed for the
Bahamas on Saturday after an
assault on the Dominican
Republic that killed 10
people, destroyed hundreds of
houses and forced thousands
from their homes." + ], + [ + "An explosion tore apart a car
in Gaza City Monday, killing
at least one person,
Palestinian witnesses said.
They said Israeli warplanes
were circling overhead at the
time of the blast, indicating
a possible missile strike." + ], + [ + " WASHINGTON (Reuters) - A
former Fannie Mae <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=FNM.N
target=/stocks/quickinfo/fullq
uote\">FNM.N</A>
employee who gave U.S.
officials information about
what he saw as accounting
irregularities will not
testify as planned before a
congressional hearing next
week, a House committee said
on Friday." + ], + [ + "Beijing, Oct. 25 (PTI): China
and the US today agreed to
work jointly to re-energise
the six-party talks mechanism
aimed at dismantling North
Korea #39;s nuclear programmes
while Washington urged Beijing
to resume" + ], + [ + "AFP - Sporadic gunfire and
shelling took place overnight
in the disputed Georgian
region of South Ossetia in
violation of a fragile
ceasefire, wounding seven
Georgian servicemen." + ], + [ + "PARIS, Nov 4 (AFP) - The
European Aeronautic Defence
and Space Company reported
Thursday that its nine-month
net profit more than doubled,
thanks largely to sales of
Airbus aircraft, and raised
its full-year forecast." + ], + [ + "AP - Eric Hinske and Vernon
Wells homered, and the Toronto
Blue Jays completed a three-
game sweep of the Baltimore
Orioles with an 8-5 victory
Sunday." + ], + [ + "SiliconValley.com - When
\"Halo\" became a smash video
game hit following Microsoft's
launch of the Xbox console in
2001, it was a no-brainer that
there would be a sequel to the
science fiction shoot-em-up." + ], + [ + "The number of people claiming
unemployment benefit last
month fell by 6,100 to
830,200, according to the
Office for National
Statistics." + ], + [ + " NEW YORK (Reuters) - Todd
Walker homered, had three hits
and drove in four runs to lead
the Chicago Cubs to a 12-5 win
over the Cincinnati Reds in
National League play at
Wrigley Field on Monday." + ], + [ + "PARIS -- The city of Paris
intends to reduce its
dependence on software
suppliers with \"de facto
monopolies,\" but considers an
immediate switch of its 17,000
desktops to open source
software too costly, it said
Wednesday." + ], + [ + " FALLUJA, Iraq (Reuters) -
U.S. forces hit Iraq's rebel
stronghold of Falluja with the
fiercest air and ground
bombardment in months, as
insurgents struck back on
Saturday with attacks that
killed up to 37 people in
Samarra." + ], + [ + "MIAMI (Sports Network) -
Shaquille O #39;Neal made his
home debut, but once again it
was Dwyane Wade stealing the
show with 28 points as the
Miami Heat downed the
Cleveland Cavaliers, 92-86, in
front of a record crowd at
AmericanAirlines Arena." + ], + [ + "AP - The San Diego Chargers
looked sharp #151; and played
the same way. Wearing their
powder-blue throwback jerseys
and white helmets from the
1960s, the Chargers did almost
everything right in beating
the Jacksonville Jaguars 34-21
on Sunday." + ], + [ + "The vast majority of consumers
are unaware that an Apple iPod
digital music player only
plays proprietary iTunes
files, while a smaller
majority agree that it is
within RealNetworks #39;
rights to develop a program
that will make its music files
compatible" + ], + [ + "Tyler airlines are gearing up
for the beginning of holiday
travel, as officials offer
tips to help travelers secure
tickets and pass through
checkpoints with ease." + ], + [ + " NAJAF, Iraq (Reuters) - The
fate of a radical Shi'ite
rebellion in the holy city of
Najaf was uncertain Friday
amid disputed reports that
Iraqi police had gained
control of the Imam Ali
Mosque." + ], + [ + " PROVIDENCE, R.I. (Reuters) -
You change the oil in your car
every 5,000 miles or so. You
clean your house every week or
two. Your PC needs regular
maintenance as well --
especially if you're using
Windows and you spend a lot of
time on the Internet." + ], + [ + "NERVES - no problem. That
#39;s the verdict of Jose
Mourinho today after his
Chelsea side gave a resolute
display of character at
Highbury." + ], + [ + "AP - The latest low point in
Ron Zook's tenure at Florida
even has the coach wondering
what went wrong. Meanwhile,
Sylvester Croom's first big
win at Mississippi State has
given the Bulldogs and their
fans a reason to believe in
their first-year leader.
Jerious Norwood's 37-yard
touchdown run with 32 seconds
remaining lifted the Bulldogs
to a 38-31 upset of the 20th-
ranked Gators on Saturday." + ], + [ + "A criminal trial scheduled to
start Monday involving former
Enron Corp. executives may
shine a rare and potentially
harsh spotlight on the inner
workings" + ], + [ + "The Motley Fool - Here's
something you don't see every
day -- the continuing brouhaha
between Oracle (Nasdaq: ORCL -
News) and PeopleSoft (Nasdaq:
PSFT - News) being a notable
exception. South Africa's
Harmony Gold Mining Company
(NYSE: HMY - News) has
announced a hostile takeover
bid to acquire fellow South
African miner Gold Fields
Limited (NYSE: GFI - News).
The transaction, if it takes
place, would be an all-stock
acquisition, with Harmony
issuing 1.275 new shares in
payment for each share of Gold
Fields. The deal would value
Gold Fields at more than
#36;8 billion. ..." + ], + [ + "Someone forgot to inform the
US Olympic basketball team
that it was sent to Athens to
try to win a gold medal, not
to embarrass its country." + ], + [ + "SPACE.com - NASA's Mars \\rover
Opportunity nbsp;will back its
\\way out of a nbsp;crater it
has spent four months
exploring after reaching
terrain nbsp;that appears \\too
treacherous to tread. nbsp;" + ], + [ + "Sony Corp. announced Tuesday a
new 20 gigabyte digital music
player with MP3 support that
will be available in Great
Britain and Japan before
Christmas and elsewhere in
Europe in early 2005." + ], + [ + "Wal-Mart Stores Inc. #39;s
Asda, the UK #39;s second
biggest supermarket chain,
surpassed Marks amp; Spencer
Group Plc as Britain #39;s
largest clothing retailer in
the last three months,
according to the Sunday
Telegraph." + ], + [ + "Ten-man Paris St Germain
clinched their seventh
consecutive victory over arch-
rivals Olympique Marseille
with a 2-1 triumph in Ligue 1
on Sunday thanks to a second-
half winner by substitute
Edouard Cisse." + ], + [ + "Until this week, only a few
things about the strange,
long-ago disappearance of
Charles Robert Jenkins were
known beyond a doubt. In the
bitter cold of Jan. 5, 1965,
the 24-year-old US Army
sergeant was leading" + ], + [ + "Roy Oswalt wasn #39;t
surprised to hear the Astros
were flying Sunday night
through the remnants of a
tropical depression that
dumped several inches of rain
in Louisiana and could bring
showers today in Atlanta." + ], + [ + "AP - This hardly seemed
possible when Pitt needed
frantic rallies to overcome
Division I-AA Furman or Big
East cellar dweller Temple. Or
when the Panthers could barely
move the ball against Ohio
#151; not Ohio State, but Ohio
U." + ], + [ + "Everyone is moaning about the
fallout from last weekend but
they keep on reporting the
aftermath. #39;The fall-out
from the so-called
quot;Battle of Old Trafford
quot; continues to settle over
the nation and the debate" + ], + [ + "Oil supply concerns and broker
downgrades of blue-chip
companies left stocks mixed
yesterday, raising doubts that
Wall Street #39;s year-end
rally would continue." + ], + [ + "Genentech Inc. said the
marketing of Rituxan, a cancer
drug that is the company #39;s
best-selling product, is the
subject of a US criminal
investigation." + ], + [ + "American Lindsay Davenport
regained the No. 1 ranking in
the world for the first time
since early 2002 by defeating
Dinara Safina of Russia 6-4,
6-2 in the second round of the
Kremlin Cup on Thursday." + ], + [ + " The world's No. 2 soft drink
company said on Thursday
quarterly profit rose due to
tax benefits." + ], + [ + "TOKYO (AP) -- The electronics
and entertainment giant Sony
Corp. (SNE) is talking with
Wal-Mart Stores Inc..." + ], + [ + "After an unprecedented span of
just five days, SpaceShipOne
is ready for a return trip to
space on Monday, its final
flight to clinch a \\$10
million prize." + ], + [ + "The United States on Tuesday
modified slightly a threat of
sanctions on Sudan #39;s oil
industry in a revised text of
its UN resolution on
atrocities in the country
#39;s Darfur region." + ], + [ + "Freshman Jeremy Ito kicked
four field goals and Ryan
Neill scored on a 31-yard
interception return to lead
improving Rutgers to a 19-14
victory on Saturday over
visiting Michigan State." + ], + [ + "Hi-tech monitoring of
livestock at pig farms could
help improve the animal growth
process and reduce costs." + ], + [ + "Third-seeded Guillermo Canas
defeated Guillermo Garcia-
Lopez of Spain 7-6 (1), 6-3
Monday on the first day of the
Shanghai Open on Monday." + ], + [ + "AP - France intensified
efforts Tuesday to save the
lives of two journalists held
hostage in Iraq, and the Arab
League said the militants'
deadline for France to revoke
a ban on Islamic headscarves
in schools had been extended." + ], + [ + "Cable amp; Wireless plc
(NYSE: CWP - message board) is
significantly ramping up its
investment in local loop
unbundling (LLU) in the UK,
and it plans to spend up to 85
million (\\$152." + ], + [ + "USATODAY.com - Personal
finance software programs are
the computer industry's
version of veggies: Everyone
knows they're good for you,
but it's just hard to get
anyone excited about them." + ], + [ + " NEW YORK (Reuters) - The
dollar rebounded on Monday
after last week's heavy
selloff, but analysts were
uncertain if the rally would
hold after fresh economic data
suggested the December U.S.
jobs report due Friday might
not live up to expectations." + ], + [ + "AFP - Microsoft said that it
had launched a new desktop
search tool that allows
personal computer users to
find documents or messages on
their PCs." + ], + [ + "At least 12 people die in an
explosion at a fuel pipeline
on the outskirts of Nigeria's
biggest city, Lagos." + ], + [ + "The three largest computer
makers spearheaded a program
today designed to standardize
working conditions for their
non-US workers." + ], + [ + "Description: Illinois Gov. Rod
Blagojevich is backing state
legislation that would ban
sales or rentals of video
games with graphic sexual or
violent content to children
under 18." + ], + [ + "Volkswagen demanded a two-year
wage freeze for the
170,000-strong workforce at
Europe #39;s biggest car maker
yesterday, provoking union
warnings of imminent conflict
at key pay and conditions
negotiations." + ], + [ + " NEW YORK (Reuters) - U.S.
stock futures pointed to a
lower open on Wall Street on
Thursday, extending the
previous session's sharp
fall, with rising energy
prices feeding investor
concerns about corporate
profits and slower growth." + ], + [ + "But to play as feebly as it
did for about 35 minutes last
night in Game 1 of the WNBA
Finals and lose by only four
points -- on the road, no less
-- has to be the best
confidence builder since Cindy
St." + ], + [ + "MILAN General Motors and Fiat
on Wednesday edged closer to
initiating a legal battle that
could pit the two carmakers
against each other in a New
York City court room as early
as next month." + ], + [ + "Are you bidding on keywords
through Overture's Precision
Match, Google's AdWords or
another pay-for-placement
service? If so, you're
eligible to participate in
their contextual advertising
programs." + ], + [ + "Two of the Ford Motor Company
#39;s most senior executives
retired on Thursday in a sign
that the company #39;s deep
financial crisis has abated,
though serious challenges
remain." + ], + [ + "Citing security concerns, the
U.S. Embassy on Thursday
banned its employees from
using the highway linking the
embassy area to the
international airport, a
10-mile stretch of road
plagued by frequent suicide
car-bomb attacks." + ], + [ + "Nobel Laureate Wilkins, 87,
played an important role in
the discovery of the double
helix structure of DNA, the
molecule that carries our
quot;life code quot;,
Kazinform refers to BBC News." + ], + [ + "With yesterday #39;s report on
its athletic department
violations completed, the
University of Washington says
it is pleased to be able to
move forward." + ], + [ + " LONDON (Reuters) - Wall
Street was expected to start
little changed on Friday as
investors continue to fret
over the impact of high oil
prices on earnings, while
Boeing <A HREF=\"http://www.
investor.reuters.com/FullQuote
.aspx?ticker=BA.N target=/stoc
ks/quickinfo/fullquote\">BA.
N</A> will be eyed
after it reiterated its
earnings forecast." + ], + [ + "AP - Tom Daschle bade his
fellow Senate Democrats
farewell Tuesday with a plea
that they seek common ground
with Republicans yet continue
to fight for the less
fortunate." + ], + [ + "Sammy Sosa was fined \\$87,400
-- one day's salary -- for
arriving late to the Cubs'
regular-season finale at
Wrigley Field and leaving the
game early. The slugger's
agent, Adam Katz , said
yesterday Sosa most likely
will file a grievance. Sosa
arrived 70 minutes before
Sunday's first pitch, and he
apparently left 15 minutes
after the game started without
..." + ], + [ + "Having an always-on, fast net
connection is changing the way
Britons use the internet,
research suggests." + ], + [ + "AP - Police defused a bomb in
a town near Prime Minister
Silvio Berlusconi's villa on
the island of Sardinia on
Wednesday shortly after
British Prime Minister Tony
Blair finished a visit there
with the Italian leader." + ], + [ + "Is the Oklahoma defense a
notch below its predecessors?
Is Texas #39; offense a step-
ahead? Why is Texas Tech
feeling good about itself
despite its recent loss?" + ], + [ + "The coffin of Yasser Arafat,
draped with the Palestinian
flag, was bound for Ramallah
in the West Bank Friday,
following a formal funeral on
a military compound near
Cairo." + ], + [ + "US Ambassador to the United
Nations John Danforth resigned
on Thursday after serving in
the post for less than six
months. Danforth, 68, said in
a letter released Thursday" + ], + [ + "Crude oil futures prices
dropped below \\$51 a barrel
yesterday as supply concerns
ahead of the Northern
Hemisphere winter eased after
an unexpectedly high rise in
US inventories." + ], + [ + "New York gets 57 combined
points from its starting
backcourt of Jamal Crawford
and Stephon Marbury and tops
Denver, 107-96." + ], + [ + "ISLAMABAD, Pakistan -- Photos
were published yesterday in
newspapers across Pakistan of
six terror suspects, including
a senior Al Qaeda operative,
the government says were
behind attempts to assassinate
the nation's president." + ], + [ + "AP - Shawn Marion had a
season-high 33 points and 15
rebounds, leading the Phoenix
Suns on a fourth-quarter
comeback despite the absence
of Steve Nash in a 95-86 win
over the New Orleans Hornets
on Friday night." + ], + [ + "By Lilly Vitorovich Of DOW
JONES NEWSWIRES SYDNEY (Dow
Jones)--Rupert Murdoch has
seven weeks to convince News
Corp. (NWS) shareholders a
move to the US will make the
media conglomerate more
attractive to" + ], + [ + "A number of signs point to
increasing demand for tech
workers, but not all the
clouds have been driven away." + ], + [ + "Messina upset defending
champion AC Milan 2-1
Wednesday, while Juventus won
its third straight game to
stay alone atop the Italian
league standings." + ], + [ + "Microsoft Corp. (MSFT.O:
Quote, Profile, Research)
filed nine new lawsuits
against spammers who send
unsolicited e-mail, including
an e-mail marketing Web
hosting company, the world
#39;s largest software maker
said on Thursday." + ], + [ + "AP - Padraig Harrington
rallied to a three-stroke
victory in the German Masters
on a windy Sunday, closing
with a 2-under-par 70 and
giving his game a big boost
before the Ryder Cup." + ], + [ + " ATHENS (Reuters) - The Athens
Paralympics canceled
celebrations at its closing
ceremony after seven
schoolchildren traveling to
watch the event died in a bus
crash on Monday." + ], + [ + "The rocket plane SpaceShipOne
is just one flight away from
claiming the Ansari X-Prize, a
\\$10m award designed to kick-
start private space travel." + ], + [ + "Reuters - Three American
scientists won the\\2004 Nobel
physics prize on Tuesday for
showing how tiny
quark\\particles interact,
helping to explain everything
from how a\\coin spins to how
the universe was built." + ], + [ + "Ironically it was the first
regular season game for the
Carolina Panthers that not
only began the history of the
franchise, but also saw the
beginning of a rivalry that
goes on to this day." + ], + [ + "Baltimore Ravens running back
Jamal Lewis did not appear at
his arraignment Friday, but
his lawyers entered a not
guilty plea on charges in an
expanded drug conspiracy
indictment." + ], + [ + "AP - Sharp Electronics Corp.
plans to stop selling its
Linux-based handheld computer
in the United States, another
sign of the slowing market for
personal digital assistants." + ], + [ + "After serving a five-game
suspension, Milton Bradley
worked out with the Dodgers as
they prepared for Tuesday's
opener against the St. Louis
Cardinals." + ], + [ + "AP - Prime Time won't be
playing in prime time this
time. Deion Sanders was on the
inactive list and missed a
chance to strut his stuff on
\"Monday Night Football.\"" + ], + [ + "Reuters - Glaciers once held
up by a floating\\ice shelf off
Antarctica are now sliding off
into the sea --\\and they are
going fast, scientists said on
Tuesday." + ], + [ + "DUBAI : An Islamist group has
threatened to kill two Italian
women held hostage in Iraq if
Rome does not withdraw its
troops from the war-torn
country within 24 hours,
according to an internet
statement." + ], + [ + "AP - Warning lights flashed
atop four police cars as the
caravan wound its way up the
driveway in a procession fit
for a presidential candidate.
At long last, Azy and Indah
had arrived. They even flew
through a hurricane to get
here." + ], + [ + "The man who delivered the
knockout punch was picked up
from the Seattle scrap heap
just after the All-Star Game.
Before that, John Olerud
certainly hadn't figured on
facing Pedro Martinez in
Yankee Stadium in October." + ], + [ + "\\Female undergraduates work
harder and are more open-
minded than males, leading to
better results, say
scientists." + ], + [ + "A heavy quake rocked Indonesia
#39;s Papua province killing
at least 11 people and
wounding 75. The quake
destroyed 150 buildings,
including churches, mosques
and schools." + ], + [ + "LONDON : A consortium,
including former world
champion Nigel Mansell, claims
it has agreed terms to ensure
Silverstone remains one of the
venues for the 2005 Formula
One world championship." + ], + [ + " BATON ROUGE, La. (Sports
Network) - LSU has named Les
Miles its new head football
coach, replacing Nick Saban." + ], + [ + "The United Nations annual
World Robotics Survey predicts
the use of robots around the
home will surge seven-fold by
2007. The boom is expected to
be seen in robots that can mow
lawns and vacuum floors, among
other chores." + ], + [ + "The long-term economic health
of the United States is
threatened by \\$53 trillion in
government debts and
liabilities that start to come
due in four years when baby
boomers begin to retire." + ], + [ + "Reuters - A small group of
suspected\\gunmen stormed
Uganda's Water Ministry
Wednesday and took
three\\people hostage to
protest against proposals to
allow President\\Yoweri
Museveni for a third
term.\\Police and soldiers with
assault rifles cordoned off
the\\three-story building, just
328 feet from Uganda's
parliament\\building in the
capital Kampala." + ], + [ + "The Moscow Arbitration Court
ruled on Monday that the YUKOS
oil company must pay RUR
39.113bn (about \\$1.34bn) as
part of its back tax claim for
2001." + ], + [ + "NOVEMBER 11, 2004 -- Bankrupt
US Airways this morning said
it had reached agreements with
lenders and lessors to
continue operating nearly all
of its mainline and US Airways
Express fleets." + ], + [ + "Venezuela suggested Friday
that exiles living in Florida
may have masterminded the
assassination of a prosecutor
investigating a short-lived
coup against leftist President
Hugo Chvez" + ], + [ + "Want to dive deep -- really
deep -- into the technical
literature about search
engines? Here's a road map to
some of the best web
information retrieval
resources available online." + ], + [ + "Reuters - Ancel Keys, a
pioneer in public health\\best
known for identifying the
connection between
a\\cholesterol-rich diet and
heart disease, has died." + ], + [ + "The US government asks the
World Trade Organisation to
step in to stop EU member
states from \"subsidising\"
planemaker Airbus." + ], + [ + "Reuters - T-Mobile USA, the
U.S. wireless unit\\of Deutsche
Telekom AG (DTEGn.DE), does
not expect to offer\\broadband
mobile data services for at
least the next two years,\\its
chief executive said on
Thursday." + ], + [ + "Verizon Communications is
stepping further into video as
a way to compete against cable
companies." + ], + [ + "Facing a popular outcry at
home and stern warnings from
Europe, the Turkish government
discreetly stepped back
Tuesday from a plan to
introduce a motion into a
crucial penal reform bill to
make adultery a crime
punishable by prison." + ], + [ + "Boston Scientific Corp.
(BSX.N: Quote, Profile,
Research) said on Wednesday it
received US regulatory
approval for a device to treat
complications that arise in
patients with end-stage kidney
disease who need dialysis." + ], + [ + "North-west Norfolk MP Henry
Bellingham has called for the
release of an old college
friend accused of plotting a
coup in Equatorial Guinea." + ], + [ + "With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." + ], + [ + "AP - The Chicago Blackhawks
re-signed goaltender Michael
Leighton to a one-year
contract Wednesday." + ], + [ + "Oracle Corp. plans to release
the latest version of its CRM
(customer relationship
management) applications
within the next two months, as
part of an ongoing update of
its E-Business Suite." + ], + [ + "Toyota Motor Corp. #39;s
shares fell for a second day,
after the world #39;s second-
biggest automaker had an
unexpected quarterly profit
drop." + ], + [ + "AFP - Want to buy a castle?
Head for the former East
Germany." + ], + [ + "Hosted CRM service provider
Salesforce.com took another
step forward last week in its
strategy to build an online
ecosystem of vendors that
offer software as a service." + ], + [ + "Britain-based HBOS says it
will file a complaint to the
European Commission against
Spanish bank Santander Central
Hispano (SCH) in connection
with SCH #39;s bid to acquire
British bank Abbey National" + ], + [ + "AFP - Steven Gerrard has moved
to allay Liverpool fans' fears
that he could be out until
Christmas after breaking a
metatarsal bone in his left
foot." + ], + [ + "Verizon Wireless on Thursday
announced an agreement to
acquire all the PCS spectrum
licenses of NextWave Telecom
Inc. in 23 markets for \\$3
billion." + ], + [ + "washingtonpost.com -
Technology giants IBM and
Hewlett-Packard are injecting
hundreds of millions of
dollars into radio-frequency
identification technology,
which aims to advance the
tracking of items from ho-hum
bar codes to smart tags packed
with data." + ], + [ + "ATHENS -- She won her first
Olympic gold medal in kayaking
when she was 18, the youngest
paddler to do so in Games
history. Yesterday, at 42,
Germany #39;s golden girl
Birgit Fischer won her eighth
Olympic gold in the four-woman
500-metre kayak race." + ], + [ + "England boss Sven Goran
Eriksson has defended
goalkeeper David James after
last night #39;s 2-2 draw in
Austria. James allowed Andreas
Ivanschitz #39;s shot to slip
through his fingers to
complete Austria comeback from
two goals down." + ], + [ + "MINSK - Legislative elections
in Belarus held at the same
time as a referendum on
whether President Alexander
Lukashenko should be allowed
to seek a third term fell
significantly short of
democratic standards, foreign
observers said here Monday." + ], + [ + "An Olympic sailor is charged
with the manslaughter of a
Briton who died after being
hit by a car in Athens." + ], + [ + "The Norfolk Broads are on
their way to getting a clean
bill of ecological health
after a century of stagnation." + ], + [ + "AP - Secretary of State Colin
Powell on Friday praised the
peace deal that ended fighting
in Iraq's holy city of Najaf
and said the presence of U.S.
forces in the area helped make
it possible." + ], + [ + "The quot;future quot; is
getting a chance to revive the
presently struggling New York
Giants. Two other teams also
decided it was time for a
change at quarterback, but the
Buffalo Bills are not one of
them." + ], + [ + "For the second time this year,
an alliance of major Internet
providers - including Atlanta-
based EarthLink -iled a
coordinated group of lawsuits
aimed at stemming the flood of
online junk mail." + ], + [ + " WASHINGTON (Reuters) - The
PIMCO mutual fund group has
agreed to pay \\$50 million to
settle fraud charges involving
improper rapid dealing in
mutual fund shares, the U.S.
Securities and Exchange
Commission said on Monday." + ], + [ + "Via Technologies has released
a version of the open-source
Xine media player that is
designed to take advantage of
hardware digital video
acceleration capabilities in
two of the company #39;s PC
chipsets, the CN400 and
CLE266." + ], + [ + "The Conference Board reported
Thursday that the Leading
Economic Index fell for a
third consecutive month in
August, suggesting slower
economic growth ahead amid
rising oil prices." + ], + [ + " SAN FRANCISCO (Reuters) -
Software maker Adobe Systems
Inc.<A HREF=\"http://www.inv
estor.reuters.com/FullQuote.as
px?ticker=ADBE.O target=/stock
s/quickinfo/fullquote\">ADBE
.O</A> on Thursday
posted a quarterly profit that
rose more than one-third from
a year ago, but shares fell 3
percent after the maker of
Photoshop and Acrobat software
did not raise forecasts for
fiscal 2005." + ], + [ + "William Morrison Supermarkets
has agreed to sell 114 small
Safeway stores and a
distribution centre for 260.2
million pounds. Morrison
bought these stores as part of
its 3 billion pound" + ], + [ + "SCO Group has a plan to keep
itself fit enough to continue
its legal battles against
Linux and to develop its Unix-
on-Intel operating systems." + ], + [ + "Flushing Meadows, NY (Sports
Network) - The men #39;s
semifinals at the 2004 US Open
will be staged on Saturday,
with three of the tournament
#39;s top-five seeds ready for
action at the USTA National
Tennis Center." + ], + [ + "Pepsi pushes a blue version of
Mountain Dew only at Taco
Bell. Is this a winning
strategy?" + ], + [ + "New software helps corporate
travel managers track down
business travelers who
overspend. But it also poses a
dilemma for honest travelers
who are only trying to save
money." + ], + [ + "NATO Secretary-General Jaap de
Hoop Scheffer has called a
meeting of NATO states and
Russia on Tuesday to discuss
the siege of a school by
Chechen separatists in which
more than 335 people died, a
NATO spokesman said." + ], + [ + "26 August 2004 -- Iraq #39;s
top Shi #39;ite cleric, Grand
Ayatollah Ali al-Sistani,
arrived in the city of Al-
Najaf today in a bid to end a
weeks-long conflict between US
forces and militiamen loyal to
Shi #39;ite cleric Muqtada al-
Sadr." + ], + [ + "AFP - Senior executives at
business software group
PeopleSoft unanimously
recommended that its
shareholders reject a 8.8
billion dollar takeover bid
from Oracle Corp, PeopleSoft
said in a statement Wednesday." + ], + [ + "Reuters - Neolithic people in
China may have\\been the first
in the world to make wine,
according to\\scientists who
have found the earliest
evidence of winemaking\\from
pottery shards dating from
7,000 BC in northern China." + ], + [ + "Given nearly a week to examine
the security issues raised by
the now-infamous brawl between
players and fans in Auburn
Hills, Mich., Nov. 19, the
Celtics returned to the
FleetCenter last night with
two losses and few concerns
about their on-court safety." + ], + [ + " TOKYO (Reuters) - Electronics
conglomerate Sony Corp.
unveiled eight new flat-screen
televisions on Thursday in a
product push it hopes will
help it secure a leading 35
percent of the domestic
market in the key month of
December." + ], + [ + "As the election approaches,
Congress abandons all pretense
of fiscal responsibility,
voting tax cuts that would
drive 10-year deficits past
\\$3 trillion." + ], + [ + "PARIS : French trade unions
called on workers at France
Telecom to stage a 24-hour
strike September 7 to protest
government plans to privatize
the public telecommunications
operator, union sources said." + ], + [ + "ServiceMaster profitably
bundles services and pays a
healthy 3.5 dividend." + ], + [ + "The Indonesian tourism
industry has so far not been
affected by last week #39;s
bombing outside the Australian
embassy in Jakarta and
officials said they do not
expect a significant drop in
visitor numbers as a result of
the attack." + ], + [ + "\\$222.5 million -- in an
ongoing securities class
action lawsuit against Enron
Corp. The settlement,
announced Friday and" + ], + [ + "Arsenals Thierry Henry today
missed out on the European
Footballer of the Year award
as Andriy Shevchenko took the
honour. AC Milan frontman
Shevchenko held off
competition from Barcelona
pair Deco and" + ], + [ + "Donald Halsted, one target of
a class-action suit alleging
financial improprieties at
bankrupt Polaroid, officially
becomes CFO." + ], + [ + "Neil Mellor #39;s sensational
late winner for Liverpool
against Arsenal on Sunday has
earned the back-up striker the
chance to salvage a career
that had appeared to be
drifting irrevocably towards
the lower divisions." + ], + [ + "ABOUT 70,000 people were
forced to evacuate Real Madrid
#39;s Santiago Bernabeu
stadium minutes before the end
of a Primera Liga match
yesterday after a bomb threat
in the name of ETA Basque
separatist guerillas." + ], + [ + "The team learned on Monday
that full-back Jon Ritchie
will miss the rest of the
season with a torn anterior
cruciate ligament in his left
knee." + ], + [ + " NEW YORK (Reuters) -
Lifestyle guru Martha Stewart
said on Wednesday she wants
to start serving her prison
sentence for lying about a
suspicious stock sale as soon
as possible, so she can put
her \"nightmare\" behind her." + ], + [ + "Apple Computer's iPod remains
the king of digital music
players, but robust pretenders
to the throne have begun to
emerge in the Windows
universe. One of them is the
Zen Touch, from Creative Labs." + ], + [ + "The 7710 model features a
touch screen, pen input, a
digital camera, an Internet
browser, a radio, video
playback and streaming and
recording capabilities, the
company said." + ], + [ + "SAN FRANCISCO (CBS.MW) --
Crude futures closed under
\\$46 a barrel Wednesday for
the first time since late
September and heating-oil and
unleaded gasoline prices
dropped more than 6 percent
following an across-the-board
climb in US petroleum
inventories." + ], + [ + "The University of Iowa #39;s
market for US presidential
futures, founded 16-years ago,
has been overtaken by a
Dublin-based exchange that is
now 25 times larger." + ], + [ + "Venus Williams barely kept
alive her hopes of qualifying
for next week #39;s WTA Tour
Championships. Williams,
seeded fifth, survived a
third-set tiebreaker to
outlast Yuilana Fedak of the
Ukraine, 6-4 2-6 7-6" + ], + [ + " SYDNEY (Reuters) - Arnold
Palmer has taken a swing at
America's top players,
criticizing their increasing
reluctance to travel abroad
to play in tournaments." + ], + [ + "MARK Thatcher will have to
wait until at least next April
to face trial on allegations
he helped bankroll a coup
attempt in oil-rich Equatorial
Guinea." + ], + [ + "A top Red Hat executive has
attacked the open-source
credentials of its sometime
business partner Sun
Microsystems. In a Web log
posting Thursday, Michael
Tiemann, Red Hat #39;s vice
president of open-source
affairs" + ], + [ + "President Bush #39;s drive to
deploy a multibillion-dollar
shield against ballistic
missiles was set back on
Wednesday by what critics
called a stunning failure of
its first full flight test in
two years." + ], + [ + "Although he was well-beaten by
Retief Goosen in Sunday #39;s
final round of The Tour
Championship in Atlanta, there
has been some compensation for
the former world number one,
Tiger Woods." + ], + [ + "WAYNE Rooney and Henrik
Larsson are among the players
nominated for FIFAs
prestigious World Player of
the Year award. Rooney is one
of four Manchester United
players on a list which is
heavily influenced by the
Premiership." + ], + [ + "It didn #39;t look good when
it happened on the field, and
it looked worse in the
clubhouse. Angels second
baseman Adam Kennedy left the
Angels #39; 5-2 win over the
Seattle Mariners" + ], + [ + "Air travelers moved one step
closer to being able to talk
on cell phones and surf the
Internet from laptops while in
flight, thanks to votes by the
Federal Communications
Commission yesterday." + ], + [ + "MySQL developers turn to an
unlikely source for database
tool: Microsoft. Also: SGI
visualizes Linux, and the
return of Java veteran Kim
Polese." + ], + [ + "DESPITE the budget deficit,
continued increases in oil and
consumer prices, the economy,
as measured by gross domestic
product, grew by 6.3 percent
in the third" + ], + [ + "NEW YORK - A drop in oil
prices and upbeat outlooks
from Wal-Mart and Lowe's
helped send stocks sharply
higher Monday on Wall Street,
with the swing exaggerated by
thin late summer trading. The
Dow Jones industrials surged
nearly 130 points..." + ], + [ + "Freshman Darius Walker ran for
115 yards and scored two
touchdowns, helping revive an
Irish offense that had managed
just one touchdown in the
season's first six quarters." + ], + [ + "Consumers who cut it close by
paying bills from their
checking accounts a couple of
days before depositing funds
will be out of luck under a
new law that takes effect Oct.
28." + ], + [ + "Dell Inc. said its profit
surged 25 percent in the third
quarter as the world's largest
personal computer maker posted
record sales due to rising
technology spending in the
corporate and government
sectors in the United States
and abroad." + ], + [ + "AP - NBC is adding a 5-second
delay to its NASCAR telecasts
after Dale Earnhardt Jr. used
a vulgarity during a postrace
TV interview last weekend." + ], + [ + " BOSTON (Sports Network) - The
New York Yankees will start
Orlando \"El Duque\" Hernandez
in Game 4 of the American
League Championship Series on
Saturday against the Boston
Red Sox." + ], + [ + "The future of Sven-Goran
Eriksson as England coach is
the subject of intense
discussion after the draw in
Austria. Has the Swede lost
the confidence of the nation
or does he remain the best man
for the job?" + ], + [ + "Component problems meant
Brillian's new big screens
missed the NFL's kickoff
party." + ], + [ + "Spain begin their third final
in five seasons at the Olympic
stadium hoping to secure their
second title since their first
in Barcelona against Australia
in 2000." + ], + [ + "Second-seeded David Nalbandian
of Argentina lost at the Japan
Open on Thursday, beaten by
Gilles Muller of Luxembourg
7-6 (4), 3-6, 6-4 in the third
round." + ], + [ + "Thursday #39;s unexpected
resignation of Memphis
Grizzlies coach Hubie Brown
left a lot of questions
unanswered. In his unique way
of putting things, the
71-year-old Brown seemed to
indicate he was burned out and
had some health concerns." + ], + [ + "RUDI Voller had quit as coach
of Roma after a 3-1 defeat
away to Bologna, the Serie A
club said today. Under the
former Germany coach, Roma had
taken just four league points
from a possible 12." + ], + [ + "A Russian court on Thursday
rejected an appeal by the
Yukos oil company seeking to
overturn a freeze on the
accounts of the struggling oil
giant #39;s core subsidiaries." + ], + [ + "ONE by one, the players #39;
faces had flashed up on the
giant Ibrox screens offering
season #39;s greetings to the
Rangers fans. But the main
presents were reserved for
Auxerre." + ], + [ + "Switzerland #39;s struggling
national airline reported a
second-quarter profit of 45
million Swiss francs (\\$35.6
million) Tuesday, although its
figures were boosted by a
legal settlement in France." + ], + [ + "ROSTOV-ON-DON, Russia --
Hundreds of protesters
ransacked and occupied the
regional administration
building in a southern Russian
province Tuesday, demanding
the resignation of the region
#39;s president, whose former
son-in-law has been linked to
a multiple" + ], + [ + "SIPTU has said it is strongly
opposed to any privatisation
of Aer Lingus as pressure
mounts on the Government to
make a decision on the future
funding of the airline." + ], + [ + "Reuters - SBC Communications
said on Monday it\\would offer
a television set-top box that
can handle music,\\photos and
Internet downloads, part of
SBC's efforts to expand\\into
home entertainment." + ], + [ + "Molson Inc. Chief Executive
Officer Daniel O #39;Neill
said he #39;ll provide
investors with a positive #39;
#39; response to their
concerns over the company
#39;s plan to let stock-
option holders vote on its
planned merger with Adolph
Coors Co." + ], + [ + "South Korea #39;s Grace Park
shot a seven-under-par 65 to
triumph at the CJ Nine Bridges
Classic on Sunday. Park #39;s
victory made up her final-
round collapse at the Samsung
World Championship two weeks
ago." + ], + [ + " WASHINGTON (Reuters) - Hopes
-- and worries -- that U.S.
regulators will soon end the
ban on using wireless phones
during U.S. commercial flights
are likely at least a year or
two early, government
officials and analysts say." + ], + [ + "AFP - Iraqi Foreign Minister
Hoshyar Zebari arrived
unexpectedly in the holy city
of Mecca Wednesday where he
met Crown Prince Abdullah bin
Abdul Aziz, the official SPA
news agency reported." + ], + [ + "Titans QB Steve McNair was
released from a Nashville
hospital after a two-night
stay for treatment of a
bruised sternum. McNair was
injured during the fourth
quarter of the Titans #39;
15-12 loss to Jacksonville on
Sunday." + ], + [ + "Keith Miller, Australia #39;s
most prolific all-rounder in
Test cricket, died today at a
nursing home, Cricket
Australia said. He was 84." + ], + [ + "Haitian police and UN troops
moved into a slum neighborhood
on Sunday and cleared street
barricades that paralyzed a
part of the capital." + ], + [ + "TORONTO Former Toronto pitcher
John Cerutti (seh-ROO
#39;-tee) was found dead in
his hotel room today,
according to the team. He was
44." + ], + [ + "withdrawal of troops and
settlers from occupied Gaza
next year. Militants seek to
claim any pullout as a
victory. quot;Islamic Jihad
will not be broken by this
martyrdom, quot; said Khaled
al-Batsh, a senior political
leader in Gaza." + ], + [ + " NEW YORK (Reuters) - The
world's largest gold producer,
Newmont Mining Corp. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=NEM
.N target=/stocks/quickinfo/fu
llquote\">NEM.N</A>,
on Wednesday said higher gold
prices drove up quarterly
profit by 12.5 percent, even
though it sold less of the
precious metal." + ], + [ + "The U.S. military has found
nearly 20 houses where
intelligence officers believe
hostages were tortured or
killed in this city, including
the house with the cage that
held a British contractor who
was beheaded last month." + ], + [ + "AFP - Opponents of the Lao
government may be plotting
bomb attacks in Vientiane and
other areas of Laos timed to
coincide with a summit of
Southeast Asian leaders the
country is hosting next month,
the United States said." + ], + [ + "After a year of pilots and
trials, Siebel Systems jumped
with both feet into the SMB
market Tuesday, announcing a
new approach to offer Siebel
Professional CRM applications
to SMBs (small and midsize
businesses) -- companies with
revenues up to about \\$500
million." + ], + [ + "AP - Russia agreed Thursday to
send warships to help NATO
naval patrols that monitor
suspicious vessels in the
Mediterranean, part of a push
for closer counterterrorism
cooperation between Moscow and
the western alliance." + ], + [ + "Intel won't release a 4-GHz
version of its flagship
Pentium 4 product, having
decided instead to realign its
engineers around the company's
new design priorities, an
Intel spokesman said today.
\\\\" + ], + [ + "AP - A Soyuz spacecraft
carrying two Russians and an
American rocketed closer
Friday to its docking with the
international space station,
where the current three-man
crew made final departure
preparations." + ], + [ + "Defense: Ken Lucas. His
biggest play was his first
one. The fourth-year
cornerback intercepted a Ken
Dorsey pass that kissed off
the hands of wide receiver
Rashaun Woods and returned it
25 yards to set up the
Seahawks #39; first score." + ], + [ + "Scientists have manipulated
carbon atoms to create a
material that could be used to
create light-based, versus
electronic, switches. The
material could lead to a
supercharged Internet based
entirely on light, scientists
say." + ], + [ + "A military plane crashed in
the mountains near Caracas,
killing all 16 persons on
board, including two high-
ranking military officers,
officials said." + ], + [ + "The powerful St. Louis trio of
Albert Pujols, Scott Rolen and
Jim Edmonds is 4 for 23 with
one RBI in the series and with
runners on base, they are 1
for 13." + ], + [ + "A voice recording said to be
that of suspected Al Qaeda
commander Abu Mussab al-
Zarqawi, claims Iraq #39;s
Prime Minister Iyad Allawi is
the militant network #39;s
number one target." + ], + [ + "BEIJING -- More than a year
after becoming China's
president, Hu Jintao was
handed the full reins of power
yesterday when his
predecessor, Jiang Zemin, gave
up the nation's most powerful
military post." + ], + [ + "LOS ANGELES (Reuters)
Qualcomm has dropped an \\$18
million claim for monetary
damages from rival Texas
Instruments for publicly
discussing terms of a
licensing pact, a TI
spokeswoman confirmed Tuesday." + ], + [ + "Hewlett-Packard is the latest
IT vendor to try blogging. But
analysts wonder if the weblog
trend is the 21st century
equivalent of CB radios, which
made a big splash in the 1970s
before fading." + ], + [ + " WASHINGTON (Reuters) - Fannie
Mae executives and their
regulator squared off on
Wednesday, with executives
denying any accounting
irregularity and the regulator
saying the housing finance
company's management may need
to go." + ], + [ + "The scientists behind Dolly
the sheep apply for a license
to clone human embryos. They
want to take stem cells from
the embryos to study Lou
Gehrig's disease." + ], + [ + "As the first criminal trial
stemming from the financial
deals at Enron opened in
Houston on Monday, it is
notable as much for who is not
among the six defendants as
who is - and for how little
money was involved compared
with how much in other Enron" + ], + [ + "LONDON (CBS.MW) -- British
bank Barclays on Thursday said
it is in talks to buy a
majority stake in South
African bank ABSA. Free!" + ], + [ + "The Jets signed 33-year-old
cornerback Terrell Buckley,
who was released by New
England on Sunday, after
putting nickel back Ray
Mickens on season-ending
injured reserve yesterday with
a torn ACL in his left knee." + ], + [ + "Some of the silly tunes
Japanese pay to download to
use as the ring tone for their
mobile phones sure have their
knockers, but it #39;s for
precisely that reason that a
well-known counselor is raking
it in at the moment, according
to Shukan Gendai (10/2)." + ], + [ + "WEST INDIES thrilling victory
yesterday in the International
Cricket Council Champions
Trophy meant the world to the
five million people of the
Caribbean." + ], + [ + "AP - Greenpeace activists
scaled the walls of Ford Motor
Co.'s Norwegian headquarters
Tuesday to protest plans to
destroy hundreds of non-
polluting electric cars." + ], + [ + "Investors sent stocks sharply
lower yesterday as oil prices
continued their climb higher
and new questions about the
safety of arthritis drugs
pressured pharmaceutical
stocks." + ], + [ + "Scotland manager Berti Vogts
insists he is expecting
nothing but victory against
Moldova on Wednesday. The game
certainly is a must-win affair
if the Scots are to have any
chance of qualifying for the
2006 World Cup finals." + ], + [ + "IBM announced yesterday that
it will invest US\\$250 million
(S\\$425 million) over the next
five years and employ 1,000
people in a new business unit
to support products and
services related to sensor
networks." + ], + [ + "AFP - The chances of Rupert
Murdoch's News Corp relocating
from Australia to the United
States have increased after
one of its biggest
institutional investors has
chosen to abstain from a vote
next week on the move." + ], + [ + "AFP - An Indian minister said
a school text-book used in the
violence-prone western state
of Gujarat portrayed Adolf
Hitler as a role model." + ], + [ + "Reuters - The head of UAL
Corp.'s United\\Airlines said
on Thursday the airline's
restructuring plan\\would lead
to a significant number of job
losses, but it was\\not clear
how many." + ], + [ + "DOVER, N.H. (AP) -- Democrat
John Kerry is seizing on the
Bush administration's failure
to secure hundreds of tons of
explosives now missing in
Iraq." + ], + [ + "AP - Microsoft Corp. goes into
round two Friday of its battle
to get the European Union's
sweeping antitrust ruling
lifted having told a judge
that it had been prepared
during settlement talks to
share more software code with
its rivals than the EU
ultimately demanded." + ], + [ + " INDIANAPOLIS (Reuters) -
Jenny Thompson will take the
spotlight from injured U.S.
team mate Michael Phelps at
the world short course
championships Saturday as she
brings down the curtain on a
spectacular swimming career." + ], + [ + "Martin Brodeur made 27 saves,
and Brad Richards, Kris Draper
and Joe Sakic scored goals to
help Canada beat Russia 3-1
last night in the World Cup of
Hockey, giving the Canadians a
3-0 record in round-robin
play." + ], + [ + "AP - Sears, Roebuck and Co.,
which has successfully sold
its tools and appliances on
the Web, is counting on having
the same magic with bedspreads
and sweaters, thanks in part
to expertise gained by its
purchase of Lands' End Inc." + ], + [ + "com September 14, 2004, 9:12
AM PT. With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." + ], + [ + " NEW YORK (Reuters) -
Children's Place Retail Stores
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=PLCE.O target=/sto
cks/quickinfo/fullquote\">PL
CE.O</A> said on
Wednesday it will buy 313
retail stores from Walt
Disney Co., and its stock rose
more than 14 percent in early
morning trade." + ], + [ + "ALBANY, N.Y. -- A California-
based company that brokers
life, accident, and disability
policies for leading US
companies pocketed millions of
dollars a year in hidden
payments from insurers and
from charges on clients'
unsuspecting workers, New York
Attorney General Eliot Spitzer
charged yesterday." + ], + [ + "NORTEL Networks plans to slash
its workforce by 3500, or ten
per cent, as it struggles to
recover from an accounting
scandal that toppled three top
executives and led to a
criminal investigation and
lawsuits." + ], + [ + "Ebay Inc. (EBAY.O: Quote,
Profile, Research) said on
Friday it would buy Rent.com,
an Internet housing rental
listing service, for \\$415
million in a deal that gives
it access to a new segment of
the online real estate market." + ], + [ + "Austin police are working with
overseas officials to bring
charges against an English man
for sexual assault of a child,
a second-degree felony." + ], + [ + "United Nations officials
report security breaches in
internally displaced people
and refugee camps in Sudan
#39;s embattled Darfur region
and neighboring Chad." + ], + [ + "Noranda Inc., Canada #39;s
biggest mining company, began
exclusive talks on a takeover
proposal from China Minmetals
Corp. that would lead to the
spinoff of Noranda #39;s
aluminum business to
shareholders." + ], + [ + "San Francisco
developer/publisher lands
coveted Paramount sci-fi
license, \\$6.5 million in
funding on same day. Although
it is less than two years old,
Perpetual Entertainment has
acquired one of the most
coveted sci-fi licenses on the
market." + ], + [ + "ST. LOUIS -- Mike Martz #39;s
week of anger was no empty
display. He saw the defending
NFC West champions slipping
and thought taking potshots at
his players might be his best
shot at turning things around." + ], + [ + "Google warned Thursday that
increased competition and the
maturing of the company would
result in an quot;inevitable
quot; slowing of its growth." + ], + [ + "By KELLY WIESE JEFFERSON
CITY, Mo. (AP) -- Missouri
will allow members of the
military stationed overseas to
return absentee ballots via
e-mail, raising concerns from
Internet security experts
about fraud and ballot
secrecy..." + ], + [ + "Avis Europe PLC has dumped a
new ERP system based on
software from PeopleSoft Inc.
before it was even rolled out,
citing substantial delays and
higher-than-expected costs." + ], + [ + " TOKYO (Reuters) - The Nikkei
average rose 0.55 percent by
midsession on Wednesday as
some techs including Advantest
Corp. gained ground after
Wall Street reacted positively
to results from Intel Corp.
released after the U.S. market
close." + ], + [ + "Yahoo #39;s (Quote, Chart)
public embrace of the RSS
content syndication format
took a major leap forward with
the release of a revamped My
Yahoo portal seeking to
introduce the technology to
mainstream consumers." + ], + [ + "KINGSTON, Jamaica - Hurricane
Ivan's deadly winds and
monstrous waves bore down on
Jamaica on Friday, threatening
a direct hit on its densely
populated capital after
ravaging Grenada and killing
at least 33 people. The
Jamaican government ordered
the evacuation of half a
million people from coastal
areas, where rains on Ivan's
outer edges were already
flooding roads..." + ], + [ + "North Korea has denounced as
quot;wicked terrorists quot;
the South Korean officials who
orchestrated last month #39;s
airlift to Seoul of 468 North
Korean defectors." + ], + [ + "The Black Watch regiment has
returned to its base in Basra
in southern Iraq after a
month-long mission standing in
for US troops in a more
violent part of the country,
the Ministry of Defence says." + ], + [ + "Romanian soccer star Adrian
Mutu as he arrives at the
British Football Association
in London, ahead of his
disciplinary hearing, Thursday
Nov. 4, 2004." + ], + [ + "Australia completed an
emphatic Test series sweep
over New Zealand with a
213-run win Tuesday, prompting
a caution from Black Caps
skipper Stephen Fleming for
other cricket captains around
the globe." + ], + [ + "AP - A senior Congolese
official said Tuesday his
nation had been invaded by
neighboring Rwanda, and U.N.
officials said they were
investigating claims of
Rwandan forces clashing with
militias in the east." + ], + [ + "Liverpool, England (Sports
Network) - Former English
international and Liverpool
great Emlyn Hughes passed away
Tuesday from a brain tumor." + ], + [ + " ATLANTA (Reuters) - Soft
drink giant Coca-Cola Co.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=KO.N target=/stocks/quic
kinfo/fullquote\">KO.N</A
>, stung by a prolonged
downturn in North America,
Germany and other major
markets, on Thursday lowered
its key long-term earnings
and sales targets." + ], + [ + "JACKSONVILLE, Fla. -- They
were singing in the Colts #39;
locker room today, singing
like a bunch of wounded
songbirds. Never mind that
Marcus Pollard, Dallas Clark
and Ben Hartsock won #39;t be
recording a remake of Kenny
Chesney #39;s song,
quot;Young, quot; any time
soon." + ], + [ + "TOKYO (AP) -- Japanese
electronics and entertainment
giant Sony Corp. (SNE) plans
to begin selling a camcorder
designed for consumers that
takes video at digital high-
definition quality and is
being priced at about
\\$3,600..." + ], + [ + "As the close-knit NASCAR
community mourns the loss of
team owner Rick Hendrick #39;s
son, brother, twin nieces and
six others in a plane crash
Sunday, perhaps no one outside
of the immediate family
grieves more deeply than
Darrell Waltrip." + ], + [ + "AP - Purdue quarterback Kyle
Orton has no trouble
remembering how he felt after
last year's game at Michigan." + ], + [ + "UNITED NATIONS - The United
Nations #39; nuclear agency
says it is concerned about the
disappearance of equipment and
materials from Iraq that could
be used to make nuclear
weapons." + ], + [ + " BRUSSELS (Reuters) - The EU's
historic deal with Turkey to
open entry talks with the vast
Muslim country was hailed by
supporters as a bridge builder
between Europe and the Islamic
world." + ], + [ + "Iraqi President Ghazi al-
Yawar, who was due in Paris on
Sunday to start a European
tour, has postponed his visit
to France due to the ongoing
hostage drama involving two
French journalists, Arab
diplomats said Friday." + ], + [ + " SAO PAULO, Brazil (Reuters) -
President Luiz Inacio Lula da
Silva's Workers' Party (PT)
won the mayoralty of six state
capitals in Sunday's municipal
vote but was forced into a
run-off to defend its hold on
the race's biggest prize, the
city of Sao Paulo." + ], + [ + "ATHENS, Greece - They are
America's newest golden girls
- powerful and just a shade
from perfection. The U.S..." + ], + [ + "AMMAN, Sept. 15. - The owner
of a Jordanian truck company
announced today that he had
ordered its Iraq operations
stopped in a bid to save the
life of a driver held hostage
by a militant group." + ], + [ + "AP - U.S. State Department
officials learned that seven
American children had been
abandoned at a Nigerian
orphanage but waited more than
a week to check on the youths,
who were suffering from
malnutrition, malaria and
typhoid, a newspaper reported
Saturday." + ], + [ + "\\Angry mobs in Ivory Coast's
main city, Abidjan, marched on
the airport, hours after it
came under French control." + ], + [ + "Several workers are believed
to have been killed and others
injured after a contruction
site collapsed at Dubai
airport. The workers were
trapped under rubble at the
site of a \\$4." + ], + [ + "Talks between Sudan #39;s
government and two rebel
groups to resolve the nearly
two-year battle resume Friday.
By Abraham McLaughlin Staff
writer of The Christian
Science Monitor." + ], + [ + "In a meeting of the cinematic
with the scientific, Hollywood
helicopter stunt pilots will
try to snatch a returning NASA
space probe out of the air
before it hits the ground." + ], + [ + "Legend has it (incorrectly, it
seems) that infamous bank
robber Willie Sutton, when
asked why banks were his
favorite target, responded,
quot;Because that #39;s where
the money is." + ], + [ + "Brown is a second year player
from Memphis and has spent the
2004 season on the Steelers
#39; practice squad. He played
in two games last year." + ], + [ + "A Canadian court approved Air
Canada #39;s (AC.TO: Quote,
Profile, Research) plan of
arrangement with creditors on
Monday, clearing the way for
the world #39;s 11th largest
airline to emerge from
bankruptcy protection at the
end of next month" + ], + [ + "Pfizer, GlaxoSmithKline and
Purdue Pharma are the first
drugmakers willing to take the
plunge and use radio frequency
identification technology to
protect their US drug supply
chains from counterfeiters." + ], + [ + "Barret Jackman, the last of
the Blues left to sign before
the league #39;s probable
lockout on Wednesday,
finalized a deal Monday that
is rare in the current
economic climate but fitting
for him." + ], + [ + " LONDON (Reuters) - Oil prices
eased on Monday after rebels
in Nigeria withdrew a threat
to target oil operations, but
lingering concerns over
stretched supplies ahead of
winter kept prices close to
\\$50." + ], + [ + "AP - In the tumult of the
visitors' clubhouse at Yankee
Stadium, champagne pouring all
around him, Theo Epstein held
a beer. \"I came in and there
was no champagne left,\" he
said this week. \"I said, 'I'll
have champagne if we win it
all.'\" Get ready to pour a
glass of bubbly for Epstein.
No I.D. necessary." + ], + [ + "Search any fee-based digital
music service for the best-
loved musical artists of the
20th century and most of the
expected names show up." + ], + [ + "Barcelona held on from an
early Deco goal to edge game
local rivals Espanyol 1-0 and
carve out a five point
tabletop cushion. Earlier,
Ronaldo rescued a point for
Real Madrid, who continued
their middling form with a 1-1
draw at Real Betis." + ], + [ + "MONTREAL (CP) - The Expos may
be history, but their demise
has heated up the market for
team memorabilia. Vintage
1970s and 1980s shirts are
already sold out, but
everything from caps, beer
glasses and key-chains to
dolls of mascot Youppi!" + ], + [ + "Stansted airport is the
designated emergency landing
ground for planes in British
airspace hit by in-flight
security alerts. Emergency
services at Stansted have
successfully dealt" + ], + [ + "The massive military operation
to retake Fallujah has been
quot;accomplished quot;, a
senior Iraqi official said.
Fierce fighting continued in
the war-torn city where
pockets of resistance were
still holding out against US
forces." + ], + [ + "There are some signs of
progress in resolving the
Nigerian conflict that is
riling global oil markets. The
leader of militia fighters
threatening to widen a battle
for control of Nigeria #39;s
oil-rich south has" + ], + [ + "A strong earthquake hit Taiwan
on Monday, shaking buildings
in the capital Taipei for
several seconds. No casualties
were reported." + ], + [ + "America Online Inc. is
packaging new features to
combat viruses, spam and
spyware in response to growing
online security threats.
Subscribers will be able to
get the free tools" + ], + [ + "A 76th minute goal from
European Footballer of the
Year Pavel Nedved gave
Juventus a 1-0 win over Bayern
Munich on Tuesday handing the
Italians clear control at the
top of Champions League Group
C." + ], + [ + " LONDON (Reuters) - Oil prices
climbed above \\$42 a barrel on
Wednesday, rising for the
third day in a row as cold
weather gripped the U.S.
Northeast, the world's biggest
heating fuel market." + ], + [ + "A policeman ran amok at a
security camp in Indian-
controlled Kashmir after an
argument and shot dead seven
colleagues before he was
gunned down, police said on
Sunday." + ], + [ + "ANN ARBOR, Mich. -- Some NHL
players who took part in a
charity hockey game at the
University of Michigan on
Thursday were hopeful the news
that the NHL and the players
association will resume talks
next week" + ], + [ + "New York police have developed
a pre-emptive strike policy,
cutting off demonstrations
before they grow large." + ], + [ + "CAPE CANAVERAL-- NASA aims to
launch its first post-Columbia
shuttle mission during a
shortened nine-day window
March, and failure to do so
likely would delay a planned
return to flight until at
least May." + ], + [ + "Travelers headed home for
Thanksgiving were greeted
Wednesday with snow-covered
highways in the Midwest, heavy
rain and tornadoes in parts of
the South, and long security
lines at some of the nation
#39;s airports." + ], + [ + "BOULDER, Colo. -- Vernand
Morency ran for 165 yards and
two touchdowns and Donovan
Woods threw for three more
scores, lifting No. 22
Oklahoma State to a 42-14
victory over Colorado
yesterday." + ], + [ + "The Chinese city of Beijing
has cancelled an order for
Microsoft software, apparently
bowing to protectionist
sentiment. The deal has come
under fire in China, which is
trying to build a domestic
software industry." + ], + [ + "Apple says it will deliver its
iTunes music service to more
European countries next month.
Corroborating several reports
in recent months, Reuters is
reporting today that Apple
Computer is planning the next" + ], + [ + "Reuters - Motorola Inc., the
world's\\second-largest mobile
phone maker, said on Tuesday
it expects\\to sustain strong
sales growth in the second
half of 2004\\thanks to new
handsets with innovative
designs and features." + ], + [ + "PULLMAN - Last week, in
studying USC game film, Cougar
coaches thought they found a
chink in the national
champions armor. And not just
any chink - one with the
potential, right from the get
go, to" + ], + [ + "The union representing flight
attendants on Friday said it
mailed more than 5,000 strike
authorization ballots to its
members employed by US Airways
as both sides continued talks
that are expected to stretch
through the weekend." + ], + [ + "AP - Matt Leinart was quite a
baseball prospect growing up,
showing so much promise as a
left-handed pitcher that
scouts took notice before high
school." + ], + [ + "PRAGUE, Czech Republic --
Eugene Cernan, the last man to
walk on the moon during the
final Apollo landing, said
Thursday he doesn't expect
space tourism to become
reality in the near future,
despite a strong demand.
Cernan, now 70, who was
commander of NASA's Apollo 17
mission and set foot on the
lunar surface in December 1972
during his third space flight,
acknowledged that \"there are
many people interested in
space tourism.\" But the
former astronaut said he
believed \"we are a long way
away from the day when we can
send a bus of tourists to the
moon.\" He spoke to reporters
before being awarded a medal
by the Czech Academy of
Sciences for his contribution
to science..." + ], + [ + "Never shy about entering a
market late, Microsoft Corp.
is planning to open the
virtual doors of its long-
planned Internet music store
next week. <FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</B></FONT>" + ], + [ + "AP - On his first birthday
Thursday, giant panda cub Mei
Sheng delighted visitors by
playing for the first time in
snow delivered to him at the
San Diego Zoo. He also sat on
his ice cake, wrestled with
his mom, got his coat
incredibly dirty, and didn't
read any of the more than 700
birthday wishes sent him via
e-mail from as far away as
Ireland and Argentina." + ], + [ + "AP - Researchers put a
satellite tracking device on a
15-foot shark that appeared to
be lost in shallow water off
Cape Cod, the first time a
great white has been tagged
that way in the Atlantic." + ], + [ + "LSU will stick with a two-
quarterback rotation Saturday
at Auburn, according to Tigers
coach Nick Saban, who seemed
to have some fun telling the
media what he will and won
#39;t discuss Monday." + ], + [ + "Bulgaria has started its first
co-mission with the EU in
Bosnia and Herzegovina, along
with some 30 countries,
including Canada and Turkey." + ], + [ + "The Windows Future Storage
(WinFS) technology that got
cut out of Windows
quot;Longhorn quot; is in
serious trouble, and not just
the hot water a feature might
encounter for missing its
intended production vehicle." + ], + [ + "Seattle -- - Not so long ago,
the 49ers were inflicting on
other teams the kind of pain
and embarrassment they felt in
their 34-0 loss to the
Seahawks on Sunday." + ], + [ + "AP - The pileup of events in
the city next week, including
the Republican National
Convention, will add to the
security challenge for the New
York Police Department, but
commissioner Ray Kelly says,
\"With a big, experienced
police force, we can do it.\"" + ], + [ + "washingtonpost.com - Let the
games begin. Not the Olympics
again, but the all-out battle
between video game giants Sony
Corp. and Nintendo Co. Ltd.
The two Japanese companies are
rolling out new gaming
consoles, but Nintendo has
beaten Sony to the punch by
announcing an earlier launch
date for its new hand-held
game player." + ], + [ + "London - Manchester City held
fierce crosstown rivals
Manchester United to a 0-0
draw on Sunday, keeping the
Red Devils eleven points
behind leaders Chelsea." + ], + [ + "LONDON, Dec 11 (IranMania) -
Iraqi Vice-President Ibrahim
al-Jaafari refused to believe
in remarks published Friday
that Iran was attempting to
influence Iraqi polls with the
aim of creating a
quot;crescent quot; dominated
by Shiites in the region." + ], + [ + "LOS ANGELES (CBS.MW) - The US
Securities and Exchange
Commission is probing
transactions between Delphi
Corp and EDS, which supplies
the automotive parts and
components giant with
technology services, Delphi
said late Wednesday." + ], + [ + "MONTREAL (CP) - Molson Inc.
and Adolph Coors Co. are
sweetening their brewery
merger plan with a special
dividend to Molson
shareholders worth \\$381
million." + ], + [ + "AP - Echoing what NASA
officials said a day earlier,
a Russian space official on
Friday said the two-man crew
on the international space
station could be forced to
return to Earth if a planned
resupply flight cannot reach
them with food supplies later
this month." + ], + [ + "InfoWorld - SANTA CLARA,
CALIF. -- Accommodating large
patch sets in Linux is
expected to mean forking off
of the 2.7 version of the
platform to accommodate these
changes, according to Andrew
Morton, lead maintainer of the
Linux kernel for Open Source
Development Labs (OSDL)." + ], + [ + "AMSTERDAM The mobile phone
giants Vodafone and Nokia
teamed up on Thursday to
simplify cellphone software
written with the Java computer
language." + ], + [ + "WELLINGTON: National carrier
Air New Zealand said yesterday
the Australian Competition
Tribunal has approved a
proposed alliance with Qantas
Airways Ltd, despite its
rejection in New Zealand." + ], + [ + "The late Princess Dianas
former bodyguard, Ken Wharfe,
dismisses her suspicions that
one of her lovers was bumped
off. Princess Diana had an
affair with Barry Mannakee, a
policeman who was assigned to
protect her." + ], + [ + "Long considered beyond the
reach of mainland mores, the
Florida city is trying to
limit blatant displays of
sexual behavior." + ], + [ + "The overall Linux market is
far larger than previous
estimates show, a new study
says. In an analysis of the
Linux market released late
Tuesday, market research firm
IDC estimated that the Linux
market -- including" + ], + [ + "By PAUL ELIAS SAN FRANCISCO
(AP) -- Several California
cities and counties, including
San Francisco and Los Angeles,
sued Microsoft Corp. (MSFT) on
Friday, accusing the software
giant of illegally charging
inflated prices for its
products because of monopoly
control of the personal
computer operating system
market..." + ], + [ + "New Ole Miss head coach Ed
Orgeron, speaking for the
first time since his hiring,
made clear the goal of his
football program. quot;The
goal of this program will be
to go to the Sugar Bowl, quot;
Orgeron said." + ], + [ + "\"Everyone's nervous,\" Acting
Undersecretary of Defense
Michael W. Wynne warned in a
confidential e-mail to Air
Force Secretary James G. Roche
on July 8, 2003." + ], + [ + "Reuters - Alpharma Inc. on
Friday began\\selling a cheaper
generic version of Pfizer
Inc.'s #36;3\\billion a year
epilepsy drug Neurontin
without waiting for a\\court
ruling on Pfizer's request to
block the copycat medicine." + ], + [ + "Public opinion of the database
giant sinks to 12-year low, a
new report indicates." + ], + [ + "Opinion: Privacy hysterics
bring old whine in new bottles
to the Internet party. The
desktop search beta from this
Web search leader doesn #39;t
do anything you can #39;t do
already." + ], + [ + "It is much too easy to call
Pedro Martinez the selfish
one, to say he is walking out
on the Red Sox, his baseball
family, for the extra year of
the Mets #39; crazy money." + ], + [ + "It is impossible for young
tennis players today to know
what it was like to be Althea
Gibson and not to be able to
quot;walk in the front door,
quot; Garrison said." + ], + [ + "Senator John Kerry said today
that the war in Iraq was a
\"profound diversion\" from the
war on terror and Osama bin
Laden." + ], + [ + "For spammers, it #39;s been a
summer of love. Two newly
issued reports tracking the
circulation of unsolicited
e-mails say pornographic spam
dominated this summer, nearly
all of it originating from
Internet addresses in North
America." + ], + [ + "The Nordics fared well because
of their long-held ideals of
keeping corruption clamped
down and respect for
contracts, rule of law and
dedication to one-on-one
business relationships." + ], + [ + "Microsoft portrayed its
Longhorn decision as a
necessary winnowing to hit the
2006 timetable. The
announcement on Friday,
Microsoft executives insisted,
did not point to a setback in
software" + ], + [ + "Here #39;s an obvious word of
advice to Florida athletic
director Jeremy Foley as he
kicks off another search for
the Gators football coach: Get
Steve Spurrier on board." + ], + [ + "Don't bother with the small
stuff. Here's what really
matters to your lender." + ], + [ + "A problem in the Service Pack
2 update for Windows XP may
keep owners of AMD-based
computers from using the long-
awaited security package,
according to Microsoft." + ], + [ + "Five years ago, running a
telephone company was an
immensely profitable
proposition. Since then, those
profits have inexorably
declined, and now that decline
has taken another gut-
wrenching dip." + ], + [ + "NEW YORK - The litigious
Recording Industry Association
of America (RIAA) is involved
in another legal dispute with
a P-to-P (peer-to-peer)
technology maker, but this
time, the RIAA is on defense.
Altnet Inc. filed a lawsuit
Wednesday accusing the RIAA
and several of its partners of
infringing an Altnet patent
covering technology for
identifying requested files on
a P-to-P network." + ], + [ + "A group claiming to have
captured two Indonesian women
in Iraq has said it will
release them if Jakarta frees
Muslim cleric Abu Bakar Bashir
being held for alleged
terrorist links." + ], + [ + "Amid the stormy gloom in
Gotham, the rain-idled Yankees
last night had plenty of time
to gather in front of their
televisions and watch the Red
Sox Express roar toward them.
The national telecast might
have been enough to send a
jittery Boss Steinbrenner
searching his Bartlett's
Familiar Quotations for some
quot;Little Engine That Could
quot; metaphor." + ], + [ + "FULHAM fans would have been
singing the late Elvis #39;
hit #39;The wonder of you
#39; to their player Elvis
Hammond. If not for Frank
Lampard spoiling the party,
with his dedication to his
late grandfather." + ], + [ + "Indonesian police said
yesterday that DNA tests had
identified a suicide bomber
involved in a deadly attack
this month on the Australian
embassy in Jakarta." + ], + [ + "NEW YORK - Wal-Mart Stores
Inc.'s warning of
disappointing sales sent
stocks fluctuating Monday as
investors' concerns about a
slowing economy offset their
relief over a drop in oil
prices. October contracts
for a barrel of light crude
were quoted at \\$46.48, down
24 cents, on the New York
Mercantile Exchange..." + ], + [ + "Iraq #39;s top Shi #39;ite
cleric made a sudden return to
the country on Wednesday and
said he had a plan to end an
uprising in the quot;burning
city quot; of Najaf, where
fighting is creeping ever
closer to its holiest shrine." + ], + [ + "For two weeks before MTV
debuted U2 #39;s video for the
new single quot;Vertigo,
quot; fans had a chance to see
the band perform the song on
TV -- in an iPod commercial." + ], + [ + "A bird #39;s eye view of the
circuit at Shanghai shows what
an event Sunday #39;s Chinese
Grand Prix will be. The course
is arguably one of the best
there is, and so it should be
considering the amount of
money that has been spent on
it." + ], + [ + "KABUL, Afghanistan Aug. 22,
2004 - US soldiers sprayed a
pickup truck with bullets
after it failed to stop at a
roadblock in central
Afghanistan, killing two women
and a man and critically
wounding two other" + ], + [ + "Oct. 26, 2004 - The US-
European spacecraft Cassini-
Huygens on Tuesday made a
historic flyby of Titan,
Saturn #39;s largest moon,
passing so low as to almost
touch the fringes of its
atmosphere." + ], + [ + "Reuters - A volcano in central
Japan sent smoke and\\ash high
into the sky and spat out
molten rock as it erupted\\for
a fourth straight day on
Friday, but experts said the
peak\\appeared to be quieting
slightly." + ], + [ + "Shares of Google Inc. made
their market debut on Thursday
and quickly traded up 19
percent at \\$101.28. The Web
search company #39;s initial
public offering priced at \\$85" + ], + [ + "SYDNEY -- Prime Minister John
Howard of Australia, a key US
ally and supporter of the Iraq
war, celebrated his election
win over opposition Labor
after voters enjoying the
fruits of a strong economy
gave him another term." + ], + [ + "Reuters - Global warming is
melting\\Ecuador's cherished
mountain glaciers and could
cause several\\of them to
disappear over the next two
decades, Ecuadorean and\\French
scientists said on Wednesday." + ], + [ + "AP - Sirius Satellite Radio
signed a deal to air the men's
NCAA basketball tournament
through 2007, the latest move
made in an attempt to draw
customers through sports
programming." + ], + [ + "Rather than tell you, Dan
Kranzler chooses instead to
show you how he turned Mforma
into a worldwide publisher of
video games, ringtones and
other hot downloads for mobile
phones." + ], + [ + "UK interest rates have been
kept on hold at 4.75 following
the latest meeting of the Bank
of England #39;s rate-setting
committee." + ], + [ + "BAGHDAD, Iraq - Two rockets
hit a downtown Baghdad hotel
housing foreigners and
journalists Thursday, and
gunfire erupted in the
neighborhood across the Tigris
River from the U.S. Embassy
compound..." + ], + [ + "The Prevention of Terrorism
Act 2002 (Pota) polarised the
country, not just by the
manner in which it was pushed
through by the NDA government
through a joint session of
Parliament but by the shabby
and often biased manner in
which it was enforced." + ], + [ + "Not being part of a culture
with a highly developed
language, could limit your
thoughts, at least as far as
numbers are concerned, reveals
a new study conducted by a
psychologist at the Columbia
University in New York." + ], + [ + "CAMBRIDGE, Mass. A native of
Red Oak, Iowa, who was a
pioneer in astronomy who
proposed the quot;dirty
snowball quot; theory for the
substance of comets, has died." + ], + [ + "Resurgent oil prices paused
for breath as the United
States prepared to draw on its
emergency reserves to ease
supply strains caused by
Hurricane Ivan." + ], + [ + "(Sports Network) - The
inconsistent San Diego Padres
will try for consecutive wins
for the first time since
August 28-29 tonight, when
they begin a huge four-game
set against the Los Angeles
Dodgers at Dodger Stadium." + ], + [ + "A Portuguese-sounding version
of the virus has appeared in
the wild. Be wary of mail from
Manaus." + ], + [ + " NEW YORK (Reuters) - Top seed
Roger Federer survived a
stirring comeback from twice
champion Andre Agassi to reach
the semifinals of the U.S.
Open for the first time on
Thursday, squeezing through
6-3, 2-6, 7-5, 3-6, 6-3." + ], + [ + "President Bush, who credits
three years of tax relief
programs with helping
strengthen the slow economy,
said Saturday he would sign
into law the Working Families
Tax Relief Act to preserve tax
cuts." + ], + [ + "HEN investors consider the
bond market these days, the
low level of interest rates
should be more cause for worry
than for gratitude." + ], + [ + "Reuters - Hunters soon may be
able to sit at\\their computers
and blast away at animals on a
Texas ranch via\\the Internet,
a prospect that has state
wildlife officials up\\in arms." + ], + [ + "The Bedminster-based company
yesterday said it was pushing
into 21 new markets with the
service, AT amp;T CallVantage,
and extending an introductory
rate offer until Sept. 30. In
addition, the company is
offering in-home installation
of up to five ..." + ], + [ + "Samsung Electronics Co., Ltd.
has developed a new LCD
(liquid crystal display)
technology that builds a touch
screen into the display, a
development that could lead to
thinner and cheaper display
panels for mobile phones, the
company said Tuesday." + ], + [ + "The US military says marines
in Fallujah shot and killed an
insurgent who engaged them as
he was faking being dead, a
week after footage of a marine
killing an apparently unarmed
and wounded Iraqi caused a
stir in the region." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks rallied on Monday after
software maker PeopleSoft Inc.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=PSFT.O target=/stocks/qu
ickinfo/fullquote\">PSFT.O&l
t;/A> accepted a sweetened
\\$10.3 billion buyout by rival
Oracle Corp.'s <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=ORCL.O ta
rget=/stocks/quickinfo/fullquo
te\">ORCL.O</A> and
other big deals raised
expectations of more
takeovers." + ], + [ + "SAN FRANCISCO - What Babe Ruth
was to the first half of the
20th century and Hank Aaron
was to the second, Barry Bonds
has become for the home run
generation." + ], + [ + "Description: NPR #39;s Alex
Chadwick talks to Colin Brown,
deputy political editor for
the United Kingdom #39;s
Independent newspaper,
currently covering the British
Labour Party Conference." + ], + [ + "Birgit Fischer settled for
silver, leaving the 42-year-
old Olympian with two medals
in two days against decidedly
younger competition." + ], + [ + "The New Jersey-based Accoona
Corporation, an industry
pioneer in artificial
intelligence search
technology, announced on
Monday the launch of Accoona." + ], + [ + "Hamas vowed revenge yesterday
after an Israeli airstrike in
Gaza killed one of its senior
commanders - the latest
assassination to have weakened
the militant group." + ], + [ + "There was no mystery ... no
secret strategy ... no baited
trap that snapped shut and
changed the course of history
#39;s most lucrative non-
heavyweight fight." + ], + [ + "Notre Dame accepted an
invitation Sunday to play in
the Insight Bowl in Phoenix
against a Pac-10 team on Dec.
28. The Irish (6-5) accepted
the bid a day after losing to
Southern California" + ], + [ + "Greg Anderson has so dominated
Pro Stock this season that his
championship quest has evolved
into a pursuit of NHRA
history. By Bob Hesser, Racers
Edge Photography." + ], + [ + "Goldman Sachs Group Inc. on
Thursday said fourth-quarter
profit rose as its fixed-
income, currency and
commodities business soared
while a rebounding stock
market boosted investment
banking." + ], + [ + " NEW YORK (Reuters) - The
dollar rose on Monday in a
retracement from last week's
steep losses, but dealers said
the bias toward a weaker
greenback remained intact." + ], + [ + "Michael Powell, chairman of
the FCC, said Wednesday he was
disappointed with ABC for
airing a sexually suggestive
opening to \"Monday Night
Football.\"" + ], + [ + "The message against illegally
copying CDs for uses such as
in file-sharing over the
Internet has widely sunk in,
said the company in it #39;s
recent announcement to drop
the Copy-Control program." + ], + [ + "Former Washington football
coach Rick Neuheisel looked
forward to a return to
coaching Wednesday after being
cleared by the NCAA of
wrongdoing related to his
gambling on basketball games." + ], + [ + "Reuters - California will
become hotter and\\drier by the
end of the century, menacing
the valuable wine and\\dairy
industries, even if dramatic
steps are taken to curb\\global
warming, researchers said on
Monday." + ], + [ + "A senior member of the
Palestinian resistance group
Hamas has been released from
an Israeli prison after
completing a two-year
sentence." + ], + [ + "IBM said Monday that it won a
500 million (AUD\\$1.25
billion), seven-year services
contract to help move UK bank
Lloyds TBS from its
traditional voice
infrastructure to a converged
voice and data network." + ], + [ + "MPs have announced a new
inquiry into family courts and
whether parents are treated
fairly over issues such as
custody or contact with their
children." + ], + [ + "Canadian Press - MELBOURNE,
Australia (AP) - A 36-year-old
businesswoman was believed to
be the first woman to walk
around Australia on Friday
after striding into her
hometown of Melbourne to
complete her 16,700-kilometre
trek in 365 days." + ], + [ + "Most remaining Pakistani
prisoners held at the US
Guantanamo Bay prison camp are
freed, officials say." + ], + [ + "Space shuttle astronauts will
fly next year without the
ability to repair in orbit the
type of damage that destroyed
the Columbia vehicle in
February 2003." + ], + [ + "Moscow - Russia plans to
combine Gazprom, the world
#39;s biggest natural gas
producer, with state-owned oil
producer Rosneft, easing rules
for trading Gazprom shares and
creating a company that may
dominate the country #39;s
energy industry." + ], + [ + "AP - Rutgers basketball player
Shalicia Hurns was suspended
from the team after pleading
guilty to punching and tying
up her roommate during a
dispute over painkilling
drugs." + ], + [ + "By cutting WinFS from Longhorn
and indefinitely delaying the
storage system, Microsoft
Corp. has also again delayed
the Microsoft Business
Framework (MBF), a new Windows
programming layer that is
closely tied to WinFS." + ], + [ + "French police are
investigating an arson-caused
fire at a Jewish Social Center
that might have killed dozens
without the quick response of
firefighters." + ], + [ + "Rodney King, whose videotaped
beating led to riots in Los
Angeles in 1992, is out of
jail now and talking frankly
for the first time about the
riots, himself and the
American way of life." + ], + [ + "AFP - Radical Islamic cleric
Abu Hamza al-Masri was set to
learn Thursday whether he
would be charged under
Britain's anti-terrorism law,
thus delaying his possible
extradition to the United
States to face terrorism-
related charges." + ], + [ + "Diversified manufacturer
Honeywell International Inc.
(HON.N: Quote, Profile,
Research) posted a rise in
quarterly profit as strong
demand for aerospace equipment
and automobile components" + ], + [ + "This was the event Michael
Phelps didn't really need to
compete in if his goal was to
win eight golds. He probably
would have had a better chance
somewhere else." + ], + [ + "Samsung's new SPH-V5400 mobile
phone sports a built-in
1-inch, 1.5-gigabyte hard disk
that can store about 15 times
more data than conventional
handsets, Samsung said." + ], + [ + "Reuters - U.S. housing starts
jumped a\\larger-than-expected
6.4 percent in October to the
busiest pace\\since December as
buyers took advantage of low
mortgage rates,\\a government
report showed on Wednesday." + ], + [ + "Gabe Kapler became the first
player to leave the World
Series champion Boston Red
Sox, agreeing to a one-year
contract with the Yomiuri
Giants in Tokyo." + ], + [ + "Louisen Louis, 30, walked
Monday in the middle of a
street that resembled a small
river with brown rivulets and
waves. He wore sandals and had
a cut on one of his big toes." + ], + [ + "BALI, Indonesia - Svetlana
Kuznetsova, fresh off her
championship at the US Open,
defeated Australian qualifier
Samantha Stosur 6-4, 6-4
Thursday to reach the
quarterfinals of the Wismilak
International." + ], + [ + "The Securities and Exchange
Commission ordered mutual
funds to stop paying higher
commissions to brokers who
promote the companies' funds
and required portfolio
managers to reveal investments
in funds they supervise." + ], + [ + "A car bomb exploded outside
the main hospital in Chechny
#39;s capital, Grozny, on
Sunday, injuring 17 people in
an attack apparently targeting
members of a Chechen security
force bringing in wounded from
an earlier explosion" + ], + [ + "AP - Just like the old days in
Dallas, Emmitt Smith made life
miserable for the New York
Giants on Sunday." + ], + [ + "Sumitomo Mitsui Financial
Group (SMFG), Japans second
largest bank, today put
forward a 3,200 billion (\\$29
billion) takeover bid for
United Financial Group (UFJ),
the countrys fourth biggest
lender, in an effort to regain
initiative in its bidding" + ], + [ + "AP - Gay marriage is emerging
as a big enough issue in
several states to influence
races both for Congress and
the presidency." + ], + [ + "TAMPA, Fla. - Chris Simms
first NFL start lasted 19
plays, and it might be a while
before he plays again for the
Tampa Bay Buccaneers." + ], + [ + "Vendor says it #39;s
developing standards-based
servers in various form
factors for the telecom
market. By Darrell Dunn.
Hewlett-Packard on Thursday
unveiled plans to create a
portfolio of products and
services" + ], + [ + "Jarno Trulli made the most of
the conditions in qualifying
to claim pole ahead of Michael
Schumacher, while Fernando
finished third." + ], + [ + "More than 30 aid workers have
been airlifted to safety from
a town in Sudan #39;s troubled
Darfur region after fighting
broke out and their base was
bombed, a British charity
says." + ], + [ + " NEW YORK (Reuters) - U.S.
chain store retail sales
slipped during the
Thanksgiving holiday week, as
consumers took advantage of
discounted merchandise, a
retail report said on
Tuesday." + ], + [ + "Ziff Davis - The company this
week will unveil more programs
and technologies designed to
ease users of its high-end
servers onto its Integrity
line, which uses Intel's
64-bit Itanium processor." + ], + [ + "The Mac maker says it will
replace about 28,000 batteries
in one model of PowerBook G4
and tells people to stop using
the notebook." + ], + [ + "It #39;s the mildest of mild
winters down here in the south
of Italy and, last weekend at
Bcoli, a pretty suburb by the
seaside west of Naples, the
customers of Pizzeria quot;Da
Enrico quot; were making the
most of it." + ], + [ + "By George Chamberlin , Daily
Transcript Financial
Correspondent. Concerns about
oil production leading into
the winter months sent shivers
through the stock market
Wednesday." + ], + [ + "Airbus has withdrawn a filing
that gave support for
Microsoft in an antitrust case
before the European Union
#39;s Court of First Instance,
a source close to the
situation said on Friday." + ], + [ + "WASHINGTON - A spotty job
market and stagnant paychecks
cloud this Labor Day holiday
for many workers, highlighting
the importance of pocketbook
issues in the presidential
election. \"Working harder
and enjoying it less,\" said
economist Ken Mayland,
president of ClearView
Economics, summing up the
state of working America..." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks rose on Wednesday
lifted by a merger between
retailers Kmart and Sears,
better-than-expected earnings
from Hewlett-Packard and data
showing a slight rise in core
inflation." + ], + [ + "AP - Authorities are
investigating whether bettors
at New York's top thoroughbred
tracks were properly informed
when jockeys came in
overweight at races, a source
familiar with the probe told
The Associated Press." + ], + [ + "European Commission president
Romano Prodi has unveiled
proposals to loosen the
deficit rules under the EU
Stability Pact. The loosening
was drafted by monetary
affairs commissioner Joaquin
Almunia, who stood beside the
president at the announcement." + ], + [ + "Canadian Press - MONTREAL (CP)
- A 19-year-old man charged in
a firebombing at a Jewish
elementary school pleaded
guilty Thursday to arson." + ], + [ + "Retail sales in Britain saw
the fastest growth in
September since January,
casting doubts on the view
that the economy is slowing
down, according to official
figures released Thursday." + ], + [ + " NEW YORK (Reuters) -
Interstate Bakeries Corp.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=IBC.N target=/stocks/qui
ckinfo/fullquote\">IBC.N<
/A>, maker of Hostess
Twinkies and Wonder Bread,
filed for bankruptcy on
Wednesday after struggling
with more than \\$1.3 billion
in debt and high costs." + ], + [ + "Delta Air Lines (DAL.N: Quote,
Profile, Research) on Thursday
said it reached a deal with
FedEx Express to sell eight
McDonnell Douglas MD11
aircraft and four spare
engines for delivery in 2004." + ], + [ + "Michael Owen scored his first
goal for Real Madrid in a 1-0
home victory over Dynamo Kiev
in the Champions League. The
England striker toe-poked home
Ronaldo #39;s cross in the
35th minute to join the
Russians" + ], + [ + " quot;Resuming uranium
enrichment is not in our
agenda. We are still committed
to the suspension, quot;
Foreign Ministry spokesman
Hamid Reza." + ], + [ + "Leading OPEC producer Saudi
Arabia said on Monday in
Vienna, Austria, that it had
made a renewed effort to
deflate record high world oil
prices by upping crude output
again." + ], + [ + "The U.S. military presence in
Iraq will grow to 150,000
troops by next month, the
highest level since the
invasion last year." + ], + [ + "UPDATE, SUN 9PM: More than a
million people have left their
homes in Cuba, as Hurricane
Ivan approaches. The ferocious
storm is headed that way,
after ripping through the
Cayman Islands, tearing off
roofs, flooding homes and
causing general havoc." + ], + [ + "Jason Giambi has returned to
the New York Yankees'
clubhouse but is still
clueless as to when he will be
able to play again." + ], + [ + "Seventy-five National Hockey
League players met with union
leaders yesterday to get an
update on a lockout that shows
no sign of ending." + ], + [ + "Howard, at 6-4 overall and 3-3
in the Mid-Eastern Athletic
Conference, can clinch a
winning record in the MEAC
with a win over Delaware State
on Saturday." + ], + [ + "Millions of casual US anglers
are having are larger than
appreciated impact on sea fish
stocks, scientists claim." + ], + [ + "The founders of the Pilgrim
Baxter amp; Associates money-
management firm agreed
yesterday to personally fork
over \\$160 million to settle
charges they allowed a friend
to" + ], + [ + " ATHENS (Reuters) - Top-ranked
Argentina booked their berth
in the women's hockey semi-
finals at the Athens Olympics
on Friday but defending
champions Australia now face
an obstacle course to qualify
for the medal matches." + ], + [ + "IBM Corp. Tuesday announced
plans to acquire software
vendor Systemcorp ALG for an
undisclosed amount. Systemcorp
of Montreal makes project
portfolio management software
aimed at helping companies
better manage their IT
projects." + ], + [ + "The Notre Dame message boards
are no longer discussing
whether Tyrone Willingham
should be fired. Theyre
already arguing about whether
the next coach should be Barry
Alvarez or Steve Spurrier." + ], + [ + "Forbes.com - By now you
probably know that earnings of
Section 529 college savings
accounts are free of federal
tax if used for higher
education. But taxes are only
part of the problem. What if
your investments tank? Just
ask Laurence and Margo
Williams of Alexandria, Va. In
2000 they put #36;45,000 into
the Virginia Education Savings
Trust to open accounts for
daughters Lea, now 5, and
Anne, now 3. Since then their
investment has shrunk 5 while
the average private college
tuition has climbed 18 to
#36;18,300." + ], + [ + "German Chancellor Gerhard
Schroeder said Sunday that
there was quot;no problem
quot; with Germany #39;s
support to the start of
negotiations on Turkey #39;s
entrance into EU." + ], + [ + "Coca-Cola Amatil Ltd.,
Australia #39;s biggest soft-
drink maker, offered A\\$500
million (\\$382 million) in
cash and stock for fruit
canner SPC Ardmona Ltd." + ], + [ + "US technology shares tumbled
on Friday after technology
bellwether Intel Corp.
(INTC.O: Quote, Profile,
Research) slashed its revenue
forecast, but blue chips were
only moderately lower as drug
and industrial stocks made
solid gains." + ], + [ + " WASHINGTON (Reuters) - Final
U.S. government tests on an
animal suspected of having mad
cow disease were not yet
complete, the U.S. Agriculture
Department said, with no
announcement on the results
expected on Monday." + ], + [ + "MANILA Fernando Poe Jr., the
popular actor who challenged
President Gloria Macapagal
Arroyo in the presidential
elections this year, died
early Tuesday." + ], + [ + " THOMASTOWN, Ireland (Reuters)
- World number three Ernie
Els overcame difficult weather
conditions to fire a sparkling
eight-under-par 64 and move
two shots clear after two
rounds of the WGC-American
Express Championship Friday." + ], + [ + "Lamar Odom supplemented 20
points with 13 rebounds and
Kobe Bryant added 19 points to
overcome a big night from Yao
Ming as the Los Angeles Lakers
ground out an 84-79 win over
the Rockets in Houston
Saturday." + ], + [ + "AMSTERDAM, NETHERLANDS - A
Dutch filmmaker who outraged
members of the Muslim
community by making a film
critical of the mistreatment
of women in Islamic society
was gunned down and stabbed to
death Tuesday on an Amsterdam
street." + ], + [ + "Microsoft Xbox Live traffic on
service provider networks
quadrupled following the
November 9th launch of Halo-II
-- which set entertainment
industry records by selling
2.4-million units in the US
and Canada on the first day of
availability, driving cash" + ], + [ + "Lawyers in a California class
action suit against Microsoft
will get less than half the
payout they had hoped for. A
judge in San Francisco ruled
that the attorneys will
collect only \\$112." + ], + [ + "Google Browser May Become
Reality\\\\There has been much
fanfare in the Mozilla fan
camps about the possibility of
Google using Mozilla browser
technology to produce a
GBrowser - the Google Browser.
Over the past two weeks, the
news and speculation has
escalated to the point where
even Google itself is ..." + ], + [ + "Metro, Germany's biggest
retailer, turns in weaker-
than-expected profits as sales
at its core supermarkets
division dip lower." + ], + [ + "It rained Sunday, of course,
and but another soppy, sloppy
gray day at Westside Tennis
Club did nothing to deter
Roger Federer from his
appointed rounds." + ], + [ + "Hewlett-Packard has joined
with Brocade to integrate
Brocade #39;s storage area
network switching technology
into HP Bladesystem servers to
reduce the amount of fabric
infrastructure needed in a
datacentre." + ], + [ + "Zimbabwe #39;s most persecuted
white MP began a year of hard
labour last night after
parliament voted to jail him
for shoving the Justice
Minister during a debate over
land seizures." + ], + [ + "BOSTON (CBS.MW) -- First
Command has reached a \\$12
million settlement with
federal regulators for making
misleading statements and
omitting important information
when selling mutual funds to
US military personnel." + ], + [ + "A smashing blow is being dealt
to thousands of future
pensioners by a law that has
just been brought into force
by the Federal Government." + ], + [ + "The US Senate Commerce
Committee on Wednesday
approved a measure that would
provide up to \\$1 billion to
ensure consumers can still
watch television when
broadcasters switch to new,
crisp digital signals." + ], + [ + "Amelie Mauresmo was handed a
place in the Advanta
Championships final after
Maria Sharapova withdrew from
their semi-final because of
injury." + ], + [ + "AP - An Israeli helicopter
fired two missiles in Gaza
City after nightfall
Wednesday, one at a building
in the Zeitoun neighborhood,
witnesses said, setting a
fire." + ], + [ + "Reuters - Internet stocks
are\\as volatile as ever, with
growth-starved investors
flocking to\\the sector in the
hope they've bought shares in
the next online\\blue chip." + ], + [ + "The federal government, banks
and aircraft lenders are
putting the clamps on
airlines, particularly those
operating under bankruptcy
protection." + ], + [ + "EURO DISNEY, the financially
crippled French theme park
operator, has admitted that
its annual losses more than
doubled last financial year as
it was hit by a surge in
costs." + ], + [ + " EAST RUTHERFORD, New Jersey
(Sports Network) - Retired NBA
center and seven-time All-
Star Alonzo Mourning is going
to give his playing career
one more shot." + ], + [ + "NASA has released an inventory
of the scientific devices to
be put on board the Mars
Science Laboratory rover
scheduled to land on the
surface of Mars in 2009, NASAs
news release reads." + ], + [ + "The U.S. Congress needs to
invest more in the U.S.
education system and do more
to encourage broadband
adoption, the chief executive
of Cisco said Wednesday.<p&
gt;ADVERTISEMENT</p><
p><img src=\"http://ad.do
ubleclick.net/ad/idg.us.ifw.ge
neral/sbcspotrssfeed;sz=1x1;or
d=200301151450?\" width=\"1\"
height=\"1\"
border=\"0\"/><a href=\"htt
p://ad.doubleclick.net/clk;922
8975;9651165;a?http://www.info
world.com/spotlights/sbc/main.
html?lpid0103035400730000idlp\"
>SBC Case Study: Crate Ba
rrel</a><br/>What
sold them on improving their
network? A system that could
cut management costs from the
get-go. Find out
more.</p>" + ], + [ + "The Philippines put the toll
at more than 1,000 dead or
missing in four storms in two
weeks but, even with a break
in the weather on Saturday" + ], + [ + "Reuters - Four explosions were
reported at petrol\\stations in
the Madrid area on Friday,
Spanish radio stations\\said,
following a phone warning in
the name of the armed
Basque\\separatist group ETA to
a Basque newspaper." + ], + [ + "Thirty-two countries and
regions will participate the
Fifth China International
Aviation and Aerospace
Exhibition, opening Nov. 1 in
Zhuhai, a city in south China
#39;s Guangdong Province." + ], + [ + "Jordan have confirmed that
Timo Glock will replace
Giorgio Pantano for this
weekend #39;s Chinese GP as
the team has terminated its
contract with Pantano." + ], + [ + "WEST PALM BEACH, Fla. -
Hurricane Jeanne got stronger,
bigger and faster as it
battered the Bahamas and bore
down on Florida Saturday,
sending huge waves crashing
onto beaches and forcing
thousands into shelters just
weeks after Frances ravaged
this area..." + ], + [ + "p2pnet.net News:- Virgin
Electronics has joined the mp3
race with a \\$250, five gig
player which also handles
Microsoft #39;s WMA format." + ], + [ + "WASHINGTON The idea of a no-
bid contract for maintaining
airport security equipment has
turned into a non-starter for
the Transportation Security
Administration." + ], + [ + "Eyetech (EYET:Nasdaq - news -
research) did not open for
trading Friday because a Food
and Drug Administration
advisory committee is meeting
to review the small New York-
based biotech #39;s
experimental eye disease drug." + ], + [ + "The continuing heartache of
Wake Forest #39;s ACC football
season was best described by
fifth-ranked Florida State
coach Bobby Bowden, after his
Seminoles had edged the
Deacons 20-17 Saturday at
Groves Stadium." + ], + [ + "On September 13, 2001, most
Americans were still reeling
from the shock of the
terrorist attacks on New York
and the Pentagon two days
before." + ], + [ + "Microsoft has suspended the
beta testing of the next
version of its MSN Messenger
client because of a potential
security problem, a company
spokeswoman said Wednesday." + ], + [ + "AP - Business software maker
Oracle Corp. attacked the
credibility and motives of
PeopleSoft Inc.'s board of
directors Monday, hoping to
rally investor support as the
17-month takeover battle
between the bitter business
software rivals nears a
climactic showdown." + ], + [ + "NEW YORK - Elena Dementieva
shook off a subpar serve that
produced 15 double-faults, an
aching left thigh and an upset
stomach to advance to the
semifinals at the U.S. Open
with a 4-6, 6-4, 7-6 (1)
victory Tuesday over Amelie
Mauresmo..." + ], + [ + "THE glory days have returned
to White Hart Lane. When Spurs
new first-team coach Martin
Jol promised a return to the
traditions of the 1960s,
nobody could have believed he
was so determined to act so
quickly and so literally." + ], + [ + "A new worm has been discovered
in the wild that #39;s not
just settling for invading
users #39; PCs--it wants to
invade their homes too." + ], + [ + "Domestic air travelers could
be surfing the Web by 2006
with government-approved
technology that allows people
access to high-speed Internet
connections while they fly." + ], + [ + "GENEVA: Cross-border
investment is set to bounce in
2004 after three years of deep
decline, reflecting a stronger
world economy and more
international merger activity,
the United Nations (UN) said
overnight." + ], + [ + "Researchers have for the first
time established the existence
of odd-parity superconductors,
materials that can carry
electric current without any
resistance." + ], + [ + "Chewing gum giant Wm. Wrigley
Jr. Co. on Thursday said it
plans to phase out production
of its Eclipse breath strips
at a plant in Phoenix, Arizona
and shift manufacturing to
Poznan, Poland." + ], + [ + "Prime Minister Dr Manmohan
Singh inaugurated a research
centre in the Capital on
Thursday to mark 400 years of
compilation of Sikh holy book
the Guru Granth Sahib." + ], + [ + "com September 16, 2004, 7:58
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." + ], + [ + "BRUSSELS, Belgium (AP) --
European antitrust regulators
said Monday they have extended
their review of a deal between
Microsoft Corp. (MSFT) and
Time Warner Inc..." + ], + [ + "AP - When Paula Radcliffe
dropped out of the Olympic
marathon miles from the
finish, she sobbed
uncontrollably. Margaret Okayo
knew the feeling. Okayo pulled
out of the marathon at the
15th mile with a left leg
injury, and she cried, too.
When she watched Radcliffe
quit, Okayo thought, \"Let's
cry together.\"" + ], + [ + "Tightness in the labour market
notwithstanding, the prospects
for hiring in the third
quarter are down from the
second quarter, according to
the new Manpower Employment
Outlook Survey." + ], + [ + "Fans who can't get enough of
\"The Apprentice\" can visit a
new companion Web site each
week and watch an extra 40
minutes of video not broadcast
on the Thursday
show.<br><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</b></font>" + ], + [ + " ATLANTA (Sports Network) -
The Atlanta Hawks signed free
agent Kevin Willis on
Wednesday, nearly a decade
after the veteran big man
ended an 11- year stint with
the team." + ], + [ + "An adult Web site publisher is
suing Google, saying the
search engine company made it
easier for users to see the
site #39;s copyrighted nude
photographs without paying or
gaining access through the
proper channels." + ], + [ + "When his right-front tire went
flying off early in the Ford
400, the final race of the
NASCAR Nextel Cup Series
season, Kurt Busch, it seemed,
was destined to spend his
offseason" + ], + [ + "A Washington-based public
opinion firm has released the
results of an election day
survey of Nevada voters
showing 81 support for the
issuance of paper receipts
when votes are cast
electronically." + ], + [ + "NAPSTER creator SHAWN FANNING
has revealed his plans for a
new licensed file-sharing
service with an almost
unlimited selection of tracks." + ], + [ + " NEW YORK (Reuters) - The
dollar rose on Friday, after a
U.S. report showed consumer
prices in line with
expections, reminding
investors that the Federal
Reserve was likely to
continue raising interest
rates, analysts said." + ], + [ + "Brandon Backe and Woody
Williams pitched well last
night even though neither
earned a win. But their
outings will show up in the
record books." + ], + [ + "President George W. Bush
pledged Friday to spend some
of the political capital from
his re-election trying to
secure a lasting Middle East
peace, and he envisioned the
establishment" + ], + [ + "The Football Association today
decided not to charge David
Beckham with bringing the game
into disrepute. The FA made
the surprise announcement
after their compliance unit
ruled" + ], + [ + "Last year some election
watchers made a bold
prediction that this
presidential election would
set a record: the first half
billion dollar campaign in
hard money alone." + ], + [ + "It #39;s one more blow to
patients who suffer from
arthritis. Pfizer, the maker
of Celebrex, says it #39;s
painkiller poses an increased
risk of heart attacks to
patients using the drugs." + ], + [ + "NEW DELHI - A bomb exploded
during an Independence Day
parade in India's remote
northeast on Sunday, killing
at least 15 people, officials
said, just an hour after Prime
Minister Manmohan Singh
pledged to fight terrorism.
The outlawed United Liberation
Front of Asom was suspected of
being behind the attack in
Assam state and a second one
later in the area, said Assam
Inspector General of Police
Khagen Sharma..." + ], + [ + "Two separate studies by U.S.
researchers find that super
drug-resistant strains of
tuberculosis are at the
tipping point of a global
epidemic, and only small
changes could help them spread
quickly." + ], + [ + " CRANS-SUR-SIERRE, Switzerland
(Reuters) - World number
three Ernie Els says he feels
a failure after narrowly
missing out on three of the
year's four major
championships." + ], + [ + "A UN envoy to Sudan will visit
Darfur tomorrow to check on
the government #39;s claim
that some 70,000 people
displaced by conflict there
have voluntarily returned to
their homes, a spokesman said." + ], + [ + "Dell cut prices on some
servers and PCs by as much as
22 percent because it #39;s
paying less for parts. The
company will pass the savings
on components such as memory
and liquid crystal displays" + ], + [ + "AP - Most of the presidential
election provisional ballots
rejected so far in Ohio came
from people who were not even
registered to vote, election
officials said after spending
nearly two weeks poring over
thousands of disputed votes." + ], + [ + "Striker Bonaventure Kalou
netted twice to send AJ
Auxerre through to the first
knockout round of the UEFA Cup
at the expense of Rangers on
Wednesday." + ], + [ + "AP - Rival inmates fought each
other with knives and sticks
Wednesday at a San Salvador
prison, leaving at least 31
people dead and two dozen
injured, officials said." + ], + [ + "WASHINGTON: The European-
American Cassini-Huygens space
probe has detected traces of
ice flowing on the surface of
Saturn #39;s largest moon,
Titan, suggesting the
existence of an ice volcano,
NASA said Tuesday." + ], + [ + "The economic growth rate in
the July-September period was
revised slightly downward from
an already weak preliminary
report, the government said
Wednesday." + ], + [ + "All ISS systems continue to
function nominally, except
those noted previously or
below. Day 7 of joint
Exp.9/Exp.10 operations and
last full day before 8S
undocking." + ], + [ + "BAGHDAD - Two Egyptian
employees of a mobile phone
company were seized when
gunmen stormed into their
Baghdad office, the latest in
a series of kidnappings in the
country." + ], + [ + "BRISBANE, Australia - The body
of a whale resembling a giant
dolphin that washed up on an
eastern Australian beach has
intrigued local scientists,
who agreed Wednesday that it
is rare but are not sure just
how rare." + ], + [ + " quot;Magic can happen. quot;
Sirius Satellite Radio
(nasdaq: SIRI - news - people
) may have signed Howard Stern
and the men #39;s NCAA
basketball tournaments, but XM
Satellite Radio (nasdaq: XMSR
- news - people ) has its
sights on your cell phone." + ], + [ + "Trick-or-treaters can expect
an early Halloween treat on
Wednesday night, when a total
lunar eclipse makes the moon
look like a glowing pumpkin." + ], + [ + "THIS weekend sees the
quot;other quot; showdown
between New York and New
England as the Jets and
Patriots clash in a battle of
the unbeaten teams." + ], + [ + "Sifting through millions of
documents to locate a valuable
few is tedious enough, but
what happens when those files
are scattered across different
repositories?" + ], + [ + "President Bush aims to
highlight American drug-
fighting aid in Colombia and
boost a conservative Latin
American leader with a stop in
the Andean nation where
thousands of security forces
are deployed to safeguard his
brief stay." + ], + [ + "Dubai - Former Palestinian
security minister Mohammed
Dahlan said on Monday that a
quot;gang of mercenaries quot;
known to the Palestinian
police were behind the
shooting that resulted in two
deaths in a mourning tent for
Yasser Arafat in Gaza." + ], + [ + "A drug company executive who
spoke out in support of
Montgomery County's proposal
to import drugs from Canada
and similar legislation before
Congress said that his company
has launched an investigation
into his political activities." + ], + [ + "Nicolas Anelka is fit for
Manchester City #39;s
Premiership encounter against
Tottenham at Eastlands, but
the 13million striker will
have to be content with a
place on the bench." + ], + [ + " PITTSBURGH (Reuters) - Ben
Roethlisberger passed for 183
yards and two touchdowns,
Hines Ward scored twice and
the Pittsburgh Steelers
rolled to a convincing 27-3
victory over Philadelphia on
Sunday for their second
straight win against an
undefeated opponent." + ], + [ + " ATHENS (Reuters) - The U.S.
men's basketball team got
their first comfortable win
at the Olympic basketball
tournament Monday, routing
winless Angola 89-53 in their
final preliminary round game." + ], + [ + "A Frenchman working for Thales
SA, Europe #39;s biggest maker
of military electronics, was
shot dead while driving home
at night in the Saudi Arabian
city of Jeddah." + ], + [ + "Often, the older a pitcher
becomes, the less effective he
is on the mound. Roger Clemens
apparently didn #39;t get that
memo. On Tuesday, the 42-year-
old Clemens won an
unprecedented" + ], + [ + "NASA #39;s Mars rovers have
uncovered more tantalizing
evidence of a watery past on
the Red Planet, scientists
said Wednesday. And the
rovers, Spirit and
Opportunity, are continuing to
do their jobs months after
they were expected to ..." + ], + [ + "SYDNEY (AFP) - Australia #39;s
commodity exports are forecast
to increase by 15 percent to a
record 95 billion dollars (71
million US), the government
#39;s key economic forecaster
said." + ], + [ + "Google won a major legal
victory when a federal judge
ruled that the search engines
advertising policy does not
violate federal trademark
laws." + ], + [ + "Intel Chief Technology Officer
Pat Gelsinger said on
Thursday, Sept. 9, that the
Internet needed to be upgraded
in order to deal with problems
that will become real issues
soon." + ], + [ + "China will take tough measures
this winter to improve the
country #39;s coal mine safety
and prevent accidents. State
Councilor Hua Jianmin said
Thursday the industry should
take" + ], + [ + "AT amp;T Corp. on Thursday
said it is reducing one fifth
of its workforce this year and
will record a non-cash charge
of approximately \\$11." + ], + [ + "BAGHDAD (Iraq): As the
intensity of skirmishes
swelled on the soils of Iraq,
dozens of people were put to
death with toxic shots by the
US helicopter gunship, which
targeted the civilians,
milling around a burning
American vehicle in a Baghdad
street on" + ], + [ + " LONDON (Reuters) - Television
junkies of the world, get
ready for \"Friends,\" \"Big
Brother\" and \"The Simpsons\" to
phone home." + ], + [ + "A rift appeared within Canada
#39;s music industry yesterday
as prominent artists called on
the CRTC to embrace satellite
radio and the industry warned
of lost revenue and job
losses." + ], + [ + "Reuters - A key Iranian
nuclear facility which
the\\U.N.'s nuclear watchdog
has urged Tehran to shut down
is\\nearing completion, a
senior Iranian nuclear
official said on\\Sunday." + ], + [ + "Spain's Football Federation
launches an investigation into
racist comments made by
national coach Luis Aragones." + ], + [ + "Bricks and plaster blew inward
from the wall, as the windows
all shattered and I fell to
the floorwhether from the
shock wave, or just fright, it
wasn #39;t clear." + ], + [ + "Surfersvillage Global Surf
News, 13 September 2004: - -
Hurricane Ivan, one of the
most powerful storms to ever
hit the Caribbean, killed at
least 16 people in Jamaica,
where it wrecked houses and
washed away roads on Saturday,
but appears to have spared" + ], + [ + "I #39;M FEELING a little bit
better about the hundreds of
junk e-mails I get every day
now that I #39;ve read that
someone else has much bigger
e-mail troubles." + ], + [ + "NEW DELHI: India and Pakistan
agreed on Monday to step up
cooperation in the energy
sector, which could lead to
Pakistan importing large
amounts of diesel fuel from
its neighbour, according to
Pakistani Foreign Minister
Khurshid Mehmood Kasuri." + ], + [ + "LONDON, England -- A US
scientist is reported to have
observed a surprising jump in
the amount of carbon dioxide,
the main greenhouse gas." + ], + [ + "Microsoft's antispam Sender ID
technology continues to get
the cold shoulder. Now AOL
adds its voice to a growing
chorus of businesses and
organizations shunning the
proprietary e-mail
authentication system." + ], + [ + "PSV Eindhoven faces Arsenal at
Highbury tomorrow night on the
back of a free-scoring start
to the season. Despite losing
Mateja Kezman to Chelsea in
the summer, the Dutch side has
scored 12 goals in the first" + ], + [ + "Through the World Community
Grid, your computer could help
address the world's health and
social problems." + ], + [ + "Zimbabwe #39;s ruling Zanu-PF
old guard has emerged on top
after a bitter power struggle
in the deeply divided party
during its five-yearly
congress, which ended
yesterday." + ], + [ + "PLAYER OF THE GAME: Playing
with a broken nose, Seattle
point guard Sue Bird set a
WNBA playoff record for
assists with 14, also pumping
in 10 points as the Storm
claimed the Western Conference
title last night." + ], + [ + "Reuters - Thousands of
demonstrators pressing
to\\install Ukraine's
opposition leader as president
after a\\disputed election
launched fresh street rallies
in the capital\\for the third
day Wednesday." + ], + [ + "Michael Jackson wishes he had
fought previous child
molestation claims instead of
trying to \"buy peace\", his
lawyer says." + ], + [ + "North Korea says it will not
abandon its weapons programme
after the South admitted
nuclear activities." + ], + [ + "While there is growing
attention to ongoing genocide
in Darfur, this has not
translated into either a
meaningful international
response or an accurate
rendering of the scale and
evident course of the
catastrophe." + ], + [ + "A planned component for
Microsoft #39;s next version
of Windows is causing
consternation among antivirus
experts, who say that the new
module, a scripting platform
called Microsoft Shell, could
give birth to a whole new
generation of viruses and
remotely" + ], + [ + "THE prosecution on terrorism
charges of extremist Islamic
cleric and accused Jemaah
Islamiah leader Abu Bakar
Bashir will rely heavily on
the potentially tainted
testimony of at least two
convicted Bali bombers, his
lawyers have said." + ], + [ + "SAN JOSE, California Yahoo
will likely have a tough time
getting American courts to
intervene in a dispute over
the sale of Nazi memorabilia
in France after a US appeals
court ruling." + ], + [ + "TORONTO (CP) - Glamis Gold of
Reno, Nev., is planning a
takeover bid for Goldcorp Inc.
of Toronto - but only if
Goldcorp drops its
\\$2.4-billion-Cdn offer for
another Canadian firm, made in
early December." + ], + [ + "Clashes between US troops and
Sadr militiamen escalated
Thursday, as the US surrounded
Najaf for possible siege." + ], + [ + "eBay Style director Constance
White joins Post fashion
editor Robin Givhan and host
Janet Bennett to discuss how
to find trends and bargains
and pull together a wardrobe
online." + ], + [ + "This week will see the release
of October new and existing
home sales, a measure of
strength in the housing
industry. But the short
holiday week will also leave
investors looking ahead to the
holiday travel season." + ], + [ + "Frankfurt - World Cup winners
Brazil were on Monday drawn to
meet European champions
Greece, Gold Cup winners
Mexico and Asian champions
Japan at the 2005
Confederations Cup." + ], + [ + "Third baseman Vinny Castilla
said he fits fine with the
Colorado youth movement, even
though he #39;ll turn 38 next
season and the Rockies are
coming off the second-worst" + ], + [ + "With a sudden shudder, the
ground collapsed and the pipe
pushed upward, buckling into a
humped shape as Cornell
University scientists produced
the first simulated earthquake" + ], + [ + "(Sports Network) - Two of the
top teams in the American
League tangle in a possible
American League Division
Series preview tonight, as the
West-leading Oakland Athletics
host the wild card-leading
Boston Red Sox for the first
of a three-game set at the" + ], + [ + "over half the children in the
world - suffer extreme
deprivation because of war,
HIV/AIDS or poverty, according
to a report released yesterday
by the United Nations Children
#39;s Fund." + ], + [ + "Microsoft (Quote, Chart) has
fired another salvo in its
ongoing spam battle, this time
against porn peddlers who don
#39;t keep their smut inside
the digital equivalent of a
quot;Brown Paper Wrapper." + ], + [ + " BETHESDA, Md. (Reuters) - The
use of some antidepressant
drugs appears linked to an
increase in suicidal behavior
in some children and teen-
agers, a U.S. advisory panel
concluded on Tuesday." + ], + [ + " SEATTLE (Reuters) - The next
version of the Windows
operating system, Microsoft
Corp.'s <A HREF=\"http://www
.reuters.co.uk/financeQuoteLoo
kup.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
flagship product, will ship
in 2006, the world's largest
software maker said on
Friday." + ], + [ + "Reuters - Philippine rescue
teams\\evacuated thousands of
people from the worst flooding
in the\\central Luzon region
since the 1970s as hungry
victims hunted\\rats and birds
for food." + ], + [ + "Inverness Caledonian Thistle
appointed Craig Brewster as
its new manager-player
Thursday although he #39;s
unable to play for the team
until January." + ], + [ + "The Afghan president expresses
deep concern after a bomb
attack which left at least
seven people dead." + ], + [ + " NEW YORK (Reuters) - U.S.
technology stocks opened lower
on Thursday after a sales
warning from Applied Materials
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AMAT.O target=/sto
cks/quickinfo/fullquote\">AM
AT.O</A>, while weekly
jobless claims data met Wall
Street's expectations,
leaving the Dow and S P 500
market measures little
changed." + ], + [ + "ATHENS, Greece -- Alan Shearer
converted an 87th-minute
penalty to give Newcastle a
1-0 win over Panionios in
their UEFA Cup Group D match." + ], + [ + "Fossil remains of the oldest
and smallest known ancestor of
Tyrannosaurus rex, the world
#39;s favorite ferocious
dinosaur, have been discovered
in China with evidence that
its body was cloaked in downy
quot;protofeathers." + ], + [ + "Derek Jeter turned a season
that started with a terrible
slump into one of the best in
his accomplished 10-year
career. quot;I don #39;t
think there is any question,
quot; the New York Yankees
manager said." + ], + [ + "Gardez (Afghanistan), Sept. 16
(Reuters): Afghan President
Hamid Karzai escaped an
assassination bid today when a
rocket was fired at his US
military helicopter as it was
landing in the southeastern
town of Gardez." + ], + [ + "The Jets came up with four
turnovers by Dolphins
quarterback Jay Fiedler in the
second half, including an
interception returned 66 yards
for a touchdown." + ], + [ + "China's Guo Jingjing easily
won the women's 3-meter
springboard last night, and Wu
Minxia made it a 1-2 finish
for the world's diving
superpower, taking the silver." + ], + [ + "GREEN BAY, Wisconsin (Ticker)
-- Brett Favre will be hoping
his 200th consecutive start
turns out better than his last
two have against the St." + ], + [ + "People fishing for sport are
doing far more damage to US
marine fish stocks than anyone
thought, accounting for nearly
a quarter of the" + ], + [ + "When an NFL team opens with a
prolonged winning streak,
former Miami Dolphins coach
Don Shula and his players from
the 17-0 team of 1972 root
unabashedly for the next
opponent." + ], + [ + "MIANNE Bagger, the transsexual
golfer who prompted a change
in the rules to allow her to
compete on the professional
circuit, made history
yesterday by qualifying to
play full-time on the Ladies
European Tour." + ], + [ + "Great Britain #39;s gold medal
tally now stands at five after
Leslie Law was handed the
individual three day eventing
title - in a courtroom." + ], + [ + "This particular index is
produced by the University of
Michigan Business School, in
partnership with the American
Society for Quality and CFI
Group, and is supported in
part by ForeSee Results" + ], + [ + "CHICAGO : Interstate Bakeries
Corp., the maker of popular,
old-style snacks Twinkies and
Hostess Cakes, filed for
bankruptcy, citing rising
costs and falling sales." + ], + [ + "Delta Air Lines (DAL:NYSE -
commentary - research) will
cut employees and benefits but
give a bigger-than-expected
role to Song, its low-cost
unit, in a widely anticipated
but still unannounced
overhaul, TheStreet.com has
learned." + ], + [ + "PC World - Symantec, McAfee
hope raising virus-definition
fees will move users to\\
suites." + ], + [ + "By byron kho. A consortium of
movie and record companies
joined forces on Friday to
request that the US Supreme
Court take another look at
peer-to-peer file-sharing
programs." + ], + [ + "DUBLIN -- Prime Minister
Bertie Ahern urged Irish
Republican Army commanders
yesterday to meet what he
acknowledged was ''a heavy
burden quot;: disarming and
disbanding their organization
in support of Northern
Ireland's 1998 peace accord." + ], + [ + "Mayor Tom Menino must be
proud. His Boston Red Sox just
won their first World Series
in 86 years and his Hyde Park
Blue Stars yesterday clinched
their first Super Bowl berth
in 32 years, defeating
O'Bryant, 14-0. Who would have
thought?" + ], + [ + "While reproductive planning
and women #39;s equality have
improved substantially over
the past decade, says a United
Nations report, world
population will increase from
6.4 billion today to 8.9
billion by 2050, with the 50
poorest countries tripling in" + ], + [ + "Instead of the skinny black
line, showing a hurricane
#39;s forecast track,
forecasters have drafted a
couple of alternative graphics
to depict where the storms
might go -- and they want your
opinion." + ], + [ + "South Korea have appealed to
sport #39;s supreme legal body
in an attempt to award Yang
Tae-young the Olympic
gymnastics all-round gold
medal after a scoring error
robbed him of the title in
Athens." + ], + [ + "BERLIN - Volkswagen AG #39;s
announcement this week that it
has forged a new partnership
deal with Malaysian carmaker
Proton comes as a strong euro
and Europe #39;s weak economic
performance triggers a fresh
wave of German investment in
Asia." + ], + [ + "AP - Johan Santana had an
early lead and was well on his
way to his 10th straight win
when the rain started to fall." + ], + [ + "ATHENS-In one of the biggest
shocks in Olympic judo
history, defending champion
Kosei Inoue was defeated by
Dutchman Elco van der Geest in
the men #39;s 100-kilogram
category Thursday." + ], + [ + "Consumers in Dublin pay more
for basic goods and services
that people elsewhere in the
country, according to figures
released today by the Central
Statistics Office." + ], + [ + "LIBERTY Media #39;s move last
week to grab up to 17.1 per
cent of News Corporation
voting stock has prompted the
launch of a defensive
shareholder rights plan." + ], + [ + "NBC is adding a 5-second delay
to its Nascar telecasts after
Dale Earnhardt Jr. used a
vulgarity during a postrace
interview last weekend." + ], + [ + "LONDON - Wild capuchin monkeys
can understand cause and
effect well enough to use
rocks to dig for food,
scientists have found.
Capuchin monkeys often use
tools and solve problems in
captivity and sometimes" + ], + [ + "San Francisco Giants
outfielder Barry Bonds, who
became the third player in
Major League Baseball history
to hit 700 career home runs,
won the National League Most
Valuable Player Award" + ], + [ + "The blue-chip Hang Seng Index
rose 171.88 points, or 1.22
percent, to 14,066.91. On
Friday, the index had slipped
31.58 points, or 0.2 percent." + ], + [ + "BAR's Anthony Davidson and
Jenson Button set the pace at
the first Chinese Grand Prix." + ], + [ + "Shares plunge after company
says its vein graft treatment
failed to show benefit in
late-stage test. CHICAGO
(Reuters) - Biotechnology
company Corgentech Inc." + ], + [ + "WASHINGTON - Contradicting the
main argument for a war that
has cost more than 1,000
American lives, the top U.S.
arms inspector reported
Wednesday that he found no
evidence that Iraq produced
any weapons of mass
destruction after 1991..." + ], + [ + "The key to hidden treasure
lies in your handheld GPS
unit. GPS-based \"geocaching\"
is a high-tech sport being
played by thousands of people
across the globe." + ], + [ + "AFP - Style mavens will be
scanning the catwalks in Paris
this week for next spring's
must-have handbag, as a
sweeping exhibition at the
French capital's fashion and
textile museum reveals the bag
in all its forms." + ], + [ + "Canadian Press - SAINT-
QUENTIN, N.B. (CP) - A major
highway in northern New
Brunswick remained closed to
almost all traffic Monday, as
local residents protested
planned health care cuts." + ], + [ + "The U.S. information tech
sector lost 403,300 jobs
between March 2001 and April
2004, and the market for tech
workers remains bleak,
according to a new report." + ], + [ + " NAJAF, Iraq (Reuters) - A
radical Iraqi cleric leading a
Shi'ite uprising agreed on
Wednesday to disarm his
militia and leave one of the
country's holiest Islamic
shrines after warnings of an
onslaught by government
forces." + ], + [ + "Saudi security forces have
killed a wanted militant near
the scene of a deadly shootout
Thursday. Officials say the
militant was killed in a
gunbattle Friday in the
northern town of Buraida,
hours after one" + ], + [ + "Portsmouth chairman Milan
Mandaric said on Tuesday that
Harry Redknapp, who resigned
as manager last week, was
innocent of any wrong-doing
over agent or transfer fees." + ], + [ + "This record is for all the
little guys, for all the
players who have to leg out
every hit instead of taking a
relaxing trot around the
bases, for all the batters
whose muscles aren #39;t" + ], + [ + "Two South Africans acquitted
by a Zimbabwean court of
charges related to the alleged
coup plot in Equatorial Guinea
are to be questioned today by
the South African authorities." + ], + [ + "Charlie Hodgson #39;s record-
equalling performance against
South Africa was praised by
coach Andy Robinson after the
Sale flyhalf scored 27 points
in England #39;s 32-16 victory
here at Twickenham on
Saturday." + ], + [ + "com September 30, 2004, 11:11
AM PT. SanDisk announced
Thursday increased capacities
for several different flash
memory cards. The Sunnyvale,
Calif." + ], + [ + "MOSCOW (CP) - Russia mourned
89 victims of a double air
disaster today as debate
intensified over whether the
two passenger liners could
have plunged almost
simultaneously from the sky by
accident." + ], + [ + "US blue-chip stocks rose
slightly on Friday as
government data showed better-
than-expected demand in August
for durable goods other than
transportation equipment, but
climbing oil prices limited
gains." + ], + [ + "BASEBALL Atlanta (NL):
Optioned P Roman Colon to
Greenville (Southern);
recalled OF Dewayne Wise from
Richmond (IL). Boston (AL):
Purchased C Sandy Martinez
from Cleveland (AL) and
assigned him to Pawtucket
(IL). Cleveland (AL): Recalled
OF Ryan Ludwick from Buffalo
(IL). Chicago (NL): Acquired
OF Ben Grieve from Milwaukee
(NL) for player to be named
and cash; acquired C Mike ..." + ], + [ + "Australia #39;s prime minister
says a body found in Fallujah
is likely that of kidnapped
aid worker Margaret Hassan.
John Howard told Parliament a
videotape of an Iraqi
terrorist group executing a
Western woman appears to have
been genuine." + ], + [ + "roundup Plus: Tech firms rally
against copyright bill...Apple
.Mac customers suffer e-mail
glitches...Alvarion expands
wireless broadband in China." + ], + [ + "BRONX, New York (Ticker) --
Kelvim Escobar was the latest
Anaheim Angels #39; pitcher to
subdue the New York Yankees.
Escobar pitched seven strong
innings and Bengie Molina tied
a career-high with four hits,
including" + ], + [ + "Business software maker
PeopleSoft Inc. said Monday
that it expects third-quarter
revenue to range between \\$680
million and \\$695 million,
above average Wall Street
estimates of \\$651." + ], + [ + "Sep 08 - Vijay Singh revelled
in his status as the new world
number one after winning the
Deutsche Bank Championship by
three shots in Boston on
Monday." + ], + [ + "Reuters - Enron Corp. ,
desperate to\\meet profit
targets, \"parked\" unwanted
power generating barges\\at
Merrill Lynch in a sham sale
designed to be reversed,
a\\prosecutor said on Tuesday
in the first criminal trial
of\\former executives at the
fallen energy company." + ], + [ + " NEW YORK (Reuters) - The
dollar rebounded on Monday
after a heavy selloff last
week, but analysts were
uncertain if the rally could
hold as the drumbeat of
expectation began for to the
December U.S. jobs report due
Friday." + ], + [ + "AP - Their first debate less
than a week away, President
Bush and Democrat John Kerry
kept their public schedules
clear on Saturday and began to
focus on their prime-time
showdown." + ], + [ + "Many people in golf are asking
that today. He certainly wasn
#39;t A-list and he wasn #39;t
Larry Nelson either. But you
couldn #39;t find a more solid
guy to lead the United States
into Ireland for the 2006
Ryder Cup Matches." + ], + [ + "Coles Myer Ltd. Australia
#39;s biggest retailer,
increased second-half profit
by 26 percent after opening
fuel and convenience stores,
selling more-profitable
groceries and cutting costs." + ], + [ + "MOSCOW: Us oil major
ConocoPhillips is seeking to
buy up to 25 in Russian oil
giant Lukoil to add billions
of barrels of reserves to its
books, an industry source
familiar with the matter said
on Friday." + ], + [ + "Australian Stuart Appleby, who
was the joint second-round
leader, returned a two-over 74
to drop to third at three-
under while American Chris
DiMarco moved into fourth with
a round of 69." + ], + [ + "PARIS Getting to the bottom of
what killed Yassar Arafat
could shape up to be an ugly
family tug-of-war. Arafat
#39;s half-brother and nephew
want copies of Arafat #39;s
medical records from the
suburban Paris hospital" + ], + [ + "Red Hat is acquiring security
and authentication tools from
Netscape Security Solutions to
bolster its software arsenal.
Red Hat #39;s CEO and chairman
Matthew Szulik spoke about the
future strategy of the Linux
supplier." + ], + [ + "With a doubleheader sweep of
the Minnesota Twins, the New
York Yankees moved to the
verge of clinching their
seventh straight AL East
title." + ], + [ + "Global Web portal Yahoo! Inc.
Wednesday night made available
a beta version of a new search
service for videos. Called
Yahoo! Video Search, the
search engine crawls the Web
for different types of media
files" + ], + [ + "Interactive posters at 25
underground stations are
helping Londoners travel
safely over Christmas." + ], + [ + "Athens, Greece (Sports
Network) - The first official
track event took place this
morning and Italy #39;s Ivano
Brugnetti won the men #39;s
20km walk at the Summer
Olympics in Athens." + ], + [ + " THE HAGUE (Reuters) - Former
Yugoslav President Slobodan
Milosevic condemned his war
crimes trial as a \"pure farce\"
on Wednesday in a defiant
finish to his opening defense
statement against charges of
ethnic cleansing in the
Balkans." + ], + [ + "US Airways Group (otc: UAIRQ -
news - people ) on Thursday
said it #39;ll seek a court
injunction to prohibit a
strike by disaffected unions." + ], + [ + "Shares in Unilever fall after
the Anglo-Dutch consumer goods
giant issued a surprise
profits warning." + ], + [ + "SAN FRANCISCO (CBS.MW) - The
Canadian government will sell
its 19 percent stake in Petro-
Canada for \\$2.49 billion,
according to the final
prospectus filed with the US
Securities and Exchange
Commission Thursday." + ], + [ + "Champions Arsenal opened a
five-point lead at the top of
the Premier League after a 4-0
thrashing of Charlton Athletic
at Highbury Saturday." + ], + [ + "The Redskins and Browns have
traded field goals and are
tied, 3-3, in the first
quarter in Cleveland." + ], + [ + " HYDERABAD, India (Reuters) -
Microsoft Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=MSFT.O t
arget=/stocks/quickinfo/fullqu
ote\">MSFT.O</A> will
hire several hundred new staff
at its new Indian campus in
the next year, its chief
executive said on Monday, in a
move aimed at strengthening
its presence in Asia's fourth-
biggest economy." + ], + [ + "According to Swiss
authorities, history was made
Sunday when 2723 people in
four communities in canton
Geneva, Switzerland, voted
online in a national federal
referendum." + ], + [ + " GUWAHATI, India (Reuters) -
People braved a steady drizzle
to come out to vote in a
remote northeast Indian state
on Thursday, as troops
guarded polling stations in an
election being held under the
shadow of violence." + ], + [ + "AFP - Three of the nine
Canadian sailors injured when
their newly-delivered,
British-built submarine caught
fire in the North Atlantic
were airlifted Wednesday to
hospital in northwest Ireland,
officials said." + ], + [ + "Alitalia SpA, Italy #39;s
largest airline, reached an
agreement with its flight
attendants #39; unions to cut
900 jobs, qualifying the
company for a government
bailout that will keep it in
business for another six
months." + ], + [ + "BAGHDAD, Iraq - A series of
strong explosions shook
central Baghdad near dawn
Sunday, and columns of thick
black smoke rose from the
Green Zone where U.S. and
Iraqi government offices are
located..." + ], + [ + "Forget September call-ups. The
Red Sox may tap their minor
league system for an extra
player or two when the rules
allow them to expand their
25-man roster Wednesday, but
any help from the farm is
likely to pale against the
abundance of talent they gain
from the return of numerous
players, including Trot Nixon
, from the disabled list." + ], + [ + "P amp;Os cutbacks announced
today are the result of the
waves of troubles that have
swamped the ferry industry of
late. Some would say the
company has done well to
weather the storms for as long
as it has." + ], + [ + "Sven-Goran Eriksson may gamble
by playing goalkeeper Paul
Robinson and striker Jermain
Defoe in Poland." + ], + [ + "Foreign Secretary Jack Straw
has flown to Khartoum on a
mission to pile the pressure
on the Sudanese government to
tackle the humanitarian
catastrophe in Darfur." + ], + [ + " Nextel was the big story in
telecommunications yesterday,
thanks to the Reston company's
mega-merger with Sprint, but
the future of wireless may be
percolating in dozens of
Washington area start-ups." + ], + [ + "Reuters - A senior U.S.
official said on
Wednesday\\deals should not be
done with hostage-takers ahead
of the\\latest deadline set by
Afghan Islamic militants who
have\\threatened to kill three
kidnapped U.N. workers." + ], + [ + "I have been anticipating this
day like a child waits for
Christmas. Today, PalmOne
introduces the Treo 650, the
answer to my quot;what smart
phone will I buy?" + ], + [ + "THOUSAND OAKS -- Anonymity is
only a problem if you want it
to be, and it is obvious Vijay
Singh doesn #39;t want it to
be. Let others chase fame." + ], + [ + "Wikipedia has surprised Web
watchers by growing fast and
maturing into one of the most
popular reference sites." + ], + [ + "It only takes 20 minutes on
the Internet for an
unprotected computer running
Microsoft Windows to be taken
over by a hacker. Any personal
or financial information
stored" + ], + [ + "TORONTO (CP) - Russia #39;s
Severstal has made an offer to
buy Stelco Inc., in what #39;s
believed to be one of several
competing offers emerging for
the restructuring but
profitable Hamilton steel
producer." + ], + [ + "Prices for flash memory cards
-- the little modules used by
digital cameras, handheld
organizers, MP3 players and
cell phones to store pictures,
music and other data -- are
headed down -- way down. Past
trends suggest that prices
will drop 35 percent a year,
but industry analysts think
that rate will be more like 40
or 50 percent this year and
next, due to more
manufacturers entering the
market." + ], + [ + "Walt Disney Co. #39;s
directors nominated Michael
Ovitz to serve on its board
for another three years at a
meeting just weeks before
forcing him out of his job as" + ], + [ + "The European Union, Japan,
Brazil and five other
countries won World Trade
Organization approval to
impose tariffs worth more than
\\$150 million a year on
imports from the United" + ], + [ + "Industrial conglomerate
Honeywell International on
Wednesday said it has filed a
lawsuit against 34 electronics
companies including Apple
Computer and Eastman Kodak,
claiming patent infringement
of its liquid crystal display
technology." + ], + [ + "Sinn Fein leader Gerry Adams
has put the pressure for the
success or failure of the
Northern Ireland assembly
talks firmly on the shoulders
of Ian Paisley." + ], + [ + "Australia #39;s Computershare
has agreed to buy EquiServe of
the United States for US\\$292
million (\\$423 million),
making it the largest US share
registrar and driving its
shares up by a third." + ], + [ + "David Coulthard #39;s season-
long search for a Formula One
drive next year is almost
over. Negotiations between Red
Bull Racing and Coulthard, who
tested for the Austrian team
for the first time" + ], + [ + "Interest rates on short-term
Treasury securities were mixed
in yesterday's auction. The
Treasury Department sold \\$18
billion in three-month bills
at a discount rate of 1.640
percent, up from 1.635 percent
last week. An additional \\$16
billion was sold in six-month
bills at a rate of 1.840
percent, down from 1.860
percent." + ], + [ + "Two top executives of scandal-
tarred insurance firm Marsh
Inc. were ousted yesterday,
the company said, the latest
casualties of an industry
probe by New York's attorney
general." + ], + [ + "AP - Southern California
tailback LenDale White
remembers Justin Holland from
high school. The Colorado
State quarterback made quite
an impression." + ], + [ + "TIM HENMAN last night admitted
all of his energy has been
drained away as he bowed out
of the Madrid Masters. The top
seed, who had a blood test on
Wednesday to get to the bottom
of his fatigue, went down" + ], + [ + "USDA #39;s Animal Plant Health
Inspection Service (APHIS)
this morning announced it has
confirmed a detection of
soybean rust from two test
plots at Louisiana State
University near Baton Rouge,
Louisiana." + ], + [ + " JAKARTA (Reuters) - President
Megawati Sukarnoputri urged
Indonesians on Thursday to
accept the results of the
country's first direct
election of a leader, but
stopped short of conceding
defeat." + ], + [ + "ISLAMABAD: Pakistan early
Monday test-fired its
indigenously developed short-
range nuclear-capable Ghaznavi
missile, the Inter Services
Public Relations (ISPR) said
in a statement." + ], + [ + "While the US software giant
Microsoft has achieved almost
sweeping victories in
government procurement
projects in several Chinese
provinces and municipalities,
the process" + ], + [ + "Mexican Cemex, being the third
largest cement maker in the
world, agreed to buy its
British competitor - RMC Group
- for \\$5.8 billion, as well
as their debts in order to
expand their activity on the
building materials market of
the USA and Europe." + ], + [ + "Microsoft Corp. has delayed
automated distribution of a
major security upgrade to its
Windows XP Professional
operating system, citing a
desire to give companies more
time to test it." + ], + [ + "The trial of a man accused of
murdering York backpacker
Caroline Stuttle begins in
Australia." + ], + [ + "Gateway computers will be more
widely available at Office
Depot, in the PC maker #39;s
latest move to broaden
distribution at retail stores
since acquiring rival
eMachines this year." + ], + [ + "ATHENS -- US sailors needed a
big day to bring home gold and
bronze medals from the sailing
finale here yesterday. But
rolling the dice on windshifts
and starting tactics backfired
both in Star and Tornado
classes, and the Americans had
to settle for a single silver
medal." + ], + [ + "Intel Corp. is refreshing its
64-bit Itanium 2 processor
line with six new chips based
on the Madison core. The new
processors represent the last
single-core Itanium chips that
the Santa Clara, Calif." + ], + [ + "The world's largest insurance
group pays \\$126m in fines as
part of a settlement with US
regulators over its dealings
with two firms." + ], + [ + "BRUSSELS: The EU sought
Wednesday to keep pressure on
Turkey over its bid to start
talks on joining the bloc, as
last-minute haggling seemed
set to go down to the wire at
a summit poised to give a
green light to Ankara." + ], + [ + "AP - J. Cofer Black, the State
Department official in charge
of counterterrorism, is
leaving government in the next
few weeks." + ], + [ + "For the first time, broadband
connections are reaching more
than half (51 percent) of the
American online population at
home, according to measurement
taken in July by
Nielsen/NetRatings, a
Milpitas-based Internet
audience measurement and
research ..." + ], + [ + "AP - Cavaliers forward Luke
Jackson was activated
Wednesday after missing five
games because of tendinitis in
his right knee. Cleveland also
placed forward Sasha Pavlovic
on the injured list." + ], + [ + "The tobacco firm John Player
amp; Sons has announced plans
to lay off 90 workers at its
cigarette factory in Dublin.
The company said it was
planning a phased closure of
the factory between now and
February as part of a review
of its global operations." + ], + [ + "AP - Consumers borrowed more
freely in September,
especially when it came to
racking up charges on their
credit cards, the Federal
Reserve reported Friday." + ], + [ + "AFP - The United States
presented a draft UN
resolution that steps up the
pressure on Sudan over the
crisis in Darfur, including
possible international
sanctions against its oil
sector." + ], + [ + "AFP - At least 33 people were
killed and dozens others
wounded when two bombs ripped
through a congregation of
Sunni Muslims in Pakistan's
central city of Multan, police
said." + ], + [ + "Bold, innovative solutions are
key to addressing the rapidly
rising costs of higher
education and the steady
reduction in government-
subsidized help to finance
such education." + ], + [ + "Just as the PhD crowd emerge
with different interpretations
of today's economy, everyday
Americans battling to balance
the checkbook hold diverse
opinions about where things
stand now and in the future." + ], + [ + "The Brisbane Lions #39;
football manager stepped out
of the changerooms just before
six o #39;clock last night and
handed one of the milling
supporters a six-pack of beer." + ], + [ + "Authorities here are always
eager to show off their
accomplishments, so when
Beijing hosted the World
Toilet Organization conference
last week, delegates were
given a grand tour of the
city's toilets." + ], + [ + "Cavaliers owner Gordon Gund is
in quot;serious quot;
negotiations to sell the NBA
franchise, which has enjoyed a
dramatic financial turnaround
since the arrival of star
LeBron James." + ], + [ + "WASHINGTON Trying to break a
deadlock on energy policy, a
diverse group of
environmentalists, academics
and former government
officials were to publish a
report on Wednesday that
presents strategies for making
the United States cleaner,
more competitive" + ], + [ + "After two days of gloom, China
was back on the winning rails
on Thursday with Liu Chunhong
winning a weightlifting title
on her record-shattering binge
and its shuttlers contributing
two golds in the cliff-hanging
finals." + ], + [ + "One question that arises
whenever a player is linked to
steroids is, \"What would he
have done without them?\"
Baseball history whispers an
answer." + ], + [ + "AFP - A series of torchlight
rallies and vigils were held
after darkness fell on this
central Indian city as victims
and activists jointly
commemorated a night of horror
20 years ago when lethal gas
leaked from a pesticide plant
and killed thousands." + ], + [ + "Consider the New World of
Information - stuff that,
unlike the paper days of the
past, doesn't always
physically exist. You've got
notes, scrawlings and
snippets, Web graphics, photos
and sounds. Stuff needs to be
cut, pasted, highlighted,
annotated, crossed out,
dragged away. And, as Ross
Perot used to say (or maybe it
was Dana Carvey impersonating
him), don't forget the graphs
and charts." + ], + [ + "The second round of the
Canadian Open golf tournament
continues Saturday Glenn Abbey
Golf Club in Oakville,
Ontario, after play was
suspended late Friday due to
darkness." + ], + [ + "A consortium led by Royal
Dutch/Shell Group that is
developing gas reserves off
Russia #39;s Sakhalin Island
said Thursday it has struck a
US\\$6 billion (euro4." + ], + [ + "Major Hollywood studios on
Tuesday announced scores of
lawsuits against computer
server operators worldwide,
including eDonkey, BitTorrent
and DirectConnect networks,
for allowing trading of
pirated movies." + ], + [ + "A massive plan to attract the
2012 Summer Olympics to New
York, touting the city's
diversity, financial and media
power, was revealed Wednesday." + ], + [ + "A Zimbabwe court Friday
convicted a British man
accused of leading a coup plot
against the government of oil-
rich Equatorial Guinea on
weapons charges, but acquitted
most of the 69 other men held
with him." + ], + [ + "But will Wi-Fi, high-
definition broadcasts, mobile
messaging and other
enhancements improve the game,
or wreck it?\\<br />
Photos of tech-friendly parks\\" + ], + [ + "An audit by international
observers supported official
elections results that gave
President Hugo Chavez a
victory over a recall vote
against him, the secretary-
general of the Organisation of
American States announced." + ], + [ + "Canadian Press - TORONTO (CP)
- The fatal stabbing of a
young man trying to eject
unwanted party guests from his
family home, the third such
knifing in just weeks, has
police worried about a
potentially fatal holiday
recipe: teens, alcohol and
knives." + ], + [ + "NICK Heidfeld #39;s test with
Williams has been brought
forward after BAR blocked
plans for Anthony Davidson to
drive its Formula One rival
#39;s car." + ], + [ + "MOSCOW - A female suicide
bomber set off a shrapnel-
filled explosive device
outside a busy Moscow subway
station on Tuesday night,
officials said, killing 10
people and injuring more than
50." + ], + [ + "Grace Park closed with an
eagle and two birdies for a
7-under-par 65 and a two-
stroke lead after three rounds
of the Wachovia LPGA Classic
on Saturday." + ], + [ + "ABIDJAN (AFP) - Two Ivory
Coast military aircraft
carried out a second raid on
Bouake, the stronghold of the
former rebel New Forces (FN)
in the divided west African
country, a French military
source told AFP." + ], + [ + "Carlos Beltran drives in five
runs to carry the Astros to a
12-3 rout of the Braves in
Game 5 of their first-round NL
playoff series." + ], + [ + "On-demand viewing isn't just
for TiVo owners anymore.
Television over internet
protocol, or TVIP, offers
custom programming over
standard copper wires." + ], + [ + "Apple is recalling 28,000
faulty batteries for its
15-inch Powerbook G4 laptops." + ], + [ + "Since Lennox Lewis #39;s
retirement, the heavyweight
division has been knocked for
having more quantity than
quality. Eight heavyweights on
Saturday night #39;s card at
Madison Square Garden hope to
change that perception, at
least for one night." + ], + [ + "PalmSource #39;s European
developer conference is going
on now in Germany, and this
company is using this
opportunity to show off Palm
OS Cobalt 6.1, the latest
version of its operating
system." + ], + [ + "The former Chief Executive
Officer of Computer Associates
was indicted by a federal
grand jury in New York
Wednesday for allegedly
participating in a massive
fraud conspiracy and an
elaborate cover up of a scheme
that cost investors" + ], + [ + "Speaking to members of the
Massachusetts Software
Council, Microsoft CEO Steve
Ballmer touted a bright future
for technology but warned his
listeners to think twice
before adopting open-source
products like Linux." + ], + [ + "MIAMI - The Trillian instant
messaging (IM) application
will feature several
enhancements in its upcoming
version 3.0, including new
video and audio chat
capabilities, enhanced IM
session logs and integration
with the Wikipedia online
encyclopedia, according to
information posted Friday on
the product developer's Web
site." + ], + [ + "Honeywell International Inc.,
the world #39;s largest
supplier of building controls,
agreed to buy Novar Plc for
798 million pounds (\\$1.53
billion) to expand its
security, fire and
ventilation-systems business
in Europe." + ], + [ + "San Francisco investment bank
Thomas Weisel Partners on
Thursday agreed to pay \\$12.5
million to settle allegations
that some of the stock
research the bank published
during the Internet boom was
tainted by conflicts of
interest." + ], + [ + "AFP - A state of civil
emergency in the rebellion-hit
Indonesian province of Aceh
has been formally extended by
six month, as the country's
president pledged to end
violence there without foreign
help." + ], + [ + "Forbes.com - Peter Frankling
tapped an unusual source to
fund his new business, which
makes hot-dog-shaped ice cream
treats known as Cool Dogs: Two
investors, one a friend and
the other a professional
venture capitalist, put in
more than #36;100,000 each
from their Individual
Retirement Accounts. Later
Franklin added #36;150,000
from his own IRA." + ], + [ + "Reuters - Online DVD rental
service Netflix Inc.\\and TiVo
Inc., maker of a digital video
recorder, on Thursday\\said
they have agreed to develop a
joint entertainment\\offering,
driving shares of both
companies higher." + ], + [ + "A San Diego insurance
brokerage has been sued by New
York Attorney General Elliot
Spitzer for allegedly
soliciting payoffs in exchange
for steering business to
preferred insurance companies." + ], + [ + "The European Union agreed
Monday to lift penalties that
have cost American exporters
\\$300 million, following the
repeal of a US corporate tax
break deemed illegal under
global trade rules." + ], + [ + "US Secretary of State Colin
Powell on Monday said he had
spoken to both Indian Foreign
Minister K Natwar Singh and
his Pakistani counterpart
Khurshid Mahmud Kasuri late
last week before the two met
in New Delhi this week for
talks." + ], + [ + "NEW YORK - Victor Diaz hit a
tying, three-run homer with
two outs in the ninth inning,
and Craig Brazell's first
major league home run in the
11th gave the New York Mets a
stunning 4-3 victory over the
Chicago Cubs on Saturday.
The Cubs had much on the
line..." + ], + [ + "AFP - At least 54 people have
died and more than a million
have fled their homes as
torrential rains lashed parts
of India and Bangladesh,
officials said." + ], + [ + "LOS ANGELES - California has
adopted the world's first
rules to reduce greenhouse
emissions for autos, taking
what supporters see as a
dramatic step toward cleaning
up the environment but also
ensuring higher costs for
drivers. The rules may lead
to sweeping changes in
vehicles nationwide,
especially if other states opt
to follow California's
example..." + ], + [ + " LONDON (Reuters) - European
stock markets scaled
near-2-1/2 year highs on
Friday as oil prices held
below \\$48 a barrel, and the
euro held off from mounting
another assault on \\$1.30 but
hovered near record highs
against the dollar." + ], + [ + "Tim Duncan had 17 points and
10 rebounds, helping the San
Antonio Spurs to a 99-81
victory over the New York
Kicks. This was the Spurs
fourth straight win this
season." + ], + [ + "Nagpur: India suffered a
double blow even before the
first ball was bowled in the
crucial third cricket Test
against Australia on Tuesday
when captain Sourav Ganguly
and off spinner Harbhajan
Singh were ruled out of the
match." + ], + [ + "AFP - Republican and
Democratic leaders each
declared victory after the
first head-to-head sparring
match between President George
W. Bush and Democratic
presidential hopeful John
Kerry." + ], + [ + "THIS YULE is all about console
supply and there #39;s
precious little units around,
it has emerged. Nintendo has
announced that it is going to
ship another 400,000 units of
its DS console to the United
States to meet the shortfall
there." + ], + [ + "Annual global semiconductor
sales growth will probably
fall by half in 2005 and
memory chip sales could
collapse as a supply glut saps
prices, world-leading memory
chip maker Samsung Electronics
said on Monday." + ], + [ + "NEW YORK - Traditional phone
systems may be going the way
of the Pony Express. Voice-
over-Internet Protocol,
technology that allows users
to make and receive phone
calls using the Internet, is
giving the old circuit-
switched system a run for its
money." + ], + [ + "AP - Former New York Yankees
hitting coach Rick Down was
hired for the same job by the
Mets on Friday, reuniting him
with new manager Willie
Randolph." + ], + [ + "Last night in New York, the UN
secretary-general was given a
standing ovation - a robust
response to a series of
attacks in past weeks." + ], + [ + "FILDERSTADT (Germany) - Amelie
Mauresmo and Lindsay Davenport
took their battle for the No.
1 ranking and Porsche Grand
Prix title into the semi-
finals with straight-sets
victories on Friday." + ], + [ + "Reuters - The company behind
the Atkins Diet\\on Friday
shrugged off a recent decline
in interest in low-carb\\diets
as a seasonal blip, and its
marketing chief said\\consumers
would cut out starchy foods
again after picking up\\pounds
over the holidays." + ], + [ + "There #39;s something to be
said for being the quot;first
mover quot; in an industry
trend. Those years of extra
experience in tinkering with a
new idea can be invaluable in
helping the first" + ], + [ + "JOHANNESBURG -- Meeting in
Nigeria four years ago,
African leaders set a goal
that 60 percent of children
and pregnant women in malaria-
affected areas around the
continent would be sleeping
under bed nets by the end of
2005." + ], + [ + "AP - The first U.S. cases of
the fungus soybean rust, which
hinders plant growth and
drastically cuts crop
production, were found at two
research sites in Louisiana,
officials said Wednesday." + ], + [ + "Carter returned, but it was
running back Curtis Martin and
the offensive line that put
the Jets ahead. Martin rushed
for all but 10 yards of a
45-yard drive that stalled at
the Cardinals 10." + ], + [ + " quot;There #39;s no way
anyone would hire them to
fight viruses, quot; said
Sophos security analyst Gregg
Mastoras. quot;For one, no
security firm could maintain
its reputation by employing
hackers." + ], + [ + "Symantec has revoked its
decision to blacklist a
program that allows Web
surfers in China to browse
government-blocked Web sites.
The move follows reports that
the firm labelled the Freegate
program, which" + ], + [ + " NEW YORK (Reuters) - Shares
of Chiron Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=CHIR.O t
arget=/stocks/quickinfo/fullqu
ote\">CHIR.O</A> fell
7 percent before the market
open on Friday, a day after
the biopharmaceutical company
said it is delaying shipment
of its flu vaccine, Fluvirin,
because lots containing 4
million vaccines do not meet
product sterility standards." + ], + [ + "The nation's top
telecommunications regulator
said yesterday he will push --
before the next president is
inaugurated -- to protect
fledgling Internet telephone
services from getting taxed
and heavily regulated by the
50 state governments." + ], + [ + "Microsoft has signed a pact to
work with the United Nations
Educational, Scientific and
Cultural Organization (UNESCO)
to increase computer use,
Internet access and teacher
training in developing
countries." + ], + [ + "DENVER (Ticker) -- Jake
Plummer more than made up for
a lack of a running game.
Plummer passed for 294 yards
and two touchdowns as the
Denver Broncos posted a 23-13
victory over the San Diego
Chargers in a battle of AFC
West Division rivals." + ], + [ + "DALLAS -- Belo Corp. said
yesterday that it would cut
250 jobs, more than half of
them at its flagship
newspaper, The Dallas Morning
News, and that an internal
investigation into circulation
overstatements" + ], + [ + "AP - Duke Bainum outspent Mufi
Hannemann in Honolulu's most
expensive mayoral race, but
apparently failed to garner
enough votes in Saturday's
primary to claim the office
outright." + ], + [ + "roundup Plus: Microsoft tests
Windows Marketplace...Nortel
delays financials
again...Microsoft updates
SharePoint." + ], + [ + "The Federal Reserve still has
some way to go to restore US
interest rates to more normal
levels, Philadelphia Federal
Reserve President Anthony
Santomero said on Monday." + ], + [ + "It took all of about five
minutes of an introductory
press conference Wednesday at
Heritage Hall for USC
basketball to gain something
it never really had before." + ], + [ + "Delta Air Lines (DAL.N: Quote,
Profile, Research) said on
Wednesday its auditors have
expressed doubt about the
airline #39;s financial
viability." + ], + [ + "POLITICIANS and aid agencies
yesterday stressed the
importance of the media in
keeping the spotlight on the
appalling human rights abuses
taking place in the Darfur
region of Sudan." + ], + [ + "AP - The Boston Red Sox looked
at the out-of-town scoreboard
and could hardly believe what
they saw. The New York Yankees
were trailing big at home
against the Cleveland Indians
in what would be the worst
loss in the 101-year history
of the storied franchise." + ], + [ + "The Red Sox will either
complete an amazing comeback
as the first team to rebound
from a 3-0 deficit in
postseason history, or the
Yankees will stop them." + ], + [ + "\\Children who have a poor diet
are more likely to become
aggressive and anti-social, US
researchers believe." + ], + [ + "OPEN SOURCE champion Microsoft
is expanding its programme to
give government organisations
some of its source code. In a
communique from the lair of
the Vole, in Redmond,
spinsters have said that
Microsoft" + ], + [ + "The Red Sox have reached
agreement with free agent
pitcher Matt Clement yesterday
on a three-year deal that will
pay him around \\$25 million,
his agent confirmed yesterday." + ], + [ + "Takeover target Ronin Property
Group said it would respond to
an offer by Multiplex Group
for all the securities in the
company in about three weeks." + ], + [ + "Canadian Press - OTTAWA (CP) -
Contrary to Immigration
Department claims, there is no
shortage of native-borne
exotic dancers in Canada, says
a University of Toronto law
professor who has studied the
strip club business." + ], + [ + "HEN Manny Ramirez and David
Ortiz hit consecutive home
runs Sunday night in Chicago
to put the Red Sox ahead,
there was dancing in the
streets in Boston." + ], + [ + "Google Inc. is trying to
establish an online reading
room for five major libraries
by scanning stacks of hard-to-
find books into its widely
used Internet search engine." + ], + [ + "HOUSTON--(BUSINESS
WIRE)--Sept. 1, 2004-- L
#39;operazione crea una
centrale globale per l
#39;analisi strategica el
#39;approfondimento del
settore energetico IHS Energy,
fonte globale leader di
software, analisi e
informazioni" + ], + [ + "The European Union presidency
yesterday expressed optimism
that a deal could be struck
over Turkey #39;s refusal to
recognize Cyprus in the lead-
up to next weekend #39;s EU
summit, which will decide
whether to give Ankara a date
for the start of accession
talks." + ], + [ + "WASHINGTON Can you always tell
when somebody #39;s lying? If
so, you might be a wizard of
the fib. A California
psychology professor says
there #39;s a tiny subculture
of people that can pick out a
lie nearly every time they
hear one." + ], + [ + " KHARTOUM (Reuters) - Sudan on
Saturday questioned U.N.
estimates that up to 70,000
people have died from hunger
and disease in its remote
Darfur region since a
rebellion began 20 months
ago." + ], + [ + "Type design was once the
province of skilled artisans.
With the help of new computer
programs, neophytes have
flooded the Internet with
their creations." + ], + [ + "RCN Inc., co-owner of
Starpower Communications LLC,
the Washington area
television, telephone and
Internet provider, filed a
plan of reorganization
yesterday that it said puts
the company" + ], + [ + "MIAMI -- Bryan Randall grabbed
a set of Mardi Gras beads and
waved them aloft, while his
teammates exalted in the
prospect of a trip to New
Orleans." + ], + [ + "<strong>Letters</stro
ng> Reports of demise
premature" + ], + [ + "TORONTO (CP) - With an injured
Vince Carter on the bench, the
Toronto Raptors dropped their
sixth straight game Friday,
101-87 to the Denver Nuggets." + ], + [ + "The US airline industry,
riddled with excess supply,
will see a significant drop in
capacity, or far fewer seats,
as a result of at least one
airline liquidating in the
next year, according to
AirTran Airways Chief
Executive Joe Leonard." + ], + [ + "Boeing (nyse: BA - news -
people ) Chief Executive Harry
Stonecipher is keeping the
faith. On Monday, the head of
the aerospace and military
contractor insists he #39;s
confident his firm will
ultimately win out" + ], + [ + "While not quite a return to
glory, Monday represents the
Redskins' return to the
national consciousness." + ], + [ + "Australia #39;s biggest
supplier of fresh milk,
National Foods, has posted a
net profit of \\$68.7 million,
an increase of 14 per cent on
last financial year." + ], + [ + "Lawyers for customers suing
Merck amp; Co. want to
question CEO Raymond Gilmartin
about what he knew about the
dangers of Vioxx before the
company withdrew the drug from
the market because of health
hazards." + ], + [ + "Vijay Singh has won the US PGA
Tour player of the year award
for the first time, ending
Tiger Woods #39;s five-year
hold on the honour." + ], + [ + "New York; September 23, 2004 -
The Department of Justice
(DoJ), FBI and US Attorney
#39;s Office handed down a
10-count indictment against
former Computer Associates
(CA) chairman and CEO Sanjay
Kumar and Stephen Richards,
former CA head of worldwide
sales." + ], + [ + "AFP - At least four Georgian
soldiers were killed and five
wounded in overnight clashes
in Georgia's separatist, pro-
Russian region of South
Ossetia, Georgian officers
near the frontline with
Ossetian forces said early
Thursday." + ], + [ + "Intel, the world #39;s largest
chip maker, scrapped a plan
Thursday to enter the digital
television chip business,
marking a major retreat from
its push into consumer
electronics." + ], + [ + "PACIFIC Hydro shares yesterday
caught an updraught that sent
them more than 20 per cent
higher after the wind farmer
moved to flush out a bidder." + ], + [ + "The European Commission is
expected later this week to
recommend EU membership talks
with Turkey. Meanwhile, German
Chancellor Gerhard Schroeder
and Turkish Prime Minister
Tayyip Erdogan are
anticipating a quot;positive
report." + ], + [ + "The US is the originator of
over 42 of the worlds
unsolicited commercial e-mail,
making it the worst offender
in a league table of the top
12 spam producing countries
published yesterday by anti-
virus firm Sophos." + ], + [ + " NEW YORK (Reuters) - U.S.
consumer confidence retreated
in August while Chicago-area
business activity slowed,
according to reports on
Tuesday that added to worries
the economy's patch of slow
growth may last beyond the
summer." + ], + [ + "Intel is drawing the curtain
on some of its future research
projects to continue making
transistors smaller, faster,
and less power-hungry out as
far as 2020." + ], + [ + "England got strikes from
sparkling debut starter
Jermain Defoe and Michael Owen
to defeat Poland in a Uefa
World Cup qualifier in
Chorzow." + ], + [ + "The Canadian government
signalled its intention
yesterday to reintroduce
legislation to decriminalise
the possession of small
amounts of marijuana." + ], + [ + "A screensaver targeting spam-
related websites appears to
have been too successful." + ], + [ + "Titleholder Ernie Els moved
within sight of a record sixth
World Match Play title on
Saturday by solving a putting
problem to overcome injured
Irishman Padraig Harrington 5
and 4." + ], + [ + "If the Washington Nationals
never win a pennant, they have
no reason to ever doubt that
DC loves them. Yesterday, the
District City Council
tentatively approved a tab for
a publicly financed ballpark
that could amount to as much
as \\$630 million." + ], + [ + "Plus: Experts fear Check 21
could lead to massive bank
fraud." + ], + [ + "By SIOBHAN McDONOUGH
WASHINGTON (AP) -- Fewer
American youths are using
marijuana, LSD and Ecstasy,
but more are abusing
prescription drugs, says a
government report released
Thursday. The 2003 National
Survey on Drug Use and Health
also found youths and young
adults are more aware of the
risks of using pot once a
month or more frequently..." + ], + [ + " ABIDJAN (Reuters) - Ivory
Coast warplanes killed nine
French soldiers on Saturday in
a bombing raid during the
fiercest clashes with rebels
for 18 months and France hit
back by destroying most of
the West African country's
small airforce." + ], + [ + "Unilever has reported a three
percent rise in third-quarter
earnings but warned it is
reviewing its targets up to
2010, after issuing a shock
profits warning last month." + ], + [ + "A TNO engineer prepares to
start capturing images for the
world's biggest digital photo." + ], + [ + "Defensive back Brandon
Johnson, who had two
interceptions for Tennessee at
Mississippi, was suspended
indefinitely Monday for
violation of team rules." + ], + [ + "Points leader Kurt Busch spun
out and ran out of fuel, and
his misfortune was one of the
reasons crew chief Jimmy
Fennig elected not to pit with
20 laps to go." + ], + [ + " LONDON (Reuters) - Oil prices
extended recent heavy losses
on Wednesday ahead of weekly
U.S. data expected to show
fuel stocks rising in time
for peak winter demand." + ], + [ + "(CP) - The NHL all-star game
hasn #39;t been cancelled
after all. It #39;s just been
moved to Russia. The agent for
New York Rangers winger
Jaromir Jagr confirmed Monday
that the Czech star had joined
Omsk Avangard" + ], + [ + "PalmOne is aiming to sharpen
up its image with the launch
of the Treo 650 on Monday. As
previously reported, the smart
phone update has a higher-
resolution screen and a faster
processor than the previous
top-of-the-line model, the
Treo 600." + ], + [ + "GAZA CITY, Gaza Strip --
Islamic militant groups behind
many suicide bombings
dismissed yesterday a call
from Mahmoud Abbas, the
interim Palestinian leader, to
halt attacks in the run-up to
a Jan. 9 election to replace
Yasser Arafat." + ], + [ + "Secretary of State Colin
Powell is wrapping up an East
Asia trip focused on prodding
North Korea to resume talks
aimed at ending its nuclear-
weapons program." + ], + [ + "HERE in the land of myth, that
familiar god of sports --
karma -- threw a bolt of
lightning into the Olympic
stadium yesterday. Marion
Jones lunged desperately with
her baton in the 4 x 100m
relay final, but couldn #39;t
reach her target." + ], + [ + "kinrowan writes quot;MIT,
inventor of Kerberos, has
announced a pair of
vulnerabities in the software
that will allow an attacker to
either execute a DOS attack or
execute code on the machine." + ], + [ + "The risk of intestinal damage
from common painkillers may be
higher than thought, research
suggests." + ], + [ + "AN earthquake measuring 7.3 on
the Richter Scale hit western
Japan this morning, just hours
after another strong quake
rocked the area." + ], + [ + "Vodafone has increased the
competition ahead of Christmas
with plans to launch 10
handsets before the festive
season. The Newbury-based
group said it will begin
selling the phones in
November." + ], + [ + "Reuters - Former Pink Floyd
mainman Roger\\Waters released
two new songs, both inspired
by the U.S.-led\\invasion of
Iraq, via online download
outlets Tuesday." + ], + [ + "A former assistant treasurer
at Enron Corp. (ENRNQ.PK:
Quote, Profile, Research)
agreed to plead guilty to
conspiracy to commit
securities fraud on Tuesday
and will cooperate with" + ], + [ + "Britain #39;s Prince Harry,
struggling to shed a growing
quot;wild child quot; image,
won #39;t apologize to a
photographer he scuffled with
outside an exclusive London
nightclub, a royal spokesman
said on Saturday." + ], + [ + "UK house prices fell by 1.1 in
October, confirming a
softening of the housing
market, Halifax has said. The
UK #39;s biggest mortgage
lender said prices rose 18." + ], + [ + "Pakistan #39;s interim Prime
Minister Chaudhry Shaujaat
Hussain has announced his
resignation, paving the way
for his successor Shauket
Aziz." + ], + [ + "A previously unknown group
calling itself Jamaat Ansar
al-Jihad al-Islamiya says it
set fire to a Jewish soup
kitchen in Paris, according to
an Internet statement." + ], + [ + "More than six newspaper
companies have received
letters from the Securities
and Exchange Commission
seeking information about
their circulation practices." + ], + [ + "THE 64,000 dollar -
correction, make that 500
million dollar -uestion
hanging over Shire
Pharmaceuticals is whether the
5 per cent jump in the
companys shares yesterday
reflects relief that US
regulators have finally
approved its drug for" + ], + [ + "The deadliest attack on
Americans in Iraq since May
came as Iraqi officials
announced that Saddam
Hussein's deputy had not been
captured on Sunday." + ], + [ + "AP - Kenny Rogers lost at the
Coliseum for the first time in
more than 10 years, with Bobby
Crosby's three-run double in
the fifth inning leading the
Athletics to a 5-4 win over
the Texas Rangers on Thursday." + ], + [ + "A fundraising concert will be
held in London in memory of
the hundreds of victims of the
Beslan school siege." + ], + [ + "Dambulla, Sri Lanka - Kumar
Sangakkara and Avishka
Gunawardene slammed impressive
half-centuries to help an
under-strength Sri Lanka crush
South Africa by seven wickets
in the fourth one-day
international here on
Saturday." + ], + [ + "Fresh off being the worst team
in baseball, the Arizona
Diamondbacks set a new record
this week: fastest team to
both hire and fire a manager." + ], + [ + "Nebraska head coach Bill
Callahan runs off the field at
halftime of the game against
Baylor in Lincoln, Neb.,
Saturday, Oct. 16, 2004." + ], + [ + "NASA has been working on a
test flight project for the
last few years involving
hypersonic flight. Hypersonic
flight is fight at speeds
greater than Mach 5, or five
times the speed of sound." + ], + [ + "AFP - The landmark trial of a
Rwandan Roman Catholic priest
accused of supervising the
massacre of 2,000 of his Tutsi
parishioners during the
central African country's 1994
genocide opens at a UN court
in Tanzania." + ], + [ + "The Irish government has
stepped up its efforts to free
the British hostage in Iraq,
Ken Bigley, whose mother is
from Ireland, by talking to
diplomats from Iran and
Jordan." + ], + [ + "AP - Republican Sen. Lincoln
Chafee, who flirted with
changing political parties in
the wake of President Bush's
re-election victory, says he
will stay in the GOP." + ], + [ + "AP - Microsoft Corp. announced
Wednesday that it would offer
a low-cost, localized version
of its Windows XP operating
system in India to tap the
large market potential in this
country of 1 billion people,
most of whom do not speak
English." + ], + [ + "Businesses saw inventories
rise in July and sales picked
up, the government reported
Wednesday. The Commerce
Department said that stocks of
unsold goods increased 0.9 in
July, down from a 1.1 rise in
June." + ], + [ + " EAST RUTHERFORD, N.J. (Sports
Network) - The Toronto
Raptors have traded All-Star
swingman Vince Carter to the
New Jersey Nets in exchange
for center Alonzo Mourning,
forward Eric Williams,
center/forward Aaron Williams
and two first- round draft
picks." + ], + [ + "AP - Utah State University has
secured a #36;40 million
contract with NASA to build an
orbiting telescope that will
examine galaxies and try to
find new stars." + ], + [ + "South Korean President Roh
Moo-hyun pays a surprise visit
to troops in Iraq, after his
government decided to extend
their mandate." + ], + [ + "As the European Union
approaches a contentious
decision - whether to let
Turkey join the club - the
Continent #39;s rulers seem to
have left their citizens
behind." + ], + [ + "What riot? quot;An Argentine
friend of mine was a little
derisive of the Pacers-Pistons
eruption, quot; says reader
Mike Gaynes. quot;He snorted,
#39;Is that what Americans
call a riot?" + ], + [ + "All season, Chris Barnicle
seemed prepared for just about
everything, but the Newton
North senior was not ready for
the hot weather he encountered
yesterday in San Diego at the
Footlocker Cross-Country
National Championships. Racing
in humid conditions with
temperatures in the 70s, the
Massachusetts Division 1 state
champion finished sixth in 15
minutes 34 seconds in the
5-kilometer race. ..." + ], + [ + "Shares of Genta Inc. (GNTA.O:
Quote, Profile, Research)
soared nearly 50 percent on
Monday after the biotechnology
company presented promising
data on an experimental
treatment for blood cancers." + ], + [ + "I spend anywhere from three to
eight hours every week
sweating along with a motley
crew of local misfits,
shelving, sorting, and hauling
ton after ton of written
matter in a rowhouse basement
in Baltimore. We have no heat
nor air conditioning, but
still, every week, we come and
work. Volunteer night is
Wednesday, but many of us also
work on the weekends, when
we're open to the public.
There are times when we're
freezing and we have to wear
coats and gloves inside,
making handling books somewhat
tricky; other times, we're all
soaked with sweat, since it's
90 degrees out and the
basement is thick with bodies.
One learns to forget about
personal space when working at
The Book Thing, since you can
scarcely breathe without
bumping into someone, and we
are all so accustomed to
having to scrape by each other
that most of us no longer
bother to say \"excuse me\"
unless some particularly
dramatic brushing occurs." + ], + [ + " BAGHDAD (Reuters) - A
deadline set by militants who
have threatened to kill two
Americans and a Briton seized
in Iraq was due to expire
Monday, and more than two
dozen other hostages were
also facing death unless rebel
demands were met." + ], + [ + "Alarmed by software glitches,
security threats and computer
crashes with ATM-like voting
machines, officials from
Washington, D.C., to
California are considering an
alternative from an unlikely
place: Nevada." + ], + [ + "Some Venezuelan television
channels began altering their
programs Thursday, citing
fears of penalties under a new
law restricting violence and
sexual content over the
airwaves." + ], + [ + "SBC Communications Inc. plans
to cut at least 10,000 jobs,
or 6 percent of its work
force, by the end of next year
to compensate for a drop in
the number of local-telephone
customers." + ], + [ + "afrol News, 4 October -
Hundred years of conservation
efforts have lifted the
southern black rhino
population from about hundred
to 11,000 animals." + ], + [ + "THE death of Philippine movie
star and defeated presidential
candidate Fernando Poe has
drawn some political backlash,
with some people seeking to
use his sudden demise as a
platform to attack President
Gloria Arroyo." + ], + [ + " WASHINGTON (Reuters) - Major
cigarette makers go on trial
on Tuesday in the U.S.
government's \\$280 billion
racketeering case that
charges the tobacco industry
with deliberately deceiving
the public about the risks of
smoking since the 1950s." + ], + [ + "More Americans are quitting
their jobs and taking the risk
of starting a business despite
a still-lackluster job market." + ], + [ + "AP - Coach Tyrone Willingham
was fired by Notre Dame on
Tuesday after three seasons in
which he failed to return one
of the nation's most storied
football programs to
prominence." + ], + [ + "COLLEGE PARK, Md. -- Joel
Statham completed 18 of 25
passes for 268 yards and two
touchdowns in No. 23
Maryland's 45-22 victory over
Temple last night, the
Terrapins' 12th straight win
at Byrd Stadium." + ], + [ + "Manchester United boss Sir
Alex Ferguson wants the FA to
punish Arsenal good guy Dennis
Bergkamp for taking a swing at
Alan Smith last Sunday." + ], + [ + "VIENTIANE, Laos China moved
yet another step closer in
cementing its economic and
diplomatic relationships with
Southeast Asia today when
Prime Minister Wen Jiabao
signed a trade accord at a
regional summit that calls for
zero tariffs on a wide range
of" + ], + [ + "SPACE.com - With nbsp;food
stores nbsp;running low, the
two \\astronauts living aboard
the International Space
Station (ISS) are cutting back
\\their meal intake and
awaiting a critical cargo
nbsp;delivery expected to
arrive \\on Dec. 25." + ], + [ + "British judges in London
Tuesday ordered radical Muslim
imam Abu Hamza to stand trial
for soliciting murder and
inciting racial hatred." + ], + [ + "Federal Reserve policy-makers
raised the benchmark US
interest rate a quarter point
to 2.25 per cent and restated
a plan to carry out" + ], + [ + " DETROIT (Reuters) - A
Canadian law firm on Tuesday
said it had filed a lawsuit
against Ford Motor Co. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=F
.N target=/stocks/quickinfo/fu
llquote\">F.N</A> over
what it claims are defective
door latches on about 400,000
of the automaker's popular
pickup trucks and SUVs." + ], + [ + "Published reports say Barry
Bonds has testified that he
used a clear substance and a
cream given to him by a
trainer who was indicted in a
steroid-distribution ring." + ], + [ + "SARASOTA, Fla. - The
devastation brought on by
Hurricane Charley has been
especially painful for an
elderly population that is
among the largest in the
nation..." + ], + [ + " ATHENS (Reuters) - Christos
Angourakis added his name to
Greece's list of Paralympic
medal winners when he claimed
a bronze in the T53 shot put
competition Thursday." + ], + [ + " quot;He is charged for having
a part in the Bali incident,
quot; state prosecutor Andi
Herman told Reuters on
Saturday. bombing attack at
the US-run JW." + ], + [ + "Jay Fiedler threw for one
touchdown, Sage Rosenfels
threw for another and the
Miami Dolphins got a victory
in a game they did not want to
play, beating the New Orleans
Saints 20-19 Friday night." + ], + [ + " NEW YORK (Reuters) - Terrell
Owens scored three touchdowns
and the Philadelphia Eagles
amassed 35 first-half points
on the way to a 49-21
drubbing of the Dallas Cowboys
in Irving, Texas, Monday." + ], + [ + "AstraZeneca Plc suffered its
third setback in two months on
Friday as lung cancer drug
Iressa failed to help patients
live longer" + ], + [ + "Virgin Electronics hopes its
slim Virgin Player, which
debuts today and is smaller
than a deck of cards, will
rise as a lead competitor to
Apple's iPod." + ], + [ + "Researchers train a monkey to
feed itself by guiding a
mechanical arm with its mind.
It could be a big step forward
for prosthetics. By David
Cohn." + ], + [ + "Bruce Wasserstein, the
combative chief executive of
investment bank Lazard, is
expected to agree this week
that he will quit the group
unless he can pull off a
successful" + ], + [ + "A late strike by Salomon Kalou
sealed a 2-1 win for Feyenoord
over NEC Nijmegen, while
second placed AZ Alkmaar
defeated ADO Den Haag 2-0 in
the Dutch first division on
Sunday." + ], + [ + "The United Nations called on
Monday for an immediate
ceasefire in eastern Congo as
fighting between rival army
factions flared for a third
day." + ], + [ + "What a disgrace Ron Artest has
become. And the worst part is,
the Indiana Pacers guard just
doesn #39;t get it. Four days
after fueling one of the
biggest brawls in the history
of pro sports, Artest was on
national" + ], + [ + "Allowing dozens of casinos to
be built in the UK would bring
investment and thousands of
jobs, Tony Blair says." + ], + [ + "The shock here was not just
from the awful fact itself,
that two vibrant young Italian
women were kidnapped in Iraq,
dragged from their office by
attackers who, it seems, knew
their names." + ], + [ + "Tehran/Vianna, Sept. 19 (NNN):
Iran on Sunday rejected the
International Atomic Energy
Agency (IAEA) call to suspend
of all its nuclear activities,
saying that it will not agree
to halt uranium enrichment." + ], + [ + "A 15-year-old Argentine
student opened fire at his
classmates on Tuesday in a
middle school in the south of
the Buenos Aires province,
leaving at least four dead and
five others wounded, police
said." + ], + [ + "Dr. David J. Graham, the FDA
drug safety reviewer who
sounded warnings over five
drugs he felt could become the
next Vioxx has turned to a
Whistleblower protection group
for legal help." + ], + [ + "AP - Scientists in 17
countries will scout waterways
to locate and study the
world's largest freshwater
fish species, many of which
are declining in numbers,
hoping to learn how to better
protect them, researchers
announced Thursday." + ], + [ + "AP - Google Inc.'s plans to
move ahead with its initial
public stock offering ran into
a roadblock when the
Securities and Exchange
Commission didn't approve the
Internet search giant's
regulatory paperwork as
requested." + ], + [ + "Jenson Button has revealed
dissatisfaction with the way
his management handled a
fouled switch to Williams. Not
only did the move not come
off, his reputation may have
been irreparably damaged amid
news headline" + ], + [ + "The Kmart purchase of Sears,
Roebuck may be the ultimate
expression of that old saying
in real estate: location,
location, location." + ], + [ + "Citing security risks, a state
university is urging students
to drop Internet Explorer in
favor of alternative Web
browsers such as Firefox and
Safari." + ], + [ + "Redknapp and his No2 Jim Smith
resigned from Portsmouth
yesterday, leaving
controversial new director
Velimir Zajec in temporary
control." + ], + [ + "Despite being ranked eleventh
in the world in broadband
penetration, the United States
is rolling out high-speed
services on a quot;reasonable
and timely basis to all
Americans, quot; according to
a new report narrowly approved
today by the Federal
Communications" + ], + [ + "Sprint Corp. (FON.N: Quote,
Profile, Research) on Friday
said it plans to cut up to 700
jobs as it realigns its
business to focus on wireless
and Internet services and
takes a non-cash network
impairment charge." + ], + [ + "As the season winds down for
the Frederick Keys, Manager
Tom Lawless is starting to
enjoy the progress his
pitching staff has made this
season." + ], + [ + "Britain #39;s Bradley Wiggins
won the gold medal in men
#39;s individual pursuit
Saturday, finishing the
4,000-meter final in 4:16." + ], + [ + "And when David Akers #39;
50-yard field goal cleared the
crossbar in overtime, they did
just that. They escaped a
raucous Cleveland Browns
Stadium relieved but not
broken, tested but not
cracked." + ], + [ + "NEW YORK - A cable pay-per-
view company has decided not
to show a three-hour election
eve special with filmmaker
Michael Moore that included a
showing of his documentary
\"Fahrenheit 9/11,\" which is
sharply critical of President
Bush. The company, iN
DEMAND, said Friday that its
decision is due to \"legitimate
business and legal concerns.\"
A spokesman would not
elaborate..." + ], + [ + "Democracy candidates picked up
at least one more seat in
parliament, according to exit
polls." + ], + [ + "The IOC wants suspended
Olympic member Ivan Slavkov to
be thrown out of the
organisation." + ], + [ + " BANGKOK (Reuters) - The
ouster of Myanmar's prime
minister, architect of a
tentative \"roadmap to
democracy,\" has dashed faint
hopes for an end to military
rule and leaves Southeast
Asia's policy of constructive
engagement in tatters." + ], + [ + "San Antonio, TX (Sports
Network) - Dean Wilson shot a
five-under 65 on Friday to
move into the lead at the
halfway point of the Texas
Open." + ], + [ + "Now that Chelsea have added
Newcastle United to the list
of clubs that they have given
what for lately, what price
Jose Mourinho covering the
Russian-funded aristocrats of
west London in glittering
glory to the tune of four
trophies?" + ], + [ + "AP - Former Seattle Seahawks
running back Chris Warren has
been arrested in Virginia on a
federal warrant, accused of
failing to pay #36;137,147 in
child support for his two
daughters in Washington state." + ], + [ + "The anguish of hostage Kenneth
Bigley in Iraq hangs over
Prime Minister Tony Blair
today as he faces the twin
test of a local election and a
debate by his Labour Party
about the divisive war." + ], + [ + "Says that amount would have
been earned for the first 9
months of 2004, before AT
amp;T purchase. LOS ANGELES,
(Reuters) - Cingular Wireless
would have posted a net profit
of \\$650 million for the first
nine months" + ], + [ + "NewsFactor - Taking an
innovative approach to the
marketing of high-performance
\\computing, Sun Microsystems
(Nasdaq: SUNW) is offering its
N1 Grid program in a pay-for-
use pricing model that mimics
the way such commodities as
electricity and wireless phone
plans are sold." + ], + [ + " CHICAGO (Reuters) - Goodyear
Tire Rubber Co. <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=GT.N t
arget=/stocks/quickinfo/fullqu
ote\">GT.N</A> said
on Friday it will cut 340 jobs
in its engineered products and
chemical units as part of its
cost-reduction efforts,
resulting in a third-quarter
charge." + ], + [ + " quot;There were 16 people
travelling aboard. ... It
crashed into a mountain, quot;
Col. Antonio Rivero, head of
the Civil Protection service,
told." + ], + [ + "Reuters - Shares of long-
distance phone\\companies AT T
Corp. and MCI Inc. have
plunged\\about 20 percent this
year, but potential buyers
seem to be\\holding out for
clearance sales." + ], + [ + "Reuters - Madonna and m-Qube
have\\made it possible for the
star's North American fans to
download\\polyphonic ring tones
and other licensed mobile
content from\\her official Web
site, across most major
carriers and without\\the need
for a credit card." + ], + [ + "President Bush is reveling in
winning the popular vote and
feels he can no longer be
considered a one-term accident
of history." + ], + [ + "Russian oil giant Yukos files
for bankruptcy protection in
the US in a last ditch effort
to stop the Kremlin auctioning
its main production unit." + ], + [ + "British Airways #39; (BA)
chief executive Rod Eddington
has admitted that the company
quot;got it wrong quot; after
staff shortages led to three
days of travel chaos for
passengers." + ], + [ + "It #39;s official: US Open had
never gone into the third
round with only two American
men, including the defending
champion, Andy Roddick." + ], + [ + "Canadian Press - TORONTO (CP)
- Thousands of Royal Bank
clerks are being asked to
display rainbow stickers at
their desks and cubicles to
promote a safe work
environment for gays,
lesbians, and bisexuals." + ], + [ + "The chipmaker is back on a
buying spree, having scooped
up five other companies this
year." + ], + [ + "The issue of drug advertising
directly aimed at consumers is
becoming political." + ], + [ + "WASHINGTON, Aug. 17
(Xinhuanet) -- England coach
Sven-Goran Eriksson has urged
the international soccer
authorities to preserve the
health of the world superstar
footballers for major
tournaments, who expressed his
will in Slaley of England on
Tuesday ..." + ], + [ + " BAGHDAD (Reuters) - A car
bomb killed two American
soldiers and wounded eight
when it exploded in Baghdad on
Saturday, the U.S. military
said in a statement." + ], + [ + "Juventus coach Fabio Capello
has ordered his players not to
kick the ball out of play when
an opponent falls to the
ground apparently hurt because
he believes some players fake
injury to stop the match." + ], + [ + "AP - China's economic boom is
still roaring despite efforts
to cool sizzling growth, with
the gross domestic product
climbing 9.5 percent in the
first three quarters of this
year, the government reported
Friday." + ], + [ + "Soaring petroleum prices
pushed the cost of goods
imported into the U.S. much
higher than expected in
August, the government said
today." + ], + [ + "Anheuser-Busch teams up with
Vietnam's largest brewer,
laying the groundwork for
future growth in the region." + ], + [ + "You #39;re angry. You want to
lash out. The Red Sox are
doing it to you again. They
#39;re blowing a playoff
series, and to the Yankees no
less." + ], + [ + "TORONTO -- There is no
mystique to it anymore,
because after all, the
Russians have become commoners
in today's National Hockey
League, and Finns, Czechs,
Slovaks, and Swedes also have
been entrenched in the
Original 30 long enough to
turn the ongoing World Cup of
Hockey into a protracted
trailer for the NHL season." + ], + [ + "Sudanese authorities have
moved hundreds of pro-
government fighters from the
crisis-torn Darfur region to
other parts of the country to
keep them out of sight of
foreign military observers
demanding the militia #39;s
disarmament, a rebel leader
charged" + ], + [ + "CHARLOTTE, NC - Shares of
Krispy Kreme Doughnuts Inc.
#39;s fell sharply Monday as a
79 percent plunge in third-
quarter earnings and an
intensifying accounting
investigation overshadowed the
pastrymaker #39;s statement
that the low-carb craze might
be easing." + ], + [ + "The company hopes to lure
software partners by promising
to save them from
infrastructure headaches." + ], + [ + "BAGHDAD, Iraq - A car bomb
Tuesday ripped through a busy
market near a Baghdad police
headquarters where Iraqis were
waiting to apply for jobs on
the force, and gunmen opened
fire on a van carrying police
home from work in Baqouba,
killing at least 59 people
total and wounding at least
114. The attacks were the
latest attempts by militants
to wreck the building of a
strong Iraqi security force, a
keystone of the U.S..." + ], + [ + "The Israeli prime minister
said today that he wanted to
begin withdrawing settlers
from Gaza next May or June." + ], + [ + "Indianapolis, IN (Sports
Network) - The Indiana Pacers
try to win their second
straight game tonight, as they
host Kevin Garnett and the
Minnesota Timberwolves in the
third of a four-game homestand
at Conseco Fieldhouse." + ], + [ + "OXNARD - A leak of explosive
natural gas forced dozens of
workers to evacuate an
offshore oil platform for
hours Thursday but no damage
or injuries were reported." + ], + [ + "AP - Assets of the nation's
retail money market mutual
funds rose by #36;2.85
billion in the latest week to
#36;845.69 billion, the
Investment Company Institute
said Thursday." + ], + [ + "Peter Mandelson provoked fresh
Labour in-fighting yesterday
with an implied attack on
Gordon Brown #39;s
quot;exaggerated gloating
quot; about the health of the
British economy." + ], + [ + "Queen Elizabeth II stopped
short of apologizing for the
Allies #39; bombing of Dresden
during her first state visit
to Germany in 12 years and
instead acknowledged quot;the
appalling suffering of war on
both sides." + ], + [ + "JC Penney said yesterday that
Allen I. Questrom, the chief
executive who has restyled the
once-beleaguered chain into a
sleeker and more profitable
entity, would be succeeded by
Myron E. Ullman III, another
longtime retail executive." + ], + [ + " In the cosmetics department
at Hecht's in downtown
Washington, construction crews
have ripped out the
traditional glass display
cases, replacing them with a
system of open shelves stacked
high with fragrances from
Chanel, Burberry and Armani,
now easily within arm's reach
of the impulse buyer." + ], + [ + " LONDON (Reuters) - Oil prices
slid from record highs above
\\$50 a barrel Wednesday as the
U.S. government reported a
surprise increase in crude
stocks and rebels in Nigeria's
oil-rich delta region agreed
to a preliminary cease-fire." + ], + [ + "Rocky Shoes and Boots makes an
accretive acquisition -- and
gets Dickies and John Deere as
part of the deal." + ], + [ + "AP - Fugitive Taliban leader
Mullah Mohammed Omar has
fallen out with some of his
lieutenants, who blame him for
the rebels' failure to disrupt
the landmark Afghan
presidential election, the
U.S. military said Wednesday." + ], + [ + "HAVANA -- Cuban President
Fidel Castro's advancing age
-- and ultimately his
mortality -- were brought home
yesterday, a day after he
fractured a knee and arm when
he tripped and fell at a
public event." + ], + [ + " BRASILIA, Brazil (Reuters) -
The United States and Brazil
predicted on Tuesday Latin
America's largest country
would resolve a dispute with
the U.N. nuclear watchdog over
inspections of a uranium
enrichment plant." + ], + [ + "Call it the Maximus factor.
Archaeologists working at the
site of an old Roman temple
near Guy #39;s hospital in
London have uncovered a pot of
cosmetic cream dating back to
AD2." + ], + [ + "It is a team game, this Ryder
Cup stuff that will commence
Friday at Oakland Hills
Country Club. So what are the
teams? For the Americans,
captain Hal Sutton isn't
saying." + ], + [ + "Two bombs exploded today near
a tea shop and wounded 20
people in southern Thailand,
police said, as violence
continued unabated in the
Muslim-majority region where
residents are seething over
the deaths of 78 detainees
while in military custody." + ], + [ + "BONN: Deutsche Telekom is
bidding 2.9 bn for the 26 it
does not own in T-Online
International, pulling the
internet service back into the
fold four years after selling
stock to the public." + ], + [ + "Motorola Inc. says it #39;s
ready to make inroads in the
cell-phone market after
posting a third straight
strong quarter and rolling out
a series of new handsets in
time for the holiday selling
season." + ], + [ + "Prime Minister Junichiro
Koizumi, back in Tokyo after
an 11-day diplomatic mission
to the Americas, hunkered down
with senior ruling party
officials on Friday to focus
on a major reshuffle of
cabinet and top party posts." + ], + [ + "Costs of employer-sponsored
health plans are expected to
climb an average of 8 percent
in 2005, the first time in
five years increases have been
in single digits." + ], + [ + "NEW YORK - A sluggish gross
domestic product reading was
nonetheless better than
expected, prompting investors
to send stocks slightly higher
Friday on hopes that the
economic slowdown would not be
as bad as first thought.
The 2.8 percent GDP growth in
the second quarter, a revision
from the 3 percent preliminary
figure reported in July, is a
far cry from the 4.5 percent
growth in the first quarter..." + ], + [ + "After again posting record
earnings for the third
quarter, Taiwan Semiconductor
Manufacturing Company (TSMC)
expects to see its first
sequential drop in fourth-
quarter revenues, coupled with
a sharp drop in capacity
utilization rates." + ], + [ + " SEOUL (Reuters) - A huge
explosion in North Korea last
week may have been due to a
combination of demolition work
for a power plant and
atmospheric clouds, South
Korea's spy agency said on
Wednesday." + ], + [ + "The deal comes as Cisco pushes
to develop a market for CRS-1,
a new line of routers aimed at
telephone, wireless and cable
companies." + ], + [ + "Many people who have never
bounced a check in their life
could soon bounce their first
check if they write checks to
pay bills a couple of days
before their paycheck is
deposited into their checking
account." + ], + [ + " LUXEMBOURG (Reuters) -
Microsoft Corp told a judge on
Thursday that the European
Commission must be stopped
from ordering it to give up
secret technology to
competitors." + ], + [ + "SEPTEMBER 14, 2004 (IDG NEWS
SERVICE) - Sun Microsystems
Inc. and Microsoft Corp. next
month plan to provide more
details on the work they are
doing to make their products
interoperable, a Sun executive
said yesterday." + ], + [ + "MANCHESTER United today
dramatically rejected the
advances of Malcolm Glazer,
the US sports boss who is
mulling an 825m bid for the
football club." + ], + [ + "Dow Jones Industrial Average
futures declined amid concern
an upcoming report on
manufacturing may point to
slowing economic growth." + ], + [ + "AP - Astronomy buffs and
amateur stargazers turned out
to watch a total lunar eclipse
Wednesday night #151; the
last one Earth will get for
nearly two and a half years." + ], + [ + "Reuters - U.S. industrial
output advanced in\\July, as
American factories operated at
their highest capacity\\in more
than three years, a Federal
Reserve report on
Tuesday\\showed." + ], + [ + "As usual, the Big Ten coaches
were out in full force at
today #39;s Big Ten
Teleconference. Both OSU head
coach Jim Tressel and Iowa
head coach Kirk Ferentz
offered some thoughts on the
upcoming game between OSU" + ], + [ + "Sir Martin Sorrell, chief
executive of WPP, yesterday
declared he was quot;very
impressed quot; with Grey
Global, stoking speculation
WPP will bid for the US
advertising company." + ], + [ + "The compact disc has at least
another five years as the most
popular music format before
online downloads chip away at
its dominance, a new study
said on Tuesday." + ], + [ + "New York Knicks #39; Stephon
Marbury (3) fires over New
Orleans Hornets #39; Dan
Dickau (2) during the second
half in New Orleans Wednesday
night, Dec. 8, 2004." + ], + [ + "AP - Sudan's U.N. ambassador
challenged the United States
to send troops to the Darfur
region if it really believes a
genocide is taking place as
the U.S. Congress and
President Bush's
administration have
determined." + ], + [ + "At the very moment when the
Red Sox desperately need
someone slightly larger than
life to rally around, they
suddenly have the man for the
job: Thrilling Schilling." + ], + [ + "Does Your Site Need a Custom
Search Engine
Toolbar?\\\\Today's surfers
aren't always too comfortable
installing software on their
computers. Especially free
software that they don't
necessarily understand. With
all the horror stories of
viruses, spyware, and adware
that make the front page these
days, it's no wonder. So is
there ..." + ], + [ + "Dale Earnhardt Jr, right,
talks with Matt Kenseth, left,
during a break in practice at
Lowe #39;s Motor Speedway in
Concord, NC, Thursday Oct. 14,
2004 before qualifying for
Saturday #39;s UAW-GM Quality
500 NASCAR Nextel Cup race." + ], + [ + "Several starting spots may
have been usurped or at least
threatened after relatively
solid understudy showings
Sunday, but few players
welcome the kind of shot
delivered to Oakland" + ], + [ + "TEMPE, Ariz. -- Neil Rackers
kicked four field goals and
the Arizona Cardinals stifled
rookie Chris Simms and the
rest of the Tampa Bay offense
for a 12-7 victory yesterday
in a matchup of two sputtering
teams out of playoff
contention. Coach Jon Gruden's
team lost its fourth in a row
to finish 5-11, Tampa Bay's
worst record since going ..." + ], + [ + "AFP - A junior British
minister said that people
living in camps after fleeing
their villages because of
conflict in Sudan's Darfur
region lived in fear of
leaving their temporary homes
despite a greater presence now
of aid workers." + ], + [ + "US Deputy Secretary of State
Richard Armitage (L) shakes
hands with Pakistani Foreign
Minister Khurshid Kasuri prior
to their meeting in Islamabad,
09 November 2004." + ], + [ + "Spammers aren't ducking
antispam laws by operating
offshore, they're just
ignoring it." + ], + [ + "AP - Biologists plan to use
large nets and traps this week
in Chicago's Burnham Harbor to
search for the northern
snakehead #151; a type of
fish known for its voracious
appetite and ability to wreak
havoc on freshwater
ecosystems." + ], + [ + "BAE Systems PLC and Northrop
Grumman Corp. won \\$45 million
contracts yesterday to develop
prototypes of anti-missile
technology that could protect
commercial aircraft from
shoulder-fired missiles." + ], + [ + "AP - Democrat John Edwards
kept up a long-distance debate
over his \"two Americas\"
campaign theme with Vice
President Dick Cheney on
Tuesday, saying it was no
illusion to thousands of laid-
off workers in Ohio." + ], + [ + "Like introductory credit card
rates and superior customer
service, some promises just
aren #39;t built to last. And
so it is that Bank of America
- mere months after its pledge
to preserve" + ], + [ + "A group of 76 Eritreans on a
repatriation flight from Libya
Friday forced their plane to
change course and land in the
Sudanese capital, Khartoum,
where they" + ], + [ + "Reuters - Accounting firm KPMG
will pay #36;10\\million to
settle charges of improper
professional conduct\\while
acting as auditor for Gemstar-
TV Guide International Inc.\\,
the U.S. Securities and
Exchange Commission said
on\\Wednesday." + ], + [ + "Family matters made public: As
eager cousins wait for a slice
of the \\$15 billion cake that
is the Pritzker Empire,
Circuit Court Judge David
Donnersberger has ruled that
the case will be conducted in
open court." + ], + [ + "Users of Microsoft #39;s
Hotmail, most of whom are
accustomed to getting regular
sales pitches for premium
e-mail accounts, got a
pleasant surprise in their
inboxes this week: extra
storage for free." + ], + [ + "AP - They say that opposites
attract, and in the case of
Sen. John Kerry and Teresa
Heinz Kerry, that may be true
#151; at least in their public
personas." + ], + [ + "The weekly survey from
mortgage company Freddie Mac
had rates on 30-year fixed-
rate mortgages inching higher
this week, up to an average
5.82 percent from last week
#39;s 5.81 percent." + ], + [ + "The classic power struggle
between Walt Disney Co. CEO
Michael Eisner and former
feared talent agent Michael
Ovitz makes for high drama in
the courtroom - and apparently
on cable." + ], + [ + "ATHENS France, Britain and the
United States issued a joint
challenge Thursday to Germany
#39;s gold medal in equestrian
team three-day eventing." + ], + [ + "LONDON (CBS.MW) -- Elan
(UK:ELA) (ELN) and partner
Biogen (BIIB) said the FDA has
approved new drug Tysabri to
treat relapsing forms of
multiple sclerosis." + ], + [ + "IT services provider
Electronic Data Systems
yesterday reported a net loss
of \\$153 million for the third
quarter, with earnings hit in
part by an asset impairment
charge of \\$375 million
connected with EDS's N/MCI
project." + ], + [ + "ATLANTA - Who could have
imagined Tommy Tuberville in
this position? Yet there he
was Friday, standing alongside
the SEC championship trophy,
posing for pictures and
undoubtedly chuckling a bit on
the inside." + ], + [ + "AP - Impoverished North Korea
might resort to selling
weapons-grade plutonium to
terrorists for much-needed
cash, and that would be
\"disastrous for the world,\"
the top U.S. military
commander in South Korea said
Friday." + ], + [ + "International Business
Machines Corp. said Wednesday
it had agreed to settle most
of the issues in a suit over
changes in its pension plans
on terms that allow the
company to continue to appeal
a key question while capping
its liability at \\$1.7
billion. <BR><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-The Washington
Post</B></FONT>" + ], + [ + "Dubai: Osama bin Laden on
Thursday called on his
fighters to strike Gulf oil
supplies and warned Saudi
leaders they risked a popular
uprising in an audio message
said to be by the Western
world #39;s most wanted terror
mastermind." + ], + [ + "Bank of New Zealand has frozen
all accounts held in the name
of Access Brokerage, which was
yesterday placed in
liquidation after a client
fund shortfall of around \\$5
million was discovered." + ], + [ + "Edward Kozel, Cisco's former
chief technology officer,
joins the board of Linux
seller Red Hat." + ], + [ + "Reuters - International
Business Machines\\Corp. late
on Wednesday rolled out a new
version of its\\database
software aimed at users of
Linux and Unix
operating\\systems that it
hopes will help the company
take away market\\share from
market leader Oracle Corp. ." + ], + [ + "NASA #39;s Mars Odyssey
mission, originally scheduled
to end on Tuesday, has been
granted a stay of execution
until at least September 2006,
reveal NASA scientists." + ], + [ + "ZDNet #39;s survey of IT
professionals in August kept
Wired amp; Wireless on top
for the 18th month in a row.
Telecommunications equipment
maker Motorola said Tuesday
that it would cut 1,000 jobs
and take related" + ], + [ + "John Thomson threw shutout
ball for seven innings
Wednesday night in carrying
Atlanta to a 2-0 blanking of
the New York Mets. New York
lost for the 21st time in 25
games on the" + ], + [ + "BEIJING -- Chinese authorities
have arrested or reprimanded
more than 750 officials in
recent months in connection
with billions of dollars in
financial irregularities
ranging from unpaid taxes to
embezzlement, according to a
report made public yesterday." + ], + [ + "Canadian Press - GAUHATI,
India (AP) - Residents of
northeastern India were
bracing for more violence
Friday, a day after bombs
ripped through two buses and a
grenade was hurled into a
crowded market in attacks that
killed four people and wounded
54." + ], + [ + "Vikram Solanki beat the rain
clouds to register his second
one-day international century
as England won the third one-
day international to wrap up a
series victory." + ], + [ + "Some 28 people are charged
with trying to overthrow
Sudan's President Bashir,
reports say." + ], + [ + "Hong Kong #39;s Beijing-backed
chief executive yesterday
ruled out any early moves to
pass a controversial national
security law which last year
sparked a street protest by
half a million people." + ], + [ + "BRITAIN #39;S largest
financial institutions are
being urged to take lead roles
in lawsuits seeking hundreds
of millions of dollars from
the scandal-struck US
insurance industry." + ], + [ + "Bankrupt UAL Corp. (UALAQ.OB:
Quote, Profile, Research) on
Thursday reported a narrower
third-quarter net loss. The
parent of United Airlines
posted a loss of \\$274
million, or" + ], + [ + "A three-touchdown point spread
and a recent history of late-
season collapses had many
thinking the UCLA football
team would provide little
opposition to rival USC #39;s
march to the BCS-championship
game at the Orange Bowl." + ], + [ + " PHOENIX (Sports Network) -
Free agent third baseman Troy
Glaus is reportedly headed to
the Arizona Diamondbacks." + ], + [ + "The European Union #39;s head
office issued a bleak economic
report Tuesday, warning that
the sharp rise in oil prices
will quot;take its toll quot;
on economic growth next year
while the euro #39;s renewed
climb could threaten crucial
exports." + ], + [ + "Third-string tailback Chris
Markey ran for 131 yards to
lead UCLA to a 34-26 victory
over Oregon on Saturday.
Markey, playing because of an
injury to starter Maurice
Drew, also caught five passes
for 84 yards" + ], + [ + "Though Howard Stern's
defection from broadcast to
satellite radio is still 16
months off, the industry is
already trying to figure out
what will fill the crater in
ad revenue and listenership
that he is expected to leave
behind." + ], + [ + " WASHINGTON (Reuters) - The
United States set final anti-
dumping duties of up to 112.81
percent on shrimp imported
from China and up to 25.76
percent on shrimp from Vietnam
to offset unfair pricing, the
Commerce Department said on
Tuesday." + ], + [ + "Jay Payton #39;s three-run
homer led the San Diego Padres
to a 5-1 win over the San
Francisco Giants in National
League play Saturday, despite
a 701st career blast from
Barry Bonds." + ], + [ + "The optimists among Rutgers
fans were delighted, but the
Scarlet Knights still gave the
pessimists something to worry
about." + ], + [ + "Beer consumption has doubled
over the past five years,
prompting legislators to
implement new rules." + ], + [ + "BusinessWeek Online - The
jubilation that swept East
Germany after the fall of the
Berlin Wall in 1989 long ago
gave way to the sober reality
of globalization and market
forces. Now a decade of
resentment seems to be boiling
over. In Eastern cities such
as Leipzig or Chemnitz,
thousands have taken to the
streets since July to protest
cuts in unemployment benefits,
the main source of livelihood
for 1.6 million East Germans.
Discontent among
reunification's losers fueled
big gains by the far left and
far right in Brandenburg and
Saxony state elections Sept.
19. ..." + ], + [ + "NEW YORK -- When Office Depot
Inc. stores ran an electronics
recycling drive last summer
that accepted everything from
cellphones to televisions,
some stores were overwhelmed
by the amount of e-trash they
received." + ], + [ + "In a discovery sure to set off
a firestorm of debate over
human migration to the western
hemisphere, archaeologists in
South Carolina say they have
uncovered evidence that people
lived in eastern North America
at least 50,000 years ago -
far earlier than" + ], + [ + "AP - Former president Bill
Clinton on Monday helped
launch a new Internet search
company backed by the Chinese
government which says its
technology uses artificial
intelligence to produce better
results than Google Inc." + ], + [ + "The International Monetary
Fund expressed concern Tuesday
about the impact of the
troubles besetting oil major
Yukos on Russia #39;s standing
as a place to invest." + ], + [ + "Perhaps the sight of Maria
Sharapova opposite her tonight
will jog Serena Williams #39;
memory. Wimbledon. The final.
You and Maria." + ], + [ + "At least 79 people were killed
and 74 were missing after some
of the worst rainstorms in
recent years triggered
landslides and flash floods in
southwest China, disaster
relief officials said
yesterday." + ], + [ + "LinuxWorld Conference amp;
Expo will come to Boston for
the first time in February,
underscoring the area's
standing as a hub for the
open-source software being
adopted by thousands of
businesses." + ], + [ + "BANGKOK Thai military aircraft
dropped 100 million paper
birds over southern Thailand
on Sunday in a gesture
intended to promote peace in
mainly Muslim provinces, where
more than 500 people have died
this year in attacks by
separatist militants and" + ], + [ + "Insurgents threatened on
Saturday to cut the throats of
two Americans and a Briton
seized in Baghdad, and
launched a suicide car bomb
attack on Iraqi security
forces in Kirkuk that killed
at least 23 people." + ], + [ + "Five days after making the
putt that won the Ryder Cup,
Colin Montgomerie looked set
to miss the cut at a European
PGA tour event." + ], + [ + "Sun Microsystems plans to
release its Sun Studio 10
development tool in the fourth
quarter of this year,
featuring support for 64-bit
applications running on AMD
Opteron and Nocona processors,
Sun officials said on Tuesday." + ], + [ + "Karachi - Captain Inzamam ul-
Haq and coach Bob Woolmer came
under fire on Thursday for
choosing to bat first on a
tricky pitch after Pakistan
#39;s humiliating defeat in
the ICC Champions Trophy semi-
final." + ], + [ + "Scottish champions Celtic saw
their three-year unbeaten home
record in Europe broken
Tuesday as they lost 3-1 to
Barcelona in the Champions
League Group F opener." + ], + [ + "Interest rates on short-term
Treasury securities rose in
yesterday's auction. The
Treasury Department sold \\$19
billion in three-month bills
at a discount rate of 1.710
percent, up from 1.685 percent
last week. An additional \\$17
billion was sold in six-month
bills at a rate of 1.950
percent, up from 1.870
percent." + ], + [ + "Most IT Managers won #39;t
question the importance of
security, but this priority
has been sliding between the
third and fourth most
important focus for companies." + ], + [ + "AP - Andy Roddick searched out
Carlos Moya in the throng of
jumping, screaming Spanish
tennis players, hoping to
shake hands." + ], + [ + "AP - The Federal Trade
Commission on Thursday filed
the first case in the country
against software companies
accused of infecting computers
with intrusive \"spyware\" and
then trying to sell people the
solution." + ], + [ + "While shares of Apple have
climbed more than 10 percent
this week, reaching 52-week
highs, two research firms told
investors Friday they continue
to remain highly bullish about
the stock." + ], + [ + "Moody #39;s Investors Service
on Wednesday said it may cut
its bond ratings on HCA Inc.
(HCA.N: Quote, Profile,
Research) deeper into junk,
citing the hospital operator
#39;s plan to buy back about
\\$2." + ], + [ + "States are now barred from
imposing telecommunications
regulations on Net phone
providers." + ], + [ + "Telekom Austria AG, the
country #39;s biggest phone
operator, won the right to buy
Bulgaria #39;s largest mobile
phone company, MobilTel EAD,
for 1.6 billion euros (\\$2.1
billion), an acquisition that
would add 3 million
subscribers." + ], + [ + "Shares of ID Biomedical jumped
after the company reported
Monday that it signed long-
term agreements with the three
largest flu vaccine
wholesalers in the United
States in light of the
shortage of vaccine for the
current flu season." + ], + [ + "ENGLAND captain and Real
Madrid midfielder David
Beckham has played down
speculation that his club are
moving for England manager
Sven-Goran Eriksson." + ], + [ + "The federal government closed
its window on the oil industry
Thursday, saying that it is
selling its last 19 per cent
stake in Calgary-based Petro-
Canada." + ], + [ + "Strong sales of new mobile
phone models boosts profits at
Carphone Warehouse but the
retailer's shares fall on
concerns at a decline in
profits from pre-paid phones." + ], + [ + " NEW YORK (Reuters) - IBM and
top scientific research
organizations are joining
forces in a humanitarian
effort to tap the unused
power of millions of computers
and help solve complex social
problems." + ], + [ + " NEW YORK, Nov. 11 -- The 40
percent share price slide in
Merck #38; Co. in the five
weeks after it pulled the
painkiller Vioxx off the
market highlighted larger
problems in the pharmaceutical
industry that may depress
performance for years,
according to academics and
stock analysts who follow the
sector." + ], + [ + "Low-fare carrier Southwest
Airlines Co. said Thursday
that its third-quarter profit
rose 12.3 percent to beat Wall
Street expectations despite
higher fuel costs." + ], + [ + "Another shock hit the drug
sector Friday when
pharmaceutical giant Pfizer
Inc. announced that it found
an increased heart risk to
patients for its blockbuster
arthritis drug Celebrex." + ], + [ + "AFP - National Basketball
Association players trying to
win a fourth consecutive
Olympic gold medal for the
United States have gotten the
wake-up call that the \"Dream
Team\" days are done even if
supporters have not." + ], + [ + " BEIJING (Reuters) - Secretary
of State Colin Powell will
urge China Monday to exert its
influence over North Korea to
resume six-party negotiations
on scrapping its suspected
nuclear weapons program." + ], + [ + "Reuters - An Israeli missile
strike killed at least\\two
Hamas gunmen in Gaza City
Monday, a day after a
top\\commander of the Islamic
militant group was killed in a
similar\\attack, Palestinian
witnesses and security sources
said." + ], + [ + "England will be seeking their
third clean sweep of the
summer when the NatWest
Challenge against India
concludes at Lord #39;s. After
beating New Zealand and West
Indies 3-0 and 4-0 in Tests,
they have come alive" + ], + [ + "AP - The Pentagon has restored
access to a Web site that
assists soldiers and other
Americans living overseas in
voting, after receiving
complaints that its security
measures were preventing
legitimate voters from using
it." + ], + [ + "The number of US information
technology workers rose 2
percent to 10.5 million in the
first quarter of this year,
but demand for them is
dropping, according to a new
report." + ], + [ + "Montreal - The British-built
Canadian submarine HMCS
Chicoutimi, crippled since a
fire at sea that killed one of
its crew, is under tow and is
expected in Faslane, Scotland,
early next week, Canadian
naval commander Tyrone Pile
said on Friday." + ], + [ + "AP - Grizzly and black bears
killed a majority of elk
calves in northern Yellowstone
National Park for the second
year in a row, preliminary
study results show." + ], + [ + "Thirty-five Pakistanis freed
from the US Guantanamo Bay
prison camp arrived home on
Saturday and were taken
straight to prison for further
interrogation, the interior
minister said." + ], + [ + "AP - Mark Richt knows he'll
have to get a little creative
when he divvies up playing
time for Georgia's running
backs next season. Not so on
Saturday. Thomas Brown is the
undisputed starter for the
biggest game of the season." + ], + [ + "Witnesses in the trial of a US
soldier charged with abusing
prisoners at Abu Ghraib have
told the court that the CIA
sometimes directed abuse and
orders were received from
military command to toughen
interrogations." + ], + [ + "Insurance firm says its board
now consists of its new CEO
Michael Cherkasky and 10
outside members. NEW YORK
(Reuters) - Marsh amp;
McLennan Cos." + ], + [ + "Michael Phelps, the six-time
Olympic champion, issued an
apology yesterday after being
arrested and charged with
drunken driving in the United
States." + ], + [ + "PC World - The one-time World
Class Product of the Year PDA
gets a much-needed upgrade." + ], + [ + "AP - Italian and Lebanese
authorities have arrested 10
suspected terrorists who
planned to blow up the Italian
Embassy in Beirut, an Italian
news agency and the Defense
Ministry said Tuesday." + ], + [ + "CORAL GABLES, Fla. -- John F.
Kerry and President Bush
clashed sharply over the war
in Iraq last night during the
first debate of the
presidential campaign season,
with the senator from
Massachusetts accusing" + ], + [ + "GONAIVES, Haiti -- While
desperately hungry flood
victims wander the streets of
Gonaives searching for help,
tons of food aid is stacking
up in a warehouse guarded by
United Nations peacekeepers." + ], + [ + "As part of its much-touted new
MSN Music offering, Microsoft
Corp. (MSFT) is testing a Web-
based radio service that
mimics nearly 1,000 local
radio stations, allowing users
to hear a version of their
favorite radio station with
far fewer interruptions." + ], + [ + "AFP - The resignation of
disgraced Fiji Vice-President
Ratu Jope Seniloli failed to
quell anger among opposition
leaders and the military over
his surprise release from
prison after serving just
three months of a four-year
jail term for his role in a
failed 2000 coup." + ], + [ + "ATHENS Larry Brown, the US
coach, leaned back against the
scorer #39;s table, searching
for support on a sinking ship.
His best player, Tim Duncan,
had just fouled out, and the
options for an American team
that" + ], + [ + "Sourav Ganguly files an appeal
against a two-match ban
imposed for time-wasting." + ], + [ + "Ziff Davis - A quick
resolution to the Mambo open-
source copyright dispute seems
unlikely now that one of the
parties has rejected an offer
for mediation." + ], + [ + "Greek police surrounded a bus
full of passengers seized by
armed hijackers along a
highway from an Athens suburb
Wednesday, police said." + ], + [ + "Spain have named an unchanged
team for the Davis Cup final
against the United States in
Seville on 3-5 December.
Carlos Moya, Juan Carlos
Ferrero, Rafael Nadal and
Tommy Robredo will take on the
US in front of 22,000 fans at
the converted Olympic stadium." + ], + [ + "BAGHDAD: A suicide car bomber
attacked a police convoy in
Baghdad yesterday as guerillas
kept pressure on Iraq #39;s
security forces despite a
bloody rout of insurgents in
Fallujah." + ], + [ + "Slot machine maker
International Game Technology
(IGT.N: Quote, Profile,
Research) on Tuesday posted
better-than-expected quarterly
earnings, as casinos bought" + ], + [ + "Fixtures and fittings from
Damien Hirst's restaurant
Pharmacy sell for 11.1, 8m
more than expected." + ], + [ + "Last Tuesday night, Harvard
knocked off rival Boston
College, which was ranked No.
1 in the country. Last night,
the Crimson knocked off
another local foe, Boston
University, 2-1, at Walter
Brown Arena, which marked the
first time since 1999 that
Harvard had beaten them both
in the same season." + ], + [ + "Venezuelan election officials
say they expect to announce
Saturday, results of a partial
audit of last Sunday #39;s
presidential recall
referendum." + ], + [ + "About 500 prospective jurors
will be in an Eagle, Colorado,
courtroom Friday, answering an
82-item questionnaire in
preparation for the Kobe
Bryant sexual assault trial." + ], + [ + "Students take note - endless
journeys to the library could
become a thing of the past
thanks to a new multimillion-
pound scheme to make classic
texts available at the click
of a mouse." + ], + [ + "Moscow - The next crew of the
International Space Station
(ISS) is to contribute to the
Russian search for a vaccine
against Aids, Russian
cosmonaut Salijan Sharipovthe
said on Thursday." + ], + [ + "Locked away in fossils is
evidence of a sudden solar
cooling. Kate Ravilious meets
the experts who say it could
explain a 3,000-year-old mass
migration - and today #39;s
global warming." + ], + [ + "By ANICK JESDANUN NEW YORK
(AP) -- Taran Rampersad didn't
complain when he failed to
find anything on his hometown
in the online encyclopedia
Wikipedia. Instead, he simply
wrote his own entry for San
Fernando, Trinidad and
Tobago..." + ], + [ + "Five years after catastrophic
floods and mudslides killed
thousands along Venezuela's
Caribbean coast, survivors in
this town still see the signs
of destruction - shattered
concrete walls and tall weeds
growing atop streets covered
in dried mud." + ], + [ + "How do you top a battle
between Marines and an alien
religious cult fighting to the
death on a giant corona in
outer space? The next logical
step is to take that battle to
Earth, which is exactly what
Microsoft" + ], + [ + "Sony Ericsson Mobile
Communications Ltd., the
mobile-phone venture owned by
Sony Corp. and Ericsson AB,
said third-quarter profit rose
45 percent on camera phone
demand and forecast this
quarter will be its strongest." + ], + [ + "NEW YORK, September 17
(newratings.com) - Alcatel
(ALA.NYS) has expanded its
operations and presence in the
core North American
telecommunication market with
two separate acquisitions for
about \\$277 million." + ], + [ + "Four U.S. agencies yesterday
announced a coordinated attack
to stem the global trade in
counterfeit merchandise and
pirated music and movies, an
underground industry that law-
enforcement officials estimate
to be worth \\$500 billion each
year." + ], + [ + "BEIJING The NBA has reached
booming, basketball-crazy
China _ but the league doesn
#39;t expect to make any money
soon. The NBA flew more than
100 people halfway around the
world for its first games in
China, featuring" + ], + [ + "The UN nuclear watchdog
confirmed Monday that nearly
400 tons of powerful
explosives that could be used
in conventional or nuclear
missiles disappeared from an
unguarded military
installation in Iraq." + ], + [ + " NEW YORK (Reuters) - Curt
Schilling pitched 6 2/3
innings and Manny Ramirez hit
a three-run homer in a seven-
run fourth frame to lead the
Boston Red Sox to a 9-3 win
over the host Anaheim Angels
in their American League
Divisional Series opener
Tuesday." + ], + [ + "AP - The game between the
Minnesota Twins and the New
York Yankees on Tuesday night
was postponed by rain." + ], + [ + "Oil giant Shell swept aside
nearly 100 years of history
today when it unveiled plans
to merge its UK and Dutch
parent companies. Shell said
scrapping its twin-board
structure" + ], + [ + "Caesars Entertainment Inc. on
Thursday posted a rise in
third-quarter profit as Las
Vegas hotels filled up and
Atlantic City properties
squeaked out a profit that was
unexpected by the company." + ], + [ + "Half of all U.S. Secret
Service agents are dedicated
to protecting President
Washington #151;and all the
other Presidents on U.S.
currency #151;from
counterfeiters." + ], + [ + "One of India #39;s leading
telecommunications providers
will use Cisco Systems #39;
gear to build its new
Ethernet-based broadband
network." + ], + [ + "Militants in Iraq have killed
the second of two US civilians
they were holding hostage,
according to a statement on an
Islamist website." + ], + [ + "PARIS The verdict is in: The
world #39;s greatest race car
driver, the champion of
champions - all disciplines
combined - is Heikki
Kovalainen." + ], + [ + "Pakistan won the toss and
unsurprisingly chose to bowl
first as they and West Indies
did battle at the Rose Bowl
today for a place in the ICC
Champions Trophy final against
hosts England." + ], + [ + "Shares of Beacon Roofing
Suppler Inc. shot up as much
as 26 percent in its trading
debut Thursday, edging out
bank holding company Valley
Bancorp as the biggest gainer
among a handful of new stocks
that went public this week." + ], + [ + "More than 4,000 American and
Iraqi soldiers mounted a
military assault on the
insurgent-held city of
Samarra." + ], + [ + "Maxime Faget conceived and
proposed the development of
the one-man spacecraft used in
Project Mercury, which put the
first American astronauts into
suborbital flight, then
orbital flight" + ], + [ + "The first weekend of holiday
shopping went from red-hot to
dead white, as a storm that
delivered freezing, snowy
weather across Colorado kept
consumers at home." + ], + [ + " MEXICO CITY (Reuters) -
Mexican President Vicente Fox
said on Monday he hoped
President Bush's re-election
meant a bilateral accord on
migration would be reached
before his own term runs out
at the end of 2006." + ], + [ + " LONDON (Reuters) - The dollar
fell within half a cent of
last week's record low against
the euro on Thursday after
capital inflows data added to
worries the United States may
struggle to fund its current
account deficit." + ], + [ + " BRUSSELS (Reuters) - French
President Jacques Chirac said
on Friday he had not refused
to meet Iraqi interim Prime
Minister Iyad Allawi after
reports he was snubbing the
Iraqi leader by leaving an EU
meeting in Brussels early." + ], + [ + "China has pledged to invest
\\$20 billion in Argentina in
the next 10 years, La Nacion
reported Wednesday. The
announcement came during the
first day of a two-day visit" + ], + [ + "NEW YORK - Former President
Bill Clinton was in good
spirits Saturday, walking
around his hospital room in
street clothes and buoyed by
thousands of get-well messages
as he awaited heart bypass
surgery early this coming
week, people close to the
family said. Clinton was
expected to undergo surgery as
early as Monday but probably
Tuesday, said Democratic Party
Chairman Terry McAuliffe, who
said the former president was
\"upbeat\" when he spoke to him
by phone Friday..." + ], + [ + "Toyota Motor Corp., the world
#39;s biggest carmaker by
value, will invest 3.8 billion
yuan (\\$461 million) with its
partner Guangzhou Automobile
Group to boost manufacturing
capacity in" + ], + [ + "Poland will cut its troops in
Iraq early next year and won
#39;t stay in the country
quot;an hour longer quot; than
needed, the country #39;s
prime minister said Friday." + ], + [ + "AP - Boston Red Sox center
fielder Johnny Damon is having
a recurrence of migraine
headaches that first bothered
him after a collision in last
year's playoffs." + ], + [ + "The patch fixes a flaw in the
e-mail server software that
could be used to get access to
in-boxes and information." + ], + [ + "Felix Cardenas of Colombia won
the 17th stage of the Spanish
Vuelta cycling race Wednesday,
while defending champion
Roberto Heras held onto the
overall leader #39;s jersey
for the sixth day in a row." + ], + [ + "AP - Being the biggest dog may
pay off at feeding time, but
species that grow too large
may be more vulnerable to
extinction, new research
suggests." + ], + [ + "A car bomb exploded at a US
military convoy in the
northern Iraqi city of Mosul,
causing several casualties,
the army and Iraqi officials
said." + ], + [ + "Newest Efficeon processor also
offers higher frequency using
less power." + ], + [ + "BAE Systems has launched a
search for a senior American
businessman to become a non-
executive director. The high-
profile appointment is
designed to strengthen the
board at a time when the
defence giant is" + ], + [ + "AP - In what it calls a first
in international air travel,
Finnair says it will let its
frequent fliers check in using
text messages on mobile
phones." + ], + [ + "Computer Associates
International is expected to
announce that its new chief
executive will be John
Swainson, an I.B.M. executive
with strong technical and
sales credentials." + ], + [ + "Established star Landon
Donovan and rising sensation
Eddie Johnson carried the
United States into the
regional qualifying finals for
the 2006 World Cup in emphatic
fashion Wednesday night." + ], + [ + "STOCKHOLM (Dow
Jones)--Expectations for
Telefon AB LM Ericsson #39;s
(ERICY) third-quarter
performance imply that while
sales of mobile telephony
equipment are expected to have
dipped, the company" + ], + [ + "ATHENS, Greece - Michael
Phelps took care of qualifying
for the Olympic 200-meter
freestyle semifinals Sunday,
and then found out he had been
added to the American team for
the evening's 400 freestyle
relay final. Phelps' rivals
Ian Thorpe and Pieter van den
Hoogenband and teammate Klete
Keller were faster than the
teenager in the 200 free
preliminaries..." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
intends to present a timetable
for withdrawal from Gaza to
lawmakers from his Likud Party
Tuesday despite a mutiny in
the right-wing bloc over the
plan." + ], + [ + "Search Engine for Programming
Code\\\\An article at Newsforge
pointed me to Koders (
http://www.koders.com ) a
search engine for finding
programming code. Nifty.\\\\The
front page allows you to
specify keywords, sixteen
languages (from ASP to VB.NET)
and sixteen licenses (from AFL
to ZPL -- fortunately there's
an information page to ..." + ], + [ + " #147;Apple once again was the
star of the show at the annual
MacUser awards, #148; reports
MacUser, #147;taking away six
Maxine statues including
Product of the Year for the
iTunes Music Store. #148;
Other Apple award winners:
Power Mac G5, AirPort Express,
20-inch Apple Cinema Display,
GarageBand and DVD Studio Pro
3. Nov 22" + ], + [ + " KABUL (Reuters) - Three U.N.
workers held by militants in
Afghanistan were in their
third week of captivity on
Friday after calls from both
sides for the crisis to be
resolved ahead of this
weekend's Muslim festival of
Eid al-Fitr." + ], + [ + "AP - A sweeping wildlife
preserve in southwestern
Arizona is among the nation's
10 most endangered refuges,
due in large part to illegal
drug and immigrant traffic and
Border Patrol operations, a
conservation group said
Friday." + ], + [ + "ROME, Oct 29 (AFP) - French
President Jacques Chirac urged
the head of the incoming
European Commission Friday to
take quot;the appropriate
decisions quot; to resolve a
row over his EU executive team
which has left the EU in
limbo." + ], + [ + "The Anglo-Dutch oil giant
Shell today sought to draw a
line under its reserves
scandal by announcing plans to
spend \\$15bn (8.4bn) a year to
replenish reserves and develop
production in its oil and gas
business." + ], + [ + "SEPTEMBER 20, 2004
(COMPUTERWORLD) - At
PeopleSoft Inc. #39;s Connect
2004 conference in San
Francisco this week, the
software vendor is expected to
face questions from users
about its ability to fend off
Oracle Corp." + ], + [ + "Russia's parliament will
launch an inquiry into a
school siege that killed over
300 hostages, President
Vladimir Putin said on Friday,
but analysts doubt it will
satisfy those who blame the
carnage on security services." + ], + [ + "Tony Blair talks to business
leaders about new proposals
for a major shake-up of the
English exam system." + ], + [ + "KUALA LUMPUR, Malaysia A
Malaysian woman has claimed a
new world record after living
with over six-thousand
scorpions for 36 days
straight." + ], + [ + "PBS's Charlie Rose quizzes Sun
co-founder Bill Joy,
Monster.com chief Jeff Taylor,
and venture capitalist John
Doerr." + ], + [ + "Circulation declined at most
major US newspapers in the
last half year, the latest
blow for an industry already
rocked by a scandal involving
circulation misstatements that
has undermined the confidence
of investors and advertisers." + ], + [ + "Skype Technologies is teaming
with Siemens to offer cordless
phone users the ability to
make Internet telephony calls,
in addition to traditional
calls, from their handsets." + ], + [ + "AP - After years of
resistance, the U.S. trucking
industry says it will not try
to impede or delay a new
federal rule aimed at cutting
diesel pollution." + ], + [ + "INDIANAPOLIS - ATA Airlines
has accepted a \\$117 million
offer from Southwest Airlines
that would forge close ties
between two of the largest US
discount carriers." + ], + [ + "could take drastic steps if
the talks did not proceed as
Tehran wants. Mehr news agency
quoted him as saying on
Wednesday. that could be used" + ], + [ + "Wizards coach Eddie Jordan
says the team is making a
statement that immaturity will
not be tolerated by suspending
Kwame Brown one game for not
taking part in a team huddle
during a loss to Denver." + ], + [ + "EVERETT Fire investigators
are still trying to determine
what caused a two-alarm that
destroyed a portion of a South
Everett shopping center this
morning." + ], + [ + "Umesh Patel, a 36-year old
software engineer from
California, debated until the
last minute." + ], + [ + "Sure, the PeopleSoft board
told shareholders to just say
no. This battle will go down
to the wire, and even
afterward Ellison could
prevail." + ], + [ + "Investors cheered by falling
oil prices and an improving
job picture sent stocks higher
Tuesday, hoping that the news
signals a renewal of economic
strength and a fall rally in
stocks." + ], + [ + "AP - Andy Roddick has yet to
face a challenge in his U.S.
Open title defense. He beat
No. 18 Tommy Robredo of Spain
6-3, 6-2, 6-4 Tuesday night to
move into the quarterfinals
without having lost a set." + ], + [ + "Now that hell froze over in
Boston, New England braces for
its rarest season -- a winter
of content. Red Sox fans are
adrift from the familiar
torture of the past." + ], + [ + " CHICAGO (Reuters) - Wal-Mart
Stores Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=WMT.N target
=/stocks/quickinfo/fullquote\"&
gt;WMT.N</A>, the
world's largest retailer, said
on Saturday it still
anticipates September U.S.
sales to be up 2 percent to 4
percent at stores open at
least a year." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei stock average opened
down 0.15 percent on
Wednesday as investors took a
breather from the market's
recent rises and sold shares
of gainers such as Sharp
Corp." + ], + [ + "AP - From now until the start
of winter, male tarantulas are
roaming around, searching for
female mates, an ideal time to
find out where the spiders
flourish in Arkansas." + ], + [ + "Israeli authorities have
launched an investigation into
death threats against Israeli
Prime Minister Ariel Sharon
and other officials supporting
his disengagement plan from
Gaza and parts of the West
Bank, Jerusalem police said
Tuesday." + ], + [ + "NewsFactor - While a U.S.
District Court continues to
weigh the legality of
\\Oracle's (Nasdaq: ORCL)
attempted takeover of
\\PeopleSoft (Nasdaq: PSFT),
Oracle has taken the necessary
steps to ensure the offer does
not die on the vine with
PeopleSoft's shareholders." + ], + [ + "NEW YORK : World oil prices
fell, capping a drop of more
than 14 percent in a two-and-
a-half-week slide triggered by
a perception of growing US
crude oil inventories." + ], + [ + "SUFFOLK -- Virginia Tech
scientists are preparing to
protect the state #39;s
largest crop from a disease
with strong potential to do
damage." + ], + [ + "It was a carefully scripted
moment when Russian President
Vladimir Putin began quoting
Taras Shevchenko, this country
#39;s 19th-century bard,
during a live television" + ], + [ + "China says it welcomes Russia
#39;s ratification of the
Kyoto Protocol that aims to
stem global warming by
reducing greenhouse-gas
emissions." + ], + [ + "Analysts said the smartphone
enhancements hold the
potential to bring PalmSource
into an expanding market that
still has room despite early
inroads by Symbian and
Microsoft." + ], + [ + "The Metropolitan
Transportation Authority is
proposing a tax increase to
raise \\$900 million a year to
help pay for a five-year
rebuilding program." + ], + [ + "AFP - The Canadian armed
forces chief of staff on was
elected to take over as head
of NATO's Military Committee,
the alliance's highest
military authority, military
and diplomatic sources said." + ], + [ + "AFP - Two proposed resolutions
condemning widespread rights
abuses in Sudan and Zimbabwe
failed to pass a UN committee,
mired in debate between
African and western nations." + ], + [ + " NEW YORK (Reuters) -
Citigroup Inc. <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=C.N targe
t=/stocks/quickinfo/fullquote\"
>C.N</A> the world's
largest financial services
company, said on Tuesday it
will acquire First American
Bank in the quickly-growing
Texas market." + ], + [ + " SINGAPORE (Reuters) - U.S.
oil prices hovered just below
\\$50 a barrel on Tuesday,
holding recent gains on a rash
of crude supply outages and
fears over thin heating oil
tanks." + ], + [ + "THE South Africans have called
the Wallabies scrum cheats as
a fresh round of verbal
warfare opened in the Republic
last night." + ], + [ + "The return of noted reformer
Nabil Amr to Palestinian
politics comes at a crucial
juncture." + ], + [ + "An overwhelming majority of
NHL players who expressed
their opinion in a poll said
they would not support a
salary cap even if it meant
saving a season that was
supposed to have started Oct.
13." + ], + [ + "Tony Eury Sr. oversees Dale
Earnhardt Jr. on the
racetrack, but Sunday he
extended his domain to Victory
Lane. Earnhardt was unbuckling
to crawl out of the No." + ], + [ + "AP - After two debates, voters
have seen President Bush look
peevish and heard him pass the
buck. They've watched Sen.
John Kerry deny he's a flip-
flopper and then argue that
Saddam Hussein was a threat
#151; and wasn't. It's no
wonder so few minds have
changed." + ], + [ + "(Sports Network) - The New
York Yankees try to move one
step closer to a division
title when they conclude their
critical series with the
Boston Red Sox at Fenway Park." + ], + [ + "US retail sales fell 0.3 in
August as rising energy costs
and bad weather persuaded
shoppers to reduce their
spending." + ], + [ + "The United States says the
Lebanese parliament #39;s
decision Friday to extend the
term of pro-Syrian President
Emile Lahoud made a
quot;crude mockery of
democratic principles." + ], + [ + "Kurt Busch claimed a stake in
the points lead in the NASCAR
Chase for the Nextel Cup
yesterday, winning the
Sylvania 300 at New Hampshire
International Speedway." + ], + [ + "TOKYO (AFP) - Japan #39;s
Fujitsu and Cisco Systems of
the US said they have agreed
to form a strategic alliance
focusing on routers and
switches that will enable
businesses to build advanced
Internet Protocol (IP)
networks." + ], + [ + "GEORGETOWN, Del., Oct. 28 --
Plaintiffs in a shareholder
lawsuit over former Walt
Disney Co. president Michael
Ovitz's \\$140 million
severance package attempted
Thursday to portray Ovitz as a
dishonest bumbler who botched
the hiring of a major
television executive and
pushed the release of a movie
that angered the Chinese
government, damaging Disney's
business prospects in the
country." + ], + [ + "US stocks gained ground in
early trading Thursday after
tame inflation reports and
better than expected jobless
news. Oil prices held steady
as Hurricane Ivan battered the
Gulf coast, where oil
operations have halted." + ], + [ + "It #39;s a new internet
browser. The first full
version, Firefox 1.0, was
launched earlier this month.
In the sense it #39;s a
browser, yes, but the
differences are larger than
the similarities." + ], + [ + "LOUDON, NH - As this
newfangled stretch drive for
the Nextel Cup championship
ensues, Jeff Gordon has to be
considered the favorite for a
fifth title." + ], + [ + "PepsiCo. Inc., the world #39;s
No. 2 soft- drink maker, plans
to buy General Mills Inc.
#39;s stake in their European
joint venture for \\$750
million in cash, giving it
complete ownership of Europe
#39;s largest snack-food
company." + ], + [ + "Microsoft will accelerate SP2
distribution to meet goal of
100 million downloads in two
months." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks opened little changed
on Friday, after third-
quarter gross domestic product
data showed the U.S. economy
grew at a slower-than-expected
pace." + ], + [ + "International Business
Machines Corp., taken to court
by workers over changes it
made to its traditional
pension plan, has decided to
stop offering any such plan to
new employees." + ], + [ + "NEW YORK - With four of its
executives pleading guilty to
price-fixing charges today,
Infineon will have a hard time
arguing that it didn #39;t fix
prices in its ongoing
litigation with Rambus." + ], + [ + "By nick giongco. YOU KNOW you
have reached the status of a
boxing star when ring
announcer extraordinaire
Michael Buffer calls out your
name in his trademark booming
voice during a high-profile
event like yesterday" + ], + [ + "Canadian Press - OTTAWA (CP) -
The prime minister says he's
been assured by President
George W. Bush that the U.S.
missile defence plan does not
necessarily involve the
weaponization of space." + ], + [ + "AP - Even with a big lead,
Eric Gagne wanted to pitch in
front of his hometown fans one
last time." + ], + [ + " NEW YORK (Reuters) - Limited
Brands Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=LTD.N target
=/stocks/quickinfo/fullquote\"&
gt;LTD.N</A> on
Thursday reported higher
quarterly operating profit as
cost controls and strong
lingerie sales offset poor
results at the retailer's
Express apparel stores." + ], + [ + "General Motors and
DaimlerChrysler are
collaborating on development
of fuel- saving hybrid engines
in hopes of cashing in on an
expanding market dominated by
hybrid leaders Toyota and
Honda." + ], + [ + "ATHENS, Greece -- Larry Brown
was despondent, the head of
the US selection committee was
defensive and an irritated
Allen Iverson was hanging up
on callers who asked what went
wrong." + ], + [ + "Fifty-six miners are dead and
another 92 are still stranded
underground after a gas blast
at the Daping coal mine in
Central China #39;s Henan
Province, safety officials
said Thursday." + ], + [ + "BEIRUT, Lebanon - Three
Lebanese men and their Iraqi
driver have been kidnapped in
Iraq, the Lebanese Foreign
Ministry said Sunday, as
Iraq's prime minister said his
government was working for the
release of two Americans and a
Briton also being held
hostage. Gunmen snatched
the three Lebanese, who worked
for a travel agency with a
branch in Baghdad, as they
drove on the highway between
the capital and Fallujah on
Friday night, a ministry
official said..." + ], + [ + "PORTLAND, Ore. - It's been
almost a year since singer-
songwriter Elliott Smith
committed suicide, and fans
and friends will be looking
for answers as the posthumous
\"From a Basement on the Hill\"
is released..." + ], + [ + "AP - Courtney Brown refuses to
surrender to injuries. He's
already planning another
comeback." + ], + [ + " GAZA (Reuters) - Israel
expanded its military
offensive in northern Gaza,
launching two air strikes
early on Monday that killed
at least three Palestinians
and wounded two, including a
senior Hamas leader, witnesses
and medics said." + ], + [ + " TOKYO (Reuters) - Nintendo
Co. Ltd. raised its 2004
shipment target for its DS
handheld video game device by
40 percent to 2.8 million
units on Thursday after many
stores in Japan and the
United States sold out in the
first week of sales." + ], + [ + "It took an off-the-cuff
reference to a serial
murderer/cannibal to punctuate
the Robby Gordon storyline.
Gordon has been vilified by
his peers and put on probation" + ], + [ + "By BOBBY ROSS JR. Associated
Press Writer. play the Atlanta
Hawks. They will be treated to
free food and drink and have.
their pictures taken with
Mavericks players, dancers and
team officials." + ], + [ + "ARSENAL #39;S Brazilian World
Cup winning midfielder
Gilberto Silva is set to be
out for at least a month with
a back injury, the Premiership
leaders said." + ], + [ + "INDIANAPOLIS -- With a package
of academic reforms in place,
the NCAA #39;s next crusade
will address what its
president calls a dangerous
drift toward professionalism
and sports entertainment." + ], + [ + "Autodesk this week unwrapped
an updated version of its
hosted project collaboration
service targeted at the
construction and manufacturing
industries. Autodesk Buzzsaw
lets multiple, dispersed
project participants --
including building owners,
developers, architects,
construction teams, and
facility managers -- share and
manage data throughout the
life of a project, according
to Autodesk officials." + ], + [ + "AP - It was difficult for
Southern California's Pete
Carroll and Oklahoma's Bob
Stoops to keep from repeating
each other when the two
coaches met Thursday." + ], + [ + "Reuters - Sudan's government
resumed\\talks with rebels in
the oil-producing south on
Thursday while\\the United
Nations set up a panel to
investigate charges
of\\genocide in the west of
Africa's largest country." + ], + [ + "Andy Roddick, along with
Olympic silver medalist Mardy
Fish and the doubles pair of
twins Bob and Mike Bryan will
make up the US team to compete
with Belarus in the Davis Cup,
reported CRIENGLISH." + ], + [ + "NEW YORK - Optimism that the
embattled technology sector
was due for a recovery sent
stocks modestly higher Monday
despite a new revenue warning
from semiconductor company
Broadcom Inc. While
Broadcom, which makes chips
for television set-top boxes
and other electronics, said
high inventories resulted in
delayed shipments, investors
were encouraged as it said
future quarters looked
brighter..." + ], + [ + "A report indicates that many
giants of the industry have
been able to capture lasting
feelings of customer loyalty." + ], + [ + "It is the news that Internet
users do not want to hear: the
worldwide web is in danger of
collapsing around us. Patrick
Gelsinger, the chief
technology officer for
computer chip maker Intel,
told a conference" + ], + [ + " WASHINGTON (Reuters) - U.S.
employers hired just 96,000
workers in September, the
government said on Friday in a
weak jobs snapshot, the final
one ahead of presidential
elections that also fueled
speculation about a pause in
interest-rate rises." + ], + [ + "Cisco Systems CEO John
Chambers said today that his
company plans to offer twice
as many new products this year
as ever before." + ], + [ + "While the world #39;s best
athletes fight the noble
Olympic battle in stadiums and
pools, their fans storm the
streets of Athens, turning the
Greek capital into a huge
international party every
night." + ], + [ + "Carlos Barrios Orta squeezed
himself into his rubber diving
suit, pulled on an 18-pound
helmet that made him look like
an astronaut, then lowered
himself into the sewer. He
disappeared into the filthy
water, which looked like some
cauldron of rancid beef stew,
until the only sign of him was
air bubbles breaking the
surface." + ], + [ + "The mutilated body of a woman
of about sixty years,
amputated of arms and legs and
with the sliced throat, has
been discovered this morning
in a street of the south of
Faluya by the Marines,
according to a statement by
AFP photographer." + ], + [ + "Every day, somewhere in the
universe, there #39;s an
explosion that puts the power
of the Sun in the shade. Steve
Connor investigates the riddle
of gamma-ray bursts." + ], + [ + "Ten months after NASA #39;s
twin rovers landed on Mars,
scientists reported this week
that both robotic vehicles are
still navigating their rock-
studded landscapes with all
instruments operating" + ], + [ + "EVERTON showed they would not
be bullied into selling Wayne
Rooney last night by rejecting
a 23.5million bid from
Newcastle - as Manchester
United gamble on Goodison
#39;s resolve to keep the
striker." + ], + [ + "SAN FRANCISCO (CBS.MW) -- The
magazine known for evaluating
cars and electronics is
setting its sights on finding
the best value and quality of
prescription drugs on the
market." + ], + [ + "After months of legal
wrangling, the case of
<em>People v. Kobe Bean
Bryant</em> commences on
Friday in Eagle, Colo., with
testimony set to begin next
month." + ], + [ + "(SH) - In Afghanistan, Hamid
Karzai defeated a raft of
candidates to win his historic
election. In Iraq, more than
200 political parties have
registered for next month
#39;s elections." + ], + [ + "Lyon coach Paul le Guen has
admitted his side would be
happy with a draw at Old
Trafford on Tuesday night. The
three-times French champions
have assured themselves of
qualification for the
Champions League" + ], + [ + "British scientists say they
have found a new, greener way
to power cars and homes using
sunflower oil, a commodity
more commonly used for cooking
fries." + ], + [ + "A mouse, a house, and your
tax-planning spouse all factor
huge in the week of earnings
that lies ahead." + ], + [ + "The United States #39; top
computer-security official has
resigned after a little more
than a year on the job, the US
Department of Homeland
Security said on Friday." + ], + [ + "Reuters - Barry Bonds failed
to collect a hit in\\his bid to
join the 700-homer club, but
he did score a run to\\help the
San Francisco Giants edge the
host Milwaukee Brewers\\3-2 in
National League action on
Tuesday." + ], + [ + " SAN FRANCISCO (Reuters) -
Intel Corp. <A HREF=\"http:/
/www.reuters.co.uk/financeQuot
eLookup.jhtml?ticker=INTC.O
qtype=sym infotype=info
qcat=news\">INTC.O</A>
on Thursday outlined its
vision of the Internet of the
future, one in which millions
of computer servers would
analyze and direct network
traffic to make the Web safer
and more efficient." + ], + [ + "Margaret Hassan, who works for
charity Care International,
was taken hostage while on her
way to work in Baghdad. Here
are the main events since her
kidnapping." + ], + [ + "Check out new gadgets as
holiday season approaches." + ], + [ + "DALLAS (CBS.MW) -- Royal
Dutch/Shell Group will pay a
\\$120 million penalty to
settle a Securities and
Exchange Commission
investigation of its
overstatement of nearly 4.5
billion barrels of proven
reserves, the federal agency
said Tuesday." + ], + [ + "After waiting an entire summer
for the snow to fall and
Beaver Creek to finally open,
skiers from around the planet
are coming to check out the
Birds of Prey World Cup action
Dec. 1 - 5. Although everyones" + ], + [ + "TOKYO, Sep 08, 2004 (AFX-UK
via COMTEX) -- Sony Corp will
launch its popular projection
televisions with large liquid
crystal display (LCD) screens
in China early next year, a
company spokeswoman said." + ], + [ + "I confess that I am a complete
ignoramus when it comes to
women #39;s beach volleyball.
In fact, I know only one thing
about the sport - that it is
the supreme aesthetic
experience available on planet
Earth." + ], + [ + "Manchester United Plc may
offer US billionaire Malcolm
Glazer a seat on its board if
he agrees to drop a takeover
bid for a year, the Observer
said, citing an unidentified
person in the soccer industry." + ], + [ + "PHOENIX America West Airlines
has backed away from a
potential bidding war for
bankrupt ATA Airlines, paving
the way for AirTran to take
over ATA operations." + ], + [ + "US stock-index futures
declined. Dow Jones Industrial
Average shares including
General Electric Co. slipped
in Europe. Citigroup Inc." + ], + [ + "For a guy who spent most of
his first four professional
seasons on the disabled list,
Houston Astros reliever Brad
Lidge has developed into quite
the ironman these past two
days." + ], + [ + "AFP - Outgoing EU competition
commissioner Mario Monti wants
to reach a decision on
Oracle's proposed takeover of
business software rival
PeopleSoft by the end of next
month, an official said." + ], + [ + "Former Bengal Corey Dillon
found his stride Sunday in his
second game for the New
England Patriots. Dillon
gained 158 yards on 32 carries
as the Patriots beat the
Arizona Cardinals, 23-12, for
their 17th victory in a row." + ], + [ + "If legend is to be believed,
the end of a victorious war
was behind Pheidippides #39;
trek from Marathon to Athens
2,500 years ago." + ], + [ + " WASHINGTON (Reuters) -
Satellite companies would be
able to retransmit
broadcasters' television
signals for another five
years but would have to offer
those signals on a single
dish, under legislation
approved by Congress on
Saturday." + ], + [ + " BEIJING (Reuters) - Wimbledon
champion Maria Sharapova
demolished fellow Russian
Tatiana Panova 6-1, 6-1 to
advance to the quarter-finals
of the China Open on
Wednesday." + ], + [ + "Sven Jaschan, who may face
five years in prison for
spreading the Netsky and
Sasser worms, is now working
in IT security. Photo: AFP." + ], + [ + "Padres general manager Kevin
Towers called Expos general
manager Omar Minaya on
Thursday afternoon and told
him he needed a shortstop
because Khalil Greene had
broken his" + ], + [ + "Motorsport.com. Nine of the
ten Formula One teams have
united to propose cost-cutting
measures for the future, with
the notable exception of
Ferrari." + ], + [ + "Reuters - The United States
modified its\\call for U.N.
sanctions against Sudan on
Tuesday but still kept\\up the
threat of punitive measures if
Khartoum did not
stop\\atrocities in its Darfur
region." + ], + [ + "Human rights and environmental
activists have hailed the
award of the 2004 Nobel Peace
Prize to Wangari Maathai of
Kenya as fitting recognition
of the growing role of civil
society" + ], + [ + "Federal Reserve Chairman Alan
Greenspan has done it again.
For at least the fourth time
this year, he has touched the
electrified third rail of
American politics - Social
Security." + ], + [ + "An Al Qaeda-linked militant
group beheaded an American
hostage in Iraq and threatened
last night to kill another two
Westerners in 24 hours unless
women prisoners were freed
from Iraqi jails." + ], + [ + "TechWeb - An Indian Institute
of Technology professor--and
open-source evangelist--
discusses the role of Linux
and open source in India." + ], + [ + "Here are some of the latest
health and medical news
developments, compiled by
editors of HealthDay: -----
Contaminated Fish in Many U.S.
Lakes and Rivers Fish
that may be contaminated with
dioxin, mercury, PCBs and
pesticides are swimming in
more than one-third of the
United States' lakes and
nearly one-quarter of its
rivers, according to a list of
advisories released by the
Environmental Protection
Agency..." + ], + [ + "Argentina, Denmark, Greece,
Japan and Tanzania on Friday
won coveted two-year terms on
the UN Security Council at a
time when pressure is mounting
to expand the powerful
15-nation body." + ], + [ + "Description: Scientists say
the arthritis drug Bextra may
pose increased risk of
cardiovascular troubles.
Bextra is related to Vioxx,
which was pulled off the
market in September for the
same reason." + ], + [ + "Steve Francis and Shaquille O
#39;Neal enjoyed big debuts
with their new teams. Kobe
Bryant found out he can #39;t
carry the Lakers all by
himself." + ], + [ + "The trial of a lawsuit by Walt
Disney Co. shareholders who
accuse the board of directors
of rubberstamping a deal to
hire Michael Ovitz" + ], + [ + "Australia, by winning the
third Test at Nagpur on
Friday, also won the four-
match series 2-0 with one
match to go. Australia had
last won a Test series in
India way back in December
1969 when Bill Lawry #39;s
team beat Nawab Pataudi #39;s
Indian team 3-1." + ], + [ + "LOS ANGELES Grocery giant
Albertsons says it has
purchased Bristol Farms, which
operates eleven upscale stores
in Southern California." + ], + [ + "ISLAMABAD: Pakistan and India
agreed on Friday to re-open
the Khokhropar-Munabao railway
link, which was severed nearly
40 years ago." + ], + [ + "Final Score: Connecticut 61,
New York 51 New York, NY
(Sports Network) - Nykesha
Sales scored 15 points to lead
Connecticut to a 61-51 win
over New York in Game 1 of
their best-of-three Eastern
Conference Finals series at
Madison Square Garden." + ], + [ + "NEW YORK - Stocks moved higher
Friday as a stronger than
expected retail sales report
showed that higher oil prices
aren't scaring consumers away
from spending. Federal Reserve
Chairman Alan Greenspan's
positive comments on oil
prices also encouraged
investors..." + ], + [ + "The figures from a survey
released today are likely to
throw more people into the
ranks of the uninsured,
analysts said." + ], + [ + "Bill Gates is giving his big
speech right now at Microsofts
big Digital Entertainment
Anywhere event in Los Angeles.
Well have a proper report for
you soon, but in the meantime
we figured wed" + ], + [ + "Spinal and non-spinal
fractures are reduced by
almost a third in women age 80
or older who take a drug
called strontium ranelate,
European investigators
announced" + ], + [ + "JB Oxford Holdings Inc., a
Beverly Hills-based discount
brokerage firm, was sued by
the Securities and Exchange
Commission for allegedly
allowing thousands of improper
trades in more than 600 mutual
funds." + ], + [ + "BERLIN: Apple Computer Inc is
planning the next wave of
expansion for its popular
iTunes online music store with
a multi-country European
launch in October, the
services chief architect said
on Wednesday." + ], + [ + "Charlotte, NC -- LeBron James
poured in a game-high 19
points and Jeff McInnis scored
18 as the Cleveland Cavaliers
routed the Charlotte Bobcats,
106-89, at the Charlotte
Coliseum." + ], + [ + "Goldman Sachs reported strong
fourth quarter and full year
earnings of \\$1.19bn, up 36
per cent on the previous
quarter. Full year profit rose
52 per cent from the previous
year to \\$4.55bn." + ], + [ + "Saddam Hussein lives in an
air-conditioned 10-by-13 foot
cell on the grounds of one of
his former palaces, tending
plants and proclaiming himself
Iraq's lawful ruler." + ], + [ + "Lehmann, who was at fault in
two matches in the tournament
last season, was blundering
again with the German set to
take the rap for both Greek
goals." + ], + [ + "Calls to 13 other countries
will be blocked to thwart
auto-dialer software." + ], + [ + "AP - Brazilian U.N.
peacekeepers will remain in
Haiti until presidential
elections are held in that
Caribbean nation sometime next
year, President Luiz Inacio
Lula da Silva said Monday." + ], + [ + "com September 27, 2004, 5:00
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." + ], + [ + "Jeju Island, South Korea
(Sports Network) - Grace Park
and Carin Koch posted matching
rounds of six-under-par 66 on
Friday to share the lead after
the first round of the CJ Nine
Bridges Classic." + ], + [ + "SPACE.com - The Zero Gravity
Corporation \\ has been given
the thumbs up by the Federal
Aviation Administration (FAA)
to \\ conduct quot;weightless
flights quot; for the general
public, providing the \\
sensation of floating in
space." + ], + [ + "A 32-year-old woman in Belgium
has become the first woman
ever to give birth after
having ovarian tissue removed,
frozen and then implanted back
in her body." + ], + [ + "Argosy Gaming (AGY:NYSE - news
- research) jumped in early
trading Thursday, after the
company agreed to be acquired
by Penn National Gaming
(PENN:Nasdaq - news -
research) in a \\$1." + ], + [ + "No sweat, says the new CEO,
who's imported from IBM. He
also knows this will be the
challenge of a lifetime." + ], + [ + "Iraq is quot;working 24 hours
a day to ... stop the
terrorists, quot; interim
Iraqi Prime Minister Ayad
Allawi said Monday. Iraqis are
pushing ahead with reforms and
improvements, Allawi told" + ], + [ + "AP - Their discoveries may be
hard for many to comprehend,
but this year's Nobel Prize
winners still have to explain
what they did and how they did
it." + ], + [ + "The DuPont Co. has agreed to
pay up to \\$340 million to
settle a lawsuit that it
contaminated water supplies in
West Virginia and Ohio with a
chemical used to make Teflon,
one of its best-known brands." + ], + [ + "Benfica and Real Madrid set
the standard for soccer
success in Europe in the late
1950s and early #39;60s. The
clubs have evolved much
differently, but both have
struggled" + ], + [ + "AP - Musicians, composers and
authors were among the more
than two dozen people
Wednesday honored with
National Medal of Arts and
National Humanities awards at
the White House." + ], + [ + "More than 15 million homes in
the UK will be able to get on-
demand movies by 2008, say
analysts." + ], + [ + "Wynton Marsalis, the trumpet-
playing star and artistic
director of Jazz at Lincoln
Center, has become an entity
above and around the daily
jazz world." + ], + [ + "A well-researched study by the
National Academy of Sciences
makes a persuasive case for
refurbishing the Hubble Space
Telescope. NASA, which is
reluctant to take this
mission, should rethink its
position." + ], + [ + "BUENOS AIRES: Pakistan has
ratified the Kyoto Protocol on
Climatic Change, Environment
Minister Malik Khan said on
Thursday. He said that
Islamabad has notified UN
authorities of ratification,
which formally comes into
effect in February 2005." + ], + [ + "Reuters - Jeffrey Greenberg,
chairman and chief\\executive
of embattled insurance broker
Marsh McLennan Cos.\\, is
expected to step down within
hours, a newspaper\\reported on
Friday, citing people close to
the discussions." + ], + [ + "Staff Sgt. Johnny Horne, Jr.,
and Staff Sgt. Cardenas Alban
have been charged with murder
in the death of an Iraqi, the
1st Cavalry Division announced
Monday." + ], + [ + "LONDON -- In a deal that
appears to buck the growing
trend among governments to
adopt open-source
alternatives, the U.K.'s
Office of Government Commerce
(OGC) is negotiating a renewal
of a three-year agreement with
Microsoft Corp." + ], + [ + "WASHINGTON - A Pennsylvania
law requiring Internet service
providers (ISPs) to block Web
sites the state's prosecuting
attorneys deem to be child
pornography has been reversed
by a U.S. federal court, with
the judge ruling the law
violated free speech rights." + ], + [ + "The Microsoft Corp. chairman
receives four million e-mails
a day, but practically an
entire department at the
company he founded is
dedicated to ensuring that
nothing unwanted gets into his
inbox, the company #39;s chief
executive said Thursday." + ], + [ + "The 7100t has a mobile phone,
e-mail, instant messaging, Web
browsing and functions as an
organiser. The device looks
like a mobile phone and has
the features of the other
BlackBerry models, with a
large screen" + ], + [ + "President Vladimir Putin makes
a speech as he hosts Russia
#39;s Olympic athletes at a
Kremlin banquet in Moscow,
Thursday, Nov. 4, 2004." + ], + [ + "Apple Computer has unveiled
two new versions of its hugely
successful iPod: the iPod
Photo and the U2 iPod. Apple
also has expanded" + ], + [ + "Pakistan are closing in fast
on Sri Lanka #39;s first
innings total after impressing
with both ball and bat on the
second day of the opening Test
in Faisalabad." + ], + [ + "A state regulatory board
yesterday handed a five-year
suspension to a Lawrence
funeral director accused of
unprofessional conduct and
deceptive practices, including
one case where he refused to
complete funeral arrangements
for a client because she had
purchased a lower-priced
casket elsewhere." + ], + [ + "AP - This year's hurricanes
spread citrus canker to at
least 11,000 trees in
Charlotte County, one of the
largest outbreaks of the
fruit-damaging infection to
ever affect Florida's citrus
industry, state officials
said." + ], + [ + "AFP - Hundreds of Buddhists in
southern Russia marched
through snow to see and hear
the Dalai Lama as he continued
a long-awaited visit to the
country in spite of Chinese
protests." + ], + [ + "British officials were on
diplomatic tenterhooks as they
awaited the arrival on
Thursday of French President
Jacques Chirac for a two-day
state visit to Britain." + ], + [ + " SYDNEY (Reuters) - A group of
women on Pitcairn Island in
the South Pacific are standing
by their men, who face
underage sex charges, saying
having sex at age 12 is a
tradition dating back to 18th
century mutineers who settled
on the island." + ], + [ + "Two aircraft are flying out
from the UK on Sunday to
deliver vital aid and supplies
to Haiti, which has been
devastated by tropical storm
Jeanne." + ], + [ + "A scare triggered by a
vibrating sex toy shut down a
major Australian regional
airport for almost an hour
Monday, police said. The
vibrating object was
discovered Monday morning" + ], + [ + "IBM moved back into the iSCSI
(Internet SCSI) market Friday
with a new array priced at
US\\$3,000 and aimed at the
small and midsize business
market." + ], + [ + "Ron Artest has been hit with a
season long suspension,
unprecedented for the NBA
outside doping cases; Stephen
Jackson banned for 30 games;
Jermaine O #39;Neal for 25
games and Anthony Johnson for
five." + ], + [ + "The California Public
Utilities Commission on
Thursday upheld a \\$12.1
million fine against Cingular
Wireless, related to a two-
year investigation into the
cellular telephone company
#39;s business practices." + ], + [ + "Barclays, the British bank
that left South Africa in 1986
after apartheid protests, may
soon resume retail operations
in the nation." + ], + [ + "AP - An Indiana congressional
candidate abruptly walked off
the set of a debate because
she got stage fright." + ], + [ + "Italian-based Parmalat is
suing its former auditors --
Grant Thornton International
and Deloitte Touche Tohmatsu
-- for billions of dollars in
damages. Parmalat blames its
demise on the two companies
#39; mismanagement of its
finances." + ], + [ + "Reuters - Having reached out
to Kashmiris during a two-day
visit to the region, the prime
minister heads this weekend to
the country's volatile
northeast, where public anger
is high over alleged abuses by
Indian soldiers." + ], + [ + "SAN JUAN IXTAYOPAN, Mexico --
A pair of wooden crosses
outside the elementary school
are all that mark the macabre
site where, just weeks ago, an
angry mob captured two federal
police officers, beat them
unconscious, and set them on
fire." + ], + [ + "Three shots behind Grace Park
with five holes to go, six-
time LPGA player of the year
Sorenstam rallied with an
eagle, birdie and three pars
to win her fourth Samsung
World Championship by three
shots over Park on Sunday." + ], + [ + "update An alliance of
technology workers on Tuesday
accused conglomerate Honeywell
International of planning to
move thousands of jobs to low-
cost regions over the next
five years--a charge that
Honeywell denies." + ], + [ + "NSW Rugby CEO Fraser Neill
believes Waratah star Mat
Rogers has learned his lesson
after he was fined and ordered
to do community service
following his controversial
comments about the club rugby
competition." + ], + [ + "American Technology Research
analyst Shaw Wu has initiated
coverage of Apple Computer
(AAPL) with a #39;buy #39;
recommendation, and a 12-month
target of US\\$78 per share." + ], + [ + "washingtonpost.com - Cell
phone shoppers looking for new
deals and features didn't find
them yesterday, a day after
Sprint Corp. and Nextel
Communications Inc. announced
a merger that the phone
companies promised would shake
up the wireless business." + ], + [ + "ATHENS: China, the dominant
force in world diving for the
best part of 20 years, won six
out of eight Olympic titles in
Athens and prompted
speculation about a clean
sweep when they stage the
Games in Beijing in 2008." + ], + [ + " NEW YORK (Reuters) - Northrop
Grumman Corp. <A HREF=\"http
://www.investor.reuters.com/Fu
llQuote.aspx?ticker=NOC.N targ
et=/stocks/quickinfo/fullquote
\">NOC.N</A> reported
higher third-quarter earnings
on Wednesday and an 11
percent increase in sales on
strength in its mission
systems, integrated systems,
ships and space technology
businesses." + ], + [ + "JAPANESE GIANT Sharp has
pulled the plug on its Linux-
based PDAs in the United
States as no-one seems to want
them. The company said that it
will continue to sell them in
Japan where they sell like hot
cakes" + ], + [ + "DUBLIN : An Olympic Airlines
plane diverted to Ireland
following a bomb alert, the
second faced by the Greek
carrier in four days, resumed
its journey to New York after
no device was found on board,
airport officials said." + ], + [ + "New NavOne offers handy PDA
and Pocket PC connectivity,
but fails to impress on
everything else." + ], + [ + "TORONTO (CP) - Shares of
Iamgold fell more than 10 per
cent after its proposed merger
with Gold Fields Ltd. was
thrown into doubt Monday as
South Africa #39;s Harmony
Gold Mining Company Ltd." + ], + [ + "The Marvel deal calls for
Mforma to work with the comic
book giant to develop a
variety of mobile applications
based on Marvel content." + ], + [ + " LONDON (Reuters) - Oil prices
tumbled again on Monday to an
8-week low under \\$46 a
barrel, as growing fuel stocks
in the United States eased
fears of a winter supply
crunch." + ], + [ + " WASHINGTON (Reuters) - The
United States said on Friday
it is preparing a new U.N.
resolution on Darfur and that
Secretary of State Colin
Powell might address next week
whether the violence in
western Sudan constitutes
genocide." + ], + [ + "The Houston Astros won their
19th straight game at home and
are one game from winning
their first playoff series in
42 years." + ], + [ + "washingtonpost.com - The
Portable Media Center -- a
new, Microsoft-conceived
handheld device that presents
video and photos as well as
music -- would be a decent
idea if there weren't such a
thing as lampposts. Or street
signs. Or trees. Or other
cars." + ], + [ + "Alcoa Inc., one of the world
#39;s top producers of
aluminum, said Monday that it
received an unsolicited
quot;mini-tender quot; offer
from Toronto-based TRC Capital
Corp." + ], + [ + "The European Commission is to
warn Greece about publishing
false information about its
public finances." + ], + [ + "The Air Force Reserve #39;s
Hurricane Hunters, those
fearless crews who dive into
the eyewalls of hurricanes to
relay critical data on
tropical systems, were chased
from their base on the
Mississippi Gulf Coast by
Hurricane Ivan." + ], + [ + " LONDON (Reuters) - U.S.
shares were expected to open
lower on Wednesday after
crude oil pushed to a fresh
high overnight, while Web
search engine Google Inc.
dented sentiment as it
slashed the price range on its
initial public offering." + ], + [ + "The eighth-seeded American
fell to sixth-seeded Elena
Dementieva of Russia, 0-6 6-2
7-6 (7-5), on Friday - despite
being up a break on four
occasions in the third set." + ], + [ + "TUCSON, Arizona (Ticker) --
No. 20 Arizona State tries to
post its first three-game
winning streak over Pac-10
Conference rival Arizona in 26
years when they meet Friday." + ], + [ + "NAJAF, Iraq : Iraq #39;s top
Shiite Muslim clerics, back in
control of Najaf #39;s Imam
Ali shrine after a four-month
militia occupation, met amid
the ruins of the city as life
spluttered back to normality." + ], + [ + "Embargo or not, Fidel Castro's
socialist paradise has quietly
become a pharmaceutical
powerhouse. (They're still
working on the capitalism
thing.) By Douglas Starr from
Wired magazine." + ], + [ + "AP - Phillip Fulmer kept his
cool when starting center
Jason Respert drove off in the
coach's golf cart at practice." + ], + [ + "GOALS from Wayne Rooney and
Ruud van Nistelrooy gave
Manchester United the win at
Newcastle. Alan Shearer
briefly levelled matters for
the Magpies but United managed
to scrape through." + ], + [ + "The telemarketer at the other
end of Orlando Castelblanco
#39;s line promised to reduce
the consumer #39;s credit card
debt by at least \\$2,500 and
get his 20 percent -- and
growing -- interest rates down
to single digits." + ], + [ + " NEW YORK (Reuters) - Blue-
chip stocks fell slightly on
Monday after No. 1 retailer
Wal-Mart Stores Inc. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=WMT
.N target=/stocks/quickinfo/fu
llquote\">WMT.N</A>
reported lower-than-expected
Thanksgiving sales, while
technology shares were lifted
by a rally in Apple Computer
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AAPL.O target=/sto
cks/quickinfo/fullquote\">AA
PL.O</A>." + ], + [ + "Reuters - House of
Representatives
Majority\\Leader Tom DeLay,
admonished twice in six days
by his chamber's\\ethics
committee, withstood calls on
Thursday by rival\\Democrats
and citizen groups that he
step aside." + ], + [ + "WPP Group Inc., the world
#39;s second- largest
marketing and advertising
company, said it won the
bidding for Grey Global Group
Inc." + ], + [ + "AFP - Australia has accounted
for all its nationals known to
be working in Iraq following a
claim by a radical Islamic
group to have kidnapped two
Australians, Foreign Affairs
Minister Alexander Downer
said." + ], + [ + "A new worm can spy on users by
hijacking their Web cameras, a
security firm warned Monday.
The Rbot.gr worm -- the latest
in a long line of similar
worms; one security firm
estimates that more than 4,000
variations" + ], + [ + " NEW YORK (Reuters) - Stocks
were slightly lower on
Tuesday, as concerns about
higher oil prices cutting into
corporate profits and
consumer demand weighed on
sentiment, while retail sales
posted a larger-than-expected
decline in August." + ], + [ + "The separation of PalmOne and
PalmSource will be complete
with Eric Benhamou's
resignation as the latter's
chairman." + ], + [ + "AP - Two-time U.S. Open
doubles champion Max Mirnyi
will lead the Belarus team
that faces the United States
in the Davis Cup semifinals in
Charleston later this month." + ], + [ + "The hurricane appeared to be
strengthening and forecasters
warned of an increased threat
of serious flooding and wind
damage." + ], + [ + "AP - New York Jets safety Erik
Coleman got his souvenir
football from the equipment
manager and held it tightly." + ], + [ + " SINGAPORE (Reuters) - Oil
prices climbed above \\$42 a
barrel on Wednesday, rising
for the third day in a row as
the heavy consuming U.S.
Northeast feels the first
chills of winter." + ], + [ + "\\\\\"(CNN) -- A longtime
associate of al Qaeda leader
Osama bin Laden surrendered
to\\Saudi Arabian officials
Tuesday, a Saudi Interior
Ministry official said.\"\\\\\"But
it is unclear what role, if
any, Khaled al-Harbi may have
had in any terror\\attacks
because no public charges have
been filed against him.\"\\\\\"The
Saudi government -- in a
statement released by its
embassy in Washington
--\\called al-Harbi's surrender
\"the latest direct result\" of
its limited, one-month\\offer
of leniency to terror
suspects.\"\\\\This is great! I
hope this really starts to pay
off. Creative solutions
to\\terrorism that don't
involve violence. \\\\How
refreshing! \\\\Are you paying
attention Bush
administration?\\\\" + ], + [ + " NEW YORK (Reuters) - Merck
Co Inc. <A HREF=\"http://www
.investor.reuters.com/FullQuot
e.aspx?ticker=MRK.N target=/st
ocks/quickinfo/fullquote\">M
RK.N</A> on Thursday
pulled its arthritis drug
Vioxx off the market after a
study showed it doubled the
risk of heart attack and
stroke, a move that sent its
shares plunging and erased
\\$25 billion from its market
value." + ], + [ + "AT T Corp. is cutting 7,400
more jobs and slashing the
book value of its assets by
\\$11.4 billion, drastic moves
prompted by the company's plan
to retreat from the
traditional consumer telephone
business following a lost
court battle." + ], + [ + "The government on Wednesday
defended its decision to
radically revise the country
#39;s deficit figures, ahead
of a European Commission
meeting to consider possible
disciplinary action against
Greece for submitting faulty
figures." + ], + [ + "Ivan Hlinka coached the Czech
Republic to the hockey gold
medal at the 1998 Nagano
Olympics and became the coach
of the Pittsburgh Penguins two
years later." + ], + [ + "Four homeless men were
bludgeoned to death and six
were in critical condition on
Friday following early morning
attacks by unknown assailants
in downtown streets of Sao
Paulo, a police spokesman
said." + ], + [ + "OPEC ministers yesterday
agreed to increase their
ceiling for oil production to
help bring down stubbornly
high prices in a decision that
traders and analysts dismissed
as symbolic because the cartel
already is pumping more than
its new target." + ], + [ + "Williams-Sonoma Inc. said
second- quarter profit rose 55
percent, boosted by the
addition of Pottery Barn
stores and sale of outdoor
furniture." + ], + [ + "Celerons form the basis of
Intel #39;s entry-level
platform which includes
integrated/value motherboards
as well. The Celeron 335D is
the fastest - until the
Celeron 340D - 2.93GHz -
becomes mainstream - of the" + ], + [ + "The entertainment industry
asks the Supreme Court to
reverse the Grokster decision,
which held that peer-to-peer
networks are not liable for
copyright abuses of their
users. By Michael Grebb." + ], + [ + " HONG KONG/SAN FRANCISCO
(Reuters) - China's largest
personal computer maker,
Lenovo Group Ltd., said on
Tuesday it was in acquisition
talks with a major technology
company, which a source
familiar with the situation
said was IBM." + ], + [ + "Phone companies are not doing
enough to warn customers about
internet \"rogue-dialling\"
scams, watchdog Icstis warns." + ], + [ + "Reuters - Oil prices stayed
close to #36;49 a\\barrel on
Thursday, supported by a
forecast for an early
cold\\snap in the United States
that could put a strain on a
thin\\supply cushion of winter
heating fuel." + ], + [ + "AUBURN - Ah, easy street. No
game this week. Light
practices. And now Auburn is
being touted as the No. 3 team
in the Bowl Championship
Series standings." + ], + [ + "Portsmouth #39;s Harry
Redknapp has been named as the
Barclays manager of the month
for October. Redknapp #39;s
side were unbeaten during the
month and maintained an
impressive climb to ninth in
the Premiership." + ], + [ + "India's main opposition party
takes action against senior
party member Uma Bharti after
a public row." + ], + [ + "Hewlett-Packard will shell out
\\$16.1 billion for chips in
2005, but Dell's wallet is
wide open, too." + ], + [ + "WASHINGTON (CBS.MW) --
President Bush announced
Monday that Kellogg chief
executive Carlos Gutierrez
would replace Don Evans as
Commerce secretary, naming the
first of many expected changes
to his economic team." + ], + [ + "Iron Mountain moved further
into the backup and recovery
space Tuesday with the
acquisition of Connected Corp.
for \\$117 million. Connected
backs up desktop data for more
than 600 corporations, with
more than" + ], + [ + "Three directors of Manchester
United have been ousted from
the board after US tycoon
Malcolm Glazer, who is
attempting to buy the club,
voted against their re-
election." + ], + [ + "The Russian military yesterday
extended its offer of a \\$10
million reward for information
leading to the capture of two
separatist leaders who, the
Kremlin claims, were behind
the Beslan massacre." + ], + [ + "AP - Manny Ramirez singled and
scored before leaving with a
bruised knee, and the
streaking Boston Red Sox beat
the Detroit Tigers 5-3 Friday
night for their 10th victory
in 11 games." + ], + [ + "A remote attacker could take
complete control over
computers running many
versions of Microsoft software
by inserting malicious code in
a JPEG image that executes
through an unchecked buffer" + ], + [ + "Montgomery County (website -
news) is a big step closer to
shopping for prescription
drugs north of the border. On
a 7-2 vote, the County Council
is approving a plan that would
give county" + ], + [ + "Israels Shin Bet security
service has tightened
protection of the prime
minister, MPs and parliament
ahead of next weeks crucial
vote on a Gaza withdrawal." + ], + [ + "The news comes fast and
furious. Pedro Martinez goes
to Tampa to visit George
Steinbrenner. Theo Epstein and
John Henry go to Florida for
their turn with Pedro. Carl
Pavano comes to Boston to
visit Curt Schilling. Jason
Varitek says he's not a goner.
Derek Lowe is a goner, but he
says he wishes it could be
different. Orlando Cabrera ..." + ], + [ + "The disclosure this week that
a Singapore-listed company
controlled by a Chinese state-
owned enterprise lost \\$550
million in derivatives
transactions" + ], + [ + "Reuters - Iraq's interim
defense minister
accused\\neighbors Iran and
Syria on Wednesday of aiding
al Qaeda\\Islamist Abu Musab
al-Zarqawi and former agents
of Saddam\\Hussein to promote a
\"terrorist\" insurgency in
Iraq." + ], + [ + "AP - The Senate race in
Kentucky stayed at fever pitch
on Thursday as Democratic
challenger Daniel Mongiardo
stressed his opposition to gay
marriage while accusing
Republican incumbent Jim
Bunning of fueling personal
attacks that seemed to suggest
Mongiardo is gay." + ], + [ + "The lawsuit claims the
companies use a patented
Honeywell technology for
brightening images and
reducing interference on
displays." + ], + [ + "The co-president of Oracle
testified that her company was
serious about its takeover
offer for PeopleSoft and was
not trying to scare off its
customers." + ], + [ + "VANCOUVER - A Vancouver-based
firm won #39;t sell 1.2
million doses of influenza
vaccine to the United States
after all, announcing Tuesday
that it will sell the doses
within Canada instead." + ], + [ + "An extremely rare Hawaiian
bird dies in captivity,
possibly marking the
extinction of its entire
species only 31 years after it
was first discovered." + ], + [ + "Does Geico's trademark lawsuit
against Google have merit? How
will the case be argued?
What's the likely outcome of
the trial? A mock court of
trademark experts weighs in
with their verdict." + ], + [ + "Treo 650 boasts a high-res
display, an improved keyboard
and camera, a removable
battery, and more. PalmOne
this week is announcing the
Treo 650, a hybrid PDA/cell-
phone device that addresses
many of the shortcomings" + ], + [ + "FRED Hale Sr, documented as
the worlds oldest man, has
died at the age of 113. Hale
died in his sleep on Friday at
a hospital in Syracuse, New
York, while trying to recover
from a bout of pneumonia, his
grandson, Fred Hale III said." + ], + [ + "The Oakland Raiders have
traded Jerry Rice to the
Seattle Seahawks in a move
expected to grant the most
prolific receiver in National
Football League history his
wish to get more playing time." + ], + [ + "consortium led by the Sony
Corporation of America reached
a tentative agreement today to
buy Metro-Goldwyn-Mayer, the
Hollywood studio famous for
James Bond and the Pink
Panther, for" + ], + [ + "International Business
Machines Corp.'s possible exit
from the personal computer
business would be the latest
move in what amounts to a long
goodbye from a field it
pioneered and revolutionized." + ], + [ + "Leipzig Game Convention in
Germany, the stage for price-
slash revelations. Sony has
announced that it #39;s
slashing the cost of PS2 in
the UK and Europe to 104.99
GBP." + ], + [ + "AP - Florida coach Ron Zook
was fired Monday but will be
allowed to finish the season,
athletic director Jeremy Foley
told The Gainesville Sun." + ], + [ + "The country-cooking restaurant
chain has agreed to pay \\$8.7
million over allegations that
it segregated black customers,
subjected them to racial slurs
and gave black workers
inferior jobs." + ], + [ + "Troy Brown has had to make a
lot of adjustments while
playing both sides of the
football. quot;You always
want to score when you get the
ball -- offense or defense" + ], + [ + " PORT LOUIS, Aug. 17
(Xinhuanet) -- Southern
African countries Tuesday
pledged better trade and
investment relations with
China as well as India in the
final communique released at
the end of their two-day
summit." + ], + [ + "In yet another devastating
body blow to the company,
Intel (Nasdaq: INTC) announced
it would be canceling its
4-GHz Pentium chip. The
semiconductor bellwether said
it was switching" + ], + [ + "Jenson Button will tomorrow
discover whether he is allowed
to quit BAR and move to
Williams for 2005. The
Englishman has signed
contracts with both teams but
prefers a switch to Williams,
where he began his Formula One
career in 2000." + ], + [ + "Seagate #39;s native SATA
interface technology with
Native Command Queuing (NCQ)
allows the Barracuda 7200.8 to
match the performance of
10,000-rpm SATA drives without
sacrificing capacity" + ], + [ + "ARSENAL boss Arsene Wenger
last night suffered a
Champions League setback as
Brazilian midfielder Gilberto
Silva (above) was left facing
a long-term injury absence." + ], + [ + "BAGHDAD - A militant group has
released a video saying it
kidnapped a missing journalist
in Iraq and would kill him
unless US forces left Najaf
within 48 hours." + ], + [ + "18 August 2004 -- There has
been renewed fighting in the
Iraqi city of Al-Najaf between
US and Iraqi troops and Shi
#39;a militiamen loyal to
radical cleric Muqtada al-
Sadr." + ], + [ + "You #39;re probably already
familiar with one of the most
common questions we hear at
iPodlounge: quot;how can I
load my iPod up with free
music?" + ], + [ + " SINGAPORE (Reuters) -
Investors bought shares in
Asian exporters and
electronics firms such as
Fujitsu Ltd. on Tuesday,
buoyed by a favorable outlook
from U.S. technology
bellwethers and a slide in oil
prices." + ], + [ + "Ace Ltd. will stop paying
brokers for steering business
its way, becoming the third
company to make concessions in
the five days since New York
Attorney General Eliot Spitzer
unveiled a probe of the
insurance industry." + ], + [ + "Vice chairman of the office of
the chief executive officer in
Novell Inc, Chris Stone is
leaving the company to pursue
other opportunities in life." + ], + [ + "Wm. Wrigley Jr. Co., the world
#39;s largest maker of chewing
gum, agreed to buy candy
businesses including Altoids
mints and Life Savers from
Kraft Foods Inc." + ], + [ + "American improves to 3-1 on
the season with a hard-fought
overtime win, 74-63, against
Loyala at Bender Arena on
Friday night." + ], + [ + "Australia tighten their grip
on the third Test and the
series after dominating India
on day two in Nagpur." + ], + [ + " #39;Reaching a preliminary
pilot agreement is the single
most important hurdle they
have to clear, but certainly
not the only one." + ], + [ + "Bee Staff Writers. SAN
FRANCISCO - As Eric Johnson
drove to the stadium Sunday
morning, his bruised ribs were
so sore, he wasn #39;t sure he
#39;d be able to suit up for
the game." + ], + [ + "The New England Patriots are
so single-minded in pursuing
their third Super Bowl triumph
in four years that they almost
have no room for any other
history." + ], + [ + "TORONTO (CP) - Canada #39;s
big banks are increasing
mortgage rates following a
decision by the Bank of Canada
to raise its overnight rate by
one-quarter of a percentage
point to 2.25 per cent." + ], + [ + " SEOUL (Reuters) - North
Korea's two-year-old nuclear
crisis has taxed the world's
patience, the chief United
Nations nuclear regulator
said on Wednesday, urging
communist Pyongyang to return
to its disarmament treaty
obligations." + ], + [ + "washingtonpost.com - Microsoft
is going to Tinseltown today
to announce plans for its
revamped Windows XP Media
Center, part of an aggressive
push to get ahead in the
digital entertainment race." + ], + [ + "GROZNY, Russia - The Russian
government's choice for
president of war-ravaged
Chechnya appeared to be the
victor Sunday in an election
tainted by charges of fraud
and shadowed by last week's
terrorist destruction of two
airliners. Little more than
two hours after polls closed,
acting Chechen president
Sergei Abramov said
preliminary results showed
Maj..." + ], + [ + "Because, while the Eagles are
certain to stumble at some
point during the regular
season, it seems inconceivable
that they will falter against
a team with as many offensive
problems as Baltimore has
right now." + ], + [ + "AP - J.J. Arrington ran for 84
of his 121 yards in the second
half and Aaron Rodgers shook
off a slow start to throw two
touchdown passes to help No. 5
California beat Washington
42-12 on Saturday." + ], + [ + "BAGHDAD, Sept 5 (AFP) - Izzat
Ibrahim al-Duri, Saddam
Hussein #39;s deputy whose
capture was announced Sunday,
is 62 and riddled with cancer,
but was public enemy number
two in Iraq for the world
#39;s most powerful military." + ], + [ + "AP - An explosion targeted the
Baghdad governor's convoy as
he was traveling through the
capital Tuesday, killing two
people but leaving him
uninjured, the Interior
Ministry said." + ], + [ + "GENEVA: Rescuers have found
the bodies of five Swiss
firemen who died after the
ceiling of an underground car
park collapsed during a fire,
a police spokesman said last
night." + ], + [ + "John Kerry has held 10 \"front
porch visit\" events an actual
front porch is optional where
perhaps 100 people ask
questions in a low-key
campaigning style." + ], + [ + "AP - Most of the turkeys
gracing the nation's dinner
tables Thursday have been
selectively bred for their
white meat for so many
generations that simply
walking can be a problem for
many of the big-breasted birds
and sex is no longer possible." + ], + [ + "OTTAWA (CP) - The economy
created another 43,000 jobs
last month, pushing the
unemployment rate down to 7.1
per cent from 7.2 per cent in
August, Statistics Canada said
Friday." + ], + [ + "The right-win opposition
Conservative Party and Liberal
Center Union won 43 seats in
the 141-member Lithuanian
parliament, after more than 99
percent of the votes were
counted" + ], + [ + " TOKYO (Reuters) - Japan's
Nikkei average rose 0.39
percent by midsession on
Friday, bolstered by solid
gains in stocks dependent on
domestic business such as Kao
Corp. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=4452.T target=/sto
cks/quickinfo/fullquote\">44
52.T</A>." + ], + [ + " FRANKFURT (Reuters) -
DaimlerChrysler and General
Motors will jointly develop
new hybrid motors to compete
against Japanese rivals on
the fuel-saving technology
that reduces harmful
emissions, the companies said
on Monday." + ], + [ + " SEATTLE (Reuters) - Microsoft
Corp. <A HREF=\"http://www.r
euters.co.uk/financeQuoteLooku
p.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
is making a renewed push this
week to get its software into
living rooms with the launch
of a new version of its
Windows XP Media Center, a
personal computer designed for
viewing movies, listening to
music and scrolling through
digital pictures." + ], + [ + "KHARTOUM, Aug 18 (Reuters) -
The United Nations said on
Wednesday it was very
concerned by Sudan #39;s lack
of practical progress in
bringing security to Darfur,
where more than a million
people have fled their homes
for fear of militia ..." + ], + [ + "SHANGHAI, China The Houston
Rockets have arrived in
Shanghai with hometown
favorite Yao Ming declaring
himself quot;here on
business." + ], + [ + "Charleston, SC (Sports
Network) - Andy Roddick and
Mardy Fish will play singles
for the United States in this
weekend #39;s Davis Cup
semifinal matchup against
Belarus." + ], + [ + "AFP - With less than two
months until the November 2
election, President George W.
Bush is working to shore up
support among his staunchest
supporters even as he courts
undecided voters." + ], + [ + "The bass should be in your
face. That's what Matt Kelly,
of Boston's popular punk rock
band Dropkick Murphys, thinks
is the mark of a great stereo
system. And he should know.
Kelly, 29, is the drummer for
the band that likes to think
of itself as a bit of an Irish
lucky charm for the Red Sox." + ], + [ + "Chile's government says it
will build a prison for
officers convicted of human
rights abuses in the Pinochet
era." + ], + [ + "Slumping corporate spending
and exports caused the economy
to slow to a crawl in the
July-September period, with
real gross domestic product
expanding just 0.1 percent
from the previous quarter,
Cabinet Office data showed
Friday." + ], + [ + "US President George W. Bush
signed into law a bill
replacing an export tax
subsidy that violated
international trade rules with
a \\$145 billion package of new
corporate tax cuts and a
buyout for tobacco farmers." + ], + [ + "The Nikkei average was up 0.37
percent in mid-morning trade
on Thursday as a recovery in
the dollar helped auto makers
among other exporters, but
trade was slow as investors
waited for important Japanese
economic data." + ], + [ + "Jennifer Canada knew she was
entering a boy's club when she
enrolled in Southern Methodist
University's Guildhall school
of video-game making." + ], + [ + "RICKY PONTING believes the
game #39;s watchers have
fallen for the quot;myth
quot; that New Zealand know
how to rattle Australia." + ], + [ + "MILWAUKEE (SportsTicker) -
Barry Bonds tries to go where
just two players have gone
before when the San Francisco
Giants visit the Milwaukee
Brewers on Tuesday." + ], + [ + "Palestinian leader Mahmoud
Abbas reiterated calls for his
people to drop their weapons
in the struggle for a state. a
clear change of strategy for
peace with Israel after Yasser
Arafat #39;s death." + ], + [ + "The new software is designed
to simplify the process of
knitting together back-office
business applications." + ], + [ + "SYDNEY (Dow Jones)--Colorado
Group Ltd. (CDO.AU), an
Australian footwear and
clothing retailer, said Monday
it expects net profit for the
fiscal year ending Jan. 29 to
be over 30 higher than that of
a year earlier." + ], + [ + "NEW YORK - What are the odds
that a tiny nation like
Antigua and Barbuda could take
on the United States in an
international dispute and win?" + ], + [ + "AP - With Tom Brady as their
quarterback and a stingy,
opportunistic defense, it's
difficult to imagine when the
New England Patriots might
lose again. Brady and
defensive end Richard Seymour
combined to secure the
Patriots' record-tying 18th
straight victory, 31-17 over
the Buffalo Bills on Sunday." + ], + [ + "FRANKFURT, GERMANY -- The
German subsidiaries of
Hewlett-Packard Co. (HP) and
Novell Inc. are teaming to
offer Linux-based products to
the country's huge public
sector." + ], + [ + "SBC Communications expects to
cut 10,000 or more jobs by the
end of next year through
layoffs and attrition. That
#39;s about six percent of the
San Antonio-based company
#39;s work force." + ], + [ + " BAGHDAD (Reuters) - Iraq's
U.S.-backed government said on
Tuesday that \"major neglect\"
by its American-led military
allies led to a massacre of 49
army recruits at the weekend." + ], + [ + "SiliconValley.com - \"I'm
back,\" declared Apple
Computer's Steve Jobs on
Thursday morning in his first
public appearance before
reporters since cancer surgery
in late July." + ], + [ + "BEIJING -- Police have
detained a man accused of
slashing as many as nine boys
to death as they slept in
their high school dormitory in
central China, state media
reported today." + ], + [ + "Health India: London, Nov 4 :
Cosmetic face cream used by
fashionable Roman women was
discovered at an ongoing
archaeological dig in London,
in a metal container, complete
with the lid and contents." + ], + [ + "Israeli Prime Minister Ariel
Sharon #39;s Likud party
agreed on Thursday to a
possible alliance with
opposition Labour in a vote
that averted a snap election
and strengthened his Gaza
withdrawal plan." + ], + [ + "Another United Airlines union
is seeking to oust senior
management at the troubled
airline, saying its strategies
are reckless and incompetent." + ], + [ + "Approaching Hurricane Ivan has
led to postponement of the
game Thursday night between
10th-ranked California and
Southern Mississippi in
Hattiesburg, Cal #39;s
athletic director said Monday." + ], + [ + "Global oil prices boomed on
Wednesday, spreading fear that
energy prices will restrain
economic activity, as traders
worried about a heating oil
supply crunch in the American
winter." + ], + [ + "Custom-designed imported
furniture was once an
exclusive realm. Now, it's the
economical alternative for
commercial developers and
designers needing everything
from seats to beds to desks
for their projects." + ], + [ + "SAN DIEGO (Ticker) - The San
Diego Padres lacked speed and
an experienced bench last
season, things veteran
infielder Eric Young is
capable of providing." + ], + [ + "This is an eye chart,
reprinted as a public service
to the New York Mets so they
may see from what they suffer:
myopia. Has ever a baseball
franchise been so shortsighted
for so long?" + ], + [ + "Short-term interest rate
futures struggled on Thursday
after a government report
showing US core inflation for
August below market
expectations failed to alter
views on Federal Reserve rate
policy." + ], + [ + "MacCentral - Microsoft's
Macintosh Business Unit on
Tuesday issued a patch for
Virtual PC 7 that fixes a
problem that occurred when
running the software on Power
Mac G5 computers with more
than 2GB of RAM installed.
Previously, Virtual PC 7 would
not run on those computers,
causing a fatal error that
crashed the application.
Microsoft also noted that
Virtual PC 7.0.1 also offers
stability improvements,
although it wasn't more
specific than that -- some
users have reported problems
with using USB devices and
other issues." + ], + [ + "Samsung is now the world #39;s
second-largest mobile phone
maker, behind Nokia. According
to market watcher Gartner, the
South Korean company has
finally knocked Motorola into
third place." + ], + [ + "Don't bother buying Star Wars:
Battlefront if you're looking
for a first-class shooter --
there are far better games out
there. But if you're a Star
Wars freak and need a fix,
this title will suffice. Lore
Sjberg reviews Battlefront." + ], + [ + "While the American forces are
preparing to launch a large-
scale attack against Falluja
and al-Ramadi, one of the
chieftains of Falluja said
that contacts are still
continuous yesterday between
members representing the city
#39;s delegation and the
interim" + ], + [ + "UN Security Council
ambassadors were still
quibbling over how best to
pressure Sudan and rebels to
end two different wars in the
country even as they left for
Kenya on Tuesday for a meeting
on the crisis." + ], + [ + "HENDALA, Sri Lanka -- Day
after day, locked in a cement
room somewhere in Iraq, the
hooded men beat him. They told
him he would be beheaded.
''Ameriqi! quot; they shouted,
even though he comes from this
poor Sri Lankan fishing
village." + ], + [ + "THE kidnappers of British aid
worker Margaret Hassan last
night threatened to turn her
over to the group which
beheaded Ken Bigley if the
British Government refuses to
pull its troops out of Iraq." + ], + [ + " TOKYO (Reuters) - Tokyo
stocks climbed to a two week
high on Friday after Tokyo
Electron Ltd. and other chip-
related stocks were boosted
by a bullish revenue outlook
from industry leader Intel
Corp." + ], + [ + "More than by brain size or
tool-making ability, the human
species was set apart from its
ancestors by the ability to
jog mile after lung-stabbing
mile with greater endurance
than any other primate,
according to research
published today in the journal" + ], + [ + " LONDON (Reuters) - A medical
product used to treat both
male hair loss and prostate
problems has been added to the
list of banned drugs for
athletes." + ], + [ + "AP - Curtis Martin and Jerome
Bettis have the opportunity to
go over 13,000 career yards
rushing in the same game when
Bettis and the Pittsburgh
Steelers play Martin and the
New York Jets in a big AFC
matchup Sunday." + ], + [ + "<p></p><p>
BOGOTA, Colombia (Reuters) -
Ten Colombian police
officerswere killed on Tuesday
in an ambush by the National
LiberationArmy, or ELN, in the
worst attack by the Marxist
group inyears, authorities
told Reuters.</p>" + ], + [ + "Woodland Hills-based Brilliant
Digital Entertainment and its
subsidiary Altnet announced
yesterday that they have filed
a patent infringement suit
against the Recording Industry
Association of America (RIAA)." + ], + [ + "AFP - US President George W.
Bush called his Philippines
counterpart Gloria Arroyo and
said their countries should
keep strong ties, a spokesman
said after a spat over
Arroyo's handling of an Iraq
kidnapping." + ], + [ + "A scathing judgment from the
UK #39;s highest court
condemning the UK government
#39;s indefinite detention of
foreign terror suspects as a
threat to the life of the
nation, left anti-terrorist
laws in the UK in tatters on
Thursday." + ], + [ + "Sony Ericsson and Cingular
provide Z500a phones and
service for military families
to stay connected on today
#39;s quot;Dr. Phil Show
quot;." + ], + [ + "Reuters - Australia's
conservative Prime
Minister\\John Howard, handed
the most powerful mandate in a
generation,\\got down to work
on Monday with reform of
telecommunications,\\labor and
media laws high on his agenda." + ], + [ + " TOKYO (Reuters) - Typhoon
Megi killed one person as it
slammed ashore in northern
Japan on Friday, bringing the
death toll to at least 13 and
cutting power to thousands of
homes before heading out into
the Pacific." + ], + [ + "Cairo: Egyptian President
Hosni Mubarak held telephone
talks with Palestinian leader
Yasser Arafat about Israel
#39;s plan to pull troops and
the 8,000 Jewish settlers out
of the Gaza Strip next year,
the Egyptian news agency Mena
said." + ], + [ + " NEW YORK (Reuters) - Adobe
Systems Inc. <A HREF=\"http:
//www.investor.reuters.com/Ful
lQuote.aspx?ticker=ADBE.O targ
et=/stocks/quickinfo/fullquote
\">ADBE.O</A> on
Monday reported a sharp rise
in quarterly profit, driven by
robust demand for its
Photoshop and document-sharing
software." + ], + [ + "Dow Jones amp; Co., publisher
of The Wall Street Journal,
has agreed to buy online
financial news provider
MarketWatch Inc. for about
\\$463 million in a bid to
boost its revenue from the
fast-growing Internet
advertising market." + ], + [ + "The first American military
intelligence soldier to be
court-martialed over the Abu
Ghraib abuse scandal was
sentenced Saturday to eight
months in jail, a reduction in
rank and a bad-conduct
discharge." + ], + [ + " TOKYO (Reuters) - Japan will
seek an explanation at weekend
talks with North Korea on
activity indicating Pyongyang
may be preparing a missile
test, although Tokyo does not
think a launch is imminent,
Japan's top government
spokesman said." + ], + [ + "Michigan Stadium was mostly
filled with empty seats. The
only cheers were coming from
near one end zone -he Iowa
section. By Carlos Osorio, AP." + ], + [ + "The International Rugby Board
today confirmed that three
countries have expressed an
interest in hosting the 2011
World Cup. New Zealand, South
Africa and Japan are leading
the race to host rugby union
#39;s global spectacular in
seven years #39; time." + ], + [ + "Officials at EarthLink #39;s
(Quote, Chart) R amp;D
facility have quietly released
a proof-of-concept file-
sharing application based on
the Session Initiated Protocol
(define)." + ], + [ + "Low-fare airline ATA has
announced plans to lay off
hundreds of employees and to
drop most of its flights out
of Midway Airport in Chicago." + ], + [ + " BEIJING (Reuters) - Iran will
never be prepared to
dismantle its nuclear program
entirely but remains committed
to the non-proliferation
treaty (NPT), its chief
delegate to the International
Atomic Energy Agency said on
Wednesday." + ], + [ + " WASHINGTON (Reuters) -
Germany's Bayer AG <A HREF=
\"http://www.investor.reuters.c
om/FullQuote.aspx?ticker=BAYG.
DE target=/stocks/quickinfo/fu
llquote\">BAYG.DE</A>
has agreed to plead guilty
and pay a \\$4.7 million fine
for taking part in a
conspiracy to fix the prices
of synthetic rubber, the U.S.
Justice Department said on
Wednesday." + ], + [ + "MIAMI (Ticker) -- In its first
season in the Atlantic Coast
Conference, No. 11 Virginia
Tech is headed to the BCS.
Bryan Randall threw two
touchdown passes and the
Virginia Tech defense came up
big all day as the Hokies
knocked off No." + ], + [ + "(CP) - Somehow, in the span of
half an hour, the Detroit
Tigers #39; pitching went from
brutal to brilliant. Shortly
after being on the wrong end
of several records in a 26-5
thrashing from to the Kansas
City" + ], + [ + "<p></p><p>
SANTIAGO, Chile (Reuters) -
President Bush on
Saturdayreached into a throng
of squabbling bodyguards and
pulled aSecret Service agent
away from Chilean security
officers afterthey stopped the
U.S. agent from accompanying
the president ata
dinner.</p>" + ], + [ + " quot;It #39;s your mail,
quot; the Google Web site
said. quot;You should be able
to choose how and where you
read it. You can even switch
to other e-mail services
without having to worry about
losing access to your
messages." + ], + [ + "The US oil giant got a good
price, Russia #39;s No. 1 oil
company acquired a savvy
partner, and Putin polished
Russia #39;s image." + ], + [ + "With the Huskies reeling at
0-4 - the only member of a
Bowl Championship Series
conference left without a win
-an Jose State suddenly looms
as the only team left on the
schedule that UW will be
favored to beat." + ], + [ + "Darryl Sutter, who coached the
Calgary Flames to the Stanley
Cup finals last season, had an
emergency appendectomy and was
recovering Friday." + ], + [ + "The maker of Hostess Twinkies,
a cake bar and a piece of
Americana children have
snacked on for almost 75
years, yesterday raised
concerns about the company
#39;s ability to stay in
business." + ], + [ + "Iranian deputy foreign
minister Gholamali Khoshrou
denied Tuesday that his
country #39;s top leaders were
at odds over whether nuclear
weapons were un-Islamic,
insisting that it will
quot;never quot; make the
bomb." + ], + [ + " quot;Israel mercenaries
assisting the Ivory Coast army
operated unmanned aircraft
that aided aerial bombings of
a French base in the country,
quot; claimed" + ], + [ + "Athens, Greece (Sports
Network) - For the second
straight day a Briton captured
gold at the Olympic Velodrome.
Bradley Wiggins won the men
#39;s individual 4,000-meter
pursuit Saturday, one day
after teammate" + ], + [ + "AFP - SAP, the world's leading
maker of business software,
may need an extra year to
achieve its medium-term profit
target of an operating margin
of 30 percent, its chief
financial officer said." + ], + [ + "Tenet Healthcare Corp., the
second- largest US hospital
chain, said fourth-quarter
charges may exceed \\$1 billion
and its loss from continuing
operations will widen from the
third quarter #39;s because of
increased bad debt." + ], + [ + "AFP - The airline Swiss said
it had managed to cut its
first-half net loss by about
90 percent but warned that
spiralling fuel costs were
hampering a turnaround despite
increasing passenger travel." + ], + [ + "Vulnerable youngsters expelled
from schools in England are
being let down by the system,
say inspectors." + ], + [ + "Yasser Arafat was undergoing
tests for possible leukaemia
at a military hospital outside
Paris last night after being
airlifted from his Ramallah
headquarters to an anxious
farewell from Palestinian
well-wishers." + ], + [ + "The world #39;s only captive
great white shark made history
this week when she ate several
salmon fillets, marking the
first time that a white shark
in captivity" + ], + [ + "Nepal #39;s main opposition
party urged the government on
Monday to call a unilateral
ceasefire with Maoist rebels
and seek peace talks to end a
road blockade that has cut the
capital off from the rest of
the country." + ], + [ + "Communications aggregator
iPass said Monday that it is
adding in-flight Internet
access to its access
portfolio. Specifically, iPass
said it will add access
offered by Connexion by Boeing
to its list of access
providers." + ], + [ + "The London-based brokerage
Collins Stewart Tullett placed
55m of new shares yesterday to
help fund the 69.5m purchase
of the money and futures
broker Prebon." + ], + [ + "BOSTON - The New York Yankees
and Boston were tied 4-4 after
13 innings Monday night with
the Red Sox trying to stay
alive in the AL championship
series. Boston tied the
game with two runs in the
eighth inning on David Ortiz's
solo homer, a walk to Kevin
Millar, a single by Trot Nixon
and a sacrifice fly by Jason
Varitek..." + ], + [ + "A Steffen Iversen penalty was
sufficient to secure the
points for Norway at Hampden
on Saturday. James McFadden
was ordered off after 53
minutes for deliberate
handball as he punched Claus
Lundekvam #39;s header off the
line." + ], + [ + "Reuters - The country may be
more\\or less evenly divided
along partisan lines when it
comes to\\the presidential
race, but the Republican Party
prevailed in\\the Nielsen
polling of this summer's
nominating conventions." + ], + [ + " PARIS (Reuters) - European
equities flirted with 5-month
peaks as hopes that economic
growth was sustainable and a
small dip in oil prices
helped lure investors back to
recent underperformers such
as technology and insurance
stocks." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks looked to open higher
on Friday, as the fourth
quarter begins on Wall Street
with oil prices holding below
\\$50 a barrel." + ], + [ + "LONDON : World oil prices
stormed above 54 US dollars
for the first time Tuesday as
strikes in Nigeria and Norway
raised worries about possible
supply shortages during the
northern hemisphere winter." + ], + [ + "AP - Ailing St. Louis reliever
Steve Kline was unavailable
for Game 3 of the NL
championship series on
Saturday, but Cardinals
manager Tony LaRussa hopes the
left-hander will pitch later
this postseason." + ], + [ + "Company launches free test
version of service that
fosters popular Internet
activity." + ], + [ + "Outdated computer systems are
hampering the work of
inspectors, says the UN
nuclear agency." + ], + [ + " In Vice President Cheney's
final push before next
Tuesday's election, talk of
nuclear annihilation and
escalating war rhetoric have
blended with balloon drops,
confetti cannons and the other
trappings of modern
campaigning with such ferocity
that it is sometimes tough to
tell just who the enemy is." + ], + [ + "MADRID: A stunning first-half
free kick from David Beckham
gave Real Madrid a 1-0 win
over newly promoted Numancia
at the Bernabeu last night." + ], + [ + "MacCentral - You Software Inc.
announced on Tuesday the
availability of You Control:
iTunes, a free\\download that
places iTunes controls in the
Mac OS X menu bar.
Without\\leaving the current
application, you can pause,
play, rewind or skip songs,\\as
well as control iTunes' volume
and even browse your entire
music library\\by album, artist
or genre. Each time a new song
plays, You Control:
iTunes\\also pops up a window
that displays the artist and
song name and the
album\\artwork, if it's in the
library. System requirements
call for Mac OS X\\v10.2.6 and
10MB free hard drive space.
..." + ], + [ + "Favourites Argentina beat
Italy 3-0 this morning to
claim their place in the final
of the men #39;s Olympic
football tournament. Goals by
leading goalscorer Carlos
Tevez, with a smart volley
after 16 minutes, and" + ], + [ + "Shortly after Steve Spurrier
arrived at Florida in 1990,
the Gators were placed on NCAA
probation for a year stemming
from a child-support payment
former coach Galen Hall made
for a player." + ], + [ + "The US Secret Service Thursday
announced arrests in eight
states and six foreign
countries of 28 suspected
cybercrime gangsters on
charges of identity theft,
computer fraud, credit-card
fraud, and conspiracy." + ], + [ + "US stocks were little changed
on Thursday as an upbeat
earnings report from chip
maker National Semiconductor
Corp. (NSM) sparked some
buying, but higher oil prices
limited gains." + ] + ], + "hovertemplate": "label=Other
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Other", + "marker": { + "color": "#636efa", + "size": 5, + "symbol": "circle" + }, + "mode": "markers", + "name": "Other", + "showlegend": true, + "type": "scattergl", + "x": [ + -50.70537, + 16.660423, + -47.977715, + -51.65402, + 17.206654, + -23.452963, + 28.167477, + 14.671119, + -37.6373, + 21.907679, + 49.959976, + -36.581165, + -19.791555, + 11.003371, + 12.786125, + 54.445854, + -20.928211, + 28.192938, + 27.511831, + 30.389194, + 31.907536, + 27.685726, + -0.48236108, + -53.210773, + 24.631432, + -39.491558, + 19.87161, + -22.567608, + 13.9476, + 42.217842, + 23.463217, + -19.96729, + -49.124306, + 15.450646, + -7.3474283, + -28.231606, + 22.48473, + 47.958393, + -22.541676, + -52.717, + 47.226242, + 51.068775, + 50.124294, + -19.31264, + -28.148346, + 44.945942, + -42.825386, + -47.410145, + -29.877638, + 7.788443, + 46.406788, + 48.53827, + -5.8827424, + -35.965088, + 31.687206, + 26.455547, + -10.623615, + -40.76841, + -4.8219795, + -18.956379, + 40.537342, + 3.2403526, + -5.107883, + 63.37852, + 56.515934, + 45.10189, + -42.131943, + -8.153443, + 48.401085, + 0.8158772, + -29.768171, + 7.324227, + 36.802402, + -32.52056, + 24.88585, + -39.654697, + 12.912951, + -18.497515, + 54.15107, + -31.347673, + -48.55776, + 38.79396, + -53.367012, + -27.265852, + -6.4607854, + 13.661437, + 30.355759, + 58.71805, + -25.208595, + 3.1252713, + -41.868053, + 38.756367, + 59.531124, + 47.890396, + -17.98721, + 36.084118, + -13.634508, + 39.42093, + 18.820461, + -30.356327, + -49.554066, + -29.197483, + 55.4732, + -43.75864, + 60.896523, + 56.4989, + -33.627903, + 48.16754, + -28.459074, + 13.827141, + -11.594272, + 47.840588, + -33.894855, + -5.484721, + 1.4320391, + 60.467564, + -13.830222, + -26.233013, + 31.210938, + -36.616104, + 12.191131, + 49.345882, + -53.822376, + -44.628685, + -2.3659778, + -19.861258, + 58.657722, + -44.997097, + -37.276833, + 25.89013, + -10.061741, + -32.693943, + -1.0874362, + -19.60824, + -38.45829, + -15.162608, + 16.015558, + -38.214413, + -18.354656, + 20.328302, + 37.406326, + 45.95487, + 27.38124, + 46.569256, + 15.950565, + 11.055151, + 14.368044, + 40.19783, + -38.585472, + -46.83205, + 15.940281, + 48.277, + -38.63723, + 48.961315, + -7.0522246, + 28.371725, + -23.330465, + -50.323666, + 46.0185, + -44.325756, + -35.855865, + -18.20647, + -7.6315646, + 59.281708, + -53.65577, + -33.435104, + 17.925577, + -48.104885, + 15.851762, + 58.10119, + 41.329796, + 60.929493, + -56.60167, + 49.080627, + 1.6593695, + -1.9387143, + -40.07204, + -32.65333, + -11.705121, + 18.05479, + 52.442997, + 25.193996, + 28.471008, + -23.656853, + 13.627039, + 10.705277, + -8.970166, + 18.345266, + 49.169395, + -44.30361, + -36.139923, + 31.360231, + 50.538635, + 50.209198, + -48.951195, + 29.55601, + -21.136202, + -21.265085, + -41.619125, + -34.370987, + -33.846046, + -16.407732, + 4.6381655, + 16.084637, + 38.928047, + -41.55955, + -33.503048, + 57.835648, + 25.167212, + -31.4103, + 51.287056, + -41.654114, + -43.58004, + -40.584736, + 54.942364, + 12.821454, + 24.011929, + 31.13371, + -20.22472, + 17.79019, + -24.227406, + 15.120078, + -41.80841, + -43.47724, + -39.450546, + 19.99747, + 15.529904, + 45.565693, + -28.54648, + 56.076347, + 19.791918, + -55.67927, + -41.094902, + -27.870377, + -41.256504, + -11.352515, + -46.313496, + -46.637367, + -18.041924, + -18.929783, + -38.786, + -18.44551, + -45.789707, + -9.525661, + 12.873272, + 47.481384, + 16.33122, + 22.366423, + -35.619858, + 54.362545, + 2.4414933, + 25.421625, + 53.08307, + -29.50228, + -34.186226, + -37.5602, + -36.813686, + 20.893494, + 19.51864, + -5.632542, + 11.30494, + 2.9794881, + -45.209023, + -31.500145, + 12.285144, + 55.395947, + 32.21779, + 3.3651476, + -51.390118, + 36.722115, + 58.705086, + 30.618706, + -16.802534, + 2.6427522, + -42.6501, + 18.079544, + -6.2927465, + -46.257027, + -23.344019, + -44.347576, + 23.218187, + 48.1655, + -8.361857, + 2.7036908, + 6.7110343, + -1.3755705, + -17.157036, + 39.944633, + 51.72719, + -18.672327, + 57.632236, + 18.106745, + 10.5324745, + 40.575005, + -23.71305, + -55.86017, + 18.966919, + 31.810251, + -34.78124, + 49.25058, + 52.102192, + 17.243034, + 45.8365, + 4.507162, + 41.15665, + 21.953882, + 45.06367, + 50.470036, + 12.895756, + 13.899155, + -18.132378, + -35.732872, + -8.266737, + 55.406406, + -34.572502, + -5.21674, + 34.643066, + 9.645002, + -39.5287, + -21.22969, + 8.84193, + -6.6976542, + -26.256298, + -42.24456, + 44.60481, + -34.82042, + -45.549995, + -48.911743, + 7.766448, + 40.65631, + -7.7231383, + -46.522186, + 49.13915, + 10.753302, + -36.61156, + 60.835865, + 29.961258, + -37.112934, + -10.257471, + 31.980536, + 27.892096, + 45.230713, + -16.951935, + 29.803865, + -5.4085217, + 25.779589, + -19.065458, + -22.693665, + 56.531708, + -44.68482, + 23.273722, + 2.6259706, + 37.571487, + -29.42919, + -30.674974, + 65.59185, + -16.563955, + 38.63354, + 3.1031818, + -43.12336, + -57.728573, + 13.034079, + 46.481388, + -48.13403, + -27.2014, + -10.165841, + 46.68557, + 49.08016, + -15.232134, + -53.29632, + -7.2577205, + 14.032702, + -31.430248, + 23.928396, + 12.880273, + -27.285727, + -42.180077, + -15.3164215, + -6.620774, + -47.9015, + 11.016033, + -37.857563, + 45.88543, + 35.342182, + -30.674488, + -23.828165, + -37.931133, + -31.504562, + -47.091602, + 17.860275, + -6.3850884, + -16.122215, + -3.119768, + 47.523766, + -28.833778, + 12.732019, + -7.503495, + 47.32294, + -26.526276, + 16.701687, + 34.786186, + -42.6552, + 14.009928, + 18.774673, + -37.64758, + 43.796356, + -14.742917, + 49.697426, + -19.793585, + -53.133896, + 37.340225, + -22.841238, + 2.979671, + -51.962658, + 54.74676, + 41.444393, + -15.730567, + 30.604837, + -44.145668, + 8.863162, + 60.995483, + -44.98284, + 9.040379, + 24.042429, + 25.076736, + 30.519775, + -47.514927, + -40.143944, + -29.305222, + 12.896586, + -30.795404, + 25.85091, + 19.948092, + 20.974108, + -19.33182, + -50.141148, + 66.21081, + -49.376717, + 35.24333, + 18.678154, + -43.173016, + 57.111607, + 48.019886, + -4.780475, + 49.229675, + 52.86177, + -32.70729, + -13.887877, + 19.741331, + 52.435543, + -34.81799, + -22.524883, + -12.82885, + -46.24378, + -29.501112, + -5.0456986, + 31.022472, + -36.60279, + -47.141144, + -11.186273, + -36.80437, + 18.250782, + -8.335074, + -34.644447, + -11.501718, + -9.537627, + 24.104692, + 51.07264, + 61.549442, + 1.9518297, + -42.394825, + 42.282997, + -11.57566, + 15.377659, + -29.24355, + -47.198746, + 33.828228, + 14.915583, + 56.65421, + -53.3065, + 19.173527, + 43.26525, + 62.865932, + -37.63518, + -42.896793, + -15.898649, + 1.5646982, + -46.367523, + -51.349506, + -47.68558, + -10.65396, + -49.790844, + 39.05222, + -27.663815, + -7.4475813, + 41.410755, + 38.42368, + -45.67376, + -20.76551, + -29.445877, + -23.031208, + -37.19868, + 4.37751, + -31.336668, + 8.212411, + -45.453674, + 5.134725, + 14.638772, + -6.1798644, + 1.3949758, + 15.7138605, + -50.869877, + 56.985188, + -44.030884, + -44.016224, + 57.739395, + -24.544922, + 17.170551, + 50.66318, + 48.850945, + -50.093754, + -24.092436, + 17.347712, + -19.335144, + 36.300686, + 39.32889, + -6.3253975, + -25.326218, + 31.211935, + -6.78382, + -50.14104, + -49.846096, + 39.84183, + 24.028929, + 40.939545, + 48.37629, + 25.452175, + -37.92231, + -20.485857, + 51.559708, + -52.251915, + -33.647213, + -9.488163, + 5.1724906, + 46.41191, + -14.337521, + 52.77315, + 27.314432, + -29.272839, + -42.127716, + -45.652252, + -43.886417, + 12.80685, + -32.545635, + 4.3835373, + -26.112938, + -39.891853, + 55.190277, + -24.499039, + -43.219402, + -9.765382, + 12.646676, + 17.87465, + 38.03023, + -42.07617, + 9.909096, + 59.759964, + -46.420776, + 22.181377, + 48.908253, + 29.485273, + -1.1384851, + -36.030876, + 49.40172, + 24.128727, + 49.71215, + -16.348925, + -31.277052, + 40.702366, + -16.921326, + 57.916637, + 16.221085, + -9.602789, + -13.009839, + 51.67792, + 30.202019, + -35.251965, + 57.654526, + 34.585587, + -9.096614, + 25.097984, + 2.5471764, + 61.278408, + -16.914656, + 8.574884, + 23.422081, + 39.786133, + -44.31037, + -28.402678, + 21.295237, + -18.734745, + -28.94602, + 25.800558, + -42.23089, + 33.389534, + -23.825924, + -37.813118, + -45.167847, + 5.8968763, + -22.952112, + 24.184378, + -45.96224, + 54.46313, + -36.155823, + -37.039566, + -54.980534, + -33.989525, + -15.194927, + 46.068615, + -28.049438, + -30.086702, + 48.63991, + 35.595135, + 20.787077, + 29.313784, + -1.0110728, + 8.255305, + 50.553566, + -17.443365, + -12.567652, + -42.189774, + -40.062756, + -52.438923, + -24.236567, + -35.002357, + 54.101334, + -54.033554, + -24.72687, + 31.629358, + 15.709119, + -25.166414, + -7.6725793, + 51.79524, + -51.84964, + -27.801344, + -26.129557, + -52.71393, + 10.710161, + -13.981046, + 42.513103, + -6.9841313, + 39.504147, + 20.738512, + 47.29459, + -10.861444, + -48.87469, + -50.175316, + -35.826897, + -31.892145, + 51.208797, + 61.06654, + 23.318872, + -35.0245, + -3.1801224, + 59.059566, + 41.391323, + 56.262905, + -31.190088, + -35.842033, + -44.238426, + -46.75248, + 46.51155, + -24.869604, + -48.693672, + 27.989025, + 57.75085, + 35.379726, + 34.078384, + 40.129883, + 51.36741, + 44.124233, + 38.459312, + -19.088882, + 42.09723, + -32.190376, + 20.918581, + -25.71062, + -44.228165, + -22.265373, + 54.85633, + -31.057253, + 11.446291, + 48.645084, + 33.95719, + 10.624376, + 34.440483, + 13.654009, + -52.42964, + 23.009165, + 0.9160498, + -33.267147, + 1.6248351, + -36.05716, + -7.2311153, + 45.367435, + -12.550289, + 15.268804, + 57.216434, + 20.570063, + 16.519796, + -20.162544, + -39.967007, + -7.045784, + -13.788036, + -6.436385, + -21.87981, + 49.150986, + -31.409056, + 40.020714, + 1.7450867, + -37.63322, + 36.398586, + -31.830273, + -16.392036, + 19.578056, + -30.145031, + 9.273592, + -22.6749, + 49.763367, + 44.87128, + -31.353453, + -45.900715, + -54.722725, + -9.91315, + -33.41616, + -29.682985, + 27.913883, + 5.769484, + -21.902475, + -28.206575, + 34.063007, + -38.86974, + -19.676825, + -27.176733, + -49.362682, + -44.44146, + 5.3805246, + 2.333401, + 14.209792, + 29.130596, + 40.65309, + 7.33986, + 33.885105, + -38.69257, + 42.74865, + -51.24567, + 46.39128, + 63.1983, + -1.2716205, + 2.7853732, + 26.978062, + 18.276062, + 20.191584, + 25.01299, + 4.6009235, + 9.839586, + 11.750173, + 7.9382405, + -10.997008, + 21.737896, + 49.79338, + -29.136082, + -29.750324, + 54.410885, + -35.14978, + 63.605362, + -51.056225, + 39.934895, + 17.519335, + 17.778656, + 15.492881, + 7.7321296, + 8.952756, + -19.130821, + 40.63617, + -37.452244, + 20.371246, + 30.811249, + -9.127035, + -5.5860677, + 1.1558152, + 47.465935, + -24.740665, + -47.064148, + -54.69983, + 47.272655, + -27.990711, + 63.177612, + -7.06102, + -43.44754, + 24.795843, + -4.7836714, + 41.66488, + 1.8769449, + -24.956768, + 51.543095, + 12.356418, + -53.22971, + 38.820065, + 4.2263513, + -7.9959974, + -23.705156, + -6.0662427, + -37.926384, + -41.1264, + -27.350927, + 31.053217, + -9.149289, + -37.36757, + -16.533398, + 40.088383, + 7.0387945, + -22.092422, + -30.736622, + -44.570576, + 60.45724, + 52.433907, + 9.723743, + -15.802876, + -49.361073, + -25.432766, + 38.667847, + -28.812906, + -22.672857, + -35.77931, + -16.137821, + 27.65755, + 57.766346, + 42.41823, + 26.112234, + -39.176956, + 16.072603, + -48.2029, + 19.677572, + 17.410772, + -6.2585354, + 7.9719267, + -53.251343, + 12.662249, + -9.297528, + -36.831997, + -44.267094, + -42.660313, + 18.940567, + 20.549877, + -19.017382, + 33.992294, + -34.603184, + 56.381645, + -15.977553, + 53.579098, + 7.4309235, + -35.853523, + -15.548051, + -44.87483, + -51.507732, + 19.506048, + -52.502518, + 59.620773, + 8.936648, + 48.37667, + -32.07786, + 14.902041, + 35.445507, + 46.157833, + 49.838924, + -48.87661, + -45.17925, + 29.182852, + -22.362936, + 38.542347, + -10.216267, + 22.10423, + -31.37848, + -2.6893454, + 51.905163, + 21.657618, + -5.704888, + -20.502497, + 30.625587, + -24.823088, + 13.691204, + 28.035511, + 23.045893, + -50.661304, + 43.841885, + -50.370255, + -47.05539, + 56.74711, + 30.591192, + 51.738125, + -11.594388, + 17.440117, + 51.774147, + -23.063238, + -9.929121, + 43.796253, + -38.724506, + 47.406204, + 7.212067, + -41.108536, + -38.19549, + -21.799944, + 14.5572, + -36.380856, + -22.186844, + -33.03146, + -47.564026, + -6.8207917, + -0.203547, + 26.660809, + -45.418346, + -32.97913, + -29.1495, + 41.08887, + 2.4019308, + -34.859055, + 14.605348, + 5.080946, + 62.321815, + 30.915781, + 49.839912, + -13.132145, + -12.614871, + 48.11404, + -33.125538, + 37.93922, + -30.265446, + 4.331932, + -24.302145, + -38.971054, + -6.6933794, + 32.7655, + 58.07306, + 50.09836, + 23.97021, + -44.289158, + -16.34018, + -42.824986, + -37.11219, + 54.922394, + -38.334126, + 22.242004, + -12.324585, + -28.60194, + -35.730442, + 52.352432, + 14.265632, + -36.50344, + -27.018137, + -30.541101, + 53.529724, + -7.2380333, + -40.239014, + 7.0784307, + 20.74278, + 2.5284033, + 25.636118, + 4.454403, + -49.050774, + -23.530384, + -23.313187, + 38.338932, + 9.910433, + -22.21815, + -25.737848, + 51.55675, + 37.103165, + -17.621637, + -31.606474, + -46.921032, + -12.631271, + -34.711258, + 14.978659, + -43.354763, + -22.281115, + 45.54423, + -33.235416, + -43.594814, + 53.86991, + -15.313636, + 47.012283, + -21.579958, + -46.839928, + -45.437263, + 60.093002, + 11.213355, + 32.56739, + -27.061964, + -20.385843, + 15.526145, + -8.932405, + 60.606064, + 9.335806, + -38.67932, + -8.953644, + 39.772743, + 18.62211, + -6.674851, + -41.675705, + -6.503544, + 23.033293, + -5.5465455, + -36.837105, + -4.2590623, + 48.95457, + -42.01228, + 10.529721, + 13.965547, + -3.9804885, + 44.68764, + 48.906673, + 47.63983, + 21.258057, + 62.788, + -6.2482433, + -48.024345, + -12.530503, + -39.613857, + 10.181149, + -34.855972, + 17.598188, + -46.561874, + -17.363302, + 1.3672223, + 32.536667, + 10.24864, + 5.8206697, + -45.638084, + -0.31910038, + -10.62197, + -21.206648, + 38.030407, + -34.547794, + 21.86292, + 56.60054, + 20.400032, + 27.48848, + 2.2426317, + 5.0682087, + -18.876629, + 27.914957, + -17.48441, + -20.422543, + 16.509165, + -27.667318, + -48.115654, + 40.073948, + 60.232296, + 9.352251, + 56.806816, + 2.808305, + -16.641712, + -19.632275, + -41.143227, + 6.707939, + 45.64992, + 19.51436, + -41.17226, + 39.266872, + -6.392582, + 62.91453, + 18.935217, + 46.280994, + 50.306213, + 53.805332, + -13.137335, + 50.443317, + 53.03957, + 44.309578, + -30.403149, + -33.03263, + -30.970875, + -50.138874, + -14.373312, + 8.379798, + 54.42772, + 2.4920332, + 1.7612854, + 34.023724, + -28.959257, + 61.473892, + 50.651646, + -42.69843, + -18.173891, + 27.97626, + -11.489995, + 59.39454, + -50.46992, + 47.18665, + -22.095016, + -0.99369574, + -48.586517, + -28.31348, + 2.79127, + -32.614243, + 16.340908, + 20.619595, + 32.39917, + 59.94177, + 23.400663, + 42.23158, + -40.497093, + 14.445518, + -43.79571, + 56.222717, + 26.900372, + -34.05016, + 59.36177, + -48.04673, + 57.550297, + -10.504851, + -45.725693, + 12.496445, + 60.801098, + -49.58257, + -20.070473, + 57.966537, + 28.753572, + -35.82806, + 55.964886, + -44.020023, + -23.90992, + 45.870426, + 21.319304, + -27.236769, + -37.01328, + -19.117485, + 38.638237, + 49.729176, + -39.115543, + 17.625916, + 11.094263, + 7.11425, + -29.740028, + 18.546873, + 58.080826, + -34.482994, + 37.20064, + 9.897873, + -27.855904, + 24.480858, + -52.830154, + 58.289707, + -48.07056, + -19.067713, + -21.63138, + -40.71425, + -4.696033, + -4.852559, + -17.729515, + 8.527567, + -29.865084, + 25.88273, + -46.45139, + -9.0318775, + 63.36231, + 50.890648, + -8.188348, + 16.88663, + 13.06387, + -25.576069, + -26.325634, + -23.095638, + 29.025854, + -40.87854, + 45.88053, + -38.34742, + -13.60535, + 3.984353, + -1.1919637, + -50.887096, + 50.78542, + -34.409237, + -46.677288, + 5.320594, + 14.373686, + -45.882183, + -32.426746, + 43.456127, + 2.8495433, + 28.731657, + -2.2277532, + 50.339493, + 61.357586, + 11.930037, + -42.132465, + 56.755314, + -18.868166, + -14.928126, + 13.779188, + 23.310764, + -42.33495, + 19.120626, + 18.960714, + 25.783823, + 17.941885, + 55.462612, + 10.820086, + 58.314003, + -45.8806, + -21.790516, + 53.49091, + -51.873066, + -8.934254, + -35.644184, + -43.46856, + -26.787775, + -12.61641, + 11.278602, + -12.760466, + -35.958366, + -9.973649, + -5.3010283, + 8.342169, + 58.012913, + 7.6059, + -7.4377956, + -46.84005, + 49.449314, + 37.930157, + 12.515653, + -54.239532, + -39.886326, + -43.70516, + 57.86416, + 8.195707, + 52.26376, + -40.78544, + -46.046387, + 1.8771796, + -8.241421, + 47.072803, + -12.890557, + -23.360226, + -23.913462, + -10.10402, + -33.456993, + 48.17513, + -34.200912, + 22.029692, + -34.14632, + -40.844006, + 44.906193, + -29.91782, + 4.4929285, + 56.61765, + 56.60834, + -17.537066, + -30.420895, + 56.066643, + -19.92304, + -2.2965894, + 56.162464, + -41.66086, + -57.68235, + 3.6962993, + -19.05618, + 5.52023, + -48.503033, + -18.99317, + 53.77512, + -14.034199, + 47.758217, + -29.327738, + -27.266224, + 50.96032, + -49.10616, + -4.6537275, + 58.05466, + -8.695738, + 15.926025, + -35.493626, + -52.898724, + 4.0713243, + -16.14875, + -40.76337, + -36.11573, + 41.446438, + -3.7340183, + -15.154654, + 58.41072, + 11.970405, + -16.320389, + -19.673914, + 11.040503, + -36.72977, + -9.829185, + 35.8429, + 47.047108, + -37.2606, + 54.494556, + -52.1362, + 13.273838, + 7.288217, + 47.79968, + -20.01322, + -18.065994, + 8.75742, + -54.428818, + 18.142248, + -9.159126, + 29.14241, + -46.200623, + 17.28087, + 13.877883, + -13.831901, + -21.605253, + 21.1013, + 59.32574, + 13.981468, + 40.920834, + 55.53207, + 44.559975, + -10.860374, + 10.2113, + 28.748735, + 10.333969, + -37.78618, + -45.533035, + 53.77833, + -8.867661, + 12.468114, + 3.0369818, + 32.079, + 47.351242, + -55.4472, + 5.742987, + 24.300056, + -21.27348, + -8.906268, + -34.02309, + -0.9226989, + 32.861256, + -5.918376, + -30.542126, + 38.30059, + 48.4094, + 33.499294, + 1.5139743, + -5.9578004, + 22.857521, + -42.396126, + -16.095537, + 29.347134, + 4.3284388, + 45.721344, + 26.680521, + 45.999187, + 49.048878, + -21.678917, + -48.91647, + -11.771681, + -10.15981, + 39.29256, + 8.132189, + 32.81585, + 11.17274, + 22.79567, + 2.0400486, + 19.547178, + -4.0862207, + -9.854177, + -23.889015, + 26.376568, + -54.596252, + -22.090435, + 32.12724, + -50.986782, + -34.252632, + 59.9222, + 45.969334, + 47.935875, + -4.5817585, + 17.717125, + 32.523216, + 19.772266, + 57.007023, + 34.043217, + 30.42877, + 10.665481, + -16.827753, + -38.59416, + -32.974155, + 15.195456, + -36.174, + -45.269844, + 11.543438, + -19.309122, + -28.692097, + 53.714108, + -18.300999, + -49.752243, + -10.5037985, + 34.008293, + 18.401154, + 33.648438, + -44.20961, + -39.52826, + -27.136961, + 59.613667, + 31.749115, + 7.0795293, + -34.181965, + -37.106304, + 19.923655, + 14.908174, + 52.849945, + 10.556734, + -48.20029, + 9.239984, + 15.951407, + -7.4418893, + -28.779457, + -35.19683, + -54.1994, + 20.179276, + 31.14273, + 0.258186, + -2.1609035, + 61.664543, + 14.35011, + -26.758255, + -54.634964, + 14.368874, + -43.92253, + -42.005432, + -39.611347, + 9.892005, + -39.611637, + -24.87918, + -22.471472, + -38.19811, + 30.838337, + -36.996124, + -4.4758306, + -46.204945, + 43.08786, + -24.678703, + -50.613956, + 49.605602, + 6.150114, + 63.165108, + -20.649567, + 47.894882, + 51.314476, + 44.60029, + 6.031961, + 8.659726, + -15.612729, + -9.729161, + -28.362564, + 10.755605, + -36.588448, + 8.7123785, + -12.811854, + -0.94040644, + -45.534595, + 12.619259, + -44.44866, + -4.227074, + 44.015842, + -22.860474, + -30.753082, + 39.41502, + 0.080250725, + -15.496077, + 20.854275, + -10.390649, + -35.993237, + -36.425526, + -5.6656046, + -36.567635, + 59.81665, + -11.675889, + 14.897927, + 41.209156, + 8.117931, + 6.539579, + -12.951042, + -30.48289, + 47.579025, + 56.48261, + -38.7589, + 46.025146, + -36.49073, + -6.355229, + 58.74744, + 46.206974, + -52.00866, + -31.978811, + -43.13706, + -7.6462755, + -31.936037, + -19.532629, + 53.145702, + 7.7298007, + -36.42381, + 12.733178, + 23.083542, + 60.687424, + -38.00677, + 38.102413, + 39.646805, + -46.434704, + -42.961407, + 52.38563, + -16.082205, + -50.200237, + -29.59413, + -10.404932, + -27.002981, + -20.752146, + 34.14185, + -18.822731, + -38.39936, + -34.192577, + -23.879477, + -49.73623, + -20.585733, + 31.320894, + 6.8278956, + 14.610548, + 40.573475, + -19.3257, + -32.563313, + 7.079915, + -7.734347, + 21.593582, + 41.94092, + 22.709345, + -8.220228, + 30.75048, + -50.261745, + 23.351994, + 10.662044, + 6.3287606, + -44.1901, + 20.248484, + 39.690254, + 34.33151, + -21.206255, + 17.894573, + 53.560726, + 18.915913, + -50.147823, + -56.14451, + 50.696335, + 19.135786, + 0.011293956, + -41.132812, + -7.490298, + -6.7789235, + 21.208382, + 5.4172053, + -44.169758, + -47.881756, + -28.388693, + -12.397968, + 29.16581, + -0.9005222, + 58.507614, + 40.03086, + -17.01861, + -49.997864, + -11.5951185, + -38.691113, + 24.29299, + 48.50645, + 38.79774, + -53.174366, + 15.59622, + -8.326396, + 0.79674417, + 10.643132, + -44.02579, + 5.560217, + 0.5841107, + 24.635311, + -28.108793, + 13.113659, + 62.77733, + -20.166492, + 47.435825, + -15.611658, + 18.401346, + -38.040787, + -8.663238, + -30.962019, + -8.084352, + 43.003845, + -39.750137, + 46.27362, + 14.899461, + -45.082096, + -47.16861, + 24.252523, + -4.7970433, + 5.36986, + -16.89367, + -26.904469, + 31.625498, + 10.970106, + 51.867313, + -17.731619, + -34.483925, + -43.073074, + -6.7949033, + -27.989662, + 2.5174408, + 34.368248, + 12.8087, + 35.39813, + -25.524998, + -46.526306, + 53.752186, + 55.804855, + -54.849133, + -40.10975, + -11.253943, + 15.975605, + -24.282412, + -36.69884, + -9.612953, + 27.581682, + 1.6741688, + -53.5042, + -27.687584, + 16.295555, + 3.6958573, + -28.30938, + -35.854397, + 26.508045, + 17.794357, + 30.6338, + 47.806313, + 10.886147, + 56.805237, + -40.808376, + 18.907486, + 49.249695, + -38.4294, + -5.0891867, + -45.114822, + -46.690304, + 49.522606, + -25.18432, + -36.175987, + -41.517033, + -33.290382, + -15.035485, + 61.757652, + 3.8529873, + 61.630924, + -54.139446, + -25.219833, + 39.668633, + 10.995691, + 23.637348, + 33.6961, + 51.79226, + 14.72486, + -53.989174, + 28.194004, + 53.427227, + 45.15016, + 36.015182, + -34.2908, + 43.020264, + 7.9172506, + 54.577732, + -48.755344, + -49.55056, + -39.571285, + -40.278057, + -51.21703, + 18.002365, + -3.3571925, + 19.580015, + -8.731081, + -6.0078135, + 31.860546, + -28.372087, + -0.10420398, + 19.054085, + 37.094307, + -11.813869, + -28.535112, + -1.1245881, + 58.735332, + -40.870914, + 26.428055, + -52.076916, + -16.299625, + 12.898047, + -51.801567, + 35.940807, + 30.280912, + -27.921608, + -36.991142, + 63.004868, + -23.981367, + 47.676117, + 43.803253, + -35.73722, + 52.02361, + 0.08228831, + 57.569775, + -31.23627, + 4.8372564, + 2.4959075, + 6.9097495, + 24.6171, + -36.47172, + -11.448383, + -3.8125634, + -20.261177, + 51.3331, + -4.775729, + 40.77166, + -24.145273, + -0.46443436, + 48.259228, + -45.570045, + -29.613533, + -40.73366, + -19.412077, + -11.283554, + -47.05097, + 49.969627, + -48.772636, + 25.599476, + 36.618427, + -10.298156, + 14.019283, + -43.35723, + 55.361397, + -10.978807, + 51.953743, + -53.829735, + -8.411927, + 15.602155, + -13.247851, + -15.053305, + 40.71827, + -13.399857, + -47.515026, + 62.178337, + -4.658773, + 1.1374025, + -8.963649, + 25.336575, + -29.961985, + -12.003402, + -17.52331, + -50.23115, + 27.973917, + -48.06655, + 39.666965, + -9.277499, + -7.6838555, + 23.369677, + 6.171623, + 26.484608, + 7.0410976, + 19.369898, + -33.914284, + 33.43409, + -15.22937, + -21.86168, + 20.71207, + -7.6405187, + 12.614038, + 17.452501, + 55.207115, + -31.572515, + 32.183567, + -50.991158, + -38.40225, + 16.695406, + -52.86785, + -35.325897, + 18.572897, + -51.80827, + -35.83179, + -41.270184, + -36.710674, + 6.0333753, + 55.5641, + -49.167038, + -21.823997, + -1.3200667, + 5.044943, + -40.638805, + 51.27627, + 47.339336, + 16.012442, + -27.684992, + 63.347527, + 39.062187, + -12.411886, + -41.362526, + 9.572269, + -24.7866, + 26.459038, + -17.990955, + -40.258007, + -2.3985894, + 54.67712, + 2.9941561, + 65.51466, + -37.48171, + 17.726252, + -23.877874, + -34.57765, + -0.9994553, + 45.10427, + 17.785444, + -34.842422, + -51.40557, + -39.0015, + -14.16722, + -31.760511, + -16.35767, + -36.74732, + 47.36583, + 35.328148, + 20.736986, + -50.34398, + -5.775708, + -32.659416, + 30.716692, + 24.382677, + 58.147617, + -19.314493, + -3.8920984, + 16.1644, + 64.86492, + -10.574449, + 19.621206, + 8.682678, + -17.94723, + -24.707636, + -20.651194, + -5.6782784, + -13.584895, + -52.063236, + 32.677113, + 55.061314, + -26.427645, + -33.76177, + -50.93683, + 38.546684, + -14.214475, + 43.151165, + 1.4400622, + -35.708652, + 26.161028, + -41.237144, + 44.980778, + 25.263475, + 16.929596, + -50.64484, + -48.196377, + -10.817454, + -2.0928724, + -25.303522, + 47.840103, + 39.76294, + -23.521646, + 49.251343, + 52.69105, + -43.41168, + 0.50536364, + -41.631573, + 19.154146, + 49.939175, + 46.95092, + 26.26559, + 19.381176, + 12.624142, + 13.547113, + -15.368924, + -44.33141, + 17.735638, + -49.86946, + -25.189764, + -41.6564, + 5.6944747, + 28.887644, + 54.523384, + 11.9049635, + 64.17483, + 19.661798, + -40.866665, + 7.287593, + -48.861267, + 22.103119, + 27.097654, + 58.47151, + 12.937629, + -37.111736, + -49.37285, + -0.5269812, + 50.23622, + -37.09859, + -33.893284, + 18.126286, + -41.025192, + 19.819803, + -2.1707618, + 14.775703, + -27.523653, + 39.812546, + -37.509644, + -48.43532, + 59.636997, + 57.34273, + -37.623196, + -40.202778, + -55.58907, + -41.903214, + 16.772926, + 3.6852949, + 25.670559, + 26.078526, + -49.322422, + -9.049681, + -18.721113, + 48.26851, + 17.1552, + -16.408047, + 9.536311, + 21.02507, + -42.958614, + 12.836097, + 6.9077144, + 13.885367, + -52.688995, + -29.522964, + 25.294838, + 0.9785223, + 42.70037, + 15.134995, + -42.372177, + -30.956533, + 1.8828108, + -15.489649, + 49.12623, + 59.67211, + 10.278181, + -45.431026, + -21.36634, + 47.292377, + 47.805153, + -32.821945, + 3.350846, + 10.675423, + 46.018627, + -27.676836, + -30.13521, + -31.987688, + 2.7699895, + 29.804829, + -4.7174063, + 8.834031, + -30.901245, + -20.815348, + 57.51465, + 37.074707, + 14.13684, + -47.19078, + -45.82224, + -36.344696, + 64.22567, + -46.568104, + -2.3207862, + 10.008406, + 40.90623, + -45.59506, + 42.02211, + 36.001675, + -13.1443, + -43.422806 + ], + "xaxis": "x", + "y": [ + -10.254759, + -20.803589, + -22.326504, + -8.559602, + 22.728033, + 7.8286805, + 23.284092, + 21.800117, + -20.467894, + 22.159718, + -3.7095485, + -16.367886, + 34.67725, + 29.896206, + 6.133116, + -10.627376, + 0.20705454, + 8.674217, + 25.905638, + -3.9541492, + 9.192532, + 25.749458, + 39.722248, + 13.600263, + 7.8999453, + 18.938295, + -7.791385, + -2.1101115, + -17.816854, + -27.949192, + 10.707973, + -5.9476533, + -0.62792206, + 21.421028, + 17.02401, + 3.4177885, + 23.633503, + 9.072081, + 2.5558534, + -10.392384, + -21.783358, + -12.137919, + 8.247171, + -23.29184, + -18.170088, + -27.218586, + -29.326565, + 12.92886, + 7.4292397, + 11.001149, + -35.47235, + 0.2254613, + 26.266212, + -12.614066, + -32.542274, + 6.058426, + 14.819815, + 25.03953, + 4.199548, + -0.6994232, + -29.048313, + 8.901868, + -3.3929446, + -18.953293, + -16.318848, + 1.29799, + 24.970749, + 14.150794, + 0.27214336, + 7.6804514, + -22.118223, + 4.9480743, + -22.427275, + 0.9335098, + 26.755693, + 26.929127, + -26.440922, + 9.1828, + 1.5617585, + -19.087698, + -14.938796, + -2.3146381, + 13.022359, + -21.471975, + 25.554472, + 2.532362, + 23.373753, + -13.859794, + -25.318462, + 22.522005, + -32.54127, + -10.261337, + 2.3437028, + -11.51763, + 24.106895, + -28.920532, + -2.2139447, + -14.537146, + 9.316687, + 9.6741905, + 12.820546, + 4.9088416, + -28.30168, + -17.468342, + -17.185091, + 10.214211, + -20.446613, + -11.397742, + 8.161042, + 27.62886, + 26.281322, + -29.872732, + 5.007877, + 6.2455893, + 5.951754, + -19.64737, + -1.2898456, + -8.973769, + 0.16790108, + 12.575957, + 15.638516, + 9.72588, + 13.993413, + 22.678474, + -17.482075, + 14.391562, + -19.213398, + 17.073126, + 30.481924, + 20.813839, + 32.03464, + -26.55557, + 12.0553255, + -16.22469, + -18.176107, + -3.6315196, + -19.35426, + 20.519714, + 4.681029, + -24.165535, + -17.097263, + -23.540205, + -22.659904, + -30.161833, + 28.830767, + 14.3665, + 0.061168052, + -32.789925, + 4.0007343, + -27.456821, + 23.813591, + -30.553509, + -24.490698, + -19.611755, + 29.56973, + 21.227903, + 7.7406225, + -10.1600275, + 8.52158, + -10.852377, + 5.442065, + 9.661537, + 22.864084, + 4.1424494, + 7.6243353, + 4.249151, + 31.043804, + -10.734537, + 3.844936, + 1.4751459, + -12.852704, + -20.392239, + -6.189112, + -4.5837173, + 1.4175098, + -21.713743, + -13.330445, + 12.867583, + -10.440891, + 3.6453905, + 8.166061, + -11.337284, + 16.828537, + -8.8150835, + -16.43065, + 9.330847, + 20.16529, + 9.5765, + -22.28117, + -9.1425705, + -23.877768, + -10.817816, + 5.1117396, + 7.9826016, + 11.228575, + 17.663988, + 2.542931, + -1.3406546, + -23.981632, + 12.972686, + 4.730411, + 30.063469, + 3.6004014, + 18.804445, + -13.418971, + 30.71818, + -14.152756, + -24.45379, + -11.355663, + 6.520791, + -9.840589, + 21.164257, + -8.373115, + -8.409088, + 9.545558, + 4.881303, + 30.134317, + -32.61165, + -17.390278, + 32.50385, + 19.963877, + 8.090675, + 31.114712, + 30.646704, + 21.478413, + 14.554468, + 20.755419, + 11.230936, + 6.923768, + -13.468946, + 23.0764, + 20.141148, + -15.70016, + 8.499566, + -19.558147, + -10.837732, + 7.830664, + 24.00407, + 6.959669, + -17.884281, + 24.8098, + -24.985989, + -12.053112, + 8.462659, + 18.15951, + -5.462048, + 2.4064643, + 8.999294, + -12.727203, + -13.069021, + -6.154228, + 14.864804, + 1.5735915, + -25.217607, + -11.249722, + 27.957365, + -0.7906725, + 19.460798, + -2.3412774, + 6.4599543, + 2.4203362, + 32.717518, + 28.99686, + -18.920874, + -7.624435, + -23.937035, + -15.694869, + 2.3350112, + 9.491719, + -25.943512, + 0.82049704, + -3.9954906, + -16.211517, + -10.548126, + 33.583965, + 22.352716, + -0.7140172, + 28.585188, + 20.132593, + 10.375427, + -18.380714, + -21.956186, + 18.302557, + 8.7813, + 27.98141, + 5.231712, + -1.274212, + -17.928478, + -17.166925, + 5.588625, + 1.8213869, + -20.784616, + -9.940092, + -11.329836, + 1.3020672, + -5.6699047, + 2.9951952, + 7.513018, + 18.828894, + -8.567718, + -11.798271, + -2.4976819, + -25.911339, + 22.716187, + -10.770047, + 15.819128, + -15.446808, + -32.171726, + 5.0620914, + 12.743932, + 7.1431947, + 20.908062, + 27.65378, + -29.32608, + -12.216588, + 3.5037541, + -35.429436, + -8.023369, + 19.798025, + -4.302394, + 16.329193, + -23.965172, + 8.796663, + 16.477135, + -11.357406, + 32.09736, + 26.441679, + 21.586815, + 30.292624, + -14.503349, + 19.197943, + -14.683218, + -3.407611, + 23.7153, + -14.726069, + -17.214022, + 15.711783, + -8.98979, + -22.324871, + 0.59863055, + 16.493795, + -27.750652, + -28.93897, + -5.3719177, + -23.418943, + -9.659326, + -23.277813, + 16.425425, + -19.531103, + 18.54026, + 0.31460643, + 31.197924, + -14.720505, + -0.26035935, + -21.057713, + -27.277906, + -7.310227, + -15.416589, + -1.605775, + -8.874298, + -13.5169735, + -26.390093, + 0.7872089, + -7.2581453, + 22.63779, + 28.57203, + -23.089176, + -19.599855, + -21.929888, + -10.379873, + -11.895842, + -17.141865, + -16.003376, + -14.515779, + 10.840164, + -26.575148, + 3.1463404, + -3.7059593, + -8.936446, + -23.257317, + 30.278105, + 15.54324, + -31.523523, + -15.298813, + -29.652391, + -9.050367, + 18.134205, + -14.212201, + 10.717227, + 19.883846, + 21.597916, + -19.211506, + 28.315454, + -11.721406, + 16.122732, + -6.269737, + -14.575271, + -20.626392, + -9.711501, + 20.470428, + -8.267473, + 33.287487, + 25.027699, + 15.167711, + 12.847039, + -22.223913, + -13.995945, + -28.966488, + 14.344031, + 7.419209, + -21.779205, + 24.548212, + 23.27041, + -17.763275, + -27.218397, + -36.186253, + 5.0752234, + 0.31401816, + -0.48519766, + 9.704817, + -22.044197, + 28.721743, + 14.702273, + 18.21779, + 16.7961, + 9.027207, + 21.439281, + 25.772839, + 5.9104095, + 18.049044, + 11.854107, + 25.408955, + -1.7761685, + 7.837817, + -11.143075, + -11.487356, + -25.348227, + 20.674139, + -15.303513, + 34.420277, + -6.806543, + 2.799256, + -27.043676, + 32.15406, + 6.988793, + -29.502745, + 5.2307787, + 24.185543, + 17.168627, + -6.9711366, + 28.700588, + -16.839674, + -6.9957857, + 19.155857, + 22.57425, + 4.2664466, + 10.645888, + -2.8677607, + 17.716654, + 33.268223, + 13.592724, + 35.533974, + 35.79897, + -9.217092, + -7.505608, + 16.755838, + 19.649885, + -13.013833, + 2.553211, + 5.488912, + 25.960653, + -14.678428, + -6.362675, + 15.933173, + -25.562366, + -7.9709535, + -19.333553, + 5.761818, + 5.2738123, + 14.799318, + 0.9805258, + -30.191147, + -8.254407, + -9.329842, + 24.331854, + -1.1096494, + -27.81828, + -23.302309, + 10.189425, + -0.9053779, + 14.969123, + -12.578425, + -16.734713, + -25.194714, + 34.912987, + -36.29533, + -0.7015533, + -21.124685, + 33.794212, + -20.977274, + -19.704374, + 23.483368, + -15.128482, + 8.0363655, + 2.2579987, + -16.33133, + 31.233051, + 22.297411, + -11.6483135, + 3.5171926, + 23.886812, + 12.337329, + -19.59588, + -30.116133, + 27.538383, + -19.748474, + -4.7339125, + 19.465944, + -18.429428, + -24.985508, + -24.043522, + 26.484413, + 16.774218, + 5.9628015, + -14.398376, + -23.032887, + -16.154268, + -11.766295, + -27.591204, + 20.015493, + -20.486948, + 7.6020126, + -13.656402, + 14.815331, + -33.948692, + -33.920197, + -9.174384, + 20.629124, + 16.143784, + 8.925708, + 7.7047353, + -21.596968, + 16.84247, + 11.881365, + -22.970503, + 24.66648, + 1.9238061, + 25.418554, + -17.758942, + 3.5172246, + 23.261137, + -8.986503, + 28.923544, + -7.5245304, + -15.130549, + 5.0646152, + 21.07103, + -5.8668604, + -14.940109, + -6.4981833, + -20.06512, + 23.290081, + -11.591567, + -27.786598, + 20.645449, + -5.3597302, + -11.159512, + -13.735753, + 18.798145, + -32.18803, + 8.9016, + -22.157974, + 26.788364, + -16.650103, + 18.377977, + -18.147429, + -24.88111, + 21.901451, + -14.823587, + -0.6368593, + 3.2132275, + 31.100603, + 16.802742, + 20.371767, + -28.899687, + 0.73946625, + 0.94949424, + -14.675726, + -24.362509, + 31.862827, + 23.13797, + 35.12017, + -18.907366, + 24.827017, + 31.66899, + -18.148087, + -24.660992, + 9.816621, + 16.572128, + 25.328583, + -15.456796, + 1.9859632, + 5.658062, + -5.2393093, + 9.180699, + 7.721218, + 3.9763682, + -14.759153, + 8.72019, + -12.5096655, + 4.320076, + 2.0307107, + -12.368451, + -11.865506, + 16.297318, + 0.7318651, + -13.755454, + -21.899122, + -11.081378, + -19.075409, + -13.679028, + 10.51185, + -10.045945, + -2.6716044, + 13.364902, + 20.333702, + 5.9486156, + -30.512154, + -1.8922254, + -14.551722, + -13.595177, + 24.951237, + 15.502925, + -26.033178, + -15.84722, + -0.48769227, + 5.509095, + 25.674028, + 23.005444, + 12.414623, + -7.935221, + 24.642124, + -22.191689, + -19.237648, + 16.660208, + 5.5806613, + 9.362999, + 16.740986, + -14.059228, + -9.914337, + -20.576859, + -10.982109, + 31.096636, + -11.43558, + -17.933233, + -22.175861, + -14.856947, + 26.15921, + -23.924995, + 6.894826, + 4.1693807, + 5.6076837, + -17.656506, + 15.090964, + 1.2161766, + -9.937122, + -27.618727, + -3.5818095, + -14.13704, + 25.846468, + 19.352674, + -22.007416, + 23.278618, + 11.748135, + -22.37126, + -22.028944, + -10.037108, + -25.306404, + -7.7222157, + 3.5807598, + -6.6086307, + -19.699232, + -15.10728, + -17.251148, + 10.148522, + -0.68818355, + 7.5768538, + -17.733555, + -23.194473, + 9.637636, + -2.6014824, + 9.428179, + -10.8705435, + 8.272561, + 18.622755, + 8.240764, + 7.8728004, + 13.976609, + 21.211613, + 10.388335, + -13.317306, + -0.20468314, + -0.7534798, + 16.867065, + -22.69967, + 22.19843, + 29.903488, + -29.479254, + 14.083497, + 0.6598771, + -8.660773, + -7.2729115, + -11.945698, + 23.76637, + -16.428364, + -28.303225, + -11.955685, + -30.203144, + -4.9588523, + 26.250034, + 19.381159, + -16.469437, + -14.535694, + -24.852484, + 12.103588, + 7.8694215, + -8.026257, + -6.199936, + 9.750696, + -14.905879, + -22.042368, + 2.0052595, + 15.873175, + -11.668809, + 7.235856, + -21.42294, + 14.838855, + 16.791052, + -21.904455, + -23.169117, + -20.787516, + 9.315685, + 34.738625, + 10.819606, + 20.726511, + -10.898081, + 31.885904, + 11.005908, + 15.028398, + -3.1344242, + -3.9499974, + 14.654819, + 8.201109, + 17.144817, + -19.819767, + -19.525257, + -4.076858, + -24.730019, + 11.900147, + -1.3390135, + 26.11797, + -2.478072, + -23.535704, + 27.143415, + 17.844543, + 19.694197, + 30.822157, + 11.223421, + 17.761076, + 13.325627, + -13.261404, + 2.2092547, + -13.576142, + -11.716383, + 27.541485, + -18.290712, + -25.388409, + -15.495678, + -32.85601, + 34.832695, + 15.818021, + 12.122141, + 33.150494, + -0.5336322, + -13.886067, + 28.821224, + 20.72354, + -33.77542, + 3.162032, + 17.181808, + 34.996464, + -22.37821, + -4.1373553, + -20.077517, + -16.791988, + -33.790863, + 4.8909636, + -23.158052, + 13.435741, + -22.73552, + -0.6918705, + 27.578976, + -23.911886, + -0.9915625, + 0.41720697, + -28.11098, + -15.606873, + -21.062717, + -15.843517, + 7.1253057, + -12.007193, + -23.275118, + 15.710144, + -13.556541, + -15.989742, + 1.5220636, + 15.600531, + 3.0372694, + -13.601137, + -7.148113, + -24.879805, + -0.8274632, + -11.567605, + 19.323282, + -7.7168093, + -27.03218, + 5.8135962, + -7.6383777, + 1.1989386, + 3.9182017, + -0.47444645, + -25.135891, + 22.896002, + 0.94497335, + 9.556583, + -4.4569497, + 21.02248, + -25.89945, + -18.168903, + 17.865675, + 22.459995, + 12.360714, + -24.076357, + -15.80312, + 21.917862, + 21.659195, + 33.719093, + 19.704102, + -2.2529974, + 31.99901, + -29.042156, + -26.121319, + 33.52397, + 23.902458, + 7.067429, + 26.534893, + 9.6071, + 29.210163, + -23.639217, + 3.7444665, + 1.8415234, + -4.9220414, + 22.216219, + 21.501694, + -17.915682, + -17.60881, + 19.686275, + 16.870352, + -16.338673, + -2.4079158, + 10.431047, + -11.452592, + 20.084156, + -34.98855, + -30.50168, + -1.8533841, + 13.475318, + 22.79436, + -23.127438, + -2.6888435, + -26.898434, + 32.299854, + 9.865102, + -15.889842, + -7.1564, + 14.4235935, + 10.5956135, + 16.942707, + -17.442066, + -6.0696855, + 0.2748501, + 33.509598, + 2.4050539, + 7.209693, + 12.352939, + -0.83113074, + -16.57776, + 26.730667, + -13.937987, + 5.5682783, + 8.4994335, + -12.461162, + 24.32622, + -25.814455, + -19.692043, + 8.181132, + -25.507462, + -16.080286, + -1.2937344, + 18.989775, + 16.529331, + -10.464009, + 11.700205, + -25.712864, + 24.65294, + -5.132745, + 24.787573, + 19.01329, + -9.251707, + -2.7055879, + 14.609039, + 27.475252, + 14.475491, + 0.96339697, + -11.8820095, + 7.1217036, + 31.858027, + 16.848389, + 32.03336, + -13.837845, + -33.480656, + -20.987251, + 30.462563, + -16.143095, + 6.7093077, + -15.854709, + -24.921698, + 16.484713, + -1.7420386, + -23.097334, + 18.896671, + 34.8398, + 10.520301, + 3.5488389, + -18.068623, + 30.076416, + -29.86582, + -8.282391, + -8.46684, + 13.576438, + 3.0699391, + -16.238358, + 2.9773757, + -14.182415, + 17.441216, + -25.85015, + 9.083556, + 22.073168, + 19.385956, + 8.168441, + 13.999631, + -13.918425, + 19.32553, + -19.83609, + 29.535501, + 31.019588, + -6.5198464, + -16.273378, + 31.29178, + -20.836182, + 8.972529, + 14.504229, + -22.65874, + 24.289896, + 0.45974386, + -8.057026, + 7.783574, + -12.477235, + 3.8825731, + -3.5055225, + 15.380986, + 22.033895, + 3.7059414, + -1.0848922, + 0.16963075, + -5.582006, + 11.250292, + 21.913166, + -23.06022, + -13.376665, + -5.6566067, + -5.0115275, + 33.256733, + -27.384535, + 22.36791, + -23.036457, + 3.1787782, + -11.463062, + 16.85544, + 17.925854, + 26.127491, + 34.042473, + 3.7194152, + 11.578919, + -3.056115, + 8.806574, + -12.564382, + 26.605755, + 21.529955, + 25.043688, + 17.78518, + 25.579552, + 27.044067, + -29.090658, + 21.886444, + -29.44567, + -3.69288, + 7.423554, + 19.89922, + -13.892162, + -9.352621, + -23.756565, + -17.759132, + 21.111221, + -15.3389635, + 20.052608, + 8.306711, + -6.695091, + -0.2840251, + 29.565565, + 6.3890157, + 20.825033, + -15.78091, + -3.9792998, + 8.250312, + -4.315795, + 33.91667, + 31.587502, + -4.7497973, + 0.70931256, + 22.03959, + -1.3183376, + -13.819872, + -8.057265, + 2.5191355, + -6.09211, + -1.2537154, + 1.041188, + 6.271001, + 15.563097, + 3.0869732, + 19.476908, + -7.959283, + -20.58928, + 17.528534, + -34.817635, + 26.520325, + -7.863438, + -13.616495, + 34.081158, + -23.899826, + 19.227066, + -0.1847365, + 21.436638, + -21.634756, + 27.98984, + -9.426962, + 17.888885, + 18.802984, + -12.24037, + 25.563747, + -18.85435, + 20.995552, + -25.321373, + 11.024011, + -19.68378, + 30.48236, + -26.103676, + 10.497953, + 3.9857144, + -11.662108, + 14.603634, + 7.0568976, + -9.688497, + -4.4100275, + 2.030001, + -22.706993, + 19.098873, + 31.796051, + -2.4754145, + -26.096392, + -21.39815, + 3.600532, + 28.98958, + -24.192507, + -22.364601, + 24.713762, + -16.769764, + 21.682661, + -1.3566388, + 16.40951, + 8.210962, + -15.716439, + -34.972008, + 26.949068, + 21.239998, + 12.173473, + 20.502365, + -12.030829, + -28.317688, + 4.4826207, + -4.760545, + -10.980467, + 30.730364, + 20.87726, + -17.78651, + 22.801989, + -5.3119135, + -20.541088, + 12.556309, + 1.3681566, + -15.915366, + 23.323511, + -7.8275642, + 1.0861593, + 8.230685, + -17.60057, + 4.390221, + 9.649646, + -16.683647, + -22.447065, + -10.756376, + 27.087646, + 2.2553952, + 5.474195, + 6.01643, + 14.907442, + -19.740395, + -14.250181, + -28.855429, + -21.907415, + -6.474749, + 26.200584, + 23.3236, + 5.0985155, + 23.742764, + -23.47392, + 10.961509, + -9.009804, + 10.729193, + -16.08439, + 24.293411, + -14.420636, + -0.6379835, + -7.351985, + 4.601816, + -21.606695, + 10.600249, + -19.460848, + -1.0193497, + -5.6577854, + 1.2037258, + -19.941338, + -17.019722, + 32.26841, + -20.533716, + -23.794706, + 2.3137836, + 35.702885, + -2.6479704, + 21.060795, + -4.315942, + -22.034695, + 0.85024196, + 13.542582, + -8.745571, + 6.832896, + -10.188763, + -13.390235, + -0.5990197, + -23.021431, + -5.876716, + -11.976225, + 4.2575808, + 27.501059, + 11.98244, + 26.565365, + -1.931646, + 24.216267, + -16.869408, + -8.099275, + -14.887161, + 2.2845645, + 11.149261, + -15.141055, + 27.739674, + -3.0804467, + 5.0789285, + -17.30228, + -3.2769468, + -17.239506, + 4.583181, + -19.281757, + -3.5722063, + 28.793531, + -16.723783, + 25.030203, + 9.832679, + 20.863323, + -19.392942, + -15.235338, + 11.71164, + -24.406261, + -15.53886, + -16.890417, + -19.303434, + -12.302218, + -21.589676, + -14.588415, + 15.091036, + -17.137983, + -23.051016, + -11.65064, + -1.327813, + 4.7823358, + -19.877468, + 29.76316, + -3.8284235, + 21.326263, + -17.971964, + -2.6890767, + -8.098414, + -20.775913, + 11.0288925, + -15.161179, + -13.708067, + 6.9839473, + 9.420364, + 15.523962, + -1.839738, + 18.062141, + 35.796543, + -26.4286, + 4.53065, + -3.197111, + 15.938968, + -5.59304, + -9.126152, + -23.904675, + 8.384921, + -3.4012072, + -5.3693423, + 32.041183, + -33.521553, + 9.530565, + 15.937399, + -27.414234, + -24.713099, + 24.769993, + -8.645808, + -13.032957, + -23.740261, + 8.2281, + -20.86936, + -5.3864436, + -13.534582, + -1.0408515, + 26.929934, + 16.484713, + -3.2705798, + -22.339233, + -17.725012, + 6.1994753, + -13.713904, + 8.064646, + -8.887762, + -27.97785, + 8.281391, + -14.383507, + 1.1649175, + -17.226963, + 23.824167, + -0.03827765, + 21.001068, + -1.6157911, + 1.0350281, + 23.757103, + -2.2386749, + -12.003306, + -5.807004, + 5.4682074, + 3.4183521, + -18.329607, + 10.1421995, + 21.500256, + 20.873947, + 14.622503, + 20.323536, + -22.500238, + -5.1817036, + 26.616285, + -10.172258, + -14.895687, + 7.471235, + 4.855366, + 8.929348, + 3.4454656, + 24.15315, + 33.191727, + -17.779476, + 13.368094, + -16.79903, + -1.2212269, + 29.02862, + 1.353517, + 33.686493, + -9.61028, + -10.290435, + 17.499424, + -18.92016, + 10.638852, + -4.0155163, + -29.874123, + -23.89452, + 17.025469, + 12.36343, + 25.982975, + -5.359385, + -20.511335, + 26.314108, + -14.478729, + 20.105099, + 10.390779, + -2.7448454, + -21.707514, + 2.8463974, + 20.082417, + 39.494793, + 23.544054, + 33.45021, + 1.2731425, + 7.00291, + 20.49504, + 11.026453, + -14.920918, + 21.672586, + -24.179169, + -22.502762, + -18.470171, + -5.233647, + 15.536683, + 7.5924697, + 31.43023, + -10.685339, + -5.5552483, + 30.057226, + 2.6354103, + 17.865553, + -25.625107, + -23.603718, + 16.79463, + -21.343506, + 24.513098, + -22.31949, + -13.1784725, + 14.572172, + -21.927172, + -0.43766883, + 26.446459, + 7.797492, + 27.607801, + -14.08771, + 28.953205, + -1.2875158, + -17.776453, + 1.3350589, + -0.14630945, + -12.744574, + -5.8219385, + 6.380316, + -24.39855, + 1.6557639, + -25.33089, + -10.88375, + -5.4497714, + -3.2008982, + 3.516546, + 3.7044208, + -14.088412, + 1.8123101, + -2.0853994, + -12.914869, + -14.570528, + 6.286185, + 29.915886, + 18.577192, + -19.750566, + -4.8032465, + -14.996935, + 9.808406, + 3.1115727, + 10.539988, + -0.25747964, + 7.8065777, + -9.5224, + 22.650063, + -8.527657, + 25.720367, + 27.335323, + -27.719013, + -27.493273, + -28.8183, + 16.676228, + 15.222469, + -6.1142654, + 23.31772, + 6.885112, + -21.120987, + 31.183216, + 16.581377, + -1.3270321, + -3.024608, + -24.535004, + -35.037914, + 27.32407, + 2.2356973, + 16.557335, + 8.043718, + 4.2089057, + 24.168753, + -0.42459357, + 26.167639, + -19.28855, + -16.932995, + 0.031842478, + 11.079847, + 23.264338, + 11.247658, + 28.108557, + -17.26478, + 23.26528, + -5.613793, + -12.292187, + -13.964472, + 21.349566, + 21.782167, + 26.02513, + -30.554207, + -20.807219, + -22.266432, + -16.260057, + 13.463569, + -20.409258, + 5.911049, + -3.838907, + -30.899261, + -25.502863, + 17.450424, + 4.5370917, + 7.3130083, + 29.060263, + -1.2906566, + -9.992426, + 9.496942, + 19.615667, + -15.057436, + -14.524883, + -5.6858554, + -8.944074, + 30.993462, + -18.399357, + 4.312004, + -12.452006, + 11.88096, + -26.893, + 10.486003, + -14.269513, + 13.904057, + -14.193346, + -17.597988, + -13.744734, + 19.081799, + 7.1376367, + -20.63535, + 0.17712176, + 26.276295, + -4.0243726, + 18.80954, + 8.85504, + -11.71116, + 10.333615, + -33.28943, + -13.433018, + 25.406893, + -21.37861, + -30.53585, + -0.6449607, + -17.676962, + -33.109673, + 6.502574, + 25.979095, + 13.889341, + 24.452019, + -11.330729, + -14.508683, + 7.7211857, + 30.14757, + -15.281551, + 25.445856, + 23.137957, + 2.9930232, + -11.392148, + -3.4584122, + -17.335155, + 32.249325, + 1.1835473, + 0.4309157, + -1.922125, + 18.76773, + 12.763572, + -5.1183553, + -19.383118, + -11.329933, + -9.979049, + -19.62514, + 14.371391, + -9.079416, + 17.039936, + 12.198028, + 17.744976, + -27.767008, + 4.7606173, + 20.943676, + -2.7953665, + 34.946663, + 21.359537, + 23.354967, + 32.181087, + 10.895949, + -23.63617, + 16.164768, + -21.386267, + -0.20407373, + -10.61583, + 18.747564, + -8.708449, + 26.564816, + -20.358099, + 3.6623113, + 2.833431, + -2.406363, + -7.6430187, + 30.990358, + -1.6160171, + 22.291674, + 14.2712755, + 8.649531, + -22.09123, + -3.9283407, + -15.144995, + -5.257486, + 16.290205, + 24.053005, + -5.443865, + 29.637974, + -30.894657, + 10.8514185, + -19.329512, + -1.7249132, + -27.617838, + 12.135396, + -20.576097, + -32.521618, + -17.759117, + 14.102587, + -1.4501517, + -17.441105, + 22.34238, + -1.5771652, + -3.4706712, + 19.873198, + 17.654528, + 14.297588, + 35.126564, + 3.530811, + 22.92706, + 1.305536, + -5.8584995, + -3.4917607, + -25.70212, + 15.667845, + -13.110925, + 1.5236746, + 1.27955, + 26.836803, + 22.695467, + -7.542444, + -24.459936, + -4.085233, + -24.834877, + -13.123537, + 13.346765, + 3.3096304, + 5.8128743, + -9.243302, + -22.380308, + 24.534492, + 32.18937, + 0.7944149, + -17.298498, + -7.3226933, + 23.025293, + -0.33986145, + 14.641378, + -32.17766, + 9.108203, + -15.654366, + -3.2708795, + 1.7839518, + 4.667992, + -21.404385, + 33.032204, + 0.07473384, + -8.874142, + 19.918457, + 2.485261, + -26.038076, + 13.791234, + 19.88417, + 26.989523, + 6.4794717, + -8.599584, + 26.08512, + 35.79187, + -3.0957053, + 1.5328475, + -15.78256, + 14.641849, + 0.75382006, + 13.353416, + -20.758772, + 27.588259, + -8.591754, + 7.6756034, + -32.257572, + -3.6816385, + -8.807442, + -23.705658, + 26.69215, + 5.574528, + -3.3590631, + -16.991213, + -18.813177, + 20.353582, + -8.202672, + -2.241037, + -22.663652, + -10.86935, + 22.6146, + 0.538039, + -11.617886, + -7.3185177, + 5.459471, + -20.510658, + 14.793362, + -15.245933, + 2.8498745, + 30.176495, + 25.41137, + 12.340705, + -14.110134, + 20.984993, + -20.736963, + -21.078281, + -16.38932, + -10.101326, + -29.059853, + -14.522557, + -31.21759, + 11.320027, + -1.3346295, + 19.095402, + 3.5003624, + -0.27149853, + 23.530079, + -1.4504046, + -20.799906, + 26.357058, + 25.323908, + 21.914633, + 19.832611, + -14.345478, + -12.780764, + -15.090428, + 0.40740138, + -16.226871, + 22.365917, + 24.898293, + -22.19336, + -17.027992, + -19.892523, + 23.981928, + -11.016326, + -16.473738, + -20.647305, + -18.943878, + -34.179035, + -14.075991, + 1.9298484, + 20.942158, + -15.682211, + -9.76076, + -23.77744, + 2.101532, + -25.935007, + 8.422051, + -21.395668, + -12.298222, + 2.824297, + 12.158624, + 15.439734, + -5.986609, + 22.680363, + -19.286484, + 30.605867, + -0.7899231, + 18.014528, + -18.204716, + -18.893454, + -2.6403008, + -26.197563, + 0.6461262, + -17.935425, + 21.006203, + 19.50926, + -25.124516, + 19.076454, + -13.34786, + -20.217596, + -18.721956, + 13.471852, + 10.719515, + -6.343975, + -4.427436, + 2.1415112, + 0.124456935, + 9.154357, + 15.850318, + 14.106509, + 18.979578, + -25.880474, + 15.075585, + 20.326845, + -15.592323, + -16.127396, + 19.439365, + -18.178284, + -7.721521, + 18.546848, + 1.3289208, + -21.118057, + 15.136754, + -8.462077, + -6.078381, + 0.24295494, + -14.893564, + -3.098876, + -22.965818, + -2.973772, + -10.412807, + 36.82579, + 0.043326903, + -0.730605, + 20.569399, + 20.47704, + 34.56152, + -12.61784, + -22.44099, + -13.279965, + -28.35139, + -9.076381, + -14.49968, + 11.381456, + 27.552359, + 10.113919, + 4.322983, + -16.923988, + 18.366398, + 4.072649, + -18.502573, + 14.2359915, + 1.2205616, + 34.52153, + -13.276994, + 16.888266, + -17.09381, + 26.655972, + 12.712044, + -22.337847, + -18.344118, + -21.796993, + -2.695157, + 33.12794, + 20.795307, + 5.892835, + -30.008844, + 13.092032, + -12.617298, + -26.583797, + -12.331805, + -25.788994, + 18.527788, + -5.358728, + -20.973848, + 21.975595, + 3.6332028, + 21.49163, + -24.02265, + -1.2270886, + 31.648344, + -26.34871, + 28.852188, + 11.337199, + 16.580437, + 6.917111, + -2.6463892, + -13.808859, + 27.402872, + 31.668863, + 10.09489, + -9.203751, + -4.5927486, + -19.010218, + 7.268004, + 27.96568, + -32.725826, + -12.638194, + -9.072612, + 0.687024, + -24.00849, + -16.797096, + -13.887938, + 21.008837, + -20.714098, + 4.003382, + -5.864986, + 6.308118, + -18.954786, + -14.093458, + 14.5252905, + -10.20566, + -5.714998, + -7.6352305, + -11.445573, + 28.259352, + -7.4210625, + -14.774667, + 8.2712965, + -14.246153, + -23.317041, + 0.21726441, + -20.630865, + -24.174063, + -15.430166, + -22.63233, + -5.336508, + -0.4162142, + -17.627256, + -12.0516205, + -10.120339, + 22.627249, + 17.18417, + -24.923342, + 20.119074, + -11.128392, + -23.75025, + -22.75563, + -18.194794, + -2.677447, + 5.6336102, + -8.593113, + -27.35188, + 30.831476, + 6.842084, + -23.006275, + -2.1064568, + -31.873516, + -21.917208, + 11.057577, + 21.760345, + 31.105818, + -7.2484465, + 27.442217, + 27.198599, + -5.4786696, + 20.937487, + -15.238694, + -22.516329, + 16.441422, + -27.548603, + -0.95101047, + -5.9703918, + -20.764137, + 9.63625, + 25.318214, + -4.7924676, + 22.43602, + -29.857277, + -8.804195, + -16.590578, + 6.1655693, + -6.229835, + 9.825396, + 3.6917143, + -25.044327, + -15.101339, + 7.166533, + 18.591246, + -25.983875, + 27.819729, + 24.170658, + 5.3510475, + 6.549803, + -32.0242, + 27.198914, + -3.37324, + -14.339118, + -28.126497, + 22.221628, + -13.358003, + -16.78678, + -32.53302, + 15.152627, + 13.393224, + 19.411095, + 23.425772, + 20.027725, + 24.710947, + 17.26326, + -27.410538, + 26.30866, + -4.510418, + 5.3038287, + 7.526191, + -15.999681, + -2.2162335, + 31.378555, + 6.302167, + 15.184932, + -21.060045, + 14.10122, + 5.90295, + -27.716919, + -16.625145, + -10.241354, + 6.1585164, + 7.223655, + -11.634907, + -21.870625, + -21.870728, + 6.634295, + -6.066459, + -17.1438, + -32.103767, + -10.273103, + 15.137199, + 7.232844, + 21.119562, + 1.9282136, + 12.128642, + 12.653392, + 9.936496, + 21.916615, + 9.071596, + 27.73088, + 14.497267, + -4.162361, + -25.22734, + -22.694798, + -10.849964, + -8.824205, + 20.774977, + 20.526009, + 28.81767, + -15.895552, + -11.81379, + 11.597373, + -10.619046, + -12.564632, + -21.738821, + -13.048038, + -23.010983, + -1.3630763, + 19.897066 + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "AP - President Bush declared
Friday that charges of voter
fraud have cast doubt on the
Ukrainian election, and warned
that any European-negotiated
pact on Iran's nuclear program
must ensure the world can
verify Tehran's compliance." + ], + [ + "Israel is prepared to back a
Middle East conference
convened by Tony Blair early
next year despite having
expressed fears that the
British plans were over-
ambitious and designed" + ], + [ + "THE re-election of British
Prime Minister Tony Blair
would be seen as an
endorsement of the military
action in Iraq, Prime Minister
John Howard said today." + ], + [ + "AFP - A battle group of
British troops rolled out of
southern Iraq on a US-
requested mission to deadlier
areas near Baghdad, in a major
political gamble for British
Prime Minister Tony Blair." + ], + [ + " WASHINGTON (Reuters) -
President Bush on Friday set a
four-year goal of seeing a
Palestinian state established
and he and British Prime
Minister Tony Blair vowed to
mobilize international
support to help make it happen
now that Yasser Arafat is
dead." + ] + ], + "hovertemplate": "label=Nearest neighbor (top 5)
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Nearest neighbor (top 5)", + "marker": { + "color": "#EF553B", + "size": 5, + "symbol": "diamond" + }, + "mode": "markers", + "name": "Nearest neighbor (top 5)", + "showlegend": true, + "type": "scattergl", + "x": [ + 4.836007, + 10.248553, + 11.338411, + 14.155432, + 11.67213 + ], + "xaxis": "x", + "y": [ + 5.3443413, + 0.81385136, + -1.632514, + 14.279812, + 3.6773765 + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "BRITAIN: BLAIR WARNS OF
CLIMATE THREAT Prime Minister
Tony Blair urged the
international community to
consider global warming a dire
threat and agree on a plan of
action to curb the
quot;alarming quot; growth of
greenhouse gases." + ] + ], + "hovertemplate": "label=Source
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Source", + "marker": { + "color": "#00cc96", + "size": 5, + "symbol": "square" + }, + "mode": "markers", + "name": "Source", + "showlegend": true, + "type": "scattergl", + "x": [ + 9.787297 + ], + "xaxis": "x", + "y": [ + -0.25818 + ], + "yaxis": "y" + } + ], + "layout": { + "height": 500, + "legend": { + "title": { + "text": "label" + }, + "tracegroupgap": 0 + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Nearest neighbors of the Tony Blair article" + }, + "width": 600, + "xaxis": { + "anchor": "y", + "domain": [ + 0, + 1 + ], + "title": { + "text": "Component 0" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0, + 1 + ], + "title": { + "text": "Component 1" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# a 2D chart of nearest neighbors of the Tony Blair article\n", + "chart_from_components(\n", + " components=tsne_components,\n", + " labels=tony_blair_labels,\n", + " strings=article_descriptions,\n", + " width=600,\n", + " height=500,\n", + " title=\"Nearest neighbors of the Tony Blair article\",\n", + " category_orders={\"label\": [\"Other\", \"Nearest neighbor (top 5)\", \"Source\"]},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Looking at the 2D chart above, we can see that the articles about Tony Blair are somewhat close together inside of the World news cluster. Interestingly, although the 5 nearest neighbors (red) were closest in high dimensional space, they are not the closest points in this compressed 2D space. Compressing the embeddings from 2048 dimensions to 2 dimensions discards much of their information, and the nearest neighbors in the 2D space don't seem to be as relevant as those in the full embedding space." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly/" + }, + "data": [ + { + "customdata": [ + [ + "BRITAIN: BLAIR WARNS OF
CLIMATE THREAT Prime Minister
Tony Blair urged the
international community to
consider global warming a dire
threat and agree on a plan of
action to curb the
quot;alarming quot; growth of
greenhouse gases." + ], + [ + "Newspapers in Greece reflect a
mixture of exhilaration that
the Athens Olympics proved
successful, and relief that
they passed off without any
major setback." + ], + [ + "SAN JOSE, Calif. -- Apple
Computer (Quote, Chart)
unveiled a batch of new iPods,
iTunes software and promos
designed to keep it atop the
heap of digital music players." + ], + [ + "Any product, any shape, any
size -- manufactured on your
desktop! The future is the
fabricator. By Bruce Sterling
from Wired magazine." + ], + [ + "KABUL, Sept 22 (AFP): Three US
soldiers were killed and 14
wounded in a series of fierce
clashes with suspected Taliban
fighters in south and eastern
Afghanistan this week, the US
military said Wednesday." + ], + [ + "The EU and US moved closer to
an aviation trade war after
failing to reach agreement
during talks Thursday on
subsidies to Airbus Industrie." + ], + [ + "AUSTRALIAN journalist John
Martinkus is lucky to be alive
after spending 24 hours in the
hands of Iraqi militants at
the weekend. Martinkus was in
Baghdad working for the SBS
Dateline TV current affairs
program" + ], + [ + " GAZA (Reuters) - An Israeli
helicopter fired a missile
into a town in the southern
Gaza Strip late on Wednesday,
witnesses said, hours after a
Palestinian suicide bomber
blew herself up in Jerusalem,
killing two Israeli border
policemen." + ], + [ + "The Microsoft CEO says one way
to stem growing piracy of
Windows and Office in emerging
markets is to offer low-cost
computers." + ], + [ + "RIYADH, Saudi Arabia -- Saudi
police are seeking two young
men in the killing of a Briton
in a Riyadh parking lot, the
Interior Ministry said today,
and the British ambassador
called it a terrorist attack." + ], + [ + "Loudon, NH -- As the rain
began falling late Friday
afternoon at New Hampshire
International Speedway, the
rich in the Nextel Cup garage
got richer." + ], + [ + "PalmSource surprised the
mobile vendor community today
with the announcement that it
will acquire China MobileSoft
(CMS), ostensibly to leverage
that company's expertise in
building a mobile version of
the Linux operating system." + ], + [ + "JEFFERSON CITY - An election
security expert has raised
questions about Missouri
Secretary of State Matt Blunt
#39;s plan to let soldiers at
remote duty stations or in
combat areas cast their
ballots with the help of
e-mail." + ], + [ + "A gas explosion at a coal mine
in northern China killed 33
workers in the 10th deadly
mine blast reported in three
months. The explosion occurred
yesterday at 4:20 pm at Nanlou
township" + ], + [ + "Reuters - Palestinian leader
Mahmoud Abbas called\\Israel
\"the Zionist enemy\" Tuesday,
unprecedented language for\\the
relative moderate who is
expected to succeed Yasser
Arafat." + ], + [ + "AP - Ottawa Senators right
wing Marian Hossa is switching
European teams during the NHL
lockout." + ], + [ + "A new, optional log-on service
from America Online that makes
it harder for scammers to
access a persons online
account will not be available
for Macintosh" + ], + [ + "Nasser al-Qidwa, Palestinian
representative at the United
Nations and nephew of late
leader Yasser Arafat, handed
Arafat #39;s death report to
the Palestinian National
Authority (PNA) on Saturday." + ], + [ + "CAIRO, Egypt - France's
foreign minister appealed
Monday for the release of two
French journalists abducted in
Baghdad, saying the French
respect all religions. He did
not rule out traveling to
Baghdad..." + ], + [ + "Chelsea terminated Romania
striker Adrian Mutu #39;s
contract, citing gross
misconduct after the player
failed a doping test for
cocaine and admitted taking
the drug, the English soccer
club said in a statement." + ], + [ + "United Arab Emirates President
and ruler of Abu Dhabi Sheik
Zayed bin Sultan al-Nayhan
died Tuesday, official
television reports. He was 86." + ], + [ + "PALESTINIAN leader Yasser
Arafat today issued an urgent
call for the immediate release
of two French journalists
taken hostage in Iraq." + ], + [ + "The al-Qaida terrorist network
spent less than \\$50,000 on
each of its major attacks
except for the Sept. 11, 2001,
suicide hijackings, and one of
its hallmarks is using" + ], + [ + " SAN FRANCISCO (Reuters) -
Nike Inc. <A HREF=\"http://w
ww.investor.reuters.com/FullQu
ote.aspx?ticker=NKE.N target=/
stocks/quickinfo/fullquote\">
;NKE.N</A>, the world's
largest athletic shoe company,
on Monday reported a 25
percent rise in quarterly
profit, beating analysts'
estimates, on strong demand
for high-end running and
basketball shoes in the
United States." + ], + [ + "A FATHER who scaled the walls
of a Cardiff court dressed as
superhero Robin said the
Buckingham Palace protester
posed no threat. Fathers 4
Justice activist Jim Gibson,
who earlier this year staged
an eye-catching" + ], + [ + "NEW YORK (CBS.MW) - US stocks
traded mixed Thurday as Merck
shares lost a quarter of their
value, dragging blue chips
lower, but the Nasdaq moved
higher, buoyed by gains in the
semiconductor sector." + ], + [ + "Julia Gillard has reportedly
bowed out of the race to
become shadow treasurer,
taking enormous pressure off
Opposition Leader Mark Latham." + ], + [ + "It #39;s official. Microsoft
recently stated
definitivelyand contrary to
rumorsthat there will be no
new versions of Internet
Explorer for users of older
versions of Windows." + ], + [ + "The success of Paris #39; bid
for Olympic Games 2012 would
bring an exceptional
development for France for at
least 6 years, said Jean-
Francois Lamour, French
minister for Youth and Sports
on Tuesday." + ], + [ + "AFP - Maybe it's something to
do with the fact that the
playing area is so vast that
you need a good pair of
binoculars to see the action
if it's not taking place right
in front of the stands." + ], + [ + "Egypt #39;s release of accused
Israeli spy Azzam Azzam in an
apparent swap for six Egyptian
students held on suspicion of
terrorism is expected to melt
the ice and perhaps result" + ], + [ + "But fresh antitrust suit is in
the envelope, says Novell" + ], + [ + "Chips that help a computer's
main microprocessors perform
specific types of math
problems are becoming a big
business once again.\\" + ], + [ + "GAZA CITY, Gaza Strip: Hamas
militants killed an Israeli
soldier and wounded four with
an explosion in a booby-
trapped chicken coop on
Tuesday, in what the Islamic
group said was an elaborate
scheme to lure troops to the
area with the help of a double" + ], + [ + "A rocket carrying two Russian
cosmonauts and an American
astronaut to the international
space station streaked into
orbit on Thursday, the latest
flight of a Russian space
vehicle to fill in for
grounded US shuttles." + ], + [ + "Bankrupt ATA Airlines #39;
withdrawal from Chicago #39;s
Midway International Airport
presents a juicy opportunity
for another airline to beef up
service in the Midwest" + ], + [ + "AP - The 300 men filling out
forms in the offices of an
Iranian aid group were offered
three choices: Train for
suicide attacks against U.S.
troops in Iraq, for suicide
attacks against Israelis or to
assassinate British author
Salman Rushdie." + ], + [ + "ATHENS, Greece - Gail Devers,
the most talented yet star-
crossed hurdler of her
generation, was unable to
complete even one hurdle in
100-meter event Sunday -
failing once again to win an
Olympic hurdling medal.
Devers, 37, who has three
world championships in the
hurdles but has always flopped
at the Olympics, pulled up
short and screamed as she slid
under the first hurdle..." + ], + [ + "OCTOBER 12, 2004
(COMPUTERWORLD) - Microsoft
Corp. #39;s monthly patch
release cycle may be making it
more predictable for users to
deploy software updates, but
there appears to be little
letup in the number of holes
being discovered in the
company #39;s software" + ], + [ + "Wearable tech goes mainstream
as the Gap introduces jacket
with built-in radio.\\" + ], + [ + "Madison, WI (Sports Network) -
Anthony Davis ran for 122
yards and two touchdowns to
lead No. 6 Wisconsin over
Northwestern, 24-12, to
celebrate Homecoming weekend
at Camp Randall Stadium." + ], + [ + "LaVar Arrington participated
in his first practice since
Oct. 25, when he aggravated a
knee injury that sidelined him
for 10 games." + ], + [ + " NEW YORK (Reuters) - Billie
Jean King cut her final tie
with the U.S. Fed Cup team
Tuesday when she retired as
coach." + ], + [ + "The Instinet Group, owner of
one of the largest electronic
stock trading systems, is for
sale, executives briefed on
the plan say." + ], + [ + "Funding round of \\$105 million
brings broadband Internet
telephony company's total haul
to \\$208 million." + ], + [ + "The Miami Dolphins arrived for
their final exhibition game
last night in New Orleans
short nine players." + ], + [ + "washingtonpost.com - Anthony
Casalena was 17 when he got
his first job as a programmer
for a start-up called
HyperOffice, a software
company that makes e-mail and
contact management
applications for the Web.
Hired as an intern, he became
a regular programmer after two
weeks and rewrote the main
product line." + ], + [ + "The Auburn Hills-based
Chrysler Group made a profit
of \\$269 million in the third
quarter, even though worldwide
sales and revenues declined,
contributing to a \\$1." + ], + [ + "SAN FRANCISCO (CBS.MW) -- UAL
Corp., parent of United
Airlines, said Wednesday it
will overhaul its route
structure to reduce costs and
offset rising fuel costs." + ], + [ + " NAIROBI (Reuters) - The
Sudanese government and its
southern rebel opponents have
agreed to sign a pledge in the
Kenyan capital on Friday to
formally end a brutal 21-year-
old civil war, with U.N.
Security Council ambassadors
as witnesses." + ], + [ + "AP - From LSU at No. 1 to Ohio
State at No. 10, The AP
women's basketball poll had no
changes Monday." + ], + [ + "nother stage victory for race
leader Petter Solberg, his
fifth since the start of the
rally. The Subaru driver is
not pulling away at a fast
pace from Gronholm and Loeb
but the gap is nonetheless
increasing steadily." + ], + [ + "The fossilized neck bones of a
230-million-year-old sea
creature have features
suggesting that the animal
#39;s snakelike throat could
flare open and create suction
that would pull in prey." + ], + [ + "IBM late on Tuesday announced
the biggest update of its
popular WebSphere business
software in two years, adding
features such as automatically
detecting and fixing problems." + ], + [ + "April 1980 -- Players strike
the last eight days of spring
training. Ninety-two
exhibition games are canceled.
June 1981 -- Players stage
first midseason strike in
history." + ], + [ + "AP - Former Guatemalan
President Alfonso Portillo
#151; suspected of corruption
at home #151; is living and
working part-time in the same
Mexican city he fled two
decades ago to avoid arrest on
murder charges, his close
associates told The Associated
Press on Sunday." + ], + [ + "AP - British entrepreneur
Richard Branson said Monday
that his company plans to
launch commercial space
flights over the next few
years." + ], + [ + "Annual economic growth in
China has slowed for the third
quarter in a row, falling to
9.1 per cent, third-quarter
data shows. The slowdown shows
the economy is responding to
Beijing #39;s efforts to rein
in break-neck investment and
lending." + ], + [ + "washingtonpost.com - BRUSSELS,
Aug. 26 -- The United States
will have to wait until next
year to see its fight with the
European Union over biotech
foods resolved, as the World
Trade Organization agreed to
an E.U. request to bring
scientists into the debate,
officials said Thursday." + ], + [ + "A group of Internet security
and instant messaging
providers have teamed up to
detect and thwart the growing
threat of IM and peer-to-peer
viruses and worms, they said
this week." + ], + [ + "On Sunday, the day after Ohio
State dropped to 0-3 in the
Big Ten with a 33-7 loss at
Iowa, the Columbus Dispatch
ran a single word above its
game story on the Buckeyes:
quot;Embarrassing." + ], + [ + "Insisting that Hurriyat
Conference is the real
representative of Kashmiris,
Pakistan has claimed that
India is not ready to accept
ground realities in Kashmir." + ], + [ + "THE All-India Motor Transport
Congress (AIMTC) on Saturday
called off its seven-day
strike after finalising an
agreement with the Government
on the contentious issue of
service tax and the various
demands including tax deducted
at source (TDS), scrapping" + ], + [ + "BOSTON - Curt Schilling got
his 20th win on the eve of
Boston #39;s big series with
the New York Yankees. Now he
wants much more. quot;In a
couple of weeks, hopefully, it
will get a lot better, quot;
he said after becoming" + ], + [ + "Shooed the ghosts of the
Bambino and the Iron Horse and
the Yankee Clipper and the
Mighty Mick, on his 73rd
birthday, no less, and turned
Yankee Stadium into a morgue." + ], + [ + "Gold medal-winning Marlon
Devonish says the men #39;s
4x100m Olympic relay triumph
puts British sprinting back on
the map. Devonish, Darren
Campbell, Jason Gardener and
Mark Lewis-Francis edged out
the American" + ], + [ + "AP - The euro-zone economy
grew by 0.5 percent in the
second quarter of 2004, a
touch slower than in the first
three months of the year,
according to initial estimates
released Tuesday by the
European Union." + ], + [ + "His first space flight was in
1965 when he piloted the first
manned Gemini mission. Later
he made two trips to the moon
-- orbiting during a 1969
flight and then walking on the
lunar surface during a mission
in 1972." + ], + [ + "he difficult road conditions
created a few incidents in the
first run of the Epynt stage.
Francois Duval takes his
second stage victory since the
start of the rally, nine
tenths better than Sebastien
Loeb #39;s performance in
second position." + ], + [ + "VIENNA -- After two years of
investigating Iran's atomic
program, the UN nuclear
watchdog still cannot rule out
that Tehran has a secret atom
bomb project as Washington
insists, the agency's chief
said yesterday." + ], + [ + "By RACHEL KONRAD SAN
FRANCISCO (AP) -- EBay Inc.,
which has been aggressively
expanding in Asia, plans to
increase its stake in South
Korea's largest online auction
company. The Internet
auction giant said Tuesday
night that it would purchase
nearly 3 million publicly held
shares of Internet Auction
Co..." + ], + [ + "AFP - US Secretary of State
Colin Powell wrapped up a
three-nation tour of Asia
after winning pledges from
Japan, China and South Korea
to press North Korea to resume
stalled talks on its nuclear
weapons programs." + ], + [ + "Tallahassee, FL (Sports
Network) - Florida State head
coach Bobby Bowden has
suspended senior wide receiver
Craphonso Thorpe for the
Seminoles #39; game against
fellow Atlantic Coast
Conference member Duke on
Saturday." + ], + [ + "A few years ago, casinos
across the United States were
closing their poker rooms to
make space for more popular
and lucrative slot machines." + ], + [ + "CAIRO, Egypt An Egyptian
company says one of its four
workers who had been kidnapped
in Iraq has been freed. It
says it can #39;t give the
status of the others being
held hostage but says it is
quot;doing its best to secure
quot; their release." + ], + [ + "WASHINGTON -- Consumers were
tightfisted amid soaring
gasoline costs in August and
hurricane-related disruptions
last week sent applications
for jobless benefits to their
highest level in seven months." + ], + [ + "Talking kitchens and vanities.
Musical jump ropes and potty
seats. Blusterous miniature
leaf blowers and vacuum
cleaners -- almost as loud as
the real things." + ], + [ + "Online merchants in the United
States have become better at
weeding out fraudulent credit
card orders, a new survey
indicates. But shipping
overseas remains a risky
venture. By Joanna Glasner." + ], + [ + "Former champion Lleyton Hewitt
bristled, battled and
eventually blossomed as he
took another step towards a
second US Open title with a
second-round victory over
Moroccan Hicham Arazi on
Friday." + ], + [ + "AP - China's biggest computer
maker, Lenovo Group, said
Wednesday it has acquired a
majority stake in
International Business
Machines Corp.'s personal
computer business for
#36;1.75 billion, one of the
biggest Chinese overseas
acquisitions ever." + ], + [ + "Popping a gadget into a cradle
to recharge it used to mean
downtime, but these days
chargers are doing double
duty, keeping your portable
devices playing even when
they're charging." + ], + [ + "AFP - Hosts India braced
themselves for a harrowing
chase on a wearing wicket in
the first Test after Australia
declined to enforce the
follow-on here." + ], + [ + " SAN FRANCISCO (Reuters) -
Texas Instruments Inc. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=T
XN.N target=/stocks/quickinfo/
fullquote\">TXN.N</A>,
the largest maker of chips for
cellular phones, on Monday
said record demand for its
handset and television chips
boosted quarterly profit by
26 percent, even as it
struggles with a nagging
inventory problem." + ], + [ + "LONDON ARM Holdings, a British
semiconductor designer, said
on Monday that it would buy
Artisan Components for \\$913
million to broaden its product
range." + ], + [ + "Big evolutionary insights
sometimes come in little
packages. Witness the
startling discovery, in a cave
on the eastern Indonesian
island of Flores, of the
partial skeleton of a half-
size Homo species that" + ], + [ + "Prime Minister Paul Martin of
Canada urged Haitian leaders
on Sunday to allow the
political party of the deposed
president, Jean-Bertrand
Aristide, to take part in new
elections." + ], + [ + "Hostage takers holding up to
240 people at a school in
southern Russia have refused
to talk with a top Islamic
leader and demanded to meet
with regional leaders instead,
ITAR-TASS reported on
Wednesday." + ], + [ + "As the Mets round out their
search for a new manager, the
club is giving a last-minute
nod to its past. Wally
Backman, an infielder for the
Mets from 1980-88 who played
second base on the 1986" + ], + [ + "MELBOURNE: Global shopping
mall owner Westfield Group
will team up with Australian
developer Multiplex Group to
bid 585mil (US\\$1." + ], + [ + "Three children from a care
home are missing on the
Lancashire moors after they
are separated from a group." + ], + [ + "Luke Skywalker and Darth Vader
may get all the glory, but a
new Star Wars video game
finally gives credit to the
everyday grunts who couldn
#39;t summon the Force for
help." + ], + [ + "AMSTERDAM, Aug 18 (Reuters) -
Midfielder Edgar Davids #39;s
leadership qualities and
never-say-die attitude have
earned him the captaincy of
the Netherlands under new
coach Marco van Basten." + ], + [ + "COUNTY KILKENNY, Ireland (PA)
-- Hurricane Jeanne has led to
world No. 1 Vijay Singh
pulling out of this week #39;s
WGC-American Express
Championship at Mount Juliet." + ], + [ + "Green Bay, WI (Sports Network)
- The Green Bay Packers will
be without the services of Pro
Bowl center Mike Flanagan for
the remainder of the season,
as he will undergo left knee
surgery." + ], + [ + "Diabetics should test their
blood sugar levels more
regularly to reduce the risk
of cardiovascular disease, a
study says." + ], + [ + "COLUMBUS, Ohio -- NCAA
investigators will return to
Ohio State University Monday
to take another look at the
football program after the
latest round of allegations
made by former players,
according to the Akron Beacon
Journal." + ], + [ + " LOS ANGELES (Reuters) -
Federal authorities raided
three Washington, D.C.-area
video game stores and arrested
two people for modifying
video game consoles to play
pirated video games, a video
game industry group said on
Wednesday." + ], + [ + "Manchester United gave Alex
Ferguson a 1,000th game
anniversary present by
reaching the last 16 of the
Champions League yesterday,
while four-time winners Bayern
Munich romped into the second
round with a 5-1 beating of
Maccabi Tel Aviv." + ], + [ + "Iraq's interim Prime Minister
Ayad Allawi announced that
proceedings would begin
against former Baath Party
leaders." + ], + [ + "Reuters - Delta Air Lines Inc.
, which is\\racing to cut costs
to avoid bankruptcy, on
Wednesday reported\\a wider
quarterly loss amid soaring
fuel prices and weak\\domestic
airfares." + ], + [ + "Energy utility TXU Corp. on
Monday more than quadrupled
its quarterly dividend payment
and raised its projected
earnings for 2004 and 2005
after a companywide
performance review." + ], + [ + "Northwest Airlines Corp., the
fourth- largest US airline,
and its pilots union reached
tentative agreement on a
contract that would cut pay
and benefits, saving the
company \\$265 million a year." + ], + [ + "The last time the Angels
played the Texas Rangers, they
dropped two consecutive
shutouts at home in their most
agonizing lost weekend of the
season." + ], + [ + "Microsoft Corp. MSFT.O and
cable television provider
Comcast Corp. CMCSA.O said on
Monday that they would begin
deploying set-top boxes
powered by Microsoft software
starting next week." + ], + [ + "SEATTLE - Chasing a nearly
forgotten ghost of the game,
Ichiro Suzuki broke one of
baseball #39;s oldest records
Friday night, smoking a single
up the middle for his 258th
hit of the year and breaking
George Sisler #39;s record for
the most hits in a season" + ], + [ + "Grace Park, her caddie - and
fans - were poking around in
the desert brush alongside the
18th fairway desperately
looking for her ball." + ], + [ + "Philippines mobile phone
operator Smart Communications
Inc. is in talks with
Singapore #39;s Mobile One for
a possible tie-up,
BusinessWorld reported Monday." + ], + [ + "Washington Redskins kicker
John Hall strained his right
groin during practice
Thursday, his third leg injury
of the season. Hall will be
held out of practice Friday
and is questionable for Sunday
#39;s game against the Chicago
Bears." + ], + [ + "Airline warns it may file for
bankruptcy if too many senior
pilots take early retirement
option. Delta Air LInes #39;
CEO says it faces bankruptcy
if it can #39;t slow the pace
of pilots taking early
retirement." + ], + [ + "A toxic batch of home-brewed
alcohol has killed 31 people
in several towns in central
Pakistan, police and hospital
officials say." + ], + [ + "Thornbugs communicate by
vibrating the branches they
live on. Now scientists are
discovering just what the bugs
are \"saying.\"" + ], + [ + "The Florida Gators and
Arkansas Razorbacks meet for
just the sixth time Saturday.
The Gators hold a 4-1
advantage in the previous five
meetings, including last year
#39;s 33-28 win." + ], + [ + "Kodak Versamark #39;s parent
company, Eastman Kodak Co.,
reported Tuesday it plans to
eliminate almost 900 jobs this
year in a restructuring of its
overseas operations." + ], + [ + "A top official of the US Food
and Drug Administration said
Friday that he and his
colleagues quot;categorically
reject quot; earlier
Congressional testimony that
the agency has failed to
protect the public against
dangerous drugs." + ], + [ + " BEIJING (Reuters) - North
Korea is committed to holding
six-party talks aimed at
resolving the crisis over its
nuclear weapons program, but
has not indicated when, a top
British official said on
Tuesday." + ], + [ + "AP - 1941 #151; Brooklyn
catcher Mickey Owen dropped a
third strike on Tommy Henrich
of what would have been the
last out of a Dodgers victory
against the New York Yankees.
Given the second chance, the
Yankees scored four runs for a
7-4 victory and a 3-1 lead in
the World Series, which they
ended up winning." + ], + [ + "Federal prosecutors cracked a
global cartel that had
illegally fixed prices of
memory chips in personal
computers and servers for
three years." + ], + [ + "AP - Oracle Corp. CEO Larry
Ellison reiterated his
determination to prevail in a
long-running takeover battle
with rival business software
maker PeopleSoft Inc.,
predicting the proposed deal
will create a more competitive
company with improved customer
service." + ], + [ + "Braves shortstop Rafael Furcal
was arrested on drunken
driving charges Friday, his
second D.U.I. arrest in four
years." + ], + [ + "AFP - British retailer Marks
and Spencer announced a major
management shake-up as part of
efforts to revive its
fortunes, saying trading has
become tougher ahead of the
crucial Christmas period." + ], + [ + " BAGHDAD (Reuters) - Iraq's
interim government extended
the closure of Baghdad
international airport
indefinitely on Saturday
under emergency rule imposed
ahead of this week's U.S.-led
offensive on Falluja." + ], + [ + " ATHENS (Reuters) - Natalie
Coughlin's run of bad luck
finally took a turn for the
better when she won the gold
medal in the women's 100
meters backstroke at the
Athens Olympics Monday." + ], + [ + " ATLANTA (Reuters) - Home
Depot Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=HD.N target=/
stocks/quickinfo/fullquote\">
;HD.N</A>, the top home
improvement retailer, on
Tuesday reported a 15 percent
rise in third-quarter profit,
topping estimates, aided by
installed services and sales
to contractors." + ], + [ + " LONDON (Reuters) - The dollar
fought back from one-month
lows against the euro and
Swiss franc on Wednesday as
investors viewed its sell-off
in the wake of the Federal
Reserve's verdict on interest
rates as overdone." + ], + [ + "Rivaling Bush vs. Kerry for
bitterness, doctors and trial
lawyers are squaring off this
fall in an unprecedented four-
state struggle over limiting
malpractice awards..." + ], + [ + "Boston Scientific Corp said on
Friday it has recalled an ear
implant the company acquired
as part of its purchase of
Advanced Bionics in June." + ], + [ + "AP - Pedro Feliz hit a
tiebreaking grand slam with
two outs in the eighth inning
for his fourth hit of the day,
and the Giants helped their
playoff chances with a 9-5
victory over the Los Angeles
Dodgers on Saturday." + ], + [ + "MarketWatch.com. Richemont
sees significant H1 lift,
unclear on FY (2:53 AM ET)
LONDON (CBS.MW) -- Swiss
luxury goods maker
Richemont(zz:ZZ:001273145:
news, chart, profile), which
also is a significant" + ], + [ + "Crude oil climbed more than
\\$1 in New York on the re-
election of President George
W. Bush, who has been filling
the US Strategic Petroleum
Reserve." + ], + [ + "AP - Hundreds of tribesmen
gathered Tuesday near the area
where suspected al-Qaida-
linked militants are holding
two Chinese engineers and
demanding safe passage to
their reputed leader, a former
U.S. prisoner from Guantanamo
Bay, Cuba, officials and
residents said." + ], + [ + "AP - Scientists warned Tuesday
that a long-term increase in
global temperature of 3.5
degrees could threaten Latin
American water supplies,
reduce food yields in Asia and
result in a rise in extreme
weather conditions in the
Caribbean." + ], + [ + "AP - Further proof New York's
real estate market is
inflated: The city plans to
sell space on top of lampposts
to wireless phone companies
for #36;21.6 million a year." + ], + [ + "In an alarming development,
high-precision equipment and
materials which could be used
for making nuclear bombs have
disappeared from some Iraqi
facilities, the United Nations
watchdog agency has said." + ], + [ + "Yukos #39; largest oil-
producing unit regained power
supplies from Tyumenenergo, a
Siberia-based electricity
generator, Friday after the
subsidiary pledged to pay 440
million rubles (\\$15 million)
in debt by Oct. 3." + ], + [ + "The rollout of new servers and
networking switches in stores
is part of a five-year
agreement supporting 7-Eleven
#39;s Retail Information
System." + ], + [ + "Top Hollywood studios have
launched a wave of court cases
against internet users who
illegally download film files.
The Motion Picture Association
of America, which acts as the
representative of major film" + ], + [ + "AUSTRALIANS went into a
television-buying frenzy the
run-up to the Athens Olympics,
suggesting that as a nation we
could easily have scored a
gold medal for TV purchasing." + ], + [ + "US STOCKS vacillated yesterday
as rising oil prices muted
Wall Streets excitement over
strong results from Lehman
Brothers and Sprints \\$35
billion acquisition of Nextel
Communications." + ], + [ + "The next time you are in your
bedroom with your PC plus
Webcam switched on, don #39;t
think that your privacy is all
intact. If you have a Webcam
plugged into an infected
computer, there is a
possibility that" + ], + [ + "At the head of the class,
Rosabeth Moss Kanter is an
intellectual whirlwind: loud,
expansive, in constant motion." + ], + [ + "LEVERKUSEN/ROME, Dec 7 (SW) -
Dynamo Kiev, Bayer Leverkusen,
and Real Madrid all have a
good chance of qualifying for
the Champions League Round of
16 if they can get the right
results in Group F on
Wednesday night." + ], + [ + "Ed Hinkel made a diving,
fingertip catch for a key
touchdown and No. 16 Iowa
stiffened on defense when it
needed to most to beat Iowa
State 17-10 Saturday." + ], + [ + "During last Sunday #39;s
Nextel Cup race, amid the
ongoing furor over Dale
Earnhardt Jr. #39;s salty
language, NBC ran a commercial
for a show coming on later
that night called quot;Law
amp; Order: Criminal Intent." + ], + [ + "AP - After playing in hail,
fog and chill, top-ranked
Southern California finishes
its season in familiar
comfort. The Trojans (9-0, 6-0
Pacific-10) have two games at
home #151; against Arizona on
Saturday and Notre Dame on
Nov. 27 #151; before their
rivalry game at UCLA." + ], + [ + "A US airman dies and two are
hurt as a helicopter crashes
due to technical problems in
western Afghanistan." + ], + [ + "Jacques Chirac has ruled out
any withdrawal of French
troops from Ivory Coast,
despite unrest and anti-French
attacks, which have forced the
evacuation of thousands of
Westerners." + ], + [ + "Japanese Prime Minister
Junichiro Koizumi reshuffled
his cabinet yesterday,
replacing several top
ministers in an effort to
boost his popularity,
consolidate political support
and quicken the pace of
reforms in the world #39;s
second-largest economy." + ], + [ + "The remnants of Hurricane
Jeanne rained out Monday's
game between the Mets and the
Atlanta Braves. It will be
made up Tuesday as part of a
doubleheader." + ], + [ + "AP - NASCAR is not expecting
any immediate changes to its
top-tier racing series
following the merger between
telecommunications giant
Sprint Corp. and Nextel
Communications Inc." + ], + [ + "AP - Shawn Fanning's Napster
software enabled countless
music fans to swap songs on
the Internet for free, turning
him into the recording
industry's enemy No. 1 in the
process." + ], + [ + "TBILISI (Reuters) - At least
two Georgian soldiers were
killed and five wounded in
artillery fire with
separatists in the breakaway
region of South Ossetia,
Georgian officials said on
Wednesday." + ], + [ + "Like wide-open races? You
#39;ll love the Big 12 North.
Here #39;s a quick morning
line of the Big 12 North as it
opens conference play this
weekend." + ], + [ + "Reuters - Walt Disney Co.
is\\increasing investment in
video games for hand-held
devices and\\plans to look for
acquisitions of small game
publishers and\\developers,
Disney consumer products
Chairman Andy Mooney said\\on
Monday." + ], + [ + "Taquan Dean scored 22 points,
Francisco Garcia added 19 and
No. 13 Louisville withstood a
late rally to beat Florida
74-70 Saturday." + ], + [ + "BANGKOK - A UN conference last
week banned commercial trade
in the rare Irrawaddy dolphin,
a move environmentalists said
was needed to save the
threatened species." + ], + [ + "Laksamana.Net - Two Indonesian
female migrant workers freed
by militants in Iraq are
expected to arrive home within
a day or two, the Foreign
Affairs Ministry said
Wednesday (6/10/04)." + ], + [ + "A bitter row between America
and the European Union over
alleged subsidies to rival
aircraft makers Boeing and
Airbus intensified yesterday." + ], + [ + "NEW YORK -- This was all about
Athens, about redemption for
one and validation for the
other. Britain's Paula
Radcliffe, the fastest female
marathoner in history, failed
to finish either of her
Olympic races last summer.
South Africa's Hendrik Ramaala
was a five-ringed dropout,
too, reinforcing his
reputation as a man who could
go only half the distance." + ], + [ + "Reuters - Online media company
Yahoo Inc.\\ late on Monday
rolled out tests of redesigned
start\\pages for its popular
Yahoo.com and My.Yahoo.com
sites." + ], + [ + "Amsterdam (pts) - Dutch
electronics company Philips
http://www.philips.com has
announced today, Friday, that
it has cut its stake in Atos
Origin by more than a half." + ], + [ + "TORONTO (CP) - Two-thirds of
banks around the world have
reported an increase in the
volume of suspicious
activities that they report to
police, a new report by KPMG
suggests." + ], + [ + "The Sun may have captured
thousands or even millions of
asteroids from another
planetary system during an
encounter more than four
billion years ago, astronomers
are reporting." + ], + [ + "LONDON -- Ernie Els has set
his sights on an improved
putting display this week at
the World Golf Championships
#39; NEC Invitational in
Akron, Ohio, after the
disappointment of tying for
fourth place at the PGA
Championship last Sunday." + ], + [ + "The Atkins diet frenzy slowed
growth briefly, but the
sandwich business is booming,
with \\$105 billion in sales
last year." + ], + [ + "Luxury carmaker Jaguar said
Friday it was stopping
production at a factory in
central England, resulting in
a loss of 1,100 jobs,
following poor sales in the
key US market." + ], + [ + "A bus was hijacked today and
shots were fired at police who
surrounded it on the outskirts
of Athens. Police did not know
how many passengers were
aboard the bus." + ], + [ + "Thumb through the book - then
buy a clean copy from Amazon" + ], + [ + "AP - President Bashar Assad
shuffled his Cabinet on
Monday, just weeks after the
United States and the United
Nations challenged Syria over
its military presence in
Lebanon and the security
situation along its border
with Iraq." + ], + [ + "Fiji #39;s Vijay Singh
replaced Tiger Woods as the
world #39;s No.1 ranked golfer
today by winning the PGA
Deutsche Bank Championship." + ], + [ + "LEIPZIG, Germany : Jurgen
Klinsmann enjoyed his first
home win as German manager
with his team defeating ten-
man Cameroon 3-0 in a friendly
match." + ], + [ + "AP - Kevin Brown had a chance
to claim a place in Yankees
postseason history with his
start in Game 7 of the AL
championship series." + ], + [ + "Reuters - High-definition
television can\\show the sweat
beading on an athlete's brow,
but the cost of\\all the
necessary electronic equipment
can get a shopper's own\\pulse
racing." + ], + [ + "HOMESTEAD, Fla. -- Kurt Busch
got his first big break in
NASCAR by winning a 1999
talent audition nicknamed
quot;The Gong Show. quot; He
was selected from dozens of
unknown, young race drivers to
work for one of the sport
#39;s most famous team owners,
Jack Roush." + ], + [ + "AP - President Vladimir Putin
has signed a bill confirming
Russia's ratification of the
Kyoto Protocol, the Kremlin
said Friday, clearing the way
for the global climate pact to
come into force early next
year." + ], + [ + "John Gibson said Friday that
he decided to resign as chief
executive officer of
Halliburton Energy Services
when it became apparent he
couldn #39;t become the CEO of
the entire corporation, after
getting a taste of the No." + ], + [ + "MacCentral - Apple Computer
Inc. on Monday released an
update for Apple Remote
Desktop (ARD), the company's
software solution to assist
Mac system administrators and
computer managers with asset
management, software
distribution and help desk
support. ARD 2.1 includes
several enhancements and bug
fixes." + ], + [ + "NEW YORK (Reuters) - Outback
Steakhouse Inc. said Tuesday
it lost about 130 operating
days and up to \\$2 million in
revenue because it had to
close some restaurants in the
South due to Hurricane
Charley." + ], + [ + "State insurance commissioners
from across the country have
proposed new rules governing
insurance brokerage fees,
winning praise from an
industry group and criticism
from" + ], + [ + "AP - The authenticity of newly
unearthed memos stating that
George W. Bush failed to meet
standards of the Texas Air
National Guard during the
Vietnam War was questioned
Thursday by the son of the
late officer who reportedly
wrote the memos." + ], + [ + "Zurich, Switzerland (Sports
Network) - Former world No. 1
Venus Williams advanced on
Thursday and will now meet
Wimbledon champion Maria
Sharapova in the quarterfinals
at the \\$1." + ], + [ + "INDIA #39;S cricket chiefs
began a frenetic search today
for a broadcaster to show next
month #39;s home series
against world champion
Australia after cancelling a
controversial \\$US308 million
(\\$440 million) television
deal." + ], + [ + "Canadian Press - OAKVILLE,
Ont. (CP) - The body of a
missing autistic man was
pulled from a creek Monday,
just metres from where a key
piece of evidence was
uncovered but originally
overlooked because searchers
had the wrong information." + ], + [ + "SOFTWARE giant Oracle #39;s
stalled \\$7.7bn (4.2bn) bid to
take over competitor
PeopleSoft has received a huge
boost after a US judge threw
out an anti-trust lawsuit
filed by the Department of
Justice to block the
acquisition." + ], + [ + "The International Olympic
Committee (IOC) has urged
Beijing to ensure the city is
ready to host the 2008 Games
well in advance, an official
said on Wednesday." + ], + [ + "AFP - German Chancellor
Gerhard Schroeder arrived in
Libya for an official visit
during which he is to hold
talks with Libyan leader
Moamer Kadhafi." + ], + [ + "The fastest-swiveling space
science observatory ever built
rocketed into orbit on
Saturday to scan the universe
for celestial explosions." + ], + [ + "The government will examine
claims 100,000 Iraqi civilians
have been killed since the US-
led invasion, Jack Straw says." + ], + [ + "Virginia Tech scores 24 points
off four first-half turnovers
in a 55-6 wipeout of Maryland
on Thursday to remain alone
atop the ACC." + ], + [ + "Copernic Unleashes Desktop
Search Tool\\\\Copernic
Technologies Inc. today
announced Copernic Desktop
Search(TM) (CDS(TM)), \"The
Search Engine For Your PC
(TM).\" Copernic has used the
experience gained from over 30
million downloads of its
Windows-based Web search
software to develop CDS, a
desktop search product that
users are saying is far ..." + ], + [ + "The DVD Forum moved a step
further toward the advent of
HD DVD media and drives with
the approval of key physical
specifications at a meeting of
the organisations steering
committee last week." + ], + [ + "Eton College and Clarence
House joined forces yesterday
to deny allegations due to be
made at an employment tribunal
today by a former art teacher
that she improperly helped
Prince Harry secure an A-level
pass in art two years ago." + ], + [ + "AFP - Great Britain's chances
of qualifying for the World
Group of the Davis Cup were
evaporating rapidly after
Austria moved into a 2-1 lead
following the doubles." + ], + [ + "AP - Martina Navratilova's
long, illustrious career will
end without an Olympic medal.
The 47-year-old Navratilova
and Lisa Raymond lost 6-4,
4-6, 6-4 on Thursday night to
fifth-seeded Shinobu Asagoe
and Ai Sugiyama of Japan in
the quarterfinals, one step
shy of the medal round." + ], + [ + "Often pigeonholed as just a
seller of televisions and DVD
players, Royal Philips
Electronics said third-quarter
profit surged despite a slide
into the red by its consumer
electronics division." + ], + [ + "AP - Google, the Internet
search engine, has done
something that law enforcement
officials and their computer
tools could not: Identify a
man who died in an apparent
hit-and-run accident 11 years
ago in this small town outside
Yakima." + ], + [ + "We are all used to IE getting
a monthly new security bug
found, but Winamp? In fact,
this is not the first security
flaw found in the application." + ], + [ + "The Apache Software Foundation
and the Debian Project said
they won't support the Sender
ID e-mail authentication
standard in their products." + ], + [ + "USATODAY.com - The newly
restored THX 1138 arrives on
DVD today with Star Wars
creator George Lucas' vision
of a Brave New World-like
future." + ], + [ + "Office Depot Inc. (ODP.N:
Quote, Profile, Research) on
Tuesday warned of weaker-than-
expected profits for the rest
of the year because of
disruptions from the string of
hurricanes" + ], + [ + "THE photo-equipment maker
Kodak yesterday announced
plans to axe 600 jobs in the
UK and close a factory in
Nottinghamshire, in a move in
line with the giants global
restructuring strategy
unveiled last January." + ], + [ + "The chances of scientists
making any one of five
discoveries by 2010 have been
hugely underestimated,
according to bookmakers." + ], + [ + "Asia-Pacific leaders meet in
Australia to discuss how to
keep nuclear weapons out of
the hands of extremists." + ], + [ + " TALL AFAR, Iraq -- A three-
foot-high coil of razor wire,
21-ton armored vehicles and
American soldiers with black
M-4 assault rifles stood
between tens of thousands of
people and their homes last
week." + ], + [ + "PSV Eindhoven re-established
their five-point lead at the
top of the Dutch Eredivisie
today with a 2-0 win at
Vitesse Arnhem. Former
Sheffield Wednesday striker
Gerald Sibon put PSV ahead in
the 56th minute" + ], + [ + "China's central bank on
Thursday raised interest rates
for the first time in nearly a
decade, signaling deepening
unease with the breakneck pace
of development and an intent
to reign in a construction
boom now sowing fears of
runaway inflation." + ], + [ + "Deepening its commitment to
help corporate users create
SOAs (service-oriented
architectures) through the use
of Web services, IBM's Global
Services unit on Thursday
announced the formation of an
SOA Management Practice." + ], + [ + "TODAY AUTO RACING 3 p.m. --
NASCAR Nextel Cup Sylvania 300
qualifying at N.H.
International Speedway,
Loudon, N.H., TNT PRO BASEBALL
7 p.m. -- Red Sox at New York
Yankees, Ch. 38, WEEI (850)
(on cable systems where Ch. 38
is not available, the game
will air on NESN); Chicago
Cubs at Cincinnati, ESPN 6
p.m. -- Eastern League finals:
..." + ], + [ + "MUMBAI, SEPTEMBER 21: The
Board of Control for Cricket
in India (BCCI) today informed
the Bombay High Court that it
was cancelling the entire
tender process regarding
cricket telecast rights as
also the conditional deal with
Zee TV." + ], + [ + "CHICAGO - Delta Air Lines
(DAL) said Wednesday it plans
to eliminate between 6,000 and
6,900 jobs during the next 18
months, implement a 10 across-
the-board pay reduction and
reduce employee benefits." + ], + [ + "LAKE GEORGE, N.Y. - Even
though he's facing double hip
replacement surgery, Bill
Smith is more than happy to
struggle out the door each
morning, limp past his brand
new P.T..." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks were likely to open
flat on Wednesday, with high
oil prices and profit warnings
weighing on the market before
earnings reports start and key
jobs data is released this
week." + ], + [ + "Best known for its popular
search engine, Google is
rapidly rolling out new
products and muscling into
Microsoft's stronghold: the
computer desktop. The
competition means consumers
get more choices and better
products." + ], + [ + "Toshiba Corp. #39;s new
desktop-replacement multimedia
notebooks, introduced on
Tuesday, are further evidence
that US consumers still have
yet to embrace the mobility
offered by Intel Corp." + ], + [ + "JEJU, South Korea : Grace Park
of South Korea won an LPGA
Tour tournament, firing a
seven-under-par 65 in the
final round of the
1.35-million dollar CJ Nine
Bridges Classic." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
poured cold water on Tuesday
on recent international
efforts to restart stalled
peace talks with Syria, saying
there was \"no possibility\" of
returning to previous
discussions." + ], + [ + "Dutch smugness was slapped
hard during the past
fortnight. The rude awakening
began with the barbaric
slaying of controversial
filmmaker Theo van Gogh on
November 2. Then followed a
reciprocal cycle of some" + ], + [ + "AP - The NHL will lock out its
players Thursday, starting a
work stoppage that threatens
to keep the sport off the ice
for the entire 2004-05 season." + ], + [ + " MOSCOW (Reuters) - Russia's
Gazprom said on Tuesday it
will bid for embattled oil
firm YUKOS' main unit next
month, as the Kremlin seeks
to turn the world's biggest
gas producer into a major oil
player." + ], + [ + "pee writes quot;A passenger
on a commuter plane in
northern Norway attacked both
pilots and at least one
passenger with an axe as the
aircraft was coming in to
land." + ], + [ + "Aregular Amazon customer,
Yvette Thompson has found
shopping online to be mostly
convenient and trouble-free.
But last month, after ordering
two CDs on Amazon.com, the
Silver Spring reader
discovered on her bank
statement that she was double-
charged for the \\$26.98 order.
And there was a \\$25 charge
that was a mystery." + ], + [ + "Prime Minister Ariel Sharon
pledged Sunday to escalate a
broad Israeli offensive in
northern Gaza, saying troops
will remain until Palestinian
rocket attacks are halted.
Israeli officials said the
offensive -- in which 58
Palestinians and three
Israelis have been killed --
will help clear the way for an
Israeli withdrawal." + ], + [ + "Federal Reserve officials
raised a key short-term
interest rate Tuesday for the
fifth time this year, and
indicated they will gradually
move rates higher next year to
keep inflation under control
as the economy expands." + ], + [ + "Canadians are paying more to
borrow money for homes, cars
and other purchases today
after a quarter-point
interest-rate increase by the
Bank of Canada yesterday was
quickly matched by the
chartered banks." + ], + [ + "NEW YORK - Wall Street
professionals know to keep
their expectations in check in
September, historically the
worst month of the year for
stocks. As summertime draws to
a close, money managers are
getting back to business,
cleaning house, and often
sending the market lower in
the process..." + ], + [ + "A group linked to al Qaeda
ally Abu Musab al-Zarqawi said
it had tried to kill Iraq
#39;s environment minister on
Tuesday and warned it would
not miss next time, according
to an Internet statement." + ], + [ + "The Israeli military killed
four Palestinian militants on
Wednesday as troops in tanks
and armored vehicles pushed
into another town in the
northern Gaza Strip, extending" + ], + [ + "When Paula Radcliffe dropped
out of the Olympic marathon
miles from the finish, she
sobbed uncontrollably.
Margaret Okayo knew the
feeling." + ], + [ + "Delta Air Lines is to issue
millions of new shares without
shareholder consent as part of
moves to ensure its survival." + ], + [ + "First baseman Richie Sexson
agrees to a four-year contract
with the Seattle Mariners on
Wednesday." + ], + [ + "KIRKUK, Iraq - A suicide
attacker detonated a car bomb
Saturday outside a police
academy in the northern Iraqi
city of Kirkuk as hundreds of
trainees and civilians were
leaving for the day, killing
at least 20 people and
wounding 36, authorities said.
Separately, U.S and Iraqi
forces clashed with insurgents
in another part of northern
Iraq after launching an
operation to destroy an
alleged militant cell in the
town of Tal Afar, the U.S..." + ], + [ + "Genta (GNTA:Nasdaq - news -
research) is never boring!
Monday night, the company
announced that its phase III
Genasense study in chronic
lymphocytic leukemia (CLL) met
its primary endpoint, which
was tumor shrinkage." + ], + [ + "Finnish mobile giant Nokia has
described its new Preminet
solution, which it launched
Monday (Oct. 25), as a
quot;major worldwide
initiative." + ], + [ + "While the entire airline
industry #39;s finances are
under water, ATA Airlines will
have to hold its breath longer
than its competitors to keep
from going belly up." + ], + [ + " SAN FRANCISCO (Reuters) - At
virtually every turn, Intel
Corp. executives are heaping
praise on an emerging long-
range wireless technology
known as WiMAX, which can
blanket entire cities with
high-speed Internet access." + ], + [ + "One day after ousting its
chief executive, the nation's
largest insurance broker said
it will tell clients exactly
how much they are paying for
services and renounce back-
door payments from carriers." + ], + [ + "LONDON (CBS.MW) -- Outlining
an expectation for higher oil
prices and increasing demand,
Royal Dutch/Shell on Wednesday
said it #39;s lifting project
spending to \\$45 billion over
the next three years." + ], + [ + "Tuesday #39;s meeting could
hold clues to whether it
#39;ll be a November or
December pause in rate hikes.
By Chris Isidore, CNN/Money
senior writer." + ], + [ + "Phishing is one of the
fastest-growing forms of
personal fraud in the world.
While consumers are the most
obvious victims, the damage
spreads far wider--hurting
companies #39; finances and
reputations and potentially" + ], + [ + "Reuters - The U.S. Interior
Department on\\Friday gave
final approval to a plan by
ConocoPhillips and\\partner
Anadarko Petroleum Corp. to
develop five tracts around\\the
oil-rich Alpine field on
Alaska's North Slope." + ], + [ + "The dollar may fall against
the euro for a third week in
four on concern near-record
crude oil prices will temper
the pace of expansion in the
US economy, a survey by
Bloomberg News indicates." + ], + [ + "The battle for the British-
based Chelsfield plc hotted up
at the weekend, with reports
from London that the property
giant #39;s management was
working on its own bid to
thwart the 585 million (\\$A1." + ], + [ + "Atari has opened the initial
sign-up phase of the closed
beta for its Dungeons amp;
Dragons real-time-strategy
title, Dragonshard." + ], + [ + "AP - Many states are facing
legal challenges over possible
voting problems Nov. 2. A look
at some of the developments
Thursday:" + ], + [ + "Israeli troops withdrew from
the southern Gaza Strip town
of Khan Yunis on Tuesday
morning, following a 30-hour
operation that left 17
Palestinians dead." + ], + [ + "Notes and quotes from various
drivers following California
Speedway #39;s Pop Secret 500.
Jeff Gordon slipped to second
in points following an engine
failure while Jimmie Johnson
moved back into first." + ], + [ + "PM-designate Omar Karameh
forms a new 30-member cabinet
which includes women for the
first time." + ], + [ + "Bahrain #39;s king pardoned a
human rights activist who
convicted of inciting hatred
of the government and
sentenced to one year in
prison Sunday in a case linked
to criticism of the prime
minister." + ], + [ + "Big Blue adds features, beefs
up training efforts in China;
rival Unisys debuts new line
and pricing plan." + ], + [ + " MEMPHIS, Tenn. (Sports
Network) - The Memphis
Grizzlies signed All-Star
forward Pau Gasol to a multi-
year contract on Friday.
Terms of the deal were not
announced." + ], + [ + "Leaders from 38 Asian and
European nations are gathering
in Vietnam for a summit of the
Asia-Europe Meeting, know as
ASEM. One thousand delegates
are to discuss global trade
and regional politics during
the two-day forum." + ], + [ + "A US soldier has pleaded
guilty to murdering a wounded
16-year-old Iraqi boy. Staff
Sergeant Johnny Horne was
convicted Friday of the
unpremeditated murder" + ], + [ + "Andre Agassi brushed past
Jonas Bjorkman 6-3 6-4 at the
Stockholm Open on Thursday to
set up a quarter-final meeting
with Spanish sixth seed
Fernando Verdasco." + ], + [ + "South Korea's Hynix
Semiconductor Inc. and Swiss-
based STMicroelectronics NV
signed a joint-venture
agreement on Tuesday to
construct a memory chip
manufacturing plant in Wuxi,
about 100 kilometers west of
Shanghai, in China." + ], + [ + "SAN DIEGO --(Business Wire)--
Oct. 11, 2004 -- Breakthrough
PeopleSoft EnterpriseOne 8.11
Applications Enable
Manufacturers to Become
Demand-Driven." + ], + [ + "Reuters - Oil prices rose on
Friday as tight\\supplies of
distillate fuel, including
heating oil, ahead of\\the
northern hemisphere winter
spurred buying." + ], + [ + "Well, Intel did say -
dismissively of course - that
wasn #39;t going to try to
match AMD #39;s little dual-
core Opteron demo coup of last
week and show off a dual-core
Xeon at the Intel Developer
Forum this week and - as good
as its word - it didn #39;t." + ], + [ + "Guinea-Bissau #39;s army chief
of staff and former interim
president, General Verissimo
Correia Seabra, was killed
Wednesday during unrest by
mutinous soldiers in the
former Portuguese" + ], + [ + "31 October 2004 -- Exit polls
show that Prime Minister
Viktor Yanukovich and
challenger Viktor Yushchenko
finished on top in Ukraine
#39;s presidential election
today and will face each other
in a run-off next month." + ], + [ + "Nov. 18, 2004 - An FDA
scientist says the FDA, which
is charged with protecting
America #39;s prescription
drug supply, is incapable of
doing so." + ], + [ + "Rock singer Bono pledges to
spend the rest of his life
trying to eradicate extreme
poverty around the world." + ], + [ + "AP - Just when tourists
thought it was safe to go back
to the Princess Diana memorial
fountain, the mud has struck." + ], + [ + "The UK's inflation rate fell
in September, thanks in part
to a fall in the price of
airfares, increasing the
chance that interest rates
will be kept on hold." + ], + [ + " HONG KONG/SAN FRANCISCO
(Reuters) - IBM is selling its
PC-making business to China's
largest personal computer
company, Lenovo Group Ltd.,
for \\$1.25 billion, marking
the U.S. firm's retreat from
an industry it helped pioneer
in 1981." + ], + [ + "AP - Three times a week, The
Associated Press picks an
issue and asks President Bush
and Democratic presidential
candidate John Kerry a
question about it. Today's
question and their responses:" + ], + [ + " BOSTON (Reuters) - Boston was
tingling with anticipation on
Saturday as the Red Sox
prepared to host Game One of
the World Series against the
St. Louis Cardinals and take a
step toward ridding
themselves of a hex that has
hung over the team for eight
decades." + ], + [ + "FOR the first time since his
appointment as Newcastle
United manager, Graeme Souness
has been required to display
the strong-arm disciplinary
qualities that, to Newcastle
directors, made" + ], + [ + "In an apparent damage control
exercise, Russian President
Vladimir Putin on Saturday
said he favored veto rights
for India as new permanent
member of the UN Security
Council." + ], + [ + "Nordstrom reported a strong
second-quarter profit as it
continued to select more
relevant inventory and sell
more items at full price." + ], + [ + "WHY IT HAPPENED Tom Coughlin
won his first game as Giants
coach and immediately
announced a fine amnesty for
all Giants. Just kidding." + ], + [ + "A second-place finish in his
first tournament since getting
married was good enough to
take Tiger Woods from third to
second in the world rankings." + ], + [ + " COLORADO SPRINGS, Colorado
(Reuters) - World 400 meters
champion Jerome Young has been
given a lifetime ban from
athletics for a second doping
offense, the U.S. Anti-Doping
Agency (USADA) said Wednesday." + ], + [ + "AP - Nigeria's Senate has
ordered a subsidiary of
petroleum giant Royal/Dutch
Shell to pay a Nigerian ethnic
group #36;1.5 billion for oil
spills in their homelands, but
the legislative body can't
enforce the resolution, an
official said Wednesday." + ], + [ + "IT #39;S BEEN a heck of an
interesting two days here in
Iceland. I #39;ve seen some
interesting technology, heard
some inventive speeches and
met some people with different
ideas." + ], + [ + "The Bank of England is set to
keep interest rates on hold
following the latest meeting
of the its Monetary Policy
Committee." + ], + [ + "Australian troops in Baghdad
came under attack today for
the first time since the end
of the Iraq war when a car
bomb exploded injuring three
soldiers and damaging an
Australian armoured convoy." + ], + [ + "The Bush administration upheld
yesterday the imposition of
penalty tariffs on shrimp
imports from China and
Vietnam, handing a victory to
beleaguered US shrimp
producers." + ], + [ + "House prices rose 0.2 percent
in September compared with the
month before to stand up 17.8
percent on a year ago, the
Nationwide Building Society
says." + ], + [ + "Reuters - Two clients of
Germany's Postbank\\(DPBGn.DE)
fell for an e-mail fraud that
led them to reveal\\money
transfer codes to a bogus Web
site -- the first case of\\this
scam in German, prosecutors
said on Thursday." + ], + [ + "US spending on information
technology goods, services,
and staff will grow seven
percent in 2005 and continue
at a similar pace through
2008, according to a study
released Monday by Forrester
Research." + ], + [ + "LONDON - In two years, Arsenal
will play their home matches
in the Emirates stadium. That
is what their new stadium at
Ashburton Grove will be called
after the Premiership
champions yesterday agreed to
the" + ], + [ + "KNOXVILLE, Tenn. -- Jason
Campbell threw for 252 yards
and two touchdowns, and No. 8
Auburn proved itself as a
national title contender by
overwhelming No. 10 Tennessee,
34-10, last night." + ], + [ + "Look, Ma, no hands! The U.S.
space agency's latest
spacecraft can run an entire
mission by itself. By Amit
Asaravala." + ], + [ + "Pakistans decision to refuse
the International Atomic
Energy Agency to have direct
access to Dr AQ Khan is
correct on both legal and
political counts." + ], + [ + "MANILA, 4 December 2004 - With
floods receding, rescuers
raced to deliver food to
famished survivors in
northeastern Philippine
villages isolated by back-to-
back storms that left more
than 650 people dead and
almost 400 missing." + ], + [ + "Talks on where to build the
world #39;s first nuclear
fusion reactor ended without a
deal on Tuesday but the
European Union said Japan and
the United States no longer
firmly opposed its bid to put
the plant in France." + ], + [ + "Joining America Online,
EarthLink and Yahoo against
spamming, Microsoft Corp.
today announced the filing of
three new anti-Spam lawsuits
under the CAN-SPAM federal law
as part of its initiative in
solving the Spam problem for
Internet users worldwide." + ], + [ + "WASHINGTON -- Another
Revolution season concluded
with an overtime elimination.
Last night, the Revolution
thrice rallied from deficits
for a 3-3 tie with D.C. United
in the Eastern Conference
final, then lost in the first-
ever Major League Soccer match
decided by penalty kicks." + ], + [ + "Dwyane Wade calls himself a
quot;sidekick, quot; gladly
accepting the role Kobe Bryant
never wanted in Los Angeles.
And not only does second-year
Heat point" + ], + [ + "A nationwide inspection shows
Internet users are not as safe
online as they believe. The
inspections found most
consumers have no firewall
protection, outdated antivirus
software and dozens of spyware
programs secretly running on
their computers." + ], + [ + "World number one golfer Vijay
Singh of Fiji has captured his
eighth PGA Tour event of the
year with a win at the 84
Lumber Classic in Farmington,
Pennsylvania." + ], + [ + "The noise was deafening and
potentially unsettling in the
minutes before the start of
the men #39;s Olympic
200-meter final. The Olympic
Stadium crowd chanted
quot;Hellas!" + ], + [ + "CLEVELAND - The White House
said Vice President Dick
Cheney faces a \"master
litigator\" when he debates
Sen. John Edwards Tuesday
night, a backhanded compliment
issued as the Republican
administration defended itself
against criticism that it has
not acknowledged errors in
waging war in Iraq..." + ], + [ + "Brazilian forward Ronaldinho
scored a sensational goal for
Barcelona against Milan,
making for a last-gasp 2-1.
The 24-year-old #39;s
brilliant left-foot hit at the
Nou Camp Wednesday night sent
Barcelona atop of Group F." + ], + [ + "Nortel Networks says it will
again delay the release of its
restated financial results.
The Canadian telecom vendor
originally promised to release
the restated results in
September." + ], + [ + " CHICAGO (Reuters) - At first
glance, paying \\$13 or \\$14
for a hard-wired Internet
laptop connection in a hotel
room might seem expensive." + ], + [ + "SEOUL (Reuters) - The chairman
of South Korea #39;s ruling
Uri Party resigned on Thursday
after saying his father had
served as a military police
officer during Japan #39;s
1910-1945 colonial rule on the
peninsula." + ], + [ + "ALERE, Uganda -- Kasmiro
Bongonyinge remembers sitting
up suddenly in his bed. It was
just after sunrise on a summer
morning two years ago, and the
old man, 87 years old and
blind, knew something was
wrong." + ], + [ + "Reuters - An investigation
into U.S. insurers\\and brokers
rattled insurance industry
stocks for a second day\\on
Friday as investors, shaken
further by subpoenas
delivered\\to the top U.S. life
insurer, struggled to gauge
how deep the\\probe might
reach." + ], + [ + "Bee Staff Writer. SANTA CLARA
- Andre Carter #39;s back
injury has kept him out of the
49ers #39; lineup since Week
1. It #39;s also interfering
with him rooting on his alma
mater in person." + ], + [ + "AP - Kellen Winslow Jr. ended
his second NFL holdout Friday." + ], + [ + "JAKARTA - Official results
have confirmed former army
general Susilo Bambang
Yudhoyono as the winner of
Indonesia #39;s first direct
presidential election, while
incumbent Megawati
Sukarnoputri urged her nation
Thursday to wait for the
official announcement" + ], + [ + "HOUSTON - With only a few
seconds left in a certain
victory for Miami, Peyton
Manning threw a meaningless
6-yard touchdown pass to
Marvin Harrison to cut the
score to 24-15." + ], + [ + "Reuters - A ragged band of
children\\emerges ghost-like
from mists in Ethiopia's
highlands,\\thrusting bunches
of carrots at a car full of
foreigners." + ], + [ + "DEADLY SCORER: Manchester
United #39;s Wayne Rooney
celebrating his three goals
against Fenerbahce this week
at Old Trafford. (AP)." + ], + [ + "AP - A U.N. human rights
expert criticized the U.S.-led
coalition forces in
Afghanistan for violating
international law by allegedly
beating Afghans to death and
forcing some to remove their
clothes or wear hoods." + ], + [ + "You can feel the confidence
level, not just with Team
Canada but with all of Canada.
There #39;s every expectation,
from one end of the bench to
the other, that Martin Brodeur
is going to hold the fort." + ], + [ + "Heading into the first game of
a new season, every team has
question marks. But in 2004,
the Denver Broncos seemed to
have more than normal." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
said on Thursday Yasser
Arafat's death could be a
turning point for peacemaking
but he would pursue a
unilateral plan that would
strip Palestinians of some
land they want for a state." + ], + [ + " AL-ASAD AIRBASE, Iraq
(Reuters) - Defense Secretary
Donald Rumsfeld swept into an
airbase in Iraq's western
desert Sunday to make a
first-hand evaluation of
operations to quell a raging
Iraqi insurgency in his first
such visit in five months." + ], + [ + "More than three out of four
(76 percent) consumers are
experiencing an increase in
spoofing and phishing
incidents, and 35 percent
receive fake e-mails at least
once a week, according to a
recent national study." + ], + [ + "The Dow Jones Industrial
Average failed three times
this year to exceed its
previous high and fell to
about 10,000 each time, most
recently a week ago." + ], + [ + "AP - Deep in the Atlantic
Ocean, undersea explorers are
living a safer life thanks to
germ-fighting clothing made in
Kinston." + ], + [ + "Anaheim, Calif. - There is a
decidedly right lean to the
three-time champions of the
American League Central. In a
span of 26 days, the Minnesota
Twins lost the left side of
their infield to free agency." + ], + [ + "Computer Associates Monday
announced the general
availability of three
Unicenter performance
management products for
mainframe data management." + ], + [ + "Reuters - The European Union
approved on\\Wednesday the
first biotech seeds for
planting and sale across\\EU
territory, flying in the face
of widespread
consumer\\resistance to
genetically modified (GMO)
crops and foods." + ], + [ + "With the NFL trading deadline
set for 4 p.m. Tuesday,
Patriots coach Bill Belichick
said there didn't seem to be
much happening on the trade
front around the league." + ], + [ + "WASHINGTON - Democrat John
Kerry accused President Bush
on Monday of sending U.S.
troops to the \"wrong war in
the wrong place at the wrong
time\" and said he'd try to
bring them all home in four
years..." + ], + [ + " SINGAPORE (Reuters) - Asian
share markets staged a broad-
based retreat on Wednesday,
led by steelmakers amid
warnings of price declines,
but also enveloping technology
and financial stocks on
worries that earnings may
disappoint." + ], + [ + "p2pnet.net News:- A Microsoft
UK quot;WEIGHING THE COST OF
LINUX VS. WINDOWS? LET #39;S
REVIEW THE FACTS quot;
magazine ad has been nailed as
misleading by Britain #39;s
Advertising Standards
Authority (ASA)." + ], + [ + "More lorry drivers are
bringing supplies to Nepal's
capital in defiance of an
indefinite blockade by Maoist
rebels." + ], + [ + "NEW YORK - CNN has a new boss
for the second time in 14
months: former CBS News
executive Jonathan Klein, who
will oversee programming and
editorial direction at the
second-ranked cable news
network." + ], + [ + "Cut-price carrier Virgin Blue
said Tuesday the cost of using
Australian airports is
spiraling upward and asked the
government to review the
deregulated system of charges." + ], + [ + "The retail sector overall may
be reporting a sluggish start
to the season, but holiday
shoppers are scooping up tech
goods at a brisk pace -- and
they're scouring the Web for
bargains more than ever.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\" color=\"#666666\">&
lt;B>-washingtonpost.com<
;/B></FONT>" + ], + [ + "AP - David Beckham broke his
rib moments after scoring
England's second goal in
Saturday's 2-0 win over Wales
in a World Cup qualifying
game." + ], + [ + "Saudi Arabia, Kuwait and the
United Arab Emirates, which
account for almost half of
OPEC #39;s oil output, said
they #39;re committed to
boosting capacity to meet
soaring demand that has driven
prices to a record." + ], + [ + "The US Commerce Department
said Thursday personal income
posted its biggest increase in
three months in August. The
government agency also said
personal spending was
unchanged after rising
strongly in July." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei average opened up 0.54
percent on Monday with banks
and exporters leading the way
as a stronger finish on Wall
Street and declining oil
prices soothed worries over
the global economic outlook." + ], + [ + " BEIJING (Reuters) - Floods
and landslides have killed 76
people in southwest China in
the past four days and washed
away homes and roads, knocked
down power lines and cut off
at least one city, state
media said on Monday." + ], + [ + "Nothing changed at the top of
Serie A as all top teams won
their games to keep the
distance between one another
unaltered. Juventus came back
from behind against Lazio to
win thanks to another goal by
Ibrahimovic" + ], + [ + "The team behind the Beagle 2
mission has unveiled its
design for a successor to the
British Mars lander." + ], + [ + "Survey points to popularity in
Europe, the Middle East and
Asia of receivers that skip
the pay TV and focus on free
programming." + ], + [ + "RICHMOND, Va. Jeremy Mayfield
won his first race in over
four years, taking the
Chevrolet 400 at Richmond
International Raceway after
leader Kurt Busch ran out of
gas eight laps from the
finish." + ], + [ + "AP - Victims of the Sept. 11
attacks were mourned worldwide
Saturday, but in the Middle
East, amid sympathy for the
dead, Arabs said Washington's
support for Israel and the war
on terror launched in the
aftermath of the World Trade
Center's collapse have only
fueled anger and violence." + ], + [ + "Linux publisher Red Hat Inc.
said Tuesday that information-
technology consulting firm
Unisys Corp. will begin
offering a business version of
the company #39;s open-source
operating system on its
servers." + ], + [ + "SEATTLE - Ichiro Suzuki set
the major league record for
hits in a season with 258,
breaking George Sisler's
84-year-old mark with a pair
of singles Friday night. The
Seattle star chopped a leadoff
single in the first inning,
then made history with a
grounder up the middle in the
third..." + ], + [ + "The intruder who entered
British Queen Elizabeth II
#39;s official Scottish
residence and caused a
security scare was a reporter
from the London-based Sunday
Times newspaper, local media
reported Friday." + ], + [ + "IBM's p5-575, a specialized
server geared for high-
performance computing, has
eight 1.9GHz Power5
processors." + ], + [ + "Bruce Wasserstein, head of
Lazard LLC, is asking partners
to take a one-third pay cut as
he readies the world #39;s
largest closely held
investment bank for a share
sale, people familiar with the
situation said." + ], + [ + "Canadian Press - FREDERICTON
(CP) - A New Brunswick truck
driver arrested in Ontario
this week has been accused by
police of stealing 50,000 cans
of Moosehead beer." + ], + [ + "Reuters - British police said
on Monday they had\\charged a
man with sending hoax emails
to relatives of people\\missing
since the Asian tsunami,
saying their loved ones
had\\been confirmed dead." + ], + [ + "The Lemon Bay Manta Rays were
not going to let a hurricane
get in the way of football. On
Friday, they headed to the
practice field for the first
time in eight" + ], + [ + "Microsoft Corp. Chairman Bill
Gates has donated \\$400,000 to
a campaign in California
trying to win approval of a
measure calling for the state
to sell \\$3 billion in bonds
to fund stem-cell research." + ], + [ + "AP - Track star Marion Jones
filed a defamation lawsuit
Wednesday against the man
whose company is at the center
of a federal investigation
into illegal steroid use among
some of the nation's top
athletes." + ], + [ + "LOS ANGELES - On Sept. 1,
former secretary of
Agriculture Dan Glickman
replaced the legendary Jack
Valenti as president and CEO
of Hollywood #39;s trade
group, the Motion Picture
Association of America." + ], + [ + "England #39;s players hit out
at cricket #39;s authorities
tonight and claimed they had
been used as quot;political
pawns quot; after the Zimbabwe
government produced a
spectacular U-turn to ensure
the controversial one-day
series will go ahead." + ], + [ + "Newspaper publisher Pulitzer
Inc. said Sunday that company
officials are considering a
possible sale of the firm to
boost shareholder value." + ], + [ + "Shares of Merck amp; Co.
plunged almost 10 percent
yesterday after a media report
said that documents show the
pharmaceutical giant hid or
denied" + ], + [ + "AP - The Japanese won the
pregame home run derby. Then
the game started and the major
league All-Stars put their
bats to work. Back-to-back
home runs by Moises Alou and
Vernon Wells in the fourth
inning and by Johnny Estrada
and Brad Wilkerson in the
ninth powered the major
leaguers past the Japanese
stars 7-3 Sunday for a 3-0
lead in the eight-game series." + ], + [ + "Reuters - Wall Street was
expected to dip at\\Thursday's
opening, but shares of Texas
Instruments Inc.\\, may climb
after it gave upbeat earnings
guidance." + ], + [ + "Chinese authorities detained a
prominent, U.S.-based Buddhist
leader in connection with his
plans to reopen an ancient
temple complex in the Chinese
province of Inner Mongolia
last week and have forced
dozens of his American
followers to leave the region,
local officials said
Wednesday." + ], + [ + "The director of the National
Hurricane Center stays calm in
the midst of a storm, but
wants everyone in hurricane-
prone areas to get the message
from his media advisories:
Respect the storm's power and
make proper response plans." + ], + [ + "With Chelsea losing their
unbeaten record and Manchester
United failing yet again to
win, William Hill now make
Arsenal red-hot 2/5 favourites
to retain the title." + ], + [ + "Late in August, Boeing #39;s
top sales execs flew to
Singapore for a crucial sales
pitch. They were close to
persuading Singapore Airlines,
one of the world #39;s leading
airlines, to buy the American
company #39;s new jet, the
mid-sized 7E7." + ], + [ + "SBC Communications and
BellSouth will acquire
YellowPages.com with the goal
of building the site into a
nationwide online business
index, the companies said
Thursday." + ], + [ + "Theresa special bookcase in Al
Grohs office completely full
of game plans from his days in
the NFL. Green ones are from
the Jets." + ], + [ + "SAN FRANCISCO Several
California cities and
counties, including Los
Angeles and San Francisco, are
suing Microsoft for what could
amount to billions of dollars." + ], + [ + "Newcastle ensured their place
as top seeds in Friday #39;s
third round UEFA Cup draw
after holding Sporting Lisbon
to a 1-1 draw at St James #39;
Park." + ], + [ + "Adorned with Turkish and EU
flags, Turkey #39;s newspapers
hailed Thursday an official EU
report recommending the
country start talks to join
the bloc, while largely
ignoring the stringent
conditions attached to the
announcement." + ], + [ + "Google plans to release a
version of its desktop search
tool for computers that run
Apple Computer #39;s Mac
operating system, Google #39;s
chief executive, Eric Schmidt,
said Friday." + ], + [ + "AMD : sicurezza e prestazioni
ottimali con il nuovo
processore mobile per notebook
leggeri e sottili; Acer Inc.
preme sull #39;acceleratore
con il nuovo notebook a
marchio Ferrari." + ], + [ + "The sounds of tinkling bells
could be heard above the
bustle of the Farmers Market
on the Long Beach Promenade,
leading shoppers to a row of
bright red tin kettles dotting
a pathway Friday." + ], + [ + "CBC SPORTS ONLINE - Bode
Miller continued his
impressive 2004-05 World Cup
skiing season by winning a
night slalom race in
Sestriere, Italy on Monday." + ], + [ + "Firefox use around the world
climbed 34 percent in the last
month, according to a report
published by Web analytics
company WebSideStory Monday." + ], + [ + "If a plastic card that gives
you credit for something you
don't want isn't your idea of
a great gift, you can put it
up for sale or swap." + ], + [ + "WASHINGTON Aug. 17, 2004
Scientists are planning to
take the pulse of the planet
and more in an effort to
improve weather forecasts,
predict energy needs months in
advance, anticipate disease
outbreaks and even tell
fishermen where the catch will
be ..." + ], + [ + "Damien Rhodes scored on a
2-yard run in the second
overtime, then Syracuse's
defense stopped Pittsburgh on
fourth and 1, sending the
Orange to a 38-31 victory
yesterday in Syracuse, N.Y." + ], + [ + "AP - Anthony Harris scored 18
of his career-high 23 points
in the second half to help
Miami upset No. 19 Florida
72-65 Saturday and give first-
year coach Frank Haith his
biggest victory." + ], + [ + "LONDON Santander Central
Hispano of Spain looked
certain to clinch its bid for
the British mortgage lender
Abbey National, after HBOS,
Britain #39;s biggest home-
loan company, said Wednesday
it would not counterbid, and
after the European Commission
cleared" + ], + [ + "New communications technology
could spawn future products.
Could your purse tell you to
bring an umbrella?" + ], + [ + " WASHINGTON (Reuters) - The
Justice Department is
investigating possible
accounting fraud at Fannie
Mae, bringing greater
government scrutiny to bear on
the mortgage finance company,
already facing a parallel
inquiry by the SEC, a source
close to the matter said on
Thursday." + ], + [ + "AP - The five cities looking
to host the 2012 Summer Games
submitted bids to the
International Olympic
Committee on Monday, entering
the final stage of a long
process in hopes of landing
one of the biggest prizes in
sports." + ], + [ + "SAP has won a \\$35 million
contract to install its human
resources software for the US
Postal Service. The NetWeaver-
based system will replace the
Post Office #39;s current
25-year-old legacy application" + ], + [ + "The FIA has already cancelled
todays activities at Suzuka as
Super Typhoon Ma-On heads
towards the 5.807km circuit.
Saturday practice has been
cancelled altogether while
pre-qualifying and final
qualifying" + ], + [ + "Thailand's prime minister
visits the southern town where
scores of Muslims died in army
custody after a rally." + ], + [ + "Indian industrial group Tata
agrees to invest \\$2bn in
Bangladesh, the biggest single
deal agreed by a firm in the
south Asian country." + ], + [ + "NewsFactor - For years,
companies large and small have
been convinced that if they
want the sophisticated
functionality of enterprise-
class software like ERP and
CRM systems, they must buy
pre-packaged applications.
And, to a large extent, that
remains true." + ], + [ + "Following in the footsteps of
the RIAA, the MPAA (Motion
Picture Association of
America) announced that they
have began filing lawsuits
against people who use peer-
to-peer software to download
copyrighted movies off the
Internet." + ], + [ + " GRAND PRAIRIE, Texas
(Reuters) - Betting on horses
was banned in Texas until as
recently as 1987. Times have
changed rapidly since.
Saturday, Lone Star Park race
track hosts the \\$14 million
Breeders Cup, global racing's
end-of-season extravaganza." + ], + [ + "MacCentral - At a special
music event featuring Bono and
The Edge from rock group U2
held on Tuesday, Apple took
the wraps off the iPod Photo,
a color iPod available in 40GB
or 60GB storage capacities.
The company also introduced
the iPod U2, a special edition
of Apple's 20GB player clad in
black, equipped with a red
Click Wheel and featuring
engraved U2 band member
signatures. The iPod Photo is
available immediately, and
Apple expects the iPod U2 to
ship in mid-November." + ], + [ + "Beijing: At least 170 miners
were trapped underground after
a gas explosion on Sunday
ignited a fire in a coalmine
in north-west China #39;s
Shaanxi province, reports
said." + ], + [ + "The steel tubing company
reports sharply higher
earnings, but the stock is
falling." + ], + [ + "It might be a stay of
execution for Coach P, or it
might just be a Christmas
miracle come early. SU #39;s
upset win over BC has given
hope to the Orange playing in
a post season Bowl game." + ], + [ + "PHIL Neville insists
Manchester United don #39;t
fear anyone in the Champions
League last 16 and declared:
quot;Bring on the Italians." + ], + [ + "Playboy Enterprises, the adult
entertainment company, has
announced plans to open a
private members club in
Shanghai even though the
company #39;s flagship men
#39;s magazine is still banned
in China." + ], + [ + "Reuters - Oracle Corp is
likely to win clearance\\from
the European Commission for
its hostile #36;7.7
billion\\takeover of rival
software firm PeopleSoft Inc.,
a source close\\to the
situation said on Friday." + ], + [ + "TORONTO (CP) - Earnings
warnings from Celestica and
Coca-Cola along with a
slowdown in US industrial
production sent stock markets
lower Wednesday." + ], + [ + "IBM (Quote, Chart) said it
would spend a quarter of a
billion dollars over the next
year and a half to grow its
RFID (define) business." + ], + [ + "Eastman Kodak Co., the world
#39;s largest maker of
photographic film, said
Wednesday it expects sales of
digital products and services
to grow at an annual rate of
36 percent between 2003 and
2007, above prior growth rate
estimates of 26 percent
between 2002" + ], + [ + "SAMARRA (Iraq): With renewe d
wave of skirmishes between the
Iraqi insurgents and the US-
led coalition marines, several
people including top police
officers were put to death on
Saturday." + ], + [ + "SPACE.com - NASA released one
of the best pictures ever made
of Saturn's moon Titan as the
Cassini spacecraft begins a
close-up inspection of the
satellite today. Cassini is
making the nearest flyby ever
of the smog-shrouded moon." + ], + [ + "AFP - The Iraqi government
plans to phase out slowly
subsidies on basic products,
such as oil and electricity,
which comprise 50 percent of
public spending, equal to 15
billion dollars, the planning
minister said." + ], + [ + "ANNAPOLIS ROYAL, NS - Nova
Scotia Power officials
continued to keep the sluice
gates open at one of the
utility #39;s hydroelectric
plants Wednesday in hopes a
wayward whale would leave the
area and head for the open
waters of the Bay of Fundy." + ], + [ + "TORONTO -- Toronto Raptors
point guard Alvin Williams
will miss the rest of the
season after undergoing
surgery on his right knee
Monday." + ], + [ + "The federal agency that
insures pension plans said
that its deficit, already at
the highest in its history,
had doubled in its last fiscal
year, to \\$23.3 billion." + ], + [ + "AFP - Like most US Latinos,
members of the extended
Rodriguez family say they will
cast their votes for Democrat
John Kerry in next month's
presidential polls." + ], + [ + "A Milan judge on Tuesday opens
hearings into whether to put
on trial 32 executives and
financial institutions over
the collapse of international
food group Parmalat in one of
Europe #39;s biggest fraud
cases." + ], + [ + "AP - Tennessee's two freshmen
quarterbacks have Volunteers
fans fantasizing about the
next four years. Brent
Schaeffer and Erik Ainge
surprised many with the nearly
seamless way they rotated
throughout a 42-17 victory
over UNLV on Sunday night." + ], + [ + "In fact, Larry Ellison
compares himself to the
warlord, according to
PeopleSoft's former CEO,
defending previous remarks he
made." + ], + [ + "FALLUJAH, Iraq -- Four Iraqi
fighters huddled in a trench,
firing rocket-propelled
grenades at Lieutenant Eric
Gregory's Bradley Fighting
Vehicle and the US tanks and
Humvees that were lumbering
through tight streets between
boxlike beige houses." + ], + [ + "MADRID, Aug 18 (Reuters) -
Portugal captain Luis Figo
said on Wednesday he was
taking an indefinite break
from international football,
but would not confirm whether
his decision was final." + ], + [ + "The Bank of England on
Thursday left its benchmark
interest rate unchanged, at
4.75 percent, as policy makers
assessed whether borrowing
costs, already the highest in
the Group of Seven, are
constraining consumer demand." + ], + [ + "AP - Several thousand
Christians who packed a
cathedral compound in the
Egyptian capital hurled stones
at riot police Wednesday to
protest a woman's alleged
forced conversion to Islam. At
least 30 people were injured." + ], + [ + "A group of Saudi religious
scholars have signed an open
letter urging Iraqis to
support jihad against US-led
forces. quot;Fighting the
occupiers is a duty for all
those who are able, quot; they
said in a statement posted on
the internet at the weekend." + ], + [ + "Fashion retailers Austin Reed
and Ted Baker have reported
contrasting fortunes on the
High Street. Austin Reed
reported interim losses of 2." + ], + [ + "AP - Shaun Rogers is in the
backfield as often as some
running backs. Whether teams
dare to block Detroit's star
defensive tackle with one
player or follow the trend of
double-teaming him, he often
rips through offensive lines
with a rare combination of
size, speed, strength and
nimble footwork." + ], + [ + " NEW YORK (Reuters) - A
federal judge on Friday
approved Citigroup Inc.'s
\\$2.6 billion settlement with
WorldCom Inc. investors who
lost billions when an
accounting scandal plunged
the telecommunications company
into bankruptcy protection." + ], + [ + "The Lions and Eagles entered
Sunday #39;s game at Ford
Field in the same place --
atop their respective
divisions -- and with
identical 2-0 records." + ], + [ + "An unspecified number of
cochlear implants to help
people with severe hearing
loss are being recalled
because they may malfunction
due to ear moisture, the US
Food and Drug Administration
announced." + ], + [ + "Profits triple at McDonald's
Japan after the fast-food
chain starts selling larger
burgers." + ], + [ + "After Marcos Moreno threw four
more interceptions in last
week's 14-13 overtime loss at
N.C. A T, Bison Coach Ray
Petty will start Antoine
Hartfield against Norfolk
State on Saturday." + ], + [ + "You can empty your pockets of
change, take off your belt and
shoes and stick your keys in
the little tray. But if you've
had radiation therapy
recently, you still might set
off Homeland Security alarms." + ], + [ + "Mountaineers retrieve three
bodies believed to have been
buried for 22 years on an
Indian glacier." + ], + [ + "SEOUL, Oct 19 Asia Pulse -
LG.Philips LCD Co.
(KSE:034220), the world #39;s
second-largest maker of liquid
crystal display (LCD), said
Tuesday it has developed the
world #39;s largest organic
light emitting diode" + ], + [ + "SOUTH WILLIAMSPORT, Pa. --
Looking ahead to the US
championship game almost cost
Conejo Valley in the
semifinals of the Little
League World Series." + ], + [ + "The Cubs didn #39;t need to
fly anywhere near Florida to
be in the eye of the storm.
For a team that is going on
100 years since last winning a
championship, the only thing
they never are at a loss for
is controversy." + ], + [ + "Security experts warn of
banner ads with a bad attitude
--and a link to malicious
code. Also: Phishers, be gone." + ], + [ + "KETTERING, Ohio Oct. 12, 2004
- Cincinnati Bengals defensive
end Justin Smith pleaded not
guilty to a driving under the
influence charge." + ], + [ + "com October 15, 2004, 5:11 AM
PT. Wood paneling and chrome
made your dad #39;s station
wagon look like a million
bucks, and they might also be
just the ticket for Microsoft
#39;s fledgling" + ], + [ + "President Thabo Mbeki met with
Ivory Coast Prime Minister
Seydou Diarra for three hours
yesterday as part of talks
aimed at bringing peace to the
conflict-wracked Ivory Coast." + ], + [ + "MINNEAPOLIS -- For much of the
2004 season, Twins pitcher
Johan Santana didn #39;t just
beat opposing hitters. Often,
he overwhelmed and owned them
in impressive fashion." + ], + [ + "Britain #39;s inflation rate
fell in August further below
its 2.0 percent government-set
upper limit target with
clothing and footwear prices
actually falling, official
data showed on Tuesday." + ], + [ + " KATHMANDU (Reuters) - Nepal's
Maoist rebels have
temporarily suspended a
crippling economic blockade of
the capital from Wednesday,
saying the move was in
response to popular appeals." + ], + [ + "Reuters - An Algerian
suspected of being a leader\\of
the Madrid train bombers has
been identified as one of
seven\\people who blew
themselves up in April to
avoid arrest, Spain's\\Interior
Ministry said on Friday." + ], + [ + "KABUL: An Afghan man was found
guilty on Saturday of killing
four journalists in 2001,
including two from Reuters,
and sentenced to death." + ], + [ + "Yasser Arafat, the leader for
decades of a fight for
Palestinian independence from
Israel, has died at a military
hospital in Paris, according
to news reports." + ], + [ + " LONDON (Reuters) - European
shares shrugged off a spike in
the euro to a fresh all-time
high Wednesday, with telecoms
again leading the way higher
after interim profits at
Britain's mm02 beat
expectations." + ], + [ + "WASHINGTON - Weighed down by
high energy prices, the US
economy grew slower than the
government estimated in the
April-June quarter, as higher
oil prices limited consumer
spending and contributed to a
record trade deficit." + ], + [ + "CHICAGO United Airlines says
it will need even more labor
cuts than anticipated to get
out of bankruptcy. United told
a bankruptcy court judge in
Chicago today that it intends
to start talks with unions
next month on a new round of
cost savings." + ], + [ + " JABALYA, Gaza Strip (Reuters)
- Israel pulled most of its
forces out of the northern
Gaza Strip Saturday after a
four-day incursion it said
was staged to halt Palestinian
rocket attacks on southern
Israeli towns." + ], + [ + "Computer Associates
International yesterday
reported a 6 increase in
revenue during its second
fiscal quarter, but posted a
\\$94 million loss after paying
to settle government
investigations into the
company, it said yesterday." + ], + [ + "THE Turkish embassy in Baghdad
was investigating a television
report that two Turkish
hostages had been killed in
Iraq, but no confirmation was
available so far, a senior
Turkish diplomat said today." + ], + [ + "Reuters - Thousands of
supporters of
Ukraine's\\opposition leader,
Viktor Yushchenko, celebrated
on the streets\\in the early
hours on Monday after an exit
poll showed him\\winner of a
bitterly fought presidential
election." + ], + [ + "LONDON : The United States
faced rare criticism over
human rights from close ally
Britain, with an official
British government report
taking Washington to task over
concerns about Iraq and the
Guantanamo Bay jail." + ], + [ + "The University of California,
Berkeley, has signed an
agreement with the Samoan
government to isolate, from a
tree, the gene for a promising
anti- Aids drug and to share
any royalties from the sale of
a gene-derived drug with the
people of Samoa." + ], + [ + "At a charity auction in New
Jersey last weekend, baseball
memorabilia dealer Warren
Heller was approached by a man
with an unusual but topical
request." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei average jumped 2.5
percent by mid-afternoon on
Monday as semiconductor-
related stocks such as
Advantest Corp. mirrored a
rally by their U.S. peers
while banks and brokerages
extended last week's gains." + ], + [ + "INTER Milan coach Roberto
Mancini believes the club
#39;s lavish (northern) summer
signings will enable them to
mount a serious Serie A
challenge this season." + ], + [ + "LONDON - A bomb threat that
mentioned Iraq forced a New
York-bound Greek airliner to
make an emergency landing
Sunday at London's Stansted
Airport escorted by military
jets, authorities said. An
airport spokeswoman said an
Athens newspaper had received
a phone call saying there was
a bomb on board the Olympic
Airlines plane..." + ], + [ + "Links to this week's topics
from search engine forums
across the web: New MSN Search
Goes LIVE in Beta - Microsoft
To Launch New Search Engine -
Google Launches 'Google
Advertising Professionals' -
Organic vs Paid Traffic ROI? -
Making Money With AdWords? -
Link Building 101" + ], + [ + "AP - Brad Ott shot an 8-under
64 on Sunday to win the
Nationwide Tour's Price Cutter
Charity Championship for his
first Nationwide victory." + ], + [ + "AP - New York Jets running
back Curtis Martin passed Eric
Dickerson and Jerome Bettis on
the NFL career rushing list
Sunday against the St. Louis
Rams, moving to fourth all-
time." + ], + [ + "Eight conservation groups are
fighting the US government
over a plan to poison
thousands of prairie dogs in
the grasslands of South
Dakota, saying wildlife should
not take a backseat to
ranching interests." + ], + [ + "ATHENS, Greece - Sheryl
Swoopes made three big plays
at the end - two baskets and
another on defense - to help
the United States squeeze out
a 66-62 semifinal victory over
Russia on Friday. Now, only
one game stands between the
U.S..." + ], + [ + "Instead of standing for ante
meridian and post meridian,
though, fans will remember the
time periods of pre-Mia and
after-Mia. After playing for
18 years and shattering nearly
every record" + ], + [ + "General Motors (GM) plans to
announce a massive
restructuring Thursday that
will eliminate as many as
12,000 jobs in Europe in a
move to stem the five-year
flow of red ink from its auto
operations in the region." + ], + [ + "Scientists are developing a
device which could improve the
lives of kidney dialysis
patients." + ], + [ + "KABUL, Afghanistan The Afghan
government is blaming drug
smugglers for yesterday #39;s
attack on the leading vice
presidential candidate ." + ], + [ + "Stephon Marbury, concerned
about his lousy shooting in
Athens, used an off day to go
to the gym and work on his
shot. By finding his range, he
saved the United States #39;
hopes for a basketball gold
medal." + ], + [ + " LONDON (Reuters) - Oil prices
held firm on Wednesday as
Hurricane Ivan closed off
crude output and shut
refineries in the Gulf of
Mexico, while OPEC's Gulf
producers tried to reassure
traders by recommending an
output hike." + ], + [ + "State-owned, running a
monopoly on imports of jet
fuel to China #39;s fast-
growing aviation industry and
a prized member of Singapore
#39;s Stock Exchange." + ], + [ + "Google has won a trade mark
dispute, with a District Court
judge finding that the search
engines sale of sponsored
search terms Geico and Geico
Direct did not breach car
insurance firm GEICOs rights
in the trade marked terms." + ], + [ + "Wall Street bounded higher for
the second straight day
yesterday as investors reveled
in sharply falling oil prices
and the probusiness agenda of
the second Bush
administration. The Dow Jones
industrials gained more than
177 points for its best day of
2004, while the Standard amp;
Poor's 500 closed at its
highest level since early
2002." + ], + [ + "Key factors help determine if
outsourcing benefits or hurts
Americans." + ], + [ + "The US Trade Representative on
Monday rejected the European
Union #39;s assertion that its
ban on beef from hormone-
treated cattle is now
justified by science and that
US and Canadian retaliatory
sanctions should be lifted." + ], + [ + "One of the leading figures in
the Greek Orthodox Church, the
Patriarch of Alexandria Peter
VII, has been killed in a
helicopter crash in the Aegean
Sea." + ], + [ + "Siding with chip makers,
Microsoft said it won't charge
double for its per-processor
licenses when dual-core chips
come to market next year." + ], + [ + "NEW YORK -- Wall Street's
fourth-quarter rally gave
stock mutual funds a solid
performance for 2004, with
small-cap equity funds and
real estate funds scoring some
of the biggest returns. Large-
cap growth equities and
technology-focused funds had
the slimmest gains." + ], + [ + "CANBERRA, Australia -- The
sweat-stained felt hats worn
by Australian cowboys, as much
a part of the Outback as
kangaroos and sun-baked soil,
may be heading for the history
books. They fail modern
industrial safety standards." + ], + [ + "Big Food Group Plc, the UK
owner of the Iceland grocery
chain, said second-quarter
sales at stores open at least
a year dropped 3.3 percent,
the second consecutive
decline, after competitors cut
prices." + ], + [ + "A London-to-Washington flight
is diverted after a security
alert involving the singer
formerly known as Cat Stevens." + ], + [ + " WASHINGTON (Reuters) - The
first case of soybean rust has
been found on the mainland
United States and could affect
U.S. crops for the near
future, costing farmers
millions of dollars, the
Agriculture Department said on
Wednesday." + ], + [ + "IBM and the Spanish government
have introduced a new
supercomputer they hope will
be the most powerful in
Europe, and one of the 10 most
powerful in the world." + ], + [ + "The Supreme Court today
overturned a five-figure
damage award to an Alexandria
man for a local auto dealer
#39;s alleged loan scam,
ruling that a Richmond-based
federal appeals court had
wrongly" + ], + [ + "AP - President Bush declared
Friday that charges of voter
fraud have cast doubt on the
Ukrainian election, and warned
that any European-negotiated
pact on Iran's nuclear program
must ensure the world can
verify Tehran's compliance." + ], + [ + "TheSpaceShipOne team is handed
the \\$10m cheque and trophy it
won for claiming the Ansari
X-Prize." + ], + [ + "Security officials have
identified six of the
militants who seized a school
in southern Russia as being
from Chechnya, drawing a
strong connection to the
Chechen insurgents who have
been fighting Russian forces
for years." + ], + [ + "AP - Randy Moss is expected to
play a meaningful role for the
Minnesota Vikings this weekend
against the Giants, even
without a fully healed right
hamstring." + ], + [ + "Pros: Fits the recent profile
(44, past PGA champion, fiery
Ryder Cup player); the job is
his if he wants it. Cons:
Might be too young to be
willing to burn two years of
play on tour." + ], + [ + "SEOUL -- North Korea set three
conditions yesterday to be met
before it would consider
returning to six-party talks
on its nuclear programs." + ], + [ + "Official figures show the
12-nation eurozone economy
continues to grow, but there
are warnings it may slow down
later in the year." + ], + [ + "Elmer Santos scored in the
second half, lifting East
Boston to a 1-0 win over
Brighton yesterday afternoon
and giving the Jets an early
leg up in what is shaping up
to be a tight Boston City
League race." + ], + [ + "In upholding a lower court
#39;s ruling, the Supreme
Court rejected arguments that
the Do Not Call list violates
telemarketers #39; First
Amendment rights." + ], + [ + "US-backed Iraqi commandos were
poised Friday to storm rebel
strongholds in the northern
city of Mosul, as US military
commanders said they had
quot;broken the back quot; of
the insurgency with their
assault on the former rebel
bastion of Fallujah." + ], + [ + "Infineon Technologies, the
second-largest chip maker in
Europe, said Wednesday that it
planned to invest about \\$1
billion in a new factory in
Malaysia to expand its
automotive chip business and
be closer to customers in the
region." + ], + [ + "Mozilla's new web browser is
smart, fast and user-friendly
while offering a slew of
advanced, customizable
functions. By Michelle Delio." + ], + [ + "Saints special teams captain
Steve Gleason expects to be
fined by the league after
being ejected from Sunday's
game against the Carolina
Panthers for throwing a punch." + ], + [ + "JERUSALEM (Reuters) - Prime
Minister Ariel Sharon, facing
a party mutiny over his plan
to quit the Gaza Strip, has
approved 1,000 more Israeli
settler homes in the West Bank
in a move that drew a cautious
response on Tuesday from ..." + ], + [ + "Play has begun in the
Australian Masters at
Huntingdale in Melbourne with
around half the field of 120
players completing their first
rounds." + ], + [ + " NEW YORK (Reuters) -
Washington Post Co. <A HREF
=\"http://www.investor.reuters.
com/FullQuote.aspx?ticker=WPO.
N target=/stocks/quickinfo/ful
lquote\">WPO.N</A>
said on Friday that quarterly
profit jumped, beating
analysts' forecasts, boosted
by results at its Kaplan
education unit and television
broadcasting operations." + ], + [ + "GHAZNI, Afghanistan, 6 October
2004 - Wartime security was
rolled out for Afghanistans
interim President Hamid Karzai
as he addressed his first
election campaign rally
outside the capital yesterday
amid spiraling violence." + ], + [ + "LOUISVILLE, Ky. - Louisville
men #39;s basketball head
coach Rick Pitino and senior
forward Ellis Myles met with
members of the media on Friday
to preview the Cardinals #39;
home game against rival
Kentucky on Satursday." + ], + [ + "AP - Sounds like David
Letterman is as big a \"Pops\"
fan as most everyone else." + ], + [ + "originally offered on notebook
PCs -- to its Opteron 32- and
64-bit x86 processors for
server applications. The
technology will help servers
to run" + ], + [ + "New orders for US-made durable
goods increased 0.2pc in
September, held back by a big
drop in orders for
transportation goods, the US
Commerce Department said
today." + ], + [ + "Siblings are the first ever to
be convicted for sending
boatloads of junk e-mail
pushing bogus products. Also:
Microsoft takes MSN music
download on a Euro trip....
Nokia begins legal battle
against European
counterparts.... and more." + ], + [ + "I always get a kick out of the
annual list published by
Forbes singling out the
richest people in the country.
It #39;s almost as amusing as
those on the list bickering
over their placement." + ], + [ + "MacCentral - After Apple
unveiled the iMac G5 in Paris
this week, Vice President of
Hardware Product Marketing
Greg Joswiak gave Macworld
editors a guided tour of the
desktop's new design. Among
the topics of conversation:
the iMac's cooling system, why
pre-installed Bluetooth
functionality and FireWire 800
were left out, and how this
new model fits in with Apple's
objectives." + ], + [ + "Williams-Sonoma Inc., operator
of home furnishing chains
including Pottery Barn, said
third-quarter earnings rose 19
percent, boosted by store
openings and catalog sales." + ], + [ + "We #39;ve known about
quot;strained silicon quot;
for a while--but now there
#39;s a better way to do it.
Straining silicon improves
chip performance." + ], + [ + "This week, Sir Richard Branson
announced his new company,
Virgin Galactic, has the
rights to the first commercial
flights into space." + ], + [ + "71-inch HDTV comes with a home
stereo system and components
painted in 24-karat gold." + ], + [ + "Arsenal was held to a 1-1 tie
by struggling West Bromwich
Albion on Saturday, failing to
pick up a Premier League
victory when Rob Earnshaw
scored with 11 minutes left." + ], + [ + "TOKYO - Mitsubishi Heavy
Industries said today it #39;s
in talks to buy a plot of land
in central Japan #39;s Nagoya
city from Mitsubishi Motors
for building aircraft parts." + ], + [ + "China has confirmed that it
found a deadly strain of bird
flu in pigs as early as two
years ago. China #39;s
Agriculture Ministry said two
cases had been discovered, but
it did not say exactly where
the samples had been taken." + ], + [ + "Baseball #39;s executive vice
president Sandy Alderson
insisted last month that the
Cubs, disciplined for an
assortment of run-ins with
umpires, would not be targeted
the rest of the season by
umpires who might hold a
grudge." + ], + [ + "As Superman and Batman would
no doubt reflect during their
cigarette breaks, the really
draining thing about being a
hero was that you have to keep
riding to the rescue." + ], + [ + "MacCentral - RealNetworks Inc.
said on Tuesday that it has
sold more than a million songs
at its online music store
since slashing prices last
week as part of a limited-time
sale aimed at growing the user
base of its new digital media
software." + ], + [ + "With the presidential election
less than six weeks away,
activists and security experts
are ratcheting up concern over
the use of touch-screen
machines to cast votes.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-washingtonpost.com&l
t;/B></FONT>" + ], + [ + "NEW YORK, September 14 (New
Ratings) - Yahoo! Inc
(YHOO.NAS) has agreed to
acquire Musicmatch Inc, a
privately held digital music
software company, for about
\\$160 million in cash." + ], + [ + "Japan #39;s Sumitomo Mitsui
Financial Group Inc. said
Tuesday it proposed to UFJ
Holdings Inc. that the two
banks merge on an equal basis
in its latest attempt to woo
UFJ away from a rival suitor." + ], + [ + "Oil futures prices were little
changed Thursday as traders
anxiously watched for
indications that the supply or
demand picture would change in
some way to add pressure to
the market or take some away." + ], + [ + "Gov. Rod Blagojevich plans to
propose a ban Thursday on the
sale of violent and sexually
explicit video games to
minors, something other states
have tried with little
success." + ], + [ + " CHICAGO (Reuters) - Delta Air
Lines Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=DAL.N target=
/stocks/quickinfo/fullquote\"&g
t;DAL.N</A> said on
Tuesday it will cut wages by
10 percent and its chief
executive will go unpaid for
the rest of the year, but it
still warned of bankruptcy
within weeks unless more cuts
are made." + ], + [ + "AP - Ten years after the Irish
Republican Army's momentous
cease-fire, negotiations
resumed Wednesday in hope of
reviving a Catholic-Protestant
administration, an elusive
goal of Northern Ireland's
hard-fought peace process." + ], + [ + "A cable channel plans to
resurrect each of the 1,230
regular-season games listed on
the league's defunct 2004-2005
schedule by setting them in
motion on a video game
console." + ], + [ + " SANTO DOMINGO, Dominican
Republic, Sept. 18 -- Tropical
Storm Jeanne headed for the
Bahamas on Saturday after an
assault on the Dominican
Republic that killed 10
people, destroyed hundreds of
houses and forced thousands
from their homes." + ], + [ + "An explosion tore apart a car
in Gaza City Monday, killing
at least one person,
Palestinian witnesses said.
They said Israeli warplanes
were circling overhead at the
time of the blast, indicating
a possible missile strike." + ], + [ + " WASHINGTON (Reuters) - A
former Fannie Mae <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=FNM.N
target=/stocks/quickinfo/fullq
uote\">FNM.N</A>
employee who gave U.S.
officials information about
what he saw as accounting
irregularities will not
testify as planned before a
congressional hearing next
week, a House committee said
on Friday." + ], + [ + "Beijing, Oct. 25 (PTI): China
and the US today agreed to
work jointly to re-energise
the six-party talks mechanism
aimed at dismantling North
Korea #39;s nuclear programmes
while Washington urged Beijing
to resume" + ], + [ + "AFP - Sporadic gunfire and
shelling took place overnight
in the disputed Georgian
region of South Ossetia in
violation of a fragile
ceasefire, wounding seven
Georgian servicemen." + ], + [ + "PARIS, Nov 4 (AFP) - The
European Aeronautic Defence
and Space Company reported
Thursday that its nine-month
net profit more than doubled,
thanks largely to sales of
Airbus aircraft, and raised
its full-year forecast." + ], + [ + "AP - Eric Hinske and Vernon
Wells homered, and the Toronto
Blue Jays completed a three-
game sweep of the Baltimore
Orioles with an 8-5 victory
Sunday." + ], + [ + "SiliconValley.com - When
\"Halo\" became a smash video
game hit following Microsoft's
launch of the Xbox console in
2001, it was a no-brainer that
there would be a sequel to the
science fiction shoot-em-up." + ], + [ + "The number of people claiming
unemployment benefit last
month fell by 6,100 to
830,200, according to the
Office for National
Statistics." + ], + [ + " NEW YORK (Reuters) - Todd
Walker homered, had three hits
and drove in four runs to lead
the Chicago Cubs to a 12-5 win
over the Cincinnati Reds in
National League play at
Wrigley Field on Monday." + ], + [ + "PARIS -- The city of Paris
intends to reduce its
dependence on software
suppliers with \"de facto
monopolies,\" but considers an
immediate switch of its 17,000
desktops to open source
software too costly, it said
Wednesday." + ], + [ + " FALLUJA, Iraq (Reuters) -
U.S. forces hit Iraq's rebel
stronghold of Falluja with the
fiercest air and ground
bombardment in months, as
insurgents struck back on
Saturday with attacks that
killed up to 37 people in
Samarra." + ], + [ + "MIAMI (Sports Network) -
Shaquille O #39;Neal made his
home debut, but once again it
was Dwyane Wade stealing the
show with 28 points as the
Miami Heat downed the
Cleveland Cavaliers, 92-86, in
front of a record crowd at
AmericanAirlines Arena." + ], + [ + "AP - The San Diego Chargers
looked sharp #151; and played
the same way. Wearing their
powder-blue throwback jerseys
and white helmets from the
1960s, the Chargers did almost
everything right in beating
the Jacksonville Jaguars 34-21
on Sunday." + ], + [ + "The vast majority of consumers
are unaware that an Apple iPod
digital music player only
plays proprietary iTunes
files, while a smaller
majority agree that it is
within RealNetworks #39;
rights to develop a program
that will make its music files
compatible" + ], + [ + "Tyler airlines are gearing up
for the beginning of holiday
travel, as officials offer
tips to help travelers secure
tickets and pass through
checkpoints with ease." + ], + [ + " NAJAF, Iraq (Reuters) - The
fate of a radical Shi'ite
rebellion in the holy city of
Najaf was uncertain Friday
amid disputed reports that
Iraqi police had gained
control of the Imam Ali
Mosque." + ], + [ + " PROVIDENCE, R.I. (Reuters) -
You change the oil in your car
every 5,000 miles or so. You
clean your house every week or
two. Your PC needs regular
maintenance as well --
especially if you're using
Windows and you spend a lot of
time on the Internet." + ], + [ + "NERVES - no problem. That
#39;s the verdict of Jose
Mourinho today after his
Chelsea side gave a resolute
display of character at
Highbury." + ], + [ + "AP - The latest low point in
Ron Zook's tenure at Florida
even has the coach wondering
what went wrong. Meanwhile,
Sylvester Croom's first big
win at Mississippi State has
given the Bulldogs and their
fans a reason to believe in
their first-year leader.
Jerious Norwood's 37-yard
touchdown run with 32 seconds
remaining lifted the Bulldogs
to a 38-31 upset of the 20th-
ranked Gators on Saturday." + ], + [ + "A criminal trial scheduled to
start Monday involving former
Enron Corp. executives may
shine a rare and potentially
harsh spotlight on the inner
workings" + ], + [ + "The Motley Fool - Here's
something you don't see every
day -- the continuing brouhaha
between Oracle (Nasdaq: ORCL -
News) and PeopleSoft (Nasdaq:
PSFT - News) being a notable
exception. South Africa's
Harmony Gold Mining Company
(NYSE: HMY - News) has
announced a hostile takeover
bid to acquire fellow South
African miner Gold Fields
Limited (NYSE: GFI - News).
The transaction, if it takes
place, would be an all-stock
acquisition, with Harmony
issuing 1.275 new shares in
payment for each share of Gold
Fields. The deal would value
Gold Fields at more than
#36;8 billion. ..." + ], + [ + "Someone forgot to inform the
US Olympic basketball team
that it was sent to Athens to
try to win a gold medal, not
to embarrass its country." + ], + [ + "SPACE.com - NASA's Mars \\rover
Opportunity nbsp;will back its
\\way out of a nbsp;crater it
has spent four months
exploring after reaching
terrain nbsp;that appears \\too
treacherous to tread. nbsp;" + ], + [ + "Sony Corp. announced Tuesday a
new 20 gigabyte digital music
player with MP3 support that
will be available in Great
Britain and Japan before
Christmas and elsewhere in
Europe in early 2005." + ], + [ + "Wal-Mart Stores Inc. #39;s
Asda, the UK #39;s second
biggest supermarket chain,
surpassed Marks amp; Spencer
Group Plc as Britain #39;s
largest clothing retailer in
the last three months,
according to the Sunday
Telegraph." + ], + [ + "Ten-man Paris St Germain
clinched their seventh
consecutive victory over arch-
rivals Olympique Marseille
with a 2-1 triumph in Ligue 1
on Sunday thanks to a second-
half winner by substitute
Edouard Cisse." + ], + [ + "Until this week, only a few
things about the strange,
long-ago disappearance of
Charles Robert Jenkins were
known beyond a doubt. In the
bitter cold of Jan. 5, 1965,
the 24-year-old US Army
sergeant was leading" + ], + [ + "Roy Oswalt wasn #39;t
surprised to hear the Astros
were flying Sunday night
through the remnants of a
tropical depression that
dumped several inches of rain
in Louisiana and could bring
showers today in Atlanta." + ], + [ + "AP - This hardly seemed
possible when Pitt needed
frantic rallies to overcome
Division I-AA Furman or Big
East cellar dweller Temple. Or
when the Panthers could barely
move the ball against Ohio
#151; not Ohio State, but Ohio
U." + ], + [ + "Everyone is moaning about the
fallout from last weekend but
they keep on reporting the
aftermath. #39;The fall-out
from the so-called
quot;Battle of Old Trafford
quot; continues to settle over
the nation and the debate" + ], + [ + "Oil supply concerns and broker
downgrades of blue-chip
companies left stocks mixed
yesterday, raising doubts that
Wall Street #39;s year-end
rally would continue." + ], + [ + "Genentech Inc. said the
marketing of Rituxan, a cancer
drug that is the company #39;s
best-selling product, is the
subject of a US criminal
investigation." + ], + [ + "American Lindsay Davenport
regained the No. 1 ranking in
the world for the first time
since early 2002 by defeating
Dinara Safina of Russia 6-4,
6-2 in the second round of the
Kremlin Cup on Thursday." + ], + [ + " The world's No. 2 soft drink
company said on Thursday
quarterly profit rose due to
tax benefits." + ], + [ + "TOKYO (AP) -- The electronics
and entertainment giant Sony
Corp. (SNE) is talking with
Wal-Mart Stores Inc..." + ], + [ + "After an unprecedented span of
just five days, SpaceShipOne
is ready for a return trip to
space on Monday, its final
flight to clinch a \\$10
million prize." + ], + [ + "The United States on Tuesday
modified slightly a threat of
sanctions on Sudan #39;s oil
industry in a revised text of
its UN resolution on
atrocities in the country
#39;s Darfur region." + ], + [ + "Freshman Jeremy Ito kicked
four field goals and Ryan
Neill scored on a 31-yard
interception return to lead
improving Rutgers to a 19-14
victory on Saturday over
visiting Michigan State." + ], + [ + "Hi-tech monitoring of
livestock at pig farms could
help improve the animal growth
process and reduce costs." + ], + [ + "Third-seeded Guillermo Canas
defeated Guillermo Garcia-
Lopez of Spain 7-6 (1), 6-3
Monday on the first day of the
Shanghai Open on Monday." + ], + [ + "AP - France intensified
efforts Tuesday to save the
lives of two journalists held
hostage in Iraq, and the Arab
League said the militants'
deadline for France to revoke
a ban on Islamic headscarves
in schools had been extended." + ], + [ + "Cable amp; Wireless plc
(NYSE: CWP - message board) is
significantly ramping up its
investment in local loop
unbundling (LLU) in the UK,
and it plans to spend up to 85
million (\\$152." + ], + [ + "USATODAY.com - Personal
finance software programs are
the computer industry's
version of veggies: Everyone
knows they're good for you,
but it's just hard to get
anyone excited about them." + ], + [ + " NEW YORK (Reuters) - The
dollar rebounded on Monday
after last week's heavy
selloff, but analysts were
uncertain if the rally would
hold after fresh economic data
suggested the December U.S.
jobs report due Friday might
not live up to expectations." + ], + [ + "AFP - Microsoft said that it
had launched a new desktop
search tool that allows
personal computer users to
find documents or messages on
their PCs." + ], + [ + "At least 12 people die in an
explosion at a fuel pipeline
on the outskirts of Nigeria's
biggest city, Lagos." + ], + [ + "The three largest computer
makers spearheaded a program
today designed to standardize
working conditions for their
non-US workers." + ], + [ + "Description: Illinois Gov. Rod
Blagojevich is backing state
legislation that would ban
sales or rentals of video
games with graphic sexual or
violent content to children
under 18." + ], + [ + "Volkswagen demanded a two-year
wage freeze for the
170,000-strong workforce at
Europe #39;s biggest car maker
yesterday, provoking union
warnings of imminent conflict
at key pay and conditions
negotiations." + ], + [ + " NEW YORK (Reuters) - U.S.
stock futures pointed to a
lower open on Wall Street on
Thursday, extending the
previous session's sharp
fall, with rising energy
prices feeding investor
concerns about corporate
profits and slower growth." + ], + [ + "But to play as feebly as it
did for about 35 minutes last
night in Game 1 of the WNBA
Finals and lose by only four
points -- on the road, no less
-- has to be the best
confidence builder since Cindy
St." + ], + [ + "MILAN General Motors and Fiat
on Wednesday edged closer to
initiating a legal battle that
could pit the two carmakers
against each other in a New
York City court room as early
as next month." + ], + [ + "Are you bidding on keywords
through Overture's Precision
Match, Google's AdWords or
another pay-for-placement
service? If so, you're
eligible to participate in
their contextual advertising
programs." + ], + [ + "Two of the Ford Motor Company
#39;s most senior executives
retired on Thursday in a sign
that the company #39;s deep
financial crisis has abated,
though serious challenges
remain." + ], + [ + "Citing security concerns, the
U.S. Embassy on Thursday
banned its employees from
using the highway linking the
embassy area to the
international airport, a
10-mile stretch of road
plagued by frequent suicide
car-bomb attacks." + ], + [ + "Nobel Laureate Wilkins, 87,
played an important role in
the discovery of the double
helix structure of DNA, the
molecule that carries our
quot;life code quot;,
Kazinform refers to BBC News." + ], + [ + "With yesterday #39;s report on
its athletic department
violations completed, the
University of Washington says
it is pleased to be able to
move forward." + ], + [ + " LONDON (Reuters) - Wall
Street was expected to start
little changed on Friday as
investors continue to fret
over the impact of high oil
prices on earnings, while
Boeing <A HREF=\"http://www.
investor.reuters.com/FullQuote
.aspx?ticker=BA.N target=/stoc
ks/quickinfo/fullquote\">BA.
N</A> will be eyed
after it reiterated its
earnings forecast." + ], + [ + "AP - Tom Daschle bade his
fellow Senate Democrats
farewell Tuesday with a plea
that they seek common ground
with Republicans yet continue
to fight for the less
fortunate." + ], + [ + "Sammy Sosa was fined \\$87,400
-- one day's salary -- for
arriving late to the Cubs'
regular-season finale at
Wrigley Field and leaving the
game early. The slugger's
agent, Adam Katz , said
yesterday Sosa most likely
will file a grievance. Sosa
arrived 70 minutes before
Sunday's first pitch, and he
apparently left 15 minutes
after the game started without
..." + ], + [ + "Having an always-on, fast net
connection is changing the way
Britons use the internet,
research suggests." + ], + [ + "AP - Police defused a bomb in
a town near Prime Minister
Silvio Berlusconi's villa on
the island of Sardinia on
Wednesday shortly after
British Prime Minister Tony
Blair finished a visit there
with the Italian leader." + ], + [ + "Is the Oklahoma defense a
notch below its predecessors?
Is Texas #39; offense a step-
ahead? Why is Texas Tech
feeling good about itself
despite its recent loss?" + ], + [ + "The coffin of Yasser Arafat,
draped with the Palestinian
flag, was bound for Ramallah
in the West Bank Friday,
following a formal funeral on
a military compound near
Cairo." + ], + [ + "US Ambassador to the United
Nations John Danforth resigned
on Thursday after serving in
the post for less than six
months. Danforth, 68, said in
a letter released Thursday" + ], + [ + "Crude oil futures prices
dropped below \\$51 a barrel
yesterday as supply concerns
ahead of the Northern
Hemisphere winter eased after
an unexpectedly high rise in
US inventories." + ], + [ + "New York gets 57 combined
points from its starting
backcourt of Jamal Crawford
and Stephon Marbury and tops
Denver, 107-96." + ], + [ + "ISLAMABAD, Pakistan -- Photos
were published yesterday in
newspapers across Pakistan of
six terror suspects, including
a senior Al Qaeda operative,
the government says were
behind attempts to assassinate
the nation's president." + ], + [ + "AP - Shawn Marion had a
season-high 33 points and 15
rebounds, leading the Phoenix
Suns on a fourth-quarter
comeback despite the absence
of Steve Nash in a 95-86 win
over the New Orleans Hornets
on Friday night." + ], + [ + "By Lilly Vitorovich Of DOW
JONES NEWSWIRES SYDNEY (Dow
Jones)--Rupert Murdoch has
seven weeks to convince News
Corp. (NWS) shareholders a
move to the US will make the
media conglomerate more
attractive to" + ], + [ + "A number of signs point to
increasing demand for tech
workers, but not all the
clouds have been driven away." + ], + [ + "Messina upset defending
champion AC Milan 2-1
Wednesday, while Juventus won
its third straight game to
stay alone atop the Italian
league standings." + ], + [ + "Microsoft Corp. (MSFT.O:
Quote, Profile, Research)
filed nine new lawsuits
against spammers who send
unsolicited e-mail, including
an e-mail marketing Web
hosting company, the world
#39;s largest software maker
said on Thursday." + ], + [ + "AP - Padraig Harrington
rallied to a three-stroke
victory in the German Masters
on a windy Sunday, closing
with a 2-under-par 70 and
giving his game a big boost
before the Ryder Cup." + ], + [ + " ATHENS (Reuters) - The Athens
Paralympics canceled
celebrations at its closing
ceremony after seven
schoolchildren traveling to
watch the event died in a bus
crash on Monday." + ], + [ + "The rocket plane SpaceShipOne
is just one flight away from
claiming the Ansari X-Prize, a
\\$10m award designed to kick-
start private space travel." + ], + [ + "Reuters - Three American
scientists won the\\2004 Nobel
physics prize on Tuesday for
showing how tiny
quark\\particles interact,
helping to explain everything
from how a\\coin spins to how
the universe was built." + ], + [ + "Ironically it was the first
regular season game for the
Carolina Panthers that not
only began the history of the
franchise, but also saw the
beginning of a rivalry that
goes on to this day." + ], + [ + "Baltimore Ravens running back
Jamal Lewis did not appear at
his arraignment Friday, but
his lawyers entered a not
guilty plea on charges in an
expanded drug conspiracy
indictment." + ], + [ + "AP - Sharp Electronics Corp.
plans to stop selling its
Linux-based handheld computer
in the United States, another
sign of the slowing market for
personal digital assistants." + ], + [ + "After serving a five-game
suspension, Milton Bradley
worked out with the Dodgers as
they prepared for Tuesday's
opener against the St. Louis
Cardinals." + ], + [ + "AP - Prime Time won't be
playing in prime time this
time. Deion Sanders was on the
inactive list and missed a
chance to strut his stuff on
\"Monday Night Football.\"" + ], + [ + "Reuters - Glaciers once held
up by a floating\\ice shelf off
Antarctica are now sliding off
into the sea --\\and they are
going fast, scientists said on
Tuesday." + ], + [ + "DUBAI : An Islamist group has
threatened to kill two Italian
women held hostage in Iraq if
Rome does not withdraw its
troops from the war-torn
country within 24 hours,
according to an internet
statement." + ], + [ + "AP - Warning lights flashed
atop four police cars as the
caravan wound its way up the
driveway in a procession fit
for a presidential candidate.
At long last, Azy and Indah
had arrived. They even flew
through a hurricane to get
here." + ], + [ + "The man who delivered the
knockout punch was picked up
from the Seattle scrap heap
just after the All-Star Game.
Before that, John Olerud
certainly hadn't figured on
facing Pedro Martinez in
Yankee Stadium in October." + ], + [ + "\\Female undergraduates work
harder and are more open-
minded than males, leading to
better results, say
scientists." + ], + [ + "A heavy quake rocked Indonesia
#39;s Papua province killing
at least 11 people and
wounding 75. The quake
destroyed 150 buildings,
including churches, mosques
and schools." + ], + [ + "LONDON : A consortium,
including former world
champion Nigel Mansell, claims
it has agreed terms to ensure
Silverstone remains one of the
venues for the 2005 Formula
One world championship." + ], + [ + " BATON ROUGE, La. (Sports
Network) - LSU has named Les
Miles its new head football
coach, replacing Nick Saban." + ], + [ + "The United Nations annual
World Robotics Survey predicts
the use of robots around the
home will surge seven-fold by
2007. The boom is expected to
be seen in robots that can mow
lawns and vacuum floors, among
other chores." + ], + [ + "The long-term economic health
of the United States is
threatened by \\$53 trillion in
government debts and
liabilities that start to come
due in four years when baby
boomers begin to retire." + ], + [ + "Reuters - A small group of
suspected\\gunmen stormed
Uganda's Water Ministry
Wednesday and took
three\\people hostage to
protest against proposals to
allow President\\Yoweri
Museveni for a third
term.\\Police and soldiers with
assault rifles cordoned off
the\\three-story building, just
328 feet from Uganda's
parliament\\building in the
capital Kampala." + ], + [ + "The Moscow Arbitration Court
ruled on Monday that the YUKOS
oil company must pay RUR
39.113bn (about \\$1.34bn) as
part of its back tax claim for
2001." + ], + [ + "NOVEMBER 11, 2004 -- Bankrupt
US Airways this morning said
it had reached agreements with
lenders and lessors to
continue operating nearly all
of its mainline and US Airways
Express fleets." + ], + [ + "Venezuela suggested Friday
that exiles living in Florida
may have masterminded the
assassination of a prosecutor
investigating a short-lived
coup against leftist President
Hugo Chvez" + ], + [ + "Want to dive deep -- really
deep -- into the technical
literature about search
engines? Here's a road map to
some of the best web
information retrieval
resources available online." + ], + [ + "Reuters - Ancel Keys, a
pioneer in public health\\best
known for identifying the
connection between
a\\cholesterol-rich diet and
heart disease, has died." + ], + [ + "The US government asks the
World Trade Organisation to
step in to stop EU member
states from \"subsidising\"
planemaker Airbus." + ], + [ + "Reuters - T-Mobile USA, the
U.S. wireless unit\\of Deutsche
Telekom AG (DTEGn.DE), does
not expect to offer\\broadband
mobile data services for at
least the next two years,\\its
chief executive said on
Thursday." + ], + [ + "Verizon Communications is
stepping further into video as
a way to compete against cable
companies." + ], + [ + "Facing a popular outcry at
home and stern warnings from
Europe, the Turkish government
discreetly stepped back
Tuesday from a plan to
introduce a motion into a
crucial penal reform bill to
make adultery a crime
punishable by prison." + ], + [ + "Boston Scientific Corp.
(BSX.N: Quote, Profile,
Research) said on Wednesday it
received US regulatory
approval for a device to treat
complications that arise in
patients with end-stage kidney
disease who need dialysis." + ], + [ + "North-west Norfolk MP Henry
Bellingham has called for the
release of an old college
friend accused of plotting a
coup in Equatorial Guinea." + ], + [ + "With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." + ], + [ + "AP - The Chicago Blackhawks
re-signed goaltender Michael
Leighton to a one-year
contract Wednesday." + ], + [ + "Oracle Corp. plans to release
the latest version of its CRM
(customer relationship
management) applications
within the next two months, as
part of an ongoing update of
its E-Business Suite." + ], + [ + "Toyota Motor Corp. #39;s
shares fell for a second day,
after the world #39;s second-
biggest automaker had an
unexpected quarterly profit
drop." + ], + [ + "AFP - Want to buy a castle?
Head for the former East
Germany." + ], + [ + "Hosted CRM service provider
Salesforce.com took another
step forward last week in its
strategy to build an online
ecosystem of vendors that
offer software as a service." + ], + [ + "Britain-based HBOS says it
will file a complaint to the
European Commission against
Spanish bank Santander Central
Hispano (SCH) in connection
with SCH #39;s bid to acquire
British bank Abbey National" + ], + [ + "AFP - Steven Gerrard has moved
to allay Liverpool fans' fears
that he could be out until
Christmas after breaking a
metatarsal bone in his left
foot." + ], + [ + "Verizon Wireless on Thursday
announced an agreement to
acquire all the PCS spectrum
licenses of NextWave Telecom
Inc. in 23 markets for \\$3
billion." + ], + [ + "washingtonpost.com -
Technology giants IBM and
Hewlett-Packard are injecting
hundreds of millions of
dollars into radio-frequency
identification technology,
which aims to advance the
tracking of items from ho-hum
bar codes to smart tags packed
with data." + ], + [ + "ATHENS -- She won her first
Olympic gold medal in kayaking
when she was 18, the youngest
paddler to do so in Games
history. Yesterday, at 42,
Germany #39;s golden girl
Birgit Fischer won her eighth
Olympic gold in the four-woman
500-metre kayak race." + ], + [ + "England boss Sven Goran
Eriksson has defended
goalkeeper David James after
last night #39;s 2-2 draw in
Austria. James allowed Andreas
Ivanschitz #39;s shot to slip
through his fingers to
complete Austria comeback from
two goals down." + ], + [ + "MINSK - Legislative elections
in Belarus held at the same
time as a referendum on
whether President Alexander
Lukashenko should be allowed
to seek a third term fell
significantly short of
democratic standards, foreign
observers said here Monday." + ], + [ + "An Olympic sailor is charged
with the manslaughter of a
Briton who died after being
hit by a car in Athens." + ], + [ + "The Norfolk Broads are on
their way to getting a clean
bill of ecological health
after a century of stagnation." + ], + [ + "AP - Secretary of State Colin
Powell on Friday praised the
peace deal that ended fighting
in Iraq's holy city of Najaf
and said the presence of U.S.
forces in the area helped make
it possible." + ], + [ + "The quot;future quot; is
getting a chance to revive the
presently struggling New York
Giants. Two other teams also
decided it was time for a
change at quarterback, but the
Buffalo Bills are not one of
them." + ], + [ + "For the second time this year,
an alliance of major Internet
providers - including Atlanta-
based EarthLink -iled a
coordinated group of lawsuits
aimed at stemming the flood of
online junk mail." + ], + [ + " WASHINGTON (Reuters) - The
PIMCO mutual fund group has
agreed to pay \\$50 million to
settle fraud charges involving
improper rapid dealing in
mutual fund shares, the U.S.
Securities and Exchange
Commission said on Monday." + ], + [ + "Via Technologies has released
a version of the open-source
Xine media player that is
designed to take advantage of
hardware digital video
acceleration capabilities in
two of the company #39;s PC
chipsets, the CN400 and
CLE266." + ], + [ + "The Conference Board reported
Thursday that the Leading
Economic Index fell for a
third consecutive month in
August, suggesting slower
economic growth ahead amid
rising oil prices." + ], + [ + " SAN FRANCISCO (Reuters) -
Software maker Adobe Systems
Inc.<A HREF=\"http://www.inv
estor.reuters.com/FullQuote.as
px?ticker=ADBE.O target=/stock
s/quickinfo/fullquote\">ADBE
.O</A> on Thursday
posted a quarterly profit that
rose more than one-third from
a year ago, but shares fell 3
percent after the maker of
Photoshop and Acrobat software
did not raise forecasts for
fiscal 2005." + ], + [ + "William Morrison Supermarkets
has agreed to sell 114 small
Safeway stores and a
distribution centre for 260.2
million pounds. Morrison
bought these stores as part of
its 3 billion pound" + ], + [ + "SCO Group has a plan to keep
itself fit enough to continue
its legal battles against
Linux and to develop its Unix-
on-Intel operating systems." + ], + [ + "Flushing Meadows, NY (Sports
Network) - The men #39;s
semifinals at the 2004 US Open
will be staged on Saturday,
with three of the tournament
#39;s top-five seeds ready for
action at the USTA National
Tennis Center." + ], + [ + "Pepsi pushes a blue version of
Mountain Dew only at Taco
Bell. Is this a winning
strategy?" + ], + [ + "New software helps corporate
travel managers track down
business travelers who
overspend. But it also poses a
dilemma for honest travelers
who are only trying to save
money." + ], + [ + "NATO Secretary-General Jaap de
Hoop Scheffer has called a
meeting of NATO states and
Russia on Tuesday to discuss
the siege of a school by
Chechen separatists in which
more than 335 people died, a
NATO spokesman said." + ], + [ + "26 August 2004 -- Iraq #39;s
top Shi #39;ite cleric, Grand
Ayatollah Ali al-Sistani,
arrived in the city of Al-
Najaf today in a bid to end a
weeks-long conflict between US
forces and militiamen loyal to
Shi #39;ite cleric Muqtada al-
Sadr." + ], + [ + "AFP - Senior executives at
business software group
PeopleSoft unanimously
recommended that its
shareholders reject a 8.8
billion dollar takeover bid
from Oracle Corp, PeopleSoft
said in a statement Wednesday." + ], + [ + "Reuters - Neolithic people in
China may have\\been the first
in the world to make wine,
according to\\scientists who
have found the earliest
evidence of winemaking\\from
pottery shards dating from
7,000 BC in northern China." + ], + [ + "Given nearly a week to examine
the security issues raised by
the now-infamous brawl between
players and fans in Auburn
Hills, Mich., Nov. 19, the
Celtics returned to the
FleetCenter last night with
two losses and few concerns
about their on-court safety." + ], + [ + " TOKYO (Reuters) - Electronics
conglomerate Sony Corp.
unveiled eight new flat-screen
televisions on Thursday in a
product push it hopes will
help it secure a leading 35
percent of the domestic
market in the key month of
December." + ], + [ + "As the election approaches,
Congress abandons all pretense
of fiscal responsibility,
voting tax cuts that would
drive 10-year deficits past
\\$3 trillion." + ], + [ + "PARIS : French trade unions
called on workers at France
Telecom to stage a 24-hour
strike September 7 to protest
government plans to privatize
the public telecommunications
operator, union sources said." + ], + [ + "ServiceMaster profitably
bundles services and pays a
healthy 3.5 dividend." + ], + [ + "The Indonesian tourism
industry has so far not been
affected by last week #39;s
bombing outside the Australian
embassy in Jakarta and
officials said they do not
expect a significant drop in
visitor numbers as a result of
the attack." + ], + [ + "\\$222.5 million -- in an
ongoing securities class
action lawsuit against Enron
Corp. The settlement,
announced Friday and" + ], + [ + "Arsenals Thierry Henry today
missed out on the European
Footballer of the Year award
as Andriy Shevchenko took the
honour. AC Milan frontman
Shevchenko held off
competition from Barcelona
pair Deco and" + ], + [ + "Donald Halsted, one target of
a class-action suit alleging
financial improprieties at
bankrupt Polaroid, officially
becomes CFO." + ], + [ + "Neil Mellor #39;s sensational
late winner for Liverpool
against Arsenal on Sunday has
earned the back-up striker the
chance to salvage a career
that had appeared to be
drifting irrevocably towards
the lower divisions." + ], + [ + "ABOUT 70,000 people were
forced to evacuate Real Madrid
#39;s Santiago Bernabeu
stadium minutes before the end
of a Primera Liga match
yesterday after a bomb threat
in the name of ETA Basque
separatist guerillas." + ], + [ + "The team learned on Monday
that full-back Jon Ritchie
will miss the rest of the
season with a torn anterior
cruciate ligament in his left
knee." + ], + [ + " NEW YORK (Reuters) -
Lifestyle guru Martha Stewart
said on Wednesday she wants
to start serving her prison
sentence for lying about a
suspicious stock sale as soon
as possible, so she can put
her \"nightmare\" behind her." + ], + [ + "Apple Computer's iPod remains
the king of digital music
players, but robust pretenders
to the throne have begun to
emerge in the Windows
universe. One of them is the
Zen Touch, from Creative Labs." + ], + [ + "The 7710 model features a
touch screen, pen input, a
digital camera, an Internet
browser, a radio, video
playback and streaming and
recording capabilities, the
company said." + ], + [ + "SAN FRANCISCO (CBS.MW) --
Crude futures closed under
\\$46 a barrel Wednesday for
the first time since late
September and heating-oil and
unleaded gasoline prices
dropped more than 6 percent
following an across-the-board
climb in US petroleum
inventories." + ], + [ + "The University of Iowa #39;s
market for US presidential
futures, founded 16-years ago,
has been overtaken by a
Dublin-based exchange that is
now 25 times larger." + ], + [ + "Venus Williams barely kept
alive her hopes of qualifying
for next week #39;s WTA Tour
Championships. Williams,
seeded fifth, survived a
third-set tiebreaker to
outlast Yuilana Fedak of the
Ukraine, 6-4 2-6 7-6" + ], + [ + " SYDNEY (Reuters) - Arnold
Palmer has taken a swing at
America's top players,
criticizing their increasing
reluctance to travel abroad
to play in tournaments." + ], + [ + "MARK Thatcher will have to
wait until at least next April
to face trial on allegations
he helped bankroll a coup
attempt in oil-rich Equatorial
Guinea." + ], + [ + "A top Red Hat executive has
attacked the open-source
credentials of its sometime
business partner Sun
Microsystems. In a Web log
posting Thursday, Michael
Tiemann, Red Hat #39;s vice
president of open-source
affairs" + ], + [ + "President Bush #39;s drive to
deploy a multibillion-dollar
shield against ballistic
missiles was set back on
Wednesday by what critics
called a stunning failure of
its first full flight test in
two years." + ], + [ + "Although he was well-beaten by
Retief Goosen in Sunday #39;s
final round of The Tour
Championship in Atlanta, there
has been some compensation for
the former world number one,
Tiger Woods." + ], + [ + "WAYNE Rooney and Henrik
Larsson are among the players
nominated for FIFAs
prestigious World Player of
the Year award. Rooney is one
of four Manchester United
players on a list which is
heavily influenced by the
Premiership." + ], + [ + "It didn #39;t look good when
it happened on the field, and
it looked worse in the
clubhouse. Angels second
baseman Adam Kennedy left the
Angels #39; 5-2 win over the
Seattle Mariners" + ], + [ + "Air travelers moved one step
closer to being able to talk
on cell phones and surf the
Internet from laptops while in
flight, thanks to votes by the
Federal Communications
Commission yesterday." + ], + [ + "MySQL developers turn to an
unlikely source for database
tool: Microsoft. Also: SGI
visualizes Linux, and the
return of Java veteran Kim
Polese." + ], + [ + "DESPITE the budget deficit,
continued increases in oil and
consumer prices, the economy,
as measured by gross domestic
product, grew by 6.3 percent
in the third" + ], + [ + "NEW YORK - A drop in oil
prices and upbeat outlooks
from Wal-Mart and Lowe's
helped send stocks sharply
higher Monday on Wall Street,
with the swing exaggerated by
thin late summer trading. The
Dow Jones industrials surged
nearly 130 points..." + ], + [ + "Freshman Darius Walker ran for
115 yards and scored two
touchdowns, helping revive an
Irish offense that had managed
just one touchdown in the
season's first six quarters." + ], + [ + "Consumers who cut it close by
paying bills from their
checking accounts a couple of
days before depositing funds
will be out of luck under a
new law that takes effect Oct.
28." + ], + [ + "Dell Inc. said its profit
surged 25 percent in the third
quarter as the world's largest
personal computer maker posted
record sales due to rising
technology spending in the
corporate and government
sectors in the United States
and abroad." + ], + [ + "AP - NBC is adding a 5-second
delay to its NASCAR telecasts
after Dale Earnhardt Jr. used
a vulgarity during a postrace
TV interview last weekend." + ], + [ + " BOSTON (Sports Network) - The
New York Yankees will start
Orlando \"El Duque\" Hernandez
in Game 4 of the American
League Championship Series on
Saturday against the Boston
Red Sox." + ], + [ + "The future of Sven-Goran
Eriksson as England coach is
the subject of intense
discussion after the draw in
Austria. Has the Swede lost
the confidence of the nation
or does he remain the best man
for the job?" + ], + [ + "Component problems meant
Brillian's new big screens
missed the NFL's kickoff
party." + ], + [ + "Spain begin their third final
in five seasons at the Olympic
stadium hoping to secure their
second title since their first
in Barcelona against Australia
in 2000." + ], + [ + "Second-seeded David Nalbandian
of Argentina lost at the Japan
Open on Thursday, beaten by
Gilles Muller of Luxembourg
7-6 (4), 3-6, 6-4 in the third
round." + ], + [ + "Thursday #39;s unexpected
resignation of Memphis
Grizzlies coach Hubie Brown
left a lot of questions
unanswered. In his unique way
of putting things, the
71-year-old Brown seemed to
indicate he was burned out and
had some health concerns." + ], + [ + "RUDI Voller had quit as coach
of Roma after a 3-1 defeat
away to Bologna, the Serie A
club said today. Under the
former Germany coach, Roma had
taken just four league points
from a possible 12." + ], + [ + "A Russian court on Thursday
rejected an appeal by the
Yukos oil company seeking to
overturn a freeze on the
accounts of the struggling oil
giant #39;s core subsidiaries." + ], + [ + "ONE by one, the players #39;
faces had flashed up on the
giant Ibrox screens offering
season #39;s greetings to the
Rangers fans. But the main
presents were reserved for
Auxerre." + ], + [ + "Switzerland #39;s struggling
national airline reported a
second-quarter profit of 45
million Swiss francs (\\$35.6
million) Tuesday, although its
figures were boosted by a
legal settlement in France." + ], + [ + "ROSTOV-ON-DON, Russia --
Hundreds of protesters
ransacked and occupied the
regional administration
building in a southern Russian
province Tuesday, demanding
the resignation of the region
#39;s president, whose former
son-in-law has been linked to
a multiple" + ], + [ + "SIPTU has said it is strongly
opposed to any privatisation
of Aer Lingus as pressure
mounts on the Government to
make a decision on the future
funding of the airline." + ], + [ + "Reuters - SBC Communications
said on Monday it\\would offer
a television set-top box that
can handle music,\\photos and
Internet downloads, part of
SBC's efforts to expand\\into
home entertainment." + ], + [ + "Molson Inc. Chief Executive
Officer Daniel O #39;Neill
said he #39;ll provide
investors with a positive #39;
#39; response to their
concerns over the company
#39;s plan to let stock-
option holders vote on its
planned merger with Adolph
Coors Co." + ], + [ + "South Korea #39;s Grace Park
shot a seven-under-par 65 to
triumph at the CJ Nine Bridges
Classic on Sunday. Park #39;s
victory made up her final-
round collapse at the Samsung
World Championship two weeks
ago." + ], + [ + " WASHINGTON (Reuters) - Hopes
-- and worries -- that U.S.
regulators will soon end the
ban on using wireless phones
during U.S. commercial flights
are likely at least a year or
two early, government
officials and analysts say." + ], + [ + "AFP - Iraqi Foreign Minister
Hoshyar Zebari arrived
unexpectedly in the holy city
of Mecca Wednesday where he
met Crown Prince Abdullah bin
Abdul Aziz, the official SPA
news agency reported." + ], + [ + "Titans QB Steve McNair was
released from a Nashville
hospital after a two-night
stay for treatment of a
bruised sternum. McNair was
injured during the fourth
quarter of the Titans #39;
15-12 loss to Jacksonville on
Sunday." + ], + [ + "Keith Miller, Australia #39;s
most prolific all-rounder in
Test cricket, died today at a
nursing home, Cricket
Australia said. He was 84." + ], + [ + "Haitian police and UN troops
moved into a slum neighborhood
on Sunday and cleared street
barricades that paralyzed a
part of the capital." + ], + [ + "TORONTO Former Toronto pitcher
John Cerutti (seh-ROO
#39;-tee) was found dead in
his hotel room today,
according to the team. He was
44." + ], + [ + "withdrawal of troops and
settlers from occupied Gaza
next year. Militants seek to
claim any pullout as a
victory. quot;Islamic Jihad
will not be broken by this
martyrdom, quot; said Khaled
al-Batsh, a senior political
leader in Gaza." + ], + [ + " NEW YORK (Reuters) - The
world's largest gold producer,
Newmont Mining Corp. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=NEM
.N target=/stocks/quickinfo/fu
llquote\">NEM.N</A>,
on Wednesday said higher gold
prices drove up quarterly
profit by 12.5 percent, even
though it sold less of the
precious metal." + ], + [ + "The U.S. military has found
nearly 20 houses where
intelligence officers believe
hostages were tortured or
killed in this city, including
the house with the cage that
held a British contractor who
was beheaded last month." + ], + [ + "AFP - Opponents of the Lao
government may be plotting
bomb attacks in Vientiane and
other areas of Laos timed to
coincide with a summit of
Southeast Asian leaders the
country is hosting next month,
the United States said." + ], + [ + "After a year of pilots and
trials, Siebel Systems jumped
with both feet into the SMB
market Tuesday, announcing a
new approach to offer Siebel
Professional CRM applications
to SMBs (small and midsize
businesses) -- companies with
revenues up to about \\$500
million." + ], + [ + "AP - Russia agreed Thursday to
send warships to help NATO
naval patrols that monitor
suspicious vessels in the
Mediterranean, part of a push
for closer counterterrorism
cooperation between Moscow and
the western alliance." + ], + [ + "Intel won't release a 4-GHz
version of its flagship
Pentium 4 product, having
decided instead to realign its
engineers around the company's
new design priorities, an
Intel spokesman said today.
\\\\" + ], + [ + "AP - A Soyuz spacecraft
carrying two Russians and an
American rocketed closer
Friday to its docking with the
international space station,
where the current three-man
crew made final departure
preparations." + ], + [ + "Defense: Ken Lucas. His
biggest play was his first
one. The fourth-year
cornerback intercepted a Ken
Dorsey pass that kissed off
the hands of wide receiver
Rashaun Woods and returned it
25 yards to set up the
Seahawks #39; first score." + ], + [ + "Scientists have manipulated
carbon atoms to create a
material that could be used to
create light-based, versus
electronic, switches. The
material could lead to a
supercharged Internet based
entirely on light, scientists
say." + ], + [ + "A military plane crashed in
the mountains near Caracas,
killing all 16 persons on
board, including two high-
ranking military officers,
officials said." + ], + [ + "The powerful St. Louis trio of
Albert Pujols, Scott Rolen and
Jim Edmonds is 4 for 23 with
one RBI in the series and with
runners on base, they are 1
for 13." + ], + [ + "A voice recording said to be
that of suspected Al Qaeda
commander Abu Mussab al-
Zarqawi, claims Iraq #39;s
Prime Minister Iyad Allawi is
the militant network #39;s
number one target." + ], + [ + "BEIJING -- More than a year
after becoming China's
president, Hu Jintao was
handed the full reins of power
yesterday when his
predecessor, Jiang Zemin, gave
up the nation's most powerful
military post." + ], + [ + "LOS ANGELES (Reuters)
Qualcomm has dropped an \\$18
million claim for monetary
damages from rival Texas
Instruments for publicly
discussing terms of a
licensing pact, a TI
spokeswoman confirmed Tuesday." + ], + [ + "Hewlett-Packard is the latest
IT vendor to try blogging. But
analysts wonder if the weblog
trend is the 21st century
equivalent of CB radios, which
made a big splash in the 1970s
before fading." + ], + [ + " WASHINGTON (Reuters) - Fannie
Mae executives and their
regulator squared off on
Wednesday, with executives
denying any accounting
irregularity and the regulator
saying the housing finance
company's management may need
to go." + ], + [ + "The scientists behind Dolly
the sheep apply for a license
to clone human embryos. They
want to take stem cells from
the embryos to study Lou
Gehrig's disease." + ], + [ + "As the first criminal trial
stemming from the financial
deals at Enron opened in
Houston on Monday, it is
notable as much for who is not
among the six defendants as
who is - and for how little
money was involved compared
with how much in other Enron" + ], + [ + "LONDON (CBS.MW) -- British
bank Barclays on Thursday said
it is in talks to buy a
majority stake in South
African bank ABSA. Free!" + ], + [ + "The Jets signed 33-year-old
cornerback Terrell Buckley,
who was released by New
England on Sunday, after
putting nickel back Ray
Mickens on season-ending
injured reserve yesterday with
a torn ACL in his left knee." + ], + [ + "Some of the silly tunes
Japanese pay to download to
use as the ring tone for their
mobile phones sure have their
knockers, but it #39;s for
precisely that reason that a
well-known counselor is raking
it in at the moment, according
to Shukan Gendai (10/2)." + ], + [ + "WEST INDIES thrilling victory
yesterday in the International
Cricket Council Champions
Trophy meant the world to the
five million people of the
Caribbean." + ], + [ + "AP - Greenpeace activists
scaled the walls of Ford Motor
Co.'s Norwegian headquarters
Tuesday to protest plans to
destroy hundreds of non-
polluting electric cars." + ], + [ + "Investors sent stocks sharply
lower yesterday as oil prices
continued their climb higher
and new questions about the
safety of arthritis drugs
pressured pharmaceutical
stocks." + ], + [ + "Scotland manager Berti Vogts
insists he is expecting
nothing but victory against
Moldova on Wednesday. The game
certainly is a must-win affair
if the Scots are to have any
chance of qualifying for the
2006 World Cup finals." + ], + [ + "IBM announced yesterday that
it will invest US\\$250 million
(S\\$425 million) over the next
five years and employ 1,000
people in a new business unit
to support products and
services related to sensor
networks." + ], + [ + "AFP - The chances of Rupert
Murdoch's News Corp relocating
from Australia to the United
States have increased after
one of its biggest
institutional investors has
chosen to abstain from a vote
next week on the move." + ], + [ + "AFP - An Indian minister said
a school text-book used in the
violence-prone western state
of Gujarat portrayed Adolf
Hitler as a role model." + ], + [ + "Reuters - The head of UAL
Corp.'s United\\Airlines said
on Thursday the airline's
restructuring plan\\would lead
to a significant number of job
losses, but it was\\not clear
how many." + ], + [ + "DOVER, N.H. (AP) -- Democrat
John Kerry is seizing on the
Bush administration's failure
to secure hundreds of tons of
explosives now missing in
Iraq." + ], + [ + "AP - Microsoft Corp. goes into
round two Friday of its battle
to get the European Union's
sweeping antitrust ruling
lifted having told a judge
that it had been prepared
during settlement talks to
share more software code with
its rivals than the EU
ultimately demanded." + ], + [ + " INDIANAPOLIS (Reuters) -
Jenny Thompson will take the
spotlight from injured U.S.
team mate Michael Phelps at
the world short course
championships Saturday as she
brings down the curtain on a
spectacular swimming career." + ], + [ + "Martin Brodeur made 27 saves,
and Brad Richards, Kris Draper
and Joe Sakic scored goals to
help Canada beat Russia 3-1
last night in the World Cup of
Hockey, giving the Canadians a
3-0 record in round-robin
play." + ], + [ + "AP - Sears, Roebuck and Co.,
which has successfully sold
its tools and appliances on
the Web, is counting on having
the same magic with bedspreads
and sweaters, thanks in part
to expertise gained by its
purchase of Lands' End Inc." + ], + [ + "com September 14, 2004, 9:12
AM PT. With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." + ], + [ + " NEW YORK (Reuters) -
Children's Place Retail Stores
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=PLCE.O target=/sto
cks/quickinfo/fullquote\">PL
CE.O</A> said on
Wednesday it will buy 313
retail stores from Walt
Disney Co., and its stock rose
more than 14 percent in early
morning trade." + ], + [ + "ALBANY, N.Y. -- A California-
based company that brokers
life, accident, and disability
policies for leading US
companies pocketed millions of
dollars a year in hidden
payments from insurers and
from charges on clients'
unsuspecting workers, New York
Attorney General Eliot Spitzer
charged yesterday." + ], + [ + "NORTEL Networks plans to slash
its workforce by 3500, or ten
per cent, as it struggles to
recover from an accounting
scandal that toppled three top
executives and led to a
criminal investigation and
lawsuits." + ], + [ + "Ebay Inc. (EBAY.O: Quote,
Profile, Research) said on
Friday it would buy Rent.com,
an Internet housing rental
listing service, for \\$415
million in a deal that gives
it access to a new segment of
the online real estate market." + ], + [ + "Austin police are working with
overseas officials to bring
charges against an English man
for sexual assault of a child,
a second-degree felony." + ], + [ + "United Nations officials
report security breaches in
internally displaced people
and refugee camps in Sudan
#39;s embattled Darfur region
and neighboring Chad." + ], + [ + "Noranda Inc., Canada #39;s
biggest mining company, began
exclusive talks on a takeover
proposal from China Minmetals
Corp. that would lead to the
spinoff of Noranda #39;s
aluminum business to
shareholders." + ], + [ + "San Francisco
developer/publisher lands
coveted Paramount sci-fi
license, \\$6.5 million in
funding on same day. Although
it is less than two years old,
Perpetual Entertainment has
acquired one of the most
coveted sci-fi licenses on the
market." + ], + [ + "ST. LOUIS -- Mike Martz #39;s
week of anger was no empty
display. He saw the defending
NFC West champions slipping
and thought taking potshots at
his players might be his best
shot at turning things around." + ], + [ + "Google warned Thursday that
increased competition and the
maturing of the company would
result in an quot;inevitable
quot; slowing of its growth." + ], + [ + "By KELLY WIESE JEFFERSON
CITY, Mo. (AP) -- Missouri
will allow members of the
military stationed overseas to
return absentee ballots via
e-mail, raising concerns from
Internet security experts
about fraud and ballot
secrecy..." + ], + [ + "Avis Europe PLC has dumped a
new ERP system based on
software from PeopleSoft Inc.
before it was even rolled out,
citing substantial delays and
higher-than-expected costs." + ], + [ + " TOKYO (Reuters) - The Nikkei
average rose 0.55 percent by
midsession on Wednesday as
some techs including Advantest
Corp. gained ground after
Wall Street reacted positively
to results from Intel Corp.
released after the U.S. market
close." + ], + [ + "Yahoo #39;s (Quote, Chart)
public embrace of the RSS
content syndication format
took a major leap forward with
the release of a revamped My
Yahoo portal seeking to
introduce the technology to
mainstream consumers." + ], + [ + "KINGSTON, Jamaica - Hurricane
Ivan's deadly winds and
monstrous waves bore down on
Jamaica on Friday, threatening
a direct hit on its densely
populated capital after
ravaging Grenada and killing
at least 33 people. The
Jamaican government ordered
the evacuation of half a
million people from coastal
areas, where rains on Ivan's
outer edges were already
flooding roads..." + ], + [ + "North Korea has denounced as
quot;wicked terrorists quot;
the South Korean officials who
orchestrated last month #39;s
airlift to Seoul of 468 North
Korean defectors." + ], + [ + "The Black Watch regiment has
returned to its base in Basra
in southern Iraq after a
month-long mission standing in
for US troops in a more
violent part of the country,
the Ministry of Defence says." + ], + [ + "Romanian soccer star Adrian
Mutu as he arrives at the
British Football Association
in London, ahead of his
disciplinary hearing, Thursday
Nov. 4, 2004." + ], + [ + "Australia completed an
emphatic Test series sweep
over New Zealand with a
213-run win Tuesday, prompting
a caution from Black Caps
skipper Stephen Fleming for
other cricket captains around
the globe." + ], + [ + "AP - A senior Congolese
official said Tuesday his
nation had been invaded by
neighboring Rwanda, and U.N.
officials said they were
investigating claims of
Rwandan forces clashing with
militias in the east." + ], + [ + "Liverpool, England (Sports
Network) - Former English
international and Liverpool
great Emlyn Hughes passed away
Tuesday from a brain tumor." + ], + [ + " ATLANTA (Reuters) - Soft
drink giant Coca-Cola Co.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=KO.N target=/stocks/quic
kinfo/fullquote\">KO.N</A
>, stung by a prolonged
downturn in North America,
Germany and other major
markets, on Thursday lowered
its key long-term earnings
and sales targets." + ], + [ + "JACKSONVILLE, Fla. -- They
were singing in the Colts #39;
locker room today, singing
like a bunch of wounded
songbirds. Never mind that
Marcus Pollard, Dallas Clark
and Ben Hartsock won #39;t be
recording a remake of Kenny
Chesney #39;s song,
quot;Young, quot; any time
soon." + ], + [ + "TOKYO (AP) -- Japanese
electronics and entertainment
giant Sony Corp. (SNE) plans
to begin selling a camcorder
designed for consumers that
takes video at digital high-
definition quality and is
being priced at about
\\$3,600..." + ], + [ + "As the close-knit NASCAR
community mourns the loss of
team owner Rick Hendrick #39;s
son, brother, twin nieces and
six others in a plane crash
Sunday, perhaps no one outside
of the immediate family
grieves more deeply than
Darrell Waltrip." + ], + [ + "AP - Purdue quarterback Kyle
Orton has no trouble
remembering how he felt after
last year's game at Michigan." + ], + [ + "UNITED NATIONS - The United
Nations #39; nuclear agency
says it is concerned about the
disappearance of equipment and
materials from Iraq that could
be used to make nuclear
weapons." + ], + [ + " BRUSSELS (Reuters) - The EU's
historic deal with Turkey to
open entry talks with the vast
Muslim country was hailed by
supporters as a bridge builder
between Europe and the Islamic
world." + ], + [ + "Iraqi President Ghazi al-
Yawar, who was due in Paris on
Sunday to start a European
tour, has postponed his visit
to France due to the ongoing
hostage drama involving two
French journalists, Arab
diplomats said Friday." + ], + [ + " SAO PAULO, Brazil (Reuters) -
President Luiz Inacio Lula da
Silva's Workers' Party (PT)
won the mayoralty of six state
capitals in Sunday's municipal
vote but was forced into a
run-off to defend its hold on
the race's biggest prize, the
city of Sao Paulo." + ], + [ + "ATHENS, Greece - They are
America's newest golden girls
- powerful and just a shade
from perfection. The U.S..." + ], + [ + "AMMAN, Sept. 15. - The owner
of a Jordanian truck company
announced today that he had
ordered its Iraq operations
stopped in a bid to save the
life of a driver held hostage
by a militant group." + ], + [ + "Israel is prepared to back a
Middle East conference
convened by Tony Blair early
next year despite having
expressed fears that the
British plans were over-
ambitious and designed" + ], + [ + "AP - U.S. State Department
officials learned that seven
American children had been
abandoned at a Nigerian
orphanage but waited more than
a week to check on the youths,
who were suffering from
malnutrition, malaria and
typhoid, a newspaper reported
Saturday." + ], + [ + "\\Angry mobs in Ivory Coast's
main city, Abidjan, marched on
the airport, hours after it
came under French control." + ], + [ + "Several workers are believed
to have been killed and others
injured after a contruction
site collapsed at Dubai
airport. The workers were
trapped under rubble at the
site of a \\$4." + ], + [ + "Talks between Sudan #39;s
government and two rebel
groups to resolve the nearly
two-year battle resume Friday.
By Abraham McLaughlin Staff
writer of The Christian
Science Monitor." + ], + [ + "In a meeting of the cinematic
with the scientific, Hollywood
helicopter stunt pilots will
try to snatch a returning NASA
space probe out of the air
before it hits the ground." + ], + [ + "Legend has it (incorrectly, it
seems) that infamous bank
robber Willie Sutton, when
asked why banks were his
favorite target, responded,
quot;Because that #39;s where
the money is." + ], + [ + "Brown is a second year player
from Memphis and has spent the
2004 season on the Steelers
#39; practice squad. He played
in two games last year." + ], + [ + "A Canadian court approved Air
Canada #39;s (AC.TO: Quote,
Profile, Research) plan of
arrangement with creditors on
Monday, clearing the way for
the world #39;s 11th largest
airline to emerge from
bankruptcy protection at the
end of next month" + ], + [ + "Pfizer, GlaxoSmithKline and
Purdue Pharma are the first
drugmakers willing to take the
plunge and use radio frequency
identification technology to
protect their US drug supply
chains from counterfeiters." + ], + [ + "Barret Jackman, the last of
the Blues left to sign before
the league #39;s probable
lockout on Wednesday,
finalized a deal Monday that
is rare in the current
economic climate but fitting
for him." + ], + [ + " LONDON (Reuters) - Oil prices
eased on Monday after rebels
in Nigeria withdrew a threat
to target oil operations, but
lingering concerns over
stretched supplies ahead of
winter kept prices close to
\\$50." + ], + [ + "AP - In the tumult of the
visitors' clubhouse at Yankee
Stadium, champagne pouring all
around him, Theo Epstein held
a beer. \"I came in and there
was no champagne left,\" he
said this week. \"I said, 'I'll
have champagne if we win it
all.'\" Get ready to pour a
glass of bubbly for Epstein.
No I.D. necessary." + ], + [ + "Search any fee-based digital
music service for the best-
loved musical artists of the
20th century and most of the
expected names show up." + ], + [ + "Barcelona held on from an
early Deco goal to edge game
local rivals Espanyol 1-0 and
carve out a five point
tabletop cushion. Earlier,
Ronaldo rescued a point for
Real Madrid, who continued
their middling form with a 1-1
draw at Real Betis." + ], + [ + "MONTREAL (CP) - The Expos may
be history, but their demise
has heated up the market for
team memorabilia. Vintage
1970s and 1980s shirts are
already sold out, but
everything from caps, beer
glasses and key-chains to
dolls of mascot Youppi!" + ], + [ + "Stansted airport is the
designated emergency landing
ground for planes in British
airspace hit by in-flight
security alerts. Emergency
services at Stansted have
successfully dealt" + ], + [ + "The massive military operation
to retake Fallujah has been
quot;accomplished quot;, a
senior Iraqi official said.
Fierce fighting continued in
the war-torn city where
pockets of resistance were
still holding out against US
forces." + ], + [ + "There are some signs of
progress in resolving the
Nigerian conflict that is
riling global oil markets. The
leader of militia fighters
threatening to widen a battle
for control of Nigeria #39;s
oil-rich south has" + ], + [ + "A strong earthquake hit Taiwan
on Monday, shaking buildings
in the capital Taipei for
several seconds. No casualties
were reported." + ], + [ + "America Online Inc. is
packaging new features to
combat viruses, spam and
spyware in response to growing
online security threats.
Subscribers will be able to
get the free tools" + ], + [ + "A 76th minute goal from
European Footballer of the
Year Pavel Nedved gave
Juventus a 1-0 win over Bayern
Munich on Tuesday handing the
Italians clear control at the
top of Champions League Group
C." + ], + [ + " LONDON (Reuters) - Oil prices
climbed above \\$42 a barrel on
Wednesday, rising for the
third day in a row as cold
weather gripped the U.S.
Northeast, the world's biggest
heating fuel market." + ], + [ + "A policeman ran amok at a
security camp in Indian-
controlled Kashmir after an
argument and shot dead seven
colleagues before he was
gunned down, police said on
Sunday." + ], + [ + "ANN ARBOR, Mich. -- Some NHL
players who took part in a
charity hockey game at the
University of Michigan on
Thursday were hopeful the news
that the NHL and the players
association will resume talks
next week" + ], + [ + "New York police have developed
a pre-emptive strike policy,
cutting off demonstrations
before they grow large." + ], + [ + "CAPE CANAVERAL-- NASA aims to
launch its first post-Columbia
shuttle mission during a
shortened nine-day window
March, and failure to do so
likely would delay a planned
return to flight until at
least May." + ], + [ + "Travelers headed home for
Thanksgiving were greeted
Wednesday with snow-covered
highways in the Midwest, heavy
rain and tornadoes in parts of
the South, and long security
lines at some of the nation
#39;s airports." + ], + [ + "BOULDER, Colo. -- Vernand
Morency ran for 165 yards and
two touchdowns and Donovan
Woods threw for three more
scores, lifting No. 22
Oklahoma State to a 42-14
victory over Colorado
yesterday." + ], + [ + "The Chinese city of Beijing
has cancelled an order for
Microsoft software, apparently
bowing to protectionist
sentiment. The deal has come
under fire in China, which is
trying to build a domestic
software industry." + ], + [ + "Apple says it will deliver its
iTunes music service to more
European countries next month.
Corroborating several reports
in recent months, Reuters is
reporting today that Apple
Computer is planning the next" + ], + [ + "Reuters - Motorola Inc., the
world's\\second-largest mobile
phone maker, said on Tuesday
it expects\\to sustain strong
sales growth in the second
half of 2004\\thanks to new
handsets with innovative
designs and features." + ], + [ + "PULLMAN - Last week, in
studying USC game film, Cougar
coaches thought they found a
chink in the national
champions armor. And not just
any chink - one with the
potential, right from the get
go, to" + ], + [ + "The union representing flight
attendants on Friday said it
mailed more than 5,000 strike
authorization ballots to its
members employed by US Airways
as both sides continued talks
that are expected to stretch
through the weekend." + ], + [ + "AP - Matt Leinart was quite a
baseball prospect growing up,
showing so much promise as a
left-handed pitcher that
scouts took notice before high
school." + ], + [ + "PRAGUE, Czech Republic --
Eugene Cernan, the last man to
walk on the moon during the
final Apollo landing, said
Thursday he doesn't expect
space tourism to become
reality in the near future,
despite a strong demand.
Cernan, now 70, who was
commander of NASA's Apollo 17
mission and set foot on the
lunar surface in December 1972
during his third space flight,
acknowledged that \"there are
many people interested in
space tourism.\" But the
former astronaut said he
believed \"we are a long way
away from the day when we can
send a bus of tourists to the
moon.\" He spoke to reporters
before being awarded a medal
by the Czech Academy of
Sciences for his contribution
to science..." + ], + [ + "Never shy about entering a
market late, Microsoft Corp.
is planning to open the
virtual doors of its long-
planned Internet music store
next week. <FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</B></FONT>" + ], + [ + "AP - On his first birthday
Thursday, giant panda cub Mei
Sheng delighted visitors by
playing for the first time in
snow delivered to him at the
San Diego Zoo. He also sat on
his ice cake, wrestled with
his mom, got his coat
incredibly dirty, and didn't
read any of the more than 700
birthday wishes sent him via
e-mail from as far away as
Ireland and Argentina." + ], + [ + "AP - Researchers put a
satellite tracking device on a
15-foot shark that appeared to
be lost in shallow water off
Cape Cod, the first time a
great white has been tagged
that way in the Atlantic." + ], + [ + "LSU will stick with a two-
quarterback rotation Saturday
at Auburn, according to Tigers
coach Nick Saban, who seemed
to have some fun telling the
media what he will and won
#39;t discuss Monday." + ], + [ + "Bulgaria has started its first
co-mission with the EU in
Bosnia and Herzegovina, along
with some 30 countries,
including Canada and Turkey." + ], + [ + "The Windows Future Storage
(WinFS) technology that got
cut out of Windows
quot;Longhorn quot; is in
serious trouble, and not just
the hot water a feature might
encounter for missing its
intended production vehicle." + ], + [ + "Seattle -- - Not so long ago,
the 49ers were inflicting on
other teams the kind of pain
and embarrassment they felt in
their 34-0 loss to the
Seahawks on Sunday." + ], + [ + "AP - The pileup of events in
the city next week, including
the Republican National
Convention, will add to the
security challenge for the New
York Police Department, but
commissioner Ray Kelly says,
\"With a big, experienced
police force, we can do it.\"" + ], + [ + "washingtonpost.com - Let the
games begin. Not the Olympics
again, but the all-out battle
between video game giants Sony
Corp. and Nintendo Co. Ltd.
The two Japanese companies are
rolling out new gaming
consoles, but Nintendo has
beaten Sony to the punch by
announcing an earlier launch
date for its new hand-held
game player." + ], + [ + "London - Manchester City held
fierce crosstown rivals
Manchester United to a 0-0
draw on Sunday, keeping the
Red Devils eleven points
behind leaders Chelsea." + ], + [ + "LONDON, Dec 11 (IranMania) -
Iraqi Vice-President Ibrahim
al-Jaafari refused to believe
in remarks published Friday
that Iran was attempting to
influence Iraqi polls with the
aim of creating a
quot;crescent quot; dominated
by Shiites in the region." + ], + [ + "LOS ANGELES (CBS.MW) - The US
Securities and Exchange
Commission is probing
transactions between Delphi
Corp and EDS, which supplies
the automotive parts and
components giant with
technology services, Delphi
said late Wednesday." + ], + [ + "MONTREAL (CP) - Molson Inc.
and Adolph Coors Co. are
sweetening their brewery
merger plan with a special
dividend to Molson
shareholders worth \\$381
million." + ], + [ + "AP - Echoing what NASA
officials said a day earlier,
a Russian space official on
Friday said the two-man crew
on the international space
station could be forced to
return to Earth if a planned
resupply flight cannot reach
them with food supplies later
this month." + ], + [ + "InfoWorld - SANTA CLARA,
CALIF. -- Accommodating large
patch sets in Linux is
expected to mean forking off
of the 2.7 version of the
platform to accommodate these
changes, according to Andrew
Morton, lead maintainer of the
Linux kernel for Open Source
Development Labs (OSDL)." + ], + [ + "AMSTERDAM The mobile phone
giants Vodafone and Nokia
teamed up on Thursday to
simplify cellphone software
written with the Java computer
language." + ], + [ + "WELLINGTON: National carrier
Air New Zealand said yesterday
the Australian Competition
Tribunal has approved a
proposed alliance with Qantas
Airways Ltd, despite its
rejection in New Zealand." + ], + [ + "The late Princess Dianas
former bodyguard, Ken Wharfe,
dismisses her suspicions that
one of her lovers was bumped
off. Princess Diana had an
affair with Barry Mannakee, a
policeman who was assigned to
protect her." + ], + [ + "Long considered beyond the
reach of mainland mores, the
Florida city is trying to
limit blatant displays of
sexual behavior." + ], + [ + "The overall Linux market is
far larger than previous
estimates show, a new study
says. In an analysis of the
Linux market released late
Tuesday, market research firm
IDC estimated that the Linux
market -- including" + ], + [ + "By PAUL ELIAS SAN FRANCISCO
(AP) -- Several California
cities and counties, including
San Francisco and Los Angeles,
sued Microsoft Corp. (MSFT) on
Friday, accusing the software
giant of illegally charging
inflated prices for its
products because of monopoly
control of the personal
computer operating system
market..." + ], + [ + "New Ole Miss head coach Ed
Orgeron, speaking for the
first time since his hiring,
made clear the goal of his
football program. quot;The
goal of this program will be
to go to the Sugar Bowl, quot;
Orgeron said." + ], + [ + "\"Everyone's nervous,\" Acting
Undersecretary of Defense
Michael W. Wynne warned in a
confidential e-mail to Air
Force Secretary James G. Roche
on July 8, 2003." + ], + [ + "Reuters - Alpharma Inc. on
Friday began\\selling a cheaper
generic version of Pfizer
Inc.'s #36;3\\billion a year
epilepsy drug Neurontin
without waiting for a\\court
ruling on Pfizer's request to
block the copycat medicine." + ], + [ + "Public opinion of the database
giant sinks to 12-year low, a
new report indicates." + ], + [ + "Opinion: Privacy hysterics
bring old whine in new bottles
to the Internet party. The
desktop search beta from this
Web search leader doesn #39;t
do anything you can #39;t do
already." + ], + [ + "It is much too easy to call
Pedro Martinez the selfish
one, to say he is walking out
on the Red Sox, his baseball
family, for the extra year of
the Mets #39; crazy money." + ], + [ + "It is impossible for young
tennis players today to know
what it was like to be Althea
Gibson and not to be able to
quot;walk in the front door,
quot; Garrison said." + ], + [ + "Senator John Kerry said today
that the war in Iraq was a
\"profound diversion\" from the
war on terror and Osama bin
Laden." + ], + [ + "For spammers, it #39;s been a
summer of love. Two newly
issued reports tracking the
circulation of unsolicited
e-mails say pornographic spam
dominated this summer, nearly
all of it originating from
Internet addresses in North
America." + ], + [ + "The Nordics fared well because
of their long-held ideals of
keeping corruption clamped
down and respect for
contracts, rule of law and
dedication to one-on-one
business relationships." + ], + [ + "Microsoft portrayed its
Longhorn decision as a
necessary winnowing to hit the
2006 timetable. The
announcement on Friday,
Microsoft executives insisted,
did not point to a setback in
software" + ], + [ + "Here #39;s an obvious word of
advice to Florida athletic
director Jeremy Foley as he
kicks off another search for
the Gators football coach: Get
Steve Spurrier on board." + ], + [ + "Don't bother with the small
stuff. Here's what really
matters to your lender." + ], + [ + "A problem in the Service Pack
2 update for Windows XP may
keep owners of AMD-based
computers from using the long-
awaited security package,
according to Microsoft." + ], + [ + "Five years ago, running a
telephone company was an
immensely profitable
proposition. Since then, those
profits have inexorably
declined, and now that decline
has taken another gut-
wrenching dip." + ], + [ + "NEW YORK - The litigious
Recording Industry Association
of America (RIAA) is involved
in another legal dispute with
a P-to-P (peer-to-peer)
technology maker, but this
time, the RIAA is on defense.
Altnet Inc. filed a lawsuit
Wednesday accusing the RIAA
and several of its partners of
infringing an Altnet patent
covering technology for
identifying requested files on
a P-to-P network." + ], + [ + "A group claiming to have
captured two Indonesian women
in Iraq has said it will
release them if Jakarta frees
Muslim cleric Abu Bakar Bashir
being held for alleged
terrorist links." + ], + [ + "Amid the stormy gloom in
Gotham, the rain-idled Yankees
last night had plenty of time
to gather in front of their
televisions and watch the Red
Sox Express roar toward them.
The national telecast might
have been enough to send a
jittery Boss Steinbrenner
searching his Bartlett's
Familiar Quotations for some
quot;Little Engine That Could
quot; metaphor." + ], + [ + "FULHAM fans would have been
singing the late Elvis #39;
hit #39;The wonder of you
#39; to their player Elvis
Hammond. If not for Frank
Lampard spoiling the party,
with his dedication to his
late grandfather." + ], + [ + "Indonesian police said
yesterday that DNA tests had
identified a suicide bomber
involved in a deadly attack
this month on the Australian
embassy in Jakarta." + ], + [ + "NEW YORK - Wal-Mart Stores
Inc.'s warning of
disappointing sales sent
stocks fluctuating Monday as
investors' concerns about a
slowing economy offset their
relief over a drop in oil
prices. October contracts
for a barrel of light crude
were quoted at \\$46.48, down
24 cents, on the New York
Mercantile Exchange..." + ], + [ + "Iraq #39;s top Shi #39;ite
cleric made a sudden return to
the country on Wednesday and
said he had a plan to end an
uprising in the quot;burning
city quot; of Najaf, where
fighting is creeping ever
closer to its holiest shrine." + ], + [ + "For two weeks before MTV
debuted U2 #39;s video for the
new single quot;Vertigo,
quot; fans had a chance to see
the band perform the song on
TV -- in an iPod commercial." + ], + [ + "A bird #39;s eye view of the
circuit at Shanghai shows what
an event Sunday #39;s Chinese
Grand Prix will be. The course
is arguably one of the best
there is, and so it should be
considering the amount of
money that has been spent on
it." + ], + [ + "KABUL, Afghanistan Aug. 22,
2004 - US soldiers sprayed a
pickup truck with bullets
after it failed to stop at a
roadblock in central
Afghanistan, killing two women
and a man and critically
wounding two other" + ], + [ + "Oct. 26, 2004 - The US-
European spacecraft Cassini-
Huygens on Tuesday made a
historic flyby of Titan,
Saturn #39;s largest moon,
passing so low as to almost
touch the fringes of its
atmosphere." + ], + [ + "Reuters - A volcano in central
Japan sent smoke and\\ash high
into the sky and spat out
molten rock as it erupted\\for
a fourth straight day on
Friday, but experts said the
peak\\appeared to be quieting
slightly." + ], + [ + "Shares of Google Inc. made
their market debut on Thursday
and quickly traded up 19
percent at \\$101.28. The Web
search company #39;s initial
public offering priced at \\$85" + ], + [ + "SYDNEY -- Prime Minister John
Howard of Australia, a key US
ally and supporter of the Iraq
war, celebrated his election
win over opposition Labor
after voters enjoying the
fruits of a strong economy
gave him another term." + ], + [ + "Reuters - Global warming is
melting\\Ecuador's cherished
mountain glaciers and could
cause several\\of them to
disappear over the next two
decades, Ecuadorean and\\French
scientists said on Wednesday." + ], + [ + "AP - Sirius Satellite Radio
signed a deal to air the men's
NCAA basketball tournament
through 2007, the latest move
made in an attempt to draw
customers through sports
programming." + ], + [ + "Rather than tell you, Dan
Kranzler chooses instead to
show you how he turned Mforma
into a worldwide publisher of
video games, ringtones and
other hot downloads for mobile
phones." + ], + [ + "UK interest rates have been
kept on hold at 4.75 following
the latest meeting of the Bank
of England #39;s rate-setting
committee." + ], + [ + "BAGHDAD, Iraq - Two rockets
hit a downtown Baghdad hotel
housing foreigners and
journalists Thursday, and
gunfire erupted in the
neighborhood across the Tigris
River from the U.S. Embassy
compound..." + ], + [ + "The Prevention of Terrorism
Act 2002 (Pota) polarised the
country, not just by the
manner in which it was pushed
through by the NDA government
through a joint session of
Parliament but by the shabby
and often biased manner in
which it was enforced." + ], + [ + "Not being part of a culture
with a highly developed
language, could limit your
thoughts, at least as far as
numbers are concerned, reveals
a new study conducted by a
psychologist at the Columbia
University in New York." + ], + [ + "CAMBRIDGE, Mass. A native of
Red Oak, Iowa, who was a
pioneer in astronomy who
proposed the quot;dirty
snowball quot; theory for the
substance of comets, has died." + ], + [ + "Resurgent oil prices paused
for breath as the United
States prepared to draw on its
emergency reserves to ease
supply strains caused by
Hurricane Ivan." + ], + [ + "(Sports Network) - The
inconsistent San Diego Padres
will try for consecutive wins
for the first time since
August 28-29 tonight, when
they begin a huge four-game
set against the Los Angeles
Dodgers at Dodger Stadium." + ], + [ + "A Portuguese-sounding version
of the virus has appeared in
the wild. Be wary of mail from
Manaus." + ], + [ + " NEW YORK (Reuters) - Top seed
Roger Federer survived a
stirring comeback from twice
champion Andre Agassi to reach
the semifinals of the U.S.
Open for the first time on
Thursday, squeezing through
6-3, 2-6, 7-5, 3-6, 6-3." + ], + [ + "President Bush, who credits
three years of tax relief
programs with helping
strengthen the slow economy,
said Saturday he would sign
into law the Working Families
Tax Relief Act to preserve tax
cuts." + ], + [ + "HEN investors consider the
bond market these days, the
low level of interest rates
should be more cause for worry
than for gratitude." + ], + [ + "Reuters - Hunters soon may be
able to sit at\\their computers
and blast away at animals on a
Texas ranch via\\the Internet,
a prospect that has state
wildlife officials up\\in arms." + ], + [ + "The Bedminster-based company
yesterday said it was pushing
into 21 new markets with the
service, AT amp;T CallVantage,
and extending an introductory
rate offer until Sept. 30. In
addition, the company is
offering in-home installation
of up to five ..." + ], + [ + "Samsung Electronics Co., Ltd.
has developed a new LCD
(liquid crystal display)
technology that builds a touch
screen into the display, a
development that could lead to
thinner and cheaper display
panels for mobile phones, the
company said Tuesday." + ], + [ + "The US military says marines
in Fallujah shot and killed an
insurgent who engaged them as
he was faking being dead, a
week after footage of a marine
killing an apparently unarmed
and wounded Iraqi caused a
stir in the region." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks rallied on Monday after
software maker PeopleSoft Inc.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=PSFT.O target=/stocks/qu
ickinfo/fullquote\">PSFT.O&l
t;/A> accepted a sweetened
\\$10.3 billion buyout by rival
Oracle Corp.'s <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=ORCL.O ta
rget=/stocks/quickinfo/fullquo
te\">ORCL.O</A> and
other big deals raised
expectations of more
takeovers." + ], + [ + "SAN FRANCISCO - What Babe Ruth
was to the first half of the
20th century and Hank Aaron
was to the second, Barry Bonds
has become for the home run
generation." + ], + [ + "Description: NPR #39;s Alex
Chadwick talks to Colin Brown,
deputy political editor for
the United Kingdom #39;s
Independent newspaper,
currently covering the British
Labour Party Conference." + ], + [ + "Birgit Fischer settled for
silver, leaving the 42-year-
old Olympian with two medals
in two days against decidedly
younger competition." + ], + [ + "The New Jersey-based Accoona
Corporation, an industry
pioneer in artificial
intelligence search
technology, announced on
Monday the launch of Accoona." + ], + [ + "Hamas vowed revenge yesterday
after an Israeli airstrike in
Gaza killed one of its senior
commanders - the latest
assassination to have weakened
the militant group." + ], + [ + "There was no mystery ... no
secret strategy ... no baited
trap that snapped shut and
changed the course of history
#39;s most lucrative non-
heavyweight fight." + ], + [ + "Notre Dame accepted an
invitation Sunday to play in
the Insight Bowl in Phoenix
against a Pac-10 team on Dec.
28. The Irish (6-5) accepted
the bid a day after losing to
Southern California" + ], + [ + "Greg Anderson has so dominated
Pro Stock this season that his
championship quest has evolved
into a pursuit of NHRA
history. By Bob Hesser, Racers
Edge Photography." + ], + [ + "Goldman Sachs Group Inc. on
Thursday said fourth-quarter
profit rose as its fixed-
income, currency and
commodities business soared
while a rebounding stock
market boosted investment
banking." + ], + [ + " NEW YORK (Reuters) - The
dollar rose on Monday in a
retracement from last week's
steep losses, but dealers said
the bias toward a weaker
greenback remained intact." + ], + [ + "Michael Powell, chairman of
the FCC, said Wednesday he was
disappointed with ABC for
airing a sexually suggestive
opening to \"Monday Night
Football.\"" + ], + [ + "The message against illegally
copying CDs for uses such as
in file-sharing over the
Internet has widely sunk in,
said the company in it #39;s
recent announcement to drop
the Copy-Control program." + ], + [ + "Former Washington football
coach Rick Neuheisel looked
forward to a return to
coaching Wednesday after being
cleared by the NCAA of
wrongdoing related to his
gambling on basketball games." + ], + [ + "Reuters - California will
become hotter and\\drier by the
end of the century, menacing
the valuable wine and\\dairy
industries, even if dramatic
steps are taken to curb\\global
warming, researchers said on
Monday." + ], + [ + "A senior member of the
Palestinian resistance group
Hamas has been released from
an Israeli prison after
completing a two-year
sentence." + ], + [ + "IBM said Monday that it won a
500 million (AUD\\$1.25
billion), seven-year services
contract to help move UK bank
Lloyds TBS from its
traditional voice
infrastructure to a converged
voice and data network." + ], + [ + "MPs have announced a new
inquiry into family courts and
whether parents are treated
fairly over issues such as
custody or contact with their
children." + ], + [ + "Canadian Press - MELBOURNE,
Australia (AP) - A 36-year-old
businesswoman was believed to
be the first woman to walk
around Australia on Friday
after striding into her
hometown of Melbourne to
complete her 16,700-kilometre
trek in 365 days." + ], + [ + "Most remaining Pakistani
prisoners held at the US
Guantanamo Bay prison camp are
freed, officials say." + ], + [ + "Space shuttle astronauts will
fly next year without the
ability to repair in orbit the
type of damage that destroyed
the Columbia vehicle in
February 2003." + ], + [ + "Moscow - Russia plans to
combine Gazprom, the world
#39;s biggest natural gas
producer, with state-owned oil
producer Rosneft, easing rules
for trading Gazprom shares and
creating a company that may
dominate the country #39;s
energy industry." + ], + [ + "AP - Rutgers basketball player
Shalicia Hurns was suspended
from the team after pleading
guilty to punching and tying
up her roommate during a
dispute over painkilling
drugs." + ], + [ + "By cutting WinFS from Longhorn
and indefinitely delaying the
storage system, Microsoft
Corp. has also again delayed
the Microsoft Business
Framework (MBF), a new Windows
programming layer that is
closely tied to WinFS." + ], + [ + "French police are
investigating an arson-caused
fire at a Jewish Social Center
that might have killed dozens
without the quick response of
firefighters." + ], + [ + "Rodney King, whose videotaped
beating led to riots in Los
Angeles in 1992, is out of
jail now and talking frankly
for the first time about the
riots, himself and the
American way of life." + ], + [ + "AFP - Radical Islamic cleric
Abu Hamza al-Masri was set to
learn Thursday whether he
would be charged under
Britain's anti-terrorism law,
thus delaying his possible
extradition to the United
States to face terrorism-
related charges." + ], + [ + "Diversified manufacturer
Honeywell International Inc.
(HON.N: Quote, Profile,
Research) posted a rise in
quarterly profit as strong
demand for aerospace equipment
and automobile components" + ], + [ + "This was the event Michael
Phelps didn't really need to
compete in if his goal was to
win eight golds. He probably
would have had a better chance
somewhere else." + ], + [ + "Samsung's new SPH-V5400 mobile
phone sports a built-in
1-inch, 1.5-gigabyte hard disk
that can store about 15 times
more data than conventional
handsets, Samsung said." + ], + [ + "Reuters - U.S. housing starts
jumped a\\larger-than-expected
6.4 percent in October to the
busiest pace\\since December as
buyers took advantage of low
mortgage rates,\\a government
report showed on Wednesday." + ], + [ + "Gabe Kapler became the first
player to leave the World
Series champion Boston Red
Sox, agreeing to a one-year
contract with the Yomiuri
Giants in Tokyo." + ], + [ + "Louisen Louis, 30, walked
Monday in the middle of a
street that resembled a small
river with brown rivulets and
waves. He wore sandals and had
a cut on one of his big toes." + ], + [ + "BALI, Indonesia - Svetlana
Kuznetsova, fresh off her
championship at the US Open,
defeated Australian qualifier
Samantha Stosur 6-4, 6-4
Thursday to reach the
quarterfinals of the Wismilak
International." + ], + [ + "The Securities and Exchange
Commission ordered mutual
funds to stop paying higher
commissions to brokers who
promote the companies' funds
and required portfolio
managers to reveal investments
in funds they supervise." + ], + [ + "A car bomb exploded outside
the main hospital in Chechny
#39;s capital, Grozny, on
Sunday, injuring 17 people in
an attack apparently targeting
members of a Chechen security
force bringing in wounded from
an earlier explosion" + ], + [ + "AP - Just like the old days in
Dallas, Emmitt Smith made life
miserable for the New York
Giants on Sunday." + ], + [ + "Sumitomo Mitsui Financial
Group (SMFG), Japans second
largest bank, today put
forward a 3,200 billion (\\$29
billion) takeover bid for
United Financial Group (UFJ),
the countrys fourth biggest
lender, in an effort to regain
initiative in its bidding" + ], + [ + "AP - Gay marriage is emerging
as a big enough issue in
several states to influence
races both for Congress and
the presidency." + ], + [ + "TAMPA, Fla. - Chris Simms
first NFL start lasted 19
plays, and it might be a while
before he plays again for the
Tampa Bay Buccaneers." + ], + [ + "Vendor says it #39;s
developing standards-based
servers in various form
factors for the telecom
market. By Darrell Dunn.
Hewlett-Packard on Thursday
unveiled plans to create a
portfolio of products and
services" + ], + [ + "Jarno Trulli made the most of
the conditions in qualifying
to claim pole ahead of Michael
Schumacher, while Fernando
finished third." + ], + [ + "More than 30 aid workers have
been airlifted to safety from
a town in Sudan #39;s troubled
Darfur region after fighting
broke out and their base was
bombed, a British charity
says." + ], + [ + " NEW YORK (Reuters) - U.S.
chain store retail sales
slipped during the
Thanksgiving holiday week, as
consumers took advantage of
discounted merchandise, a
retail report said on
Tuesday." + ], + [ + "The Mac maker says it will
replace about 28,000 batteries
in one model of PowerBook G4
and tells people to stop using
the notebook." + ], + [ + "It #39;s the mildest of mild
winters down here in the south
of Italy and, last weekend at
Bcoli, a pretty suburb by the
seaside west of Naples, the
customers of Pizzeria quot;Da
Enrico quot; were making the
most of it." + ], + [ + "By George Chamberlin , Daily
Transcript Financial
Correspondent. Concerns about
oil production leading into
the winter months sent shivers
through the stock market
Wednesday." + ], + [ + "Airbus has withdrawn a filing
that gave support for
Microsoft in an antitrust case
before the European Union
#39;s Court of First Instance,
a source close to the
situation said on Friday." + ], + [ + "WASHINGTON - A spotty job
market and stagnant paychecks
cloud this Labor Day holiday
for many workers, highlighting
the importance of pocketbook
issues in the presidential
election. \"Working harder
and enjoying it less,\" said
economist Ken Mayland,
president of ClearView
Economics, summing up the
state of working America..." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks rose on Wednesday
lifted by a merger between
retailers Kmart and Sears,
better-than-expected earnings
from Hewlett-Packard and data
showing a slight rise in core
inflation." + ], + [ + "AP - Authorities are
investigating whether bettors
at New York's top thoroughbred
tracks were properly informed
when jockeys came in
overweight at races, a source
familiar with the probe told
The Associated Press." + ], + [ + "European Commission president
Romano Prodi has unveiled
proposals to loosen the
deficit rules under the EU
Stability Pact. The loosening
was drafted by monetary
affairs commissioner Joaquin
Almunia, who stood beside the
president at the announcement." + ], + [ + "Canadian Press - MONTREAL (CP)
- A 19-year-old man charged in
a firebombing at a Jewish
elementary school pleaded
guilty Thursday to arson." + ], + [ + "Retail sales in Britain saw
the fastest growth in
September since January,
casting doubts on the view
that the economy is slowing
down, according to official
figures released Thursday." + ], + [ + " NEW YORK (Reuters) -
Interstate Bakeries Corp.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=IBC.N target=/stocks/qui
ckinfo/fullquote\">IBC.N<
/A>, maker of Hostess
Twinkies and Wonder Bread,
filed for bankruptcy on
Wednesday after struggling
with more than \\$1.3 billion
in debt and high costs." + ], + [ + "Delta Air Lines (DAL.N: Quote,
Profile, Research) on Thursday
said it reached a deal with
FedEx Express to sell eight
McDonnell Douglas MD11
aircraft and four spare
engines for delivery in 2004." + ], + [ + "Michael Owen scored his first
goal for Real Madrid in a 1-0
home victory over Dynamo Kiev
in the Champions League. The
England striker toe-poked home
Ronaldo #39;s cross in the
35th minute to join the
Russians" + ], + [ + " quot;Resuming uranium
enrichment is not in our
agenda. We are still committed
to the suspension, quot;
Foreign Ministry spokesman
Hamid Reza." + ], + [ + "Leading OPEC producer Saudi
Arabia said on Monday in
Vienna, Austria, that it had
made a renewed effort to
deflate record high world oil
prices by upping crude output
again." + ], + [ + "The U.S. military presence in
Iraq will grow to 150,000
troops by next month, the
highest level since the
invasion last year." + ], + [ + "UPDATE, SUN 9PM: More than a
million people have left their
homes in Cuba, as Hurricane
Ivan approaches. The ferocious
storm is headed that way,
after ripping through the
Cayman Islands, tearing off
roofs, flooding homes and
causing general havoc." + ], + [ + "Jason Giambi has returned to
the New York Yankees'
clubhouse but is still
clueless as to when he will be
able to play again." + ], + [ + "Seventy-five National Hockey
League players met with union
leaders yesterday to get an
update on a lockout that shows
no sign of ending." + ], + [ + "Howard, at 6-4 overall and 3-3
in the Mid-Eastern Athletic
Conference, can clinch a
winning record in the MEAC
with a win over Delaware State
on Saturday." + ], + [ + "Millions of casual US anglers
are having are larger than
appreciated impact on sea fish
stocks, scientists claim." + ], + [ + "The founders of the Pilgrim
Baxter amp; Associates money-
management firm agreed
yesterday to personally fork
over \\$160 million to settle
charges they allowed a friend
to" + ], + [ + " ATHENS (Reuters) - Top-ranked
Argentina booked their berth
in the women's hockey semi-
finals at the Athens Olympics
on Friday but defending
champions Australia now face
an obstacle course to qualify
for the medal matches." + ], + [ + "IBM Corp. Tuesday announced
plans to acquire software
vendor Systemcorp ALG for an
undisclosed amount. Systemcorp
of Montreal makes project
portfolio management software
aimed at helping companies
better manage their IT
projects." + ], + [ + "The Notre Dame message boards
are no longer discussing
whether Tyrone Willingham
should be fired. Theyre
already arguing about whether
the next coach should be Barry
Alvarez or Steve Spurrier." + ], + [ + "Forbes.com - By now you
probably know that earnings of
Section 529 college savings
accounts are free of federal
tax if used for higher
education. But taxes are only
part of the problem. What if
your investments tank? Just
ask Laurence and Margo
Williams of Alexandria, Va. In
2000 they put #36;45,000 into
the Virginia Education Savings
Trust to open accounts for
daughters Lea, now 5, and
Anne, now 3. Since then their
investment has shrunk 5 while
the average private college
tuition has climbed 18 to
#36;18,300." + ], + [ + "German Chancellor Gerhard
Schroeder said Sunday that
there was quot;no problem
quot; with Germany #39;s
support to the start of
negotiations on Turkey #39;s
entrance into EU." + ], + [ + "Coca-Cola Amatil Ltd.,
Australia #39;s biggest soft-
drink maker, offered A\\$500
million (\\$382 million) in
cash and stock for fruit
canner SPC Ardmona Ltd." + ], + [ + "US technology shares tumbled
on Friday after technology
bellwether Intel Corp.
(INTC.O: Quote, Profile,
Research) slashed its revenue
forecast, but blue chips were
only moderately lower as drug
and industrial stocks made
solid gains." + ], + [ + " WASHINGTON (Reuters) - Final
U.S. government tests on an
animal suspected of having mad
cow disease were not yet
complete, the U.S. Agriculture
Department said, with no
announcement on the results
expected on Monday." + ], + [ + "MANILA Fernando Poe Jr., the
popular actor who challenged
President Gloria Macapagal
Arroyo in the presidential
elections this year, died
early Tuesday." + ], + [ + " THOMASTOWN, Ireland (Reuters)
- World number three Ernie
Els overcame difficult weather
conditions to fire a sparkling
eight-under-par 64 and move
two shots clear after two
rounds of the WGC-American
Express Championship Friday." + ], + [ + "Lamar Odom supplemented 20
points with 13 rebounds and
Kobe Bryant added 19 points to
overcome a big night from Yao
Ming as the Los Angeles Lakers
ground out an 84-79 win over
the Rockets in Houston
Saturday." + ], + [ + "AMSTERDAM, NETHERLANDS - A
Dutch filmmaker who outraged
members of the Muslim
community by making a film
critical of the mistreatment
of women in Islamic society
was gunned down and stabbed to
death Tuesday on an Amsterdam
street." + ], + [ + "Microsoft Xbox Live traffic on
service provider networks
quadrupled following the
November 9th launch of Halo-II
-- which set entertainment
industry records by selling
2.4-million units in the US
and Canada on the first day of
availability, driving cash" + ], + [ + "Lawyers in a California class
action suit against Microsoft
will get less than half the
payout they had hoped for. A
judge in San Francisco ruled
that the attorneys will
collect only \\$112." + ], + [ + "Google Browser May Become
Reality\\\\There has been much
fanfare in the Mozilla fan
camps about the possibility of
Google using Mozilla browser
technology to produce a
GBrowser - the Google Browser.
Over the past two weeks, the
news and speculation has
escalated to the point where
even Google itself is ..." + ], + [ + "Metro, Germany's biggest
retailer, turns in weaker-
than-expected profits as sales
at its core supermarkets
division dip lower." + ], + [ + "It rained Sunday, of course,
and but another soppy, sloppy
gray day at Westside Tennis
Club did nothing to deter
Roger Federer from his
appointed rounds." + ], + [ + "Hewlett-Packard has joined
with Brocade to integrate
Brocade #39;s storage area
network switching technology
into HP Bladesystem servers to
reduce the amount of fabric
infrastructure needed in a
datacentre." + ], + [ + "Zimbabwe #39;s most persecuted
white MP began a year of hard
labour last night after
parliament voted to jail him
for shoving the Justice
Minister during a debate over
land seizures." + ], + [ + "BOSTON (CBS.MW) -- First
Command has reached a \\$12
million settlement with
federal regulators for making
misleading statements and
omitting important information
when selling mutual funds to
US military personnel." + ], + [ + "A smashing blow is being dealt
to thousands of future
pensioners by a law that has
just been brought into force
by the Federal Government." + ], + [ + "The US Senate Commerce
Committee on Wednesday
approved a measure that would
provide up to \\$1 billion to
ensure consumers can still
watch television when
broadcasters switch to new,
crisp digital signals." + ], + [ + "Amelie Mauresmo was handed a
place in the Advanta
Championships final after
Maria Sharapova withdrew from
their semi-final because of
injury." + ], + [ + "AP - An Israeli helicopter
fired two missiles in Gaza
City after nightfall
Wednesday, one at a building
in the Zeitoun neighborhood,
witnesses said, setting a
fire." + ], + [ + "Reuters - Internet stocks
are\\as volatile as ever, with
growth-starved investors
flocking to\\the sector in the
hope they've bought shares in
the next online\\blue chip." + ], + [ + "The federal government, banks
and aircraft lenders are
putting the clamps on
airlines, particularly those
operating under bankruptcy
protection." + ], + [ + "EURO DISNEY, the financially
crippled French theme park
operator, has admitted that
its annual losses more than
doubled last financial year as
it was hit by a surge in
costs." + ], + [ + " EAST RUTHERFORD, New Jersey
(Sports Network) - Retired NBA
center and seven-time All-
Star Alonzo Mourning is going
to give his playing career
one more shot." + ], + [ + "NASA has released an inventory
of the scientific devices to
be put on board the Mars
Science Laboratory rover
scheduled to land on the
surface of Mars in 2009, NASAs
news release reads." + ], + [ + "The U.S. Congress needs to
invest more in the U.S.
education system and do more
to encourage broadband
adoption, the chief executive
of Cisco said Wednesday.<p&
gt;ADVERTISEMENT</p><
p><img src=\"http://ad.do
ubleclick.net/ad/idg.us.ifw.ge
neral/sbcspotrssfeed;sz=1x1;or
d=200301151450?\" width=\"1\"
height=\"1\"
border=\"0\"/><a href=\"htt
p://ad.doubleclick.net/clk;922
8975;9651165;a?http://www.info
world.com/spotlights/sbc/main.
html?lpid0103035400730000idlp\"
>SBC Case Study: Crate Ba
rrel</a><br/>What
sold them on improving their
network? A system that could
cut management costs from the
get-go. Find out
more.</p>" + ], + [ + "The Philippines put the toll
at more than 1,000 dead or
missing in four storms in two
weeks but, even with a break
in the weather on Saturday" + ], + [ + "Reuters - Four explosions were
reported at petrol\\stations in
the Madrid area on Friday,
Spanish radio stations\\said,
following a phone warning in
the name of the armed
Basque\\separatist group ETA to
a Basque newspaper." + ], + [ + "Thirty-two countries and
regions will participate the
Fifth China International
Aviation and Aerospace
Exhibition, opening Nov. 1 in
Zhuhai, a city in south China
#39;s Guangdong Province." + ], + [ + "Jordan have confirmed that
Timo Glock will replace
Giorgio Pantano for this
weekend #39;s Chinese GP as
the team has terminated its
contract with Pantano." + ], + [ + "WEST PALM BEACH, Fla. -
Hurricane Jeanne got stronger,
bigger and faster as it
battered the Bahamas and bore
down on Florida Saturday,
sending huge waves crashing
onto beaches and forcing
thousands into shelters just
weeks after Frances ravaged
this area..." + ], + [ + "p2pnet.net News:- Virgin
Electronics has joined the mp3
race with a \\$250, five gig
player which also handles
Microsoft #39;s WMA format." + ], + [ + "WASHINGTON The idea of a no-
bid contract for maintaining
airport security equipment has
turned into a non-starter for
the Transportation Security
Administration." + ], + [ + "Eyetech (EYET:Nasdaq - news -
research) did not open for
trading Friday because a Food
and Drug Administration
advisory committee is meeting
to review the small New York-
based biotech #39;s
experimental eye disease drug." + ], + [ + "The continuing heartache of
Wake Forest #39;s ACC football
season was best described by
fifth-ranked Florida State
coach Bobby Bowden, after his
Seminoles had edged the
Deacons 20-17 Saturday at
Groves Stadium." + ], + [ + "On September 13, 2001, most
Americans were still reeling
from the shock of the
terrorist attacks on New York
and the Pentagon two days
before." + ], + [ + "Microsoft has suspended the
beta testing of the next
version of its MSN Messenger
client because of a potential
security problem, a company
spokeswoman said Wednesday." + ], + [ + "AP - Business software maker
Oracle Corp. attacked the
credibility and motives of
PeopleSoft Inc.'s board of
directors Monday, hoping to
rally investor support as the
17-month takeover battle
between the bitter business
software rivals nears a
climactic showdown." + ], + [ + "NEW YORK - Elena Dementieva
shook off a subpar serve that
produced 15 double-faults, an
aching left thigh and an upset
stomach to advance to the
semifinals at the U.S. Open
with a 4-6, 6-4, 7-6 (1)
victory Tuesday over Amelie
Mauresmo..." + ], + [ + "THE glory days have returned
to White Hart Lane. When Spurs
new first-team coach Martin
Jol promised a return to the
traditions of the 1960s,
nobody could have believed he
was so determined to act so
quickly and so literally." + ], + [ + "A new worm has been discovered
in the wild that #39;s not
just settling for invading
users #39; PCs--it wants to
invade their homes too." + ], + [ + "Domestic air travelers could
be surfing the Web by 2006
with government-approved
technology that allows people
access to high-speed Internet
connections while they fly." + ], + [ + "GENEVA: Cross-border
investment is set to bounce in
2004 after three years of deep
decline, reflecting a stronger
world economy and more
international merger activity,
the United Nations (UN) said
overnight." + ], + [ + "Researchers have for the first
time established the existence
of odd-parity superconductors,
materials that can carry
electric current without any
resistance." + ], + [ + "Chewing gum giant Wm. Wrigley
Jr. Co. on Thursday said it
plans to phase out production
of its Eclipse breath strips
at a plant in Phoenix, Arizona
and shift manufacturing to
Poznan, Poland." + ], + [ + "Prime Minister Dr Manmohan
Singh inaugurated a research
centre in the Capital on
Thursday to mark 400 years of
compilation of Sikh holy book
the Guru Granth Sahib." + ], + [ + "com September 16, 2004, 7:58
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." + ], + [ + "BRUSSELS, Belgium (AP) --
European antitrust regulators
said Monday they have extended
their review of a deal between
Microsoft Corp. (MSFT) and
Time Warner Inc..." + ], + [ + "AP - When Paula Radcliffe
dropped out of the Olympic
marathon miles from the
finish, she sobbed
uncontrollably. Margaret Okayo
knew the feeling. Okayo pulled
out of the marathon at the
15th mile with a left leg
injury, and she cried, too.
When she watched Radcliffe
quit, Okayo thought, \"Let's
cry together.\"" + ], + [ + "Tightness in the labour market
notwithstanding, the prospects
for hiring in the third
quarter are down from the
second quarter, according to
the new Manpower Employment
Outlook Survey." + ], + [ + "THE re-election of British
Prime Minister Tony Blair
would be seen as an
endorsement of the military
action in Iraq, Prime Minister
John Howard said today." + ], + [ + "Fans who can't get enough of
\"The Apprentice\" can visit a
new companion Web site each
week and watch an extra 40
minutes of video not broadcast
on the Thursday
show.<br><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</b></font>" + ], + [ + " ATLANTA (Sports Network) -
The Atlanta Hawks signed free
agent Kevin Willis on
Wednesday, nearly a decade
after the veteran big man
ended an 11- year stint with
the team." + ], + [ + "An adult Web site publisher is
suing Google, saying the
search engine company made it
easier for users to see the
site #39;s copyrighted nude
photographs without paying or
gaining access through the
proper channels." + ], + [ + "When his right-front tire went
flying off early in the Ford
400, the final race of the
NASCAR Nextel Cup Series
season, Kurt Busch, it seemed,
was destined to spend his
offseason" + ], + [ + "A Washington-based public
opinion firm has released the
results of an election day
survey of Nevada voters
showing 81 support for the
issuance of paper receipts
when votes are cast
electronically." + ], + [ + "NAPSTER creator SHAWN FANNING
has revealed his plans for a
new licensed file-sharing
service with an almost
unlimited selection of tracks." + ], + [ + " NEW YORK (Reuters) - The
dollar rose on Friday, after a
U.S. report showed consumer
prices in line with
expections, reminding
investors that the Federal
Reserve was likely to
continue raising interest
rates, analysts said." + ], + [ + "Brandon Backe and Woody
Williams pitched well last
night even though neither
earned a win. But their
outings will show up in the
record books." + ], + [ + "President George W. Bush
pledged Friday to spend some
of the political capital from
his re-election trying to
secure a lasting Middle East
peace, and he envisioned the
establishment" + ], + [ + "The Football Association today
decided not to charge David
Beckham with bringing the game
into disrepute. The FA made
the surprise announcement
after their compliance unit
ruled" + ], + [ + "Last year some election
watchers made a bold
prediction that this
presidential election would
set a record: the first half
billion dollar campaign in
hard money alone." + ], + [ + "It #39;s one more blow to
patients who suffer from
arthritis. Pfizer, the maker
of Celebrex, says it #39;s
painkiller poses an increased
risk of heart attacks to
patients using the drugs." + ], + [ + "NEW DELHI - A bomb exploded
during an Independence Day
parade in India's remote
northeast on Sunday, killing
at least 15 people, officials
said, just an hour after Prime
Minister Manmohan Singh
pledged to fight terrorism.
The outlawed United Liberation
Front of Asom was suspected of
being behind the attack in
Assam state and a second one
later in the area, said Assam
Inspector General of Police
Khagen Sharma..." + ], + [ + "Two separate studies by U.S.
researchers find that super
drug-resistant strains of
tuberculosis are at the
tipping point of a global
epidemic, and only small
changes could help them spread
quickly." + ], + [ + " CRANS-SUR-SIERRE, Switzerland
(Reuters) - World number
three Ernie Els says he feels
a failure after narrowly
missing out on three of the
year's four major
championships." + ], + [ + "A UN envoy to Sudan will visit
Darfur tomorrow to check on
the government #39;s claim
that some 70,000 people
displaced by conflict there
have voluntarily returned to
their homes, a spokesman said." + ], + [ + "Dell cut prices on some
servers and PCs by as much as
22 percent because it #39;s
paying less for parts. The
company will pass the savings
on components such as memory
and liquid crystal displays" + ], + [ + "AP - Most of the presidential
election provisional ballots
rejected so far in Ohio came
from people who were not even
registered to vote, election
officials said after spending
nearly two weeks poring over
thousands of disputed votes." + ], + [ + "Striker Bonaventure Kalou
netted twice to send AJ
Auxerre through to the first
knockout round of the UEFA Cup
at the expense of Rangers on
Wednesday." + ], + [ + "AP - Rival inmates fought each
other with knives and sticks
Wednesday at a San Salvador
prison, leaving at least 31
people dead and two dozen
injured, officials said." + ], + [ + "WASHINGTON: The European-
American Cassini-Huygens space
probe has detected traces of
ice flowing on the surface of
Saturn #39;s largest moon,
Titan, suggesting the
existence of an ice volcano,
NASA said Tuesday." + ], + [ + "The economic growth rate in
the July-September period was
revised slightly downward from
an already weak preliminary
report, the government said
Wednesday." + ], + [ + "All ISS systems continue to
function nominally, except
those noted previously or
below. Day 7 of joint
Exp.9/Exp.10 operations and
last full day before 8S
undocking." + ], + [ + "BAGHDAD - Two Egyptian
employees of a mobile phone
company were seized when
gunmen stormed into their
Baghdad office, the latest in
a series of kidnappings in the
country." + ], + [ + "BRISBANE, Australia - The body
of a whale resembling a giant
dolphin that washed up on an
eastern Australian beach has
intrigued local scientists,
who agreed Wednesday that it
is rare but are not sure just
how rare." + ], + [ + " quot;Magic can happen. quot;
Sirius Satellite Radio
(nasdaq: SIRI - news - people
) may have signed Howard Stern
and the men #39;s NCAA
basketball tournaments, but XM
Satellite Radio (nasdaq: XMSR
- news - people ) has its
sights on your cell phone." + ], + [ + "Trick-or-treaters can expect
an early Halloween treat on
Wednesday night, when a total
lunar eclipse makes the moon
look like a glowing pumpkin." + ], + [ + "THIS weekend sees the
quot;other quot; showdown
between New York and New
England as the Jets and
Patriots clash in a battle of
the unbeaten teams." + ], + [ + "Sifting through millions of
documents to locate a valuable
few is tedious enough, but
what happens when those files
are scattered across different
repositories?" + ], + [ + "President Bush aims to
highlight American drug-
fighting aid in Colombia and
boost a conservative Latin
American leader with a stop in
the Andean nation where
thousands of security forces
are deployed to safeguard his
brief stay." + ], + [ + "Dubai - Former Palestinian
security minister Mohammed
Dahlan said on Monday that a
quot;gang of mercenaries quot;
known to the Palestinian
police were behind the
shooting that resulted in two
deaths in a mourning tent for
Yasser Arafat in Gaza." + ], + [ + "A drug company executive who
spoke out in support of
Montgomery County's proposal
to import drugs from Canada
and similar legislation before
Congress said that his company
has launched an investigation
into his political activities." + ], + [ + "Nicolas Anelka is fit for
Manchester City #39;s
Premiership encounter against
Tottenham at Eastlands, but
the 13million striker will
have to be content with a
place on the bench." + ], + [ + " PITTSBURGH (Reuters) - Ben
Roethlisberger passed for 183
yards and two touchdowns,
Hines Ward scored twice and
the Pittsburgh Steelers
rolled to a convincing 27-3
victory over Philadelphia on
Sunday for their second
straight win against an
undefeated opponent." + ], + [ + " ATHENS (Reuters) - The U.S.
men's basketball team got
their first comfortable win
at the Olympic basketball
tournament Monday, routing
winless Angola 89-53 in their
final preliminary round game." + ], + [ + "A Frenchman working for Thales
SA, Europe #39;s biggest maker
of military electronics, was
shot dead while driving home
at night in the Saudi Arabian
city of Jeddah." + ], + [ + "Often, the older a pitcher
becomes, the less effective he
is on the mound. Roger Clemens
apparently didn #39;t get that
memo. On Tuesday, the 42-year-
old Clemens won an
unprecedented" + ], + [ + "NASA #39;s Mars rovers have
uncovered more tantalizing
evidence of a watery past on
the Red Planet, scientists
said Wednesday. And the
rovers, Spirit and
Opportunity, are continuing to
do their jobs months after
they were expected to ..." + ], + [ + "SYDNEY (AFP) - Australia #39;s
commodity exports are forecast
to increase by 15 percent to a
record 95 billion dollars (71
million US), the government
#39;s key economic forecaster
said." + ], + [ + "Google won a major legal
victory when a federal judge
ruled that the search engines
advertising policy does not
violate federal trademark
laws." + ], + [ + "Intel Chief Technology Officer
Pat Gelsinger said on
Thursday, Sept. 9, that the
Internet needed to be upgraded
in order to deal with problems
that will become real issues
soon." + ], + [ + "China will take tough measures
this winter to improve the
country #39;s coal mine safety
and prevent accidents. State
Councilor Hua Jianmin said
Thursday the industry should
take" + ], + [ + "AT amp;T Corp. on Thursday
said it is reducing one fifth
of its workforce this year and
will record a non-cash charge
of approximately \\$11." + ], + [ + "BAGHDAD (Iraq): As the
intensity of skirmishes
swelled on the soils of Iraq,
dozens of people were put to
death with toxic shots by the
US helicopter gunship, which
targeted the civilians,
milling around a burning
American vehicle in a Baghdad
street on" + ], + [ + " LONDON (Reuters) - Television
junkies of the world, get
ready for \"Friends,\" \"Big
Brother\" and \"The Simpsons\" to
phone home." + ], + [ + "A rift appeared within Canada
#39;s music industry yesterday
as prominent artists called on
the CRTC to embrace satellite
radio and the industry warned
of lost revenue and job
losses." + ], + [ + "Reuters - A key Iranian
nuclear facility which
the\\U.N.'s nuclear watchdog
has urged Tehran to shut down
is\\nearing completion, a
senior Iranian nuclear
official said on\\Sunday." + ], + [ + "Spain's Football Federation
launches an investigation into
racist comments made by
national coach Luis Aragones." + ], + [ + "Bricks and plaster blew inward
from the wall, as the windows
all shattered and I fell to
the floorwhether from the
shock wave, or just fright, it
wasn #39;t clear." + ], + [ + "Surfersvillage Global Surf
News, 13 September 2004: - -
Hurricane Ivan, one of the
most powerful storms to ever
hit the Caribbean, killed at
least 16 people in Jamaica,
where it wrecked houses and
washed away roads on Saturday,
but appears to have spared" + ], + [ + "I #39;M FEELING a little bit
better about the hundreds of
junk e-mails I get every day
now that I #39;ve read that
someone else has much bigger
e-mail troubles." + ], + [ + "NEW DELHI: India and Pakistan
agreed on Monday to step up
cooperation in the energy
sector, which could lead to
Pakistan importing large
amounts of diesel fuel from
its neighbour, according to
Pakistani Foreign Minister
Khurshid Mehmood Kasuri." + ], + [ + "LONDON, England -- A US
scientist is reported to have
observed a surprising jump in
the amount of carbon dioxide,
the main greenhouse gas." + ], + [ + "Microsoft's antispam Sender ID
technology continues to get
the cold shoulder. Now AOL
adds its voice to a growing
chorus of businesses and
organizations shunning the
proprietary e-mail
authentication system." + ], + [ + "PSV Eindhoven faces Arsenal at
Highbury tomorrow night on the
back of a free-scoring start
to the season. Despite losing
Mateja Kezman to Chelsea in
the summer, the Dutch side has
scored 12 goals in the first" + ], + [ + "Through the World Community
Grid, your computer could help
address the world's health and
social problems." + ], + [ + "Zimbabwe #39;s ruling Zanu-PF
old guard has emerged on top
after a bitter power struggle
in the deeply divided party
during its five-yearly
congress, which ended
yesterday." + ], + [ + "PLAYER OF THE GAME: Playing
with a broken nose, Seattle
point guard Sue Bird set a
WNBA playoff record for
assists with 14, also pumping
in 10 points as the Storm
claimed the Western Conference
title last night." + ], + [ + "Reuters - Thousands of
demonstrators pressing
to\\install Ukraine's
opposition leader as president
after a\\disputed election
launched fresh street rallies
in the capital\\for the third
day Wednesday." + ], + [ + "Michael Jackson wishes he had
fought previous child
molestation claims instead of
trying to \"buy peace\", his
lawyer says." + ], + [ + "North Korea says it will not
abandon its weapons programme
after the South admitted
nuclear activities." + ], + [ + "While there is growing
attention to ongoing genocide
in Darfur, this has not
translated into either a
meaningful international
response or an accurate
rendering of the scale and
evident course of the
catastrophe." + ], + [ + "A planned component for
Microsoft #39;s next version
of Windows is causing
consternation among antivirus
experts, who say that the new
module, a scripting platform
called Microsoft Shell, could
give birth to a whole new
generation of viruses and
remotely" + ], + [ + "THE prosecution on terrorism
charges of extremist Islamic
cleric and accused Jemaah
Islamiah leader Abu Bakar
Bashir will rely heavily on
the potentially tainted
testimony of at least two
convicted Bali bombers, his
lawyers have said." + ], + [ + "SAN JOSE, California Yahoo
will likely have a tough time
getting American courts to
intervene in a dispute over
the sale of Nazi memorabilia
in France after a US appeals
court ruling." + ], + [ + "TORONTO (CP) - Glamis Gold of
Reno, Nev., is planning a
takeover bid for Goldcorp Inc.
of Toronto - but only if
Goldcorp drops its
\\$2.4-billion-Cdn offer for
another Canadian firm, made in
early December." + ], + [ + "Clashes between US troops and
Sadr militiamen escalated
Thursday, as the US surrounded
Najaf for possible siege." + ], + [ + "eBay Style director Constance
White joins Post fashion
editor Robin Givhan and host
Janet Bennett to discuss how
to find trends and bargains
and pull together a wardrobe
online." + ], + [ + "This week will see the release
of October new and existing
home sales, a measure of
strength in the housing
industry. But the short
holiday week will also leave
investors looking ahead to the
holiday travel season." + ], + [ + "Frankfurt - World Cup winners
Brazil were on Monday drawn to
meet European champions
Greece, Gold Cup winners
Mexico and Asian champions
Japan at the 2005
Confederations Cup." + ], + [ + "Third baseman Vinny Castilla
said he fits fine with the
Colorado youth movement, even
though he #39;ll turn 38 next
season and the Rockies are
coming off the second-worst" + ], + [ + "With a sudden shudder, the
ground collapsed and the pipe
pushed upward, buckling into a
humped shape as Cornell
University scientists produced
the first simulated earthquake" + ], + [ + "AFP - A battle group of
British troops rolled out of
southern Iraq on a US-
requested mission to deadlier
areas near Baghdad, in a major
political gamble for British
Prime Minister Tony Blair." + ], + [ + "(Sports Network) - Two of the
top teams in the American
League tangle in a possible
American League Division
Series preview tonight, as the
West-leading Oakland Athletics
host the wild card-leading
Boston Red Sox for the first
of a three-game set at the" + ], + [ + "over half the children in the
world - suffer extreme
deprivation because of war,
HIV/AIDS or poverty, according
to a report released yesterday
by the United Nations Children
#39;s Fund." + ], + [ + "Microsoft (Quote, Chart) has
fired another salvo in its
ongoing spam battle, this time
against porn peddlers who don
#39;t keep their smut inside
the digital equivalent of a
quot;Brown Paper Wrapper." + ], + [ + " BETHESDA, Md. (Reuters) - The
use of some antidepressant
drugs appears linked to an
increase in suicidal behavior
in some children and teen-
agers, a U.S. advisory panel
concluded on Tuesday." + ], + [ + " SEATTLE (Reuters) - The next
version of the Windows
operating system, Microsoft
Corp.'s <A HREF=\"http://www
.reuters.co.uk/financeQuoteLoo
kup.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
flagship product, will ship
in 2006, the world's largest
software maker said on
Friday." + ], + [ + "Reuters - Philippine rescue
teams\\evacuated thousands of
people from the worst flooding
in the\\central Luzon region
since the 1970s as hungry
victims hunted\\rats and birds
for food." + ], + [ + "Inverness Caledonian Thistle
appointed Craig Brewster as
its new manager-player
Thursday although he #39;s
unable to play for the team
until January." + ], + [ + "The Afghan president expresses
deep concern after a bomb
attack which left at least
seven people dead." + ], + [ + " NEW YORK (Reuters) - U.S.
technology stocks opened lower
on Thursday after a sales
warning from Applied Materials
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AMAT.O target=/sto
cks/quickinfo/fullquote\">AM
AT.O</A>, while weekly
jobless claims data met Wall
Street's expectations,
leaving the Dow and S P 500
market measures little
changed." + ], + [ + "ATHENS, Greece -- Alan Shearer
converted an 87th-minute
penalty to give Newcastle a
1-0 win over Panionios in
their UEFA Cup Group D match." + ], + [ + "Fossil remains of the oldest
and smallest known ancestor of
Tyrannosaurus rex, the world
#39;s favorite ferocious
dinosaur, have been discovered
in China with evidence that
its body was cloaked in downy
quot;protofeathers." + ], + [ + "Derek Jeter turned a season
that started with a terrible
slump into one of the best in
his accomplished 10-year
career. quot;I don #39;t
think there is any question,
quot; the New York Yankees
manager said." + ], + [ + "Gardez (Afghanistan), Sept. 16
(Reuters): Afghan President
Hamid Karzai escaped an
assassination bid today when a
rocket was fired at his US
military helicopter as it was
landing in the southeastern
town of Gardez." + ], + [ + "The Jets came up with four
turnovers by Dolphins
quarterback Jay Fiedler in the
second half, including an
interception returned 66 yards
for a touchdown." + ], + [ + "China's Guo Jingjing easily
won the women's 3-meter
springboard last night, and Wu
Minxia made it a 1-2 finish
for the world's diving
superpower, taking the silver." + ], + [ + "GREEN BAY, Wisconsin (Ticker)
-- Brett Favre will be hoping
his 200th consecutive start
turns out better than his last
two have against the St." + ], + [ + "People fishing for sport are
doing far more damage to US
marine fish stocks than anyone
thought, accounting for nearly
a quarter of the" + ], + [ + "When an NFL team opens with a
prolonged winning streak,
former Miami Dolphins coach
Don Shula and his players from
the 17-0 team of 1972 root
unabashedly for the next
opponent." + ], + [ + "MIANNE Bagger, the transsexual
golfer who prompted a change
in the rules to allow her to
compete on the professional
circuit, made history
yesterday by qualifying to
play full-time on the Ladies
European Tour." + ], + [ + "Great Britain #39;s gold medal
tally now stands at five after
Leslie Law was handed the
individual three day eventing
title - in a courtroom." + ], + [ + "This particular index is
produced by the University of
Michigan Business School, in
partnership with the American
Society for Quality and CFI
Group, and is supported in
part by ForeSee Results" + ], + [ + "CHICAGO : Interstate Bakeries
Corp., the maker of popular,
old-style snacks Twinkies and
Hostess Cakes, filed for
bankruptcy, citing rising
costs and falling sales." + ], + [ + "Delta Air Lines (DAL:NYSE -
commentary - research) will
cut employees and benefits but
give a bigger-than-expected
role to Song, its low-cost
unit, in a widely anticipated
but still unannounced
overhaul, TheStreet.com has
learned." + ], + [ + "By byron kho. A consortium of
movie and record companies
joined forces on Friday to
request that the US Supreme
Court take another look at
peer-to-peer file-sharing
programs." + ], + [ + "DUBLIN -- Prime Minister
Bertie Ahern urged Irish
Republican Army commanders
yesterday to meet what he
acknowledged was ''a heavy
burden quot;: disarming and
disbanding their organization
in support of Northern
Ireland's 1998 peace accord." + ], + [ + "Mayor Tom Menino must be
proud. His Boston Red Sox just
won their first World Series
in 86 years and his Hyde Park
Blue Stars yesterday clinched
their first Super Bowl berth
in 32 years, defeating
O'Bryant, 14-0. Who would have
thought?" + ], + [ + "While reproductive planning
and women #39;s equality have
improved substantially over
the past decade, says a United
Nations report, world
population will increase from
6.4 billion today to 8.9
billion by 2050, with the 50
poorest countries tripling in" + ], + [ + "Instead of the skinny black
line, showing a hurricane
#39;s forecast track,
forecasters have drafted a
couple of alternative graphics
to depict where the storms
might go -- and they want your
opinion." + ], + [ + "South Korea have appealed to
sport #39;s supreme legal body
in an attempt to award Yang
Tae-young the Olympic
gymnastics all-round gold
medal after a scoring error
robbed him of the title in
Athens." + ], + [ + "BERLIN - Volkswagen AG #39;s
announcement this week that it
has forged a new partnership
deal with Malaysian carmaker
Proton comes as a strong euro
and Europe #39;s weak economic
performance triggers a fresh
wave of German investment in
Asia." + ], + [ + "AP - Johan Santana had an
early lead and was well on his
way to his 10th straight win
when the rain started to fall." + ], + [ + "ATHENS-In one of the biggest
shocks in Olympic judo
history, defending champion
Kosei Inoue was defeated by
Dutchman Elco van der Geest in
the men #39;s 100-kilogram
category Thursday." + ], + [ + "Consumers in Dublin pay more
for basic goods and services
that people elsewhere in the
country, according to figures
released today by the Central
Statistics Office." + ], + [ + "LIBERTY Media #39;s move last
week to grab up to 17.1 per
cent of News Corporation
voting stock has prompted the
launch of a defensive
shareholder rights plan." + ], + [ + "NBC is adding a 5-second delay
to its Nascar telecasts after
Dale Earnhardt Jr. used a
vulgarity during a postrace
interview last weekend." + ], + [ + "LONDON - Wild capuchin monkeys
can understand cause and
effect well enough to use
rocks to dig for food,
scientists have found.
Capuchin monkeys often use
tools and solve problems in
captivity and sometimes" + ], + [ + "San Francisco Giants
outfielder Barry Bonds, who
became the third player in
Major League Baseball history
to hit 700 career home runs,
won the National League Most
Valuable Player Award" + ], + [ + "The blue-chip Hang Seng Index
rose 171.88 points, or 1.22
percent, to 14,066.91. On
Friday, the index had slipped
31.58 points, or 0.2 percent." + ], + [ + "BAR's Anthony Davidson and
Jenson Button set the pace at
the first Chinese Grand Prix." + ], + [ + "Shares plunge after company
says its vein graft treatment
failed to show benefit in
late-stage test. CHICAGO
(Reuters) - Biotechnology
company Corgentech Inc." + ], + [ + "WASHINGTON - Contradicting the
main argument for a war that
has cost more than 1,000
American lives, the top U.S.
arms inspector reported
Wednesday that he found no
evidence that Iraq produced
any weapons of mass
destruction after 1991..." + ], + [ + "The key to hidden treasure
lies in your handheld GPS
unit. GPS-based \"geocaching\"
is a high-tech sport being
played by thousands of people
across the globe." + ], + [ + "AFP - Style mavens will be
scanning the catwalks in Paris
this week for next spring's
must-have handbag, as a
sweeping exhibition at the
French capital's fashion and
textile museum reveals the bag
in all its forms." + ], + [ + "Canadian Press - SAINT-
QUENTIN, N.B. (CP) - A major
highway in northern New
Brunswick remained closed to
almost all traffic Monday, as
local residents protested
planned health care cuts." + ], + [ + "The U.S. information tech
sector lost 403,300 jobs
between March 2001 and April
2004, and the market for tech
workers remains bleak,
according to a new report." + ], + [ + " NAJAF, Iraq (Reuters) - A
radical Iraqi cleric leading a
Shi'ite uprising agreed on
Wednesday to disarm his
militia and leave one of the
country's holiest Islamic
shrines after warnings of an
onslaught by government
forces." + ], + [ + "Saudi security forces have
killed a wanted militant near
the scene of a deadly shootout
Thursday. Officials say the
militant was killed in a
gunbattle Friday in the
northern town of Buraida,
hours after one" + ], + [ + "Portsmouth chairman Milan
Mandaric said on Tuesday that
Harry Redknapp, who resigned
as manager last week, was
innocent of any wrong-doing
over agent or transfer fees." + ], + [ + "This record is for all the
little guys, for all the
players who have to leg out
every hit instead of taking a
relaxing trot around the
bases, for all the batters
whose muscles aren #39;t" + ], + [ + "Two South Africans acquitted
by a Zimbabwean court of
charges related to the alleged
coup plot in Equatorial Guinea
are to be questioned today by
the South African authorities." + ], + [ + "Charlie Hodgson #39;s record-
equalling performance against
South Africa was praised by
coach Andy Robinson after the
Sale flyhalf scored 27 points
in England #39;s 32-16 victory
here at Twickenham on
Saturday." + ], + [ + "com September 30, 2004, 11:11
AM PT. SanDisk announced
Thursday increased capacities
for several different flash
memory cards. The Sunnyvale,
Calif." + ], + [ + "MOSCOW (CP) - Russia mourned
89 victims of a double air
disaster today as debate
intensified over whether the
two passenger liners could
have plunged almost
simultaneously from the sky by
accident." + ], + [ + "US blue-chip stocks rose
slightly on Friday as
government data showed better-
than-expected demand in August
for durable goods other than
transportation equipment, but
climbing oil prices limited
gains." + ], + [ + "BASEBALL Atlanta (NL):
Optioned P Roman Colon to
Greenville (Southern);
recalled OF Dewayne Wise from
Richmond (IL). Boston (AL):
Purchased C Sandy Martinez
from Cleveland (AL) and
assigned him to Pawtucket
(IL). Cleveland (AL): Recalled
OF Ryan Ludwick from Buffalo
(IL). Chicago (NL): Acquired
OF Ben Grieve from Milwaukee
(NL) for player to be named
and cash; acquired C Mike ..." + ], + [ + "Australia #39;s prime minister
says a body found in Fallujah
is likely that of kidnapped
aid worker Margaret Hassan.
John Howard told Parliament a
videotape of an Iraqi
terrorist group executing a
Western woman appears to have
been genuine." + ], + [ + "roundup Plus: Tech firms rally
against copyright bill...Apple
.Mac customers suffer e-mail
glitches...Alvarion expands
wireless broadband in China." + ], + [ + "BRONX, New York (Ticker) --
Kelvim Escobar was the latest
Anaheim Angels #39; pitcher to
subdue the New York Yankees.
Escobar pitched seven strong
innings and Bengie Molina tied
a career-high with four hits,
including" + ], + [ + "Business software maker
PeopleSoft Inc. said Monday
that it expects third-quarter
revenue to range between \\$680
million and \\$695 million,
above average Wall Street
estimates of \\$651." + ], + [ + "Sep 08 - Vijay Singh revelled
in his status as the new world
number one after winning the
Deutsche Bank Championship by
three shots in Boston on
Monday." + ], + [ + "Reuters - Enron Corp. ,
desperate to\\meet profit
targets, \"parked\" unwanted
power generating barges\\at
Merrill Lynch in a sham sale
designed to be reversed,
a\\prosecutor said on Tuesday
in the first criminal trial
of\\former executives at the
fallen energy company." + ], + [ + " NEW YORK (Reuters) - The
dollar rebounded on Monday
after a heavy selloff last
week, but analysts were
uncertain if the rally could
hold as the drumbeat of
expectation began for to the
December U.S. jobs report due
Friday." + ], + [ + "AP - Their first debate less
than a week away, President
Bush and Democrat John Kerry
kept their public schedules
clear on Saturday and began to
focus on their prime-time
showdown." + ], + [ + "Many people in golf are asking
that today. He certainly wasn
#39;t A-list and he wasn #39;t
Larry Nelson either. But you
couldn #39;t find a more solid
guy to lead the United States
into Ireland for the 2006
Ryder Cup Matches." + ], + [ + "Coles Myer Ltd. Australia
#39;s biggest retailer,
increased second-half profit
by 26 percent after opening
fuel and convenience stores,
selling more-profitable
groceries and cutting costs." + ], + [ + "MOSCOW: Us oil major
ConocoPhillips is seeking to
buy up to 25 in Russian oil
giant Lukoil to add billions
of barrels of reserves to its
books, an industry source
familiar with the matter said
on Friday." + ], + [ + "Australian Stuart Appleby, who
was the joint second-round
leader, returned a two-over 74
to drop to third at three-
under while American Chris
DiMarco moved into fourth with
a round of 69." + ], + [ + "PARIS Getting to the bottom of
what killed Yassar Arafat
could shape up to be an ugly
family tug-of-war. Arafat
#39;s half-brother and nephew
want copies of Arafat #39;s
medical records from the
suburban Paris hospital" + ], + [ + "Red Hat is acquiring security
and authentication tools from
Netscape Security Solutions to
bolster its software arsenal.
Red Hat #39;s CEO and chairman
Matthew Szulik spoke about the
future strategy of the Linux
supplier." + ], + [ + "With a doubleheader sweep of
the Minnesota Twins, the New
York Yankees moved to the
verge of clinching their
seventh straight AL East
title." + ], + [ + "Global Web portal Yahoo! Inc.
Wednesday night made available
a beta version of a new search
service for videos. Called
Yahoo! Video Search, the
search engine crawls the Web
for different types of media
files" + ], + [ + "Interactive posters at 25
underground stations are
helping Londoners travel
safely over Christmas." + ], + [ + "Athens, Greece (Sports
Network) - The first official
track event took place this
morning and Italy #39;s Ivano
Brugnetti won the men #39;s
20km walk at the Summer
Olympics in Athens." + ], + [ + " THE HAGUE (Reuters) - Former
Yugoslav President Slobodan
Milosevic condemned his war
crimes trial as a \"pure farce\"
on Wednesday in a defiant
finish to his opening defense
statement against charges of
ethnic cleansing in the
Balkans." + ], + [ + "US Airways Group (otc: UAIRQ -
news - people ) on Thursday
said it #39;ll seek a court
injunction to prohibit a
strike by disaffected unions." + ], + [ + "Shares in Unilever fall after
the Anglo-Dutch consumer goods
giant issued a surprise
profits warning." + ], + [ + "SAN FRANCISCO (CBS.MW) - The
Canadian government will sell
its 19 percent stake in Petro-
Canada for \\$2.49 billion,
according to the final
prospectus filed with the US
Securities and Exchange
Commission Thursday." + ], + [ + "Champions Arsenal opened a
five-point lead at the top of
the Premier League after a 4-0
thrashing of Charlton Athletic
at Highbury Saturday." + ], + [ + "The Redskins and Browns have
traded field goals and are
tied, 3-3, in the first
quarter in Cleveland." + ], + [ + " HYDERABAD, India (Reuters) -
Microsoft Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=MSFT.O t
arget=/stocks/quickinfo/fullqu
ote\">MSFT.O</A> will
hire several hundred new staff
at its new Indian campus in
the next year, its chief
executive said on Monday, in a
move aimed at strengthening
its presence in Asia's fourth-
biggest economy." + ], + [ + "According to Swiss
authorities, history was made
Sunday when 2723 people in
four communities in canton
Geneva, Switzerland, voted
online in a national federal
referendum." + ], + [ + " GUWAHATI, India (Reuters) -
People braved a steady drizzle
to come out to vote in a
remote northeast Indian state
on Thursday, as troops
guarded polling stations in an
election being held under the
shadow of violence." + ], + [ + "AFP - Three of the nine
Canadian sailors injured when
their newly-delivered,
British-built submarine caught
fire in the North Atlantic
were airlifted Wednesday to
hospital in northwest Ireland,
officials said." + ], + [ + "Alitalia SpA, Italy #39;s
largest airline, reached an
agreement with its flight
attendants #39; unions to cut
900 jobs, qualifying the
company for a government
bailout that will keep it in
business for another six
months." + ], + [ + "BAGHDAD, Iraq - A series of
strong explosions shook
central Baghdad near dawn
Sunday, and columns of thick
black smoke rose from the
Green Zone where U.S. and
Iraqi government offices are
located..." + ], + [ + "Forget September call-ups. The
Red Sox may tap their minor
league system for an extra
player or two when the rules
allow them to expand their
25-man roster Wednesday, but
any help from the farm is
likely to pale against the
abundance of talent they gain
from the return of numerous
players, including Trot Nixon
, from the disabled list." + ], + [ + "P amp;Os cutbacks announced
today are the result of the
waves of troubles that have
swamped the ferry industry of
late. Some would say the
company has done well to
weather the storms for as long
as it has." + ], + [ + "Sven-Goran Eriksson may gamble
by playing goalkeeper Paul
Robinson and striker Jermain
Defoe in Poland." + ], + [ + "Foreign Secretary Jack Straw
has flown to Khartoum on a
mission to pile the pressure
on the Sudanese government to
tackle the humanitarian
catastrophe in Darfur." + ], + [ + " Nextel was the big story in
telecommunications yesterday,
thanks to the Reston company's
mega-merger with Sprint, but
the future of wireless may be
percolating in dozens of
Washington area start-ups." + ], + [ + "Reuters - A senior U.S.
official said on
Wednesday\\deals should not be
done with hostage-takers ahead
of the\\latest deadline set by
Afghan Islamic militants who
have\\threatened to kill three
kidnapped U.N. workers." + ], + [ + "I have been anticipating this
day like a child waits for
Christmas. Today, PalmOne
introduces the Treo 650, the
answer to my quot;what smart
phone will I buy?" + ], + [ + "THOUSAND OAKS -- Anonymity is
only a problem if you want it
to be, and it is obvious Vijay
Singh doesn #39;t want it to
be. Let others chase fame." + ], + [ + "Wikipedia has surprised Web
watchers by growing fast and
maturing into one of the most
popular reference sites." + ], + [ + "It only takes 20 minutes on
the Internet for an
unprotected computer running
Microsoft Windows to be taken
over by a hacker. Any personal
or financial information
stored" + ], + [ + "TORONTO (CP) - Russia #39;s
Severstal has made an offer to
buy Stelco Inc., in what #39;s
believed to be one of several
competing offers emerging for
the restructuring but
profitable Hamilton steel
producer." + ], + [ + "Prices for flash memory cards
-- the little modules used by
digital cameras, handheld
organizers, MP3 players and
cell phones to store pictures,
music and other data -- are
headed down -- way down. Past
trends suggest that prices
will drop 35 percent a year,
but industry analysts think
that rate will be more like 40
or 50 percent this year and
next, due to more
manufacturers entering the
market." + ], + [ + "Walt Disney Co. #39;s
directors nominated Michael
Ovitz to serve on its board
for another three years at a
meeting just weeks before
forcing him out of his job as" + ], + [ + "The European Union, Japan,
Brazil and five other
countries won World Trade
Organization approval to
impose tariffs worth more than
\\$150 million a year on
imports from the United" + ], + [ + "Industrial conglomerate
Honeywell International on
Wednesday said it has filed a
lawsuit against 34 electronics
companies including Apple
Computer and Eastman Kodak,
claiming patent infringement
of its liquid crystal display
technology." + ], + [ + "Sinn Fein leader Gerry Adams
has put the pressure for the
success or failure of the
Northern Ireland assembly
talks firmly on the shoulders
of Ian Paisley." + ], + [ + "Australia #39;s Computershare
has agreed to buy EquiServe of
the United States for US\\$292
million (\\$423 million),
making it the largest US share
registrar and driving its
shares up by a third." + ], + [ + "David Coulthard #39;s season-
long search for a Formula One
drive next year is almost
over. Negotiations between Red
Bull Racing and Coulthard, who
tested for the Austrian team
for the first time" + ], + [ + "Interest rates on short-term
Treasury securities were mixed
in yesterday's auction. The
Treasury Department sold \\$18
billion in three-month bills
at a discount rate of 1.640
percent, up from 1.635 percent
last week. An additional \\$16
billion was sold in six-month
bills at a rate of 1.840
percent, down from 1.860
percent." + ], + [ + "Two top executives of scandal-
tarred insurance firm Marsh
Inc. were ousted yesterday,
the company said, the latest
casualties of an industry
probe by New York's attorney
general." + ], + [ + "AP - Southern California
tailback LenDale White
remembers Justin Holland from
high school. The Colorado
State quarterback made quite
an impression." + ], + [ + "TIM HENMAN last night admitted
all of his energy has been
drained away as he bowed out
of the Madrid Masters. The top
seed, who had a blood test on
Wednesday to get to the bottom
of his fatigue, went down" + ], + [ + "USDA #39;s Animal Plant Health
Inspection Service (APHIS)
this morning announced it has
confirmed a detection of
soybean rust from two test
plots at Louisiana State
University near Baton Rouge,
Louisiana." + ], + [ + " JAKARTA (Reuters) - President
Megawati Sukarnoputri urged
Indonesians on Thursday to
accept the results of the
country's first direct
election of a leader, but
stopped short of conceding
defeat." + ], + [ + "ISLAMABAD: Pakistan early
Monday test-fired its
indigenously developed short-
range nuclear-capable Ghaznavi
missile, the Inter Services
Public Relations (ISPR) said
in a statement." + ], + [ + "While the US software giant
Microsoft has achieved almost
sweeping victories in
government procurement
projects in several Chinese
provinces and municipalities,
the process" + ], + [ + "Mexican Cemex, being the third
largest cement maker in the
world, agreed to buy its
British competitor - RMC Group
- for \\$5.8 billion, as well
as their debts in order to
expand their activity on the
building materials market of
the USA and Europe." + ], + [ + "Microsoft Corp. has delayed
automated distribution of a
major security upgrade to its
Windows XP Professional
operating system, citing a
desire to give companies more
time to test it." + ], + [ + "The trial of a man accused of
murdering York backpacker
Caroline Stuttle begins in
Australia." + ], + [ + "Gateway computers will be more
widely available at Office
Depot, in the PC maker #39;s
latest move to broaden
distribution at retail stores
since acquiring rival
eMachines this year." + ], + [ + "ATHENS -- US sailors needed a
big day to bring home gold and
bronze medals from the sailing
finale here yesterday. But
rolling the dice on windshifts
and starting tactics backfired
both in Star and Tornado
classes, and the Americans had
to settle for a single silver
medal." + ], + [ + "Intel Corp. is refreshing its
64-bit Itanium 2 processor
line with six new chips based
on the Madison core. The new
processors represent the last
single-core Itanium chips that
the Santa Clara, Calif." + ], + [ + "The world's largest insurance
group pays \\$126m in fines as
part of a settlement with US
regulators over its dealings
with two firms." + ], + [ + "BRUSSELS: The EU sought
Wednesday to keep pressure on
Turkey over its bid to start
talks on joining the bloc, as
last-minute haggling seemed
set to go down to the wire at
a summit poised to give a
green light to Ankara." + ], + [ + "AP - J. Cofer Black, the State
Department official in charge
of counterterrorism, is
leaving government in the next
few weeks." + ], + [ + "For the first time, broadband
connections are reaching more
than half (51 percent) of the
American online population at
home, according to measurement
taken in July by
Nielsen/NetRatings, a
Milpitas-based Internet
audience measurement and
research ..." + ], + [ + "AP - Cavaliers forward Luke
Jackson was activated
Wednesday after missing five
games because of tendinitis in
his right knee. Cleveland also
placed forward Sasha Pavlovic
on the injured list." + ], + [ + "The tobacco firm John Player
amp; Sons has announced plans
to lay off 90 workers at its
cigarette factory in Dublin.
The company said it was
planning a phased closure of
the factory between now and
February as part of a review
of its global operations." + ], + [ + "AP - Consumers borrowed more
freely in September,
especially when it came to
racking up charges on their
credit cards, the Federal
Reserve reported Friday." + ], + [ + "AFP - The United States
presented a draft UN
resolution that steps up the
pressure on Sudan over the
crisis in Darfur, including
possible international
sanctions against its oil
sector." + ], + [ + "AFP - At least 33 people were
killed and dozens others
wounded when two bombs ripped
through a congregation of
Sunni Muslims in Pakistan's
central city of Multan, police
said." + ], + [ + "Bold, innovative solutions are
key to addressing the rapidly
rising costs of higher
education and the steady
reduction in government-
subsidized help to finance
such education." + ], + [ + "Just as the PhD crowd emerge
with different interpretations
of today's economy, everyday
Americans battling to balance
the checkbook hold diverse
opinions about where things
stand now and in the future." + ], + [ + "The Brisbane Lions #39;
football manager stepped out
of the changerooms just before
six o #39;clock last night and
handed one of the milling
supporters a six-pack of beer." + ], + [ + "Authorities here are always
eager to show off their
accomplishments, so when
Beijing hosted the World
Toilet Organization conference
last week, delegates were
given a grand tour of the
city's toilets." + ], + [ + "Cavaliers owner Gordon Gund is
in quot;serious quot;
negotiations to sell the NBA
franchise, which has enjoyed a
dramatic financial turnaround
since the arrival of star
LeBron James." + ], + [ + "WASHINGTON Trying to break a
deadlock on energy policy, a
diverse group of
environmentalists, academics
and former government
officials were to publish a
report on Wednesday that
presents strategies for making
the United States cleaner,
more competitive" + ], + [ + "After two days of gloom, China
was back on the winning rails
on Thursday with Liu Chunhong
winning a weightlifting title
on her record-shattering binge
and its shuttlers contributing
two golds in the cliff-hanging
finals." + ], + [ + "One question that arises
whenever a player is linked to
steroids is, \"What would he
have done without them?\"
Baseball history whispers an
answer." + ], + [ + "AFP - A series of torchlight
rallies and vigils were held
after darkness fell on this
central Indian city as victims
and activists jointly
commemorated a night of horror
20 years ago when lethal gas
leaked from a pesticide plant
and killed thousands." + ], + [ + "Consider the New World of
Information - stuff that,
unlike the paper days of the
past, doesn't always
physically exist. You've got
notes, scrawlings and
snippets, Web graphics, photos
and sounds. Stuff needs to be
cut, pasted, highlighted,
annotated, crossed out,
dragged away. And, as Ross
Perot used to say (or maybe it
was Dana Carvey impersonating
him), don't forget the graphs
and charts." + ], + [ + "The second round of the
Canadian Open golf tournament
continues Saturday Glenn Abbey
Golf Club in Oakville,
Ontario, after play was
suspended late Friday due to
darkness." + ], + [ + "A consortium led by Royal
Dutch/Shell Group that is
developing gas reserves off
Russia #39;s Sakhalin Island
said Thursday it has struck a
US\\$6 billion (euro4." + ], + [ + "Major Hollywood studios on
Tuesday announced scores of
lawsuits against computer
server operators worldwide,
including eDonkey, BitTorrent
and DirectConnect networks,
for allowing trading of
pirated movies." + ], + [ + "A massive plan to attract the
2012 Summer Olympics to New
York, touting the city's
diversity, financial and media
power, was revealed Wednesday." + ], + [ + "A Zimbabwe court Friday
convicted a British man
accused of leading a coup plot
against the government of oil-
rich Equatorial Guinea on
weapons charges, but acquitted
most of the 69 other men held
with him." + ], + [ + "But will Wi-Fi, high-
definition broadcasts, mobile
messaging and other
enhancements improve the game,
or wreck it?\\<br />
Photos of tech-friendly parks\\" + ], + [ + "An audit by international
observers supported official
elections results that gave
President Hugo Chavez a
victory over a recall vote
against him, the secretary-
general of the Organisation of
American States announced." + ], + [ + "Canadian Press - TORONTO (CP)
- The fatal stabbing of a
young man trying to eject
unwanted party guests from his
family home, the third such
knifing in just weeks, has
police worried about a
potentially fatal holiday
recipe: teens, alcohol and
knives." + ], + [ + "NICK Heidfeld #39;s test with
Williams has been brought
forward after BAR blocked
plans for Anthony Davidson to
drive its Formula One rival
#39;s car." + ], + [ + "MOSCOW - A female suicide
bomber set off a shrapnel-
filled explosive device
outside a busy Moscow subway
station on Tuesday night,
officials said, killing 10
people and injuring more than
50." + ], + [ + "Grace Park closed with an
eagle and two birdies for a
7-under-par 65 and a two-
stroke lead after three rounds
of the Wachovia LPGA Classic
on Saturday." + ], + [ + "ABIDJAN (AFP) - Two Ivory
Coast military aircraft
carried out a second raid on
Bouake, the stronghold of the
former rebel New Forces (FN)
in the divided west African
country, a French military
source told AFP." + ], + [ + "Carlos Beltran drives in five
runs to carry the Astros to a
12-3 rout of the Braves in
Game 5 of their first-round NL
playoff series." + ], + [ + "On-demand viewing isn't just
for TiVo owners anymore.
Television over internet
protocol, or TVIP, offers
custom programming over
standard copper wires." + ], + [ + "Apple is recalling 28,000
faulty batteries for its
15-inch Powerbook G4 laptops." + ], + [ + "Since Lennox Lewis #39;s
retirement, the heavyweight
division has been knocked for
having more quantity than
quality. Eight heavyweights on
Saturday night #39;s card at
Madison Square Garden hope to
change that perception, at
least for one night." + ], + [ + "PalmSource #39;s European
developer conference is going
on now in Germany, and this
company is using this
opportunity to show off Palm
OS Cobalt 6.1, the latest
version of its operating
system." + ], + [ + "The former Chief Executive
Officer of Computer Associates
was indicted by a federal
grand jury in New York
Wednesday for allegedly
participating in a massive
fraud conspiracy and an
elaborate cover up of a scheme
that cost investors" + ], + [ + "Speaking to members of the
Massachusetts Software
Council, Microsoft CEO Steve
Ballmer touted a bright future
for technology but warned his
listeners to think twice
before adopting open-source
products like Linux." + ], + [ + "MIAMI - The Trillian instant
messaging (IM) application
will feature several
enhancements in its upcoming
version 3.0, including new
video and audio chat
capabilities, enhanced IM
session logs and integration
with the Wikipedia online
encyclopedia, according to
information posted Friday on
the product developer's Web
site." + ], + [ + "Honeywell International Inc.,
the world #39;s largest
supplier of building controls,
agreed to buy Novar Plc for
798 million pounds (\\$1.53
billion) to expand its
security, fire and
ventilation-systems business
in Europe." + ], + [ + "San Francisco investment bank
Thomas Weisel Partners on
Thursday agreed to pay \\$12.5
million to settle allegations
that some of the stock
research the bank published
during the Internet boom was
tainted by conflicts of
interest." + ], + [ + "AFP - A state of civil
emergency in the rebellion-hit
Indonesian province of Aceh
has been formally extended by
six month, as the country's
president pledged to end
violence there without foreign
help." + ], + [ + "Forbes.com - Peter Frankling
tapped an unusual source to
fund his new business, which
makes hot-dog-shaped ice cream
treats known as Cool Dogs: Two
investors, one a friend and
the other a professional
venture capitalist, put in
more than #36;100,000 each
from their Individual
Retirement Accounts. Later
Franklin added #36;150,000
from his own IRA." + ], + [ + "Reuters - Online DVD rental
service Netflix Inc.\\and TiVo
Inc., maker of a digital video
recorder, on Thursday\\said
they have agreed to develop a
joint entertainment\\offering,
driving shares of both
companies higher." + ], + [ + "A San Diego insurance
brokerage has been sued by New
York Attorney General Elliot
Spitzer for allegedly
soliciting payoffs in exchange
for steering business to
preferred insurance companies." + ], + [ + "The European Union agreed
Monday to lift penalties that
have cost American exporters
\\$300 million, following the
repeal of a US corporate tax
break deemed illegal under
global trade rules." + ], + [ + "US Secretary of State Colin
Powell on Monday said he had
spoken to both Indian Foreign
Minister K Natwar Singh and
his Pakistani counterpart
Khurshid Mahmud Kasuri late
last week before the two met
in New Delhi this week for
talks." + ], + [ + "NEW YORK - Victor Diaz hit a
tying, three-run homer with
two outs in the ninth inning,
and Craig Brazell's first
major league home run in the
11th gave the New York Mets a
stunning 4-3 victory over the
Chicago Cubs on Saturday.
The Cubs had much on the
line..." + ], + [ + "AFP - At least 54 people have
died and more than a million
have fled their homes as
torrential rains lashed parts
of India and Bangladesh,
officials said." + ], + [ + "LOS ANGELES - California has
adopted the world's first
rules to reduce greenhouse
emissions for autos, taking
what supporters see as a
dramatic step toward cleaning
up the environment but also
ensuring higher costs for
drivers. The rules may lead
to sweeping changes in
vehicles nationwide,
especially if other states opt
to follow California's
example..." + ], + [ + " LONDON (Reuters) - European
stock markets scaled
near-2-1/2 year highs on
Friday as oil prices held
below \\$48 a barrel, and the
euro held off from mounting
another assault on \\$1.30 but
hovered near record highs
against the dollar." + ], + [ + "Tim Duncan had 17 points and
10 rebounds, helping the San
Antonio Spurs to a 99-81
victory over the New York
Kicks. This was the Spurs
fourth straight win this
season." + ], + [ + "Nagpur: India suffered a
double blow even before the
first ball was bowled in the
crucial third cricket Test
against Australia on Tuesday
when captain Sourav Ganguly
and off spinner Harbhajan
Singh were ruled out of the
match." + ], + [ + "AFP - Republican and
Democratic leaders each
declared victory after the
first head-to-head sparring
match between President George
W. Bush and Democratic
presidential hopeful John
Kerry." + ], + [ + "THIS YULE is all about console
supply and there #39;s
precious little units around,
it has emerged. Nintendo has
announced that it is going to
ship another 400,000 units of
its DS console to the United
States to meet the shortfall
there." + ], + [ + "Annual global semiconductor
sales growth will probably
fall by half in 2005 and
memory chip sales could
collapse as a supply glut saps
prices, world-leading memory
chip maker Samsung Electronics
said on Monday." + ], + [ + "NEW YORK - Traditional phone
systems may be going the way
of the Pony Express. Voice-
over-Internet Protocol,
technology that allows users
to make and receive phone
calls using the Internet, is
giving the old circuit-
switched system a run for its
money." + ], + [ + "AP - Former New York Yankees
hitting coach Rick Down was
hired for the same job by the
Mets on Friday, reuniting him
with new manager Willie
Randolph." + ], + [ + "Last night in New York, the UN
secretary-general was given a
standing ovation - a robust
response to a series of
attacks in past weeks." + ], + [ + "FILDERSTADT (Germany) - Amelie
Mauresmo and Lindsay Davenport
took their battle for the No.
1 ranking and Porsche Grand
Prix title into the semi-
finals with straight-sets
victories on Friday." + ], + [ + "Reuters - The company behind
the Atkins Diet\\on Friday
shrugged off a recent decline
in interest in low-carb\\diets
as a seasonal blip, and its
marketing chief said\\consumers
would cut out starchy foods
again after picking up\\pounds
over the holidays." + ], + [ + "There #39;s something to be
said for being the quot;first
mover quot; in an industry
trend. Those years of extra
experience in tinkering with a
new idea can be invaluable in
helping the first" + ], + [ + "JOHANNESBURG -- Meeting in
Nigeria four years ago,
African leaders set a goal
that 60 percent of children
and pregnant women in malaria-
affected areas around the
continent would be sleeping
under bed nets by the end of
2005." + ], + [ + "AP - The first U.S. cases of
the fungus soybean rust, which
hinders plant growth and
drastically cuts crop
production, were found at two
research sites in Louisiana,
officials said Wednesday." + ], + [ + "Carter returned, but it was
running back Curtis Martin and
the offensive line that put
the Jets ahead. Martin rushed
for all but 10 yards of a
45-yard drive that stalled at
the Cardinals 10." + ], + [ + " quot;There #39;s no way
anyone would hire them to
fight viruses, quot; said
Sophos security analyst Gregg
Mastoras. quot;For one, no
security firm could maintain
its reputation by employing
hackers." + ], + [ + "Symantec has revoked its
decision to blacklist a
program that allows Web
surfers in China to browse
government-blocked Web sites.
The move follows reports that
the firm labelled the Freegate
program, which" + ], + [ + " NEW YORK (Reuters) - Shares
of Chiron Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=CHIR.O t
arget=/stocks/quickinfo/fullqu
ote\">CHIR.O</A> fell
7 percent before the market
open on Friday, a day after
the biopharmaceutical company
said it is delaying shipment
of its flu vaccine, Fluvirin,
because lots containing 4
million vaccines do not meet
product sterility standards." + ], + [ + "The nation's top
telecommunications regulator
said yesterday he will push --
before the next president is
inaugurated -- to protect
fledgling Internet telephone
services from getting taxed
and heavily regulated by the
50 state governments." + ], + [ + "Microsoft has signed a pact to
work with the United Nations
Educational, Scientific and
Cultural Organization (UNESCO)
to increase computer use,
Internet access and teacher
training in developing
countries." + ], + [ + "DENVER (Ticker) -- Jake
Plummer more than made up for
a lack of a running game.
Plummer passed for 294 yards
and two touchdowns as the
Denver Broncos posted a 23-13
victory over the San Diego
Chargers in a battle of AFC
West Division rivals." + ], + [ + "DALLAS -- Belo Corp. said
yesterday that it would cut
250 jobs, more than half of
them at its flagship
newspaper, The Dallas Morning
News, and that an internal
investigation into circulation
overstatements" + ], + [ + "AP - Duke Bainum outspent Mufi
Hannemann in Honolulu's most
expensive mayoral race, but
apparently failed to garner
enough votes in Saturday's
primary to claim the office
outright." + ], + [ + "roundup Plus: Microsoft tests
Windows Marketplace...Nortel
delays financials
again...Microsoft updates
SharePoint." + ], + [ + "The Federal Reserve still has
some way to go to restore US
interest rates to more normal
levels, Philadelphia Federal
Reserve President Anthony
Santomero said on Monday." + ], + [ + "It took all of about five
minutes of an introductory
press conference Wednesday at
Heritage Hall for USC
basketball to gain something
it never really had before." + ], + [ + "Delta Air Lines (DAL.N: Quote,
Profile, Research) said on
Wednesday its auditors have
expressed doubt about the
airline #39;s financial
viability." + ], + [ + "POLITICIANS and aid agencies
yesterday stressed the
importance of the media in
keeping the spotlight on the
appalling human rights abuses
taking place in the Darfur
region of Sudan." + ], + [ + "AP - The Boston Red Sox looked
at the out-of-town scoreboard
and could hardly believe what
they saw. The New York Yankees
were trailing big at home
against the Cleveland Indians
in what would be the worst
loss in the 101-year history
of the storied franchise." + ], + [ + "The Red Sox will either
complete an amazing comeback
as the first team to rebound
from a 3-0 deficit in
postseason history, or the
Yankees will stop them." + ], + [ + "\\Children who have a poor diet
are more likely to become
aggressive and anti-social, US
researchers believe." + ], + [ + "OPEN SOURCE champion Microsoft
is expanding its programme to
give government organisations
some of its source code. In a
communique from the lair of
the Vole, in Redmond,
spinsters have said that
Microsoft" + ], + [ + "The Red Sox have reached
agreement with free agent
pitcher Matt Clement yesterday
on a three-year deal that will
pay him around \\$25 million,
his agent confirmed yesterday." + ], + [ + "Takeover target Ronin Property
Group said it would respond to
an offer by Multiplex Group
for all the securities in the
company in about three weeks." + ], + [ + "Canadian Press - OTTAWA (CP) -
Contrary to Immigration
Department claims, there is no
shortage of native-borne
exotic dancers in Canada, says
a University of Toronto law
professor who has studied the
strip club business." + ], + [ + "HEN Manny Ramirez and David
Ortiz hit consecutive home
runs Sunday night in Chicago
to put the Red Sox ahead,
there was dancing in the
streets in Boston." + ], + [ + "Google Inc. is trying to
establish an online reading
room for five major libraries
by scanning stacks of hard-to-
find books into its widely
used Internet search engine." + ], + [ + "HOUSTON--(BUSINESS
WIRE)--Sept. 1, 2004-- L
#39;operazione crea una
centrale globale per l
#39;analisi strategica el
#39;approfondimento del
settore energetico IHS Energy,
fonte globale leader di
software, analisi e
informazioni" + ], + [ + "The European Union presidency
yesterday expressed optimism
that a deal could be struck
over Turkey #39;s refusal to
recognize Cyprus in the lead-
up to next weekend #39;s EU
summit, which will decide
whether to give Ankara a date
for the start of accession
talks." + ], + [ + " WASHINGTON (Reuters) -
President Bush on Friday set a
four-year goal of seeing a
Palestinian state established
and he and British Prime
Minister Tony Blair vowed to
mobilize international
support to help make it happen
now that Yasser Arafat is
dead." + ], + [ + "WASHINGTON Can you always tell
when somebody #39;s lying? If
so, you might be a wizard of
the fib. A California
psychology professor says
there #39;s a tiny subculture
of people that can pick out a
lie nearly every time they
hear one." + ], + [ + " KHARTOUM (Reuters) - Sudan on
Saturday questioned U.N.
estimates that up to 70,000
people have died from hunger
and disease in its remote
Darfur region since a
rebellion began 20 months
ago." + ], + [ + "Type design was once the
province of skilled artisans.
With the help of new computer
programs, neophytes have
flooded the Internet with
their creations." + ], + [ + "RCN Inc., co-owner of
Starpower Communications LLC,
the Washington area
television, telephone and
Internet provider, filed a
plan of reorganization
yesterday that it said puts
the company" + ], + [ + "MIAMI -- Bryan Randall grabbed
a set of Mardi Gras beads and
waved them aloft, while his
teammates exalted in the
prospect of a trip to New
Orleans." + ], + [ + "<strong>Letters</stro
ng> Reports of demise
premature" + ], + [ + "TORONTO (CP) - With an injured
Vince Carter on the bench, the
Toronto Raptors dropped their
sixth straight game Friday,
101-87 to the Denver Nuggets." + ], + [ + "The US airline industry,
riddled with excess supply,
will see a significant drop in
capacity, or far fewer seats,
as a result of at least one
airline liquidating in the
next year, according to
AirTran Airways Chief
Executive Joe Leonard." + ], + [ + "Boeing (nyse: BA - news -
people ) Chief Executive Harry
Stonecipher is keeping the
faith. On Monday, the head of
the aerospace and military
contractor insists he #39;s
confident his firm will
ultimately win out" + ], + [ + "While not quite a return to
glory, Monday represents the
Redskins' return to the
national consciousness." + ], + [ + "Australia #39;s biggest
supplier of fresh milk,
National Foods, has posted a
net profit of \\$68.7 million,
an increase of 14 per cent on
last financial year." + ], + [ + "Lawyers for customers suing
Merck amp; Co. want to
question CEO Raymond Gilmartin
about what he knew about the
dangers of Vioxx before the
company withdrew the drug from
the market because of health
hazards." + ], + [ + "Vijay Singh has won the US PGA
Tour player of the year award
for the first time, ending
Tiger Woods #39;s five-year
hold on the honour." + ], + [ + "New York; September 23, 2004 -
The Department of Justice
(DoJ), FBI and US Attorney
#39;s Office handed down a
10-count indictment against
former Computer Associates
(CA) chairman and CEO Sanjay
Kumar and Stephen Richards,
former CA head of worldwide
sales." + ], + [ + "AFP - At least four Georgian
soldiers were killed and five
wounded in overnight clashes
in Georgia's separatist, pro-
Russian region of South
Ossetia, Georgian officers
near the frontline with
Ossetian forces said early
Thursday." + ], + [ + "Intel, the world #39;s largest
chip maker, scrapped a plan
Thursday to enter the digital
television chip business,
marking a major retreat from
its push into consumer
electronics." + ], + [ + "PACIFIC Hydro shares yesterday
caught an updraught that sent
them more than 20 per cent
higher after the wind farmer
moved to flush out a bidder." + ], + [ + "The European Commission is
expected later this week to
recommend EU membership talks
with Turkey. Meanwhile, German
Chancellor Gerhard Schroeder
and Turkish Prime Minister
Tayyip Erdogan are
anticipating a quot;positive
report." + ], + [ + "The US is the originator of
over 42 of the worlds
unsolicited commercial e-mail,
making it the worst offender
in a league table of the top
12 spam producing countries
published yesterday by anti-
virus firm Sophos." + ], + [ + " NEW YORK (Reuters) - U.S.
consumer confidence retreated
in August while Chicago-area
business activity slowed,
according to reports on
Tuesday that added to worries
the economy's patch of slow
growth may last beyond the
summer." + ], + [ + "Intel is drawing the curtain
on some of its future research
projects to continue making
transistors smaller, faster,
and less power-hungry out as
far as 2020." + ], + [ + "England got strikes from
sparkling debut starter
Jermain Defoe and Michael Owen
to defeat Poland in a Uefa
World Cup qualifier in
Chorzow." + ], + [ + "The Canadian government
signalled its intention
yesterday to reintroduce
legislation to decriminalise
the possession of small
amounts of marijuana." + ], + [ + "A screensaver targeting spam-
related websites appears to
have been too successful." + ], + [ + "Titleholder Ernie Els moved
within sight of a record sixth
World Match Play title on
Saturday by solving a putting
problem to overcome injured
Irishman Padraig Harrington 5
and 4." + ], + [ + "If the Washington Nationals
never win a pennant, they have
no reason to ever doubt that
DC loves them. Yesterday, the
District City Council
tentatively approved a tab for
a publicly financed ballpark
that could amount to as much
as \\$630 million." + ], + [ + "Plus: Experts fear Check 21
could lead to massive bank
fraud." + ], + [ + "By SIOBHAN McDONOUGH
WASHINGTON (AP) -- Fewer
American youths are using
marijuana, LSD and Ecstasy,
but more are abusing
prescription drugs, says a
government report released
Thursday. The 2003 National
Survey on Drug Use and Health
also found youths and young
adults are more aware of the
risks of using pot once a
month or more frequently..." + ], + [ + " ABIDJAN (Reuters) - Ivory
Coast warplanes killed nine
French soldiers on Saturday in
a bombing raid during the
fiercest clashes with rebels
for 18 months and France hit
back by destroying most of
the West African country's
small airforce." + ], + [ + "Unilever has reported a three
percent rise in third-quarter
earnings but warned it is
reviewing its targets up to
2010, after issuing a shock
profits warning last month." + ], + [ + "A TNO engineer prepares to
start capturing images for the
world's biggest digital photo." + ], + [ + "Defensive back Brandon
Johnson, who had two
interceptions for Tennessee at
Mississippi, was suspended
indefinitely Monday for
violation of team rules." + ], + [ + "Points leader Kurt Busch spun
out and ran out of fuel, and
his misfortune was one of the
reasons crew chief Jimmy
Fennig elected not to pit with
20 laps to go." + ], + [ + " LONDON (Reuters) - Oil prices
extended recent heavy losses
on Wednesday ahead of weekly
U.S. data expected to show
fuel stocks rising in time
for peak winter demand." + ], + [ + "(CP) - The NHL all-star game
hasn #39;t been cancelled
after all. It #39;s just been
moved to Russia. The agent for
New York Rangers winger
Jaromir Jagr confirmed Monday
that the Czech star had joined
Omsk Avangard" + ], + [ + "PalmOne is aiming to sharpen
up its image with the launch
of the Treo 650 on Monday. As
previously reported, the smart
phone update has a higher-
resolution screen and a faster
processor than the previous
top-of-the-line model, the
Treo 600." + ], + [ + "GAZA CITY, Gaza Strip --
Islamic militant groups behind
many suicide bombings
dismissed yesterday a call
from Mahmoud Abbas, the
interim Palestinian leader, to
halt attacks in the run-up to
a Jan. 9 election to replace
Yasser Arafat." + ], + [ + "Secretary of State Colin
Powell is wrapping up an East
Asia trip focused on prodding
North Korea to resume talks
aimed at ending its nuclear-
weapons program." + ], + [ + "HERE in the land of myth, that
familiar god of sports --
karma -- threw a bolt of
lightning into the Olympic
stadium yesterday. Marion
Jones lunged desperately with
her baton in the 4 x 100m
relay final, but couldn #39;t
reach her target." + ], + [ + "kinrowan writes quot;MIT,
inventor of Kerberos, has
announced a pair of
vulnerabities in the software
that will allow an attacker to
either execute a DOS attack or
execute code on the machine." + ], + [ + "The risk of intestinal damage
from common painkillers may be
higher than thought, research
suggests." + ], + [ + "AN earthquake measuring 7.3 on
the Richter Scale hit western
Japan this morning, just hours
after another strong quake
rocked the area." + ], + [ + "Vodafone has increased the
competition ahead of Christmas
with plans to launch 10
handsets before the festive
season. The Newbury-based
group said it will begin
selling the phones in
November." + ], + [ + "Reuters - Former Pink Floyd
mainman Roger\\Waters released
two new songs, both inspired
by the U.S.-led\\invasion of
Iraq, via online download
outlets Tuesday." + ], + [ + "A former assistant treasurer
at Enron Corp. (ENRNQ.PK:
Quote, Profile, Research)
agreed to plead guilty to
conspiracy to commit
securities fraud on Tuesday
and will cooperate with" + ], + [ + "Britain #39;s Prince Harry,
struggling to shed a growing
quot;wild child quot; image,
won #39;t apologize to a
photographer he scuffled with
outside an exclusive London
nightclub, a royal spokesman
said on Saturday." + ], + [ + "UK house prices fell by 1.1 in
October, confirming a
softening of the housing
market, Halifax has said. The
UK #39;s biggest mortgage
lender said prices rose 18." + ], + [ + "Pakistan #39;s interim Prime
Minister Chaudhry Shaujaat
Hussain has announced his
resignation, paving the way
for his successor Shauket
Aziz." + ], + [ + "A previously unknown group
calling itself Jamaat Ansar
al-Jihad al-Islamiya says it
set fire to a Jewish soup
kitchen in Paris, according to
an Internet statement." + ], + [ + "More than six newspaper
companies have received
letters from the Securities
and Exchange Commission
seeking information about
their circulation practices." + ], + [ + "THE 64,000 dollar -
correction, make that 500
million dollar -uestion
hanging over Shire
Pharmaceuticals is whether the
5 per cent jump in the
companys shares yesterday
reflects relief that US
regulators have finally
approved its drug for" + ], + [ + "The deadliest attack on
Americans in Iraq since May
came as Iraqi officials
announced that Saddam
Hussein's deputy had not been
captured on Sunday." + ], + [ + "AP - Kenny Rogers lost at the
Coliseum for the first time in
more than 10 years, with Bobby
Crosby's three-run double in
the fifth inning leading the
Athletics to a 5-4 win over
the Texas Rangers on Thursday." + ], + [ + "A fundraising concert will be
held in London in memory of
the hundreds of victims of the
Beslan school siege." + ], + [ + "Dambulla, Sri Lanka - Kumar
Sangakkara and Avishka
Gunawardene slammed impressive
half-centuries to help an
under-strength Sri Lanka crush
South Africa by seven wickets
in the fourth one-day
international here on
Saturday." + ], + [ + "Fresh off being the worst team
in baseball, the Arizona
Diamondbacks set a new record
this week: fastest team to
both hire and fire a manager." + ], + [ + "Nebraska head coach Bill
Callahan runs off the field at
halftime of the game against
Baylor in Lincoln, Neb.,
Saturday, Oct. 16, 2004." + ], + [ + "NASA has been working on a
test flight project for the
last few years involving
hypersonic flight. Hypersonic
flight is fight at speeds
greater than Mach 5, or five
times the speed of sound." + ], + [ + "AFP - The landmark trial of a
Rwandan Roman Catholic priest
accused of supervising the
massacre of 2,000 of his Tutsi
parishioners during the
central African country's 1994
genocide opens at a UN court
in Tanzania." + ], + [ + "The Irish government has
stepped up its efforts to free
the British hostage in Iraq,
Ken Bigley, whose mother is
from Ireland, by talking to
diplomats from Iran and
Jordan." + ], + [ + "AP - Republican Sen. Lincoln
Chafee, who flirted with
changing political parties in
the wake of President Bush's
re-election victory, says he
will stay in the GOP." + ], + [ + "AP - Microsoft Corp. announced
Wednesday that it would offer
a low-cost, localized version
of its Windows XP operating
system in India to tap the
large market potential in this
country of 1 billion people,
most of whom do not speak
English." + ], + [ + "Businesses saw inventories
rise in July and sales picked
up, the government reported
Wednesday. The Commerce
Department said that stocks of
unsold goods increased 0.9 in
July, down from a 1.1 rise in
June." + ], + [ + " EAST RUTHERFORD, N.J. (Sports
Network) - The Toronto
Raptors have traded All-Star
swingman Vince Carter to the
New Jersey Nets in exchange
for center Alonzo Mourning,
forward Eric Williams,
center/forward Aaron Williams
and two first- round draft
picks." + ], + [ + "AP - Utah State University has
secured a #36;40 million
contract with NASA to build an
orbiting telescope that will
examine galaxies and try to
find new stars." + ], + [ + "South Korean President Roh
Moo-hyun pays a surprise visit
to troops in Iraq, after his
government decided to extend
their mandate." + ], + [ + "As the European Union
approaches a contentious
decision - whether to let
Turkey join the club - the
Continent #39;s rulers seem to
have left their citizens
behind." + ], + [ + "What riot? quot;An Argentine
friend of mine was a little
derisive of the Pacers-Pistons
eruption, quot; says reader
Mike Gaynes. quot;He snorted,
#39;Is that what Americans
call a riot?" + ], + [ + "All season, Chris Barnicle
seemed prepared for just about
everything, but the Newton
North senior was not ready for
the hot weather he encountered
yesterday in San Diego at the
Footlocker Cross-Country
National Championships. Racing
in humid conditions with
temperatures in the 70s, the
Massachusetts Division 1 state
champion finished sixth in 15
minutes 34 seconds in the
5-kilometer race. ..." + ], + [ + "Shares of Genta Inc. (GNTA.O:
Quote, Profile, Research)
soared nearly 50 percent on
Monday after the biotechnology
company presented promising
data on an experimental
treatment for blood cancers." + ], + [ + "I spend anywhere from three to
eight hours every week
sweating along with a motley
crew of local misfits,
shelving, sorting, and hauling
ton after ton of written
matter in a rowhouse basement
in Baltimore. We have no heat
nor air conditioning, but
still, every week, we come and
work. Volunteer night is
Wednesday, but many of us also
work on the weekends, when
we're open to the public.
There are times when we're
freezing and we have to wear
coats and gloves inside,
making handling books somewhat
tricky; other times, we're all
soaked with sweat, since it's
90 degrees out and the
basement is thick with bodies.
One learns to forget about
personal space when working at
The Book Thing, since you can
scarcely breathe without
bumping into someone, and we
are all so accustomed to
having to scrape by each other
that most of us no longer
bother to say \"excuse me\"
unless some particularly
dramatic brushing occurs." + ], + [ + " BAGHDAD (Reuters) - A
deadline set by militants who
have threatened to kill two
Americans and a Briton seized
in Iraq was due to expire
Monday, and more than two
dozen other hostages were
also facing death unless rebel
demands were met." + ], + [ + "Alarmed by software glitches,
security threats and computer
crashes with ATM-like voting
machines, officials from
Washington, D.C., to
California are considering an
alternative from an unlikely
place: Nevada." + ], + [ + "Some Venezuelan television
channels began altering their
programs Thursday, citing
fears of penalties under a new
law restricting violence and
sexual content over the
airwaves." + ], + [ + "SBC Communications Inc. plans
to cut at least 10,000 jobs,
or 6 percent of its work
force, by the end of next year
to compensate for a drop in
the number of local-telephone
customers." + ], + [ + "afrol News, 4 October -
Hundred years of conservation
efforts have lifted the
southern black rhino
population from about hundred
to 11,000 animals." + ], + [ + "THE death of Philippine movie
star and defeated presidential
candidate Fernando Poe has
drawn some political backlash,
with some people seeking to
use his sudden demise as a
platform to attack President
Gloria Arroyo." + ], + [ + " WASHINGTON (Reuters) - Major
cigarette makers go on trial
on Tuesday in the U.S.
government's \\$280 billion
racketeering case that
charges the tobacco industry
with deliberately deceiving
the public about the risks of
smoking since the 1950s." + ], + [ + "More Americans are quitting
their jobs and taking the risk
of starting a business despite
a still-lackluster job market." + ], + [ + "AP - Coach Tyrone Willingham
was fired by Notre Dame on
Tuesday after three seasons in
which he failed to return one
of the nation's most storied
football programs to
prominence." + ], + [ + "COLLEGE PARK, Md. -- Joel
Statham completed 18 of 25
passes for 268 yards and two
touchdowns in No. 23
Maryland's 45-22 victory over
Temple last night, the
Terrapins' 12th straight win
at Byrd Stadium." + ], + [ + "Manchester United boss Sir
Alex Ferguson wants the FA to
punish Arsenal good guy Dennis
Bergkamp for taking a swing at
Alan Smith last Sunday." + ], + [ + "VIENTIANE, Laos China moved
yet another step closer in
cementing its economic and
diplomatic relationships with
Southeast Asia today when
Prime Minister Wen Jiabao
signed a trade accord at a
regional summit that calls for
zero tariffs on a wide range
of" + ], + [ + "SPACE.com - With nbsp;food
stores nbsp;running low, the
two \\astronauts living aboard
the International Space
Station (ISS) are cutting back
\\their meal intake and
awaiting a critical cargo
nbsp;delivery expected to
arrive \\on Dec. 25." + ], + [ + "British judges in London
Tuesday ordered radical Muslim
imam Abu Hamza to stand trial
for soliciting murder and
inciting racial hatred." + ], + [ + "Federal Reserve policy-makers
raised the benchmark US
interest rate a quarter point
to 2.25 per cent and restated
a plan to carry out" + ], + [ + " DETROIT (Reuters) - A
Canadian law firm on Tuesday
said it had filed a lawsuit
against Ford Motor Co. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=F
.N target=/stocks/quickinfo/fu
llquote\">F.N</A> over
what it claims are defective
door latches on about 400,000
of the automaker's popular
pickup trucks and SUVs." + ], + [ + "Published reports say Barry
Bonds has testified that he
used a clear substance and a
cream given to him by a
trainer who was indicted in a
steroid-distribution ring." + ], + [ + "SARASOTA, Fla. - The
devastation brought on by
Hurricane Charley has been
especially painful for an
elderly population that is
among the largest in the
nation..." + ], + [ + " ATHENS (Reuters) - Christos
Angourakis added his name to
Greece's list of Paralympic
medal winners when he claimed
a bronze in the T53 shot put
competition Thursday." + ], + [ + " quot;He is charged for having
a part in the Bali incident,
quot; state prosecutor Andi
Herman told Reuters on
Saturday. bombing attack at
the US-run JW." + ], + [ + "Jay Fiedler threw for one
touchdown, Sage Rosenfels
threw for another and the
Miami Dolphins got a victory
in a game they did not want to
play, beating the New Orleans
Saints 20-19 Friday night." + ], + [ + " NEW YORK (Reuters) - Terrell
Owens scored three touchdowns
and the Philadelphia Eagles
amassed 35 first-half points
on the way to a 49-21
drubbing of the Dallas Cowboys
in Irving, Texas, Monday." + ], + [ + "AstraZeneca Plc suffered its
third setback in two months on
Friday as lung cancer drug
Iressa failed to help patients
live longer" + ], + [ + "Virgin Electronics hopes its
slim Virgin Player, which
debuts today and is smaller
than a deck of cards, will
rise as a lead competitor to
Apple's iPod." + ], + [ + "Researchers train a monkey to
feed itself by guiding a
mechanical arm with its mind.
It could be a big step forward
for prosthetics. By David
Cohn." + ], + [ + "Bruce Wasserstein, the
combative chief executive of
investment bank Lazard, is
expected to agree this week
that he will quit the group
unless he can pull off a
successful" + ], + [ + "A late strike by Salomon Kalou
sealed a 2-1 win for Feyenoord
over NEC Nijmegen, while
second placed AZ Alkmaar
defeated ADO Den Haag 2-0 in
the Dutch first division on
Sunday." + ], + [ + "The United Nations called on
Monday for an immediate
ceasefire in eastern Congo as
fighting between rival army
factions flared for a third
day." + ], + [ + "What a disgrace Ron Artest has
become. And the worst part is,
the Indiana Pacers guard just
doesn #39;t get it. Four days
after fueling one of the
biggest brawls in the history
of pro sports, Artest was on
national" + ], + [ + "Allowing dozens of casinos to
be built in the UK would bring
investment and thousands of
jobs, Tony Blair says." + ], + [ + "The shock here was not just
from the awful fact itself,
that two vibrant young Italian
women were kidnapped in Iraq,
dragged from their office by
attackers who, it seems, knew
their names." + ], + [ + "Tehran/Vianna, Sept. 19 (NNN):
Iran on Sunday rejected the
International Atomic Energy
Agency (IAEA) call to suspend
of all its nuclear activities,
saying that it will not agree
to halt uranium enrichment." + ], + [ + "A 15-year-old Argentine
student opened fire at his
classmates on Tuesday in a
middle school in the south of
the Buenos Aires province,
leaving at least four dead and
five others wounded, police
said." + ], + [ + "Dr. David J. Graham, the FDA
drug safety reviewer who
sounded warnings over five
drugs he felt could become the
next Vioxx has turned to a
Whistleblower protection group
for legal help." + ], + [ + "AP - Scientists in 17
countries will scout waterways
to locate and study the
world's largest freshwater
fish species, many of which
are declining in numbers,
hoping to learn how to better
protect them, researchers
announced Thursday." + ], + [ + "AP - Google Inc.'s plans to
move ahead with its initial
public stock offering ran into
a roadblock when the
Securities and Exchange
Commission didn't approve the
Internet search giant's
regulatory paperwork as
requested." + ], + [ + "Jenson Button has revealed
dissatisfaction with the way
his management handled a
fouled switch to Williams. Not
only did the move not come
off, his reputation may have
been irreparably damaged amid
news headline" + ], + [ + "The Kmart purchase of Sears,
Roebuck may be the ultimate
expression of that old saying
in real estate: location,
location, location." + ], + [ + "Citing security risks, a state
university is urging students
to drop Internet Explorer in
favor of alternative Web
browsers such as Firefox and
Safari." + ], + [ + "Redknapp and his No2 Jim Smith
resigned from Portsmouth
yesterday, leaving
controversial new director
Velimir Zajec in temporary
control." + ], + [ + "Despite being ranked eleventh
in the world in broadband
penetration, the United States
is rolling out high-speed
services on a quot;reasonable
and timely basis to all
Americans, quot; according to
a new report narrowly approved
today by the Federal
Communications" + ], + [ + "Sprint Corp. (FON.N: Quote,
Profile, Research) on Friday
said it plans to cut up to 700
jobs as it realigns its
business to focus on wireless
and Internet services and
takes a non-cash network
impairment charge." + ], + [ + "As the season winds down for
the Frederick Keys, Manager
Tom Lawless is starting to
enjoy the progress his
pitching staff has made this
season." + ], + [ + "Britain #39;s Bradley Wiggins
won the gold medal in men
#39;s individual pursuit
Saturday, finishing the
4,000-meter final in 4:16." + ], + [ + "And when David Akers #39;
50-yard field goal cleared the
crossbar in overtime, they did
just that. They escaped a
raucous Cleveland Browns
Stadium relieved but not
broken, tested but not
cracked." + ], + [ + "NEW YORK - A cable pay-per-
view company has decided not
to show a three-hour election
eve special with filmmaker
Michael Moore that included a
showing of his documentary
\"Fahrenheit 9/11,\" which is
sharply critical of President
Bush. The company, iN
DEMAND, said Friday that its
decision is due to \"legitimate
business and legal concerns.\"
A spokesman would not
elaborate..." + ], + [ + "Democracy candidates picked up
at least one more seat in
parliament, according to exit
polls." + ], + [ + "The IOC wants suspended
Olympic member Ivan Slavkov to
be thrown out of the
organisation." + ], + [ + " BANGKOK (Reuters) - The
ouster of Myanmar's prime
minister, architect of a
tentative \"roadmap to
democracy,\" has dashed faint
hopes for an end to military
rule and leaves Southeast
Asia's policy of constructive
engagement in tatters." + ], + [ + "San Antonio, TX (Sports
Network) - Dean Wilson shot a
five-under 65 on Friday to
move into the lead at the
halfway point of the Texas
Open." + ], + [ + "Now that Chelsea have added
Newcastle United to the list
of clubs that they have given
what for lately, what price
Jose Mourinho covering the
Russian-funded aristocrats of
west London in glittering
glory to the tune of four
trophies?" + ], + [ + "AP - Former Seattle Seahawks
running back Chris Warren has
been arrested in Virginia on a
federal warrant, accused of
failing to pay #36;137,147 in
child support for his two
daughters in Washington state." + ], + [ + "The anguish of hostage Kenneth
Bigley in Iraq hangs over
Prime Minister Tony Blair
today as he faces the twin
test of a local election and a
debate by his Labour Party
about the divisive war." + ], + [ + "Says that amount would have
been earned for the first 9
months of 2004, before AT
amp;T purchase. LOS ANGELES,
(Reuters) - Cingular Wireless
would have posted a net profit
of \\$650 million for the first
nine months" + ], + [ + "NewsFactor - Taking an
innovative approach to the
marketing of high-performance
\\computing, Sun Microsystems
(Nasdaq: SUNW) is offering its
N1 Grid program in a pay-for-
use pricing model that mimics
the way such commodities as
electricity and wireless phone
plans are sold." + ], + [ + " CHICAGO (Reuters) - Goodyear
Tire Rubber Co. <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=GT.N t
arget=/stocks/quickinfo/fullqu
ote\">GT.N</A> said
on Friday it will cut 340 jobs
in its engineered products and
chemical units as part of its
cost-reduction efforts,
resulting in a third-quarter
charge." + ], + [ + " quot;There were 16 people
travelling aboard. ... It
crashed into a mountain, quot;
Col. Antonio Rivero, head of
the Civil Protection service,
told." + ], + [ + "Reuters - Shares of long-
distance phone\\companies AT T
Corp. and MCI Inc. have
plunged\\about 20 percent this
year, but potential buyers
seem to be\\holding out for
clearance sales." + ], + [ + "Reuters - Madonna and m-Qube
have\\made it possible for the
star's North American fans to
download\\polyphonic ring tones
and other licensed mobile
content from\\her official Web
site, across most major
carriers and without\\the need
for a credit card." + ], + [ + "President Bush is reveling in
winning the popular vote and
feels he can no longer be
considered a one-term accident
of history." + ], + [ + "Russian oil giant Yukos files
for bankruptcy protection in
the US in a last ditch effort
to stop the Kremlin auctioning
its main production unit." + ], + [ + "British Airways #39; (BA)
chief executive Rod Eddington
has admitted that the company
quot;got it wrong quot; after
staff shortages led to three
days of travel chaos for
passengers." + ], + [ + "It #39;s official: US Open had
never gone into the third
round with only two American
men, including the defending
champion, Andy Roddick." + ], + [ + "Canadian Press - TORONTO (CP)
- Thousands of Royal Bank
clerks are being asked to
display rainbow stickers at
their desks and cubicles to
promote a safe work
environment for gays,
lesbians, and bisexuals." + ], + [ + "The chipmaker is back on a
buying spree, having scooped
up five other companies this
year." + ], + [ + "The issue of drug advertising
directly aimed at consumers is
becoming political." + ], + [ + "WASHINGTON, Aug. 17
(Xinhuanet) -- England coach
Sven-Goran Eriksson has urged
the international soccer
authorities to preserve the
health of the world superstar
footballers for major
tournaments, who expressed his
will in Slaley of England on
Tuesday ..." + ], + [ + " BAGHDAD (Reuters) - A car
bomb killed two American
soldiers and wounded eight
when it exploded in Baghdad on
Saturday, the U.S. military
said in a statement." + ], + [ + "Juventus coach Fabio Capello
has ordered his players not to
kick the ball out of play when
an opponent falls to the
ground apparently hurt because
he believes some players fake
injury to stop the match." + ], + [ + "AP - China's economic boom is
still roaring despite efforts
to cool sizzling growth, with
the gross domestic product
climbing 9.5 percent in the
first three quarters of this
year, the government reported
Friday." + ], + [ + "Soaring petroleum prices
pushed the cost of goods
imported into the U.S. much
higher than expected in
August, the government said
today." + ], + [ + "Anheuser-Busch teams up with
Vietnam's largest brewer,
laying the groundwork for
future growth in the region." + ], + [ + "You #39;re angry. You want to
lash out. The Red Sox are
doing it to you again. They
#39;re blowing a playoff
series, and to the Yankees no
less." + ], + [ + "TORONTO -- There is no
mystique to it anymore,
because after all, the
Russians have become commoners
in today's National Hockey
League, and Finns, Czechs,
Slovaks, and Swedes also have
been entrenched in the
Original 30 long enough to
turn the ongoing World Cup of
Hockey into a protracted
trailer for the NHL season." + ], + [ + "Sudanese authorities have
moved hundreds of pro-
government fighters from the
crisis-torn Darfur region to
other parts of the country to
keep them out of sight of
foreign military observers
demanding the militia #39;s
disarmament, a rebel leader
charged" + ], + [ + "CHARLOTTE, NC - Shares of
Krispy Kreme Doughnuts Inc.
#39;s fell sharply Monday as a
79 percent plunge in third-
quarter earnings and an
intensifying accounting
investigation overshadowed the
pastrymaker #39;s statement
that the low-carb craze might
be easing." + ], + [ + "The company hopes to lure
software partners by promising
to save them from
infrastructure headaches." + ], + [ + "BAGHDAD, Iraq - A car bomb
Tuesday ripped through a busy
market near a Baghdad police
headquarters where Iraqis were
waiting to apply for jobs on
the force, and gunmen opened
fire on a van carrying police
home from work in Baqouba,
killing at least 59 people
total and wounding at least
114. The attacks were the
latest attempts by militants
to wreck the building of a
strong Iraqi security force, a
keystone of the U.S..." + ], + [ + "The Israeli prime minister
said today that he wanted to
begin withdrawing settlers
from Gaza next May or June." + ], + [ + "Indianapolis, IN (Sports
Network) - The Indiana Pacers
try to win their second
straight game tonight, as they
host Kevin Garnett and the
Minnesota Timberwolves in the
third of a four-game homestand
at Conseco Fieldhouse." + ], + [ + "OXNARD - A leak of explosive
natural gas forced dozens of
workers to evacuate an
offshore oil platform for
hours Thursday but no damage
or injuries were reported." + ], + [ + "AP - Assets of the nation's
retail money market mutual
funds rose by #36;2.85
billion in the latest week to
#36;845.69 billion, the
Investment Company Institute
said Thursday." + ], + [ + "Peter Mandelson provoked fresh
Labour in-fighting yesterday
with an implied attack on
Gordon Brown #39;s
quot;exaggerated gloating
quot; about the health of the
British economy." + ], + [ + "Queen Elizabeth II stopped
short of apologizing for the
Allies #39; bombing of Dresden
during her first state visit
to Germany in 12 years and
instead acknowledged quot;the
appalling suffering of war on
both sides." + ], + [ + "JC Penney said yesterday that
Allen I. Questrom, the chief
executive who has restyled the
once-beleaguered chain into a
sleeker and more profitable
entity, would be succeeded by
Myron E. Ullman III, another
longtime retail executive." + ], + [ + " In the cosmetics department
at Hecht's in downtown
Washington, construction crews
have ripped out the
traditional glass display
cases, replacing them with a
system of open shelves stacked
high with fragrances from
Chanel, Burberry and Armani,
now easily within arm's reach
of the impulse buyer." + ], + [ + " LONDON (Reuters) - Oil prices
slid from record highs above
\\$50 a barrel Wednesday as the
U.S. government reported a
surprise increase in crude
stocks and rebels in Nigeria's
oil-rich delta region agreed
to a preliminary cease-fire." + ], + [ + "Rocky Shoes and Boots makes an
accretive acquisition -- and
gets Dickies and John Deere as
part of the deal." + ], + [ + "AP - Fugitive Taliban leader
Mullah Mohammed Omar has
fallen out with some of his
lieutenants, who blame him for
the rebels' failure to disrupt
the landmark Afghan
presidential election, the
U.S. military said Wednesday." + ], + [ + "HAVANA -- Cuban President
Fidel Castro's advancing age
-- and ultimately his
mortality -- were brought home
yesterday, a day after he
fractured a knee and arm when
he tripped and fell at a
public event." + ], + [ + " BRASILIA, Brazil (Reuters) -
The United States and Brazil
predicted on Tuesday Latin
America's largest country
would resolve a dispute with
the U.N. nuclear watchdog over
inspections of a uranium
enrichment plant." + ], + [ + "Call it the Maximus factor.
Archaeologists working at the
site of an old Roman temple
near Guy #39;s hospital in
London have uncovered a pot of
cosmetic cream dating back to
AD2." + ], + [ + "It is a team game, this Ryder
Cup stuff that will commence
Friday at Oakland Hills
Country Club. So what are the
teams? For the Americans,
captain Hal Sutton isn't
saying." + ], + [ + "Two bombs exploded today near
a tea shop and wounded 20
people in southern Thailand,
police said, as violence
continued unabated in the
Muslim-majority region where
residents are seething over
the deaths of 78 detainees
while in military custody." + ], + [ + "BONN: Deutsche Telekom is
bidding 2.9 bn for the 26 it
does not own in T-Online
International, pulling the
internet service back into the
fold four years after selling
stock to the public." + ], + [ + "Motorola Inc. says it #39;s
ready to make inroads in the
cell-phone market after
posting a third straight
strong quarter and rolling out
a series of new handsets in
time for the holiday selling
season." + ], + [ + "Prime Minister Junichiro
Koizumi, back in Tokyo after
an 11-day diplomatic mission
to the Americas, hunkered down
with senior ruling party
officials on Friday to focus
on a major reshuffle of
cabinet and top party posts." + ], + [ + "Costs of employer-sponsored
health plans are expected to
climb an average of 8 percent
in 2005, the first time in
five years increases have been
in single digits." + ], + [ + "NEW YORK - A sluggish gross
domestic product reading was
nonetheless better than
expected, prompting investors
to send stocks slightly higher
Friday on hopes that the
economic slowdown would not be
as bad as first thought.
The 2.8 percent GDP growth in
the second quarter, a revision
from the 3 percent preliminary
figure reported in July, is a
far cry from the 4.5 percent
growth in the first quarter..." + ], + [ + "After again posting record
earnings for the third
quarter, Taiwan Semiconductor
Manufacturing Company (TSMC)
expects to see its first
sequential drop in fourth-
quarter revenues, coupled with
a sharp drop in capacity
utilization rates." + ], + [ + " SEOUL (Reuters) - A huge
explosion in North Korea last
week may have been due to a
combination of demolition work
for a power plant and
atmospheric clouds, South
Korea's spy agency said on
Wednesday." + ], + [ + "The deal comes as Cisco pushes
to develop a market for CRS-1,
a new line of routers aimed at
telephone, wireless and cable
companies." + ], + [ + "Many people who have never
bounced a check in their life
could soon bounce their first
check if they write checks to
pay bills a couple of days
before their paycheck is
deposited into their checking
account." + ], + [ + " LUXEMBOURG (Reuters) -
Microsoft Corp told a judge on
Thursday that the European
Commission must be stopped
from ordering it to give up
secret technology to
competitors." + ], + [ + "SEPTEMBER 14, 2004 (IDG NEWS
SERVICE) - Sun Microsystems
Inc. and Microsoft Corp. next
month plan to provide more
details on the work they are
doing to make their products
interoperable, a Sun executive
said yesterday." + ], + [ + "MANCHESTER United today
dramatically rejected the
advances of Malcolm Glazer,
the US sports boss who is
mulling an 825m bid for the
football club." + ], + [ + "Dow Jones Industrial Average
futures declined amid concern
an upcoming report on
manufacturing may point to
slowing economic growth." + ], + [ + "AP - Astronomy buffs and
amateur stargazers turned out
to watch a total lunar eclipse
Wednesday night #151; the
last one Earth will get for
nearly two and a half years." + ], + [ + "Reuters - U.S. industrial
output advanced in\\July, as
American factories operated at
their highest capacity\\in more
than three years, a Federal
Reserve report on
Tuesday\\showed." + ], + [ + "As usual, the Big Ten coaches
were out in full force at
today #39;s Big Ten
Teleconference. Both OSU head
coach Jim Tressel and Iowa
head coach Kirk Ferentz
offered some thoughts on the
upcoming game between OSU" + ], + [ + "Sir Martin Sorrell, chief
executive of WPP, yesterday
declared he was quot;very
impressed quot; with Grey
Global, stoking speculation
WPP will bid for the US
advertising company." + ], + [ + "The compact disc has at least
another five years as the most
popular music format before
online downloads chip away at
its dominance, a new study
said on Tuesday." + ], + [ + "New York Knicks #39; Stephon
Marbury (3) fires over New
Orleans Hornets #39; Dan
Dickau (2) during the second
half in New Orleans Wednesday
night, Dec. 8, 2004." + ], + [ + "AP - Sudan's U.N. ambassador
challenged the United States
to send troops to the Darfur
region if it really believes a
genocide is taking place as
the U.S. Congress and
President Bush's
administration have
determined." + ], + [ + "At the very moment when the
Red Sox desperately need
someone slightly larger than
life to rally around, they
suddenly have the man for the
job: Thrilling Schilling." + ], + [ + "Does Your Site Need a Custom
Search Engine
Toolbar?\\\\Today's surfers
aren't always too comfortable
installing software on their
computers. Especially free
software that they don't
necessarily understand. With
all the horror stories of
viruses, spyware, and adware
that make the front page these
days, it's no wonder. So is
there ..." + ], + [ + "Dale Earnhardt Jr, right,
talks with Matt Kenseth, left,
during a break in practice at
Lowe #39;s Motor Speedway in
Concord, NC, Thursday Oct. 14,
2004 before qualifying for
Saturday #39;s UAW-GM Quality
500 NASCAR Nextel Cup race." + ], + [ + "Several starting spots may
have been usurped or at least
threatened after relatively
solid understudy showings
Sunday, but few players
welcome the kind of shot
delivered to Oakland" + ], + [ + "TEMPE, Ariz. -- Neil Rackers
kicked four field goals and
the Arizona Cardinals stifled
rookie Chris Simms and the
rest of the Tampa Bay offense
for a 12-7 victory yesterday
in a matchup of two sputtering
teams out of playoff
contention. Coach Jon Gruden's
team lost its fourth in a row
to finish 5-11, Tampa Bay's
worst record since going ..." + ], + [ + "AFP - A junior British
minister said that people
living in camps after fleeing
their villages because of
conflict in Sudan's Darfur
region lived in fear of
leaving their temporary homes
despite a greater presence now
of aid workers." + ], + [ + "US Deputy Secretary of State
Richard Armitage (L) shakes
hands with Pakistani Foreign
Minister Khurshid Kasuri prior
to their meeting in Islamabad,
09 November 2004." + ], + [ + "Spammers aren't ducking
antispam laws by operating
offshore, they're just
ignoring it." + ], + [ + "AP - Biologists plan to use
large nets and traps this week
in Chicago's Burnham Harbor to
search for the northern
snakehead #151; a type of
fish known for its voracious
appetite and ability to wreak
havoc on freshwater
ecosystems." + ], + [ + "BAE Systems PLC and Northrop
Grumman Corp. won \\$45 million
contracts yesterday to develop
prototypes of anti-missile
technology that could protect
commercial aircraft from
shoulder-fired missiles." + ], + [ + "AP - Democrat John Edwards
kept up a long-distance debate
over his \"two Americas\"
campaign theme with Vice
President Dick Cheney on
Tuesday, saying it was no
illusion to thousands of laid-
off workers in Ohio." + ], + [ + "Like introductory credit card
rates and superior customer
service, some promises just
aren #39;t built to last. And
so it is that Bank of America
- mere months after its pledge
to preserve" + ], + [ + "A group of 76 Eritreans on a
repatriation flight from Libya
Friday forced their plane to
change course and land in the
Sudanese capital, Khartoum,
where they" + ], + [ + "Reuters - Accounting firm KPMG
will pay #36;10\\million to
settle charges of improper
professional conduct\\while
acting as auditor for Gemstar-
TV Guide International Inc.\\,
the U.S. Securities and
Exchange Commission said
on\\Wednesday." + ], + [ + "Family matters made public: As
eager cousins wait for a slice
of the \\$15 billion cake that
is the Pritzker Empire,
Circuit Court Judge David
Donnersberger has ruled that
the case will be conducted in
open court." + ], + [ + "Users of Microsoft #39;s
Hotmail, most of whom are
accustomed to getting regular
sales pitches for premium
e-mail accounts, got a
pleasant surprise in their
inboxes this week: extra
storage for free." + ], + [ + "AP - They say that opposites
attract, and in the case of
Sen. John Kerry and Teresa
Heinz Kerry, that may be true
#151; at least in their public
personas." + ], + [ + "The weekly survey from
mortgage company Freddie Mac
had rates on 30-year fixed-
rate mortgages inching higher
this week, up to an average
5.82 percent from last week
#39;s 5.81 percent." + ], + [ + "The classic power struggle
between Walt Disney Co. CEO
Michael Eisner and former
feared talent agent Michael
Ovitz makes for high drama in
the courtroom - and apparently
on cable." + ], + [ + "ATHENS France, Britain and the
United States issued a joint
challenge Thursday to Germany
#39;s gold medal in equestrian
team three-day eventing." + ], + [ + "LONDON (CBS.MW) -- Elan
(UK:ELA) (ELN) and partner
Biogen (BIIB) said the FDA has
approved new drug Tysabri to
treat relapsing forms of
multiple sclerosis." + ], + [ + "IT services provider
Electronic Data Systems
yesterday reported a net loss
of \\$153 million for the third
quarter, with earnings hit in
part by an asset impairment
charge of \\$375 million
connected with EDS's N/MCI
project." + ], + [ + "ATLANTA - Who could have
imagined Tommy Tuberville in
this position? Yet there he
was Friday, standing alongside
the SEC championship trophy,
posing for pictures and
undoubtedly chuckling a bit on
the inside." + ], + [ + "AP - Impoverished North Korea
might resort to selling
weapons-grade plutonium to
terrorists for much-needed
cash, and that would be
\"disastrous for the world,\"
the top U.S. military
commander in South Korea said
Friday." + ], + [ + "International Business
Machines Corp. said Wednesday
it had agreed to settle most
of the issues in a suit over
changes in its pension plans
on terms that allow the
company to continue to appeal
a key question while capping
its liability at \\$1.7
billion. <BR><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-The Washington
Post</B></FONT>" + ], + [ + "Dubai: Osama bin Laden on
Thursday called on his
fighters to strike Gulf oil
supplies and warned Saudi
leaders they risked a popular
uprising in an audio message
said to be by the Western
world #39;s most wanted terror
mastermind." + ], + [ + "Bank of New Zealand has frozen
all accounts held in the name
of Access Brokerage, which was
yesterday placed in
liquidation after a client
fund shortfall of around \\$5
million was discovered." + ], + [ + "Edward Kozel, Cisco's former
chief technology officer,
joins the board of Linux
seller Red Hat." + ], + [ + "Reuters - International
Business Machines\\Corp. late
on Wednesday rolled out a new
version of its\\database
software aimed at users of
Linux and Unix
operating\\systems that it
hopes will help the company
take away market\\share from
market leader Oracle Corp. ." + ], + [ + "NASA #39;s Mars Odyssey
mission, originally scheduled
to end on Tuesday, has been
granted a stay of execution
until at least September 2006,
reveal NASA scientists." + ], + [ + "ZDNet #39;s survey of IT
professionals in August kept
Wired amp; Wireless on top
for the 18th month in a row.
Telecommunications equipment
maker Motorola said Tuesday
that it would cut 1,000 jobs
and take related" + ], + [ + "John Thomson threw shutout
ball for seven innings
Wednesday night in carrying
Atlanta to a 2-0 blanking of
the New York Mets. New York
lost for the 21st time in 25
games on the" + ], + [ + "BEIJING -- Chinese authorities
have arrested or reprimanded
more than 750 officials in
recent months in connection
with billions of dollars in
financial irregularities
ranging from unpaid taxes to
embezzlement, according to a
report made public yesterday." + ], + [ + "Canadian Press - GAUHATI,
India (AP) - Residents of
northeastern India were
bracing for more violence
Friday, a day after bombs
ripped through two buses and a
grenade was hurled into a
crowded market in attacks that
killed four people and wounded
54." + ], + [ + "Vikram Solanki beat the rain
clouds to register his second
one-day international century
as England won the third one-
day international to wrap up a
series victory." + ], + [ + "Some 28 people are charged
with trying to overthrow
Sudan's President Bashir,
reports say." + ], + [ + "Hong Kong #39;s Beijing-backed
chief executive yesterday
ruled out any early moves to
pass a controversial national
security law which last year
sparked a street protest by
half a million people." + ], + [ + "BRITAIN #39;S largest
financial institutions are
being urged to take lead roles
in lawsuits seeking hundreds
of millions of dollars from
the scandal-struck US
insurance industry." + ], + [ + "Bankrupt UAL Corp. (UALAQ.OB:
Quote, Profile, Research) on
Thursday reported a narrower
third-quarter net loss. The
parent of United Airlines
posted a loss of \\$274
million, or" + ], + [ + "A three-touchdown point spread
and a recent history of late-
season collapses had many
thinking the UCLA football
team would provide little
opposition to rival USC #39;s
march to the BCS-championship
game at the Orange Bowl." + ], + [ + " PHOENIX (Sports Network) -
Free agent third baseman Troy
Glaus is reportedly headed to
the Arizona Diamondbacks." + ], + [ + "The European Union #39;s head
office issued a bleak economic
report Tuesday, warning that
the sharp rise in oil prices
will quot;take its toll quot;
on economic growth next year
while the euro #39;s renewed
climb could threaten crucial
exports." + ], + [ + "Third-string tailback Chris
Markey ran for 131 yards to
lead UCLA to a 34-26 victory
over Oregon on Saturday.
Markey, playing because of an
injury to starter Maurice
Drew, also caught five passes
for 84 yards" + ], + [ + "Though Howard Stern's
defection from broadcast to
satellite radio is still 16
months off, the industry is
already trying to figure out
what will fill the crater in
ad revenue and listenership
that he is expected to leave
behind." + ], + [ + " WASHINGTON (Reuters) - The
United States set final anti-
dumping duties of up to 112.81
percent on shrimp imported
from China and up to 25.76
percent on shrimp from Vietnam
to offset unfair pricing, the
Commerce Department said on
Tuesday." + ], + [ + "Jay Payton #39;s three-run
homer led the San Diego Padres
to a 5-1 win over the San
Francisco Giants in National
League play Saturday, despite
a 701st career blast from
Barry Bonds." + ], + [ + "The optimists among Rutgers
fans were delighted, but the
Scarlet Knights still gave the
pessimists something to worry
about." + ], + [ + "Beer consumption has doubled
over the past five years,
prompting legislators to
implement new rules." + ], + [ + "BusinessWeek Online - The
jubilation that swept East
Germany after the fall of the
Berlin Wall in 1989 long ago
gave way to the sober reality
of globalization and market
forces. Now a decade of
resentment seems to be boiling
over. In Eastern cities such
as Leipzig or Chemnitz,
thousands have taken to the
streets since July to protest
cuts in unemployment benefits,
the main source of livelihood
for 1.6 million East Germans.
Discontent among
reunification's losers fueled
big gains by the far left and
far right in Brandenburg and
Saxony state elections Sept.
19. ..." + ], + [ + "NEW YORK -- When Office Depot
Inc. stores ran an electronics
recycling drive last summer
that accepted everything from
cellphones to televisions,
some stores were overwhelmed
by the amount of e-trash they
received." + ], + [ + "In a discovery sure to set off
a firestorm of debate over
human migration to the western
hemisphere, archaeologists in
South Carolina say they have
uncovered evidence that people
lived in eastern North America
at least 50,000 years ago -
far earlier than" + ], + [ + "AP - Former president Bill
Clinton on Monday helped
launch a new Internet search
company backed by the Chinese
government which says its
technology uses artificial
intelligence to produce better
results than Google Inc." + ], + [ + "The International Monetary
Fund expressed concern Tuesday
about the impact of the
troubles besetting oil major
Yukos on Russia #39;s standing
as a place to invest." + ], + [ + "Perhaps the sight of Maria
Sharapova opposite her tonight
will jog Serena Williams #39;
memory. Wimbledon. The final.
You and Maria." + ], + [ + "At least 79 people were killed
and 74 were missing after some
of the worst rainstorms in
recent years triggered
landslides and flash floods in
southwest China, disaster
relief officials said
yesterday." + ], + [ + "LinuxWorld Conference amp;
Expo will come to Boston for
the first time in February,
underscoring the area's
standing as a hub for the
open-source software being
adopted by thousands of
businesses." + ], + [ + "BANGKOK Thai military aircraft
dropped 100 million paper
birds over southern Thailand
on Sunday in a gesture
intended to promote peace in
mainly Muslim provinces, where
more than 500 people have died
this year in attacks by
separatist militants and" + ], + [ + "Insurgents threatened on
Saturday to cut the throats of
two Americans and a Briton
seized in Baghdad, and
launched a suicide car bomb
attack on Iraqi security
forces in Kirkuk that killed
at least 23 people." + ], + [ + "Five days after making the
putt that won the Ryder Cup,
Colin Montgomerie looked set
to miss the cut at a European
PGA tour event." + ], + [ + "Sun Microsystems plans to
release its Sun Studio 10
development tool in the fourth
quarter of this year,
featuring support for 64-bit
applications running on AMD
Opteron and Nocona processors,
Sun officials said on Tuesday." + ], + [ + "Karachi - Captain Inzamam ul-
Haq and coach Bob Woolmer came
under fire on Thursday for
choosing to bat first on a
tricky pitch after Pakistan
#39;s humiliating defeat in
the ICC Champions Trophy semi-
final." + ], + [ + "Scottish champions Celtic saw
their three-year unbeaten home
record in Europe broken
Tuesday as they lost 3-1 to
Barcelona in the Champions
League Group F opener." + ], + [ + "Interest rates on short-term
Treasury securities rose in
yesterday's auction. The
Treasury Department sold \\$19
billion in three-month bills
at a discount rate of 1.710
percent, up from 1.685 percent
last week. An additional \\$17
billion was sold in six-month
bills at a rate of 1.950
percent, up from 1.870
percent." + ], + [ + "Most IT Managers won #39;t
question the importance of
security, but this priority
has been sliding between the
third and fourth most
important focus for companies." + ], + [ + "AP - Andy Roddick searched out
Carlos Moya in the throng of
jumping, screaming Spanish
tennis players, hoping to
shake hands." + ], + [ + "AP - The Federal Trade
Commission on Thursday filed
the first case in the country
against software companies
accused of infecting computers
with intrusive \"spyware\" and
then trying to sell people the
solution." + ], + [ + "While shares of Apple have
climbed more than 10 percent
this week, reaching 52-week
highs, two research firms told
investors Friday they continue
to remain highly bullish about
the stock." + ], + [ + "Moody #39;s Investors Service
on Wednesday said it may cut
its bond ratings on HCA Inc.
(HCA.N: Quote, Profile,
Research) deeper into junk,
citing the hospital operator
#39;s plan to buy back about
\\$2." + ], + [ + "States are now barred from
imposing telecommunications
regulations on Net phone
providers." + ], + [ + "Telekom Austria AG, the
country #39;s biggest phone
operator, won the right to buy
Bulgaria #39;s largest mobile
phone company, MobilTel EAD,
for 1.6 billion euros (\\$2.1
billion), an acquisition that
would add 3 million
subscribers." + ], + [ + "Shares of ID Biomedical jumped
after the company reported
Monday that it signed long-
term agreements with the three
largest flu vaccine
wholesalers in the United
States in light of the
shortage of vaccine for the
current flu season." + ], + [ + "ENGLAND captain and Real
Madrid midfielder David
Beckham has played down
speculation that his club are
moving for England manager
Sven-Goran Eriksson." + ], + [ + "The federal government closed
its window on the oil industry
Thursday, saying that it is
selling its last 19 per cent
stake in Calgary-based Petro-
Canada." + ], + [ + "Strong sales of new mobile
phone models boosts profits at
Carphone Warehouse but the
retailer's shares fall on
concerns at a decline in
profits from pre-paid phones." + ], + [ + " NEW YORK (Reuters) - IBM and
top scientific research
organizations are joining
forces in a humanitarian
effort to tap the unused
power of millions of computers
and help solve complex social
problems." + ], + [ + " NEW YORK, Nov. 11 -- The 40
percent share price slide in
Merck #38; Co. in the five
weeks after it pulled the
painkiller Vioxx off the
market highlighted larger
problems in the pharmaceutical
industry that may depress
performance for years,
according to academics and
stock analysts who follow the
sector." + ], + [ + "Low-fare carrier Southwest
Airlines Co. said Thursday
that its third-quarter profit
rose 12.3 percent to beat Wall
Street expectations despite
higher fuel costs." + ], + [ + "Another shock hit the drug
sector Friday when
pharmaceutical giant Pfizer
Inc. announced that it found
an increased heart risk to
patients for its blockbuster
arthritis drug Celebrex." + ], + [ + "AFP - National Basketball
Association players trying to
win a fourth consecutive
Olympic gold medal for the
United States have gotten the
wake-up call that the \"Dream
Team\" days are done even if
supporters have not." + ], + [ + " BEIJING (Reuters) - Secretary
of State Colin Powell will
urge China Monday to exert its
influence over North Korea to
resume six-party negotiations
on scrapping its suspected
nuclear weapons program." + ], + [ + "Reuters - An Israeli missile
strike killed at least\\two
Hamas gunmen in Gaza City
Monday, a day after a
top\\commander of the Islamic
militant group was killed in a
similar\\attack, Palestinian
witnesses and security sources
said." + ], + [ + "England will be seeking their
third clean sweep of the
summer when the NatWest
Challenge against India
concludes at Lord #39;s. After
beating New Zealand and West
Indies 3-0 and 4-0 in Tests,
they have come alive" + ], + [ + "AP - The Pentagon has restored
access to a Web site that
assists soldiers and other
Americans living overseas in
voting, after receiving
complaints that its security
measures were preventing
legitimate voters from using
it." + ], + [ + "The number of US information
technology workers rose 2
percent to 10.5 million in the
first quarter of this year,
but demand for them is
dropping, according to a new
report." + ], + [ + "Montreal - The British-built
Canadian submarine HMCS
Chicoutimi, crippled since a
fire at sea that killed one of
its crew, is under tow and is
expected in Faslane, Scotland,
early next week, Canadian
naval commander Tyrone Pile
said on Friday." + ], + [ + "AP - Grizzly and black bears
killed a majority of elk
calves in northern Yellowstone
National Park for the second
year in a row, preliminary
study results show." + ], + [ + "Thirty-five Pakistanis freed
from the US Guantanamo Bay
prison camp arrived home on
Saturday and were taken
straight to prison for further
interrogation, the interior
minister said." + ], + [ + "AP - Mark Richt knows he'll
have to get a little creative
when he divvies up playing
time for Georgia's running
backs next season. Not so on
Saturday. Thomas Brown is the
undisputed starter for the
biggest game of the season." + ], + [ + "Witnesses in the trial of a US
soldier charged with abusing
prisoners at Abu Ghraib have
told the court that the CIA
sometimes directed abuse and
orders were received from
military command to toughen
interrogations." + ], + [ + "Insurance firm says its board
now consists of its new CEO
Michael Cherkasky and 10
outside members. NEW YORK
(Reuters) - Marsh amp;
McLennan Cos." + ], + [ + "Michael Phelps, the six-time
Olympic champion, issued an
apology yesterday after being
arrested and charged with
drunken driving in the United
States." + ], + [ + "AP - Italian and Lebanese
authorities have arrested 10
suspected terrorists who
planned to blow up the Italian
Embassy in Beirut, an Italian
news agency and the Defense
Ministry said Tuesday." + ], + [ + "CORAL GABLES, Fla. -- John F.
Kerry and President Bush
clashed sharply over the war
in Iraq last night during the
first debate of the
presidential campaign season,
with the senator from
Massachusetts accusing" + ], + [ + "GONAIVES, Haiti -- While
desperately hungry flood
victims wander the streets of
Gonaives searching for help,
tons of food aid is stacking
up in a warehouse guarded by
United Nations peacekeepers." + ], + [ + "As part of its much-touted new
MSN Music offering, Microsoft
Corp. (MSFT) is testing a Web-
based radio service that
mimics nearly 1,000 local
radio stations, allowing users
to hear a version of their
favorite radio station with
far fewer interruptions." + ], + [ + "AFP - The resignation of
disgraced Fiji Vice-President
Ratu Jope Seniloli failed to
quell anger among opposition
leaders and the military over
his surprise release from
prison after serving just
three months of a four-year
jail term for his role in a
failed 2000 coup." + ], + [ + "ATHENS Larry Brown, the US
coach, leaned back against the
scorer #39;s table, searching
for support on a sinking ship.
His best player, Tim Duncan,
had just fouled out, and the
options for an American team
that" + ], + [ + "Sourav Ganguly files an appeal
against a two-match ban
imposed for time-wasting." + ], + [ + "Ziff Davis - A quick
resolution to the Mambo open-
source copyright dispute seems
unlikely now that one of the
parties has rejected an offer
for mediation." + ], + [ + "Greek police surrounded a bus
full of passengers seized by
armed hijackers along a
highway from an Athens suburb
Wednesday, police said." + ], + [ + "Spain have named an unchanged
team for the Davis Cup final
against the United States in
Seville on 3-5 December.
Carlos Moya, Juan Carlos
Ferrero, Rafael Nadal and
Tommy Robredo will take on the
US in front of 22,000 fans at
the converted Olympic stadium." + ], + [ + "BAGHDAD: A suicide car bomber
attacked a police convoy in
Baghdad yesterday as guerillas
kept pressure on Iraq #39;s
security forces despite a
bloody rout of insurgents in
Fallujah." + ], + [ + "Slot machine maker
International Game Technology
(IGT.N: Quote, Profile,
Research) on Tuesday posted
better-than-expected quarterly
earnings, as casinos bought" + ], + [ + "Fixtures and fittings from
Damien Hirst's restaurant
Pharmacy sell for 11.1, 8m
more than expected." + ], + [ + "Last Tuesday night, Harvard
knocked off rival Boston
College, which was ranked No.
1 in the country. Last night,
the Crimson knocked off
another local foe, Boston
University, 2-1, at Walter
Brown Arena, which marked the
first time since 1999 that
Harvard had beaten them both
in the same season." + ], + [ + "Venezuelan election officials
say they expect to announce
Saturday, results of a partial
audit of last Sunday #39;s
presidential recall
referendum." + ], + [ + "About 500 prospective jurors
will be in an Eagle, Colorado,
courtroom Friday, answering an
82-item questionnaire in
preparation for the Kobe
Bryant sexual assault trial." + ], + [ + "Students take note - endless
journeys to the library could
become a thing of the past
thanks to a new multimillion-
pound scheme to make classic
texts available at the click
of a mouse." + ], + [ + "Moscow - The next crew of the
International Space Station
(ISS) is to contribute to the
Russian search for a vaccine
against Aids, Russian
cosmonaut Salijan Sharipovthe
said on Thursday." + ], + [ + "Locked away in fossils is
evidence of a sudden solar
cooling. Kate Ravilious meets
the experts who say it could
explain a 3,000-year-old mass
migration - and today #39;s
global warming." + ], + [ + "By ANICK JESDANUN NEW YORK
(AP) -- Taran Rampersad didn't
complain when he failed to
find anything on his hometown
in the online encyclopedia
Wikipedia. Instead, he simply
wrote his own entry for San
Fernando, Trinidad and
Tobago..." + ], + [ + "Five years after catastrophic
floods and mudslides killed
thousands along Venezuela's
Caribbean coast, survivors in
this town still see the signs
of destruction - shattered
concrete walls and tall weeds
growing atop streets covered
in dried mud." + ], + [ + "How do you top a battle
between Marines and an alien
religious cult fighting to the
death on a giant corona in
outer space? The next logical
step is to take that battle to
Earth, which is exactly what
Microsoft" + ], + [ + "Sony Ericsson Mobile
Communications Ltd., the
mobile-phone venture owned by
Sony Corp. and Ericsson AB,
said third-quarter profit rose
45 percent on camera phone
demand and forecast this
quarter will be its strongest." + ], + [ + "NEW YORK, September 17
(newratings.com) - Alcatel
(ALA.NYS) has expanded its
operations and presence in the
core North American
telecommunication market with
two separate acquisitions for
about \\$277 million." + ], + [ + "Four U.S. agencies yesterday
announced a coordinated attack
to stem the global trade in
counterfeit merchandise and
pirated music and movies, an
underground industry that law-
enforcement officials estimate
to be worth \\$500 billion each
year." + ], + [ + "BEIJING The NBA has reached
booming, basketball-crazy
China _ but the league doesn
#39;t expect to make any money
soon. The NBA flew more than
100 people halfway around the
world for its first games in
China, featuring" + ], + [ + "The UN nuclear watchdog
confirmed Monday that nearly
400 tons of powerful
explosives that could be used
in conventional or nuclear
missiles disappeared from an
unguarded military
installation in Iraq." + ], + [ + " NEW YORK (Reuters) - Curt
Schilling pitched 6 2/3
innings and Manny Ramirez hit
a three-run homer in a seven-
run fourth frame to lead the
Boston Red Sox to a 9-3 win
over the host Anaheim Angels
in their American League
Divisional Series opener
Tuesday." + ], + [ + "AP - The game between the
Minnesota Twins and the New
York Yankees on Tuesday night
was postponed by rain." + ], + [ + "Oil giant Shell swept aside
nearly 100 years of history
today when it unveiled plans
to merge its UK and Dutch
parent companies. Shell said
scrapping its twin-board
structure" + ], + [ + "Caesars Entertainment Inc. on
Thursday posted a rise in
third-quarter profit as Las
Vegas hotels filled up and
Atlantic City properties
squeaked out a profit that was
unexpected by the company." + ], + [ + "Half of all U.S. Secret
Service agents are dedicated
to protecting President
Washington #151;and all the
other Presidents on U.S.
currency #151;from
counterfeiters." + ], + [ + "One of India #39;s leading
telecommunications providers
will use Cisco Systems #39;
gear to build its new
Ethernet-based broadband
network." + ], + [ + "Militants in Iraq have killed
the second of two US civilians
they were holding hostage,
according to a statement on an
Islamist website." + ], + [ + "PARIS The verdict is in: The
world #39;s greatest race car
driver, the champion of
champions - all disciplines
combined - is Heikki
Kovalainen." + ], + [ + "Pakistan won the toss and
unsurprisingly chose to bowl
first as they and West Indies
did battle at the Rose Bowl
today for a place in the ICC
Champions Trophy final against
hosts England." + ], + [ + "Shares of Beacon Roofing
Suppler Inc. shot up as much
as 26 percent in its trading
debut Thursday, edging out
bank holding company Valley
Bancorp as the biggest gainer
among a handful of new stocks
that went public this week." + ], + [ + "More than 4,000 American and
Iraqi soldiers mounted a
military assault on the
insurgent-held city of
Samarra." + ], + [ + "Maxime Faget conceived and
proposed the development of
the one-man spacecraft used in
Project Mercury, which put the
first American astronauts into
suborbital flight, then
orbital flight" + ], + [ + "The first weekend of holiday
shopping went from red-hot to
dead white, as a storm that
delivered freezing, snowy
weather across Colorado kept
consumers at home." + ], + [ + " MEXICO CITY (Reuters) -
Mexican President Vicente Fox
said on Monday he hoped
President Bush's re-election
meant a bilateral accord on
migration would be reached
before his own term runs out
at the end of 2006." + ], + [ + " LONDON (Reuters) - The dollar
fell within half a cent of
last week's record low against
the euro on Thursday after
capital inflows data added to
worries the United States may
struggle to fund its current
account deficit." + ], + [ + " BRUSSELS (Reuters) - French
President Jacques Chirac said
on Friday he had not refused
to meet Iraqi interim Prime
Minister Iyad Allawi after
reports he was snubbing the
Iraqi leader by leaving an EU
meeting in Brussels early." + ], + [ + "China has pledged to invest
\\$20 billion in Argentina in
the next 10 years, La Nacion
reported Wednesday. The
announcement came during the
first day of a two-day visit" + ], + [ + "NEW YORK - Former President
Bill Clinton was in good
spirits Saturday, walking
around his hospital room in
street clothes and buoyed by
thousands of get-well messages
as he awaited heart bypass
surgery early this coming
week, people close to the
family said. Clinton was
expected to undergo surgery as
early as Monday but probably
Tuesday, said Democratic Party
Chairman Terry McAuliffe, who
said the former president was
\"upbeat\" when he spoke to him
by phone Friday..." + ], + [ + "Toyota Motor Corp., the world
#39;s biggest carmaker by
value, will invest 3.8 billion
yuan (\\$461 million) with its
partner Guangzhou Automobile
Group to boost manufacturing
capacity in" + ], + [ + "Poland will cut its troops in
Iraq early next year and won
#39;t stay in the country
quot;an hour longer quot; than
needed, the country #39;s
prime minister said Friday." + ], + [ + "AP - Boston Red Sox center
fielder Johnny Damon is having
a recurrence of migraine
headaches that first bothered
him after a collision in last
year's playoffs." + ], + [ + "The patch fixes a flaw in the
e-mail server software that
could be used to get access to
in-boxes and information." + ], + [ + "Felix Cardenas of Colombia won
the 17th stage of the Spanish
Vuelta cycling race Wednesday,
while defending champion
Roberto Heras held onto the
overall leader #39;s jersey
for the sixth day in a row." + ], + [ + "AP - Being the biggest dog may
pay off at feeding time, but
species that grow too large
may be more vulnerable to
extinction, new research
suggests." + ], + [ + "A car bomb exploded at a US
military convoy in the
northern Iraqi city of Mosul,
causing several casualties,
the army and Iraqi officials
said." + ], + [ + "Newest Efficeon processor also
offers higher frequency using
less power." + ], + [ + "BAE Systems has launched a
search for a senior American
businessman to become a non-
executive director. The high-
profile appointment is
designed to strengthen the
board at a time when the
defence giant is" + ], + [ + "AP - In what it calls a first
in international air travel,
Finnair says it will let its
frequent fliers check in using
text messages on mobile
phones." + ], + [ + "Computer Associates
International is expected to
announce that its new chief
executive will be John
Swainson, an I.B.M. executive
with strong technical and
sales credentials." + ], + [ + "Established star Landon
Donovan and rising sensation
Eddie Johnson carried the
United States into the
regional qualifying finals for
the 2006 World Cup in emphatic
fashion Wednesday night." + ], + [ + "STOCKHOLM (Dow
Jones)--Expectations for
Telefon AB LM Ericsson #39;s
(ERICY) third-quarter
performance imply that while
sales of mobile telephony
equipment are expected to have
dipped, the company" + ], + [ + "ATHENS, Greece - Michael
Phelps took care of qualifying
for the Olympic 200-meter
freestyle semifinals Sunday,
and then found out he had been
added to the American team for
the evening's 400 freestyle
relay final. Phelps' rivals
Ian Thorpe and Pieter van den
Hoogenband and teammate Klete
Keller were faster than the
teenager in the 200 free
preliminaries..." + ], + [ + " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
intends to present a timetable
for withdrawal from Gaza to
lawmakers from his Likud Party
Tuesday despite a mutiny in
the right-wing bloc over the
plan." + ], + [ + "Search Engine for Programming
Code\\\\An article at Newsforge
pointed me to Koders (
http://www.koders.com ) a
search engine for finding
programming code. Nifty.\\\\The
front page allows you to
specify keywords, sixteen
languages (from ASP to VB.NET)
and sixteen licenses (from AFL
to ZPL -- fortunately there's
an information page to ..." + ], + [ + " #147;Apple once again was the
star of the show at the annual
MacUser awards, #148; reports
MacUser, #147;taking away six
Maxine statues including
Product of the Year for the
iTunes Music Store. #148;
Other Apple award winners:
Power Mac G5, AirPort Express,
20-inch Apple Cinema Display,
GarageBand and DVD Studio Pro
3. Nov 22" + ], + [ + " KABUL (Reuters) - Three U.N.
workers held by militants in
Afghanistan were in their
third week of captivity on
Friday after calls from both
sides for the crisis to be
resolved ahead of this
weekend's Muslim festival of
Eid al-Fitr." + ], + [ + "AP - A sweeping wildlife
preserve in southwestern
Arizona is among the nation's
10 most endangered refuges,
due in large part to illegal
drug and immigrant traffic and
Border Patrol operations, a
conservation group said
Friday." + ], + [ + "ROME, Oct 29 (AFP) - French
President Jacques Chirac urged
the head of the incoming
European Commission Friday to
take quot;the appropriate
decisions quot; to resolve a
row over his EU executive team
which has left the EU in
limbo." + ], + [ + "The Anglo-Dutch oil giant
Shell today sought to draw a
line under its reserves
scandal by announcing plans to
spend \\$15bn (8.4bn) a year to
replenish reserves and develop
production in its oil and gas
business." + ], + [ + "SEPTEMBER 20, 2004
(COMPUTERWORLD) - At
PeopleSoft Inc. #39;s Connect
2004 conference in San
Francisco this week, the
software vendor is expected to
face questions from users
about its ability to fend off
Oracle Corp." + ], + [ + "Russia's parliament will
launch an inquiry into a
school siege that killed over
300 hostages, President
Vladimir Putin said on Friday,
but analysts doubt it will
satisfy those who blame the
carnage on security services." + ], + [ + "Tony Blair talks to business
leaders about new proposals
for a major shake-up of the
English exam system." + ], + [ + "KUALA LUMPUR, Malaysia A
Malaysian woman has claimed a
new world record after living
with over six-thousand
scorpions for 36 days
straight." + ], + [ + "PBS's Charlie Rose quizzes Sun
co-founder Bill Joy,
Monster.com chief Jeff Taylor,
and venture capitalist John
Doerr." + ], + [ + "Circulation declined at most
major US newspapers in the
last half year, the latest
blow for an industry already
rocked by a scandal involving
circulation misstatements that
has undermined the confidence
of investors and advertisers." + ], + [ + "Skype Technologies is teaming
with Siemens to offer cordless
phone users the ability to
make Internet telephony calls,
in addition to traditional
calls, from their handsets." + ], + [ + "AP - After years of
resistance, the U.S. trucking
industry says it will not try
to impede or delay a new
federal rule aimed at cutting
diesel pollution." + ], + [ + "INDIANAPOLIS - ATA Airlines
has accepted a \\$117 million
offer from Southwest Airlines
that would forge close ties
between two of the largest US
discount carriers." + ], + [ + "could take drastic steps if
the talks did not proceed as
Tehran wants. Mehr news agency
quoted him as saying on
Wednesday. that could be used" + ], + [ + "Wizards coach Eddie Jordan
says the team is making a
statement that immaturity will
not be tolerated by suspending
Kwame Brown one game for not
taking part in a team huddle
during a loss to Denver." + ], + [ + "EVERETT Fire investigators
are still trying to determine
what caused a two-alarm that
destroyed a portion of a South
Everett shopping center this
morning." + ], + [ + "Umesh Patel, a 36-year old
software engineer from
California, debated until the
last minute." + ], + [ + "Sure, the PeopleSoft board
told shareholders to just say
no. This battle will go down
to the wire, and even
afterward Ellison could
prevail." + ], + [ + "Investors cheered by falling
oil prices and an improving
job picture sent stocks higher
Tuesday, hoping that the news
signals a renewal of economic
strength and a fall rally in
stocks." + ], + [ + "AP - Andy Roddick has yet to
face a challenge in his U.S.
Open title defense. He beat
No. 18 Tommy Robredo of Spain
6-3, 6-2, 6-4 Tuesday night to
move into the quarterfinals
without having lost a set." + ], + [ + "Now that hell froze over in
Boston, New England braces for
its rarest season -- a winter
of content. Red Sox fans are
adrift from the familiar
torture of the past." + ], + [ + " CHICAGO (Reuters) - Wal-Mart
Stores Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=WMT.N target
=/stocks/quickinfo/fullquote\"&
gt;WMT.N</A>, the
world's largest retailer, said
on Saturday it still
anticipates September U.S.
sales to be up 2 percent to 4
percent at stores open at
least a year." + ], + [ + " TOKYO (Reuters) - Tokyo's
Nikkei stock average opened
down 0.15 percent on
Wednesday as investors took a
breather from the market's
recent rises and sold shares
of gainers such as Sharp
Corp." + ], + [ + "AP - From now until the start
of winter, male tarantulas are
roaming around, searching for
female mates, an ideal time to
find out where the spiders
flourish in Arkansas." + ], + [ + "Israeli authorities have
launched an investigation into
death threats against Israeli
Prime Minister Ariel Sharon
and other officials supporting
his disengagement plan from
Gaza and parts of the West
Bank, Jerusalem police said
Tuesday." + ], + [ + "NewsFactor - While a U.S.
District Court continues to
weigh the legality of
\\Oracle's (Nasdaq: ORCL)
attempted takeover of
\\PeopleSoft (Nasdaq: PSFT),
Oracle has taken the necessary
steps to ensure the offer does
not die on the vine with
PeopleSoft's shareholders." + ], + [ + "NEW YORK : World oil prices
fell, capping a drop of more
than 14 percent in a two-and-
a-half-week slide triggered by
a perception of growing US
crude oil inventories." + ], + [ + "SUFFOLK -- Virginia Tech
scientists are preparing to
protect the state #39;s
largest crop from a disease
with strong potential to do
damage." + ], + [ + "It was a carefully scripted
moment when Russian President
Vladimir Putin began quoting
Taras Shevchenko, this country
#39;s 19th-century bard,
during a live television" + ], + [ + "China says it welcomes Russia
#39;s ratification of the
Kyoto Protocol that aims to
stem global warming by
reducing greenhouse-gas
emissions." + ], + [ + "Analysts said the smartphone
enhancements hold the
potential to bring PalmSource
into an expanding market that
still has room despite early
inroads by Symbian and
Microsoft." + ], + [ + "The Metropolitan
Transportation Authority is
proposing a tax increase to
raise \\$900 million a year to
help pay for a five-year
rebuilding program." + ], + [ + "AFP - The Canadian armed
forces chief of staff on was
elected to take over as head
of NATO's Military Committee,
the alliance's highest
military authority, military
and diplomatic sources said." + ], + [ + "AFP - Two proposed resolutions
condemning widespread rights
abuses in Sudan and Zimbabwe
failed to pass a UN committee,
mired in debate between
African and western nations." + ], + [ + " NEW YORK (Reuters) -
Citigroup Inc. <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=C.N targe
t=/stocks/quickinfo/fullquote\"
>C.N</A> the world's
largest financial services
company, said on Tuesday it
will acquire First American
Bank in the quickly-growing
Texas market." + ], + [ + " SINGAPORE (Reuters) - U.S.
oil prices hovered just below
\\$50 a barrel on Tuesday,
holding recent gains on a rash
of crude supply outages and
fears over thin heating oil
tanks." + ], + [ + "THE South Africans have called
the Wallabies scrum cheats as
a fresh round of verbal
warfare opened in the Republic
last night." + ], + [ + "The return of noted reformer
Nabil Amr to Palestinian
politics comes at a crucial
juncture." + ], + [ + "An overwhelming majority of
NHL players who expressed
their opinion in a poll said
they would not support a
salary cap even if it meant
saving a season that was
supposed to have started Oct.
13." + ], + [ + "Tony Eury Sr. oversees Dale
Earnhardt Jr. on the
racetrack, but Sunday he
extended his domain to Victory
Lane. Earnhardt was unbuckling
to crawl out of the No." + ], + [ + "AP - After two debates, voters
have seen President Bush look
peevish and heard him pass the
buck. They've watched Sen.
John Kerry deny he's a flip-
flopper and then argue that
Saddam Hussein was a threat
#151; and wasn't. It's no
wonder so few minds have
changed." + ], + [ + "(Sports Network) - The New
York Yankees try to move one
step closer to a division
title when they conclude their
critical series with the
Boston Red Sox at Fenway Park." + ], + [ + "US retail sales fell 0.3 in
August as rising energy costs
and bad weather persuaded
shoppers to reduce their
spending." + ], + [ + "The United States says the
Lebanese parliament #39;s
decision Friday to extend the
term of pro-Syrian President
Emile Lahoud made a
quot;crude mockery of
democratic principles." + ], + [ + "Kurt Busch claimed a stake in
the points lead in the NASCAR
Chase for the Nextel Cup
yesterday, winning the
Sylvania 300 at New Hampshire
International Speedway." + ], + [ + "TOKYO (AFP) - Japan #39;s
Fujitsu and Cisco Systems of
the US said they have agreed
to form a strategic alliance
focusing on routers and
switches that will enable
businesses to build advanced
Internet Protocol (IP)
networks." + ], + [ + "GEORGETOWN, Del., Oct. 28 --
Plaintiffs in a shareholder
lawsuit over former Walt
Disney Co. president Michael
Ovitz's \\$140 million
severance package attempted
Thursday to portray Ovitz as a
dishonest bumbler who botched
the hiring of a major
television executive and
pushed the release of a movie
that angered the Chinese
government, damaging Disney's
business prospects in the
country." + ], + [ + "US stocks gained ground in
early trading Thursday after
tame inflation reports and
better than expected jobless
news. Oil prices held steady
as Hurricane Ivan battered the
Gulf coast, where oil
operations have halted." + ], + [ + "It #39;s a new internet
browser. The first full
version, Firefox 1.0, was
launched earlier this month.
In the sense it #39;s a
browser, yes, but the
differences are larger than
the similarities." + ], + [ + "LOUDON, NH - As this
newfangled stretch drive for
the Nextel Cup championship
ensues, Jeff Gordon has to be
considered the favorite for a
fifth title." + ], + [ + "PepsiCo. Inc., the world #39;s
No. 2 soft- drink maker, plans
to buy General Mills Inc.
#39;s stake in their European
joint venture for \\$750
million in cash, giving it
complete ownership of Europe
#39;s largest snack-food
company." + ], + [ + "Microsoft will accelerate SP2
distribution to meet goal of
100 million downloads in two
months." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks opened little changed
on Friday, after third-
quarter gross domestic product
data showed the U.S. economy
grew at a slower-than-expected
pace." + ], + [ + "International Business
Machines Corp., taken to court
by workers over changes it
made to its traditional
pension plan, has decided to
stop offering any such plan to
new employees." + ], + [ + "NEW YORK - With four of its
executives pleading guilty to
price-fixing charges today,
Infineon will have a hard time
arguing that it didn #39;t fix
prices in its ongoing
litigation with Rambus." + ], + [ + "By nick giongco. YOU KNOW you
have reached the status of a
boxing star when ring
announcer extraordinaire
Michael Buffer calls out your
name in his trademark booming
voice during a high-profile
event like yesterday" + ], + [ + "Canadian Press - OTTAWA (CP) -
The prime minister says he's
been assured by President
George W. Bush that the U.S.
missile defence plan does not
necessarily involve the
weaponization of space." + ], + [ + "AP - Even with a big lead,
Eric Gagne wanted to pitch in
front of his hometown fans one
last time." + ], + [ + " NEW YORK (Reuters) - Limited
Brands Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=LTD.N target
=/stocks/quickinfo/fullquote\"&
gt;LTD.N</A> on
Thursday reported higher
quarterly operating profit as
cost controls and strong
lingerie sales offset poor
results at the retailer's
Express apparel stores." + ], + [ + "General Motors and
DaimlerChrysler are
collaborating on development
of fuel- saving hybrid engines
in hopes of cashing in on an
expanding market dominated by
hybrid leaders Toyota and
Honda." + ], + [ + "ATHENS, Greece -- Larry Brown
was despondent, the head of
the US selection committee was
defensive and an irritated
Allen Iverson was hanging up
on callers who asked what went
wrong." + ], + [ + "Fifty-six miners are dead and
another 92 are still stranded
underground after a gas blast
at the Daping coal mine in
Central China #39;s Henan
Province, safety officials
said Thursday." + ], + [ + "BEIRUT, Lebanon - Three
Lebanese men and their Iraqi
driver have been kidnapped in
Iraq, the Lebanese Foreign
Ministry said Sunday, as
Iraq's prime minister said his
government was working for the
release of two Americans and a
Briton also being held
hostage. Gunmen snatched
the three Lebanese, who worked
for a travel agency with a
branch in Baghdad, as they
drove on the highway between
the capital and Fallujah on
Friday night, a ministry
official said..." + ], + [ + "PORTLAND, Ore. - It's been
almost a year since singer-
songwriter Elliott Smith
committed suicide, and fans
and friends will be looking
for answers as the posthumous
\"From a Basement on the Hill\"
is released..." + ], + [ + "AP - Courtney Brown refuses to
surrender to injuries. He's
already planning another
comeback." + ], + [ + " GAZA (Reuters) - Israel
expanded its military
offensive in northern Gaza,
launching two air strikes
early on Monday that killed
at least three Palestinians
and wounded two, including a
senior Hamas leader, witnesses
and medics said." + ], + [ + " TOKYO (Reuters) - Nintendo
Co. Ltd. raised its 2004
shipment target for its DS
handheld video game device by
40 percent to 2.8 million
units on Thursday after many
stores in Japan and the
United States sold out in the
first week of sales." + ], + [ + "It took an off-the-cuff
reference to a serial
murderer/cannibal to punctuate
the Robby Gordon storyline.
Gordon has been vilified by
his peers and put on probation" + ], + [ + "By BOBBY ROSS JR. Associated
Press Writer. play the Atlanta
Hawks. They will be treated to
free food and drink and have.
their pictures taken with
Mavericks players, dancers and
team officials." + ], + [ + "ARSENAL #39;S Brazilian World
Cup winning midfielder
Gilberto Silva is set to be
out for at least a month with
a back injury, the Premiership
leaders said." + ], + [ + "INDIANAPOLIS -- With a package
of academic reforms in place,
the NCAA #39;s next crusade
will address what its
president calls a dangerous
drift toward professionalism
and sports entertainment." + ], + [ + "Autodesk this week unwrapped
an updated version of its
hosted project collaboration
service targeted at the
construction and manufacturing
industries. Autodesk Buzzsaw
lets multiple, dispersed
project participants --
including building owners,
developers, architects,
construction teams, and
facility managers -- share and
manage data throughout the
life of a project, according
to Autodesk officials." + ], + [ + "AP - It was difficult for
Southern California's Pete
Carroll and Oklahoma's Bob
Stoops to keep from repeating
each other when the two
coaches met Thursday." + ], + [ + "Reuters - Sudan's government
resumed\\talks with rebels in
the oil-producing south on
Thursday while\\the United
Nations set up a panel to
investigate charges
of\\genocide in the west of
Africa's largest country." + ], + [ + "Andy Roddick, along with
Olympic silver medalist Mardy
Fish and the doubles pair of
twins Bob and Mike Bryan will
make up the US team to compete
with Belarus in the Davis Cup,
reported CRIENGLISH." + ], + [ + "NEW YORK - Optimism that the
embattled technology sector
was due for a recovery sent
stocks modestly higher Monday
despite a new revenue warning
from semiconductor company
Broadcom Inc. While
Broadcom, which makes chips
for television set-top boxes
and other electronics, said
high inventories resulted in
delayed shipments, investors
were encouraged as it said
future quarters looked
brighter..." + ], + [ + "A report indicates that many
giants of the industry have
been able to capture lasting
feelings of customer loyalty." + ], + [ + "It is the news that Internet
users do not want to hear: the
worldwide web is in danger of
collapsing around us. Patrick
Gelsinger, the chief
technology officer for
computer chip maker Intel,
told a conference" + ], + [ + " WASHINGTON (Reuters) - U.S.
employers hired just 96,000
workers in September, the
government said on Friday in a
weak jobs snapshot, the final
one ahead of presidential
elections that also fueled
speculation about a pause in
interest-rate rises." + ], + [ + "Cisco Systems CEO John
Chambers said today that his
company plans to offer twice
as many new products this year
as ever before." + ], + [ + "While the world #39;s best
athletes fight the noble
Olympic battle in stadiums and
pools, their fans storm the
streets of Athens, turning the
Greek capital into a huge
international party every
night." + ], + [ + "Carlos Barrios Orta squeezed
himself into his rubber diving
suit, pulled on an 18-pound
helmet that made him look like
an astronaut, then lowered
himself into the sewer. He
disappeared into the filthy
water, which looked like some
cauldron of rancid beef stew,
until the only sign of him was
air bubbles breaking the
surface." + ], + [ + "The mutilated body of a woman
of about sixty years,
amputated of arms and legs and
with the sliced throat, has
been discovered this morning
in a street of the south of
Faluya by the Marines,
according to a statement by
AFP photographer." + ], + [ + "Every day, somewhere in the
universe, there #39;s an
explosion that puts the power
of the Sun in the shade. Steve
Connor investigates the riddle
of gamma-ray bursts." + ], + [ + "Ten months after NASA #39;s
twin rovers landed on Mars,
scientists reported this week
that both robotic vehicles are
still navigating their rock-
studded landscapes with all
instruments operating" + ], + [ + "EVERTON showed they would not
be bullied into selling Wayne
Rooney last night by rejecting
a 23.5million bid from
Newcastle - as Manchester
United gamble on Goodison
#39;s resolve to keep the
striker." + ], + [ + "SAN FRANCISCO (CBS.MW) -- The
magazine known for evaluating
cars and electronics is
setting its sights on finding
the best value and quality of
prescription drugs on the
market." + ], + [ + "After months of legal
wrangling, the case of
<em>People v. Kobe Bean
Bryant</em> commences on
Friday in Eagle, Colo., with
testimony set to begin next
month." + ], + [ + "(SH) - In Afghanistan, Hamid
Karzai defeated a raft of
candidates to win his historic
election. In Iraq, more than
200 political parties have
registered for next month
#39;s elections." + ], + [ + "Lyon coach Paul le Guen has
admitted his side would be
happy with a draw at Old
Trafford on Tuesday night. The
three-times French champions
have assured themselves of
qualification for the
Champions League" + ], + [ + "British scientists say they
have found a new, greener way
to power cars and homes using
sunflower oil, a commodity
more commonly used for cooking
fries." + ], + [ + "A mouse, a house, and your
tax-planning spouse all factor
huge in the week of earnings
that lies ahead." + ], + [ + "The United States #39; top
computer-security official has
resigned after a little more
than a year on the job, the US
Department of Homeland
Security said on Friday." + ], + [ + "Reuters - Barry Bonds failed
to collect a hit in\\his bid to
join the 700-homer club, but
he did score a run to\\help the
San Francisco Giants edge the
host Milwaukee Brewers\\3-2 in
National League action on
Tuesday." + ], + [ + " SAN FRANCISCO (Reuters) -
Intel Corp. <A HREF=\"http:/
/www.reuters.co.uk/financeQuot
eLookup.jhtml?ticker=INTC.O
qtype=sym infotype=info
qcat=news\">INTC.O</A>
on Thursday outlined its
vision of the Internet of the
future, one in which millions
of computer servers would
analyze and direct network
traffic to make the Web safer
and more efficient." + ], + [ + "Margaret Hassan, who works for
charity Care International,
was taken hostage while on her
way to work in Baghdad. Here
are the main events since her
kidnapping." + ], + [ + "Check out new gadgets as
holiday season approaches." + ], + [ + "DALLAS (CBS.MW) -- Royal
Dutch/Shell Group will pay a
\\$120 million penalty to
settle a Securities and
Exchange Commission
investigation of its
overstatement of nearly 4.5
billion barrels of proven
reserves, the federal agency
said Tuesday." + ], + [ + "After waiting an entire summer
for the snow to fall and
Beaver Creek to finally open,
skiers from around the planet
are coming to check out the
Birds of Prey World Cup action
Dec. 1 - 5. Although everyones" + ], + [ + "TOKYO, Sep 08, 2004 (AFX-UK
via COMTEX) -- Sony Corp will
launch its popular projection
televisions with large liquid
crystal display (LCD) screens
in China early next year, a
company spokeswoman said." + ], + [ + "I confess that I am a complete
ignoramus when it comes to
women #39;s beach volleyball.
In fact, I know only one thing
about the sport - that it is
the supreme aesthetic
experience available on planet
Earth." + ], + [ + "Manchester United Plc may
offer US billionaire Malcolm
Glazer a seat on its board if
he agrees to drop a takeover
bid for a year, the Observer
said, citing an unidentified
person in the soccer industry." + ], + [ + "PHOENIX America West Airlines
has backed away from a
potential bidding war for
bankrupt ATA Airlines, paving
the way for AirTran to take
over ATA operations." + ], + [ + "US stock-index futures
declined. Dow Jones Industrial
Average shares including
General Electric Co. slipped
in Europe. Citigroup Inc." + ], + [ + "For a guy who spent most of
his first four professional
seasons on the disabled list,
Houston Astros reliever Brad
Lidge has developed into quite
the ironman these past two
days." + ], + [ + "AFP - Outgoing EU competition
commissioner Mario Monti wants
to reach a decision on
Oracle's proposed takeover of
business software rival
PeopleSoft by the end of next
month, an official said." + ], + [ + "Former Bengal Corey Dillon
found his stride Sunday in his
second game for the New
England Patriots. Dillon
gained 158 yards on 32 carries
as the Patriots beat the
Arizona Cardinals, 23-12, for
their 17th victory in a row." + ], + [ + "If legend is to be believed,
the end of a victorious war
was behind Pheidippides #39;
trek from Marathon to Athens
2,500 years ago." + ], + [ + " WASHINGTON (Reuters) -
Satellite companies would be
able to retransmit
broadcasters' television
signals for another five
years but would have to offer
those signals on a single
dish, under legislation
approved by Congress on
Saturday." + ], + [ + " BEIJING (Reuters) - Wimbledon
champion Maria Sharapova
demolished fellow Russian
Tatiana Panova 6-1, 6-1 to
advance to the quarter-finals
of the China Open on
Wednesday." + ], + [ + "Sven Jaschan, who may face
five years in prison for
spreading the Netsky and
Sasser worms, is now working
in IT security. Photo: AFP." + ], + [ + "Padres general manager Kevin
Towers called Expos general
manager Omar Minaya on
Thursday afternoon and told
him he needed a shortstop
because Khalil Greene had
broken his" + ], + [ + "Motorsport.com. Nine of the
ten Formula One teams have
united to propose cost-cutting
measures for the future, with
the notable exception of
Ferrari." + ], + [ + "Reuters - The United States
modified its\\call for U.N.
sanctions against Sudan on
Tuesday but still kept\\up the
threat of punitive measures if
Khartoum did not
stop\\atrocities in its Darfur
region." + ], + [ + "Human rights and environmental
activists have hailed the
award of the 2004 Nobel Peace
Prize to Wangari Maathai of
Kenya as fitting recognition
of the growing role of civil
society" + ], + [ + "Federal Reserve Chairman Alan
Greenspan has done it again.
For at least the fourth time
this year, he has touched the
electrified third rail of
American politics - Social
Security." + ], + [ + "An Al Qaeda-linked militant
group beheaded an American
hostage in Iraq and threatened
last night to kill another two
Westerners in 24 hours unless
women prisoners were freed
from Iraqi jails." + ], + [ + "TechWeb - An Indian Institute
of Technology professor--and
open-source evangelist--
discusses the role of Linux
and open source in India." + ], + [ + "Here are some of the latest
health and medical news
developments, compiled by
editors of HealthDay: -----
Contaminated Fish in Many U.S.
Lakes and Rivers Fish
that may be contaminated with
dioxin, mercury, PCBs and
pesticides are swimming in
more than one-third of the
United States' lakes and
nearly one-quarter of its
rivers, according to a list of
advisories released by the
Environmental Protection
Agency..." + ], + [ + "Argentina, Denmark, Greece,
Japan and Tanzania on Friday
won coveted two-year terms on
the UN Security Council at a
time when pressure is mounting
to expand the powerful
15-nation body." + ], + [ + "Description: Scientists say
the arthritis drug Bextra may
pose increased risk of
cardiovascular troubles.
Bextra is related to Vioxx,
which was pulled off the
market in September for the
same reason." + ], + [ + "Steve Francis and Shaquille O
#39;Neal enjoyed big debuts
with their new teams. Kobe
Bryant found out he can #39;t
carry the Lakers all by
himself." + ], + [ + "The trial of a lawsuit by Walt
Disney Co. shareholders who
accuse the board of directors
of rubberstamping a deal to
hire Michael Ovitz" + ], + [ + "Australia, by winning the
third Test at Nagpur on
Friday, also won the four-
match series 2-0 with one
match to go. Australia had
last won a Test series in
India way back in December
1969 when Bill Lawry #39;s
team beat Nawab Pataudi #39;s
Indian team 3-1." + ], + [ + "LOS ANGELES Grocery giant
Albertsons says it has
purchased Bristol Farms, which
operates eleven upscale stores
in Southern California." + ], + [ + "ISLAMABAD: Pakistan and India
agreed on Friday to re-open
the Khokhropar-Munabao railway
link, which was severed nearly
40 years ago." + ], + [ + "Final Score: Connecticut 61,
New York 51 New York, NY
(Sports Network) - Nykesha
Sales scored 15 points to lead
Connecticut to a 61-51 win
over New York in Game 1 of
their best-of-three Eastern
Conference Finals series at
Madison Square Garden." + ], + [ + "NEW YORK - Stocks moved higher
Friday as a stronger than
expected retail sales report
showed that higher oil prices
aren't scaring consumers away
from spending. Federal Reserve
Chairman Alan Greenspan's
positive comments on oil
prices also encouraged
investors..." + ], + [ + "The figures from a survey
released today are likely to
throw more people into the
ranks of the uninsured,
analysts said." + ], + [ + "Bill Gates is giving his big
speech right now at Microsofts
big Digital Entertainment
Anywhere event in Los Angeles.
Well have a proper report for
you soon, but in the meantime
we figured wed" + ], + [ + "Spinal and non-spinal
fractures are reduced by
almost a third in women age 80
or older who take a drug
called strontium ranelate,
European investigators
announced" + ], + [ + "JB Oxford Holdings Inc., a
Beverly Hills-based discount
brokerage firm, was sued by
the Securities and Exchange
Commission for allegedly
allowing thousands of improper
trades in more than 600 mutual
funds." + ], + [ + "BERLIN: Apple Computer Inc is
planning the next wave of
expansion for its popular
iTunes online music store with
a multi-country European
launch in October, the
services chief architect said
on Wednesday." + ], + [ + "Charlotte, NC -- LeBron James
poured in a game-high 19
points and Jeff McInnis scored
18 as the Cleveland Cavaliers
routed the Charlotte Bobcats,
106-89, at the Charlotte
Coliseum." + ], + [ + "Goldman Sachs reported strong
fourth quarter and full year
earnings of \\$1.19bn, up 36
per cent on the previous
quarter. Full year profit rose
52 per cent from the previous
year to \\$4.55bn." + ], + [ + "Saddam Hussein lives in an
air-conditioned 10-by-13 foot
cell on the grounds of one of
his former palaces, tending
plants and proclaiming himself
Iraq's lawful ruler." + ], + [ + "Lehmann, who was at fault in
two matches in the tournament
last season, was blundering
again with the German set to
take the rap for both Greek
goals." + ], + [ + "Calls to 13 other countries
will be blocked to thwart
auto-dialer software." + ], + [ + "AP - Brazilian U.N.
peacekeepers will remain in
Haiti until presidential
elections are held in that
Caribbean nation sometime next
year, President Luiz Inacio
Lula da Silva said Monday." + ], + [ + "com September 27, 2004, 5:00
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." + ], + [ + "Jeju Island, South Korea
(Sports Network) - Grace Park
and Carin Koch posted matching
rounds of six-under-par 66 on
Friday to share the lead after
the first round of the CJ Nine
Bridges Classic." + ], + [ + "SPACE.com - The Zero Gravity
Corporation \\ has been given
the thumbs up by the Federal
Aviation Administration (FAA)
to \\ conduct quot;weightless
flights quot; for the general
public, providing the \\
sensation of floating in
space." + ], + [ + "A 32-year-old woman in Belgium
has become the first woman
ever to give birth after
having ovarian tissue removed,
frozen and then implanted back
in her body." + ], + [ + "Argosy Gaming (AGY:NYSE - news
- research) jumped in early
trading Thursday, after the
company agreed to be acquired
by Penn National Gaming
(PENN:Nasdaq - news -
research) in a \\$1." + ], + [ + "No sweat, says the new CEO,
who's imported from IBM. He
also knows this will be the
challenge of a lifetime." + ], + [ + "Iraq is quot;working 24 hours
a day to ... stop the
terrorists, quot; interim
Iraqi Prime Minister Ayad
Allawi said Monday. Iraqis are
pushing ahead with reforms and
improvements, Allawi told" + ], + [ + "AP - Their discoveries may be
hard for many to comprehend,
but this year's Nobel Prize
winners still have to explain
what they did and how they did
it." + ], + [ + "The DuPont Co. has agreed to
pay up to \\$340 million to
settle a lawsuit that it
contaminated water supplies in
West Virginia and Ohio with a
chemical used to make Teflon,
one of its best-known brands." + ], + [ + "Benfica and Real Madrid set
the standard for soccer
success in Europe in the late
1950s and early #39;60s. The
clubs have evolved much
differently, but both have
struggled" + ], + [ + "AP - Musicians, composers and
authors were among the more
than two dozen people
Wednesday honored with
National Medal of Arts and
National Humanities awards at
the White House." + ], + [ + "More than 15 million homes in
the UK will be able to get on-
demand movies by 2008, say
analysts." + ], + [ + "Wynton Marsalis, the trumpet-
playing star and artistic
director of Jazz at Lincoln
Center, has become an entity
above and around the daily
jazz world." + ], + [ + "A well-researched study by the
National Academy of Sciences
makes a persuasive case for
refurbishing the Hubble Space
Telescope. NASA, which is
reluctant to take this
mission, should rethink its
position." + ], + [ + "BUENOS AIRES: Pakistan has
ratified the Kyoto Protocol on
Climatic Change, Environment
Minister Malik Khan said on
Thursday. He said that
Islamabad has notified UN
authorities of ratification,
which formally comes into
effect in February 2005." + ], + [ + "Reuters - Jeffrey Greenberg,
chairman and chief\\executive
of embattled insurance broker
Marsh McLennan Cos.\\, is
expected to step down within
hours, a newspaper\\reported on
Friday, citing people close to
the discussions." + ], + [ + "Staff Sgt. Johnny Horne, Jr.,
and Staff Sgt. Cardenas Alban
have been charged with murder
in the death of an Iraqi, the
1st Cavalry Division announced
Monday." + ], + [ + "LONDON -- In a deal that
appears to buck the growing
trend among governments to
adopt open-source
alternatives, the U.K.'s
Office of Government Commerce
(OGC) is negotiating a renewal
of a three-year agreement with
Microsoft Corp." + ], + [ + "WASHINGTON - A Pennsylvania
law requiring Internet service
providers (ISPs) to block Web
sites the state's prosecuting
attorneys deem to be child
pornography has been reversed
by a U.S. federal court, with
the judge ruling the law
violated free speech rights." + ], + [ + "The Microsoft Corp. chairman
receives four million e-mails
a day, but practically an
entire department at the
company he founded is
dedicated to ensuring that
nothing unwanted gets into his
inbox, the company #39;s chief
executive said Thursday." + ], + [ + "The 7100t has a mobile phone,
e-mail, instant messaging, Web
browsing and functions as an
organiser. The device looks
like a mobile phone and has
the features of the other
BlackBerry models, with a
large screen" + ], + [ + "President Vladimir Putin makes
a speech as he hosts Russia
#39;s Olympic athletes at a
Kremlin banquet in Moscow,
Thursday, Nov. 4, 2004." + ], + [ + "Apple Computer has unveiled
two new versions of its hugely
successful iPod: the iPod
Photo and the U2 iPod. Apple
also has expanded" + ], + [ + "Pakistan are closing in fast
on Sri Lanka #39;s first
innings total after impressing
with both ball and bat on the
second day of the opening Test
in Faisalabad." + ], + [ + "A state regulatory board
yesterday handed a five-year
suspension to a Lawrence
funeral director accused of
unprofessional conduct and
deceptive practices, including
one case where he refused to
complete funeral arrangements
for a client because she had
purchased a lower-priced
casket elsewhere." + ], + [ + "AP - This year's hurricanes
spread citrus canker to at
least 11,000 trees in
Charlotte County, one of the
largest outbreaks of the
fruit-damaging infection to
ever affect Florida's citrus
industry, state officials
said." + ], + [ + "AFP - Hundreds of Buddhists in
southern Russia marched
through snow to see and hear
the Dalai Lama as he continued
a long-awaited visit to the
country in spite of Chinese
protests." + ], + [ + "British officials were on
diplomatic tenterhooks as they
awaited the arrival on
Thursday of French President
Jacques Chirac for a two-day
state visit to Britain." + ], + [ + " SYDNEY (Reuters) - A group of
women on Pitcairn Island in
the South Pacific are standing
by their men, who face
underage sex charges, saying
having sex at age 12 is a
tradition dating back to 18th
century mutineers who settled
on the island." + ], + [ + "Two aircraft are flying out
from the UK on Sunday to
deliver vital aid and supplies
to Haiti, which has been
devastated by tropical storm
Jeanne." + ], + [ + "A scare triggered by a
vibrating sex toy shut down a
major Australian regional
airport for almost an hour
Monday, police said. The
vibrating object was
discovered Monday morning" + ], + [ + "IBM moved back into the iSCSI
(Internet SCSI) market Friday
with a new array priced at
US\\$3,000 and aimed at the
small and midsize business
market." + ], + [ + "Ron Artest has been hit with a
season long suspension,
unprecedented for the NBA
outside doping cases; Stephen
Jackson banned for 30 games;
Jermaine O #39;Neal for 25
games and Anthony Johnson for
five." + ], + [ + "The California Public
Utilities Commission on
Thursday upheld a \\$12.1
million fine against Cingular
Wireless, related to a two-
year investigation into the
cellular telephone company
#39;s business practices." + ], + [ + "Barclays, the British bank
that left South Africa in 1986
after apartheid protests, may
soon resume retail operations
in the nation." + ], + [ + "AP - An Indiana congressional
candidate abruptly walked off
the set of a debate because
she got stage fright." + ], + [ + "Italian-based Parmalat is
suing its former auditors --
Grant Thornton International
and Deloitte Touche Tohmatsu
-- for billions of dollars in
damages. Parmalat blames its
demise on the two companies
#39; mismanagement of its
finances." + ], + [ + "Reuters - Having reached out
to Kashmiris during a two-day
visit to the region, the prime
minister heads this weekend to
the country's volatile
northeast, where public anger
is high over alleged abuses by
Indian soldiers." + ], + [ + "SAN JUAN IXTAYOPAN, Mexico --
A pair of wooden crosses
outside the elementary school
are all that mark the macabre
site where, just weeks ago, an
angry mob captured two federal
police officers, beat them
unconscious, and set them on
fire." + ], + [ + "Three shots behind Grace Park
with five holes to go, six-
time LPGA player of the year
Sorenstam rallied with an
eagle, birdie and three pars
to win her fourth Samsung
World Championship by three
shots over Park on Sunday." + ], + [ + "update An alliance of
technology workers on Tuesday
accused conglomerate Honeywell
International of planning to
move thousands of jobs to low-
cost regions over the next
five years--a charge that
Honeywell denies." + ], + [ + "NSW Rugby CEO Fraser Neill
believes Waratah star Mat
Rogers has learned his lesson
after he was fined and ordered
to do community service
following his controversial
comments about the club rugby
competition." + ], + [ + "American Technology Research
analyst Shaw Wu has initiated
coverage of Apple Computer
(AAPL) with a #39;buy #39;
recommendation, and a 12-month
target of US\\$78 per share." + ], + [ + "washingtonpost.com - Cell
phone shoppers looking for new
deals and features didn't find
them yesterday, a day after
Sprint Corp. and Nextel
Communications Inc. announced
a merger that the phone
companies promised would shake
up the wireless business." + ], + [ + "ATHENS: China, the dominant
force in world diving for the
best part of 20 years, won six
out of eight Olympic titles in
Athens and prompted
speculation about a clean
sweep when they stage the
Games in Beijing in 2008." + ], + [ + " NEW YORK (Reuters) - Northrop
Grumman Corp. <A HREF=\"http
://www.investor.reuters.com/Fu
llQuote.aspx?ticker=NOC.N targ
et=/stocks/quickinfo/fullquote
\">NOC.N</A> reported
higher third-quarter earnings
on Wednesday and an 11
percent increase in sales on
strength in its mission
systems, integrated systems,
ships and space technology
businesses." + ], + [ + "JAPANESE GIANT Sharp has
pulled the plug on its Linux-
based PDAs in the United
States as no-one seems to want
them. The company said that it
will continue to sell them in
Japan where they sell like hot
cakes" + ], + [ + "DUBLIN : An Olympic Airlines
plane diverted to Ireland
following a bomb alert, the
second faced by the Greek
carrier in four days, resumed
its journey to New York after
no device was found on board,
airport officials said." + ], + [ + "New NavOne offers handy PDA
and Pocket PC connectivity,
but fails to impress on
everything else." + ], + [ + "TORONTO (CP) - Shares of
Iamgold fell more than 10 per
cent after its proposed merger
with Gold Fields Ltd. was
thrown into doubt Monday as
South Africa #39;s Harmony
Gold Mining Company Ltd." + ], + [ + "The Marvel deal calls for
Mforma to work with the comic
book giant to develop a
variety of mobile applications
based on Marvel content." + ], + [ + " LONDON (Reuters) - Oil prices
tumbled again on Monday to an
8-week low under \\$46 a
barrel, as growing fuel stocks
in the United States eased
fears of a winter supply
crunch." + ], + [ + " WASHINGTON (Reuters) - The
United States said on Friday
it is preparing a new U.N.
resolution on Darfur and that
Secretary of State Colin
Powell might address next week
whether the violence in
western Sudan constitutes
genocide." + ], + [ + "The Houston Astros won their
19th straight game at home and
are one game from winning
their first playoff series in
42 years." + ], + [ + "washingtonpost.com - The
Portable Media Center -- a
new, Microsoft-conceived
handheld device that presents
video and photos as well as
music -- would be a decent
idea if there weren't such a
thing as lampposts. Or street
signs. Or trees. Or other
cars." + ], + [ + "Alcoa Inc., one of the world
#39;s top producers of
aluminum, said Monday that it
received an unsolicited
quot;mini-tender quot; offer
from Toronto-based TRC Capital
Corp." + ], + [ + "The European Commission is to
warn Greece about publishing
false information about its
public finances." + ], + [ + "The Air Force Reserve #39;s
Hurricane Hunters, those
fearless crews who dive into
the eyewalls of hurricanes to
relay critical data on
tropical systems, were chased
from their base on the
Mississippi Gulf Coast by
Hurricane Ivan." + ], + [ + " LONDON (Reuters) - U.S.
shares were expected to open
lower on Wednesday after
crude oil pushed to a fresh
high overnight, while Web
search engine Google Inc.
dented sentiment as it
slashed the price range on its
initial public offering." + ], + [ + "The eighth-seeded American
fell to sixth-seeded Elena
Dementieva of Russia, 0-6 6-2
7-6 (7-5), on Friday - despite
being up a break on four
occasions in the third set." + ], + [ + "TUCSON, Arizona (Ticker) --
No. 20 Arizona State tries to
post its first three-game
winning streak over Pac-10
Conference rival Arizona in 26
years when they meet Friday." + ], + [ + "NAJAF, Iraq : Iraq #39;s top
Shiite Muslim clerics, back in
control of Najaf #39;s Imam
Ali shrine after a four-month
militia occupation, met amid
the ruins of the city as life
spluttered back to normality." + ], + [ + "Embargo or not, Fidel Castro's
socialist paradise has quietly
become a pharmaceutical
powerhouse. (They're still
working on the capitalism
thing.) By Douglas Starr from
Wired magazine." + ], + [ + "AP - Phillip Fulmer kept his
cool when starting center
Jason Respert drove off in the
coach's golf cart at practice." + ], + [ + "GOALS from Wayne Rooney and
Ruud van Nistelrooy gave
Manchester United the win at
Newcastle. Alan Shearer
briefly levelled matters for
the Magpies but United managed
to scrape through." + ], + [ + "The telemarketer at the other
end of Orlando Castelblanco
#39;s line promised to reduce
the consumer #39;s credit card
debt by at least \\$2,500 and
get his 20 percent -- and
growing -- interest rates down
to single digits." + ], + [ + " NEW YORK (Reuters) - Blue-
chip stocks fell slightly on
Monday after No. 1 retailer
Wal-Mart Stores Inc. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=WMT
.N target=/stocks/quickinfo/fu
llquote\">WMT.N</A>
reported lower-than-expected
Thanksgiving sales, while
technology shares were lifted
by a rally in Apple Computer
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AAPL.O target=/sto
cks/quickinfo/fullquote\">AA
PL.O</A>." + ], + [ + "Reuters - House of
Representatives
Majority\\Leader Tom DeLay,
admonished twice in six days
by his chamber's\\ethics
committee, withstood calls on
Thursday by rival\\Democrats
and citizen groups that he
step aside." + ], + [ + "WPP Group Inc., the world
#39;s second- largest
marketing and advertising
company, said it won the
bidding for Grey Global Group
Inc." + ], + [ + "AFP - Australia has accounted
for all its nationals known to
be working in Iraq following a
claim by a radical Islamic
group to have kidnapped two
Australians, Foreign Affairs
Minister Alexander Downer
said." + ], + [ + "A new worm can spy on users by
hijacking their Web cameras, a
security firm warned Monday.
The Rbot.gr worm -- the latest
in a long line of similar
worms; one security firm
estimates that more than 4,000
variations" + ], + [ + " NEW YORK (Reuters) - Stocks
were slightly lower on
Tuesday, as concerns about
higher oil prices cutting into
corporate profits and
consumer demand weighed on
sentiment, while retail sales
posted a larger-than-expected
decline in August." + ], + [ + "The separation of PalmOne and
PalmSource will be complete
with Eric Benhamou's
resignation as the latter's
chairman." + ], + [ + "AP - Two-time U.S. Open
doubles champion Max Mirnyi
will lead the Belarus team
that faces the United States
in the Davis Cup semifinals in
Charleston later this month." + ], + [ + "The hurricane appeared to be
strengthening and forecasters
warned of an increased threat
of serious flooding and wind
damage." + ], + [ + "AP - New York Jets safety Erik
Coleman got his souvenir
football from the equipment
manager and held it tightly." + ], + [ + " SINGAPORE (Reuters) - Oil
prices climbed above \\$42 a
barrel on Wednesday, rising
for the third day in a row as
the heavy consuming U.S.
Northeast feels the first
chills of winter." + ], + [ + "\\\\\"(CNN) -- A longtime
associate of al Qaeda leader
Osama bin Laden surrendered
to\\Saudi Arabian officials
Tuesday, a Saudi Interior
Ministry official said.\"\\\\\"But
it is unclear what role, if
any, Khaled al-Harbi may have
had in any terror\\attacks
because no public charges have
been filed against him.\"\\\\\"The
Saudi government -- in a
statement released by its
embassy in Washington
--\\called al-Harbi's surrender
\"the latest direct result\" of
its limited, one-month\\offer
of leniency to terror
suspects.\"\\\\This is great! I
hope this really starts to pay
off. Creative solutions
to\\terrorism that don't
involve violence. \\\\How
refreshing! \\\\Are you paying
attention Bush
administration?\\\\" + ], + [ + " NEW YORK (Reuters) - Merck
Co Inc. <A HREF=\"http://www
.investor.reuters.com/FullQuot
e.aspx?ticker=MRK.N target=/st
ocks/quickinfo/fullquote\">M
RK.N</A> on Thursday
pulled its arthritis drug
Vioxx off the market after a
study showed it doubled the
risk of heart attack and
stroke, a move that sent its
shares plunging and erased
\\$25 billion from its market
value." + ], + [ + "AT T Corp. is cutting 7,400
more jobs and slashing the
book value of its assets by
\\$11.4 billion, drastic moves
prompted by the company's plan
to retreat from the
traditional consumer telephone
business following a lost
court battle." + ], + [ + "The government on Wednesday
defended its decision to
radically revise the country
#39;s deficit figures, ahead
of a European Commission
meeting to consider possible
disciplinary action against
Greece for submitting faulty
figures." + ], + [ + "Ivan Hlinka coached the Czech
Republic to the hockey gold
medal at the 1998 Nagano
Olympics and became the coach
of the Pittsburgh Penguins two
years later." + ], + [ + "Four homeless men were
bludgeoned to death and six
were in critical condition on
Friday following early morning
attacks by unknown assailants
in downtown streets of Sao
Paulo, a police spokesman
said." + ], + [ + "OPEC ministers yesterday
agreed to increase their
ceiling for oil production to
help bring down stubbornly
high prices in a decision that
traders and analysts dismissed
as symbolic because the cartel
already is pumping more than
its new target." + ], + [ + "Williams-Sonoma Inc. said
second- quarter profit rose 55
percent, boosted by the
addition of Pottery Barn
stores and sale of outdoor
furniture." + ], + [ + "Celerons form the basis of
Intel #39;s entry-level
platform which includes
integrated/value motherboards
as well. The Celeron 335D is
the fastest - until the
Celeron 340D - 2.93GHz -
becomes mainstream - of the" + ], + [ + "The entertainment industry
asks the Supreme Court to
reverse the Grokster decision,
which held that peer-to-peer
networks are not liable for
copyright abuses of their
users. By Michael Grebb." + ], + [ + " HONG KONG/SAN FRANCISCO
(Reuters) - China's largest
personal computer maker,
Lenovo Group Ltd., said on
Tuesday it was in acquisition
talks with a major technology
company, which a source
familiar with the situation
said was IBM." + ], + [ + "Phone companies are not doing
enough to warn customers about
internet \"rogue-dialling\"
scams, watchdog Icstis warns." + ], + [ + "Reuters - Oil prices stayed
close to #36;49 a\\barrel on
Thursday, supported by a
forecast for an early
cold\\snap in the United States
that could put a strain on a
thin\\supply cushion of winter
heating fuel." + ], + [ + "AUBURN - Ah, easy street. No
game this week. Light
practices. And now Auburn is
being touted as the No. 3 team
in the Bowl Championship
Series standings." + ], + [ + "Portsmouth #39;s Harry
Redknapp has been named as the
Barclays manager of the month
for October. Redknapp #39;s
side were unbeaten during the
month and maintained an
impressive climb to ninth in
the Premiership." + ], + [ + "India's main opposition party
takes action against senior
party member Uma Bharti after
a public row." + ], + [ + "Hewlett-Packard will shell out
\\$16.1 billion for chips in
2005, but Dell's wallet is
wide open, too." + ], + [ + "WASHINGTON (CBS.MW) --
President Bush announced
Monday that Kellogg chief
executive Carlos Gutierrez
would replace Don Evans as
Commerce secretary, naming the
first of many expected changes
to his economic team." + ], + [ + "Iron Mountain moved further
into the backup and recovery
space Tuesday with the
acquisition of Connected Corp.
for \\$117 million. Connected
backs up desktop data for more
than 600 corporations, with
more than" + ], + [ + "Three directors of Manchester
United have been ousted from
the board after US tycoon
Malcolm Glazer, who is
attempting to buy the club,
voted against their re-
election." + ], + [ + "The Russian military yesterday
extended its offer of a \\$10
million reward for information
leading to the capture of two
separatist leaders who, the
Kremlin claims, were behind
the Beslan massacre." + ], + [ + "AP - Manny Ramirez singled and
scored before leaving with a
bruised knee, and the
streaking Boston Red Sox beat
the Detroit Tigers 5-3 Friday
night for their 10th victory
in 11 games." + ], + [ + "A remote attacker could take
complete control over
computers running many
versions of Microsoft software
by inserting malicious code in
a JPEG image that executes
through an unchecked buffer" + ], + [ + "Montgomery County (website -
news) is a big step closer to
shopping for prescription
drugs north of the border. On
a 7-2 vote, the County Council
is approving a plan that would
give county" + ], + [ + "Israels Shin Bet security
service has tightened
protection of the prime
minister, MPs and parliament
ahead of next weeks crucial
vote on a Gaza withdrawal." + ], + [ + "The news comes fast and
furious. Pedro Martinez goes
to Tampa to visit George
Steinbrenner. Theo Epstein and
John Henry go to Florida for
their turn with Pedro. Carl
Pavano comes to Boston to
visit Curt Schilling. Jason
Varitek says he's not a goner.
Derek Lowe is a goner, but he
says he wishes it could be
different. Orlando Cabrera ..." + ], + [ + "The disclosure this week that
a Singapore-listed company
controlled by a Chinese state-
owned enterprise lost \\$550
million in derivatives
transactions" + ], + [ + "Reuters - Iraq's interim
defense minister
accused\\neighbors Iran and
Syria on Wednesday of aiding
al Qaeda\\Islamist Abu Musab
al-Zarqawi and former agents
of Saddam\\Hussein to promote a
\"terrorist\" insurgency in
Iraq." + ], + [ + "AP - The Senate race in
Kentucky stayed at fever pitch
on Thursday as Democratic
challenger Daniel Mongiardo
stressed his opposition to gay
marriage while accusing
Republican incumbent Jim
Bunning of fueling personal
attacks that seemed to suggest
Mongiardo is gay." + ], + [ + "The lawsuit claims the
companies use a patented
Honeywell technology for
brightening images and
reducing interference on
displays." + ], + [ + "The co-president of Oracle
testified that her company was
serious about its takeover
offer for PeopleSoft and was
not trying to scare off its
customers." + ], + [ + "VANCOUVER - A Vancouver-based
firm won #39;t sell 1.2
million doses of influenza
vaccine to the United States
after all, announcing Tuesday
that it will sell the doses
within Canada instead." + ], + [ + "An extremely rare Hawaiian
bird dies in captivity,
possibly marking the
extinction of its entire
species only 31 years after it
was first discovered." + ], + [ + "Does Geico's trademark lawsuit
against Google have merit? How
will the case be argued?
What's the likely outcome of
the trial? A mock court of
trademark experts weighs in
with their verdict." + ], + [ + "Treo 650 boasts a high-res
display, an improved keyboard
and camera, a removable
battery, and more. PalmOne
this week is announcing the
Treo 650, a hybrid PDA/cell-
phone device that addresses
many of the shortcomings" + ], + [ + "FRED Hale Sr, documented as
the worlds oldest man, has
died at the age of 113. Hale
died in his sleep on Friday at
a hospital in Syracuse, New
York, while trying to recover
from a bout of pneumonia, his
grandson, Fred Hale III said." + ], + [ + "The Oakland Raiders have
traded Jerry Rice to the
Seattle Seahawks in a move
expected to grant the most
prolific receiver in National
Football League history his
wish to get more playing time." + ], + [ + "consortium led by the Sony
Corporation of America reached
a tentative agreement today to
buy Metro-Goldwyn-Mayer, the
Hollywood studio famous for
James Bond and the Pink
Panther, for" + ], + [ + "International Business
Machines Corp.'s possible exit
from the personal computer
business would be the latest
move in what amounts to a long
goodbye from a field it
pioneered and revolutionized." + ], + [ + "Leipzig Game Convention in
Germany, the stage for price-
slash revelations. Sony has
announced that it #39;s
slashing the cost of PS2 in
the UK and Europe to 104.99
GBP." + ], + [ + "AP - Florida coach Ron Zook
was fired Monday but will be
allowed to finish the season,
athletic director Jeremy Foley
told The Gainesville Sun." + ], + [ + "The country-cooking restaurant
chain has agreed to pay \\$8.7
million over allegations that
it segregated black customers,
subjected them to racial slurs
and gave black workers
inferior jobs." + ], + [ + "Troy Brown has had to make a
lot of adjustments while
playing both sides of the
football. quot;You always
want to score when you get the
ball -- offense or defense" + ], + [ + " PORT LOUIS, Aug. 17
(Xinhuanet) -- Southern
African countries Tuesday
pledged better trade and
investment relations with
China as well as India in the
final communique released at
the end of their two-day
summit." + ], + [ + "In yet another devastating
body blow to the company,
Intel (Nasdaq: INTC) announced
it would be canceling its
4-GHz Pentium chip. The
semiconductor bellwether said
it was switching" + ], + [ + "Jenson Button will tomorrow
discover whether he is allowed
to quit BAR and move to
Williams for 2005. The
Englishman has signed
contracts with both teams but
prefers a switch to Williams,
where he began his Formula One
career in 2000." + ], + [ + "Seagate #39;s native SATA
interface technology with
Native Command Queuing (NCQ)
allows the Barracuda 7200.8 to
match the performance of
10,000-rpm SATA drives without
sacrificing capacity" + ], + [ + "ARSENAL boss Arsene Wenger
last night suffered a
Champions League setback as
Brazilian midfielder Gilberto
Silva (above) was left facing
a long-term injury absence." + ], + [ + "BAGHDAD - A militant group has
released a video saying it
kidnapped a missing journalist
in Iraq and would kill him
unless US forces left Najaf
within 48 hours." + ], + [ + "18 August 2004 -- There has
been renewed fighting in the
Iraqi city of Al-Najaf between
US and Iraqi troops and Shi
#39;a militiamen loyal to
radical cleric Muqtada al-
Sadr." + ], + [ + "You #39;re probably already
familiar with one of the most
common questions we hear at
iPodlounge: quot;how can I
load my iPod up with free
music?" + ], + [ + " SINGAPORE (Reuters) -
Investors bought shares in
Asian exporters and
electronics firms such as
Fujitsu Ltd. on Tuesday,
buoyed by a favorable outlook
from U.S. technology
bellwethers and a slide in oil
prices." + ], + [ + "Ace Ltd. will stop paying
brokers for steering business
its way, becoming the third
company to make concessions in
the five days since New York
Attorney General Eliot Spitzer
unveiled a probe of the
insurance industry." + ], + [ + "Vice chairman of the office of
the chief executive officer in
Novell Inc, Chris Stone is
leaving the company to pursue
other opportunities in life." + ], + [ + "Wm. Wrigley Jr. Co., the world
#39;s largest maker of chewing
gum, agreed to buy candy
businesses including Altoids
mints and Life Savers from
Kraft Foods Inc." + ], + [ + "American improves to 3-1 on
the season with a hard-fought
overtime win, 74-63, against
Loyala at Bender Arena on
Friday night." + ], + [ + "Australia tighten their grip
on the third Test and the
series after dominating India
on day two in Nagpur." + ], + [ + " #39;Reaching a preliminary
pilot agreement is the single
most important hurdle they
have to clear, but certainly
not the only one." + ], + [ + "Bee Staff Writers. SAN
FRANCISCO - As Eric Johnson
drove to the stadium Sunday
morning, his bruised ribs were
so sore, he wasn #39;t sure he
#39;d be able to suit up for
the game." + ], + [ + "The New England Patriots are
so single-minded in pursuing
their third Super Bowl triumph
in four years that they almost
have no room for any other
history." + ], + [ + "TORONTO (CP) - Canada #39;s
big banks are increasing
mortgage rates following a
decision by the Bank of Canada
to raise its overnight rate by
one-quarter of a percentage
point to 2.25 per cent." + ], + [ + " SEOUL (Reuters) - North
Korea's two-year-old nuclear
crisis has taxed the world's
patience, the chief United
Nations nuclear regulator
said on Wednesday, urging
communist Pyongyang to return
to its disarmament treaty
obligations." + ], + [ + "washingtonpost.com - Microsoft
is going to Tinseltown today
to announce plans for its
revamped Windows XP Media
Center, part of an aggressive
push to get ahead in the
digital entertainment race." + ], + [ + "GROZNY, Russia - The Russian
government's choice for
president of war-ravaged
Chechnya appeared to be the
victor Sunday in an election
tainted by charges of fraud
and shadowed by last week's
terrorist destruction of two
airliners. Little more than
two hours after polls closed,
acting Chechen president
Sergei Abramov said
preliminary results showed
Maj..." + ], + [ + "Because, while the Eagles are
certain to stumble at some
point during the regular
season, it seems inconceivable
that they will falter against
a team with as many offensive
problems as Baltimore has
right now." + ], + [ + "AP - J.J. Arrington ran for 84
of his 121 yards in the second
half and Aaron Rodgers shook
off a slow start to throw two
touchdown passes to help No. 5
California beat Washington
42-12 on Saturday." + ], + [ + "BAGHDAD, Sept 5 (AFP) - Izzat
Ibrahim al-Duri, Saddam
Hussein #39;s deputy whose
capture was announced Sunday,
is 62 and riddled with cancer,
but was public enemy number
two in Iraq for the world
#39;s most powerful military." + ], + [ + "AP - An explosion targeted the
Baghdad governor's convoy as
he was traveling through the
capital Tuesday, killing two
people but leaving him
uninjured, the Interior
Ministry said." + ], + [ + "GENEVA: Rescuers have found
the bodies of five Swiss
firemen who died after the
ceiling of an underground car
park collapsed during a fire,
a police spokesman said last
night." + ], + [ + "John Kerry has held 10 \"front
porch visit\" events an actual
front porch is optional where
perhaps 100 people ask
questions in a low-key
campaigning style." + ], + [ + "AP - Most of the turkeys
gracing the nation's dinner
tables Thursday have been
selectively bred for their
white meat for so many
generations that simply
walking can be a problem for
many of the big-breasted birds
and sex is no longer possible." + ], + [ + "OTTAWA (CP) - The economy
created another 43,000 jobs
last month, pushing the
unemployment rate down to 7.1
per cent from 7.2 per cent in
August, Statistics Canada said
Friday." + ], + [ + "The right-win opposition
Conservative Party and Liberal
Center Union won 43 seats in
the 141-member Lithuanian
parliament, after more than 99
percent of the votes were
counted" + ], + [ + " TOKYO (Reuters) - Japan's
Nikkei average rose 0.39
percent by midsession on
Friday, bolstered by solid
gains in stocks dependent on
domestic business such as Kao
Corp. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=4452.T target=/sto
cks/quickinfo/fullquote\">44
52.T</A>." + ], + [ + " FRANKFURT (Reuters) -
DaimlerChrysler and General
Motors will jointly develop
new hybrid motors to compete
against Japanese rivals on
the fuel-saving technology
that reduces harmful
emissions, the companies said
on Monday." + ], + [ + " SEATTLE (Reuters) - Microsoft
Corp. <A HREF=\"http://www.r
euters.co.uk/financeQuoteLooku
p.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
is making a renewed push this
week to get its software into
living rooms with the launch
of a new version of its
Windows XP Media Center, a
personal computer designed for
viewing movies, listening to
music and scrolling through
digital pictures." + ], + [ + "KHARTOUM, Aug 18 (Reuters) -
The United Nations said on
Wednesday it was very
concerned by Sudan #39;s lack
of practical progress in
bringing security to Darfur,
where more than a million
people have fled their homes
for fear of militia ..." + ], + [ + "SHANGHAI, China The Houston
Rockets have arrived in
Shanghai with hometown
favorite Yao Ming declaring
himself quot;here on
business." + ], + [ + "Charleston, SC (Sports
Network) - Andy Roddick and
Mardy Fish will play singles
for the United States in this
weekend #39;s Davis Cup
semifinal matchup against
Belarus." + ], + [ + "AFP - With less than two
months until the November 2
election, President George W.
Bush is working to shore up
support among his staunchest
supporters even as he courts
undecided voters." + ], + [ + "The bass should be in your
face. That's what Matt Kelly,
of Boston's popular punk rock
band Dropkick Murphys, thinks
is the mark of a great stereo
system. And he should know.
Kelly, 29, is the drummer for
the band that likes to think
of itself as a bit of an Irish
lucky charm for the Red Sox." + ], + [ + "Chile's government says it
will build a prison for
officers convicted of human
rights abuses in the Pinochet
era." + ], + [ + "Slumping corporate spending
and exports caused the economy
to slow to a crawl in the
July-September period, with
real gross domestic product
expanding just 0.1 percent
from the previous quarter,
Cabinet Office data showed
Friday." + ], + [ + "US President George W. Bush
signed into law a bill
replacing an export tax
subsidy that violated
international trade rules with
a \\$145 billion package of new
corporate tax cuts and a
buyout for tobacco farmers." + ], + [ + "The Nikkei average was up 0.37
percent in mid-morning trade
on Thursday as a recovery in
the dollar helped auto makers
among other exporters, but
trade was slow as investors
waited for important Japanese
economic data." + ], + [ + "Jennifer Canada knew she was
entering a boy's club when she
enrolled in Southern Methodist
University's Guildhall school
of video-game making." + ], + [ + "RICKY PONTING believes the
game #39;s watchers have
fallen for the quot;myth
quot; that New Zealand know
how to rattle Australia." + ], + [ + "MILWAUKEE (SportsTicker) -
Barry Bonds tries to go where
just two players have gone
before when the San Francisco
Giants visit the Milwaukee
Brewers on Tuesday." + ], + [ + "Palestinian leader Mahmoud
Abbas reiterated calls for his
people to drop their weapons
in the struggle for a state. a
clear change of strategy for
peace with Israel after Yasser
Arafat #39;s death." + ], + [ + "The new software is designed
to simplify the process of
knitting together back-office
business applications." + ], + [ + "SYDNEY (Dow Jones)--Colorado
Group Ltd. (CDO.AU), an
Australian footwear and
clothing retailer, said Monday
it expects net profit for the
fiscal year ending Jan. 29 to
be over 30 higher than that of
a year earlier." + ], + [ + "NEW YORK - What are the odds
that a tiny nation like
Antigua and Barbuda could take
on the United States in an
international dispute and win?" + ], + [ + "AP - With Tom Brady as their
quarterback and a stingy,
opportunistic defense, it's
difficult to imagine when the
New England Patriots might
lose again. Brady and
defensive end Richard Seymour
combined to secure the
Patriots' record-tying 18th
straight victory, 31-17 over
the Buffalo Bills on Sunday." + ], + [ + "FRANKFURT, GERMANY -- The
German subsidiaries of
Hewlett-Packard Co. (HP) and
Novell Inc. are teaming to
offer Linux-based products to
the country's huge public
sector." + ], + [ + "SBC Communications expects to
cut 10,000 or more jobs by the
end of next year through
layoffs and attrition. That
#39;s about six percent of the
San Antonio-based company
#39;s work force." + ], + [ + " BAGHDAD (Reuters) - Iraq's
U.S.-backed government said on
Tuesday that \"major neglect\"
by its American-led military
allies led to a massacre of 49
army recruits at the weekend." + ], + [ + "SiliconValley.com - \"I'm
back,\" declared Apple
Computer's Steve Jobs on
Thursday morning in his first
public appearance before
reporters since cancer surgery
in late July." + ], + [ + "BEIJING -- Police have
detained a man accused of
slashing as many as nine boys
to death as they slept in
their high school dormitory in
central China, state media
reported today." + ], + [ + "Health India: London, Nov 4 :
Cosmetic face cream used by
fashionable Roman women was
discovered at an ongoing
archaeological dig in London,
in a metal container, complete
with the lid and contents." + ], + [ + "Israeli Prime Minister Ariel
Sharon #39;s Likud party
agreed on Thursday to a
possible alliance with
opposition Labour in a vote
that averted a snap election
and strengthened his Gaza
withdrawal plan." + ], + [ + "Another United Airlines union
is seeking to oust senior
management at the troubled
airline, saying its strategies
are reckless and incompetent." + ], + [ + "Approaching Hurricane Ivan has
led to postponement of the
game Thursday night between
10th-ranked California and
Southern Mississippi in
Hattiesburg, Cal #39;s
athletic director said Monday." + ], + [ + "Global oil prices boomed on
Wednesday, spreading fear that
energy prices will restrain
economic activity, as traders
worried about a heating oil
supply crunch in the American
winter." + ], + [ + "Custom-designed imported
furniture was once an
exclusive realm. Now, it's the
economical alternative for
commercial developers and
designers needing everything
from seats to beds to desks
for their projects." + ], + [ + "SAN DIEGO (Ticker) - The San
Diego Padres lacked speed and
an experienced bench last
season, things veteran
infielder Eric Young is
capable of providing." + ], + [ + "This is an eye chart,
reprinted as a public service
to the New York Mets so they
may see from what they suffer:
myopia. Has ever a baseball
franchise been so shortsighted
for so long?" + ], + [ + "Short-term interest rate
futures struggled on Thursday
after a government report
showing US core inflation for
August below market
expectations failed to alter
views on Federal Reserve rate
policy." + ], + [ + "MacCentral - Microsoft's
Macintosh Business Unit on
Tuesday issued a patch for
Virtual PC 7 that fixes a
problem that occurred when
running the software on Power
Mac G5 computers with more
than 2GB of RAM installed.
Previously, Virtual PC 7 would
not run on those computers,
causing a fatal error that
crashed the application.
Microsoft also noted that
Virtual PC 7.0.1 also offers
stability improvements,
although it wasn't more
specific than that -- some
users have reported problems
with using USB devices and
other issues." + ], + [ + "Samsung is now the world #39;s
second-largest mobile phone
maker, behind Nokia. According
to market watcher Gartner, the
South Korean company has
finally knocked Motorola into
third place." + ], + [ + "Don't bother buying Star Wars:
Battlefront if you're looking
for a first-class shooter --
there are far better games out
there. But if you're a Star
Wars freak and need a fix,
this title will suffice. Lore
Sjberg reviews Battlefront." + ], + [ + "While the American forces are
preparing to launch a large-
scale attack against Falluja
and al-Ramadi, one of the
chieftains of Falluja said
that contacts are still
continuous yesterday between
members representing the city
#39;s delegation and the
interim" + ], + [ + "UN Security Council
ambassadors were still
quibbling over how best to
pressure Sudan and rebels to
end two different wars in the
country even as they left for
Kenya on Tuesday for a meeting
on the crisis." + ], + [ + "HENDALA, Sri Lanka -- Day
after day, locked in a cement
room somewhere in Iraq, the
hooded men beat him. They told
him he would be beheaded.
''Ameriqi! quot; they shouted,
even though he comes from this
poor Sri Lankan fishing
village." + ], + [ + "THE kidnappers of British aid
worker Margaret Hassan last
night threatened to turn her
over to the group which
beheaded Ken Bigley if the
British Government refuses to
pull its troops out of Iraq." + ], + [ + " TOKYO (Reuters) - Tokyo
stocks climbed to a two week
high on Friday after Tokyo
Electron Ltd. and other chip-
related stocks were boosted
by a bullish revenue outlook
from industry leader Intel
Corp." + ], + [ + "More than by brain size or
tool-making ability, the human
species was set apart from its
ancestors by the ability to
jog mile after lung-stabbing
mile with greater endurance
than any other primate,
according to research
published today in the journal" + ], + [ + " LONDON (Reuters) - A medical
product used to treat both
male hair loss and prostate
problems has been added to the
list of banned drugs for
athletes." + ], + [ + "AP - Curtis Martin and Jerome
Bettis have the opportunity to
go over 13,000 career yards
rushing in the same game when
Bettis and the Pittsburgh
Steelers play Martin and the
New York Jets in a big AFC
matchup Sunday." + ], + [ + "<p></p><p>
BOGOTA, Colombia (Reuters) -
Ten Colombian police
officerswere killed on Tuesday
in an ambush by the National
LiberationArmy, or ELN, in the
worst attack by the Marxist
group inyears, authorities
told Reuters.</p>" + ], + [ + "Woodland Hills-based Brilliant
Digital Entertainment and its
subsidiary Altnet announced
yesterday that they have filed
a patent infringement suit
against the Recording Industry
Association of America (RIAA)." + ], + [ + "AFP - US President George W.
Bush called his Philippines
counterpart Gloria Arroyo and
said their countries should
keep strong ties, a spokesman
said after a spat over
Arroyo's handling of an Iraq
kidnapping." + ], + [ + "A scathing judgment from the
UK #39;s highest court
condemning the UK government
#39;s indefinite detention of
foreign terror suspects as a
threat to the life of the
nation, left anti-terrorist
laws in the UK in tatters on
Thursday." + ], + [ + "Sony Ericsson and Cingular
provide Z500a phones and
service for military families
to stay connected on today
#39;s quot;Dr. Phil Show
quot;." + ], + [ + "Reuters - Australia's
conservative Prime
Minister\\John Howard, handed
the most powerful mandate in a
generation,\\got down to work
on Monday with reform of
telecommunications,\\labor and
media laws high on his agenda." + ], + [ + " TOKYO (Reuters) - Typhoon
Megi killed one person as it
slammed ashore in northern
Japan on Friday, bringing the
death toll to at least 13 and
cutting power to thousands of
homes before heading out into
the Pacific." + ], + [ + "Cairo: Egyptian President
Hosni Mubarak held telephone
talks with Palestinian leader
Yasser Arafat about Israel
#39;s plan to pull troops and
the 8,000 Jewish settlers out
of the Gaza Strip next year,
the Egyptian news agency Mena
said." + ], + [ + " NEW YORK (Reuters) - Adobe
Systems Inc. <A HREF=\"http:
//www.investor.reuters.com/Ful
lQuote.aspx?ticker=ADBE.O targ
et=/stocks/quickinfo/fullquote
\">ADBE.O</A> on
Monday reported a sharp rise
in quarterly profit, driven by
robust demand for its
Photoshop and document-sharing
software." + ], + [ + "Dow Jones amp; Co., publisher
of The Wall Street Journal,
has agreed to buy online
financial news provider
MarketWatch Inc. for about
\\$463 million in a bid to
boost its revenue from the
fast-growing Internet
advertising market." + ], + [ + "The first American military
intelligence soldier to be
court-martialed over the Abu
Ghraib abuse scandal was
sentenced Saturday to eight
months in jail, a reduction in
rank and a bad-conduct
discharge." + ], + [ + " TOKYO (Reuters) - Japan will
seek an explanation at weekend
talks with North Korea on
activity indicating Pyongyang
may be preparing a missile
test, although Tokyo does not
think a launch is imminent,
Japan's top government
spokesman said." + ], + [ + "Michigan Stadium was mostly
filled with empty seats. The
only cheers were coming from
near one end zone -he Iowa
section. By Carlos Osorio, AP." + ], + [ + "The International Rugby Board
today confirmed that three
countries have expressed an
interest in hosting the 2011
World Cup. New Zealand, South
Africa and Japan are leading
the race to host rugby union
#39;s global spectacular in
seven years #39; time." + ], + [ + "Officials at EarthLink #39;s
(Quote, Chart) R amp;D
facility have quietly released
a proof-of-concept file-
sharing application based on
the Session Initiated Protocol
(define)." + ], + [ + "Low-fare airline ATA has
announced plans to lay off
hundreds of employees and to
drop most of its flights out
of Midway Airport in Chicago." + ], + [ + " BEIJING (Reuters) - Iran will
never be prepared to
dismantle its nuclear program
entirely but remains committed
to the non-proliferation
treaty (NPT), its chief
delegate to the International
Atomic Energy Agency said on
Wednesday." + ], + [ + " WASHINGTON (Reuters) -
Germany's Bayer AG <A HREF=
\"http://www.investor.reuters.c
om/FullQuote.aspx?ticker=BAYG.
DE target=/stocks/quickinfo/fu
llquote\">BAYG.DE</A>
has agreed to plead guilty
and pay a \\$4.7 million fine
for taking part in a
conspiracy to fix the prices
of synthetic rubber, the U.S.
Justice Department said on
Wednesday." + ], + [ + "MIAMI (Ticker) -- In its first
season in the Atlantic Coast
Conference, No. 11 Virginia
Tech is headed to the BCS.
Bryan Randall threw two
touchdown passes and the
Virginia Tech defense came up
big all day as the Hokies
knocked off No." + ], + [ + "(CP) - Somehow, in the span of
half an hour, the Detroit
Tigers #39; pitching went from
brutal to brilliant. Shortly
after being on the wrong end
of several records in a 26-5
thrashing from to the Kansas
City" + ], + [ + "<p></p><p>
SANTIAGO, Chile (Reuters) -
President Bush on
Saturdayreached into a throng
of squabbling bodyguards and
pulled aSecret Service agent
away from Chilean security
officers afterthey stopped the
U.S. agent from accompanying
the president ata
dinner.</p>" + ], + [ + " quot;It #39;s your mail,
quot; the Google Web site
said. quot;You should be able
to choose how and where you
read it. You can even switch
to other e-mail services
without having to worry about
losing access to your
messages." + ], + [ + "The US oil giant got a good
price, Russia #39;s No. 1 oil
company acquired a savvy
partner, and Putin polished
Russia #39;s image." + ], + [ + "With the Huskies reeling at
0-4 - the only member of a
Bowl Championship Series
conference left without a win
-an Jose State suddenly looms
as the only team left on the
schedule that UW will be
favored to beat." + ], + [ + "Darryl Sutter, who coached the
Calgary Flames to the Stanley
Cup finals last season, had an
emergency appendectomy and was
recovering Friday." + ], + [ + "The maker of Hostess Twinkies,
a cake bar and a piece of
Americana children have
snacked on for almost 75
years, yesterday raised
concerns about the company
#39;s ability to stay in
business." + ], + [ + "Iranian deputy foreign
minister Gholamali Khoshrou
denied Tuesday that his
country #39;s top leaders were
at odds over whether nuclear
weapons were un-Islamic,
insisting that it will
quot;never quot; make the
bomb." + ], + [ + " quot;Israel mercenaries
assisting the Ivory Coast army
operated unmanned aircraft
that aided aerial bombings of
a French base in the country,
quot; claimed" + ], + [ + "Athens, Greece (Sports
Network) - For the second
straight day a Briton captured
gold at the Olympic Velodrome.
Bradley Wiggins won the men
#39;s individual 4,000-meter
pursuit Saturday, one day
after teammate" + ], + [ + "AFP - SAP, the world's leading
maker of business software,
may need an extra year to
achieve its medium-term profit
target of an operating margin
of 30 percent, its chief
financial officer said." + ], + [ + "Tenet Healthcare Corp., the
second- largest US hospital
chain, said fourth-quarter
charges may exceed \\$1 billion
and its loss from continuing
operations will widen from the
third quarter #39;s because of
increased bad debt." + ], + [ + "AFP - The airline Swiss said
it had managed to cut its
first-half net loss by about
90 percent but warned that
spiralling fuel costs were
hampering a turnaround despite
increasing passenger travel." + ], + [ + "Vulnerable youngsters expelled
from schools in England are
being let down by the system,
say inspectors." + ], + [ + "Yasser Arafat was undergoing
tests for possible leukaemia
at a military hospital outside
Paris last night after being
airlifted from his Ramallah
headquarters to an anxious
farewell from Palestinian
well-wishers." + ], + [ + "The world #39;s only captive
great white shark made history
this week when she ate several
salmon fillets, marking the
first time that a white shark
in captivity" + ], + [ + "Nepal #39;s main opposition
party urged the government on
Monday to call a unilateral
ceasefire with Maoist rebels
and seek peace talks to end a
road blockade that has cut the
capital off from the rest of
the country." + ], + [ + "Communications aggregator
iPass said Monday that it is
adding in-flight Internet
access to its access
portfolio. Specifically, iPass
said it will add access
offered by Connexion by Boeing
to its list of access
providers." + ], + [ + "The London-based brokerage
Collins Stewart Tullett placed
55m of new shares yesterday to
help fund the 69.5m purchase
of the money and futures
broker Prebon." + ], + [ + "BOSTON - The New York Yankees
and Boston were tied 4-4 after
13 innings Monday night with
the Red Sox trying to stay
alive in the AL championship
series. Boston tied the
game with two runs in the
eighth inning on David Ortiz's
solo homer, a walk to Kevin
Millar, a single by Trot Nixon
and a sacrifice fly by Jason
Varitek..." + ], + [ + "A Steffen Iversen penalty was
sufficient to secure the
points for Norway at Hampden
on Saturday. James McFadden
was ordered off after 53
minutes for deliberate
handball as he punched Claus
Lundekvam #39;s header off the
line." + ], + [ + "Reuters - The country may be
more\\or less evenly divided
along partisan lines when it
comes to\\the presidential
race, but the Republican Party
prevailed in\\the Nielsen
polling of this summer's
nominating conventions." + ], + [ + " PARIS (Reuters) - European
equities flirted with 5-month
peaks as hopes that economic
growth was sustainable and a
small dip in oil prices
helped lure investors back to
recent underperformers such
as technology and insurance
stocks." + ], + [ + " NEW YORK (Reuters) - U.S.
stocks looked to open higher
on Friday, as the fourth
quarter begins on Wall Street
with oil prices holding below
\\$50 a barrel." + ], + [ + "LONDON : World oil prices
stormed above 54 US dollars
for the first time Tuesday as
strikes in Nigeria and Norway
raised worries about possible
supply shortages during the
northern hemisphere winter." + ], + [ + "AP - Ailing St. Louis reliever
Steve Kline was unavailable
for Game 3 of the NL
championship series on
Saturday, but Cardinals
manager Tony LaRussa hopes the
left-hander will pitch later
this postseason." + ], + [ + "Company launches free test
version of service that
fosters popular Internet
activity." + ], + [ + "Outdated computer systems are
hampering the work of
inspectors, says the UN
nuclear agency." + ], + [ + " In Vice President Cheney's
final push before next
Tuesday's election, talk of
nuclear annihilation and
escalating war rhetoric have
blended with balloon drops,
confetti cannons and the other
trappings of modern
campaigning with such ferocity
that it is sometimes tough to
tell just who the enemy is." + ], + [ + "MADRID: A stunning first-half
free kick from David Beckham
gave Real Madrid a 1-0 win
over newly promoted Numancia
at the Bernabeu last night." + ], + [ + "MacCentral - You Software Inc.
announced on Tuesday the
availability of You Control:
iTunes, a free\\download that
places iTunes controls in the
Mac OS X menu bar.
Without\\leaving the current
application, you can pause,
play, rewind or skip songs,\\as
well as control iTunes' volume
and even browse your entire
music library\\by album, artist
or genre. Each time a new song
plays, You Control:
iTunes\\also pops up a window
that displays the artist and
song name and the
album\\artwork, if it's in the
library. System requirements
call for Mac OS X\\v10.2.6 and
10MB free hard drive space.
..." + ], + [ + "Favourites Argentina beat
Italy 3-0 this morning to
claim their place in the final
of the men #39;s Olympic
football tournament. Goals by
leading goalscorer Carlos
Tevez, with a smart volley
after 16 minutes, and" + ], + [ + "Shortly after Steve Spurrier
arrived at Florida in 1990,
the Gators were placed on NCAA
probation for a year stemming
from a child-support payment
former coach Galen Hall made
for a player." + ], + [ + "The US Secret Service Thursday
announced arrests in eight
states and six foreign
countries of 28 suspected
cybercrime gangsters on
charges of identity theft,
computer fraud, credit-card
fraud, and conspiracy." + ], + [ + "US stocks were little changed
on Thursday as an upbeat
earnings report from chip
maker National Semiconductor
Corp. (NSM) sparked some
buying, but higher oil prices
limited gains." + ] + ], + "hovertemplate": "label=Other
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Other", + "marker": { + "color": "#636efa", + "size": 5, + "symbol": "circle" + }, + "mode": "markers", + "name": "Other", + "showlegend": true, + "type": "scattergl", + "x": [ + 9.787297, + 16.660423, + -47.977715, + -51.65402, + 17.206654, + -23.452963, + 28.167477, + 14.671119, + -37.6373, + 21.907679, + 49.959976, + -36.581165, + -19.791555, + 11.003371, + 12.786125, + 54.445854, + -20.928211, + 28.192938, + 27.511831, + 30.389194, + 31.907536, + 27.685726, + -0.48236108, + -53.210773, + 24.631432, + -39.491558, + 19.87161, + -22.567608, + 13.9476, + 42.217842, + 23.463217, + -19.96729, + -49.124306, + 15.450646, + -7.3474283, + -28.231606, + 22.48473, + 47.958393, + -22.541676, + -52.717, + 47.226242, + 51.068775, + 50.124294, + -19.31264, + -28.148346, + 44.945942, + -42.825386, + -47.410145, + -29.877638, + 7.788443, + 46.406788, + 48.53827, + -5.8827424, + -35.965088, + 31.687206, + 26.455547, + -10.623615, + -40.76841, + -4.8219795, + -18.956379, + 40.537342, + 3.2403526, + -5.107883, + 63.37852, + 56.515934, + 45.10189, + -42.131943, + -8.153443, + 48.401085, + 0.8158772, + -29.768171, + 7.324227, + 36.802402, + -32.52056, + 24.88585, + -39.654697, + 12.912951, + -18.497515, + 54.15107, + -31.347673, + -48.55776, + 38.79396, + -53.367012, + -27.265852, + -6.4607854, + 13.661437, + 30.355759, + 58.71805, + -25.208595, + 3.1252713, + -41.868053, + 38.756367, + 59.531124, + 47.890396, + -17.98721, + 36.084118, + -13.634508, + 39.42093, + 18.820461, + -30.356327, + -49.554066, + -29.197483, + 55.4732, + -43.75864, + 60.896523, + 56.4989, + -33.627903, + 48.16754, + -28.459074, + 13.827141, + -11.594272, + 47.840588, + -33.894855, + -5.484721, + 1.4320391, + 60.467564, + -13.830222, + -26.233013, + 31.210938, + -36.616104, + 12.191131, + 49.345882, + -53.822376, + -44.628685, + -2.3659778, + -19.861258, + 58.657722, + -44.997097, + -37.276833, + 25.89013, + -10.061741, + -32.693943, + -1.0874362, + -19.60824, + -38.45829, + -15.162608, + 16.015558, + -38.214413, + -18.354656, + 20.328302, + 37.406326, + 45.95487, + 27.38124, + 46.569256, + 15.950565, + 11.055151, + 14.368044, + 40.19783, + -38.585472, + -46.83205, + 15.940281, + 48.277, + -38.63723, + 48.961315, + -7.0522246, + 28.371725, + -23.330465, + 46.0185, + -44.325756, + -35.855865, + -18.20647, + -7.6315646, + 59.281708, + -53.65577, + -33.435104, + 17.925577, + -48.104885, + 15.851762, + 58.10119, + 41.329796, + 60.929493, + -56.60167, + 49.080627, + 1.6593695, + -1.9387143, + -40.07204, + -32.65333, + -11.705121, + 18.05479, + 52.442997, + 25.193996, + 28.471008, + -23.656853, + 13.627039, + 10.705277, + -8.970166, + 18.345266, + 49.169395, + -44.30361, + -36.139923, + 31.360231, + 50.538635, + 50.209198, + -48.951195, + 29.55601, + -21.136202, + -21.265085, + -41.619125, + -34.370987, + -33.846046, + -16.407732, + 4.6381655, + 16.084637, + 38.928047, + -41.55955, + -33.503048, + 57.835648, + 25.167212, + -31.4103, + 51.287056, + -41.654114, + -43.58004, + -40.584736, + 54.942364, + 12.821454, + 24.011929, + 31.13371, + -20.22472, + 17.79019, + -24.227406, + 15.120078, + -41.80841, + -43.47724, + -39.450546, + 19.99747, + 15.529904, + 45.565693, + -28.54648, + 56.076347, + 19.791918, + -55.67927, + -41.094902, + -27.870377, + -41.256504, + -11.352515, + -46.313496, + -46.637367, + -18.041924, + -18.929783, + -38.786, + -18.44551, + -45.789707, + -9.525661, + 12.873272, + 47.481384, + 16.33122, + 22.366423, + -35.619858, + 54.362545, + 2.4414933, + 25.421625, + 53.08307, + -29.50228, + -34.186226, + -37.5602, + -36.813686, + 20.893494, + 19.51864, + -5.632542, + 11.30494, + 2.9794881, + -45.209023, + -31.500145, + 12.285144, + 55.395947, + 32.21779, + 3.3651476, + -51.390118, + 36.722115, + 58.705086, + 30.618706, + -16.802534, + 2.6427522, + -42.6501, + 18.079544, + -6.2927465, + -46.257027, + -23.344019, + -44.347576, + 23.218187, + 48.1655, + -8.361857, + 2.7036908, + 6.7110343, + -1.3755705, + -17.157036, + 39.944633, + 51.72719, + -18.672327, + 57.632236, + 18.106745, + 10.5324745, + 40.575005, + -23.71305, + -55.86017, + 18.966919, + 31.810251, + -34.78124, + 49.25058, + 52.102192, + 17.243034, + 45.8365, + 4.507162, + 41.15665, + 21.953882, + 45.06367, + 50.470036, + 12.895756, + 13.899155, + -18.132378, + -35.732872, + -8.266737, + 55.406406, + -34.572502, + -5.21674, + 34.643066, + 9.645002, + -39.5287, + -21.22969, + 8.84193, + -6.6976542, + -26.256298, + -42.24456, + 44.60481, + -34.82042, + -45.549995, + -48.911743, + 7.766448, + 40.65631, + -7.7231383, + -46.522186, + 49.13915, + 10.753302, + -36.61156, + 60.835865, + 29.961258, + -37.112934, + -10.257471, + 31.980536, + 27.892096, + 45.230713, + -16.951935, + 29.803865, + -5.4085217, + 25.779589, + -19.065458, + -22.693665, + 56.531708, + -44.68482, + 23.273722, + 2.6259706, + 37.571487, + -29.42919, + -30.674974, + 65.59185, + -16.563955, + 38.63354, + 3.1031818, + -43.12336, + -57.728573, + 13.034079, + 46.481388, + -48.13403, + -27.2014, + -10.165841, + 46.68557, + 49.08016, + -15.232134, + -53.29632, + -7.2577205, + 14.032702, + -31.430248, + 23.928396, + 12.880273, + -27.285727, + -42.180077, + -15.3164215, + -6.620774, + -47.9015, + 11.016033, + -37.857563, + 45.88543, + 35.342182, + -30.674488, + -23.828165, + -37.931133, + -31.504562, + -47.091602, + 17.860275, + -6.3850884, + -16.122215, + -3.119768, + 47.523766, + -28.833778, + 12.732019, + -7.503495, + 47.32294, + -26.526276, + 16.701687, + 34.786186, + -42.6552, + 14.009928, + 18.774673, + -37.64758, + 43.796356, + -14.742917, + 49.697426, + -19.793585, + -53.133896, + 37.340225, + -22.841238, + 2.979671, + -51.962658, + 54.74676, + 41.444393, + -15.730567, + 30.604837, + -44.145668, + 8.863162, + 60.995483, + -44.98284, + 9.040379, + 24.042429, + 25.076736, + 30.519775, + -47.514927, + -40.143944, + -29.305222, + 12.896586, + -30.795404, + 25.85091, + 19.948092, + 20.974108, + -19.33182, + 66.21081, + -49.376717, + 35.24333, + 18.678154, + -43.173016, + 57.111607, + 48.019886, + -4.780475, + 49.229675, + 52.86177, + -32.70729, + -13.887877, + 19.741331, + 52.435543, + -34.81799, + -22.524883, + -12.82885, + -46.24378, + -29.501112, + -5.0456986, + 31.022472, + -36.60279, + -47.141144, + -11.186273, + -36.80437, + 18.250782, + -8.335074, + -34.644447, + -11.501718, + 4.836007, + -9.537627, + 24.104692, + 51.07264, + 61.549442, + 1.9518297, + -42.394825, + 42.282997, + -11.57566, + 15.377659, + -29.24355, + -47.198746, + 33.828228, + 14.915583, + 56.65421, + -53.3065, + 19.173527, + 43.26525, + 62.865932, + -37.63518, + -42.896793, + -15.898649, + 1.5646982, + -46.367523, + -51.349506, + -47.68558, + -10.65396, + -49.790844, + 39.05222, + -27.663815, + -7.4475813, + 41.410755, + 38.42368, + -45.67376, + -20.76551, + -29.445877, + -23.031208, + -37.19868, + 4.37751, + -31.336668, + 8.212411, + -45.453674, + 5.134725, + 14.638772, + -6.1798644, + 1.3949758, + 15.7138605, + -50.869877, + 56.985188, + -44.030884, + -44.016224, + 57.739395, + -24.544922, + 17.170551, + 50.66318, + 48.850945, + -50.093754, + -24.092436, + 17.347712, + -19.335144, + 36.300686, + 39.32889, + -6.3253975, + -25.326218, + 31.211935, + -6.78382, + -50.14104, + -49.846096, + 39.84183, + 24.028929, + 40.939545, + 48.37629, + 25.452175, + -37.92231, + -20.485857, + 51.559708, + -52.251915, + -33.647213, + -9.488163, + 5.1724906, + 46.41191, + -14.337521, + 52.77315, + 27.314432, + -29.272839, + -42.127716, + -45.652252, + -43.886417, + 12.80685, + -32.545635, + 4.3835373, + -26.112938, + -39.891853, + 55.190277, + -24.499039, + -43.219402, + -9.765382, + 12.646676, + 17.87465, + 38.03023, + -42.07617, + 9.909096, + 59.759964, + -46.420776, + 22.181377, + 48.908253, + 29.485273, + -1.1384851, + -36.030876, + 49.40172, + 24.128727, + 49.71215, + -16.348925, + -31.277052, + 40.702366, + -16.921326, + 57.916637, + 16.221085, + -9.602789, + -13.009839, + 51.67792, + 30.202019, + -35.251965, + 57.654526, + 34.585587, + -9.096614, + 25.097984, + 2.5471764, + 61.278408, + -16.914656, + 8.574884, + 23.422081, + 39.786133, + -44.31037, + -28.402678, + 21.295237, + -18.734745, + -28.94602, + 25.800558, + -42.23089, + 33.389534, + -23.825924, + -37.813118, + -45.167847, + 5.8968763, + -22.952112, + 24.184378, + -45.96224, + 54.46313, + -36.155823, + -37.039566, + -54.980534, + -33.989525, + -15.194927, + 46.068615, + -28.049438, + -30.086702, + 48.63991, + 35.595135, + 20.787077, + 29.313784, + -1.0110728, + 8.255305, + 50.553566, + -17.443365, + -12.567652, + -42.189774, + -40.062756, + -52.438923, + -24.236567, + -35.002357, + 54.101334, + -54.033554, + -24.72687, + 31.629358, + 15.709119, + -25.166414, + -7.6725793, + 51.79524, + -51.84964, + -27.801344, + -26.129557, + -52.71393, + 10.710161, + -13.981046, + 42.513103, + -6.9841313, + 39.504147, + 20.738512, + 47.29459, + -10.861444, + -48.87469, + -50.175316, + -35.826897, + -31.892145, + 51.208797, + 61.06654, + 23.318872, + -35.0245, + -3.1801224, + 59.059566, + 41.391323, + 56.262905, + -31.190088, + -35.842033, + -44.238426, + -46.75248, + 46.51155, + -24.869604, + -48.693672, + 27.989025, + 57.75085, + 35.379726, + 34.078384, + 40.129883, + 51.36741, + 44.124233, + 38.459312, + -19.088882, + 42.09723, + -32.190376, + 20.918581, + -25.71062, + -44.228165, + -22.265373, + 54.85633, + -31.057253, + 11.446291, + 48.645084, + 33.95719, + 10.624376, + 34.440483, + 13.654009, + -52.42964, + 23.009165, + 0.9160498, + -33.267147, + 1.6248351, + -36.05716, + -7.2311153, + 45.367435, + -12.550289, + 15.268804, + 57.216434, + 20.570063, + 16.519796, + -20.162544, + -39.967007, + -7.045784, + -13.788036, + -6.436385, + -21.87981, + 49.150986, + -31.409056, + 40.020714, + 1.7450867, + -37.63322, + 36.398586, + -31.830273, + -16.392036, + 19.578056, + -30.145031, + 9.273592, + -22.6749, + 49.763367, + 44.87128, + -31.353453, + -45.900715, + -54.722725, + -9.91315, + -33.41616, + -29.682985, + 27.913883, + 5.769484, + -21.902475, + -28.206575, + 34.063007, + -38.86974, + -19.676825, + -27.176733, + -49.362682, + -44.44146, + 5.3805246, + 2.333401, + 14.209792, + 29.130596, + 40.65309, + 7.33986, + 33.885105, + -38.69257, + 42.74865, + -51.24567, + 46.39128, + 63.1983, + -1.2716205, + 2.7853732, + 26.978062, + 18.276062, + 20.191584, + 25.01299, + 10.248553, + 4.6009235, + 9.839586, + 11.750173, + 7.9382405, + -10.997008, + 21.737896, + 49.79338, + -29.136082, + -29.750324, + 54.410885, + -35.14978, + 63.605362, + -51.056225, + 39.934895, + 17.519335, + 17.778656, + 15.492881, + 7.7321296, + 8.952756, + -19.130821, + 40.63617, + -37.452244, + 20.371246, + 30.811249, + -9.127035, + -5.5860677, + 1.1558152, + 47.465935, + -24.740665, + -47.064148, + -54.69983, + 47.272655, + -27.990711, + 63.177612, + -7.06102, + -43.44754, + 24.795843, + -4.7836714, + 41.66488, + 1.8769449, + -24.956768, + 51.543095, + 12.356418, + -53.22971, + 38.820065, + 4.2263513, + -7.9959974, + -23.705156, + -6.0662427, + -37.926384, + -41.1264, + -27.350927, + 31.053217, + -9.149289, + -37.36757, + -16.533398, + 40.088383, + 7.0387945, + -22.092422, + -30.736622, + -44.570576, + 60.45724, + 52.433907, + 9.723743, + -15.802876, + -49.361073, + -25.432766, + 38.667847, + -28.812906, + -22.672857, + -35.77931, + -16.137821, + 27.65755, + 57.766346, + 42.41823, + 26.112234, + -39.176956, + 16.072603, + -48.2029, + 19.677572, + 17.410772, + -6.2585354, + 7.9719267, + -53.251343, + 12.662249, + -9.297528, + -36.831997, + -44.267094, + -42.660313, + 18.940567, + 20.549877, + -19.017382, + 33.992294, + -34.603184, + 56.381645, + -15.977553, + 53.579098, + 7.4309235, + -35.853523, + -15.548051, + -44.87483, + -51.507732, + 19.506048, + -52.502518, + 59.620773, + 8.936648, + 48.37667, + -32.07786, + 14.902041, + 35.445507, + 46.157833, + 49.838924, + -48.87661, + -45.17925, + 29.182852, + -22.362936, + 38.542347, + -10.216267, + 22.10423, + -31.37848, + -2.6893454, + 51.905163, + 21.657618, + -5.704888, + -20.502497, + 30.625587, + -24.823088, + 13.691204, + 28.035511, + 23.045893, + -50.661304, + 43.841885, + -50.370255, + -47.05539, + 56.74711, + 30.591192, + 51.738125, + -11.594388, + 17.440117, + 51.774147, + -23.063238, + -9.929121, + 43.796253, + -38.724506, + 47.406204, + 7.212067, + -41.108536, + -21.799944, + 14.5572, + -36.380856, + -22.186844, + -33.03146, + -47.564026, + -6.8207917, + -0.203547, + 26.660809, + -45.418346, + -32.97913, + -29.1495, + 41.08887, + 2.4019308, + -34.859055, + 14.605348, + 5.080946, + 62.321815, + 30.915781, + 49.839912, + -13.132145, + -12.614871, + 48.11404, + -33.125538, + 37.93922, + -30.265446, + 4.331932, + -24.302145, + -38.971054, + -6.6933794, + 32.7655, + 58.07306, + 50.09836, + 23.97021, + -44.289158, + -16.34018, + -42.824986, + -37.11219, + 54.922394, + -38.334126, + 22.242004, + -12.324585, + -28.60194, + -35.730442, + 52.352432, + 14.265632, + -36.50344, + -27.018137, + -30.541101, + 53.529724, + -7.2380333, + -40.239014, + 7.0784307, + 20.74278, + 2.5284033, + 25.636118, + 4.454403, + -49.050774, + -23.530384, + -23.313187, + 38.338932, + 9.910433, + -22.21815, + -25.737848, + 51.55675, + 37.103165, + -17.621637, + -31.606474, + -46.921032, + -12.631271, + -34.711258, + 14.978659, + -43.354763, + -22.281115, + 45.54423, + -33.235416, + 11.338411, + -43.594814, + 53.86991, + -15.313636, + 47.012283, + -21.579958, + -46.839928, + -45.437263, + 60.093002, + 11.213355, + 32.56739, + -27.061964, + -20.385843, + 15.526145, + -8.932405, + 60.606064, + 9.335806, + -38.67932, + -8.953644, + 39.772743, + 18.62211, + -6.674851, + -41.675705, + -6.503544, + 23.033293, + -5.5465455, + -36.837105, + -4.2590623, + 48.95457, + -42.01228, + 10.529721, + 13.965547, + -3.9804885, + 44.68764, + 48.906673, + 47.63983, + 21.258057, + 62.788, + -6.2482433, + -48.024345, + -12.530503, + -39.613857, + 10.181149, + -34.855972, + 17.598188, + -46.561874, + -17.363302, + 1.3672223, + 32.536667, + 10.24864, + 5.8206697, + -45.638084, + -0.31910038, + -10.62197, + -21.206648, + 38.030407, + -34.547794, + 21.86292, + 56.60054, + 20.400032, + 27.48848, + 2.2426317, + 5.0682087, + -18.876629, + 27.914957, + -17.48441, + -20.422543, + 16.509165, + -27.667318, + -48.115654, + 40.073948, + 60.232296, + 9.352251, + 14.155432, + 56.806816, + 2.808305, + -16.641712, + -19.632275, + -41.143227, + 6.707939, + 45.64992, + 19.51436, + -41.17226, + 39.266872, + -6.392582, + 62.91453, + 18.935217, + 46.280994, + 50.306213, + 53.805332, + -13.137335, + 50.443317, + 53.03957, + 44.309578, + -30.403149, + -33.03263, + -30.970875, + -14.373312, + 8.379798, + 54.42772, + 2.4920332, + 1.7612854, + 34.023724, + -28.959257, + 61.473892, + 50.651646, + -42.69843, + -18.173891, + 27.97626, + -11.489995, + 59.39454, + -50.46992, + 47.18665, + -22.095016, + -0.99369574, + -48.586517, + -28.31348, + 2.79127, + -32.614243, + 16.340908, + 20.619595, + 32.39917, + 59.94177, + 23.400663, + 42.23158, + -40.497093, + 14.445518, + -43.79571, + 56.222717, + 26.900372, + -34.05016, + 59.36177, + -48.04673, + 57.550297, + -10.504851, + -45.725693, + 12.496445, + 60.801098, + -49.58257, + -20.070473, + 57.966537, + 28.753572, + -35.82806, + 55.964886, + -44.020023, + -23.90992, + 45.870426, + 21.319304, + -27.236769, + -37.01328, + -19.117485, + 38.638237, + 49.729176, + -39.115543, + 17.625916, + 11.094263, + 7.11425, + -29.740028, + 18.546873, + 58.080826, + -34.482994, + 37.20064, + 9.897873, + -27.855904, + 24.480858, + -52.830154, + 58.289707, + -48.07056, + -19.067713, + -21.63138, + -40.71425, + -4.696033, + -4.852559, + -17.729515, + 8.527567, + -29.865084, + 25.88273, + -46.45139, + -9.0318775, + 63.36231, + 50.890648, + -8.188348, + 16.88663, + 13.06387, + -25.576069, + -26.325634, + -23.095638, + 29.025854, + -40.87854, + 45.88053, + -38.34742, + -13.60535, + 3.984353, + -1.1919637, + -50.887096, + 50.78542, + -34.409237, + -46.677288, + 5.320594, + 14.373686, + -45.882183, + -32.426746, + 43.456127, + 2.8495433, + 28.731657, + -2.2277532, + 50.339493, + 61.357586, + 11.930037, + -42.132465, + 56.755314, + -18.868166, + -14.928126, + 13.779188, + 23.310764, + -42.33495, + 19.120626, + 18.960714, + 25.783823, + 17.941885, + 55.462612, + 10.820086, + 58.314003, + -45.8806, + -21.790516, + 53.49091, + -51.873066, + -8.934254, + -35.644184, + -43.46856, + -26.787775, + -12.61641, + 11.278602, + -12.760466, + -35.958366, + -9.973649, + -5.3010283, + 8.342169, + 58.012913, + 7.6059, + -7.4377956, + -46.84005, + 49.449314, + 37.930157, + 12.515653, + -54.239532, + -39.886326, + -43.70516, + 57.86416, + 8.195707, + 52.26376, + -40.78544, + -46.046387, + 1.8771796, + -8.241421, + 47.072803, + -12.890557, + -23.360226, + -23.913462, + -10.10402, + -33.456993, + 48.17513, + -34.200912, + 22.029692, + -34.14632, + -40.844006, + 44.906193, + -29.91782, + 4.4929285, + 56.61765, + 56.60834, + -17.537066, + -30.420895, + 56.066643, + -19.92304, + -2.2965894, + 56.162464, + -41.66086, + -57.68235, + 3.6962993, + 11.67213, + -19.05618, + 5.52023, + -48.503033, + -18.99317, + 53.77512, + -14.034199, + 47.758217, + -29.327738, + -27.266224, + 50.96032, + -49.10616, + -4.6537275, + 58.05466, + -8.695738, + 15.926025, + -35.493626, + -52.898724, + 4.0713243, + -16.14875, + -40.76337, + -36.11573, + 41.446438, + -3.7340183, + -15.154654, + 58.41072, + 11.970405, + -16.320389, + -19.673914, + 11.040503, + -36.72977, + -9.829185, + 35.8429, + 47.047108, + -37.2606, + 54.494556, + -52.1362, + 13.273838, + 7.288217, + 47.79968, + -20.01322, + -18.065994, + 8.75742, + -54.428818, + 18.142248, + -9.159126, + 29.14241, + -46.200623, + 17.28087, + 13.877883, + -13.831901, + -21.605253, + 21.1013, + 59.32574, + 13.981468, + 40.920834, + 55.53207, + 44.559975, + -10.860374, + 10.2113, + 28.748735, + 10.333969, + -37.78618, + -45.533035, + 53.77833, + -8.867661, + 12.468114, + 3.0369818, + 32.079, + 47.351242, + -55.4472, + 5.742987, + 24.300056, + -21.27348, + -8.906268, + -34.02309, + -0.9226989, + 32.861256, + -5.918376, + -30.542126, + 38.30059, + 48.4094, + 33.499294, + 1.5139743, + -5.9578004, + 22.857521, + -42.396126, + -16.095537, + 29.347134, + 4.3284388, + 45.721344, + 26.680521, + 45.999187, + 49.048878, + -21.678917, + -48.91647, + -11.771681, + -10.15981, + 39.29256, + 8.132189, + 32.81585, + 11.17274, + 22.79567, + 2.0400486, + 19.547178, + -4.0862207, + -9.854177, + -23.889015, + 26.376568, + -54.596252, + -22.090435, + 32.12724, + -50.986782, + -34.252632, + 59.9222, + 45.969334, + 47.935875, + -4.5817585, + 17.717125, + 32.523216, + 19.772266, + 57.007023, + 34.043217, + 30.42877, + 10.665481, + -16.827753, + -38.59416, + -32.974155, + 15.195456, + -36.174, + -45.269844, + 11.543438, + -19.309122, + -28.692097, + 53.714108, + -18.300999, + -49.752243, + -10.5037985, + 34.008293, + 18.401154, + 33.648438, + -44.20961, + -39.52826, + -27.136961, + 59.613667, + 31.749115, + 7.0795293, + -34.181965, + -37.106304, + 19.923655, + 14.908174, + 52.849945, + 10.556734, + -48.20029, + 9.239984, + 15.951407, + -7.4418893, + -28.779457, + -35.19683, + -54.1994, + 20.179276, + 31.14273, + 0.258186, + -2.1609035, + 61.664543, + 14.35011, + -26.758255, + -54.634964, + 14.368874, + -43.92253, + -42.005432, + -39.611347, + 9.892005, + -39.611637, + -24.87918, + -22.471472, + -38.19811, + 30.838337, + -36.996124, + -4.4758306, + -46.204945, + 43.08786, + -24.678703, + -50.613956, + 49.605602, + 6.150114, + 63.165108, + -20.649567, + 47.894882, + 51.314476, + 44.60029, + 6.031961, + 8.659726, + -15.612729, + -9.729161, + -28.362564, + 10.755605, + -36.588448, + 8.7123785, + -12.811854, + -0.94040644, + -45.534595, + 12.619259, + -44.44866, + -4.227074, + 44.015842, + -22.860474, + -30.753082, + 39.41502, + 0.080250725, + -15.496077, + 20.854275, + -10.390649, + -35.993237, + -36.425526, + -5.6656046, + -36.567635, + 59.81665, + -11.675889, + 14.897927, + 41.209156, + 8.117931, + 6.539579, + -12.951042, + -30.48289, + 47.579025, + 56.48261, + -38.7589, + 46.025146, + -36.49073, + -6.355229, + 58.74744, + 46.206974, + -52.00866, + -31.978811, + -43.13706, + -7.6462755, + -31.936037, + -19.532629, + 53.145702, + 7.7298007, + -36.42381, + 12.733178, + 23.083542, + 60.687424, + -38.00677, + 38.102413, + 39.646805, + -46.434704, + -42.961407, + 52.38563, + -16.082205, + -50.200237, + -29.59413, + -10.404932, + -27.002981, + -20.752146, + 34.14185, + -18.822731, + -38.39936, + -34.192577, + -23.879477, + -49.73623, + -20.585733, + 31.320894, + 6.8278956, + 14.610548, + 40.573475, + -19.3257, + -32.563313, + 7.079915, + -7.734347, + 21.593582, + 41.94092, + 22.709345, + -8.220228, + 30.75048, + 23.351994, + 10.662044, + 6.3287606, + -44.1901, + 20.248484, + 39.690254, + 34.33151, + -21.206255, + 17.894573, + 53.560726, + 18.915913, + -50.147823, + -56.14451, + 50.696335, + 19.135786, + 0.011293956, + -41.132812, + -7.490298, + -6.7789235, + 21.208382, + 5.4172053, + -44.169758, + -47.881756, + -28.388693, + -12.397968, + 29.16581, + -0.9005222, + 58.507614, + 40.03086, + -17.01861, + -49.997864, + -11.5951185, + -38.691113, + 24.29299, + 48.50645, + 38.79774, + -53.174366, + 15.59622, + -8.326396, + 0.79674417, + 10.643132, + -44.02579, + 5.560217, + 0.5841107, + 24.635311, + -28.108793, + 13.113659, + 62.77733, + -20.166492, + 47.435825, + -15.611658, + 18.401346, + -38.040787, + -8.663238, + -30.962019, + -8.084352, + 43.003845, + -39.750137, + 46.27362, + 14.899461, + -45.082096, + -47.16861, + 24.252523, + -4.7970433, + 5.36986, + -16.89367, + -26.904469, + 31.625498, + 10.970106, + 51.867313, + -17.731619, + -34.483925, + -43.073074, + -6.7949033, + -27.989662, + 2.5174408, + 34.368248, + 12.8087, + 35.39813, + -25.524998, + -46.526306, + 53.752186, + 55.804855, + -54.849133, + -40.10975, + -11.253943, + 15.975605, + -24.282412, + -36.69884, + -9.612953, + 27.581682, + 1.6741688, + -53.5042, + -27.687584, + 16.295555, + 3.6958573, + -28.30938, + -35.854397, + 26.508045, + 17.794357, + 30.6338, + 47.806313, + 10.886147, + 56.805237, + -40.808376, + 18.907486, + 49.249695, + -38.4294, + -5.0891867, + -45.114822, + -46.690304, + 49.522606, + -25.18432, + -36.175987, + -41.517033, + -33.290382, + -15.035485, + 61.757652, + 3.8529873, + 61.630924, + -54.139446, + -25.219833, + 39.668633, + 10.995691, + 23.637348, + 33.6961, + 51.79226, + 14.72486, + -53.989174, + 28.194004, + 53.427227, + 45.15016, + 36.015182, + -34.2908, + 43.020264, + 7.9172506, + 54.577732, + -48.755344, + -49.55056, + -39.571285, + -40.278057, + -51.21703, + 18.002365, + -3.3571925, + 19.580015, + -8.731081, + -6.0078135, + 31.860546, + -28.372087, + -0.10420398, + 19.054085, + 37.094307, + -11.813869, + -28.535112, + -1.1245881, + 58.735332, + -40.870914, + 26.428055, + -52.076916, + -16.299625, + 12.898047, + -51.801567, + 35.940807, + 30.280912, + -27.921608, + -36.991142, + 63.004868, + -23.981367, + 47.676117, + 43.803253, + -35.73722, + 52.02361, + 0.08228831, + 57.569775, + -31.23627, + 4.8372564, + 2.4959075, + 6.9097495, + 24.6171, + -36.47172, + -11.448383, + -3.8125634, + -20.261177, + 51.3331, + -4.775729, + 40.77166, + -24.145273, + -0.46443436, + 48.259228, + -45.570045, + -29.613533, + -40.73366, + -19.412077, + -11.283554, + -47.05097, + 49.969627, + -48.772636, + 25.599476, + 36.618427, + -10.298156, + 14.019283, + -43.35723, + 55.361397, + -10.978807, + 51.953743, + -53.829735, + -8.411927, + 15.602155, + -13.247851, + -15.053305, + 40.71827, + -13.399857, + -47.515026, + 62.178337, + -4.658773, + 1.1374025, + -8.963649, + 25.336575, + -29.961985, + -12.003402, + -17.52331, + -50.23115, + 27.973917, + -48.06655, + 39.666965, + -9.277499, + -7.6838555, + 23.369677, + 6.171623, + 26.484608, + 7.0410976, + 19.369898, + -33.914284, + 33.43409, + -15.22937, + -21.86168, + 20.71207, + -7.6405187, + 12.614038, + 17.452501, + 55.207115, + -31.572515, + 32.183567, + -50.991158, + -38.40225, + 16.695406, + -52.86785, + -35.325897, + 18.572897, + -51.80827, + -35.83179, + -41.270184, + -36.710674, + 6.0333753, + 55.5641, + -49.167038, + -21.823997, + -1.3200667, + 5.044943, + -40.638805, + 51.27627, + 47.339336, + 16.012442, + -27.684992, + 63.347527, + 39.062187, + -12.411886, + -41.362526, + 9.572269, + -24.7866, + 26.459038, + -17.990955, + -40.258007, + -2.3985894, + 54.67712, + 2.9941561, + 65.51466, + -37.48171, + 17.726252, + -23.877874, + -34.57765, + -0.9994553, + 45.10427, + 17.785444, + -34.842422, + -51.40557, + -39.0015, + -14.16722, + -31.760511, + -16.35767, + -36.74732, + 47.36583, + 35.328148, + 20.736986, + -50.34398, + -5.775708, + -32.659416, + 30.716692, + 24.382677, + 58.147617, + -19.314493, + -3.8920984, + 16.1644, + 64.86492, + -10.574449, + 19.621206, + 8.682678, + -17.94723, + -24.707636, + -20.651194, + -5.6782784, + -13.584895, + -52.063236, + 32.677113, + 55.061314, + -26.427645, + -33.76177, + -50.93683, + 38.546684, + -14.214475, + 43.151165, + 1.4400622, + -35.708652, + 26.161028, + -41.237144, + 44.980778, + 25.263475, + 16.929596, + -50.64484, + -48.196377, + -10.817454, + -2.0928724, + -25.303522, + 47.840103, + 39.76294, + -23.521646, + 49.251343, + 52.69105, + -43.41168, + 0.50536364, + -41.631573, + 19.154146, + 49.939175, + 46.95092, + 26.26559, + 19.381176, + 12.624142, + 13.547113, + -15.368924, + -44.33141, + 17.735638, + -49.86946, + -25.189764, + -41.6564, + 5.6944747, + 28.887644, + 54.523384, + 11.9049635, + 64.17483, + 19.661798, + -40.866665, + 7.287593, + -48.861267, + 22.103119, + 27.097654, + 58.47151, + 12.937629, + -37.111736, + -49.37285, + -0.5269812, + 50.23622, + -37.09859, + -33.893284, + 18.126286, + -41.025192, + 19.819803, + -2.1707618, + 14.775703, + -27.523653, + 39.812546, + -37.509644, + -48.43532, + 59.636997, + 57.34273, + -37.623196, + -40.202778, + -55.58907, + -41.903214, + 16.772926, + 3.6852949, + 25.670559, + 26.078526, + -49.322422, + -9.049681, + -18.721113, + 48.26851, + 17.1552, + -16.408047, + 9.536311, + 21.02507, + -42.958614, + 12.836097, + 6.9077144, + 13.885367, + -52.688995, + -29.522964, + 25.294838, + 0.9785223, + 42.70037, + 15.134995, + -42.372177, + -30.956533, + 1.8828108, + -15.489649, + 49.12623, + 59.67211, + 10.278181, + -45.431026, + -21.36634, + 47.292377, + 47.805153, + -32.821945, + 3.350846, + 10.675423, + 46.018627, + -27.676836, + -30.13521, + -31.987688, + 2.7699895, + 29.804829, + -4.7174063, + 8.834031, + -30.901245, + -20.815348, + 57.51465, + 37.074707, + 14.13684, + -47.19078, + -45.82224, + -36.344696, + 64.22567, + -46.568104, + -2.3207862, + 10.008406, + 40.90623, + -45.59506, + 42.02211, + 36.001675, + -13.1443, + -43.422806 + ], + "xaxis": "x", + "y": [ + -0.25818, + -20.803589, + -22.326504, + -8.559602, + 22.728033, + 7.8286805, + 23.284092, + 21.800117, + -20.467894, + 22.159718, + -3.7095485, + -16.367886, + 34.67725, + 29.896206, + 6.133116, + -10.627376, + 0.20705454, + 8.674217, + 25.905638, + -3.9541492, + 9.192532, + 25.749458, + 39.722248, + 13.600263, + 7.8999453, + 18.938295, + -7.791385, + -2.1101115, + -17.816854, + -27.949192, + 10.707973, + -5.9476533, + -0.62792206, + 21.421028, + 17.02401, + 3.4177885, + 23.633503, + 9.072081, + 2.5558534, + -10.392384, + -21.783358, + -12.137919, + 8.247171, + -23.29184, + -18.170088, + -27.218586, + -29.326565, + 12.92886, + 7.4292397, + 11.001149, + -35.47235, + 0.2254613, + 26.266212, + -12.614066, + -32.542274, + 6.058426, + 14.819815, + 25.03953, + 4.199548, + -0.6994232, + -29.048313, + 8.901868, + -3.3929446, + -18.953293, + -16.318848, + 1.29799, + 24.970749, + 14.150794, + 0.27214336, + 7.6804514, + -22.118223, + 4.9480743, + -22.427275, + 0.9335098, + 26.755693, + 26.929127, + -26.440922, + 9.1828, + 1.5617585, + -19.087698, + -14.938796, + -2.3146381, + 13.022359, + -21.471975, + 25.554472, + 2.532362, + 23.373753, + -13.859794, + -25.318462, + 22.522005, + -32.54127, + -10.261337, + 2.3437028, + -11.51763, + 24.106895, + -28.920532, + -2.2139447, + -14.537146, + 9.316687, + 9.6741905, + 12.820546, + 4.9088416, + -28.30168, + -17.468342, + -17.185091, + 10.214211, + -20.446613, + -11.397742, + 8.161042, + 27.62886, + 26.281322, + -29.872732, + 5.007877, + 6.2455893, + 5.951754, + -19.64737, + -1.2898456, + -8.973769, + 0.16790108, + 12.575957, + 15.638516, + 9.72588, + 13.993413, + 22.678474, + -17.482075, + 14.391562, + -19.213398, + 17.073126, + 30.481924, + 20.813839, + 32.03464, + -26.55557, + 12.0553255, + -16.22469, + -18.176107, + -3.6315196, + -19.35426, + 20.519714, + 4.681029, + -24.165535, + -17.097263, + -23.540205, + -22.659904, + -30.161833, + 28.830767, + 14.3665, + 0.061168052, + -32.789925, + 4.0007343, + -27.456821, + 23.813591, + -30.553509, + -24.490698, + -19.611755, + 29.56973, + 21.227903, + 7.7406225, + 8.52158, + -10.852377, + 5.442065, + 9.661537, + 22.864084, + 4.1424494, + 7.6243353, + 4.249151, + 31.043804, + -10.734537, + 3.844936, + 1.4751459, + -12.852704, + -20.392239, + -6.189112, + -4.5837173, + 1.4175098, + -21.713743, + -13.330445, + 12.867583, + -10.440891, + 3.6453905, + 8.166061, + -11.337284, + 16.828537, + -8.8150835, + -16.43065, + 9.330847, + 20.16529, + 9.5765, + -22.28117, + -9.1425705, + -23.877768, + -10.817816, + 5.1117396, + 7.9826016, + 11.228575, + 17.663988, + 2.542931, + -1.3406546, + -23.981632, + 12.972686, + 4.730411, + 30.063469, + 3.6004014, + 18.804445, + -13.418971, + 30.71818, + -14.152756, + -24.45379, + -11.355663, + 6.520791, + -9.840589, + 21.164257, + -8.373115, + -8.409088, + 9.545558, + 4.881303, + 30.134317, + -32.61165, + -17.390278, + 32.50385, + 19.963877, + 8.090675, + 31.114712, + 30.646704, + 21.478413, + 14.554468, + 20.755419, + 11.230936, + 6.923768, + -13.468946, + 23.0764, + 20.141148, + -15.70016, + 8.499566, + -19.558147, + -10.837732, + 7.830664, + 24.00407, + 6.959669, + -17.884281, + 24.8098, + -24.985989, + -12.053112, + 8.462659, + 18.15951, + -5.462048, + 2.4064643, + 8.999294, + -12.727203, + -13.069021, + -6.154228, + 14.864804, + 1.5735915, + -25.217607, + -11.249722, + 27.957365, + -0.7906725, + 19.460798, + -2.3412774, + 6.4599543, + 2.4203362, + 32.717518, + 28.99686, + -18.920874, + -7.624435, + -23.937035, + -15.694869, + 2.3350112, + 9.491719, + -25.943512, + 0.82049704, + -3.9954906, + -16.211517, + -10.548126, + 33.583965, + 22.352716, + -0.7140172, + 28.585188, + 20.132593, + 10.375427, + -18.380714, + -21.956186, + 18.302557, + 8.7813, + 27.98141, + 5.231712, + -1.274212, + -17.928478, + -17.166925, + 5.588625, + 1.8213869, + -20.784616, + -9.940092, + -11.329836, + 1.3020672, + -5.6699047, + 2.9951952, + 7.513018, + 18.828894, + -8.567718, + -11.798271, + -2.4976819, + -25.911339, + 22.716187, + -10.770047, + 15.819128, + -15.446808, + -32.171726, + 5.0620914, + 12.743932, + 7.1431947, + 20.908062, + 27.65378, + -29.32608, + -12.216588, + 3.5037541, + -35.429436, + -8.023369, + 19.798025, + -4.302394, + 16.329193, + -23.965172, + 8.796663, + 16.477135, + -11.357406, + 32.09736, + 26.441679, + 21.586815, + 30.292624, + -14.503349, + 19.197943, + -14.683218, + -3.407611, + 23.7153, + -14.726069, + -17.214022, + 15.711783, + -8.98979, + -22.324871, + 0.59863055, + 16.493795, + -27.750652, + -28.93897, + -5.3719177, + -23.418943, + -9.659326, + -23.277813, + 16.425425, + -19.531103, + 18.54026, + 0.31460643, + 31.197924, + -14.720505, + -0.26035935, + -21.057713, + -27.277906, + -7.310227, + -15.416589, + -1.605775, + -8.874298, + -13.5169735, + -26.390093, + 0.7872089, + -7.2581453, + 22.63779, + 28.57203, + -23.089176, + -19.599855, + -21.929888, + -10.379873, + -11.895842, + -17.141865, + -16.003376, + -14.515779, + 10.840164, + -26.575148, + 3.1463404, + -3.7059593, + -8.936446, + -23.257317, + 30.278105, + 15.54324, + -31.523523, + -15.298813, + -29.652391, + -9.050367, + 18.134205, + -14.212201, + 10.717227, + 19.883846, + 21.597916, + -19.211506, + 28.315454, + -11.721406, + 16.122732, + -6.269737, + -14.575271, + -20.626392, + -9.711501, + 20.470428, + -8.267473, + 33.287487, + 25.027699, + 15.167711, + 12.847039, + -22.223913, + -13.995945, + -28.966488, + 14.344031, + 7.419209, + -21.779205, + 24.548212, + 23.27041, + -17.763275, + -27.218397, + -36.186253, + 5.0752234, + 0.31401816, + -0.48519766, + 9.704817, + -22.044197, + 28.721743, + 14.702273, + 18.21779, + 16.7961, + 9.027207, + 21.439281, + 25.772839, + 5.9104095, + 18.049044, + 11.854107, + 25.408955, + -1.7761685, + 7.837817, + -11.143075, + -25.348227, + 20.674139, + -15.303513, + 34.420277, + -6.806543, + 2.799256, + -27.043676, + 32.15406, + 6.988793, + -29.502745, + 5.2307787, + 24.185543, + 17.168627, + -6.9711366, + 28.700588, + -16.839674, + -6.9957857, + 19.155857, + 22.57425, + 4.2664466, + 10.645888, + -2.8677607, + 17.716654, + 33.268223, + 13.592724, + 35.533974, + 35.79897, + -9.217092, + -7.505608, + 5.3443413, + 16.755838, + 19.649885, + -13.013833, + 2.553211, + 5.488912, + 25.960653, + -14.678428, + -6.362675, + 15.933173, + -25.562366, + -7.9709535, + -19.333553, + 5.761818, + 5.2738123, + 14.799318, + 0.9805258, + -30.191147, + -8.254407, + -9.329842, + 24.331854, + -1.1096494, + -27.81828, + -23.302309, + 10.189425, + -0.9053779, + 14.969123, + -12.578425, + -16.734713, + -25.194714, + 34.912987, + -36.29533, + -0.7015533, + -21.124685, + 33.794212, + -20.977274, + -19.704374, + 23.483368, + -15.128482, + 8.0363655, + 2.2579987, + -16.33133, + 31.233051, + 22.297411, + -11.6483135, + 3.5171926, + 23.886812, + 12.337329, + -19.59588, + -30.116133, + 27.538383, + -19.748474, + -4.7339125, + 19.465944, + -18.429428, + -24.985508, + -24.043522, + 26.484413, + 16.774218, + 5.9628015, + -14.398376, + -23.032887, + -16.154268, + -11.766295, + -27.591204, + 20.015493, + -20.486948, + 7.6020126, + -13.656402, + 14.815331, + -33.948692, + -33.920197, + -9.174384, + 20.629124, + 16.143784, + 8.925708, + 7.7047353, + -21.596968, + 16.84247, + 11.881365, + -22.970503, + 24.66648, + 1.9238061, + 25.418554, + -17.758942, + 3.5172246, + 23.261137, + -8.986503, + 28.923544, + -7.5245304, + -15.130549, + 5.0646152, + 21.07103, + -5.8668604, + -14.940109, + -6.4981833, + -20.06512, + 23.290081, + -11.591567, + -27.786598, + 20.645449, + -5.3597302, + -11.159512, + -13.735753, + 18.798145, + -32.18803, + 8.9016, + -22.157974, + 26.788364, + -16.650103, + 18.377977, + -18.147429, + -24.88111, + 21.901451, + -14.823587, + -0.6368593, + 3.2132275, + 31.100603, + 16.802742, + 20.371767, + -28.899687, + 0.73946625, + 0.94949424, + -14.675726, + -24.362509, + 31.862827, + 23.13797, + 35.12017, + -18.907366, + 24.827017, + 31.66899, + -18.148087, + -24.660992, + 9.816621, + 16.572128, + 25.328583, + -15.456796, + 1.9859632, + 5.658062, + -5.2393093, + 9.180699, + 7.721218, + 3.9763682, + -14.759153, + 8.72019, + -12.5096655, + 4.320076, + 2.0307107, + -12.368451, + -11.865506, + 16.297318, + 0.7318651, + -13.755454, + -21.899122, + -11.081378, + -19.075409, + -13.679028, + 10.51185, + -10.045945, + -2.6716044, + 13.364902, + 20.333702, + 5.9486156, + -30.512154, + -1.8922254, + -14.551722, + -13.595177, + 24.951237, + 15.502925, + -26.033178, + -15.84722, + -0.48769227, + 5.509095, + 25.674028, + 23.005444, + 12.414623, + -7.935221, + 24.642124, + -22.191689, + -19.237648, + 16.660208, + 5.5806613, + 9.362999, + 16.740986, + -14.059228, + -9.914337, + -20.576859, + -10.982109, + 31.096636, + -11.43558, + -17.933233, + -22.175861, + -14.856947, + 26.15921, + -23.924995, + 6.894826, + 4.1693807, + 5.6076837, + -17.656506, + 15.090964, + 1.2161766, + -9.937122, + -27.618727, + -3.5818095, + -14.13704, + 25.846468, + 19.352674, + -22.007416, + 23.278618, + 11.748135, + -22.37126, + -22.028944, + -10.037108, + -25.306404, + -7.7222157, + 3.5807598, + -6.6086307, + -19.699232, + -15.10728, + -17.251148, + 10.148522, + -0.68818355, + 7.5768538, + -17.733555, + -23.194473, + 9.637636, + -2.6014824, + 9.428179, + -10.8705435, + 8.272561, + 18.622755, + 8.240764, + 7.8728004, + 13.976609, + 21.211613, + 10.388335, + -13.317306, + -0.20468314, + -0.7534798, + 16.867065, + -22.69967, + 22.19843, + 29.903488, + -29.479254, + 14.083497, + 0.6598771, + -8.660773, + -7.2729115, + -11.945698, + 23.76637, + -16.428364, + -28.303225, + -11.955685, + -30.203144, + -4.9588523, + 26.250034, + 19.381159, + -16.469437, + -14.535694, + -24.852484, + 12.103588, + 7.8694215, + -8.026257, + -6.199936, + 9.750696, + -14.905879, + -22.042368, + 2.0052595, + 15.873175, + -11.668809, + 7.235856, + -21.42294, + 14.838855, + 16.791052, + -21.904455, + -23.169117, + -20.787516, + 9.315685, + 34.738625, + 10.819606, + 20.726511, + -10.898081, + 31.885904, + 11.005908, + 15.028398, + -3.1344242, + -3.9499974, + 14.654819, + 8.201109, + 17.144817, + -19.819767, + -19.525257, + -4.076858, + -24.730019, + 11.900147, + -1.3390135, + 26.11797, + -2.478072, + -23.535704, + 27.143415, + 0.81385136, + 17.844543, + 19.694197, + 30.822157, + 11.223421, + 17.761076, + 13.325627, + -13.261404, + 2.2092547, + -13.576142, + -11.716383, + 27.541485, + -18.290712, + -25.388409, + -15.495678, + -32.85601, + 34.832695, + 15.818021, + 12.122141, + 33.150494, + -0.5336322, + -13.886067, + 28.821224, + 20.72354, + -33.77542, + 3.162032, + 17.181808, + 34.996464, + -22.37821, + -4.1373553, + -20.077517, + -16.791988, + -33.790863, + 4.8909636, + -23.158052, + 13.435741, + -22.73552, + -0.6918705, + 27.578976, + -23.911886, + -0.9915625, + 0.41720697, + -28.11098, + -15.606873, + -21.062717, + -15.843517, + 7.1253057, + -12.007193, + -23.275118, + 15.710144, + -13.556541, + -15.989742, + 1.5220636, + 15.600531, + 3.0372694, + -13.601137, + -7.148113, + -24.879805, + -0.8274632, + -11.567605, + 19.323282, + -7.7168093, + -27.03218, + 5.8135962, + -7.6383777, + 1.1989386, + 3.9182017, + -0.47444645, + -25.135891, + 22.896002, + 0.94497335, + 9.556583, + -4.4569497, + 21.02248, + -25.89945, + -18.168903, + 17.865675, + 22.459995, + 12.360714, + -24.076357, + -15.80312, + 21.917862, + 21.659195, + 33.719093, + 19.704102, + -2.2529974, + 31.99901, + -29.042156, + -26.121319, + 33.52397, + 23.902458, + 7.067429, + 26.534893, + 9.6071, + 29.210163, + -23.639217, + 3.7444665, + 1.8415234, + -4.9220414, + 22.216219, + 21.501694, + -17.915682, + -17.60881, + 19.686275, + 16.870352, + -16.338673, + -2.4079158, + 10.431047, + -11.452592, + 20.084156, + -34.98855, + -30.50168, + -1.8533841, + 13.475318, + 22.79436, + -23.127438, + -2.6888435, + -26.898434, + 32.299854, + 9.865102, + -15.889842, + -7.1564, + 14.4235935, + 10.5956135, + 16.942707, + -17.442066, + -6.0696855, + 0.2748501, + 33.509598, + 2.4050539, + 7.209693, + 12.352939, + -0.83113074, + -16.57776, + 26.730667, + -13.937987, + 5.5682783, + 8.4994335, + -12.461162, + 24.32622, + -25.814455, + -19.692043, + 8.181132, + -25.507462, + -16.080286, + -1.2937344, + 18.989775, + 16.529331, + 11.700205, + -25.712864, + 24.65294, + -5.132745, + 24.787573, + 19.01329, + -9.251707, + -2.7055879, + 14.609039, + 27.475252, + 14.475491, + 0.96339697, + -11.8820095, + 7.1217036, + 31.858027, + 16.848389, + 32.03336, + -13.837845, + -33.480656, + -20.987251, + 30.462563, + -16.143095, + 6.7093077, + -15.854709, + -24.921698, + 16.484713, + -1.7420386, + -23.097334, + 18.896671, + 34.8398, + 10.520301, + 3.5488389, + -18.068623, + 30.076416, + -29.86582, + -8.282391, + -8.46684, + 13.576438, + 3.0699391, + -16.238358, + 2.9773757, + -14.182415, + 17.441216, + -25.85015, + 9.083556, + 22.073168, + 19.385956, + 8.168441, + 13.999631, + -13.918425, + 19.32553, + -19.83609, + 29.535501, + 31.019588, + -6.5198464, + -16.273378, + 31.29178, + -20.836182, + 8.972529, + 14.504229, + -22.65874, + 24.289896, + 0.45974386, + -8.057026, + 7.783574, + -12.477235, + 3.8825731, + -3.5055225, + 15.380986, + 22.033895, + 3.7059414, + -1.0848922, + 0.16963075, + -5.582006, + 11.250292, + 21.913166, + -1.632514, + -23.06022, + -13.376665, + -5.6566067, + -5.0115275, + 33.256733, + -27.384535, + 22.36791, + -23.036457, + 3.1787782, + -11.463062, + 16.85544, + 17.925854, + 26.127491, + 34.042473, + 3.7194152, + 11.578919, + -3.056115, + 8.806574, + -12.564382, + 26.605755, + 21.529955, + 25.043688, + 17.78518, + 25.579552, + 27.044067, + -29.090658, + 21.886444, + -29.44567, + -3.69288, + 7.423554, + 19.89922, + -13.892162, + -9.352621, + -23.756565, + -17.759132, + 21.111221, + -15.3389635, + 20.052608, + 8.306711, + -6.695091, + -0.2840251, + 29.565565, + 6.3890157, + 20.825033, + -15.78091, + -3.9792998, + 8.250312, + -4.315795, + 33.91667, + 31.587502, + -4.7497973, + 0.70931256, + 22.03959, + -1.3183376, + -13.819872, + -8.057265, + 2.5191355, + -6.09211, + -1.2537154, + 1.041188, + 6.271001, + 15.563097, + 3.0869732, + 19.476908, + -7.959283, + -20.58928, + 17.528534, + -34.817635, + 26.520325, + -7.863438, + -13.616495, + 34.081158, + 14.279812, + -23.899826, + 19.227066, + -0.1847365, + 21.436638, + -21.634756, + 27.98984, + -9.426962, + 17.888885, + 18.802984, + -12.24037, + 25.563747, + -18.85435, + 20.995552, + -25.321373, + 11.024011, + -19.68378, + 30.48236, + -26.103676, + 10.497953, + 3.9857144, + -11.662108, + 14.603634, + 7.0568976, + -4.4100275, + 2.030001, + -22.706993, + 19.098873, + 31.796051, + -2.4754145, + -26.096392, + -21.39815, + 3.600532, + 28.98958, + -24.192507, + -22.364601, + 24.713762, + -16.769764, + 21.682661, + -1.3566388, + 16.40951, + 8.210962, + -15.716439, + -34.972008, + 26.949068, + 21.239998, + 12.173473, + 20.502365, + -12.030829, + -28.317688, + 4.4826207, + -4.760545, + -10.980467, + 30.730364, + 20.87726, + -17.78651, + 22.801989, + -5.3119135, + -20.541088, + 12.556309, + 1.3681566, + -15.915366, + 23.323511, + -7.8275642, + 1.0861593, + 8.230685, + -17.60057, + 4.390221, + 9.649646, + -16.683647, + -22.447065, + -10.756376, + 27.087646, + 2.2553952, + 5.474195, + 6.01643, + 14.907442, + -19.740395, + -14.250181, + -28.855429, + -21.907415, + -6.474749, + 26.200584, + 23.3236, + 5.0985155, + 23.742764, + -23.47392, + 10.961509, + -9.009804, + 10.729193, + -16.08439, + 24.293411, + -14.420636, + -0.6379835, + -7.351985, + 4.601816, + -21.606695, + 10.600249, + -19.460848, + -1.0193497, + -5.6577854, + 1.2037258, + -19.941338, + -17.019722, + 32.26841, + -20.533716, + -23.794706, + 2.3137836, + 35.702885, + -2.6479704, + 21.060795, + -4.315942, + -22.034695, + 0.85024196, + 13.542582, + -8.745571, + 6.832896, + -10.188763, + -13.390235, + -0.5990197, + -23.021431, + -5.876716, + -11.976225, + 4.2575808, + 27.501059, + 11.98244, + 26.565365, + -1.931646, + 24.216267, + -16.869408, + -8.099275, + -14.887161, + 2.2845645, + 11.149261, + -15.141055, + 27.739674, + -3.0804467, + 5.0789285, + -17.30228, + -3.2769468, + -17.239506, + 4.583181, + -19.281757, + -3.5722063, + 28.793531, + -16.723783, + 25.030203, + 9.832679, + 20.863323, + -19.392942, + -15.235338, + 11.71164, + -24.406261, + -15.53886, + -16.890417, + -19.303434, + -12.302218, + -21.589676, + -14.588415, + 15.091036, + -17.137983, + -23.051016, + -11.65064, + -1.327813, + 4.7823358, + -19.877468, + 29.76316, + -3.8284235, + 21.326263, + -17.971964, + -2.6890767, + -8.098414, + -20.775913, + 11.0288925, + -15.161179, + -13.708067, + 6.9839473, + 9.420364, + 15.523962, + -1.839738, + 18.062141, + 35.796543, + -26.4286, + 4.53065, + -3.197111, + 15.938968, + -5.59304, + -9.126152, + -23.904675, + 8.384921, + -3.4012072, + -5.3693423, + 32.041183, + -33.521553, + 9.530565, + 15.937399, + -27.414234, + -24.713099, + 24.769993, + -8.645808, + -13.032957, + -23.740261, + 8.2281, + -20.86936, + -5.3864436, + -13.534582, + -1.0408515, + 3.6773765, + 26.929934, + 16.484713, + -3.2705798, + -22.339233, + -17.725012, + 6.1994753, + -13.713904, + 8.064646, + -8.887762, + -27.97785, + 8.281391, + -14.383507, + 1.1649175, + -17.226963, + 23.824167, + -0.03827765, + 21.001068, + -1.6157911, + 1.0350281, + 23.757103, + -2.2386749, + -12.003306, + -5.807004, + 5.4682074, + 3.4183521, + -18.329607, + 10.1421995, + 21.500256, + 20.873947, + 14.622503, + 20.323536, + -22.500238, + -5.1817036, + 26.616285, + -10.172258, + -14.895687, + 7.471235, + 4.855366, + 8.929348, + 3.4454656, + 24.15315, + 33.191727, + -17.779476, + 13.368094, + -16.79903, + -1.2212269, + 29.02862, + 1.353517, + 33.686493, + -9.61028, + -10.290435, + 17.499424, + -18.92016, + 10.638852, + -4.0155163, + -29.874123, + -23.89452, + 17.025469, + 12.36343, + 25.982975, + -5.359385, + -20.511335, + 26.314108, + -14.478729, + 20.105099, + 10.390779, + -2.7448454, + -21.707514, + 2.8463974, + 20.082417, + 39.494793, + 23.544054, + 33.45021, + 1.2731425, + 7.00291, + 20.49504, + 11.026453, + -14.920918, + 21.672586, + -24.179169, + -22.502762, + -18.470171, + -5.233647, + 15.536683, + 7.5924697, + 31.43023, + -10.685339, + -5.5552483, + 30.057226, + 2.6354103, + 17.865553, + -25.625107, + -23.603718, + 16.79463, + -21.343506, + 24.513098, + -22.31949, + -13.1784725, + 14.572172, + -21.927172, + -0.43766883, + 26.446459, + 7.797492, + 27.607801, + -14.08771, + 28.953205, + -1.2875158, + -17.776453, + 1.3350589, + -0.14630945, + -12.744574, + -5.8219385, + 6.380316, + -24.39855, + 1.6557639, + -25.33089, + -10.88375, + -5.4497714, + -3.2008982, + 3.516546, + 3.7044208, + -14.088412, + 1.8123101, + -2.0853994, + -12.914869, + -14.570528, + 6.286185, + 29.915886, + 18.577192, + -19.750566, + -4.8032465, + -14.996935, + 9.808406, + 3.1115727, + 10.539988, + -0.25747964, + 7.8065777, + -9.5224, + 22.650063, + -8.527657, + 25.720367, + 27.335323, + -27.719013, + -27.493273, + -28.8183, + 16.676228, + 15.222469, + -6.1142654, + 23.31772, + 6.885112, + -21.120987, + 31.183216, + 16.581377, + -1.3270321, + -3.024608, + -24.535004, + -35.037914, + 27.32407, + 2.2356973, + 16.557335, + 8.043718, + 4.2089057, + 24.168753, + -0.42459357, + 26.167639, + -19.28855, + -16.932995, + 0.031842478, + 11.079847, + 23.264338, + 11.247658, + 28.108557, + -17.26478, + 23.26528, + -5.613793, + -12.292187, + -13.964472, + 21.349566, + 21.782167, + 26.02513, + -30.554207, + -20.807219, + -22.266432, + -16.260057, + 13.463569, + -20.409258, + 5.911049, + -3.838907, + -30.899261, + -25.502863, + 17.450424, + 4.5370917, + 7.3130083, + 29.060263, + -1.2906566, + -9.992426, + 9.496942, + 19.615667, + -15.057436, + -14.524883, + -5.6858554, + -8.944074, + 30.993462, + -18.399357, + 4.312004, + -12.452006, + 11.88096, + -26.893, + 10.486003, + -14.269513, + 13.904057, + -14.193346, + -17.597988, + -13.744734, + 19.081799, + 7.1376367, + -20.63535, + 0.17712176, + 26.276295, + -4.0243726, + 18.80954, + 8.85504, + -11.71116, + 10.333615, + -33.28943, + -13.433018, + 25.406893, + -21.37861, + -30.53585, + -0.6449607, + -17.676962, + -33.109673, + 6.502574, + 25.979095, + 13.889341, + 24.452019, + -11.330729, + -14.508683, + 7.7211857, + 30.14757, + -15.281551, + 25.445856, + 23.137957, + 2.9930232, + -11.392148, + -3.4584122, + -17.335155, + 32.249325, + 1.1835473, + 0.4309157, + -1.922125, + 18.76773, + 12.763572, + -5.1183553, + -19.383118, + -11.329933, + -9.979049, + -19.62514, + 14.371391, + -9.079416, + 17.039936, + 12.198028, + 17.744976, + -27.767008, + 4.7606173, + 20.943676, + -2.7953665, + 34.946663, + 21.359537, + 23.354967, + 32.181087, + 10.895949, + -23.63617, + 16.164768, + -21.386267, + -0.20407373, + 18.747564, + -8.708449, + 26.564816, + -20.358099, + 3.6623113, + 2.833431, + -2.406363, + -7.6430187, + 30.990358, + -1.6160171, + 22.291674, + 14.2712755, + 8.649531, + -22.09123, + -3.9283407, + -15.144995, + -5.257486, + 16.290205, + 24.053005, + -5.443865, + 29.637974, + -30.894657, + 10.8514185, + -19.329512, + -1.7249132, + -27.617838, + 12.135396, + -20.576097, + -32.521618, + -17.759117, + 14.102587, + -1.4501517, + -17.441105, + 22.34238, + -1.5771652, + -3.4706712, + 19.873198, + 17.654528, + 14.297588, + 35.126564, + 3.530811, + 22.92706, + 1.305536, + -5.8584995, + -3.4917607, + -25.70212, + 15.667845, + -13.110925, + 1.5236746, + 1.27955, + 26.836803, + 22.695467, + -7.542444, + -24.459936, + -4.085233, + -24.834877, + -13.123537, + 13.346765, + 3.3096304, + 5.8128743, + -9.243302, + -22.380308, + 24.534492, + 32.18937, + 0.7944149, + -17.298498, + -7.3226933, + 23.025293, + -0.33986145, + 14.641378, + -32.17766, + 9.108203, + -15.654366, + -3.2708795, + 1.7839518, + 4.667992, + -21.404385, + 33.032204, + 0.07473384, + -8.874142, + 19.918457, + 2.485261, + -26.038076, + 13.791234, + 19.88417, + 26.989523, + 6.4794717, + -8.599584, + 26.08512, + 35.79187, + -3.0957053, + 1.5328475, + -15.78256, + 14.641849, + 0.75382006, + 13.353416, + -20.758772, + 27.588259, + -8.591754, + 7.6756034, + -32.257572, + -3.6816385, + -8.807442, + -23.705658, + 26.69215, + 5.574528, + -3.3590631, + -16.991213, + -18.813177, + 20.353582, + -8.202672, + -2.241037, + -22.663652, + -10.86935, + 22.6146, + 0.538039, + -11.617886, + -7.3185177, + 5.459471, + -20.510658, + 14.793362, + -15.245933, + 2.8498745, + 30.176495, + 25.41137, + 12.340705, + -14.110134, + 20.984993, + -20.736963, + -21.078281, + -16.38932, + -10.101326, + -29.059853, + -14.522557, + -31.21759, + 11.320027, + -1.3346295, + 19.095402, + 3.5003624, + -0.27149853, + 23.530079, + -1.4504046, + -20.799906, + 26.357058, + 25.323908, + 21.914633, + 19.832611, + -14.345478, + -12.780764, + -15.090428, + 0.40740138, + -16.226871, + 22.365917, + 24.898293, + -22.19336, + -17.027992, + -19.892523, + 23.981928, + -11.016326, + -16.473738, + -20.647305, + -18.943878, + -34.179035, + -14.075991, + 1.9298484, + 20.942158, + -15.682211, + -9.76076, + -23.77744, + 2.101532, + -25.935007, + 8.422051, + -21.395668, + -12.298222, + 2.824297, + 12.158624, + 15.439734, + -5.986609, + 22.680363, + -19.286484, + 30.605867, + -0.7899231, + 18.014528, + -18.204716, + -18.893454, + -2.6403008, + -26.197563, + 0.6461262, + -17.935425, + 21.006203, + 19.50926, + -25.124516, + 19.076454, + -13.34786, + -20.217596, + -18.721956, + 13.471852, + 10.719515, + -6.343975, + -4.427436, + 2.1415112, + 0.124456935, + 9.154357, + 15.850318, + 14.106509, + 18.979578, + -25.880474, + 15.075585, + 20.326845, + -15.592323, + -16.127396, + 19.439365, + -18.178284, + -7.721521, + 18.546848, + 1.3289208, + -21.118057, + 15.136754, + -8.462077, + -6.078381, + 0.24295494, + -14.893564, + -3.098876, + -22.965818, + -2.973772, + -10.412807, + 36.82579, + 0.043326903, + -0.730605, + 20.569399, + 20.47704, + 34.56152, + -12.61784, + -22.44099, + -13.279965, + -28.35139, + -9.076381, + -14.49968, + 11.381456, + 27.552359, + 10.113919, + 4.322983, + -16.923988, + 18.366398, + 4.072649, + -18.502573, + 14.2359915, + 1.2205616, + 34.52153, + -13.276994, + 16.888266, + -17.09381, + 26.655972, + 12.712044, + -22.337847, + -18.344118, + -21.796993, + -2.695157, + 33.12794, + 20.795307, + 5.892835, + -30.008844, + 13.092032, + -12.617298, + -26.583797, + -12.331805, + -25.788994, + 18.527788, + -5.358728, + -20.973848, + 21.975595, + 3.6332028, + 21.49163, + -24.02265, + -1.2270886, + 31.648344, + -26.34871, + 28.852188, + 11.337199, + 16.580437, + 6.917111, + -2.6463892, + -13.808859, + 27.402872, + 31.668863, + 10.09489, + -9.203751, + -4.5927486, + -19.010218, + 7.268004, + 27.96568, + -32.725826, + -12.638194, + -9.072612, + 0.687024, + -24.00849, + -16.797096, + -13.887938, + 21.008837, + -20.714098, + 4.003382, + -5.864986, + 6.308118, + -18.954786, + -14.093458, + 14.5252905, + -10.20566, + -5.714998, + -7.6352305, + -11.445573, + 28.259352, + -7.4210625, + -14.774667, + 8.2712965, + -14.246153, + -23.317041, + 0.21726441, + -20.630865, + -24.174063, + -15.430166, + -22.63233, + -5.336508, + -0.4162142, + -17.627256, + -12.0516205, + -10.120339, + 22.627249, + 17.18417, + -24.923342, + 20.119074, + -11.128392, + -23.75025, + -22.75563, + -18.194794, + -2.677447, + 5.6336102, + -8.593113, + -27.35188, + 30.831476, + 6.842084, + -23.006275, + -2.1064568, + -31.873516, + -21.917208, + 11.057577, + 21.760345, + 31.105818, + -7.2484465, + 27.442217, + 27.198599, + -5.4786696, + 20.937487, + -15.238694, + -22.516329, + 16.441422, + -27.548603, + -0.95101047, + -5.9703918, + -20.764137, + 9.63625, + 25.318214, + -4.7924676, + 22.43602, + -29.857277, + -8.804195, + -16.590578, + 6.1655693, + -6.229835, + 9.825396, + 3.6917143, + -25.044327, + -15.101339, + 7.166533, + 18.591246, + -25.983875, + 27.819729, + 24.170658, + 5.3510475, + 6.549803, + -32.0242, + 27.198914, + -3.37324, + -14.339118, + -28.126497, + 22.221628, + -13.358003, + -16.78678, + -32.53302, + 15.152627, + 13.393224, + 19.411095, + 23.425772, + 20.027725, + 24.710947, + 17.26326, + -27.410538, + 26.30866, + -4.510418, + 5.3038287, + 7.526191, + -15.999681, + -2.2162335, + 31.378555, + 6.302167, + 15.184932, + -21.060045, + 14.10122, + 5.90295, + -27.716919, + -16.625145, + -10.241354, + 6.1585164, + 7.223655, + -11.634907, + -21.870625, + -21.870728, + 6.634295, + -6.066459, + -17.1438, + -32.103767, + -10.273103, + 15.137199, + 7.232844, + 21.119562, + 1.9282136, + 12.128642, + 12.653392, + 9.936496, + 21.916615, + 9.071596, + 27.73088, + 14.497267, + -4.162361, + -25.22734, + -22.694798, + -10.849964, + -8.824205, + 20.774977, + 20.526009, + 28.81767, + -15.895552, + -11.81379, + 11.597373, + -10.619046, + -12.564632, + -21.738821, + -13.048038, + -23.010983, + -1.3630763, + 19.897066 + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "PC World - Updated antivirus
software for businesses adds
intrusion prevention features." + ], + [ + "PC World - Send your video
throughout your house--
wirelessly--with new gateways
and media adapters." + ], + [ + "Ziff Davis - The company this
week will unveil more programs
and technologies designed to
ease users of its high-end
servers onto its Integrity
line, which uses Intel's
64-bit Itanium processor." + ], + [ + "PC World - Symantec, McAfee
hope raising virus-definition
fees will move users to\\
suites." + ], + [ + "PC World - The one-time World
Class Product of the Year PDA
gets a much-needed upgrade." + ] + ], + "hovertemplate": "label=Nearest neighbor (top 5)
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Nearest neighbor (top 5)", + "marker": { + "color": "#EF553B", + "size": 5, + "symbol": "diamond" + }, + "mode": "markers", + "name": "Nearest neighbor (top 5)", + "showlegend": true, + "type": "scattergl", + "x": [ + -50.323666, + -50.141148, + -38.19549, + -50.138874, + -50.261745 + ], + "xaxis": "x", + "y": [ + -10.1600275, + -11.487356, + -10.464009, + -9.688497, + -10.61583 + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "PC World - Upcoming chip set
will include built-in security
features for your PC." + ] + ], + "hovertemplate": "label=Source
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", + "legendgroup": "Source", + "marker": { + "color": "#00cc96", + "size": 5, + "symbol": "square" + }, + "mode": "markers", + "name": "Source", + "showlegend": true, + "type": "scattergl", + "x": [ + -50.70537 + ], + "xaxis": "x", + "y": [ + -10.254759 + ], + "yaxis": "y" + } + ], + "layout": { + "height": 500, + "legend": { + "title": { + "text": "label" + }, + "tracegroupgap": 0 + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Nearest neighbors of the chipset security article" + }, + "width": 600, + "xaxis": { + "anchor": "y", + "domain": [ + 0, + 1 + ], + "title": { + "text": "Component 0" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0, + 1 + ], + "title": { + "text": "Component 1" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# a 2D chart of nearest neighbors of the chipset security article\n", + "chart_from_components(\n", + " components=tsne_components,\n", + " labels=chipset_security_labels,\n", + " strings=article_descriptions,\n", + " width=600,\n", + " height=500,\n", + " title=\"Nearest neighbors of the chipset security article\",\n", + " category_orders={\"label\": [\"Other\", \"Nearest neighbor (top 5)\", \"Source\"]},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For the chipset security example, the 4 closest nearest neighbors in the full embedding space remain nearest neighbors in this compressed 2D visualization. The fifth is displayed as more distant, despite being closer in the full embedding space." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Should you want to, you can also make an interactive 3D plot of the embeddings with the function `chart_from_components_3D`. (Doing so will require recomputing the t-SNE components with `n_components=3`.)" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + }, + "kernelspec": { + "display_name": "Python 3.9.9 64-bit ('openai': virtualenv)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb index 58a41439c4..259355a987 100644 --- a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb +++ b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb @@ -53,7 +53,7 @@ "\n", "# search through the reviews for a specific product\n", "def search_reviews(df, product_description, n=3, pprint=True):\n", - " embedding = get_embedding(product_description, engine='babbage-search-query')\n", + " embedding = get_embedding(product_description, engine='text-search-babbage-query-001')\n", " df['similarities'] = df.babbage_search.apply(lambda x: cosine_similarity(x, embedding))\n", "\n", " res = df.sort_values('similarities', ascending=False).head(n).combined.str.replace('Title: ','').str.replace('; Content:', ': ')\n", diff --git a/examples/embeddings/User_and_product_embeddings.ipynb b/examples/embeddings/User_and_product_embeddings.ipynb index ea0ef2f81c..9dacfb8253 100644 --- a/examples/embeddings/User_and_product_embeddings.ipynb +++ b/examples/embeddings/User_and_product_embeddings.ipynb @@ -127,11 +127,8 @@ "import statsmodels.api as sm\n", "\n", "\n", - "\n", - "\n", "correlation = X_test[['percentile_cosine_similarity', 'Score']].corr().values[0,1]\n", - "print('Correlation between user&vector similarity percentile metric and review number of stars (score): %.2f%%' % (100*correlation))\n", - "\n", + "print('Correlation between user & vector similarity percentile metric and review number of stars (score): %.2f%%' % (100*correlation))\n", "\n", "# boxplot of cosine similarity for each score\n", "X_test.boxplot(column='percentile_cosine_similarity', by='Score')\n", diff --git a/examples/embeddings/Visualize_in_3d.ipynb b/examples/embeddings/Visualize_in_3d.ipynb index 990c8edb07..ebbfdf635e 100644 --- a/examples/embeddings/Visualize_in_3d.ipynb +++ b/examples/embeddings/Visualize_in_3d.ipynb @@ -137,7 +137,7 @@ "source": [ "from openai.embeddings_utils import get_embeddings\n", "# NOTE: The following code will send a query of batch size 200 to /embeddings, cost about $0.2\n", - "matrix = get_embeddings(samples[\"text\"].to_list(), engine=\"babbage-similarity\")" + "matrix = get_embeddings(samples[\"text\"].to_list(), engine=\"text-similarity-babbage-001\")" ] }, { diff --git a/examples/embeddings/Zero-shot_classification.ipynb b/examples/embeddings/Zero-shot_classification.ipynb index d4fc1b38ff..d539b256b6 100644 --- a/examples/embeddings/Zero-shot_classification.ipynb +++ b/examples/embeddings/Zero-shot_classification.ipynb @@ -22,9 +22,7 @@ "import pandas as pd\n", "import numpy as np\n", "\n", - "from sklearn.ensemble import RandomForestClassifier\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.metrics import classification_report, accuracy_score\n", + "from sklearn.metrics import classification_report\n", "\n", "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", @@ -83,7 +81,7 @@ "\n", "def evaluate_emeddings_approach(\n", " labels = ['negative', 'positive'], \n", - " engine = 'babbage-similarity',\n", + " engine = 'text-similarity-babbage-001',\n", "):\n", " label_embeddings = [get_embedding(label, engine=engine) for label in labels]\n", "\n", @@ -100,7 +98,7 @@ " display = PrecisionRecallDisplay.from_predictions(df.sentiment, probas, pos_label='positive')\n", " _ = display.ax_.set_title(\"2-class Precision-Recall curve\")\n", "\n", - "evaluate_emeddings_approach(labels=['negative', 'positive'], engine='babbage-similarity')" + "evaluate_emeddings_approach(labels=['negative', 'positive'], engine='text-similarity-babbage-001')" ] }, { @@ -144,7 +142,7 @@ } ], "source": [ - "evaluate_emeddings_approach(labels=['An Amazon review with a negative sentiment.', 'An Amazon review with a positive sentiment.'], engine='babbage-similarity')" + "evaluate_emeddings_approach(labels=['An Amazon review with a negative sentiment.', 'An Amazon review with a positive sentiment.'], engine='text-similarity-babbage-001')" ] }, { @@ -188,7 +186,7 @@ } ], "source": [ - "evaluate_emeddings_approach(labels=['An Amazon review with a negative sentiment.', 'An Amazon review with a positive sentiment.'], engine='babbage-search-query')" + "evaluate_emeddings_approach(labels=['An Amazon review with a negative sentiment.', 'An Amazon review with a positive sentiment.'], engine='text-similarity-babbage-001')" ] }, { diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index e8b29a946b..2db5514c64 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -1,8 +1,13 @@ -from typing import List +import textwrap as tr +from typing import List, Optional import matplotlib.pyplot as plt import numpy as np import pandas as pd +import plotly.express as px +from scipy import spatial +from sklearn.decomposition import PCA +from sklearn.manifold import TSNE from sklearn.metrics import average_precision_score, precision_recall_curve from tenacity import retry, stop_after_attempt, wait_random_exponential @@ -104,3 +109,119 @@ def plot_multiclass_precision_recall( plt.ylabel("Precision") plt.title(f"{classifier_name}: Precision-Recall curve for each class") plt.legend(lines, labels) + + +def distances_from_embeddings( + query_embedding: List[float], + embeddings: List[List[float]], + distance_metric="cosine", +) -> List[List]: + """Return the distances between a query embedding and a list of embeddings.""" + distance_metrics = { + "cosine": spatial.distance.cosine, + "L1": spatial.distance.cityblock, + "L2": spatial.distance.euclidean, + "Linf": spatial.distance.chebyshev, + } + distances = [ + distance_metrics[distance_metric](query_embedding, embedding) + for embedding in embeddings + ] + return distances + + +def indices_of_nearest_neighbors_from_distances(distances) -> np.ndarray: + """Return a list of indices of nearest neighbors from a list of distances.""" + return np.argsort(distances) + + +def pca_components_from_embeddings( + embeddings: List[List[float]], n_components=2 +) -> np.ndarray: + """Return the PCA components of a list of embeddings.""" + pca = PCA(n_components=n_components) + array_of_embeddings = np.array(embeddings) + return pca.fit_transform(array_of_embeddings) + + +def tsne_components_from_embeddings( + embeddings: List[List[float]], n_components=2, **kwargs +) -> np.ndarray: + """Returns t-SNE components of a list of embeddings.""" + # use better defaults if not specified + if "init" not in kwargs.keys(): + kwargs["init"] = "pca" + if "learning_rate" not in kwargs.keys(): + kwargs["learning_rate"] = "auto" + tsne = TSNE(n_components=n_components, **kwargs) + array_of_embeddings = np.array(embeddings) + return tsne.fit_transform(array_of_embeddings) + + +def chart_from_components( + components: np.ndarray, + labels: Optional[List[str]] = None, + strings: Optional[List[str]] = None, + x_title="Component 0", + y_title="Component 1", + mark_size=5, + **kwargs, +): + """Return an interactive 2D chart of embedding components.""" + empty_list = ["" for _ in components] + data = pd.DataFrame( + { + x_title: components[:, 0], + y_title: components[:, 1], + "label": labels if labels else empty_list, + "string": ["
".join(tr.wrap(string, width=30)) for string in strings] + if strings + else empty_list, + } + ) + chart = px.scatter( + data, + x=x_title, + y=y_title, + color="label" if labels else None, + symbol="label" if labels else None, + hover_data=["string"] if strings else None, + **kwargs, + ).update_traces(marker=dict(size=mark_size)) + return chart + + +def chart_from_components_3D( + components: np.ndarray, + labels: Optional[List[str]] = None, + strings: Optional[List[str]] = None, + x_title: str = "Component 0", + y_title: str = "Component 1", + z_title: str = "Compontent 2", + mark_size: int = 5, + **kwargs, +): + """Return an interactive 3D chart of embedding components.""" + empty_list = ["" for _ in components] + data = pd.DataFrame( + { + x_title: components[:, 0], + y_title: components[:, 1], + z_title: components[:, 2], + "label": labels if labels else empty_list, + "string": ["
".join(tr.wrap(string, width=30)) for string in strings] + if strings + else empty_list, + } + ) + chart = px.scatter_3d( + data, + x=x_title, + y=y_title, + z=z_title, + color="label" if labels else None, + symbol="label" if labels else None, + hover_data=["string"] if strings else None, + **kwargs, + ).update_traces(marker=dict(size=mark_size)) + return chart diff --git a/openai/version.py b/openai/version.py index c1830e4131..364338ecfe 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.12.0" +VERSION = "0.13.0" From 3dc4b91eca9b28695876695a2a0b2c39ebdfad15 Mon Sep 17 00:00:00 2001 From: Bastian Zimmermann <10774221+BastianZim@users.noreply.github.com> Date: Tue, 25 Jan 2022 23:56:55 +0300 Subject: [PATCH 014/914] Add entry_points (#52) --- bin/openai => openai/_openai_scripts.py | 0 setup.py | 6 +++++- 2 files changed, 5 insertions(+), 1 deletion(-) rename bin/openai => openai/_openai_scripts.py (100%) diff --git a/bin/openai b/openai/_openai_scripts.py similarity index 100% rename from bin/openai rename to openai/_openai_scripts.py diff --git a/setup.py b/setup.py index 1c468d17f0..7d50fccd3f 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,11 @@ ], extras_require={"dev": ["black~=21.6b0", "pytest==6.*"]}, python_requires=">=3.7.1", - scripts=["bin/openai"], + entry_points={ + 'console_scripts': [ + 'openai=openai._openai_scripts:main', + ], + }, packages=find_packages(exclude=["tests", "tests.*"]), package_data={ "openai": [ From f288b0017ad71da7140851f44cebff6f772f0655 Mon Sep 17 00:00:00 2001 From: Sorin Suciu Date: Thu, 27 Jan 2022 05:10:03 +0100 Subject: [PATCH 015/914] Add documentation for usage with Azure endpoints. (#68) --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index cb688fde7a..1aad1ff16b 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,32 @@ completion = openai.Completion.create(engine="ada", prompt="Hello world") print(completion.choices[0].text) ``` +### Microsoft Azure Endpoints + +In order to use the library with Microsoft Azure endpoints, you need to set the api_type, api_base and api_version in addition to the api_key. The api_type must be set to 'azure' and the others correspond to the properites of your endpoint. +In addition, the deployment name must be passed as the engine parameter. + +```python +import openai +openai.api_type = "azure" +openai.api_key = "..." +openai.api_base = "https://example-endpoint.openai.azure.com/" +openai.api_version = "2021-11-01-preview" + +# create a completion +completion = openai.Completion.create(engine="deployment-namme", prompt="Hello world") + +# print the completion +print(completion.choices[0].text) + +# create a search and pass the deployment-name as the engine Id. +search = openai.Engine(id="deployment-namme").search(documents=["White House", "hospital", "school"], query ="the president") + +# print the search +print(search) +``` +Please note that for the moment, the Microsoft Azure endpoints can only be used for completion and search operations. + ### Command-line interface This library additionally provides an `openai` command-line utility From 62b51ca067e51854ce77cf9dc7b8a891074b6ac6 Mon Sep 17 00:00:00 2001 From: Boris Dayma Date: Tue, 1 Feb 2022 13:58:29 -0600 Subject: [PATCH 016/914] feat: openai wandb sync (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: log fine_tune with wandb * feat: ensure we are logged in * feat: cli wandb namespace * feat: add fine_tuned_model to summary * feat: log training & validation files * feat: re-log if was not successful or force * doc: add docstring * feat: set wandb api only when needed * fix: train/validation files are inputs * feat: rename artifact type * feat: improve config logging * feat: log all jobs by default * feat: log job details * feat: log -> sync * feat: cli wandb log -> sync * fix: validation_files not always present * feat: format created_at + style * feat: log number of training/validation samples * feat(wandb): avoid download if file already synced * feat(wandb): add number of items to metadata * fix(wandb): allow force sync * feat(wandb): job -> fine-tune * refactor(wandb): use show_individual_warnings * feat(wandb): Logger -> WandbLogger * feat(wandb): retrive number of items from artifact * doc(wandb): add link to documentation --- README.md | 7 + openai/_openai_scripts.py | 4 +- openai/cli.py | 51 +++++++ openai/wandb_logger.py | 290 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 openai/wandb_logger.py diff --git a/README.md b/README.md index 1aad1ff16b..9da57aaed8 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ search = openai.Engine(id="deployment-namme").search(documents=["White House", " # print the search print(search) ``` + Please note that for the moment, the Microsoft Azure endpoints can only be used for completion and search operations. ### Command-line interface @@ -142,6 +143,12 @@ Examples of fine tuning are shared in the following Jupyter notebooks: - [Step 2: Creating a synthetic Q&A dataset](https://github.com/openai/openai-python/blob/main/examples/finetuning/olympics-2-create-qa.ipynb) - [Step 3: Train a fine-tuning model specialized for Q&A](https://github.com/openai/openai-python/blob/main/examples/finetuning/olympics-3-train-qa.ipynb) +Sync your fine-tunes to [Weights & Biases](https://wandb.me/openai-docs) to track experiments, models, and datasets in your central dashboard with: + +```bash +openai wandb sync +``` + For more information on fine tuning, read the [fine-tuning guide](https://beta.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. ## Requirements diff --git a/openai/_openai_scripts.py b/openai/_openai_scripts.py index 3c34b69347..d234256c62 100755 --- a/openai/_openai_scripts.py +++ b/openai/_openai_scripts.py @@ -4,7 +4,7 @@ import sys import openai -from openai.cli import api_register, display_error, tools_register +from openai.cli import api_register, display_error, tools_register, wandb_register logger = logging.getLogger() formatter = logging.Formatter("[%(asctime)s] %(message)s") @@ -39,9 +39,11 @@ def help(args): subparsers = parser.add_subparsers() sub_api = subparsers.add_parser("api", help="Direct API calls") sub_tools = subparsers.add_parser("tools", help="Client side tools for convenience") + sub_wandb = subparsers.add_parser("wandb", help="Logging with Weights & Biases") api_register(sub_api) tools_register(sub_tools) + wandb_register(sub_wandb) args = parser.parse_args() if args.verbosity == 1: diff --git a/openai/cli.py b/openai/cli.py index 4c5c8559cf..c57d4c973e 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -19,6 +19,7 @@ write_out_file, write_out_search_file, ) +import openai.wandb_logger class bcolors: @@ -535,6 +536,19 @@ def prepare_data(cls, args): ) +class WandbLogger: + @classmethod + def sync(cls, args): + resp = openai.wandb_logger.WandbLogger.sync( + id=args.id, + n_fine_tunes=args.n_fine_tunes, + project=args.project, + entity=args.entity, + force=args.force, + ) + print(resp) + + def tools_register(parser): subparsers = parser.add_subparsers( title="Tools", help="Convenience client side tools" @@ -954,3 +968,40 @@ def help(args): sub = subparsers.add_parser("fine_tunes.cancel") sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") sub.set_defaults(func=FineTune.cancel) + + +def wandb_register(parser): + subparsers = parser.add_subparsers( + title="wandb", help="Logging with Weights & Biases" + ) + + def help(args): + parser.print_help() + + parser.set_defaults(func=help) + + sub = subparsers.add_parser("sync") + sub.add_argument("-i", "--id", help="The id of the fine-tune job (optional)") + sub.add_argument( + "-n", + "--n_fine_tunes", + type=int, + default=None, + help="Number of most recent fine-tunes to log when an id is not provided. By default, every fine-tune is synced.", + ) + sub.add_argument( + "--project", + default="GPT-3", + help="""Name of the project where you're sending runs. By default, it is "GPT-3".""", + ) + sub.add_argument( + "--entity", + help="Username or team name where you're sending runs. By default, your default entity is used, which is usually your username.", + ) + sub.add_argument( + "--force", + action="http://github.com/store_true", + help="Forces logging and overwrite existing wandb run of the same fine-tune.", + ) + sub.set_defaults(force=False) + sub.set_defaults(func=WandbLogger.sync) diff --git a/openai/wandb_logger.py b/openai/wandb_logger.py new file mode 100644 index 0000000000..7bdacd711c --- /dev/null +++ b/openai/wandb_logger.py @@ -0,0 +1,290 @@ +try: + import wandb + + WANDB_AVAILABLE = True +except: + WANDB_AVAILABLE = False + + +if WANDB_AVAILABLE: + import datetime + import io + import json + from pathlib import Path + + import numpy as np + import pandas as pd + + from openai import File, FineTune + + +class WandbLogger: + """ + Log fine-tunes to [Weights & Biases](https://wandb.me/openai-docs) + """ + + if not WANDB_AVAILABLE: + print("Logging requires wandb to be installed. Run `pip install wandb`.") + else: + _wandb_api = None + _logged_in = False + + @classmethod + def sync( + cls, + id=None, + n_fine_tunes=None, + project="GPT-3", + entity=None, + force=False, + **kwargs_wandb_init, + ): + """ + Sync fine-tunes to Weights & Biases. + :param id: The id of the fine-tune (optional) + :param n_fine_tunes: Number of most recent fine-tunes to log when an id is not provided. By default, every fine-tune is synced. + :param project: Name of the project where you're sending runs. By default, it is "GPT-3". + :param entity: Username or team name where you're sending runs. By default, your default entity is used, which is usually your username. + :param force: Forces logging and overwrite existing wandb run of the same fine-tune. + """ + + if not WANDB_AVAILABLE: + return + + if id: + fine_tune = FineTune.retrieve(id=id) + fine_tune.pop("events", None) + fine_tunes = [fine_tune] + + else: + # get list of fine_tune to log + fine_tunes = FineTune.list() + if not fine_tunes or fine_tunes.get("data") is None: + print("No fine-tune has been retrieved") + return + fine_tunes = fine_tunes["data"][ + -n_fine_tunes if n_fine_tunes is not None else None : + ] + + # log starting from oldest fine_tune + show_individual_warnings = ( + False if id is None and n_fine_tunes is None else True + ) + fine_tune_logged = [ + cls._log_fine_tune( + fine_tune, + project, + entity, + force, + show_individual_warnings, + **kwargs_wandb_init, + ) + for fine_tune in fine_tunes + ] + + if not show_individual_warnings and not any(fine_tune_logged): + print("No new successful fine-tunes were found") + + return "🎉 wandb sync completed successfully" + + @classmethod + def _log_fine_tune( + cls, + fine_tune, + project, + entity, + force, + show_individual_warnings, + **kwargs_wandb_init, + ): + fine_tune_id = fine_tune.get("id") + status = fine_tune.get("status") + + # check run completed successfully + if show_individual_warnings and status != "succeeded": + print( + f'Fine-tune {fine_tune_id} has the status "{status}" and will not be logged' + ) + return + + # check run has not been logged already + run_path = f"{project}/{fine_tune_id}" + if entity is not None: + run_path = f"{entity}/{run_path}" + wandb_run = cls._get_wandb_run(run_path) + if wandb_run: + wandb_status = wandb_run.summary.get("status") + if show_individual_warnings: + if wandb_status == "succeeded": + print( + f"Fine-tune {fine_tune_id} has already been logged successfully at {wandb_run.url}" + ) + if not force: + print( + 'Use "--force" in the CLI or "force=True" in python if you want to overwrite previous run' + ) + else: + print( + f"A run for fine-tune {fine_tune_id} was previously created but didn't end successfully" + ) + if wandb_status != "succeeded" or force: + print( + f"A new wandb run will be created for fine-tune {fine_tune_id} and previous run will be overwritten" + ) + if wandb_status == "succeeded" and not force: + return + + # retrieve results + results_id = fine_tune["result_files"][0]["id"] + results = File.download(id=results_id).decode("utf-8") + + # start a wandb run + wandb.init( + job_type="fine-tune", + config=cls._get_config(fine_tune), + project=project, + entity=entity, + name=fine_tune_id, + id=fine_tune_id, + **kwargs_wandb_init, + ) + + # log results + df_results = pd.read_csv(io.StringIO(results)) + for _, row in df_results.iterrows(): + metrics = {k: v for k, v in row.items() if not np.isnan(v)} + step = metrics.pop("step") + if step is not None: + step = int(step) + wandb.log(metrics, step=step) + fine_tuned_model = fine_tune.get("fine_tuned_model") + if fine_tuned_model is not None: + wandb.summary["fine_tuned_model"] = fine_tuned_model + + # training/validation files and fine-tune details + cls._log_artifacts(fine_tune, project, entity) + + # mark run as complete + wandb.summary["status"] = "succeeded" + + wandb.finish() + return True + + @classmethod + def _ensure_logged_in(cls): + if not cls._logged_in: + if wandb.login(): + cls._logged_in = True + else: + raise Exception("You need to log in to wandb") + + @classmethod + def _get_wandb_run(cls, run_path): + cls._ensure_logged_in() + try: + if cls._wandb_api is None: + cls._wandb_api = wandb.Api() + return cls._wandb_api.run(run_path) + except Exception: + return None + + @classmethod + def _get_wandb_artifact(cls, artifact_path): + cls._ensure_logged_in() + try: + if cls._wandb_api is None: + cls._wandb_api = wandb.Api() + return cls._wandb_api.artifact(artifact_path) + except Exception: + return None + + @classmethod + def _get_config(cls, fine_tune): + config = dict(fine_tune) + for key in ("training_files", "validation_files", "result_files"): + if config.get(key) and len(config[key]): + config[key] = config[key][0] + if config.get("created_at"): + config["created_at"] = datetime.datetime.fromtimestamp(config["created_at"]) + return config + + @classmethod + def _log_artifacts(cls, fine_tune, project, entity): + # training/validation files + training_file = ( + fine_tune["training_files"][0] + if fine_tune.get("training_files") and len(fine_tune["training_files"]) + else None + ) + validation_file = ( + fine_tune["validation_files"][0] + if fine_tune.get("validation_files") and len(fine_tune["validation_files"]) + else None + ) + for file, prefix, artifact_type in ( + (training_file, "train", "training_files"), + (validation_file, "valid", "validation_files"), + ): + if file is not None: + cls._log_artifact_inputs(file, prefix, artifact_type, project, entity) + + # fine-tune details + fine_tune_id = fine_tune.get("id") + artifact = wandb.Artifact( + "fine_tune_details", + type="fine_tune_details", + metadata=fine_tune, + ) + with artifact.new_file("fine_tune_details.json") as f: + json.dump(fine_tune, f, indent=2) + wandb.run.log_artifact( + artifact, + aliases=["latest", fine_tune_id], + ) + + @classmethod + def _log_artifact_inputs(cls, file, prefix, artifact_type, project, entity): + file_id = file["id"] + filename = Path(file["filename"]).name + stem = Path(file["filename"]).stem + + # get input artifact + artifact_name = f"{prefix}-{filename}" + artifact_alias = file_id + artifact_path = f"{project}/{artifact_name}:{artifact_alias}" + if entity is not None: + artifact_path = f"{entity}/{artifact_path}" + artifact = cls._get_wandb_artifact(artifact_path) + + # create artifact if file not already logged previously + if artifact is None: + # get file content + try: + file_content = File.download(id=file_id).decode("utf-8") + except: + print( + f"File {file_id} could not be retrieved. Make sure you are allowed to download training/validation files" + ) + return + artifact = wandb.Artifact(artifact_name, type=artifact_type, metadata=file) + with artifact.new_file(filename, mode="w") as f: + f.write(file_content) + + # create a Table + try: + table, n_items = cls._make_table(file_content) + artifact.add(table, stem) + wandb.config.update({f"n_{prefix}": n_items}) + artifact.metadata["items"] = n_items + except: + print(f"File {file_id} could not be read as a valid JSON file") + else: + # log number of items + wandb.config.update({f"n_{prefix}": artifact.metadata.get("items")}) + + wandb.run.use_artifact(artifact, aliases=["latest", artifact_alias]) + + @classmethod + def _make_table(cls, file_content): + df = pd.read_json(io.StringIO(file_content), orient="records", lines=True) + return wandb.Table(dataframe=df), len(df) From 946aa8fa4a49f8331a1ad73ce2b063e26b1980d7 Mon Sep 17 00:00:00 2001 From: hallacy Date: Wed, 2 Feb 2022 13:59:23 -0800 Subject: [PATCH 017/914] [headers] Allow user provided headers in completion (#116) (#71) --- openai/api_requestor.py | 35 +++++++++++++++---- .../abstract/engine_api_resource.py | 8 ++++- openai/openai_object.py | 7 +++- openai/tests/test_endpoints.py | 6 ++++ 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index cac62f5544..dd618c6e5d 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -100,8 +100,8 @@ def request( result = self.request_raw( method.lower(), url, - params, - headers, + params=params, + supplied_headers=headers, files=files, stream=stream, request_id=request_id, @@ -212,18 +212,41 @@ def request_headers( return headers + def _validate_headers( + self, supplied_headers: Optional[Dict[str, str]] + ) -> Dict[str, str]: + headers: Dict[str, str] = {} + if supplied_headers is None: + return headers + + if not isinstance(supplied_headers, dict): + raise TypeError("Headers must be a dictionary") + + for k, v in supplied_headers.items(): + if not isinstance(k, str): + raise TypeError("Header keys must be strings") + if not isinstance(v, str): + raise TypeError("Header values must be strings") + headers[k] = v + + # NOTE: It is possible to do more validation of the headers, but a request could always + # be made to the API manually with invalid headers, so we need to handle them server side. + + return headers + def request_raw( self, method, url, + *, params=None, - supplied_headers=None, + supplied_headers: Dict[str, str] = None, files=None, - stream=False, + stream: bool = False, request_id: Optional[str] = None, ) -> requests.Response: abs_url = "%s%s" % (self.api_base, url) - headers = {} + headers = self._validate_headers(supplied_headers) data = None if method == "get" or method == "delete": @@ -246,8 +269,6 @@ def request_raw( ) headers = self.request_headers(method, headers, request_id) - if supplied_headers is not None: - headers.update(supplied_headers) util.log_info("Request to OpenAI API", method=method, path=abs_url) util.log_debug("Post details", data=data, api_version=self.api_version) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index 84904e1f80..e3592c8599 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -63,6 +63,7 @@ def create( engine = params.pop("engine", None) timeout = params.pop("timeout", None) stream = params.get("stream", False) + headers = params.pop("headers", None) if engine is None and cls.engine_required: raise error.InvalidRequestError( "Must provide an 'engine' parameter to create a %s" % cls, "engine" @@ -87,7 +88,12 @@ def create( ) url = cls.class_url(http://github.com/engine,%20api_type,%20api_version) response, _, api_key = requestor.request( - "post", url, params, stream=stream, request_id=request_id + "post", + url, + params=params, + headers=headers, + stream=stream, + request_id=request_id, ) if stream: diff --git a/openai/openai_object.py b/openai/openai_object.py index 3dbd2a8ed7..f785c89484 100644 --- a/openai/openai_object.py +++ b/openai/openai_object.py @@ -176,7 +176,12 @@ def request( organization=self.organization, ) response, stream, api_key = requestor.request( - method, url, params, stream=stream, headers=headers, request_id=request_id + method, + url, + params=params, + stream=stream, + headers=headers, + request_id=request_id, ) if stream: diff --git a/openai/tests/test_endpoints.py b/openai/tests/test_endpoints.py index 6ef4d2d373..80039aa995 100644 --- a/openai/tests/test_endpoints.py +++ b/openai/tests/test_endpoints.py @@ -28,3 +28,9 @@ def test_completions_multiple_prompts(): prompt=["This was a test", "This was another test"], n=5, engine="ada" ) assert len(result.choices) == 10 + + +def test_completions_model(): + result = openai.Completion.create(prompt="This was a test", n=5, model="ada") + assert len(result.choices) == 5 + assert result.model.startswith("ada:") From a7e259c66f734d0400527320bbb2a0e9e9f7cf5f Mon Sep 17 00:00:00 2001 From: hallacy Date: Wed, 2 Feb 2022 14:05:26 -0800 Subject: [PATCH 018/914] Bump version to 0.14.0 (#72) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 364338ecfe..ecea435b31 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.13.0" +VERSION = "0.14.0" From 54852345065612bebfe26ad3d02cff1794abcafc Mon Sep 17 00:00:00 2001 From: Tomer Kaftan Date: Wed, 23 Feb 2022 15:39:12 -0800 Subject: [PATCH 019/914] (Version 0.15.0) Add fine tune creation suffix arg to openai cli (#75) * Add fine tune creation suffix arg to openai cli (#120) * Update the minor version from 0.14 to 0.15 due to adding a new `suffix` CLI arg when creating finetune models --- openai/cli.py | 12 ++++++++++++ openai/version.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/openai/cli.py b/openai/cli.py index c57d4c973e..c46632eb2f 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -394,6 +394,7 @@ def create(cls, args): for hparam in ( "model", + "suffix", "n_epochs", "batch_size", "learning_rate_multiplier", @@ -880,6 +881,17 @@ def help(args): "--model", help="The model to start fine-tuning from", ) + sub.add_argument( + "--suffix", + help="If set, this argument can be used to customize the generated fine-tuned model name." + "All punctuation and whitespace in `suffix` will be replaced with a " + "single dash, and the string will be lower cased. The max " + "length of `suffix` is 40 chars. " + "The generated name will match the form `{base_model}:ft-{org-title}:{suffix}-{timestamp}`. " + 'For example, `openai api fine_tunes.create -t test.jsonl -m ada --suffix "custom model name" ' + "could generate a model with the name " + "ada:ft-your-org:custom-model-name-2022-02-15-04-21-04", + ) sub.add_argument( "--no_follow", action="http://github.com/store_true", diff --git a/openai/version.py b/openai/version.py index ecea435b31..7376d84f5a 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.14.0" +VERSION = "0.15.0" From 101b444cd0f2ce11123466995ba9007f16933664 Mon Sep 17 00:00:00 2001 From: Boris Dayma Date: Mon, 28 Feb 2022 19:18:10 -0600 Subject: [PATCH 020/914] =?UTF-8?q?fix(wandb):=E2=80=AFno=20results=20if?= =?UTF-8?q?=20not=20succeeded=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openai/wandb_logger.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openai/wandb_logger.py b/openai/wandb_logger.py index 7bdacd711c..fd2df9d7b6 100644 --- a/openai/wandb_logger.py +++ b/openai/wandb_logger.py @@ -101,10 +101,11 @@ def _log_fine_tune( status = fine_tune.get("status") # check run completed successfully - if show_individual_warnings and status != "succeeded": - print( - f'Fine-tune {fine_tune_id} has the status "{status}" and will not be logged' - ) + if status != "succeeded": + if show_individual_warnings: + print( + f'Fine-tune {fine_tune_id} has the status "{status}" and will not be logged' + ) return # check run has not been logged already From 2b264c39f8ce6960e81df0cd401c7581767a0f64 Mon Sep 17 00:00:00 2001 From: Sorin Suciu Date: Fri, 11 Mar 2022 11:19:57 -0800 Subject: [PATCH 021/914] Add support for fine-tunning and files using the Azure API. (#76) * Add support for fine-tunning and files using the Azure API. * Small changes + version bumps * Version bump after merge * fix typo * adressed comments --- .gitignore | 3 +- README.md | 2 +- openai/api_requestor.py | 6 ++- openai/api_resources/abstract/api_resource.py | 25 +++++++--- .../abstract/createable_api_resource.py | 16 ++++++- .../abstract/deletable_api_resource.py | 20 ++++++-- .../abstract/engine_api_resource.py | 14 +++--- .../abstract/listable_api_resource.py | 17 ++++++- openai/api_resources/file.py | 46 ++++++++++++++++--- openai/api_resources/fine_tune.py | 39 ++++++++++++++-- openai/cli.py | 2 +- openai/version.py | 2 +- 12 files changed, 153 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index 6af1d15f0a..de92124490 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ __pycache__ build *.egg .vscode/settings.json -.ipynb_checkpoints \ No newline at end of file +.ipynb_checkpoints +.vscode/launch.json diff --git a/README.md b/README.md index 9da57aaed8..a50484a8fc 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ search = openai.Engine(id="deployment-namme").search(documents=["White House", " print(search) ``` -Please note that for the moment, the Microsoft Azure endpoints can only be used for completion and search operations. +Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, search and fine-tuning operations. ### Command-line interface diff --git a/openai/api_requestor.py b/openai/api_requestor.py index dd618c6e5d..346e148c1a 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -111,7 +111,7 @@ def request( def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): try: - error_data = resp["error"] + error_data = resp["error"] if self.api_type == ApiType.OPEN_AI else resp except (KeyError, TypeError): raise error.APIError( "Invalid response object from API: %r (HTTP response code " @@ -322,6 +322,10 @@ def _interpret_response( def _interpret_response_line( self, rbody, rcode, rheaders, stream: bool ) -> OpenAIResponse: + # HTTP 204 response code does not have any content in the body. + if rcode == 204: + return OpenAIResponse(None, rheaders) + if rcode == 503: raise error.ServiceUnavailableError( "The server is overloaded or not ready yet.", rbody, rcode, headers=rheaders diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index c25aafd656..d9cfda2e05 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -8,7 +8,8 @@ class APIResource(OpenAIObject): api_prefix = "" - azure_api_prefix = 'openai/deployments' + azure_api_prefix = 'openai' + azure_deployments_prefix = 'deployments' @classmethod def retrieve(cls, id, api_key=None, request_id=None, **params): @@ -46,20 +47,21 @@ def instance_url(http://github.com/self,%20operation=None): "id", ) api_version = self.api_version or openai.api_version + extn = quote_plus(id) if self.typed_api_type == ApiType.AZURE: if not api_version: raise error.InvalidRequestError("An API version is required for the Azure API type.") + if not operation: - raise error.InvalidRequestError( - "The request needs an operation (eg: 'search') for the Azure OpenAI API type." - ) - extn = quote_plus(id) - return "/%s/%s/%s?api-version=%s" % (self.azure_api_prefix, extn, operation, api_version) + base = self.class_url() + return "/%s%s/%s?api-version=%s" % (self.azure_api_prefix, base, extn, api_version) + + return "/%s/%s/%s/%s?api-version=%s" % ( + self.azure_api_prefix, self.azure_deployments_prefix, extn, operation, api_version) elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url() - extn = quote_plus(id) return "%s/%s" % (base, extn) else: @@ -75,6 +77,7 @@ def _static_request( url_, api_key=None, api_base=None, + api_type=None, request_id=None, api_version=None, organization=None, @@ -85,6 +88,7 @@ def _static_request( api_version=api_version, organization=organization, api_base=api_base, + api_type=api_type ) response, _, api_key = requestor.request( method_, url_, params, request_id=request_id @@ -92,3 +96,10 @@ def _static_request( return util.convert_to_openai_object( response, api_key, api_version, organization ) + + @classmethod + def _get_api_type_and_version(cls, api_type: str, api_version: str): + typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) + typed_api_version = api_version or openai.api_version + return (typed_api_type, typed_api_version) + diff --git a/openai/api_resources/abstract/createable_api_resource.py b/openai/api_resources/abstract/createable_api_resource.py index 1538cb3719..6ca2368d13 100644 --- a/openai/api_resources/abstract/createable_api_resource.py +++ b/openai/api_resources/abstract/createable_api_resource.py @@ -1,5 +1,6 @@ -from openai import api_requestor, util +from openai import api_requestor, util, error from openai.api_resources.abstract.api_resource import APIResource +from openai.util import ApiType class CreateableAPIResource(APIResource): @@ -10,6 +11,7 @@ def create( cls, api_key=None, api_base=None, + api_type=None, request_id=None, api_version=None, organization=None, @@ -18,10 +20,20 @@ def create( requestor = api_requestor.APIRequestor( api_key, api_base=api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = cls.class_url() + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + response, _, api_key = requestor.request( "post", url, params, request_id=request_id ) diff --git a/openai/api_resources/abstract/deletable_api_resource.py b/openai/api_resources/abstract/deletable_api_resource.py index 47111b153c..3a6e83ff0e 100644 --- a/openai/api_resources/abstract/deletable_api_resource.py +++ b/openai/api_resources/abstract/deletable_api_resource.py @@ -1,12 +1,24 @@ from urllib.parse import quote_plus +from openai import error from openai.api_resources.abstract.api_resource import APIResource - +from openai.util import ApiType class DeletableAPIResource(APIResource): @classmethod - def delete(cls, sid, **params): + def delete(cls, sid, api_type=None, api_version=None, **params): if isinstance(cls, APIResource): raise ValueError(".delete may only be called as a class method now.") - url = "%s/%s" % (cls.class_url(), quote_plus(sid)) - return cls._static_request("delete", url, **params) + + base = cls.class_url() + extn = quote_plus(sid) + + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + if typed_api_type == ApiType.AZURE: + url = "/%s%s/%s?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s" % (base, extn) + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + + return cls._static_request("delete", url, api_type=api_type, api_version=api_version, **params) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index e3592c8599..5441def2a8 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -15,7 +15,6 @@ class EngineAPIResource(APIResource): engine_required = True plain_old_data = False - azure_api_prefix = 'openai/deployments' def __init__(self, engine: Optional[str] = None, **kwargs): super().__init__(engine=engine, **kwargs) @@ -25,8 +24,7 @@ def class_url(cls, engine: Optional[str] = None, api_type : Optional[str] = None # Namespaces are separated in object names with periods (.) and in URLs # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) - api_version = api_version or openai.api_version + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) if typed_api_type == ApiType.AZURE: if not api_version: @@ -36,7 +34,8 @@ def class_url(cls, engine: Optional[str] = None, api_type : Optional[str] = None "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service" ) extn = quote_plus(engine) - return "/%s/%s/%ss?api-version=%s" % (cls.azure_api_prefix, extn, base, api_version) + return "/%s/%s/%s/%ss?api-version=%s" % ( + cls.azure_api_prefix, cls.azure_deployments_prefix, extn, base, api_version) elif typed_api_type == ApiType.OPEN_AI: if engine is None: @@ -133,19 +132,20 @@ def instance_url(http://github.com/self): "id", ) + extn = quote_plus(id) params_connector = '?' + if self.typed_api_type == ApiType.AZURE: api_version = self.api_version or openai.api_version if not api_version: raise error.InvalidRequestError("An API version is required for the Azure API type.") - extn = quote_plus(id) base = self.OBJECT_NAME.replace(".", "/") - url = "/%s/%s/%ss/%s?api-version=%s" % (self.azure_api_prefix, self.engine, base, extn, api_version) + url = "/%s/%s/%s/%ss/%s?api-version=%s" % ( + self.azure_api_prefix, self.azure_deployments_prefix, self.engine, base, extn, api_version) params_connector = '&' elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url(http://github.com/self.engine,%20self.api_type,%20self.api_version) - extn = quote_plus(id) url = "%s/%s" % (base, extn) else: diff --git a/openai/api_resources/abstract/listable_api_resource.py b/openai/api_resources/abstract/listable_api_resource.py index b9cf952a91..c01af74236 100644 --- a/openai/api_resources/abstract/listable_api_resource.py +++ b/openai/api_resources/abstract/listable_api_resource.py @@ -1,5 +1,6 @@ -from openai import api_requestor, util +from openai import api_requestor, util, error from openai.api_resources.abstract.api_resource import APIResource +from openai.util import ApiType class ListableAPIResource(APIResource): @@ -15,15 +16,27 @@ def list( api_version=None, organization=None, api_base=None, + api_type=None, **params, ): requestor = api_requestor.APIRequestor( api_key, api_base=api_base or cls.api_base(), api_version=api_version, + api_type=api_type, organization=organization, ) - url = cls.class_url() + + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + response, _, api_key = requestor.request( "get", url, params, request_id=request_id ) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index dbe387b157..4f2879f5f6 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -3,8 +3,9 @@ from typing import cast import openai -from openai import api_requestor, util +from openai import api_requestor, util, error from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource +from openai.util import ApiType class File(ListableAPIResource, DeletableAPIResource): @@ -18,6 +19,7 @@ def create( model=None, api_key=None, api_base=None, + api_type=None, api_version=None, organization=None, user_provided_filename=None, @@ -27,19 +29,29 @@ def create( requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = cls.class_url() + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + # Set the filename on 'purpose' and 'model' to None so they are # interpreted as form data. files = [("purpose", (None, purpose))] if model is not None: files.append(("model", (None, model))) if user_provided_filename is not None: - files.append(("file", (user_provided_filename, file))) + files.append(("file", (user_provided_filename, file, 'application/octet-stream'))) else: - files.append(("file", file)) + files.append(("file", file, 'application/octet-stream')) response, _, api_key = requestor.request("post", url, files=files) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -47,15 +59,31 @@ def create( @classmethod def download( - cls, id, api_key=None, api_base=None, api_version=None, organization=None + cls, + id, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None ): requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = f"{cls.class_url()}/{id}/content" + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + base = cls.class_url() + url = "/%s%s/%s/content?api-version=%s" % (cls.azure_api_prefix, base, id, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = f"{cls.class_url()}/{id}/content" + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + result = requestor.request_raw("get", url) if not 200 <= result.status_code < 300: raise requestor.handle_error_response( @@ -75,6 +103,7 @@ def find_matching_files( purpose, api_key=None, api_base=None, + api_type=None, api_version=None, organization=None, ): @@ -82,6 +111,7 @@ def find_matching_files( all_files = cls.list( api_key=api_key, api_base=api_base or openai.api_base, + api_type=api_type, api_version=api_version, organization=organization, ).get("data", []) @@ -93,7 +123,9 @@ def find_matching_files( file_basename = os.path.basename(f["filename"]) if file_basename != basename: continue - if f["bytes"] != bytes: + if "bytes" in f and f["bytes"] != bytes: + continue + if "size" in f and int(f["size"]) != bytes: continue matching_files.append(f) return matching_files diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index c53671ae68..3e596b6bfd 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -1,23 +1,41 @@ from urllib.parse import quote_plus -from openai import api_requestor, util +from openai import api_requestor, util, error from openai.api_resources.abstract import ( CreateableAPIResource, ListableAPIResource, nested_resource_class_methods, ) +from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource from openai.openai_response import OpenAIResponse +from openai.util import ApiType @nested_resource_class_methods("event", operations=["list"]) -class FineTune(ListableAPIResource, CreateableAPIResource): +class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource): OBJECT_NAME = "fine-tune" @classmethod - def cancel(cls, id, api_key=None, request_id=None, **params): + def cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params + ): base = cls.class_url() extn = quote_plus(id) - url = "%s/%s/cancel" % (base, extn) + + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + if typed_api_type == ApiType.AZURE: + url = "/%s%s/%s/cancel?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s/cancel" % (base, extn) + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + instance = cls(id, api_key, **params) return instance.request("post", url, request_id=request_id) @@ -27,6 +45,7 @@ def stream_events( id, api_key=None, api_base=None, + api_type=None, request_id=None, api_version=None, organization=None, @@ -38,10 +57,20 @@ def stream_events( requestor = api_requestor.APIRequestor( api_key, api_base=api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = "%s/%s/events?stream=true" % (base, extn) + + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + url = "/%s%s/%s/events?stream=true&api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s/events?stream=true" % (base, extn) + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + response, _, api_key = requestor.request( "get", url, params, stream=True, request_id=request_id ) diff --git a/openai/cli.py b/openai/cli.py index c46632eb2f..178ff03f4b 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -320,7 +320,7 @@ def _maybe_upload_file( sys.stdout.write( "Found potentially duplicated files with name '{name}', purpose 'fine-tune' and size {size} bytes\n".format( name=os.path.basename(matching_files[0]["filename"]), - size=matching_files[0]["bytes"], + size=matching_files[0]["bytes"] if "bytes" in matching_files[0] else matching_files[0]["size"], ) ) sys.stdout.write("\n".join(file_ids)) diff --git a/openai/version.py b/openai/version.py index 7376d84f5a..99977b4919 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.15.0" +VERSION = "0.16.0" From 718e7a427d49049968e86629e0a717f788125890 Mon Sep 17 00:00:00 2001 From: kennyhsu5 <1762087+kennyhsu5@users.noreply.github.com> Date: Fri, 11 Mar 2022 14:12:43 -0800 Subject: [PATCH 022/914] Revert "Add support for fine-tunning and files using the Azure API. (#76)" (#79) This reverts commit 2b264c39f8ce6960e81df0cd401c7581767a0f64. --- .gitignore | 3 +- README.md | 2 +- openai/api_requestor.py | 6 +-- openai/api_resources/abstract/api_resource.py | 25 +++------- .../abstract/createable_api_resource.py | 16 +------ .../abstract/deletable_api_resource.py | 20 ++------ .../abstract/engine_api_resource.py | 14 +++--- .../abstract/listable_api_resource.py | 17 +------ openai/api_resources/file.py | 46 +++---------------- openai/api_resources/fine_tune.py | 39 ++-------------- openai/cli.py | 2 +- openai/version.py | 2 +- 12 files changed, 39 insertions(+), 153 deletions(-) diff --git a/.gitignore b/.gitignore index de92124490..6af1d15f0a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,4 @@ __pycache__ build *.egg .vscode/settings.json -.ipynb_checkpoints -.vscode/launch.json +.ipynb_checkpoints \ No newline at end of file diff --git a/README.md b/README.md index a50484a8fc..9da57aaed8 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ search = openai.Engine(id="deployment-namme").search(documents=["White House", " print(search) ``` -Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, search and fine-tuning operations. +Please note that for the moment, the Microsoft Azure endpoints can only be used for completion and search operations. ### Command-line interface diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 346e148c1a..dd618c6e5d 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -111,7 +111,7 @@ def request( def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): try: - error_data = resp["error"] if self.api_type == ApiType.OPEN_AI else resp + error_data = resp["error"] except (KeyError, TypeError): raise error.APIError( "Invalid response object from API: %r (HTTP response code " @@ -322,10 +322,6 @@ def _interpret_response( def _interpret_response_line( self, rbody, rcode, rheaders, stream: bool ) -> OpenAIResponse: - # HTTP 204 response code does not have any content in the body. - if rcode == 204: - return OpenAIResponse(None, rheaders) - if rcode == 503: raise error.ServiceUnavailableError( "The server is overloaded or not ready yet.", rbody, rcode, headers=rheaders diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index d9cfda2e05..c25aafd656 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -8,8 +8,7 @@ class APIResource(OpenAIObject): api_prefix = "" - azure_api_prefix = 'openai' - azure_deployments_prefix = 'deployments' + azure_api_prefix = 'openai/deployments' @classmethod def retrieve(cls, id, api_key=None, request_id=None, **params): @@ -47,21 +46,20 @@ def instance_url(http://github.com/self,%20operation=None): "id", ) api_version = self.api_version or openai.api_version - extn = quote_plus(id) if self.typed_api_type == ApiType.AZURE: if not api_version: raise error.InvalidRequestError("An API version is required for the Azure API type.") - if not operation: - base = self.class_url() - return "/%s%s/%s?api-version=%s" % (self.azure_api_prefix, base, extn, api_version) - - return "/%s/%s/%s/%s?api-version=%s" % ( - self.azure_api_prefix, self.azure_deployments_prefix, extn, operation, api_version) + raise error.InvalidRequestError( + "The request needs an operation (eg: 'search') for the Azure OpenAI API type." + ) + extn = quote_plus(id) + return "/%s/%s/%s?api-version=%s" % (self.azure_api_prefix, extn, operation, api_version) elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url() + extn = quote_plus(id) return "%s/%s" % (base, extn) else: @@ -77,7 +75,6 @@ def _static_request( url_, api_key=None, api_base=None, - api_type=None, request_id=None, api_version=None, organization=None, @@ -88,7 +85,6 @@ def _static_request( api_version=api_version, organization=organization, api_base=api_base, - api_type=api_type ) response, _, api_key = requestor.request( method_, url_, params, request_id=request_id @@ -96,10 +92,3 @@ def _static_request( return util.convert_to_openai_object( response, api_key, api_version, organization ) - - @classmethod - def _get_api_type_and_version(cls, api_type: str, api_version: str): - typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) - typed_api_version = api_version or openai.api_version - return (typed_api_type, typed_api_version) - diff --git a/openai/api_resources/abstract/createable_api_resource.py b/openai/api_resources/abstract/createable_api_resource.py index 6ca2368d13..1538cb3719 100644 --- a/openai/api_resources/abstract/createable_api_resource.py +++ b/openai/api_resources/abstract/createable_api_resource.py @@ -1,6 +1,5 @@ -from openai import api_requestor, util, error +from openai import api_requestor, util from openai.api_resources.abstract.api_resource import APIResource -from openai.util import ApiType class CreateableAPIResource(APIResource): @@ -11,7 +10,6 @@ def create( cls, api_key=None, api_base=None, - api_type=None, request_id=None, api_version=None, organization=None, @@ -20,20 +18,10 @@ def create( requestor = api_requestor.APIRequestor( api_key, api_base=api_base, - api_type=api_type, api_version=api_version, organization=organization, ) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - if typed_api_type == ApiType.AZURE: - base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = cls.class_url() - else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + url = cls.class_url() response, _, api_key = requestor.request( "post", url, params, request_id=request_id ) diff --git a/openai/api_resources/abstract/deletable_api_resource.py b/openai/api_resources/abstract/deletable_api_resource.py index 3a6e83ff0e..47111b153c 100644 --- a/openai/api_resources/abstract/deletable_api_resource.py +++ b/openai/api_resources/abstract/deletable_api_resource.py @@ -1,24 +1,12 @@ from urllib.parse import quote_plus -from openai import error from openai.api_resources.abstract.api_resource import APIResource -from openai.util import ApiType + class DeletableAPIResource(APIResource): @classmethod - def delete(cls, sid, api_type=None, api_version=None, **params): + def delete(cls, sid, **params): if isinstance(cls, APIResource): raise ValueError(".delete may only be called as a class method now.") - - base = cls.class_url() - extn = quote_plus(sid) - - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - if typed_api_type == ApiType.AZURE: - url = "/%s%s/%s?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = "%s/%s" % (base, extn) - else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - - return cls._static_request("delete", url, api_type=api_type, api_version=api_version, **params) + url = "%s/%s" % (cls.class_url(), quote_plus(sid)) + return cls._static_request("delete", url, **params) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index 5441def2a8..e3592c8599 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -15,6 +15,7 @@ class EngineAPIResource(APIResource): engine_required = True plain_old_data = False + azure_api_prefix = 'openai/deployments' def __init__(self, engine: Optional[str] = None, **kwargs): super().__init__(engine=engine, **kwargs) @@ -24,7 +25,8 @@ def class_url(cls, engine: Optional[str] = None, api_type : Optional[str] = None # Namespaces are separated in object names with periods (.) and in URLs # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) + api_version = api_version or openai.api_version if typed_api_type == ApiType.AZURE: if not api_version: @@ -34,8 +36,7 @@ def class_url(cls, engine: Optional[str] = None, api_type : Optional[str] = None "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service" ) extn = quote_plus(engine) - return "/%s/%s/%s/%ss?api-version=%s" % ( - cls.azure_api_prefix, cls.azure_deployments_prefix, extn, base, api_version) + return "/%s/%s/%ss?api-version=%s" % (cls.azure_api_prefix, extn, base, api_version) elif typed_api_type == ApiType.OPEN_AI: if engine is None: @@ -132,20 +133,19 @@ def instance_url(http://github.com/self): "id", ) - extn = quote_plus(id) params_connector = '?' - if self.typed_api_type == ApiType.AZURE: api_version = self.api_version or openai.api_version if not api_version: raise error.InvalidRequestError("An API version is required for the Azure API type.") + extn = quote_plus(id) base = self.OBJECT_NAME.replace(".", "/") - url = "/%s/%s/%s/%ss/%s?api-version=%s" % ( - self.azure_api_prefix, self.azure_deployments_prefix, self.engine, base, extn, api_version) + url = "/%s/%s/%ss/%s?api-version=%s" % (self.azure_api_prefix, self.engine, base, extn, api_version) params_connector = '&' elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url(http://github.com/self.engine,%20self.api_type,%20self.api_version) + extn = quote_plus(id) url = "%s/%s" % (base, extn) else: diff --git a/openai/api_resources/abstract/listable_api_resource.py b/openai/api_resources/abstract/listable_api_resource.py index c01af74236..b9cf952a91 100644 --- a/openai/api_resources/abstract/listable_api_resource.py +++ b/openai/api_resources/abstract/listable_api_resource.py @@ -1,6 +1,5 @@ -from openai import api_requestor, util, error +from openai import api_requestor, util from openai.api_resources.abstract.api_resource import APIResource -from openai.util import ApiType class ListableAPIResource(APIResource): @@ -16,27 +15,15 @@ def list( api_version=None, organization=None, api_base=None, - api_type=None, **params, ): requestor = api_requestor.APIRequestor( api_key, api_base=api_base or cls.api_base(), api_version=api_version, - api_type=api_type, organization=organization, ) - - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - if typed_api_type == ApiType.AZURE: - base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = cls.class_url() - else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + url = cls.class_url() response, _, api_key = requestor.request( "get", url, params, request_id=request_id ) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 4f2879f5f6..dbe387b157 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -3,9 +3,8 @@ from typing import cast import openai -from openai import api_requestor, util, error +from openai import api_requestor, util from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource -from openai.util import ApiType class File(ListableAPIResource, DeletableAPIResource): @@ -19,7 +18,6 @@ def create( model=None, api_key=None, api_base=None, - api_type=None, api_version=None, organization=None, user_provided_filename=None, @@ -29,29 +27,19 @@ def create( requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, - api_type=api_type, api_version=api_version, organization=organization, ) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - if typed_api_type == ApiType.AZURE: - base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = cls.class_url() - else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + url = cls.class_url() # Set the filename on 'purpose' and 'model' to None so they are # interpreted as form data. files = [("purpose", (None, purpose))] if model is not None: files.append(("model", (None, model))) if user_provided_filename is not None: - files.append(("file", (user_provided_filename, file, 'application/octet-stream'))) + files.append(("file", (user_provided_filename, file))) else: - files.append(("file", file, 'application/octet-stream')) + files.append(("file", file)) response, _, api_key = requestor.request("post", url, files=files) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -59,31 +47,15 @@ def create( @classmethod def download( - cls, - id, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None + cls, id, api_key=None, api_base=None, api_version=None, organization=None ): requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, - api_type=api_type, api_version=api_version, organization=organization, ) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - if typed_api_type == ApiType.AZURE: - base = cls.class_url() - url = "/%s%s/%s/content?api-version=%s" % (cls.azure_api_prefix, base, id, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = f"{cls.class_url()}/{id}/content" - else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + url = f"{cls.class_url()}/{id}/content" result = requestor.request_raw("get", url) if not 200 <= result.status_code < 300: raise requestor.handle_error_response( @@ -103,7 +75,6 @@ def find_matching_files( purpose, api_key=None, api_base=None, - api_type=None, api_version=None, organization=None, ): @@ -111,7 +82,6 @@ def find_matching_files( all_files = cls.list( api_key=api_key, api_base=api_base or openai.api_base, - api_type=api_type, api_version=api_version, organization=organization, ).get("data", []) @@ -123,9 +93,7 @@ def find_matching_files( file_basename = os.path.basename(f["filename"]) if file_basename != basename: continue - if "bytes" in f and f["bytes"] != bytes: - continue - if "size" in f and int(f["size"]) != bytes: + if f["bytes"] != bytes: continue matching_files.append(f) return matching_files diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index 3e596b6bfd..c53671ae68 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -1,41 +1,23 @@ from urllib.parse import quote_plus -from openai import api_requestor, util, error +from openai import api_requestor, util from openai.api_resources.abstract import ( CreateableAPIResource, ListableAPIResource, nested_resource_class_methods, ) -from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource from openai.openai_response import OpenAIResponse -from openai.util import ApiType @nested_resource_class_methods("event", operations=["list"]) -class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource): +class FineTune(ListableAPIResource, CreateableAPIResource): OBJECT_NAME = "fine-tune" @classmethod - def cancel( - cls, - id, - api_key=None, - api_type=None, - request_id=None, - api_version=None, - **params - ): + def cancel(cls, id, api_key=None, request_id=None, **params): base = cls.class_url() extn = quote_plus(id) - - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - if typed_api_type == ApiType.AZURE: - url = "/%s%s/%s/cancel?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = "%s/%s/cancel" % (base, extn) - else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + url = "%s/%s/cancel" % (base, extn) instance = cls(id, api_key, **params) return instance.request("post", url, request_id=request_id) @@ -45,7 +27,6 @@ def stream_events( id, api_key=None, api_base=None, - api_type=None, request_id=None, api_version=None, organization=None, @@ -57,20 +38,10 @@ def stream_events( requestor = api_requestor.APIRequestor( api_key, api_base=api_base, - api_type=api_type, api_version=api_version, organization=organization, ) - - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - if typed_api_type == ApiType.AZURE: - url = "/%s%s/%s/events?stream=true&api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = "%s/%s/events?stream=true" % (base, extn) - else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + url = "%s/%s/events?stream=true" % (base, extn) response, _, api_key = requestor.request( "get", url, params, stream=True, request_id=request_id ) diff --git a/openai/cli.py b/openai/cli.py index 178ff03f4b..c46632eb2f 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -320,7 +320,7 @@ def _maybe_upload_file( sys.stdout.write( "Found potentially duplicated files with name '{name}', purpose 'fine-tune' and size {size} bytes\n".format( name=os.path.basename(matching_files[0]["filename"]), - size=matching_files[0]["bytes"] if "bytes" in matching_files[0] else matching_files[0]["size"], + size=matching_files[0]["bytes"], ) ) sys.stdout.write("\n".join(file_ids)) diff --git a/openai/version.py b/openai/version.py index 99977b4919..7376d84f5a 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.16.0" +VERSION = "0.15.0" From db4750c16cdba8e97300a95537e390f62c9039ec Mon Sep 17 00:00:00 2001 From: Henrique Oliveira Pinto Date: Thu, 17 Mar 2022 14:35:57 -0700 Subject: [PATCH 023/914] [lint] standardize isort and black in pyproject.toml (#81) * [lint] standardize isort and black in pyproject.toml * run isort * run black --- .isort.cfg | 6 -- openai/__init__.py | 2 +- openai/api_requestor.py | 22 ++++- openai/api_resources/abstract/api_resource.py | 18 ++-- .../abstract/engine_api_resource.py | 49 ++++++++--- openai/api_resources/engine.py | 2 +- openai/cli.py | 2 +- openai/error.py | 1 + openai/openai_object.py | 6 +- openai/tests/test_api_requestor.py | 20 +++-- openai/tests/test_url_composition.py | 85 ++++++++++++++----- openai/util.py | 17 ++-- pyproject.toml | 9 ++ setup.py | 4 +- 14 files changed, 178 insertions(+), 65 deletions(-) delete mode 100644 .isort.cfg create mode 100644 pyproject.toml diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 1a11349890..0000000000 --- a/.isort.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[settings] -include_trailing_comma=True -line_length=88 -known_first_party= -multi_line_output=3 -py_version=36 diff --git a/openai/__init__.py b/openai/__init__.py index 3137717cce..dd9d163fc7 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -28,7 +28,7 @@ organization = os.environ.get("OPENAI_ORGANIZATION") api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") -api_version = '2021-11-01-preview' if api_type == "azure" else None +api_version = "2021-11-01-preview" if api_type == "azure" else None verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None app_info = None diff --git a/openai/api_requestor.py b/openai/api_requestor.py index dd618c6e5d..ac5c5af6c6 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -1,8 +1,8 @@ -from email import header import json import platform import threading import warnings +from email import header from json import JSONDecodeError from typing import Dict, Iterator, Optional, Tuple, Union from urllib.parse import urlencode, urlsplit, urlunsplit @@ -71,10 +71,21 @@ def parse_stream(rbody): class APIRequestor: - def __init__(self, key=None, api_base=None, api_type=None, api_version=None, organization=None): + def __init__( + self, + key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): self.api_base = api_base or openai.api_base self.api_key = key or util.default_api_key() - self.api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) + self.api_type = ( + ApiType.from_str(api_type) + if api_type + else ApiType.from_str(openai.api_type) + ) self.api_version = api_version or openai.api_version self.organization = organization or openai.organization @@ -324,7 +335,10 @@ def _interpret_response_line( ) -> OpenAIResponse: if rcode == 503: raise error.ServiceUnavailableError( - "The server is overloaded or not ready yet.", rbody, rcode, headers=rheaders + "The server is overloaded or not ready yet.", + rbody, + rcode, + headers=rheaders, ) try: if hasattr(rbody, "decode"): diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index c25aafd656..5d10da1d29 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -1,14 +1,14 @@ from urllib.parse import quote_plus -from openai import api_requestor, error, util import openai +from openai import api_requestor, error, util from openai.openai_object import OpenAIObject from openai.util import ApiType class APIResource(OpenAIObject): api_prefix = "" - azure_api_prefix = 'openai/deployments' + azure_api_prefix = "openai/deployments" @classmethod def retrieve(cls, id, api_key=None, request_id=None, **params): @@ -49,13 +49,20 @@ def instance_url(http://github.com/self,%20operation=None): if self.typed_api_type == ApiType.AZURE: if not api_version: - raise error.InvalidRequestError("An API version is required for the Azure API type.") + raise error.InvalidRequestError( + "An API version is required for the Azure API type." + ) if not operation: raise error.InvalidRequestError( "The request needs an operation (eg: 'search') for the Azure OpenAI API type." ) extn = quote_plus(id) - return "/%s/%s/%s?api-version=%s" % (self.azure_api_prefix, extn, operation, api_version) + return "/%s/%s/%s?api-version=%s" % ( + self.azure_api_prefix, + extn, + operation, + api_version, + ) elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url() @@ -63,8 +70,7 @@ def instance_url(http://github.com/self,%20operation=None): return "%s/%s" % (base, extn) else: - raise error.InvalidAPIType('Unsupported API type %s' % self.api_type) - + raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) # The `method_` and `url_` arguments are suffixed with an underscore to # avoid conflicting with actual request parameters in `params`. diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index e3592c8599..cd79528df4 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -1,5 +1,5 @@ -from pydoc import apropos import time +from pydoc import apropos from typing import Optional from urllib.parse import quote_plus @@ -15,28 +15,44 @@ class EngineAPIResource(APIResource): engine_required = True plain_old_data = False - azure_api_prefix = 'openai/deployments' + azure_api_prefix = "openai/deployments" def __init__(self, engine: Optional[str] = None, **kwargs): super().__init__(engine=engine, **kwargs) @classmethod - def class_url(http://github.com/cls,%20engine:%20Optional[str]%20=%20None,%20api_type%20:%20Optional[str]%20=%20None,%20api_version:%20Optional[str]%20=%20None): + def class_url( + cls, + engine: Optional[str] = None, + api_type: Optional[str] = None, + api_version: Optional[str] = None, + ): # Namespaces are separated in object names with periods (.) and in URLs # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) + typed_api_type = ( + ApiType.from_str(api_type) + if api_type + else ApiType.from_str(openai.api_type) + ) api_version = api_version or openai.api_version if typed_api_type == ApiType.AZURE: if not api_version: - raise error.InvalidRequestError("An API version is required for the Azure API type.") + raise error.InvalidRequestError( + "An API version is required for the Azure API type." + ) if engine is None: raise error.InvalidRequestError( "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service" ) extn = quote_plus(engine) - return "/%s/%s/%ss?api-version=%s" % (cls.azure_api_prefix, extn, base, api_version) + return "/%s/%s/%ss?api-version=%s" % ( + cls.azure_api_prefix, + extn, + base, + api_version, + ) elif typed_api_type == ApiType.OPEN_AI: if engine is None: @@ -46,8 +62,7 @@ def class_url(cls, engine: Optional[str] = None, api_type : Optional[str] = None return "/engines/%s/%ss" % (extn, base) else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + raise error.InvalidAPIType("Unsupported API type %s" % api_type) @classmethod def create( @@ -133,15 +148,23 @@ def instance_url(http://github.com/self): "id", ) - params_connector = '?' + params_connector = "?" if self.typed_api_type == ApiType.AZURE: api_version = self.api_version or openai.api_version if not api_version: - raise error.InvalidRequestError("An API version is required for the Azure API type.") + raise error.InvalidRequestError( + "An API version is required for the Azure API type." + ) extn = quote_plus(id) base = self.OBJECT_NAME.replace(".", "/") - url = "/%s/%s/%ss/%s?api-version=%s" % (self.azure_api_prefix, self.engine, base, extn, api_version) - params_connector = '&' + url = "/%s/%s/%ss/%s?api-version=%s" % ( + self.azure_api_prefix, + self.engine, + base, + extn, + api_version, + ) + params_connector = "&" elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url(http://github.com/self.engine,%20self.api_type,%20self.api_version) @@ -149,7 +172,7 @@ def instance_url(http://github.com/self): url = "%s/%s" % (base, extn) else: - raise error.InvalidAPIType('Unsupported API type %s' % self.api_type) + raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) timeout = self.get("timeout") if timeout is not None: diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py index 4a7b03cfbf..41ddd0d45c 100644 --- a/openai/api_resources/engine.py +++ b/openai/api_resources/engine.py @@ -33,7 +33,7 @@ def search(self, **params): elif self.typed_api_type == ApiType.OPEN_AI: return self.request("post", self.instance_url() + "/search", params) else: - raise InvalidAPIType('Unsupported API type %s' % self.api_type) + raise InvalidAPIType("Unsupported API type %s" % self.api_type) def embeddings(self, **params): warnings.warn( diff --git a/openai/cli.py b/openai/cli.py index c46632eb2f..3b0625611f 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -9,6 +9,7 @@ import requests import openai +import openai.wandb_logger from openai.upload_progress import BufferReader from openai.validators import ( apply_necessary_remediation, @@ -19,7 +20,6 @@ write_out_file, write_out_search_file, ) -import openai.wandb_logger class bcolors: diff --git a/openai/error.py b/openai/error.py index b357093e7e..47f9aab6bc 100644 --- a/openai/error.py +++ b/openai/error.py @@ -146,6 +146,7 @@ class RateLimitError(OpenAIError): class ServiceUnavailableError(OpenAIError): pass + class InvalidAPIType(OpenAIError): pass diff --git a/openai/openai_object.py b/openai/openai_object.py index f785c89484..58e458dfed 100644 --- a/openai/openai_object.py +++ b/openai/openai_object.py @@ -248,7 +248,11 @@ def openai_id(self): @property def typed_api_type(self): - return ApiType.from_str(self.api_type) if self.api_type else ApiType.from_str(openai.api_type) + return ( + ApiType.from_str(self.api_type) + if self.api_type + else ApiType.from_str(openai.api_type) + ) # This class overrides __setitem__ to throw exceptions on inputs that it # doesn't like. This can cause problems when we try to copy an object diff --git a/openai/tests/test_api_requestor.py b/openai/tests/test_api_requestor.py index 039dd6c09a..1b252fc4fb 100644 --- a/openai/tests/test_api_requestor.py +++ b/openai/tests/test_api_requestor.py @@ -1,4 +1,5 @@ import json + import pytest import requests from pytest_mock import MockerFixture @@ -6,6 +7,7 @@ from openai import Model from openai.api_requestor import APIRequestor + @pytest.mark.requestor def test_requestor_sets_request_id(mocker: MockerFixture) -> None: # Fake out 'requests' and confirm that the X-Request-Id header is set. @@ -27,24 +29,30 @@ def fake_request(self, *args, **kwargs): got_request_id = got_headers.get("X-Request-Id") assert got_request_id == fake_request_id + @pytest.mark.requestor def test_requestor_open_ai_headers() -> None: api_requestor = APIRequestor(key="test_key", api_type="open_ai") headers = {"Test_Header": "Unit_Test_Header"} - headers = api_requestor.request_headers(method="get", extra=headers, request_id="test_id") + headers = api_requestor.request_headers( + method="get", extra=headers, request_id="test_id" + ) print(headers) - assert "Test_Header"in headers + assert "Test_Header" in headers assert headers["Test_Header"] == "Unit_Test_Header" - assert "Authorization"in headers + assert "Authorization" in headers assert headers["Authorization"] == "Bearer test_key" + @pytest.mark.requestor def test_requestor_azure_headers() -> None: api_requestor = APIRequestor(key="test_key", api_type="azure") headers = {"Test_Header": "Unit_Test_Header"} - headers = api_requestor.request_headers(method="get", extra=headers, request_id="test_id") + headers = api_requestor.request_headers( + method="get", extra=headers, request_id="test_id" + ) print(headers) - assert "Test_Header"in headers + assert "Test_Header" in headers assert headers["Test_Header"] == "Unit_Test_Header" - assert "api-key"in headers + assert "api-key" in headers assert headers["api-key"] == "test_key" diff --git a/openai/tests/test_url_composition.py b/openai/tests/test_url_composition.py index 7298fe8ec1..2b561c6d53 100644 --- a/openai/tests/test_url_composition.py +++ b/openai/tests/test_url_composition.py @@ -1,81 +1,123 @@ from sys import api_version + import pytest -from openai import Completion -from openai import Engine +from openai import Completion, Engine from openai.util import ApiType + @pytest.mark.url def test_completions_url_composition_azure() -> None: - url = Completion.class_url("http://github.com/test_engine%22,%20%22azure%22,%20'2021-11-01-preview') - assert url == '/openai/deployments/test_engine/completions?api-version=2021-11-01-preview' + url = Completion.class_url("http://github.com/test_engine%22,%20%22azure%22,%20%222021-11-01-preview") + assert ( + url + == "/openai/deployments/test_engine/completions?api-version=2021-11-01-preview" + ) + @pytest.mark.url def test_completions_url_composition_default() -> None: url = Completion.class_url("http://github.com/test_engine") - assert url == '/engines/test_engine/completions' + assert url == "/engines/test_engine/completions" + @pytest.mark.url def test_completions_url_composition_open_ai() -> None: url = Completion.class_url("http://github.com/test_engine%22,%20%22open_ai") - assert url == '/engines/test_engine/completions' + assert url == "/engines/test_engine/completions" + @pytest.mark.url def test_completions_url_composition_invalid_type() -> None: with pytest.raises(Exception): url = Completion.class_url("http://github.com/test_engine%22,%20%22invalid") + @pytest.mark.url def test_completions_url_composition_instance_url_azure() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="azure", api_version='2021-11-01-preview') + completion = Completion( + id="test_id", + engine="test_engine", + api_type="azure", + api_version="2021-11-01-preview", + ) url = completion.instance_url() - assert url == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview" + assert ( + url + == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview" + ) + @pytest.mark.url def test_completions_url_composition_instance_url_azure_no_version() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="azure", api_version=None) + completion = Completion( + id="test_id", engine="test_engine", api_type="azure", api_version=None + ) with pytest.raises(Exception): completion.instance_url() + @pytest.mark.url def test_completions_url_composition_instance_url_default() -> None: completion = Completion(id="test_id", engine="test_engine") url = completion.instance_url() assert url == "/engines/test_engine/completions/test_id" + @pytest.mark.url def test_completions_url_composition_instance_url_open_ai() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="open_ai", api_version='2021-11-01-preview') + completion = Completion( + id="test_id", + engine="test_engine", + api_type="open_ai", + api_version="2021-11-01-preview", + ) url = completion.instance_url() assert url == "/engines/test_engine/completions/test_id" + @pytest.mark.url def test_completions_url_composition_instance_url_invalid() -> None: completion = Completion(id="test_id", engine="test_engine", api_type="invalid") with pytest.raises(Exception): url = completion.instance_url() + @pytest.mark.url def test_completions_url_composition_instance_url_timeout_azure() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="azure", api_version='2021-11-01-preview') + completion = Completion( + id="test_id", + engine="test_engine", + api_type="azure", + api_version="2021-11-01-preview", + ) completion["timeout"] = 12 url = completion.instance_url() - assert url == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview&timeout=12" + assert ( + url + == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview&timeout=12" + ) + @pytest.mark.url def test_completions_url_composition_instance_url_timeout_openai() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="open_ai" ) + completion = Completion(id="test_id", engine="test_engine", api_type="open_ai") completion["timeout"] = 12 url = completion.instance_url() assert url == "/engines/test_engine/completions/test_id?timeout=12" + @pytest.mark.url def test_engine_search_url_composition_azure() -> None: - engine = Engine(id="test_id", api_type="azure", api_version='2021-11-01-preview') + engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") assert engine.api_type == "azure" assert engine.typed_api_type == ApiType.AZURE url = engine.instance_url("http://github.com/test_operation") - assert url == '/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview' + assert ( + url + == "/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview" + ) + @pytest.mark.url def test_engine_search_url_composition_azure_no_version() -> None: @@ -85,13 +127,15 @@ def test_engine_search_url_composition_azure_no_version() -> None: with pytest.raises(Exception): engine.instance_url("http://github.com/test_operation") + @pytest.mark.url def test_engine_search_url_composition_azure_no_operation() -> None: - engine = Engine(id="test_id", api_type="azure", api_version='2021-11-01-preview') + engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") assert engine.api_type == "azure" assert engine.typed_api_type == ApiType.AZURE with pytest.raises(Exception): - engine.instance_url() + engine.instance_url() + @pytest.mark.url def test_engine_search_url_composition_default() -> None: @@ -99,7 +143,8 @@ def test_engine_search_url_composition_default() -> None: assert engine.api_type == None assert engine.typed_api_type == ApiType.OPEN_AI url = engine.instance_url() - assert url == '/engines/test_id' + assert url == "/engines/test_id" + @pytest.mark.url def test_engine_search_url_composition_open_ai() -> None: @@ -107,7 +152,8 @@ def test_engine_search_url_composition_open_ai() -> None: assert engine.api_type == "open_ai" assert engine.typed_api_type == ApiType.OPEN_AI url = engine.instance_url() - assert url == '/engines/test_id' + assert url == "/engines/test_id" + @pytest.mark.url def test_engine_search_url_composition_invalid_type() -> None: @@ -116,6 +162,7 @@ def test_engine_search_url_composition_invalid_type() -> None: with pytest.raises(Exception): assert engine.typed_api_type == ApiType.OPEN_AI + @pytest.mark.url def test_engine_search_url_composition_invalid_search() -> None: engine = Engine(id="test_id", api_type="invalid") diff --git a/openai/util.py b/openai/util.py index b57cd5ea5a..becd7d14db 100644 --- a/openai/util.py +++ b/openai/util.py @@ -2,8 +2,8 @@ import os import re import sys -from typing import Optional from enum import Enum +from typing import Optional import openai @@ -18,7 +18,12 @@ "logfmt", ] -api_key_to_header = lambda api, key: {"Authorization": f"Bearer {key}"} if api == ApiType.OPEN_AI else {"api-key": f"{key}"} +api_key_to_header = ( + lambda api, key: {"Authorization": f"Bearer {key}"} + if api == ApiType.OPEN_AI + else {"api-key": f"{key}"} +) + class ApiType(Enum): AZURE = 1 @@ -26,12 +31,14 @@ class ApiType(Enum): @staticmethod def from_str(label): - if label.lower() == 'azure': + if label.lower() == "azure": return ApiType.AZURE - elif label.lower() in ('open_ai', 'openai'): + elif label.lower() in ("open_ai", "openai"): return ApiType.OPEN_AI else: - raise openai.error.InvalidAPIType("The API type provided in invalid. Please select one of the supported API types: 'azure', 'open_ai'") + raise openai.error.InvalidAPIType( + "The API type provided in invalid. Please select one of the supported API types: 'azure', 'open_ai'" + ) def _console_log_level(): diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..c745249e3d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[tool.black] +target-version = ['py36'] +exclude = '.*\.ipynb' + +[tool.isort] +py_version = 36 +include_trailing_comma = "true" +line_length = 88 +multi_line_output = 3 \ No newline at end of file diff --git a/setup.py b/setup.py index 7d50fccd3f..493213d85e 100644 --- a/setup.py +++ b/setup.py @@ -23,8 +23,8 @@ extras_require={"dev": ["black~=21.6b0", "pytest==6.*"]}, python_requires=">=3.7.1", entry_points={ - 'console_scripts': [ - 'openai=openai._openai_scripts:main', + "console_scripts": [ + "openai=openai._openai_scripts:main", ], }, packages=find_packages(exclude=["tests", "tests.*"]), From 63cc289e92e2ff966e9c45aabc9d979b22c96c4a Mon Sep 17 00:00:00 2001 From: Henrique Oliveira Pinto Date: Thu, 17 Mar 2022 14:59:27 -0700 Subject: [PATCH 024/914] (Version 0.15.0) Add support for edit call (#82) * Add support for edit call * Add version bump (0.16.0) Co-authored-by: hallacy --- openai/__init__.py | 2 ++ openai/api_resources/__init__.py | 1 + openai/api_resources/edit.py | 32 ++++++++++++++++++++++++++++++++ openai/version.py | 2 +- 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 openai/api_resources/edit.py diff --git a/openai/__init__.py b/openai/__init__.py index dd9d163fc7..59d717b4cc 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -9,6 +9,7 @@ Answer, Classification, Completion, + Edit, Embedding, Engine, ErrorObject, @@ -42,6 +43,7 @@ "Answer", "Classification", "Completion", + "Edit", "Embedding", "Engine", "ErrorObject", diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index 7f4731bacf..393c714be7 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -1,6 +1,7 @@ from openai.api_resources.answer import Answer # noqa: F401 from openai.api_resources.classification import Classification # noqa: F401 from openai.api_resources.completion import Completion # noqa: F401 +from openai.api_resources.edit import Edit # noqa: F401 from openai.api_resources.embedding import Embedding # noqa: F401 from openai.api_resources.engine import Engine # noqa: F401 from openai.api_resources.error_object import ErrorObject # noqa: F401 diff --git a/openai/api_resources/edit.py b/openai/api_resources/edit.py new file mode 100644 index 0000000000..e4378eec60 --- /dev/null +++ b/openai/api_resources/edit.py @@ -0,0 +1,32 @@ +import time + +from openai import util +from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.error import InvalidRequestError, TryAgain + + +class Edit(EngineAPIResource): + engine_required = False + OBJECT_NAME = "edit" + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new edit for the provided input, instruction, and parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + if kwargs.get("model", None) is None and kwargs.get("engine", None) is None: + raise InvalidRequestError( + "Must provide an 'engine' or 'model' parameter to create an Edit.", + param="engine", + ) + + while True: + try: + return super().create(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/version.py b/openai/version.py index 7376d84f5a..99977b4919 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.15.0" +VERSION = "0.16.0" From 02e4008dda2f0dd7b1ebbf939ebfa74372414184 Mon Sep 17 00:00:00 2001 From: Sorin Suciu Date: Fri, 18 Mar 2022 11:48:40 -0700 Subject: [PATCH 025/914] Add support for fine-tuning and files using the Azure API. (#80) * Add support for fine-tunning and files using the Azure API. * Small changes + version bumps * Version bump after merge * fix typo * adressed comments * Fixed 2 small issues that cause unit tests to fail. * Adressed comments * Version bump --- .gitignore | 3 +- README.md | 2 +- openai/api_requestor.py | 6 ++- openai/api_resources/abstract/api_resource.py | 31 ++++++++++--- .../abstract/createable_api_resource.py | 16 ++++++- .../abstract/deletable_api_resource.py | 20 ++++++-- .../abstract/engine_api_resource.py | 27 +++++------ .../abstract/listable_api_resource.py | 17 ++++++- openai/api_resources/file.py | 46 ++++++++++++++++--- openai/api_resources/fine_tune.py | 39 ++++++++++++++-- openai/cli.py | 2 +- openai/tests/test_url_composition.py | 4 +- openai/version.py | 2 +- 13 files changed, 165 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index 6af1d15f0a..de92124490 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ __pycache__ build *.egg .vscode/settings.json -.ipynb_checkpoints \ No newline at end of file +.ipynb_checkpoints +.vscode/launch.json diff --git a/README.md b/README.md index 9da57aaed8..a50484a8fc 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ search = openai.Engine(id="deployment-namme").search(documents=["White House", " print(search) ``` -Please note that for the moment, the Microsoft Azure endpoints can only be used for completion and search operations. +Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, search and fine-tuning operations. ### Command-line interface diff --git a/openai/api_requestor.py b/openai/api_requestor.py index ac5c5af6c6..8bb4eb6a83 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -122,7 +122,7 @@ def request( def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): try: - error_data = resp["error"] + error_data = resp["error"] if self.api_type == ApiType.OPEN_AI else resp except (KeyError, TypeError): raise error.APIError( "Invalid response object from API: %r (HTTP response code " @@ -333,6 +333,10 @@ def _interpret_response( def _interpret_response_line( self, rbody, rcode, rheaders, stream: bool ) -> OpenAIResponse: + # HTTP 204 response code does not have any content in the body. + if rcode == 204: + return OpenAIResponse(None, rheaders) + if rcode == 503: raise error.ServiceUnavailableError( "The server is overloaded or not ready yet.", diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index 5d10da1d29..c7245aecd4 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -8,7 +8,8 @@ class APIResource(OpenAIObject): api_prefix = "" - azure_api_prefix = "openai/deployments" + azure_api_prefix = "openai" + azure_deployments_prefix = "deployments" @classmethod def retrieve(cls, id, api_key=None, request_id=None, **params): @@ -46,27 +47,34 @@ def instance_url(http://github.com/self,%20operation=None): "id", ) api_version = self.api_version or openai.api_version + extn = quote_plus(id) if self.typed_api_type == ApiType.AZURE: if not api_version: raise error.InvalidRequestError( "An API version is required for the Azure API type." ) + if not operation: - raise error.InvalidRequestError( - "The request needs an operation (eg: 'search') for the Azure OpenAI API type." + base = self.class_url() + return "/%s%s/%s?api-version=%s" % ( + self.azure_api_prefix, + base, + extn, + api_version ) - extn = quote_plus(id) - return "/%s/%s/%s?api-version=%s" % ( + + return "/%s/%s/%s/%s?api-version=%s" % ( self.azure_api_prefix, + self.azure_deployments_prefix, extn, operation, - api_version, + api_version ) + elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url() - extn = quote_plus(id) return "%s/%s" % (base, extn) else: @@ -81,6 +89,7 @@ def _static_request( url_, api_key=None, api_base=None, + api_type=None, request_id=None, api_version=None, organization=None, @@ -91,6 +100,7 @@ def _static_request( api_version=api_version, organization=organization, api_base=api_base, + api_type=api_type ) response, _, api_key = requestor.request( method_, url_, params, request_id=request_id @@ -98,3 +108,10 @@ def _static_request( return util.convert_to_openai_object( response, api_key, api_version, organization ) + + @classmethod + def _get_api_type_and_version(cls, api_type: str, api_version: str): + typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) + typed_api_version = api_version or openai.api_version + return (typed_api_type, typed_api_version) + diff --git a/openai/api_resources/abstract/createable_api_resource.py b/openai/api_resources/abstract/createable_api_resource.py index 1538cb3719..6ca2368d13 100644 --- a/openai/api_resources/abstract/createable_api_resource.py +++ b/openai/api_resources/abstract/createable_api_resource.py @@ -1,5 +1,6 @@ -from openai import api_requestor, util +from openai import api_requestor, util, error from openai.api_resources.abstract.api_resource import APIResource +from openai.util import ApiType class CreateableAPIResource(APIResource): @@ -10,6 +11,7 @@ def create( cls, api_key=None, api_base=None, + api_type=None, request_id=None, api_version=None, organization=None, @@ -18,10 +20,20 @@ def create( requestor = api_requestor.APIRequestor( api_key, api_base=api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = cls.class_url() + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + response, _, api_key = requestor.request( "post", url, params, request_id=request_id ) diff --git a/openai/api_resources/abstract/deletable_api_resource.py b/openai/api_resources/abstract/deletable_api_resource.py index 47111b153c..3a6e83ff0e 100644 --- a/openai/api_resources/abstract/deletable_api_resource.py +++ b/openai/api_resources/abstract/deletable_api_resource.py @@ -1,12 +1,24 @@ from urllib.parse import quote_plus +from openai import error from openai.api_resources.abstract.api_resource import APIResource - +from openai.util import ApiType class DeletableAPIResource(APIResource): @classmethod - def delete(cls, sid, **params): + def delete(cls, sid, api_type=None, api_version=None, **params): if isinstance(cls, APIResource): raise ValueError(".delete may only be called as a class method now.") - url = "%s/%s" % (cls.class_url(), quote_plus(sid)) - return cls._static_request("delete", url, **params) + + base = cls.class_url() + extn = quote_plus(sid) + + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + if typed_api_type == ApiType.AZURE: + url = "/%s%s/%s?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s" % (base, extn) + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + + return cls._static_request("delete", url, api_type=api_type, api_version=api_version, **params) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index cd79528df4..b07ab2ec02 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -15,7 +15,6 @@ class EngineAPIResource(APIResource): engine_required = True plain_old_data = False - azure_api_prefix = "openai/deployments" def __init__(self, engine: Optional[str] = None, **kwargs): super().__init__(engine=engine, **kwargs) @@ -30,12 +29,7 @@ def class_url( # Namespaces are separated in object names with periods (.) and in URLs # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - typed_api_type = ( - ApiType.from_str(api_type) - if api_type - else ApiType.from_str(openai.api_type) - ) - api_version = api_version or openai.api_version + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) if typed_api_type == ApiType.AZURE: if not api_version: @@ -47,11 +41,12 @@ def class_url( "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service" ) extn = quote_plus(engine) - return "/%s/%s/%ss?api-version=%s" % ( + return "/%s/%s/%s/%ss?api-version=%s" % ( cls.azure_api_prefix, + cls.azure_deployments_prefix, extn, base, - api_version, + api_version ) elif typed_api_type == ApiType.OPEN_AI: @@ -148,27 +143,29 @@ def instance_url(http://github.com/self): "id", ) - params_connector = "?" + extn = quote_plus(id) + params_connector = '?' + if self.typed_api_type == ApiType.AZURE: api_version = self.api_version or openai.api_version if not api_version: raise error.InvalidRequestError( "An API version is required for the Azure API type." ) - extn = quote_plus(id) base = self.OBJECT_NAME.replace(".", "/") - url = "/%s/%s/%ss/%s?api-version=%s" % ( + url = "/%s/%s/%s/%ss/%s?api-version=%s" % ( self.azure_api_prefix, + self.azure_deployments_prefix, self.engine, base, extn, - api_version, + api_version ) - params_connector = "&" + params_connector = '&' + elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url(http://github.com/self.engine,%20self.api_type,%20self.api_version) - extn = quote_plus(id) url = "%s/%s" % (base, extn) else: diff --git a/openai/api_resources/abstract/listable_api_resource.py b/openai/api_resources/abstract/listable_api_resource.py index b9cf952a91..c01af74236 100644 --- a/openai/api_resources/abstract/listable_api_resource.py +++ b/openai/api_resources/abstract/listable_api_resource.py @@ -1,5 +1,6 @@ -from openai import api_requestor, util +from openai import api_requestor, util, error from openai.api_resources.abstract.api_resource import APIResource +from openai.util import ApiType class ListableAPIResource(APIResource): @@ -15,15 +16,27 @@ def list( api_version=None, organization=None, api_base=None, + api_type=None, **params, ): requestor = api_requestor.APIRequestor( api_key, api_base=api_base or cls.api_base(), api_version=api_version, + api_type=api_type, organization=organization, ) - url = cls.class_url() + + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + response, _, api_key = requestor.request( "get", url, params, request_id=request_id ) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index dbe387b157..2780f543d4 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -3,8 +3,9 @@ from typing import cast import openai -from openai import api_requestor, util +from openai import api_requestor, util, error from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource +from openai.util import ApiType class File(ListableAPIResource, DeletableAPIResource): @@ -18,6 +19,7 @@ def create( model=None, api_key=None, api_base=None, + api_type=None, api_version=None, organization=None, user_provided_filename=None, @@ -27,19 +29,29 @@ def create( requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = cls.class_url() + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + # Set the filename on 'purpose' and 'model' to None so they are # interpreted as form data. files = [("purpose", (None, purpose))] if model is not None: files.append(("model", (None, model))) if user_provided_filename is not None: - files.append(("file", (user_provided_filename, file))) + files.append(("file", (user_provided_filename, file, 'application/octet-stream'))) else: - files.append(("file", file)) + files.append(("file", ("file", file, 'application/octet-stream'))) response, _, api_key = requestor.request("post", url, files=files) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -47,15 +59,31 @@ def create( @classmethod def download( - cls, id, api_key=None, api_base=None, api_version=None, organization=None + cls, + id, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None ): requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = f"{cls.class_url()}/{id}/content" + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + base = cls.class_url() + url = "/%s%s/%s/content?api-version=%s" % (cls.azure_api_prefix, base, id, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = f"{cls.class_url()}/{id}/content" + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + result = requestor.request_raw("get", url) if not 200 <= result.status_code < 300: raise requestor.handle_error_response( @@ -75,6 +103,7 @@ def find_matching_files( purpose, api_key=None, api_base=None, + api_type=None, api_version=None, organization=None, ): @@ -82,6 +111,7 @@ def find_matching_files( all_files = cls.list( api_key=api_key, api_base=api_base or openai.api_base, + api_type=api_type, api_version=api_version, organization=organization, ).get("data", []) @@ -93,7 +123,9 @@ def find_matching_files( file_basename = os.path.basename(f["filename"]) if file_basename != basename: continue - if f["bytes"] != bytes: + if "bytes" in f and f["bytes"] != bytes: + continue + if "size" in f and int(f["size"]) != bytes: continue matching_files.append(f) return matching_files diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index c53671ae68..3e596b6bfd 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -1,23 +1,41 @@ from urllib.parse import quote_plus -from openai import api_requestor, util +from openai import api_requestor, util, error from openai.api_resources.abstract import ( CreateableAPIResource, ListableAPIResource, nested_resource_class_methods, ) +from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource from openai.openai_response import OpenAIResponse +from openai.util import ApiType @nested_resource_class_methods("event", operations=["list"]) -class FineTune(ListableAPIResource, CreateableAPIResource): +class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource): OBJECT_NAME = "fine-tune" @classmethod - def cancel(cls, id, api_key=None, request_id=None, **params): + def cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params + ): base = cls.class_url() extn = quote_plus(id) - url = "%s/%s/cancel" % (base, extn) + + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + if typed_api_type == ApiType.AZURE: + url = "/%s%s/%s/cancel?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s/cancel" % (base, extn) + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + instance = cls(id, api_key, **params) return instance.request("post", url, request_id=request_id) @@ -27,6 +45,7 @@ def stream_events( id, api_key=None, api_base=None, + api_type=None, request_id=None, api_version=None, organization=None, @@ -38,10 +57,20 @@ def stream_events( requestor = api_requestor.APIRequestor( api_key, api_base=api_base, + api_type=api_type, api_version=api_version, organization=organization, ) - url = "%s/%s/events?stream=true" % (base, extn) + + typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + if typed_api_type == ApiType.AZURE: + url = "/%s%s/%s/events?stream=true&api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s/events?stream=true" % (base, extn) + else: + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + response, _, api_key = requestor.request( "get", url, params, stream=True, request_id=request_id ) diff --git a/openai/cli.py b/openai/cli.py index 3b0625611f..dc8436701d 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -320,7 +320,7 @@ def _maybe_upload_file( sys.stdout.write( "Found potentially duplicated files with name '{name}', purpose 'fine-tune' and size {size} bytes\n".format( name=os.path.basename(matching_files[0]["filename"]), - size=matching_files[0]["bytes"], + size=matching_files[0]["bytes"] if "bytes" in matching_files[0] else matching_files[0]["size"], ) ) sys.stdout.write("\n".join(file_ids)) diff --git a/openai/tests/test_url_composition.py b/openai/tests/test_url_composition.py index 2b561c6d53..f5a3251dba 100644 --- a/openai/tests/test_url_composition.py +++ b/openai/tests/test_url_composition.py @@ -133,9 +133,7 @@ def test_engine_search_url_composition_azure_no_operation() -> None: engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") assert engine.api_type == "azure" assert engine.typed_api_type == ApiType.AZURE - with pytest.raises(Exception): - engine.instance_url() - + assert engine.instance_url() == "/openai/engines/test_id?api-version=2021-11-01-preview" @pytest.mark.url def test_engine_search_url_composition_default() -> None: diff --git a/openai/version.py b/openai/version.py index 99977b4919..b87ca6327d 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.16.0" +VERSION = "0.17.0" From 3269db84943f7ab6ce7c86d67ebf01a7320332d3 Mon Sep 17 00:00:00 2001 From: Boris Dayma Date: Mon, 28 Mar 2022 17:43:55 -0500 Subject: [PATCH 026/914] feat(wandb): sanitize name (#77) * feat(wandb): sanitize name * feat(wandb): ensure results are present --- openai/wandb_logger.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/openai/wandb_logger.py b/openai/wandb_logger.py index fd2df9d7b6..bc6696fb39 100644 --- a/openai/wandb_logger.py +++ b/openai/wandb_logger.py @@ -10,6 +10,7 @@ import datetime import io import json + import re from pathlib import Path import numpy as np @@ -108,6 +109,15 @@ def _log_fine_tune( ) return + # check results are present + try: + results_id = fine_tune["result_files"][0]["id"] + results = File.download(id=results_id).decode("utf-8") + except: + if show_individual_warnings: + print(f"Fine-tune {fine_tune_id} has no results and will not be logged") + return + # check run has not been logged already run_path = f"{project}/{fine_tune_id}" if entity is not None: @@ -135,10 +145,6 @@ def _log_fine_tune( if wandb_status == "succeeded" and not force: return - # retrieve results - results_id = fine_tune["result_files"][0]["id"] - results = File.download(id=results_id).decode("utf-8") - # start a wandb run wandb.init( job_type="fine-tune", @@ -251,6 +257,8 @@ def _log_artifact_inputs(cls, file, prefix, artifact_type, project, entity): # get input artifact artifact_name = f"{prefix}-{filename}" + # sanitize name to valid wandb artifact name + artifact_name = re.sub(r"[^a-zA-Z0-9_\-.]", "_", artifact_name) artifact_alias = file_id artifact_path = f"{project}/{artifact_name}:{artifact_alias}" if entity is not None: From 0f80728a9982d1b429aaeacd136f20a170ee733e Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 8 Apr 2022 10:54:02 -0700 Subject: [PATCH 027/914] Add customer endpoints (#126) (#87) * Add customer endpoints * getting in other changes --- openai/__init__.py | 2 ++ openai/api_resources/__init__.py | 1 + openai/api_resources/customer.py | 12 ++++++++++++ openai/tests/test_endpoints.py | 2 +- openai/version.py | 2 +- 5 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 openai/api_resources/customer.py diff --git a/openai/__init__.py b/openai/__init__.py index 59d717b4cc..58444ed948 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -9,6 +9,7 @@ Answer, Classification, Completion, + Customer, Edit, Embedding, Engine, @@ -43,6 +44,7 @@ "Answer", "Classification", "Completion", + "Customer", "Edit", "Embedding", "Engine", diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index 393c714be7..e34b1422b1 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -1,6 +1,7 @@ from openai.api_resources.answer import Answer # noqa: F401 from openai.api_resources.classification import Classification # noqa: F401 from openai.api_resources.completion import Completion # noqa: F401 +from openai.api_resources.customer import Customer # noqa: F401 from openai.api_resources.edit import Edit # noqa: F401 from openai.api_resources.embedding import Embedding # noqa: F401 from openai.api_resources.engine import Engine # noqa: F401 diff --git a/openai/api_resources/customer.py b/openai/api_resources/customer.py new file mode 100644 index 0000000000..571adf8eac --- /dev/null +++ b/openai/api_resources/customer.py @@ -0,0 +1,12 @@ +from openai.openai_object import OpenAIObject + + +class Customer(OpenAIObject): + @classmethod + def get_url(http://github.com/self,%20customer,%20endpoint): + return f"/customer/{customer}/{endpoint}" + + @classmethod + def create(cls, customer, endpoint, **params): + instance = cls() + return instance.request("post", cls.get_url(http://github.com/customer,%20endpoint), params) diff --git a/openai/tests/test_endpoints.py b/openai/tests/test_endpoints.py index 80039aa995..f590328eec 100644 --- a/openai/tests/test_endpoints.py +++ b/openai/tests/test_endpoints.py @@ -33,4 +33,4 @@ def test_completions_multiple_prompts(): def test_completions_model(): result = openai.Completion.create(prompt="This was a test", n=5, model="ada") assert len(result.choices) == 5 - assert result.model.startswith("ada:") + assert result.model.startswith("ada") diff --git a/openai/version.py b/openai/version.py index b87ca6327d..7d785f07df 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.17.0" +VERSION = "0.18.0" From 95fa7d07c2eb8305652606b361154a02aa1e72a8 Mon Sep 17 00:00:00 2001 From: Sorin Suciu Date: Thu, 14 Apr 2022 10:47:12 -0700 Subject: [PATCH 028/914] Add azure deployments + an example/tutorial for using Azure endpoints. (#83) * Add azure deployments + an example/tutorial for using Azure endpoints. * Small change to the error response for the Azure API * Adressed comeents * Fix error message --- .gitignore | 2 + README.md | 2 + examples/azure/finetuning.ipynb | 444 +++++++++++++++++++++++++++++ openai/__init__.py | 2 + openai/api_requestor.py | 2 +- openai/api_resources/__init__.py | 1 + openai/api_resources/deployment.py | 62 ++++ openai/version.py | 2 +- 8 files changed, 515 insertions(+), 2 deletions(-) create mode 100644 examples/azure/finetuning.ipynb create mode 100644 openai/api_resources/deployment.py diff --git a/.gitignore b/.gitignore index de92124490..7ad641a0c8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ build .vscode/settings.json .ipynb_checkpoints .vscode/launch.json +examples/azure/training.jsonl +examples/azure/validation.jsonl diff --git a/README.md b/README.md index a50484a8fc..1dc48c8ec8 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ print(search) ``` Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, search and fine-tuning operations. +For a detailed example on how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebook: +[Using Azure fine-tuning](https://github.com/openai/openai-python/blob/main/examples/azure/finetuning.ipynb) ### Command-line interface diff --git a/examples/azure/finetuning.ipynb b/examples/azure/finetuning.ipynb new file mode 100644 index 0000000000..7b73c700b0 --- /dev/null +++ b/examples/azure/finetuning.ipynb @@ -0,0 +1,444 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Azure Fune tuning example\n", + "In this example we'll try to go over all operations that can be done using the Azure endpoints and their differences with the openAi endpoints (if any).
\n", + "This example focuses on finetuning but touches on the majority of operations that are also available using the API. This example is meant to be a quick way of showing simple operations and is not meant as a finetune model adaptation tutorial.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import openai\n", + "from openai import cli" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "In the following section the endpoint and key need to be set up of the next sections to work.
Please go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for the \"Endpoint\" value and one of the Keys. They will act as api_base and api_key in the code below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "openai.api_key = '' # Please add your api key here\n", + "openai.api_base = '' # Please add your endpoint here\n", + "\n", + "openai.api_type = 'azure'\n", + "openai.api_version = '2022-03-01-preview' # this may change in the future" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Files\n", + "In the next section we will focus on the files operations: importing, listing, retrieving, deleting. For this we need to create 2 temporary files with some sample data. For the sake of simplicity, we will use the same data for training and validation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import shutil\n", + "import json\n", + "\n", + "training_file_name = 'training.jsonl'\n", + "validation_file_name = 'validation.jsonl'\n", + "\n", + "sample_data = [{\"prompt\": \"When I go to the store, I want an\", \"completion\": \"apple\"},\n", + " {\"prompt\": \"When I go to work, I want a\", \"completion\": \"coffe\"},\n", + " {\"prompt\": \"When I go home, I want a\", \"completion\": \"soda\"}]\n", + "\n", + "print(f'Generating the training file: {training_file_name}')\n", + "with open(training_file_name, 'w') as training_file:\n", + " for entry in sample_data:\n", + " json.dump(entry, training_file)\n", + " training_file.write('\\n')\n", + "\n", + "print(f'Copying the training file to the validation file')\n", + "shutil.copy(training_file_name, validation_file_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Files: Listing\n", + "List all of the uploaded files and check for the ones that are named \"training.jsonl\" or \"validation.jsonl\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('Checking for existing uploaded files.')\n", + "results = []\n", + "files = openai.File.list().data\n", + "print(f'Found {len(files)} total uploaded files in the subscription.')\n", + "for item in files:\n", + " if item[\"filename\"] in [training_file_name, validation_file_name]:\n", + " results.append(item[\"id\"])\n", + "print(f'Found {len(results)} already uploaded files that match our names.')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Files: Deleting\n", + "Let's now delete those found files (if any) since we're going to be re-uploading them next." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Deleting already uploaded files.')\n", + "for id in results:\n", + " openai.File.delete(sid = id)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Files: Importing & Retrieving\n", + "Now, let's import our two files ('training.jsonl' and 'validation.jsonl') and keep those IDs since we're going to use them later for finetuning.
\n", + "For this operation we are going to use the cli wrapper which does a bit more checks before uploading and also gives us progress. In addition, after uploading we're going to check the status our import until it has succeeded (or failed if something goes wrong)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "\n", + "def check_status(training_id, validation_id):\n", + " train_status = openai.File.retrieve(training_id)[\"status\"]\n", + " valid_status = openai.File.retrieve(validation_id)[\"status\"]\n", + " print(f'Status (training_file | validation_file): {train_status} | {valid_status}')\n", + " return (train_status, valid_status)\n", + "\n", + "#importing our two files\n", + "training_id = cli.FineTune._get_or_upload(training_file_name, True)\n", + "validation_id = cli.FineTune._get_or_upload(validation_file_name, True)\n", + "\n", + "#checking the status of the imports\n", + "(train_status, valid_status) = check_status(training_id, validation_id)\n", + "\n", + "while train_status not in [\"succeeded\", \"failed\"] or valid_status not in [\"succeeded\", \"failed\"]:\n", + " time.sleep(1)\n", + " (train_status, valid_status) = check_status(training_id, validation_id)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Files: Downloading\n", + "Now let's download one of the files, the training file for example, to check that everything was in order during importing and all bits are there." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Downloading training file: {training_id}')\n", + "result = openai.File.download(training_id)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Finetune\n", + "In this section we are going to use the two training and validation files that we imported in the previous section, to train a finetune model." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Finetune: Adapt\n", + "First let's create the finetune adaptation job." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "create_args = {\n", + " \"training_file\": training_id,\n", + " \"validation_file\": validation_id,\n", + " \"model\": \"curie\",\n", + " \"compute_classification_metrics\": True,\n", + " \"classification_n_classes\": 3\n", + "}\n", + "resp = openai.FineTune.create(**create_args)\n", + "job_id = resp[\"id\"]\n", + "status = resp[\"status\"]\n", + "\n", + "print(f'Fine-tunning model with jobID: {job_id}.')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Finetune: Streaming\n", + "While the job runs, we can subscribe to the streaming events to check the progress of the operation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import signal\n", + "import datetime\n", + "\n", + "def signal_handler(sig, frame):\n", + " status = openai.FineTune.retrieve(job_id).status\n", + " print(f\"Stream interrupted. Job is still {status}.\")\n", + " return\n", + "\n", + "print('Streaming events for the fine-tuning job: {job_id}')\n", + "signal.signal(signal.SIGINT, signal_handler)\n", + "\n", + "events = openai.FineTune.stream_events(job_id)\n", + "try:\n", + " for event in events:\n", + " print(f'{datetime.datetime.fromtimestamp(event[\"created_at\"])} {event[\"message\"]}')\n", + "\n", + "except Exception:\n", + " print(\"Stream interrupted (client disconnected).\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Finetune: Listing and Retrieving\n", + "Now let's check that our operation was successful and in addition we can look at all of the finetuning operations using a list operation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "status = openai.FineTune.retrieve(id=job_id)[\"status\"]\n", + "if status not in [\"succeeded\", \"failed\"]:\n", + " print(f'Job not in terminal status: {status}. Waiting.')\n", + " while status not in [\"succeeded\", \"failed\"]:\n", + " time.sleep(2)\n", + " status = openai.FineTune.retrieve(id=job_id)[\"status\"]\n", + " print(f'Status: {status}')\n", + "else:\n", + " print(f'Finetune job {job_id} finished with status: {status}')\n", + "\n", + "print('Checking other finetune jobs in the subscription.')\n", + "result = openai.FineTune.list()\n", + "print(f'Found {len(result)} finetune jobs.')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Finetune: Deleting\n", + "Finally we can delete our finetune job.
\n", + "WARNING: Please skip this step if you want to continue with the next section as the finetune model is needed. (The delete code is commented out by default)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# openai.FineTune.delete(sid=job_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Deployments\n", + "In this section we are going to create a deployment using the finetune model that we just adapted and then used the deployment to create a simple completion operation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deployments: Create\n", + "Let's create a deployment using the fine-tune model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Fist let's get the model of the previous job:\n", + "result = openai.FineTune.retrieve(id=job_id)\n", + "if result[\"status\"] == 'succeeded':\n", + " model = result[\"fine_tuned_model\"]\n", + "\n", + "# Now let's create the deployment\n", + "print(f'Creating a new deployment with model: {model}')\n", + "result = openai.Deployment.create(model=model, scale_settings={\"scale_type\":\"manual\", \"capacity\": 1})\n", + "deployment_id = result[\"id\"]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deployments: Retrieving\n", + "Now let's check the status of the newly created deployment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Checking for deployment status.')\n", + "resp = openai.Deployment.retrieve(id=deployment_id)\n", + "status = resp[\"status\"]\n", + "print(f'Deployment {deployment_id} is with status: {status}')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deployments: Listing\n", + "Now because creating a new deployment takes a long time, let's look in the subscription for an already finished deployment that succeeded." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('While deployment running, selecting a completed one.')\n", + "deployment_id = None\n", + "result = openai.Deployment.list()\n", + "for deployment in result.data:\n", + " if deployment[\"status\"] == \"succeeded\":\n", + " deployment_id = deployment[\"id\"]\n", + " break\n", + "\n", + "if not deployment_id:\n", + " print('No deployment with status: succeeded found.')\n", + "else:\n", + " print(f'Found a successful deployment with id: {deployment_id}.')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Completions\n", + "Now let's send a sample completion to the deployment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('Sending a test completion job')\n", + "start_phrase = 'When I go to the store, I want a'\n", + "response = openai.Completion.create(engine=deployment_id, model=model, prompt=start_phrase, max_tokens=4)\n", + "text = response['choices'][0]['text'].replace('\\n', '').replace(' .', '.').strip()\n", + "print(f'\"{start_phrase} {text}\"')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deployments: Delete\n", + "Finally let's delete the deployment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Deleting deployment: {deployment_id}')\n", + "openai.Deployment.delete(sid=deployment_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Thank you" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "1efaa68c6557ae864f04a55d1c611eb06843d0ca160c97bf33f135c19475264d" + }, + "kernelspec": { + "display_name": "Python 3.8.10 ('openai-env')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/openai/__init__.py b/openai/__init__.py index 58444ed948..86aa9e2c34 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -11,6 +11,7 @@ Completion, Customer, Edit, + Deployment, Embedding, Engine, ErrorObject, @@ -46,6 +47,7 @@ "Completion", "Customer", "Edit", + "Deployment", "Embedding", "Engine", "ErrorObject", diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 8bb4eb6a83..d2160c1810 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -122,7 +122,7 @@ def request( def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): try: - error_data = resp["error"] if self.api_type == ApiType.OPEN_AI else resp + error_data = resp["error"] except (KeyError, TypeError): raise error.APIError( "Invalid response object from API: %r (HTTP response code " diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index e34b1422b1..1c08ef3b57 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -3,6 +3,7 @@ from openai.api_resources.completion import Completion # noqa: F401 from openai.api_resources.customer import Customer # noqa: F401 from openai.api_resources.edit import Edit # noqa: F401 +from openai.api_resources.deployment import Deployment # noqa: F401 from openai.api_resources.embedding import Embedding # noqa: F401 from openai.api_resources.engine import Engine # noqa: F401 from openai.api_resources.error_object import ErrorObject # noqa: F401 diff --git a/openai/api_resources/deployment.py b/openai/api_resources/deployment.py new file mode 100644 index 0000000000..91562ee1c6 --- /dev/null +++ b/openai/api_resources/deployment.py @@ -0,0 +1,62 @@ +from openai import util +from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource, CreateableAPIResource +from openai.error import InvalidRequestError, APIError + + +class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResource): + engine_required = False + OBJECT_NAME = "deployment" + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new deployment for the provided prompt and parameters. + """ + typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None) + if typed_api_type != util.ApiType.AZURE: + raise APIError("Deployment operations are only available for the Azure API type.") + + if kwargs.get("model", None) is None: + raise InvalidRequestError( + "Must provide a 'model' parameter to create a Deployment.", + param="model", + ) + + scale_settings = kwargs.get("scale_settings", None) + if scale_settings is None: + raise InvalidRequestError( + "Must provide a 'scale_settings' parameter to create a Deployment.", + param="scale_settings", + ) + + if "scale_type" not in scale_settings or "capacity" not in scale_settings: + raise InvalidRequestError( + "The 'scale_settings' parameter contains invalid or incomplete values.", + param="scale_settings", + ) + + return super().create(*args, **kwargs) + + @classmethod + def list(cls, *args, **kwargs): + typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None) + if typed_api_type != util.ApiType.AZURE: + raise APIError("Deployment operations are only available for the Azure API type.") + + return super().list(*args, **kwargs) + + @classmethod + def delete(cls, *args, **kwargs): + typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None) + if typed_api_type != util.ApiType.AZURE: + raise APIError("Deployment operations are only available for the Azure API type.") + + return super().delete(*args, **kwargs) + + @classmethod + def retrieve(cls, *args, **kwargs): + typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None) + if typed_api_type != util.ApiType.AZURE: + raise APIError("Deployment operations are only available for the Azure API type.") + + return super().retrieve(*args, **kwargs) diff --git a/openai/version.py b/openai/version.py index 7d785f07df..85cdcf4e52 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.18.0" +VERSION = "0.18.1" From 0b07e1e9457d3a6252431036b725b3e891f95a40 Mon Sep 17 00:00:00 2001 From: Keiji Kanazawa Date: Mon, 2 May 2022 06:16:08 -0700 Subject: [PATCH 029/914] Remove model param in Completion call (#88) The `model` param is not required for Azure, and its presence results in an authentication error. --- examples/azure/finetuning.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/azure/finetuning.ipynb b/examples/azure/finetuning.ipynb index 7b73c700b0..76459d5545 100644 --- a/examples/azure/finetuning.ipynb +++ b/examples/azure/finetuning.ipynb @@ -385,7 +385,7 @@ "source": [ "print('Sending a test completion job')\n", "start_phrase = 'When I go to the store, I want a'\n", - "response = openai.Completion.create(engine=deployment_id, model=model, prompt=start_phrase, max_tokens=4)\n", + "response = openai.Completion.create(engine=deployment_id, prompt=start_phrase, max_tokens=4)\n", "text = response['choices'][0]['text'].replace('\\n', '').replace(' .', '.').strip()\n", "print(f'\"{start_phrase} {text}\"')\n" ] From 4d2cab10bbfcc8e7f8364a3d26dab0ed386f8451 Mon Sep 17 00:00:00 2001 From: andrewchernyh Date: Fri, 20 May 2022 04:47:00 +0700 Subject: [PATCH 030/914] Fix http reuse connections (#94) Co-authored-by: andrey@inworld.ai --- openai/api_requestor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index d2160c1810..2b0982be75 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -62,7 +62,9 @@ def parse_stream(rbody): for line in rbody: if line: if line == b"data: [DONE]": - return + # return here will cause GeneratorExit exception in urllib3 + # and it will close http connection with TCP Reset + continue if hasattr(line, "decode"): line = line.decode("utf-8") if line.startswith("data: "): From 64c4533098067e5e22f86f43e4cee1b6c5052752 Mon Sep 17 00:00:00 2001 From: hallacy Date: Tue, 24 May 2022 16:51:52 -0700 Subject: [PATCH 031/914] V0.19.0: Support engineless and adds openai.Search.create (#97) * Remove engine requirement for embeddings (#131) * Add search helper (#130) * Add search helper * Move to default plurals * Remove bad imports * Add cli support and remove create_alpha * Bump version --- openai/api_resources/abstract/api_resource.py | 4 +- .../abstract/engine_api_resource.py | 8 ++-- openai/api_resources/completion.py | 2 +- openai/api_resources/deployment.py | 2 +- openai/api_resources/edit.py | 2 +- openai/api_resources/embedding.py | 4 +- openai/api_resources/engine.py | 2 +- .../experimental/completion_config.py | 2 +- openai/api_resources/file.py | 2 +- openai/api_resources/fine_tune.py | 2 +- openai/api_resources/model.py | 2 +- openai/api_resources/search.py | 40 +++++++++++++++---- openai/cli.py | 35 ++++++++-------- openai/version.py | 2 +- 14 files changed, 66 insertions(+), 43 deletions(-) diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index c7245aecd4..69e998ab0b 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -33,8 +33,8 @@ def class_url(http://github.com/cls): # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore if cls.api_prefix: - return "/%s/%ss" % (cls.api_prefix, base) - return "/%ss" % (base) + return "/%s/%s" % (cls.api_prefix, base) + return "/%s" % (base) def instance_url(http://github.com/self,%20operation=None): id = self.get("id") diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index b07ab2ec02..84e77c32f7 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -41,7 +41,7 @@ def class_url( "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service" ) extn = quote_plus(engine) - return "/%s/%s/%s/%ss?api-version=%s" % ( + return "/%s/%s/%s/%s?api-version=%s" % ( cls.azure_api_prefix, cls.azure_deployments_prefix, extn, @@ -51,10 +51,10 @@ def class_url( elif typed_api_type == ApiType.OPEN_AI: if engine is None: - return "/%ss" % (base) + return "/%s" % (base) extn = quote_plus(engine) - return "/engines/%s/%ss" % (extn, base) + return "/engines/%s/%s" % (extn, base) else: raise error.InvalidAPIType("Unsupported API type %s" % api_type) @@ -153,7 +153,7 @@ def instance_url(http://github.com/self): "An API version is required for the Azure API type." ) base = self.OBJECT_NAME.replace(".", "/") - url = "/%s/%s/%s/%ss/%s?api-version=%s" % ( + url = "/%s/%s/%s/%s/%s?api-version=%s" % ( self.azure_api_prefix, self.azure_deployments_prefix, self.engine, diff --git a/openai/api_resources/completion.py b/openai/api_resources/completion.py index 5c4a1a34ea..3d6d9efe1b 100644 --- a/openai/api_resources/completion.py +++ b/openai/api_resources/completion.py @@ -8,7 +8,7 @@ class Completion(EngineAPIResource, ListableAPIResource, DeletableAPIResource): engine_required = False - OBJECT_NAME = "completion" + OBJECT_NAME = "completions" @classmethod def create(cls, *args, **kwargs): diff --git a/openai/api_resources/deployment.py b/openai/api_resources/deployment.py index 91562ee1c6..7d6c3b5cd7 100644 --- a/openai/api_resources/deployment.py +++ b/openai/api_resources/deployment.py @@ -5,7 +5,7 @@ class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResource): engine_required = False - OBJECT_NAME = "deployment" + OBJECT_NAME = "deployments" @classmethod def create(cls, *args, **kwargs): diff --git a/openai/api_resources/edit.py b/openai/api_resources/edit.py index e4378eec60..18295c22f4 100644 --- a/openai/api_resources/edit.py +++ b/openai/api_resources/edit.py @@ -7,7 +7,7 @@ class Edit(EngineAPIResource): engine_required = False - OBJECT_NAME = "edit" + OBJECT_NAME = "edits" @classmethod def create(cls, *args, **kwargs): diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py index 5bbd8e39d4..fd9c7ac27d 100644 --- a/openai/api_resources/embedding.py +++ b/openai/api_resources/embedding.py @@ -10,8 +10,8 @@ class Embedding(EngineAPIResource, ListableAPIResource, DeletableAPIResource): - engine_required = True - OBJECT_NAME = "embedding" + engine_required = False + OBJECT_NAME = "embeddings" @classmethod def create(cls, *args, **kwargs): diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py index 41ddd0d45c..e2c6f1c955 100644 --- a/openai/api_resources/engine.py +++ b/openai/api_resources/engine.py @@ -8,7 +8,7 @@ class Engine(ListableAPIResource, UpdateableAPIResource): - OBJECT_NAME = "engine" + OBJECT_NAME = "engines" def generate(self, timeout=None, **params): start = time.time() diff --git a/openai/api_resources/experimental/completion_config.py b/openai/api_resources/experimental/completion_config.py index 57f4a34f66..5d4feb40e1 100644 --- a/openai/api_resources/experimental/completion_config.py +++ b/openai/api_resources/experimental/completion_config.py @@ -8,4 +8,4 @@ class CompletionConfig( CreateableAPIResource, ListableAPIResource, DeletableAPIResource ): - OBJECT_NAME = "experimental.completion_config" + OBJECT_NAME = "experimental.completion_configs" diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 2780f543d4..83f3a5e602 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -9,7 +9,7 @@ class File(ListableAPIResource, DeletableAPIResource): - OBJECT_NAME = "file" + OBJECT_NAME = "files" @classmethod def create( diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index 3e596b6bfd..b0ca5b494b 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -13,7 +13,7 @@ @nested_resource_class_methods("event", operations=["list"]) class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource): - OBJECT_NAME = "fine-tune" + OBJECT_NAME = "fine-tunes" @classmethod def cancel( diff --git a/openai/api_resources/model.py b/openai/api_resources/model.py index f0b123a974..6db9bb590e 100644 --- a/openai/api_resources/model.py +++ b/openai/api_resources/model.py @@ -3,4 +3,4 @@ class Model(ListableAPIResource, DeletableAPIResource): engine_required = False - OBJECT_NAME = "model" + OBJECT_NAME = "models" diff --git a/openai/api_resources/search.py b/openai/api_resources/search.py index fc7c4326f6..e4b32a1f0f 100644 --- a/openai/api_resources/search.py +++ b/openai/api_resources/search.py @@ -1,12 +1,36 @@ -from openai.api_resources.abstract.api_resource import APIResource +import time +from openai import util +from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.error import InvalidRequestError, TryAgain -class Search(APIResource): - @classmethod - def class_url(http://github.com/cls): - return "/search_indices/search" + +class Search(EngineAPIResource): + engine_required = False + OBJECT_NAME = "search" @classmethod - def create_alpha(cls, **params): - instance = cls() - return instance.request("post", cls.class_url(), params) + def create(cls, *args, **kwargs): + """ + Creates a new search for the provided input and parameters. + + See https://beta.openai.com/docs/api-reference/search for a list + of valid parameters. + """ + + start = time.time() + timeout = kwargs.pop("timeout", None) + if kwargs.get("model", None) is None and kwargs.get("engine", None) is None: + raise InvalidRequestError( + "Must provide an 'engine' or 'model' parameter to create a Search.", + param="engine", + ) + + while True: + try: + return super().create(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/cli.py b/openai/cli.py index dc8436701d..fd9c8469ad 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -266,11 +266,11 @@ def prepare_data(cls, args, purpose): ) @classmethod - def create_alpha(cls, args): - resp = openai.Search.create_alpha( - query=[args.query], - max_documents=args.max_documents, - file_id=args.file, + def create(cls, args): + resp = openai.Search.create( + query=args.query, + documents=args.documents, + model=args.model, ) print(resp) @@ -827,20 +827,14 @@ def help(args): sub.set_defaults(func=File.list) # Search - sub = subparsers.add_parser("search.create_alpha") + sub = subparsers.add_parser("search.create") sub.add_argument( - "-f", - "--file", - required=True, - help="ID for previously uploaded file that contains the documents you want to search", - ) - sub.add_argument( - "-m", - "--max_documents", - help="The maximum number of documents to return", - type=int, - default=200, + "-d", + "--documents", + help="Documents to search over", + type=str, + nargs="+", ) sub.add_argument( "-q", @@ -848,7 +842,12 @@ def help(args): required=True, help="Search query", ) - sub.set_defaults(func=Search.create_alpha) + sub.add_argument( + "-m", + "--model", + help="The model to search with", + ) + sub.set_defaults(func=Search.create) # Finetune sub = subparsers.add_parser("fine_tunes.list") diff --git a/openai/version.py b/openai/version.py index 85cdcf4e52..e50bfd968b 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.18.1" +VERSION = "0.19.0" From 3852e25d0a261a7f777d3f0589cdf73f06a9a31e Mon Sep 17 00:00:00 2001 From: Lilian Date: Tue, 14 Jun 2022 21:15:44 -0700 Subject: [PATCH 032/914] V0.20.0: Add moderation endpoint (#134) (#101) * V0.19.1: Add moderation endpoint (#134) * Add moderation endpoint * Add version * version to model * s/version/model * fix formatting * model default to None * fix test * update value error message * version to 0.20.0 --- openai/__init__.py | 2 ++ openai/api_resources/__init__.py | 1 + openai/api_resources/moderation.py | 22 ++++++++++++++++++++++ openai/version.py | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 openai/api_resources/moderation.py diff --git a/openai/__init__.py b/openai/__init__.py index 86aa9e2c34..48215352b7 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -18,6 +18,7 @@ File, FineTune, Model, + Moderation, Search, ) from openai.error import APIError, InvalidRequestError, OpenAIError @@ -55,6 +56,7 @@ "FineTune", "InvalidRequestError", "Model", + "Moderation", "OpenAIError", "Search", "api_base", diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index 1c08ef3b57..c01690945e 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -10,4 +10,5 @@ from openai.api_resources.file import File # noqa: F401 from openai.api_resources.fine_tune import FineTune # noqa: F401 from openai.api_resources.model import Model # noqa: F401 +from openai.api_resources.moderation import Moderation # noqa: F401 from openai.api_resources.search import Search # noqa: F401 diff --git a/openai/api_resources/moderation.py b/openai/api_resources/moderation.py new file mode 100644 index 0000000000..ecf1518cbd --- /dev/null +++ b/openai/api_resources/moderation.py @@ -0,0 +1,22 @@ +from typing import List, Optional, Union + +from openai.openai_object import OpenAIObject + + +class Moderation(OpenAIObject): + VALID_MODEL_NAMES: List[str] = ["text-moderation-stable", "text-moderation-latest"] + + @classmethod + def get_url(http://github.com/self): + return "/moderations" + + @classmethod + def create(cls, input: Union[str, List[str]], model: Optional[str] = None): + if model not in cls.VALID_MODEL_NAMES: + raise ValueError( + f"The parameter model should be chosen from {cls.VALID_MODEL_NAMES} " + f"and it is default to be None." + ) + + instance = cls() + return instance.request("post", cls.get_url(), {"input": input, "model": model}) diff --git a/openai/version.py b/openai/version.py index e50bfd968b..62c0a105e2 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.19.0" +VERSION = "0.20.0" From 24fc6924c8d9ba4e43574ee6db3a8fdad147f5da Mon Sep 17 00:00:00 2001 From: Lilian Date: Tue, 14 Jun 2022 22:43:38 -0700 Subject: [PATCH 033/914] v0.20.1: patch how model is handled in moderation endpoint. (#102) --- openai/api_resources/moderation.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openai/api_resources/moderation.py b/openai/api_resources/moderation.py index ecf1518cbd..12a4e19d7e 100644 --- a/openai/api_resources/moderation.py +++ b/openai/api_resources/moderation.py @@ -12,11 +12,14 @@ def get_url(http://github.com/self): @classmethod def create(cls, input: Union[str, List[str]], model: Optional[str] = None): - if model not in cls.VALID_MODEL_NAMES: + if model is not None and model not in cls.VALID_MODEL_NAMES: raise ValueError( f"The parameter model should be chosen from {cls.VALID_MODEL_NAMES} " f"and it is default to be None." ) instance = cls() - return instance.request("post", cls.get_url(), {"input": input, "model": model}) + params = {"input": input} + if model is not None: + params["model"] = model + return instance.request("post", cls.get_url(), params) From 0e37b4925e2942c92b8672ac67a1b01199b26cc0 Mon Sep 17 00:00:00 2001 From: Sorin Suciu Date: Tue, 21 Jun 2022 07:12:10 -0700 Subject: [PATCH 034/914] [Azure] Capacity is not needed for scale types other than 'manual' (#103) * [Azure] Capacity is not needed for scale types other than 'manual' * Bump version --- openai/__init__.py | 2 +- openai/api_resources/deployment.py | 3 ++- openai/version.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/openai/__init__.py b/openai/__init__.py index 48215352b7..0af34274da 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -32,7 +32,7 @@ organization = os.environ.get("OPENAI_ORGANIZATION") api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") -api_version = "2021-11-01-preview" if api_type == "azure" else None +api_version = "2022-03-01-preview" if api_type == "azure" else None verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None app_info = None diff --git a/openai/api_resources/deployment.py b/openai/api_resources/deployment.py index 7d6c3b5cd7..e3b9b78bc3 100644 --- a/openai/api_resources/deployment.py +++ b/openai/api_resources/deployment.py @@ -29,7 +29,8 @@ def create(cls, *args, **kwargs): param="scale_settings", ) - if "scale_type" not in scale_settings or "capacity" not in scale_settings: + if "scale_type" not in scale_settings or \ + (scale_settings["scale_type"].lower() == 'manual' and "capacity" not in scale_settings): raise InvalidRequestError( "The 'scale_settings' parameter contains invalid or incomplete values.", param="scale_settings", diff --git a/openai/version.py b/openai/version.py index 62c0a105e2..9565641640 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.20.0" +VERSION = "0.20.1" From d4f2d0fdff975217b958e752903bcf32ad522894 Mon Sep 17 00:00:00 2001 From: t-asutedjo <100279393+t-asutedjo@users.noreply.github.com> Date: Tue, 21 Jun 2022 19:01:27 +0200 Subject: [PATCH 035/914] Add azure embeddings example notebook (#104) * Remove invalid inheritances from Embedding and Completion - Remove ListableAPIResource inheritance - Remove DeletableAPIResource * Add embeddings example notebook * bump version --- examples/azure/embeddings.ipynb | 194 +++++++++++++++++++++++++++++ openai/api_resources/completion.py | 2 +- openai/api_resources/embedding.py | 2 +- 3 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 examples/azure/embeddings.ipynb diff --git a/examples/azure/embeddings.ipynb b/examples/azure/embeddings.ipynb new file mode 100644 index 0000000000..d7deefd9de --- /dev/null +++ b/examples/azure/embeddings.ipynb @@ -0,0 +1,194 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Azure embeddings example\n", + "In this example we'll try to go over all operations for embeddings that can be done using the Azure endpoints. \\\n", + "This example focuses on finetuning but touches on the majority of operations that are also available using the API. This example is meant to be a quick way of showing simple operations and is not meant as a tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import openai\n", + "from openai import cli" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "In the following section the endpoint and key need to be set up of the next sections to work. \\\n", + "Please go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for the \"Endpoint\" value and one of the Keys. They will act as api_base and api_key in the code below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "openai.api_key = '' # Please add your api key here\n", + "openai.api_base = '' # Please add your endpoint here\n", + "\n", + "openai.api_type = 'azure'\n", + "openai.api_version = '2022-03-01-preview' # this may change in the future" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Deployments\n", + "In this section we are going to create a deployment using the finetune model that we just adapted and then used the deployment to create a simple completion operation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deployments: Create Manually\n", + "Let's create a deployment using the text-similarity-curie-001 engine. You can create a new deployment by going to your Resource in your portal under \"Resource Management\" -> \"Deployments\"." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### (Optional) Deployments: Create Programatically\n", + "We can also create a deployment using code:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = \"text-similarity-curie-001\"\n", + "\n", + "# Now let's create the deployment\n", + "print(f'Creating a new deployment with model: {model}')\n", + "result = openai.Deployment.create(model=model, scale_settings={\"scale_type\":\"manual\", \"capacity\": 1})\n", + "deployment_id = result[\"id\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### (Optional) Deployments: Retrieving\n", + "Now let's check the status of the newly created deployment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Checking for deployment status.')\n", + "resp = openai.Deployment.retrieve(id=deployment_id)\n", + "status = resp[\"status\"]\n", + "print(f'Deployment {deployment_id} is with status: {status}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deployments: Listing\n", + "Now because creating a new deployment takes a long time, let's look in the subscription for an already finished deployment that succeeded." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('While deployment running, selecting a completed one.')\n", + "deployment_id = None\n", + "result = openai.Deployment.list()\n", + "for deployment in result.data:\n", + " if deployment[\"status\"] == \"succeeded\":\n", + " deployment_id = deployment[\"id\"]\n", + " break\n", + "\n", + "if not deployment_id:\n", + " print('No deployment with status: succeeded found.')\n", + "else:\n", + " print(f'Found a successful deployment with id: {deployment_id}.')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Embeddings\n", + "Now let's send a sample embedding to the deployment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "embeddings = openai.Embedding.create(engine=deployment_id,\n", + " input=\"The food was delicious and the waiter...\")\n", + " \n", + "print(embeddings)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### (Optional) Deployments: Delete\n", + "Finally let's delete the deployment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Deleting deployment: {deployment_id}')\n", + "openai.Deployment.delete(sid=deployment_id)" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + }, + "kernelspec": { + "display_name": "Python 3.8.10 64-bit", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/openai/api_resources/completion.py b/openai/api_resources/completion.py index 3d6d9efe1b..d1a0ec1df4 100644 --- a/openai/api_resources/completion.py +++ b/openai/api_resources/completion.py @@ -6,7 +6,7 @@ from openai.error import InvalidRequestError, TryAgain -class Completion(EngineAPIResource, ListableAPIResource, DeletableAPIResource): +class Completion(EngineAPIResource): engine_required = False OBJECT_NAME = "completions" diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py index fd9c7ac27d..883a5e1744 100644 --- a/openai/api_resources/embedding.py +++ b/openai/api_resources/embedding.py @@ -9,7 +9,7 @@ from openai.error import InvalidRequestError, TryAgain -class Embedding(EngineAPIResource, ListableAPIResource, DeletableAPIResource): +class Embedding(EngineAPIResource): engine_required = False OBJECT_NAME = "embeddings" From 53e5ba4bf87dd89ed066a8a194e6aeed08290fb1 Mon Sep 17 00:00:00 2001 From: t-asutedjo <100279393+t-asutedjo@users.noreply.github.com> Date: Fri, 24 Jun 2022 09:53:19 +0200 Subject: [PATCH 036/914] Add Azure AD authentication support (#92) * Add Azure AD authentication support * Add azure_ad code examples * Remove debug print statement * Bump version --- README.md | 25 +++++++++- examples/azure/finetuning.ipynb | 33 +++++++++++- openai/__init__.py | 3 +- openai/api_resources/abstract/api_resource.py | 12 ++--- .../abstract/createable_api_resource.py | 10 ++-- .../abstract/deletable_api_resource.py | 16 +++--- .../abstract/engine_api_resource.py | 14 +++--- .../abstract/listable_api_resource.py | 10 ++-- openai/api_resources/deployment.py | 36 +++++++------ openai/api_resources/engine.py | 2 +- openai/api_resources/file.py | 36 +++++++------ openai/api_resources/fine_tune.py | 22 ++++---- openai/tests/test_api_requestor.py | 15 +++++- openai/tests/test_url_composition.py | 50 +++++++++++++++++-- openai/util.py | 8 ++- openai/version.py | 2 +- 16 files changed, 215 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 1dc48c8ec8..0a79a36fcd 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,13 @@ openai.api_base = "https://example-endpoint.openai.azure.com/" openai.api_version = "2021-11-01-preview" # create a completion -completion = openai.Completion.create(engine="deployment-namme", prompt="Hello world") +completion = openai.Completion.create(engine="deployment-name", prompt="Hello world") # print the completion print(completion.choices[0].text) # create a search and pass the deployment-name as the engine Id. -search = openai.Engine(id="deployment-namme").search(documents=["White House", "hospital", "school"], query ="the president") +search = openai.Engine(id="deployment-name").search(documents=["White House", "hospital", "school"], query ="the president") # print the search print(search) @@ -81,6 +81,27 @@ Please note that for the moment, the Microsoft Azure endpoints can only be used For a detailed example on how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebook: [Using Azure fine-tuning](https://github.com/openai/openai-python/blob/main/examples/azure/finetuning.ipynb) +### Microsoft Azure Active Directory Authentication + +In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the api_type to "azure_ad" and pass the acquired credential token to api_key. The rest of the parameters need to be set as specified in the previous section. + + +```python +from azure.identity import DefaultAzureCredential +import openai + +# Request credential +default_credential = DefaultAzureCredential() +token = default_credential.get_token("https://cognitiveservices.azure.com/") + +# Setup parameters +openai.api_type = "azure_ad" +openai.api_key = token.token +openai.api_base = "https://example-endpoint.openai.azure.com/" +openai.api_version = "2022-03-01-preview" + +# ... +``` ### Command-line interface This library additionally provides an `openai` command-line utility diff --git a/examples/azure/finetuning.ipynb b/examples/azure/finetuning.ipynb index 76459d5545..f691980a92 100644 --- a/examples/azure/finetuning.ipynb +++ b/examples/azure/finetuning.ipynb @@ -40,6 +40,35 @@ "openai.api_version = '2022-03-01-preview' # this may change in the future" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Microsoft Active Directory Authentication\n", + "Instead of key based authentication, you can use Active Directory to authenticate using credential tokens. Uncomment the next code section to use credential based authentication:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "from azure.identity import DefaultAzureCredential\n", + "\n", + "default_credential = DefaultAzureCredential()\n", + "token = default_credential.get_token(\"https://cognitiveservices.azure.com/")\n", + "\n", + "openai.api_type = 'azure_ad'\n", + "openai.api_key = token.token\n", + "openai.api_version = '2022-03-01-preview' # this may change in the future\n", + "\n", + "\n", + "openai.api_base = '' # Please add your endpoint here\n", + "\"\"\"" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -418,10 +447,10 @@ ], "metadata": { "interpreter": { - "hash": "1efaa68c6557ae864f04a55d1c611eb06843d0ca160c97bf33f135c19475264d" + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" }, "kernelspec": { - "display_name": "Python 3.8.10 ('openai-env')", + "display_name": "Python 3.8.10 64-bit", "language": "python", "name": "python3" }, diff --git a/openai/__init__.py b/openai/__init__.py index 0af34274da..554af337ab 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -32,7 +32,8 @@ organization = os.environ.get("OPENAI_ORGANIZATION") api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") -api_version = "2022-03-01-preview" if api_type == "azure" else None +api_version = "2022-03-01-preview" if api_type in ( + "azure", "azure_ad", "azuread") else None verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None app_info = None diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index 69e998ab0b..7324401af9 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -49,12 +49,12 @@ def instance_url(http://github.com/self,%20operation=None): api_version = self.api_version or openai.api_version extn = quote_plus(id) - if self.typed_api_type == ApiType.AZURE: + if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): if not api_version: raise error.InvalidRequestError( "An API version is required for the Azure API type." ) - + if not operation: base = self.class_url() return "/%s%s/%s?api-version=%s" % ( @@ -72,13 +72,13 @@ def instance_url(http://github.com/self,%20operation=None): api_version ) - elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url() return "%s/%s" % (base, extn) else: - raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) + raise error.InvalidAPIType( + "Unsupported API type %s" % self.api_type) # The `method_` and `url_` arguments are suffixed with an underscore to # avoid conflicting with actual request parameters in `params`. @@ -111,7 +111,7 @@ def _static_request( @classmethod def _get_api_type_and_version(cls, api_type: str, api_version: str): - typed_api_type = ApiType.from_str(api_type) if api_type else ApiType.from_str(openai.api_type) + typed_api_type = ApiType.from_str( + api_type) if api_type else ApiType.from_str(openai.api_type) typed_api_version = api_version or openai.api_version return (typed_api_type, typed_api_version) - diff --git a/openai/api_resources/abstract/createable_api_resource.py b/openai/api_resources/abstract/createable_api_resource.py index 6ca2368d13..57889b24e9 100644 --- a/openai/api_resources/abstract/createable_api_resource.py +++ b/openai/api_resources/abstract/createable_api_resource.py @@ -24,15 +24,17 @@ def create( api_version=api_version, organization=organization, ) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version) - if typed_api_type == ApiType.AZURE: + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, + base, api_version) elif typed_api_type == ApiType.OPEN_AI: url = cls.class_url() else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType('Unsupported API type %s' % api_type) response, _, api_key = requestor.request( "post", url, params, request_id=request_id diff --git a/openai/api_resources/abstract/deletable_api_resource.py b/openai/api_resources/abstract/deletable_api_resource.py index 3a6e83ff0e..f1235c4a4f 100644 --- a/openai/api_resources/abstract/deletable_api_resource.py +++ b/openai/api_resources/abstract/deletable_api_resource.py @@ -4,21 +4,25 @@ from openai.api_resources.abstract.api_resource import APIResource from openai.util import ApiType + class DeletableAPIResource(APIResource): @classmethod def delete(cls, sid, api_type=None, api_version=None, **params): if isinstance(cls, APIResource): - raise ValueError(".delete may only be called as a class method now.") + raise ValueError( + ".delete may only be called as a class method now.") base = cls.class_url() extn = quote_plus(sid) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - if typed_api_type == ApiType.AZURE: - url = "/%s%s/%s?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version) + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + url = "/%s%s/%s?api-version=%s" % ( + cls.azure_api_prefix, base, extn, api_version) elif typed_api_type == ApiType.OPEN_AI: url = "%s/%s" % (base, extn) else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + return cls._static_request("delete", url, api_type=api_type, api_version=api_version, **params) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index 84e77c32f7..3126725e0c 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -29,9 +29,10 @@ def class_url( # Namespaces are separated in object names with periods (.) and in URLs # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version) - if typed_api_type == ApiType.AZURE: + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): if not api_version: raise error.InvalidRequestError( "An API version is required for the Azure API type." @@ -107,7 +108,8 @@ def create( ) if stream: - assert not isinstance(response, OpenAIResponse) # must be an iterator + # must be an iterator + assert not isinstance(response, OpenAIResponse) return ( util.convert_to_openai_object( line, @@ -146,7 +148,7 @@ def instance_url(http://github.com/self): extn = quote_plus(id) params_connector = '?' - if self.typed_api_type == ApiType.AZURE: + if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): api_version = self.api_version or openai.api_version if not api_version: raise error.InvalidRequestError( @@ -163,13 +165,13 @@ def instance_url(http://github.com/self): ) params_connector = '&' - elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url(http://github.com/self.engine,%20self.api_type,%20self.api_version) url = "%s/%s" % (base, extn) else: - raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) + raise error.InvalidAPIType( + "Unsupported API type %s" % self.api_type) timeout = self.get("timeout") if timeout is not None: diff --git a/openai/api_resources/abstract/listable_api_resource.py b/openai/api_resources/abstract/listable_api_resource.py index c01af74236..18e49b887b 100644 --- a/openai/api_resources/abstract/listable_api_resource.py +++ b/openai/api_resources/abstract/listable_api_resource.py @@ -27,15 +27,17 @@ def list( organization=organization, ) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version) - if typed_api_type == ApiType.AZURE: + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, + base, api_version) elif typed_api_type == ApiType.OPEN_AI: url = cls.class_url() else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType('Unsupported API type %s' % api_type) response, _, api_key = requestor.request( "get", url, params, request_id=request_id diff --git a/openai/api_resources/deployment.py b/openai/api_resources/deployment.py index e3b9b78bc3..e7a59d91cd 100644 --- a/openai/api_resources/deployment.py +++ b/openai/api_resources/deployment.py @@ -12,9 +12,11 @@ def create(cls, *args, **kwargs): """ Creates a new deployment for the provided prompt and parameters. """ - typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None) - if typed_api_type != util.ApiType.AZURE: - raise APIError("Deployment operations are only available for the Azure API type.") + typed_api_type, _ = cls._get_api_type_and_version( + kwargs.get("api_type", None), None) + if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise APIError( + "Deployment operations are only available for the Azure API type.") if kwargs.get("model", None) is None: raise InvalidRequestError( @@ -28,9 +30,9 @@ def create(cls, *args, **kwargs): "Must provide a 'scale_settings' parameter to create a Deployment.", param="scale_settings", ) - + if "scale_type" not in scale_settings or \ - (scale_settings["scale_type"].lower() == 'manual' and "capacity" not in scale_settings): + (scale_settings["scale_type"].lower() == 'manual' and "capacity" not in scale_settings): raise InvalidRequestError( "The 'scale_settings' parameter contains invalid or incomplete values.", param="scale_settings", @@ -40,24 +42,30 @@ def create(cls, *args, **kwargs): @classmethod def list(cls, *args, **kwargs): - typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None) - if typed_api_type != util.ApiType.AZURE: - raise APIError("Deployment operations are only available for the Azure API type.") + typed_api_type, _ = cls._get_api_type_and_version( + kwargs.get("api_type", None), None) + if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise APIError( + "Deployment operations are only available for the Azure API type.") return super().list(*args, **kwargs) @classmethod def delete(cls, *args, **kwargs): - typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None) - if typed_api_type != util.ApiType.AZURE: - raise APIError("Deployment operations are only available for the Azure API type.") + typed_api_type, _ = cls._get_api_type_and_version( + kwargs.get("api_type", None), None) + if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise APIError( + "Deployment operations are only available for the Azure API type.") return super().delete(*args, **kwargs) @classmethod def retrieve(cls, *args, **kwargs): - typed_api_type, _ = cls._get_api_type_and_version(kwargs.get("api_type", None), None) - if typed_api_type != util.ApiType.AZURE: - raise APIError("Deployment operations are only available for the Azure API type.") + typed_api_type, _ = cls._get_api_type_and_version( + kwargs.get("api_type", None), None) + if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise APIError( + "Deployment operations are only available for the Azure API type.") return super().retrieve(*args, **kwargs) diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py index e2c6f1c955..11c8ec9ec9 100644 --- a/openai/api_resources/engine.py +++ b/openai/api_resources/engine.py @@ -28,7 +28,7 @@ def generate(self, timeout=None, **params): util.log_info("Waiting for model to warm up", error=e) def search(self, **params): - if self.typed_api_type == ApiType.AZURE: + if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): return self.request("post", self.instance_url("http://github.com/search"), params) elif self.typed_api_type == ApiType.OPEN_AI: return self.request("post", self.instance_url() + "/search", params) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 83f3a5e602..3bf2afbe65 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -25,7 +25,8 @@ def create( user_provided_filename=None, ): if purpose != "search" and model is not None: - raise ValueError("'model' is only meaningful if 'purpose' is 'search'") + raise ValueError( + "'model' is only meaningful if 'purpose' is 'search'") requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, @@ -33,15 +34,17 @@ def create( api_version=api_version, organization=organization, ) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version) - if typed_api_type == ApiType.AZURE: + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, + base, api_version) elif typed_api_type == ApiType.OPEN_AI: url = cls.class_url() else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType('Unsupported API type %s' % api_type) # Set the filename on 'purpose' and 'model' to None so they are # interpreted as form data. @@ -49,7 +52,8 @@ def create( if model is not None: files.append(("model", (None, model))) if user_provided_filename is not None: - files.append(("file", (user_provided_filename, file, 'application/octet-stream'))) + files.append( + ("file", (user_provided_filename, file, 'application/octet-stream'))) else: files.append(("file", ("file", file, 'application/octet-stream'))) response, _, api_key = requestor.request("post", url, files=files) @@ -59,12 +63,12 @@ def create( @classmethod def download( - cls, - id, - api_key=None, + cls, + id, + api_key=None, api_base=None, api_type=None, - api_version=None, + api_version=None, organization=None ): requestor = api_requestor.APIRequestor( @@ -74,16 +78,18 @@ def download( api_version=api_version, organization=organization, ) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version) - if typed_api_type == ApiType.AZURE: + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s/%s/content?api-version=%s" % (cls.azure_api_prefix, base, id, api_version) + url = "/%s%s/%s/content?api-version=%s" % ( + cls.azure_api_prefix, base, id, api_version) elif typed_api_type == ApiType.OPEN_AI: url = f"{cls.class_url()}/{id}/content" else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + result = requestor.request_raw("get", url) if not 200 <= result.status_code < 300: raise requestor.handle_error_response( diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index b0ca5b494b..bfdecbf8cd 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -28,13 +28,15 @@ def cancel( base = cls.class_url() extn = quote_plus(id) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - if typed_api_type == ApiType.AZURE: - url = "/%s%s/%s/cancel?api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version) + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + url = "/%s%s/%s/cancel?api-version=%s" % ( + cls.azure_api_prefix, base, extn, api_version) elif typed_api_type == ApiType.OPEN_AI: url = "%s/%s/cancel" % (base, extn) else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType('Unsupported API type %s' % api_type) instance = cls(id, api_key, **params) return instance.request("post", url, request_id=request_id) @@ -62,15 +64,17 @@ def stream_events( organization=organization, ) - typed_api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version) - if typed_api_type == ApiType.AZURE: - url = "/%s%s/%s/events?stream=true&api-version=%s" % (cls.azure_api_prefix, base, extn, api_version) + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + url = "/%s%s/%s/events?stream=true&api-version=%s" % ( + cls.azure_api_prefix, base, extn, api_version) elif typed_api_type == ApiType.OPEN_AI: url = "%s/%s/events?stream=true" % (base, extn) else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) - + raise error.InvalidAPIType('Unsupported API type %s' % api_type) + response, _, api_key = requestor.request( "get", url, params, stream=True, request_id=request_id ) diff --git a/openai/tests/test_api_requestor.py b/openai/tests/test_api_requestor.py index 1b252fc4fb..4998a0ffb2 100644 --- a/openai/tests/test_api_requestor.py +++ b/openai/tests/test_api_requestor.py @@ -37,7 +37,6 @@ def test_requestor_open_ai_headers() -> None: headers = api_requestor.request_headers( method="get", extra=headers, request_id="test_id" ) - print(headers) assert "Test_Header" in headers assert headers["Test_Header"] == "Unit_Test_Header" assert "Authorization" in headers @@ -51,8 +50,20 @@ def test_requestor_azure_headers() -> None: headers = api_requestor.request_headers( method="get", extra=headers, request_id="test_id" ) - print(headers) assert "Test_Header" in headers assert headers["Test_Header"] == "Unit_Test_Header" assert "api-key" in headers assert headers["api-key"] == "test_key" + + +@pytest.mark.requestor +def test_requestor_azure_ad_headers() -> None: + api_requestor = APIRequestor(key="test_key", api_type="azure_ad") + headers = {"Test_Header": "Unit_Test_Header"} + headers = api_requestor.request_headers( + method="get", extra=headers, request_id="test_id" + ) + assert "Test_Header" in headers + assert headers["Test_Header"] == "Unit_Test_Header" + assert "Authorization" in headers + assert headers["Authorization"] == "Bearer test_key" diff --git a/openai/tests/test_url_composition.py b/openai/tests/test_url_composition.py index f5a3251dba..5d3da919bf 100644 --- a/openai/tests/test_url_composition.py +++ b/openai/tests/test_url_composition.py @@ -15,6 +15,15 @@ def test_completions_url_composition_azure() -> None: ) +@pytest.mark.url +def test_completions_url_composition_azure_ad() -> None: + url = Completion.class_url("http://github.com/test_engine%22,%20%22azure_ad%22,%20%222021-11-01-preview") + assert ( + url + == "/openai/deployments/test_engine/completions?api-version=2021-11-01-preview" + ) + + @pytest.mark.url def test_completions_url_composition_default() -> None: url = Completion.class_url("http://github.com/test_engine") @@ -48,6 +57,21 @@ def test_completions_url_composition_instance_url_azure() -> None: ) +@pytest.mark.url +def test_completions_url_composition_instance_url_azure_ad() -> None: + completion = Completion( + id="test_id", + engine="test_engine", + api_type="azure_ad", + api_version="2021-11-01-preview", + ) + url = completion.instance_url() + assert ( + url + == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview" + ) + + @pytest.mark.url def test_completions_url_composition_instance_url_azure_no_version() -> None: completion = Completion( @@ -78,7 +102,8 @@ def test_completions_url_composition_instance_url_open_ai() -> None: @pytest.mark.url def test_completions_url_composition_instance_url_invalid() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="invalid") + completion = Completion( + id="test_id", engine="test_engine", api_type="invalid") with pytest.raises(Exception): url = completion.instance_url() @@ -101,7 +126,8 @@ def test_completions_url_composition_instance_url_timeout_azure() -> None: @pytest.mark.url def test_completions_url_composition_instance_url_timeout_openai() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="open_ai") + completion = Completion( + id="test_id", engine="test_engine", api_type="open_ai") completion["timeout"] = 12 url = completion.instance_url() assert url == "/engines/test_engine/completions/test_id?timeout=12" @@ -109,7 +135,8 @@ def test_completions_url_composition_instance_url_timeout_openai() -> None: @pytest.mark.url def test_engine_search_url_composition_azure() -> None: - engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") + engine = Engine(id="test_id", api_type="azure", + api_version="2021-11-01-preview") assert engine.api_type == "azure" assert engine.typed_api_type == ApiType.AZURE url = engine.instance_url("http://github.com/test_operation") @@ -119,6 +146,19 @@ def test_engine_search_url_composition_azure() -> None: ) +@pytest.mark.url +def test_engine_search_url_composition_azure_ad() -> None: + engine = Engine(id="test_id", api_type="azure_ad", + api_version="2021-11-01-preview") + assert engine.api_type == "azure_ad" + assert engine.typed_api_type == ApiType.AZURE_AD + url = engine.instance_url("http://github.com/test_operation") + assert ( + url + == "/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview" + ) + + @pytest.mark.url def test_engine_search_url_composition_azure_no_version() -> None: engine = Engine(id="test_id", api_type="azure", api_version=None) @@ -130,11 +170,13 @@ def test_engine_search_url_composition_azure_no_version() -> None: @pytest.mark.url def test_engine_search_url_composition_azure_no_operation() -> None: - engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") + engine = Engine(id="test_id", api_type="azure", + api_version="2021-11-01-preview") assert engine.api_type == "azure" assert engine.typed_api_type == ApiType.AZURE assert engine.instance_url() == "/openai/engines/test_id?api-version=2021-11-01-preview" + @pytest.mark.url def test_engine_search_url_composition_default() -> None: engine = Engine(id="test_id") diff --git a/openai/util.py b/openai/util.py index becd7d14db..e69fad0903 100644 --- a/openai/util.py +++ b/openai/util.py @@ -20,7 +20,7 @@ api_key_to_header = ( lambda api, key: {"Authorization": f"Bearer {key}"} - if api == ApiType.OPEN_AI + if api in (ApiType.OPEN_AI, ApiType.AZURE_AD) else {"api-key": f"{key}"} ) @@ -28,11 +28,14 @@ class ApiType(Enum): AZURE = 1 OPEN_AI = 2 + AZURE_AD = 3 @staticmethod def from_str(label): if label.lower() == "azure": return ApiType.AZURE + elif label.lower() in ("azure_ad", "azuread"): + return ApiType.AZURE_AD elif label.lower() in ("open_ai", "openai"): return ApiType.OPEN_AI else: @@ -175,7 +178,8 @@ def default_api_key() -> str: with open(openai.api_key_path, "rt") as k: api_key = k.read().strip() if not api_key.startswith("sk-"): - raise ValueError(f"Malformed API key in {openai.api_key_path}.") + raise ValueError( + f"Malformed API key in {openai.api_key_path}.") return api_key elif openai.api_key is not None: return openai.api_key diff --git a/openai/version.py b/openai/version.py index 9565641640..ccccb9d9b5 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.20.1" +VERSION = "0.21.0" From 8f8f791c5f3cf1f59851177059bfa5cedd7b603b Mon Sep 17 00:00:00 2001 From: oscar-king Date: Mon, 27 Jun 2022 19:14:39 +0200 Subject: [PATCH 037/914] Update setup.py (#99) * Update setup.py Add missing dependency scikit-learn * Update setup.py Versioning issue fix * Added requirements into extras_require --- setup.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 493213d85e..d1110c6c19 100644 --- a/setup.py +++ b/setup.py @@ -18,9 +18,15 @@ "tqdm", # Needed for progress bars "pandas>=1.2.3", # Needed for CLI fine-tuning data preparation tool "pandas-stubs>=1.1.0.11", # Needed for type hints for mypy - "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format + "openpyxl>=3.0.7" # Needed for CLI fine-tuning data preparation tool xlsx format ], - extras_require={"dev": ["black~=21.6b0", "pytest==6.*"]}, + extras_require={ + "dev": ["black~=21.6b0", "pytest==6.*"], + "embeddings": [ + "scikit-learn>=1.0.2", # Needed for embedding utils, versions >= 1.1 require python 3.8 + "tenacity>=8.0.1" + ], + }, python_requires=">=3.7.1", entry_points={ "console_scripts": [ From 3005ee57e2e7b44a80a2f85502d1580d76f2a45d Mon Sep 17 00:00:00 2001 From: Zafer Cavdar Date: Fri, 1 Jul 2022 02:46:13 +0300 Subject: [PATCH 038/914] Fixed args in refresh_from call in ErrorObject (#107) * Fixed args in refresh_from call * Removed unused import --- openai/api_requestor.py | 1 - openai/api_resources/error_object.py | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 2b0982be75..954afc05e0 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -2,7 +2,6 @@ import platform import threading import warnings -from email import header from json import JSONDecodeError from typing import Dict, Iterator, Optional, Tuple, Union from urllib.parse import urlencode, urlsplit, urlunsplit diff --git a/openai/api_resources/error_object.py b/openai/api_resources/error_object.py index 38d8fbf16b..e329fa25e4 100644 --- a/openai/api_resources/error_object.py +++ b/openai/api_resources/error_object.py @@ -18,5 +18,9 @@ def refresh_from( # values here to facilitate generic error handling. values = merge_dicts({"message": None, "type": None}, values) return super(ErrorObject, self).refresh_from( - values, api_key, api_version, organization, response_ms + values=values, + api_key=api_key, + api_version=api_version, + organization=organization, + response_ms=response_ms, ) From a7e3edf031f11d088e47e4dae45564d90e9ec20c Mon Sep 17 00:00:00 2001 From: t-asutedjo <100279393+t-asutedjo@users.noreply.github.com> Date: Wed, 20 Jul 2022 23:30:45 +0200 Subject: [PATCH 039/914] Add deployment_id parameter for azure (#109) * Add deployment_id parameter overload for az * Refactor error messages into engine_api_resource * Add unsupported warning for edit * bump version * Correct optional type annotation * Rebump version * Change unsupported method warning message --- examples/azure/embeddings.ipynb | 2 +- examples/azure/finetuning.ipynb | 2 +- openai/api_resources/abstract/api_resource.py | 3 ++- .../abstract/engine_api_resource.py | 21 +++++++++++++------ openai/api_resources/completion.py | 8 +------ openai/api_resources/deployment.py | 1 - openai/api_resources/edit.py | 16 +++++++------- openai/api_resources/embedding.py | 8 +------ openai/api_resources/model.py | 1 - openai/api_resources/search.py | 8 +------ openai/util.py | 2 +- openai/version.py | 2 +- 12 files changed, 32 insertions(+), 42 deletions(-) diff --git a/examples/azure/embeddings.ipynb b/examples/azure/embeddings.ipynb index d7deefd9de..a3aac70f39 100644 --- a/examples/azure/embeddings.ipynb +++ b/examples/azure/embeddings.ipynb @@ -141,7 +141,7 @@ "metadata": {}, "outputs": [], "source": [ - "embeddings = openai.Embedding.create(engine=deployment_id,\n", + "embeddings = openai.Embedding.create(deployment_id=deployment_id,\n", " input=\"The food was delicious and the waiter...\")\n", " \n", "print(embeddings)" diff --git a/examples/azure/finetuning.ipynb b/examples/azure/finetuning.ipynb index f691980a92..76ed2f97b4 100644 --- a/examples/azure/finetuning.ipynb +++ b/examples/azure/finetuning.ipynb @@ -414,7 +414,7 @@ "source": [ "print('Sending a test completion job')\n", "start_phrase = 'When I go to the store, I want a'\n", - "response = openai.Completion.create(engine=deployment_id, prompt=start_phrase, max_tokens=4)\n", + "response = openai.Completion.create(deployment_id=deployment_id, prompt=start_phrase, max_tokens=4)\n", "text = response['choices'][0]['text'].replace('\\n', '').replace(' .', '.').strip()\n", "print(f'\"{start_phrase} {text}\"')\n" ] diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index 7324401af9..c1091554ef 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -4,6 +4,7 @@ from openai import api_requestor, error, util from openai.openai_object import OpenAIObject from openai.util import ApiType +from typing import Optional class APIResource(OpenAIObject): @@ -110,7 +111,7 @@ def _static_request( ) @classmethod - def _get_api_type_and_version(cls, api_type: str, api_version: str): + def _get_api_type_and_version(cls, api_type: Optional[str] = None, api_version: Optional[str] = None): typed_api_type = ApiType.from_str( api_type) if api_type else ApiType.from_str(openai.api_type) typed_api_version = api_version or openai.api_version diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index 3126725e0c..b480060255 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -13,7 +13,6 @@ class EngineAPIResource(APIResource): - engine_required = True plain_old_data = False def __init__(self, engine: Optional[str] = None, **kwargs): @@ -71,14 +70,24 @@ def create( organization=None, **params, ): - engine = params.pop("engine", None) + deployment_id = params.pop("deployment_id", None) + engine = params.pop("engine", deployment_id) + model = params.get("model", None) timeout = params.pop("timeout", None) stream = params.get("stream", False) headers = params.pop("headers", None) - if engine is None and cls.engine_required: - raise error.InvalidRequestError( - "Must provide an 'engine' parameter to create a %s" % cls, "engine" - ) + + typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] + if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + if deployment_id is None and engine is None: + raise error.InvalidRequestError( + "Must provide an 'engine' or 'deployment_id' parameter to create a %s" % cls, "engine" + ) + else: + if model is None and engine is None: + raise error.InvalidRequestError( + "Must provide an 'engine' or 'model' parameter to create a %s" % cls, "engine" + ) if timeout is None: # No special timeout handling diff --git a/openai/api_resources/completion.py b/openai/api_resources/completion.py index d1a0ec1df4..429597b46e 100644 --- a/openai/api_resources/completion.py +++ b/openai/api_resources/completion.py @@ -3,11 +3,10 @@ from openai import util from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import InvalidRequestError, TryAgain +from openai.error import TryAgain class Completion(EngineAPIResource): - engine_required = False OBJECT_NAME = "completions" @classmethod @@ -20,11 +19,6 @@ def create(cls, *args, **kwargs): """ start = time.time() timeout = kwargs.pop("timeout", None) - if kwargs.get("model", None) is None and kwargs.get("engine", None) is None: - raise InvalidRequestError( - "Must provide an 'engine' or 'model' parameter to create a Completion.", - param="engine", - ) while True: try: diff --git a/openai/api_resources/deployment.py b/openai/api_resources/deployment.py index e7a59d91cd..dbc1df765e 100644 --- a/openai/api_resources/deployment.py +++ b/openai/api_resources/deployment.py @@ -4,7 +4,6 @@ class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResource): - engine_required = False OBJECT_NAME = "deployments" @classmethod diff --git a/openai/api_resources/edit.py b/openai/api_resources/edit.py index 18295c22f4..61f1c36aa5 100644 --- a/openai/api_resources/edit.py +++ b/openai/api_resources/edit.py @@ -1,12 +1,11 @@ import time -from openai import util +from openai import util, error from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import InvalidRequestError, TryAgain +from openai.error import TryAgain class Edit(EngineAPIResource): - engine_required = False OBJECT_NAME = "edits" @classmethod @@ -16,11 +15,12 @@ def create(cls, *args, **kwargs): """ start = time.time() timeout = kwargs.pop("timeout", None) - if kwargs.get("model", None) is None and kwargs.get("engine", None) is None: - raise InvalidRequestError( - "Must provide an 'engine' or 'model' parameter to create an Edit.", - param="engine", - ) + + api_type = kwargs.pop("api_type", None) + typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] + if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType( + "This operation is not supported by the Azure OpenAI API yet.") while True: try: diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py index 883a5e1744..85ede2c088 100644 --- a/openai/api_resources/embedding.py +++ b/openai/api_resources/embedding.py @@ -6,11 +6,10 @@ from openai import util from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import InvalidRequestError, TryAgain +from openai.error import TryAgain class Embedding(EngineAPIResource): - engine_required = False OBJECT_NAME = "embeddings" @classmethod @@ -23,11 +22,6 @@ def create(cls, *args, **kwargs): """ start = time.time() timeout = kwargs.pop("timeout", None) - if kwargs.get("model", None) is None and kwargs.get("engine", None) is None: - raise InvalidRequestError( - "Must provide an 'engine' or 'model' parameter to create an Embedding.", - param="engine", - ) user_provided_encoding_format = kwargs.get("encoding_format", None) diff --git a/openai/api_resources/model.py b/openai/api_resources/model.py index 6db9bb590e..9785e17fe1 100644 --- a/openai/api_resources/model.py +++ b/openai/api_resources/model.py @@ -2,5 +2,4 @@ class Model(ListableAPIResource, DeletableAPIResource): - engine_required = False OBJECT_NAME = "models" diff --git a/openai/api_resources/search.py b/openai/api_resources/search.py index e4b32a1f0f..adc113c1c4 100644 --- a/openai/api_resources/search.py +++ b/openai/api_resources/search.py @@ -2,11 +2,10 @@ from openai import util from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import InvalidRequestError, TryAgain +from openai.error import TryAgain class Search(EngineAPIResource): - engine_required = False OBJECT_NAME = "search" @classmethod @@ -20,11 +19,6 @@ def create(cls, *args, **kwargs): start = time.time() timeout = kwargs.pop("timeout", None) - if kwargs.get("model", None) is None and kwargs.get("engine", None) is None: - raise InvalidRequestError( - "Must provide an 'engine' or 'model' parameter to create a Search.", - param="engine", - ) while True: try: diff --git a/openai/util.py b/openai/util.py index e69fad0903..9cc0ba233e 100644 --- a/openai/util.py +++ b/openai/util.py @@ -40,7 +40,7 @@ def from_str(label): return ApiType.OPEN_AI else: raise openai.error.InvalidAPIType( - "The API type provided in invalid. Please select one of the supported API types: 'azure', 'open_ai'" + "The API type provided in invalid. Please select one of the supported API types: 'azure', 'azure_ad', 'open_ai'" ) diff --git a/openai/version.py b/openai/version.py index ccccb9d9b5..a31140d7da 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.21.0" +VERSION = "0.22.0" From 7884a7b96f43defdd1f18bccfad23d6f336b83d3 Mon Sep 17 00:00:00 2001 From: Ted Sanders <95656834+ted-at-openai@users.noreply.github.com> Date: Tue, 26 Jul 2022 06:35:13 -0700 Subject: [PATCH 040/914] Ted/move examples to openai cookbook (#112) * removes examples (which can now be found in the OpenAI Cookbook repo) * Update numpy requirements due to vuln in 1.21.6 * adds api_type to ErrorObject * formats by Black --- README.md | 44 +- examples/README.md | 7 + examples/azure/embeddings.ipynb | 174 +- examples/azure/finetuning.ipynb | 453 +- examples/codex/backtranslation.py | 190 +- examples/embeddings/Classification.ipynb | 112 +- examples/embeddings/Clustering.ipynb | 242 +- examples/embeddings/Code_search.ipynb | 378 +- examples/embeddings/Get_embeddings.ipynb | 89 +- examples/embeddings/Obtain_dataset.ipynb | 172 +- examples/embeddings/Recommendation.ipynb | 33412 +--------------- examples/embeddings/Regression.ipynb | 91 +- ...emantic_text_search_using_embeddings.ipynb | 167 +- .../User_and_product_embeddings.ipynb | 156 +- examples/embeddings/Visualize_in_2d.ipynb | 124 +- examples/embeddings/Visualize_in_3d.ipynb | 246 +- .../embeddings/Zero-shot_classification.ipynb | 206 +- examples/embeddings/dbpedia_samples.jsonl | 200 - examples/finetuning/answers_with_ft.py | 152 +- .../finetuning-classification.ipynb | 738 +- .../finetuning/olympics-1-collect-data.ipynb | 495 +- .../finetuning/olympics-2-create-qa.ipynb | 733 +- examples/finetuning/olympics-3-train-qa.ipynb | 619 +- examples/semanticsearch/README.md | 30 - examples/semanticsearch/semanticsearch.py | 126 - openai/__init__.py | 5 +- openai/api_resources/abstract/api_resource.py | 20 +- .../abstract/createable_api_resource.py | 8 +- .../abstract/deletable_api_resource.py | 18 +- .../abstract/engine_api_resource.py | 22 +- .../abstract/listable_api_resource.py | 8 +- openai/api_resources/deployment.py | 36 +- openai/api_resources/edit.py | 3 +- openai/api_resources/error_object.py | 2 + openai/api_resources/file.py | 29 +- openai/api_resources/fine_tune.py | 24 +- openai/cli.py | 4 +- openai/tests/test_url_composition.py | 20 +- openai/upload_progress.py | 2 +- openai/util.py | 3 +- setup.py | 3 +- 41 files changed, 338 insertions(+), 39225 deletions(-) create mode 100644 examples/README.md delete mode 100644 examples/embeddings/dbpedia_samples.jsonl delete mode 100644 examples/semanticsearch/README.md delete mode 100755 examples/semanticsearch/semanticsearch.py diff --git a/README.md b/README.md index 0a79a36fcd..ab98ec4403 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ print(completion.choices[0].text) ### Microsoft Azure Endpoints -In order to use the library with Microsoft Azure endpoints, you need to set the api_type, api_base and api_version in addition to the api_key. The api_type must be set to 'azure' and the others correspond to the properites of your endpoint. +In order to use the library with Microsoft Azure endpoints, you need to set the api_type, api_base and api_version in addition to the api_key. The api_type must be set to 'azure' and the others correspond to the properties of your endpoint. In addition, the deployment name must be passed as the engine parameter. ```python @@ -78,8 +78,9 @@ print(search) ``` Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, search and fine-tuning operations. -For a detailed example on how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebook: -[Using Azure fine-tuning](https://github.com/openai/openai-python/blob/main/examples/azure/finetuning.ipynb) +For a detailed example on how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: +* [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) +* [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) ### Microsoft Azure Active Directory Authentication @@ -118,7 +119,18 @@ openai api completions.create -e ada -p "Hello world" ## Example code -Examples of how to use [embeddings](https://github.com/openai/openai-python/tree/main/examples/embeddings), [fine tuning](https://github.com/openai/openai-python/tree/main/examples/finetuning), [semantic search](https://github.com/openai/openai-python/tree/main/examples/semanticsearch), and [codex](https://github.com/openai/openai-python/tree/main/examples/codex) can be found in the [examples folder](https://github.com/openai/openai-python/tree/main/examples). +Examples of how to use this Python library to accomplish various tasks can be found in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). It contains code examples for: + +* Classification using fine-tuning +* Clustering +* Code search +* Customizing embeddings +* Question answering from a corpus of documents +* Recommendations +* Visualization of embeddings +* And more + +Prior to July 2022, this OpenAI Python library hosted code examples in its examples folder, but since then all examples have been migrated to the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). ### Embeddings @@ -140,17 +152,17 @@ model_id = "text-similarity-davinci-001" embedding = openai.Embedding.create(input=text_string, engine=model_id)['data'][0]['embedding'] ``` -An example of how to call the embeddings method is shown in the [get embeddings notebook](https://github.com/openai/openai-python/blob/main/examples/embeddings/Get_embeddings.ipynb). +An example of how to call the embeddings method is shown in this [get embeddings notebook](https://github.com/openai/openai-cookbook/blob/main/examples/Get_embeddings.ipynb). Examples of how to use embeddings are shared in the following Jupyter notebooks: -- [Classification using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Classification.ipynb) -- [Clustering using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Clustering.ipynb) -- [Code search using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Code_search.ipynb) -- [Semantic text search using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Semantic_text_search_using_embeddings.ipynb) -- [User and product embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/User_and_product_embeddings.ipynb) -- [Zero-shot classification using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Zero-shot_classification.ipynb) -- [Recommendation using embeddings](https://github.com/openai/openai-python/blob/main/examples/embeddings/Recommendation.ipynb) +- [Classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Classification_using_embeddings.ipynb) +- [Clustering using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Clustering.ipynb) +- [Code search using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Code_search.ipynb) +- [Semantic text search using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Semantic_text_search_using_embeddings.ipynb) +- [User and product embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/User_and_product_embeddings.ipynb) +- [Zero-shot classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Zero-shot_classification_with_embeddings.ipynb) +- [Recommendation using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Recommendation_using_embeddings.ipynb) For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://beta.openai.com/docs/guides/embeddings) in the OpenAI documentation. @@ -160,11 +172,11 @@ Fine tuning a model on training data can both improve the results (by giving the Examples of fine tuning are shared in the following Jupyter notebooks: -- [Classification with fine tuning](https://github.com/openai/openai-python/blob/main/examples/finetuning/finetuning-classification.ipynb) (a simple notebook that shows the steps required for fine tuning) +- [Classification with fine tuning](https://github.com/openai/openai-cookbook/blob/main/examples/Fine-tuned_classification.ipynb) (a simple notebook that shows the steps required for fine tuning) - Fine tuning a model that answers questions about the 2020 Olympics - - [Step 1: Collecting data](https://github.com/openai/openai-python/blob/main/examples/finetuning/olympics-1-collect-data.ipynb) - - [Step 2: Creating a synthetic Q&A dataset](https://github.com/openai/openai-python/blob/main/examples/finetuning/olympics-2-create-qa.ipynb) - - [Step 3: Train a fine-tuning model specialized for Q&A](https://github.com/openai/openai-python/blob/main/examples/finetuning/olympics-3-train-qa.ipynb) + - [Step 1: Collecting data](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-1-collect-data.ipynb) + - [Step 2: Creating a synthetic Q&A dataset](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-2-create-qa.ipynb) + - [Step 3: Train a fine-tuning model specialized for Q&A](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-3-train-qa.ipynb) Sync your fine-tunes to [Weights & Biases](https://wandb.me/openai-docs) to track experiments, models, and datasets in your central dashboard with: diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000..ffa3b42709 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,7 @@ +# Examples have moved to the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/) + +Looking for code examples? Visit the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/), which shares examples of how to use the OpenAI Python library to accomplish common tasks. + +Prior to July 2022, code examples were hosted in this examples folder; going forward, code examples will be hosted in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). + +This separation will help keep the [OpenAI Python library](https://github.com/openai/openai-python) simple and small, without extra files or dependencies. diff --git a/examples/azure/embeddings.ipynb b/examples/azure/embeddings.ipynb index a3aac70f39..c350e597ac 100644 --- a/examples/azure/embeddings.ipynb +++ b/examples/azure/embeddings.ipynb @@ -4,174 +4,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Azure embeddings example\n", - "In this example we'll try to go over all operations for embeddings that can be done using the Azure endpoints. \\\n", - "This example focuses on finetuning but touches on the majority of operations that are also available using the API. This example is meant to be a quick way of showing simple operations and is not meant as a tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import openai\n", - "from openai import cli" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "In the following section the endpoint and key need to be set up of the next sections to work. \\\n", - "Please go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for the \"Endpoint\" value and one of the Keys. They will act as api_base and api_key in the code below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "openai.api_key = '' # Please add your api key here\n", - "openai.api_base = '' # Please add your endpoint here\n", - "\n", - "openai.api_type = 'azure'\n", - "openai.api_version = '2022-03-01-preview' # this may change in the future" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Deployments\n", - "In this section we are going to create a deployment using the finetune model that we just adapted and then used the deployment to create a simple completion operation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Deployments: Create Manually\n", - "Let's create a deployment using the text-similarity-curie-001 engine. You can create a new deployment by going to your Resource in your portal under \"Resource Management\" -> \"Deployments\"." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### (Optional) Deployments: Create Programatically\n", - "We can also create a deployment using code:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model = \"text-similarity-curie-001\"\n", - "\n", - "# Now let's create the deployment\n", - "print(f'Creating a new deployment with model: {model}')\n", - "result = openai.Deployment.create(model=model, scale_settings={\"scale_type\":\"manual\", \"capacity\": 1})\n", - "deployment_id = result[\"id\"]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### (Optional) Deployments: Retrieving\n", - "Now let's check the status of the newly created deployment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(f'Checking for deployment status.')\n", - "resp = openai.Deployment.retrieve(id=deployment_id)\n", - "status = resp[\"status\"]\n", - "print(f'Deployment {deployment_id} is with status: {status}')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Deployments: Listing\n", - "Now because creating a new deployment takes a long time, let's look in the subscription for an already finished deployment that succeeded." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print('While deployment running, selecting a completed one.')\n", - "deployment_id = None\n", - "result = openai.Deployment.list()\n", - "for deployment in result.data:\n", - " if deployment[\"status\"] == \"succeeded\":\n", - " deployment_id = deployment[\"id\"]\n", - " break\n", - "\n", - "if not deployment_id:\n", - " print('No deployment with status: succeeded found.')\n", - "else:\n", - " print(f'Found a successful deployment with id: {deployment_id}.')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Embeddings\n", - "Now let's send a sample embedding to the deployment." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "embeddings = openai.Embedding.create(deployment_id=deployment_id,\n", - " input=\"The food was delicious and the waiter...\")\n", - " \n", - "print(embeddings)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### (Optional) Deployments: Delete\n", - "Finally let's delete the deployment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(f'Deleting deployment: {deployment_id}')\n", - "openai.Deployment.delete(sid=deployment_id)" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/azure/embeddings.ipynb](https://github.com/openai/openai-cookbook/tree/main/examples/azure/embeddings.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" - }, "kernelspec": { - "display_name": "Python 3.8.10 64-bit", + "display_name": "Python 3.9.9 ('openai')", "language": "python", "name": "python3" }, @@ -185,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/azure/finetuning.ipynb b/examples/azure/finetuning.ipynb index 76ed2f97b4..07aa224e54 100644 --- a/examples/azure/finetuning.ipynb +++ b/examples/azure/finetuning.ipynb @@ -4,453 +4,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Azure Fune tuning example\n", - "In this example we'll try to go over all operations that can be done using the Azure endpoints and their differences with the openAi endpoints (if any).
\n", - "This example focuses on finetuning but touches on the majority of operations that are also available using the API. This example is meant to be a quick way of showing simple operations and is not meant as a finetune model adaptation tutorial.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import openai\n", - "from openai import cli" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "In the following section the endpoint and key need to be set up of the next sections to work.
Please go to https://portal.azure.com, find your resource and then under \"Resource Management\" -> \"Keys and Endpoints\" look for the \"Endpoint\" value and one of the Keys. They will act as api_base and api_key in the code below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "openai.api_key = '' # Please add your api key here\n", - "openai.api_base = '' # Please add your endpoint here\n", - "\n", - "openai.api_type = 'azure'\n", - "openai.api_version = '2022-03-01-preview' # this may change in the future" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Microsoft Active Directory Authentication\n", - "Instead of key based authentication, you can use Active Directory to authenticate using credential tokens. Uncomment the next code section to use credential based authentication:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "from azure.identity import DefaultAzureCredential\n", - "\n", - "default_credential = DefaultAzureCredential()\n", - "token = default_credential.get_token(\"https://cognitiveservices.azure.com/")\n", - "\n", - "openai.api_type = 'azure_ad'\n", - "openai.api_key = token.token\n", - "openai.api_version = '2022-03-01-preview' # this may change in the future\n", - "\n", - "\n", - "openai.api_base = '' # Please add your endpoint here\n", - "\"\"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Files\n", - "In the next section we will focus on the files operations: importing, listing, retrieving, deleting. For this we need to create 2 temporary files with some sample data. For the sake of simplicity, we will use the same data for training and validation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import shutil\n", - "import json\n", - "\n", - "training_file_name = 'training.jsonl'\n", - "validation_file_name = 'validation.jsonl'\n", - "\n", - "sample_data = [{\"prompt\": \"When I go to the store, I want an\", \"completion\": \"apple\"},\n", - " {\"prompt\": \"When I go to work, I want a\", \"completion\": \"coffe\"},\n", - " {\"prompt\": \"When I go home, I want a\", \"completion\": \"soda\"}]\n", - "\n", - "print(f'Generating the training file: {training_file_name}')\n", - "with open(training_file_name, 'w') as training_file:\n", - " for entry in sample_data:\n", - " json.dump(entry, training_file)\n", - " training_file.write('\\n')\n", - "\n", - "print(f'Copying the training file to the validation file')\n", - "shutil.copy(training_file_name, validation_file_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Files: Listing\n", - "List all of the uploaded files and check for the ones that are named \"training.jsonl\" or \"validation.jsonl\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print('Checking for existing uploaded files.')\n", - "results = []\n", - "files = openai.File.list().data\n", - "print(f'Found {len(files)} total uploaded files in the subscription.')\n", - "for item in files:\n", - " if item[\"filename\"] in [training_file_name, validation_file_name]:\n", - " results.append(item[\"id\"])\n", - "print(f'Found {len(results)} already uploaded files that match our names.')\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Files: Deleting\n", - "Let's now delete those found files (if any) since we're going to be re-uploading them next." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(f'Deleting already uploaded files.')\n", - "for id in results:\n", - " openai.File.delete(sid = id)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Files: Importing & Retrieving\n", - "Now, let's import our two files ('training.jsonl' and 'validation.jsonl') and keep those IDs since we're going to use them later for finetuning.
\n", - "For this operation we are going to use the cli wrapper which does a bit more checks before uploading and also gives us progress. In addition, after uploading we're going to check the status our import until it has succeeded (or failed if something goes wrong)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import time\n", - "\n", - "def check_status(training_id, validation_id):\n", - " train_status = openai.File.retrieve(training_id)[\"status\"]\n", - " valid_status = openai.File.retrieve(validation_id)[\"status\"]\n", - " print(f'Status (training_file | validation_file): {train_status} | {valid_status}')\n", - " return (train_status, valid_status)\n", - "\n", - "#importing our two files\n", - "training_id = cli.FineTune._get_or_upload(training_file_name, True)\n", - "validation_id = cli.FineTune._get_or_upload(validation_file_name, True)\n", - "\n", - "#checking the status of the imports\n", - "(train_status, valid_status) = check_status(training_id, validation_id)\n", - "\n", - "while train_status not in [\"succeeded\", \"failed\"] or valid_status not in [\"succeeded\", \"failed\"]:\n", - " time.sleep(1)\n", - " (train_status, valid_status) = check_status(training_id, validation_id)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Files: Downloading\n", - "Now let's download one of the files, the training file for example, to check that everything was in order during importing and all bits are there." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(f'Downloading training file: {training_id}')\n", - "result = openai.File.download(training_id)\n", - "print(result)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Finetune\n", - "In this section we are going to use the two training and validation files that we imported in the previous section, to train a finetune model." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Finetune: Adapt\n", - "First let's create the finetune adaptation job." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "create_args = {\n", - " \"training_file\": training_id,\n", - " \"validation_file\": validation_id,\n", - " \"model\": \"curie\",\n", - " \"compute_classification_metrics\": True,\n", - " \"classification_n_classes\": 3\n", - "}\n", - "resp = openai.FineTune.create(**create_args)\n", - "job_id = resp[\"id\"]\n", - "status = resp[\"status\"]\n", - "\n", - "print(f'Fine-tunning model with jobID: {job_id}.')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Finetune: Streaming\n", - "While the job runs, we can subscribe to the streaming events to check the progress of the operation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import signal\n", - "import datetime\n", - "\n", - "def signal_handler(sig, frame):\n", - " status = openai.FineTune.retrieve(job_id).status\n", - " print(f\"Stream interrupted. Job is still {status}.\")\n", - " return\n", - "\n", - "print('Streaming events for the fine-tuning job: {job_id}')\n", - "signal.signal(signal.SIGINT, signal_handler)\n", - "\n", - "events = openai.FineTune.stream_events(job_id)\n", - "try:\n", - " for event in events:\n", - " print(f'{datetime.datetime.fromtimestamp(event[\"created_at\"])} {event[\"message\"]}')\n", - "\n", - "except Exception:\n", - " print(\"Stream interrupted (client disconnected).\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Finetune: Listing and Retrieving\n", - "Now let's check that our operation was successful and in addition we can look at all of the finetuning operations using a list operation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "status = openai.FineTune.retrieve(id=job_id)[\"status\"]\n", - "if status not in [\"succeeded\", \"failed\"]:\n", - " print(f'Job not in terminal status: {status}. Waiting.')\n", - " while status not in [\"succeeded\", \"failed\"]:\n", - " time.sleep(2)\n", - " status = openai.FineTune.retrieve(id=job_id)[\"status\"]\n", - " print(f'Status: {status}')\n", - "else:\n", - " print(f'Finetune job {job_id} finished with status: {status}')\n", - "\n", - "print('Checking other finetune jobs in the subscription.')\n", - "result = openai.FineTune.list()\n", - "print(f'Found {len(result)} finetune jobs.')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Finetune: Deleting\n", - "Finally we can delete our finetune job.
\n", - "WARNING: Please skip this step if you want to continue with the next section as the finetune model is needed. (The delete code is commented out by default)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# openai.FineTune.delete(sid=job_id)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Deployments\n", - "In this section we are going to create a deployment using the finetune model that we just adapted and then used the deployment to create a simple completion operation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Deployments: Create\n", - "Let's create a deployment using the fine-tune model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#Fist let's get the model of the previous job:\n", - "result = openai.FineTune.retrieve(id=job_id)\n", - "if result[\"status\"] == 'succeeded':\n", - " model = result[\"fine_tuned_model\"]\n", - "\n", - "# Now let's create the deployment\n", - "print(f'Creating a new deployment with model: {model}')\n", - "result = openai.Deployment.create(model=model, scale_settings={\"scale_type\":\"manual\", \"capacity\": 1})\n", - "deployment_id = result[\"id\"]\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Deployments: Retrieving\n", - "Now let's check the status of the newly created deployment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(f'Checking for deployment status.')\n", - "resp = openai.Deployment.retrieve(id=deployment_id)\n", - "status = resp[\"status\"]\n", - "print(f'Deployment {deployment_id} is with status: {status}')\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Deployments: Listing\n", - "Now because creating a new deployment takes a long time, let's look in the subscription for an already finished deployment that succeeded." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print('While deployment running, selecting a completed one.')\n", - "deployment_id = None\n", - "result = openai.Deployment.list()\n", - "for deployment in result.data:\n", - " if deployment[\"status\"] == \"succeeded\":\n", - " deployment_id = deployment[\"id\"]\n", - " break\n", - "\n", - "if not deployment_id:\n", - " print('No deployment with status: succeeded found.')\n", - "else:\n", - " print(f'Found a successful deployment with id: {deployment_id}.')\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Completions\n", - "Now let's send a sample completion to the deployment." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print('Sending a test completion job')\n", - "start_phrase = 'When I go to the store, I want a'\n", - "response = openai.Completion.create(deployment_id=deployment_id, prompt=start_phrase, max_tokens=4)\n", - "text = response['choices'][0]['text'].replace('\\n', '').replace(' .', '.').strip()\n", - "print(f'\"{start_phrase} {text}\"')\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Deployments: Delete\n", - "Finally let's delete the deployment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(f'Deleting deployment: {deployment_id}')\n", - "openai.Deployment.delete(sid=deployment_id)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Thank you" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/azure/finetuning.ipynb](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" - }, "kernelspec": { - "display_name": "Python 3.8.10 64-bit", + "display_name": "Python 3.9.9 ('openai')", "language": "python", "name": "python3" }, @@ -464,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/codex/backtranslation.py b/examples/codex/backtranslation.py index 8289a73ade..6fff98bb2d 100644 --- a/examples/codex/backtranslation.py +++ b/examples/codex/backtranslation.py @@ -1,189 +1 @@ -from typing import List, Union - -from smokey import Smokey - -import openai - - -def get_candidates( - prompt: str, - stop: List[str], - temperature: float, - priming_prefix: str, - engine: str, - n: int = 5, -) -> List[str]: - """ - Generate N candidate completions based on the prompt, generated with a specific temperature. - - :param prompt: The prompt to start the conversation with. - :param stop: A list of tokens that indicate the end of the generation. - :param temperature: The temperature of the generation. - :param priming_prefix: The prefix to use for the priming. - :param engine: The engine to use for the generation. - :param n: The number of completions to generate. - :return: A list of completions. - """ - response = openai.Completion.create( - engine=engine, - prompt=prompt, - temperature=temperature, - max_tokens=150, - top_p=1, - frequency_penalty=0, - presence_penalty=0, - stop=stop, - n=n, - ) - responses = [priming_prefix + choice.text for choice in response.choices] - return responses - - -def rindex(lst: List, value: str) -> int: - """ - Return the index of the last occurence of a value in a list. - - :param lst: The list to search in. - :param value: The value to search for. - :return: The index of the last occurence of the value. - """ - try: - return len(lst) - lst[::-1].index(value) - 1 - except ValueError: - raise ValueError(f"Answer start token `{value}` not found in the eval template") - - -def eval_candidate( - candidate_answer: str, - original_instruction: str, - eval_template: str, - answer_start_token: str, - engine: str, -) -> float: - """ - Evaluate a candidate answer by calculating the average log probability - of the original instruction, given the candidate answer with a specific - evaluation template, aimed at reconstructing the original instruction. - - :param candidate_answer: The candidate answer to evaluate. - :param original_instruction: The original instruction. - :param eval_template: The template to use for the evaluation. - :param answer_start_token: The token to use to indicate the start of the answer. - :param engine: The engine to use for the evaluation. - :return: The evaluation of the candidate answer. - """ - response = openai.Completion.create( - engine=engine, - prompt=eval_template.format(candidate_answer, original_instruction), - temperature=0, - max_tokens=0, - top_p=1, - frequency_penalty=0, - presence_penalty=0, - logprobs=1, - echo=True, - ) - - answer_start = rindex( - response["choices"][0]["logprobs"]["tokens"], answer_start_token - ) - logprobs = response["choices"][0]["logprobs"]["token_logprobs"][answer_start + 1 :] - return sum(logprobs) / len(logprobs) - - -def backtranslation( - prompt_template: str, - additional_info: str, - instruction: str, - eval_template: str, - priming_prefix: str = "SELECT", - stop1: List[str] = ["#", ";"], - answer_start_token: str = "--", - n: int = 5, - temperature: float = 0.5, - return_all_results: bool = False, - engine: str = "davinci-codex", -) -> Union[str, List[str, float]]: - """ - Generate a number of SQL queries given a natural language instruction, - and pick the best one based on the average log probability of explaining the - candidate SQL query with the exact original instruction, when prompted for - a natural language explanation of the candidate SQL query. - - :param prompt_template: The template to use for the prompt to generate SQL. - :param additional_info: Additional information to include in the prompt - (SQL Tables, and their properties). - :param instruction: The instruction in natural language. - :param eval_template: The template to use for the evaluation. - :param priming_prefix: The prefix to use for the priming of the SQL query. - :param stop1: A list of tokens that indicate the end of the generation. - :param answer_start_token: The token to use to indicate the start of the - natural answer. - :param n: The number of candidates to generate. - :param temperature: The temperature of the generation. - :param return_all_results: Whether to return all results or just the best one. - :param engine: The engine to use for the generation and evaluation. - :return: The best SQL query, or a list of all scored generated SQL queries. - """ - prompt_template = prompt_template.format( - additional_info, instruction, priming_prefix - ) - - candidates = [] - responses = get_candidates( - prompt_template, stop1, temperature, priming_prefix, engine=engine, n=n - ) - for i in range(n): - quality = eval_candidate( - responses[i], - instruction, - eval_template, - answer_start_token, - engine=engine, - ) - candidates.append((responses[i], quality)) - - candidates.sort(key=lambda x: x[1], reverse=True) - if return_all_results: - return candidates - return candidates[0][0] - - -def main( - nl_query: str = "Return the name of each department that had more than 10 employees in June 2021", - eval_template: str = "{};\n-- Explanation of the above query in human readable format\n-- {}", - table_definitions: str = "# Employee(id, name, department_id)\n# Department(id, name, address)\n# Salary_Payments(id, employee_id, amount, date)\n", - prompt_template: str = "### Postgres SQL tables, with their properties:\n#\n{}#\n### {}\n{}", - n: int = 3, - temperature: float = 0.3, - engine: str = "davinci-codex", -): - """ - Generate a number of SQL queries given a natural language instruction, - and pick the best one based on the highest backtranslation score. - - :param nl_query: The natural language query. - :param eval_template: The template to use for the evaluation. - :param table_definitions: The definitions of the tables used in the query. - :param prompt_template: The template to use for the prompt to generate SQL. - :param n: The number of candidates to generate. - :param temperature: The temperature of the generation. - :param engine: The engine to use for the generation and evaluation. - :return: The best SQL query, or a list of all scored generated SQL queries. - """ - - result = backtranslation( - prompt_template, - table_definitions, - nl_query, - eval_template, - priming_prefix="SELECT", - temperature=temperature, - n=n, - engine=engine, - ) - print(result) - - -if __name__ == "__main__": - Smokey(main) +# this example has moved to https://github.com/openai/openai-cookbook/blob/main/examples/Backtranslation_of_SQL_queries.py diff --git a/examples/embeddings/Classification.ipynb b/examples/embeddings/Classification.ipynb index 54ad13fe89..b44d6a76a5 100644 --- a/examples/embeddings/Classification.ipynb +++ b/examples/embeddings/Classification.ipynb @@ -4,111 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Classification using the embeddings\n", - "\n", - "In the classification task we predict one of the predefined categories given an input. We will predict the score based on the embedding of the review's text, where the algorithm is correct only if it guesses the exact number of stars. We split the dataset into a training and a testing set for all the following tasks, so we can realistically evaluate performance on unseen data. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb).\n", - "\n", - "In the following example we're predicting the number of stars in a review, from 1 to 5." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " precision recall f1-score support\n", - "\n", - " 1 0.82 0.67 0.74 21\n", - " 2 0.50 0.50 0.50 6\n", - " 3 1.00 0.46 0.63 13\n", - " 4 0.75 0.35 0.48 17\n", - " 5 0.88 1.00 0.93 143\n", - "\n", - " accuracy 0.86 200\n", - " macro avg 0.79 0.60 0.66 200\n", - "weighted avg 0.86 0.86 0.84 200\n", - "\n" - ] - } - ], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "from sklearn.ensemble import RandomForestClassifier\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.metrics import classification_report, accuracy_score\n", - "\n", - "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", - "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", - "\n", - "X_train, X_test, y_train, y_test = train_test_split(list(df.babbage_similarity.values), df.Score, test_size = 0.2, random_state=42)\n", - "\n", - "clf = RandomForestClassifier(n_estimators=100)\n", - "clf.fit(X_train, y_train)\n", - "preds = clf.predict(X_test)\n", - "probas = clf.predict_proba(X_test)\n", - "\n", - "report = classification_report(y_test, preds)\n", - "print(report)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the model has learnt to distinguish between the categories decently. 5-star reviews show the best performance overall, and this is not too surprising, since they are the most common in the dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "RandomForestClassifier() - Average precision score over all classes: 0.93\n" - ] - }, - { - "data": { - "image/png": "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I+9ynph431RPPvlk1qxZw1tvvcXixYv55S9/2ahNn3/+Oddccw0Ad911F/Pnt1+SKYPBELvohsNh/P5jgVc2m61W24ULF7Jq1Sq+++47LrzwQkATTL/5zW9i9tXkiy++4O233+a3v/0ts2bN4rbbbms3uzsDIQTUESQyECDw3VFCrnKtjcmEsXdv9JFhOEX3xGQykZGRQWlpKWVlZWSoOCiFIqFQnph2Zv/+/fTo0YOrr76aq666ii+++ILJkyfzn//8h+LiYkKhEM899xwzZsxgypQpbN68mc2bN7dawJxyyim8+OKLlETylUSHkwYMGMCmTZsAbZiqqQDFCy+8kOeff55Vq1axcOFCAObMmcOTTz6J2+0G4PDhwzGPTUpKCj/60Y9YsmQJX3yRnMXjavpigiUl+PLzNQEjBIacHMxDhigBowDAbDaTlpaGz+ejvLxcTTtWKBKILueJiTeffPIJ999/P0ajEbvdztNPP02vXr247777+MEPfoCUknnz5nH22Wcft68NGzawYMECysrKePPNN/n973/PN998U6tNXl4et956KzNmzECv13PiiSeyfPlyrr76as4++2zGjh3L3Llz63lf6vZRWVlJbm4uvXr1AuC0005j+/btTJs2DdACg//1r3+xa9culixZgk6nw2g08uijj7Zhb8UPodNETLi6mnB1NQD61FQMvXqhM5niaZoiAbFarYRCISoqKtDpdHH3+CoUCo0uUTtp+/btjBgxIk4WdTzRVPnRKsyKthOqqsK/Zw8AwmjE2KsXutTUDh8qUGUHmknh9kjZgeGxsgNR4nm+V1RU4Ha7SUlJwdlEeYlkZ8N3G7jivSuY2GMiT819Kt7mKBKdfZPBuwH6f96qwN5uXztJoWgpOosFXWoqmEyYcnJUtl1Fs3A4HAghqKysREpJmiozoVDEFSViFN0SodNh7NuXYDCIbGK2kkJRl9SIx66iogIpJenp6UrIKBRxQo1NKLot7VEMUtE9sdvtOJ1OvF4vpaWl6hhSKOKEEjGKbosQAp1Op/J/KFqFzWYjPT0dv99PSUmJOo4UijigREwSoDwGHYcQAiml2reKVmG1WklPTycQCCgho1DEASViuil6vT5W32nhwoVUVVW1uc/bbruN1atXN7p+2bJlPP30023eTnsSne0VvflEa0zt27ePUaNGNfiZgoICzjzzzM4xsB3x+XxceOGFDBkyhClTprBv374G2z300EOMGjWKvLw8Hnzwwdj7v/vd7xgzZgzjxo3jtNNO48iRIwC8+eabSZfwsD2xWCxkZGQQDAZjuaAUCkXnoERMAtORF0Or1crmzZvZunUrJpOJZcuW1VrfmvpTd911F6eeemqj66+99louu+yyFvd7PNpSK0sIEfPGNJe//OUvXH311a3eZnPoiKKD//znP0lPT2fXrl384he/aLAQ6datW3niiSdYv349X331FW+++Sa7du0CYMmSJWzZsoXNmzdz5plnctdddwEwb9483njjjXYRwsmK2WwmMzOTcDhMSUlJtywaqVDEgw4VMUKIuUKIb4UQu4QQSxtpc4EQYpsQ4hshxL870p6O5JxzzmHChAnk5eXx+OOPA5rnYcmSJbE2y5cv5/rrrwfgX//6F5MnT2bcuHFcc801McFit9v51a9+xdixY1m3bh133XUXkydP5sQTT+Taa6+N3Ww3bNgQ+1e8ZMmSmNcgFAqxZMmSWPXpxx577Li2T58+nV27dtWrcN1UX3/84x8ZPXo0Y8eOZelS7aetWTl76dKljBw5kjFjxnDTTTcBtStkb968malTpzJmzJhYQj+AmTNncvPNNzN58mSGDRsWqwJel5kzZ3LjjTcyceJEHnroITZt2sSMGTOYMGECc+bMoaCgAIBdu3Zx6qmnMnbsWMaPH8/u3btxu93MmjWL8ePHM3r0aN58803C4XCzhcxLL73E3LlzAc1jM336dMaPH8/48eP59NNPAS3pYU1vzfXXX8/y5csBGD5nDjffdhujR49m8uTJMZGwePFirr32WqZMmcKvf/3rBm1vC6+99hqLFi0C4Pzzz+fDDz+s9523b9/OlClTSElJwWAwMGPGDF5++WVAm14cxePxxIY5hRDMnDmTN998s032JTvREgVKyCgUnUg0HqC9H4Ae2A0MAkzAV8DIOm2GAl8C6ZHXOcfrd8KECbIu27Ztq/deZ1NSUiKllLKqqkrm5eXJ4uJiWVhYKAcPHhxrM3fuXLl27Vq5bds2eeaZZ0q/3y+llPK6666TK1askFJKCciVK1fW69fn88lLLrlEvv7661JKKfPy8uSnn34qpZTy5ptvlnl5eVJKKR977DH5+9//XkoppdfrlRMmTJB79uypZ6/NZpNSShkIBOT8+fPl3//+d/nxxx/LlJSUWPvG+nr77bfltGnTpMfjqWXjokWL5IsvviiLi4vlsGHDZDgcllJKWVZWJqWU8vbbb5f333+/lFLK0aNHy08++URKKeXvfvc7+fOf/1xKKeWMGTPkL3/5SymllG+99ZacNWtWg/t7xowZ8rrrrpNSSun3++W0adNkYWGhlFLK559/Xl5++eVSSiknT54sX375ZSmllNXV1dLj8chAICBdLpeUUsqioiI5ePBg6fV6ZSgUiu2XvXv3xvZpTfbs2SPHjx8fe+3xeGR1dbWUUsqdO3fK6PH58ccfy3nz5sXa/fSnP5VPPfWU9O3fL/v17i3v+u1vpZRSrlixItZu0aJFct68eTIYDDZqe12+//3vy7Fjx9Z7fPDBB/Xa5uXlyYMHD8ZeDxo0SBYVFdVqs23bNjl06FBZXFwsPR6PnDp1qrz++utj62+55RbZp08fmZeXF9vfUkr5r3/9q1a7NnN0m5SHv5DSX1VvVSKc703h9/vld999JwsKCqTP54u3Oa1ifcF6OWr5KLn4ncXxNkWRDOydJOV2pKz6vFUfBzbKVmqNjswTMxnYJaXcAyCEeB44G9hWo83VwN+klGURQVXY5q3u6KB8DcOb/pf+17/+lVdeeQWAgwcPkp+fz9SpUxk0aBCfffYZQ4cOZceOHZx00kn87W9/Y9OmTUyaNAmA6upqcnJyAC1W5bzzzov1+/HHH/OnP/0Jj8dDWVkZo0aNYvr06VRWVsZKAlx88cWxf8Hvv/8+W7ZsiXlEXC4X+fn5sarVUaqrqxk3bhygeWKuvPJKPv3001oVrhvra/Xq1Vx++eWkpKQA9StnO51OLBYLV155JWeeeWa9+BGXy0V5eTkzZswAYNGiRbGaTQDnnnsuABMmTGg0bgOIFav89ttv2bp1K7NnzwY0b1SvXr2orKzk8OHDLFiwANBiFwACgQC33HILa9asQafTxepCRUsuNEVBQQHZ2dmx14FAgOuvv57Nmzej1+vZuXPncfsAuOj88wH44Q9/yC9+8YvY+wsXLkSv1zdqe10a81S1lhEjRnDzzTdz2mmnYbPZGDduHPoaiQDvuece7rnnHv7whz/wyCOPcOeddwKQk5MTi5Hp7hiNRjIzMyktLaWkpASn0xk7VxQKRfvSkSImFzhY4/UhYEqdNsMAhBD/Q/Pc3CGlfLcDbeoQPvnkE1avXs26detISUlh5syZeL1eAC666CJeeOEFhg8fzoIFC2LxF4sWLeIPf/hDvb4sFkvspuH1evnJT37Cxo0b6dmzJ7///e9j/TaGlJKHH36YOXPmNNkuGhNTl5o1lhrr67333muyb4PBwPr16/nwww9ZtWoVjzzyCB999FGTn6mJ2WwGNEEXdclffvnlfPnll/Tu3Zu33367lq1SSvLy8li3bl2tfiorKxvs/9lnn6WoqIhNmzZhNBoZMGAAPp+vWcNJVqu11m/wf//3f/To0YOvvvqKcDgcExs1q4gD9X63mlK7ZqK0pmpcNURU0NblgQceqBeflJuby8GDB+nTpw/BYBCXy0VmZma9z1555ZVceeWVANxyyy306dOnXptLLrmEM844IyZivF4vVqu1XrvuisFgICsri7KyMsrLywkGg7EkeQqFov2Id2CvAW1IaSbwQ+AJIURa3UZCiB8LITYKITYWFRU13eNw2TGPJnC5XKSnp5OSksKOHTv47LPPYusWLFjAa6+9xnPPPcdFF10EwKxZs1i1ahWFhZrjqbS0lP3799frN3rjy8rKwu12x2IT0tLSSE1N5fPPPwfg+eefj31mzpw5PProo7Gq1Tt37sTj8TS9zxqhsb5mz57NU089FQvkjFbOjuJ2u3G5XJxxxhn83//9H1999VWt9U6nk/T09JgX4Zlnnol5ZRrjqaeeYvPmzTEBU5MTTjiBoqKimIgJBAJ88803pKam0qdPH1599VVAm51TVVWFy+UiJycHo9HIxx9/zP79+9HpdM0SMcOGDavlHXK5XPTq1QudTsczzzwTi23q378/27Zti1U+/vDDD2v180LEa7dy5cqYR60mjdlel7Vr18Yqodd8NBRgPX/+fFasWAHAqlWrOOWUUxq8qUaPywMHDvDyyy9z8cUXA5Cfnx9r89prrzF8+PDY6507dzY6m6u7otPpyMjIwGaz4Xa7KS0tVVOwFV2czj++O9ITcxjoW+N1n8h7NTkEfC6lDAB7hRA70UTNhpqNpJSPA4+DVgCywyxuJXPnzmXZsmWMGDGCE044galTp8bWpaenM2LECLZt28bkyVphrJEjR3L33Xdz2mmnEQ6HMRqN/O1vf6N///61+k1LS+Pqq69m1KhR9OjRgwkTJsTW/fOf/+Tqq69Gp9MxY8aMWFXdq666in379jF+/HiklGRnZ8duhC2l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bBEREcbtN954Q997771648aN+mc/+5meOHGi7tGjh3a5XPqRRx7Rqampunfv3nrZsmXGYxYtWqR79eqlU1JS9OOPP6611nrGjBn6ww8/1Fpr/fjjj+urrrpK9+rVSz/00ENaa62ffvpp/eKLL2qttf7+++/14MGDde/evfWUKVN0Tk6O1lrrkSNH6scee0wPHDhQ9+jRQ3/99ddVln/kyJH6gQce0AMGDNBLlizRO3fu1CNGjND9+/fX1157rT516pTWWuuDBw/qsWPH6pSUFN2vXz996NAhXVBQoMeMGaP79eune/XqpdesWVPpuBw9elQnJydXet6JEydqk8mk+/Tpo99//3199OhRPXr0aN27d289ZswYfezYMeNY3HPPPXrQoEHG3+/jcrn0ww8/rJOTk3Xv3r31q6++avxNO3bs0FprPXfuXD1gwADds2dPvWDBAuOxvuPau3dv/fDDD2uttV61apVOTk7WKSkpevjw4dW95JW4XC5tt9v12bNndefOnfXJkycDetwTTzyh33nnHeN+dWW99NJLdWZmptZa6x07duiRI0dqrb3vg9tuu00PGTJEd+/eXS9fvlxrrY3334033KC7X3qpLty7t8rjVFd//vOf9d13323cv/vuu/Wf//znarf3eDy6U6dO+sCBA1rr6t8TWmudk5NT7brm8P8eVHLStX46WuuXegX8kO2nt+teK3rpmetmNmDBqldaWqpzcnL0qVOn9MmTJ3VGRobOz8/XTqezScoj6knpD1r/GKn1PrTO/l9jMbBT1zETBPpz9nFgDvBv4B7gM+D3Fx+hWh6Xy8W6deu4/vrrAfjuu+/Ys2cPXbt2Zfny5cTExLBjxw7sdjvDhg3j2muvZf/+/axdu5Zt27YZE/P5y87OZvXq1ezbtw+Xy1XlbNJ33HEHS5cuZeTIkSxYsICFCxfy8ssvG2Xavn07n332GQsXLmTDhg1Vlt3hcLBz506cTicjR45k7dq1JCQk8MEHH/DUU0/xdtnkgfPmzWPq1KnGXE42m43Vq1cTHR1NVlYWQ4YMYdKkSQFVBX/yySdERkYav8YnTpzIjBkzmDFjBm+//Tb3338/a9asAbyzJG/ZsqVSjc3y5ctJT09n165dxgSQFT333HO0adMGt9vN2LFj2b17Nx07dmT16tXs378fpRS5ubmAtwZs/fr1dOzY0VgWiFOnTjFhwgQOHz7MCy+8QIcOHQJ63ObNm/n5z39eY1lrGwhv9+7dfPvttxQVFdGvXz9jbqXvvvuO3WlpdHC5eOuDD2o9Ti+++GK5GiafESNG8Oqrr5ZbdvLkSTp37mzc79SpEydPnqy2jN988w1JSUn06NHDWHb06FH69etHdHQ0v/71r40JP+Pi4rDb7WRnZ5ebfFO0Dr65mjweD6WlpZSUlFBQUEBBQQFWq9WooZGzm4JMSE9o9w6cmgZnH4HQ/hA+4qJ2WWuIUUqZgR+01lcCb13UszWC3it7N8h+/z3j3zWuLykpMSb7Gz58OLNnz2bLli0MGjSIrl27AvD3v/+d3bt3GzMQ5+XlcfDgQTZs2MCdd95p9KGpOLNwTEwMoaGhzJ49m/HjxzNxYvn5OPPy8sjNzTUm0ZsxYwbTpk0z1t90k3cS8gEDBlSaFNDfrbfeCsCPP/7Inj17GDduHOCds6l9+/YUFBRw8uRJpk6dCnjnSwFwOp08+eSTfP3115hMJk6ePElGRkadJu/bunUrH3/snVf09ttv57HHHjPWTZs2rcoPrQ0bNjB37lyjiani8QNYtWoVy5cvx+Vycfr0afbu3UvPnj2N43rjjTdy443eYe6HDRvGzJkzueWWW4xjF4jOnTvzr3/9i+PHjzNt2jSmTZtGUlLtHdhOnz5NQkJCjWWtLcRMnjzZ+GAfPXo027dvJzY21nj/2Q8e5KutW/nlI4/UeJweffRRHn300YD/5gvxl7/8pVxYa9++PcePH6dt27akpaUxZcoUfvjhB6KjowFITEzk1KlTEmJaMZPJRHh4OOHh4bjdbiPQ5Ofnk5+fT0hICGFhYYSGhsqZgcEi+mYofQRylkD28w0fYrTWbqXUj0qpS7TWMrlLNfz7xPiLiDjfeUlrzdKlS7nuuuvKbbN+fc1D7lgsFrZv386XX37JqlWreOONN9i4cWPAZQsJ8Z6R4DsVGODOO+/k+++/p0OHDnz22Wflyqq1Jjk5ma1bt5bbT1U1QADvvfcemZmZpKWlYbVa6dKlC6WlpQGXL1D+x/JCHD16lCVLlrBjxw7i4uKYOXMmpaWl5Y7rRx99xGuvvcZXX33FsmXL2LZtG59++ikDBgwgLS0t4C9Sk8lEp06dSE5O5h//+Ae33FJ7B9SwsDDjeFVXVvC+D3zDuFc8vhVrvXz3L/SYXUhNTMeOHdm0aZNx/8SJE4waNarK/bpcLj7++GPS0tKMZb5f2+AN2JdddhkHDhwwOmKXlpYSFhZW5f5E6+MbXyYiIgKXy0VJSQklJSXk5uailCIkJMToXyOBppmLnOwNMZ7Ci95VoM1JccAPSqntQJFvodZ60kWXoJ7VVmPSlK677jreeOMNxowZg9Vq5cCBA3Ts2JFx48bx7LPPMn36dKM5yf9XcmFhIcXFxUyYMIFBgwYZZ3P4xMTEEBcXxzfffMPw4cN59913jVqZ6rzzTuWZaX2uuOIKMjMz2bp1K1dffTVOp5MDBw6QnJxMp06dWLNmDVOmTMFut+N2u8nLyyMxMRGr1crGjRs5dqyKM0oCNHToUN5//31uv/123nvvPaN5oSbjxo3jzTffZPTo0UYzif/xy8/PJyIigpiYGDIyMli3bh2jRo0qd1yHDRtmdKw9fPgwgwcPZvDgwaxbt46ffvqp1hBz4sQJ2rZtS1hYGPn5+WzevJkHHnggoL/5qquu4tChQ3Tp0qXasgJ06dKFtLQ0xo8fz1//+tdy+1i7di1PPPEERUVFbNq0iUWLFnHgwIFy24wdOrTG4wQXVhNz3XXX8eSTT3Lu3DnAW9P4m9/8psptN2zYwJVXXkmnTp2MZZmZmbRp0waz2cyRI0c4ePCg8RporTlz5gxdunQJqCyidbFYLERFRREVFYXT6TQCTWlpKUopbDab1NC0EoGGmPkNWopWYs6cOaSnp9O/f3+01iQkJLBmzRquv/56du3aRWpqKjabjQkTJvD8888bjysoKGDy5MlGH5SqTkNeuXIlc+fOpbi4mG7dutUYUmpjs9n46KOPuP/++415Tx588EGSk5N59913ueeee1iwYAFWq5UPP/yQ6dOnM3HiRHr37k1qaipXXnllnZ976dKl3Hnnnbz44oskJCQE9HfMmTOHAwcOkJKSgtVq5a677uK+++4z1vfp04d+/fpx5ZVX0rlzZ4YN804L739ctda89NJLgPeL/ODBg2itGTt2LH361H4mzL59+3j44YdRSqG15uGHHyY5OTmgAfBuuOEGNm3axDXXXFNtWQGefvppZs+ezfz58yvVeKSkpDB69GiysrKYP38+HTp0qBRi7pw2jaN5edUepwvVpk0b5s+fz8CB3lE4FyxYYISiOXPmMHfuXKNW5f333y/XlATw9ddfG+8jk8nEsmXLjMenpaUxZMiQJj8LTTR/VqsVq9VKdHQ0DoeD0tJSSktLjf5s/jU00oem5VG6honrlFKhwFygO95OvX/QWjfpTF6pqal6586d5Zbt27ePq666qolK1LicTidKKflwb+a01kbTncViqbGTc0lJCaNHj2bz5s11+pB95plniIyM5JFHHqlyvcdux37wIMpmI/Tyyy94/03hgQceYNKkSYwdW3k8zdb0/14vzh2DV1Ig5hJ4KLCa6h1ndjBr/SxSk1J55/q6/yBqSk6n0+hD4/tftNlsRqBpKZ+hr776Km+88QY9e/bk1KlTfPfddzz33HPVfh5UJScnh1tvvZX09HS6dOnCqlWrKg2VAHD8+HHmzJnDTz/9hFKKzz77rG61pcX/hOPDIWwYXPpPlFJpWus6DehV26u4EnAC3wDjgZ5AYHXkQrRiSqly0xHUFE7CwsJYuHAhJ0+e5JJLLmnEUjZfvXr1qjLACBEoXw1NVFSU0YemtLTU6BRssViMWhqbzRa0A+u9/vrrbNiwAZvNxrFjx4yzOS/EokWLGDt2LPPmzWPRokUsWrSIxYsXV9rujjvu4KmnnmLcuHEUFhY2i6a62kJMT611bwCl1B+A7Q1fJCFaBpPJhMlkwu12GxNFVqdiZ+8L8cwzz9T5sc3VXXfd1dRFEC2Ifx8a31lOvpGCi4qKMJlMRqAJCQlpFl/OgZg7dy5Hjhxh/PjxzJo1i4ceeohPP/30gvezdu1ao5P+jBkzGDVqVKUQs3fvXlwul3HWamRk5EWXvz7UFmKcvhtaa1ewJtWWxNfnQgQHs9mM1hq3291iqq+FCGb+ZzlprbHb7UaoKSkpAc43O4WEhGC1Wpu4xNVbtmwZn3/+ORs3biQ+Pr7a7YYPH17l2aVLlizhmmuuISMjg/bt2wPQrl07MjIyKm174MABYmNjuemmmzh69CjXXHMNixYtavJ+RrV9qvZRSuWX3VZAWNl9BWitdXSDlk6IIKeUMmpjZJZrIZoX/2kPwDvgpy/U5Od7v/rMZrMxHEAw1dL4++abbwLe1jehbUUul4tvvvmG77//nksuuYRbb72VFStWMHv27Pos6gWrMcRoraUrtxAXyWQy4fF4cLvd1X5ACCGans1mw2azGc1OdrvdCDXFxcWAt6+NL9AES1+a2mpikpKSOH36NO3bt+f06dMkJlae37lTp0707dvXGAZhypQpfPvtt807xAghLt6FdPIVQjQPZrPZGC1Yl81O7ws1RUVFFBYWGoPs+QJNc216qq0mZtKkSaxcuZJ58+axcuVKJk+eXGmbgQMHkpubS2ZmJgkJCXz11VfGEApNKfjqxYRoYvn5+QwfPrzKduOq3HbbbXz11VdGs5L0aRIiuPgG0IuKiiI+Pp6kpCTatGlDeHg4LpeLvLw8MjMzOXPmDOfOnaOoqMg4rbuxnDlzhk6dOvHSSy/x61//mk6dOhlNYrWZN28eX3zxBT169GDDhg3MmzcPgJ07dzJnzhzAG+qWLFnC2LFj6d27N1rrZtEBX0JMPTGbzfTt25devXoxbdo0o+rxYixYsKDSZI3+HXuXLVvGH//4x4t+nobk68Genp5Or169qtzm0UcfJTk5ud7n7Bk1ahQVxxSqD9HR0bz11lv86le/qrQuPz+fTp06lRtEbvny5bzyyitGda7b7a70uJtvvpkjR47Ue1kb2sqVK+nRowc9evRg5cqVVW7zzDPP0LFjR/r27Uvfvn2NaS58jh8/TmRkJEuWLAG8/RJGjBjR6F8CQgTKZDIRGhpKTEwMiYmJJCUlERsbS2hoKA6Hg7y8PM6ePUtGRga5ubkUFxdX+X9fH9LT04mPj6ddu3acOHGC/Px8cnNzOXHihDEPWW3atm3Ll19+aczl5xt0MjU1ld///vxcz+PGjWP37t38+9//ZsWKFdhstgb5my6ENCfVE/+5k6ZPn86yZcvKfcm5XK4LPjvl2WefrXad1pq5c+fWqay1qUtZL8by5cvJyckJuJmlsctXlSuvvLLKOYbmz5/PiBHlJzQLDw/n//7v/wBvgKnYyfeHH37A7XYbbc0NweVyYa3nD5ycnBwWLlzIzp07UUoxYMAAJk2aVOUgWQ899FC1g2/96le/Yvz48cZ9m83G2LFj+eCDD5g+fXq9llmIhuDf9ATe/ze73W6MIOz7UWs2m41+NyEhIU3+OdYSSE1MAxg+fDiHDh1i06ZNDB8+nEmTJtGzZ0/cbjePPvooAwcOJCUlhTfffNN4zOLFi+nduzd9+vQxqvJmzpxpzHg9b948evbsSb9+/Xj88ccB7y9c36/XXbt2MWTIEFJSUpg6daoxn82oUaN4/PHHGTRoEJdffnm1baOjRo3iwQcfJDU1lVdeeYW0tDRGjhzJgAEDuO666zh9+jQAhw4dMobH79+/P4cPH6awsJCxY8fSv39/evfuzdq1awM+VpMmTaKwsJABAwbwwQcfkJ6ezpgxY0hJSWHs2LEcP37cOBZz585l8ODB5Wa2Bm8weOSRR+jVqxcpKSksXbq00vPce++9pKamkpyczNNPP20s9x3XlJQU40v2ww8/pFevXvTp06dSIKlJWloaGRkZXHvttdVu4xsvxr9Z6b333ivXBl1dWbt06UJWVhbgreb1TT3wzDPPcPvtt3P11VfTo0cP3nrLO9m87/03+T/+g/5TpgR0nC7E+vXrGTduHG3atCEuLo5x48bx+eefX9A+1qxZQ9euXUlOTi63fMqUKVWGRCGCgcViISIigri4ONq1a0dCQgIxMTHYbLZyNTVnzpwhJyeHwsJCnE6nNDXXgcTAeuZyuVi3bh3XX389AN999x179uyha9euLF++nJiYGHbs2IHdbmfYsGFce+217N+/n7Vr17Jt2zZjAkh/2dnZrF69mv379+PxeMjOzq70vHfccQdLly5l5MiRLFiwgIULF/Lyyy8bZdq+fTufffYZCxcurNRE5eNwONi5cydOp5ORI0eydu1aEhIS+OCDD3jqqad4++23mT59OvPmzWPq1KnGXE42m43Vq1cTHR1NVlYWQ4YMYdKkSQH12v/kk0+IjIw0arEmTpzIjBkzmDFjBm+//Tb333+/MQLliRMn2LJlS6Uam+XLl5Oens6uXbuMiQ0reu6552jTpg1ut5uxY8eye/duOnbsaBxXpZQx18qzzz7L+vXr6dixo7GsNh6Ph4cffpg//elP1R5fwJgywuVyGTVKmzdvLjevUFVlTUlJqfH5d+/ezbfffktRURH9+vXjhhtuALzvv91paXRwuXirLCTWdJwuZBbrkydP0rlzZ+N+p06dOHnyZJXle+211/jjH/9Iamoqv/3tb4mLi6OwsJDFixfzxRdfGGHcp1evXuzYsaPGv1mIYOEbPdg3q7zL5cLhcBgX36z0vr43votvXjFRvRYXYvZd2TBzqly1f1+N60tKSujbty/grYmZPXs2W7ZsYdCgQXTt2hXwzvK7e/duo3YlLy/PaIO88847jarIijMLx8TEEBoayuzZs5kwYYIRkHzy8vLIzc01Zq6eMWMG06ZNM9bfdNNNAAwYMID09PRq/4Zbb70VgB9//JE9e/YYIzO63W7at29P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deumDx8+rO12u05JSdE//PBDpe1uvvlmvWLFCq211l9++aW+7bbbtNZab968WQ8dOlS7XC7tcrn0kCFD9MaNG7XWWq9YsUL/+te/rvJ5m8P/e1DJSdf66WitX+rV1CWpF//vn/9P91rRS3984OOmLoqoq6JvtN6H1unDtNZaAzt1HTOB1MQ0gOHDh3Po0CE2bdrE8OHDmTRpEj179sTtdvPoo48ycOBAUlJSyg2/vnjxYnr37k2fPn2YN28eADNnzjRmvJ43bx49e/YkJSWFxx9/HPCOD7JkyRIAdu3axZAhQ0hJSWHq1KnGr9hRo0bx+OOPM2jQIC6//PJy89X4GzVqFA8++CCpqam88sorpKWlMXLkSAYMGMB1113H6dOnATh06BDXXHMNffr0oX///hw+fJjCwkLGjh1L//796d27N2vXrg34WE2aNInCwkIGDBjABx98QHp6OmPGjCElJYWxY8dy/Phx41jMnTuXwYMHl5vZGrydzB555BF69epFSkoKS5curfQ89957L6mpqSQnJ/P0008by/2Pq2/E2w8//JBevXrRp08fRowYEfDfUld//etfy81M/uyzzzJw4EB69erFPffcg8lkQillzE4NkJWVRZcuXQDvXE2TJ09m1KhR9OjRg4ULFwLe2q8rrriCGbNmkTp1Kj+dPl3l+6yutm/fTvfu3enWrRs2m43//M//rPK137t3L2PGjAFg9OjRxjZKKUpLS3E4HNjtdpxOpzFZ5KRJk/jLX/5yUeUTQrR80rG3nrlcLtatW2d8KX333Xfs2bOHrl27snz5cmJiYtixYwd2u51hw4Zx7bXXsn//ftauXcu2bduMCSD9ZWdns3r1avbv349SiqysrEp9Yu644w6WLl3KyJEjWbBgAQsXLuTll182yrR9+3Y+++wzFi5cWG0TlcPhYOfOnTidTkaOHMnatWtJSEjggw8+4KmnnuLtt99m+vTpzJs3j6lTp1JaWorH48Fms7F69Wqio6PJyspiyJAhTJo0KaBOyJ988gmRkZHGDOATJ05kxowZzJgxg7fffpv777+fNWvWAN5Zkrds2VKpo+vy5ctJT09n165dxsSGFT333HO0adMGt9vN2LFj2b17Nx07dix3XHNzcwFviFi/fj0dO3Y0lgWitLSU1NRULBYL8+bNY8qUKQE9bvPmzdx8883G/fvuu48FCxYAcPvtt/Ppp58aMz9X17S0fft29uzZQ3h4OAMHDuSGG24gPj6egwcP8s5bb9HviSf4+9atNb7PwDuR5Ysvvlhpeffu3Y1A7XPy5Ek6d+5s3O/UqRPbtm2r9Ng+ffrw8ccf88ADD7B69WoKCgrIzs7m6quvZvTo0bRv3x6tNffdd58xnUBcXBx2u53s7OxaJ98UQrReLS7E/G7uVw2y318uG1Pj+pKSEvr27Qt4a2Jmz57Nli1bGDRoEF27dgW8s/zu3r3b+DLIy8vj4MGDbNiwgTvvvNOY2KtiG2FMTAyhoaHMnj2bG2+8kfHjxxtVab795ObmGjNXz5gxg2nTphmPv+mmmwAYMGAA6enp1f4Nt956KwA//vgje/bsYdw479kAbreb9u3bU1BQwMmTJ5k6dSqAMVCS0+nkySef5Ouvv8ZkMnHy5EkyMjJo165djcesKlu3buXjjz8GvF/g/rUu06ZNq/JMnQ0bNjB37lzjdOSq2lhXrVrF8uXLcblcnD59mr1799KzZ89yx9UXFIYNG8bMmTO55ZZbjGMXiGPHjtGxY0eOHDnCmDFj6N27N5dddlmtjzt9+jQJCQnG/Y0bN/LCCy9QXFxMTk4OycnJTJw40RgAz+VyVQoy48aNM77sb7rpJv75z38yZcoULr30UoYMHoz94EG+2rq1xvcZwPTp05k+fXrAf3MglixZwn333ceKFSsYMWIEHTt2xGw2c+jQIfbt22f0kRk3bpwxEztAYmIip06dkhAjhKhWiwsxTSUsLMyoTfAXEXF+yGytNUuXLuW6664rt8369etr3LfFYmH79u18+eWXfPTRRyxdurTWx/jznQbnG38E4M477+T777+nQ4cOfPbZZ+XKqrUmOTmZrVvLj5zpP/W7v/fee4/MzEzS0tKwWq106dKF0tLSgMsXKP9jeSGOHj3KkiVL2LFjB3FxccycOZPS0tJKx/W1117jq6++YtmyZWzbto1PP/2UAQMGkJaWFtAXaceOHQHo1q0bo0aN4vvvvw8oxISFhRnHq7S0lP/6r/9i586ddO7cmWeeecZY5xv8TilFYWFhuX1UrPXy3b/QY3YhNTEdO3bkp59+Mu6fOHHCOAb+OnToYATTwsJC/vrXvxIbG8tbb73FkCFDjM7f48ePZ+vWrUaIKS0tLTdUuhBCVNTiQkxtNSZN6brrruONN95gzJgxWK1WDhw4QMeOHRk3bhzPPvss06dPN6r5/X8lFxYWUlxczIQJExg2bBjdunUrt9+YmBji4uKMX7HvvvuuUStTnXfeeafadVdccQWZmZls3bqVq6++GqfTyYEDB0hOTqZTp06sWbOGKVOmYLfbcbvd5OXlkZiYiNVqZePGjRw7dqzOx2jo0KG8//773H777bz33nvGF1pNxo0bx5tvvsno0aON5iT/45efn09ERAQxMTFkZGSwbt06Ro0aVe1xPXz4MIMHD2bw4MGsW7eOn376qdYQc+7cOcLDwwkJCSErK4vNmzdX6rtTnauuuopDhw4xatQoI7DEx8dTWFjIRx99ZDQ1denShe+++45BgwaxevVqAKNG5osvviAnJ4ewsDDWrFnD22+/Xel5xg4dyqKyJsGq3mdwYTUxAwcO5ODBgxw9epSOHTvy/vvv8+c//7nSdllZWbRp0waTycRvfvMbZs2aBcAll1zCW2+9xRNPPIHWmn/84x88+OCDgDdInzlzxuj3I4QQVWlxIaY5mzNnDunp6fTv3x+tNQkJCaxZs4brr7+eXbt2kZqais1mY8KECTz//PPG4woKCpg8eTKlpaVorfntb38LlJ8EcuXKlcydO5fi4mK6detWY0ipjc1m46OPPuL+++8nLy8Pl8vFgw8+SHJyMu+++y733HMPCxYswGq18uGHHzJ9+nQmTpxI7969SU1N5corr6zzcy9dupQ777yTF198kYSEhID+jjlz5nDgwAFSUlKwWq3cdddd3Hfffcb6Pn360K9fP6688ko6d+7MsGHDgMrH9aWXXgK8p3wfPHgQrTVjx46lT58+tZZh3759Ridcj8djdBgOxA033MCbb77JnDlziI2N5a677qJXr160a9eOgQMHGts98sgj3HLLLSxfvpwJEyYAGE1LgwYN4j/+4z84ceIEt912G6mpqZWaDq8dMYK9WVnVvs8ulMVi4bXXXuO6667D7XYza9YskpOTAe/wAKmpqUyaNIlNmzbxxBNPoJRixIgR/O53vwPg5ptv5quvvqJ3794opbj++uuZOHEiAGlpaQwZMkRGLBZC1EhV7CBarztX6nrgFcAM/F5rXeVAEkqp/wA+AgZqrWsc2CM1NVVXHPtj3759RofA1sLpdBrn34vg97Of/Yy//e1vxMbGBvwYXTavycqVK/n++++NcFCRx27H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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "from openai.embeddings_utils import plot_multiclass_precision_recall\n", - "\n", - "plot_multiclass_precision_recall(probas, y_test, [1,2,3,4,5], clf)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Unsurprisingly 5-star and 1-star reviews seem to be easier to predict. Perhaps with more data, the nuances between 2-4 stars could be better predicted, but there's also probably more subjectivity in how people use the inbetween scores." + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Classification_using_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Classification_using_embeddings.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -121,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/Clustering.ipynb b/examples/embeddings/Clustering.ipynb index a03eec5357..7a4f14193d 100644 --- a/examples/embeddings/Clustering.ipynb +++ b/examples/embeddings/Clustering.ipynb @@ -4,243 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Clustering\n", - "\n", - "We use a simple k-means algorithm to demonstrate how clustering can be done. Clustering can help discover valuable, hidden groupings within the data. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(1000, 2048)" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "\n", - "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", - "df['text-similarity-babbage-001'] = df.babbage_similarity.apply(eval).apply(np.array)\n", - "matrix = np.vstack(df.babbage_similarity.values)\n", - "matrix.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 1. Find the clusters using K-means" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We show the simplest use of K-means. You can pick the number of clusters that fits your use case best." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Cluster\n", - "2 2.543478\n", - "3 4.374046\n", - "0 4.709402\n", - "1 4.832099\n", - "Name: Score, dtype: float64" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from sklearn.cluster import KMeans\n", - "\n", - "n_clusters = 4\n", - "\n", - "kmeans = KMeans(n_clusters = n_clusters,init='k-means++',random_state=42)\n", - "kmeans.fit(matrix)\n", - "labels = kmeans.labels_\n", - "df['Cluster'] = labels\n", - "\n", - "df.groupby('Cluster').Score.mean().sort_values()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It looks like cluster 2 focused on negative reviews, while cluster 0 and 1 focused on positive reviews." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Text(0.5, 1.0, 'Clusters identified visualized in language 2d using t-SNE')" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "from sklearn.manifold import TSNE\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt\n", - "\n", - "tsne = TSNE(n_components=2, perplexity=15, random_state=42, init='random', learning_rate=200)\n", - "vis_dims2 = tsne.fit_transform(matrix)\n", - "\n", - "x = [x for x,y in vis_dims2]\n", - "y = [y for x,y in vis_dims2]\n", - "\n", - "for category, color in enumerate(['purple', 'green', 'red', 'blue']):\n", - " xs = np.array(x)[df.Cluster==category]\n", - " ys = np.array(y)[df.Cluster==category]\n", - " plt.scatter(xs, ys, color=color, alpha=0.3)\n", - "\n", - " avg_x = xs.mean()\n", - " avg_y = ys.mean()\n", - " \n", - " plt.scatter(avg_x, avg_y, marker='x', color=color, s=100)\n", - "plt.title(\"Clusters identified visualized in language 2d using t-SNE\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Visualization of clusters in a 2d projection. The red cluster clearly represents negative reviews. The blue cluster seems quite different from the others. Let's see a few samples from each cluster." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2. Text samples in the clusters & naming the clusters\n", - "\n", - "Let's show random samples from each cluster. We'll use davinci-instruct-beta-v3 to name the clusters, based on a random sample of 6 reviews from that cluster." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cluster 0 Theme: All of the customer reviews mention the great flavor of the product.\n", - "5, French Vanilla Cappuccino: Great price. Really love the the flavor. No need to add anything to \n", - "5, great coffee: A bit pricey once you add the S & H but this is one of the best flavor\n", - "5, Love It: First let me say I'm new to drinking tea. So you're not getting a well\n", - "----------------------------------------------------------------------------------------------------\n", - "Cluster 1 Theme: All three reviews mention the quality of the product.\n", - "5, Beautiful: I don't plan to grind these, have plenty other peppers for that. I go\n", - "5, Awesome: I can't find this in the stores and thought I would like it. So I bou\n", - "5, Came as expected: It was tasty and fresh. The other one I bought was old and tasted mold\n", - "----------------------------------------------------------------------------------------------------\n", - "Cluster 2 Theme: All reviews are about customer's disappointment.\n", - "1, Disappointed...: I should read the fine print, I guess. I mostly went by the picture a\n", - "5, Excellent but Price?: I first heard about this on America's Test Kitchen where it won a blin\n", - "1, Disappointed: I received the offer from Amazon and had never tried this brand before\n", - "----------------------------------------------------------------------------------------------------\n", - "Cluster 3 Theme: The reviews for these products have in common that the customers' dogs love them.\n", - "5, My Dog's Favorite Snack!: I was first introduced to this snack at my dog's training classes at p\n", - "4, Fruitables Crunchy Dog Treats: My lab goes wild for these and I am almost tempted to have a go at som\n", - "5, Happy with the product: My dog was suffering with itchy skin. He had been eating Natural Choi\n", - "----------------------------------------------------------------------------------------------------\n" - ] - } - ], - "source": [ - "import openai\n", - "\n", - "# Reading a review which belong to each group.\n", - "rev_per_cluster = 3\n", - "\n", - "for i in range(n_clusters):\n", - " print(f\"Cluster {i} Theme:\", end=\" \")\n", - " \n", - " reviews = \"\\n\".join(df[df.Cluster == i].combined.str.replace(\"Title: \", \"\").str.replace(\"\\n\\nContent: \", \": \").sample(rev_per_cluster, random_state=42).values)\n", - " response = openai.Completion.create(\n", - " engine=\"davinci-instruct-beta-v3\",\n", - " prompt=f\"What do the following customer reviews have in common?\\n\\nCustomer reviews:\\n\\\"\\\"\\\"\\n{reviews}\\n\\\"\\\"\\\"\\n\\nTheme:\",\n", - " temperature=0,\n", - " max_tokens=64,\n", - " top_p=1,\n", - " frequency_penalty=0,\n", - " presence_penalty=0\n", - " )\n", - " print(response[\"choices\"][0][\"text\"].replace('\\n',''))\n", - "\n", - " sample_cluster_rows = df[df.Cluster == i].sample(rev_per_cluster, random_state=42) \n", - " for j in range(rev_per_cluster):\n", - " print(sample_cluster_rows.Score.values[j], end=\", \")\n", - " print(sample_cluster_rows.Summary.values[j], end=\": \")\n", - " print(sample_cluster_rows.Text.str[:70].values[j])\n", - " \n", - " print(\"-\" * 100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see based on the average ratings per cluster, that Cluster 2 contains mostly negative reviews. Cluster 0 and 1 contain mostly positive reviews, whilst Cluster 3 appears to contain reviews about dog products." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's important to note that clusters will not necessarily match what you intend to use them for. A larger amount of clusters will focus on more specific patterns, whereas a small number of clusters will usually focus on largest discrepencies in the data." + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Clustering.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Clustering.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -255,7 +26,12 @@ "pygments_lexer": "ipython3", "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/Code_search.ipynb b/examples/embeddings/Code_search.ipynb index 02aa162068..440f8f56d5 100644 --- a/examples/embeddings/Code_search.ipynb +++ b/examples/embeddings/Code_search.ipynb @@ -4,377 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Code search\n", - "\n", - "We index our own openai-python code repository, and show how it can be searched. We implement a simple version of file parsing and extracting of functions from python files. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Total number of py files: 40\n", - "Total number of functions extracted: 64\n" - ] - } - ], - "source": [ - "import os\n", - "from glob import glob\n", - "import pandas as pd\n", - "\n", - "def get_function_name(code):\n", - " \"\"\"\n", - " Extract function name from a line beginning with \"def \"\n", - " \"\"\"\n", - " assert code.startswith(\"def \")\n", - " return code[len(\"def \"): code.index(\"(\")]\n", - "\n", - "def get_until_no_space(all_lines, i) -> str:\n", - " \"\"\"\n", - " Get all lines until a line outside the function definition is found.\n", - " \"\"\"\n", - " ret = [all_lines[i]]\n", - " for j in range(i + 1, i + 10000):\n", - " if j < len(all_lines):\n", - " if len(all_lines[j]) == 0 or all_lines[j][0] in [\" \", \"\\t\", \")\"]:\n", - " ret.append(all_lines[j])\n", - " else:\n", - " break\n", - " return \"\\n\".join(ret)\n", - "\n", - "def get_functions(filepath):\n", - " \"\"\"\n", - " Get all functions in a Python file.\n", - " \"\"\"\n", - " whole_code = open(filepath).read().replace(\"\\r\", \"\\n\")\n", - " all_lines = whole_code.split(\"\\n\")\n", - " for i, l in enumerate(all_lines):\n", - " if l.startswith(\"def \"):\n", - " code = get_until_no_space(all_lines, i)\n", - " function_name = get_function_name(code)\n", - " yield {\"code\": code, \"function_name\": function_name, \"filepath\": filepath}\n", - "\n", - "\n", - "# get user root directory\n", - "root_dir = os.path.expanduser(\"~\")\n", - "\n", - "# path to code repository directory\n", - "code_root = root_dir + \"/openai-python\"\n", - "code_files = [y for x in os.walk(code_root) for y in glob(os.path.join(x[0], '*.py'))]\n", - "print(\"Total number of py files:\", len(code_files))\n", - "all_funcs = []\n", - "for code_file in code_files:\n", - " funcs = list(get_functions(code_file))\n", - " for func in funcs:\n", - " all_funcs.append(func)\n", - "\n", - "print(\"Total number of functions extracted:\", len(all_funcs))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For code search models we use code-search-{model}-code to obtain embeddings for code snippets, and code-search-{model}-text to embed natural language queries." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
codefunction_namefilepathcode_embedding
0def semantic_search(engine, query, documents):...semantic_search/examples/semanticsearch/semanticsearch.py[-0.038976121693849564, -0.0031428150832653046...
1def main():\\n parser = argparse.ArgumentPar...main/examples/semanticsearch/semanticsearch.py[-0.024289356544613838, -0.017748363316059113,...
2def get_candidates(\\n prompt: str,\\n sto...get_candidates/examples/codex/backtranslation.py[-0.04161201789975166, -0.0169310811907053, 0....
3def rindex(lst: List, value: str) -> int:\\n ...rindex/examples/codex/backtranslation.py[-0.027255680412054062, -0.007931121625006199,...
4def eval_candidate(\\n candidate_answer: str...eval_candidate/examples/codex/backtranslation.py[-0.00999179296195507, -0.01640152558684349, 0...
\n", - "
" - ], - "text/plain": [ - " code function_name \\\n", - "0 def semantic_search(engine, query, documents):... semantic_search \n", - "1 def main():\\n parser = argparse.ArgumentPar... main \n", - "2 def get_candidates(\\n prompt: str,\\n sto... get_candidates \n", - "3 def rindex(lst: List, value: str) -> int:\\n ... rindex \n", - "4 def eval_candidate(\\n candidate_answer: str... eval_candidate \n", - "\n", - " filepath \\\n", - "0 /examples/semanticsearch/semanticsearch.py \n", - "1 /examples/semanticsearch/semanticsearch.py \n", - "2 /examples/codex/backtranslation.py \n", - "3 /examples/codex/backtranslation.py \n", - "4 /examples/codex/backtranslation.py \n", - "\n", - " code_embedding \n", - "0 [-0.038976121693849564, -0.0031428150832653046... \n", - "1 [-0.024289356544613838, -0.017748363316059113,... \n", - "2 [-0.04161201789975166, -0.0169310811907053, 0.... \n", - "3 [-0.027255680412054062, -0.007931121625006199,... \n", - "4 [-0.00999179296195507, -0.01640152558684349, 0... " - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from openai.embeddings_utils import get_embedding\n", - "\n", - "df = pd.DataFrame(all_funcs)\n", - "df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, engine='code-search-babbage-code-001'))\n", - "df['filepath'] = df['filepath'].apply(lambda x: x.replace(code_root, \"\"))\n", - "df.to_csv(\"output/code_search_openai-python.csv\", index=False)\n", - "df.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/openai/tests/test_endpoints.py:test_completions_multiple_prompts score=0.681\n", - "def test_completions_multiple_prompts():\n", - " result = openai.Completion.create(\n", - " prompt=[\"This was a test\", \"This was another test\"], n=5, engine=\"ada\"\n", - " )\n", - " assert len(result.choices) == 10\n", - "\n", - "----------------------------------------------------------------------\n", - "/openai/tests/test_endpoints.py:test_completions score=0.675\n", - "def test_completions():\n", - " result = openai.Completion.create(prompt=\"This was a test\", n=5, engine=\"ada\")\n", - " assert len(result.choices) == 5\n", - "\n", - "\n", - "----------------------------------------------------------------------\n", - "/openai/tests/test_api_requestor.py:test_requestor_sets_request_id score=0.635\n", - "def test_requestor_sets_request_id(mocker: MockerFixture) -> None:\n", - " # Fake out 'requests' and confirm that the X-Request-Id header is set.\n", - "\n", - " got_headers = {}\n", - "\n", - " def fake_request(self, *args, **kwargs):\n", - " nonlocal got_headers\n", - "----------------------------------------------------------------------\n" - ] - } - ], - "source": [ - "from openai.embeddings_utils import cosine_similarity\n", - "\n", - "def search_functions(df, code_query, n=3, pprint=True, n_lines=7):\n", - " embedding = get_embedding(code_query, engine='code-search-babbage-text-001')\n", - " df['similarities'] = df.code_embedding.apply(lambda x: cosine_similarity(x, embedding))\n", - "\n", - " res = df.sort_values('similarities', ascending=False).head(n)\n", - " if pprint:\n", - " for r in res.iterrows():\n", - " print(r[1].filepath+\":\"+r[1].function_name + \" score=\" + str(round(r[1].similarities, 3)))\n", - " print(\"\\n\".join(r[1].code.split(\"\\n\")[:n_lines]))\n", - " print('-'*70)\n", - " return res\n", - "res = search_functions(df, 'Completions API tests', n=3)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/openai/validators.py:format_inferrer_validator score=0.655\n", - "def format_inferrer_validator(df):\n", - " \"\"\"\n", - " This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification.\n", - " It will also suggest to use ada and explain train/validation split benefits.\n", - " \"\"\"\n", - " ft_type = infer_task_type(df)\n", - " immediate_msg = None\n", - "----------------------------------------------------------------------\n", - "/openai/validators.py:long_examples_validator score=0.649\n", - "def long_examples_validator(df):\n", - " \"\"\"\n", - " This validator will suggest to the user to remove examples that are too long.\n", - " \"\"\"\n", - " immediate_msg = None\n", - " optional_msg = None\n", - " optional_fn = None\n", - "----------------------------------------------------------------------\n", - "/openai/validators.py:non_empty_completion_validator score=0.646\n", - "def non_empty_completion_validator(df):\n", - " \"\"\"\n", - " This validator will ensure that no completion is empty.\n", - " \"\"\"\n", - " necessary_msg = None\n", - " necessary_fn = None\n", - " immediate_msg = None\n", - "----------------------------------------------------------------------\n" - ] - } - ], - "source": [ - "res = search_functions(df, 'fine-tuning input data validation logic', n=3)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/openai/validators.py:common_completion_suffix_validator score=0.665\n", - "def common_completion_suffix_validator(df):\n", - " \"\"\"\n", - " This validator will suggest to add a common suffix to the completion if one doesn't already exist in case of classification or conditional generation.\n", - " \"\"\"\n", - " error_msg = None\n", - " immediate_msg = None\n", - " optional_msg = None\n", - " optional_fn = None\n", - "\n", - " ft_type = infer_task_type(df)\n", - "----------------------------------------------------------------------\n", - "/openai/validators.py:get_outfnames score=0.66\n", - "def get_outfnames(fname, split):\n", - " suffixes = [\"_train\", \"_valid\"] if split else [\"\"]\n", - " i = 0\n", - " while True:\n", - " index_suffix = f\" ({i})\" if i > 0 else \"\"\n", - " candidate_fnames = [\n", - " fname.split(\".\")[0] + \"_prepared\" + suffix + index_suffix + \".jsonl\"\n", - " for suffix in suffixes\n", - " ]\n", - " if not any(os.path.isfile(f) for f in candidate_fnames):\n", - "----------------------------------------------------------------------\n" - ] - } - ], - "source": [ - "res = search_functions(df, 'find common suffix', n=2, n_lines=10)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/openai/cli.py:tools_register score=0.651\n", - "def tools_register(parser):\n", - " subparsers = parser.add_subparsers(\n", - " title=\"Tools\", help=\"Convenience client side tools\"\n", - " )\n", - "\n", - " def help(args):\n", - " parser.print_help()\n", - "\n", - " parser.set_defaults(func=help)\n", - "\n", - " sub = subparsers.add_parser(\"fine_tunes.prepare_data\")\n", - " sub.add_argument(\n", - " \"-f\",\n", - " \"--file\",\n", - " required=True,\n", - " help=\"JSONL, JSON, CSV, TSV, TXT or XLSX file containing prompt-completion examples to be analyzed.\"\n", - " \"This should be the local file path.\",\n", - " )\n", - " sub.add_argument(\n", - " \"-q\",\n", - "----------------------------------------------------------------------\n" - ] - } - ], - "source": [ - "res = search_functions(df, 'Command line interface for fine-tuning', n=1, n_lines=20)" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Code_search.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Code_search.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -387,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/Get_embeddings.ipynb b/examples/embeddings/Get_embeddings.ipynb index e1b17327e2..199c2dd156 100644 --- a/examples/embeddings/Get_embeddings.ipynb +++ b/examples/embeddings/Get_embeddings.ipynb @@ -4,88 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Get embeddings\n", - "\n", - "The function `get_embedding` will give us an embedding for an input text." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "12288" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import openai\n", - "\n", - "embedding = openai.Embedding.create(input=\"Sample document text goes here\", engine=\"text-similarity-davinci-001\")['data'][0]['embedding']\n", - "len(embedding)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1024\n" - ] - } - ], - "source": [ - "import openai\n", - "from tenacity import retry, wait_random_exponential, stop_after_attempt\n", - "\n", - "@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))\n", - "def get_embedding(text: str, engine=\"text-similarity-davinci-001\") -> List[float]:\n", - "\n", - " # replace newlines, which can negatively affect performance.\n", - " text = text.replace(\"\\n\", \" \")\n", - "\n", - " return openai.Embedding.create(input=[text], engine=engine)[\"data\"][0][\"embedding\"]\n", - "\n", - "embedding = get_embedding(\"Sample query text goes here\", engine=\"text-search-ada-query-001\")\n", - "print(len(embedding))" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1024\n" - ] - } - ], - "source": [ - "embedding = get_embedding(\"Sample document text goes here\", engine=\"text-search-ada-doc-001\")\n", - "print(len(embedding))" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Get_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Get_embeddings.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -98,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/Obtain_dataset.ipynb b/examples/embeddings/Obtain_dataset.ipynb index 80d1db33a9..9d04f9bce9 100644 --- a/examples/embeddings/Obtain_dataset.ipynb +++ b/examples/embeddings/Obtain_dataset.ipynb @@ -4,173 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 1. Load the dataset\n", - "\n", - "The dataset used in this example is [fine-food reviews](https://www.kaggle.com/snap/amazon-fine-food-reviews) from Amazon. The dataset contains a total of 568,454 food reviews Amazon users left up to October 2012. We will use a subset of this dataset, consisting of 1,000 most recent reviews for illustration purposes. The reviews are in English and tend to be positive or negative. Each review has a ProductId, UserId, Score, review title (Summary) and review body (Text).\n", - "\n", - "We will combine the review summary and review text into a single combined text. The model will encode this combined text and it will output a single vector embedding." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
TimeProductIdUserIdScoreSummaryTextcombined
Id
11303862400B001E4KFG0A3SGXH7AUHU8GW5Good Quality Dog FoodI have bought several of the Vitality canned d...Title: Good Quality Dog Food; Content: I have ...
21346976000B00813GRG4A1D87F6ZCVE5NK1Not as AdvertisedProduct arrived labeled as Jumbo Salted Peanut...Title: Not as Advertised; Content: Product arr...
\n", - "
" - ], - "text/plain": [ - " Time ProductId UserId Score Summary \\\n", - "Id \n", - "1 1303862400 B001E4KFG0 A3SGXH7AUHU8GW 5 Good Quality Dog Food \n", - "2 1346976000 B00813GRG4 A1D87F6ZCVE5NK 1 Not as Advertised \n", - "\n", - " Text \\\n", - "Id \n", - "1 I have bought several of the Vitality canned d... \n", - "2 Product arrived labeled as Jumbo Salted Peanut... \n", - "\n", - " combined \n", - "Id \n", - "1 Title: Good Quality Dog Food; Content: I have ... \n", - "2 Title: Not as Advertised; Content: Product arr... " - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import pandas as pd\n", - "\n", - "df = pd.read_csv('input/Reviews.csv', index_col=0)\n", - "df = df[['Time', 'ProductId', 'UserId', 'Score', 'Summary', 'Text']]\n", - "df = df.dropna()\n", - "df['combined'] = \"Title: \" + df.Summary.str.strip() + \"; Content: \" + df.Text.str.strip()\n", - "df.head(2)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1000" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# subsample to 1k most recent reviews and remove samples that are too long\n", - "df = df.sort_values('Time').tail(1_100)\n", - "df.drop('Time', axis=1, inplace=True)\n", - "\n", - "from transformers import GPT2TokenizerFast\n", - "tokenizer = GPT2TokenizerFast.from_pretrained(\"gpt2\")\n", - "\n", - "# remove reviews that are too long\n", - "df['n_tokens'] = df.combined.apply(lambda x: len(tokenizer.encode(x)))\n", - "df = df[df.n_tokens<2000].tail(1_000)\n", - "len(df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2. Get embeddings and save them for future reuse" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "from openai.embeddings_utils import get_embedding\n", - "\n", - "# This will take just under 10 minutes\n", - "df['babbage_similarity'] = df.combined.apply(lambda x: get_embedding(x, engine='text-similarity-babbage-001'))\n", - "df['babbage_search'] = df.combined.apply(lambda x: get_embedding(x, engine='text-search-babbage-doc-001'))\n", - "df.to_csv('output/embedded_1k_reviews.csv')" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Obtain_dataset.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Obtain_dataset.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -185,7 +26,12 @@ "pygments_lexer": "ipython3", "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/Recommendation.ipynb b/examples/embeddings/Recommendation.ipynb index 9a2cce82c0..7be5be31d7 100644 --- a/examples/embeddings/Recommendation.ipynb +++ b/examples/embeddings/Recommendation.ipynb @@ -4,33417 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Recommendation using embeddings and nearest neighbor search\n", - "\n", - "Recommendations are widespread across the web.\n", - "\n", - "- 'Bought that item? Try these similar items.'\n", - "- 'Enjoy that book? Try these similar titles.'\n", - "- 'Not the help page you were looking for? Try these similar pages.'\n", - "\n", - "This notebook demonstrates how to use embeddings to find similar items to recommend. In particular, we use [AG's corpus of news articles](http://groups.di.unipi.it/~gulli/AG_corpus_of_news_articles.html) as our dataset.\n", - "\n", - "Our model will answer the question: given an article, what are the articles most similar to it?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 1. Imports\n", - "\n", - "First, let's import the packages and functions we'll need for later. If you don't have these, you'll need to install them. You can install them via your terminal by running `pip install {package_name}`, e.g. `pip install pandas`." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# imports\n", - "from typing import List\n", - "\n", - "import pandas as pd\n", - "import pickle\n", - "\n", - "from openai.embeddings_utils import (\n", - " get_embedding,\n", - " distances_from_embeddings,\n", - " tsne_components_from_embeddings,\n", - " chart_from_components,\n", - " indices_of_nearest_neighbors_from_distances,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2. Load data\n", - "\n", - "Next, let's load the AG news data and see what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titledescriptionlabel_intlabel
0World BriefingsBRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime M...1World
1Nvidia Puts a Firewall on a Motherboard (PC Wo...PC World - Upcoming chip set will include buil...4Sci/Tech
2Olympic joy in Greek, Chinese pressNewspapers in Greece reflect a mixture of exhi...2Sports
3U2 Can iPod with PicturesSAN JOSE, Calif. -- Apple Computer (Quote, Cha...4Sci/Tech
4The Dream FactoryAny product, any shape, any size -- manufactur...4Sci/Tech
\n", - "
" - ], - "text/plain": [ - " title \\\n", - "0 World Briefings \n", - "1 Nvidia Puts a Firewall on a Motherboard (PC Wo... \n", - "2 Olympic joy in Greek, Chinese press \n", - "3 U2 Can iPod with Pictures \n", - "4 The Dream Factory \n", - "\n", - " description label_int label \n", - "0 BRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime M... 1 World \n", - "1 PC World - Upcoming chip set will include buil... 4 Sci/Tech \n", - "2 Newspapers in Greece reflect a mixture of exhi... 2 Sports \n", - "3 SAN JOSE, Calif. -- Apple Computer (Quote, Cha... 4 Sci/Tech \n", - "4 Any product, any shape, any size -- manufactur... 4 Sci/Tech " - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# load data\n", - "dataset_path = \"https://cdn.openai.com/API/examples/data/AG_news_samples.csv/"\n", - "df = pd.read_csv(dataset_path)\n", - "\n", - "# print dataframe\n", - "n_examples = 5\n", - "df.head(n_examples)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's take a look at those same examples, but not truncated by ellipses." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Title: World Briefings\n", - "Description: BRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime Minister Tony Blair urged the international community to consider global warming a dire threat and agree on a plan of action to curb the quot;alarming quot; growth of greenhouse gases.\n", - "Label: World\n", - "\n", - "Title: Nvidia Puts a Firewall on a Motherboard (PC World)\n", - "Description: PC World - Upcoming chip set will include built-in security features for your PC.\n", - "Label: Sci/Tech\n", - "\n", - "Title: Olympic joy in Greek, Chinese press\n", - "Description: Newspapers in Greece reflect a mixture of exhilaration that the Athens Olympics proved successful, and relief that they passed off without any major setback.\n", - "Label: Sports\n", - "\n", - "Title: U2 Can iPod with Pictures\n", - "Description: SAN JOSE, Calif. -- Apple Computer (Quote, Chart) unveiled a batch of new iPods, iTunes software and promos designed to keep it atop the heap of digital music players.\n", - "Label: Sci/Tech\n", - "\n", - "Title: The Dream Factory\n", - "Description: Any product, any shape, any size -- manufactured on your desktop! The future is the fabricator. By Bruce Sterling from Wired magazine.\n", - "Label: Sci/Tech\n" - ] - } - ], - "source": [ - "# print the title, description, and label of each example\n", - "for idx, row in df.head(n_examples).iterrows():\n", - " print(\"\")\n", - " print(f\"Title: {row['title']}\")\n", - " print(f\"Description: {row['description']}\")\n", - " print(f\"Label: {row['label']}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 3. Build cache to save embeddings\n", - "\n", - "Before getting embeddings for these articles, let's set up a cache to save the embeddings we generate. In general, it's a good idea to save your embeddings so you can re-use them later. If you don't save them, you'll pay again each time you compute them again.\n", - "\n", - "To save you the expense of computing the embeddings needed for this demo, we've provided a pre-filled cache via the URL below. The cache is a dictionary that maps tuples of `(text, engine)` to a `list of floats` embedding. The cache is saved as a Python pickle file." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# establish a cache of embeddings to avoid recomputing\n", - "# cache is a dict of tuples (text, engine) -> embedding, saved as a pickle file\n", - "\n", - "# set path to embedding cache\n", - "embedding_cache_path_to_load = \"https://cdn.openai.com/API/examples/data/example_embeddings_cache.pkl/"\n", - "embedding_cache_path_to_save = \"example_embeddings_cache.pkl\"\n", - "\n", - "# load the cache if it exists, and save a copy to disk\n", - "try:\n", - " embedding_cache = pd.read_pickle(embedding_cache_path_to_load)\n", - "except FileNotFoundError:\n", - " embedding_cache = {}\n", - "with open(embedding_cache_path_to_save, \"wb\") as embedding_cache_file:\n", - " pickle.dump(embedding_cache, embedding_cache_file)\n", - "\n", - "# define a function to retrieve embeddings from the cache if present, and otherwise request via the API\n", - "def embedding_from_string(\n", - " string: str,\n", - " engine: str = \"text-similarity-babbage-001\",\n", - " embedding_cache=embedding_cache\n", - ") -> List:\n", - " \"\"\"Return embedding of given string, using a cache to avoid recomputing.\"\"\"\n", - " if (string, engine) not in embedding_cache.keys():\n", - " embedding_cache[(string, engine)] = get_embedding(string, engine)\n", - " print('NOT FOUND')\n", - " with open(embedding_cache_path_to_save, \"wb\") as embedding_cache_file:\n", - " pickle.dump(embedding_cache, embedding_cache_file)\n", - " return embedding_cache[(string, engine)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's check that it works by getting an embedding." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Example string: BRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime Minister Tony Blair urged the international community to consider global warming a dire threat and agree on a plan of action to curb the quot;alarming quot; growth of greenhouse gases.\n", - "\n", - "Example embedding: [-0.029093433171510696, 0.007570988964289427, -0.011933144181966782, -0.016499919816851616, 0.026675179600715637, -0.016704540699720383, -0.019439026713371277, 0.015421006828546524, -0.009700911119580269, -0.02580088935792446]...\n" - ] - } - ], - "source": [ - "# as an example, take the first description from the dataset\n", - "example_string = df[\"description\"].values[0]\n", - "print(f\"\\nExample string: {example_string}\")\n", - "\n", - "# print the first 10 dimensions of the embedding\n", - "example_embedding = embedding_from_string(example_string, engine=\"text-similarity-babbage-001\")\n", - "print(f\"\\nExample embedding: {example_embedding[:10]}...\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4. Recommend similar articles based on embeddings\n", - "\n", - "To find similar articles, let's follow a three-step plan:\n", - "1. Get the similarity embeddings of all the article descriptions\n", - "2. Calculate the distance between a source title and all other articles\n", - "3. Print out the other articles closest to the source title" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def print_recommendations_from_strings(\n", - " strings: List[str],\n", - " index_of_source_string: int,\n", - " k_nearest_neighbors: int = 1,\n", - " engine=\"text-similarity-babbage-001\",\n", - ") -> List[int]:\n", - " \"\"\"Print out the k nearest neighbors of a given string.\"\"\"\n", - " # get embeddings for all strings\n", - " embeddings = [embedding_from_string(string, engine=engine) for string in strings]\n", - " # get the embedding of the source string\n", - " query_embedding = embeddings[index_of_source_string]\n", - " # get distances between the source embedding and other embeddings (function from embeddings_utils.py)\n", - " distances = distances_from_embeddings(query_embedding, embeddings, distance_metric=\"cosine\")\n", - " # get indices of nearest neighbors (function from embeddings_utils.py)\n", - " indices_of_nearest_neighbors = indices_of_nearest_neighbors_from_distances(distances)\n", - "\n", - " # print out source string\n", - " query_string = strings[index_of_source_string]\n", - " print(f\"Source string: {query_string}\")\n", - " # print out its k nearest neighbors\n", - " k_counter = 0\n", - " for i in indices_of_nearest_neighbors:\n", - " # skip any strings that are identical matches to the starting string\n", - " if query_string == strings[i]:\n", - " continue\n", - " # stop after printing out k articles\n", - " if k_counter >= k_nearest_neighbors:\n", - " break\n", - " k_counter += 1\n", - "\n", - " # print out the similar strings and their distances\n", - " print(\n", - " f\"\"\"\n", - " --- Recommendation #{k_counter} (nearest neighbor {k_counter} of {k_nearest_neighbors}) ---\n", - " String: {strings[i]}\n", - " Distance: {distances[i]:0.3f}\"\"\"\n", - " )\n", - "\n", - " return indices_of_nearest_neighbors" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 5. Example recommendations\n", - "\n", - "Let's look for articles similar to first one, which was about Tony Blair." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Source string: BRITAIN: BLAIR WARNS OF CLIMATE THREAT Prime Minister Tony Blair urged the international community to consider global warming a dire threat and agree on a plan of action to curb the quot;alarming quot; growth of greenhouse gases.\n", - "\n", - " --- Recommendation #1 (nearest neighbor 1 of 5) ---\n", - " String: THE re-election of British Prime Minister Tony Blair would be seen as an endorsement of the military action in Iraq, Prime Minister John Howard said today.\n", - " Distance: 0.164\n", - "\n", - " --- Recommendation #2 (nearest neighbor 2 of 5) ---\n", - " String: Israel is prepared to back a Middle East conference convened by Tony Blair early next year despite having expressed fears that the British plans were over-ambitious and designed \n", - " Distance: 0.169\n", - "\n", - " --- Recommendation #3 (nearest neighbor 3 of 5) ---\n", - " String: WASHINGTON (Reuters) - President Bush on Friday set a four-year goal of seeing a Palestinian state established and he and British Prime Minister Tony Blair vowed to mobilize international support to help make it happen now that Yasser Arafat is dead.\n", - " Distance: 0.174\n", - "\n", - " --- Recommendation #4 (nearest neighbor 4 of 5) ---\n", - " String: AP - President Bush declared Friday that charges of voter fraud have cast doubt on the Ukrainian election, and warned that any European-negotiated pact on Iran's nuclear program must ensure the world can verify Tehran's compliance.\n", - " Distance: 0.179\n", - "\n", - " --- Recommendation #5 (nearest neighbor 5 of 5) ---\n", - " String: AFP - A battle group of British troops rolled out of southern Iraq on a US-requested mission to deadlier areas near Baghdad, in a major political gamble for British Prime Minister Tony Blair.\n", - " Distance: 0.182\n" - ] - } - ], - "source": [ - "article_descriptions = df[\"description\"].tolist()\n", - "\n", - "tony_blair_articles = print_recommendations_from_strings(\n", - " strings=article_descriptions, # let's base similarity off of the article description\n", - " index_of_source_string=0, # let's look at articles similar to the first one about Tony Blair\n", - " k_nearest_neighbors=5, # let's look at the 5 most similar articles\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Pretty good! All 5 of the recommendations look similar to the original article about Tony Blair. Interestingly, note that #4 doesn't mention the words Tony Blair, but is nonetheless recommended by the model, presumably because the model understands that Tony Blair tends to be related to President Bush or European pacts over Iran's nuclear program. This illustrates the potential power of using embeddings rather than basic string matching; our models understand what topics are related to one another, even when their words don't overlap." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's see how our recommender does on the second example article about NVIDIA's new chipset with more security." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Source string: PC World - Upcoming chip set will include built-in security features for your PC.\n", - "\n", - " --- Recommendation #1 (nearest neighbor 1 of 5) ---\n", - " String: PC World - Updated antivirus software for businesses adds intrusion prevention features.\n", - " Distance: 0.108\n", - "\n", - " --- Recommendation #2 (nearest neighbor 2 of 5) ---\n", - " String: PC World - Send your video throughout your house--wirelessly--with new gateways and media adapters.\n", - " Distance: 0.160\n", - "\n", - " --- Recommendation #3 (nearest neighbor 3 of 5) ---\n", - " String: PC World - The one-time World Class Product of the Year PDA gets a much-needed upgrade.\n", - " Distance: 0.161\n", - "\n", - " --- Recommendation #4 (nearest neighbor 4 of 5) ---\n", - " String: PC World - Symantec, McAfee hope raising virus-definition fees will move users to\\ suites.\n", - " Distance: 0.166\n", - "\n", - " --- Recommendation #5 (nearest neighbor 5 of 5) ---\n", - " String: Ziff Davis - The company this week will unveil more programs and technologies designed to ease users of its high-end servers onto its Integrity line, which uses Intel's 64-bit Itanium processor.\n", - " Distance: 0.193\n" - ] - } - ], - "source": [ - "chipset_security_articles = print_recommendations_from_strings(\n", - " strings=article_descriptions, # let's base similarity off of the article description\n", - " index_of_source_string=1, # let's look at articles similar to the second one about a more secure chipset\n", - " k_nearest_neighbors=5, # let's look at the 5 most similar articles\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "From the printed distances, you can see that the #1 recommendation is much closer than all the others (0.108 vs 0.160+). And the #1 recommendation looks very similar to the starting article - it's another article from PC World about increasing computer security. Pretty good! " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Appendix: Using embeddings in more sophisticated recommenders\n", - "\n", - "A more sophisticated way to build a recommender system is to train a machine learning model that takes in tens or hundreds of signals, such as item popularity or user click data. Even in this system, embeddings can be a very useful signal into the recommender, especially for items that are being 'cold started' with no user data yet (e.g., a brand new product added to the catalog without any clicks yet)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Appendix: Using embeddings to visualize similar articles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To get a sense of what our nearest neighbor recommender is doing, let's visualize the article embeddings. Although we can't plot the 2048 dimensions of each embedding vector, we can use techniques like [t-SNE](https://en.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding) or [PCA](https://en.wikipedia.org/wiki/Principal_component_analysis) to compress the embeddings down into 2 or 3 dimensions, which we can chart.\n", - "\n", - "Before visualizing the nearest neighbors, let's visualize all of the article descriptions using t-SNE. Note that t-SNE is not deterministic, meaning that results may vary from run to run." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/ted/.virtualenvs/openai/lib/python3.9/site-packages/sklearn/manifold/_t_sne.py:982: FutureWarning: The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.\n", - " warnings.warn(\n" - ] - }, - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly/" - }, - "data": [ - { - "customdata": [ - [ - "BRITAIN: BLAIR WARNS OF
CLIMATE THREAT Prime Minister
Tony Blair urged the
international community to
consider global warming a dire
threat and agree on a plan of
action to curb the
quot;alarming quot; growth of
greenhouse gases." - ], - [ - "KABUL, Sept 22 (AFP): Three US
soldiers were killed and 14
wounded in a series of fierce
clashes with suspected Taliban
fighters in south and eastern
Afghanistan this week, the US
military said Wednesday." - ], - [ - "AUSTRALIAN journalist John
Martinkus is lucky to be alive
after spending 24 hours in the
hands of Iraqi militants at
the weekend. Martinkus was in
Baghdad working for the SBS
Dateline TV current affairs
program" - ], - [ - " GAZA (Reuters) - An Israeli
helicopter fired a missile
into a town in the southern
Gaza Strip late on Wednesday,
witnesses said, hours after a
Palestinian suicide bomber
blew herself up in Jerusalem,
killing two Israeli border
policemen." - ], - [ - "RIYADH, Saudi Arabia -- Saudi
police are seeking two young
men in the killing of a Briton
in a Riyadh parking lot, the
Interior Ministry said today,
and the British ambassador
called it a terrorist attack." - ], - [ - "A gas explosion at a coal mine
in northern China killed 33
workers in the 10th deadly
mine blast reported in three
months. The explosion occurred
yesterday at 4:20 pm at Nanlou
township" - ], - [ - "Reuters - Palestinian leader
Mahmoud Abbas called\\Israel
\"the Zionist enemy\" Tuesday,
unprecedented language for\\the
relative moderate who is
expected to succeed Yasser
Arafat." - ], - [ - "Nasser al-Qidwa, Palestinian
representative at the United
Nations and nephew of late
leader Yasser Arafat, handed
Arafat #39;s death report to
the Palestinian National
Authority (PNA) on Saturday." - ], - [ - "CAIRO, Egypt - France's
foreign minister appealed
Monday for the release of two
French journalists abducted in
Baghdad, saying the French
respect all religions. He did
not rule out traveling to
Baghdad..." - ], - [ - "United Arab Emirates President
and ruler of Abu Dhabi Sheik
Zayed bin Sultan al-Nayhan
died Tuesday, official
television reports. He was 86." - ], - [ - "PALESTINIAN leader Yasser
Arafat today issued an urgent
call for the immediate release
of two French journalists
taken hostage in Iraq." - ], - [ - "The al-Qaida terrorist network
spent less than \\$50,000 on
each of its major attacks
except for the Sept. 11, 2001,
suicide hijackings, and one of
its hallmarks is using" - ], - [ - "A FATHER who scaled the walls
of a Cardiff court dressed as
superhero Robin said the
Buckingham Palace protester
posed no threat. Fathers 4
Justice activist Jim Gibson,
who earlier this year staged
an eye-catching" - ], - [ - "Julia Gillard has reportedly
bowed out of the race to
become shadow treasurer,
taking enormous pressure off
Opposition Leader Mark Latham." - ], - [ - "AFP - Maybe it's something to
do with the fact that the
playing area is so vast that
you need a good pair of
binoculars to see the action
if it's not taking place right
in front of the stands." - ], - [ - "Egypt #39;s release of accused
Israeli spy Azzam Azzam in an
apparent swap for six Egyptian
students held on suspicion of
terrorism is expected to melt
the ice and perhaps result" - ], - [ - "GAZA CITY, Gaza Strip: Hamas
militants killed an Israeli
soldier and wounded four with
an explosion in a booby-
trapped chicken coop on
Tuesday, in what the Islamic
group said was an elaborate
scheme to lure troops to the
area with the help of a double" - ], - [ - "AP - The 300 men filling out
forms in the offices of an
Iranian aid group were offered
three choices: Train for
suicide attacks against U.S.
troops in Iraq, for suicide
attacks against Israelis or to
assassinate British author
Salman Rushdie." - ], - [ - "ATHENS, Greece - Gail Devers,
the most talented yet star-
crossed hurdler of her
generation, was unable to
complete even one hurdle in
100-meter event Sunday -
failing once again to win an
Olympic hurdling medal.
Devers, 37, who has three
world championships in the
hurdles but has always flopped
at the Olympics, pulled up
short and screamed as she slid
under the first hurdle..." - ], - [ - " NAIROBI (Reuters) - The
Sudanese government and its
southern rebel opponents have
agreed to sign a pledge in the
Kenyan capital on Friday to
formally end a brutal 21-year-
old civil war, with U.N.
Security Council ambassadors
as witnesses." - ], - [ - "AP - Former Guatemalan
President Alfonso Portillo
#151; suspected of corruption
at home #151; is living and
working part-time in the same
Mexican city he fled two
decades ago to avoid arrest on
murder charges, his close
associates told The Associated
Press on Sunday." - ], - [ - "washingtonpost.com - BRUSSELS,
Aug. 26 -- The United States
will have to wait until next
year to see its fight with the
European Union over biotech
foods resolved, as the World
Trade Organization agreed to
an E.U. request to bring
scientists into the debate,
officials said Thursday." - ], - [ - "Insisting that Hurriyat
Conference is the real
representative of Kashmiris,
Pakistan has claimed that
India is not ready to accept
ground realities in Kashmir." - ], - [ - "VIENNA -- After two years of
investigating Iran's atomic
program, the UN nuclear
watchdog still cannot rule out
that Tehran has a secret atom
bomb project as Washington
insists, the agency's chief
said yesterday." - ], - [ - "AFP - US Secretary of State
Colin Powell wrapped up a
three-nation tour of Asia
after winning pledges from
Japan, China and South Korea
to press North Korea to resume
stalled talks on its nuclear
weapons programs." - ], - [ - "CAIRO, Egypt An Egyptian
company says one of its four
workers who had been kidnapped
in Iraq has been freed. It
says it can #39;t give the
status of the others being
held hostage but says it is
quot;doing its best to secure
quot; their release." - ], - [ - "AFP - Hosts India braced
themselves for a harrowing
chase on a wearing wicket in
the first Test after Australia
declined to enforce the
follow-on here." - ], - [ - "Prime Minister Paul Martin of
Canada urged Haitian leaders
on Sunday to allow the
political party of the deposed
president, Jean-Bertrand
Aristide, to take part in new
elections." - ], - [ - "Hostage takers holding up to
240 people at a school in
southern Russia have refused
to talk with a top Islamic
leader and demanded to meet
with regional leaders instead,
ITAR-TASS reported on
Wednesday." - ], - [ - "Three children from a care
home are missing on the
Lancashire moors after they
are separated from a group." - ], - [ - "Diabetics should test their
blood sugar levels more
regularly to reduce the risk
of cardiovascular disease, a
study says." - ], - [ - "Iraq's interim Prime Minister
Ayad Allawi announced that
proceedings would begin
against former Baath Party
leaders." - ], - [ - "A toxic batch of home-brewed
alcohol has killed 31 people
in several towns in central
Pakistan, police and hospital
officials say." - ], - [ - " BEIJING (Reuters) - North
Korea is committed to holding
six-party talks aimed at
resolving the crisis over its
nuclear weapons program, but
has not indicated when, a top
British official said on
Tuesday." - ], - [ - " BAGHDAD (Reuters) - Iraq's
interim government extended
the closure of Baghdad
international airport
indefinitely on Saturday
under emergency rule imposed
ahead of this week's U.S.-led
offensive on Falluja." - ], - [ - "Rivaling Bush vs. Kerry for
bitterness, doctors and trial
lawyers are squaring off this
fall in an unprecedented four-
state struggle over limiting
malpractice awards..." - ], - [ - "AP - Hundreds of tribesmen
gathered Tuesday near the area
where suspected al-Qaida-
linked militants are holding
two Chinese engineers and
demanding safe passage to
their reputed leader, a former
U.S. prisoner from Guantanamo
Bay, Cuba, officials and
residents said." - ], - [ - "In an alarming development,
high-precision equipment and
materials which could be used
for making nuclear bombs have
disappeared from some Iraqi
facilities, the United Nations
watchdog agency has said." - ], - [ - "A US airman dies and two are
hurt as a helicopter crashes
due to technical problems in
western Afghanistan." - ], - [ - "Jacques Chirac has ruled out
any withdrawal of French
troops from Ivory Coast,
despite unrest and anti-French
attacks, which have forced the
evacuation of thousands of
Westerners." - ], - [ - "Japanese Prime Minister
Junichiro Koizumi reshuffled
his cabinet yesterday,
replacing several top
ministers in an effort to
boost his popularity,
consolidate political support
and quicken the pace of
reforms in the world #39;s
second-largest economy." - ], - [ - "TBILISI (Reuters) - At least
two Georgian soldiers were
killed and five wounded in
artillery fire with
separatists in the breakaway
region of South Ossetia,
Georgian officials said on
Wednesday." - ], - [ - "Laksamana.Net - Two Indonesian
female migrant workers freed
by militants in Iraq are
expected to arrive home within
a day or two, the Foreign
Affairs Ministry said
Wednesday (6/10/04)." - ], - [ - "A bus was hijacked today and
shots were fired at police who
surrounded it on the outskirts
of Athens. Police did not know
how many passengers were
aboard the bus." - ], - [ - "AP - President Bashar Assad
shuffled his Cabinet on
Monday, just weeks after the
United States and the United
Nations challenged Syria over
its military presence in
Lebanon and the security
situation along its border
with Iraq." - ], - [ - "AP - President Vladimir Putin
has signed a bill confirming
Russia's ratification of the
Kyoto Protocol, the Kremlin
said Friday, clearing the way
for the global climate pact to
come into force early next
year." - ], - [ - "AP - The authenticity of newly
unearthed memos stating that
George W. Bush failed to meet
standards of the Texas Air
National Guard during the
Vietnam War was questioned
Thursday by the son of the
late officer who reportedly
wrote the memos." - ], - [ - "Canadian Press - OAKVILLE,
Ont. (CP) - The body of a
missing autistic man was
pulled from a creek Monday,
just metres from where a key
piece of evidence was
uncovered but originally
overlooked because searchers
had the wrong information." - ], - [ - "AFP - German Chancellor
Gerhard Schroeder arrived in
Libya for an official visit
during which he is to hold
talks with Libyan leader
Moamer Kadhafi." - ], - [ - "The government will examine
claims 100,000 Iraqi civilians
have been killed since the US-
led invasion, Jack Straw says." - ], - [ - "Eton College and Clarence
House joined forces yesterday
to deny allegations due to be
made at an employment tribunal
today by a former art teacher
that she improperly helped
Prince Harry secure an A-level
pass in art two years ago." - ], - [ - "AFP - Great Britain's chances
of qualifying for the World
Group of the Davis Cup were
evaporating rapidly after
Austria moved into a 2-1 lead
following the doubles." - ], - [ - "Asia-Pacific leaders meet in
Australia to discuss how to
keep nuclear weapons out of
the hands of extremists." - ], - [ - " TALL AFAR, Iraq -- A three-
foot-high coil of razor wire,
21-ton armored vehicles and
American soldiers with black
M-4 assault rifles stood
between tens of thousands of
people and their homes last
week." - ], - [ - "LAKE GEORGE, N.Y. - Even
though he's facing double hip
replacement surgery, Bill
Smith is more than happy to
struggle out the door each
morning, limp past his brand
new P.T..." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
poured cold water on Tuesday
on recent international
efforts to restart stalled
peace talks with Syria, saying
there was \"no possibility\" of
returning to previous
discussions." - ], - [ - "Dutch smugness was slapped
hard during the past
fortnight. The rude awakening
began with the barbaric
slaying of controversial
filmmaker Theo van Gogh on
November 2. Then followed a
reciprocal cycle of some" - ], - [ - "pee writes quot;A passenger
on a commuter plane in
northern Norway attacked both
pilots and at least one
passenger with an axe as the
aircraft was coming in to
land." - ], - [ - "Prime Minister Ariel Sharon
pledged Sunday to escalate a
broad Israeli offensive in
northern Gaza, saying troops
will remain until Palestinian
rocket attacks are halted.
Israeli officials said the
offensive -- in which 58
Palestinians and three
Israelis have been killed --
will help clear the way for an
Israeli withdrawal." - ], - [ - "NEW YORK - Wall Street
professionals know to keep
their expectations in check in
September, historically the
worst month of the year for
stocks. As summertime draws to
a close, money managers are
getting back to business,
cleaning house, and often
sending the market lower in
the process..." - ], - [ - "A group linked to al Qaeda
ally Abu Musab al-Zarqawi said
it had tried to kill Iraq
#39;s environment minister on
Tuesday and warned it would
not miss next time, according
to an Internet statement." - ], - [ - "The Israeli military killed
four Palestinian militants on
Wednesday as troops in tanks
and armored vehicles pushed
into another town in the
northern Gaza Strip, extending" - ], - [ - "KIRKUK, Iraq - A suicide
attacker detonated a car bomb
Saturday outside a police
academy in the northern Iraqi
city of Kirkuk as hundreds of
trainees and civilians were
leaving for the day, killing
at least 20 people and
wounding 36, authorities said.
Separately, U.S and Iraqi
forces clashed with insurgents
in another part of northern
Iraq after launching an
operation to destroy an
alleged militant cell in the
town of Tal Afar, the U.S..." - ], - [ - "AP - Many states are facing
legal challenges over possible
voting problems Nov. 2. A look
at some of the developments
Thursday:" - ], - [ - "Israeli troops withdrew from
the southern Gaza Strip town
of Khan Yunis on Tuesday
morning, following a 30-hour
operation that left 17
Palestinians dead." - ], - [ - "PM-designate Omar Karameh
forms a new 30-member cabinet
which includes women for the
first time." - ], - [ - "Bahrain #39;s king pardoned a
human rights activist who
convicted of inciting hatred
of the government and
sentenced to one year in
prison Sunday in a case linked
to criticism of the prime
minister." - ], - [ - "Leaders from 38 Asian and
European nations are gathering
in Vietnam for a summit of the
Asia-Europe Meeting, know as
ASEM. One thousand delegates
are to discuss global trade
and regional politics during
the two-day forum." - ], - [ - "A US soldier has pleaded
guilty to murdering a wounded
16-year-old Iraqi boy. Staff
Sergeant Johnny Horne was
convicted Friday of the
unpremeditated murder" - ], - [ - "Guinea-Bissau #39;s army chief
of staff and former interim
president, General Verissimo
Correia Seabra, was killed
Wednesday during unrest by
mutinous soldiers in the
former Portuguese" - ], - [ - "31 October 2004 -- Exit polls
show that Prime Minister
Viktor Yanukovich and
challenger Viktor Yushchenko
finished on top in Ukraine
#39;s presidential election
today and will face each other
in a run-off next month." - ], - [ - "Rock singer Bono pledges to
spend the rest of his life
trying to eradicate extreme
poverty around the world." - ], - [ - "AP - Just when tourists
thought it was safe to go back
to the Princess Diana memorial
fountain, the mud has struck." - ], - [ - "AP - Three times a week, The
Associated Press picks an
issue and asks President Bush
and Democratic presidential
candidate John Kerry a
question about it. Today's
question and their responses:" - ], - [ - "In an apparent damage control
exercise, Russian President
Vladimir Putin on Saturday
said he favored veto rights
for India as new permanent
member of the UN Security
Council." - ], - [ - "AP - Nigeria's Senate has
ordered a subsidiary of
petroleum giant Royal/Dutch
Shell to pay a Nigerian ethnic
group #36;1.5 billion for oil
spills in their homelands, but
the legislative body can't
enforce the resolution, an
official said Wednesday." - ], - [ - "Australian troops in Baghdad
came under attack today for
the first time since the end
of the Iraq war when a car
bomb exploded injuring three
soldiers and damaging an
Australian armoured convoy." - ], - [ - "Pakistans decision to refuse
the International Atomic
Energy Agency to have direct
access to Dr AQ Khan is
correct on both legal and
political counts." - ], - [ - "MANILA, 4 December 2004 - With
floods receding, rescuers
raced to deliver food to
famished survivors in
northeastern Philippine
villages isolated by back-to-
back storms that left more
than 650 people dead and
almost 400 missing." - ], - [ - "Talks on where to build the
world #39;s first nuclear
fusion reactor ended without a
deal on Tuesday but the
European Union said Japan and
the United States no longer
firmly opposed its bid to put
the plant in France." - ], - [ - "CLEVELAND - The White House
said Vice President Dick
Cheney faces a \"master
litigator\" when he debates
Sen. John Edwards Tuesday
night, a backhanded compliment
issued as the Republican
administration defended itself
against criticism that it has
not acknowledged errors in
waging war in Iraq..." - ], - [ - "SEOUL (Reuters) - The chairman
of South Korea #39;s ruling
Uri Party resigned on Thursday
after saying his father had
served as a military police
officer during Japan #39;s
1910-1945 colonial rule on the
peninsula." - ], - [ - "ALERE, Uganda -- Kasmiro
Bongonyinge remembers sitting
up suddenly in his bed. It was
just after sunrise on a summer
morning two years ago, and the
old man, 87 years old and
blind, knew something was
wrong." - ], - [ - "JAKARTA - Official results
have confirmed former army
general Susilo Bambang
Yudhoyono as the winner of
Indonesia #39;s first direct
presidential election, while
incumbent Megawati
Sukarnoputri urged her nation
Thursday to wait for the
official announcement" - ], - [ - "Reuters - A ragged band of
children\\emerges ghost-like
from mists in Ethiopia's
highlands,\\thrusting bunches
of carrots at a car full of
foreigners." - ], - [ - "AP - A U.N. human rights
expert criticized the U.S.-led
coalition forces in
Afghanistan for violating
international law by allegedly
beating Afghans to death and
forcing some to remove their
clothes or wear hoods." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
said on Thursday Yasser
Arafat's death could be a
turning point for peacemaking
but he would pursue a
unilateral plan that would
strip Palestinians of some
land they want for a state." - ], - [ - " AL-ASAD AIRBASE, Iraq
(Reuters) - Defense Secretary
Donald Rumsfeld swept into an
airbase in Iraq's western
desert Sunday to make a
first-hand evaluation of
operations to quell a raging
Iraqi insurgency in his first
such visit in five months." - ], - [ - "WASHINGTON - Democrat John
Kerry accused President Bush
on Monday of sending U.S.
troops to the \"wrong war in
the wrong place at the wrong
time\" and said he'd try to
bring them all home in four
years..." - ], - [ - "More lorry drivers are
bringing supplies to Nepal's
capital in defiance of an
indefinite blockade by Maoist
rebels." - ], - [ - " BEIJING (Reuters) - Floods
and landslides have killed 76
people in southwest China in
the past four days and washed
away homes and roads, knocked
down power lines and cut off
at least one city, state
media said on Monday." - ], - [ - "AP - Victims of the Sept. 11
attacks were mourned worldwide
Saturday, but in the Middle
East, amid sympathy for the
dead, Arabs said Washington's
support for Israel and the war
on terror launched in the
aftermath of the World Trade
Center's collapse have only
fueled anger and violence." - ], - [ - "SEATTLE - Ichiro Suzuki set
the major league record for
hits in a season with 258,
breaking George Sisler's
84-year-old mark with a pair
of singles Friday night. The
Seattle star chopped a leadoff
single in the first inning,
then made history with a
grounder up the middle in the
third..." - ], - [ - "The intruder who entered
British Queen Elizabeth II
#39;s official Scottish
residence and caused a
security scare was a reporter
from the London-based Sunday
Times newspaper, local media
reported Friday." - ], - [ - "Canadian Press - FREDERICTON
(CP) - A New Brunswick truck
driver arrested in Ontario
this week has been accused by
police of stealing 50,000 cans
of Moosehead beer." - ], - [ - "Chinese authorities detained a
prominent, U.S.-based Buddhist
leader in connection with his
plans to reopen an ancient
temple complex in the Chinese
province of Inner Mongolia
last week and have forced
dozens of his American
followers to leave the region,
local officials said
Wednesday." - ], - [ - "Adorned with Turkish and EU
flags, Turkey #39;s newspapers
hailed Thursday an official EU
report recommending the
country start talks to join
the bloc, while largely
ignoring the stringent
conditions attached to the
announcement." - ], - [ - "Thailand's prime minister
visits the southern town where
scores of Muslims died in army
custody after a rally." - ], - [ - "Beijing: At least 170 miners
were trapped underground after
a gas explosion on Sunday
ignited a fire in a coalmine
in north-west China #39;s
Shaanxi province, reports
said." - ], - [ - "SAMARRA (Iraq): With renewe d
wave of skirmishes between the
Iraqi insurgents and the US-
led coalition marines, several
people including top police
officers were put to death on
Saturday." - ], - [ - "AFP - Like most US Latinos,
members of the extended
Rodriguez family say they will
cast their votes for Democrat
John Kerry in next month's
presidential polls." - ], - [ - "FALLUJAH, Iraq -- Four Iraqi
fighters huddled in a trench,
firing rocket-propelled
grenades at Lieutenant Eric
Gregory's Bradley Fighting
Vehicle and the US tanks and
Humvees that were lumbering
through tight streets between
boxlike beige houses." - ], - [ - "AP - Several thousand
Christians who packed a
cathedral compound in the
Egyptian capital hurled stones
at riot police Wednesday to
protest a woman's alleged
forced conversion to Islam. At
least 30 people were injured." - ], - [ - "A group of Saudi religious
scholars have signed an open
letter urging Iraqis to
support jihad against US-led
forces. quot;Fighting the
occupiers is a duty for all
those who are able, quot; they
said in a statement posted on
the internet at the weekend." - ], - [ - "Mountaineers retrieve three
bodies believed to have been
buried for 22 years on an
Indian glacier." - ], - [ - "President Thabo Mbeki met with
Ivory Coast Prime Minister
Seydou Diarra for three hours
yesterday as part of talks
aimed at bringing peace to the
conflict-wracked Ivory Coast." - ], - [ - " KATHMANDU (Reuters) - Nepal's
Maoist rebels have
temporarily suspended a
crippling economic blockade of
the capital from Wednesday,
saying the move was in
response to popular appeals." - ], - [ - "Reuters - An Algerian
suspected of being a leader\\of
the Madrid train bombers has
been identified as one of
seven\\people who blew
themselves up in April to
avoid arrest, Spain's\\Interior
Ministry said on Friday." - ], - [ - "KABUL: An Afghan man was found
guilty on Saturday of killing
four journalists in 2001,
including two from Reuters,
and sentenced to death." - ], - [ - "Yasser Arafat, the leader for
decades of a fight for
Palestinian independence from
Israel, has died at a military
hospital in Paris, according
to news reports." - ], - [ - " JABALYA, Gaza Strip (Reuters)
- Israel pulled most of its
forces out of the northern
Gaza Strip Saturday after a
four-day incursion it said
was staged to halt Palestinian
rocket attacks on southern
Israeli towns." - ], - [ - "THE Turkish embassy in Baghdad
was investigating a television
report that two Turkish
hostages had been killed in
Iraq, but no confirmation was
available so far, a senior
Turkish diplomat said today." - ], - [ - "Reuters - Thousands of
supporters of
Ukraine's\\opposition leader,
Viktor Yushchenko, celebrated
on the streets\\in the early
hours on Monday after an exit
poll showed him\\winner of a
bitterly fought presidential
election." - ], - [ - "LONDON : The United States
faced rare criticism over
human rights from close ally
Britain, with an official
British government report
taking Washington to task over
concerns about Iraq and the
Guantanamo Bay jail." - ], - [ - "LONDON - A bomb threat that
mentioned Iraq forced a New
York-bound Greek airliner to
make an emergency landing
Sunday at London's Stansted
Airport escorted by military
jets, authorities said. An
airport spokeswoman said an
Athens newspaper had received
a phone call saying there was
a bomb on board the Olympic
Airlines plane..." - ], - [ - "ATHENS, Greece - Sheryl
Swoopes made three big plays
at the end - two baskets and
another on defense - to help
the United States squeeze out
a 66-62 semifinal victory over
Russia on Friday. Now, only
one game stands between the
U.S..." - ], - [ - "Scientists are developing a
device which could improve the
lives of kidney dialysis
patients." - ], - [ - "KABUL, Afghanistan The Afghan
government is blaming drug
smugglers for yesterday #39;s
attack on the leading vice
presidential candidate ." - ], - [ - "One of the leading figures in
the Greek Orthodox Church, the
Patriarch of Alexandria Peter
VII, has been killed in a
helicopter crash in the Aegean
Sea." - ], - [ - "CANBERRA, Australia -- The
sweat-stained felt hats worn
by Australian cowboys, as much
a part of the Outback as
kangaroos and sun-baked soil,
may be heading for the history
books. They fail modern
industrial safety standards." - ], - [ - "A London-to-Washington flight
is diverted after a security
alert involving the singer
formerly known as Cat Stevens." - ], - [ - "AP - President Bush declared
Friday that charges of voter
fraud have cast doubt on the
Ukrainian election, and warned
that any European-negotiated
pact on Iran's nuclear program
must ensure the world can
verify Tehran's compliance." - ], - [ - "TheSpaceShipOne team is handed
the \\$10m cheque and trophy it
won for claiming the Ansari
X-Prize." - ], - [ - "Security officials have
identified six of the
militants who seized a school
in southern Russia as being
from Chechnya, drawing a
strong connection to the
Chechen insurgents who have
been fighting Russian forces
for years." - ], - [ - "SEOUL -- North Korea set three
conditions yesterday to be met
before it would consider
returning to six-party talks
on its nuclear programs." - ], - [ - "US-backed Iraqi commandos were
poised Friday to storm rebel
strongholds in the northern
city of Mosul, as US military
commanders said they had
quot;broken the back quot; of
the insurgency with their
assault on the former rebel
bastion of Fallujah." - ], - [ - "JERUSALEM (Reuters) - Prime
Minister Ariel Sharon, facing
a party mutiny over his plan
to quit the Gaza Strip, has
approved 1,000 more Israeli
settler homes in the West Bank
in a move that drew a cautious
response on Tuesday from ..." - ], - [ - "GHAZNI, Afghanistan, 6 October
2004 - Wartime security was
rolled out for Afghanistans
interim President Hamid Karzai
as he addressed his first
election campaign rally
outside the capital yesterday
amid spiraling violence." - ], - [ - "China has confirmed that it
found a deadly strain of bird
flu in pigs as early as two
years ago. China #39;s
Agriculture Ministry said two
cases had been discovered, but
it did not say exactly where
the samples had been taken." - ], - [ - "AP - Ten years after the Irish
Republican Army's momentous
cease-fire, negotiations
resumed Wednesday in hope of
reviving a Catholic-Protestant
administration, an elusive
goal of Northern Ireland's
hard-fought peace process." - ], - [ - " SANTO DOMINGO, Dominican
Republic, Sept. 18 -- Tropical
Storm Jeanne headed for the
Bahamas on Saturday after an
assault on the Dominican
Republic that killed 10
people, destroyed hundreds of
houses and forced thousands
from their homes." - ], - [ - "An explosion tore apart a car
in Gaza City Monday, killing
at least one person,
Palestinian witnesses said.
They said Israeli warplanes
were circling overhead at the
time of the blast, indicating
a possible missile strike." - ], - [ - "Beijing, Oct. 25 (PTI): China
and the US today agreed to
work jointly to re-energise
the six-party talks mechanism
aimed at dismantling North
Korea #39;s nuclear programmes
while Washington urged Beijing
to resume" - ], - [ - "AFP - Sporadic gunfire and
shelling took place overnight
in the disputed Georgian
region of South Ossetia in
violation of a fragile
ceasefire, wounding seven
Georgian servicemen." - ], - [ - " FALLUJA, Iraq (Reuters) -
U.S. forces hit Iraq's rebel
stronghold of Falluja with the
fiercest air and ground
bombardment in months, as
insurgents struck back on
Saturday with attacks that
killed up to 37 people in
Samarra." - ], - [ - " NAJAF, Iraq (Reuters) - The
fate of a radical Shi'ite
rebellion in the holy city of
Najaf was uncertain Friday
amid disputed reports that
Iraqi police had gained
control of the Imam Ali
Mosque." - ], - [ - "Until this week, only a few
things about the strange,
long-ago disappearance of
Charles Robert Jenkins were
known beyond a doubt. In the
bitter cold of Jan. 5, 1965,
the 24-year-old US Army
sergeant was leading" - ], - [ - "The United States on Tuesday
modified slightly a threat of
sanctions on Sudan #39;s oil
industry in a revised text of
its UN resolution on
atrocities in the country
#39;s Darfur region." - ], - [ - "AP - France intensified
efforts Tuesday to save the
lives of two journalists held
hostage in Iraq, and the Arab
League said the militants'
deadline for France to revoke
a ban on Islamic headscarves
in schools had been extended." - ], - [ - "At least 12 people die in an
explosion at a fuel pipeline
on the outskirts of Nigeria's
biggest city, Lagos." - ], - [ - "Volkswagen demanded a two-year
wage freeze for the
170,000-strong workforce at
Europe #39;s biggest car maker
yesterday, provoking union
warnings of imminent conflict
at key pay and conditions
negotiations." - ], - [ - "Citing security concerns, the
U.S. Embassy on Thursday
banned its employees from
using the highway linking the
embassy area to the
international airport, a
10-mile stretch of road
plagued by frequent suicide
car-bomb attacks." - ], - [ - "AP - Tom Daschle bade his
fellow Senate Democrats
farewell Tuesday with a plea
that they seek common ground
with Republicans yet continue
to fight for the less
fortunate." - ], - [ - "AP - Police defused a bomb in
a town near Prime Minister
Silvio Berlusconi's villa on
the island of Sardinia on
Wednesday shortly after
British Prime Minister Tony
Blair finished a visit there
with the Italian leader." - ], - [ - "The coffin of Yasser Arafat,
draped with the Palestinian
flag, was bound for Ramallah
in the West Bank Friday,
following a formal funeral on
a military compound near
Cairo." - ], - [ - "US Ambassador to the United
Nations John Danforth resigned
on Thursday after serving in
the post for less than six
months. Danforth, 68, said in
a letter released Thursday" - ], - [ - "ISLAMABAD, Pakistan -- Photos
were published yesterday in
newspapers across Pakistan of
six terror suspects, including
a senior Al Qaeda operative,
the government says were
behind attempts to assassinate
the nation's president." - ], - [ - " ATHENS (Reuters) - The Athens
Paralympics canceled
celebrations at its closing
ceremony after seven
schoolchildren traveling to
watch the event died in a bus
crash on Monday." - ], - [ - "DUBAI : An Islamist group has
threatened to kill two Italian
women held hostage in Iraq if
Rome does not withdraw its
troops from the war-torn
country within 24 hours,
according to an internet
statement." - ], - [ - "A heavy quake rocked Indonesia
#39;s Papua province killing
at least 11 people and
wounding 75. The quake
destroyed 150 buildings,
including churches, mosques
and schools." - ], - [ - "Reuters - A small group of
suspected\\gunmen stormed
Uganda's Water Ministry
Wednesday and took
three\\people hostage to
protest against proposals to
allow President\\Yoweri
Museveni for a third
term.\\Police and soldiers with
assault rifles cordoned off
the\\three-story building, just
328 feet from Uganda's
parliament\\building in the
capital Kampala." - ], - [ - "Venezuela suggested Friday
that exiles living in Florida
may have masterminded the
assassination of a prosecutor
investigating a short-lived
coup against leftist President
Hugo Chvez" - ], - [ - "Facing a popular outcry at
home and stern warnings from
Europe, the Turkish government
discreetly stepped back
Tuesday from a plan to
introduce a motion into a
crucial penal reform bill to
make adultery a crime
punishable by prison." - ], - [ - "North-west Norfolk MP Henry
Bellingham has called for the
release of an old college
friend accused of plotting a
coup in Equatorial Guinea." - ], - [ - "AFP - Want to buy a castle?
Head for the former East
Germany." - ], - [ - "AFP - Steven Gerrard has moved
to allay Liverpool fans' fears
that he could be out until
Christmas after breaking a
metatarsal bone in his left
foot." - ], - [ - "MINSK - Legislative elections
in Belarus held at the same
time as a referendum on
whether President Alexander
Lukashenko should be allowed
to seek a third term fell
significantly short of
democratic standards, foreign
observers said here Monday." - ], - [ - "An Olympic sailor is charged
with the manslaughter of a
Briton who died after being
hit by a car in Athens." - ], - [ - "AP - Secretary of State Colin
Powell on Friday praised the
peace deal that ended fighting
in Iraq's holy city of Najaf
and said the presence of U.S.
forces in the area helped make
it possible." - ], - [ - "26 August 2004 -- Iraq #39;s
top Shi #39;ite cleric, Grand
Ayatollah Ali al-Sistani,
arrived in the city of Al-
Najaf today in a bid to end a
weeks-long conflict between US
forces and militiamen loyal to
Shi #39;ite cleric Muqtada al-
Sadr." - ], - [ - "PARIS : French trade unions
called on workers at France
Telecom to stage a 24-hour
strike September 7 to protest
government plans to privatize
the public telecommunications
operator, union sources said." - ], - [ - "The Indonesian tourism
industry has so far not been
affected by last week #39;s
bombing outside the Australian
embassy in Jakarta and
officials said they do not
expect a significant drop in
visitor numbers as a result of
the attack." - ], - [ - "MARK Thatcher will have to
wait until at least next April
to face trial on allegations
he helped bankroll a coup
attempt in oil-rich Equatorial
Guinea." - ], - [ - "NEW YORK - A drop in oil
prices and upbeat outlooks
from Wal-Mart and Lowe's
helped send stocks sharply
higher Monday on Wall Street,
with the swing exaggerated by
thin late summer trading. The
Dow Jones industrials surged
nearly 130 points..." - ], - [ - "ROSTOV-ON-DON, Russia --
Hundreds of protesters
ransacked and occupied the
regional administration
building in a southern Russian
province Tuesday, demanding
the resignation of the region
#39;s president, whose former
son-in-law has been linked to
a multiple" - ], - [ - "AFP - Iraqi Foreign Minister
Hoshyar Zebari arrived
unexpectedly in the holy city
of Mecca Wednesday where he
met Crown Prince Abdullah bin
Abdul Aziz, the official SPA
news agency reported." - ], - [ - "Haitian police and UN troops
moved into a slum neighborhood
on Sunday and cleared street
barricades that paralyzed a
part of the capital." - ], - [ - "withdrawal of troops and
settlers from occupied Gaza
next year. Militants seek to
claim any pullout as a
victory. quot;Islamic Jihad
will not be broken by this
martyrdom, quot; said Khaled
al-Batsh, a senior political
leader in Gaza." - ], - [ - "The U.S. military has found
nearly 20 houses where
intelligence officers believe
hostages were tortured or
killed in this city, including
the house with the cage that
held a British contractor who
was beheaded last month." - ], - [ - "AFP - Opponents of the Lao
government may be plotting
bomb attacks in Vientiane and
other areas of Laos timed to
coincide with a summit of
Southeast Asian leaders the
country is hosting next month,
the United States said." - ], - [ - "AP - Russia agreed Thursday to
send warships to help NATO
naval patrols that monitor
suspicious vessels in the
Mediterranean, part of a push
for closer counterterrorism
cooperation between Moscow and
the western alliance." - ], - [ - "A military plane crashed in
the mountains near Caracas,
killing all 16 persons on
board, including two high-
ranking military officers,
officials said." - ], - [ - "A voice recording said to be
that of suspected Al Qaeda
commander Abu Mussab al-
Zarqawi, claims Iraq #39;s
Prime Minister Iyad Allawi is
the militant network #39;s
number one target." - ], - [ - "BEIJING -- More than a year
after becoming China's
president, Hu Jintao was
handed the full reins of power
yesterday when his
predecessor, Jiang Zemin, gave
up the nation's most powerful
military post." - ], - [ - "AP - Greenpeace activists
scaled the walls of Ford Motor
Co.'s Norwegian headquarters
Tuesday to protest plans to
destroy hundreds of non-
polluting electric cars." - ], - [ - "AFP - The chances of Rupert
Murdoch's News Corp relocating
from Australia to the United
States have increased after
one of its biggest
institutional investors has
chosen to abstain from a vote
next week on the move." - ], - [ - "AFP - An Indian minister said
a school text-book used in the
violence-prone western state
of Gujarat portrayed Adolf
Hitler as a role model." - ], - [ - "DOVER, N.H. (AP) -- Democrat
John Kerry is seizing on the
Bush administration's failure
to secure hundreds of tons of
explosives now missing in
Iraq." - ], - [ - "United Nations officials
report security breaches in
internally displaced people
and refugee camps in Sudan
#39;s embattled Darfur region
and neighboring Chad." - ], - [ - "KINGSTON, Jamaica - Hurricane
Ivan's deadly winds and
monstrous waves bore down on
Jamaica on Friday, threatening
a direct hit on its densely
populated capital after
ravaging Grenada and killing
at least 33 people. The
Jamaican government ordered
the evacuation of half a
million people from coastal
areas, where rains on Ivan's
outer edges were already
flooding roads..." - ], - [ - "North Korea has denounced as
quot;wicked terrorists quot;
the South Korean officials who
orchestrated last month #39;s
airlift to Seoul of 468 North
Korean defectors." - ], - [ - "The Black Watch regiment has
returned to its base in Basra
in southern Iraq after a
month-long mission standing in
for US troops in a more
violent part of the country,
the Ministry of Defence says." - ], - [ - "AP - A senior Congolese
official said Tuesday his
nation had been invaded by
neighboring Rwanda, and U.N.
officials said they were
investigating claims of
Rwandan forces clashing with
militias in the east." - ], - [ - "UNITED NATIONS - The United
Nations #39; nuclear agency
says it is concerned about the
disappearance of equipment and
materials from Iraq that could
be used to make nuclear
weapons." - ], - [ - " BRUSSELS (Reuters) - The EU's
historic deal with Turkey to
open entry talks with the vast
Muslim country was hailed by
supporters as a bridge builder
between Europe and the Islamic
world." - ], - [ - "Iraqi President Ghazi al-
Yawar, who was due in Paris on
Sunday to start a European
tour, has postponed his visit
to France due to the ongoing
hostage drama involving two
French journalists, Arab
diplomats said Friday." - ], - [ - " SAO PAULO, Brazil (Reuters) -
President Luiz Inacio Lula da
Silva's Workers' Party (PT)
won the mayoralty of six state
capitals in Sunday's municipal
vote but was forced into a
run-off to defend its hold on
the race's biggest prize, the
city of Sao Paulo." - ], - [ - "ATHENS, Greece - They are
America's newest golden girls
- powerful and just a shade
from perfection. The U.S..." - ], - [ - "AMMAN, Sept. 15. - The owner
of a Jordanian truck company
announced today that he had
ordered its Iraq operations
stopped in a bid to save the
life of a driver held hostage
by a militant group." - ], - [ - "Israel is prepared to back a
Middle East conference
convened by Tony Blair early
next year despite having
expressed fears that the
British plans were over-
ambitious and designed" - ], - [ - "AP - U.S. State Department
officials learned that seven
American children had been
abandoned at a Nigerian
orphanage but waited more than
a week to check on the youths,
who were suffering from
malnutrition, malaria and
typhoid, a newspaper reported
Saturday." - ], - [ - "\\Angry mobs in Ivory Coast's
main city, Abidjan, marched on
the airport, hours after it
came under French control." - ], - [ - "Several workers are believed
to have been killed and others
injured after a contruction
site collapsed at Dubai
airport. The workers were
trapped under rubble at the
site of a \\$4." - ], - [ - "Talks between Sudan #39;s
government and two rebel
groups to resolve the nearly
two-year battle resume Friday.
By Abraham McLaughlin Staff
writer of The Christian
Science Monitor." - ], - [ - "Stansted airport is the
designated emergency landing
ground for planes in British
airspace hit by in-flight
security alerts. Emergency
services at Stansted have
successfully dealt" - ], - [ - "The massive military operation
to retake Fallujah has been
quot;accomplished quot;, a
senior Iraqi official said.
Fierce fighting continued in
the war-torn city where
pockets of resistance were
still holding out against US
forces." - ], - [ - "There are some signs of
progress in resolving the
Nigerian conflict that is
riling global oil markets. The
leader of militia fighters
threatening to widen a battle
for control of Nigeria #39;s
oil-rich south has" - ], - [ - "A strong earthquake hit Taiwan
on Monday, shaking buildings
in the capital Taipei for
several seconds. No casualties
were reported." - ], - [ - "A policeman ran amok at a
security camp in Indian-
controlled Kashmir after an
argument and shot dead seven
colleagues before he was
gunned down, police said on
Sunday." - ], - [ - "New York police have developed
a pre-emptive strike policy,
cutting off demonstrations
before they grow large." - ], - [ - "Bulgaria has started its first
co-mission with the EU in
Bosnia and Herzegovina, along
with some 30 countries,
including Canada and Turkey." - ], - [ - "AP - The pileup of events in
the city next week, including
the Republican National
Convention, will add to the
security challenge for the New
York Police Department, but
commissioner Ray Kelly says,
\"With a big, experienced
police force, we can do it.\"" - ], - [ - "LONDON, Dec 11 (IranMania) -
Iraqi Vice-President Ibrahim
al-Jaafari refused to believe
in remarks published Friday
that Iran was attempting to
influence Iraqi polls with the
aim of creating a
quot;crescent quot; dominated
by Shiites in the region." - ], - [ - "The late Princess Dianas
former bodyguard, Ken Wharfe,
dismisses her suspicions that
one of her lovers was bumped
off. Princess Diana had an
affair with Barry Mannakee, a
policeman who was assigned to
protect her." - ], - [ - "Long considered beyond the
reach of mainland mores, the
Florida city is trying to
limit blatant displays of
sexual behavior." - ], - [ - "Senator John Kerry said today
that the war in Iraq was a
\"profound diversion\" from the
war on terror and Osama bin
Laden." - ], - [ - "A group claiming to have
captured two Indonesian women
in Iraq has said it will
release them if Jakarta frees
Muslim cleric Abu Bakar Bashir
being held for alleged
terrorist links." - ], - [ - "Indonesian police said
yesterday that DNA tests had
identified a suicide bomber
involved in a deadly attack
this month on the Australian
embassy in Jakarta." - ], - [ - "NEW YORK - Wal-Mart Stores
Inc.'s warning of
disappointing sales sent
stocks fluctuating Monday as
investors' concerns about a
slowing economy offset their
relief over a drop in oil
prices. October contracts
for a barrel of light crude
were quoted at \\$46.48, down
24 cents, on the New York
Mercantile Exchange..." - ], - [ - "Iraq #39;s top Shi #39;ite
cleric made a sudden return to
the country on Wednesday and
said he had a plan to end an
uprising in the quot;burning
city quot; of Najaf, where
fighting is creeping ever
closer to its holiest shrine." - ], - [ - "KABUL, Afghanistan Aug. 22,
2004 - US soldiers sprayed a
pickup truck with bullets
after it failed to stop at a
roadblock in central
Afghanistan, killing two women
and a man and critically
wounding two other" - ], - [ - "SYDNEY -- Prime Minister John
Howard of Australia, a key US
ally and supporter of the Iraq
war, celebrated his election
win over opposition Labor
after voters enjoying the
fruits of a strong economy
gave him another term." - ], - [ - "BAGHDAD, Iraq - Two rockets
hit a downtown Baghdad hotel
housing foreigners and
journalists Thursday, and
gunfire erupted in the
neighborhood across the Tigris
River from the U.S. Embassy
compound..." - ], - [ - "The Prevention of Terrorism
Act 2002 (Pota) polarised the
country, not just by the
manner in which it was pushed
through by the NDA government
through a joint session of
Parliament but by the shabby
and often biased manner in
which it was enforced." - ], - [ - "The US military says marines
in Fallujah shot and killed an
insurgent who engaged them as
he was faking being dead, a
week after footage of a marine
killing an apparently unarmed
and wounded Iraqi caused a
stir in the region." - ], - [ - "Description: NPR #39;s Alex
Chadwick talks to Colin Brown,
deputy political editor for
the United Kingdom #39;s
Independent newspaper,
currently covering the British
Labour Party Conference." - ], - [ - "Hamas vowed revenge yesterday
after an Israeli airstrike in
Gaza killed one of its senior
commanders - the latest
assassination to have weakened
the militant group." - ], - [ - "A senior member of the
Palestinian resistance group
Hamas has been released from
an Israeli prison after
completing a two-year
sentence." - ], - [ - "MPs have announced a new
inquiry into family courts and
whether parents are treated
fairly over issues such as
custody or contact with their
children." - ], - [ - "Canadian Press - MELBOURNE,
Australia (AP) - A 36-year-old
businesswoman was believed to
be the first woman to walk
around Australia on Friday
after striding into her
hometown of Melbourne to
complete her 16,700-kilometre
trek in 365 days." - ], - [ - "Most remaining Pakistani
prisoners held at the US
Guantanamo Bay prison camp are
freed, officials say." - ], - [ - "French police are
investigating an arson-caused
fire at a Jewish Social Center
that might have killed dozens
without the quick response of
firefighters." - ], - [ - "Rodney King, whose videotaped
beating led to riots in Los
Angeles in 1992, is out of
jail now and talking frankly
for the first time about the
riots, himself and the
American way of life." - ], - [ - "AFP - Radical Islamic cleric
Abu Hamza al-Masri was set to
learn Thursday whether he
would be charged under
Britain's anti-terrorism law,
thus delaying his possible
extradition to the United
States to face terrorism-
related charges." - ], - [ - "Louisen Louis, 30, walked
Monday in the middle of a
street that resembled a small
river with brown rivulets and
waves. He wore sandals and had
a cut on one of his big toes." - ], - [ - "A car bomb exploded outside
the main hospital in Chechny
#39;s capital, Grozny, on
Sunday, injuring 17 people in
an attack apparently targeting
members of a Chechen security
force bringing in wounded from
an earlier explosion" - ], - [ - "AP - Gay marriage is emerging
as a big enough issue in
several states to influence
races both for Congress and
the presidency." - ], - [ - "More than 30 aid workers have
been airlifted to safety from
a town in Sudan #39;s troubled
Darfur region after fighting
broke out and their base was
bombed, a British charity
says." - ], - [ - "It #39;s the mildest of mild
winters down here in the south
of Italy and, last weekend at
Bcoli, a pretty suburb by the
seaside west of Naples, the
customers of Pizzeria quot;Da
Enrico quot; were making the
most of it." - ], - [ - "WASHINGTON - A spotty job
market and stagnant paychecks
cloud this Labor Day holiday
for many workers, highlighting
the importance of pocketbook
issues in the presidential
election. \"Working harder
and enjoying it less,\" said
economist Ken Mayland,
president of ClearView
Economics, summing up the
state of working America..." - ], - [ - "Canadian Press - MONTREAL (CP)
- A 19-year-old man charged in
a firebombing at a Jewish
elementary school pleaded
guilty Thursday to arson." - ], - [ - " quot;Resuming uranium
enrichment is not in our
agenda. We are still committed
to the suspension, quot;
Foreign Ministry spokesman
Hamid Reza." - ], - [ - "The U.S. military presence in
Iraq will grow to 150,000
troops by next month, the
highest level since the
invasion last year." - ], - [ - "UPDATE, SUN 9PM: More than a
million people have left their
homes in Cuba, as Hurricane
Ivan approaches. The ferocious
storm is headed that way,
after ripping through the
Cayman Islands, tearing off
roofs, flooding homes and
causing general havoc." - ], - [ - "German Chancellor Gerhard
Schroeder said Sunday that
there was quot;no problem
quot; with Germany #39;s
support to the start of
negotiations on Turkey #39;s
entrance into EU." - ], - [ - "MANILA Fernando Poe Jr., the
popular actor who challenged
President Gloria Macapagal
Arroyo in the presidential
elections this year, died
early Tuesday." - ], - [ - "AMSTERDAM, NETHERLANDS - A
Dutch filmmaker who outraged
members of the Muslim
community by making a film
critical of the mistreatment
of women in Islamic society
was gunned down and stabbed to
death Tuesday on an Amsterdam
street." - ], - [ - "Zimbabwe #39;s most persecuted
white MP began a year of hard
labour last night after
parliament voted to jail him
for shoving the Justice
Minister during a debate over
land seizures." - ], - [ - "A smashing blow is being dealt
to thousands of future
pensioners by a law that has
just been brought into force
by the Federal Government." - ], - [ - "AP - An Israeli helicopter
fired two missiles in Gaza
City after nightfall
Wednesday, one at a building
in the Zeitoun neighborhood,
witnesses said, setting a
fire." - ], - [ - "The Philippines put the toll
at more than 1,000 dead or
missing in four storms in two
weeks but, even with a break
in the weather on Saturday" - ], - [ - "Reuters - Four explosions were
reported at petrol\\stations in
the Madrid area on Friday,
Spanish radio stations\\said,
following a phone warning in
the name of the armed
Basque\\separatist group ETA to
a Basque newspaper." - ], - [ - "WEST PALM BEACH, Fla. -
Hurricane Jeanne got stronger,
bigger and faster as it
battered the Bahamas and bore
down on Florida Saturday,
sending huge waves crashing
onto beaches and forcing
thousands into shelters just
weeks after Frances ravaged
this area..." - ], - [ - "NEW YORK - Elena Dementieva
shook off a subpar serve that
produced 15 double-faults, an
aching left thigh and an upset
stomach to advance to the
semifinals at the U.S. Open
with a 4-6, 6-4, 7-6 (1)
victory Tuesday over Amelie
Mauresmo..." - ], - [ - "Prime Minister Dr Manmohan
Singh inaugurated a research
centre in the Capital on
Thursday to mark 400 years of
compilation of Sikh holy book
the Guru Granth Sahib." - ], - [ - "THE re-election of British
Prime Minister Tony Blair
would be seen as an
endorsement of the military
action in Iraq, Prime Minister
John Howard said today." - ], - [ - "President George W. Bush
pledged Friday to spend some
of the political capital from
his re-election trying to
secure a lasting Middle East
peace, and he envisioned the
establishment" - ], - [ - "NEW DELHI - A bomb exploded
during an Independence Day
parade in India's remote
northeast on Sunday, killing
at least 15 people, officials
said, just an hour after Prime
Minister Manmohan Singh
pledged to fight terrorism.
The outlawed United Liberation
Front of Asom was suspected of
being behind the attack in
Assam state and a second one
later in the area, said Assam
Inspector General of Police
Khagen Sharma..." - ], - [ - "A UN envoy to Sudan will visit
Darfur tomorrow to check on
the government #39;s claim
that some 70,000 people
displaced by conflict there
have voluntarily returned to
their homes, a spokesman said." - ], - [ - "AP - Most of the presidential
election provisional ballots
rejected so far in Ohio came
from people who were not even
registered to vote, election
officials said after spending
nearly two weeks poring over
thousands of disputed votes." - ], - [ - "AP - Rival inmates fought each
other with knives and sticks
Wednesday at a San Salvador
prison, leaving at least 31
people dead and two dozen
injured, officials said." - ], - [ - "BAGHDAD - Two Egyptian
employees of a mobile phone
company were seized when
gunmen stormed into their
Baghdad office, the latest in
a series of kidnappings in the
country." - ], - [ - "BRISBANE, Australia - The body
of a whale resembling a giant
dolphin that washed up on an
eastern Australian beach has
intrigued local scientists,
who agreed Wednesday that it
is rare but are not sure just
how rare." - ], - [ - "President Bush aims to
highlight American drug-
fighting aid in Colombia and
boost a conservative Latin
American leader with a stop in
the Andean nation where
thousands of security forces
are deployed to safeguard his
brief stay." - ], - [ - "Dubai - Former Palestinian
security minister Mohammed
Dahlan said on Monday that a
quot;gang of mercenaries quot;
known to the Palestinian
police were behind the
shooting that resulted in two
deaths in a mourning tent for
Yasser Arafat in Gaza." - ], - [ - "A Frenchman working for Thales
SA, Europe #39;s biggest maker
of military electronics, was
shot dead while driving home
at night in the Saudi Arabian
city of Jeddah." - ], - [ - "China will take tough measures
this winter to improve the
country #39;s coal mine safety
and prevent accidents. State
Councilor Hua Jianmin said
Thursday the industry should
take" - ], - [ - "BAGHDAD (Iraq): As the
intensity of skirmishes
swelled on the soils of Iraq,
dozens of people were put to
death with toxic shots by the
US helicopter gunship, which
targeted the civilians,
milling around a burning
American vehicle in a Baghdad
street on" - ], - [ - "Reuters - A key Iranian
nuclear facility which
the\\U.N.'s nuclear watchdog
has urged Tehran to shut down
is\\nearing completion, a
senior Iranian nuclear
official said on\\Sunday." - ], - [ - "Spain's Football Federation
launches an investigation into
racist comments made by
national coach Luis Aragones." - ], - [ - "Bricks and plaster blew inward
from the wall, as the windows
all shattered and I fell to
the floorwhether from the
shock wave, or just fright, it
wasn #39;t clear." - ], - [ - "Surfersvillage Global Surf
News, 13 September 2004: - -
Hurricane Ivan, one of the
most powerful storms to ever
hit the Caribbean, killed at
least 16 people in Jamaica,
where it wrecked houses and
washed away roads on Saturday,
but appears to have spared" - ], - [ - "LONDON, England -- A US
scientist is reported to have
observed a surprising jump in
the amount of carbon dioxide,
the main greenhouse gas." - ], - [ - "Zimbabwe #39;s ruling Zanu-PF
old guard has emerged on top
after a bitter power struggle
in the deeply divided party
during its five-yearly
congress, which ended
yesterday." - ], - [ - "Reuters - Thousands of
demonstrators pressing
to\\install Ukraine's
opposition leader as president
after a\\disputed election
launched fresh street rallies
in the capital\\for the third
day Wednesday." - ], - [ - "Michael Jackson wishes he had
fought previous child
molestation claims instead of
trying to \"buy peace\", his
lawyer says." - ], - [ - "North Korea says it will not
abandon its weapons programme
after the South admitted
nuclear activities." - ], - [ - "While there is growing
attention to ongoing genocide
in Darfur, this has not
translated into either a
meaningful international
response or an accurate
rendering of the scale and
evident course of the
catastrophe." - ], - [ - "THE prosecution on terrorism
charges of extremist Islamic
cleric and accused Jemaah
Islamiah leader Abu Bakar
Bashir will rely heavily on
the potentially tainted
testimony of at least two
convicted Bali bombers, his
lawyers have said." - ], - [ - "Clashes between US troops and
Sadr militiamen escalated
Thursday, as the US surrounded
Najaf for possible siege." - ], - [ - "AFP - A battle group of
British troops rolled out of
southern Iraq on a US-
requested mission to deadlier
areas near Baghdad, in a major
political gamble for British
Prime Minister Tony Blair." - ], - [ - "over half the children in the
world - suffer extreme
deprivation because of war,
HIV/AIDS or poverty, according
to a report released yesterday
by the United Nations Children
#39;s Fund." - ], - [ - "Reuters - Philippine rescue
teams\\evacuated thousands of
people from the worst flooding
in the\\central Luzon region
since the 1970s as hungry
victims hunted\\rats and birds
for food." - ], - [ - "The Afghan president expresses
deep concern after a bomb
attack which left at least
seven people dead." - ], - [ - "Gardez (Afghanistan), Sept. 16
(Reuters): Afghan President
Hamid Karzai escaped an
assassination bid today when a
rocket was fired at his US
military helicopter as it was
landing in the southeastern
town of Gardez." - ], - [ - "The Jets came up with four
turnovers by Dolphins
quarterback Jay Fiedler in the
second half, including an
interception returned 66 yards
for a touchdown." - ], - [ - "DUBLIN -- Prime Minister
Bertie Ahern urged Irish
Republican Army commanders
yesterday to meet what he
acknowledged was ''a heavy
burden quot;: disarming and
disbanding their organization
in support of Northern
Ireland's 1998 peace accord." - ], - [ - "While reproductive planning
and women #39;s equality have
improved substantially over
the past decade, says a United
Nations report, world
population will increase from
6.4 billion today to 8.9
billion by 2050, with the 50
poorest countries tripling in" - ], - [ - "BAR's Anthony Davidson and
Jenson Button set the pace at
the first Chinese Grand Prix." - ], - [ - "WASHINGTON - Contradicting the
main argument for a war that
has cost more than 1,000
American lives, the top U.S.
arms inspector reported
Wednesday that he found no
evidence that Iraq produced
any weapons of mass
destruction after 1991..." - ], - [ - "AFP - Style mavens will be
scanning the catwalks in Paris
this week for next spring's
must-have handbag, as a
sweeping exhibition at the
French capital's fashion and
textile museum reveals the bag
in all its forms." - ], - [ - "Canadian Press - SAINT-
QUENTIN, N.B. (CP) - A major
highway in northern New
Brunswick remained closed to
almost all traffic Monday, as
local residents protested
planned health care cuts." - ], - [ - " NAJAF, Iraq (Reuters) - A
radical Iraqi cleric leading a
Shi'ite uprising agreed on
Wednesday to disarm his
militia and leave one of the
country's holiest Islamic
shrines after warnings of an
onslaught by government
forces." - ], - [ - "Saudi security forces have
killed a wanted militant near
the scene of a deadly shootout
Thursday. Officials say the
militant was killed in a
gunbattle Friday in the
northern town of Buraida,
hours after one" - ], - [ - "Two South Africans acquitted
by a Zimbabwean court of
charges related to the alleged
coup plot in Equatorial Guinea
are to be questioned today by
the South African authorities." - ], - [ - "MOSCOW (CP) - Russia mourned
89 victims of a double air
disaster today as debate
intensified over whether the
two passenger liners could
have plunged almost
simultaneously from the sky by
accident." - ], - [ - "Australia #39;s prime minister
says a body found in Fallujah
is likely that of kidnapped
aid worker Margaret Hassan.
John Howard told Parliament a
videotape of an Iraqi
terrorist group executing a
Western woman appears to have
been genuine." - ], - [ - "AP - Their first debate less
than a week away, President
Bush and Democrat John Kerry
kept their public schedules
clear on Saturday and began to
focus on their prime-time
showdown." - ], - [ - "PARIS Getting to the bottom of
what killed Yassar Arafat
could shape up to be an ugly
family tug-of-war. Arafat
#39;s half-brother and nephew
want copies of Arafat #39;s
medical records from the
suburban Paris hospital" - ], - [ - " THE HAGUE (Reuters) - Former
Yugoslav President Slobodan
Milosevic condemned his war
crimes trial as a \"pure farce\"
on Wednesday in a defiant
finish to his opening defense
statement against charges of
ethnic cleansing in the
Balkans." - ], - [ - " GUWAHATI, India (Reuters) -
People braved a steady drizzle
to come out to vote in a
remote northeast Indian state
on Thursday, as troops
guarded polling stations in an
election being held under the
shadow of violence." - ], - [ - "AFP - Three of the nine
Canadian sailors injured when
their newly-delivered,
British-built submarine caught
fire in the North Atlantic
were airlifted Wednesday to
hospital in northwest Ireland,
officials said." - ], - [ - "BAGHDAD, Iraq - A series of
strong explosions shook
central Baghdad near dawn
Sunday, and columns of thick
black smoke rose from the
Green Zone where U.S. and
Iraqi government offices are
located..." - ], - [ - "Sven-Goran Eriksson may gamble
by playing goalkeeper Paul
Robinson and striker Jermain
Defoe in Poland." - ], - [ - "Foreign Secretary Jack Straw
has flown to Khartoum on a
mission to pile the pressure
on the Sudanese government to
tackle the humanitarian
catastrophe in Darfur." - ], - [ - "Reuters - A senior U.S.
official said on
Wednesday\\deals should not be
done with hostage-takers ahead
of the\\latest deadline set by
Afghan Islamic militants who
have\\threatened to kill three
kidnapped U.N. workers." - ], - [ - "Sinn Fein leader Gerry Adams
has put the pressure for the
success or failure of the
Northern Ireland assembly
talks firmly on the shoulders
of Ian Paisley." - ], - [ - " JAKARTA (Reuters) - President
Megawati Sukarnoputri urged
Indonesians on Thursday to
accept the results of the
country's first direct
election of a leader, but
stopped short of conceding
defeat." - ], - [ - "ISLAMABAD: Pakistan early
Monday test-fired its
indigenously developed short-
range nuclear-capable Ghaznavi
missile, the Inter Services
Public Relations (ISPR) said
in a statement." - ], - [ - "The trial of a man accused of
murdering York backpacker
Caroline Stuttle begins in
Australia." - ], - [ - "BRUSSELS: The EU sought
Wednesday to keep pressure on
Turkey over its bid to start
talks on joining the bloc, as
last-minute haggling seemed
set to go down to the wire at
a summit poised to give a
green light to Ankara." - ], - [ - "AP - J. Cofer Black, the State
Department official in charge
of counterterrorism, is
leaving government in the next
few weeks." - ], - [ - "AFP - The United States
presented a draft UN
resolution that steps up the
pressure on Sudan over the
crisis in Darfur, including
possible international
sanctions against its oil
sector." - ], - [ - "AFP - At least 33 people were
killed and dozens others
wounded when two bombs ripped
through a congregation of
Sunni Muslims in Pakistan's
central city of Multan, police
said." - ], - [ - "AFP - A series of torchlight
rallies and vigils were held
after darkness fell on this
central Indian city as victims
and activists jointly
commemorated a night of horror
20 years ago when lethal gas
leaked from a pesticide plant
and killed thousands." - ], - [ - "A Zimbabwe court Friday
convicted a British man
accused of leading a coup plot
against the government of oil-
rich Equatorial Guinea on
weapons charges, but acquitted
most of the 69 other men held
with him." - ], - [ - "Canadian Press - TORONTO (CP)
- The fatal stabbing of a
young man trying to eject
unwanted party guests from his
family home, the third such
knifing in just weeks, has
police worried about a
potentially fatal holiday
recipe: teens, alcohol and
knives." - ], - [ - "MOSCOW - A female suicide
bomber set off a shrapnel-
filled explosive device
outside a busy Moscow subway
station on Tuesday night,
officials said, killing 10
people and injuring more than
50." - ], - [ - "ABIDJAN (AFP) - Two Ivory
Coast military aircraft
carried out a second raid on
Bouake, the stronghold of the
former rebel New Forces (FN)
in the divided west African
country, a French military
source told AFP." - ], - [ - "AFP - A state of civil
emergency in the rebellion-hit
Indonesian province of Aceh
has been formally extended by
six month, as the country's
president pledged to end
violence there without foreign
help." - ], - [ - "US Secretary of State Colin
Powell on Monday said he had
spoken to both Indian Foreign
Minister K Natwar Singh and
his Pakistani counterpart
Khurshid Mahmud Kasuri late
last week before the two met
in New Delhi this week for
talks." - ], - [ - "NEW YORK - Victor Diaz hit a
tying, three-run homer with
two outs in the ninth inning,
and Craig Brazell's first
major league home run in the
11th gave the New York Mets a
stunning 4-3 victory over the
Chicago Cubs on Saturday.
The Cubs had much on the
line..." - ], - [ - "AFP - At least 54 people have
died and more than a million
have fled their homes as
torrential rains lashed parts
of India and Bangladesh,
officials said." - ], - [ - "LOS ANGELES - California has
adopted the world's first
rules to reduce greenhouse
emissions for autos, taking
what supporters see as a
dramatic step toward cleaning
up the environment but also
ensuring higher costs for
drivers. The rules may lead
to sweeping changes in
vehicles nationwide,
especially if other states opt
to follow California's
example..." - ], - [ - "AFP - Republican and
Democratic leaders each
declared victory after the
first head-to-head sparring
match between President George
W. Bush and Democratic
presidential hopeful John
Kerry." - ], - [ - "Last night in New York, the UN
secretary-general was given a
standing ovation - a robust
response to a series of
attacks in past weeks." - ], - [ - "JOHANNESBURG -- Meeting in
Nigeria four years ago,
African leaders set a goal
that 60 percent of children
and pregnant women in malaria-
affected areas around the
continent would be sleeping
under bed nets by the end of
2005." - ], - [ - "AP - Duke Bainum outspent Mufi
Hannemann in Honolulu's most
expensive mayoral race, but
apparently failed to garner
enough votes in Saturday's
primary to claim the office
outright." - ], - [ - "POLITICIANS and aid agencies
yesterday stressed the
importance of the media in
keeping the spotlight on the
appalling human rights abuses
taking place in the Darfur
region of Sudan." - ], - [ - "\\Children who have a poor diet
are more likely to become
aggressive and anti-social, US
researchers believe." - ], - [ - "Canadian Press - OTTAWA (CP) -
Contrary to Immigration
Department claims, there is no
shortage of native-borne
exotic dancers in Canada, says
a University of Toronto law
professor who has studied the
strip club business." - ], - [ - "The European Union presidency
yesterday expressed optimism
that a deal could be struck
over Turkey #39;s refusal to
recognize Cyprus in the lead-
up to next weekend #39;s EU
summit, which will decide
whether to give Ankara a date
for the start of accession
talks." - ], - [ - " WASHINGTON (Reuters) -
President Bush on Friday set a
four-year goal of seeing a
Palestinian state established
and he and British Prime
Minister Tony Blair vowed to
mobilize international
support to help make it happen
now that Yasser Arafat is
dead." - ], - [ - " KHARTOUM (Reuters) - Sudan on
Saturday questioned U.N.
estimates that up to 70,000
people have died from hunger
and disease in its remote
Darfur region since a
rebellion began 20 months
ago." - ], - [ - "AFP - At least four Georgian
soldiers were killed and five
wounded in overnight clashes
in Georgia's separatist, pro-
Russian region of South
Ossetia, Georgian officers
near the frontline with
Ossetian forces said early
Thursday." - ], - [ - "The European Commission is
expected later this week to
recommend EU membership talks
with Turkey. Meanwhile, German
Chancellor Gerhard Schroeder
and Turkish Prime Minister
Tayyip Erdogan are
anticipating a quot;positive
report." - ], - [ - "The Canadian government
signalled its intention
yesterday to reintroduce
legislation to decriminalise
the possession of small
amounts of marijuana." - ], - [ - "A screensaver targeting spam-
related websites appears to
have been too successful." - ], - [ - " ABIDJAN (Reuters) - Ivory
Coast warplanes killed nine
French soldiers on Saturday in
a bombing raid during the
fiercest clashes with rebels
for 18 months and France hit
back by destroying most of
the West African country's
small airforce." - ], - [ - "GAZA CITY, Gaza Strip --
Islamic militant groups behind
many suicide bombings
dismissed yesterday a call
from Mahmoud Abbas, the
interim Palestinian leader, to
halt attacks in the run-up to
a Jan. 9 election to replace
Yasser Arafat." - ], - [ - "Secretary of State Colin
Powell is wrapping up an East
Asia trip focused on prodding
North Korea to resume talks
aimed at ending its nuclear-
weapons program." - ], - [ - "The risk of intestinal damage
from common painkillers may be
higher than thought, research
suggests." - ], - [ - "AN earthquake measuring 7.3 on
the Richter Scale hit western
Japan this morning, just hours
after another strong quake
rocked the area." - ], - [ - "Britain #39;s Prince Harry,
struggling to shed a growing
quot;wild child quot; image,
won #39;t apologize to a
photographer he scuffled with
outside an exclusive London
nightclub, a royal spokesman
said on Saturday." - ], - [ - "Pakistan #39;s interim Prime
Minister Chaudhry Shaujaat
Hussain has announced his
resignation, paving the way
for his successor Shauket
Aziz." - ], - [ - "A previously unknown group
calling itself Jamaat Ansar
al-Jihad al-Islamiya says it
set fire to a Jewish soup
kitchen in Paris, according to
an Internet statement." - ], - [ - "The deadliest attack on
Americans in Iraq since May
came as Iraqi officials
announced that Saddam
Hussein's deputy had not been
captured on Sunday." - ], - [ - "A fundraising concert will be
held in London in memory of
the hundreds of victims of the
Beslan school siege." - ], - [ - "AFP - The landmark trial of a
Rwandan Roman Catholic priest
accused of supervising the
massacre of 2,000 of his Tutsi
parishioners during the
central African country's 1994
genocide opens at a UN court
in Tanzania." - ], - [ - "The Irish government has
stepped up its efforts to free
the British hostage in Iraq,
Ken Bigley, whose mother is
from Ireland, by talking to
diplomats from Iran and
Jordan." - ], - [ - "AP - Republican Sen. Lincoln
Chafee, who flirted with
changing political parties in
the wake of President Bush's
re-election victory, says he
will stay in the GOP." - ], - [ - "South Korean President Roh
Moo-hyun pays a surprise visit
to troops in Iraq, after his
government decided to extend
their mandate." - ], - [ - "As the European Union
approaches a contentious
decision - whether to let
Turkey join the club - the
Continent #39;s rulers seem to
have left their citizens
behind." - ], - [ - " BAGHDAD (Reuters) - A
deadline set by militants who
have threatened to kill two
Americans and a Briton seized
in Iraq was due to expire
Monday, and more than two
dozen other hostages were
also facing death unless rebel
demands were met." - ], - [ - "Some Venezuelan television
channels began altering their
programs Thursday, citing
fears of penalties under a new
law restricting violence and
sexual content over the
airwaves." - ], - [ - "afrol News, 4 October -
Hundred years of conservation
efforts have lifted the
southern black rhino
population from about hundred
to 11,000 animals." - ], - [ - "THE death of Philippine movie
star and defeated presidential
candidate Fernando Poe has
drawn some political backlash,
with some people seeking to
use his sudden demise as a
platform to attack President
Gloria Arroyo." - ], - [ - "VIENTIANE, Laos China moved
yet another step closer in
cementing its economic and
diplomatic relationships with
Southeast Asia today when
Prime Minister Wen Jiabao
signed a trade accord at a
regional summit that calls for
zero tariffs on a wide range
of" - ], - [ - "British judges in London
Tuesday ordered radical Muslim
imam Abu Hamza to stand trial
for soliciting murder and
inciting racial hatred." - ], - [ - "SARASOTA, Fla. - The
devastation brought on by
Hurricane Charley has been
especially painful for an
elderly population that is
among the largest in the
nation..." - ], - [ - " quot;He is charged for having
a part in the Bali incident,
quot; state prosecutor Andi
Herman told Reuters on
Saturday. bombing attack at
the US-run JW." - ], - [ - "The United Nations called on
Monday for an immediate
ceasefire in eastern Congo as
fighting between rival army
factions flared for a third
day." - ], - [ - "Allowing dozens of casinos to
be built in the UK would bring
investment and thousands of
jobs, Tony Blair says." - ], - [ - "The shock here was not just
from the awful fact itself,
that two vibrant young Italian
women were kidnapped in Iraq,
dragged from their office by
attackers who, it seems, knew
their names." - ], - [ - "Tehran/Vianna, Sept. 19 (NNN):
Iran on Sunday rejected the
International Atomic Energy
Agency (IAEA) call to suspend
of all its nuclear activities,
saying that it will not agree
to halt uranium enrichment." - ], - [ - "A 15-year-old Argentine
student opened fire at his
classmates on Tuesday in a
middle school in the south of
the Buenos Aires province,
leaving at least four dead and
five others wounded, police
said." - ], - [ - "NEW YORK - A cable pay-per-
view company has decided not
to show a three-hour election
eve special with filmmaker
Michael Moore that included a
showing of his documentary
\"Fahrenheit 9/11,\" which is
sharply critical of President
Bush. The company, iN
DEMAND, said Friday that its
decision is due to \"legitimate
business and legal concerns.\"
A spokesman would not
elaborate..." - ], - [ - "Democracy candidates picked up
at least one more seat in
parliament, according to exit
polls." - ], - [ - "The IOC wants suspended
Olympic member Ivan Slavkov to
be thrown out of the
organisation." - ], - [ - " BANGKOK (Reuters) - The
ouster of Myanmar's prime
minister, architect of a
tentative \"roadmap to
democracy,\" has dashed faint
hopes for an end to military
rule and leaves Southeast
Asia's policy of constructive
engagement in tatters." - ], - [ - "The anguish of hostage Kenneth
Bigley in Iraq hangs over
Prime Minister Tony Blair
today as he faces the twin
test of a local election and a
debate by his Labour Party
about the divisive war." - ], - [ - " quot;There were 16 people
travelling aboard. ... It
crashed into a mountain, quot;
Col. Antonio Rivero, head of
the Civil Protection service,
told." - ], - [ - "President Bush is reveling in
winning the popular vote and
feels he can no longer be
considered a one-term accident
of history." - ], - [ - "Canadian Press - TORONTO (CP)
- Thousands of Royal Bank
clerks are being asked to
display rainbow stickers at
their desks and cubicles to
promote a safe work
environment for gays,
lesbians, and bisexuals." - ], - [ - " BAGHDAD (Reuters) - A car
bomb killed two American
soldiers and wounded eight
when it exploded in Baghdad on
Saturday, the U.S. military
said in a statement." - ], - [ - "Sudanese authorities have
moved hundreds of pro-
government fighters from the
crisis-torn Darfur region to
other parts of the country to
keep them out of sight of
foreign military observers
demanding the militia #39;s
disarmament, a rebel leader
charged" - ], - [ - "BAGHDAD, Iraq - A car bomb
Tuesday ripped through a busy
market near a Baghdad police
headquarters where Iraqis were
waiting to apply for jobs on
the force, and gunmen opened
fire on a van carrying police
home from work in Baqouba,
killing at least 59 people
total and wounding at least
114. The attacks were the
latest attempts by militants
to wreck the building of a
strong Iraqi security force, a
keystone of the U.S..." - ], - [ - "The Israeli prime minister
said today that he wanted to
begin withdrawing settlers
from Gaza next May or June." - ], - [ - "Queen Elizabeth II stopped
short of apologizing for the
Allies #39; bombing of Dresden
during her first state visit
to Germany in 12 years and
instead acknowledged quot;the
appalling suffering of war on
both sides." - ], - [ - "AP - Fugitive Taliban leader
Mullah Mohammed Omar has
fallen out with some of his
lieutenants, who blame him for
the rebels' failure to disrupt
the landmark Afghan
presidential election, the
U.S. military said Wednesday." - ], - [ - "HAVANA -- Cuban President
Fidel Castro's advancing age
-- and ultimately his
mortality -- were brought home
yesterday, a day after he
fractured a knee and arm when
he tripped and fell at a
public event." - ], - [ - " BRASILIA, Brazil (Reuters) -
The United States and Brazil
predicted on Tuesday Latin
America's largest country
would resolve a dispute with
the U.N. nuclear watchdog over
inspections of a uranium
enrichment plant." - ], - [ - "Two bombs exploded today near
a tea shop and wounded 20
people in southern Thailand,
police said, as violence
continued unabated in the
Muslim-majority region where
residents are seething over
the deaths of 78 detainees
while in military custody." - ], - [ - "Prime Minister Junichiro
Koizumi, back in Tokyo after
an 11-day diplomatic mission
to the Americas, hunkered down
with senior ruling party
officials on Friday to focus
on a major reshuffle of
cabinet and top party posts." - ], - [ - "NEW YORK - A sluggish gross
domestic product reading was
nonetheless better than
expected, prompting investors
to send stocks slightly higher
Friday on hopes that the
economic slowdown would not be
as bad as first thought.
The 2.8 percent GDP growth in
the second quarter, a revision
from the 3 percent preliminary
figure reported in July, is a
far cry from the 4.5 percent
growth in the first quarter..." - ], - [ - " SEOUL (Reuters) - A huge
explosion in North Korea last
week may have been due to a
combination of demolition work
for a power plant and
atmospheric clouds, South
Korea's spy agency said on
Wednesday." - ], - [ - "AP - Sudan's U.N. ambassador
challenged the United States
to send troops to the Darfur
region if it really believes a
genocide is taking place as
the U.S. Congress and
President Bush's
administration have
determined." - ], - [ - "AFP - A junior British
minister said that people
living in camps after fleeing
their villages because of
conflict in Sudan's Darfur
region lived in fear of
leaving their temporary homes
despite a greater presence now
of aid workers." - ], - [ - "US Deputy Secretary of State
Richard Armitage (L) shakes
hands with Pakistani Foreign
Minister Khurshid Kasuri prior
to their meeting in Islamabad,
09 November 2004." - ], - [ - "AP - Democrat John Edwards
kept up a long-distance debate
over his \"two Americas\"
campaign theme with Vice
President Dick Cheney on
Tuesday, saying it was no
illusion to thousands of laid-
off workers in Ohio." - ], - [ - "A group of 76 Eritreans on a
repatriation flight from Libya
Friday forced their plane to
change course and land in the
Sudanese capital, Khartoum,
where they" - ], - [ - "AP - They say that opposites
attract, and in the case of
Sen. John Kerry and Teresa
Heinz Kerry, that may be true
#151; at least in their public
personas." - ], - [ - "AP - Impoverished North Korea
might resort to selling
weapons-grade plutonium to
terrorists for much-needed
cash, and that would be
\"disastrous for the world,\"
the top U.S. military
commander in South Korea said
Friday." - ], - [ - "Dubai: Osama bin Laden on
Thursday called on his
fighters to strike Gulf oil
supplies and warned Saudi
leaders they risked a popular
uprising in an audio message
said to be by the Western
world #39;s most wanted terror
mastermind." - ], - [ - "BEIJING -- Chinese authorities
have arrested or reprimanded
more than 750 officials in
recent months in connection
with billions of dollars in
financial irregularities
ranging from unpaid taxes to
embezzlement, according to a
report made public yesterday." - ], - [ - "Canadian Press - GAUHATI,
India (AP) - Residents of
northeastern India were
bracing for more violence
Friday, a day after bombs
ripped through two buses and a
grenade was hurled into a
crowded market in attacks that
killed four people and wounded
54." - ], - [ - "Some 28 people are charged
with trying to overthrow
Sudan's President Bashir,
reports say." - ], - [ - "Hong Kong #39;s Beijing-backed
chief executive yesterday
ruled out any early moves to
pass a controversial national
security law which last year
sparked a street protest by
half a million people." - ], - [ - "Beer consumption has doubled
over the past five years,
prompting legislators to
implement new rules." - ], - [ - "At least 79 people were killed
and 74 were missing after some
of the worst rainstorms in
recent years triggered
landslides and flash floods in
southwest China, disaster
relief officials said
yesterday." - ], - [ - "BANGKOK Thai military aircraft
dropped 100 million paper
birds over southern Thailand
on Sunday in a gesture
intended to promote peace in
mainly Muslim provinces, where
more than 500 people have died
this year in attacks by
separatist militants and" - ], - [ - "Insurgents threatened on
Saturday to cut the throats of
two Americans and a Briton
seized in Baghdad, and
launched a suicide car bomb
attack on Iraqi security
forces in Kirkuk that killed
at least 23 people." - ], - [ - " BEIJING (Reuters) - Secretary
of State Colin Powell will
urge China Monday to exert its
influence over North Korea to
resume six-party negotiations
on scrapping its suspected
nuclear weapons program." - ], - [ - "Reuters - An Israeli missile
strike killed at least\\two
Hamas gunmen in Gaza City
Monday, a day after a
top\\commander of the Islamic
militant group was killed in a
similar\\attack, Palestinian
witnesses and security sources
said." - ], - [ - "AP - The Pentagon has restored
access to a Web site that
assists soldiers and other
Americans living overseas in
voting, after receiving
complaints that its security
measures were preventing
legitimate voters from using
it." - ], - [ - "Montreal - The British-built
Canadian submarine HMCS
Chicoutimi, crippled since a
fire at sea that killed one of
its crew, is under tow and is
expected in Faslane, Scotland,
early next week, Canadian
naval commander Tyrone Pile
said on Friday." - ], - [ - "Thirty-five Pakistanis freed
from the US Guantanamo Bay
prison camp arrived home on
Saturday and were taken
straight to prison for further
interrogation, the interior
minister said." - ], - [ - "Witnesses in the trial of a US
soldier charged with abusing
prisoners at Abu Ghraib have
told the court that the CIA
sometimes directed abuse and
orders were received from
military command to toughen
interrogations." - ], - [ - "AP - Italian and Lebanese
authorities have arrested 10
suspected terrorists who
planned to blow up the Italian
Embassy in Beirut, an Italian
news agency and the Defense
Ministry said Tuesday." - ], - [ - "CORAL GABLES, Fla. -- John F.
Kerry and President Bush
clashed sharply over the war
in Iraq last night during the
first debate of the
presidential campaign season,
with the senator from
Massachusetts accusing" - ], - [ - "GONAIVES, Haiti -- While
desperately hungry flood
victims wander the streets of
Gonaives searching for help,
tons of food aid is stacking
up in a warehouse guarded by
United Nations peacekeepers." - ], - [ - "AFP - The resignation of
disgraced Fiji Vice-President
Ratu Jope Seniloli failed to
quell anger among opposition
leaders and the military over
his surprise release from
prison after serving just
three months of a four-year
jail term for his role in a
failed 2000 coup." - ], - [ - "Sourav Ganguly files an appeal
against a two-match ban
imposed for time-wasting." - ], - [ - "Greek police surrounded a bus
full of passengers seized by
armed hijackers along a
highway from an Athens suburb
Wednesday, police said." - ], - [ - "BAGHDAD: A suicide car bomber
attacked a police convoy in
Baghdad yesterday as guerillas
kept pressure on Iraq #39;s
security forces despite a
bloody rout of insurgents in
Fallujah." - ], - [ - "Fixtures and fittings from
Damien Hirst's restaurant
Pharmacy sell for 11.1, 8m
more than expected." - ], - [ - "Five years after catastrophic
floods and mudslides killed
thousands along Venezuela's
Caribbean coast, survivors in
this town still see the signs
of destruction - shattered
concrete walls and tall weeds
growing atop streets covered
in dried mud." - ], - [ - "The UN nuclear watchdog
confirmed Monday that nearly
400 tons of powerful
explosives that could be used
in conventional or nuclear
missiles disappeared from an
unguarded military
installation in Iraq." - ], - [ - "Militants in Iraq have killed
the second of two US civilians
they were holding hostage,
according to a statement on an
Islamist website." - ], - [ - "More than 4,000 American and
Iraqi soldiers mounted a
military assault on the
insurgent-held city of
Samarra." - ], - [ - " MEXICO CITY (Reuters) -
Mexican President Vicente Fox
said on Monday he hoped
President Bush's re-election
meant a bilateral accord on
migration would be reached
before his own term runs out
at the end of 2006." - ], - [ - " BRUSSELS (Reuters) - French
President Jacques Chirac said
on Friday he had not refused
to meet Iraqi interim Prime
Minister Iyad Allawi after
reports he was snubbing the
Iraqi leader by leaving an EU
meeting in Brussels early." - ], - [ - "NEW YORK - Former President
Bill Clinton was in good
spirits Saturday, walking
around his hospital room in
street clothes and buoyed by
thousands of get-well messages
as he awaited heart bypass
surgery early this coming
week, people close to the
family said. Clinton was
expected to undergo surgery as
early as Monday but probably
Tuesday, said Democratic Party
Chairman Terry McAuliffe, who
said the former president was
\"upbeat\" when he spoke to him
by phone Friday..." - ], - [ - "Poland will cut its troops in
Iraq early next year and won
#39;t stay in the country
quot;an hour longer quot; than
needed, the country #39;s
prime minister said Friday." - ], - [ - "A car bomb exploded at a US
military convoy in the
northern Iraqi city of Mosul,
causing several casualties,
the army and Iraqi officials
said." - ], - [ - "ATHENS, Greece - Michael
Phelps took care of qualifying
for the Olympic 200-meter
freestyle semifinals Sunday,
and then found out he had been
added to the American team for
the evening's 400 freestyle
relay final. Phelps' rivals
Ian Thorpe and Pieter van den
Hoogenband and teammate Klete
Keller were faster than the
teenager in the 200 free
preliminaries..." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
intends to present a timetable
for withdrawal from Gaza to
lawmakers from his Likud Party
Tuesday despite a mutiny in
the right-wing bloc over the
plan." - ], - [ - " KABUL (Reuters) - Three U.N.
workers held by militants in
Afghanistan were in their
third week of captivity on
Friday after calls from both
sides for the crisis to be
resolved ahead of this
weekend's Muslim festival of
Eid al-Fitr." - ], - [ - "ROME, Oct 29 (AFP) - French
President Jacques Chirac urged
the head of the incoming
European Commission Friday to
take quot;the appropriate
decisions quot; to resolve a
row over his EU executive team
which has left the EU in
limbo." - ], - [ - "Russia's parliament will
launch an inquiry into a
school siege that killed over
300 hostages, President
Vladimir Putin said on Friday,
but analysts doubt it will
satisfy those who blame the
carnage on security services." - ], - [ - "Tony Blair talks to business
leaders about new proposals
for a major shake-up of the
English exam system." - ], - [ - "KUALA LUMPUR, Malaysia A
Malaysian woman has claimed a
new world record after living
with over six-thousand
scorpions for 36 days
straight." - ], - [ - "could take drastic steps if
the talks did not proceed as
Tehran wants. Mehr news agency
quoted him as saying on
Wednesday. that could be used" - ], - [ - "Israeli authorities have
launched an investigation into
death threats against Israeli
Prime Minister Ariel Sharon
and other officials supporting
his disengagement plan from
Gaza and parts of the West
Bank, Jerusalem police said
Tuesday." - ], - [ - "It was a carefully scripted
moment when Russian President
Vladimir Putin began quoting
Taras Shevchenko, this country
#39;s 19th-century bard,
during a live television" - ], - [ - "China says it welcomes Russia
#39;s ratification of the
Kyoto Protocol that aims to
stem global warming by
reducing greenhouse-gas
emissions." - ], - [ - "The Metropolitan
Transportation Authority is
proposing a tax increase to
raise \\$900 million a year to
help pay for a five-year
rebuilding program." - ], - [ - "AFP - The Canadian armed
forces chief of staff on was
elected to take over as head
of NATO's Military Committee,
the alliance's highest
military authority, military
and diplomatic sources said." - ], - [ - "AFP - Two proposed resolutions
condemning widespread rights
abuses in Sudan and Zimbabwe
failed to pass a UN committee,
mired in debate between
African and western nations." - ], - [ - "The return of noted reformer
Nabil Amr to Palestinian
politics comes at a crucial
juncture." - ], - [ - "AP - After two debates, voters
have seen President Bush look
peevish and heard him pass the
buck. They've watched Sen.
John Kerry deny he's a flip-
flopper and then argue that
Saddam Hussein was a threat
#151; and wasn't. It's no
wonder so few minds have
changed." - ], - [ - "The United States says the
Lebanese parliament #39;s
decision Friday to extend the
term of pro-Syrian President
Emile Lahoud made a
quot;crude mockery of
democratic principles." - ], - [ - "Canadian Press - OTTAWA (CP) -
The prime minister says he's
been assured by President
George W. Bush that the U.S.
missile defence plan does not
necessarily involve the
weaponization of space." - ], - [ - "Fifty-six miners are dead and
another 92 are still stranded
underground after a gas blast
at the Daping coal mine in
Central China #39;s Henan
Province, safety officials
said Thursday." - ], - [ - "BEIRUT, Lebanon - Three
Lebanese men and their Iraqi
driver have been kidnapped in
Iraq, the Lebanese Foreign
Ministry said Sunday, as
Iraq's prime minister said his
government was working for the
release of two Americans and a
Briton also being held
hostage. Gunmen snatched
the three Lebanese, who worked
for a travel agency with a
branch in Baghdad, as they
drove on the highway between
the capital and Fallujah on
Friday night, a ministry
official said..." - ], - [ - "PORTLAND, Ore. - It's been
almost a year since singer-
songwriter Elliott Smith
committed suicide, and fans
and friends will be looking
for answers as the posthumous
\"From a Basement on the Hill\"
is released..." - ], - [ - " GAZA (Reuters) - Israel
expanded its military
offensive in northern Gaza,
launching two air strikes
early on Monday that killed
at least three Palestinians
and wounded two, including a
senior Hamas leader, witnesses
and medics said." - ], - [ - "Reuters - Sudan's government
resumed\\talks with rebels in
the oil-producing south on
Thursday while\\the United
Nations set up a panel to
investigate charges
of\\genocide in the west of
Africa's largest country." - ], - [ - "NEW YORK - Optimism that the
embattled technology sector
was due for a recovery sent
stocks modestly higher Monday
despite a new revenue warning
from semiconductor company
Broadcom Inc. While
Broadcom, which makes chips
for television set-top boxes
and other electronics, said
high inventories resulted in
delayed shipments, investors
were encouraged as it said
future quarters looked
brighter..." - ], - [ - "Carlos Barrios Orta squeezed
himself into his rubber diving
suit, pulled on an 18-pound
helmet that made him look like
an astronaut, then lowered
himself into the sewer. He
disappeared into the filthy
water, which looked like some
cauldron of rancid beef stew,
until the only sign of him was
air bubbles breaking the
surface." - ], - [ - "The mutilated body of a woman
of about sixty years,
amputated of arms and legs and
with the sliced throat, has
been discovered this morning
in a street of the south of
Faluya by the Marines,
according to a statement by
AFP photographer." - ], - [ - "(SH) - In Afghanistan, Hamid
Karzai defeated a raft of
candidates to win his historic
election. In Iraq, more than
200 political parties have
registered for next month
#39;s elections." - ], - [ - "Margaret Hassan, who works for
charity Care International,
was taken hostage while on her
way to work in Baghdad. Here
are the main events since her
kidnapping." - ], - [ - "Reuters - The United States
modified its\\call for U.N.
sanctions against Sudan on
Tuesday but still kept\\up the
threat of punitive measures if
Khartoum did not
stop\\atrocities in its Darfur
region." - ], - [ - "Human rights and environmental
activists have hailed the
award of the 2004 Nobel Peace
Prize to Wangari Maathai of
Kenya as fitting recognition
of the growing role of civil
society" - ], - [ - "An Al Qaeda-linked militant
group beheaded an American
hostage in Iraq and threatened
last night to kill another two
Westerners in 24 hours unless
women prisoners were freed
from Iraqi jails." - ], - [ - "Argentina, Denmark, Greece,
Japan and Tanzania on Friday
won coveted two-year terms on
the UN Security Council at a
time when pressure is mounting
to expand the powerful
15-nation body." - ], - [ - "ISLAMABAD: Pakistan and India
agreed on Friday to re-open
the Khokhropar-Munabao railway
link, which was severed nearly
40 years ago." - ], - [ - "NEW YORK - Stocks moved higher
Friday as a stronger than
expected retail sales report
showed that higher oil prices
aren't scaring consumers away
from spending. Federal Reserve
Chairman Alan Greenspan's
positive comments on oil
prices also encouraged
investors..." - ], - [ - "Saddam Hussein lives in an
air-conditioned 10-by-13 foot
cell on the grounds of one of
his former palaces, tending
plants and proclaiming himself
Iraq's lawful ruler." - ], - [ - "AP - Brazilian U.N.
peacekeepers will remain in
Haiti until presidential
elections are held in that
Caribbean nation sometime next
year, President Luiz Inacio
Lula da Silva said Monday." - ], - [ - "Iraq is quot;working 24 hours
a day to ... stop the
terrorists, quot; interim
Iraqi Prime Minister Ayad
Allawi said Monday. Iraqis are
pushing ahead with reforms and
improvements, Allawi told" - ], - [ - "AP - Musicians, composers and
authors were among the more
than two dozen people
Wednesday honored with
National Medal of Arts and
National Humanities awards at
the White House." - ], - [ - "Wynton Marsalis, the trumpet-
playing star and artistic
director of Jazz at Lincoln
Center, has become an entity
above and around the daily
jazz world." - ], - [ - "BUENOS AIRES: Pakistan has
ratified the Kyoto Protocol on
Climatic Change, Environment
Minister Malik Khan said on
Thursday. He said that
Islamabad has notified UN
authorities of ratification,
which formally comes into
effect in February 2005." - ], - [ - "Staff Sgt. Johnny Horne, Jr.,
and Staff Sgt. Cardenas Alban
have been charged with murder
in the death of an Iraqi, the
1st Cavalry Division announced
Monday." - ], - [ - "President Vladimir Putin makes
a speech as he hosts Russia
#39;s Olympic athletes at a
Kremlin banquet in Moscow,
Thursday, Nov. 4, 2004." - ], - [ - "AFP - Hundreds of Buddhists in
southern Russia marched
through snow to see and hear
the Dalai Lama as he continued
a long-awaited visit to the
country in spite of Chinese
protests." - ], - [ - "British officials were on
diplomatic tenterhooks as they
awaited the arrival on
Thursday of French President
Jacques Chirac for a two-day
state visit to Britain." - ], - [ - " SYDNEY (Reuters) - A group of
women on Pitcairn Island in
the South Pacific are standing
by their men, who face
underage sex charges, saying
having sex at age 12 is a
tradition dating back to 18th
century mutineers who settled
on the island." - ], - [ - "Two aircraft are flying out
from the UK on Sunday to
deliver vital aid and supplies
to Haiti, which has been
devastated by tropical storm
Jeanne." - ], - [ - "A scare triggered by a
vibrating sex toy shut down a
major Australian regional
airport for almost an hour
Monday, police said. The
vibrating object was
discovered Monday morning" - ], - [ - "AP - An Indiana congressional
candidate abruptly walked off
the set of a debate because
she got stage fright." - ], - [ - "Reuters - Having reached out
to Kashmiris during a two-day
visit to the region, the prime
minister heads this weekend to
the country's volatile
northeast, where public anger
is high over alleged abuses by
Indian soldiers." - ], - [ - "SAN JUAN IXTAYOPAN, Mexico --
A pair of wooden crosses
outside the elementary school
are all that mark the macabre
site where, just weeks ago, an
angry mob captured two federal
police officers, beat them
unconscious, and set them on
fire." - ], - [ - "DUBLIN : An Olympic Airlines
plane diverted to Ireland
following a bomb alert, the
second faced by the Greek
carrier in four days, resumed
its journey to New York after
no device was found on board,
airport officials said." - ], - [ - " WASHINGTON (Reuters) - The
United States said on Friday
it is preparing a new U.N.
resolution on Darfur and that
Secretary of State Colin
Powell might address next week
whether the violence in
western Sudan constitutes
genocide." - ], - [ - "NAJAF, Iraq : Iraq #39;s top
Shiite Muslim clerics, back in
control of Najaf #39;s Imam
Ali shrine after a four-month
militia occupation, met amid
the ruins of the city as life
spluttered back to normality." - ], - [ - "Reuters - House of
Representatives
Majority\\Leader Tom DeLay,
admonished twice in six days
by his chamber's\\ethics
committee, withstood calls on
Thursday by rival\\Democrats
and citizen groups that he
step aside." - ], - [ - "AFP - Australia has accounted
for all its nationals known to
be working in Iraq following a
claim by a radical Islamic
group to have kidnapped two
Australians, Foreign Affairs
Minister Alexander Downer
said." - ], - [ - "The hurricane appeared to be
strengthening and forecasters
warned of an increased threat
of serious flooding and wind
damage." - ], - [ - "Four homeless men were
bludgeoned to death and six
were in critical condition on
Friday following early morning
attacks by unknown assailants
in downtown streets of Sao
Paulo, a police spokesman
said." - ], - [ - "Phone companies are not doing
enough to warn customers about
internet \"rogue-dialling\"
scams, watchdog Icstis warns." - ], - [ - "India's main opposition party
takes action against senior
party member Uma Bharti after
a public row." - ], - [ - "The Russian military yesterday
extended its offer of a \\$10
million reward for information
leading to the capture of two
separatist leaders who, the
Kremlin claims, were behind
the Beslan massacre." - ], - [ - "Israels Shin Bet security
service has tightened
protection of the prime
minister, MPs and parliament
ahead of next weeks crucial
vote on a Gaza withdrawal." - ], - [ - "Reuters - Iraq's interim
defense minister
accused\\neighbors Iran and
Syria on Wednesday of aiding
al Qaeda\\Islamist Abu Musab
al-Zarqawi and former agents
of Saddam\\Hussein to promote a
\"terrorist\" insurgency in
Iraq." - ], - [ - "AP - The Senate race in
Kentucky stayed at fever pitch
on Thursday as Democratic
challenger Daniel Mongiardo
stressed his opposition to gay
marriage while accusing
Republican incumbent Jim
Bunning of fueling personal
attacks that seemed to suggest
Mongiardo is gay." - ], - [ - " PORT LOUIS, Aug. 17
(Xinhuanet) -- Southern
African countries Tuesday
pledged better trade and
investment relations with
China as well as India in the
final communique released at
the end of their two-day
summit." - ], - [ - "BAGHDAD - A militant group has
released a video saying it
kidnapped a missing journalist
in Iraq and would kill him
unless US forces left Najaf
within 48 hours." - ], - [ - "18 August 2004 -- There has
been renewed fighting in the
Iraqi city of Al-Najaf between
US and Iraqi troops and Shi
#39;a militiamen loyal to
radical cleric Muqtada al-
Sadr." - ], - [ - "Australia tighten their grip
on the third Test and the
series after dominating India
on day two in Nagpur." - ], - [ - " SEOUL (Reuters) - North
Korea's two-year-old nuclear
crisis has taxed the world's
patience, the chief United
Nations nuclear regulator
said on Wednesday, urging
communist Pyongyang to return
to its disarmament treaty
obligations." - ], - [ - "GROZNY, Russia - The Russian
government's choice for
president of war-ravaged
Chechnya appeared to be the
victor Sunday in an election
tainted by charges of fraud
and shadowed by last week's
terrorist destruction of two
airliners. Little more than
two hours after polls closed,
acting Chechen president
Sergei Abramov said
preliminary results showed
Maj..." - ], - [ - "BAGHDAD, Sept 5 (AFP) - Izzat
Ibrahim al-Duri, Saddam
Hussein #39;s deputy whose
capture was announced Sunday,
is 62 and riddled with cancer,
but was public enemy number
two in Iraq for the world
#39;s most powerful military." - ], - [ - "AP - An explosion targeted the
Baghdad governor's convoy as
he was traveling through the
capital Tuesday, killing two
people but leaving him
uninjured, the Interior
Ministry said." - ], - [ - "GENEVA: Rescuers have found
the bodies of five Swiss
firemen who died after the
ceiling of an underground car
park collapsed during a fire,
a police spokesman said last
night." - ], - [ - "John Kerry has held 10 \"front
porch visit\" events an actual
front porch is optional where
perhaps 100 people ask
questions in a low-key
campaigning style." - ], - [ - "The right-win opposition
Conservative Party and Liberal
Center Union won 43 seats in
the 141-member Lithuanian
parliament, after more than 99
percent of the votes were
counted" - ], - [ - "KHARTOUM, Aug 18 (Reuters) -
The United Nations said on
Wednesday it was very
concerned by Sudan #39;s lack
of practical progress in
bringing security to Darfur,
where more than a million
people have fled their homes
for fear of militia ..." - ], - [ - "AFP - With less than two
months until the November 2
election, President George W.
Bush is working to shore up
support among his staunchest
supporters even as he courts
undecided voters." - ], - [ - "Chile's government says it
will build a prison for
officers convicted of human
rights abuses in the Pinochet
era." - ], - [ - "Palestinian leader Mahmoud
Abbas reiterated calls for his
people to drop their weapons
in the struggle for a state. a
clear change of strategy for
peace with Israel after Yasser
Arafat #39;s death." - ], - [ - " BAGHDAD (Reuters) - Iraq's
U.S.-backed government said on
Tuesday that \"major neglect\"
by its American-led military
allies led to a massacre of 49
army recruits at the weekend." - ], - [ - "BEIJING -- Police have
detained a man accused of
slashing as many as nine boys
to death as they slept in
their high school dormitory in
central China, state media
reported today." - ], - [ - "Israeli Prime Minister Ariel
Sharon #39;s Likud party
agreed on Thursday to a
possible alliance with
opposition Labour in a vote
that averted a snap election
and strengthened his Gaza
withdrawal plan." - ], - [ - "While the American forces are
preparing to launch a large-
scale attack against Falluja
and al-Ramadi, one of the
chieftains of Falluja said
that contacts are still
continuous yesterday between
members representing the city
#39;s delegation and the
interim" - ], - [ - "UN Security Council
ambassadors were still
quibbling over how best to
pressure Sudan and rebels to
end two different wars in the
country even as they left for
Kenya on Tuesday for a meeting
on the crisis." - ], - [ - "HENDALA, Sri Lanka -- Day
after day, locked in a cement
room somewhere in Iraq, the
hooded men beat him. They told
him he would be beheaded.
''Ameriqi! quot; they shouted,
even though he comes from this
poor Sri Lankan fishing
village." - ], - [ - "THE kidnappers of British aid
worker Margaret Hassan last
night threatened to turn her
over to the group which
beheaded Ken Bigley if the
British Government refuses to
pull its troops out of Iraq." - ], - [ - "<p></p><p>
BOGOTA, Colombia (Reuters) -
Ten Colombian police
officerswere killed on Tuesday
in an ambush by the National
LiberationArmy, or ELN, in the
worst attack by the Marxist
group inyears, authorities
told Reuters.</p>" - ], - [ - "AFP - US President George W.
Bush called his Philippines
counterpart Gloria Arroyo and
said their countries should
keep strong ties, a spokesman
said after a spat over
Arroyo's handling of an Iraq
kidnapping." - ], - [ - "A scathing judgment from the
UK #39;s highest court
condemning the UK government
#39;s indefinite detention of
foreign terror suspects as a
threat to the life of the
nation, left anti-terrorist
laws in the UK in tatters on
Thursday." - ], - [ - "Reuters - Australia's
conservative Prime
Minister\\John Howard, handed
the most powerful mandate in a
generation,\\got down to work
on Monday with reform of
telecommunications,\\labor and
media laws high on his agenda." - ], - [ - " TOKYO (Reuters) - Typhoon
Megi killed one person as it
slammed ashore in northern
Japan on Friday, bringing the
death toll to at least 13 and
cutting power to thousands of
homes before heading out into
the Pacific." - ], - [ - "Cairo: Egyptian President
Hosni Mubarak held telephone
talks with Palestinian leader
Yasser Arafat about Israel
#39;s plan to pull troops and
the 8,000 Jewish settlers out
of the Gaza Strip next year,
the Egyptian news agency Mena
said." - ], - [ - "The first American military
intelligence soldier to be
court-martialed over the Abu
Ghraib abuse scandal was
sentenced Saturday to eight
months in jail, a reduction in
rank and a bad-conduct
discharge." - ], - [ - " TOKYO (Reuters) - Japan will
seek an explanation at weekend
talks with North Korea on
activity indicating Pyongyang
may be preparing a missile
test, although Tokyo does not
think a launch is imminent,
Japan's top government
spokesman said." - ], - [ - " BEIJING (Reuters) - Iran will
never be prepared to
dismantle its nuclear program
entirely but remains committed
to the non-proliferation
treaty (NPT), its chief
delegate to the International
Atomic Energy Agency said on
Wednesday." - ], - [ - "<p></p><p>
SANTIAGO, Chile (Reuters) -
President Bush on
Saturdayreached into a throng
of squabbling bodyguards and
pulled aSecret Service agent
away from Chilean security
officers afterthey stopped the
U.S. agent from accompanying
the president ata
dinner.</p>" - ], - [ - "Iranian deputy foreign
minister Gholamali Khoshrou
denied Tuesday that his
country #39;s top leaders were
at odds over whether nuclear
weapons were un-Islamic,
insisting that it will
quot;never quot; make the
bomb." - ], - [ - " quot;Israel mercenaries
assisting the Ivory Coast army
operated unmanned aircraft
that aided aerial bombings of
a French base in the country,
quot; claimed" - ], - [ - "AFP - SAP, the world's leading
maker of business software,
may need an extra year to
achieve its medium-term profit
target of an operating margin
of 30 percent, its chief
financial officer said." - ], - [ - "AFP - The airline Swiss said
it had managed to cut its
first-half net loss by about
90 percent but warned that
spiralling fuel costs were
hampering a turnaround despite
increasing passenger travel." - ], - [ - "Vulnerable youngsters expelled
from schools in England are
being let down by the system,
say inspectors." - ], - [ - "Yasser Arafat was undergoing
tests for possible leukaemia
at a military hospital outside
Paris last night after being
airlifted from his Ramallah
headquarters to an anxious
farewell from Palestinian
well-wishers." - ], - [ - "Nepal #39;s main opposition
party urged the government on
Monday to call a unilateral
ceasefire with Maoist rebels
and seek peace talks to end a
road blockade that has cut the
capital off from the rest of
the country." - ], - [ - "BOSTON - The New York Yankees
and Boston were tied 4-4 after
13 innings Monday night with
the Red Sox trying to stay
alive in the AL championship
series. Boston tied the
game with two runs in the
eighth inning on David Ortiz's
solo homer, a walk to Kevin
Millar, a single by Trot Nixon
and a sacrifice fly by Jason
Varitek..." - ], - [ - "Reuters - The country may be
more\\or less evenly divided
along partisan lines when it
comes to\\the presidential
race, but the Republican Party
prevailed in\\the Nielsen
polling of this summer's
nominating conventions." - ], - [ - " In Vice President Cheney's
final push before next
Tuesday's election, talk of
nuclear annihilation and
escalating war rhetoric have
blended with balloon drops,
confetti cannons and the other
trappings of modern
campaigning with such ferocity
that it is sometimes tough to
tell just who the enemy is." - ] - ], - "hovertemplate": "label=World
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "World", - "marker": { - "color": "#636efa", - "size": 5, - "symbol": "circle" - }, - "mode": "markers", - "name": "World", - "showlegend": true, - "type": "scattergl", - "x": [ - 9.787297, - 17.206654, - 28.167477, - 14.671119, - 21.907679, - 11.003371, - 12.786125, - 28.192938, - 27.511831, - 31.907536, - 27.685726, - -0.48236108, - 24.631432, - 19.87161, - 42.217842, - 23.463217, - 15.450646, - 22.48473, - 47.958393, - 7.788443, - 26.455547, - -4.8219795, - 3.2403526, - 0.8158772, - 7.324227, - 24.88585, - 38.79396, - 13.661437, - 30.355759, - 3.1252713, - -17.98721, - 18.820461, - 13.827141, - 1.4320391, - 12.191131, - -2.3659778, - 25.89013, - -1.0874362, - 15.950565, - 11.055151, - 14.368044, - 15.940281, - 28.371725, - 17.925577, - 15.851762, - 1.6593695, - 18.05479, - 28.471008, - 10.705277, - 18.345266, - 31.360231, - 50.538635, - 4.6381655, - 16.084637, - 51.287056, - 12.821454, - 24.011929, - 17.79019, - 15.120078, - -39.450546, - 19.99747, - 15.529904, - 19.791918, - -9.525661, - 12.873272, - 16.33122, - 22.366423, - 2.4414933, - 25.421625, - 20.893494, - 19.51864, - 11.30494, - 2.9794881, - 12.285144, - 3.3651476, - -16.802534, - 18.079544, - 2.7036908, - 6.7110343, - -1.3755705, - 10.5324745, - 18.966919, - 31.810251, - 17.243034, - 4.507162, - 21.953882, - 12.895756, - 13.899155, - 9.645002, - 8.84193, - 7.766448, - 10.753302, - 60.835865, - 29.961258, - 31.980536, - 23.273722, - 3.1031818, - 12.880273, - 11.016033, - 17.860275, - 12.732019, - 16.701687, - 14.009928, - 18.774673, - 2.979671, - 8.863162, - 9.040379, - 24.042429, - 25.076736, - 30.519775, - 12.896586, - 25.85091, - 19.948092, - 20.974108, - 18.678154, - 49.229675, - -13.887877, - 19.741331, - 31.022472, - -11.186273, - 18.250782, - 4.836007, - -9.537627, - 24.104692, - 1.9518297, - 15.377659, - 14.915583, - 19.173527, - -7.4475813, - 8.212411, - 5.134725, - 14.638772, - 1.3949758, - 15.7138605, - 17.170551, - 17.347712, - 24.028929, - 5.1724906, - 27.314432, - 12.80685, - -26.112938, - 12.646676, - 9.909096, - 22.181377, - 29.485273, - -1.1384851, - 24.128727, - 16.221085, - 25.097984, - 8.574884, - 21.295237, - 25.800558, - 5.8968763, - 24.184378, - -54.980534, - 46.068615, - 20.787077, - 29.313784, - 8.255305, - 15.709119, - -26.129557, - 10.710161, - 23.318872, - -46.75248, - 20.918581, - 11.446291, - 10.624376, - 13.654009, - 23.009165, - 0.9160498, - 1.6248351, - 15.268804, - 20.570063, - 16.519796, - 1.7450867, - -16.392036, - 19.578056, - 9.273592, - 5.769484, - 5.3805246, - 2.333401, - 14.209792, - 7.33986, - -1.2716205, - 2.7853732, - 26.978062, - 18.276062, - 20.191584, - 25.01299, - 10.248553, - 4.6009235, - 9.839586, - 11.750173, - 7.9382405, - 17.778656, - 15.492881, - 7.7321296, - 8.952756, - 20.371246, - -9.127035, - 1.8769449, - 12.356418, - 4.2263513, - 31.053217, - -9.149289, - 9.723743, - 27.65755, - 26.112234, - -39.176956, - 16.072603, - 17.410772, - 12.662249, - 18.940567, - 20.549877, - 19.506048, - 8.936648, - 14.902041, - 22.10423, - -2.6893454, - 51.905163, - 21.657618, - 13.691204, - 28.035511, - 23.045893, - 30.591192, - 17.440117, - -9.929121, - 7.212067, - 14.5572, - -33.03146, - 26.660809, - 2.4019308, - 14.605348, - 5.080946, - 4.331932, - 32.7655, - 23.97021, - 22.242004, - -28.60194, - 14.265632, - 7.0784307, - 20.74278, - 4.454403, - 51.55675, - 14.978659, - 11.338411, - 11.213355, - 15.526145, - 9.335806, - -8.953644, - 18.62211, - 23.033293, - -5.5465455, - 10.529721, - 13.965547, - 21.258057, - 10.181149, - 17.598188, - 1.3672223, - 32.536667, - 10.24864, - 5.8206697, - -10.62197, - 21.86292, - 20.400032, - 27.48848, - 2.2426317, - 5.0682087, - 27.914957, - 16.509165, - 14.155432, - 2.808305, - 6.707939, - 19.51436, - 18.935217, - 46.280994, - 8.379798, - 2.4920332, - 47.18665, - -0.99369574, - -28.31348, - 2.79127, - 16.340908, - 20.619595, - 23.400663, - 14.445518, - 26.900372, - 12.496445, - 28.753572, - 21.319304, - 11.094263, - 7.11425, - 18.546873, - 37.20064, - 9.897873, - 24.480858, - 8.527567, - 16.88663, - 13.06387, - 29.025854, - 3.984353, - -1.1919637, - 5.320594, - 14.373686, - 11.930037, - 23.310764, - 18.960714, - 17.941885, - 10.820086, - 11.278602, - 8.342169, - 58.012913, - 7.6059, - -7.4377956, - 12.515653, - 8.195707, - 1.8771796, - 22.029692, - 4.4929285, - -17.537066, - -2.2965894, - 3.6962993, - 11.67213, - 5.52023, - 15.926025, - 4.0713243, - -3.7340183, - -15.154654, - 11.040503, - 13.273838, - 7.288217, - -18.065994, - 8.75742, - 29.14241, - 17.28087, - 13.877883, - 21.1013, - 13.981468, - 10.2113, - 28.748735, - 10.333969, - 12.468114, - 3.0369818, - 24.300056, - -8.906268, - -0.9226989, - 32.861256, - 1.5139743, - 22.857521, - 4.3284388, - 26.680521, - 8.132189, - 11.17274, - 22.79567, - 2.0400486, - 19.547178, - -4.5817585, - 17.717125, - 32.523216, - 19.772266, - 10.665481, - 15.195456, - 11.543438, - -18.300999, - 18.401154, - 7.0795293, - 19.923655, - 14.908174, - 15.951407, - 20.179276, - 31.14273, - 0.258186, - 14.35011, - 14.368874, - -42.005432, - 9.892005, - 6.150114, - 6.031961, - 8.659726, - 10.755605, - 8.7123785, - 12.619259, - 0.080250725, - 20.854275, - -11.675889, - 14.897927, - 8.117931, - 6.539579, - -52.00866, - 7.7298007, - 12.733178, - 23.083542, - 6.8278956, - 14.610548, - -19.3257, - 7.079915, - 21.593582, - 22.709345, - 23.351994, - 10.662044, - 6.3287606, - 20.248484, - 34.33151, - 17.894573, - 18.915913, - -56.14451, - 5.4172053, - -0.9005222, - 24.29299, - 15.59622, - 10.643132, - 5.560217, - 24.635311, - 13.113659, - 18.401346, - 46.27362, - 14.899461, - 24.252523, - 5.36986, - 31.625498, - 10.970106, - 51.867313, - 2.5174408, - 15.975605, - 27.581682, - 1.6741688, - -27.687584, - 16.295555, - 3.6958573, - 17.794357, - 10.886147, - 18.907486, - 3.8529873, - 10.995691, - 23.637348, - 33.6961, - 14.72486, - 7.9172506, - -48.755344, - -3.3571925, - 19.580015, - 19.054085, - 26.428055, - 4.8372564, - 2.4959075, - 24.6171, - -3.8125634, - -0.46443436, - -45.570045, - 25.599476, - 14.019283, - 15.602155, - -13.399857, - 62.178337, - 1.1374025, - 25.336575, - 27.973917, - 23.369677, - 6.171623, - 26.484608, - 7.0410976, - 19.369898, - 20.71207, - 12.614038, - 17.452501, - 18.572897, - 6.0333753, - 16.012442, - 9.572269, - 26.459038, - 2.9941561, - 17.785444, - -16.35767, - 20.736986, - 24.382677, - 16.1644, - 19.621206, - 8.682678, - 1.4400622, - 25.263475, - 16.929596, - 39.76294, - 0.50536364, - 19.154146, - 26.26559, - 19.381176, - 12.624142, - 13.547113, - 17.735638, - 5.6944747, - 11.9049635, - 19.661798, - 12.937629, - 18.126286, - 19.819803, - 14.775703, - 16.772926, - 3.6852949, - 25.670559, - 26.078526, - 17.1552, - 9.536311, - 21.02507, - 12.836097, - 6.9077144, - 13.885367, - 25.294838, - 0.9785223, - 1.8828108, - 10.278181, - 3.350846, - 10.675423, - -27.676836, - -31.987688, - 2.7699895, - 29.804829, - 8.834031, - 57.51465, - 14.13684, - 10.008406 - ], - "xaxis": "x", - "y": [ - -0.25818, - 22.728033, - 23.284092, - 21.800117, - 22.159718, - 29.896206, - 6.133116, - 8.674217, - 25.905638, - 9.192532, - 25.749458, - 39.722248, - 7.8999453, - -7.791385, - -27.949192, - 10.707973, - 21.421028, - 23.633503, - 9.072081, - 11.001149, - 6.058426, - 4.199548, - 8.901868, - 7.6804514, - 4.9480743, - 26.755693, - -2.3146381, - 2.532362, - 23.373753, - 22.522005, - 24.106895, - 9.316687, - 27.62886, - 5.951754, - 15.638516, - -17.482075, - 20.813839, - 12.0553255, - 28.830767, - 14.3665, - 0.061168052, - 23.813591, - 21.227903, - 31.043804, - 3.844936, - 1.4175098, - 3.6453905, - 16.828537, - 9.330847, - 9.5765, - -10.817816, - 5.1117396, - 3.6004014, - 18.804445, - -9.840589, - 4.881303, - 30.134317, - 32.50385, - 8.090675, - 21.478413, - 14.554468, - 20.755419, - 23.0764, - 8.462659, - 18.15951, - 2.4064643, - 8.999294, - -6.154228, - 14.864804, - 19.460798, - -2.3412774, - 2.4203362, - 32.717518, - -7.624435, - 2.3350112, - -16.211517, - 22.352716, - 8.7813, - 27.98141, - 5.231712, - -9.940092, - 2.9951952, - 7.513018, - -2.4976819, - 22.716187, - 15.819128, - 5.0620914, - 12.743932, - -8.023369, - 16.329193, - 30.292624, - 23.7153, - -17.214022, - 15.711783, - 0.59863055, - 0.31460643, - -1.605775, - 10.840164, - 30.278105, - 19.883846, - -6.269737, - 20.470428, - 25.027699, - 15.167711, - 23.27041, - 9.704817, - 14.702273, - 18.21779, - 16.7961, - 9.027207, - 18.049044, - 25.408955, - -1.7761685, - 7.837817, - 34.420277, - 6.988793, - 24.185543, - 17.168627, - 10.645888, - 33.268223, - 35.533974, - 5.3443413, - 16.755838, - 19.649885, - 5.488912, - 15.933173, - 5.761818, - 0.9805258, - 34.912987, - 2.2579987, - 31.233051, - 22.297411, - 3.5171926, - 23.886812, - 19.465944, - 16.774218, - 14.815331, - 11.881365, - 25.418554, - 28.923544, - 5.0646152, - 23.290081, - -5.3597302, - 18.798145, - 8.9016, - -22.157974, - 18.377977, - 31.100603, - 23.13797, - 31.66899, - 25.328583, - 5.658062, - 8.72019, - 4.320076, - 0.7318651, - -11.081378, - -2.6716044, - 13.364902, - 5.9486156, - 12.414623, - 5.5806613, - 16.740986, - 5.6076837, - 19.352674, - -0.68818355, - 9.428179, - 18.622755, - 7.8728004, - 21.211613, - 10.388335, - -0.20468314, - 29.903488, - 14.083497, - 0.6598771, - 26.250034, - -24.852484, - 12.103588, - -8.026257, - 16.791052, - 31.885904, - 11.005908, - 15.028398, - 14.654819, - 11.900147, - -1.3390135, - 26.11797, - -2.478072, - -23.535704, - 27.143415, - 0.81385136, - 17.844543, - 19.694197, - 30.822157, - 11.223421, - 34.832695, - 15.818021, - 12.122141, - 33.150494, - 20.72354, - 3.162032, - -0.9915625, - -15.606873, - 7.1253057, - 15.600531, - 3.0372694, - -7.6383777, - 21.02248, - 17.865675, - 22.459995, - 12.360714, - 21.917862, - -2.2529974, - 23.902458, - 7.067429, - 19.686275, - -2.4079158, - 20.084156, - 9.865102, - -7.1564, - 14.4235935, - 10.5956135, - 33.509598, - 2.4050539, - 7.209693, - 5.5682783, - 24.32622, - 8.181132, - 18.989775, - -25.712864, - 24.787573, - 14.609039, - 7.1217036, - 16.848389, - 32.03336, - -1.7420386, - 10.520301, - 30.076416, - 2.9773757, - 17.441216, - 22.073168, - 29.535501, - 31.019588, - 31.29178, - 7.783574, - -1.0848922, - -1.632514, - 3.1787782, - 26.127491, - 11.578919, - 8.806574, - 26.605755, - 25.579552, - 27.044067, - 7.423554, - 19.89922, - 21.111221, - 29.565565, - 20.825033, - 8.250312, - -4.315795, - 33.91667, - 31.587502, - 22.03959, - 2.5191355, - -1.2537154, - 1.041188, - 6.271001, - 15.563097, - 19.476908, - 17.528534, - 14.279812, - 19.227066, - 27.98984, - 17.888885, - 20.995552, - -25.321373, - 2.030001, - 19.098873, - -1.3566388, - 8.210962, - -34.972008, - 26.949068, - 12.173473, - 20.502365, - 4.4826207, - 30.730364, - 22.801989, - -7.8275642, - 9.649646, - 5.474195, - 26.200584, - 23.3236, - 23.742764, - -9.009804, - 10.729193, - 24.293411, - 1.2037258, - -2.6479704, - 21.060795, - 13.542582, - -0.5990197, - -23.021431, - 11.98244, - 26.565365, - 27.739674, - 4.583181, - 28.793531, - 25.030203, - 20.863323, - 15.091036, - 4.7823358, - -19.877468, - 29.76316, - -3.8284235, - -8.098414, - 6.9839473, - 18.062141, - -3.4012072, - 15.937399, - 24.769993, - 8.2281, - -1.0408515, - 3.6773765, - 16.484713, - 23.824167, - -1.6157911, - -5.807004, - 5.4682074, - 20.873947, - 7.471235, - 4.855366, - 24.15315, - 33.191727, - -1.2212269, - 1.353517, - 33.686493, - 17.499424, - 10.638852, - 12.36343, - 25.982975, - -5.359385, - 10.390779, - -2.7448454, - 23.544054, - 1.2731425, - 20.49504, - 11.026453, - -5.233647, - 7.5924697, - 30.057226, - 17.865553, - 14.572172, - -0.43766883, - 26.446459, - 7.797492, - 27.607801, - -10.88375, - -5.4497714, - -3.2008982, - 3.516546, - -2.0853994, - 29.915886, - -4.8032465, - 10.539988, - 22.650063, - 16.676228, - 23.31772, - 6.885112, - -3.024608, - 16.557335, - 8.043718, - 4.2089057, - 26.167639, - 0.031842478, - 23.264338, - 28.108557, - 13.463569, - 17.450424, - 4.5370917, - -9.992426, - 19.615667, - -8.944074, - 10.486003, - 13.904057, - 0.17712176, - 26.276295, - 18.80954, - 8.85504, - 6.502574, - 30.14757, - 25.445856, - 23.137957, - 4.7606173, - 20.943676, - 34.946663, - 23.354967, - 10.895949, - 16.164768, - 18.747564, - -8.708449, - 26.564816, - 3.6623113, - -2.406363, - 30.990358, - 22.291674, - 8.649531, - 29.637974, - 12.135396, - 22.34238, - 17.654528, - 3.530811, - 1.305536, - -3.4917607, - 15.667845, - 22.695467, - 3.3096304, - 5.8128743, - 24.534492, - 0.7944149, - 23.025293, - -0.33986145, - 14.641378, - 4.667992, - 6.4794717, - -3.0957053, - 1.5328475, - 14.641849, - 0.75382006, - 13.353416, - 7.6756034, - -8.807442, - 5.574528, - 5.459471, - 30.176495, - 25.41137, - 12.340705, - 20.984993, - 11.320027, - 19.095402, - 26.357058, - 25.323908, - 0.40740138, - 23.981928, - 12.158624, - 15.439734, - 22.680363, - -0.7899231, - 0.6461262, - 21.006203, - 10.719515, - 2.1415112, - 15.075585, - 19.439365, - -7.721521, - 1.3289208, - 15.136754, - -3.098876, - 0.043326903, - -0.730605, - 20.569399, - 20.47704, - 34.56152, - -9.076381, - 11.381456, - 27.552359, - 34.52153, - 12.712044, - 13.092032, - -5.358728, - 21.975595, - 31.648344, - 27.402872, - 7.268004, - -9.072612, - 21.008837, - 6.308118, - 14.5252905, - -10.20566, - -5.336508, - 22.627249, - 17.18417, - -2.677447, - 6.842084, - -2.1064568, - 11.057577, - 21.760345, - 31.105818, - -7.2484465, - -5.4786696, - 16.441422, - -5.9703918, - 9.63625, - 6.1655693, - 18.591246, - 27.819729, - 5.3510475, - 15.152627, - 13.393224, - 19.411095, - 23.425772, - 26.30866, - 5.3038287, - 7.526191, - -2.2162335, - 31.378555, - 6.302167, - 14.10122, - 5.90295, - 7.223655, - 6.634295, - 7.232844, - 21.119562, - 12.128642, - 9.936496, - 21.916615, - 9.071596, - 14.497267, - -22.694798, - -8.824205, - -10.619046 - ], - "yaxis": "y" - }, - { - "customdata": [ - [ - "PC World - Upcoming chip set
will include built-in security
features for your PC." - ], - [ - "SAN JOSE, Calif. -- Apple
Computer (Quote, Chart)
unveiled a batch of new iPods,
iTunes software and promos
designed to keep it atop the
heap of digital music players." - ], - [ - "Any product, any shape, any
size -- manufactured on your
desktop! The future is the
fabricator. By Bruce Sterling
from Wired magazine." - ], - [ - "The Microsoft CEO says one way
to stem growing piracy of
Windows and Office in emerging
markets is to offer low-cost
computers." - ], - [ - "PalmSource surprised the
mobile vendor community today
with the announcement that it
will acquire China MobileSoft
(CMS), ostensibly to leverage
that company's expertise in
building a mobile version of
the Linux operating system." - ], - [ - "JEFFERSON CITY - An election
security expert has raised
questions about Missouri
Secretary of State Matt Blunt
#39;s plan to let soldiers at
remote duty stations or in
combat areas cast their
ballots with the help of
e-mail." - ], - [ - "A new, optional log-on service
from America Online that makes
it harder for scammers to
access a persons online
account will not be available
for Macintosh" - ], - [ - "It #39;s official. Microsoft
recently stated
definitivelyand contrary to
rumorsthat there will be no
new versions of Internet
Explorer for users of older
versions of Windows." - ], - [ - "But fresh antitrust suit is in
the envelope, says Novell" - ], - [ - "Chips that help a computer's
main microprocessors perform
specific types of math
problems are becoming a big
business once again.\\" - ], - [ - "A rocket carrying two Russian
cosmonauts and an American
astronaut to the international
space station streaked into
orbit on Thursday, the latest
flight of a Russian space
vehicle to fill in for
grounded US shuttles." - ], - [ - "OCTOBER 12, 2004
(COMPUTERWORLD) - Microsoft
Corp. #39;s monthly patch
release cycle may be making it
more predictable for users to
deploy software updates, but
there appears to be little
letup in the number of holes
being discovered in the
company #39;s software" - ], - [ - "Wearable tech goes mainstream
as the Gap introduces jacket
with built-in radio.\\" - ], - [ - "Funding round of \\$105 million
brings broadband Internet
telephony company's total haul
to \\$208 million." - ], - [ - "washingtonpost.com - Anthony
Casalena was 17 when he got
his first job as a programmer
for a start-up called
HyperOffice, a software
company that makes e-mail and
contact management
applications for the Web.
Hired as an intern, he became
a regular programmer after two
weeks and rewrote the main
product line." - ], - [ - "The fossilized neck bones of a
230-million-year-old sea
creature have features
suggesting that the animal
#39;s snakelike throat could
flare open and create suction
that would pull in prey." - ], - [ - "IBM late on Tuesday announced
the biggest update of its
popular WebSphere business
software in two years, adding
features such as automatically
detecting and fixing problems." - ], - [ - "AP - British entrepreneur
Richard Branson said Monday
that his company plans to
launch commercial space
flights over the next few
years." - ], - [ - "A group of Internet security
and instant messaging
providers have teamed up to
detect and thwart the growing
threat of IM and peer-to-peer
viruses and worms, they said
this week." - ], - [ - "His first space flight was in
1965 when he piloted the first
manned Gemini mission. Later
he made two trips to the moon
-- orbiting during a 1969
flight and then walking on the
lunar surface during a mission
in 1972." - ], - [ - "By RACHEL KONRAD SAN
FRANCISCO (AP) -- EBay Inc.,
which has been aggressively
expanding in Asia, plans to
increase its stake in South
Korea's largest online auction
company. The Internet
auction giant said Tuesday
night that it would purchase
nearly 3 million publicly held
shares of Internet Auction
Co..." - ], - [ - "AP - China's biggest computer
maker, Lenovo Group, said
Wednesday it has acquired a
majority stake in
International Business
Machines Corp.'s personal
computer business for
#36;1.75 billion, one of the
biggest Chinese overseas
acquisitions ever." - ], - [ - "Big evolutionary insights
sometimes come in little
packages. Witness the
startling discovery, in a cave
on the eastern Indonesian
island of Flores, of the
partial skeleton of a half-
size Homo species that" - ], - [ - "Luke Skywalker and Darth Vader
may get all the glory, but a
new Star Wars video game
finally gives credit to the
everyday grunts who couldn
#39;t summon the Force for
help." - ], - [ - " LOS ANGELES (Reuters) -
Federal authorities raided
three Washington, D.C.-area
video game stores and arrested
two people for modifying
video game consoles to play
pirated video games, a video
game industry group said on
Wednesday." - ], - [ - "Thornbugs communicate by
vibrating the branches they
live on. Now scientists are
discovering just what the bugs
are \"saying.\"" - ], - [ - "Federal prosecutors cracked a
global cartel that had
illegally fixed prices of
memory chips in personal
computers and servers for
three years." - ], - [ - "AP - Oracle Corp. CEO Larry
Ellison reiterated his
determination to prevail in a
long-running takeover battle
with rival business software
maker PeopleSoft Inc.,
predicting the proposed deal
will create a more competitive
company with improved customer
service." - ], - [ - "AP - Scientists warned Tuesday
that a long-term increase in
global temperature of 3.5
degrees could threaten Latin
American water supplies,
reduce food yields in Asia and
result in a rise in extreme
weather conditions in the
Caribbean." - ], - [ - "AP - Further proof New York's
real estate market is
inflated: The city plans to
sell space on top of lampposts
to wireless phone companies
for #36;21.6 million a year." - ], - [ - "The rollout of new servers and
networking switches in stores
is part of a five-year
agreement supporting 7-Eleven
#39;s Retail Information
System." - ], - [ - "Top Hollywood studios have
launched a wave of court cases
against internet users who
illegally download film files.
The Motion Picture Association
of America, which acts as the
representative of major film" - ], - [ - "AUSTRALIANS went into a
television-buying frenzy the
run-up to the Athens Olympics,
suggesting that as a nation we
could easily have scored a
gold medal for TV purchasing." - ], - [ - "The next time you are in your
bedroom with your PC plus
Webcam switched on, don #39;t
think that your privacy is all
intact. If you have a Webcam
plugged into an infected
computer, there is a
possibility that" - ], - [ - "AP - Shawn Fanning's Napster
software enabled countless
music fans to swap songs on
the Internet for free, turning
him into the recording
industry's enemy No. 1 in the
process." - ], - [ - "Reuters - Walt Disney Co.
is\\increasing investment in
video games for hand-held
devices and\\plans to look for
acquisitions of small game
publishers and\\developers,
Disney consumer products
Chairman Andy Mooney said\\on
Monday." - ], - [ - "BANGKOK - A UN conference last
week banned commercial trade
in the rare Irrawaddy dolphin,
a move environmentalists said
was needed to save the
threatened species." - ], - [ - "PC World - Updated antivirus
software for businesses adds
intrusion prevention features." - ], - [ - "Reuters - Online media company
Yahoo Inc.\\ late on Monday
rolled out tests of redesigned
start\\pages for its popular
Yahoo.com and My.Yahoo.com
sites." - ], - [ - "The Sun may have captured
thousands or even millions of
asteroids from another
planetary system during an
encounter more than four
billion years ago, astronomers
are reporting." - ], - [ - "Thumb through the book - then
buy a clean copy from Amazon" - ], - [ - "Reuters - High-definition
television can\\show the sweat
beading on an athlete's brow,
but the cost of\\all the
necessary electronic equipment
can get a shopper's own\\pulse
racing." - ], - [ - "MacCentral - Apple Computer
Inc. on Monday released an
update for Apple Remote
Desktop (ARD), the company's
software solution to assist
Mac system administrators and
computer managers with asset
management, software
distribution and help desk
support. ARD 2.1 includes
several enhancements and bug
fixes." - ], - [ - "The fastest-swiveling space
science observatory ever built
rocketed into orbit on
Saturday to scan the universe
for celestial explosions." - ], - [ - "Copernic Unleashes Desktop
Search Tool\\\\Copernic
Technologies Inc. today
announced Copernic Desktop
Search(TM) (CDS(TM)), \"The
Search Engine For Your PC
(TM).\" Copernic has used the
experience gained from over 30
million downloads of its
Windows-based Web search
software to develop CDS, a
desktop search product that
users are saying is far ..." - ], - [ - "The DVD Forum moved a step
further toward the advent of
HD DVD media and drives with
the approval of key physical
specifications at a meeting of
the organisations steering
committee last week." - ], - [ - "Often pigeonholed as just a
seller of televisions and DVD
players, Royal Philips
Electronics said third-quarter
profit surged despite a slide
into the red by its consumer
electronics division." - ], - [ - "AP - Google, the Internet
search engine, has done
something that law enforcement
officials and their computer
tools could not: Identify a
man who died in an apparent
hit-and-run accident 11 years
ago in this small town outside
Yakima." - ], - [ - "We are all used to IE getting
a monthly new security bug
found, but Winamp? In fact,
this is not the first security
flaw found in the application." - ], - [ - "The Apache Software Foundation
and the Debian Project said
they won't support the Sender
ID e-mail authentication
standard in their products." - ], - [ - "USATODAY.com - The newly
restored THX 1138 arrives on
DVD today with Star Wars
creator George Lucas' vision
of a Brave New World-like
future." - ], - [ - "The chances of scientists
making any one of five
discoveries by 2010 have been
hugely underestimated,
according to bookmakers." - ], - [ - "Deepening its commitment to
help corporate users create
SOAs (service-oriented
architectures) through the use
of Web services, IBM's Global
Services unit on Thursday
announced the formation of an
SOA Management Practice." - ], - [ - "Toshiba Corp. #39;s new
desktop-replacement multimedia
notebooks, introduced on
Tuesday, are further evidence
that US consumers still have
yet to embrace the mobility
offered by Intel Corp." - ], - [ - "Aregular Amazon customer,
Yvette Thompson has found
shopping online to be mostly
convenient and trouble-free.
But last month, after ordering
two CDs on Amazon.com, the
Silver Spring reader
discovered on her bank
statement that she was double-
charged for the \\$26.98 order.
And there was a \\$25 charge
that was a mystery." - ], - [ - "Finnish mobile giant Nokia has
described its new Preminet
solution, which it launched
Monday (Oct. 25), as a
quot;major worldwide
initiative." - ], - [ - " SAN FRANCISCO (Reuters) - At
virtually every turn, Intel
Corp. executives are heaping
praise on an emerging long-
range wireless technology
known as WiMAX, which can
blanket entire cities with
high-speed Internet access." - ], - [ - "Phishing is one of the
fastest-growing forms of
personal fraud in the world.
While consumers are the most
obvious victims, the damage
spreads far wider--hurting
companies #39; finances and
reputations and potentially" - ], - [ - "Reuters - The U.S. Interior
Department on\\Friday gave
final approval to a plan by
ConocoPhillips and\\partner
Anadarko Petroleum Corp. to
develop five tracts around\\the
oil-rich Alpine field on
Alaska's North Slope." - ], - [ - "Atari has opened the initial
sign-up phase of the closed
beta for its Dungeons amp;
Dragons real-time-strategy
title, Dragonshard." - ], - [ - "Big Blue adds features, beefs
up training efforts in China;
rival Unisys debuts new line
and pricing plan." - ], - [ - "South Korea's Hynix
Semiconductor Inc. and Swiss-
based STMicroelectronics NV
signed a joint-venture
agreement on Tuesday to
construct a memory chip
manufacturing plant in Wuxi,
about 100 kilometers west of
Shanghai, in China." - ], - [ - "Well, Intel did say -
dismissively of course - that
wasn #39;t going to try to
match AMD #39;s little dual-
core Opteron demo coup of last
week and show off a dual-core
Xeon at the Intel Developer
Forum this week and - as good
as its word - it didn #39;t." - ], - [ - "IT #39;S BEEN a heck of an
interesting two days here in
Iceland. I #39;ve seen some
interesting technology, heard
some inventive speeches and
met some people with different
ideas." - ], - [ - "Reuters - Two clients of
Germany's Postbank\\(DPBGn.DE)
fell for an e-mail fraud that
led them to reveal\\money
transfer codes to a bogus Web
site -- the first case of\\this
scam in German, prosecutors
said on Thursday." - ], - [ - "US spending on information
technology goods, services,
and staff will grow seven
percent in 2005 and continue
at a similar pace through
2008, according to a study
released Monday by Forrester
Research." - ], - [ - "Look, Ma, no hands! The U.S.
space agency's latest
spacecraft can run an entire
mission by itself. By Amit
Asaravala." - ], - [ - "Joining America Online,
EarthLink and Yahoo against
spamming, Microsoft Corp.
today announced the filing of
three new anti-Spam lawsuits
under the CAN-SPAM federal law
as part of its initiative in
solving the Spam problem for
Internet users worldwide." - ], - [ - "A nationwide inspection shows
Internet users are not as safe
online as they believe. The
inspections found most
consumers have no firewall
protection, outdated antivirus
software and dozens of spyware
programs secretly running on
their computers." - ], - [ - "More than three out of four
(76 percent) consumers are
experiencing an increase in
spoofing and phishing
incidents, and 35 percent
receive fake e-mails at least
once a week, according to a
recent national study." - ], - [ - "AP - Deep in the Atlantic
Ocean, undersea explorers are
living a safer life thanks to
germ-fighting clothing made in
Kinston." - ], - [ - "Computer Associates Monday
announced the general
availability of three
Unicenter performance
management products for
mainframe data management." - ], - [ - "Reuters - The European Union
approved on\\Wednesday the
first biotech seeds for
planting and sale across\\EU
territory, flying in the face
of widespread
consumer\\resistance to
genetically modified (GMO)
crops and foods." - ], - [ - "p2pnet.net News:- A Microsoft
UK quot;WEIGHING THE COST OF
LINUX VS. WINDOWS? LET #39;S
REVIEW THE FACTS quot;
magazine ad has been nailed as
misleading by Britain #39;s
Advertising Standards
Authority (ASA)." - ], - [ - "The retail sector overall may
be reporting a sluggish start
to the season, but holiday
shoppers are scooping up tech
goods at a brisk pace -- and
they're scouring the Web for
bargains more than ever.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\" color=\"#666666\">&
lt;B>-washingtonpost.com<
;/B></FONT>" - ], - [ - "The team behind the Beagle 2
mission has unveiled its
design for a successor to the
British Mars lander." - ], - [ - "Survey points to popularity in
Europe, the Middle East and
Asia of receivers that skip
the pay TV and focus on free
programming." - ], - [ - "Linux publisher Red Hat Inc.
said Tuesday that information-
technology consulting firm
Unisys Corp. will begin
offering a business version of
the company #39;s open-source
operating system on its
servers." - ], - [ - "IBM's p5-575, a specialized
server geared for high-
performance computing, has
eight 1.9GHz Power5
processors." - ], - [ - "Reuters - British police said
on Monday they had\\charged a
man with sending hoax emails
to relatives of people\\missing
since the Asian tsunami,
saying their loved ones
had\\been confirmed dead." - ], - [ - "LOS ANGELES - On Sept. 1,
former secretary of
Agriculture Dan Glickman
replaced the legendary Jack
Valenti as president and CEO
of Hollywood #39;s trade
group, the Motion Picture
Association of America." - ], - [ - "The director of the National
Hurricane Center stays calm in
the midst of a storm, but
wants everyone in hurricane-
prone areas to get the message
from his media advisories:
Respect the storm's power and
make proper response plans." - ], - [ - "SAN FRANCISCO Several
California cities and
counties, including Los
Angeles and San Francisco, are
suing Microsoft for what could
amount to billions of dollars." - ], - [ - "Google plans to release a
version of its desktop search
tool for computers that run
Apple Computer #39;s Mac
operating system, Google #39;s
chief executive, Eric Schmidt,
said Friday." - ], - [ - "AMD : sicurezza e prestazioni
ottimali con il nuovo
processore mobile per notebook
leggeri e sottili; Acer Inc.
preme sull #39;acceleratore
con il nuovo notebook a
marchio Ferrari." - ], - [ - "Firefox use around the world
climbed 34 percent in the last
month, according to a report
published by Web analytics
company WebSideStory Monday." - ], - [ - "If a plastic card that gives
you credit for something you
don't want isn't your idea of
a great gift, you can put it
up for sale or swap." - ], - [ - "WASHINGTON Aug. 17, 2004
Scientists are planning to
take the pulse of the planet
and more in an effort to
improve weather forecasts,
predict energy needs months in
advance, anticipate disease
outbreaks and even tell
fishermen where the catch will
be ..." - ], - [ - "New communications technology
could spawn future products.
Could your purse tell you to
bring an umbrella?" - ], - [ - "SAP has won a \\$35 million
contract to install its human
resources software for the US
Postal Service. The NetWeaver-
based system will replace the
Post Office #39;s current
25-year-old legacy application" - ], - [ - "NewsFactor - For years,
companies large and small have
been convinced that if they
want the sophisticated
functionality of enterprise-
class software like ERP and
CRM systems, they must buy
pre-packaged applications.
And, to a large extent, that
remains true." - ], - [ - "Following in the footsteps of
the RIAA, the MPAA (Motion
Picture Association of
America) announced that they
have began filing lawsuits
against people who use peer-
to-peer software to download
copyrighted movies off the
Internet." - ], - [ - "MacCentral - At a special
music event featuring Bono and
The Edge from rock group U2
held on Tuesday, Apple took
the wraps off the iPod Photo,
a color iPod available in 40GB
or 60GB storage capacities.
The company also introduced
the iPod U2, a special edition
of Apple's 20GB player clad in
black, equipped with a red
Click Wheel and featuring
engraved U2 band member
signatures. The iPod Photo is
available immediately, and
Apple expects the iPod U2 to
ship in mid-November." - ], - [ - "Reuters - Oracle Corp is
likely to win clearance\\from
the European Commission for
its hostile #36;7.7
billion\\takeover of rival
software firm PeopleSoft Inc.,
a source close\\to the
situation said on Friday." - ], - [ - "IBM (Quote, Chart) said it
would spend a quarter of a
billion dollars over the next
year and a half to grow its
RFID (define) business." - ], - [ - "SPACE.com - NASA released one
of the best pictures ever made
of Saturn's moon Titan as the
Cassini spacecraft begins a
close-up inspection of the
satellite today. Cassini is
making the nearest flyby ever
of the smog-shrouded moon." - ], - [ - "ANNAPOLIS ROYAL, NS - Nova
Scotia Power officials
continued to keep the sluice
gates open at one of the
utility #39;s hydroelectric
plants Wednesday in hopes a
wayward whale would leave the
area and head for the open
waters of the Bay of Fundy." - ], - [ - "In fact, Larry Ellison
compares himself to the
warlord, according to
PeopleSoft's former CEO,
defending previous remarks he
made." - ], - [ - "You can empty your pockets of
change, take off your belt and
shoes and stick your keys in
the little tray. But if you've
had radiation therapy
recently, you still might set
off Homeland Security alarms." - ], - [ - "SEOUL, Oct 19 Asia Pulse -
LG.Philips LCD Co.
(KSE:034220), the world #39;s
second-largest maker of liquid
crystal display (LCD), said
Tuesday it has developed the
world #39;s largest organic
light emitting diode" - ], - [ - "Security experts warn of
banner ads with a bad attitude
--and a link to malicious
code. Also: Phishers, be gone." - ], - [ - "com October 15, 2004, 5:11 AM
PT. Wood paneling and chrome
made your dad #39;s station
wagon look like a million
bucks, and they might also be
just the ticket for Microsoft
#39;s fledgling" - ], - [ - "Computer Associates
International yesterday
reported a 6 increase in
revenue during its second
fiscal quarter, but posted a
\\$94 million loss after paying
to settle government
investigations into the
company, it said yesterday." - ], - [ - "PC World - Send your video
throughout your house--
wirelessly--with new gateways
and media adapters." - ], - [ - "Links to this week's topics
from search engine forums
across the web: New MSN Search
Goes LIVE in Beta - Microsoft
To Launch New Search Engine -
Google Launches 'Google
Advertising Professionals' -
Organic vs Paid Traffic ROI? -
Making Money With AdWords? -
Link Building 101" - ], - [ - "Eight conservation groups are
fighting the US government
over a plan to poison
thousands of prairie dogs in
the grasslands of South
Dakota, saying wildlife should
not take a backseat to
ranching interests." - ], - [ - "Siding with chip makers,
Microsoft said it won't charge
double for its per-processor
licenses when dual-core chips
come to market next year." - ], - [ - "IBM and the Spanish government
have introduced a new
supercomputer they hope will
be the most powerful in
Europe, and one of the 10 most
powerful in the world." - ], - [ - "Mozilla's new web browser is
smart, fast and user-friendly
while offering a slew of
advanced, customizable
functions. By Michelle Delio." - ], - [ - "originally offered on notebook
PCs -- to its Opteron 32- and
64-bit x86 processors for
server applications. The
technology will help servers
to run" - ], - [ - "MacCentral - After Apple
unveiled the iMac G5 in Paris
this week, Vice President of
Hardware Product Marketing
Greg Joswiak gave Macworld
editors a guided tour of the
desktop's new design. Among
the topics of conversation:
the iMac's cooling system, why
pre-installed Bluetooth
functionality and FireWire 800
were left out, and how this
new model fits in with Apple's
objectives." - ], - [ - "We #39;ve known about
quot;strained silicon quot;
for a while--but now there
#39;s a better way to do it.
Straining silicon improves
chip performance." - ], - [ - "This week, Sir Richard Branson
announced his new company,
Virgin Galactic, has the
rights to the first commercial
flights into space." - ], - [ - "71-inch HDTV comes with a home
stereo system and components
painted in 24-karat gold." - ], - [ - "MacCentral - RealNetworks Inc.
said on Tuesday that it has
sold more than a million songs
at its online music store
since slashing prices last
week as part of a limited-time
sale aimed at growing the user
base of its new digital media
software." - ], - [ - "With the presidential election
less than six weeks away,
activists and security experts
are ratcheting up concern over
the use of touch-screen
machines to cast votes.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-washingtonpost.com&l
t;/B></FONT>" - ], - [ - "NEW YORK, September 14 (New
Ratings) - Yahoo! Inc
(YHOO.NAS) has agreed to
acquire Musicmatch Inc, a
privately held digital music
software company, for about
\\$160 million in cash." - ], - [ - "Gov. Rod Blagojevich plans to
propose a ban Thursday on the
sale of violent and sexually
explicit video games to
minors, something other states
have tried with little
success." - ], - [ - "A cable channel plans to
resurrect each of the 1,230
regular-season games listed on
the league's defunct 2004-2005
schedule by setting them in
motion on a video game
console." - ], - [ - "SiliconValley.com - When
\"Halo\" became a smash video
game hit following Microsoft's
launch of the Xbox console in
2001, it was a no-brainer that
there would be a sequel to the
science fiction shoot-em-up." - ], - [ - "PARIS -- The city of Paris
intends to reduce its
dependence on software
suppliers with \"de facto
monopolies,\" but considers an
immediate switch of its 17,000
desktops to open source
software too costly, it said
Wednesday." - ], - [ - "The vast majority of consumers
are unaware that an Apple iPod
digital music player only
plays proprietary iTunes
files, while a smaller
majority agree that it is
within RealNetworks #39;
rights to develop a program
that will make its music files
compatible" - ], - [ - " PROVIDENCE, R.I. (Reuters) -
You change the oil in your car
every 5,000 miles or so. You
clean your house every week or
two. Your PC needs regular
maintenance as well --
especially if you're using
Windows and you spend a lot of
time on the Internet." - ], - [ - "The Motley Fool - Here's
something you don't see every
day -- the continuing brouhaha
between Oracle (Nasdaq: ORCL -
News) and PeopleSoft (Nasdaq:
PSFT - News) being a notable
exception. South Africa's
Harmony Gold Mining Company
(NYSE: HMY - News) has
announced a hostile takeover
bid to acquire fellow South
African miner Gold Fields
Limited (NYSE: GFI - News).
The transaction, if it takes
place, would be an all-stock
acquisition, with Harmony
issuing 1.275 new shares in
payment for each share of Gold
Fields. The deal would value
Gold Fields at more than
#36;8 billion. ..." - ], - [ - "SPACE.com - NASA's Mars \\rover
Opportunity nbsp;will back its
\\way out of a nbsp;crater it
has spent four months
exploring after reaching
terrain nbsp;that appears \\too
treacherous to tread. nbsp;" - ], - [ - "Sony Corp. announced Tuesday a
new 20 gigabyte digital music
player with MP3 support that
will be available in Great
Britain and Japan before
Christmas and elsewhere in
Europe in early 2005." - ], - [ - "TOKYO (AP) -- The electronics
and entertainment giant Sony
Corp. (SNE) is talking with
Wal-Mart Stores Inc..." - ], - [ - "After an unprecedented span of
just five days, SpaceShipOne
is ready for a return trip to
space on Monday, its final
flight to clinch a \\$10
million prize." - ], - [ - "Hi-tech monitoring of
livestock at pig farms could
help improve the animal growth
process and reduce costs." - ], - [ - "Cable amp; Wireless plc
(NYSE: CWP - message board) is
significantly ramping up its
investment in local loop
unbundling (LLU) in the UK,
and it plans to spend up to 85
million (\\$152." - ], - [ - "AFP - Microsoft said that it
had launched a new desktop
search tool that allows
personal computer users to
find documents or messages on
their PCs." - ], - [ - "The three largest computer
makers spearheaded a program
today designed to standardize
working conditions for their
non-US workers." - ], - [ - "Description: Illinois Gov. Rod
Blagojevich is backing state
legislation that would ban
sales or rentals of video
games with graphic sexual or
violent content to children
under 18." - ], - [ - "Are you bidding on keywords
through Overture's Precision
Match, Google's AdWords or
another pay-for-placement
service? If so, you're
eligible to participate in
their contextual advertising
programs." - ], - [ - "Nobel Laureate Wilkins, 87,
played an important role in
the discovery of the double
helix structure of DNA, the
molecule that carries our
quot;life code quot;,
Kazinform refers to BBC News." - ], - [ - "A number of signs point to
increasing demand for tech
workers, but not all the
clouds have been driven away." - ], - [ - "Microsoft Corp. (MSFT.O:
Quote, Profile, Research)
filed nine new lawsuits
against spammers who send
unsolicited e-mail, including
an e-mail marketing Web
hosting company, the world
#39;s largest software maker
said on Thursday." - ], - [ - "The rocket plane SpaceShipOne
is just one flight away from
claiming the Ansari X-Prize, a
\\$10m award designed to kick-
start private space travel." - ], - [ - "Reuters - Three American
scientists won the\\2004 Nobel
physics prize on Tuesday for
showing how tiny
quark\\particles interact,
helping to explain everything
from how a\\coin spins to how
the universe was built." - ], - [ - "AP - Sharp Electronics Corp.
plans to stop selling its
Linux-based handheld computer
in the United States, another
sign of the slowing market for
personal digital assistants." - ], - [ - "Reuters - Glaciers once held
up by a floating\\ice shelf off
Antarctica are now sliding off
into the sea --\\and they are
going fast, scientists said on
Tuesday." - ], - [ - "AP - Warning lights flashed
atop four police cars as the
caravan wound its way up the
driveway in a procession fit
for a presidential candidate.
At long last, Azy and Indah
had arrived. They even flew
through a hurricane to get
here." - ], - [ - "\\Female undergraduates work
harder and are more open-
minded than males, leading to
better results, say
scientists." - ], - [ - "The United Nations annual
World Robotics Survey predicts
the use of robots around the
home will surge seven-fold by
2007. The boom is expected to
be seen in robots that can mow
lawns and vacuum floors, among
other chores." - ], - [ - "Want to dive deep -- really
deep -- into the technical
literature about search
engines? Here's a road map to
some of the best web
information retrieval
resources available online." - ], - [ - "Reuters - Ancel Keys, a
pioneer in public health\\best
known for identifying the
connection between
a\\cholesterol-rich diet and
heart disease, has died." - ], - [ - "Reuters - T-Mobile USA, the
U.S. wireless unit\\of Deutsche
Telekom AG (DTEGn.DE), does
not expect to offer\\broadband
mobile data services for at
least the next two years,\\its
chief executive said on
Thursday." - ], - [ - "Verizon Communications is
stepping further into video as
a way to compete against cable
companies." - ], - [ - "Oracle Corp. plans to release
the latest version of its CRM
(customer relationship
management) applications
within the next two months, as
part of an ongoing update of
its E-Business Suite." - ], - [ - "Hosted CRM service provider
Salesforce.com took another
step forward last week in its
strategy to build an online
ecosystem of vendors that
offer software as a service." - ], - [ - "washingtonpost.com -
Technology giants IBM and
Hewlett-Packard are injecting
hundreds of millions of
dollars into radio-frequency
identification technology,
which aims to advance the
tracking of items from ho-hum
bar codes to smart tags packed
with data." - ], - [ - "The Norfolk Broads are on
their way to getting a clean
bill of ecological health
after a century of stagnation." - ], - [ - "For the second time this year,
an alliance of major Internet
providers - including Atlanta-
based EarthLink -iled a
coordinated group of lawsuits
aimed at stemming the flood of
online junk mail." - ], - [ - "Via Technologies has released
a version of the open-source
Xine media player that is
designed to take advantage of
hardware digital video
acceleration capabilities in
two of the company #39;s PC
chipsets, the CN400 and
CLE266." - ], - [ - "SCO Group has a plan to keep
itself fit enough to continue
its legal battles against
Linux and to develop its Unix-
on-Intel operating systems." - ], - [ - "New software helps corporate
travel managers track down
business travelers who
overspend. But it also poses a
dilemma for honest travelers
who are only trying to save
money." - ], - [ - "NATO Secretary-General Jaap de
Hoop Scheffer has called a
meeting of NATO states and
Russia on Tuesday to discuss
the siege of a school by
Chechen separatists in which
more than 335 people died, a
NATO spokesman said." - ], - [ - "AFP - Senior executives at
business software group
PeopleSoft unanimously
recommended that its
shareholders reject a 8.8
billion dollar takeover bid
from Oracle Corp, PeopleSoft
said in a statement Wednesday." - ], - [ - "Reuters - Neolithic people in
China may have\\been the first
in the world to make wine,
according to\\scientists who
have found the earliest
evidence of winemaking\\from
pottery shards dating from
7,000 BC in northern China." - ], - [ - " TOKYO (Reuters) - Electronics
conglomerate Sony Corp.
unveiled eight new flat-screen
televisions on Thursday in a
product push it hopes will
help it secure a leading 35
percent of the domestic
market in the key month of
December." - ], - [ - "Donald Halsted, one target of
a class-action suit alleging
financial improprieties at
bankrupt Polaroid, officially
becomes CFO." - ], - [ - "The 7710 model features a
touch screen, pen input, a
digital camera, an Internet
browser, a radio, video
playback and streaming and
recording capabilities, the
company said." - ], - [ - "A top Red Hat executive has
attacked the open-source
credentials of its sometime
business partner Sun
Microsystems. In a Web log
posting Thursday, Michael
Tiemann, Red Hat #39;s vice
president of open-source
affairs" - ], - [ - "MySQL developers turn to an
unlikely source for database
tool: Microsoft. Also: SGI
visualizes Linux, and the
return of Java veteran Kim
Polese." - ], - [ - "Dell Inc. said its profit
surged 25 percent in the third
quarter as the world's largest
personal computer maker posted
record sales due to rising
technology spending in the
corporate and government
sectors in the United States
and abroad." - ], - [ - "Reuters - SBC Communications
said on Monday it\\would offer
a television set-top box that
can handle music,\\photos and
Internet downloads, part of
SBC's efforts to expand\\into
home entertainment." - ], - [ - " WASHINGTON (Reuters) - Hopes
-- and worries -- that U.S.
regulators will soon end the
ban on using wireless phones
during U.S. commercial flights
are likely at least a year or
two early, government
officials and analysts say." - ], - [ - "After a year of pilots and
trials, Siebel Systems jumped
with both feet into the SMB
market Tuesday, announcing a
new approach to offer Siebel
Professional CRM applications
to SMBs (small and midsize
businesses) -- companies with
revenues up to about \\$500
million." - ], - [ - "Intel won't release a 4-GHz
version of its flagship
Pentium 4 product, having
decided instead to realign its
engineers around the company's
new design priorities, an
Intel spokesman said today.
\\\\" - ], - [ - "AP - A Soyuz spacecraft
carrying two Russians and an
American rocketed closer
Friday to its docking with the
international space station,
where the current three-man
crew made final departure
preparations." - ], - [ - "Scientists have manipulated
carbon atoms to create a
material that could be used to
create light-based, versus
electronic, switches. The
material could lead to a
supercharged Internet based
entirely on light, scientists
say." - ], - [ - "LOS ANGELES (Reuters)
Qualcomm has dropped an \\$18
million claim for monetary
damages from rival Texas
Instruments for publicly
discussing terms of a
licensing pact, a TI
spokeswoman confirmed Tuesday." - ], - [ - "Hewlett-Packard is the latest
IT vendor to try blogging. But
analysts wonder if the weblog
trend is the 21st century
equivalent of CB radios, which
made a big splash in the 1970s
before fading." - ], - [ - "The scientists behind Dolly
the sheep apply for a license
to clone human embryos. They
want to take stem cells from
the embryos to study Lou
Gehrig's disease." - ], - [ - "Some of the silly tunes
Japanese pay to download to
use as the ring tone for their
mobile phones sure have their
knockers, but it #39;s for
precisely that reason that a
well-known counselor is raking
it in at the moment, according
to Shukan Gendai (10/2)." - ], - [ - "IBM announced yesterday that
it will invest US\\$250 million
(S\\$425 million) over the next
five years and employ 1,000
people in a new business unit
to support products and
services related to sensor
networks." - ], - [ - "AP - Microsoft Corp. goes into
round two Friday of its battle
to get the European Union's
sweeping antitrust ruling
lifted having told a judge
that it had been prepared
during settlement talks to
share more software code with
its rivals than the EU
ultimately demanded." - ], - [ - "AP - Sears, Roebuck and Co.,
which has successfully sold
its tools and appliances on
the Web, is counting on having
the same magic with bedspreads
and sweaters, thanks in part
to expertise gained by its
purchase of Lands' End Inc." - ], - [ - "Austin police are working with
overseas officials to bring
charges against an English man
for sexual assault of a child,
a second-degree felony." - ], - [ - "San Francisco
developer/publisher lands
coveted Paramount sci-fi
license, \\$6.5 million in
funding on same day. Although
it is less than two years old,
Perpetual Entertainment has
acquired one of the most
coveted sci-fi licenses on the
market." - ], - [ - "By KELLY WIESE JEFFERSON
CITY, Mo. (AP) -- Missouri
will allow members of the
military stationed overseas to
return absentee ballots via
e-mail, raising concerns from
Internet security experts
about fraud and ballot
secrecy..." - ], - [ - "Avis Europe PLC has dumped a
new ERP system based on
software from PeopleSoft Inc.
before it was even rolled out,
citing substantial delays and
higher-than-expected costs." - ], - [ - "Yahoo #39;s (Quote, Chart)
public embrace of the RSS
content syndication format
took a major leap forward with
the release of a revamped My
Yahoo portal seeking to
introduce the technology to
mainstream consumers." - ], - [ - "TOKYO (AP) -- Japanese
electronics and entertainment
giant Sony Corp. (SNE) plans
to begin selling a camcorder
designed for consumers that
takes video at digital high-
definition quality and is
being priced at about
\\$3,600..." - ], - [ - "In a meeting of the cinematic
with the scientific, Hollywood
helicopter stunt pilots will
try to snatch a returning NASA
space probe out of the air
before it hits the ground." - ], - [ - "Legend has it (incorrectly, it
seems) that infamous bank
robber Willie Sutton, when
asked why banks were his
favorite target, responded,
quot;Because that #39;s where
the money is." - ], - [ - "Pfizer, GlaxoSmithKline and
Purdue Pharma are the first
drugmakers willing to take the
plunge and use radio frequency
identification technology to
protect their US drug supply
chains from counterfeiters." - ], - [ - "Search any fee-based digital
music service for the best-
loved musical artists of the
20th century and most of the
expected names show up." - ], - [ - "America Online Inc. is
packaging new features to
combat viruses, spam and
spyware in response to growing
online security threats.
Subscribers will be able to
get the free tools" - ], - [ - "CAPE CANAVERAL-- NASA aims to
launch its first post-Columbia
shuttle mission during a
shortened nine-day window
March, and failure to do so
likely would delay a planned
return to flight until at
least May." - ], - [ - "The Chinese city of Beijing
has cancelled an order for
Microsoft software, apparently
bowing to protectionist
sentiment. The deal has come
under fire in China, which is
trying to build a domestic
software industry." - ], - [ - "Apple says it will deliver its
iTunes music service to more
European countries next month.
Corroborating several reports
in recent months, Reuters is
reporting today that Apple
Computer is planning the next" - ], - [ - "Reuters - Motorola Inc., the
world's\\second-largest mobile
phone maker, said on Tuesday
it expects\\to sustain strong
sales growth in the second
half of 2004\\thanks to new
handsets with innovative
designs and features." - ], - [ - "PRAGUE, Czech Republic --
Eugene Cernan, the last man to
walk on the moon during the
final Apollo landing, said
Thursday he doesn't expect
space tourism to become
reality in the near future,
despite a strong demand.
Cernan, now 70, who was
commander of NASA's Apollo 17
mission and set foot on the
lunar surface in December 1972
during his third space flight,
acknowledged that \"there are
many people interested in
space tourism.\" But the
former astronaut said he
believed \"we are a long way
away from the day when we can
send a bus of tourists to the
moon.\" He spoke to reporters
before being awarded a medal
by the Czech Academy of
Sciences for his contribution
to science..." - ], - [ - "Never shy about entering a
market late, Microsoft Corp.
is planning to open the
virtual doors of its long-
planned Internet music store
next week. <FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</B></FONT>" - ], - [ - "AP - On his first birthday
Thursday, giant panda cub Mei
Sheng delighted visitors by
playing for the first time in
snow delivered to him at the
San Diego Zoo. He also sat on
his ice cake, wrestled with
his mom, got his coat
incredibly dirty, and didn't
read any of the more than 700
birthday wishes sent him via
e-mail from as far away as
Ireland and Argentina." - ], - [ - "AP - Researchers put a
satellite tracking device on a
15-foot shark that appeared to
be lost in shallow water off
Cape Cod, the first time a
great white has been tagged
that way in the Atlantic." - ], - [ - "The Windows Future Storage
(WinFS) technology that got
cut out of Windows
quot;Longhorn quot; is in
serious trouble, and not just
the hot water a feature might
encounter for missing its
intended production vehicle." - ], - [ - "washingtonpost.com - Let the
games begin. Not the Olympics
again, but the all-out battle
between video game giants Sony
Corp. and Nintendo Co. Ltd.
The two Japanese companies are
rolling out new gaming
consoles, but Nintendo has
beaten Sony to the punch by
announcing an earlier launch
date for its new hand-held
game player." - ], - [ - "AP - Echoing what NASA
officials said a day earlier,
a Russian space official on
Friday said the two-man crew
on the international space
station could be forced to
return to Earth if a planned
resupply flight cannot reach
them with food supplies later
this month." - ], - [ - "InfoWorld - SANTA CLARA,
CALIF. -- Accommodating large
patch sets in Linux is
expected to mean forking off
of the 2.7 version of the
platform to accommodate these
changes, according to Andrew
Morton, lead maintainer of the
Linux kernel for Open Source
Development Labs (OSDL)." - ], - [ - "AMSTERDAM The mobile phone
giants Vodafone and Nokia
teamed up on Thursday to
simplify cellphone software
written with the Java computer
language." - ], - [ - "The overall Linux market is
far larger than previous
estimates show, a new study
says. In an analysis of the
Linux market released late
Tuesday, market research firm
IDC estimated that the Linux
market -- including" - ], - [ - "By PAUL ELIAS SAN FRANCISCO
(AP) -- Several California
cities and counties, including
San Francisco and Los Angeles,
sued Microsoft Corp. (MSFT) on
Friday, accusing the software
giant of illegally charging
inflated prices for its
products because of monopoly
control of the personal
computer operating system
market..." - ], - [ - "Public opinion of the database
giant sinks to 12-year low, a
new report indicates." - ], - [ - "For spammers, it #39;s been a
summer of love. Two newly
issued reports tracking the
circulation of unsolicited
e-mails say pornographic spam
dominated this summer, nearly
all of it originating from
Internet addresses in North
America." - ], - [ - "Microsoft portrayed its
Longhorn decision as a
necessary winnowing to hit the
2006 timetable. The
announcement on Friday,
Microsoft executives insisted,
did not point to a setback in
software" - ], - [ - "A problem in the Service Pack
2 update for Windows XP may
keep owners of AMD-based
computers from using the long-
awaited security package,
according to Microsoft." - ], - [ - "Five years ago, running a
telephone company was an
immensely profitable
proposition. Since then, those
profits have inexorably
declined, and now that decline
has taken another gut-
wrenching dip." - ], - [ - "NEW YORK - The litigious
Recording Industry Association
of America (RIAA) is involved
in another legal dispute with
a P-to-P (peer-to-peer)
technology maker, but this
time, the RIAA is on defense.
Altnet Inc. filed a lawsuit
Wednesday accusing the RIAA
and several of its partners of
infringing an Altnet patent
covering technology for
identifying requested files on
a P-to-P network." - ], - [ - "For two weeks before MTV
debuted U2 #39;s video for the
new single quot;Vertigo,
quot; fans had a chance to see
the band perform the song on
TV -- in an iPod commercial." - ], - [ - "Oct. 26, 2004 - The US-
European spacecraft Cassini-
Huygens on Tuesday made a
historic flyby of Titan,
Saturn #39;s largest moon,
passing so low as to almost
touch the fringes of its
atmosphere." - ], - [ - "Reuters - A volcano in central
Japan sent smoke and\\ash high
into the sky and spat out
molten rock as it erupted\\for
a fourth straight day on
Friday, but experts said the
peak\\appeared to be quieting
slightly." - ], - [ - "Shares of Google Inc. made
their market debut on Thursday
and quickly traded up 19
percent at \\$101.28. The Web
search company #39;s initial
public offering priced at \\$85" - ], - [ - "Reuters - Global warming is
melting\\Ecuador's cherished
mountain glaciers and could
cause several\\of them to
disappear over the next two
decades, Ecuadorean and\\French
scientists said on Wednesday." - ], - [ - "Rather than tell you, Dan
Kranzler chooses instead to
show you how he turned Mforma
into a worldwide publisher of
video games, ringtones and
other hot downloads for mobile
phones." - ], - [ - "Not being part of a culture
with a highly developed
language, could limit your
thoughts, at least as far as
numbers are concerned, reveals
a new study conducted by a
psychologist at the Columbia
University in New York." - ], - [ - "CAMBRIDGE, Mass. A native of
Red Oak, Iowa, who was a
pioneer in astronomy who
proposed the quot;dirty
snowball quot; theory for the
substance of comets, has died." - ], - [ - "A Portuguese-sounding version
of the virus has appeared in
the wild. Be wary of mail from
Manaus." - ], - [ - "Reuters - Hunters soon may be
able to sit at\\their computers
and blast away at animals on a
Texas ranch via\\the Internet,
a prospect that has state
wildlife officials up\\in arms." - ], - [ - "The Bedminster-based company
yesterday said it was pushing
into 21 new markets with the
service, AT amp;T CallVantage,
and extending an introductory
rate offer until Sept. 30. In
addition, the company is
offering in-home installation
of up to five ..." - ], - [ - "Samsung Electronics Co., Ltd.
has developed a new LCD
(liquid crystal display)
technology that builds a touch
screen into the display, a
development that could lead to
thinner and cheaper display
panels for mobile phones, the
company said Tuesday." - ], - [ - "The message against illegally
copying CDs for uses such as
in file-sharing over the
Internet has widely sunk in,
said the company in it #39;s
recent announcement to drop
the Copy-Control program." - ], - [ - "Reuters - California will
become hotter and\\drier by the
end of the century, menacing
the valuable wine and\\dairy
industries, even if dramatic
steps are taken to curb\\global
warming, researchers said on
Monday." - ], - [ - "IBM said Monday that it won a
500 million (AUD\\$1.25
billion), seven-year services
contract to help move UK bank
Lloyds TBS from its
traditional voice
infrastructure to a converged
voice and data network." - ], - [ - "Space shuttle astronauts will
fly next year without the
ability to repair in orbit the
type of damage that destroyed
the Columbia vehicle in
February 2003." - ], - [ - "By cutting WinFS from Longhorn
and indefinitely delaying the
storage system, Microsoft
Corp. has also again delayed
the Microsoft Business
Framework (MBF), a new Windows
programming layer that is
closely tied to WinFS." - ], - [ - "Samsung's new SPH-V5400 mobile
phone sports a built-in
1-inch, 1.5-gigabyte hard disk
that can store about 15 times
more data than conventional
handsets, Samsung said." - ], - [ - "Vendor says it #39;s
developing standards-based
servers in various form
factors for the telecom
market. By Darrell Dunn.
Hewlett-Packard on Thursday
unveiled plans to create a
portfolio of products and
services" - ], - [ - "Ziff Davis - The company this
week will unveil more programs
and technologies designed to
ease users of its high-end
servers onto its Integrity
line, which uses Intel's
64-bit Itanium processor." - ], - [ - "The Mac maker says it will
replace about 28,000 batteries
in one model of PowerBook G4
and tells people to stop using
the notebook." - ], - [ - "Millions of casual US anglers
are having are larger than
appreciated impact on sea fish
stocks, scientists claim." - ], - [ - "Microsoft Xbox Live traffic on
service provider networks
quadrupled following the
November 9th launch of Halo-II
-- which set entertainment
industry records by selling
2.4-million units in the US
and Canada on the first day of
availability, driving cash" - ], - [ - "Lawyers in a California class
action suit against Microsoft
will get less than half the
payout they had hoped for. A
judge in San Francisco ruled
that the attorneys will
collect only \\$112." - ], - [ - "Google Browser May Become
Reality\\\\There has been much
fanfare in the Mozilla fan
camps about the possibility of
Google using Mozilla browser
technology to produce a
GBrowser - the Google Browser.
Over the past two weeks, the
news and speculation has
escalated to the point where
even Google itself is ..." - ], - [ - "Hewlett-Packard has joined
with Brocade to integrate
Brocade #39;s storage area
network switching technology
into HP Bladesystem servers to
reduce the amount of fabric
infrastructure needed in a
datacentre." - ], - [ - "The US Senate Commerce
Committee on Wednesday
approved a measure that would
provide up to \\$1 billion to
ensure consumers can still
watch television when
broadcasters switch to new,
crisp digital signals." - ], - [ - "Reuters - Internet stocks
are\\as volatile as ever, with
growth-starved investors
flocking to\\the sector in the
hope they've bought shares in
the next online\\blue chip." - ], - [ - "NASA has released an inventory
of the scientific devices to
be put on board the Mars
Science Laboratory rover
scheduled to land on the
surface of Mars in 2009, NASAs
news release reads." - ], - [ - "The U.S. Congress needs to
invest more in the U.S.
education system and do more
to encourage broadband
adoption, the chief executive
of Cisco said Wednesday.<p&
gt;ADVERTISEMENT</p><
p><img src=\"http://ad.do
ubleclick.net/ad/idg.us.ifw.ge
neral/sbcspotrssfeed;sz=1x1;or
d=200301151450?\" width=\"1\"
height=\"1\"
border=\"0\"/><a href=\"htt
p://ad.doubleclick.net/clk;922
8975;9651165;a?http://www.info
world.com/spotlights/sbc/main.
html?lpid0103035400730000idlp\"
>SBC Case Study: Crate Ba
rrel</a><br/>What
sold them on improving their
network? A system that could
cut management costs from the
get-go. Find out
more.</p>" - ], - [ - "Thirty-two countries and
regions will participate the
Fifth China International
Aviation and Aerospace
Exhibition, opening Nov. 1 in
Zhuhai, a city in south China
#39;s Guangdong Province." - ], - [ - "p2pnet.net News:- Virgin
Electronics has joined the mp3
race with a \\$250, five gig
player which also handles
Microsoft #39;s WMA format." - ], - [ - "Microsoft has suspended the
beta testing of the next
version of its MSN Messenger
client because of a potential
security problem, a company
spokeswoman said Wednesday." - ], - [ - "AP - Business software maker
Oracle Corp. attacked the
credibility and motives of
PeopleSoft Inc.'s board of
directors Monday, hoping to
rally investor support as the
17-month takeover battle
between the bitter business
software rivals nears a
climactic showdown." - ], - [ - "A new worm has been discovered
in the wild that #39;s not
just settling for invading
users #39; PCs--it wants to
invade their homes too." - ], - [ - "Researchers have for the first
time established the existence
of odd-parity superconductors,
materials that can carry
electric current without any
resistance." - ], - [ - "BRUSSELS, Belgium (AP) --
European antitrust regulators
said Monday they have extended
their review of a deal between
Microsoft Corp. (MSFT) and
Time Warner Inc..." - ], - [ - "Fans who can't get enough of
\"The Apprentice\" can visit a
new companion Web site each
week and watch an extra 40
minutes of video not broadcast
on the Thursday
show.<br><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</b></font>" - ], - [ - "An adult Web site publisher is
suing Google, saying the
search engine company made it
easier for users to see the
site #39;s copyrighted nude
photographs without paying or
gaining access through the
proper channels." - ], - [ - "A Washington-based public
opinion firm has released the
results of an election day
survey of Nevada voters
showing 81 support for the
issuance of paper receipts
when votes are cast
electronically." - ], - [ - "NAPSTER creator SHAWN FANNING
has revealed his plans for a
new licensed file-sharing
service with an almost
unlimited selection of tracks." - ], - [ - "Two separate studies by U.S.
researchers find that super
drug-resistant strains of
tuberculosis are at the
tipping point of a global
epidemic, and only small
changes could help them spread
quickly." - ], - [ - "Dell cut prices on some
servers and PCs by as much as
22 percent because it #39;s
paying less for parts. The
company will pass the savings
on components such as memory
and liquid crystal displays" - ], - [ - "WASHINGTON: The European-
American Cassini-Huygens space
probe has detected traces of
ice flowing on the surface of
Saturn #39;s largest moon,
Titan, suggesting the
existence of an ice volcano,
NASA said Tuesday." - ], - [ - "All ISS systems continue to
function nominally, except
those noted previously or
below. Day 7 of joint
Exp.9/Exp.10 operations and
last full day before 8S
undocking." - ], - [ - " quot;Magic can happen. quot;
Sirius Satellite Radio
(nasdaq: SIRI - news - people
) may have signed Howard Stern
and the men #39;s NCAA
basketball tournaments, but XM
Satellite Radio (nasdaq: XMSR
- news - people ) has its
sights on your cell phone." - ], - [ - "Trick-or-treaters can expect
an early Halloween treat on
Wednesday night, when a total
lunar eclipse makes the moon
look like a glowing pumpkin." - ], - [ - "Sifting through millions of
documents to locate a valuable
few is tedious enough, but
what happens when those files
are scattered across different
repositories?" - ], - [ - "NASA #39;s Mars rovers have
uncovered more tantalizing
evidence of a watery past on
the Red Planet, scientists
said Wednesday. And the
rovers, Spirit and
Opportunity, are continuing to
do their jobs months after
they were expected to ..." - ], - [ - "Intel Chief Technology Officer
Pat Gelsinger said on
Thursday, Sept. 9, that the
Internet needed to be upgraded
in order to deal with problems
that will become real issues
soon." - ], - [ - " LONDON (Reuters) - Television
junkies of the world, get
ready for \"Friends,\" \"Big
Brother\" and \"The Simpsons\" to
phone home." - ], - [ - "I #39;M FEELING a little bit
better about the hundreds of
junk e-mails I get every day
now that I #39;ve read that
someone else has much bigger
e-mail troubles." - ], - [ - "Microsoft's antispam Sender ID
technology continues to get
the cold shoulder. Now AOL
adds its voice to a growing
chorus of businesses and
organizations shunning the
proprietary e-mail
authentication system." - ], - [ - "Through the World Community
Grid, your computer could help
address the world's health and
social problems." - ], - [ - "A planned component for
Microsoft #39;s next version
of Windows is causing
consternation among antivirus
experts, who say that the new
module, a scripting platform
called Microsoft Shell, could
give birth to a whole new
generation of viruses and
remotely" - ], - [ - "SAN JOSE, California Yahoo
will likely have a tough time
getting American courts to
intervene in a dispute over
the sale of Nazi memorabilia
in France after a US appeals
court ruling." - ], - [ - "eBay Style director Constance
White joins Post fashion
editor Robin Givhan and host
Janet Bennett to discuss how
to find trends and bargains
and pull together a wardrobe
online." - ], - [ - "With a sudden shudder, the
ground collapsed and the pipe
pushed upward, buckling into a
humped shape as Cornell
University scientists produced
the first simulated earthquake" - ], - [ - "Microsoft (Quote, Chart) has
fired another salvo in its
ongoing spam battle, this time
against porn peddlers who don
#39;t keep their smut inside
the digital equivalent of a
quot;Brown Paper Wrapper." - ], - [ - " SEATTLE (Reuters) - The next
version of the Windows
operating system, Microsoft
Corp.'s <A HREF=\"http://www
.reuters.co.uk/financeQuoteLoo
kup.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
flagship product, will ship
in 2006, the world's largest
software maker said on
Friday." - ], - [ - "Fossil remains of the oldest
and smallest known ancestor of
Tyrannosaurus rex, the world
#39;s favorite ferocious
dinosaur, have been discovered
in China with evidence that
its body was cloaked in downy
quot;protofeathers." - ], - [ - "People fishing for sport are
doing far more damage to US
marine fish stocks than anyone
thought, accounting for nearly
a quarter of the" - ], - [ - "This particular index is
produced by the University of
Michigan Business School, in
partnership with the American
Society for Quality and CFI
Group, and is supported in
part by ForeSee Results" - ], - [ - "PC World - Symantec, McAfee
hope raising virus-definition
fees will move users to\\
suites." - ], - [ - "By byron kho. A consortium of
movie and record companies
joined forces on Friday to
request that the US Supreme
Court take another look at
peer-to-peer file-sharing
programs." - ], - [ - "LONDON - Wild capuchin monkeys
can understand cause and
effect well enough to use
rocks to dig for food,
scientists have found.
Capuchin monkeys often use
tools and solve problems in
captivity and sometimes" - ], - [ - "The key to hidden treasure
lies in your handheld GPS
unit. GPS-based \"geocaching\"
is a high-tech sport being
played by thousands of people
across the globe." - ], - [ - "The U.S. information tech
sector lost 403,300 jobs
between March 2001 and April
2004, and the market for tech
workers remains bleak,
according to a new report." - ], - [ - "com September 30, 2004, 11:11
AM PT. SanDisk announced
Thursday increased capacities
for several different flash
memory cards. The Sunnyvale,
Calif." - ], - [ - "roundup Plus: Tech firms rally
against copyright bill...Apple
.Mac customers suffer e-mail
glitches...Alvarion expands
wireless broadband in China." - ], - [ - "Red Hat is acquiring security
and authentication tools from
Netscape Security Solutions to
bolster its software arsenal.
Red Hat #39;s CEO and chairman
Matthew Szulik spoke about the
future strategy of the Linux
supplier." - ], - [ - "Global Web portal Yahoo! Inc.
Wednesday night made available
a beta version of a new search
service for videos. Called
Yahoo! Video Search, the
search engine crawls the Web
for different types of media
files" - ], - [ - "Interactive posters at 25
underground stations are
helping Londoners travel
safely over Christmas." - ], - [ - "According to Swiss
authorities, history was made
Sunday when 2723 people in
four communities in canton
Geneva, Switzerland, voted
online in a national federal
referendum." - ], - [ - " Nextel was the big story in
telecommunications yesterday,
thanks to the Reston company's
mega-merger with Sprint, but
the future of wireless may be
percolating in dozens of
Washington area start-ups." - ], - [ - "I have been anticipating this
day like a child waits for
Christmas. Today, PalmOne
introduces the Treo 650, the
answer to my quot;what smart
phone will I buy?" - ], - [ - "Wikipedia has surprised Web
watchers by growing fast and
maturing into one of the most
popular reference sites." - ], - [ - "It only takes 20 minutes on
the Internet for an
unprotected computer running
Microsoft Windows to be taken
over by a hacker. Any personal
or financial information
stored" - ], - [ - "Prices for flash memory cards
-- the little modules used by
digital cameras, handheld
organizers, MP3 players and
cell phones to store pictures,
music and other data -- are
headed down -- way down. Past
trends suggest that prices
will drop 35 percent a year,
but industry analysts think
that rate will be more like 40
or 50 percent this year and
next, due to more
manufacturers entering the
market." - ], - [ - "While the US software giant
Microsoft has achieved almost
sweeping victories in
government procurement
projects in several Chinese
provinces and municipalities,
the process" - ], - [ - "Microsoft Corp. has delayed
automated distribution of a
major security upgrade to its
Windows XP Professional
operating system, citing a
desire to give companies more
time to test it." - ], - [ - "Gateway computers will be more
widely available at Office
Depot, in the PC maker #39;s
latest move to broaden
distribution at retail stores
since acquiring rival
eMachines this year." - ], - [ - "Intel Corp. is refreshing its
64-bit Itanium 2 processor
line with six new chips based
on the Madison core. The new
processors represent the last
single-core Itanium chips that
the Santa Clara, Calif." - ], - [ - "For the first time, broadband
connections are reaching more
than half (51 percent) of the
American online population at
home, according to measurement
taken in July by
Nielsen/NetRatings, a
Milpitas-based Internet
audience measurement and
research ..." - ], - [ - "Consider the New World of
Information - stuff that,
unlike the paper days of the
past, doesn't always
physically exist. You've got
notes, scrawlings and
snippets, Web graphics, photos
and sounds. Stuff needs to be
cut, pasted, highlighted,
annotated, crossed out,
dragged away. And, as Ross
Perot used to say (or maybe it
was Dana Carvey impersonating
him), don't forget the graphs
and charts." - ], - [ - "Major Hollywood studios on
Tuesday announced scores of
lawsuits against computer
server operators worldwide,
including eDonkey, BitTorrent
and DirectConnect networks,
for allowing trading of
pirated movies." - ], - [ - "But will Wi-Fi, high-
definition broadcasts, mobile
messaging and other
enhancements improve the game,
or wreck it?\\<br />
Photos of tech-friendly parks\\" - ], - [ - "On-demand viewing isn't just
for TiVo owners anymore.
Television over internet
protocol, or TVIP, offers
custom programming over
standard copper wires." - ], - [ - "PalmSource #39;s European
developer conference is going
on now in Germany, and this
company is using this
opportunity to show off Palm
OS Cobalt 6.1, the latest
version of its operating
system." - ], - [ - "Speaking to members of the
Massachusetts Software
Council, Microsoft CEO Steve
Ballmer touted a bright future
for technology but warned his
listeners to think twice
before adopting open-source
products like Linux." - ], - [ - "MIAMI - The Trillian instant
messaging (IM) application
will feature several
enhancements in its upcoming
version 3.0, including new
video and audio chat
capabilities, enhanced IM
session logs and integration
with the Wikipedia online
encyclopedia, according to
information posted Friday on
the product developer's Web
site." - ], - [ - "Reuters - Online DVD rental
service Netflix Inc.\\and TiVo
Inc., maker of a digital video
recorder, on Thursday\\said
they have agreed to develop a
joint entertainment\\offering,
driving shares of both
companies higher." - ], - [ - "THIS YULE is all about console
supply and there #39;s
precious little units around,
it has emerged. Nintendo has
announced that it is going to
ship another 400,000 units of
its DS console to the United
States to meet the shortfall
there." - ], - [ - "Annual global semiconductor
sales growth will probably
fall by half in 2005 and
memory chip sales could
collapse as a supply glut saps
prices, world-leading memory
chip maker Samsung Electronics
said on Monday." - ], - [ - "NEW YORK - Traditional phone
systems may be going the way
of the Pony Express. Voice-
over-Internet Protocol,
technology that allows users
to make and receive phone
calls using the Internet, is
giving the old circuit-
switched system a run for its
money." - ], - [ - "AP - The first U.S. cases of
the fungus soybean rust, which
hinders plant growth and
drastically cuts crop
production, were found at two
research sites in Louisiana,
officials said Wednesday." - ], - [ - " quot;There #39;s no way
anyone would hire them to
fight viruses, quot; said
Sophos security analyst Gregg
Mastoras. quot;For one, no
security firm could maintain
its reputation by employing
hackers." - ], - [ - "Symantec has revoked its
decision to blacklist a
program that allows Web
surfers in China to browse
government-blocked Web sites.
The move follows reports that
the firm labelled the Freegate
program, which" - ], - [ - "Microsoft has signed a pact to
work with the United Nations
Educational, Scientific and
Cultural Organization (UNESCO)
to increase computer use,
Internet access and teacher
training in developing
countries." - ], - [ - "roundup Plus: Microsoft tests
Windows Marketplace...Nortel
delays financials
again...Microsoft updates
SharePoint." - ], - [ - "OPEN SOURCE champion Microsoft
is expanding its programme to
give government organisations
some of its source code. In a
communique from the lair of
the Vole, in Redmond,
spinsters have said that
Microsoft" - ], - [ - "WASHINGTON Can you always tell
when somebody #39;s lying? If
so, you might be a wizard of
the fib. A California
psychology professor says
there #39;s a tiny subculture
of people that can pick out a
lie nearly every time they
hear one." - ], - [ - "Type design was once the
province of skilled artisans.
With the help of new computer
programs, neophytes have
flooded the Internet with
their creations." - ], - [ - "RCN Inc., co-owner of
Starpower Communications LLC,
the Washington area
television, telephone and
Internet provider, filed a
plan of reorganization
yesterday that it said puts
the company" - ], - [ - "<strong>Letters</stro
ng> Reports of demise
premature" - ], - [ - "Intel, the world #39;s largest
chip maker, scrapped a plan
Thursday to enter the digital
television chip business,
marking a major retreat from
its push into consumer
electronics." - ], - [ - "The US is the originator of
over 42 of the worlds
unsolicited commercial e-mail,
making it the worst offender
in a league table of the top
12 spam producing countries
published yesterday by anti-
virus firm Sophos." - ], - [ - "Intel is drawing the curtain
on some of its future research
projects to continue making
transistors smaller, faster,
and less power-hungry out as
far as 2020." - ], - [ - "Plus: Experts fear Check 21
could lead to massive bank
fraud." - ], - [ - "By SIOBHAN McDONOUGH
WASHINGTON (AP) -- Fewer
American youths are using
marijuana, LSD and Ecstasy,
but more are abusing
prescription drugs, says a
government report released
Thursday. The 2003 National
Survey on Drug Use and Health
also found youths and young
adults are more aware of the
risks of using pot once a
month or more frequently..." - ], - [ - "A TNO engineer prepares to
start capturing images for the
world's biggest digital photo." - ], - [ - "PalmOne is aiming to sharpen
up its image with the launch
of the Treo 650 on Monday. As
previously reported, the smart
phone update has a higher-
resolution screen and a faster
processor than the previous
top-of-the-line model, the
Treo 600." - ], - [ - "kinrowan writes quot;MIT,
inventor of Kerberos, has
announced a pair of
vulnerabities in the software
that will allow an attacker to
either execute a DOS attack or
execute code on the machine." - ], - [ - "Reuters - Former Pink Floyd
mainman Roger\\Waters released
two new songs, both inspired
by the U.S.-led\\invasion of
Iraq, via online download
outlets Tuesday." - ], - [ - "NASA has been working on a
test flight project for the
last few years involving
hypersonic flight. Hypersonic
flight is fight at speeds
greater than Mach 5, or five
times the speed of sound." - ], - [ - "AP - Microsoft Corp. announced
Wednesday that it would offer
a low-cost, localized version
of its Windows XP operating
system in India to tap the
large market potential in this
country of 1 billion people,
most of whom do not speak
English." - ], - [ - "AP - Utah State University has
secured a #36;40 million
contract with NASA to build an
orbiting telescope that will
examine galaxies and try to
find new stars." - ], - [ - "I spend anywhere from three to
eight hours every week
sweating along with a motley
crew of local misfits,
shelving, sorting, and hauling
ton after ton of written
matter in a rowhouse basement
in Baltimore. We have no heat
nor air conditioning, but
still, every week, we come and
work. Volunteer night is
Wednesday, but many of us also
work on the weekends, when
we're open to the public.
There are times when we're
freezing and we have to wear
coats and gloves inside,
making handling books somewhat
tricky; other times, we're all
soaked with sweat, since it's
90 degrees out and the
basement is thick with bodies.
One learns to forget about
personal space when working at
The Book Thing, since you can
scarcely breathe without
bumping into someone, and we
are all so accustomed to
having to scrape by each other
that most of us no longer
bother to say \"excuse me\"
unless some particularly
dramatic brushing occurs." - ], - [ - "Alarmed by software glitches,
security threats and computer
crashes with ATM-like voting
machines, officials from
Washington, D.C., to
California are considering an
alternative from an unlikely
place: Nevada." - ], - [ - "More Americans are quitting
their jobs and taking the risk
of starting a business despite
a still-lackluster job market." - ], - [ - "SPACE.com - With nbsp;food
stores nbsp;running low, the
two \\astronauts living aboard
the International Space
Station (ISS) are cutting back
\\their meal intake and
awaiting a critical cargo
nbsp;delivery expected to
arrive \\on Dec. 25." - ], - [ - "Virgin Electronics hopes its
slim Virgin Player, which
debuts today and is smaller
than a deck of cards, will
rise as a lead competitor to
Apple's iPod." - ], - [ - "Researchers train a monkey to
feed itself by guiding a
mechanical arm with its mind.
It could be a big step forward
for prosthetics. By David
Cohn." - ], - [ - "AP - Scientists in 17
countries will scout waterways
to locate and study the
world's largest freshwater
fish species, many of which
are declining in numbers,
hoping to learn how to better
protect them, researchers
announced Thursday." - ], - [ - "AP - Google Inc.'s plans to
move ahead with its initial
public stock offering ran into
a roadblock when the
Securities and Exchange
Commission didn't approve the
Internet search giant's
regulatory paperwork as
requested." - ], - [ - "Citing security risks, a state
university is urging students
to drop Internet Explorer in
favor of alternative Web
browsers such as Firefox and
Safari." - ], - [ - "Despite being ranked eleventh
in the world in broadband
penetration, the United States
is rolling out high-speed
services on a quot;reasonable
and timely basis to all
Americans, quot; according to
a new report narrowly approved
today by the Federal
Communications" - ], - [ - "NewsFactor - Taking an
innovative approach to the
marketing of high-performance
\\computing, Sun Microsystems
(Nasdaq: SUNW) is offering its
N1 Grid program in a pay-for-
use pricing model that mimics
the way such commodities as
electricity and wireless phone
plans are sold." - ], - [ - "Reuters - Madonna and m-Qube
have\\made it possible for the
star's North American fans to
download\\polyphonic ring tones
and other licensed mobile
content from\\her official Web
site, across most major
carriers and without\\the need
for a credit card." - ], - [ - "The chipmaker is back on a
buying spree, having scooped
up five other companies this
year." - ], - [ - "The company hopes to lure
software partners by promising
to save them from
infrastructure headaches." - ], - [ - "Call it the Maximus factor.
Archaeologists working at the
site of an old Roman temple
near Guy #39;s hospital in
London have uncovered a pot of
cosmetic cream dating back to
AD2." - ], - [ - "The deal comes as Cisco pushes
to develop a market for CRS-1,
a new line of routers aimed at
telephone, wireless and cable
companies." - ], - [ - "SEPTEMBER 14, 2004 (IDG NEWS
SERVICE) - Sun Microsystems
Inc. and Microsoft Corp. next
month plan to provide more
details on the work they are
doing to make their products
interoperable, a Sun executive
said yesterday." - ], - [ - "AP - Astronomy buffs and
amateur stargazers turned out
to watch a total lunar eclipse
Wednesday night #151; the
last one Earth will get for
nearly two and a half years." - ], - [ - "The compact disc has at least
another five years as the most
popular music format before
online downloads chip away at
its dominance, a new study
said on Tuesday." - ], - [ - "Does Your Site Need a Custom
Search Engine
Toolbar?\\\\Today's surfers
aren't always too comfortable
installing software on their
computers. Especially free
software that they don't
necessarily understand. With
all the horror stories of
viruses, spyware, and adware
that make the front page these
days, it's no wonder. So is
there ..." - ], - [ - "Spammers aren't ducking
antispam laws by operating
offshore, they're just
ignoring it." - ], - [ - "AP - Biologists plan to use
large nets and traps this week
in Chicago's Burnham Harbor to
search for the northern
snakehead #151; a type of
fish known for its voracious
appetite and ability to wreak
havoc on freshwater
ecosystems." - ], - [ - "BAE Systems PLC and Northrop
Grumman Corp. won \\$45 million
contracts yesterday to develop
prototypes of anti-missile
technology that could protect
commercial aircraft from
shoulder-fired missiles." - ], - [ - "Users of Microsoft #39;s
Hotmail, most of whom are
accustomed to getting regular
sales pitches for premium
e-mail accounts, got a
pleasant surprise in their
inboxes this week: extra
storage for free." - ], - [ - "IT services provider
Electronic Data Systems
yesterday reported a net loss
of \\$153 million for the third
quarter, with earnings hit in
part by an asset impairment
charge of \\$375 million
connected with EDS's N/MCI
project." - ], - [ - "International Business
Machines Corp. said Wednesday
it had agreed to settle most
of the issues in a suit over
changes in its pension plans
on terms that allow the
company to continue to appeal
a key question while capping
its liability at \\$1.7
billion. <BR><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-The Washington
Post</B></FONT>" - ], - [ - "Edward Kozel, Cisco's former
chief technology officer,
joins the board of Linux
seller Red Hat." - ], - [ - "Reuters - International
Business Machines\\Corp. late
on Wednesday rolled out a new
version of its\\database
software aimed at users of
Linux and Unix
operating\\systems that it
hopes will help the company
take away market\\share from
market leader Oracle Corp. ." - ], - [ - "NASA #39;s Mars Odyssey
mission, originally scheduled
to end on Tuesday, has been
granted a stay of execution
until at least September 2006,
reveal NASA scientists." - ], - [ - "BusinessWeek Online - The
jubilation that swept East
Germany after the fall of the
Berlin Wall in 1989 long ago
gave way to the sober reality
of globalization and market
forces. Now a decade of
resentment seems to be boiling
over. In Eastern cities such
as Leipzig or Chemnitz,
thousands have taken to the
streets since July to protest
cuts in unemployment benefits,
the main source of livelihood
for 1.6 million East Germans.
Discontent among
reunification's losers fueled
big gains by the far left and
far right in Brandenburg and
Saxony state elections Sept.
19. ..." - ], - [ - "In a discovery sure to set off
a firestorm of debate over
human migration to the western
hemisphere, archaeologists in
South Carolina say they have
uncovered evidence that people
lived in eastern North America
at least 50,000 years ago -
far earlier than" - ], - [ - "AP - Former president Bill
Clinton on Monday helped
launch a new Internet search
company backed by the Chinese
government which says its
technology uses artificial
intelligence to produce better
results than Google Inc." - ], - [ - "Sun Microsystems plans to
release its Sun Studio 10
development tool in the fourth
quarter of this year,
featuring support for 64-bit
applications running on AMD
Opteron and Nocona processors,
Sun officials said on Tuesday." - ], - [ - "Most IT Managers won #39;t
question the importance of
security, but this priority
has been sliding between the
third and fourth most
important focus for companies." - ], - [ - "AP - The Federal Trade
Commission on Thursday filed
the first case in the country
against software companies
accused of infecting computers
with intrusive \"spyware\" and
then trying to sell people the
solution." - ], - [ - "While shares of Apple have
climbed more than 10 percent
this week, reaching 52-week
highs, two research firms told
investors Friday they continue
to remain highly bullish about
the stock." - ], - [ - "States are now barred from
imposing telecommunications
regulations on Net phone
providers." - ], - [ - "Strong sales of new mobile
phone models boosts profits at
Carphone Warehouse but the
retailer's shares fall on
concerns at a decline in
profits from pre-paid phones." - ], - [ - " NEW YORK (Reuters) - IBM and
top scientific research
organizations are joining
forces in a humanitarian
effort to tap the unused
power of millions of computers
and help solve complex social
problems." - ], - [ - "AP - Grizzly and black bears
killed a majority of elk
calves in northern Yellowstone
National Park for the second
year in a row, preliminary
study results show." - ], - [ - "PC World - The one-time World
Class Product of the Year PDA
gets a much-needed upgrade." - ], - [ - "As part of its much-touted new
MSN Music offering, Microsoft
Corp. (MSFT) is testing a Web-
based radio service that
mimics nearly 1,000 local
radio stations, allowing users
to hear a version of their
favorite radio station with
far fewer interruptions." - ], - [ - "Ziff Davis - A quick
resolution to the Mambo open-
source copyright dispute seems
unlikely now that one of the
parties has rejected an offer
for mediation." - ], - [ - "Students take note - endless
journeys to the library could
become a thing of the past
thanks to a new multimillion-
pound scheme to make classic
texts available at the click
of a mouse." - ], - [ - "Moscow - The next crew of the
International Space Station
(ISS) is to contribute to the
Russian search for a vaccine
against Aids, Russian
cosmonaut Salijan Sharipovthe
said on Thursday." - ], - [ - "Locked away in fossils is
evidence of a sudden solar
cooling. Kate Ravilious meets
the experts who say it could
explain a 3,000-year-old mass
migration - and today #39;s
global warming." - ], - [ - "By ANICK JESDANUN NEW YORK
(AP) -- Taran Rampersad didn't
complain when he failed to
find anything on his hometown
in the online encyclopedia
Wikipedia. Instead, he simply
wrote his own entry for San
Fernando, Trinidad and
Tobago..." - ], - [ - "How do you top a battle
between Marines and an alien
religious cult fighting to the
death on a giant corona in
outer space? The next logical
step is to take that battle to
Earth, which is exactly what
Microsoft" - ], - [ - "Four U.S. agencies yesterday
announced a coordinated attack
to stem the global trade in
counterfeit merchandise and
pirated music and movies, an
underground industry that law-
enforcement officials estimate
to be worth \\$500 billion each
year." - ], - [ - "Half of all U.S. Secret
Service agents are dedicated
to protecting President
Washington #151;and all the
other Presidents on U.S.
currency #151;from
counterfeiters." - ], - [ - "Maxime Faget conceived and
proposed the development of
the one-man spacecraft used in
Project Mercury, which put the
first American astronauts into
suborbital flight, then
orbital flight" - ], - [ - "The patch fixes a flaw in the
e-mail server software that
could be used to get access to
in-boxes and information." - ], - [ - "AP - Being the biggest dog may
pay off at feeding time, but
species that grow too large
may be more vulnerable to
extinction, new research
suggests." - ], - [ - "Newest Efficeon processor also
offers higher frequency using
less power." - ], - [ - "AP - In what it calls a first
in international air travel,
Finnair says it will let its
frequent fliers check in using
text messages on mobile
phones." - ], - [ - "Search Engine for Programming
Code\\\\An article at Newsforge
pointed me to Koders (
http://www.koders.com ) a
search engine for finding
programming code. Nifty.\\\\The
front page allows you to
specify keywords, sixteen
languages (from ASP to VB.NET)
and sixteen licenses (from AFL
to ZPL -- fortunately there's
an information page to ..." - ], - [ - " #147;Apple once again was the
star of the show at the annual
MacUser awards, #148; reports
MacUser, #147;taking away six
Maxine statues including
Product of the Year for the
iTunes Music Store. #148;
Other Apple award winners:
Power Mac G5, AirPort Express,
20-inch Apple Cinema Display,
GarageBand and DVD Studio Pro
3. Nov 22" - ], - [ - "AP - A sweeping wildlife
preserve in southwestern
Arizona is among the nation's
10 most endangered refuges,
due in large part to illegal
drug and immigrant traffic and
Border Patrol operations, a
conservation group said
Friday." - ], - [ - "SEPTEMBER 20, 2004
(COMPUTERWORLD) - At
PeopleSoft Inc. #39;s Connect
2004 conference in San
Francisco this week, the
software vendor is expected to
face questions from users
about its ability to fend off
Oracle Corp." - ], - [ - "PBS's Charlie Rose quizzes Sun
co-founder Bill Joy,
Monster.com chief Jeff Taylor,
and venture capitalist John
Doerr." - ], - [ - "Skype Technologies is teaming
with Siemens to offer cordless
phone users the ability to
make Internet telephony calls,
in addition to traditional
calls, from their handsets." - ], - [ - "AP - After years of
resistance, the U.S. trucking
industry says it will not try
to impede or delay a new
federal rule aimed at cutting
diesel pollution." - ], - [ - "Umesh Patel, a 36-year old
software engineer from
California, debated until the
last minute." - ], - [ - "AP - From now until the start
of winter, male tarantulas are
roaming around, searching for
female mates, an ideal time to
find out where the spiders
flourish in Arkansas." - ], - [ - "NewsFactor - While a U.S.
District Court continues to
weigh the legality of
\\Oracle's (Nasdaq: ORCL)
attempted takeover of
\\PeopleSoft (Nasdaq: PSFT),
Oracle has taken the necessary
steps to ensure the offer does
not die on the vine with
PeopleSoft's shareholders." - ], - [ - "Analysts said the smartphone
enhancements hold the
potential to bring PalmSource
into an expanding market that
still has room despite early
inroads by Symbian and
Microsoft." - ], - [ - "TOKYO (AFP) - Japan #39;s
Fujitsu and Cisco Systems of
the US said they have agreed
to form a strategic alliance
focusing on routers and
switches that will enable
businesses to build advanced
Internet Protocol (IP)
networks." - ], - [ - "It #39;s a new internet
browser. The first full
version, Firefox 1.0, was
launched earlier this month.
In the sense it #39;s a
browser, yes, but the
differences are larger than
the similarities." - ], - [ - "Microsoft will accelerate SP2
distribution to meet goal of
100 million downloads in two
months." - ], - [ - "International Business
Machines Corp., taken to court
by workers over changes it
made to its traditional
pension plan, has decided to
stop offering any such plan to
new employees." - ], - [ - "NEW YORK - With four of its
executives pleading guilty to
price-fixing charges today,
Infineon will have a hard time
arguing that it didn #39;t fix
prices in its ongoing
litigation with Rambus." - ], - [ - "A report indicates that many
giants of the industry have
been able to capture lasting
feelings of customer loyalty." - ], - [ - "It is the news that Internet
users do not want to hear: the
worldwide web is in danger of
collapsing around us. Patrick
Gelsinger, the chief
technology officer for
computer chip maker Intel,
told a conference" - ], - [ - "Cisco Systems CEO John
Chambers said today that his
company plans to offer twice
as many new products this year
as ever before." - ], - [ - "Every day, somewhere in the
universe, there #39;s an
explosion that puts the power
of the Sun in the shade. Steve
Connor investigates the riddle
of gamma-ray bursts." - ], - [ - "Ten months after NASA #39;s
twin rovers landed on Mars,
scientists reported this week
that both robotic vehicles are
still navigating their rock-
studded landscapes with all
instruments operating" - ], - [ - "British scientists say they
have found a new, greener way
to power cars and homes using
sunflower oil, a commodity
more commonly used for cooking
fries." - ], - [ - "The United States #39; top
computer-security official has
resigned after a little more
than a year on the job, the US
Department of Homeland
Security said on Friday." - ], - [ - " SAN FRANCISCO (Reuters) -
Intel Corp. <A HREF=\"http:/
/www.reuters.co.uk/financeQuot
eLookup.jhtml?ticker=INTC.O
qtype=sym infotype=info
qcat=news\">INTC.O</A>
on Thursday outlined its
vision of the Internet of the
future, one in which millions
of computer servers would
analyze and direct network
traffic to make the Web safer
and more efficient." - ], - [ - "Check out new gadgets as
holiday season approaches." - ], - [ - "TOKYO, Sep 08, 2004 (AFX-UK
via COMTEX) -- Sony Corp will
launch its popular projection
televisions with large liquid
crystal display (LCD) screens
in China early next year, a
company spokeswoman said." - ], - [ - "AFP - Outgoing EU competition
commissioner Mario Monti wants
to reach a decision on
Oracle's proposed takeover of
business software rival
PeopleSoft by the end of next
month, an official said." - ], - [ - " WASHINGTON (Reuters) -
Satellite companies would be
able to retransmit
broadcasters' television
signals for another five
years but would have to offer
those signals on a single
dish, under legislation
approved by Congress on
Saturday." - ], - [ - "Sven Jaschan, who may face
five years in prison for
spreading the Netsky and
Sasser worms, is now working
in IT security. Photo: AFP." - ], - [ - "TechWeb - An Indian Institute
of Technology professor--and
open-source evangelist--
discusses the role of Linux
and open source in India." - ], - [ - "Here are some of the latest
health and medical news
developments, compiled by
editors of HealthDay: -----
Contaminated Fish in Many U.S.
Lakes and Rivers Fish
that may be contaminated with
dioxin, mercury, PCBs and
pesticides are swimming in
more than one-third of the
United States' lakes and
nearly one-quarter of its
rivers, according to a list of
advisories released by the
Environmental Protection
Agency..." - ], - [ - "Bill Gates is giving his big
speech right now at Microsofts
big Digital Entertainment
Anywhere event in Los Angeles.
Well have a proper report for
you soon, but in the meantime
we figured wed" - ], - [ - "Spinal and non-spinal
fractures are reduced by
almost a third in women age 80
or older who take a drug
called strontium ranelate,
European investigators
announced" - ], - [ - "BERLIN: Apple Computer Inc is
planning the next wave of
expansion for its popular
iTunes online music store with
a multi-country European
launch in October, the
services chief architect said
on Wednesday." - ], - [ - "Calls to 13 other countries
will be blocked to thwart
auto-dialer software." - ], - [ - "com September 27, 2004, 5:00
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." - ], - [ - "SPACE.com - The Zero Gravity
Corporation \\ has been given
the thumbs up by the Federal
Aviation Administration (FAA)
to \\ conduct quot;weightless
flights quot; for the general
public, providing the \\
sensation of floating in
space." - ], - [ - "A 32-year-old woman in Belgium
has become the first woman
ever to give birth after
having ovarian tissue removed,
frozen and then implanted back
in her body." - ], - [ - "No sweat, says the new CEO,
who's imported from IBM. He
also knows this will be the
challenge of a lifetime." - ], - [ - "AP - Their discoveries may be
hard for many to comprehend,
but this year's Nobel Prize
winners still have to explain
what they did and how they did
it." - ], - [ - "More than 15 million homes in
the UK will be able to get on-
demand movies by 2008, say
analysts." - ], - [ - "A well-researched study by the
National Academy of Sciences
makes a persuasive case for
refurbishing the Hubble Space
Telescope. NASA, which is
reluctant to take this
mission, should rethink its
position." - ], - [ - "LONDON -- In a deal that
appears to buck the growing
trend among governments to
adopt open-source
alternatives, the U.K.'s
Office of Government Commerce
(OGC) is negotiating a renewal
of a three-year agreement with
Microsoft Corp." - ], - [ - "WASHINGTON - A Pennsylvania
law requiring Internet service
providers (ISPs) to block Web
sites the state's prosecuting
attorneys deem to be child
pornography has been reversed
by a U.S. federal court, with
the judge ruling the law
violated free speech rights." - ], - [ - "The Microsoft Corp. chairman
receives four million e-mails
a day, but practically an
entire department at the
company he founded is
dedicated to ensuring that
nothing unwanted gets into his
inbox, the company #39;s chief
executive said Thursday." - ], - [ - "The 7100t has a mobile phone,
e-mail, instant messaging, Web
browsing and functions as an
organiser. The device looks
like a mobile phone and has
the features of the other
BlackBerry models, with a
large screen" - ], - [ - "Apple Computer has unveiled
two new versions of its hugely
successful iPod: the iPod
Photo and the U2 iPod. Apple
also has expanded" - ], - [ - "AP - This year's hurricanes
spread citrus canker to at
least 11,000 trees in
Charlotte County, one of the
largest outbreaks of the
fruit-damaging infection to
ever affect Florida's citrus
industry, state officials
said." - ], - [ - "IBM moved back into the iSCSI
(Internet SCSI) market Friday
with a new array priced at
US\\$3,000 and aimed at the
small and midsize business
market." - ], - [ - "American Technology Research
analyst Shaw Wu has initiated
coverage of Apple Computer
(AAPL) with a #39;buy #39;
recommendation, and a 12-month
target of US\\$78 per share." - ], - [ - "washingtonpost.com - Cell
phone shoppers looking for new
deals and features didn't find
them yesterday, a day after
Sprint Corp. and Nextel
Communications Inc. announced
a merger that the phone
companies promised would shake
up the wireless business." - ], - [ - "JAPANESE GIANT Sharp has
pulled the plug on its Linux-
based PDAs in the United
States as no-one seems to want
them. The company said that it
will continue to sell them in
Japan where they sell like hot
cakes" - ], - [ - "New NavOne offers handy PDA
and Pocket PC connectivity,
but fails to impress on
everything else." - ], - [ - "The Marvel deal calls for
Mforma to work with the comic
book giant to develop a
variety of mobile applications
based on Marvel content." - ], - [ - "washingtonpost.com - The
Portable Media Center -- a
new, Microsoft-conceived
handheld device that presents
video and photos as well as
music -- would be a decent
idea if there weren't such a
thing as lampposts. Or street
signs. Or trees. Or other
cars." - ], - [ - "The Air Force Reserve #39;s
Hurricane Hunters, those
fearless crews who dive into
the eyewalls of hurricanes to
relay critical data on
tropical systems, were chased
from their base on the
Mississippi Gulf Coast by
Hurricane Ivan." - ], - [ - "Embargo or not, Fidel Castro's
socialist paradise has quietly
become a pharmaceutical
powerhouse. (They're still
working on the capitalism
thing.) By Douglas Starr from
Wired magazine." - ], - [ - "A new worm can spy on users by
hijacking their Web cameras, a
security firm warned Monday.
The Rbot.gr worm -- the latest
in a long line of similar
worms; one security firm
estimates that more than 4,000
variations" - ], - [ - "The separation of PalmOne and
PalmSource will be complete
with Eric Benhamou's
resignation as the latter's
chairman." - ], - [ - "\\\\\"(CNN) -- A longtime
associate of al Qaeda leader
Osama bin Laden surrendered
to\\Saudi Arabian officials
Tuesday, a Saudi Interior
Ministry official said.\"\\\\\"But
it is unclear what role, if
any, Khaled al-Harbi may have
had in any terror\\attacks
because no public charges have
been filed against him.\"\\\\\"The
Saudi government -- in a
statement released by its
embassy in Washington
--\\called al-Harbi's surrender
\"the latest direct result\" of
its limited, one-month\\offer
of leniency to terror
suspects.\"\\\\This is great! I
hope this really starts to pay
off. Creative solutions
to\\terrorism that don't
involve violence. \\\\How
refreshing! \\\\Are you paying
attention Bush
administration?\\\\" - ], - [ - "AT T Corp. is cutting 7,400
more jobs and slashing the
book value of its assets by
\\$11.4 billion, drastic moves
prompted by the company's plan
to retreat from the
traditional consumer telephone
business following a lost
court battle." - ], - [ - "Celerons form the basis of
Intel #39;s entry-level
platform which includes
integrated/value motherboards
as well. The Celeron 335D is
the fastest - until the
Celeron 340D - 2.93GHz -
becomes mainstream - of the" - ], - [ - "The entertainment industry
asks the Supreme Court to
reverse the Grokster decision,
which held that peer-to-peer
networks are not liable for
copyright abuses of their
users. By Michael Grebb." - ], - [ - "Hewlett-Packard will shell out
\\$16.1 billion for chips in
2005, but Dell's wallet is
wide open, too." - ], - [ - "A remote attacker could take
complete control over
computers running many
versions of Microsoft software
by inserting malicious code in
a JPEG image that executes
through an unchecked buffer" - ], - [ - "The lawsuit claims the
companies use a patented
Honeywell technology for
brightening images and
reducing interference on
displays." - ], - [ - "The co-president of Oracle
testified that her company was
serious about its takeover
offer for PeopleSoft and was
not trying to scare off its
customers." - ], - [ - "An extremely rare Hawaiian
bird dies in captivity,
possibly marking the
extinction of its entire
species only 31 years after it
was first discovered." - ], - [ - "Does Geico's trademark lawsuit
against Google have merit? How
will the case be argued?
What's the likely outcome of
the trial? A mock court of
trademark experts weighs in
with their verdict." - ], - [ - "Treo 650 boasts a high-res
display, an improved keyboard
and camera, a removable
battery, and more. PalmOne
this week is announcing the
Treo 650, a hybrid PDA/cell-
phone device that addresses
many of the shortcomings" - ], - [ - "International Business
Machines Corp.'s possible exit
from the personal computer
business would be the latest
move in what amounts to a long
goodbye from a field it
pioneered and revolutionized." - ], - [ - "Leipzig Game Convention in
Germany, the stage for price-
slash revelations. Sony has
announced that it #39;s
slashing the cost of PS2 in
the UK and Europe to 104.99
GBP." - ], - [ - "In yet another devastating
body blow to the company,
Intel (Nasdaq: INTC) announced
it would be canceling its
4-GHz Pentium chip. The
semiconductor bellwether said
it was switching" - ], - [ - "Seagate #39;s native SATA
interface technology with
Native Command Queuing (NCQ)
allows the Barracuda 7200.8 to
match the performance of
10,000-rpm SATA drives without
sacrificing capacity" - ], - [ - "You #39;re probably already
familiar with one of the most
common questions we hear at
iPodlounge: quot;how can I
load my iPod up with free
music?" - ], - [ - "Vice chairman of the office of
the chief executive officer in
Novell Inc, Chris Stone is
leaving the company to pursue
other opportunities in life." - ], - [ - "washingtonpost.com - Microsoft
is going to Tinseltown today
to announce plans for its
revamped Windows XP Media
Center, part of an aggressive
push to get ahead in the
digital entertainment race." - ], - [ - "AP - Most of the turkeys
gracing the nation's dinner
tables Thursday have been
selectively bred for their
white meat for so many
generations that simply
walking can be a problem for
many of the big-breasted birds
and sex is no longer possible." - ], - [ - " SEATTLE (Reuters) - Microsoft
Corp. <A HREF=\"http://www.r
euters.co.uk/financeQuoteLooku
p.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
is making a renewed push this
week to get its software into
living rooms with the launch
of a new version of its
Windows XP Media Center, a
personal computer designed for
viewing movies, listening to
music and scrolling through
digital pictures." - ], - [ - "The new software is designed
to simplify the process of
knitting together back-office
business applications." - ], - [ - "FRANKFURT, GERMANY -- The
German subsidiaries of
Hewlett-Packard Co. (HP) and
Novell Inc. are teaming to
offer Linux-based products to
the country's huge public
sector." - ], - [ - "SiliconValley.com - \"I'm
back,\" declared Apple
Computer's Steve Jobs on
Thursday morning in his first
public appearance before
reporters since cancer surgery
in late July." - ], - [ - "Health India: London, Nov 4 :
Cosmetic face cream used by
fashionable Roman women was
discovered at an ongoing
archaeological dig in London,
in a metal container, complete
with the lid and contents." - ], - [ - "MacCentral - Microsoft's
Macintosh Business Unit on
Tuesday issued a patch for
Virtual PC 7 that fixes a
problem that occurred when
running the software on Power
Mac G5 computers with more
than 2GB of RAM installed.
Previously, Virtual PC 7 would
not run on those computers,
causing a fatal error that
crashed the application.
Microsoft also noted that
Virtual PC 7.0.1 also offers
stability improvements,
although it wasn't more
specific than that -- some
users have reported problems
with using USB devices and
other issues." - ], - [ - "Don't bother buying Star Wars:
Battlefront if you're looking
for a first-class shooter --
there are far better games out
there. But if you're a Star
Wars freak and need a fix,
this title will suffice. Lore
Sjberg reviews Battlefront." - ], - [ - "More than by brain size or
tool-making ability, the human
species was set apart from its
ancestors by the ability to
jog mile after lung-stabbing
mile with greater endurance
than any other primate,
according to research
published today in the journal" - ], - [ - "Woodland Hills-based Brilliant
Digital Entertainment and its
subsidiary Altnet announced
yesterday that they have filed
a patent infringement suit
against the Recording Industry
Association of America (RIAA)." - ], - [ - "Sony Ericsson and Cingular
provide Z500a phones and
service for military families
to stay connected on today
#39;s quot;Dr. Phil Show
quot;." - ], - [ - "Officials at EarthLink #39;s
(Quote, Chart) R amp;D
facility have quietly released
a proof-of-concept file-
sharing application based on
the Session Initiated Protocol
(define)." - ], - [ - " quot;It #39;s your mail,
quot; the Google Web site
said. quot;You should be able
to choose how and where you
read it. You can even switch
to other e-mail services
without having to worry about
losing access to your
messages." - ], - [ - "The world #39;s only captive
great white shark made history
this week when she ate several
salmon fillets, marking the
first time that a white shark
in captivity" - ], - [ - "Communications aggregator
iPass said Monday that it is
adding in-flight Internet
access to its access
portfolio. Specifically, iPass
said it will add access
offered by Connexion by Boeing
to its list of access
providers." - ], - [ - "Company launches free test
version of service that
fosters popular Internet
activity." - ], - [ - "Outdated computer systems are
hampering the work of
inspectors, says the UN
nuclear agency." - ], - [ - "MacCentral - You Software Inc.
announced on Tuesday the
availability of You Control:
iTunes, a free\\download that
places iTunes controls in the
Mac OS X menu bar.
Without\\leaving the current
application, you can pause,
play, rewind or skip songs,\\as
well as control iTunes' volume
and even browse your entire
music library\\by album, artist
or genre. Each time a new song
plays, You Control:
iTunes\\also pops up a window
that displays the artist and
song name and the
album\\artwork, if it's in the
library. System requirements
call for Mac OS X\\v10.2.6 and
10MB free hard drive space.
..." - ], - [ - "The US Secret Service Thursday
announced arrests in eight
states and six foreign
countries of 28 suspected
cybercrime gangsters on
charges of identity theft,
computer fraud, credit-card
fraud, and conspiracy." - ] - ], - "hovertemplate": "label=Sci/Tech
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Sci/Tech", - "marker": { - "color": "#EF553B", - "size": 5, - "symbol": "diamond" - }, - "mode": "markers", - "name": "Sci/Tech", - "showlegend": true, - "type": "scattergl", - "x": [ - -50.70537, - -47.977715, - -51.65402, - -37.6373, - -36.581165, - -19.791555, - -20.928211, - -22.567608, - -19.96729, - -49.124306, - -7.3474283, - -22.541676, - -52.717, - -28.148346, - -42.825386, - -5.8827424, - -35.965088, - -10.623615, - -18.956379, - -8.153443, - -29.768171, - -31.347673, - -6.4607854, - -41.868053, - -13.634508, - -11.594272, - -13.830222, - -26.233013, - -10.061741, - -32.693943, - -38.45829, - -15.162608, - 16.015558, - -18.354656, - -46.83205, - -38.63723, - -7.0522246, - -50.323666, - -44.325756, - -7.6315646, - -48.104885, - -56.60167, - -40.07204, - -8.970166, - -44.30361, - -36.139923, - -48.951195, - 29.55601, - -21.136202, - -21.265085, - -41.619125, - -16.407732, - -33.503048, - -40.584736, - -24.227406, - -41.094902, - -41.256504, - -18.041924, - -18.929783, - -45.789707, - -35.619858, - -29.50228, - -36.813686, - 2.6427522, - -23.344019, - -44.347576, - -8.361857, - -17.157036, - -18.672327, - -18.132378, - -8.266737, - -34.572502, - -5.21674, - -21.22969, - -42.24456, - -7.7231383, - -46.522186, - -36.61156, - -37.112934, - 27.892096, - -5.4085217, - 2.6259706, - -16.563955, - -43.12336, - -57.728573, - -48.13403, - -27.2014, - -10.165841, - -53.29632, - -31.430248, - -42.180077, - -15.3164215, - -47.9015, - -23.828165, - -31.504562, - -6.3850884, - -3.119768, - -26.526276, - -22.841238, - -51.962658, - -15.730567, - -44.145668, - -30.795404, - -50.141148, - -43.173016, - -4.780475, - -36.60279, - -34.644447, - -47.198746, - -37.63518, - -46.367523, - -47.68558, - -10.65396, - -49.790844, - -45.67376, - -20.76551, - -29.445877, - 4.37751, - -45.453674, - -44.030884, - -24.544922, - -50.093754, - -19.335144, - -25.326218, - -6.78382, - -50.14104, - -33.647213, - -9.488163, - -14.337521, - -29.272839, - -43.886417, - -32.545635, - 4.3835373, - -43.219402, - 17.87465, - -31.277052, - -16.921326, - -9.602789, - -13.009839, - -35.251965, - -9.096614, - 2.5471764, - -16.914656, - -44.31037, - -42.23089, - 33.389534, - -37.813118, - -45.167847, - -36.155823, - -33.989525, - -30.086702, - -1.0110728, - -17.443365, - -42.189774, - -35.002357, - -24.72687, - 31.629358, - -25.166414, - -7.6725793, - -51.84964, - -6.9841313, - -50.175316, - -35.0245, - -35.842033, - -48.693672, - -44.228165, - -31.057253, - -33.267147, - -36.05716, - -7.2311153, - -12.550289, - -20.162544, - -39.967007, - -13.788036, - -31.409056, - -31.830273, - -22.6749, - -31.353453, - 27.913883, - -28.206575, - -19.676825, - -27.176733, - -44.44146, - -51.24567, - -10.997008, - 21.737896, - -29.750324, - -51.056225, - -19.130821, - -5.5860677, - -24.740665, - -47.064148, - -54.69983, - -7.06102, - -43.44754, - 24.795843, - -4.7836714, - -24.956768, - -53.22971, - -6.0662427, - -37.926384, - -41.1264, - -37.36757, - -16.533398, - -30.736622, - -15.802876, - -25.432766, - -22.672857, - -35.77931, - -16.137821, - -48.2029, - -6.2585354, - 7.9719267, - -53.251343, - -9.297528, - -44.267094, - -19.017382, - 33.992294, - -15.977553, - -15.548051, - -44.87483, - -51.507732, - -22.362936, - -10.216267, - -31.37848, - -5.704888, - -24.823088, - -50.370255, - -38.724506, - -38.19549, - -21.799944, - -13.132145, - -44.289158, - -16.34018, - -42.824986, - -38.334126, - -35.730442, - -36.50344, - -7.2380333, - -40.239014, - 2.5284033, - -49.050774, - -22.21815, - -25.737848, - -17.621637, - -12.631271, - -22.281115, - -43.594814, - -15.313636, - -21.579958, - -46.839928, - -8.932405, - -38.67932, - -6.674851, - -6.503544, - -36.837105, - -4.2590623, - -42.01228, - -6.2482433, - -39.613857, - -46.561874, - -45.638084, - -21.206648, - -34.547794, - -18.876629, - -17.48441, - -27.667318, - 9.352251, - -16.641712, - -41.143227, - -6.392582, - -13.137335, - -30.403149, - -50.138874, - -14.373312, - -11.489995, - -48.586517, - -32.614243, - -40.497093, - -34.05016, - -35.82806, - -44.020023, - -23.90992, - 17.625916, - -27.855904, - -52.830154, - -48.07056, - -19.067713, - -40.71425, - -25.576069, - -23.095638, - -40.87854, - -38.34742, - -50.887096, - -42.132465, - -14.928126, - -42.33495, - -45.8806, - -51.873066, - -35.644184, - -43.46856, - -35.958366, - -54.239532, - -39.886326, - -43.70516, - -8.241421, - -12.890557, - -23.360226, - -33.456993, - -34.14632, - -30.420895, - -19.05618, - -48.503033, - -18.99317, - -14.034199, - -35.493626, - -16.14875, - -36.11573, - -16.320389, - -19.673914, - -9.829185, - -52.1362, - -20.01322, - 18.142248, - -10.860374, - -37.78618, - -8.867661, - 5.742987, - -21.27348, - -30.542126, - -5.9578004, - -48.91647, - -11.771681, - -9.854177, - -23.889015, - -22.090435, - -50.986782, - -38.59416, - -45.269844, - -49.752243, - -37.106304, - -2.1609035, - -39.611637, - -38.19811, - -4.4758306, - -50.613956, - -20.649567, - -15.612729, - -9.729161, - -28.362564, - -45.534595, - -30.753082, - -15.496077, - -35.993237, - -36.425526, - -5.6656046, - -31.978811, - -7.6462755, - -31.936037, - -38.00677, - -42.961407, - -16.082205, - -50.200237, - -10.404932, - -38.39936, - -34.192577, - -7.734347, - -50.261745, - -44.1901, - -21.206255, - -41.132812, - -7.490298, - -6.7789235, - 21.208382, - -44.169758, - -12.397968, - -11.5951185, - -8.326396, - -20.166492, - -15.611658, - -38.040787, - -30.962019, - -45.082096, - -47.16861, - -4.7970433, - -26.904469, - -17.731619, - -43.073074, - -6.7949033, - 35.39813, - -11.253943, - -24.282412, - -53.5042, - -38.4294, - -46.690304, - -36.175987, - -33.290382, - -15.035485, - -49.55056, - -39.571285, - -51.21703, - -8.731081, - -6.0078135, - -11.813869, - -1.1245881, - -40.870914, - -52.076916, - -51.801567, - -23.981367, - -35.73722, - 0.08228831, - -36.47172, - -11.448383, - -40.73366, - -19.412077, - -47.05097, - -10.298156, - -43.35723, - -10.978807, - 51.953743, - -8.411927, - -13.247851, - -47.515026, - -4.658773, - -29.961985, - -12.003402, - -17.52331, - -50.23115, - -48.06655, - -7.6838555, - -33.914284, - -50.991158, - -38.40225, - -35.325897, - -51.80827, - -41.270184, - -49.167038, - 5.044943, - -27.684992, - -17.990955, - -2.3985894, - 17.726252, - -34.57765, - -39.0015, - -14.16722, - -50.34398, - -19.314493, - -17.94723, - -24.707636, - -5.6782784, - -13.584895, - -52.063236, - -33.76177, - -50.93683, - -35.708652, - -41.237144, - -50.64484, - -2.0928724, - -41.631573, - -15.368924, - -41.6564, - -37.111736, - -37.09859, - -41.025192, - -2.1707618, - -40.202778, - -41.903214, - -9.049681, - -16.408047, - -42.958614, - -42.372177, - -45.431026, - -4.7174063, - -30.901245, - -46.568104, - -2.3207862, - -45.59506, - -13.1443 - ], - "xaxis": "x", - "y": [ - -10.254759, - -22.326504, - -8.559602, - -20.467894, - -16.367886, - 34.67725, - 0.20705454, - -2.1101115, - -5.9476533, - -0.62792206, - 17.02401, - 2.5558534, - -10.392384, - -18.170088, - -29.326565, - 26.266212, - -12.614066, - 14.819815, - -0.6994232, - 14.150794, - -22.118223, - -19.087698, - 25.554472, - -32.54127, - -2.2139447, - 26.281322, - -1.2898456, - -8.973769, - 32.03464, - -26.55557, - -18.176107, - -3.6315196, - -19.35426, - 4.681029, - -27.456821, - -24.490698, - 29.56973, - -10.1600275, - -10.852377, - 22.864084, - -10.734537, - -6.189112, - -13.330445, - 20.16529, - -9.1425705, - -23.877768, - 11.228575, - 17.663988, - 2.542931, - -1.3406546, - -23.981632, - 30.063469, - -14.152756, - -8.409088, - 19.963877, - -15.70016, - -19.558147, - 6.959669, - -17.884281, - -12.053112, - -12.727203, - -25.217607, - -0.7906725, - -10.548126, - 20.132593, - 10.375427, - 18.302557, - -1.274212, - 5.588625, - 7.1431947, - 27.65378, - -12.216588, - 3.5037541, - -4.302394, - 16.477135, - 19.197943, - -14.683218, - -14.726069, - -8.98979, - 16.493795, - -23.418943, - 31.197924, - -7.310227, - -8.874298, - -13.5169735, - -7.2581453, - 22.63779, - 28.57203, - -10.379873, - -16.003376, - 3.1463404, - -3.7059593, - -23.257317, - -9.050367, - -14.212201, - 21.597916, - 28.315454, - -9.711501, - 24.548212, - -17.763275, - 5.0752234, - -0.48519766, - 11.854107, - -11.487356, - -6.806543, - 32.15406, - -2.8677607, - -9.217092, - -7.9709535, - -9.329842, - -23.302309, - -0.9053779, - 14.969123, - -12.578425, - -21.124685, - 33.794212, - -20.977274, - -15.128482, - -16.33133, - -30.116133, - -4.7339125, - -24.043522, - 5.9628015, - -11.766295, - 20.015493, - -20.486948, - -21.596968, - 16.84247, - 24.66648, - -17.758942, - -8.986503, - -7.5245304, - -15.130549, - -6.4981833, - -11.591567, - 21.901451, - -0.6368593, - 16.802742, - 20.371767, - 0.94949424, - 31.862827, - 35.12017, - 24.827017, - 9.816621, - -5.2393093, - 9.180699, - 3.9763682, - -14.759153, - -11.865506, - -13.755454, - -13.679028, - 20.333702, - -1.8922254, - -13.595177, - -15.84722, - 25.674028, - 23.005444, - -7.935221, - 24.642124, - -19.237648, - -20.576859, - -14.856947, - -17.656506, - -14.13704, - 11.748135, - -17.733555, - -2.6014824, - -13.317306, - -0.7534798, - 16.867065, - 22.19843, - -8.660773, - -7.2729115, - 23.76637, - -30.203144, - -14.535694, - -6.199936, - -22.042368, - 14.838855, - -23.169117, - 34.738625, - 10.819606, - -10.898081, - -19.525257, - 17.761076, - 13.325627, - -13.576142, - -25.388409, - -0.5336322, - 17.181808, - -4.1373553, - -20.077517, - -16.791988, - 13.435741, - -22.73552, - -0.6918705, - 27.578976, - 0.41720697, - -21.062717, - 15.710144, - -13.556541, - -15.989742, - -13.601137, - -7.148113, - 19.323282, - 1.1989386, - -0.47444645, - 0.94497335, - 9.556583, - -4.4569497, - -24.076357, - 21.659195, - 33.719093, - 19.704102, - 31.99901, - -26.121319, - 26.534893, - 9.6071, - 3.7444665, - 21.501694, - -17.915682, - -17.60881, - -2.6888435, - 32.299854, - -15.889842, - 16.942707, - 0.2748501, - -16.57776, - -16.080286, - -10.464009, - 11.700205, - 30.462563, - -29.86582, - -8.282391, - -8.46684, - -16.238358, - -25.85015, - 19.385956, - 19.32553, - -19.83609, - -6.5198464, - -20.836182, - 0.45974386, - -8.057026, - 3.8825731, - 22.033895, - -5.582006, - -23.06022, - -5.6566067, - 33.256733, - -27.384535, - 34.042473, - -3.056115, - 21.529955, - 17.78518, - -29.090658, - 21.886444, - -3.69288, - 20.052608, - -0.2840251, - -15.78091, - -4.7497973, - -1.3183376, - -8.057265, - 3.0869732, - -7.959283, - -34.817635, - 34.081158, - -0.1847365, - -21.634756, - 25.563747, - 30.48236, - -11.662108, - -9.688497, - -4.4100275, - 24.713762, - -15.716439, - 21.239998, - -10.980467, - -5.3119135, - -16.683647, - -10.756376, - 27.087646, - -6.474749, - -16.08439, - -14.420636, - -7.351985, - 4.601816, - 10.600249, - -4.315942, - 0.85024196, - -8.745571, - -10.188763, - -5.876716, - -3.0804467, - -3.2769468, - -19.281757, - -15.235338, - -15.53886, - -19.303434, - -12.302218, - -23.051016, - -20.775913, - 11.0288925, - -15.161179, - 35.796543, - 4.53065, - -3.197111, - -9.126152, - -5.3693423, - -8.645808, - 26.929934, - -3.2705798, - -22.339233, - 6.1994753, - -0.03827765, - 1.0350281, - -2.2386749, - 10.1421995, - 21.500256, - 20.323536, - -14.895687, - 3.4454656, - 13.368094, - 17.025469, - -20.511335, - 20.105099, - 39.494793, - 33.45021, - 21.672586, - 15.536683, - -21.343506, - 24.513098, - 28.953205, - -1.2875158, - -0.14630945, - -5.8219385, - -14.570528, - -19.750566, - -0.25747964, - -6.1142654, - 24.168753, - -17.26478, - -12.292187, - 21.782167, - -22.266432, - 5.911049, - 7.3130083, - 29.060263, - -1.2906566, - -5.6858554, - 11.88096, - -14.269513, - -17.597988, - -13.744734, - 19.081799, - 25.979095, - 24.452019, - -11.330729, - -11.392148, - 1.1835473, - -1.922125, - 18.76773, - -5.1183553, - 14.371391, - -9.079416, - 32.181087, - -10.61583, - -20.358099, - -7.6430187, - -5.257486, - 16.290205, - 24.053005, - -5.443865, - -30.894657, - -1.7249132, - -1.4501517, - 14.297588, - 1.5236746, - 26.836803, - -7.542444, - -4.085233, - -9.243302, - -22.380308, - 32.18937, - -7.3226933, - -32.17766, - -15.654366, - -3.2708795, - 0.07473384, - 26.989523, - -8.599584, - -15.78256, - -16.991213, - -8.202672, - -10.86935, - 0.538039, - -11.617886, - 3.5003624, - -0.27149853, - -1.4504046, - 21.914633, - 19.832611, - 22.365917, - -22.19336, - -19.892523, - -11.016326, - -18.943878, - -9.76076, - -25.935007, - -21.395668, - -19.286484, - 30.605867, - -25.124516, - 19.076454, - -20.217596, - -4.427436, - 0.124456935, - 15.850318, - 14.106509, - -25.880474, - 20.326845, - -18.178284, - 18.546848, - -8.462077, - -6.078381, - 0.24295494, - -14.893564, - -22.965818, - 36.82579, - -12.61784, - 18.366398, - 4.072649, - 1.2205616, - -13.276994, - -17.09381, - -18.344118, - 33.12794, - -12.617298, - 3.6332028, - -24.02265, - 11.337199, - 6.917111, - -9.203751, - -4.5927486, - 0.687024, - 4.003382, - -5.714998, - -7.6352305, - 28.259352, - -7.4210625, - -14.774667, - 0.21726441, - -20.630865, - -0.4162142, - -12.0516205, - -24.923342, - -23.75025, - -23.006275, - 27.442217, - -22.516329, - -6.229835, - -15.101339, - -25.983875, - 24.170658, - -13.358003, - -32.53302, - 24.710947, - -4.510418, - -15.999681, - -10.241354, - -6.066459, - 27.73088, - -4.162361, - -11.81379, - 11.597373, - -21.738821, - -1.3630763 - ], - "yaxis": "y" - }, - { - "customdata": [ - [ - "Newspapers in Greece reflect a
mixture of exhilaration that
the Athens Olympics proved
successful, and relief that
they passed off without any
major setback." - ], - [ - "Loudon, NH -- As the rain
began falling late Friday
afternoon at New Hampshire
International Speedway, the
rich in the Nextel Cup garage
got richer." - ], - [ - "AP - Ottawa Senators right
wing Marian Hossa is switching
European teams during the NHL
lockout." - ], - [ - "Chelsea terminated Romania
striker Adrian Mutu #39;s
contract, citing gross
misconduct after the player
failed a doping test for
cocaine and admitted taking
the drug, the English soccer
club said in a statement." - ], - [ - "The success of Paris #39; bid
for Olympic Games 2012 would
bring an exceptional
development for France for at
least 6 years, said Jean-
Francois Lamour, French
minister for Youth and Sports
on Tuesday." - ], - [ - "Madison, WI (Sports Network) -
Anthony Davis ran for 122
yards and two touchdowns to
lead No. 6 Wisconsin over
Northwestern, 24-12, to
celebrate Homecoming weekend
at Camp Randall Stadium." - ], - [ - "LaVar Arrington participated
in his first practice since
Oct. 25, when he aggravated a
knee injury that sidelined him
for 10 games." - ], - [ - " NEW YORK (Reuters) - Billie
Jean King cut her final tie
with the U.S. Fed Cup team
Tuesday when she retired as
coach." - ], - [ - "The Miami Dolphins arrived for
their final exhibition game
last night in New Orleans
short nine players." - ], - [ - "AP - From LSU at No. 1 to Ohio
State at No. 10, The AP
women's basketball poll had no
changes Monday." - ], - [ - "nother stage victory for race
leader Petter Solberg, his
fifth since the start of the
rally. The Subaru driver is
not pulling away at a fast
pace from Gronholm and Loeb
but the gap is nonetheless
increasing steadily." - ], - [ - "April 1980 -- Players strike
the last eight days of spring
training. Ninety-two
exhibition games are canceled.
June 1981 -- Players stage
first midseason strike in
history." - ], - [ - "On Sunday, the day after Ohio
State dropped to 0-3 in the
Big Ten with a 33-7 loss at
Iowa, the Columbus Dispatch
ran a single word above its
game story on the Buckeyes:
quot;Embarrassing." - ], - [ - "BOSTON - Curt Schilling got
his 20th win on the eve of
Boston #39;s big series with
the New York Yankees. Now he
wants much more. quot;In a
couple of weeks, hopefully, it
will get a lot better, quot;
he said after becoming" - ], - [ - "Shooed the ghosts of the
Bambino and the Iron Horse and
the Yankee Clipper and the
Mighty Mick, on his 73rd
birthday, no less, and turned
Yankee Stadium into a morgue." - ], - [ - "Gold medal-winning Marlon
Devonish says the men #39;s
4x100m Olympic relay triumph
puts British sprinting back on
the map. Devonish, Darren
Campbell, Jason Gardener and
Mark Lewis-Francis edged out
the American" - ], - [ - "he difficult road conditions
created a few incidents in the
first run of the Epynt stage.
Francois Duval takes his
second stage victory since the
start of the rally, nine
tenths better than Sebastien
Loeb #39;s performance in
second position." - ], - [ - "Tallahassee, FL (Sports
Network) - Florida State head
coach Bobby Bowden has
suspended senior wide receiver
Craphonso Thorpe for the
Seminoles #39; game against
fellow Atlantic Coast
Conference member Duke on
Saturday." - ], - [ - "Former champion Lleyton Hewitt
bristled, battled and
eventually blossomed as he
took another step towards a
second US Open title with a
second-round victory over
Moroccan Hicham Arazi on
Friday." - ], - [ - "As the Mets round out their
search for a new manager, the
club is giving a last-minute
nod to its past. Wally
Backman, an infielder for the
Mets from 1980-88 who played
second base on the 1986" - ], - [ - "AMSTERDAM, Aug 18 (Reuters) -
Midfielder Edgar Davids #39;s
leadership qualities and
never-say-die attitude have
earned him the captaincy of
the Netherlands under new
coach Marco van Basten." - ], - [ - "COUNTY KILKENNY, Ireland (PA)
-- Hurricane Jeanne has led to
world No. 1 Vijay Singh
pulling out of this week #39;s
WGC-American Express
Championship at Mount Juliet." - ], - [ - "Green Bay, WI (Sports Network)
- The Green Bay Packers will
be without the services of Pro
Bowl center Mike Flanagan for
the remainder of the season,
as he will undergo left knee
surgery." - ], - [ - "COLUMBUS, Ohio -- NCAA
investigators will return to
Ohio State University Monday
to take another look at the
football program after the
latest round of allegations
made by former players,
according to the Akron Beacon
Journal." - ], - [ - "Manchester United gave Alex
Ferguson a 1,000th game
anniversary present by
reaching the last 16 of the
Champions League yesterday,
while four-time winners Bayern
Munich romped into the second
round with a 5-1 beating of
Maccabi Tel Aviv." - ], - [ - "The last time the Angels
played the Texas Rangers, they
dropped two consecutive
shutouts at home in their most
agonizing lost weekend of the
season." - ], - [ - "SEATTLE - Chasing a nearly
forgotten ghost of the game,
Ichiro Suzuki broke one of
baseball #39;s oldest records
Friday night, smoking a single
up the middle for his 258th
hit of the year and breaking
George Sisler #39;s record for
the most hits in a season" - ], - [ - "Grace Park, her caddie - and
fans - were poking around in
the desert brush alongside the
18th fairway desperately
looking for her ball." - ], - [ - "Washington Redskins kicker
John Hall strained his right
groin during practice
Thursday, his third leg injury
of the season. Hall will be
held out of practice Friday
and is questionable for Sunday
#39;s game against the Chicago
Bears." - ], - [ - "The Florida Gators and
Arkansas Razorbacks meet for
just the sixth time Saturday.
The Gators hold a 4-1
advantage in the previous five
meetings, including last year
#39;s 33-28 win." - ], - [ - "AP - 1941 #151; Brooklyn
catcher Mickey Owen dropped a
third strike on Tommy Henrich
of what would have been the
last out of a Dodgers victory
against the New York Yankees.
Given the second chance, the
Yankees scored four runs for a
7-4 victory and a 3-1 lead in
the World Series, which they
ended up winning." - ], - [ - "Braves shortstop Rafael Furcal
was arrested on drunken
driving charges Friday, his
second D.U.I. arrest in four
years." - ], - [ - " ATHENS (Reuters) - Natalie
Coughlin's run of bad luck
finally took a turn for the
better when she won the gold
medal in the women's 100
meters backstroke at the
Athens Olympics Monday." - ], - [ - "AP - Pedro Feliz hit a
tiebreaking grand slam with
two outs in the eighth inning
for his fourth hit of the day,
and the Giants helped their
playoff chances with a 9-5
victory over the Los Angeles
Dodgers on Saturday." - ], - [ - "LEVERKUSEN/ROME, Dec 7 (SW) -
Dynamo Kiev, Bayer Leverkusen,
and Real Madrid all have a
good chance of qualifying for
the Champions League Round of
16 if they can get the right
results in Group F on
Wednesday night." - ], - [ - "Ed Hinkel made a diving,
fingertip catch for a key
touchdown and No. 16 Iowa
stiffened on defense when it
needed to most to beat Iowa
State 17-10 Saturday." - ], - [ - "During last Sunday #39;s
Nextel Cup race, amid the
ongoing furor over Dale
Earnhardt Jr. #39;s salty
language, NBC ran a commercial
for a show coming on later
that night called quot;Law
amp; Order: Criminal Intent." - ], - [ - "AP - After playing in hail,
fog and chill, top-ranked
Southern California finishes
its season in familiar
comfort. The Trojans (9-0, 6-0
Pacific-10) have two games at
home #151; against Arizona on
Saturday and Notre Dame on
Nov. 27 #151; before their
rivalry game at UCLA." - ], - [ - "The remnants of Hurricane
Jeanne rained out Monday's
game between the Mets and the
Atlanta Braves. It will be
made up Tuesday as part of a
doubleheader." - ], - [ - "AP - NASCAR is not expecting
any immediate changes to its
top-tier racing series
following the merger between
telecommunications giant
Sprint Corp. and Nextel
Communications Inc." - ], - [ - "Like wide-open races? You
#39;ll love the Big 12 North.
Here #39;s a quick morning
line of the Big 12 North as it
opens conference play this
weekend." - ], - [ - "Taquan Dean scored 22 points,
Francisco Garcia added 19 and
No. 13 Louisville withstood a
late rally to beat Florida
74-70 Saturday." - ], - [ - "NEW YORK -- This was all about
Athens, about redemption for
one and validation for the
other. Britain's Paula
Radcliffe, the fastest female
marathoner in history, failed
to finish either of her
Olympic races last summer.
South Africa's Hendrik Ramaala
was a five-ringed dropout,
too, reinforcing his
reputation as a man who could
go only half the distance." - ], - [ - "LONDON -- Ernie Els has set
his sights on an improved
putting display this week at
the World Golf Championships
#39; NEC Invitational in
Akron, Ohio, after the
disappointment of tying for
fourth place at the PGA
Championship last Sunday." - ], - [ - "Fiji #39;s Vijay Singh
replaced Tiger Woods as the
world #39;s No.1 ranked golfer
today by winning the PGA
Deutsche Bank Championship." - ], - [ - "LEIPZIG, Germany : Jurgen
Klinsmann enjoyed his first
home win as German manager
with his team defeating ten-
man Cameroon 3-0 in a friendly
match." - ], - [ - "AP - Kevin Brown had a chance
to claim a place in Yankees
postseason history with his
start in Game 7 of the AL
championship series." - ], - [ - "HOMESTEAD, Fla. -- Kurt Busch
got his first big break in
NASCAR by winning a 1999
talent audition nicknamed
quot;The Gong Show. quot; He
was selected from dozens of
unknown, young race drivers to
work for one of the sport
#39;s most famous team owners,
Jack Roush." - ], - [ - "Zurich, Switzerland (Sports
Network) - Former world No. 1
Venus Williams advanced on
Thursday and will now meet
Wimbledon champion Maria
Sharapova in the quarterfinals
at the \\$1." - ], - [ - "INDIA #39;S cricket chiefs
began a frenetic search today
for a broadcaster to show next
month #39;s home series
against world champion
Australia after cancelling a
controversial \\$US308 million
(\\$440 million) television
deal." - ], - [ - "The International Olympic
Committee (IOC) has urged
Beijing to ensure the city is
ready to host the 2008 Games
well in advance, an official
said on Wednesday." - ], - [ - "Virginia Tech scores 24 points
off four first-half turnovers
in a 55-6 wipeout of Maryland
on Thursday to remain alone
atop the ACC." - ], - [ - "AP - Martina Navratilova's
long, illustrious career will
end without an Olympic medal.
The 47-year-old Navratilova
and Lisa Raymond lost 6-4,
4-6, 6-4 on Thursday night to
fifth-seeded Shinobu Asagoe
and Ai Sugiyama of Japan in
the quarterfinals, one step
shy of the medal round." - ], - [ - "PSV Eindhoven re-established
their five-point lead at the
top of the Dutch Eredivisie
today with a 2-0 win at
Vitesse Arnhem. Former
Sheffield Wednesday striker
Gerald Sibon put PSV ahead in
the 56th minute" - ], - [ - "TODAY AUTO RACING 3 p.m. --
NASCAR Nextel Cup Sylvania 300
qualifying at N.H.
International Speedway,
Loudon, N.H., TNT PRO BASEBALL
7 p.m. -- Red Sox at New York
Yankees, Ch. 38, WEEI (850)
(on cable systems where Ch. 38
is not available, the game
will air on NESN); Chicago
Cubs at Cincinnati, ESPN 6
p.m. -- Eastern League finals:
..." - ], - [ - "MUMBAI, SEPTEMBER 21: The
Board of Control for Cricket
in India (BCCI) today informed
the Bombay High Court that it
was cancelling the entire
tender process regarding
cricket telecast rights as
also the conditional deal with
Zee TV." - ], - [ - "JEJU, South Korea : Grace Park
of South Korea won an LPGA
Tour tournament, firing a
seven-under-par 65 in the
final round of the
1.35-million dollar CJ Nine
Bridges Classic." - ], - [ - "AP - The NHL will lock out its
players Thursday, starting a
work stoppage that threatens
to keep the sport off the ice
for the entire 2004-05 season." - ], - [ - "When Paula Radcliffe dropped
out of the Olympic marathon
miles from the finish, she
sobbed uncontrollably.
Margaret Okayo knew the
feeling." - ], - [ - "First baseman Richie Sexson
agrees to a four-year contract
with the Seattle Mariners on
Wednesday." - ], - [ - "Notes and quotes from various
drivers following California
Speedway #39;s Pop Secret 500.
Jeff Gordon slipped to second
in points following an engine
failure while Jimmie Johnson
moved back into first." - ], - [ - " MEMPHIS, Tenn. (Sports
Network) - The Memphis
Grizzlies signed All-Star
forward Pau Gasol to a multi-
year contract on Friday.
Terms of the deal were not
announced." - ], - [ - "Andre Agassi brushed past
Jonas Bjorkman 6-3 6-4 at the
Stockholm Open on Thursday to
set up a quarter-final meeting
with Spanish sixth seed
Fernando Verdasco." - ], - [ - " BOSTON (Reuters) - Boston was
tingling with anticipation on
Saturday as the Red Sox
prepared to host Game One of
the World Series against the
St. Louis Cardinals and take a
step toward ridding
themselves of a hex that has
hung over the team for eight
decades." - ], - [ - "FOR the first time since his
appointment as Newcastle
United manager, Graeme Souness
has been required to display
the strong-arm disciplinary
qualities that, to Newcastle
directors, made" - ], - [ - "WHY IT HAPPENED Tom Coughlin
won his first game as Giants
coach and immediately
announced a fine amnesty for
all Giants. Just kidding." - ], - [ - "A second-place finish in his
first tournament since getting
married was good enough to
take Tiger Woods from third to
second in the world rankings." - ], - [ - " COLORADO SPRINGS, Colorado
(Reuters) - World 400 meters
champion Jerome Young has been
given a lifetime ban from
athletics for a second doping
offense, the U.S. Anti-Doping
Agency (USADA) said Wednesday." - ], - [ - "LONDON - In two years, Arsenal
will play their home matches
in the Emirates stadium. That
is what their new stadium at
Ashburton Grove will be called
after the Premiership
champions yesterday agreed to
the" - ], - [ - "KNOXVILLE, Tenn. -- Jason
Campbell threw for 252 yards
and two touchdowns, and No. 8
Auburn proved itself as a
national title contender by
overwhelming No. 10 Tennessee,
34-10, last night." - ], - [ - "WASHINGTON -- Another
Revolution season concluded
with an overtime elimination.
Last night, the Revolution
thrice rallied from deficits
for a 3-3 tie with D.C. United
in the Eastern Conference
final, then lost in the first-
ever Major League Soccer match
decided by penalty kicks." - ], - [ - "Dwyane Wade calls himself a
quot;sidekick, quot; gladly
accepting the role Kobe Bryant
never wanted in Los Angeles.
And not only does second-year
Heat point" - ], - [ - "World number one golfer Vijay
Singh of Fiji has captured his
eighth PGA Tour event of the
year with a win at the 84
Lumber Classic in Farmington,
Pennsylvania." - ], - [ - "The noise was deafening and
potentially unsettling in the
minutes before the start of
the men #39;s Olympic
200-meter final. The Olympic
Stadium crowd chanted
quot;Hellas!" - ], - [ - "Brazilian forward Ronaldinho
scored a sensational goal for
Barcelona against Milan,
making for a last-gasp 2-1.
The 24-year-old #39;s
brilliant left-foot hit at the
Nou Camp Wednesday night sent
Barcelona atop of Group F." - ], - [ - "Bee Staff Writer. SANTA CLARA
- Andre Carter #39;s back
injury has kept him out of the
49ers #39; lineup since Week
1. It #39;s also interfering
with him rooting on his alma
mater in person." - ], - [ - "AP - Kellen Winslow Jr. ended
his second NFL holdout Friday." - ], - [ - "HOUSTON - With only a few
seconds left in a certain
victory for Miami, Peyton
Manning threw a meaningless
6-yard touchdown pass to
Marvin Harrison to cut the
score to 24-15." - ], - [ - "DEADLY SCORER: Manchester
United #39;s Wayne Rooney
celebrating his three goals
against Fenerbahce this week
at Old Trafford. (AP)." - ], - [ - "You can feel the confidence
level, not just with Team
Canada but with all of Canada.
There #39;s every expectation,
from one end of the bench to
the other, that Martin Brodeur
is going to hold the fort." - ], - [ - "Heading into the first game of
a new season, every team has
question marks. But in 2004,
the Denver Broncos seemed to
have more than normal." - ], - [ - "Anaheim, Calif. - There is a
decidedly right lean to the
three-time champions of the
American League Central. In a
span of 26 days, the Minnesota
Twins lost the left side of
their infield to free agency." - ], - [ - "With the NFL trading deadline
set for 4 p.m. Tuesday,
Patriots coach Bill Belichick
said there didn't seem to be
much happening on the trade
front around the league." - ], - [ - "AP - David Beckham broke his
rib moments after scoring
England's second goal in
Saturday's 2-0 win over Wales
in a World Cup qualifying
game." - ], - [ - "Nothing changed at the top of
Serie A as all top teams won
their games to keep the
distance between one another
unaltered. Juventus came back
from behind against Lazio to
win thanks to another goal by
Ibrahimovic" - ], - [ - "RICHMOND, Va. Jeremy Mayfield
won his first race in over
four years, taking the
Chevrolet 400 at Richmond
International Raceway after
leader Kurt Busch ran out of
gas eight laps from the
finish." - ], - [ - "AP - Track star Marion Jones
filed a defamation lawsuit
Wednesday against the man
whose company is at the center
of a federal investigation
into illegal steroid use among
some of the nation's top
athletes." - ], - [ - "England #39;s players hit out
at cricket #39;s authorities
tonight and claimed they had
been used as quot;political
pawns quot; after the Zimbabwe
government produced a
spectacular U-turn to ensure
the controversial one-day
series will go ahead." - ], - [ - "AP - The Japanese won the
pregame home run derby. Then
the game started and the major
league All-Stars put their
bats to work. Back-to-back
home runs by Moises Alou and
Vernon Wells in the fourth
inning and by Johnny Estrada
and Brad Wilkerson in the
ninth powered the major
leaguers past the Japanese
stars 7-3 Sunday for a 3-0
lead in the eight-game series." - ], - [ - "With Chelsea losing their
unbeaten record and Manchester
United failing yet again to
win, William Hill now make
Arsenal red-hot 2/5 favourites
to retain the title." - ], - [ - "Theresa special bookcase in Al
Grohs office completely full
of game plans from his days in
the NFL. Green ones are from
the Jets." - ], - [ - "Newcastle ensured their place
as top seeds in Friday #39;s
third round UEFA Cup draw
after holding Sporting Lisbon
to a 1-1 draw at St James #39;
Park." - ], - [ - "CBC SPORTS ONLINE - Bode
Miller continued his
impressive 2004-05 World Cup
skiing season by winning a
night slalom race in
Sestriere, Italy on Monday." - ], - [ - "Damien Rhodes scored on a
2-yard run in the second
overtime, then Syracuse's
defense stopped Pittsburgh on
fourth and 1, sending the
Orange to a 38-31 victory
yesterday in Syracuse, N.Y." - ], - [ - "AP - Anthony Harris scored 18
of his career-high 23 points
in the second half to help
Miami upset No. 19 Florida
72-65 Saturday and give first-
year coach Frank Haith his
biggest victory." - ], - [ - "AP - The five cities looking
to host the 2012 Summer Games
submitted bids to the
International Olympic
Committee on Monday, entering
the final stage of a long
process in hopes of landing
one of the biggest prizes in
sports." - ], - [ - "The FIA has already cancelled
todays activities at Suzuka as
Super Typhoon Ma-On heads
towards the 5.807km circuit.
Saturday practice has been
cancelled altogether while
pre-qualifying and final
qualifying" - ], - [ - " GRAND PRAIRIE, Texas
(Reuters) - Betting on horses
was banned in Texas until as
recently as 1987. Times have
changed rapidly since.
Saturday, Lone Star Park race
track hosts the \\$14 million
Breeders Cup, global racing's
end-of-season extravaganza." - ], - [ - "It might be a stay of
execution for Coach P, or it
might just be a Christmas
miracle come early. SU #39;s
upset win over BC has given
hope to the Orange playing in
a post season Bowl game." - ], - [ - "PHIL Neville insists
Manchester United don #39;t
fear anyone in the Champions
League last 16 and declared:
quot;Bring on the Italians." - ], - [ - "TORONTO -- Toronto Raptors
point guard Alvin Williams
will miss the rest of the
season after undergoing
surgery on his right knee
Monday." - ], - [ - "AP - Tennessee's two freshmen
quarterbacks have Volunteers
fans fantasizing about the
next four years. Brent
Schaeffer and Erik Ainge
surprised many with the nearly
seamless way they rotated
throughout a 42-17 victory
over UNLV on Sunday night." - ], - [ - "MADRID, Aug 18 (Reuters) -
Portugal captain Luis Figo
said on Wednesday he was
taking an indefinite break
from international football,
but would not confirm whether
his decision was final." - ], - [ - "AP - Shaun Rogers is in the
backfield as often as some
running backs. Whether teams
dare to block Detroit's star
defensive tackle with one
player or follow the trend of
double-teaming him, he often
rips through offensive lines
with a rare combination of
size, speed, strength and
nimble footwork." - ], - [ - "The Lions and Eagles entered
Sunday #39;s game at Ford
Field in the same place --
atop their respective
divisions -- and with
identical 2-0 records." - ], - [ - "After Marcos Moreno threw four
more interceptions in last
week's 14-13 overtime loss at
N.C. A T, Bison Coach Ray
Petty will start Antoine
Hartfield against Norfolk
State on Saturday." - ], - [ - "SOUTH WILLIAMSPORT, Pa. --
Looking ahead to the US
championship game almost cost
Conejo Valley in the
semifinals of the Little
League World Series." - ], - [ - "The Cubs didn #39;t need to
fly anywhere near Florida to
be in the eye of the storm.
For a team that is going on
100 years since last winning a
championship, the only thing
they never are at a loss for
is controversy." - ], - [ - "KETTERING, Ohio Oct. 12, 2004
- Cincinnati Bengals defensive
end Justin Smith pleaded not
guilty to a driving under the
influence charge." - ], - [ - "MINNEAPOLIS -- For much of the
2004 season, Twins pitcher
Johan Santana didn #39;t just
beat opposing hitters. Often,
he overwhelmed and owned them
in impressive fashion." - ], - [ - "At a charity auction in New
Jersey last weekend, baseball
memorabilia dealer Warren
Heller was approached by a man
with an unusual but topical
request." - ], - [ - "INTER Milan coach Roberto
Mancini believes the club
#39;s lavish (northern) summer
signings will enable them to
mount a serious Serie A
challenge this season." - ], - [ - "AP - Brad Ott shot an 8-under
64 on Sunday to win the
Nationwide Tour's Price Cutter
Charity Championship for his
first Nationwide victory." - ], - [ - "AP - New York Jets running
back Curtis Martin passed Eric
Dickerson and Jerome Bettis on
the NFL career rushing list
Sunday against the St. Louis
Rams, moving to fourth all-
time." - ], - [ - "Instead of standing for ante
meridian and post meridian,
though, fans will remember the
time periods of pre-Mia and
after-Mia. After playing for
18 years and shattering nearly
every record" - ], - [ - "Stephon Marbury, concerned
about his lousy shooting in
Athens, used an off day to go
to the gym and work on his
shot. By finding his range, he
saved the United States #39;
hopes for a basketball gold
medal." - ], - [ - "AP - Randy Moss is expected to
play a meaningful role for the
Minnesota Vikings this weekend
against the Giants, even
without a fully healed right
hamstring." - ], - [ - "Pros: Fits the recent profile
(44, past PGA champion, fiery
Ryder Cup player); the job is
his if he wants it. Cons:
Might be too young to be
willing to burn two years of
play on tour." - ], - [ - "Elmer Santos scored in the
second half, lifting East
Boston to a 1-0 win over
Brighton yesterday afternoon
and giving the Jets an early
leg up in what is shaping up
to be a tight Boston City
League race." - ], - [ - "Saints special teams captain
Steve Gleason expects to be
fined by the league after
being ejected from Sunday's
game against the Carolina
Panthers for throwing a punch." - ], - [ - "Play has begun in the
Australian Masters at
Huntingdale in Melbourne with
around half the field of 120
players completing their first
rounds." - ], - [ - "LOUISVILLE, Ky. - Louisville
men #39;s basketball head
coach Rick Pitino and senior
forward Ellis Myles met with
members of the media on Friday
to preview the Cardinals #39;
home game against rival
Kentucky on Satursday." - ], - [ - "AP - Sounds like David
Letterman is as big a \"Pops\"
fan as most everyone else." - ], - [ - "Arsenal was held to a 1-1 tie
by struggling West Bromwich
Albion on Saturday, failing to
pick up a Premier League
victory when Rob Earnshaw
scored with 11 minutes left." - ], - [ - "Baseball #39;s executive vice
president Sandy Alderson
insisted last month that the
Cubs, disciplined for an
assortment of run-ins with
umpires, would not be targeted
the rest of the season by
umpires who might hold a
grudge." - ], - [ - "As Superman and Batman would
no doubt reflect during their
cigarette breaks, the really
draining thing about being a
hero was that you have to keep
riding to the rescue." - ], - [ - "AP - Eric Hinske and Vernon
Wells homered, and the Toronto
Blue Jays completed a three-
game sweep of the Baltimore
Orioles with an 8-5 victory
Sunday." - ], - [ - " NEW YORK (Reuters) - Todd
Walker homered, had three hits
and drove in four runs to lead
the Chicago Cubs to a 12-5 win
over the Cincinnati Reds in
National League play at
Wrigley Field on Monday." - ], - [ - "MIAMI (Sports Network) -
Shaquille O #39;Neal made his
home debut, but once again it
was Dwyane Wade stealing the
show with 28 points as the
Miami Heat downed the
Cleveland Cavaliers, 92-86, in
front of a record crowd at
AmericanAirlines Arena." - ], - [ - "AP - The San Diego Chargers
looked sharp #151; and played
the same way. Wearing their
powder-blue throwback jerseys
and white helmets from the
1960s, the Chargers did almost
everything right in beating
the Jacksonville Jaguars 34-21
on Sunday." - ], - [ - "NERVES - no problem. That
#39;s the verdict of Jose
Mourinho today after his
Chelsea side gave a resolute
display of character at
Highbury." - ], - [ - "AP - The latest low point in
Ron Zook's tenure at Florida
even has the coach wondering
what went wrong. Meanwhile,
Sylvester Croom's first big
win at Mississippi State has
given the Bulldogs and their
fans a reason to believe in
their first-year leader.
Jerious Norwood's 37-yard
touchdown run with 32 seconds
remaining lifted the Bulldogs
to a 38-31 upset of the 20th-
ranked Gators on Saturday." - ], - [ - "Someone forgot to inform the
US Olympic basketball team
that it was sent to Athens to
try to win a gold medal, not
to embarrass its country." - ], - [ - "Ten-man Paris St Germain
clinched their seventh
consecutive victory over arch-
rivals Olympique Marseille
with a 2-1 triumph in Ligue 1
on Sunday thanks to a second-
half winner by substitute
Edouard Cisse." - ], - [ - "Roy Oswalt wasn #39;t
surprised to hear the Astros
were flying Sunday night
through the remnants of a
tropical depression that
dumped several inches of rain
in Louisiana and could bring
showers today in Atlanta." - ], - [ - "AP - This hardly seemed
possible when Pitt needed
frantic rallies to overcome
Division I-AA Furman or Big
East cellar dweller Temple. Or
when the Panthers could barely
move the ball against Ohio
#151; not Ohio State, but Ohio
U." - ], - [ - "Everyone is moaning about the
fallout from last weekend but
they keep on reporting the
aftermath. #39;The fall-out
from the so-called
quot;Battle of Old Trafford
quot; continues to settle over
the nation and the debate" - ], - [ - "American Lindsay Davenport
regained the No. 1 ranking in
the world for the first time
since early 2002 by defeating
Dinara Safina of Russia 6-4,
6-2 in the second round of the
Kremlin Cup on Thursday." - ], - [ - "Freshman Jeremy Ito kicked
four field goals and Ryan
Neill scored on a 31-yard
interception return to lead
improving Rutgers to a 19-14
victory on Saturday over
visiting Michigan State." - ], - [ - "Third-seeded Guillermo Canas
defeated Guillermo Garcia-
Lopez of Spain 7-6 (1), 6-3
Monday on the first day of the
Shanghai Open on Monday." - ], - [ - "But to play as feebly as it
did for about 35 minutes last
night in Game 1 of the WNBA
Finals and lose by only four
points -- on the road, no less
-- has to be the best
confidence builder since Cindy
St." - ], - [ - "With yesterday #39;s report on
its athletic department
violations completed, the
University of Washington says
it is pleased to be able to
move forward." - ], - [ - "Sammy Sosa was fined \\$87,400
-- one day's salary -- for
arriving late to the Cubs'
regular-season finale at
Wrigley Field and leaving the
game early. The slugger's
agent, Adam Katz , said
yesterday Sosa most likely
will file a grievance. Sosa
arrived 70 minutes before
Sunday's first pitch, and he
apparently left 15 minutes
after the game started without
..." - ], - [ - "Is the Oklahoma defense a
notch below its predecessors?
Is Texas #39; offense a step-
ahead? Why is Texas Tech
feeling good about itself
despite its recent loss?" - ], - [ - "New York gets 57 combined
points from its starting
backcourt of Jamal Crawford
and Stephon Marbury and tops
Denver, 107-96." - ], - [ - "AP - Shawn Marion had a
season-high 33 points and 15
rebounds, leading the Phoenix
Suns on a fourth-quarter
comeback despite the absence
of Steve Nash in a 95-86 win
over the New Orleans Hornets
on Friday night." - ], - [ - "Messina upset defending
champion AC Milan 2-1
Wednesday, while Juventus won
its third straight game to
stay alone atop the Italian
league standings." - ], - [ - "AP - Padraig Harrington
rallied to a three-stroke
victory in the German Masters
on a windy Sunday, closing
with a 2-under-par 70 and
giving his game a big boost
before the Ryder Cup." - ], - [ - "Ironically it was the first
regular season game for the
Carolina Panthers that not
only began the history of the
franchise, but also saw the
beginning of a rivalry that
goes on to this day." - ], - [ - "Baltimore Ravens running back
Jamal Lewis did not appear at
his arraignment Friday, but
his lawyers entered a not
guilty plea on charges in an
expanded drug conspiracy
indictment." - ], - [ - "After serving a five-game
suspension, Milton Bradley
worked out with the Dodgers as
they prepared for Tuesday's
opener against the St. Louis
Cardinals." - ], - [ - "AP - Prime Time won't be
playing in prime time this
time. Deion Sanders was on the
inactive list and missed a
chance to strut his stuff on
\"Monday Night Football.\"" - ], - [ - "The man who delivered the
knockout punch was picked up
from the Seattle scrap heap
just after the All-Star Game.
Before that, John Olerud
certainly hadn't figured on
facing Pedro Martinez in
Yankee Stadium in October." - ], - [ - "LONDON : A consortium,
including former world
champion Nigel Mansell, claims
it has agreed terms to ensure
Silverstone remains one of the
venues for the 2005 Formula
One world championship." - ], - [ - " BATON ROUGE, La. (Sports
Network) - LSU has named Les
Miles its new head football
coach, replacing Nick Saban." - ], - [ - "AP - The Chicago Blackhawks
re-signed goaltender Michael
Leighton to a one-year
contract Wednesday." - ], - [ - "ATHENS -- She won her first
Olympic gold medal in kayaking
when she was 18, the youngest
paddler to do so in Games
history. Yesterday, at 42,
Germany #39;s golden girl
Birgit Fischer won her eighth
Olympic gold in the four-woman
500-metre kayak race." - ], - [ - "England boss Sven Goran
Eriksson has defended
goalkeeper David James after
last night #39;s 2-2 draw in
Austria. James allowed Andreas
Ivanschitz #39;s shot to slip
through his fingers to
complete Austria comeback from
two goals down." - ], - [ - "The quot;future quot; is
getting a chance to revive the
presently struggling New York
Giants. Two other teams also
decided it was time for a
change at quarterback, but the
Buffalo Bills are not one of
them." - ], - [ - "Flushing Meadows, NY (Sports
Network) - The men #39;s
semifinals at the 2004 US Open
will be staged on Saturday,
with three of the tournament
#39;s top-five seeds ready for
action at the USTA National
Tennis Center." - ], - [ - "Given nearly a week to examine
the security issues raised by
the now-infamous brawl between
players and fans in Auburn
Hills, Mich., Nov. 19, the
Celtics returned to the
FleetCenter last night with
two losses and few concerns
about their on-court safety." - ], - [ - "Arsenals Thierry Henry today
missed out on the European
Footballer of the Year award
as Andriy Shevchenko took the
honour. AC Milan frontman
Shevchenko held off
competition from Barcelona
pair Deco and" - ], - [ - "Neil Mellor #39;s sensational
late winner for Liverpool
against Arsenal on Sunday has
earned the back-up striker the
chance to salvage a career
that had appeared to be
drifting irrevocably towards
the lower divisions." - ], - [ - "ABOUT 70,000 people were
forced to evacuate Real Madrid
#39;s Santiago Bernabeu
stadium minutes before the end
of a Primera Liga match
yesterday after a bomb threat
in the name of ETA Basque
separatist guerillas." - ], - [ - "The team learned on Monday
that full-back Jon Ritchie
will miss the rest of the
season with a torn anterior
cruciate ligament in his left
knee." - ], - [ - "Venus Williams barely kept
alive her hopes of qualifying
for next week #39;s WTA Tour
Championships. Williams,
seeded fifth, survived a
third-set tiebreaker to
outlast Yuilana Fedak of the
Ukraine, 6-4 2-6 7-6" - ], - [ - " SYDNEY (Reuters) - Arnold
Palmer has taken a swing at
America's top players,
criticizing their increasing
reluctance to travel abroad
to play in tournaments." - ], - [ - "Although he was well-beaten by
Retief Goosen in Sunday #39;s
final round of The Tour
Championship in Atlanta, there
has been some compensation for
the former world number one,
Tiger Woods." - ], - [ - "WAYNE Rooney and Henrik
Larsson are among the players
nominated for FIFAs
prestigious World Player of
the Year award. Rooney is one
of four Manchester United
players on a list which is
heavily influenced by the
Premiership." - ], - [ - "It didn #39;t look good when
it happened on the field, and
it looked worse in the
clubhouse. Angels second
baseman Adam Kennedy left the
Angels #39; 5-2 win over the
Seattle Mariners" - ], - [ - "Freshman Darius Walker ran for
115 yards and scored two
touchdowns, helping revive an
Irish offense that had managed
just one touchdown in the
season's first six quarters." - ], - [ - "AP - NBC is adding a 5-second
delay to its NASCAR telecasts
after Dale Earnhardt Jr. used
a vulgarity during a postrace
TV interview last weekend." - ], - [ - " BOSTON (Sports Network) - The
New York Yankees will start
Orlando \"El Duque\" Hernandez
in Game 4 of the American
League Championship Series on
Saturday against the Boston
Red Sox." - ], - [ - "The future of Sven-Goran
Eriksson as England coach is
the subject of intense
discussion after the draw in
Austria. Has the Swede lost
the confidence of the nation
or does he remain the best man
for the job?" - ], - [ - "Spain begin their third final
in five seasons at the Olympic
stadium hoping to secure their
second title since their first
in Barcelona against Australia
in 2000." - ], - [ - "Second-seeded David Nalbandian
of Argentina lost at the Japan
Open on Thursday, beaten by
Gilles Muller of Luxembourg
7-6 (4), 3-6, 6-4 in the third
round." - ], - [ - "Thursday #39;s unexpected
resignation of Memphis
Grizzlies coach Hubie Brown
left a lot of questions
unanswered. In his unique way
of putting things, the
71-year-old Brown seemed to
indicate he was burned out and
had some health concerns." - ], - [ - "RUDI Voller had quit as coach
of Roma after a 3-1 defeat
away to Bologna, the Serie A
club said today. Under the
former Germany coach, Roma had
taken just four league points
from a possible 12." - ], - [ - "ONE by one, the players #39;
faces had flashed up on the
giant Ibrox screens offering
season #39;s greetings to the
Rangers fans. But the main
presents were reserved for
Auxerre." - ], - [ - "South Korea #39;s Grace Park
shot a seven-under-par 65 to
triumph at the CJ Nine Bridges
Classic on Sunday. Park #39;s
victory made up her final-
round collapse at the Samsung
World Championship two weeks
ago." - ], - [ - "Titans QB Steve McNair was
released from a Nashville
hospital after a two-night
stay for treatment of a
bruised sternum. McNair was
injured during the fourth
quarter of the Titans #39;
15-12 loss to Jacksonville on
Sunday." - ], - [ - "Keith Miller, Australia #39;s
most prolific all-rounder in
Test cricket, died today at a
nursing home, Cricket
Australia said. He was 84." - ], - [ - "TORONTO Former Toronto pitcher
John Cerutti (seh-ROO
#39;-tee) was found dead in
his hotel room today,
according to the team. He was
44." - ], - [ - "Defense: Ken Lucas. His
biggest play was his first
one. The fourth-year
cornerback intercepted a Ken
Dorsey pass that kissed off
the hands of wide receiver
Rashaun Woods and returned it
25 yards to set up the
Seahawks #39; first score." - ], - [ - "The powerful St. Louis trio of
Albert Pujols, Scott Rolen and
Jim Edmonds is 4 for 23 with
one RBI in the series and with
runners on base, they are 1
for 13." - ], - [ - "The Jets signed 33-year-old
cornerback Terrell Buckley,
who was released by New
England on Sunday, after
putting nickel back Ray
Mickens on season-ending
injured reserve yesterday with
a torn ACL in his left knee." - ], - [ - "WEST INDIES thrilling victory
yesterday in the International
Cricket Council Champions
Trophy meant the world to the
five million people of the
Caribbean." - ], - [ - "Scotland manager Berti Vogts
insists he is expecting
nothing but victory against
Moldova on Wednesday. The game
certainly is a must-win affair
if the Scots are to have any
chance of qualifying for the
2006 World Cup finals." - ], - [ - " INDIANAPOLIS (Reuters) -
Jenny Thompson will take the
spotlight from injured U.S.
team mate Michael Phelps at
the world short course
championships Saturday as she
brings down the curtain on a
spectacular swimming career." - ], - [ - "Martin Brodeur made 27 saves,
and Brad Richards, Kris Draper
and Joe Sakic scored goals to
help Canada beat Russia 3-1
last night in the World Cup of
Hockey, giving the Canadians a
3-0 record in round-robin
play." - ], - [ - "ST. LOUIS -- Mike Martz #39;s
week of anger was no empty
display. He saw the defending
NFC West champions slipping
and thought taking potshots at
his players might be his best
shot at turning things around." - ], - [ - "Romanian soccer star Adrian
Mutu as he arrives at the
British Football Association
in London, ahead of his
disciplinary hearing, Thursday
Nov. 4, 2004." - ], - [ - "Australia completed an
emphatic Test series sweep
over New Zealand with a
213-run win Tuesday, prompting
a caution from Black Caps
skipper Stephen Fleming for
other cricket captains around
the globe." - ], - [ - "Liverpool, England (Sports
Network) - Former English
international and Liverpool
great Emlyn Hughes passed away
Tuesday from a brain tumor." - ], - [ - "JACKSONVILLE, Fla. -- They
were singing in the Colts #39;
locker room today, singing
like a bunch of wounded
songbirds. Never mind that
Marcus Pollard, Dallas Clark
and Ben Hartsock won #39;t be
recording a remake of Kenny
Chesney #39;s song,
quot;Young, quot; any time
soon." - ], - [ - "As the close-knit NASCAR
community mourns the loss of
team owner Rick Hendrick #39;s
son, brother, twin nieces and
six others in a plane crash
Sunday, perhaps no one outside
of the immediate family
grieves more deeply than
Darrell Waltrip." - ], - [ - "AP - Purdue quarterback Kyle
Orton has no trouble
remembering how he felt after
last year's game at Michigan." - ], - [ - "Brown is a second year player
from Memphis and has spent the
2004 season on the Steelers
#39; practice squad. He played
in two games last year." - ], - [ - "Barret Jackman, the last of
the Blues left to sign before
the league #39;s probable
lockout on Wednesday,
finalized a deal Monday that
is rare in the current
economic climate but fitting
for him." - ], - [ - "AP - In the tumult of the
visitors' clubhouse at Yankee
Stadium, champagne pouring all
around him, Theo Epstein held
a beer. \"I came in and there
was no champagne left,\" he
said this week. \"I said, 'I'll
have champagne if we win it
all.'\" Get ready to pour a
glass of bubbly for Epstein.
No I.D. necessary." - ], - [ - "Barcelona held on from an
early Deco goal to edge game
local rivals Espanyol 1-0 and
carve out a five point
tabletop cushion. Earlier,
Ronaldo rescued a point for
Real Madrid, who continued
their middling form with a 1-1
draw at Real Betis." - ], - [ - "MONTREAL (CP) - The Expos may
be history, but their demise
has heated up the market for
team memorabilia. Vintage
1970s and 1980s shirts are
already sold out, but
everything from caps, beer
glasses and key-chains to
dolls of mascot Youppi!" - ], - [ - "A 76th minute goal from
European Footballer of the
Year Pavel Nedved gave
Juventus a 1-0 win over Bayern
Munich on Tuesday handing the
Italians clear control at the
top of Champions League Group
C." - ], - [ - "ANN ARBOR, Mich. -- Some NHL
players who took part in a
charity hockey game at the
University of Michigan on
Thursday were hopeful the news
that the NHL and the players
association will resume talks
next week" - ], - [ - "BOULDER, Colo. -- Vernand
Morency ran for 165 yards and
two touchdowns and Donovan
Woods threw for three more
scores, lifting No. 22
Oklahoma State to a 42-14
victory over Colorado
yesterday." - ], - [ - "PULLMAN - Last week, in
studying USC game film, Cougar
coaches thought they found a
chink in the national
champions armor. And not just
any chink - one with the
potential, right from the get
go, to" - ], - [ - "AP - Matt Leinart was quite a
baseball prospect growing up,
showing so much promise as a
left-handed pitcher that
scouts took notice before high
school." - ], - [ - "LSU will stick with a two-
quarterback rotation Saturday
at Auburn, according to Tigers
coach Nick Saban, who seemed
to have some fun telling the
media what he will and won
#39;t discuss Monday." - ], - [ - "Seattle -- - Not so long ago,
the 49ers were inflicting on
other teams the kind of pain
and embarrassment they felt in
their 34-0 loss to the
Seahawks on Sunday." - ], - [ - "London - Manchester City held
fierce crosstown rivals
Manchester United to a 0-0
draw on Sunday, keeping the
Red Devils eleven points
behind leaders Chelsea." - ], - [ - "New Ole Miss head coach Ed
Orgeron, speaking for the
first time since his hiring,
made clear the goal of his
football program. quot;The
goal of this program will be
to go to the Sugar Bowl, quot;
Orgeron said." - ], - [ - "It is much too easy to call
Pedro Martinez the selfish
one, to say he is walking out
on the Red Sox, his baseball
family, for the extra year of
the Mets #39; crazy money." - ], - [ - "It is impossible for young
tennis players today to know
what it was like to be Althea
Gibson and not to be able to
quot;walk in the front door,
quot; Garrison said." - ], - [ - "Here #39;s an obvious word of
advice to Florida athletic
director Jeremy Foley as he
kicks off another search for
the Gators football coach: Get
Steve Spurrier on board." - ], - [ - "Amid the stormy gloom in
Gotham, the rain-idled Yankees
last night had plenty of time
to gather in front of their
televisions and watch the Red
Sox Express roar toward them.
The national telecast might
have been enough to send a
jittery Boss Steinbrenner
searching his Bartlett's
Familiar Quotations for some
quot;Little Engine That Could
quot; metaphor." - ], - [ - "FULHAM fans would have been
singing the late Elvis #39;
hit #39;The wonder of you
#39; to their player Elvis
Hammond. If not for Frank
Lampard spoiling the party,
with his dedication to his
late grandfather." - ], - [ - "A bird #39;s eye view of the
circuit at Shanghai shows what
an event Sunday #39;s Chinese
Grand Prix will be. The course
is arguably one of the best
there is, and so it should be
considering the amount of
money that has been spent on
it." - ], - [ - "AP - Sirius Satellite Radio
signed a deal to air the men's
NCAA basketball tournament
through 2007, the latest move
made in an attempt to draw
customers through sports
programming." - ], - [ - "(Sports Network) - The
inconsistent San Diego Padres
will try for consecutive wins
for the first time since
August 28-29 tonight, when
they begin a huge four-game
set against the Los Angeles
Dodgers at Dodger Stadium." - ], - [ - " NEW YORK (Reuters) - Top seed
Roger Federer survived a
stirring comeback from twice
champion Andre Agassi to reach
the semifinals of the U.S.
Open for the first time on
Thursday, squeezing through
6-3, 2-6, 7-5, 3-6, 6-3." - ], - [ - "SAN FRANCISCO - What Babe Ruth
was to the first half of the
20th century and Hank Aaron
was to the second, Barry Bonds
has become for the home run
generation." - ], - [ - "Birgit Fischer settled for
silver, leaving the 42-year-
old Olympian with two medals
in two days against decidedly
younger competition." - ], - [ - "There was no mystery ... no
secret strategy ... no baited
trap that snapped shut and
changed the course of history
#39;s most lucrative non-
heavyweight fight." - ], - [ - "Notre Dame accepted an
invitation Sunday to play in
the Insight Bowl in Phoenix
against a Pac-10 team on Dec.
28. The Irish (6-5) accepted
the bid a day after losing to
Southern California" - ], - [ - "Greg Anderson has so dominated
Pro Stock this season that his
championship quest has evolved
into a pursuit of NHRA
history. By Bob Hesser, Racers
Edge Photography." - ], - [ - "Michael Powell, chairman of
the FCC, said Wednesday he was
disappointed with ABC for
airing a sexually suggestive
opening to \"Monday Night
Football.\"" - ], - [ - "Former Washington football
coach Rick Neuheisel looked
forward to a return to
coaching Wednesday after being
cleared by the NCAA of
wrongdoing related to his
gambling on basketball games." - ], - [ - "AP - Rutgers basketball player
Shalicia Hurns was suspended
from the team after pleading
guilty to punching and tying
up her roommate during a
dispute over painkilling
drugs." - ], - [ - "This was the event Michael
Phelps didn't really need to
compete in if his goal was to
win eight golds. He probably
would have had a better chance
somewhere else." - ], - [ - "Gabe Kapler became the first
player to leave the World
Series champion Boston Red
Sox, agreeing to a one-year
contract with the Yomiuri
Giants in Tokyo." - ], - [ - "BALI, Indonesia - Svetlana
Kuznetsova, fresh off her
championship at the US Open,
defeated Australian qualifier
Samantha Stosur 6-4, 6-4
Thursday to reach the
quarterfinals of the Wismilak
International." - ], - [ - "AP - Just like the old days in
Dallas, Emmitt Smith made life
miserable for the New York
Giants on Sunday." - ], - [ - "TAMPA, Fla. - Chris Simms
first NFL start lasted 19
plays, and it might be a while
before he plays again for the
Tampa Bay Buccaneers." - ], - [ - "Jarno Trulli made the most of
the conditions in qualifying
to claim pole ahead of Michael
Schumacher, while Fernando
finished third." - ], - [ - "AP - Authorities are
investigating whether bettors
at New York's top thoroughbred
tracks were properly informed
when jockeys came in
overweight at races, a source
familiar with the probe told
The Associated Press." - ], - [ - "Michael Owen scored his first
goal for Real Madrid in a 1-0
home victory over Dynamo Kiev
in the Champions League. The
England striker toe-poked home
Ronaldo #39;s cross in the
35th minute to join the
Russians" - ], - [ - "Jason Giambi has returned to
the New York Yankees'
clubhouse but is still
clueless as to when he will be
able to play again." - ], - [ - "Seventy-five National Hockey
League players met with union
leaders yesterday to get an
update on a lockout that shows
no sign of ending." - ], - [ - "Howard, at 6-4 overall and 3-3
in the Mid-Eastern Athletic
Conference, can clinch a
winning record in the MEAC
with a win over Delaware State
on Saturday." - ], - [ - " ATHENS (Reuters) - Top-ranked
Argentina booked their berth
in the women's hockey semi-
finals at the Athens Olympics
on Friday but defending
champions Australia now face
an obstacle course to qualify
for the medal matches." - ], - [ - "The Notre Dame message boards
are no longer discussing
whether Tyrone Willingham
should be fired. Theyre
already arguing about whether
the next coach should be Barry
Alvarez or Steve Spurrier." - ], - [ - " THOMASTOWN, Ireland (Reuters)
- World number three Ernie
Els overcame difficult weather
conditions to fire a sparkling
eight-under-par 64 and move
two shots clear after two
rounds of the WGC-American
Express Championship Friday." - ], - [ - "Lamar Odom supplemented 20
points with 13 rebounds and
Kobe Bryant added 19 points to
overcome a big night from Yao
Ming as the Los Angeles Lakers
ground out an 84-79 win over
the Rockets in Houston
Saturday." - ], - [ - "It rained Sunday, of course,
and but another soppy, sloppy
gray day at Westside Tennis
Club did nothing to deter
Roger Federer from his
appointed rounds." - ], - [ - "Amelie Mauresmo was handed a
place in the Advanta
Championships final after
Maria Sharapova withdrew from
their semi-final because of
injury." - ], - [ - " EAST RUTHERFORD, New Jersey
(Sports Network) - Retired NBA
center and seven-time All-
Star Alonzo Mourning is going
to give his playing career
one more shot." - ], - [ - "Jordan have confirmed that
Timo Glock will replace
Giorgio Pantano for this
weekend #39;s Chinese GP as
the team has terminated its
contract with Pantano." - ], - [ - "The continuing heartache of
Wake Forest #39;s ACC football
season was best described by
fifth-ranked Florida State
coach Bobby Bowden, after his
Seminoles had edged the
Deacons 20-17 Saturday at
Groves Stadium." - ], - [ - "THE glory days have returned
to White Hart Lane. When Spurs
new first-team coach Martin
Jol promised a return to the
traditions of the 1960s,
nobody could have believed he
was so determined to act so
quickly and so literally." - ], - [ - "AP - When Paula Radcliffe
dropped out of the Olympic
marathon miles from the
finish, she sobbed
uncontrollably. Margaret Okayo
knew the feeling. Okayo pulled
out of the marathon at the
15th mile with a left leg
injury, and she cried, too.
When she watched Radcliffe
quit, Okayo thought, \"Let's
cry together.\"" - ], - [ - " ATLANTA (Sports Network) -
The Atlanta Hawks signed free
agent Kevin Willis on
Wednesday, nearly a decade
after the veteran big man
ended an 11- year stint with
the team." - ], - [ - "When his right-front tire went
flying off early in the Ford
400, the final race of the
NASCAR Nextel Cup Series
season, Kurt Busch, it seemed,
was destined to spend his
offseason" - ], - [ - "Brandon Backe and Woody
Williams pitched well last
night even though neither
earned a win. But their
outings will show up in the
record books." - ], - [ - "The Football Association today
decided not to charge David
Beckham with bringing the game
into disrepute. The FA made
the surprise announcement
after their compliance unit
ruled" - ], - [ - " CRANS-SUR-SIERRE, Switzerland
(Reuters) - World number
three Ernie Els says he feels
a failure after narrowly
missing out on three of the
year's four major
championships." - ], - [ - "Striker Bonaventure Kalou
netted twice to send AJ
Auxerre through to the first
knockout round of the UEFA Cup
at the expense of Rangers on
Wednesday." - ], - [ - "THIS weekend sees the
quot;other quot; showdown
between New York and New
England as the Jets and
Patriots clash in a battle of
the unbeaten teams." - ], - [ - "Nicolas Anelka is fit for
Manchester City #39;s
Premiership encounter against
Tottenham at Eastlands, but
the 13million striker will
have to be content with a
place on the bench." - ], - [ - " PITTSBURGH (Reuters) - Ben
Roethlisberger passed for 183
yards and two touchdowns,
Hines Ward scored twice and
the Pittsburgh Steelers
rolled to a convincing 27-3
victory over Philadelphia on
Sunday for their second
straight win against an
undefeated opponent." - ], - [ - " ATHENS (Reuters) - The U.S.
men's basketball team got
their first comfortable win
at the Olympic basketball
tournament Monday, routing
winless Angola 89-53 in their
final preliminary round game." - ], - [ - "Often, the older a pitcher
becomes, the less effective he
is on the mound. Roger Clemens
apparently didn #39;t get that
memo. On Tuesday, the 42-year-
old Clemens won an
unprecedented" - ], - [ - "PSV Eindhoven faces Arsenal at
Highbury tomorrow night on the
back of a free-scoring start
to the season. Despite losing
Mateja Kezman to Chelsea in
the summer, the Dutch side has
scored 12 goals in the first" - ], - [ - "PLAYER OF THE GAME: Playing
with a broken nose, Seattle
point guard Sue Bird set a
WNBA playoff record for
assists with 14, also pumping
in 10 points as the Storm
claimed the Western Conference
title last night." - ], - [ - "Frankfurt - World Cup winners
Brazil were on Monday drawn to
meet European champions
Greece, Gold Cup winners
Mexico and Asian champions
Japan at the 2005
Confederations Cup." - ], - [ - "Third baseman Vinny Castilla
said he fits fine with the
Colorado youth movement, even
though he #39;ll turn 38 next
season and the Rockies are
coming off the second-worst" - ], - [ - "(Sports Network) - Two of the
top teams in the American
League tangle in a possible
American League Division
Series preview tonight, as the
West-leading Oakland Athletics
host the wild card-leading
Boston Red Sox for the first
of a three-game set at the" - ], - [ - "Inverness Caledonian Thistle
appointed Craig Brewster as
its new manager-player
Thursday although he #39;s
unable to play for the team
until January." - ], - [ - "ATHENS, Greece -- Alan Shearer
converted an 87th-minute
penalty to give Newcastle a
1-0 win over Panionios in
their UEFA Cup Group D match." - ], - [ - "Derek Jeter turned a season
that started with a terrible
slump into one of the best in
his accomplished 10-year
career. quot;I don #39;t
think there is any question,
quot; the New York Yankees
manager said." - ], - [ - "China's Guo Jingjing easily
won the women's 3-meter
springboard last night, and Wu
Minxia made it a 1-2 finish
for the world's diving
superpower, taking the silver." - ], - [ - "GREEN BAY, Wisconsin (Ticker)
-- Brett Favre will be hoping
his 200th consecutive start
turns out better than his last
two have against the St." - ], - [ - "When an NFL team opens with a
prolonged winning streak,
former Miami Dolphins coach
Don Shula and his players from
the 17-0 team of 1972 root
unabashedly for the next
opponent." - ], - [ - "MIANNE Bagger, the transsexual
golfer who prompted a change
in the rules to allow her to
compete on the professional
circuit, made history
yesterday by qualifying to
play full-time on the Ladies
European Tour." - ], - [ - "Great Britain #39;s gold medal
tally now stands at five after
Leslie Law was handed the
individual three day eventing
title - in a courtroom." - ], - [ - "Mayor Tom Menino must be
proud. His Boston Red Sox just
won their first World Series
in 86 years and his Hyde Park
Blue Stars yesterday clinched
their first Super Bowl berth
in 32 years, defeating
O'Bryant, 14-0. Who would have
thought?" - ], - [ - "South Korea have appealed to
sport #39;s supreme legal body
in an attempt to award Yang
Tae-young the Olympic
gymnastics all-round gold
medal after a scoring error
robbed him of the title in
Athens." - ], - [ - "AP - Johan Santana had an
early lead and was well on his
way to his 10th straight win
when the rain started to fall." - ], - [ - "ATHENS-In one of the biggest
shocks in Olympic judo
history, defending champion
Kosei Inoue was defeated by
Dutchman Elco van der Geest in
the men #39;s 100-kilogram
category Thursday." - ], - [ - "NBC is adding a 5-second delay
to its Nascar telecasts after
Dale Earnhardt Jr. used a
vulgarity during a postrace
interview last weekend." - ], - [ - "San Francisco Giants
outfielder Barry Bonds, who
became the third player in
Major League Baseball history
to hit 700 career home runs,
won the National League Most
Valuable Player Award" - ], - [ - "Portsmouth chairman Milan
Mandaric said on Tuesday that
Harry Redknapp, who resigned
as manager last week, was
innocent of any wrong-doing
over agent or transfer fees." - ], - [ - "This record is for all the
little guys, for all the
players who have to leg out
every hit instead of taking a
relaxing trot around the
bases, for all the batters
whose muscles aren #39;t" - ], - [ - "Charlie Hodgson #39;s record-
equalling performance against
South Africa was praised by
coach Andy Robinson after the
Sale flyhalf scored 27 points
in England #39;s 32-16 victory
here at Twickenham on
Saturday." - ], - [ - "BASEBALL Atlanta (NL):
Optioned P Roman Colon to
Greenville (Southern);
recalled OF Dewayne Wise from
Richmond (IL). Boston (AL):
Purchased C Sandy Martinez
from Cleveland (AL) and
assigned him to Pawtucket
(IL). Cleveland (AL): Recalled
OF Ryan Ludwick from Buffalo
(IL). Chicago (NL): Acquired
OF Ben Grieve from Milwaukee
(NL) for player to be named
and cash; acquired C Mike ..." - ], - [ - "BRONX, New York (Ticker) --
Kelvim Escobar was the latest
Anaheim Angels #39; pitcher to
subdue the New York Yankees.
Escobar pitched seven strong
innings and Bengie Molina tied
a career-high with four hits,
including" - ], - [ - "Sep 08 - Vijay Singh revelled
in his status as the new world
number one after winning the
Deutsche Bank Championship by
three shots in Boston on
Monday." - ], - [ - "Many people in golf are asking
that today. He certainly wasn
#39;t A-list and he wasn #39;t
Larry Nelson either. But you
couldn #39;t find a more solid
guy to lead the United States
into Ireland for the 2006
Ryder Cup Matches." - ], - [ - "Australian Stuart Appleby, who
was the joint second-round
leader, returned a two-over 74
to drop to third at three-
under while American Chris
DiMarco moved into fourth with
a round of 69." - ], - [ - "With a doubleheader sweep of
the Minnesota Twins, the New
York Yankees moved to the
verge of clinching their
seventh straight AL East
title." - ], - [ - "Athens, Greece (Sports
Network) - The first official
track event took place this
morning and Italy #39;s Ivano
Brugnetti won the men #39;s
20km walk at the Summer
Olympics in Athens." - ], - [ - "Champions Arsenal opened a
five-point lead at the top of
the Premier League after a 4-0
thrashing of Charlton Athletic
at Highbury Saturday." - ], - [ - "The Redskins and Browns have
traded field goals and are
tied, 3-3, in the first
quarter in Cleveland." - ], - [ - "Forget September call-ups. The
Red Sox may tap their minor
league system for an extra
player or two when the rules
allow them to expand their
25-man roster Wednesday, but
any help from the farm is
likely to pale against the
abundance of talent they gain
from the return of numerous
players, including Trot Nixon
, from the disabled list." - ], - [ - "THOUSAND OAKS -- Anonymity is
only a problem if you want it
to be, and it is obvious Vijay
Singh doesn #39;t want it to
be. Let others chase fame." - ], - [ - "David Coulthard #39;s season-
long search for a Formula One
drive next year is almost
over. Negotiations between Red
Bull Racing and Coulthard, who
tested for the Austrian team
for the first time" - ], - [ - "AP - Southern California
tailback LenDale White
remembers Justin Holland from
high school. The Colorado
State quarterback made quite
an impression." - ], - [ - "TIM HENMAN last night admitted
all of his energy has been
drained away as he bowed out
of the Madrid Masters. The top
seed, who had a blood test on
Wednesday to get to the bottom
of his fatigue, went down" - ], - [ - "ATHENS -- US sailors needed a
big day to bring home gold and
bronze medals from the sailing
finale here yesterday. But
rolling the dice on windshifts
and starting tactics backfired
both in Star and Tornado
classes, and the Americans had
to settle for a single silver
medal." - ], - [ - "AP - Cavaliers forward Luke
Jackson was activated
Wednesday after missing five
games because of tendinitis in
his right knee. Cleveland also
placed forward Sasha Pavlovic
on the injured list." - ], - [ - "The Brisbane Lions #39;
football manager stepped out
of the changerooms just before
six o #39;clock last night and
handed one of the milling
supporters a six-pack of beer." - ], - [ - "Cavaliers owner Gordon Gund is
in quot;serious quot;
negotiations to sell the NBA
franchise, which has enjoyed a
dramatic financial turnaround
since the arrival of star
LeBron James." - ], - [ - "After two days of gloom, China
was back on the winning rails
on Thursday with Liu Chunhong
winning a weightlifting title
on her record-shattering binge
and its shuttlers contributing
two golds in the cliff-hanging
finals." - ], - [ - "One question that arises
whenever a player is linked to
steroids is, \"What would he
have done without them?\"
Baseball history whispers an
answer." - ], - [ - "The second round of the
Canadian Open golf tournament
continues Saturday Glenn Abbey
Golf Club in Oakville,
Ontario, after play was
suspended late Friday due to
darkness." - ], - [ - "A massive plan to attract the
2012 Summer Olympics to New
York, touting the city's
diversity, financial and media
power, was revealed Wednesday." - ], - [ - "NICK Heidfeld #39;s test with
Williams has been brought
forward after BAR blocked
plans for Anthony Davidson to
drive its Formula One rival
#39;s car." - ], - [ - "Grace Park closed with an
eagle and two birdies for a
7-under-par 65 and a two-
stroke lead after three rounds
of the Wachovia LPGA Classic
on Saturday." - ], - [ - "Carlos Beltran drives in five
runs to carry the Astros to a
12-3 rout of the Braves in
Game 5 of their first-round NL
playoff series." - ], - [ - "Since Lennox Lewis #39;s
retirement, the heavyweight
division has been knocked for
having more quantity than
quality. Eight heavyweights on
Saturday night #39;s card at
Madison Square Garden hope to
change that perception, at
least for one night." - ], - [ - "Tim Duncan had 17 points and
10 rebounds, helping the San
Antonio Spurs to a 99-81
victory over the New York
Kicks. This was the Spurs
fourth straight win this
season." - ], - [ - "Nagpur: India suffered a
double blow even before the
first ball was bowled in the
crucial third cricket Test
against Australia on Tuesday
when captain Sourav Ganguly
and off spinner Harbhajan
Singh were ruled out of the
match." - ], - [ - "AP - Former New York Yankees
hitting coach Rick Down was
hired for the same job by the
Mets on Friday, reuniting him
with new manager Willie
Randolph." - ], - [ - "FILDERSTADT (Germany) - Amelie
Mauresmo and Lindsay Davenport
took their battle for the No.
1 ranking and Porsche Grand
Prix title into the semi-
finals with straight-sets
victories on Friday." - ], - [ - "Carter returned, but it was
running back Curtis Martin and
the offensive line that put
the Jets ahead. Martin rushed
for all but 10 yards of a
45-yard drive that stalled at
the Cardinals 10." - ], - [ - "DENVER (Ticker) -- Jake
Plummer more than made up for
a lack of a running game.
Plummer passed for 294 yards
and two touchdowns as the
Denver Broncos posted a 23-13
victory over the San Diego
Chargers in a battle of AFC
West Division rivals." - ], - [ - "It took all of about five
minutes of an introductory
press conference Wednesday at
Heritage Hall for USC
basketball to gain something
it never really had before." - ], - [ - "AP - The Boston Red Sox looked
at the out-of-town scoreboard
and could hardly believe what
they saw. The New York Yankees
were trailing big at home
against the Cleveland Indians
in what would be the worst
loss in the 101-year history
of the storied franchise." - ], - [ - "The Red Sox will either
complete an amazing comeback
as the first team to rebound
from a 3-0 deficit in
postseason history, or the
Yankees will stop them." - ], - [ - "The Red Sox have reached
agreement with free agent
pitcher Matt Clement yesterday
on a three-year deal that will
pay him around \\$25 million,
his agent confirmed yesterday." - ], - [ - "HEN Manny Ramirez and David
Ortiz hit consecutive home
runs Sunday night in Chicago
to put the Red Sox ahead,
there was dancing in the
streets in Boston." - ], - [ - "MIAMI -- Bryan Randall grabbed
a set of Mardi Gras beads and
waved them aloft, while his
teammates exalted in the
prospect of a trip to New
Orleans." - ], - [ - "TORONTO (CP) - With an injured
Vince Carter on the bench, the
Toronto Raptors dropped their
sixth straight game Friday,
101-87 to the Denver Nuggets." - ], - [ - "While not quite a return to
glory, Monday represents the
Redskins' return to the
national consciousness." - ], - [ - "Vijay Singh has won the US PGA
Tour player of the year award
for the first time, ending
Tiger Woods #39;s five-year
hold on the honour." - ], - [ - "England got strikes from
sparkling debut starter
Jermain Defoe and Michael Owen
to defeat Poland in a Uefa
World Cup qualifier in
Chorzow." - ], - [ - "Titleholder Ernie Els moved
within sight of a record sixth
World Match Play title on
Saturday by solving a putting
problem to overcome injured
Irishman Padraig Harrington 5
and 4." - ], - [ - "If the Washington Nationals
never win a pennant, they have
no reason to ever doubt that
DC loves them. Yesterday, the
District City Council
tentatively approved a tab for
a publicly financed ballpark
that could amount to as much
as \\$630 million." - ], - [ - "Defensive back Brandon
Johnson, who had two
interceptions for Tennessee at
Mississippi, was suspended
indefinitely Monday for
violation of team rules." - ], - [ - "Points leader Kurt Busch spun
out and ran out of fuel, and
his misfortune was one of the
reasons crew chief Jimmy
Fennig elected not to pit with
20 laps to go." - ], - [ - "(CP) - The NHL all-star game
hasn #39;t been cancelled
after all. It #39;s just been
moved to Russia. The agent for
New York Rangers winger
Jaromir Jagr confirmed Monday
that the Czech star had joined
Omsk Avangard" - ], - [ - "HERE in the land of myth, that
familiar god of sports --
karma -- threw a bolt of
lightning into the Olympic
stadium yesterday. Marion
Jones lunged desperately with
her baton in the 4 x 100m
relay final, but couldn #39;t
reach her target." - ], - [ - "AP - Kenny Rogers lost at the
Coliseum for the first time in
more than 10 years, with Bobby
Crosby's three-run double in
the fifth inning leading the
Athletics to a 5-4 win over
the Texas Rangers on Thursday." - ], - [ - "Dambulla, Sri Lanka - Kumar
Sangakkara and Avishka
Gunawardene slammed impressive
half-centuries to help an
under-strength Sri Lanka crush
South Africa by seven wickets
in the fourth one-day
international here on
Saturday." - ], - [ - "Fresh off being the worst team
in baseball, the Arizona
Diamondbacks set a new record
this week: fastest team to
both hire and fire a manager." - ], - [ - "Nebraska head coach Bill
Callahan runs off the field at
halftime of the game against
Baylor in Lincoln, Neb.,
Saturday, Oct. 16, 2004." - ], - [ - " EAST RUTHERFORD, N.J. (Sports
Network) - The Toronto
Raptors have traded All-Star
swingman Vince Carter to the
New Jersey Nets in exchange
for center Alonzo Mourning,
forward Eric Williams,
center/forward Aaron Williams
and two first- round draft
picks." - ], - [ - "What riot? quot;An Argentine
friend of mine was a little
derisive of the Pacers-Pistons
eruption, quot; says reader
Mike Gaynes. quot;He snorted,
#39;Is that what Americans
call a riot?" - ], - [ - "All season, Chris Barnicle
seemed prepared for just about
everything, but the Newton
North senior was not ready for
the hot weather he encountered
yesterday in San Diego at the
Footlocker Cross-Country
National Championships. Racing
in humid conditions with
temperatures in the 70s, the
Massachusetts Division 1 state
champion finished sixth in 15
minutes 34 seconds in the
5-kilometer race. ..." - ], - [ - "AP - Coach Tyrone Willingham
was fired by Notre Dame on
Tuesday after three seasons in
which he failed to return one
of the nation's most storied
football programs to
prominence." - ], - [ - "COLLEGE PARK, Md. -- Joel
Statham completed 18 of 25
passes for 268 yards and two
touchdowns in No. 23
Maryland's 45-22 victory over
Temple last night, the
Terrapins' 12th straight win
at Byrd Stadium." - ], - [ - "Manchester United boss Sir
Alex Ferguson wants the FA to
punish Arsenal good guy Dennis
Bergkamp for taking a swing at
Alan Smith last Sunday." - ], - [ - "Published reports say Barry
Bonds has testified that he
used a clear substance and a
cream given to him by a
trainer who was indicted in a
steroid-distribution ring." - ], - [ - " ATHENS (Reuters) - Christos
Angourakis added his name to
Greece's list of Paralympic
medal winners when he claimed
a bronze in the T53 shot put
competition Thursday." - ], - [ - "Jay Fiedler threw for one
touchdown, Sage Rosenfels
threw for another and the
Miami Dolphins got a victory
in a game they did not want to
play, beating the New Orleans
Saints 20-19 Friday night." - ], - [ - " NEW YORK (Reuters) - Terrell
Owens scored three touchdowns
and the Philadelphia Eagles
amassed 35 first-half points
on the way to a 49-21
drubbing of the Dallas Cowboys
in Irving, Texas, Monday." - ], - [ - "A late strike by Salomon Kalou
sealed a 2-1 win for Feyenoord
over NEC Nijmegen, while
second placed AZ Alkmaar
defeated ADO Den Haag 2-0 in
the Dutch first division on
Sunday." - ], - [ - "What a disgrace Ron Artest has
become. And the worst part is,
the Indiana Pacers guard just
doesn #39;t get it. Four days
after fueling one of the
biggest brawls in the history
of pro sports, Artest was on
national" - ], - [ - "Jenson Button has revealed
dissatisfaction with the way
his management handled a
fouled switch to Williams. Not
only did the move not come
off, his reputation may have
been irreparably damaged amid
news headline" - ], - [ - "Redknapp and his No2 Jim Smith
resigned from Portsmouth
yesterday, leaving
controversial new director
Velimir Zajec in temporary
control." - ], - [ - "As the season winds down for
the Frederick Keys, Manager
Tom Lawless is starting to
enjoy the progress his
pitching staff has made this
season." - ], - [ - "Britain #39;s Bradley Wiggins
won the gold medal in men
#39;s individual pursuit
Saturday, finishing the
4,000-meter final in 4:16." - ], - [ - "And when David Akers #39;
50-yard field goal cleared the
crossbar in overtime, they did
just that. They escaped a
raucous Cleveland Browns
Stadium relieved but not
broken, tested but not
cracked." - ], - [ - "San Antonio, TX (Sports
Network) - Dean Wilson shot a
five-under 65 on Friday to
move into the lead at the
halfway point of the Texas
Open." - ], - [ - "Now that Chelsea have added
Newcastle United to the list
of clubs that they have given
what for lately, what price
Jose Mourinho covering the
Russian-funded aristocrats of
west London in glittering
glory to the tune of four
trophies?" - ], - [ - "AP - Former Seattle Seahawks
running back Chris Warren has
been arrested in Virginia on a
federal warrant, accused of
failing to pay #36;137,147 in
child support for his two
daughters in Washington state." - ], - [ - "It #39;s official: US Open had
never gone into the third
round with only two American
men, including the defending
champion, Andy Roddick." - ], - [ - "WASHINGTON, Aug. 17
(Xinhuanet) -- England coach
Sven-Goran Eriksson has urged
the international soccer
authorities to preserve the
health of the world superstar
footballers for major
tournaments, who expressed his
will in Slaley of England on
Tuesday ..." - ], - [ - "Juventus coach Fabio Capello
has ordered his players not to
kick the ball out of play when
an opponent falls to the
ground apparently hurt because
he believes some players fake
injury to stop the match." - ], - [ - "You #39;re angry. You want to
lash out. The Red Sox are
doing it to you again. They
#39;re blowing a playoff
series, and to the Yankees no
less." - ], - [ - "TORONTO -- There is no
mystique to it anymore,
because after all, the
Russians have become commoners
in today's National Hockey
League, and Finns, Czechs,
Slovaks, and Swedes also have
been entrenched in the
Original 30 long enough to
turn the ongoing World Cup of
Hockey into a protracted
trailer for the NHL season." - ], - [ - "Indianapolis, IN (Sports
Network) - The Indiana Pacers
try to win their second
straight game tonight, as they
host Kevin Garnett and the
Minnesota Timberwolves in the
third of a four-game homestand
at Conseco Fieldhouse." - ], - [ - "It is a team game, this Ryder
Cup stuff that will commence
Friday at Oakland Hills
Country Club. So what are the
teams? For the Americans,
captain Hal Sutton isn't
saying." - ], - [ - "MANCHESTER United today
dramatically rejected the
advances of Malcolm Glazer,
the US sports boss who is
mulling an 825m bid for the
football club." - ], - [ - "As usual, the Big Ten coaches
were out in full force at
today #39;s Big Ten
Teleconference. Both OSU head
coach Jim Tressel and Iowa
head coach Kirk Ferentz
offered some thoughts on the
upcoming game between OSU" - ], - [ - "New York Knicks #39; Stephon
Marbury (3) fires over New
Orleans Hornets #39; Dan
Dickau (2) during the second
half in New Orleans Wednesday
night, Dec. 8, 2004." - ], - [ - "At the very moment when the
Red Sox desperately need
someone slightly larger than
life to rally around, they
suddenly have the man for the
job: Thrilling Schilling." - ], - [ - "Dale Earnhardt Jr, right,
talks with Matt Kenseth, left,
during a break in practice at
Lowe #39;s Motor Speedway in
Concord, NC, Thursday Oct. 14,
2004 before qualifying for
Saturday #39;s UAW-GM Quality
500 NASCAR Nextel Cup race." - ], - [ - "Several starting spots may
have been usurped or at least
threatened after relatively
solid understudy showings
Sunday, but few players
welcome the kind of shot
delivered to Oakland" - ], - [ - "TEMPE, Ariz. -- Neil Rackers
kicked four field goals and
the Arizona Cardinals stifled
rookie Chris Simms and the
rest of the Tampa Bay offense
for a 12-7 victory yesterday
in a matchup of two sputtering
teams out of playoff
contention. Coach Jon Gruden's
team lost its fourth in a row
to finish 5-11, Tampa Bay's
worst record since going ..." - ], - [ - "ATHENS France, Britain and the
United States issued a joint
challenge Thursday to Germany
#39;s gold medal in equestrian
team three-day eventing." - ], - [ - "ATLANTA - Who could have
imagined Tommy Tuberville in
this position? Yet there he
was Friday, standing alongside
the SEC championship trophy,
posing for pictures and
undoubtedly chuckling a bit on
the inside." - ], - [ - "John Thomson threw shutout
ball for seven innings
Wednesday night in carrying
Atlanta to a 2-0 blanking of
the New York Mets. New York
lost for the 21st time in 25
games on the" - ], - [ - "Vikram Solanki beat the rain
clouds to register his second
one-day international century
as England won the third one-
day international to wrap up a
series victory." - ], - [ - "A three-touchdown point spread
and a recent history of late-
season collapses had many
thinking the UCLA football
team would provide little
opposition to rival USC #39;s
march to the BCS-championship
game at the Orange Bowl." - ], - [ - " PHOENIX (Sports Network) -
Free agent third baseman Troy
Glaus is reportedly headed to
the Arizona Diamondbacks." - ], - [ - "Third-string tailback Chris
Markey ran for 131 yards to
lead UCLA to a 34-26 victory
over Oregon on Saturday.
Markey, playing because of an
injury to starter Maurice
Drew, also caught five passes
for 84 yards" - ], - [ - "Jay Payton #39;s three-run
homer led the San Diego Padres
to a 5-1 win over the San
Francisco Giants in National
League play Saturday, despite
a 701st career blast from
Barry Bonds." - ], - [ - "The optimists among Rutgers
fans were delighted, but the
Scarlet Knights still gave the
pessimists something to worry
about." - ], - [ - "Perhaps the sight of Maria
Sharapova opposite her tonight
will jog Serena Williams #39;
memory. Wimbledon. The final.
You and Maria." - ], - [ - "Five days after making the
putt that won the Ryder Cup,
Colin Montgomerie looked set
to miss the cut at a European
PGA tour event." - ], - [ - "Karachi - Captain Inzamam ul-
Haq and coach Bob Woolmer came
under fire on Thursday for
choosing to bat first on a
tricky pitch after Pakistan
#39;s humiliating defeat in
the ICC Champions Trophy semi-
final." - ], - [ - "Scottish champions Celtic saw
their three-year unbeaten home
record in Europe broken
Tuesday as they lost 3-1 to
Barcelona in the Champions
League Group F opener." - ], - [ - "AP - Andy Roddick searched out
Carlos Moya in the throng of
jumping, screaming Spanish
tennis players, hoping to
shake hands." - ], - [ - "ENGLAND captain and Real
Madrid midfielder David
Beckham has played down
speculation that his club are
moving for England manager
Sven-Goran Eriksson." - ], - [ - "AFP - National Basketball
Association players trying to
win a fourth consecutive
Olympic gold medal for the
United States have gotten the
wake-up call that the \"Dream
Team\" days are done even if
supporters have not." - ], - [ - "England will be seeking their
third clean sweep of the
summer when the NatWest
Challenge against India
concludes at Lord #39;s. After
beating New Zealand and West
Indies 3-0 and 4-0 in Tests,
they have come alive" - ], - [ - "AP - Mark Richt knows he'll
have to get a little creative
when he divvies up playing
time for Georgia's running
backs next season. Not so on
Saturday. Thomas Brown is the
undisputed starter for the
biggest game of the season." - ], - [ - "Michael Phelps, the six-time
Olympic champion, issued an
apology yesterday after being
arrested and charged with
drunken driving in the United
States." - ], - [ - "ATHENS Larry Brown, the US
coach, leaned back against the
scorer #39;s table, searching
for support on a sinking ship.
His best player, Tim Duncan,
had just fouled out, and the
options for an American team
that" - ], - [ - "Spain have named an unchanged
team for the Davis Cup final
against the United States in
Seville on 3-5 December.
Carlos Moya, Juan Carlos
Ferrero, Rafael Nadal and
Tommy Robredo will take on the
US in front of 22,000 fans at
the converted Olympic stadium." - ], - [ - "Last Tuesday night, Harvard
knocked off rival Boston
College, which was ranked No.
1 in the country. Last night,
the Crimson knocked off
another local foe, Boston
University, 2-1, at Walter
Brown Arena, which marked the
first time since 1999 that
Harvard had beaten them both
in the same season." - ], - [ - "About 500 prospective jurors
will be in an Eagle, Colorado,
courtroom Friday, answering an
82-item questionnaire in
preparation for the Kobe
Bryant sexual assault trial." - ], - [ - "BEIJING The NBA has reached
booming, basketball-crazy
China _ but the league doesn
#39;t expect to make any money
soon. The NBA flew more than
100 people halfway around the
world for its first games in
China, featuring" - ], - [ - " NEW YORK (Reuters) - Curt
Schilling pitched 6 2/3
innings and Manny Ramirez hit
a three-run homer in a seven-
run fourth frame to lead the
Boston Red Sox to a 9-3 win
over the host Anaheim Angels
in their American League
Divisional Series opener
Tuesday." - ], - [ - "AP - The game between the
Minnesota Twins and the New
York Yankees on Tuesday night
was postponed by rain." - ], - [ - "PARIS The verdict is in: The
world #39;s greatest race car
driver, the champion of
champions - all disciplines
combined - is Heikki
Kovalainen." - ], - [ - "Pakistan won the toss and
unsurprisingly chose to bowl
first as they and West Indies
did battle at the Rose Bowl
today for a place in the ICC
Champions Trophy final against
hosts England." - ], - [ - "AP - Boston Red Sox center
fielder Johnny Damon is having
a recurrence of migraine
headaches that first bothered
him after a collision in last
year's playoffs." - ], - [ - "Felix Cardenas of Colombia won
the 17th stage of the Spanish
Vuelta cycling race Wednesday,
while defending champion
Roberto Heras held onto the
overall leader #39;s jersey
for the sixth day in a row." - ], - [ - "Established star Landon
Donovan and rising sensation
Eddie Johnson carried the
United States into the
regional qualifying finals for
the 2006 World Cup in emphatic
fashion Wednesday night." - ], - [ - "Wizards coach Eddie Jordan
says the team is making a
statement that immaturity will
not be tolerated by suspending
Kwame Brown one game for not
taking part in a team huddle
during a loss to Denver." - ], - [ - "AP - Andy Roddick has yet to
face a challenge in his U.S.
Open title defense. He beat
No. 18 Tommy Robredo of Spain
6-3, 6-2, 6-4 Tuesday night to
move into the quarterfinals
without having lost a set." - ], - [ - "Now that hell froze over in
Boston, New England braces for
its rarest season -- a winter
of content. Red Sox fans are
adrift from the familiar
torture of the past." - ], - [ - "THE South Africans have called
the Wallabies scrum cheats as
a fresh round of verbal
warfare opened in the Republic
last night." - ], - [ - "An overwhelming majority of
NHL players who expressed
their opinion in a poll said
they would not support a
salary cap even if it meant
saving a season that was
supposed to have started Oct.
13." - ], - [ - "Tony Eury Sr. oversees Dale
Earnhardt Jr. on the
racetrack, but Sunday he
extended his domain to Victory
Lane. Earnhardt was unbuckling
to crawl out of the No." - ], - [ - "(Sports Network) - The New
York Yankees try to move one
step closer to a division
title when they conclude their
critical series with the
Boston Red Sox at Fenway Park." - ], - [ - "Kurt Busch claimed a stake in
the points lead in the NASCAR
Chase for the Nextel Cup
yesterday, winning the
Sylvania 300 at New Hampshire
International Speedway." - ], - [ - "LOUDON, NH - As this
newfangled stretch drive for
the Nextel Cup championship
ensues, Jeff Gordon has to be
considered the favorite for a
fifth title." - ], - [ - "By nick giongco. YOU KNOW you
have reached the status of a
boxing star when ring
announcer extraordinaire
Michael Buffer calls out your
name in his trademark booming
voice during a high-profile
event like yesterday" - ], - [ - "AP - Even with a big lead,
Eric Gagne wanted to pitch in
front of his hometown fans one
last time." - ], - [ - "ATHENS, Greece -- Larry Brown
was despondent, the head of
the US selection committee was
defensive and an irritated
Allen Iverson was hanging up
on callers who asked what went
wrong." - ], - [ - "AP - Courtney Brown refuses to
surrender to injuries. He's
already planning another
comeback." - ], - [ - "It took an off-the-cuff
reference to a serial
murderer/cannibal to punctuate
the Robby Gordon storyline.
Gordon has been vilified by
his peers and put on probation" - ], - [ - "By BOBBY ROSS JR. Associated
Press Writer. play the Atlanta
Hawks. They will be treated to
free food and drink and have.
their pictures taken with
Mavericks players, dancers and
team officials." - ], - [ - "ARSENAL #39;S Brazilian World
Cup winning midfielder
Gilberto Silva is set to be
out for at least a month with
a back injury, the Premiership
leaders said." - ], - [ - "INDIANAPOLIS -- With a package
of academic reforms in place,
the NCAA #39;s next crusade
will address what its
president calls a dangerous
drift toward professionalism
and sports entertainment." - ], - [ - "AP - It was difficult for
Southern California's Pete
Carroll and Oklahoma's Bob
Stoops to keep from repeating
each other when the two
coaches met Thursday." - ], - [ - "Andy Roddick, along with
Olympic silver medalist Mardy
Fish and the doubles pair of
twins Bob and Mike Bryan will
make up the US team to compete
with Belarus in the Davis Cup,
reported CRIENGLISH." - ], - [ - "While the world #39;s best
athletes fight the noble
Olympic battle in stadiums and
pools, their fans storm the
streets of Athens, turning the
Greek capital into a huge
international party every
night." - ], - [ - "EVERTON showed they would not
be bullied into selling Wayne
Rooney last night by rejecting
a 23.5million bid from
Newcastle - as Manchester
United gamble on Goodison
#39;s resolve to keep the
striker." - ], - [ - "After months of legal
wrangling, the case of
<em>People v. Kobe Bean
Bryant</em> commences on
Friday in Eagle, Colo., with
testimony set to begin next
month." - ], - [ - "Lyon coach Paul le Guen has
admitted his side would be
happy with a draw at Old
Trafford on Tuesday night. The
three-times French champions
have assured themselves of
qualification for the
Champions League" - ], - [ - "Reuters - Barry Bonds failed
to collect a hit in\\his bid to
join the 700-homer club, but
he did score a run to\\help the
San Francisco Giants edge the
host Milwaukee Brewers\\3-2 in
National League action on
Tuesday." - ], - [ - "After waiting an entire summer
for the snow to fall and
Beaver Creek to finally open,
skiers from around the planet
are coming to check out the
Birds of Prey World Cup action
Dec. 1 - 5. Although everyones" - ], - [ - "I confess that I am a complete
ignoramus when it comes to
women #39;s beach volleyball.
In fact, I know only one thing
about the sport - that it is
the supreme aesthetic
experience available on planet
Earth." - ], - [ - "Manchester United Plc may
offer US billionaire Malcolm
Glazer a seat on its board if
he agrees to drop a takeover
bid for a year, the Observer
said, citing an unidentified
person in the soccer industry." - ], - [ - "For a guy who spent most of
his first four professional
seasons on the disabled list,
Houston Astros reliever Brad
Lidge has developed into quite
the ironman these past two
days." - ], - [ - "Former Bengal Corey Dillon
found his stride Sunday in his
second game for the New
England Patriots. Dillon
gained 158 yards on 32 carries
as the Patriots beat the
Arizona Cardinals, 23-12, for
their 17th victory in a row." - ], - [ - "If legend is to be believed,
the end of a victorious war
was behind Pheidippides #39;
trek from Marathon to Athens
2,500 years ago." - ], - [ - " BEIJING (Reuters) - Wimbledon
champion Maria Sharapova
demolished fellow Russian
Tatiana Panova 6-1, 6-1 to
advance to the quarter-finals
of the China Open on
Wednesday." - ], - [ - "Padres general manager Kevin
Towers called Expos general
manager Omar Minaya on
Thursday afternoon and told
him he needed a shortstop
because Khalil Greene had
broken his" - ], - [ - "Motorsport.com. Nine of the
ten Formula One teams have
united to propose cost-cutting
measures for the future, with
the notable exception of
Ferrari." - ], - [ - "Steve Francis and Shaquille O
#39;Neal enjoyed big debuts
with their new teams. Kobe
Bryant found out he can #39;t
carry the Lakers all by
himself." - ], - [ - "Australia, by winning the
third Test at Nagpur on
Friday, also won the four-
match series 2-0 with one
match to go. Australia had
last won a Test series in
India way back in December
1969 when Bill Lawry #39;s
team beat Nawab Pataudi #39;s
Indian team 3-1." - ], - [ - "Final Score: Connecticut 61,
New York 51 New York, NY
(Sports Network) - Nykesha
Sales scored 15 points to lead
Connecticut to a 61-51 win
over New York in Game 1 of
their best-of-three Eastern
Conference Finals series at
Madison Square Garden." - ], - [ - "Charlotte, NC -- LeBron James
poured in a game-high 19
points and Jeff McInnis scored
18 as the Cleveland Cavaliers
routed the Charlotte Bobcats,
106-89, at the Charlotte
Coliseum." - ], - [ - "Lehmann, who was at fault in
two matches in the tournament
last season, was blundering
again with the German set to
take the rap for both Greek
goals." - ], - [ - "Jeju Island, South Korea
(Sports Network) - Grace Park
and Carin Koch posted matching
rounds of six-under-par 66 on
Friday to share the lead after
the first round of the CJ Nine
Bridges Classic." - ], - [ - "Benfica and Real Madrid set
the standard for soccer
success in Europe in the late
1950s and early #39;60s. The
clubs have evolved much
differently, but both have
struggled" - ], - [ - "Pakistan are closing in fast
on Sri Lanka #39;s first
innings total after impressing
with both ball and bat on the
second day of the opening Test
in Faisalabad." - ], - [ - "Ron Artest has been hit with a
season long suspension,
unprecedented for the NBA
outside doping cases; Stephen
Jackson banned for 30 games;
Jermaine O #39;Neal for 25
games and Anthony Johnson for
five." - ], - [ - "Three shots behind Grace Park
with five holes to go, six-
time LPGA player of the year
Sorenstam rallied with an
eagle, birdie and three pars
to win her fourth Samsung
World Championship by three
shots over Park on Sunday." - ], - [ - "NSW Rugby CEO Fraser Neill
believes Waratah star Mat
Rogers has learned his lesson
after he was fined and ordered
to do community service
following his controversial
comments about the club rugby
competition." - ], - [ - "ATHENS: China, the dominant
force in world diving for the
best part of 20 years, won six
out of eight Olympic titles in
Athens and prompted
speculation about a clean
sweep when they stage the
Games in Beijing in 2008." - ], - [ - "The Houston Astros won their
19th straight game at home and
are one game from winning
their first playoff series in
42 years." - ], - [ - "The eighth-seeded American
fell to sixth-seeded Elena
Dementieva of Russia, 0-6 6-2
7-6 (7-5), on Friday - despite
being up a break on four
occasions in the third set." - ], - [ - "TUCSON, Arizona (Ticker) --
No. 20 Arizona State tries to
post its first three-game
winning streak over Pac-10
Conference rival Arizona in 26
years when they meet Friday." - ], - [ - "AP - Phillip Fulmer kept his
cool when starting center
Jason Respert drove off in the
coach's golf cart at practice." - ], - [ - "GOALS from Wayne Rooney and
Ruud van Nistelrooy gave
Manchester United the win at
Newcastle. Alan Shearer
briefly levelled matters for
the Magpies but United managed
to scrape through." - ], - [ - "AP - Two-time U.S. Open
doubles champion Max Mirnyi
will lead the Belarus team
that faces the United States
in the Davis Cup semifinals in
Charleston later this month." - ], - [ - "AP - New York Jets safety Erik
Coleman got his souvenir
football from the equipment
manager and held it tightly." - ], - [ - "Ivan Hlinka coached the Czech
Republic to the hockey gold
medal at the 1998 Nagano
Olympics and became the coach
of the Pittsburgh Penguins two
years later." - ], - [ - "AUBURN - Ah, easy street. No
game this week. Light
practices. And now Auburn is
being touted as the No. 3 team
in the Bowl Championship
Series standings." - ], - [ - "Portsmouth #39;s Harry
Redknapp has been named as the
Barclays manager of the month
for October. Redknapp #39;s
side were unbeaten during the
month and maintained an
impressive climb to ninth in
the Premiership." - ], - [ - "Three directors of Manchester
United have been ousted from
the board after US tycoon
Malcolm Glazer, who is
attempting to buy the club,
voted against their re-
election." - ], - [ - "AP - Manny Ramirez singled and
scored before leaving with a
bruised knee, and the
streaking Boston Red Sox beat
the Detroit Tigers 5-3 Friday
night for their 10th victory
in 11 games." - ], - [ - "The news comes fast and
furious. Pedro Martinez goes
to Tampa to visit George
Steinbrenner. Theo Epstein and
John Henry go to Florida for
their turn with Pedro. Carl
Pavano comes to Boston to
visit Curt Schilling. Jason
Varitek says he's not a goner.
Derek Lowe is a goner, but he
says he wishes it could be
different. Orlando Cabrera ..." - ], - [ - "FRED Hale Sr, documented as
the worlds oldest man, has
died at the age of 113. Hale
died in his sleep on Friday at
a hospital in Syracuse, New
York, while trying to recover
from a bout of pneumonia, his
grandson, Fred Hale III said." - ], - [ - "The Oakland Raiders have
traded Jerry Rice to the
Seattle Seahawks in a move
expected to grant the most
prolific receiver in National
Football League history his
wish to get more playing time." - ], - [ - "AP - Florida coach Ron Zook
was fired Monday but will be
allowed to finish the season,
athletic director Jeremy Foley
told The Gainesville Sun." - ], - [ - "Troy Brown has had to make a
lot of adjustments while
playing both sides of the
football. quot;You always
want to score when you get the
ball -- offense or defense" - ], - [ - "Jenson Button will tomorrow
discover whether he is allowed
to quit BAR and move to
Williams for 2005. The
Englishman has signed
contracts with both teams but
prefers a switch to Williams,
where he began his Formula One
career in 2000." - ], - [ - "ARSENAL boss Arsene Wenger
last night suffered a
Champions League setback as
Brazilian midfielder Gilberto
Silva (above) was left facing
a long-term injury absence." - ], - [ - "American improves to 3-1 on
the season with a hard-fought
overtime win, 74-63, against
Loyala at Bender Arena on
Friday night." - ], - [ - "Bee Staff Writers. SAN
FRANCISCO - As Eric Johnson
drove to the stadium Sunday
morning, his bruised ribs were
so sore, he wasn #39;t sure he
#39;d be able to suit up for
the game." - ], - [ - "The New England Patriots are
so single-minded in pursuing
their third Super Bowl triumph
in four years that they almost
have no room for any other
history." - ], - [ - "Because, while the Eagles are
certain to stumble at some
point during the regular
season, it seems inconceivable
that they will falter against
a team with as many offensive
problems as Baltimore has
right now." - ], - [ - "AP - J.J. Arrington ran for 84
of his 121 yards in the second
half and Aaron Rodgers shook
off a slow start to throw two
touchdown passes to help No. 5
California beat Washington
42-12 on Saturday." - ], - [ - "SHANGHAI, China The Houston
Rockets have arrived in
Shanghai with hometown
favorite Yao Ming declaring
himself quot;here on
business." - ], - [ - "Charleston, SC (Sports
Network) - Andy Roddick and
Mardy Fish will play singles
for the United States in this
weekend #39;s Davis Cup
semifinal matchup against
Belarus." - ], - [ - "RICKY PONTING believes the
game #39;s watchers have
fallen for the quot;myth
quot; that New Zealand know
how to rattle Australia." - ], - [ - "MILWAUKEE (SportsTicker) -
Barry Bonds tries to go where
just two players have gone
before when the San Francisco
Giants visit the Milwaukee
Brewers on Tuesday." - ], - [ - "AP - With Tom Brady as their
quarterback and a stingy,
opportunistic defense, it's
difficult to imagine when the
New England Patriots might
lose again. Brady and
defensive end Richard Seymour
combined to secure the
Patriots' record-tying 18th
straight victory, 31-17 over
the Buffalo Bills on Sunday." - ], - [ - "Approaching Hurricane Ivan has
led to postponement of the
game Thursday night between
10th-ranked California and
Southern Mississippi in
Hattiesburg, Cal #39;s
athletic director said Monday." - ], - [ - "SAN DIEGO (Ticker) - The San
Diego Padres lacked speed and
an experienced bench last
season, things veteran
infielder Eric Young is
capable of providing." - ], - [ - "This is an eye chart,
reprinted as a public service
to the New York Mets so they
may see from what they suffer:
myopia. Has ever a baseball
franchise been so shortsighted
for so long?" - ], - [ - " LONDON (Reuters) - A medical
product used to treat both
male hair loss and prostate
problems has been added to the
list of banned drugs for
athletes." - ], - [ - "AP - Curtis Martin and Jerome
Bettis have the opportunity to
go over 13,000 career yards
rushing in the same game when
Bettis and the Pittsburgh
Steelers play Martin and the
New York Jets in a big AFC
matchup Sunday." - ], - [ - "Michigan Stadium was mostly
filled with empty seats. The
only cheers were coming from
near one end zone -he Iowa
section. By Carlos Osorio, AP." - ], - [ - "The International Rugby Board
today confirmed that three
countries have expressed an
interest in hosting the 2011
World Cup. New Zealand, South
Africa and Japan are leading
the race to host rugby union
#39;s global spectacular in
seven years #39; time." - ], - [ - "MIAMI (Ticker) -- In its first
season in the Atlantic Coast
Conference, No. 11 Virginia
Tech is headed to the BCS.
Bryan Randall threw two
touchdown passes and the
Virginia Tech defense came up
big all day as the Hokies
knocked off No." - ], - [ - "(CP) - Somehow, in the span of
half an hour, the Detroit
Tigers #39; pitching went from
brutal to brilliant. Shortly
after being on the wrong end
of several records in a 26-5
thrashing from to the Kansas
City" - ], - [ - "With the Huskies reeling at
0-4 - the only member of a
Bowl Championship Series
conference left without a win
-an Jose State suddenly looms
as the only team left on the
schedule that UW will be
favored to beat." - ], - [ - "Darryl Sutter, who coached the
Calgary Flames to the Stanley
Cup finals last season, had an
emergency appendectomy and was
recovering Friday." - ], - [ - "Athens, Greece (Sports
Network) - For the second
straight day a Briton captured
gold at the Olympic Velodrome.
Bradley Wiggins won the men
#39;s individual 4,000-meter
pursuit Saturday, one day
after teammate" - ], - [ - "A Steffen Iversen penalty was
sufficient to secure the
points for Norway at Hampden
on Saturday. James McFadden
was ordered off after 53
minutes for deliberate
handball as he punched Claus
Lundekvam #39;s header off the
line." - ], - [ - "AP - Ailing St. Louis reliever
Steve Kline was unavailable
for Game 3 of the NL
championship series on
Saturday, but Cardinals
manager Tony LaRussa hopes the
left-hander will pitch later
this postseason." - ], - [ - "MADRID: A stunning first-half
free kick from David Beckham
gave Real Madrid a 1-0 win
over newly promoted Numancia
at the Bernabeu last night." - ], - [ - "Favourites Argentina beat
Italy 3-0 this morning to
claim their place in the final
of the men #39;s Olympic
football tournament. Goals by
leading goalscorer Carlos
Tevez, with a smart volley
after 16 minutes, and" - ], - [ - "Shortly after Steve Spurrier
arrived at Florida in 1990,
the Gators were placed on NCAA
probation for a year stemming
from a child-support payment
former coach Galen Hall made
for a player." - ] - ], - "hovertemplate": "label=Sports
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Sports", - "marker": { - "color": "#00cc96", - "size": 5, - "symbol": "square" - }, - "mode": "markers", - "name": "Sports", - "showlegend": true, - "type": "scattergl", - "x": [ - 16.660423, - 49.959976, - 54.445854, - 30.389194, - 13.9476, - 47.226242, - 51.068775, - 50.124294, - 44.945942, - 46.406788, - 48.53827, - 31.687206, - 40.537342, - 63.37852, - 56.515934, - 45.10189, - 48.401085, - 36.802402, - 54.15107, - 58.71805, - 38.756367, - 59.531124, - 47.890396, - 36.084118, - 39.42093, - 55.4732, - 60.896523, - 56.4989, - 48.16754, - 47.840588, - 60.467564, - 31.210938, - 49.345882, - 58.657722, - 37.406326, - 45.95487, - 27.38124, - 46.569256, - 40.19783, - -38.585472, - 48.277, - 48.961315, - 46.0185, - 59.281708, - 58.10119, - 41.329796, - 60.929493, - 49.080627, - 52.442997, - 25.193996, - 13.627039, - 49.169395, - 50.209198, - 38.928047, - 57.835648, - 25.167212, - 54.942364, - 31.13371, - 45.565693, - 56.076347, - 47.481384, - 54.362545, - 53.08307, - 55.395947, - 32.21779, - 36.722115, - 58.705086, - 30.618706, - 23.218187, - 48.1655, - 39.944633, - 51.72719, - 57.632236, - 18.106745, - 40.575005, - 49.25058, - 52.102192, - 45.8365, - 41.15665, - 45.06367, - 50.470036, - 55.406406, - 34.643066, - 44.60481, - 40.65631, - 49.13915, - 29.803865, - 25.779589, - 56.531708, - 37.571487, - 65.59185, - 38.63354, - 46.481388, - 46.68557, - 49.08016, - 14.032702, - 23.928396, - -6.620774, - 45.88543, - 35.342182, - 47.523766, - 47.32294, - 34.786186, - 43.796356, - 49.697426, - 37.340225, - 54.74676, - 41.444393, - 30.604837, - 60.995483, - 66.21081, - 35.24333, - 57.111607, - 48.019886, - 52.86177, - 52.435543, - 51.07264, - 61.549442, - 42.282997, - 33.828228, - 56.65421, - 43.26525, - 62.865932, - 39.05222, - 41.410755, - 38.42368, - 56.985188, - 57.739395, - 50.66318, - 48.850945, - 36.300686, - 39.32889, - 31.211935, - 39.84183, - 40.939545, - 48.37629, - 25.452175, - 51.559708, - 46.41191, - 52.77315, - 55.190277, - 38.03023, - 59.759964, - 48.908253, - 49.40172, - 49.71215, - 40.702366, - 57.916637, - 51.67792, - 30.202019, - 57.654526, - 34.585587, - 61.278408, - 23.422081, - 39.786133, - 54.46313, - 48.63991, - 35.595135, - 50.553566, - 54.101334, - 51.79524, - 42.513103, - 39.504147, - 20.738512, - 47.29459, - 51.208797, - 61.06654, - 59.059566, - 41.391323, - 56.262905, - 46.51155, - 27.989025, - 57.75085, - 35.379726, - 40.129883, - 51.36741, - 44.124233, - 38.459312, - 42.09723, - 54.85633, - 48.645084, - 33.95719, - 34.440483, - 45.367435, - 57.216434, - 49.150986, - 40.020714, - 36.398586, - 49.763367, - 44.87128, - 34.063007, - 29.130596, - 40.65309, - 33.885105, - 42.74865, - 46.39128, - 63.1983, - 49.79338, - 54.410885, - 63.605362, - 39.934895, - 17.519335, - 40.63617, - 30.811249, - 47.465935, - 47.272655, - 63.177612, - 41.66488, - 51.543095, - 38.820065, - 40.088383, - 60.45724, - 52.433907, - 38.667847, - 57.766346, - 42.41823, - 19.677572, - -36.831997, - 56.381645, - 53.579098, - 59.620773, - 48.37667, - 35.445507, - 46.157833, - 49.838924, - 29.182852, - 38.542347, - 30.625587, - 43.841885, - 56.74711, - 51.738125, - 51.774147, - 43.796253, - 47.406204, - -6.8207917, - 41.08887, - 62.321815, - 30.915781, - 49.839912, - 48.11404, - 37.93922, - 58.07306, - 50.09836, - 54.922394, - 52.352432, - 53.529724, - 25.636118, - 38.338932, - 37.103165, - 45.54423, - 53.86991, - 47.012283, - 60.093002, - 32.56739, - 60.606064, - 39.772743, - 48.95457, - 44.68764, - 48.906673, - 47.63983, - 62.788, - 38.030407, - 56.60054, - 40.073948, - 60.232296, - 56.806816, - 45.64992, - 39.266872, - 62.91453, - 50.306213, - 53.805332, - 50.443317, - 53.03957, - 44.309578, - 54.42772, - 34.023724, - 61.473892, - 50.651646, - 27.97626, - 59.39454, - 32.39917, - 59.94177, - 42.23158, - 56.222717, - 59.36177, - 57.550297, - 60.801098, - 57.966537, - 55.964886, - 45.870426, - 38.638237, - 49.729176, - 58.080826, - 58.289707, - 25.88273, - 63.36231, - 50.890648, - 45.88053, - 50.78542, - 43.456127, - 28.731657, - 50.339493, - 61.357586, - 56.755314, - 13.779188, - 25.783823, - 55.462612, - 58.314003, - 53.49091, - 49.449314, - 37.930157, - 57.86416, - 52.26376, - 47.072803, - 48.17513, - 44.906193, - 56.61765, - 56.60834, - 56.066643, - 56.162464, - 53.77512, - 47.758217, - 50.96032, - 58.05466, - 41.446438, - 58.41072, - 11.970405, - 35.8429, - 47.047108, - 54.494556, - 47.79968, - 59.32574, - 40.920834, - 55.53207, - 44.559975, - 53.77833, - 32.079, - 47.351242, - 38.30059, - 48.4094, - 33.499294, - 29.347134, - 45.721344, - 45.999187, - 49.048878, - 39.29256, - 32.81585, - 26.376568, - 32.12724, - 59.9222, - 45.969334, - 47.935875, - 57.007023, - 34.043217, - 30.42877, - 53.714108, - 34.008293, - 33.648438, - 59.613667, - 31.749115, - 52.849945, - 61.664543, - 30.838337, - 43.08786, - 49.605602, - 63.165108, - 47.894882, - 51.314476, - 44.60029, - 44.015842, - 39.41502, - 59.81665, - 41.209156, - 47.579025, - 56.48261, - 46.025146, - 58.74744, - 46.206974, - 53.145702, - 60.687424, - 38.102413, - 39.646805, - 52.38563, - 34.14185, - 31.320894, - 40.573475, - 41.94092, - 30.75048, - 39.690254, - 53.560726, - 50.696335, - 0.011293956, - 29.16581, - 58.507614, - 40.03086, - 48.50645, - 38.79774, - 62.77733, - 47.435825, - 43.003845, - 34.368248, - 53.752186, - 55.804855, - 26.508045, - 30.6338, - 47.806313, - 56.805237, - 49.249695, - 49.522606, - 61.757652, - 61.630924, - 39.668633, - 51.79226, - 28.194004, - 53.427227, - 45.15016, - 36.015182, - 43.020264, - 54.577732, - 18.002365, - 31.860546, - -0.10420398, - 37.094307, - 58.735332, - 12.898047, - 35.940807, - 30.280912, - 63.004868, - 47.676117, - 43.803253, - 52.02361, - 57.569775, - -31.23627, - 51.3331, - 40.77166, - 48.259228, - 49.969627, - 36.618427, - 55.361397, - 40.71827, - 39.666965, - 33.43409, - 55.207115, - 32.183567, - 16.695406, - 55.5641, - 51.27627, - 47.339336, - 63.347527, - 39.062187, - 54.67712, - 65.51466, - 45.10427, - 47.36583, - 35.328148, - 30.716692, - 58.147617, - 64.86492, - 32.677113, - 55.061314, - 38.546684, - 43.151165, - 26.161028, - 44.980778, - 47.840103, - 49.251343, - 52.69105, - 49.939175, - 46.95092, - 28.887644, - 54.523384, - 27.097654, - 58.47151, - 50.23622, - 39.812546, - 59.636997, - 57.34273, - -18.721113, - 48.26851, - 42.70037, - 15.134995, - 49.12623, - 59.67211, - 47.292377, - 47.805153, - 46.018627, - 37.074707, - 64.22567, - 40.90623, - 42.02211, - 36.001675 - ], - "xaxis": "x", - "y": [ - -20.803589, - -3.7095485, - -10.627376, - -3.9541492, - -17.816854, - -21.783358, - -12.137919, - 8.247171, - -27.218586, - -35.47235, - 0.2254613, - -32.542274, - -29.048313, - -18.953293, - -16.318848, - 1.29799, - 0.27214336, - -22.427275, - 1.5617585, - -13.859794, - -10.261337, - 2.3437028, - -11.51763, - -28.920532, - -14.537146, - -28.30168, - -17.185091, - 10.214211, - -11.397742, - -29.872732, - -19.64737, - 0.16790108, - 9.72588, - -19.213398, - -17.097263, - -23.540205, - -22.659904, - -30.161833, - -32.789925, - 4.0007343, - -30.553509, - -19.611755, - 8.52158, - 4.1424494, - 1.4751459, - -12.852704, - -20.392239, - -4.5837173, - 8.166061, - -11.337284, - -16.43065, - -22.28117, - 7.9826016, - -13.418971, - -24.45379, - -11.355663, - 9.545558, - -32.61165, - 11.230936, - -13.468946, - -5.462048, - -13.069021, - 1.5735915, - -23.937035, - -15.694869, - -25.943512, - 0.82049704, - -3.9954906, - -18.380714, - -21.956186, - -17.928478, - -17.166925, - 1.8213869, - -20.784616, - -11.329836, - -8.567718, - -11.798271, - -25.911339, - -10.770047, - -15.446808, - -32.171726, - -29.32608, - -35.429436, - -11.357406, - -14.503349, - -3.407611, - -5.3719177, - -9.659326, - -19.531103, - -14.720505, - -27.277906, - -15.416589, - 0.7872089, - -23.089176, - -19.599855, - -17.141865, - -14.515779, - -8.936446, - -31.523523, - -15.298813, - -11.721406, - -20.626392, - -8.267473, - -22.223913, - -28.966488, - -21.779205, - -27.218397, - -36.186253, - 0.31401816, - -22.044197, - -25.348227, - -15.303513, - 2.799256, - -27.043676, - -29.502745, - -6.9711366, - -13.013833, - 2.553211, - -14.678428, - -19.333553, - 5.2738123, - -30.191147, - -8.254407, - -16.734713, - -36.29533, - -0.7015533, - -19.59588, - -19.748474, - -18.429428, - -24.985508, - -14.398376, - -23.032887, - -27.591204, - -13.656402, - -33.948692, - -33.920197, - -9.174384, - 8.925708, - -22.970503, - 1.9238061, - -5.8668604, - -27.786598, - -11.159512, - -32.18803, - -16.650103, - -18.147429, - -14.823587, - 3.2132275, - -28.899687, - 0.73946625, - -14.675726, - -24.362509, - -18.907366, - -18.148087, - -24.660992, - -12.368451, - 10.51185, - -10.045945, - -30.512154, - -0.48769227, - -22.191689, - -9.914337, - -10.982109, - 31.096636, - -11.43558, - 6.894826, - 4.1693807, - 1.2161766, - -9.937122, - -27.618727, - -22.007416, - -22.37126, - -22.028944, - -10.037108, - -7.7222157, - 3.5807598, - -6.6086307, - -19.699232, - -17.251148, - 9.637636, - -10.8705435, - 8.272561, - 8.240764, - -22.69967, - -29.479254, - -11.955685, - -4.9588523, - -16.469437, - 9.750696, - -14.905879, - -20.787516, - -3.1344242, - -3.9499974, - 8.201109, - -19.819767, - -4.076858, - -24.730019, - -13.261404, - -11.716383, - -18.290712, - -15.495678, - -32.85601, - -13.886067, - -33.77542, - -22.37821, - -33.790863, - -23.158052, - -23.911886, - -28.11098, - -15.843517, - -24.879805, - -27.03218, - 5.8135962, - -25.135891, - -25.89945, - -18.168903, - -15.80312, - -29.042156, - -23.639217, - 1.8415234, - -16.338673, - 10.431047, - -34.98855, - -30.50168, - -1.8533841, - -23.127438, - -26.898434, - -6.0696855, - -0.83113074, - -13.937987, - 8.4994335, - -25.814455, - -25.507462, - -1.2937344, - -9.251707, - -11.8820095, - -13.837845, - -33.480656, - -20.987251, - 6.7093077, - -24.921698, - 3.5488389, - -18.068623, - 3.0699391, - 9.083556, - -13.918425, - -16.273378, - -22.65874, - -12.477235, - 11.250292, - -13.376665, - -5.0115275, - -23.036457, - -11.463062, - 3.7194152, - -12.564382, - -29.44567, - -9.352621, - -23.756565, - -17.759132, - -15.3389635, - -13.819872, - -6.09211, - -7.863438, - -13.616495, - -23.899826, - -9.426962, - -12.24037, - -18.85435, - 11.024011, - -19.68378, - -26.103676, - 10.497953, - 3.9857144, - -22.706993, - -2.4754145, - -21.39815, - 3.600532, - -22.364601, - -16.769764, - -12.030829, - -28.317688, - -4.760545, - -17.78651, - -20.541088, - 1.3681566, - 1.0861593, - 4.390221, - -22.447065, - 2.2553952, - -14.250181, - -28.855429, - -23.47392, - -0.6379835, - -17.019722, - -23.794706, - 2.3137836, - 6.832896, - -11.976225, - -16.869408, - -14.887161, - 11.149261, - -15.141055, - 5.0789285, - -17.239506, - -16.723783, - 9.832679, - -19.392942, - -24.406261, - -17.971964, - -2.6890767, - -13.708067, - 9.420364, - -26.4286, - -23.904675, - -33.521553, - -27.414234, - -24.713099, - -13.032957, - -20.86936, - -17.725012, - -13.713904, - -27.97785, - 1.1649175, - -12.003306, - 3.4183521, - -18.329607, - -22.500238, - -5.1817036, - -10.172258, - 8.929348, - -18.92016, - -4.0155163, - -29.874123, - -23.89452, - -14.478729, - -21.707514, - 2.8463974, - -24.179169, - -22.502762, - -18.470171, - -5.5552483, - 2.6354103, - -25.625107, - -23.603718, - -13.1784725, - -21.927172, - -17.776453, - -12.744574, - -24.39855, - 1.6557639, - -25.33089, - 3.7044208, - -14.088412, - 1.8123101, - 3.1115727, - -9.5224, - -8.527657, - -27.493273, - -28.8183, - -21.120987, - -0.42459357, - -13.964472, - -30.554207, - -16.260057, - -20.409258, - -3.838907, - -30.899261, - -25.502863, - 4.312004, - -26.893, - -20.63535, - -4.0243726, - -33.28943, - -13.433018, - -21.37861, - -17.676962, - -33.109673, - 7.7211857, - 2.9930232, - -3.4584122, - -17.335155, - 0.4309157, - -9.979049, - -27.767008, - -2.7953665, - -23.63617, - -0.20407373, - 2.833431, - -1.6160171, - -22.09123, - -15.144995, - -27.617838, - -20.576097, - -32.521618, - -1.5771652, - -3.4706712, - -13.110925, - 1.27955, - -13.123537, - -21.404385, - 2.485261, - -26.038076, - -8.591754, - -32.257572, - -3.6816385, - -23.705658, - -3.3590631, - -2.241037, - -7.3185177, - -20.510658, - 2.8498745, - -14.110134, - -21.078281, - -16.38932, - -10.101326, - -29.059853, - -31.21759, - -1.3346295, - -20.799906, - -14.345478, - -15.090428, - -16.226871, - -17.027992, - -20.647305, - -34.179035, - -14.075991, - -15.682211, - -23.77744, - 2.101532, - 8.422051, - -12.298222, - 2.824297, - -18.204716, - -2.6403008, - -17.935425, - -18.721956, - -6.343975, - 9.154357, - -16.127396, - -2.973772, - -22.44099, - 10.113919, - -16.923988, - -18.502573, - -22.337847, - 5.892835, - -30.008844, - -26.583797, - -12.331805, - -1.2270886, - -26.34871, - -13.808859, - -32.725826, - -12.638194, - -13.887938, - -20.714098, - -18.954786, - 8.2712965, - -14.246153, - -24.174063, - -22.63233, - -17.627256, - -10.120339, - -18.194794, - -8.593113, - -27.35188, - -31.873516, - -21.917208, - -27.548603, - -0.95101047, - -8.804195, - -16.590578, - -25.044327, - -32.0242, - -14.339118, - -28.126497, - 17.26326, - -27.410538, - -27.716919, - -16.625145, - -21.870625, - -21.870728, - -32.103767, - -10.273103, - 1.9282136, - -10.849964, - -15.895552, - -12.564632, - -13.048038, - -23.010983 - ], - "yaxis": "y" - }, - { - "customdata": [ - [ - "The EU and US moved closer to
an aviation trade war after
failing to reach agreement
during talks Thursday on
subsidies to Airbus Industrie." - ], - [ - " SAN FRANCISCO (Reuters) -
Nike Inc. <A HREF=\"http://w
ww.investor.reuters.com/FullQu
ote.aspx?ticker=NKE.N target=/
stocks/quickinfo/fullquote\">
;NKE.N</A>, the world's
largest athletic shoe company,
on Monday reported a 25
percent rise in quarterly
profit, beating analysts'
estimates, on strong demand
for high-end running and
basketball shoes in the
United States." - ], - [ - "NEW YORK (CBS.MW) - US stocks
traded mixed Thurday as Merck
shares lost a quarter of their
value, dragging blue chips
lower, but the Nasdaq moved
higher, buoyed by gains in the
semiconductor sector." - ], - [ - "Bankrupt ATA Airlines #39;
withdrawal from Chicago #39;s
Midway International Airport
presents a juicy opportunity
for another airline to beef up
service in the Midwest" - ], - [ - "The Instinet Group, owner of
one of the largest electronic
stock trading systems, is for
sale, executives briefed on
the plan say." - ], - [ - "The Auburn Hills-based
Chrysler Group made a profit
of \\$269 million in the third
quarter, even though worldwide
sales and revenues declined,
contributing to a \\$1." - ], - [ - "SAN FRANCISCO (CBS.MW) -- UAL
Corp., parent of United
Airlines, said Wednesday it
will overhaul its route
structure to reduce costs and
offset rising fuel costs." - ], - [ - "Annual economic growth in
China has slowed for the third
quarter in a row, falling to
9.1 per cent, third-quarter
data shows. The slowdown shows
the economy is responding to
Beijing #39;s efforts to rein
in break-neck investment and
lending." - ], - [ - "THE All-India Motor Transport
Congress (AIMTC) on Saturday
called off its seven-day
strike after finalising an
agreement with the Government
on the contentious issue of
service tax and the various
demands including tax deducted
at source (TDS), scrapping" - ], - [ - "AP - The euro-zone economy
grew by 0.5 percent in the
second quarter of 2004, a
touch slower than in the first
three months of the year,
according to initial estimates
released Tuesday by the
European Union." - ], - [ - "A few years ago, casinos
across the United States were
closing their poker rooms to
make space for more popular
and lucrative slot machines." - ], - [ - "WASHINGTON -- Consumers were
tightfisted amid soaring
gasoline costs in August and
hurricane-related disruptions
last week sent applications
for jobless benefits to their
highest level in seven months." - ], - [ - "Talking kitchens and vanities.
Musical jump ropes and potty
seats. Blusterous miniature
leaf blowers and vacuum
cleaners -- almost as loud as
the real things." - ], - [ - "Online merchants in the United
States have become better at
weeding out fraudulent credit
card orders, a new survey
indicates. But shipping
overseas remains a risky
venture. By Joanna Glasner." - ], - [ - "Popping a gadget into a cradle
to recharge it used to mean
downtime, but these days
chargers are doing double
duty, keeping your portable
devices playing even when
they're charging." - ], - [ - " SAN FRANCISCO (Reuters) -
Texas Instruments Inc. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=T
XN.N target=/stocks/quickinfo/
fullquote\">TXN.N</A>,
the largest maker of chips for
cellular phones, on Monday
said record demand for its
handset and television chips
boosted quarterly profit by
26 percent, even as it
struggles with a nagging
inventory problem." - ], - [ - "LONDON ARM Holdings, a British
semiconductor designer, said
on Monday that it would buy
Artisan Components for \\$913
million to broaden its product
range." - ], - [ - "MELBOURNE: Global shopping
mall owner Westfield Group
will team up with Australian
developer Multiplex Group to
bid 585mil (US\\$1." - ], - [ - "Reuters - Delta Air Lines Inc.
, which is\\racing to cut costs
to avoid bankruptcy, on
Wednesday reported\\a wider
quarterly loss amid soaring
fuel prices and weak\\domestic
airfares." - ], - [ - "Energy utility TXU Corp. on
Monday more than quadrupled
its quarterly dividend payment
and raised its projected
earnings for 2004 and 2005
after a companywide
performance review." - ], - [ - "Northwest Airlines Corp., the
fourth- largest US airline,
and its pilots union reached
tentative agreement on a
contract that would cut pay
and benefits, saving the
company \\$265 million a year." - ], - [ - "Microsoft Corp. MSFT.O and
cable television provider
Comcast Corp. CMCSA.O said on
Monday that they would begin
deploying set-top boxes
powered by Microsoft software
starting next week." - ], - [ - "Philippines mobile phone
operator Smart Communications
Inc. is in talks with
Singapore #39;s Mobile One for
a possible tie-up,
BusinessWorld reported Monday." - ], - [ - "Airline warns it may file for
bankruptcy if too many senior
pilots take early retirement
option. Delta Air LInes #39;
CEO says it faces bankruptcy
if it can #39;t slow the pace
of pilots taking early
retirement." - ], - [ - "Kodak Versamark #39;s parent
company, Eastman Kodak Co.,
reported Tuesday it plans to
eliminate almost 900 jobs this
year in a restructuring of its
overseas operations." - ], - [ - "A top official of the US Food
and Drug Administration said
Friday that he and his
colleagues quot;categorically
reject quot; earlier
Congressional testimony that
the agency has failed to
protect the public against
dangerous drugs." - ], - [ - "AFP - British retailer Marks
and Spencer announced a major
management shake-up as part of
efforts to revive its
fortunes, saying trading has
become tougher ahead of the
crucial Christmas period." - ], - [ - " ATLANTA (Reuters) - Home
Depot Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=HD.N target=/
stocks/quickinfo/fullquote\">
;HD.N</A>, the top home
improvement retailer, on
Tuesday reported a 15 percent
rise in third-quarter profit,
topping estimates, aided by
installed services and sales
to contractors." - ], - [ - " LONDON (Reuters) - The dollar
fought back from one-month
lows against the euro and
Swiss franc on Wednesday as
investors viewed its sell-off
in the wake of the Federal
Reserve's verdict on interest
rates as overdone." - ], - [ - "Boston Scientific Corp said on
Friday it has recalled an ear
implant the company acquired
as part of its purchase of
Advanced Bionics in June." - ], - [ - "MarketWatch.com. Richemont
sees significant H1 lift,
unclear on FY (2:53 AM ET)
LONDON (CBS.MW) -- Swiss
luxury goods maker
Richemont(zz:ZZ:001273145:
news, chart, profile), which
also is a significant" - ], - [ - "Crude oil climbed more than
\\$1 in New York on the re-
election of President George
W. Bush, who has been filling
the US Strategic Petroleum
Reserve." - ], - [ - "Yukos #39; largest oil-
producing unit regained power
supplies from Tyumenenergo, a
Siberia-based electricity
generator, Friday after the
subsidiary pledged to pay 440
million rubles (\\$15 million)
in debt by Oct. 3." - ], - [ - "US STOCKS vacillated yesterday
as rising oil prices muted
Wall Streets excitement over
strong results from Lehman
Brothers and Sprints \\$35
billion acquisition of Nextel
Communications." - ], - [ - "At the head of the class,
Rosabeth Moss Kanter is an
intellectual whirlwind: loud,
expansive, in constant motion." - ], - [ - "A bitter row between America
and the European Union over
alleged subsidies to rival
aircraft makers Boeing and
Airbus intensified yesterday." - ], - [ - "Amsterdam (pts) - Dutch
electronics company Philips
http://www.philips.com has
announced today, Friday, that
it has cut its stake in Atos
Origin by more than a half." - ], - [ - "TORONTO (CP) - Two-thirds of
banks around the world have
reported an increase in the
volume of suspicious
activities that they report to
police, a new report by KPMG
suggests." - ], - [ - "The Atkins diet frenzy slowed
growth briefly, but the
sandwich business is booming,
with \\$105 billion in sales
last year." - ], - [ - "Luxury carmaker Jaguar said
Friday it was stopping
production at a factory in
central England, resulting in
a loss of 1,100 jobs,
following poor sales in the
key US market." - ], - [ - "John Gibson said Friday that
he decided to resign as chief
executive officer of
Halliburton Energy Services
when it became apparent he
couldn #39;t become the CEO of
the entire corporation, after
getting a taste of the No." - ], - [ - "NEW YORK (Reuters) - Outback
Steakhouse Inc. said Tuesday
it lost about 130 operating
days and up to \\$2 million in
revenue because it had to
close some restaurants in the
South due to Hurricane
Charley." - ], - [ - "State insurance commissioners
from across the country have
proposed new rules governing
insurance brokerage fees,
winning praise from an
industry group and criticism
from" - ], - [ - "SOFTWARE giant Oracle #39;s
stalled \\$7.7bn (4.2bn) bid to
take over competitor
PeopleSoft has received a huge
boost after a US judge threw
out an anti-trust lawsuit
filed by the Department of
Justice to block the
acquisition." - ], - [ - "Office Depot Inc. (ODP.N:
Quote, Profile, Research) on
Tuesday warned of weaker-than-
expected profits for the rest
of the year because of
disruptions from the string of
hurricanes" - ], - [ - "THE photo-equipment maker
Kodak yesterday announced
plans to axe 600 jobs in the
UK and close a factory in
Nottinghamshire, in a move in
line with the giants global
restructuring strategy
unveiled last January." - ], - [ - "China's central bank on
Thursday raised interest rates
for the first time in nearly a
decade, signaling deepening
unease with the breakneck pace
of development and an intent
to reign in a construction
boom now sowing fears of
runaway inflation." - ], - [ - "CHICAGO - Delta Air Lines
(DAL) said Wednesday it plans
to eliminate between 6,000 and
6,900 jobs during the next 18
months, implement a 10 across-
the-board pay reduction and
reduce employee benefits." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks were likely to open
flat on Wednesday, with high
oil prices and profit warnings
weighing on the market before
earnings reports start and key
jobs data is released this
week." - ], - [ - "Best known for its popular
search engine, Google is
rapidly rolling out new
products and muscling into
Microsoft's stronghold: the
computer desktop. The
competition means consumers
get more choices and better
products." - ], - [ - " MOSCOW (Reuters) - Russia's
Gazprom said on Tuesday it
will bid for embattled oil
firm YUKOS' main unit next
month, as the Kremlin seeks
to turn the world's biggest
gas producer into a major oil
player." - ], - [ - "Federal Reserve officials
raised a key short-term
interest rate Tuesday for the
fifth time this year, and
indicated they will gradually
move rates higher next year to
keep inflation under control
as the economy expands." - ], - [ - "Canadians are paying more to
borrow money for homes, cars
and other purchases today
after a quarter-point
interest-rate increase by the
Bank of Canada yesterday was
quickly matched by the
chartered banks." - ], - [ - "Delta Air Lines is to issue
millions of new shares without
shareholder consent as part of
moves to ensure its survival." - ], - [ - "Genta (GNTA:Nasdaq - news -
research) is never boring!
Monday night, the company
announced that its phase III
Genasense study in chronic
lymphocytic leukemia (CLL) met
its primary endpoint, which
was tumor shrinkage." - ], - [ - "While the entire airline
industry #39;s finances are
under water, ATA Airlines will
have to hold its breath longer
than its competitors to keep
from going belly up." - ], - [ - "One day after ousting its
chief executive, the nation's
largest insurance broker said
it will tell clients exactly
how much they are paying for
services and renounce back-
door payments from carriers." - ], - [ - "LONDON (CBS.MW) -- Outlining
an expectation for higher oil
prices and increasing demand,
Royal Dutch/Shell on Wednesday
said it #39;s lifting project
spending to \\$45 billion over
the next three years." - ], - [ - "Tuesday #39;s meeting could
hold clues to whether it
#39;ll be a November or
December pause in rate hikes.
By Chris Isidore, CNN/Money
senior writer." - ], - [ - "The dollar may fall against
the euro for a third week in
four on concern near-record
crude oil prices will temper
the pace of expansion in the
US economy, a survey by
Bloomberg News indicates." - ], - [ - "The battle for the British-
based Chelsfield plc hotted up
at the weekend, with reports
from London that the property
giant #39;s management was
working on its own bid to
thwart the 585 million (\\$A1." - ], - [ - "SAN DIEGO --(Business Wire)--
Oct. 11, 2004 -- Breakthrough
PeopleSoft EnterpriseOne 8.11
Applications Enable
Manufacturers to Become
Demand-Driven." - ], - [ - "Reuters - Oil prices rose on
Friday as tight\\supplies of
distillate fuel, including
heating oil, ahead of\\the
northern hemisphere winter
spurred buying." - ], - [ - "Nov. 18, 2004 - An FDA
scientist says the FDA, which
is charged with protecting
America #39;s prescription
drug supply, is incapable of
doing so." - ], - [ - "The UK's inflation rate fell
in September, thanks in part
to a fall in the price of
airfares, increasing the
chance that interest rates
will be kept on hold." - ], - [ - " HONG KONG/SAN FRANCISCO
(Reuters) - IBM is selling its
PC-making business to China's
largest personal computer
company, Lenovo Group Ltd.,
for \\$1.25 billion, marking
the U.S. firm's retreat from
an industry it helped pioneer
in 1981." - ], - [ - "Nordstrom reported a strong
second-quarter profit as it
continued to select more
relevant inventory and sell
more items at full price." - ], - [ - "The Bank of England is set to
keep interest rates on hold
following the latest meeting
of the its Monetary Policy
Committee." - ], - [ - "The Bush administration upheld
yesterday the imposition of
penalty tariffs on shrimp
imports from China and
Vietnam, handing a victory to
beleaguered US shrimp
producers." - ], - [ - "House prices rose 0.2 percent
in September compared with the
month before to stand up 17.8
percent on a year ago, the
Nationwide Building Society
says." - ], - [ - "Nortel Networks says it will
again delay the release of its
restated financial results.
The Canadian telecom vendor
originally promised to release
the restated results in
September." - ], - [ - " CHICAGO (Reuters) - At first
glance, paying \\$13 or \\$14
for a hard-wired Internet
laptop connection in a hotel
room might seem expensive." - ], - [ - "Reuters - An investigation
into U.S. insurers\\and brokers
rattled insurance industry
stocks for a second day\\on
Friday as investors, shaken
further by subpoenas
delivered\\to the top U.S. life
insurer, struggled to gauge
how deep the\\probe might
reach." - ], - [ - "The Dow Jones Industrial
Average failed three times
this year to exceed its
previous high and fell to
about 10,000 each time, most
recently a week ago." - ], - [ - " SINGAPORE (Reuters) - Asian
share markets staged a broad-
based retreat on Wednesday,
led by steelmakers amid
warnings of price declines,
but also enveloping technology
and financial stocks on
worries that earnings may
disappoint." - ], - [ - "NEW YORK - CNN has a new boss
for the second time in 14
months: former CBS News
executive Jonathan Klein, who
will oversee programming and
editorial direction at the
second-ranked cable news
network." - ], - [ - "Cut-price carrier Virgin Blue
said Tuesday the cost of using
Australian airports is
spiraling upward and asked the
government to review the
deregulated system of charges." - ], - [ - "Saudi Arabia, Kuwait and the
United Arab Emirates, which
account for almost half of
OPEC #39;s oil output, said
they #39;re committed to
boosting capacity to meet
soaring demand that has driven
prices to a record." - ], - [ - "The US Commerce Department
said Thursday personal income
posted its biggest increase in
three months in August. The
government agency also said
personal spending was
unchanged after rising
strongly in July." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei average opened up 0.54
percent on Monday with banks
and exporters leading the way
as a stronger finish on Wall
Street and declining oil
prices soothed worries over
the global economic outlook." - ], - [ - "Bruce Wasserstein, head of
Lazard LLC, is asking partners
to take a one-third pay cut as
he readies the world #39;s
largest closely held
investment bank for a share
sale, people familiar with the
situation said." - ], - [ - "The Lemon Bay Manta Rays were
not going to let a hurricane
get in the way of football. On
Friday, they headed to the
practice field for the first
time in eight" - ], - [ - "Microsoft Corp. Chairman Bill
Gates has donated \\$400,000 to
a campaign in California
trying to win approval of a
measure calling for the state
to sell \\$3 billion in bonds
to fund stem-cell research." - ], - [ - "Newspaper publisher Pulitzer
Inc. said Sunday that company
officials are considering a
possible sale of the firm to
boost shareholder value." - ], - [ - "Shares of Merck amp; Co.
plunged almost 10 percent
yesterday after a media report
said that documents show the
pharmaceutical giant hid or
denied" - ], - [ - "Reuters - Wall Street was
expected to dip at\\Thursday's
opening, but shares of Texas
Instruments Inc.\\, may climb
after it gave upbeat earnings
guidance." - ], - [ - "Late in August, Boeing #39;s
top sales execs flew to
Singapore for a crucial sales
pitch. They were close to
persuading Singapore Airlines,
one of the world #39;s leading
airlines, to buy the American
company #39;s new jet, the
mid-sized 7E7." - ], - [ - "SBC Communications and
BellSouth will acquire
YellowPages.com with the goal
of building the site into a
nationwide online business
index, the companies said
Thursday." - ], - [ - "The sounds of tinkling bells
could be heard above the
bustle of the Farmers Market
on the Long Beach Promenade,
leading shoppers to a row of
bright red tin kettles dotting
a pathway Friday." - ], - [ - "LONDON Santander Central
Hispano of Spain looked
certain to clinch its bid for
the British mortgage lender
Abbey National, after HBOS,
Britain #39;s biggest home-
loan company, said Wednesday
it would not counterbid, and
after the European Commission
cleared" - ], - [ - " WASHINGTON (Reuters) - The
Justice Department is
investigating possible
accounting fraud at Fannie
Mae, bringing greater
government scrutiny to bear on
the mortgage finance company,
already facing a parallel
inquiry by the SEC, a source
close to the matter said on
Thursday." - ], - [ - "Indian industrial group Tata
agrees to invest \\$2bn in
Bangladesh, the biggest single
deal agreed by a firm in the
south Asian country." - ], - [ - "The steel tubing company
reports sharply higher
earnings, but the stock is
falling." - ], - [ - "Playboy Enterprises, the adult
entertainment company, has
announced plans to open a
private members club in
Shanghai even though the
company #39;s flagship men
#39;s magazine is still banned
in China." - ], - [ - "TORONTO (CP) - Earnings
warnings from Celestica and
Coca-Cola along with a
slowdown in US industrial
production sent stock markets
lower Wednesday." - ], - [ - "Eastman Kodak Co., the world
#39;s largest maker of
photographic film, said
Wednesday it expects sales of
digital products and services
to grow at an annual rate of
36 percent between 2003 and
2007, above prior growth rate
estimates of 26 percent
between 2002" - ], - [ - "AFP - The Iraqi government
plans to phase out slowly
subsidies on basic products,
such as oil and electricity,
which comprise 50 percent of
public spending, equal to 15
billion dollars, the planning
minister said." - ], - [ - "The federal agency that
insures pension plans said
that its deficit, already at
the highest in its history,
had doubled in its last fiscal
year, to \\$23.3 billion." - ], - [ - "A Milan judge on Tuesday opens
hearings into whether to put
on trial 32 executives and
financial institutions over
the collapse of international
food group Parmalat in one of
Europe #39;s biggest fraud
cases." - ], - [ - "The Bank of England on
Thursday left its benchmark
interest rate unchanged, at
4.75 percent, as policy makers
assessed whether borrowing
costs, already the highest in
the Group of Seven, are
constraining consumer demand." - ], - [ - "Fashion retailers Austin Reed
and Ted Baker have reported
contrasting fortunes on the
High Street. Austin Reed
reported interim losses of 2." - ], - [ - " NEW YORK (Reuters) - A
federal judge on Friday
approved Citigroup Inc.'s
\\$2.6 billion settlement with
WorldCom Inc. investors who
lost billions when an
accounting scandal plunged
the telecommunications company
into bankruptcy protection." - ], - [ - "An unspecified number of
cochlear implants to help
people with severe hearing
loss are being recalled
because they may malfunction
due to ear moisture, the US
Food and Drug Administration
announced." - ], - [ - "Profits triple at McDonald's
Japan after the fast-food
chain starts selling larger
burgers." - ], - [ - "Britain #39;s inflation rate
fell in August further below
its 2.0 percent government-set
upper limit target with
clothing and footwear prices
actually falling, official
data showed on Tuesday." - ], - [ - " LONDON (Reuters) - European
shares shrugged off a spike in
the euro to a fresh all-time
high Wednesday, with telecoms
again leading the way higher
after interim profits at
Britain's mm02 beat
expectations." - ], - [ - "WASHINGTON - Weighed down by
high energy prices, the US
economy grew slower than the
government estimated in the
April-June quarter, as higher
oil prices limited consumer
spending and contributed to a
record trade deficit." - ], - [ - "CHICAGO United Airlines says
it will need even more labor
cuts than anticipated to get
out of bankruptcy. United told
a bankruptcy court judge in
Chicago today that it intends
to start talks with unions
next month on a new round of
cost savings." - ], - [ - "The University of California,
Berkeley, has signed an
agreement with the Samoan
government to isolate, from a
tree, the gene for a promising
anti- Aids drug and to share
any royalties from the sale of
a gene-derived drug with the
people of Samoa." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei average jumped 2.5
percent by mid-afternoon on
Monday as semiconductor-
related stocks such as
Advantest Corp. mirrored a
rally by their U.S. peers
while banks and brokerages
extended last week's gains." - ], - [ - "General Motors (GM) plans to
announce a massive
restructuring Thursday that
will eliminate as many as
12,000 jobs in Europe in a
move to stem the five-year
flow of red ink from its auto
operations in the region." - ], - [ - " LONDON (Reuters) - Oil prices
held firm on Wednesday as
Hurricane Ivan closed off
crude output and shut
refineries in the Gulf of
Mexico, while OPEC's Gulf
producers tried to reassure
traders by recommending an
output hike." - ], - [ - "State-owned, running a
monopoly on imports of jet
fuel to China #39;s fast-
growing aviation industry and
a prized member of Singapore
#39;s Stock Exchange." - ], - [ - "Google has won a trade mark
dispute, with a District Court
judge finding that the search
engines sale of sponsored
search terms Geico and Geico
Direct did not breach car
insurance firm GEICOs rights
in the trade marked terms." - ], - [ - "Wall Street bounded higher for
the second straight day
yesterday as investors reveled
in sharply falling oil prices
and the probusiness agenda of
the second Bush
administration. The Dow Jones
industrials gained more than
177 points for its best day of
2004, while the Standard amp;
Poor's 500 closed at its
highest level since early
2002." - ], - [ - "Key factors help determine if
outsourcing benefits or hurts
Americans." - ], - [ - "The US Trade Representative on
Monday rejected the European
Union #39;s assertion that its
ban on beef from hormone-
treated cattle is now
justified by science and that
US and Canadian retaliatory
sanctions should be lifted." - ], - [ - "NEW YORK -- Wall Street's
fourth-quarter rally gave
stock mutual funds a solid
performance for 2004, with
small-cap equity funds and
real estate funds scoring some
of the biggest returns. Large-
cap growth equities and
technology-focused funds had
the slimmest gains." - ], - [ - "Big Food Group Plc, the UK
owner of the Iceland grocery
chain, said second-quarter
sales at stores open at least
a year dropped 3.3 percent,
the second consecutive
decline, after competitors cut
prices." - ], - [ - " WASHINGTON (Reuters) - The
first case of soybean rust has
been found on the mainland
United States and could affect
U.S. crops for the near
future, costing farmers
millions of dollars, the
Agriculture Department said on
Wednesday." - ], - [ - "The Supreme Court today
overturned a five-figure
damage award to an Alexandria
man for a local auto dealer
#39;s alleged loan scam,
ruling that a Richmond-based
federal appeals court had
wrongly" - ], - [ - "Official figures show the
12-nation eurozone economy
continues to grow, but there
are warnings it may slow down
later in the year." - ], - [ - "In upholding a lower court
#39;s ruling, the Supreme
Court rejected arguments that
the Do Not Call list violates
telemarketers #39; First
Amendment rights." - ], - [ - "Infineon Technologies, the
second-largest chip maker in
Europe, said Wednesday that it
planned to invest about \\$1
billion in a new factory in
Malaysia to expand its
automotive chip business and
be closer to customers in the
region." - ], - [ - " NEW YORK (Reuters) -
Washington Post Co. <A HREF
=\"http://www.investor.reuters.
com/FullQuote.aspx?ticker=WPO.
N target=/stocks/quickinfo/ful
lquote\">WPO.N</A>
said on Friday that quarterly
profit jumped, beating
analysts' forecasts, boosted
by results at its Kaplan
education unit and television
broadcasting operations." - ], - [ - "New orders for US-made durable
goods increased 0.2pc in
September, held back by a big
drop in orders for
transportation goods, the US
Commerce Department said
today." - ], - [ - "Siblings are the first ever to
be convicted for sending
boatloads of junk e-mail
pushing bogus products. Also:
Microsoft takes MSN music
download on a Euro trip....
Nokia begins legal battle
against European
counterparts.... and more." - ], - [ - "I always get a kick out of the
annual list published by
Forbes singling out the
richest people in the country.
It #39;s almost as amusing as
those on the list bickering
over their placement." - ], - [ - "Williams-Sonoma Inc., operator
of home furnishing chains
including Pottery Barn, said
third-quarter earnings rose 19
percent, boosted by store
openings and catalog sales." - ], - [ - "TOKYO - Mitsubishi Heavy
Industries said today it #39;s
in talks to buy a plot of land
in central Japan #39;s Nagoya
city from Mitsubishi Motors
for building aircraft parts." - ], - [ - "Japan #39;s Sumitomo Mitsui
Financial Group Inc. said
Tuesday it proposed to UFJ
Holdings Inc. that the two
banks merge on an equal basis
in its latest attempt to woo
UFJ away from a rival suitor." - ], - [ - "Oil futures prices were little
changed Thursday as traders
anxiously watched for
indications that the supply or
demand picture would change in
some way to add pressure to
the market or take some away." - ], - [ - " CHICAGO (Reuters) - Delta Air
Lines Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=DAL.N target=
/stocks/quickinfo/fullquote\"&g
t;DAL.N</A> said on
Tuesday it will cut wages by
10 percent and its chief
executive will go unpaid for
the rest of the year, but it
still warned of bankruptcy
within weeks unless more cuts
are made." - ], - [ - " WASHINGTON (Reuters) - A
former Fannie Mae <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=FNM.N
target=/stocks/quickinfo/fullq
uote\">FNM.N</A>
employee who gave U.S.
officials information about
what he saw as accounting
irregularities will not
testify as planned before a
congressional hearing next
week, a House committee said
on Friday." - ], - [ - "PARIS, Nov 4 (AFP) - The
European Aeronautic Defence
and Space Company reported
Thursday that its nine-month
net profit more than doubled,
thanks largely to sales of
Airbus aircraft, and raised
its full-year forecast." - ], - [ - "The number of people claiming
unemployment benefit last
month fell by 6,100 to
830,200, according to the
Office for National
Statistics." - ], - [ - "Tyler airlines are gearing up
for the beginning of holiday
travel, as officials offer
tips to help travelers secure
tickets and pass through
checkpoints with ease." - ], - [ - "A criminal trial scheduled to
start Monday involving former
Enron Corp. executives may
shine a rare and potentially
harsh spotlight on the inner
workings" - ], - [ - "Wal-Mart Stores Inc. #39;s
Asda, the UK #39;s second
biggest supermarket chain,
surpassed Marks amp; Spencer
Group Plc as Britain #39;s
largest clothing retailer in
the last three months,
according to the Sunday
Telegraph." - ], - [ - "Oil supply concerns and broker
downgrades of blue-chip
companies left stocks mixed
yesterday, raising doubts that
Wall Street #39;s year-end
rally would continue." - ], - [ - "Genentech Inc. said the
marketing of Rituxan, a cancer
drug that is the company #39;s
best-selling product, is the
subject of a US criminal
investigation." - ], - [ - " The world's No. 2 soft drink
company said on Thursday
quarterly profit rose due to
tax benefits." - ], - [ - "USATODAY.com - Personal
finance software programs are
the computer industry's
version of veggies: Everyone
knows they're good for you,
but it's just hard to get
anyone excited about them." - ], - [ - " NEW YORK (Reuters) - The
dollar rebounded on Monday
after last week's heavy
selloff, but analysts were
uncertain if the rally would
hold after fresh economic data
suggested the December U.S.
jobs report due Friday might
not live up to expectations." - ], - [ - " NEW YORK (Reuters) - U.S.
stock futures pointed to a
lower open on Wall Street on
Thursday, extending the
previous session's sharp
fall, with rising energy
prices feeding investor
concerns about corporate
profits and slower growth." - ], - [ - "MILAN General Motors and Fiat
on Wednesday edged closer to
initiating a legal battle that
could pit the two carmakers
against each other in a New
York City court room as early
as next month." - ], - [ - "Two of the Ford Motor Company
#39;s most senior executives
retired on Thursday in a sign
that the company #39;s deep
financial crisis has abated,
though serious challenges
remain." - ], - [ - " LONDON (Reuters) - Wall
Street was expected to start
little changed on Friday as
investors continue to fret
over the impact of high oil
prices on earnings, while
Boeing <A HREF=\"http://www.
investor.reuters.com/FullQuote
.aspx?ticker=BA.N target=/stoc
ks/quickinfo/fullquote\">BA.
N</A> will be eyed
after it reiterated its
earnings forecast." - ], - [ - "Having an always-on, fast net
connection is changing the way
Britons use the internet,
research suggests." - ], - [ - "Crude oil futures prices
dropped below \\$51 a barrel
yesterday as supply concerns
ahead of the Northern
Hemisphere winter eased after
an unexpectedly high rise in
US inventories." - ], - [ - "By Lilly Vitorovich Of DOW
JONES NEWSWIRES SYDNEY (Dow
Jones)--Rupert Murdoch has
seven weeks to convince News
Corp. (NWS) shareholders a
move to the US will make the
media conglomerate more
attractive to" - ], - [ - "The long-term economic health
of the United States is
threatened by \\$53 trillion in
government debts and
liabilities that start to come
due in four years when baby
boomers begin to retire." - ], - [ - "The Moscow Arbitration Court
ruled on Monday that the YUKOS
oil company must pay RUR
39.113bn (about \\$1.34bn) as
part of its back tax claim for
2001." - ], - [ - "NOVEMBER 11, 2004 -- Bankrupt
US Airways this morning said
it had reached agreements with
lenders and lessors to
continue operating nearly all
of its mainline and US Airways
Express fleets." - ], - [ - "The US government asks the
World Trade Organisation to
step in to stop EU member
states from \"subsidising\"
planemaker Airbus." - ], - [ - "Boston Scientific Corp.
(BSX.N: Quote, Profile,
Research) said on Wednesday it
received US regulatory
approval for a device to treat
complications that arise in
patients with end-stage kidney
disease who need dialysis." - ], - [ - "With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." - ], - [ - "Toyota Motor Corp. #39;s
shares fell for a second day,
after the world #39;s second-
biggest automaker had an
unexpected quarterly profit
drop." - ], - [ - "Britain-based HBOS says it
will file a complaint to the
European Commission against
Spanish bank Santander Central
Hispano (SCH) in connection
with SCH #39;s bid to acquire
British bank Abbey National" - ], - [ - "Verizon Wireless on Thursday
announced an agreement to
acquire all the PCS spectrum
licenses of NextWave Telecom
Inc. in 23 markets for \\$3
billion." - ], - [ - " WASHINGTON (Reuters) - The
PIMCO mutual fund group has
agreed to pay \\$50 million to
settle fraud charges involving
improper rapid dealing in
mutual fund shares, the U.S.
Securities and Exchange
Commission said on Monday." - ], - [ - "The Conference Board reported
Thursday that the Leading
Economic Index fell for a
third consecutive month in
August, suggesting slower
economic growth ahead amid
rising oil prices." - ], - [ - " SAN FRANCISCO (Reuters) -
Software maker Adobe Systems
Inc.<A HREF=\"http://www.inv
estor.reuters.com/FullQuote.as
px?ticker=ADBE.O target=/stock
s/quickinfo/fullquote\">ADBE
.O</A> on Thursday
posted a quarterly profit that
rose more than one-third from
a year ago, but shares fell 3
percent after the maker of
Photoshop and Acrobat software
did not raise forecasts for
fiscal 2005." - ], - [ - "William Morrison Supermarkets
has agreed to sell 114 small
Safeway stores and a
distribution centre for 260.2
million pounds. Morrison
bought these stores as part of
its 3 billion pound" - ], - [ - "Pepsi pushes a blue version of
Mountain Dew only at Taco
Bell. Is this a winning
strategy?" - ], - [ - "As the election approaches,
Congress abandons all pretense
of fiscal responsibility,
voting tax cuts that would
drive 10-year deficits past
\\$3 trillion." - ], - [ - "ServiceMaster profitably
bundles services and pays a
healthy 3.5 dividend." - ], - [ - "\\$222.5 million -- in an
ongoing securities class
action lawsuit against Enron
Corp. The settlement,
announced Friday and" - ], - [ - " NEW YORK (Reuters) -
Lifestyle guru Martha Stewart
said on Wednesday she wants
to start serving her prison
sentence for lying about a
suspicious stock sale as soon
as possible, so she can put
her \"nightmare\" behind her." - ], - [ - "Apple Computer's iPod remains
the king of digital music
players, but robust pretenders
to the throne have begun to
emerge in the Windows
universe. One of them is the
Zen Touch, from Creative Labs." - ], - [ - "SAN FRANCISCO (CBS.MW) --
Crude futures closed under
\\$46 a barrel Wednesday for
the first time since late
September and heating-oil and
unleaded gasoline prices
dropped more than 6 percent
following an across-the-board
climb in US petroleum
inventories." - ], - [ - "The University of Iowa #39;s
market for US presidential
futures, founded 16-years ago,
has been overtaken by a
Dublin-based exchange that is
now 25 times larger." - ], - [ - "President Bush #39;s drive to
deploy a multibillion-dollar
shield against ballistic
missiles was set back on
Wednesday by what critics
called a stunning failure of
its first full flight test in
two years." - ], - [ - "Air travelers moved one step
closer to being able to talk
on cell phones and surf the
Internet from laptops while in
flight, thanks to votes by the
Federal Communications
Commission yesterday." - ], - [ - "DESPITE the budget deficit,
continued increases in oil and
consumer prices, the economy,
as measured by gross domestic
product, grew by 6.3 percent
in the third" - ], - [ - "Consumers who cut it close by
paying bills from their
checking accounts a couple of
days before depositing funds
will be out of luck under a
new law that takes effect Oct.
28." - ], - [ - "Component problems meant
Brillian's new big screens
missed the NFL's kickoff
party." - ], - [ - "A Russian court on Thursday
rejected an appeal by the
Yukos oil company seeking to
overturn a freeze on the
accounts of the struggling oil
giant #39;s core subsidiaries." - ], - [ - "Switzerland #39;s struggling
national airline reported a
second-quarter profit of 45
million Swiss francs (\\$35.6
million) Tuesday, although its
figures were boosted by a
legal settlement in France." - ], - [ - "SIPTU has said it is strongly
opposed to any privatisation
of Aer Lingus as pressure
mounts on the Government to
make a decision on the future
funding of the airline." - ], - [ - "Molson Inc. Chief Executive
Officer Daniel O #39;Neill
said he #39;ll provide
investors with a positive #39;
#39; response to their
concerns over the company
#39;s plan to let stock-
option holders vote on its
planned merger with Adolph
Coors Co." - ], - [ - " NEW YORK (Reuters) - The
world's largest gold producer,
Newmont Mining Corp. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=NEM
.N target=/stocks/quickinfo/fu
llquote\">NEM.N</A>,
on Wednesday said higher gold
prices drove up quarterly
profit by 12.5 percent, even
though it sold less of the
precious metal." - ], - [ - " WASHINGTON (Reuters) - Fannie
Mae executives and their
regulator squared off on
Wednesday, with executives
denying any accounting
irregularity and the regulator
saying the housing finance
company's management may need
to go." - ], - [ - "As the first criminal trial
stemming from the financial
deals at Enron opened in
Houston on Monday, it is
notable as much for who is not
among the six defendants as
who is - and for how little
money was involved compared
with how much in other Enron" - ], - [ - "LONDON (CBS.MW) -- British
bank Barclays on Thursday said
it is in talks to buy a
majority stake in South
African bank ABSA. Free!" - ], - [ - "Investors sent stocks sharply
lower yesterday as oil prices
continued their climb higher
and new questions about the
safety of arthritis drugs
pressured pharmaceutical
stocks." - ], - [ - "Reuters - The head of UAL
Corp.'s United\\Airlines said
on Thursday the airline's
restructuring plan\\would lead
to a significant number of job
losses, but it was\\not clear
how many." - ], - [ - "com September 14, 2004, 9:12
AM PT. With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." - ], - [ - " NEW YORK (Reuters) -
Children's Place Retail Stores
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=PLCE.O target=/sto
cks/quickinfo/fullquote\">PL
CE.O</A> said on
Wednesday it will buy 313
retail stores from Walt
Disney Co., and its stock rose
more than 14 percent in early
morning trade." - ], - [ - "ALBANY, N.Y. -- A California-
based company that brokers
life, accident, and disability
policies for leading US
companies pocketed millions of
dollars a year in hidden
payments from insurers and
from charges on clients'
unsuspecting workers, New York
Attorney General Eliot Spitzer
charged yesterday." - ], - [ - "NORTEL Networks plans to slash
its workforce by 3500, or ten
per cent, as it struggles to
recover from an accounting
scandal that toppled three top
executives and led to a
criminal investigation and
lawsuits." - ], - [ - "Ebay Inc. (EBAY.O: Quote,
Profile, Research) said on
Friday it would buy Rent.com,
an Internet housing rental
listing service, for \\$415
million in a deal that gives
it access to a new segment of
the online real estate market." - ], - [ - "Noranda Inc., Canada #39;s
biggest mining company, began
exclusive talks on a takeover
proposal from China Minmetals
Corp. that would lead to the
spinoff of Noranda #39;s
aluminum business to
shareholders." - ], - [ - "Google warned Thursday that
increased competition and the
maturing of the company would
result in an quot;inevitable
quot; slowing of its growth." - ], - [ - " TOKYO (Reuters) - The Nikkei
average rose 0.55 percent by
midsession on Wednesday as
some techs including Advantest
Corp. gained ground after
Wall Street reacted positively
to results from Intel Corp.
released after the U.S. market
close." - ], - [ - " ATLANTA (Reuters) - Soft
drink giant Coca-Cola Co.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=KO.N target=/stocks/quic
kinfo/fullquote\">KO.N</A
>, stung by a prolonged
downturn in North America,
Germany and other major
markets, on Thursday lowered
its key long-term earnings
and sales targets." - ], - [ - "A Canadian court approved Air
Canada #39;s (AC.TO: Quote,
Profile, Research) plan of
arrangement with creditors on
Monday, clearing the way for
the world #39;s 11th largest
airline to emerge from
bankruptcy protection at the
end of next month" - ], - [ - " LONDON (Reuters) - Oil prices
eased on Monday after rebels
in Nigeria withdrew a threat
to target oil operations, but
lingering concerns over
stretched supplies ahead of
winter kept prices close to
\\$50." - ], - [ - " LONDON (Reuters) - Oil prices
climbed above \\$42 a barrel on
Wednesday, rising for the
third day in a row as cold
weather gripped the U.S.
Northeast, the world's biggest
heating fuel market." - ], - [ - "Travelers headed home for
Thanksgiving were greeted
Wednesday with snow-covered
highways in the Midwest, heavy
rain and tornadoes in parts of
the South, and long security
lines at some of the nation
#39;s airports." - ], - [ - "The union representing flight
attendants on Friday said it
mailed more than 5,000 strike
authorization ballots to its
members employed by US Airways
as both sides continued talks
that are expected to stretch
through the weekend." - ], - [ - "LOS ANGELES (CBS.MW) - The US
Securities and Exchange
Commission is probing
transactions between Delphi
Corp and EDS, which supplies
the automotive parts and
components giant with
technology services, Delphi
said late Wednesday." - ], - [ - "MONTREAL (CP) - Molson Inc.
and Adolph Coors Co. are
sweetening their brewery
merger plan with a special
dividend to Molson
shareholders worth \\$381
million." - ], - [ - "WELLINGTON: National carrier
Air New Zealand said yesterday
the Australian Competition
Tribunal has approved a
proposed alliance with Qantas
Airways Ltd, despite its
rejection in New Zealand." - ], - [ - "\"Everyone's nervous,\" Acting
Undersecretary of Defense
Michael W. Wynne warned in a
confidential e-mail to Air
Force Secretary James G. Roche
on July 8, 2003." - ], - [ - "Reuters - Alpharma Inc. on
Friday began\\selling a cheaper
generic version of Pfizer
Inc.'s #36;3\\billion a year
epilepsy drug Neurontin
without waiting for a\\court
ruling on Pfizer's request to
block the copycat medicine." - ], - [ - "Opinion: Privacy hysterics
bring old whine in new bottles
to the Internet party. The
desktop search beta from this
Web search leader doesn #39;t
do anything you can #39;t do
already." - ], - [ - "The Nordics fared well because
of their long-held ideals of
keeping corruption clamped
down and respect for
contracts, rule of law and
dedication to one-on-one
business relationships." - ], - [ - "Don't bother with the small
stuff. Here's what really
matters to your lender." - ], - [ - "UK interest rates have been
kept on hold at 4.75 following
the latest meeting of the Bank
of England #39;s rate-setting
committee." - ], - [ - "Resurgent oil prices paused
for breath as the United
States prepared to draw on its
emergency reserves to ease
supply strains caused by
Hurricane Ivan." - ], - [ - "President Bush, who credits
three years of tax relief
programs with helping
strengthen the slow economy,
said Saturday he would sign
into law the Working Families
Tax Relief Act to preserve tax
cuts." - ], - [ - "HEN investors consider the
bond market these days, the
low level of interest rates
should be more cause for worry
than for gratitude." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks rallied on Monday after
software maker PeopleSoft Inc.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=PSFT.O target=/stocks/qu
ickinfo/fullquote\">PSFT.O&l
t;/A> accepted a sweetened
\\$10.3 billion buyout by rival
Oracle Corp.'s <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=ORCL.O ta
rget=/stocks/quickinfo/fullquo
te\">ORCL.O</A> and
other big deals raised
expectations of more
takeovers." - ], - [ - "The New Jersey-based Accoona
Corporation, an industry
pioneer in artificial
intelligence search
technology, announced on
Monday the launch of Accoona." - ], - [ - "Goldman Sachs Group Inc. on
Thursday said fourth-quarter
profit rose as its fixed-
income, currency and
commodities business soared
while a rebounding stock
market boosted investment
banking." - ], - [ - " NEW YORK (Reuters) - The
dollar rose on Monday in a
retracement from last week's
steep losses, but dealers said
the bias toward a weaker
greenback remained intact." - ], - [ - "Moscow - Russia plans to
combine Gazprom, the world
#39;s biggest natural gas
producer, with state-owned oil
producer Rosneft, easing rules
for trading Gazprom shares and
creating a company that may
dominate the country #39;s
energy industry." - ], - [ - "Diversified manufacturer
Honeywell International Inc.
(HON.N: Quote, Profile,
Research) posted a rise in
quarterly profit as strong
demand for aerospace equipment
and automobile components" - ], - [ - "Reuters - U.S. housing starts
jumped a\\larger-than-expected
6.4 percent in October to the
busiest pace\\since December as
buyers took advantage of low
mortgage rates,\\a government
report showed on Wednesday." - ], - [ - "The Securities and Exchange
Commission ordered mutual
funds to stop paying higher
commissions to brokers who
promote the companies' funds
and required portfolio
managers to reveal investments
in funds they supervise." - ], - [ - "Sumitomo Mitsui Financial
Group (SMFG), Japans second
largest bank, today put
forward a 3,200 billion (\\$29
billion) takeover bid for
United Financial Group (UFJ),
the countrys fourth biggest
lender, in an effort to regain
initiative in its bidding" - ], - [ - " NEW YORK (Reuters) - U.S.
chain store retail sales
slipped during the
Thanksgiving holiday week, as
consumers took advantage of
discounted merchandise, a
retail report said on
Tuesday." - ], - [ - "By George Chamberlin , Daily
Transcript Financial
Correspondent. Concerns about
oil production leading into
the winter months sent shivers
through the stock market
Wednesday." - ], - [ - "Airbus has withdrawn a filing
that gave support for
Microsoft in an antitrust case
before the European Union
#39;s Court of First Instance,
a source close to the
situation said on Friday." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks rose on Wednesday
lifted by a merger between
retailers Kmart and Sears,
better-than-expected earnings
from Hewlett-Packard and data
showing a slight rise in core
inflation." - ], - [ - "European Commission president
Romano Prodi has unveiled
proposals to loosen the
deficit rules under the EU
Stability Pact. The loosening
was drafted by monetary
affairs commissioner Joaquin
Almunia, who stood beside the
president at the announcement." - ], - [ - "Retail sales in Britain saw
the fastest growth in
September since January,
casting doubts on the view
that the economy is slowing
down, according to official
figures released Thursday." - ], - [ - " NEW YORK (Reuters) -
Interstate Bakeries Corp.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=IBC.N target=/stocks/qui
ckinfo/fullquote\">IBC.N<
/A>, maker of Hostess
Twinkies and Wonder Bread,
filed for bankruptcy on
Wednesday after struggling
with more than \\$1.3 billion
in debt and high costs." - ], - [ - "Delta Air Lines (DAL.N: Quote,
Profile, Research) on Thursday
said it reached a deal with
FedEx Express to sell eight
McDonnell Douglas MD11
aircraft and four spare
engines for delivery in 2004." - ], - [ - "Leading OPEC producer Saudi
Arabia said on Monday in
Vienna, Austria, that it had
made a renewed effort to
deflate record high world oil
prices by upping crude output
again." - ], - [ - "The founders of the Pilgrim
Baxter amp; Associates money-
management firm agreed
yesterday to personally fork
over \\$160 million to settle
charges they allowed a friend
to" - ], - [ - "IBM Corp. Tuesday announced
plans to acquire software
vendor Systemcorp ALG for an
undisclosed amount. Systemcorp
of Montreal makes project
portfolio management software
aimed at helping companies
better manage their IT
projects." - ], - [ - "Forbes.com - By now you
probably know that earnings of
Section 529 college savings
accounts are free of federal
tax if used for higher
education. But taxes are only
part of the problem. What if
your investments tank? Just
ask Laurence and Margo
Williams of Alexandria, Va. In
2000 they put #36;45,000 into
the Virginia Education Savings
Trust to open accounts for
daughters Lea, now 5, and
Anne, now 3. Since then their
investment has shrunk 5 while
the average private college
tuition has climbed 18 to
#36;18,300." - ], - [ - "Coca-Cola Amatil Ltd.,
Australia #39;s biggest soft-
drink maker, offered A\\$500
million (\\$382 million) in
cash and stock for fruit
canner SPC Ardmona Ltd." - ], - [ - "US technology shares tumbled
on Friday after technology
bellwether Intel Corp.
(INTC.O: Quote, Profile,
Research) slashed its revenue
forecast, but blue chips were
only moderately lower as drug
and industrial stocks made
solid gains." - ], - [ - " WASHINGTON (Reuters) - Final
U.S. government tests on an
animal suspected of having mad
cow disease were not yet
complete, the U.S. Agriculture
Department said, with no
announcement on the results
expected on Monday." - ], - [ - "Metro, Germany's biggest
retailer, turns in weaker-
than-expected profits as sales
at its core supermarkets
division dip lower." - ], - [ - "BOSTON (CBS.MW) -- First
Command has reached a \\$12
million settlement with
federal regulators for making
misleading statements and
omitting important information
when selling mutual funds to
US military personnel." - ], - [ - "The federal government, banks
and aircraft lenders are
putting the clamps on
airlines, particularly those
operating under bankruptcy
protection." - ], - [ - "EURO DISNEY, the financially
crippled French theme park
operator, has admitted that
its annual losses more than
doubled last financial year as
it was hit by a surge in
costs." - ], - [ - "WASHINGTON The idea of a no-
bid contract for maintaining
airport security equipment has
turned into a non-starter for
the Transportation Security
Administration." - ], - [ - "Eyetech (EYET:Nasdaq - news -
research) did not open for
trading Friday because a Food
and Drug Administration
advisory committee is meeting
to review the small New York-
based biotech #39;s
experimental eye disease drug." - ], - [ - "On September 13, 2001, most
Americans were still reeling
from the shock of the
terrorist attacks on New York
and the Pentagon two days
before." - ], - [ - "Domestic air travelers could
be surfing the Web by 2006
with government-approved
technology that allows people
access to high-speed Internet
connections while they fly." - ], - [ - "GENEVA: Cross-border
investment is set to bounce in
2004 after three years of deep
decline, reflecting a stronger
world economy and more
international merger activity,
the United Nations (UN) said
overnight." - ], - [ - "Chewing gum giant Wm. Wrigley
Jr. Co. on Thursday said it
plans to phase out production
of its Eclipse breath strips
at a plant in Phoenix, Arizona
and shift manufacturing to
Poznan, Poland." - ], - [ - "com September 16, 2004, 7:58
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." - ], - [ - "Tightness in the labour market
notwithstanding, the prospects
for hiring in the third
quarter are down from the
second quarter, according to
the new Manpower Employment
Outlook Survey." - ], - [ - " NEW YORK (Reuters) - The
dollar rose on Friday, after a
U.S. report showed consumer
prices in line with
expections, reminding
investors that the Federal
Reserve was likely to
continue raising interest
rates, analysts said." - ], - [ - "Last year some election
watchers made a bold
prediction that this
presidential election would
set a record: the first half
billion dollar campaign in
hard money alone." - ], - [ - "It #39;s one more blow to
patients who suffer from
arthritis. Pfizer, the maker
of Celebrex, says it #39;s
painkiller poses an increased
risk of heart attacks to
patients using the drugs." - ], - [ - "The economic growth rate in
the July-September period was
revised slightly downward from
an already weak preliminary
report, the government said
Wednesday." - ], - [ - "A drug company executive who
spoke out in support of
Montgomery County's proposal
to import drugs from Canada
and similar legislation before
Congress said that his company
has launched an investigation
into his political activities." - ], - [ - "SYDNEY (AFP) - Australia #39;s
commodity exports are forecast
to increase by 15 percent to a
record 95 billion dollars (71
million US), the government
#39;s key economic forecaster
said." - ], - [ - "Google won a major legal
victory when a federal judge
ruled that the search engines
advertising policy does not
violate federal trademark
laws." - ], - [ - "AT amp;T Corp. on Thursday
said it is reducing one fifth
of its workforce this year and
will record a non-cash charge
of approximately \\$11." - ], - [ - "A rift appeared within Canada
#39;s music industry yesterday
as prominent artists called on
the CRTC to embrace satellite
radio and the industry warned
of lost revenue and job
losses." - ], - [ - "NEW DELHI: India and Pakistan
agreed on Monday to step up
cooperation in the energy
sector, which could lead to
Pakistan importing large
amounts of diesel fuel from
its neighbour, according to
Pakistani Foreign Minister
Khurshid Mehmood Kasuri." - ], - [ - "TORONTO (CP) - Glamis Gold of
Reno, Nev., is planning a
takeover bid for Goldcorp Inc.
of Toronto - but only if
Goldcorp drops its
\\$2.4-billion-Cdn offer for
another Canadian firm, made in
early December." - ], - [ - "This week will see the release
of October new and existing
home sales, a measure of
strength in the housing
industry. But the short
holiday week will also leave
investors looking ahead to the
holiday travel season." - ], - [ - " BETHESDA, Md. (Reuters) - The
use of some antidepressant
drugs appears linked to an
increase in suicidal behavior
in some children and teen-
agers, a U.S. advisory panel
concluded on Tuesday." - ], - [ - " NEW YORK (Reuters) - U.S.
technology stocks opened lower
on Thursday after a sales
warning from Applied Materials
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AMAT.O target=/sto
cks/quickinfo/fullquote\">AM
AT.O</A>, while weekly
jobless claims data met Wall
Street's expectations,
leaving the Dow and S P 500
market measures little
changed." - ], - [ - "CHICAGO : Interstate Bakeries
Corp., the maker of popular,
old-style snacks Twinkies and
Hostess Cakes, filed for
bankruptcy, citing rising
costs and falling sales." - ], - [ - "Delta Air Lines (DAL:NYSE -
commentary - research) will
cut employees and benefits but
give a bigger-than-expected
role to Song, its low-cost
unit, in a widely anticipated
but still unannounced
overhaul, TheStreet.com has
learned." - ], - [ - "Instead of the skinny black
line, showing a hurricane
#39;s forecast track,
forecasters have drafted a
couple of alternative graphics
to depict where the storms
might go -- and they want your
opinion." - ], - [ - "BERLIN - Volkswagen AG #39;s
announcement this week that it
has forged a new partnership
deal with Malaysian carmaker
Proton comes as a strong euro
and Europe #39;s weak economic
performance triggers a fresh
wave of German investment in
Asia." - ], - [ - "Consumers in Dublin pay more
for basic goods and services
that people elsewhere in the
country, according to figures
released today by the Central
Statistics Office." - ], - [ - "LIBERTY Media #39;s move last
week to grab up to 17.1 per
cent of News Corporation
voting stock has prompted the
launch of a defensive
shareholder rights plan." - ], - [ - "The blue-chip Hang Seng Index
rose 171.88 points, or 1.22
percent, to 14,066.91. On
Friday, the index had slipped
31.58 points, or 0.2 percent." - ], - [ - "Shares plunge after company
says its vein graft treatment
failed to show benefit in
late-stage test. CHICAGO
(Reuters) - Biotechnology
company Corgentech Inc." - ], - [ - "US blue-chip stocks rose
slightly on Friday as
government data showed better-
than-expected demand in August
for durable goods other than
transportation equipment, but
climbing oil prices limited
gains." - ], - [ - "Business software maker
PeopleSoft Inc. said Monday
that it expects third-quarter
revenue to range between \\$680
million and \\$695 million,
above average Wall Street
estimates of \\$651." - ], - [ - "Reuters - Enron Corp. ,
desperate to\\meet profit
targets, \"parked\" unwanted
power generating barges\\at
Merrill Lynch in a sham sale
designed to be reversed,
a\\prosecutor said on Tuesday
in the first criminal trial
of\\former executives at the
fallen energy company." - ], - [ - " NEW YORK (Reuters) - The
dollar rebounded on Monday
after a heavy selloff last
week, but analysts were
uncertain if the rally could
hold as the drumbeat of
expectation began for to the
December U.S. jobs report due
Friday." - ], - [ - "Coles Myer Ltd. Australia
#39;s biggest retailer,
increased second-half profit
by 26 percent after opening
fuel and convenience stores,
selling more-profitable
groceries and cutting costs." - ], - [ - "MOSCOW: Us oil major
ConocoPhillips is seeking to
buy up to 25 in Russian oil
giant Lukoil to add billions
of barrels of reserves to its
books, an industry source
familiar with the matter said
on Friday." - ], - [ - "US Airways Group (otc: UAIRQ -
news - people ) on Thursday
said it #39;ll seek a court
injunction to prohibit a
strike by disaffected unions." - ], - [ - "Shares in Unilever fall after
the Anglo-Dutch consumer goods
giant issued a surprise
profits warning." - ], - [ - "SAN FRANCISCO (CBS.MW) - The
Canadian government will sell
its 19 percent stake in Petro-
Canada for \\$2.49 billion,
according to the final
prospectus filed with the US
Securities and Exchange
Commission Thursday." - ], - [ - " HYDERABAD, India (Reuters) -
Microsoft Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=MSFT.O t
arget=/stocks/quickinfo/fullqu
ote\">MSFT.O</A> will
hire several hundred new staff
at its new Indian campus in
the next year, its chief
executive said on Monday, in a
move aimed at strengthening
its presence in Asia's fourth-
biggest economy." - ], - [ - "Alitalia SpA, Italy #39;s
largest airline, reached an
agreement with its flight
attendants #39; unions to cut
900 jobs, qualifying the
company for a government
bailout that will keep it in
business for another six
months." - ], - [ - "P amp;Os cutbacks announced
today are the result of the
waves of troubles that have
swamped the ferry industry of
late. Some would say the
company has done well to
weather the storms for as long
as it has." - ], - [ - "TORONTO (CP) - Russia #39;s
Severstal has made an offer to
buy Stelco Inc., in what #39;s
believed to be one of several
competing offers emerging for
the restructuring but
profitable Hamilton steel
producer." - ], - [ - "Walt Disney Co. #39;s
directors nominated Michael
Ovitz to serve on its board
for another three years at a
meeting just weeks before
forcing him out of his job as" - ], - [ - "The European Union, Japan,
Brazil and five other
countries won World Trade
Organization approval to
impose tariffs worth more than
\\$150 million a year on
imports from the United" - ], - [ - "Industrial conglomerate
Honeywell International on
Wednesday said it has filed a
lawsuit against 34 electronics
companies including Apple
Computer and Eastman Kodak,
claiming patent infringement
of its liquid crystal display
technology." - ], - [ - "Australia #39;s Computershare
has agreed to buy EquiServe of
the United States for US\\$292
million (\\$423 million),
making it the largest US share
registrar and driving its
shares up by a third." - ], - [ - "Interest rates on short-term
Treasury securities were mixed
in yesterday's auction. The
Treasury Department sold \\$18
billion in three-month bills
at a discount rate of 1.640
percent, up from 1.635 percent
last week. An additional \\$16
billion was sold in six-month
bills at a rate of 1.840
percent, down from 1.860
percent." - ], - [ - "Two top executives of scandal-
tarred insurance firm Marsh
Inc. were ousted yesterday,
the company said, the latest
casualties of an industry
probe by New York's attorney
general." - ], - [ - "USDA #39;s Animal Plant Health
Inspection Service (APHIS)
this morning announced it has
confirmed a detection of
soybean rust from two test
plots at Louisiana State
University near Baton Rouge,
Louisiana." - ], - [ - "Mexican Cemex, being the third
largest cement maker in the
world, agreed to buy its
British competitor - RMC Group
- for \\$5.8 billion, as well
as their debts in order to
expand their activity on the
building materials market of
the USA and Europe." - ], - [ - "The world's largest insurance
group pays \\$126m in fines as
part of a settlement with US
regulators over its dealings
with two firms." - ], - [ - "The tobacco firm John Player
amp; Sons has announced plans
to lay off 90 workers at its
cigarette factory in Dublin.
The company said it was
planning a phased closure of
the factory between now and
February as part of a review
of its global operations." - ], - [ - "AP - Consumers borrowed more
freely in September,
especially when it came to
racking up charges on their
credit cards, the Federal
Reserve reported Friday." - ], - [ - "Bold, innovative solutions are
key to addressing the rapidly
rising costs of higher
education and the steady
reduction in government-
subsidized help to finance
such education." - ], - [ - "Just as the PhD crowd emerge
with different interpretations
of today's economy, everyday
Americans battling to balance
the checkbook hold diverse
opinions about where things
stand now and in the future." - ], - [ - "Authorities here are always
eager to show off their
accomplishments, so when
Beijing hosted the World
Toilet Organization conference
last week, delegates were
given a grand tour of the
city's toilets." - ], - [ - "WASHINGTON Trying to break a
deadlock on energy policy, a
diverse group of
environmentalists, academics
and former government
officials were to publish a
report on Wednesday that
presents strategies for making
the United States cleaner,
more competitive" - ], - [ - "A consortium led by Royal
Dutch/Shell Group that is
developing gas reserves off
Russia #39;s Sakhalin Island
said Thursday it has struck a
US\\$6 billion (euro4." - ], - [ - "An audit by international
observers supported official
elections results that gave
President Hugo Chavez a
victory over a recall vote
against him, the secretary-
general of the Organisation of
American States announced." - ], - [ - "Apple is recalling 28,000
faulty batteries for its
15-inch Powerbook G4 laptops." - ], - [ - "The former Chief Executive
Officer of Computer Associates
was indicted by a federal
grand jury in New York
Wednesday for allegedly
participating in a massive
fraud conspiracy and an
elaborate cover up of a scheme
that cost investors" - ], - [ - "Honeywell International Inc.,
the world #39;s largest
supplier of building controls,
agreed to buy Novar Plc for
798 million pounds (\\$1.53
billion) to expand its
security, fire and
ventilation-systems business
in Europe." - ], - [ - "San Francisco investment bank
Thomas Weisel Partners on
Thursday agreed to pay \\$12.5
million to settle allegations
that some of the stock
research the bank published
during the Internet boom was
tainted by conflicts of
interest." - ], - [ - "Forbes.com - Peter Frankling
tapped an unusual source to
fund his new business, which
makes hot-dog-shaped ice cream
treats known as Cool Dogs: Two
investors, one a friend and
the other a professional
venture capitalist, put in
more than #36;100,000 each
from their Individual
Retirement Accounts. Later
Franklin added #36;150,000
from his own IRA." - ], - [ - "A San Diego insurance
brokerage has been sued by New
York Attorney General Elliot
Spitzer for allegedly
soliciting payoffs in exchange
for steering business to
preferred insurance companies." - ], - [ - "The European Union agreed
Monday to lift penalties that
have cost American exporters
\\$300 million, following the
repeal of a US corporate tax
break deemed illegal under
global trade rules." - ], - [ - " LONDON (Reuters) - European
stock markets scaled
near-2-1/2 year highs on
Friday as oil prices held
below \\$48 a barrel, and the
euro held off from mounting
another assault on \\$1.30 but
hovered near record highs
against the dollar." - ], - [ - "Reuters - The company behind
the Atkins Diet\\on Friday
shrugged off a recent decline
in interest in low-carb\\diets
as a seasonal blip, and its
marketing chief said\\consumers
would cut out starchy foods
again after picking up\\pounds
over the holidays." - ], - [ - "There #39;s something to be
said for being the quot;first
mover quot; in an industry
trend. Those years of extra
experience in tinkering with a
new idea can be invaluable in
helping the first" - ], - [ - " NEW YORK (Reuters) - Shares
of Chiron Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=CHIR.O t
arget=/stocks/quickinfo/fullqu
ote\">CHIR.O</A> fell
7 percent before the market
open on Friday, a day after
the biopharmaceutical company
said it is delaying shipment
of its flu vaccine, Fluvirin,
because lots containing 4
million vaccines do not meet
product sterility standards." - ], - [ - "The nation's top
telecommunications regulator
said yesterday he will push --
before the next president is
inaugurated -- to protect
fledgling Internet telephone
services from getting taxed
and heavily regulated by the
50 state governments." - ], - [ - "DALLAS -- Belo Corp. said
yesterday that it would cut
250 jobs, more than half of
them at its flagship
newspaper, The Dallas Morning
News, and that an internal
investigation into circulation
overstatements" - ], - [ - "The Federal Reserve still has
some way to go to restore US
interest rates to more normal
levels, Philadelphia Federal
Reserve President Anthony
Santomero said on Monday." - ], - [ - "Delta Air Lines (DAL.N: Quote,
Profile, Research) said on
Wednesday its auditors have
expressed doubt about the
airline #39;s financial
viability." - ], - [ - "Takeover target Ronin Property
Group said it would respond to
an offer by Multiplex Group
for all the securities in the
company in about three weeks." - ], - [ - "Google Inc. is trying to
establish an online reading
room for five major libraries
by scanning stacks of hard-to-
find books into its widely
used Internet search engine." - ], - [ - "HOUSTON--(BUSINESS
WIRE)--Sept. 1, 2004-- L
#39;operazione crea una
centrale globale per l
#39;analisi strategica el
#39;approfondimento del
settore energetico IHS Energy,
fonte globale leader di
software, analisi e
informazioni" - ], - [ - "The US airline industry,
riddled with excess supply,
will see a significant drop in
capacity, or far fewer seats,
as a result of at least one
airline liquidating in the
next year, according to
AirTran Airways Chief
Executive Joe Leonard." - ], - [ - "Boeing (nyse: BA - news -
people ) Chief Executive Harry
Stonecipher is keeping the
faith. On Monday, the head of
the aerospace and military
contractor insists he #39;s
confident his firm will
ultimately win out" - ], - [ - "Australia #39;s biggest
supplier of fresh milk,
National Foods, has posted a
net profit of \\$68.7 million,
an increase of 14 per cent on
last financial year." - ], - [ - "Lawyers for customers suing
Merck amp; Co. want to
question CEO Raymond Gilmartin
about what he knew about the
dangers of Vioxx before the
company withdrew the drug from
the market because of health
hazards." - ], - [ - "New York; September 23, 2004 -
The Department of Justice
(DoJ), FBI and US Attorney
#39;s Office handed down a
10-count indictment against
former Computer Associates
(CA) chairman and CEO Sanjay
Kumar and Stephen Richards,
former CA head of worldwide
sales." - ], - [ - "PACIFIC Hydro shares yesterday
caught an updraught that sent
them more than 20 per cent
higher after the wind farmer
moved to flush out a bidder." - ], - [ - " NEW YORK (Reuters) - U.S.
consumer confidence retreated
in August while Chicago-area
business activity slowed,
according to reports on
Tuesday that added to worries
the economy's patch of slow
growth may last beyond the
summer." - ], - [ - "Unilever has reported a three
percent rise in third-quarter
earnings but warned it is
reviewing its targets up to
2010, after issuing a shock
profits warning last month." - ], - [ - " LONDON (Reuters) - Oil prices
extended recent heavy losses
on Wednesday ahead of weekly
U.S. data expected to show
fuel stocks rising in time
for peak winter demand." - ], - [ - "Vodafone has increased the
competition ahead of Christmas
with plans to launch 10
handsets before the festive
season. The Newbury-based
group said it will begin
selling the phones in
November." - ], - [ - "A former assistant treasurer
at Enron Corp. (ENRNQ.PK:
Quote, Profile, Research)
agreed to plead guilty to
conspiracy to commit
securities fraud on Tuesday
and will cooperate with" - ], - [ - "UK house prices fell by 1.1 in
October, confirming a
softening of the housing
market, Halifax has said. The
UK #39;s biggest mortgage
lender said prices rose 18." - ], - [ - "More than six newspaper
companies have received
letters from the Securities
and Exchange Commission
seeking information about
their circulation practices." - ], - [ - "THE 64,000 dollar -
correction, make that 500
million dollar -uestion
hanging over Shire
Pharmaceuticals is whether the
5 per cent jump in the
companys shares yesterday
reflects relief that US
regulators have finally
approved its drug for" - ], - [ - "Businesses saw inventories
rise in July and sales picked
up, the government reported
Wednesday. The Commerce
Department said that stocks of
unsold goods increased 0.9 in
July, down from a 1.1 rise in
June." - ], - [ - "Shares of Genta Inc. (GNTA.O:
Quote, Profile, Research)
soared nearly 50 percent on
Monday after the biotechnology
company presented promising
data on an experimental
treatment for blood cancers." - ], - [ - "SBC Communications Inc. plans
to cut at least 10,000 jobs,
or 6 percent of its work
force, by the end of next year
to compensate for a drop in
the number of local-telephone
customers." - ], - [ - " WASHINGTON (Reuters) - Major
cigarette makers go on trial
on Tuesday in the U.S.
government's \\$280 billion
racketeering case that
charges the tobacco industry
with deliberately deceiving
the public about the risks of
smoking since the 1950s." - ], - [ - "Federal Reserve policy-makers
raised the benchmark US
interest rate a quarter point
to 2.25 per cent and restated
a plan to carry out" - ], - [ - " DETROIT (Reuters) - A
Canadian law firm on Tuesday
said it had filed a lawsuit
against Ford Motor Co. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=F
.N target=/stocks/quickinfo/fu
llquote\">F.N</A> over
what it claims are defective
door latches on about 400,000
of the automaker's popular
pickup trucks and SUVs." - ], - [ - "AstraZeneca Plc suffered its
third setback in two months on
Friday as lung cancer drug
Iressa failed to help patients
live longer" - ], - [ - "Bruce Wasserstein, the
combative chief executive of
investment bank Lazard, is
expected to agree this week
that he will quit the group
unless he can pull off a
successful" - ], - [ - "Dr. David J. Graham, the FDA
drug safety reviewer who
sounded warnings over five
drugs he felt could become the
next Vioxx has turned to a
Whistleblower protection group
for legal help." - ], - [ - "The Kmart purchase of Sears,
Roebuck may be the ultimate
expression of that old saying
in real estate: location,
location, location." - ], - [ - "Sprint Corp. (FON.N: Quote,
Profile, Research) on Friday
said it plans to cut up to 700
jobs as it realigns its
business to focus on wireless
and Internet services and
takes a non-cash network
impairment charge." - ], - [ - "Says that amount would have
been earned for the first 9
months of 2004, before AT
amp;T purchase. LOS ANGELES,
(Reuters) - Cingular Wireless
would have posted a net profit
of \\$650 million for the first
nine months" - ], - [ - " CHICAGO (Reuters) - Goodyear
Tire Rubber Co. <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=GT.N t
arget=/stocks/quickinfo/fullqu
ote\">GT.N</A> said
on Friday it will cut 340 jobs
in its engineered products and
chemical units as part of its
cost-reduction efforts,
resulting in a third-quarter
charge." - ], - [ - "Reuters - Shares of long-
distance phone\\companies AT T
Corp. and MCI Inc. have
plunged\\about 20 percent this
year, but potential buyers
seem to be\\holding out for
clearance sales." - ], - [ - "Russian oil giant Yukos files
for bankruptcy protection in
the US in a last ditch effort
to stop the Kremlin auctioning
its main production unit." - ], - [ - "British Airways #39; (BA)
chief executive Rod Eddington
has admitted that the company
quot;got it wrong quot; after
staff shortages led to three
days of travel chaos for
passengers." - ], - [ - "The issue of drug advertising
directly aimed at consumers is
becoming political." - ], - [ - "AP - China's economic boom is
still roaring despite efforts
to cool sizzling growth, with
the gross domestic product
climbing 9.5 percent in the
first three quarters of this
year, the government reported
Friday." - ], - [ - "Soaring petroleum prices
pushed the cost of goods
imported into the U.S. much
higher than expected in
August, the government said
today." - ], - [ - "Anheuser-Busch teams up with
Vietnam's largest brewer,
laying the groundwork for
future growth in the region." - ], - [ - "CHARLOTTE, NC - Shares of
Krispy Kreme Doughnuts Inc.
#39;s fell sharply Monday as a
79 percent plunge in third-
quarter earnings and an
intensifying accounting
investigation overshadowed the
pastrymaker #39;s statement
that the low-carb craze might
be easing." - ], - [ - "OXNARD - A leak of explosive
natural gas forced dozens of
workers to evacuate an
offshore oil platform for
hours Thursday but no damage
or injuries were reported." - ], - [ - "AP - Assets of the nation's
retail money market mutual
funds rose by #36;2.85
billion in the latest week to
#36;845.69 billion, the
Investment Company Institute
said Thursday." - ], - [ - "Peter Mandelson provoked fresh
Labour in-fighting yesterday
with an implied attack on
Gordon Brown #39;s
quot;exaggerated gloating
quot; about the health of the
British economy." - ], - [ - "JC Penney said yesterday that
Allen I. Questrom, the chief
executive who has restyled the
once-beleaguered chain into a
sleeker and more profitable
entity, would be succeeded by
Myron E. Ullman III, another
longtime retail executive." - ], - [ - " In the cosmetics department
at Hecht's in downtown
Washington, construction crews
have ripped out the
traditional glass display
cases, replacing them with a
system of open shelves stacked
high with fragrances from
Chanel, Burberry and Armani,
now easily within arm's reach
of the impulse buyer." - ], - [ - " LONDON (Reuters) - Oil prices
slid from record highs above
\\$50 a barrel Wednesday as the
U.S. government reported a
surprise increase in crude
stocks and rebels in Nigeria's
oil-rich delta region agreed
to a preliminary cease-fire." - ], - [ - "Rocky Shoes and Boots makes an
accretive acquisition -- and
gets Dickies and John Deere as
part of the deal." - ], - [ - "BONN: Deutsche Telekom is
bidding 2.9 bn for the 26 it
does not own in T-Online
International, pulling the
internet service back into the
fold four years after selling
stock to the public." - ], - [ - "Motorola Inc. says it #39;s
ready to make inroads in the
cell-phone market after
posting a third straight
strong quarter and rolling out
a series of new handsets in
time for the holiday selling
season." - ], - [ - "Costs of employer-sponsored
health plans are expected to
climb an average of 8 percent
in 2005, the first time in
five years increases have been
in single digits." - ], - [ - "After again posting record
earnings for the third
quarter, Taiwan Semiconductor
Manufacturing Company (TSMC)
expects to see its first
sequential drop in fourth-
quarter revenues, coupled with
a sharp drop in capacity
utilization rates." - ], - [ - "Many people who have never
bounced a check in their life
could soon bounce their first
check if they write checks to
pay bills a couple of days
before their paycheck is
deposited into their checking
account." - ], - [ - " LUXEMBOURG (Reuters) -
Microsoft Corp told a judge on
Thursday that the European
Commission must be stopped
from ordering it to give up
secret technology to
competitors." - ], - [ - "Dow Jones Industrial Average
futures declined amid concern
an upcoming report on
manufacturing may point to
slowing economic growth." - ], - [ - "Reuters - U.S. industrial
output advanced in\\July, as
American factories operated at
their highest capacity\\in more
than three years, a Federal
Reserve report on
Tuesday\\showed." - ], - [ - "Sir Martin Sorrell, chief
executive of WPP, yesterday
declared he was quot;very
impressed quot; with Grey
Global, stoking speculation
WPP will bid for the US
advertising company." - ], - [ - "Like introductory credit card
rates and superior customer
service, some promises just
aren #39;t built to last. And
so it is that Bank of America
- mere months after its pledge
to preserve" - ], - [ - "Reuters - Accounting firm KPMG
will pay #36;10\\million to
settle charges of improper
professional conduct\\while
acting as auditor for Gemstar-
TV Guide International Inc.\\,
the U.S. Securities and
Exchange Commission said
on\\Wednesday." - ], - [ - "Family matters made public: As
eager cousins wait for a slice
of the \\$15 billion cake that
is the Pritzker Empire,
Circuit Court Judge David
Donnersberger has ruled that
the case will be conducted in
open court." - ], - [ - "The weekly survey from
mortgage company Freddie Mac
had rates on 30-year fixed-
rate mortgages inching higher
this week, up to an average
5.82 percent from last week
#39;s 5.81 percent." - ], - [ - "The classic power struggle
between Walt Disney Co. CEO
Michael Eisner and former
feared talent agent Michael
Ovitz makes for high drama in
the courtroom - and apparently
on cable." - ], - [ - "LONDON (CBS.MW) -- Elan
(UK:ELA) (ELN) and partner
Biogen (BIIB) said the FDA has
approved new drug Tysabri to
treat relapsing forms of
multiple sclerosis." - ], - [ - "Bank of New Zealand has frozen
all accounts held in the name
of Access Brokerage, which was
yesterday placed in
liquidation after a client
fund shortfall of around \\$5
million was discovered." - ], - [ - "ZDNet #39;s survey of IT
professionals in August kept
Wired amp; Wireless on top
for the 18th month in a row.
Telecommunications equipment
maker Motorola said Tuesday
that it would cut 1,000 jobs
and take related" - ], - [ - "BRITAIN #39;S largest
financial institutions are
being urged to take lead roles
in lawsuits seeking hundreds
of millions of dollars from
the scandal-struck US
insurance industry." - ], - [ - "Bankrupt UAL Corp. (UALAQ.OB:
Quote, Profile, Research) on
Thursday reported a narrower
third-quarter net loss. The
parent of United Airlines
posted a loss of \\$274
million, or" - ], - [ - "The European Union #39;s head
office issued a bleak economic
report Tuesday, warning that
the sharp rise in oil prices
will quot;take its toll quot;
on economic growth next year
while the euro #39;s renewed
climb could threaten crucial
exports." - ], - [ - "Though Howard Stern's
defection from broadcast to
satellite radio is still 16
months off, the industry is
already trying to figure out
what will fill the crater in
ad revenue and listenership
that he is expected to leave
behind." - ], - [ - " WASHINGTON (Reuters) - The
United States set final anti-
dumping duties of up to 112.81
percent on shrimp imported
from China and up to 25.76
percent on shrimp from Vietnam
to offset unfair pricing, the
Commerce Department said on
Tuesday." - ], - [ - "NEW YORK -- When Office Depot
Inc. stores ran an electronics
recycling drive last summer
that accepted everything from
cellphones to televisions,
some stores were overwhelmed
by the amount of e-trash they
received." - ], - [ - "The International Monetary
Fund expressed concern Tuesday
about the impact of the
troubles besetting oil major
Yukos on Russia #39;s standing
as a place to invest." - ], - [ - "LinuxWorld Conference amp;
Expo will come to Boston for
the first time in February,
underscoring the area's
standing as a hub for the
open-source software being
adopted by thousands of
businesses." - ], - [ - "Interest rates on short-term
Treasury securities rose in
yesterday's auction. The
Treasury Department sold \\$19
billion in three-month bills
at a discount rate of 1.710
percent, up from 1.685 percent
last week. An additional \\$17
billion was sold in six-month
bills at a rate of 1.950
percent, up from 1.870
percent." - ], - [ - "Moody #39;s Investors Service
on Wednesday said it may cut
its bond ratings on HCA Inc.
(HCA.N: Quote, Profile,
Research) deeper into junk,
citing the hospital operator
#39;s plan to buy back about
\\$2." - ], - [ - "Telekom Austria AG, the
country #39;s biggest phone
operator, won the right to buy
Bulgaria #39;s largest mobile
phone company, MobilTel EAD,
for 1.6 billion euros (\\$2.1
billion), an acquisition that
would add 3 million
subscribers." - ], - [ - "Shares of ID Biomedical jumped
after the company reported
Monday that it signed long-
term agreements with the three
largest flu vaccine
wholesalers in the United
States in light of the
shortage of vaccine for the
current flu season." - ], - [ - "The federal government closed
its window on the oil industry
Thursday, saying that it is
selling its last 19 per cent
stake in Calgary-based Petro-
Canada." - ], - [ - " NEW YORK, Nov. 11 -- The 40
percent share price slide in
Merck #38; Co. in the five
weeks after it pulled the
painkiller Vioxx off the
market highlighted larger
problems in the pharmaceutical
industry that may depress
performance for years,
according to academics and
stock analysts who follow the
sector." - ], - [ - "Low-fare carrier Southwest
Airlines Co. said Thursday
that its third-quarter profit
rose 12.3 percent to beat Wall
Street expectations despite
higher fuel costs." - ], - [ - "Another shock hit the drug
sector Friday when
pharmaceutical giant Pfizer
Inc. announced that it found
an increased heart risk to
patients for its blockbuster
arthritis drug Celebrex." - ], - [ - "The number of US information
technology workers rose 2
percent to 10.5 million in the
first quarter of this year,
but demand for them is
dropping, according to a new
report." - ], - [ - "Insurance firm says its board
now consists of its new CEO
Michael Cherkasky and 10
outside members. NEW YORK
(Reuters) - Marsh amp;
McLennan Cos." - ], - [ - "Slot machine maker
International Game Technology
(IGT.N: Quote, Profile,
Research) on Tuesday posted
better-than-expected quarterly
earnings, as casinos bought" - ], - [ - "Venezuelan election officials
say they expect to announce
Saturday, results of a partial
audit of last Sunday #39;s
presidential recall
referendum." - ], - [ - "Sony Ericsson Mobile
Communications Ltd., the
mobile-phone venture owned by
Sony Corp. and Ericsson AB,
said third-quarter profit rose
45 percent on camera phone
demand and forecast this
quarter will be its strongest." - ], - [ - "NEW YORK, September 17
(newratings.com) - Alcatel
(ALA.NYS) has expanded its
operations and presence in the
core North American
telecommunication market with
two separate acquisitions for
about \\$277 million." - ], - [ - "Oil giant Shell swept aside
nearly 100 years of history
today when it unveiled plans
to merge its UK and Dutch
parent companies. Shell said
scrapping its twin-board
structure" - ], - [ - "Caesars Entertainment Inc. on
Thursday posted a rise in
third-quarter profit as Las
Vegas hotels filled up and
Atlantic City properties
squeaked out a profit that was
unexpected by the company." - ], - [ - "One of India #39;s leading
telecommunications providers
will use Cisco Systems #39;
gear to build its new
Ethernet-based broadband
network." - ], - [ - "Shares of Beacon Roofing
Suppler Inc. shot up as much
as 26 percent in its trading
debut Thursday, edging out
bank holding company Valley
Bancorp as the biggest gainer
among a handful of new stocks
that went public this week." - ], - [ - "The first weekend of holiday
shopping went from red-hot to
dead white, as a storm that
delivered freezing, snowy
weather across Colorado kept
consumers at home." - ], - [ - " LONDON (Reuters) - The dollar
fell within half a cent of
last week's record low against
the euro on Thursday after
capital inflows data added to
worries the United States may
struggle to fund its current
account deficit." - ], - [ - "China has pledged to invest
\\$20 billion in Argentina in
the next 10 years, La Nacion
reported Wednesday. The
announcement came during the
first day of a two-day visit" - ], - [ - "Toyota Motor Corp., the world
#39;s biggest carmaker by
value, will invest 3.8 billion
yuan (\\$461 million) with its
partner Guangzhou Automobile
Group to boost manufacturing
capacity in" - ], - [ - "BAE Systems has launched a
search for a senior American
businessman to become a non-
executive director. The high-
profile appointment is
designed to strengthen the
board at a time when the
defence giant is" - ], - [ - "Computer Associates
International is expected to
announce that its new chief
executive will be John
Swainson, an I.B.M. executive
with strong technical and
sales credentials." - ], - [ - "STOCKHOLM (Dow
Jones)--Expectations for
Telefon AB LM Ericsson #39;s
(ERICY) third-quarter
performance imply that while
sales of mobile telephony
equipment are expected to have
dipped, the company" - ], - [ - "The Anglo-Dutch oil giant
Shell today sought to draw a
line under its reserves
scandal by announcing plans to
spend \\$15bn (8.4bn) a year to
replenish reserves and develop
production in its oil and gas
business." - ], - [ - "Circulation declined at most
major US newspapers in the
last half year, the latest
blow for an industry already
rocked by a scandal involving
circulation misstatements that
has undermined the confidence
of investors and advertisers." - ], - [ - "INDIANAPOLIS - ATA Airlines
has accepted a \\$117 million
offer from Southwest Airlines
that would forge close ties
between two of the largest US
discount carriers." - ], - [ - "EVERETT Fire investigators
are still trying to determine
what caused a two-alarm that
destroyed a portion of a South
Everett shopping center this
morning." - ], - [ - "Sure, the PeopleSoft board
told shareholders to just say
no. This battle will go down
to the wire, and even
afterward Ellison could
prevail." - ], - [ - "Investors cheered by falling
oil prices and an improving
job picture sent stocks higher
Tuesday, hoping that the news
signals a renewal of economic
strength and a fall rally in
stocks." - ], - [ - " CHICAGO (Reuters) - Wal-Mart
Stores Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=WMT.N target
=/stocks/quickinfo/fullquote\"&
gt;WMT.N</A>, the
world's largest retailer, said
on Saturday it still
anticipates September U.S.
sales to be up 2 percent to 4
percent at stores open at
least a year." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei stock average opened
down 0.15 percent on
Wednesday as investors took a
breather from the market's
recent rises and sold shares
of gainers such as Sharp
Corp." - ], - [ - "NEW YORK : World oil prices
fell, capping a drop of more
than 14 percent in a two-and-
a-half-week slide triggered by
a perception of growing US
crude oil inventories." - ], - [ - "SUFFOLK -- Virginia Tech
scientists are preparing to
protect the state #39;s
largest crop from a disease
with strong potential to do
damage." - ], - [ - " NEW YORK (Reuters) -
Citigroup Inc. <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=C.N targe
t=/stocks/quickinfo/fullquote\"
>C.N</A> the world's
largest financial services
company, said on Tuesday it
will acquire First American
Bank in the quickly-growing
Texas market." - ], - [ - " SINGAPORE (Reuters) - U.S.
oil prices hovered just below
\\$50 a barrel on Tuesday,
holding recent gains on a rash
of crude supply outages and
fears over thin heating oil
tanks." - ], - [ - "US retail sales fell 0.3 in
August as rising energy costs
and bad weather persuaded
shoppers to reduce their
spending." - ], - [ - "GEORGETOWN, Del., Oct. 28 --
Plaintiffs in a shareholder
lawsuit over former Walt
Disney Co. president Michael
Ovitz's \\$140 million
severance package attempted
Thursday to portray Ovitz as a
dishonest bumbler who botched
the hiring of a major
television executive and
pushed the release of a movie
that angered the Chinese
government, damaging Disney's
business prospects in the
country." - ], - [ - "US stocks gained ground in
early trading Thursday after
tame inflation reports and
better than expected jobless
news. Oil prices held steady
as Hurricane Ivan battered the
Gulf coast, where oil
operations have halted." - ], - [ - "PepsiCo. Inc., the world #39;s
No. 2 soft- drink maker, plans
to buy General Mills Inc.
#39;s stake in their European
joint venture for \\$750
million in cash, giving it
complete ownership of Europe
#39;s largest snack-food
company." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks opened little changed
on Friday, after third-
quarter gross domestic product
data showed the U.S. economy
grew at a slower-than-expected
pace." - ], - [ - " NEW YORK (Reuters) - Limited
Brands Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=LTD.N target
=/stocks/quickinfo/fullquote\"&
gt;LTD.N</A> on
Thursday reported higher
quarterly operating profit as
cost controls and strong
lingerie sales offset poor
results at the retailer's
Express apparel stores." - ], - [ - "General Motors and
DaimlerChrysler are
collaborating on development
of fuel- saving hybrid engines
in hopes of cashing in on an
expanding market dominated by
hybrid leaders Toyota and
Honda." - ], - [ - " TOKYO (Reuters) - Nintendo
Co. Ltd. raised its 2004
shipment target for its DS
handheld video game device by
40 percent to 2.8 million
units on Thursday after many
stores in Japan and the
United States sold out in the
first week of sales." - ], - [ - "Autodesk this week unwrapped
an updated version of its
hosted project collaboration
service targeted at the
construction and manufacturing
industries. Autodesk Buzzsaw
lets multiple, dispersed
project participants --
including building owners,
developers, architects,
construction teams, and
facility managers -- share and
manage data throughout the
life of a project, according
to Autodesk officials." - ], - [ - " WASHINGTON (Reuters) - U.S.
employers hired just 96,000
workers in September, the
government said on Friday in a
weak jobs snapshot, the final
one ahead of presidential
elections that also fueled
speculation about a pause in
interest-rate rises." - ], - [ - "SAN FRANCISCO (CBS.MW) -- The
magazine known for evaluating
cars and electronics is
setting its sights on finding
the best value and quality of
prescription drugs on the
market." - ], - [ - "A mouse, a house, and your
tax-planning spouse all factor
huge in the week of earnings
that lies ahead." - ], - [ - "DALLAS (CBS.MW) -- Royal
Dutch/Shell Group will pay a
\\$120 million penalty to
settle a Securities and
Exchange Commission
investigation of its
overstatement of nearly 4.5
billion barrels of proven
reserves, the federal agency
said Tuesday." - ], - [ - "PHOENIX America West Airlines
has backed away from a
potential bidding war for
bankrupt ATA Airlines, paving
the way for AirTran to take
over ATA operations." - ], - [ - "US stock-index futures
declined. Dow Jones Industrial
Average shares including
General Electric Co. slipped
in Europe. Citigroup Inc." - ], - [ - "Federal Reserve Chairman Alan
Greenspan has done it again.
For at least the fourth time
this year, he has touched the
electrified third rail of
American politics - Social
Security." - ], - [ - "Description: Scientists say
the arthritis drug Bextra may
pose increased risk of
cardiovascular troubles.
Bextra is related to Vioxx,
which was pulled off the
market in September for the
same reason." - ], - [ - "The trial of a lawsuit by Walt
Disney Co. shareholders who
accuse the board of directors
of rubberstamping a deal to
hire Michael Ovitz" - ], - [ - "LOS ANGELES Grocery giant
Albertsons says it has
purchased Bristol Farms, which
operates eleven upscale stores
in Southern California." - ], - [ - "The figures from a survey
released today are likely to
throw more people into the
ranks of the uninsured,
analysts said." - ], - [ - "JB Oxford Holdings Inc., a
Beverly Hills-based discount
brokerage firm, was sued by
the Securities and Exchange
Commission for allegedly
allowing thousands of improper
trades in more than 600 mutual
funds." - ], - [ - "Goldman Sachs reported strong
fourth quarter and full year
earnings of \\$1.19bn, up 36
per cent on the previous
quarter. Full year profit rose
52 per cent from the previous
year to \\$4.55bn." - ], - [ - "Argosy Gaming (AGY:NYSE - news
- research) jumped in early
trading Thursday, after the
company agreed to be acquired
by Penn National Gaming
(PENN:Nasdaq - news -
research) in a \\$1." - ], - [ - "The DuPont Co. has agreed to
pay up to \\$340 million to
settle a lawsuit that it
contaminated water supplies in
West Virginia and Ohio with a
chemical used to make Teflon,
one of its best-known brands." - ], - [ - "Reuters - Jeffrey Greenberg,
chairman and chief\\executive
of embattled insurance broker
Marsh McLennan Cos.\\, is
expected to step down within
hours, a newspaper\\reported on
Friday, citing people close to
the discussions." - ], - [ - "A state regulatory board
yesterday handed a five-year
suspension to a Lawrence
funeral director accused of
unprofessional conduct and
deceptive practices, including
one case where he refused to
complete funeral arrangements
for a client because she had
purchased a lower-priced
casket elsewhere." - ], - [ - "The California Public
Utilities Commission on
Thursday upheld a \\$12.1
million fine against Cingular
Wireless, related to a two-
year investigation into the
cellular telephone company
#39;s business practices." - ], - [ - "Barclays, the British bank
that left South Africa in 1986
after apartheid protests, may
soon resume retail operations
in the nation." - ], - [ - "Italian-based Parmalat is
suing its former auditors --
Grant Thornton International
and Deloitte Touche Tohmatsu
-- for billions of dollars in
damages. Parmalat blames its
demise on the two companies
#39; mismanagement of its
finances." - ], - [ - "update An alliance of
technology workers on Tuesday
accused conglomerate Honeywell
International of planning to
move thousands of jobs to low-
cost regions over the next
five years--a charge that
Honeywell denies." - ], - [ - " NEW YORK (Reuters) - Northrop
Grumman Corp. <A HREF=\"http
://www.investor.reuters.com/Fu
llQuote.aspx?ticker=NOC.N targ
et=/stocks/quickinfo/fullquote
\">NOC.N</A> reported
higher third-quarter earnings
on Wednesday and an 11
percent increase in sales on
strength in its mission
systems, integrated systems,
ships and space technology
businesses." - ], - [ - "TORONTO (CP) - Shares of
Iamgold fell more than 10 per
cent after its proposed merger
with Gold Fields Ltd. was
thrown into doubt Monday as
South Africa #39;s Harmony
Gold Mining Company Ltd." - ], - [ - " LONDON (Reuters) - Oil prices
tumbled again on Monday to an
8-week low under \\$46 a
barrel, as growing fuel stocks
in the United States eased
fears of a winter supply
crunch." - ], - [ - "Alcoa Inc., one of the world
#39;s top producers of
aluminum, said Monday that it
received an unsolicited
quot;mini-tender quot; offer
from Toronto-based TRC Capital
Corp." - ], - [ - "The European Commission is to
warn Greece about publishing
false information about its
public finances." - ], - [ - " LONDON (Reuters) - U.S.
shares were expected to open
lower on Wednesday after
crude oil pushed to a fresh
high overnight, while Web
search engine Google Inc.
dented sentiment as it
slashed the price range on its
initial public offering." - ], - [ - "The telemarketer at the other
end of Orlando Castelblanco
#39;s line promised to reduce
the consumer #39;s credit card
debt by at least \\$2,500 and
get his 20 percent -- and
growing -- interest rates down
to single digits." - ], - [ - " NEW YORK (Reuters) - Blue-
chip stocks fell slightly on
Monday after No. 1 retailer
Wal-Mart Stores Inc. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=WMT
.N target=/stocks/quickinfo/fu
llquote\">WMT.N</A>
reported lower-than-expected
Thanksgiving sales, while
technology shares were lifted
by a rally in Apple Computer
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AAPL.O target=/sto
cks/quickinfo/fullquote\">AA
PL.O</A>." - ], - [ - "WPP Group Inc., the world
#39;s second- largest
marketing and advertising
company, said it won the
bidding for Grey Global Group
Inc." - ], - [ - " NEW YORK (Reuters) - Stocks
were slightly lower on
Tuesday, as concerns about
higher oil prices cutting into
corporate profits and
consumer demand weighed on
sentiment, while retail sales
posted a larger-than-expected
decline in August." - ], - [ - " SINGAPORE (Reuters) - Oil
prices climbed above \\$42 a
barrel on Wednesday, rising
for the third day in a row as
the heavy consuming U.S.
Northeast feels the first
chills of winter." - ], - [ - " NEW YORK (Reuters) - Merck
Co Inc. <A HREF=\"http://www
.investor.reuters.com/FullQuot
e.aspx?ticker=MRK.N target=/st
ocks/quickinfo/fullquote\">M
RK.N</A> on Thursday
pulled its arthritis drug
Vioxx off the market after a
study showed it doubled the
risk of heart attack and
stroke, a move that sent its
shares plunging and erased
\\$25 billion from its market
value." - ], - [ - "The government on Wednesday
defended its decision to
radically revise the country
#39;s deficit figures, ahead
of a European Commission
meeting to consider possible
disciplinary action against
Greece for submitting faulty
figures." - ], - [ - "OPEC ministers yesterday
agreed to increase their
ceiling for oil production to
help bring down stubbornly
high prices in a decision that
traders and analysts dismissed
as symbolic because the cartel
already is pumping more than
its new target." - ], - [ - "Williams-Sonoma Inc. said
second- quarter profit rose 55
percent, boosted by the
addition of Pottery Barn
stores and sale of outdoor
furniture." - ], - [ - " HONG KONG/SAN FRANCISCO
(Reuters) - China's largest
personal computer maker,
Lenovo Group Ltd., said on
Tuesday it was in acquisition
talks with a major technology
company, which a source
familiar with the situation
said was IBM." - ], - [ - "Reuters - Oil prices stayed
close to #36;49 a\\barrel on
Thursday, supported by a
forecast for an early
cold\\snap in the United States
that could put a strain on a
thin\\supply cushion of winter
heating fuel." - ], - [ - "WASHINGTON (CBS.MW) --
President Bush announced
Monday that Kellogg chief
executive Carlos Gutierrez
would replace Don Evans as
Commerce secretary, naming the
first of many expected changes
to his economic team." - ], - [ - "Iron Mountain moved further
into the backup and recovery
space Tuesday with the
acquisition of Connected Corp.
for \\$117 million. Connected
backs up desktop data for more
than 600 corporations, with
more than" - ], - [ - "Montgomery County (website -
news) is a big step closer to
shopping for prescription
drugs north of the border. On
a 7-2 vote, the County Council
is approving a plan that would
give county" - ], - [ - "The disclosure this week that
a Singapore-listed company
controlled by a Chinese state-
owned enterprise lost \\$550
million in derivatives
transactions" - ], - [ - "VANCOUVER - A Vancouver-based
firm won #39;t sell 1.2
million doses of influenza
vaccine to the United States
after all, announcing Tuesday
that it will sell the doses
within Canada instead." - ], - [ - "consortium led by the Sony
Corporation of America reached
a tentative agreement today to
buy Metro-Goldwyn-Mayer, the
Hollywood studio famous for
James Bond and the Pink
Panther, for" - ], - [ - "The country-cooking restaurant
chain has agreed to pay \\$8.7
million over allegations that
it segregated black customers,
subjected them to racial slurs
and gave black workers
inferior jobs." - ], - [ - " SINGAPORE (Reuters) -
Investors bought shares in
Asian exporters and
electronics firms such as
Fujitsu Ltd. on Tuesday,
buoyed by a favorable outlook
from U.S. technology
bellwethers and a slide in oil
prices." - ], - [ - "Ace Ltd. will stop paying
brokers for steering business
its way, becoming the third
company to make concessions in
the five days since New York
Attorney General Eliot Spitzer
unveiled a probe of the
insurance industry." - ], - [ - "Wm. Wrigley Jr. Co., the world
#39;s largest maker of chewing
gum, agreed to buy candy
businesses including Altoids
mints and Life Savers from
Kraft Foods Inc." - ], - [ - " #39;Reaching a preliminary
pilot agreement is the single
most important hurdle they
have to clear, but certainly
not the only one." - ], - [ - "TORONTO (CP) - Canada #39;s
big banks are increasing
mortgage rates following a
decision by the Bank of Canada
to raise its overnight rate by
one-quarter of a percentage
point to 2.25 per cent." - ], - [ - "OTTAWA (CP) - The economy
created another 43,000 jobs
last month, pushing the
unemployment rate down to 7.1
per cent from 7.2 per cent in
August, Statistics Canada said
Friday." - ], - [ - " TOKYO (Reuters) - Japan's
Nikkei average rose 0.39
percent by midsession on
Friday, bolstered by solid
gains in stocks dependent on
domestic business such as Kao
Corp. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=4452.T target=/sto
cks/quickinfo/fullquote\">44
52.T</A>." - ], - [ - " FRANKFURT (Reuters) -
DaimlerChrysler and General
Motors will jointly develop
new hybrid motors to compete
against Japanese rivals on
the fuel-saving technology
that reduces harmful
emissions, the companies said
on Monday." - ], - [ - "The bass should be in your
face. That's what Matt Kelly,
of Boston's popular punk rock
band Dropkick Murphys, thinks
is the mark of a great stereo
system. And he should know.
Kelly, 29, is the drummer for
the band that likes to think
of itself as a bit of an Irish
lucky charm for the Red Sox." - ], - [ - "Slumping corporate spending
and exports caused the economy
to slow to a crawl in the
July-September period, with
real gross domestic product
expanding just 0.1 percent
from the previous quarter,
Cabinet Office data showed
Friday." - ], - [ - "US President George W. Bush
signed into law a bill
replacing an export tax
subsidy that violated
international trade rules with
a \\$145 billion package of new
corporate tax cuts and a
buyout for tobacco farmers." - ], - [ - "The Nikkei average was up 0.37
percent in mid-morning trade
on Thursday as a recovery in
the dollar helped auto makers
among other exporters, but
trade was slow as investors
waited for important Japanese
economic data." - ], - [ - "Jennifer Canada knew she was
entering a boy's club when she
enrolled in Southern Methodist
University's Guildhall school
of video-game making." - ], - [ - "SYDNEY (Dow Jones)--Colorado
Group Ltd. (CDO.AU), an
Australian footwear and
clothing retailer, said Monday
it expects net profit for the
fiscal year ending Jan. 29 to
be over 30 higher than that of
a year earlier." - ], - [ - "NEW YORK - What are the odds
that a tiny nation like
Antigua and Barbuda could take
on the United States in an
international dispute and win?" - ], - [ - "SBC Communications expects to
cut 10,000 or more jobs by the
end of next year through
layoffs and attrition. That
#39;s about six percent of the
San Antonio-based company
#39;s work force." - ], - [ - "Another United Airlines union
is seeking to oust senior
management at the troubled
airline, saying its strategies
are reckless and incompetent." - ], - [ - "Global oil prices boomed on
Wednesday, spreading fear that
energy prices will restrain
economic activity, as traders
worried about a heating oil
supply crunch in the American
winter." - ], - [ - "Custom-designed imported
furniture was once an
exclusive realm. Now, it's the
economical alternative for
commercial developers and
designers needing everything
from seats to beds to desks
for their projects." - ], - [ - "Short-term interest rate
futures struggled on Thursday
after a government report
showing US core inflation for
August below market
expectations failed to alter
views on Federal Reserve rate
policy." - ], - [ - "Samsung is now the world #39;s
second-largest mobile phone
maker, behind Nokia. According
to market watcher Gartner, the
South Korean company has
finally knocked Motorola into
third place." - ], - [ - " TOKYO (Reuters) - Tokyo
stocks climbed to a two week
high on Friday after Tokyo
Electron Ltd. and other chip-
related stocks were boosted
by a bullish revenue outlook
from industry leader Intel
Corp." - ], - [ - " NEW YORK (Reuters) - Adobe
Systems Inc. <A HREF=\"http:
//www.investor.reuters.com/Ful
lQuote.aspx?ticker=ADBE.O targ
et=/stocks/quickinfo/fullquote
\">ADBE.O</A> on
Monday reported a sharp rise
in quarterly profit, driven by
robust demand for its
Photoshop and document-sharing
software." - ], - [ - "Dow Jones amp; Co., publisher
of The Wall Street Journal,
has agreed to buy online
financial news provider
MarketWatch Inc. for about
\\$463 million in a bid to
boost its revenue from the
fast-growing Internet
advertising market." - ], - [ - "Low-fare airline ATA has
announced plans to lay off
hundreds of employees and to
drop most of its flights out
of Midway Airport in Chicago." - ], - [ - " WASHINGTON (Reuters) -
Germany's Bayer AG <A HREF=
\"http://www.investor.reuters.c
om/FullQuote.aspx?ticker=BAYG.
DE target=/stocks/quickinfo/fu
llquote\">BAYG.DE</A>
has agreed to plead guilty
and pay a \\$4.7 million fine
for taking part in a
conspiracy to fix the prices
of synthetic rubber, the U.S.
Justice Department said on
Wednesday." - ], - [ - "The US oil giant got a good
price, Russia #39;s No. 1 oil
company acquired a savvy
partner, and Putin polished
Russia #39;s image." - ], - [ - "The maker of Hostess Twinkies,
a cake bar and a piece of
Americana children have
snacked on for almost 75
years, yesterday raised
concerns about the company
#39;s ability to stay in
business." - ], - [ - "Tenet Healthcare Corp., the
second- largest US hospital
chain, said fourth-quarter
charges may exceed \\$1 billion
and its loss from continuing
operations will widen from the
third quarter #39;s because of
increased bad debt." - ], - [ - "The London-based brokerage
Collins Stewart Tullett placed
55m of new shares yesterday to
help fund the 69.5m purchase
of the money and futures
broker Prebon." - ], - [ - " PARIS (Reuters) - European
equities flirted with 5-month
peaks as hopes that economic
growth was sustainable and a
small dip in oil prices
helped lure investors back to
recent underperformers such
as technology and insurance
stocks." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks looked to open higher
on Friday, as the fourth
quarter begins on Wall Street
with oil prices holding below
\\$50 a barrel." - ], - [ - "LONDON : World oil prices
stormed above 54 US dollars
for the first time Tuesday as
strikes in Nigeria and Norway
raised worries about possible
supply shortages during the
northern hemisphere winter." - ], - [ - "US stocks were little changed
on Thursday as an upbeat
earnings report from chip
maker National Semiconductor
Corp. (NSM) sparked some
buying, but higher oil prices
limited gains." - ] - ], - "hovertemplate": "label=Business
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Business", - "marker": { - "color": "#ab63fa", - "size": 5, - "symbol": "x" - }, - "mode": "markers", - "name": "Business", - "showlegend": true, - "type": "scattergl", - "x": [ - -23.452963, - -53.210773, - -39.491558, - -28.231606, - -19.31264, - -47.410145, - -29.877638, - -40.76841, - -5.107883, - -42.131943, - -32.52056, - -39.654697, - 12.912951, - -18.497515, - -48.55776, - -53.367012, - -27.265852, - -25.208595, - -30.356327, - -49.554066, - -29.197483, - -43.75864, - -33.627903, - -28.459074, - -33.894855, - -5.484721, - -36.616104, - -53.822376, - -44.628685, - -19.861258, - -44.997097, - -37.276833, - -19.60824, - -38.214413, - 20.328302, - -23.330465, - -35.855865, - -18.20647, - -53.65577, - -33.435104, - -1.9387143, - -32.65333, - -11.705121, - -23.656853, - -34.370987, - -33.846046, - -41.55955, - -31.4103, - -41.654114, - -43.58004, - -20.22472, - -41.80841, - -43.47724, - -28.54648, - -55.67927, - -27.870377, - -11.352515, - -46.313496, - -46.637367, - -38.786, - -18.44551, - -34.186226, - -37.5602, - -5.632542, - -45.209023, - -31.500145, - -51.390118, - -42.6501, - -6.2927465, - -46.257027, - -23.71305, - -55.86017, - -34.78124, - -35.732872, - -39.5287, - -6.6976542, - -26.256298, - -34.82042, - -45.549995, - -48.911743, - -10.257471, - 45.230713, - -16.951935, - -19.065458, - -22.693665, - -44.68482, - -29.42919, - -30.674974, - 13.034079, - -15.232134, - -7.2577205, - -27.285727, - -37.857563, - -30.674488, - -37.931133, - -47.091602, - -16.122215, - -28.833778, - -7.503495, - -42.6552, - -37.64758, - -14.742917, - -19.793585, - -53.133896, - -44.98284, - -47.514927, - -40.143944, - -29.305222, - -19.33182, - -49.376717, - -32.70729, - -34.81799, - -22.524883, - -12.82885, - -46.24378, - -29.501112, - -5.0456986, - -47.141144, - -36.80437, - -8.335074, - -11.501718, - -42.394825, - -11.57566, - -29.24355, - -53.3065, - -42.896793, - -15.898649, - 1.5646982, - -51.349506, - -27.663815, - -23.031208, - -37.19868, - -31.336668, - -6.1798644, - -50.869877, - -44.016224, - -24.092436, - -6.3253975, - -49.846096, - -37.92231, - -20.485857, - -52.251915, - -42.127716, - -45.652252, - -39.891853, - -24.499039, - -9.765382, - -42.07617, - -46.420776, - -36.030876, - -16.348925, - -28.402678, - -18.734745, - -28.94602, - -23.825924, - -22.952112, - -45.96224, - -37.039566, - -15.194927, - -28.049438, - -12.567652, - -40.062756, - -52.438923, - -24.236567, - -54.033554, - -27.801344, - -52.71393, - -13.981046, - -10.861444, - -48.87469, - -35.826897, - -31.892145, - -3.1801224, - -31.190088, - -44.238426, - -24.869604, - 34.078384, - -19.088882, - -32.190376, - -25.71062, - -22.265373, - -52.42964, - -7.045784, - -6.436385, - -21.87981, - -37.63322, - -30.145031, - -45.900715, - -54.722725, - -9.91315, - -33.41616, - -29.682985, - -21.902475, - -38.86974, - -49.362682, - -38.69257, - -29.136082, - -35.14978, - -37.452244, - 1.1558152, - -27.990711, - -7.9959974, - -23.705156, - -27.350927, - 7.0387945, - -22.092422, - -44.570576, - -49.361073, - -28.812906, - -42.660313, - -34.603184, - 7.4309235, - -35.853523, - -52.502518, - -32.07786, - -48.87661, - -45.17925, - -20.502497, - -50.661304, - -47.05539, - -11.594388, - -23.063238, - -41.108536, - -36.380856, - -22.186844, - -47.564026, - -0.203547, - -45.418346, - -32.97913, - -29.1495, - -34.859055, - -12.614871, - -33.125538, - -30.265446, - -24.302145, - -38.971054, - -6.6933794, - -37.11219, - -12.324585, - -27.018137, - -30.541101, - -23.530384, - -23.313187, - 9.910433, - -31.606474, - -46.921032, - -34.711258, - -43.354763, - -33.235416, - -45.437263, - -27.061964, - -20.385843, - -41.675705, - -3.9804885, - -48.024345, - -12.530503, - -34.855972, - -17.363302, - -0.31910038, - -20.422543, - -48.115654, - -19.632275, - -41.17226, - -33.03263, - -30.970875, - 1.7612854, - -28.959257, - -42.69843, - -18.173891, - -50.46992, - -22.095016, - -43.79571, - -48.04673, - -10.504851, - -45.725693, - -49.58257, - -20.070473, - -27.236769, - -37.01328, - -19.117485, - -39.115543, - -29.740028, - -34.482994, - -21.63138, - -4.696033, - -4.852559, - -17.729515, - -29.865084, - -46.45139, - -9.0318775, - -8.188348, - -26.325634, - -13.60535, - -34.409237, - -46.677288, - -45.882183, - -32.426746, - 2.8495433, - -2.2277532, - -18.868166, - 19.120626, - -21.790516, - -8.934254, - -26.787775, - -12.61641, - -12.760466, - -9.973649, - -5.3010283, - -46.84005, - -40.78544, - -46.046387, - -23.913462, - -10.10402, - -34.200912, - -40.844006, - -29.91782, - -19.92304, - -41.66086, - -57.68235, - -29.327738, - -27.266224, - -49.10616, - -4.6537275, - -8.695738, - -52.898724, - -40.76337, - -36.72977, - -37.2606, - -54.428818, - -9.159126, - -46.200623, - -13.831901, - -21.605253, - -45.533035, - -55.4472, - -34.02309, - -5.918376, - -42.396126, - -16.095537, - -21.678917, - -10.15981, - -4.0862207, - -54.596252, - -34.252632, - -16.827753, - -32.974155, - -36.174, - -19.309122, - -28.692097, - -10.5037985, - -44.20961, - -39.52826, - -27.136961, - -34.181965, - 10.556734, - -48.20029, - 9.239984, - -7.4418893, - -28.779457, - -35.19683, - -54.1994, - -26.758255, - -54.634964, - -43.92253, - -39.611347, - -24.87918, - -22.471472, - -36.996124, - -46.204945, - -24.678703, - -36.588448, - -12.811854, - -0.94040644, - -44.44866, - -4.227074, - -22.860474, - -10.390649, - -36.567635, - -12.951042, - -30.48289, - -38.7589, - -36.49073, - -6.355229, - -43.13706, - -19.532629, - -36.42381, - -46.434704, - -29.59413, - -27.002981, - -20.752146, - -18.822731, - -23.879477, - -49.73623, - -20.585733, - -32.563313, - -8.220228, - -50.147823, - 19.135786, - -47.881756, - -28.388693, - -17.01861, - -49.997864, - -38.691113, - -53.174366, - 0.79674417, - -44.02579, - 0.5841107, - -28.108793, - -8.663238, - -8.084352, - -39.750137, - -16.89367, - -34.483925, - -27.989662, - 12.8087, - -25.524998, - -46.526306, - -54.849133, - -40.10975, - -36.69884, - -9.612953, - -28.30938, - -35.854397, - -40.808376, - -5.0891867, - -45.114822, - -25.18432, - -41.517033, - -54.139446, - -25.219833, - -53.989174, - -34.2908, - -40.278057, - -28.372087, - -28.535112, - -16.299625, - -27.921608, - -36.991142, - 6.9097495, - -20.261177, - -4.775729, - -24.145273, - -29.613533, - -11.283554, - -48.772636, - -53.829735, - -15.053305, - -8.963649, - -9.277499, - -15.22937, - -21.86168, - -7.6405187, - -31.572515, - -52.86785, - -35.83179, - -36.710674, - -21.823997, - -1.3200667, - -40.638805, - -12.411886, - -41.362526, - -24.7866, - -40.258007, - -37.48171, - -23.877874, - -0.9994553, - -34.842422, - -51.40557, - -31.760511, - -36.74732, - -5.775708, - -32.659416, - -3.8920984, - -10.574449, - -20.651194, - -26.427645, - -14.214475, - -48.196377, - -10.817454, - -25.303522, - -23.521646, - -43.41168, - -44.33141, - -49.86946, - -25.189764, - 64.17483, - -40.866665, - 7.287593, - -48.861267, - 22.103119, - -49.37285, - -0.5269812, - -33.893284, - -27.523653, - -37.509644, - -48.43532, - -37.623196, - -55.58907, - -49.322422, - -52.688995, - -29.522964, - -30.956533, - -15.489649, - -21.36634, - -32.821945, - -30.13521, - -20.815348, - -47.19078, - -45.82224, - -36.344696, - -43.422806 - ], - "xaxis": "x", - "y": [ - 7.8286805, - 13.600263, - 18.938295, - 3.4177885, - -23.29184, - 12.92886, - 7.4292397, - 25.03953, - -3.3929446, - 24.970749, - 0.9335098, - 26.929127, - -26.440922, - 9.1828, - -14.938796, - 13.022359, - -21.471975, - -25.318462, - 9.6741905, - 12.820546, - 4.9088416, - -17.468342, - -20.446613, - 8.161042, - 5.007877, - 6.2455893, - 12.575957, - 13.993413, - 22.678474, - 14.391562, - 17.073126, - 30.481924, - -16.22469, - 20.519714, - -24.165535, - 7.7406225, - 5.442065, - 9.661537, - 7.6243353, - 4.249151, - -21.713743, - 12.867583, - -10.440891, - -8.8150835, - 12.972686, - 4.730411, - 30.71818, - 6.520791, - 21.164257, - -8.373115, - -17.390278, - 31.114712, - 30.646704, - 6.923768, - 20.141148, - 8.499566, - -10.837732, - 7.830664, - 24.00407, - 24.8098, - -24.985989, - -11.249722, - 27.957365, - 6.4599543, - 28.99686, - -18.920874, - 9.491719, - 33.583965, - -0.7140172, - 28.585188, - 1.3020672, - -5.6699047, - 18.828894, - 20.908062, - 19.798025, - -23.965172, - 8.796663, - 32.09736, - 26.441679, - 21.586815, - -22.324871, - -27.750652, - -28.93897, - -23.277813, - 16.425425, - 18.54026, - -0.26035935, - -21.057713, - -26.390093, - -21.929888, - -11.895842, - -26.575148, - 15.54324, - -29.652391, - 18.134205, - 10.717227, - -19.211506, - 16.122732, - -14.575271, - 33.287487, - 12.847039, - -13.995945, - 14.344031, - 7.419209, - 28.721743, - 21.439281, - 25.772839, - 5.9104095, - -11.143075, - 20.674139, - 5.2307787, - 28.700588, - -16.839674, - -6.9957857, - 19.155857, - 22.57425, - 4.2664466, - 17.716654, - 13.592724, - 35.79897, - -7.505608, - 25.960653, - -6.362675, - -25.562366, - 14.799318, - 24.331854, - -1.1096494, - -27.81828, - 10.189425, - -25.194714, - -19.704374, - 23.483368, - 8.0363655, - -11.6483135, - 12.337329, - 27.538383, - 26.484413, - -16.154268, - 7.6020126, - 20.629124, - 16.143784, - 7.7047353, - 3.5172246, - 23.261137, - 21.07103, - -14.940109, - -20.06512, - 20.645449, - -13.735753, - 26.788364, - -24.88111, - 16.572128, - -15.456796, - 1.9859632, - 7.721218, - -12.5096655, - 2.0307107, - 16.297318, - -21.899122, - -19.075409, - -14.551722, - 24.951237, - 15.502925, - -26.033178, - 5.509095, - 16.660208, - 9.362999, - -14.059228, - -17.933233, - -22.175861, - 26.15921, - -23.924995, - 15.090964, - -3.5818095, - 25.846468, - 23.278618, - -25.306404, - -15.10728, - 10.148522, - 7.5768538, - -23.194473, - 13.976609, - -11.945698, - -16.428364, - -28.303225, - 19.381159, - 7.8694215, - 2.0052595, - 15.873175, - -11.668809, - 7.235856, - -21.42294, - -21.904455, - 9.315685, - 20.726511, - 17.144817, - 2.2092547, - 27.541485, - 28.821224, - 34.996464, - 4.8909636, - -12.007193, - -23.275118, - 1.5220636, - -0.8274632, - -11.567605, - -7.7168093, - 3.9182017, - 22.896002, - 33.52397, - 29.210163, - -4.9220414, - 22.216219, - 16.870352, - -11.452592, - 13.475318, - 22.79436, - -17.442066, - 12.352939, - 26.730667, - -12.461162, - -19.692043, - 16.529331, - 24.65294, - -5.132745, - 19.01329, - -2.7055879, - 27.475252, - 14.475491, - 0.96339697, - 31.858027, - -16.143095, - -15.854709, - 16.484713, - -23.097334, - 18.896671, - 34.8398, - 13.576438, - -14.182415, - 8.168441, - 13.999631, - 8.972529, - 14.504229, - 24.289896, - -3.5055225, - 15.380986, - 3.7059414, - 0.16963075, - 21.913166, - 22.36791, - 16.85544, - 17.925854, - 25.043688, - -13.892162, - 8.306711, - -6.695091, - 6.3890157, - -3.9792998, - 0.70931256, - -20.58928, - 26.520325, - 21.436638, - 18.802984, - 14.603634, - 7.0568976, - 31.796051, - -26.096392, - 28.98958, - -24.192507, - 21.682661, - 16.40951, - 20.87726, - 12.556309, - -15.915366, - 23.323511, - 8.230685, - -17.60057, - 6.01643, - 14.907442, - -19.740395, - -21.907415, - 5.0985155, - 10.961509, - -21.606695, - -19.460848, - -1.0193497, - -5.6577854, - -19.941338, - 32.26841, - -20.533716, - 35.702885, - -22.034695, - -13.390235, - 4.2575808, - 27.501059, - -1.931646, - 24.216267, - -8.099275, - 2.2845645, - -17.30228, - -3.5722063, - 11.71164, - -16.890417, - -21.589676, - -14.588415, - -17.137983, - -11.65064, - -1.327813, - 21.326263, - 15.523962, - -1.839738, - 15.938968, - -5.59304, - 8.384921, - 32.041183, - 9.530565, - -23.740261, - -5.3864436, - -13.534582, - 8.064646, - -8.887762, - 8.281391, - -14.383507, - -17.226963, - 21.001068, - 23.757103, - 14.622503, - 26.616285, - -17.779476, - -16.79903, - 29.02862, - -9.61028, - -10.290435, - 26.314108, - 20.082417, - 7.00291, - -14.920918, - 31.43023, - -10.685339, - 16.79463, - -22.31949, - -14.08771, - 1.3350589, - 6.380316, - -12.914869, - 6.286185, - 18.577192, - -14.996935, - 9.808406, - 7.8065777, - 25.720367, - 27.335323, - -27.719013, - 15.222469, - 31.183216, - 16.581377, - -1.3270321, - -24.535004, - -35.037914, - 27.32407, - 2.2356973, - -19.28855, - -16.932995, - 11.079847, - 11.247658, - 23.26528, - -5.613793, - 21.349566, - 26.02513, - -20.807219, - 9.496942, - -15.057436, - -14.524883, - 30.993462, - -18.399357, - -12.452006, - -14.193346, - 7.1376367, - -11.71116, - 10.333615, - 25.406893, - -30.53585, - -0.6449607, - 13.889341, - -14.508683, - -15.281551, - 32.249325, - 12.763572, - -19.383118, - -11.329933, - -19.62514, - 17.039936, - 12.198028, - 17.744976, - 21.359537, - -21.386267, - 14.2712755, - -3.9283407, - 10.8514185, - -19.329512, - -17.759117, - 14.102587, - -17.441105, - 19.873198, - 35.126564, - 22.92706, - -5.8584995, - -25.70212, - -24.459936, - -24.834877, - 13.346765, - -17.298498, - 9.108203, - 1.7839518, - 33.032204, - -8.874142, - 19.918457, - 13.791234, - 19.88417, - 26.08512, - 35.79187, - -20.758772, - 27.588259, - 26.69215, - -18.813177, - 20.353582, - -22.663652, - 22.6146, - 14.793362, - -15.245933, - -20.736963, - -14.522557, - 23.530079, - -12.780764, - 24.898293, - -16.473738, - 1.9298484, - 20.942158, - -5.986609, - 18.014528, - -18.893454, - -26.197563, - 19.50926, - -13.34786, - 13.471852, - 18.979578, - -15.592323, - -21.118057, - -10.412807, - -13.279965, - -28.35139, - -14.49968, - 4.322983, - 14.2359915, - 16.888266, - 26.655972, - -21.796993, - -2.695157, - 20.795307, - -25.788994, - 18.527788, - -20.973848, - 21.49163, - 28.852188, - 16.580437, - -2.6463892, - 31.668863, - 10.09489, - -19.010218, - 27.96568, - -24.00849, - -16.797096, - -5.864986, - -14.093458, - -11.445573, - -23.317041, - -15.430166, - 20.119074, - -11.128392, - -22.75563, - 5.6336102, - 30.831476, - 27.198599, - 20.937487, - -15.238694, - -20.764137, - 25.318214, - -4.7924676, - 22.43602, - -29.857277, - 9.825396, - 3.6917143, - 7.166533, - 6.549803, - 27.198914, - -3.37324, - 22.221628, - -16.78678, - 20.027725, - 15.184932, - -21.060045, - 6.1585164, - -11.634907, - -17.1438, - 15.137199, - 12.653392, - -25.22734, - 20.774977, - 20.526009, - 28.81767, - 19.897066 - ], - "yaxis": "y" - } - ], - "layout": { - "height": 500, - "legend": { - "title": { - "text": "label" - }, - "tracegroupgap": 0 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "t-SNE components of article descriptions" - }, - "width": 600, - "xaxis": { - "anchor": "y", - "domain": [ - 0, - 1 - ], - "title": { - "text": "Component 0" - } - }, - "yaxis": { - "anchor": "x", - "domain": [ - 0, - 1 - ], - "title": { - "text": "Component 1" - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# get embeddings for all article descriptions\n", - "embeddings = [embedding_from_string(string) for string in article_descriptions]\n", - "# compress the 2048-dimensional embeddings into 2 dimensions using t-SNE\n", - "tsne_components = tsne_components_from_embeddings(embeddings)\n", - "# get the article labels for coloring the chart\n", - "labels = df[\"label\"].tolist()\n", - "\n", - "chart_from_components(\n", - " components=tsne_components,\n", - " labels=labels,\n", - " strings=article_descriptions,\n", - " width=600,\n", - " height=500,\n", - " title=\"t-SNE components of article descriptions\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see in the chart above, even the highly compressed embeddings do a good job of clustering article descriptions by category. And it's worth emphasizing: this clustering is done with no knowledge of the labels themselves!\n", - "\n", - "Also, if you look closely at the most egregious outliers, they are often due to mislabeling rather than poor embedding. For example, the majority of the blue World points in the green Sports cluster appear to be Sports stories." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, let's recolor the points by whether they are a source article, its nearest neighbors, or other." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# create labels for the recommended articles\n", - "def nearest_neighbor_labels(\n", - " list_of_indices: list[int],\n", - " k_nearest_neighbors: int = 5\n", - ") -> list[str]:\n", - " \"\"\"Return a list of labels to color the k nearest neighbors.\"\"\"\n", - " labels = [\"Other\" for _ in list_of_indices]\n", - " source_index = list_of_indices[0]\n", - " labels[source_index] = \"Source\"\n", - " for i in range(k_nearest_neighbors):\n", - " nearest_neighbor_index = list_of_indices[i + 1]\n", - " labels[nearest_neighbor_index] = f\"Nearest neighbor (top {k_nearest_neighbors})\"\n", - " return labels\n", - "\n", - "\n", - "tony_blair_labels = nearest_neighbor_labels(tony_blair_articles, k_nearest_neighbors=5)\n", - "chipset_security_labels = nearest_neighbor_labels(chipset_security_articles, k_nearest_neighbors=5\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly/" - }, - "data": [ - { - "customdata": [ - [ - "PC World - Upcoming chip set
will include built-in security
features for your PC." - ], - [ - "Newspapers in Greece reflect a
mixture of exhilaration that
the Athens Olympics proved
successful, and relief that
they passed off without any
major setback." - ], - [ - "SAN JOSE, Calif. -- Apple
Computer (Quote, Chart)
unveiled a batch of new iPods,
iTunes software and promos
designed to keep it atop the
heap of digital music players." - ], - [ - "Any product, any shape, any
size -- manufactured on your
desktop! The future is the
fabricator. By Bruce Sterling
from Wired magazine." - ], - [ - "KABUL, Sept 22 (AFP): Three US
soldiers were killed and 14
wounded in a series of fierce
clashes with suspected Taliban
fighters in south and eastern
Afghanistan this week, the US
military said Wednesday." - ], - [ - "The EU and US moved closer to
an aviation trade war after
failing to reach agreement
during talks Thursday on
subsidies to Airbus Industrie." - ], - [ - "AUSTRALIAN journalist John
Martinkus is lucky to be alive
after spending 24 hours in the
hands of Iraqi militants at
the weekend. Martinkus was in
Baghdad working for the SBS
Dateline TV current affairs
program" - ], - [ - " GAZA (Reuters) - An Israeli
helicopter fired a missile
into a town in the southern
Gaza Strip late on Wednesday,
witnesses said, hours after a
Palestinian suicide bomber
blew herself up in Jerusalem,
killing two Israeli border
policemen." - ], - [ - "The Microsoft CEO says one way
to stem growing piracy of
Windows and Office in emerging
markets is to offer low-cost
computers." - ], - [ - "RIYADH, Saudi Arabia -- Saudi
police are seeking two young
men in the killing of a Briton
in a Riyadh parking lot, the
Interior Ministry said today,
and the British ambassador
called it a terrorist attack." - ], - [ - "Loudon, NH -- As the rain
began falling late Friday
afternoon at New Hampshire
International Speedway, the
rich in the Nextel Cup garage
got richer." - ], - [ - "PalmSource surprised the
mobile vendor community today
with the announcement that it
will acquire China MobileSoft
(CMS), ostensibly to leverage
that company's expertise in
building a mobile version of
the Linux operating system." - ], - [ - "JEFFERSON CITY - An election
security expert has raised
questions about Missouri
Secretary of State Matt Blunt
#39;s plan to let soldiers at
remote duty stations or in
combat areas cast their
ballots with the help of
e-mail." - ], - [ - "A gas explosion at a coal mine
in northern China killed 33
workers in the 10th deadly
mine blast reported in three
months. The explosion occurred
yesterday at 4:20 pm at Nanlou
township" - ], - [ - "Reuters - Palestinian leader
Mahmoud Abbas called\\Israel
\"the Zionist enemy\" Tuesday,
unprecedented language for\\the
relative moderate who is
expected to succeed Yasser
Arafat." - ], - [ - "AP - Ottawa Senators right
wing Marian Hossa is switching
European teams during the NHL
lockout." - ], - [ - "A new, optional log-on service
from America Online that makes
it harder for scammers to
access a persons online
account will not be available
for Macintosh" - ], - [ - "Nasser al-Qidwa, Palestinian
representative at the United
Nations and nephew of late
leader Yasser Arafat, handed
Arafat #39;s death report to
the Palestinian National
Authority (PNA) on Saturday." - ], - [ - "CAIRO, Egypt - France's
foreign minister appealed
Monday for the release of two
French journalists abducted in
Baghdad, saying the French
respect all religions. He did
not rule out traveling to
Baghdad..." - ], - [ - "Chelsea terminated Romania
striker Adrian Mutu #39;s
contract, citing gross
misconduct after the player
failed a doping test for
cocaine and admitted taking
the drug, the English soccer
club said in a statement." - ], - [ - "United Arab Emirates President
and ruler of Abu Dhabi Sheik
Zayed bin Sultan al-Nayhan
died Tuesday, official
television reports. He was 86." - ], - [ - "PALESTINIAN leader Yasser
Arafat today issued an urgent
call for the immediate release
of two French journalists
taken hostage in Iraq." - ], - [ - "The al-Qaida terrorist network
spent less than \\$50,000 on
each of its major attacks
except for the Sept. 11, 2001,
suicide hijackings, and one of
its hallmarks is using" - ], - [ - " SAN FRANCISCO (Reuters) -
Nike Inc. <A HREF=\"http://w
ww.investor.reuters.com/FullQu
ote.aspx?ticker=NKE.N target=/
stocks/quickinfo/fullquote\">
;NKE.N</A>, the world's
largest athletic shoe company,
on Monday reported a 25
percent rise in quarterly
profit, beating analysts'
estimates, on strong demand
for high-end running and
basketball shoes in the
United States." - ], - [ - "A FATHER who scaled the walls
of a Cardiff court dressed as
superhero Robin said the
Buckingham Palace protester
posed no threat. Fathers 4
Justice activist Jim Gibson,
who earlier this year staged
an eye-catching" - ], - [ - "NEW YORK (CBS.MW) - US stocks
traded mixed Thurday as Merck
shares lost a quarter of their
value, dragging blue chips
lower, but the Nasdaq moved
higher, buoyed by gains in the
semiconductor sector." - ], - [ - "Julia Gillard has reportedly
bowed out of the race to
become shadow treasurer,
taking enormous pressure off
Opposition Leader Mark Latham." - ], - [ - "It #39;s official. Microsoft
recently stated
definitivelyand contrary to
rumorsthat there will be no
new versions of Internet
Explorer for users of older
versions of Windows." - ], - [ - "The success of Paris #39; bid
for Olympic Games 2012 would
bring an exceptional
development for France for at
least 6 years, said Jean-
Francois Lamour, French
minister for Youth and Sports
on Tuesday." - ], - [ - "AFP - Maybe it's something to
do with the fact that the
playing area is so vast that
you need a good pair of
binoculars to see the action
if it's not taking place right
in front of the stands." - ], - [ - "Egypt #39;s release of accused
Israeli spy Azzam Azzam in an
apparent swap for six Egyptian
students held on suspicion of
terrorism is expected to melt
the ice and perhaps result" - ], - [ - "But fresh antitrust suit is in
the envelope, says Novell" - ], - [ - "Chips that help a computer's
main microprocessors perform
specific types of math
problems are becoming a big
business once again.\\" - ], - [ - "GAZA CITY, Gaza Strip: Hamas
militants killed an Israeli
soldier and wounded four with
an explosion in a booby-
trapped chicken coop on
Tuesday, in what the Islamic
group said was an elaborate
scheme to lure troops to the
area with the help of a double" - ], - [ - "A rocket carrying two Russian
cosmonauts and an American
astronaut to the international
space station streaked into
orbit on Thursday, the latest
flight of a Russian space
vehicle to fill in for
grounded US shuttles." - ], - [ - "Bankrupt ATA Airlines #39;
withdrawal from Chicago #39;s
Midway International Airport
presents a juicy opportunity
for another airline to beef up
service in the Midwest" - ], - [ - "AP - The 300 men filling out
forms in the offices of an
Iranian aid group were offered
three choices: Train for
suicide attacks against U.S.
troops in Iraq, for suicide
attacks against Israelis or to
assassinate British author
Salman Rushdie." - ], - [ - "ATHENS, Greece - Gail Devers,
the most talented yet star-
crossed hurdler of her
generation, was unable to
complete even one hurdle in
100-meter event Sunday -
failing once again to win an
Olympic hurdling medal.
Devers, 37, who has three
world championships in the
hurdles but has always flopped
at the Olympics, pulled up
short and screamed as she slid
under the first hurdle..." - ], - [ - "OCTOBER 12, 2004
(COMPUTERWORLD) - Microsoft
Corp. #39;s monthly patch
release cycle may be making it
more predictable for users to
deploy software updates, but
there appears to be little
letup in the number of holes
being discovered in the
company #39;s software" - ], - [ - "Wearable tech goes mainstream
as the Gap introduces jacket
with built-in radio.\\" - ], - [ - "Madison, WI (Sports Network) -
Anthony Davis ran for 122
yards and two touchdowns to
lead No. 6 Wisconsin over
Northwestern, 24-12, to
celebrate Homecoming weekend
at Camp Randall Stadium." - ], - [ - "LaVar Arrington participated
in his first practice since
Oct. 25, when he aggravated a
knee injury that sidelined him
for 10 games." - ], - [ - " NEW YORK (Reuters) - Billie
Jean King cut her final tie
with the U.S. Fed Cup team
Tuesday when she retired as
coach." - ], - [ - "The Instinet Group, owner of
one of the largest electronic
stock trading systems, is for
sale, executives briefed on
the plan say." - ], - [ - "Funding round of \\$105 million
brings broadband Internet
telephony company's total haul
to \\$208 million." - ], - [ - "The Miami Dolphins arrived for
their final exhibition game
last night in New Orleans
short nine players." - ], - [ - "washingtonpost.com - Anthony
Casalena was 17 when he got
his first job as a programmer
for a start-up called
HyperOffice, a software
company that makes e-mail and
contact management
applications for the Web.
Hired as an intern, he became
a regular programmer after two
weeks and rewrote the main
product line." - ], - [ - "The Auburn Hills-based
Chrysler Group made a profit
of \\$269 million in the third
quarter, even though worldwide
sales and revenues declined,
contributing to a \\$1." - ], - [ - "SAN FRANCISCO (CBS.MW) -- UAL
Corp., parent of United
Airlines, said Wednesday it
will overhaul its route
structure to reduce costs and
offset rising fuel costs." - ], - [ - " NAIROBI (Reuters) - The
Sudanese government and its
southern rebel opponents have
agreed to sign a pledge in the
Kenyan capital on Friday to
formally end a brutal 21-year-
old civil war, with U.N.
Security Council ambassadors
as witnesses." - ], - [ - "AP - From LSU at No. 1 to Ohio
State at No. 10, The AP
women's basketball poll had no
changes Monday." - ], - [ - "nother stage victory for race
leader Petter Solberg, his
fifth since the start of the
rally. The Subaru driver is
not pulling away at a fast
pace from Gronholm and Loeb
but the gap is nonetheless
increasing steadily." - ], - [ - "The fossilized neck bones of a
230-million-year-old sea
creature have features
suggesting that the animal
#39;s snakelike throat could
flare open and create suction
that would pull in prey." - ], - [ - "IBM late on Tuesday announced
the biggest update of its
popular WebSphere business
software in two years, adding
features such as automatically
detecting and fixing problems." - ], - [ - "April 1980 -- Players strike
the last eight days of spring
training. Ninety-two
exhibition games are canceled.
June 1981 -- Players stage
first midseason strike in
history." - ], - [ - "AP - Former Guatemalan
President Alfonso Portillo
#151; suspected of corruption
at home #151; is living and
working part-time in the same
Mexican city he fled two
decades ago to avoid arrest on
murder charges, his close
associates told The Associated
Press on Sunday." - ], - [ - "AP - British entrepreneur
Richard Branson said Monday
that his company plans to
launch commercial space
flights over the next few
years." - ], - [ - "Annual economic growth in
China has slowed for the third
quarter in a row, falling to
9.1 per cent, third-quarter
data shows. The slowdown shows
the economy is responding to
Beijing #39;s efforts to rein
in break-neck investment and
lending." - ], - [ - "washingtonpost.com - BRUSSELS,
Aug. 26 -- The United States
will have to wait until next
year to see its fight with the
European Union over biotech
foods resolved, as the World
Trade Organization agreed to
an E.U. request to bring
scientists into the debate,
officials said Thursday." - ], - [ - "A group of Internet security
and instant messaging
providers have teamed up to
detect and thwart the growing
threat of IM and peer-to-peer
viruses and worms, they said
this week." - ], - [ - "On Sunday, the day after Ohio
State dropped to 0-3 in the
Big Ten with a 33-7 loss at
Iowa, the Columbus Dispatch
ran a single word above its
game story on the Buckeyes:
quot;Embarrassing." - ], - [ - "Insisting that Hurriyat
Conference is the real
representative of Kashmiris,
Pakistan has claimed that
India is not ready to accept
ground realities in Kashmir." - ], - [ - "THE All-India Motor Transport
Congress (AIMTC) on Saturday
called off its seven-day
strike after finalising an
agreement with the Government
on the contentious issue of
service tax and the various
demands including tax deducted
at source (TDS), scrapping" - ], - [ - "BOSTON - Curt Schilling got
his 20th win on the eve of
Boston #39;s big series with
the New York Yankees. Now he
wants much more. quot;In a
couple of weeks, hopefully, it
will get a lot better, quot;
he said after becoming" - ], - [ - "Shooed the ghosts of the
Bambino and the Iron Horse and
the Yankee Clipper and the
Mighty Mick, on his 73rd
birthday, no less, and turned
Yankee Stadium into a morgue." - ], - [ - "Gold medal-winning Marlon
Devonish says the men #39;s
4x100m Olympic relay triumph
puts British sprinting back on
the map. Devonish, Darren
Campbell, Jason Gardener and
Mark Lewis-Francis edged out
the American" - ], - [ - "AP - The euro-zone economy
grew by 0.5 percent in the
second quarter of 2004, a
touch slower than in the first
three months of the year,
according to initial estimates
released Tuesday by the
European Union." - ], - [ - "His first space flight was in
1965 when he piloted the first
manned Gemini mission. Later
he made two trips to the moon
-- orbiting during a 1969
flight and then walking on the
lunar surface during a mission
in 1972." - ], - [ - "he difficult road conditions
created a few incidents in the
first run of the Epynt stage.
Francois Duval takes his
second stage victory since the
start of the rally, nine
tenths better than Sebastien
Loeb #39;s performance in
second position." - ], - [ - "VIENNA -- After two years of
investigating Iran's atomic
program, the UN nuclear
watchdog still cannot rule out
that Tehran has a secret atom
bomb project as Washington
insists, the agency's chief
said yesterday." - ], - [ - "By RACHEL KONRAD SAN
FRANCISCO (AP) -- EBay Inc.,
which has been aggressively
expanding in Asia, plans to
increase its stake in South
Korea's largest online auction
company. The Internet
auction giant said Tuesday
night that it would purchase
nearly 3 million publicly held
shares of Internet Auction
Co..." - ], - [ - "AFP - US Secretary of State
Colin Powell wrapped up a
three-nation tour of Asia
after winning pledges from
Japan, China and South Korea
to press North Korea to resume
stalled talks on its nuclear
weapons programs." - ], - [ - "Tallahassee, FL (Sports
Network) - Florida State head
coach Bobby Bowden has
suspended senior wide receiver
Craphonso Thorpe for the
Seminoles #39; game against
fellow Atlantic Coast
Conference member Duke on
Saturday." - ], - [ - "A few years ago, casinos
across the United States were
closing their poker rooms to
make space for more popular
and lucrative slot machines." - ], - [ - "CAIRO, Egypt An Egyptian
company says one of its four
workers who had been kidnapped
in Iraq has been freed. It
says it can #39;t give the
status of the others being
held hostage but says it is
quot;doing its best to secure
quot; their release." - ], - [ - "WASHINGTON -- Consumers were
tightfisted amid soaring
gasoline costs in August and
hurricane-related disruptions
last week sent applications
for jobless benefits to their
highest level in seven months." - ], - [ - "Talking kitchens and vanities.
Musical jump ropes and potty
seats. Blusterous miniature
leaf blowers and vacuum
cleaners -- almost as loud as
the real things." - ], - [ - "Online merchants in the United
States have become better at
weeding out fraudulent credit
card orders, a new survey
indicates. But shipping
overseas remains a risky
venture. By Joanna Glasner." - ], - [ - "Former champion Lleyton Hewitt
bristled, battled and
eventually blossomed as he
took another step towards a
second US Open title with a
second-round victory over
Moroccan Hicham Arazi on
Friday." - ], - [ - "AP - China's biggest computer
maker, Lenovo Group, said
Wednesday it has acquired a
majority stake in
International Business
Machines Corp.'s personal
computer business for
#36;1.75 billion, one of the
biggest Chinese overseas
acquisitions ever." - ], - [ - "Popping a gadget into a cradle
to recharge it used to mean
downtime, but these days
chargers are doing double
duty, keeping your portable
devices playing even when
they're charging." - ], - [ - "AFP - Hosts India braced
themselves for a harrowing
chase on a wearing wicket in
the first Test after Australia
declined to enforce the
follow-on here." - ], - [ - " SAN FRANCISCO (Reuters) -
Texas Instruments Inc. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=T
XN.N target=/stocks/quickinfo/
fullquote\">TXN.N</A>,
the largest maker of chips for
cellular phones, on Monday
said record demand for its
handset and television chips
boosted quarterly profit by
26 percent, even as it
struggles with a nagging
inventory problem." - ], - [ - "LONDON ARM Holdings, a British
semiconductor designer, said
on Monday that it would buy
Artisan Components for \\$913
million to broaden its product
range." - ], - [ - "Big evolutionary insights
sometimes come in little
packages. Witness the
startling discovery, in a cave
on the eastern Indonesian
island of Flores, of the
partial skeleton of a half-
size Homo species that" - ], - [ - "Prime Minister Paul Martin of
Canada urged Haitian leaders
on Sunday to allow the
political party of the deposed
president, Jean-Bertrand
Aristide, to take part in new
elections." - ], - [ - "Hostage takers holding up to
240 people at a school in
southern Russia have refused
to talk with a top Islamic
leader and demanded to meet
with regional leaders instead,
ITAR-TASS reported on
Wednesday." - ], - [ - "As the Mets round out their
search for a new manager, the
club is giving a last-minute
nod to its past. Wally
Backman, an infielder for the
Mets from 1980-88 who played
second base on the 1986" - ], - [ - "MELBOURNE: Global shopping
mall owner Westfield Group
will team up with Australian
developer Multiplex Group to
bid 585mil (US\\$1." - ], - [ - "Three children from a care
home are missing on the
Lancashire moors after they
are separated from a group." - ], - [ - "Luke Skywalker and Darth Vader
may get all the glory, but a
new Star Wars video game
finally gives credit to the
everyday grunts who couldn
#39;t summon the Force for
help." - ], - [ - "AMSTERDAM, Aug 18 (Reuters) -
Midfielder Edgar Davids #39;s
leadership qualities and
never-say-die attitude have
earned him the captaincy of
the Netherlands under new
coach Marco van Basten." - ], - [ - "COUNTY KILKENNY, Ireland (PA)
-- Hurricane Jeanne has led to
world No. 1 Vijay Singh
pulling out of this week #39;s
WGC-American Express
Championship at Mount Juliet." - ], - [ - "Green Bay, WI (Sports Network)
- The Green Bay Packers will
be without the services of Pro
Bowl center Mike Flanagan for
the remainder of the season,
as he will undergo left knee
surgery." - ], - [ - "Diabetics should test their
blood sugar levels more
regularly to reduce the risk
of cardiovascular disease, a
study says." - ], - [ - "COLUMBUS, Ohio -- NCAA
investigators will return to
Ohio State University Monday
to take another look at the
football program after the
latest round of allegations
made by former players,
according to the Akron Beacon
Journal." - ], - [ - " LOS ANGELES (Reuters) -
Federal authorities raided
three Washington, D.C.-area
video game stores and arrested
two people for modifying
video game consoles to play
pirated video games, a video
game industry group said on
Wednesday." - ], - [ - "Manchester United gave Alex
Ferguson a 1,000th game
anniversary present by
reaching the last 16 of the
Champions League yesterday,
while four-time winners Bayern
Munich romped into the second
round with a 5-1 beating of
Maccabi Tel Aviv." - ], - [ - "Iraq's interim Prime Minister
Ayad Allawi announced that
proceedings would begin
against former Baath Party
leaders." - ], - [ - "Reuters - Delta Air Lines Inc.
, which is\\racing to cut costs
to avoid bankruptcy, on
Wednesday reported\\a wider
quarterly loss amid soaring
fuel prices and weak\\domestic
airfares." - ], - [ - "Energy utility TXU Corp. on
Monday more than quadrupled
its quarterly dividend payment
and raised its projected
earnings for 2004 and 2005
after a companywide
performance review." - ], - [ - "Northwest Airlines Corp., the
fourth- largest US airline,
and its pilots union reached
tentative agreement on a
contract that would cut pay
and benefits, saving the
company \\$265 million a year." - ], - [ - "The last time the Angels
played the Texas Rangers, they
dropped two consecutive
shutouts at home in their most
agonizing lost weekend of the
season." - ], - [ - "Microsoft Corp. MSFT.O and
cable television provider
Comcast Corp. CMCSA.O said on
Monday that they would begin
deploying set-top boxes
powered by Microsoft software
starting next week." - ], - [ - "SEATTLE - Chasing a nearly
forgotten ghost of the game,
Ichiro Suzuki broke one of
baseball #39;s oldest records
Friday night, smoking a single
up the middle for his 258th
hit of the year and breaking
George Sisler #39;s record for
the most hits in a season" - ], - [ - "Grace Park, her caddie - and
fans - were poking around in
the desert brush alongside the
18th fairway desperately
looking for her ball." - ], - [ - "Philippines mobile phone
operator Smart Communications
Inc. is in talks with
Singapore #39;s Mobile One for
a possible tie-up,
BusinessWorld reported Monday." - ], - [ - "Washington Redskins kicker
John Hall strained his right
groin during practice
Thursday, his third leg injury
of the season. Hall will be
held out of practice Friday
and is questionable for Sunday
#39;s game against the Chicago
Bears." - ], - [ - "Airline warns it may file for
bankruptcy if too many senior
pilots take early retirement
option. Delta Air LInes #39;
CEO says it faces bankruptcy
if it can #39;t slow the pace
of pilots taking early
retirement." - ], - [ - "A toxic batch of home-brewed
alcohol has killed 31 people
in several towns in central
Pakistan, police and hospital
officials say." - ], - [ - "Thornbugs communicate by
vibrating the branches they
live on. Now scientists are
discovering just what the bugs
are \"saying.\"" - ], - [ - "The Florida Gators and
Arkansas Razorbacks meet for
just the sixth time Saturday.
The Gators hold a 4-1
advantage in the previous five
meetings, including last year
#39;s 33-28 win." - ], - [ - "Kodak Versamark #39;s parent
company, Eastman Kodak Co.,
reported Tuesday it plans to
eliminate almost 900 jobs this
year in a restructuring of its
overseas operations." - ], - [ - "A top official of the US Food
and Drug Administration said
Friday that he and his
colleagues quot;categorically
reject quot; earlier
Congressional testimony that
the agency has failed to
protect the public against
dangerous drugs." - ], - [ - " BEIJING (Reuters) - North
Korea is committed to holding
six-party talks aimed at
resolving the crisis over its
nuclear weapons program, but
has not indicated when, a top
British official said on
Tuesday." - ], - [ - "AP - 1941 #151; Brooklyn
catcher Mickey Owen dropped a
third strike on Tommy Henrich
of what would have been the
last out of a Dodgers victory
against the New York Yankees.
Given the second chance, the
Yankees scored four runs for a
7-4 victory and a 3-1 lead in
the World Series, which they
ended up winning." - ], - [ - "Federal prosecutors cracked a
global cartel that had
illegally fixed prices of
memory chips in personal
computers and servers for
three years." - ], - [ - "AP - Oracle Corp. CEO Larry
Ellison reiterated his
determination to prevail in a
long-running takeover battle
with rival business software
maker PeopleSoft Inc.,
predicting the proposed deal
will create a more competitive
company with improved customer
service." - ], - [ - "Braves shortstop Rafael Furcal
was arrested on drunken
driving charges Friday, his
second D.U.I. arrest in four
years." - ], - [ - "AFP - British retailer Marks
and Spencer announced a major
management shake-up as part of
efforts to revive its
fortunes, saying trading has
become tougher ahead of the
crucial Christmas period." - ], - [ - " BAGHDAD (Reuters) - Iraq's
interim government extended
the closure of Baghdad
international airport
indefinitely on Saturday
under emergency rule imposed
ahead of this week's U.S.-led
offensive on Falluja." - ], - [ - " ATHENS (Reuters) - Natalie
Coughlin's run of bad luck
finally took a turn for the
better when she won the gold
medal in the women's 100
meters backstroke at the
Athens Olympics Monday." - ], - [ - " ATLANTA (Reuters) - Home
Depot Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=HD.N target=/
stocks/quickinfo/fullquote\">
;HD.N</A>, the top home
improvement retailer, on
Tuesday reported a 15 percent
rise in third-quarter profit,
topping estimates, aided by
installed services and sales
to contractors." - ], - [ - " LONDON (Reuters) - The dollar
fought back from one-month
lows against the euro and
Swiss franc on Wednesday as
investors viewed its sell-off
in the wake of the Federal
Reserve's verdict on interest
rates as overdone." - ], - [ - "Rivaling Bush vs. Kerry for
bitterness, doctors and trial
lawyers are squaring off this
fall in an unprecedented four-
state struggle over limiting
malpractice awards..." - ], - [ - "Boston Scientific Corp said on
Friday it has recalled an ear
implant the company acquired
as part of its purchase of
Advanced Bionics in June." - ], - [ - "AP - Pedro Feliz hit a
tiebreaking grand slam with
two outs in the eighth inning
for his fourth hit of the day,
and the Giants helped their
playoff chances with a 9-5
victory over the Los Angeles
Dodgers on Saturday." - ], - [ - "MarketWatch.com. Richemont
sees significant H1 lift,
unclear on FY (2:53 AM ET)
LONDON (CBS.MW) -- Swiss
luxury goods maker
Richemont(zz:ZZ:001273145:
news, chart, profile), which
also is a significant" - ], - [ - "Crude oil climbed more than
\\$1 in New York on the re-
election of President George
W. Bush, who has been filling
the US Strategic Petroleum
Reserve." - ], - [ - "AP - Hundreds of tribesmen
gathered Tuesday near the area
where suspected al-Qaida-
linked militants are holding
two Chinese engineers and
demanding safe passage to
their reputed leader, a former
U.S. prisoner from Guantanamo
Bay, Cuba, officials and
residents said." - ], - [ - "AP - Scientists warned Tuesday
that a long-term increase in
global temperature of 3.5
degrees could threaten Latin
American water supplies,
reduce food yields in Asia and
result in a rise in extreme
weather conditions in the
Caribbean." - ], - [ - "AP - Further proof New York's
real estate market is
inflated: The city plans to
sell space on top of lampposts
to wireless phone companies
for #36;21.6 million a year." - ], - [ - "In an alarming development,
high-precision equipment and
materials which could be used
for making nuclear bombs have
disappeared from some Iraqi
facilities, the United Nations
watchdog agency has said." - ], - [ - "Yukos #39; largest oil-
producing unit regained power
supplies from Tyumenenergo, a
Siberia-based electricity
generator, Friday after the
subsidiary pledged to pay 440
million rubles (\\$15 million)
in debt by Oct. 3." - ], - [ - "The rollout of new servers and
networking switches in stores
is part of a five-year
agreement supporting 7-Eleven
#39;s Retail Information
System." - ], - [ - "Top Hollywood studios have
launched a wave of court cases
against internet users who
illegally download film files.
The Motion Picture Association
of America, which acts as the
representative of major film" - ], - [ - "AUSTRALIANS went into a
television-buying frenzy the
run-up to the Athens Olympics,
suggesting that as a nation we
could easily have scored a
gold medal for TV purchasing." - ], - [ - "US STOCKS vacillated yesterday
as rising oil prices muted
Wall Streets excitement over
strong results from Lehman
Brothers and Sprints \\$35
billion acquisition of Nextel
Communications." - ], - [ - "The next time you are in your
bedroom with your PC plus
Webcam switched on, don #39;t
think that your privacy is all
intact. If you have a Webcam
plugged into an infected
computer, there is a
possibility that" - ], - [ - "At the head of the class,
Rosabeth Moss Kanter is an
intellectual whirlwind: loud,
expansive, in constant motion." - ], - [ - "LEVERKUSEN/ROME, Dec 7 (SW) -
Dynamo Kiev, Bayer Leverkusen,
and Real Madrid all have a
good chance of qualifying for
the Champions League Round of
16 if they can get the right
results in Group F on
Wednesday night." - ], - [ - "Ed Hinkel made a diving,
fingertip catch for a key
touchdown and No. 16 Iowa
stiffened on defense when it
needed to most to beat Iowa
State 17-10 Saturday." - ], - [ - "During last Sunday #39;s
Nextel Cup race, amid the
ongoing furor over Dale
Earnhardt Jr. #39;s salty
language, NBC ran a commercial
for a show coming on later
that night called quot;Law
amp; Order: Criminal Intent." - ], - [ - "AP - After playing in hail,
fog and chill, top-ranked
Southern California finishes
its season in familiar
comfort. The Trojans (9-0, 6-0
Pacific-10) have two games at
home #151; against Arizona on
Saturday and Notre Dame on
Nov. 27 #151; before their
rivalry game at UCLA." - ], - [ - "A US airman dies and two are
hurt as a helicopter crashes
due to technical problems in
western Afghanistan." - ], - [ - "Jacques Chirac has ruled out
any withdrawal of French
troops from Ivory Coast,
despite unrest and anti-French
attacks, which have forced the
evacuation of thousands of
Westerners." - ], - [ - "Japanese Prime Minister
Junichiro Koizumi reshuffled
his cabinet yesterday,
replacing several top
ministers in an effort to
boost his popularity,
consolidate political support
and quicken the pace of
reforms in the world #39;s
second-largest economy." - ], - [ - "The remnants of Hurricane
Jeanne rained out Monday's
game between the Mets and the
Atlanta Braves. It will be
made up Tuesday as part of a
doubleheader." - ], - [ - "AP - NASCAR is not expecting
any immediate changes to its
top-tier racing series
following the merger between
telecommunications giant
Sprint Corp. and Nextel
Communications Inc." - ], - [ - "AP - Shawn Fanning's Napster
software enabled countless
music fans to swap songs on
the Internet for free, turning
him into the recording
industry's enemy No. 1 in the
process." - ], - [ - "TBILISI (Reuters) - At least
two Georgian soldiers were
killed and five wounded in
artillery fire with
separatists in the breakaway
region of South Ossetia,
Georgian officials said on
Wednesday." - ], - [ - "Like wide-open races? You
#39;ll love the Big 12 North.
Here #39;s a quick morning
line of the Big 12 North as it
opens conference play this
weekend." - ], - [ - "Reuters - Walt Disney Co.
is\\increasing investment in
video games for hand-held
devices and\\plans to look for
acquisitions of small game
publishers and\\developers,
Disney consumer products
Chairman Andy Mooney said\\on
Monday." - ], - [ - "Taquan Dean scored 22 points,
Francisco Garcia added 19 and
No. 13 Louisville withstood a
late rally to beat Florida
74-70 Saturday." - ], - [ - "BANGKOK - A UN conference last
week banned commercial trade
in the rare Irrawaddy dolphin,
a move environmentalists said
was needed to save the
threatened species." - ], - [ - "Laksamana.Net - Two Indonesian
female migrant workers freed
by militants in Iraq are
expected to arrive home within
a day or two, the Foreign
Affairs Ministry said
Wednesday (6/10/04)." - ], - [ - "A bitter row between America
and the European Union over
alleged subsidies to rival
aircraft makers Boeing and
Airbus intensified yesterday." - ], - [ - "PC World - Updated antivirus
software for businesses adds
intrusion prevention features." - ], - [ - "NEW YORK -- This was all about
Athens, about redemption for
one and validation for the
other. Britain's Paula
Radcliffe, the fastest female
marathoner in history, failed
to finish either of her
Olympic races last summer.
South Africa's Hendrik Ramaala
was a five-ringed dropout,
too, reinforcing his
reputation as a man who could
go only half the distance." - ], - [ - "Reuters - Online media company
Yahoo Inc.\\ late on Monday
rolled out tests of redesigned
start\\pages for its popular
Yahoo.com and My.Yahoo.com
sites." - ], - [ - "Amsterdam (pts) - Dutch
electronics company Philips
http://www.philips.com has
announced today, Friday, that
it has cut its stake in Atos
Origin by more than a half." - ], - [ - "TORONTO (CP) - Two-thirds of
banks around the world have
reported an increase in the
volume of suspicious
activities that they report to
police, a new report by KPMG
suggests." - ], - [ - "The Sun may have captured
thousands or even millions of
asteroids from another
planetary system during an
encounter more than four
billion years ago, astronomers
are reporting." - ], - [ - "LONDON -- Ernie Els has set
his sights on an improved
putting display this week at
the World Golf Championships
#39; NEC Invitational in
Akron, Ohio, after the
disappointment of tying for
fourth place at the PGA
Championship last Sunday." - ], - [ - "The Atkins diet frenzy slowed
growth briefly, but the
sandwich business is booming,
with \\$105 billion in sales
last year." - ], - [ - "Luxury carmaker Jaguar said
Friday it was stopping
production at a factory in
central England, resulting in
a loss of 1,100 jobs,
following poor sales in the
key US market." - ], - [ - "A bus was hijacked today and
shots were fired at police who
surrounded it on the outskirts
of Athens. Police did not know
how many passengers were
aboard the bus." - ], - [ - "Thumb through the book - then
buy a clean copy from Amazon" - ], - [ - "AP - President Bashar Assad
shuffled his Cabinet on
Monday, just weeks after the
United States and the United
Nations challenged Syria over
its military presence in
Lebanon and the security
situation along its border
with Iraq." - ], - [ - "Fiji #39;s Vijay Singh
replaced Tiger Woods as the
world #39;s No.1 ranked golfer
today by winning the PGA
Deutsche Bank Championship." - ], - [ - "LEIPZIG, Germany : Jurgen
Klinsmann enjoyed his first
home win as German manager
with his team defeating ten-
man Cameroon 3-0 in a friendly
match." - ], - [ - "AP - Kevin Brown had a chance
to claim a place in Yankees
postseason history with his
start in Game 7 of the AL
championship series." - ], - [ - "Reuters - High-definition
television can\\show the sweat
beading on an athlete's brow,
but the cost of\\all the
necessary electronic equipment
can get a shopper's own\\pulse
racing." - ], - [ - "HOMESTEAD, Fla. -- Kurt Busch
got his first big break in
NASCAR by winning a 1999
talent audition nicknamed
quot;The Gong Show. quot; He
was selected from dozens of
unknown, young race drivers to
work for one of the sport
#39;s most famous team owners,
Jack Roush." - ], - [ - "AP - President Vladimir Putin
has signed a bill confirming
Russia's ratification of the
Kyoto Protocol, the Kremlin
said Friday, clearing the way
for the global climate pact to
come into force early next
year." - ], - [ - "John Gibson said Friday that
he decided to resign as chief
executive officer of
Halliburton Energy Services
when it became apparent he
couldn #39;t become the CEO of
the entire corporation, after
getting a taste of the No." - ], - [ - "MacCentral - Apple Computer
Inc. on Monday released an
update for Apple Remote
Desktop (ARD), the company's
software solution to assist
Mac system administrators and
computer managers with asset
management, software
distribution and help desk
support. ARD 2.1 includes
several enhancements and bug
fixes." - ], - [ - "NEW YORK (Reuters) - Outback
Steakhouse Inc. said Tuesday
it lost about 130 operating
days and up to \\$2 million in
revenue because it had to
close some restaurants in the
South due to Hurricane
Charley." - ], - [ - "State insurance commissioners
from across the country have
proposed new rules governing
insurance brokerage fees,
winning praise from an
industry group and criticism
from" - ], - [ - "AP - The authenticity of newly
unearthed memos stating that
George W. Bush failed to meet
standards of the Texas Air
National Guard during the
Vietnam War was questioned
Thursday by the son of the
late officer who reportedly
wrote the memos." - ], - [ - "Zurich, Switzerland (Sports
Network) - Former world No. 1
Venus Williams advanced on
Thursday and will now meet
Wimbledon champion Maria
Sharapova in the quarterfinals
at the \\$1." - ], - [ - "INDIA #39;S cricket chiefs
began a frenetic search today
for a broadcaster to show next
month #39;s home series
against world champion
Australia after cancelling a
controversial \\$US308 million
(\\$440 million) television
deal." - ], - [ - "Canadian Press - OAKVILLE,
Ont. (CP) - The body of a
missing autistic man was
pulled from a creek Monday,
just metres from where a key
piece of evidence was
uncovered but originally
overlooked because searchers
had the wrong information." - ], - [ - "SOFTWARE giant Oracle #39;s
stalled \\$7.7bn (4.2bn) bid to
take over competitor
PeopleSoft has received a huge
boost after a US judge threw
out an anti-trust lawsuit
filed by the Department of
Justice to block the
acquisition." - ], - [ - "The International Olympic
Committee (IOC) has urged
Beijing to ensure the city is
ready to host the 2008 Games
well in advance, an official
said on Wednesday." - ], - [ - "AFP - German Chancellor
Gerhard Schroeder arrived in
Libya for an official visit
during which he is to hold
talks with Libyan leader
Moamer Kadhafi." - ], - [ - "The fastest-swiveling space
science observatory ever built
rocketed into orbit on
Saturday to scan the universe
for celestial explosions." - ], - [ - "The government will examine
claims 100,000 Iraqi civilians
have been killed since the US-
led invasion, Jack Straw says." - ], - [ - "Virginia Tech scores 24 points
off four first-half turnovers
in a 55-6 wipeout of Maryland
on Thursday to remain alone
atop the ACC." - ], - [ - "Copernic Unleashes Desktop
Search Tool\\\\Copernic
Technologies Inc. today
announced Copernic Desktop
Search(TM) (CDS(TM)), \"The
Search Engine For Your PC
(TM).\" Copernic has used the
experience gained from over 30
million downloads of its
Windows-based Web search
software to develop CDS, a
desktop search product that
users are saying is far ..." - ], - [ - "The DVD Forum moved a step
further toward the advent of
HD DVD media and drives with
the approval of key physical
specifications at a meeting of
the organisations steering
committee last week." - ], - [ - "Eton College and Clarence
House joined forces yesterday
to deny allegations due to be
made at an employment tribunal
today by a former art teacher
that she improperly helped
Prince Harry secure an A-level
pass in art two years ago." - ], - [ - "AFP - Great Britain's chances
of qualifying for the World
Group of the Davis Cup were
evaporating rapidly after
Austria moved into a 2-1 lead
following the doubles." - ], - [ - "AP - Martina Navratilova's
long, illustrious career will
end without an Olympic medal.
The 47-year-old Navratilova
and Lisa Raymond lost 6-4,
4-6, 6-4 on Thursday night to
fifth-seeded Shinobu Asagoe
and Ai Sugiyama of Japan in
the quarterfinals, one step
shy of the medal round." - ], - [ - "Often pigeonholed as just a
seller of televisions and DVD
players, Royal Philips
Electronics said third-quarter
profit surged despite a slide
into the red by its consumer
electronics division." - ], - [ - "AP - Google, the Internet
search engine, has done
something that law enforcement
officials and their computer
tools could not: Identify a
man who died in an apparent
hit-and-run accident 11 years
ago in this small town outside
Yakima." - ], - [ - "We are all used to IE getting
a monthly new security bug
found, but Winamp? In fact,
this is not the first security
flaw found in the application." - ], - [ - "The Apache Software Foundation
and the Debian Project said
they won't support the Sender
ID e-mail authentication
standard in their products." - ], - [ - "USATODAY.com - The newly
restored THX 1138 arrives on
DVD today with Star Wars
creator George Lucas' vision
of a Brave New World-like
future." - ], - [ - "Office Depot Inc. (ODP.N:
Quote, Profile, Research) on
Tuesday warned of weaker-than-
expected profits for the rest
of the year because of
disruptions from the string of
hurricanes" - ], - [ - "THE photo-equipment maker
Kodak yesterday announced
plans to axe 600 jobs in the
UK and close a factory in
Nottinghamshire, in a move in
line with the giants global
restructuring strategy
unveiled last January." - ], - [ - "The chances of scientists
making any one of five
discoveries by 2010 have been
hugely underestimated,
according to bookmakers." - ], - [ - "Asia-Pacific leaders meet in
Australia to discuss how to
keep nuclear weapons out of
the hands of extremists." - ], - [ - " TALL AFAR, Iraq -- A three-
foot-high coil of razor wire,
21-ton armored vehicles and
American soldiers with black
M-4 assault rifles stood
between tens of thousands of
people and their homes last
week." - ], - [ - "PSV Eindhoven re-established
their five-point lead at the
top of the Dutch Eredivisie
today with a 2-0 win at
Vitesse Arnhem. Former
Sheffield Wednesday striker
Gerald Sibon put PSV ahead in
the 56th minute" - ], - [ - "China's central bank on
Thursday raised interest rates
for the first time in nearly a
decade, signaling deepening
unease with the breakneck pace
of development and an intent
to reign in a construction
boom now sowing fears of
runaway inflation." - ], - [ - "Deepening its commitment to
help corporate users create
SOAs (service-oriented
architectures) through the use
of Web services, IBM's Global
Services unit on Thursday
announced the formation of an
SOA Management Practice." - ], - [ - "TODAY AUTO RACING 3 p.m. --
NASCAR Nextel Cup Sylvania 300
qualifying at N.H.
International Speedway,
Loudon, N.H., TNT PRO BASEBALL
7 p.m. -- Red Sox at New York
Yankees, Ch. 38, WEEI (850)
(on cable systems where Ch. 38
is not available, the game
will air on NESN); Chicago
Cubs at Cincinnati, ESPN 6
p.m. -- Eastern League finals:
..." - ], - [ - "MUMBAI, SEPTEMBER 21: The
Board of Control for Cricket
in India (BCCI) today informed
the Bombay High Court that it
was cancelling the entire
tender process regarding
cricket telecast rights as
also the conditional deal with
Zee TV." - ], - [ - "CHICAGO - Delta Air Lines
(DAL) said Wednesday it plans
to eliminate between 6,000 and
6,900 jobs during the next 18
months, implement a 10 across-
the-board pay reduction and
reduce employee benefits." - ], - [ - "LAKE GEORGE, N.Y. - Even
though he's facing double hip
replacement surgery, Bill
Smith is more than happy to
struggle out the door each
morning, limp past his brand
new P.T..." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks were likely to open
flat on Wednesday, with high
oil prices and profit warnings
weighing on the market before
earnings reports start and key
jobs data is released this
week." - ], - [ - "Best known for its popular
search engine, Google is
rapidly rolling out new
products and muscling into
Microsoft's stronghold: the
computer desktop. The
competition means consumers
get more choices and better
products." - ], - [ - "Toshiba Corp. #39;s new
desktop-replacement multimedia
notebooks, introduced on
Tuesday, are further evidence
that US consumers still have
yet to embrace the mobility
offered by Intel Corp." - ], - [ - "JEJU, South Korea : Grace Park
of South Korea won an LPGA
Tour tournament, firing a
seven-under-par 65 in the
final round of the
1.35-million dollar CJ Nine
Bridges Classic." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
poured cold water on Tuesday
on recent international
efforts to restart stalled
peace talks with Syria, saying
there was \"no possibility\" of
returning to previous
discussions." - ], - [ - "Dutch smugness was slapped
hard during the past
fortnight. The rude awakening
began with the barbaric
slaying of controversial
filmmaker Theo van Gogh on
November 2. Then followed a
reciprocal cycle of some" - ], - [ - "AP - The NHL will lock out its
players Thursday, starting a
work stoppage that threatens
to keep the sport off the ice
for the entire 2004-05 season." - ], - [ - " MOSCOW (Reuters) - Russia's
Gazprom said on Tuesday it
will bid for embattled oil
firm YUKOS' main unit next
month, as the Kremlin seeks
to turn the world's biggest
gas producer into a major oil
player." - ], - [ - "pee writes quot;A passenger
on a commuter plane in
northern Norway attacked both
pilots and at least one
passenger with an axe as the
aircraft was coming in to
land." - ], - [ - "Aregular Amazon customer,
Yvette Thompson has found
shopping online to be mostly
convenient and trouble-free.
But last month, after ordering
two CDs on Amazon.com, the
Silver Spring reader
discovered on her bank
statement that she was double-
charged for the \\$26.98 order.
And there was a \\$25 charge
that was a mystery." - ], - [ - "Prime Minister Ariel Sharon
pledged Sunday to escalate a
broad Israeli offensive in
northern Gaza, saying troops
will remain until Palestinian
rocket attacks are halted.
Israeli officials said the
offensive -- in which 58
Palestinians and three
Israelis have been killed --
will help clear the way for an
Israeli withdrawal." - ], - [ - "Federal Reserve officials
raised a key short-term
interest rate Tuesday for the
fifth time this year, and
indicated they will gradually
move rates higher next year to
keep inflation under control
as the economy expands." - ], - [ - "Canadians are paying more to
borrow money for homes, cars
and other purchases today
after a quarter-point
interest-rate increase by the
Bank of Canada yesterday was
quickly matched by the
chartered banks." - ], - [ - "NEW YORK - Wall Street
professionals know to keep
their expectations in check in
September, historically the
worst month of the year for
stocks. As summertime draws to
a close, money managers are
getting back to business,
cleaning house, and often
sending the market lower in
the process..." - ], - [ - "A group linked to al Qaeda
ally Abu Musab al-Zarqawi said
it had tried to kill Iraq
#39;s environment minister on
Tuesday and warned it would
not miss next time, according
to an Internet statement." - ], - [ - "The Israeli military killed
four Palestinian militants on
Wednesday as troops in tanks
and armored vehicles pushed
into another town in the
northern Gaza Strip, extending" - ], - [ - "When Paula Radcliffe dropped
out of the Olympic marathon
miles from the finish, she
sobbed uncontrollably.
Margaret Okayo knew the
feeling." - ], - [ - "Delta Air Lines is to issue
millions of new shares without
shareholder consent as part of
moves to ensure its survival." - ], - [ - "First baseman Richie Sexson
agrees to a four-year contract
with the Seattle Mariners on
Wednesday." - ], - [ - "KIRKUK, Iraq - A suicide
attacker detonated a car bomb
Saturday outside a police
academy in the northern Iraqi
city of Kirkuk as hundreds of
trainees and civilians were
leaving for the day, killing
at least 20 people and
wounding 36, authorities said.
Separately, U.S and Iraqi
forces clashed with insurgents
in another part of northern
Iraq after launching an
operation to destroy an
alleged militant cell in the
town of Tal Afar, the U.S..." - ], - [ - "Genta (GNTA:Nasdaq - news -
research) is never boring!
Monday night, the company
announced that its phase III
Genasense study in chronic
lymphocytic leukemia (CLL) met
its primary endpoint, which
was tumor shrinkage." - ], - [ - "Finnish mobile giant Nokia has
described its new Preminet
solution, which it launched
Monday (Oct. 25), as a
quot;major worldwide
initiative." - ], - [ - "While the entire airline
industry #39;s finances are
under water, ATA Airlines will
have to hold its breath longer
than its competitors to keep
from going belly up." - ], - [ - " SAN FRANCISCO (Reuters) - At
virtually every turn, Intel
Corp. executives are heaping
praise on an emerging long-
range wireless technology
known as WiMAX, which can
blanket entire cities with
high-speed Internet access." - ], - [ - "One day after ousting its
chief executive, the nation's
largest insurance broker said
it will tell clients exactly
how much they are paying for
services and renounce back-
door payments from carriers." - ], - [ - "LONDON (CBS.MW) -- Outlining
an expectation for higher oil
prices and increasing demand,
Royal Dutch/Shell on Wednesday
said it #39;s lifting project
spending to \\$45 billion over
the next three years." - ], - [ - "Tuesday #39;s meeting could
hold clues to whether it
#39;ll be a November or
December pause in rate hikes.
By Chris Isidore, CNN/Money
senior writer." - ], - [ - "Phishing is one of the
fastest-growing forms of
personal fraud in the world.
While consumers are the most
obvious victims, the damage
spreads far wider--hurting
companies #39; finances and
reputations and potentially" - ], - [ - "Reuters - The U.S. Interior
Department on\\Friday gave
final approval to a plan by
ConocoPhillips and\\partner
Anadarko Petroleum Corp. to
develop five tracts around\\the
oil-rich Alpine field on
Alaska's North Slope." - ], - [ - "The dollar may fall against
the euro for a third week in
four on concern near-record
crude oil prices will temper
the pace of expansion in the
US economy, a survey by
Bloomberg News indicates." - ], - [ - "The battle for the British-
based Chelsfield plc hotted up
at the weekend, with reports
from London that the property
giant #39;s management was
working on its own bid to
thwart the 585 million (\\$A1." - ], - [ - "Atari has opened the initial
sign-up phase of the closed
beta for its Dungeons amp;
Dragons real-time-strategy
title, Dragonshard." - ], - [ - "AP - Many states are facing
legal challenges over possible
voting problems Nov. 2. A look
at some of the developments
Thursday:" - ], - [ - "Israeli troops withdrew from
the southern Gaza Strip town
of Khan Yunis on Tuesday
morning, following a 30-hour
operation that left 17
Palestinians dead." - ], - [ - "Notes and quotes from various
drivers following California
Speedway #39;s Pop Secret 500.
Jeff Gordon slipped to second
in points following an engine
failure while Jimmie Johnson
moved back into first." - ], - [ - "PM-designate Omar Karameh
forms a new 30-member cabinet
which includes women for the
first time." - ], - [ - "Bahrain #39;s king pardoned a
human rights activist who
convicted of inciting hatred
of the government and
sentenced to one year in
prison Sunday in a case linked
to criticism of the prime
minister." - ], - [ - "Big Blue adds features, beefs
up training efforts in China;
rival Unisys debuts new line
and pricing plan." - ], - [ - " MEMPHIS, Tenn. (Sports
Network) - The Memphis
Grizzlies signed All-Star
forward Pau Gasol to a multi-
year contract on Friday.
Terms of the deal were not
announced." - ], - [ - "Leaders from 38 Asian and
European nations are gathering
in Vietnam for a summit of the
Asia-Europe Meeting, know as
ASEM. One thousand delegates
are to discuss global trade
and regional politics during
the two-day forum." - ], - [ - "A US soldier has pleaded
guilty to murdering a wounded
16-year-old Iraqi boy. Staff
Sergeant Johnny Horne was
convicted Friday of the
unpremeditated murder" - ], - [ - "Andre Agassi brushed past
Jonas Bjorkman 6-3 6-4 at the
Stockholm Open on Thursday to
set up a quarter-final meeting
with Spanish sixth seed
Fernando Verdasco." - ], - [ - "South Korea's Hynix
Semiconductor Inc. and Swiss-
based STMicroelectronics NV
signed a joint-venture
agreement on Tuesday to
construct a memory chip
manufacturing plant in Wuxi,
about 100 kilometers west of
Shanghai, in China." - ], - [ - "SAN DIEGO --(Business Wire)--
Oct. 11, 2004 -- Breakthrough
PeopleSoft EnterpriseOne 8.11
Applications Enable
Manufacturers to Become
Demand-Driven." - ], - [ - "Reuters - Oil prices rose on
Friday as tight\\supplies of
distillate fuel, including
heating oil, ahead of\\the
northern hemisphere winter
spurred buying." - ], - [ - "Well, Intel did say -
dismissively of course - that
wasn #39;t going to try to
match AMD #39;s little dual-
core Opteron demo coup of last
week and show off a dual-core
Xeon at the Intel Developer
Forum this week and - as good
as its word - it didn #39;t." - ], - [ - "Guinea-Bissau #39;s army chief
of staff and former interim
president, General Verissimo
Correia Seabra, was killed
Wednesday during unrest by
mutinous soldiers in the
former Portuguese" - ], - [ - "31 October 2004 -- Exit polls
show that Prime Minister
Viktor Yanukovich and
challenger Viktor Yushchenko
finished on top in Ukraine
#39;s presidential election
today and will face each other
in a run-off next month." - ], - [ - "Nov. 18, 2004 - An FDA
scientist says the FDA, which
is charged with protecting
America #39;s prescription
drug supply, is incapable of
doing so." - ], - [ - "Rock singer Bono pledges to
spend the rest of his life
trying to eradicate extreme
poverty around the world." - ], - [ - "AP - Just when tourists
thought it was safe to go back
to the Princess Diana memorial
fountain, the mud has struck." - ], - [ - "The UK's inflation rate fell
in September, thanks in part
to a fall in the price of
airfares, increasing the
chance that interest rates
will be kept on hold." - ], - [ - " HONG KONG/SAN FRANCISCO
(Reuters) - IBM is selling its
PC-making business to China's
largest personal computer
company, Lenovo Group Ltd.,
for \\$1.25 billion, marking
the U.S. firm's retreat from
an industry it helped pioneer
in 1981." - ], - [ - "AP - Three times a week, The
Associated Press picks an
issue and asks President Bush
and Democratic presidential
candidate John Kerry a
question about it. Today's
question and their responses:" - ], - [ - " BOSTON (Reuters) - Boston was
tingling with anticipation on
Saturday as the Red Sox
prepared to host Game One of
the World Series against the
St. Louis Cardinals and take a
step toward ridding
themselves of a hex that has
hung over the team for eight
decades." - ], - [ - "FOR the first time since his
appointment as Newcastle
United manager, Graeme Souness
has been required to display
the strong-arm disciplinary
qualities that, to Newcastle
directors, made" - ], - [ - "In an apparent damage control
exercise, Russian President
Vladimir Putin on Saturday
said he favored veto rights
for India as new permanent
member of the UN Security
Council." - ], - [ - "Nordstrom reported a strong
second-quarter profit as it
continued to select more
relevant inventory and sell
more items at full price." - ], - [ - "WHY IT HAPPENED Tom Coughlin
won his first game as Giants
coach and immediately
announced a fine amnesty for
all Giants. Just kidding." - ], - [ - "A second-place finish in his
first tournament since getting
married was good enough to
take Tiger Woods from third to
second in the world rankings." - ], - [ - " COLORADO SPRINGS, Colorado
(Reuters) - World 400 meters
champion Jerome Young has been
given a lifetime ban from
athletics for a second doping
offense, the U.S. Anti-Doping
Agency (USADA) said Wednesday." - ], - [ - "AP - Nigeria's Senate has
ordered a subsidiary of
petroleum giant Royal/Dutch
Shell to pay a Nigerian ethnic
group #36;1.5 billion for oil
spills in their homelands, but
the legislative body can't
enforce the resolution, an
official said Wednesday." - ], - [ - "IT #39;S BEEN a heck of an
interesting two days here in
Iceland. I #39;ve seen some
interesting technology, heard
some inventive speeches and
met some people with different
ideas." - ], - [ - "The Bank of England is set to
keep interest rates on hold
following the latest meeting
of the its Monetary Policy
Committee." - ], - [ - "Australian troops in Baghdad
came under attack today for
the first time since the end
of the Iraq war when a car
bomb exploded injuring three
soldiers and damaging an
Australian armoured convoy." - ], - [ - "The Bush administration upheld
yesterday the imposition of
penalty tariffs on shrimp
imports from China and
Vietnam, handing a victory to
beleaguered US shrimp
producers." - ], - [ - "House prices rose 0.2 percent
in September compared with the
month before to stand up 17.8
percent on a year ago, the
Nationwide Building Society
says." - ], - [ - "Reuters - Two clients of
Germany's Postbank\\(DPBGn.DE)
fell for an e-mail fraud that
led them to reveal\\money
transfer codes to a bogus Web
site -- the first case of\\this
scam in German, prosecutors
said on Thursday." - ], - [ - "US spending on information
technology goods, services,
and staff will grow seven
percent in 2005 and continue
at a similar pace through
2008, according to a study
released Monday by Forrester
Research." - ], - [ - "LONDON - In two years, Arsenal
will play their home matches
in the Emirates stadium. That
is what their new stadium at
Ashburton Grove will be called
after the Premiership
champions yesterday agreed to
the" - ], - [ - "KNOXVILLE, Tenn. -- Jason
Campbell threw for 252 yards
and two touchdowns, and No. 8
Auburn proved itself as a
national title contender by
overwhelming No. 10 Tennessee,
34-10, last night." - ], - [ - "Look, Ma, no hands! The U.S.
space agency's latest
spacecraft can run an entire
mission by itself. By Amit
Asaravala." - ], - [ - "Pakistans decision to refuse
the International Atomic
Energy Agency to have direct
access to Dr AQ Khan is
correct on both legal and
political counts." - ], - [ - "MANILA, 4 December 2004 - With
floods receding, rescuers
raced to deliver food to
famished survivors in
northeastern Philippine
villages isolated by back-to-
back storms that left more
than 650 people dead and
almost 400 missing." - ], - [ - "Talks on where to build the
world #39;s first nuclear
fusion reactor ended without a
deal on Tuesday but the
European Union said Japan and
the United States no longer
firmly opposed its bid to put
the plant in France." - ], - [ - "Joining America Online,
EarthLink and Yahoo against
spamming, Microsoft Corp.
today announced the filing of
three new anti-Spam lawsuits
under the CAN-SPAM federal law
as part of its initiative in
solving the Spam problem for
Internet users worldwide." - ], - [ - "WASHINGTON -- Another
Revolution season concluded
with an overtime elimination.
Last night, the Revolution
thrice rallied from deficits
for a 3-3 tie with D.C. United
in the Eastern Conference
final, then lost in the first-
ever Major League Soccer match
decided by penalty kicks." - ], - [ - "Dwyane Wade calls himself a
quot;sidekick, quot; gladly
accepting the role Kobe Bryant
never wanted in Los Angeles.
And not only does second-year
Heat point" - ], - [ - "A nationwide inspection shows
Internet users are not as safe
online as they believe. The
inspections found most
consumers have no firewall
protection, outdated antivirus
software and dozens of spyware
programs secretly running on
their computers." - ], - [ - "World number one golfer Vijay
Singh of Fiji has captured his
eighth PGA Tour event of the
year with a win at the 84
Lumber Classic in Farmington,
Pennsylvania." - ], - [ - "The noise was deafening and
potentially unsettling in the
minutes before the start of
the men #39;s Olympic
200-meter final. The Olympic
Stadium crowd chanted
quot;Hellas!" - ], - [ - "CLEVELAND - The White House
said Vice President Dick
Cheney faces a \"master
litigator\" when he debates
Sen. John Edwards Tuesday
night, a backhanded compliment
issued as the Republican
administration defended itself
against criticism that it has
not acknowledged errors in
waging war in Iraq..." - ], - [ - "Brazilian forward Ronaldinho
scored a sensational goal for
Barcelona against Milan,
making for a last-gasp 2-1.
The 24-year-old #39;s
brilliant left-foot hit at the
Nou Camp Wednesday night sent
Barcelona atop of Group F." - ], - [ - "Nortel Networks says it will
again delay the release of its
restated financial results.
The Canadian telecom vendor
originally promised to release
the restated results in
September." - ], - [ - " CHICAGO (Reuters) - At first
glance, paying \\$13 or \\$14
for a hard-wired Internet
laptop connection in a hotel
room might seem expensive." - ], - [ - "SEOUL (Reuters) - The chairman
of South Korea #39;s ruling
Uri Party resigned on Thursday
after saying his father had
served as a military police
officer during Japan #39;s
1910-1945 colonial rule on the
peninsula." - ], - [ - "ALERE, Uganda -- Kasmiro
Bongonyinge remembers sitting
up suddenly in his bed. It was
just after sunrise on a summer
morning two years ago, and the
old man, 87 years old and
blind, knew something was
wrong." - ], - [ - "Reuters - An investigation
into U.S. insurers\\and brokers
rattled insurance industry
stocks for a second day\\on
Friday as investors, shaken
further by subpoenas
delivered\\to the top U.S. life
insurer, struggled to gauge
how deep the\\probe might
reach." - ], - [ - "Bee Staff Writer. SANTA CLARA
- Andre Carter #39;s back
injury has kept him out of the
49ers #39; lineup since Week
1. It #39;s also interfering
with him rooting on his alma
mater in person." - ], - [ - "AP - Kellen Winslow Jr. ended
his second NFL holdout Friday." - ], - [ - "JAKARTA - Official results
have confirmed former army
general Susilo Bambang
Yudhoyono as the winner of
Indonesia #39;s first direct
presidential election, while
incumbent Megawati
Sukarnoputri urged her nation
Thursday to wait for the
official announcement" - ], - [ - "HOUSTON - With only a few
seconds left in a certain
victory for Miami, Peyton
Manning threw a meaningless
6-yard touchdown pass to
Marvin Harrison to cut the
score to 24-15." - ], - [ - "Reuters - A ragged band of
children\\emerges ghost-like
from mists in Ethiopia's
highlands,\\thrusting bunches
of carrots at a car full of
foreigners." - ], - [ - "DEADLY SCORER: Manchester
United #39;s Wayne Rooney
celebrating his three goals
against Fenerbahce this week
at Old Trafford. (AP)." - ], - [ - "AP - A U.N. human rights
expert criticized the U.S.-led
coalition forces in
Afghanistan for violating
international law by allegedly
beating Afghans to death and
forcing some to remove their
clothes or wear hoods." - ], - [ - "You can feel the confidence
level, not just with Team
Canada but with all of Canada.
There #39;s every expectation,
from one end of the bench to
the other, that Martin Brodeur
is going to hold the fort." - ], - [ - "Heading into the first game of
a new season, every team has
question marks. But in 2004,
the Denver Broncos seemed to
have more than normal." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
said on Thursday Yasser
Arafat's death could be a
turning point for peacemaking
but he would pursue a
unilateral plan that would
strip Palestinians of some
land they want for a state." - ], - [ - " AL-ASAD AIRBASE, Iraq
(Reuters) - Defense Secretary
Donald Rumsfeld swept into an
airbase in Iraq's western
desert Sunday to make a
first-hand evaluation of
operations to quell a raging
Iraqi insurgency in his first
such visit in five months." - ], - [ - "More than three out of four
(76 percent) consumers are
experiencing an increase in
spoofing and phishing
incidents, and 35 percent
receive fake e-mails at least
once a week, according to a
recent national study." - ], - [ - "The Dow Jones Industrial
Average failed three times
this year to exceed its
previous high and fell to
about 10,000 each time, most
recently a week ago." - ], - [ - "AP - Deep in the Atlantic
Ocean, undersea explorers are
living a safer life thanks to
germ-fighting clothing made in
Kinston." - ], - [ - "Anaheim, Calif. - There is a
decidedly right lean to the
three-time champions of the
American League Central. In a
span of 26 days, the Minnesota
Twins lost the left side of
their infield to free agency." - ], - [ - "Computer Associates Monday
announced the general
availability of three
Unicenter performance
management products for
mainframe data management." - ], - [ - "Reuters - The European Union
approved on\\Wednesday the
first biotech seeds for
planting and sale across\\EU
territory, flying in the face
of widespread
consumer\\resistance to
genetically modified (GMO)
crops and foods." - ], - [ - "With the NFL trading deadline
set for 4 p.m. Tuesday,
Patriots coach Bill Belichick
said there didn't seem to be
much happening on the trade
front around the league." - ], - [ - "WASHINGTON - Democrat John
Kerry accused President Bush
on Monday of sending U.S.
troops to the \"wrong war in
the wrong place at the wrong
time\" and said he'd try to
bring them all home in four
years..." - ], - [ - " SINGAPORE (Reuters) - Asian
share markets staged a broad-
based retreat on Wednesday,
led by steelmakers amid
warnings of price declines,
but also enveloping technology
and financial stocks on
worries that earnings may
disappoint." - ], - [ - "p2pnet.net News:- A Microsoft
UK quot;WEIGHING THE COST OF
LINUX VS. WINDOWS? LET #39;S
REVIEW THE FACTS quot;
magazine ad has been nailed as
misleading by Britain #39;s
Advertising Standards
Authority (ASA)." - ], - [ - "More lorry drivers are
bringing supplies to Nepal's
capital in defiance of an
indefinite blockade by Maoist
rebels." - ], - [ - "NEW YORK - CNN has a new boss
for the second time in 14
months: former CBS News
executive Jonathan Klein, who
will oversee programming and
editorial direction at the
second-ranked cable news
network." - ], - [ - "Cut-price carrier Virgin Blue
said Tuesday the cost of using
Australian airports is
spiraling upward and asked the
government to review the
deregulated system of charges." - ], - [ - "The retail sector overall may
be reporting a sluggish start
to the season, but holiday
shoppers are scooping up tech
goods at a brisk pace -- and
they're scouring the Web for
bargains more than ever.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\" color=\"#666666\">&
lt;B>-washingtonpost.com<
;/B></FONT>" - ], - [ - "AP - David Beckham broke his
rib moments after scoring
England's second goal in
Saturday's 2-0 win over Wales
in a World Cup qualifying
game." - ], - [ - "Saudi Arabia, Kuwait and the
United Arab Emirates, which
account for almost half of
OPEC #39;s oil output, said
they #39;re committed to
boosting capacity to meet
soaring demand that has driven
prices to a record." - ], - [ - "The US Commerce Department
said Thursday personal income
posted its biggest increase in
three months in August. The
government agency also said
personal spending was
unchanged after rising
strongly in July." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei average opened up 0.54
percent on Monday with banks
and exporters leading the way
as a stronger finish on Wall
Street and declining oil
prices soothed worries over
the global economic outlook." - ], - [ - " BEIJING (Reuters) - Floods
and landslides have killed 76
people in southwest China in
the past four days and washed
away homes and roads, knocked
down power lines and cut off
at least one city, state
media said on Monday." - ], - [ - "Nothing changed at the top of
Serie A as all top teams won
their games to keep the
distance between one another
unaltered. Juventus came back
from behind against Lazio to
win thanks to another goal by
Ibrahimovic" - ], - [ - "The team behind the Beagle 2
mission has unveiled its
design for a successor to the
British Mars lander." - ], - [ - "Survey points to popularity in
Europe, the Middle East and
Asia of receivers that skip
the pay TV and focus on free
programming." - ], - [ - "RICHMOND, Va. Jeremy Mayfield
won his first race in over
four years, taking the
Chevrolet 400 at Richmond
International Raceway after
leader Kurt Busch ran out of
gas eight laps from the
finish." - ], - [ - "AP - Victims of the Sept. 11
attacks were mourned worldwide
Saturday, but in the Middle
East, amid sympathy for the
dead, Arabs said Washington's
support for Israel and the war
on terror launched in the
aftermath of the World Trade
Center's collapse have only
fueled anger and violence." - ], - [ - "Linux publisher Red Hat Inc.
said Tuesday that information-
technology consulting firm
Unisys Corp. will begin
offering a business version of
the company #39;s open-source
operating system on its
servers." - ], - [ - "SEATTLE - Ichiro Suzuki set
the major league record for
hits in a season with 258,
breaking George Sisler's
84-year-old mark with a pair
of singles Friday night. The
Seattle star chopped a leadoff
single in the first inning,
then made history with a
grounder up the middle in the
third..." - ], - [ - "The intruder who entered
British Queen Elizabeth II
#39;s official Scottish
residence and caused a
security scare was a reporter
from the London-based Sunday
Times newspaper, local media
reported Friday." - ], - [ - "IBM's p5-575, a specialized
server geared for high-
performance computing, has
eight 1.9GHz Power5
processors." - ], - [ - "Bruce Wasserstein, head of
Lazard LLC, is asking partners
to take a one-third pay cut as
he readies the world #39;s
largest closely held
investment bank for a share
sale, people familiar with the
situation said." - ], - [ - "Canadian Press - FREDERICTON
(CP) - A New Brunswick truck
driver arrested in Ontario
this week has been accused by
police of stealing 50,000 cans
of Moosehead beer." - ], - [ - "Reuters - British police said
on Monday they had\\charged a
man with sending hoax emails
to relatives of people\\missing
since the Asian tsunami,
saying their loved ones
had\\been confirmed dead." - ], - [ - "The Lemon Bay Manta Rays were
not going to let a hurricane
get in the way of football. On
Friday, they headed to the
practice field for the first
time in eight" - ], - [ - "Microsoft Corp. Chairman Bill
Gates has donated \\$400,000 to
a campaign in California
trying to win approval of a
measure calling for the state
to sell \\$3 billion in bonds
to fund stem-cell research." - ], - [ - "AP - Track star Marion Jones
filed a defamation lawsuit
Wednesday against the man
whose company is at the center
of a federal investigation
into illegal steroid use among
some of the nation's top
athletes." - ], - [ - "LOS ANGELES - On Sept. 1,
former secretary of
Agriculture Dan Glickman
replaced the legendary Jack
Valenti as president and CEO
of Hollywood #39;s trade
group, the Motion Picture
Association of America." - ], - [ - "England #39;s players hit out
at cricket #39;s authorities
tonight and claimed they had
been used as quot;political
pawns quot; after the Zimbabwe
government produced a
spectacular U-turn to ensure
the controversial one-day
series will go ahead." - ], - [ - "Newspaper publisher Pulitzer
Inc. said Sunday that company
officials are considering a
possible sale of the firm to
boost shareholder value." - ], - [ - "Shares of Merck amp; Co.
plunged almost 10 percent
yesterday after a media report
said that documents show the
pharmaceutical giant hid or
denied" - ], - [ - "AP - The Japanese won the
pregame home run derby. Then
the game started and the major
league All-Stars put their
bats to work. Back-to-back
home runs by Moises Alou and
Vernon Wells in the fourth
inning and by Johnny Estrada
and Brad Wilkerson in the
ninth powered the major
leaguers past the Japanese
stars 7-3 Sunday for a 3-0
lead in the eight-game series." - ], - [ - "Reuters - Wall Street was
expected to dip at\\Thursday's
opening, but shares of Texas
Instruments Inc.\\, may climb
after it gave upbeat earnings
guidance." - ], - [ - "Chinese authorities detained a
prominent, U.S.-based Buddhist
leader in connection with his
plans to reopen an ancient
temple complex in the Chinese
province of Inner Mongolia
last week and have forced
dozens of his American
followers to leave the region,
local officials said
Wednesday." - ], - [ - "The director of the National
Hurricane Center stays calm in
the midst of a storm, but
wants everyone in hurricane-
prone areas to get the message
from his media advisories:
Respect the storm's power and
make proper response plans." - ], - [ - "With Chelsea losing their
unbeaten record and Manchester
United failing yet again to
win, William Hill now make
Arsenal red-hot 2/5 favourites
to retain the title." - ], - [ - "Late in August, Boeing #39;s
top sales execs flew to
Singapore for a crucial sales
pitch. They were close to
persuading Singapore Airlines,
one of the world #39;s leading
airlines, to buy the American
company #39;s new jet, the
mid-sized 7E7." - ], - [ - "SBC Communications and
BellSouth will acquire
YellowPages.com with the goal
of building the site into a
nationwide online business
index, the companies said
Thursday." - ], - [ - "Theresa special bookcase in Al
Grohs office completely full
of game plans from his days in
the NFL. Green ones are from
the Jets." - ], - [ - "SAN FRANCISCO Several
California cities and
counties, including Los
Angeles and San Francisco, are
suing Microsoft for what could
amount to billions of dollars." - ], - [ - "Newcastle ensured their place
as top seeds in Friday #39;s
third round UEFA Cup draw
after holding Sporting Lisbon
to a 1-1 draw at St James #39;
Park." - ], - [ - "Adorned with Turkish and EU
flags, Turkey #39;s newspapers
hailed Thursday an official EU
report recommending the
country start talks to join
the bloc, while largely
ignoring the stringent
conditions attached to the
announcement." - ], - [ - "Google plans to release a
version of its desktop search
tool for computers that run
Apple Computer #39;s Mac
operating system, Google #39;s
chief executive, Eric Schmidt,
said Friday." - ], - [ - "AMD : sicurezza e prestazioni
ottimali con il nuovo
processore mobile per notebook
leggeri e sottili; Acer Inc.
preme sull #39;acceleratore
con il nuovo notebook a
marchio Ferrari." - ], - [ - "The sounds of tinkling bells
could be heard above the
bustle of the Farmers Market
on the Long Beach Promenade,
leading shoppers to a row of
bright red tin kettles dotting
a pathway Friday." - ], - [ - "CBC SPORTS ONLINE - Bode
Miller continued his
impressive 2004-05 World Cup
skiing season by winning a
night slalom race in
Sestriere, Italy on Monday." - ], - [ - "Firefox use around the world
climbed 34 percent in the last
month, according to a report
published by Web analytics
company WebSideStory Monday." - ], - [ - "If a plastic card that gives
you credit for something you
don't want isn't your idea of
a great gift, you can put it
up for sale or swap." - ], - [ - "WASHINGTON Aug. 17, 2004
Scientists are planning to
take the pulse of the planet
and more in an effort to
improve weather forecasts,
predict energy needs months in
advance, anticipate disease
outbreaks and even tell
fishermen where the catch will
be ..." - ], - [ - "Damien Rhodes scored on a
2-yard run in the second
overtime, then Syracuse's
defense stopped Pittsburgh on
fourth and 1, sending the
Orange to a 38-31 victory
yesterday in Syracuse, N.Y." - ], - [ - "AP - Anthony Harris scored 18
of his career-high 23 points
in the second half to help
Miami upset No. 19 Florida
72-65 Saturday and give first-
year coach Frank Haith his
biggest victory." - ], - [ - "LONDON Santander Central
Hispano of Spain looked
certain to clinch its bid for
the British mortgage lender
Abbey National, after HBOS,
Britain #39;s biggest home-
loan company, said Wednesday
it would not counterbid, and
after the European Commission
cleared" - ], - [ - "New communications technology
could spawn future products.
Could your purse tell you to
bring an umbrella?" - ], - [ - " WASHINGTON (Reuters) - The
Justice Department is
investigating possible
accounting fraud at Fannie
Mae, bringing greater
government scrutiny to bear on
the mortgage finance company,
already facing a parallel
inquiry by the SEC, a source
close to the matter said on
Thursday." - ], - [ - "AP - The five cities looking
to host the 2012 Summer Games
submitted bids to the
International Olympic
Committee on Monday, entering
the final stage of a long
process in hopes of landing
one of the biggest prizes in
sports." - ], - [ - "SAP has won a \\$35 million
contract to install its human
resources software for the US
Postal Service. The NetWeaver-
based system will replace the
Post Office #39;s current
25-year-old legacy application" - ], - [ - "The FIA has already cancelled
todays activities at Suzuka as
Super Typhoon Ma-On heads
towards the 5.807km circuit.
Saturday practice has been
cancelled altogether while
pre-qualifying and final
qualifying" - ], - [ - "Thailand's prime minister
visits the southern town where
scores of Muslims died in army
custody after a rally." - ], - [ - "Indian industrial group Tata
agrees to invest \\$2bn in
Bangladesh, the biggest single
deal agreed by a firm in the
south Asian country." - ], - [ - "NewsFactor - For years,
companies large and small have
been convinced that if they
want the sophisticated
functionality of enterprise-
class software like ERP and
CRM systems, they must buy
pre-packaged applications.
And, to a large extent, that
remains true." - ], - [ - "Following in the footsteps of
the RIAA, the MPAA (Motion
Picture Association of
America) announced that they
have began filing lawsuits
against people who use peer-
to-peer software to download
copyrighted movies off the
Internet." - ], - [ - " GRAND PRAIRIE, Texas
(Reuters) - Betting on horses
was banned in Texas until as
recently as 1987. Times have
changed rapidly since.
Saturday, Lone Star Park race
track hosts the \\$14 million
Breeders Cup, global racing's
end-of-season extravaganza." - ], - [ - "MacCentral - At a special
music event featuring Bono and
The Edge from rock group U2
held on Tuesday, Apple took
the wraps off the iPod Photo,
a color iPod available in 40GB
or 60GB storage capacities.
The company also introduced
the iPod U2, a special edition
of Apple's 20GB player clad in
black, equipped with a red
Click Wheel and featuring
engraved U2 band member
signatures. The iPod Photo is
available immediately, and
Apple expects the iPod U2 to
ship in mid-November." - ], - [ - "Beijing: At least 170 miners
were trapped underground after
a gas explosion on Sunday
ignited a fire in a coalmine
in north-west China #39;s
Shaanxi province, reports
said." - ], - [ - "The steel tubing company
reports sharply higher
earnings, but the stock is
falling." - ], - [ - "It might be a stay of
execution for Coach P, or it
might just be a Christmas
miracle come early. SU #39;s
upset win over BC has given
hope to the Orange playing in
a post season Bowl game." - ], - [ - "PHIL Neville insists
Manchester United don #39;t
fear anyone in the Champions
League last 16 and declared:
quot;Bring on the Italians." - ], - [ - "Playboy Enterprises, the adult
entertainment company, has
announced plans to open a
private members club in
Shanghai even though the
company #39;s flagship men
#39;s magazine is still banned
in China." - ], - [ - "Reuters - Oracle Corp is
likely to win clearance\\from
the European Commission for
its hostile #36;7.7
billion\\takeover of rival
software firm PeopleSoft Inc.,
a source close\\to the
situation said on Friday." - ], - [ - "TORONTO (CP) - Earnings
warnings from Celestica and
Coca-Cola along with a
slowdown in US industrial
production sent stock markets
lower Wednesday." - ], - [ - "IBM (Quote, Chart) said it
would spend a quarter of a
billion dollars over the next
year and a half to grow its
RFID (define) business." - ], - [ - "Eastman Kodak Co., the world
#39;s largest maker of
photographic film, said
Wednesday it expects sales of
digital products and services
to grow at an annual rate of
36 percent between 2003 and
2007, above prior growth rate
estimates of 26 percent
between 2002" - ], - [ - "SAMARRA (Iraq): With renewe d
wave of skirmishes between the
Iraqi insurgents and the US-
led coalition marines, several
people including top police
officers were put to death on
Saturday." - ], - [ - "SPACE.com - NASA released one
of the best pictures ever made
of Saturn's moon Titan as the
Cassini spacecraft begins a
close-up inspection of the
satellite today. Cassini is
making the nearest flyby ever
of the smog-shrouded moon." - ], - [ - "AFP - The Iraqi government
plans to phase out slowly
subsidies on basic products,
such as oil and electricity,
which comprise 50 percent of
public spending, equal to 15
billion dollars, the planning
minister said." - ], - [ - "ANNAPOLIS ROYAL, NS - Nova
Scotia Power officials
continued to keep the sluice
gates open at one of the
utility #39;s hydroelectric
plants Wednesday in hopes a
wayward whale would leave the
area and head for the open
waters of the Bay of Fundy." - ], - [ - "TORONTO -- Toronto Raptors
point guard Alvin Williams
will miss the rest of the
season after undergoing
surgery on his right knee
Monday." - ], - [ - "The federal agency that
insures pension plans said
that its deficit, already at
the highest in its history,
had doubled in its last fiscal
year, to \\$23.3 billion." - ], - [ - "AFP - Like most US Latinos,
members of the extended
Rodriguez family say they will
cast their votes for Democrat
John Kerry in next month's
presidential polls." - ], - [ - "A Milan judge on Tuesday opens
hearings into whether to put
on trial 32 executives and
financial institutions over
the collapse of international
food group Parmalat in one of
Europe #39;s biggest fraud
cases." - ], - [ - "AP - Tennessee's two freshmen
quarterbacks have Volunteers
fans fantasizing about the
next four years. Brent
Schaeffer and Erik Ainge
surprised many with the nearly
seamless way they rotated
throughout a 42-17 victory
over UNLV on Sunday night." - ], - [ - "In fact, Larry Ellison
compares himself to the
warlord, according to
PeopleSoft's former CEO,
defending previous remarks he
made." - ], - [ - "FALLUJAH, Iraq -- Four Iraqi
fighters huddled in a trench,
firing rocket-propelled
grenades at Lieutenant Eric
Gregory's Bradley Fighting
Vehicle and the US tanks and
Humvees that were lumbering
through tight streets between
boxlike beige houses." - ], - [ - "MADRID, Aug 18 (Reuters) -
Portugal captain Luis Figo
said on Wednesday he was
taking an indefinite break
from international football,
but would not confirm whether
his decision was final." - ], - [ - "The Bank of England on
Thursday left its benchmark
interest rate unchanged, at
4.75 percent, as policy makers
assessed whether borrowing
costs, already the highest in
the Group of Seven, are
constraining consumer demand." - ], - [ - "AP - Several thousand
Christians who packed a
cathedral compound in the
Egyptian capital hurled stones
at riot police Wednesday to
protest a woman's alleged
forced conversion to Islam. At
least 30 people were injured." - ], - [ - "A group of Saudi religious
scholars have signed an open
letter urging Iraqis to
support jihad against US-led
forces. quot;Fighting the
occupiers is a duty for all
those who are able, quot; they
said in a statement posted on
the internet at the weekend." - ], - [ - "Fashion retailers Austin Reed
and Ted Baker have reported
contrasting fortunes on the
High Street. Austin Reed
reported interim losses of 2." - ], - [ - "AP - Shaun Rogers is in the
backfield as often as some
running backs. Whether teams
dare to block Detroit's star
defensive tackle with one
player or follow the trend of
double-teaming him, he often
rips through offensive lines
with a rare combination of
size, speed, strength and
nimble footwork." - ], - [ - " NEW YORK (Reuters) - A
federal judge on Friday
approved Citigroup Inc.'s
\\$2.6 billion settlement with
WorldCom Inc. investors who
lost billions when an
accounting scandal plunged
the telecommunications company
into bankruptcy protection." - ], - [ - "The Lions and Eagles entered
Sunday #39;s game at Ford
Field in the same place --
atop their respective
divisions -- and with
identical 2-0 records." - ], - [ - "An unspecified number of
cochlear implants to help
people with severe hearing
loss are being recalled
because they may malfunction
due to ear moisture, the US
Food and Drug Administration
announced." - ], - [ - "Profits triple at McDonald's
Japan after the fast-food
chain starts selling larger
burgers." - ], - [ - "After Marcos Moreno threw four
more interceptions in last
week's 14-13 overtime loss at
N.C. A T, Bison Coach Ray
Petty will start Antoine
Hartfield against Norfolk
State on Saturday." - ], - [ - "You can empty your pockets of
change, take off your belt and
shoes and stick your keys in
the little tray. But if you've
had radiation therapy
recently, you still might set
off Homeland Security alarms." - ], - [ - "Mountaineers retrieve three
bodies believed to have been
buried for 22 years on an
Indian glacier." - ], - [ - "SEOUL, Oct 19 Asia Pulse -
LG.Philips LCD Co.
(KSE:034220), the world #39;s
second-largest maker of liquid
crystal display (LCD), said
Tuesday it has developed the
world #39;s largest organic
light emitting diode" - ], - [ - "SOUTH WILLIAMSPORT, Pa. --
Looking ahead to the US
championship game almost cost
Conejo Valley in the
semifinals of the Little
League World Series." - ], - [ - "The Cubs didn #39;t need to
fly anywhere near Florida to
be in the eye of the storm.
For a team that is going on
100 years since last winning a
championship, the only thing
they never are at a loss for
is controversy." - ], - [ - "Security experts warn of
banner ads with a bad attitude
--and a link to malicious
code. Also: Phishers, be gone." - ], - [ - "KETTERING, Ohio Oct. 12, 2004
- Cincinnati Bengals defensive
end Justin Smith pleaded not
guilty to a driving under the
influence charge." - ], - [ - "com October 15, 2004, 5:11 AM
PT. Wood paneling and chrome
made your dad #39;s station
wagon look like a million
bucks, and they might also be
just the ticket for Microsoft
#39;s fledgling" - ], - [ - "President Thabo Mbeki met with
Ivory Coast Prime Minister
Seydou Diarra for three hours
yesterday as part of talks
aimed at bringing peace to the
conflict-wracked Ivory Coast." - ], - [ - "MINNEAPOLIS -- For much of the
2004 season, Twins pitcher
Johan Santana didn #39;t just
beat opposing hitters. Often,
he overwhelmed and owned them
in impressive fashion." - ], - [ - "Britain #39;s inflation rate
fell in August further below
its 2.0 percent government-set
upper limit target with
clothing and footwear prices
actually falling, official
data showed on Tuesday." - ], - [ - " KATHMANDU (Reuters) - Nepal's
Maoist rebels have
temporarily suspended a
crippling economic blockade of
the capital from Wednesday,
saying the move was in
response to popular appeals." - ], - [ - "Reuters - An Algerian
suspected of being a leader\\of
the Madrid train bombers has
been identified as one of
seven\\people who blew
themselves up in April to
avoid arrest, Spain's\\Interior
Ministry said on Friday." - ], - [ - "KABUL: An Afghan man was found
guilty on Saturday of killing
four journalists in 2001,
including two from Reuters,
and sentenced to death." - ], - [ - "Yasser Arafat, the leader for
decades of a fight for
Palestinian independence from
Israel, has died at a military
hospital in Paris, according
to news reports." - ], - [ - " LONDON (Reuters) - European
shares shrugged off a spike in
the euro to a fresh all-time
high Wednesday, with telecoms
again leading the way higher
after interim profits at
Britain's mm02 beat
expectations." - ], - [ - "WASHINGTON - Weighed down by
high energy prices, the US
economy grew slower than the
government estimated in the
April-June quarter, as higher
oil prices limited consumer
spending and contributed to a
record trade deficit." - ], - [ - "CHICAGO United Airlines says
it will need even more labor
cuts than anticipated to get
out of bankruptcy. United told
a bankruptcy court judge in
Chicago today that it intends
to start talks with unions
next month on a new round of
cost savings." - ], - [ - " JABALYA, Gaza Strip (Reuters)
- Israel pulled most of its
forces out of the northern
Gaza Strip Saturday after a
four-day incursion it said
was staged to halt Palestinian
rocket attacks on southern
Israeli towns." - ], - [ - "Computer Associates
International yesterday
reported a 6 increase in
revenue during its second
fiscal quarter, but posted a
\\$94 million loss after paying
to settle government
investigations into the
company, it said yesterday." - ], - [ - "THE Turkish embassy in Baghdad
was investigating a television
report that two Turkish
hostages had been killed in
Iraq, but no confirmation was
available so far, a senior
Turkish diplomat said today." - ], - [ - "Reuters - Thousands of
supporters of
Ukraine's\\opposition leader,
Viktor Yushchenko, celebrated
on the streets\\in the early
hours on Monday after an exit
poll showed him\\winner of a
bitterly fought presidential
election." - ], - [ - "LONDON : The United States
faced rare criticism over
human rights from close ally
Britain, with an official
British government report
taking Washington to task over
concerns about Iraq and the
Guantanamo Bay jail." - ], - [ - "The University of California,
Berkeley, has signed an
agreement with the Samoan
government to isolate, from a
tree, the gene for a promising
anti- Aids drug and to share
any royalties from the sale of
a gene-derived drug with the
people of Samoa." - ], - [ - "PC World - Send your video
throughout your house--
wirelessly--with new gateways
and media adapters." - ], - [ - "At a charity auction in New
Jersey last weekend, baseball
memorabilia dealer Warren
Heller was approached by a man
with an unusual but topical
request." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei average jumped 2.5
percent by mid-afternoon on
Monday as semiconductor-
related stocks such as
Advantest Corp. mirrored a
rally by their U.S. peers
while banks and brokerages
extended last week's gains." - ], - [ - "INTER Milan coach Roberto
Mancini believes the club
#39;s lavish (northern) summer
signings will enable them to
mount a serious Serie A
challenge this season." - ], - [ - "LONDON - A bomb threat that
mentioned Iraq forced a New
York-bound Greek airliner to
make an emergency landing
Sunday at London's Stansted
Airport escorted by military
jets, authorities said. An
airport spokeswoman said an
Athens newspaper had received
a phone call saying there was
a bomb on board the Olympic
Airlines plane..." - ], - [ - "Links to this week's topics
from search engine forums
across the web: New MSN Search
Goes LIVE in Beta - Microsoft
To Launch New Search Engine -
Google Launches 'Google
Advertising Professionals' -
Organic vs Paid Traffic ROI? -
Making Money With AdWords? -
Link Building 101" - ], - [ - "AP - Brad Ott shot an 8-under
64 on Sunday to win the
Nationwide Tour's Price Cutter
Charity Championship for his
first Nationwide victory." - ], - [ - "AP - New York Jets running
back Curtis Martin passed Eric
Dickerson and Jerome Bettis on
the NFL career rushing list
Sunday against the St. Louis
Rams, moving to fourth all-
time." - ], - [ - "Eight conservation groups are
fighting the US government
over a plan to poison
thousands of prairie dogs in
the grasslands of South
Dakota, saying wildlife should
not take a backseat to
ranching interests." - ], - [ - "ATHENS, Greece - Sheryl
Swoopes made three big plays
at the end - two baskets and
another on defense - to help
the United States squeeze out
a 66-62 semifinal victory over
Russia on Friday. Now, only
one game stands between the
U.S..." - ], - [ - "Instead of standing for ante
meridian and post meridian,
though, fans will remember the
time periods of pre-Mia and
after-Mia. After playing for
18 years and shattering nearly
every record" - ], - [ - "General Motors (GM) plans to
announce a massive
restructuring Thursday that
will eliminate as many as
12,000 jobs in Europe in a
move to stem the five-year
flow of red ink from its auto
operations in the region." - ], - [ - "Scientists are developing a
device which could improve the
lives of kidney dialysis
patients." - ], - [ - "KABUL, Afghanistan The Afghan
government is blaming drug
smugglers for yesterday #39;s
attack on the leading vice
presidential candidate ." - ], - [ - "Stephon Marbury, concerned
about his lousy shooting in
Athens, used an off day to go
to the gym and work on his
shot. By finding his range, he
saved the United States #39;
hopes for a basketball gold
medal." - ], - [ - " LONDON (Reuters) - Oil prices
held firm on Wednesday as
Hurricane Ivan closed off
crude output and shut
refineries in the Gulf of
Mexico, while OPEC's Gulf
producers tried to reassure
traders by recommending an
output hike." - ], - [ - "State-owned, running a
monopoly on imports of jet
fuel to China #39;s fast-
growing aviation industry and
a prized member of Singapore
#39;s Stock Exchange." - ], - [ - "Google has won a trade mark
dispute, with a District Court
judge finding that the search
engines sale of sponsored
search terms Geico and Geico
Direct did not breach car
insurance firm GEICOs rights
in the trade marked terms." - ], - [ - "Wall Street bounded higher for
the second straight day
yesterday as investors reveled
in sharply falling oil prices
and the probusiness agenda of
the second Bush
administration. The Dow Jones
industrials gained more than
177 points for its best day of
2004, while the Standard amp;
Poor's 500 closed at its
highest level since early
2002." - ], - [ - "Key factors help determine if
outsourcing benefits or hurts
Americans." - ], - [ - "The US Trade Representative on
Monday rejected the European
Union #39;s assertion that its
ban on beef from hormone-
treated cattle is now
justified by science and that
US and Canadian retaliatory
sanctions should be lifted." - ], - [ - "One of the leading figures in
the Greek Orthodox Church, the
Patriarch of Alexandria Peter
VII, has been killed in a
helicopter crash in the Aegean
Sea." - ], - [ - "Siding with chip makers,
Microsoft said it won't charge
double for its per-processor
licenses when dual-core chips
come to market next year." - ], - [ - "NEW YORK -- Wall Street's
fourth-quarter rally gave
stock mutual funds a solid
performance for 2004, with
small-cap equity funds and
real estate funds scoring some
of the biggest returns. Large-
cap growth equities and
technology-focused funds had
the slimmest gains." - ], - [ - "CANBERRA, Australia -- The
sweat-stained felt hats worn
by Australian cowboys, as much
a part of the Outback as
kangaroos and sun-baked soil,
may be heading for the history
books. They fail modern
industrial safety standards." - ], - [ - "Big Food Group Plc, the UK
owner of the Iceland grocery
chain, said second-quarter
sales at stores open at least
a year dropped 3.3 percent,
the second consecutive
decline, after competitors cut
prices." - ], - [ - "A London-to-Washington flight
is diverted after a security
alert involving the singer
formerly known as Cat Stevens." - ], - [ - " WASHINGTON (Reuters) - The
first case of soybean rust has
been found on the mainland
United States and could affect
U.S. crops for the near
future, costing farmers
millions of dollars, the
Agriculture Department said on
Wednesday." - ], - [ - "IBM and the Spanish government
have introduced a new
supercomputer they hope will
be the most powerful in
Europe, and one of the 10 most
powerful in the world." - ], - [ - "The Supreme Court today
overturned a five-figure
damage award to an Alexandria
man for a local auto dealer
#39;s alleged loan scam,
ruling that a Richmond-based
federal appeals court had
wrongly" - ], - [ - "TheSpaceShipOne team is handed
the \\$10m cheque and trophy it
won for claiming the Ansari
X-Prize." - ], - [ - "Security officials have
identified six of the
militants who seized a school
in southern Russia as being
from Chechnya, drawing a
strong connection to the
Chechen insurgents who have
been fighting Russian forces
for years." - ], - [ - "AP - Randy Moss is expected to
play a meaningful role for the
Minnesota Vikings this weekend
against the Giants, even
without a fully healed right
hamstring." - ], - [ - "Pros: Fits the recent profile
(44, past PGA champion, fiery
Ryder Cup player); the job is
his if he wants it. Cons:
Might be too young to be
willing to burn two years of
play on tour." - ], - [ - "SEOUL -- North Korea set three
conditions yesterday to be met
before it would consider
returning to six-party talks
on its nuclear programs." - ], - [ - "Official figures show the
12-nation eurozone economy
continues to grow, but there
are warnings it may slow down
later in the year." - ], - [ - "Elmer Santos scored in the
second half, lifting East
Boston to a 1-0 win over
Brighton yesterday afternoon
and giving the Jets an early
leg up in what is shaping up
to be a tight Boston City
League race." - ], - [ - "In upholding a lower court
#39;s ruling, the Supreme
Court rejected arguments that
the Do Not Call list violates
telemarketers #39; First
Amendment rights." - ], - [ - "US-backed Iraqi commandos were
poised Friday to storm rebel
strongholds in the northern
city of Mosul, as US military
commanders said they had
quot;broken the back quot; of
the insurgency with their
assault on the former rebel
bastion of Fallujah." - ], - [ - "Infineon Technologies, the
second-largest chip maker in
Europe, said Wednesday that it
planned to invest about \\$1
billion in a new factory in
Malaysia to expand its
automotive chip business and
be closer to customers in the
region." - ], - [ - "Mozilla's new web browser is
smart, fast and user-friendly
while offering a slew of
advanced, customizable
functions. By Michelle Delio." - ], - [ - "Saints special teams captain
Steve Gleason expects to be
fined by the league after
being ejected from Sunday's
game against the Carolina
Panthers for throwing a punch." - ], - [ - "JERUSALEM (Reuters) - Prime
Minister Ariel Sharon, facing
a party mutiny over his plan
to quit the Gaza Strip, has
approved 1,000 more Israeli
settler homes in the West Bank
in a move that drew a cautious
response on Tuesday from ..." - ], - [ - "Play has begun in the
Australian Masters at
Huntingdale in Melbourne with
around half the field of 120
players completing their first
rounds." - ], - [ - " NEW YORK (Reuters) -
Washington Post Co. <A HREF
=\"http://www.investor.reuters.
com/FullQuote.aspx?ticker=WPO.
N target=/stocks/quickinfo/ful
lquote\">WPO.N</A>
said on Friday that quarterly
profit jumped, beating
analysts' forecasts, boosted
by results at its Kaplan
education unit and television
broadcasting operations." - ], - [ - "GHAZNI, Afghanistan, 6 October
2004 - Wartime security was
rolled out for Afghanistans
interim President Hamid Karzai
as he addressed his first
election campaign rally
outside the capital yesterday
amid spiraling violence." - ], - [ - "LOUISVILLE, Ky. - Louisville
men #39;s basketball head
coach Rick Pitino and senior
forward Ellis Myles met with
members of the media on Friday
to preview the Cardinals #39;
home game against rival
Kentucky on Satursday." - ], - [ - "AP - Sounds like David
Letterman is as big a \"Pops\"
fan as most everyone else." - ], - [ - "originally offered on notebook
PCs -- to its Opteron 32- and
64-bit x86 processors for
server applications. The
technology will help servers
to run" - ], - [ - "New orders for US-made durable
goods increased 0.2pc in
September, held back by a big
drop in orders for
transportation goods, the US
Commerce Department said
today." - ], - [ - "Siblings are the first ever to
be convicted for sending
boatloads of junk e-mail
pushing bogus products. Also:
Microsoft takes MSN music
download on a Euro trip....
Nokia begins legal battle
against European
counterparts.... and more." - ], - [ - "I always get a kick out of the
annual list published by
Forbes singling out the
richest people in the country.
It #39;s almost as amusing as
those on the list bickering
over their placement." - ], - [ - "MacCentral - After Apple
unveiled the iMac G5 in Paris
this week, Vice President of
Hardware Product Marketing
Greg Joswiak gave Macworld
editors a guided tour of the
desktop's new design. Among
the topics of conversation:
the iMac's cooling system, why
pre-installed Bluetooth
functionality and FireWire 800
were left out, and how this
new model fits in with Apple's
objectives." - ], - [ - "Williams-Sonoma Inc., operator
of home furnishing chains
including Pottery Barn, said
third-quarter earnings rose 19
percent, boosted by store
openings and catalog sales." - ], - [ - "We #39;ve known about
quot;strained silicon quot;
for a while--but now there
#39;s a better way to do it.
Straining silicon improves
chip performance." - ], - [ - "This week, Sir Richard Branson
announced his new company,
Virgin Galactic, has the
rights to the first commercial
flights into space." - ], - [ - "71-inch HDTV comes with a home
stereo system and components
painted in 24-karat gold." - ], - [ - "Arsenal was held to a 1-1 tie
by struggling West Bromwich
Albion on Saturday, failing to
pick up a Premier League
victory when Rob Earnshaw
scored with 11 minutes left." - ], - [ - "TOKYO - Mitsubishi Heavy
Industries said today it #39;s
in talks to buy a plot of land
in central Japan #39;s Nagoya
city from Mitsubishi Motors
for building aircraft parts." - ], - [ - "China has confirmed that it
found a deadly strain of bird
flu in pigs as early as two
years ago. China #39;s
Agriculture Ministry said two
cases had been discovered, but
it did not say exactly where
the samples had been taken." - ], - [ - "Baseball #39;s executive vice
president Sandy Alderson
insisted last month that the
Cubs, disciplined for an
assortment of run-ins with
umpires, would not be targeted
the rest of the season by
umpires who might hold a
grudge." - ], - [ - "As Superman and Batman would
no doubt reflect during their
cigarette breaks, the really
draining thing about being a
hero was that you have to keep
riding to the rescue." - ], - [ - "MacCentral - RealNetworks Inc.
said on Tuesday that it has
sold more than a million songs
at its online music store
since slashing prices last
week as part of a limited-time
sale aimed at growing the user
base of its new digital media
software." - ], - [ - "With the presidential election
less than six weeks away,
activists and security experts
are ratcheting up concern over
the use of touch-screen
machines to cast votes.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-washingtonpost.com&l
t;/B></FONT>" - ], - [ - "NEW YORK, September 14 (New
Ratings) - Yahoo! Inc
(YHOO.NAS) has agreed to
acquire Musicmatch Inc, a
privately held digital music
software company, for about
\\$160 million in cash." - ], - [ - "Japan #39;s Sumitomo Mitsui
Financial Group Inc. said
Tuesday it proposed to UFJ
Holdings Inc. that the two
banks merge on an equal basis
in its latest attempt to woo
UFJ away from a rival suitor." - ], - [ - "Oil futures prices were little
changed Thursday as traders
anxiously watched for
indications that the supply or
demand picture would change in
some way to add pressure to
the market or take some away." - ], - [ - "Gov. Rod Blagojevich plans to
propose a ban Thursday on the
sale of violent and sexually
explicit video games to
minors, something other states
have tried with little
success." - ], - [ - " CHICAGO (Reuters) - Delta Air
Lines Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=DAL.N target=
/stocks/quickinfo/fullquote\"&g
t;DAL.N</A> said on
Tuesday it will cut wages by
10 percent and its chief
executive will go unpaid for
the rest of the year, but it
still warned of bankruptcy
within weeks unless more cuts
are made." - ], - [ - "AP - Ten years after the Irish
Republican Army's momentous
cease-fire, negotiations
resumed Wednesday in hope of
reviving a Catholic-Protestant
administration, an elusive
goal of Northern Ireland's
hard-fought peace process." - ], - [ - "A cable channel plans to
resurrect each of the 1,230
regular-season games listed on
the league's defunct 2004-2005
schedule by setting them in
motion on a video game
console." - ], - [ - " SANTO DOMINGO, Dominican
Republic, Sept. 18 -- Tropical
Storm Jeanne headed for the
Bahamas on Saturday after an
assault on the Dominican
Republic that killed 10
people, destroyed hundreds of
houses and forced thousands
from their homes." - ], - [ - "An explosion tore apart a car
in Gaza City Monday, killing
at least one person,
Palestinian witnesses said.
They said Israeli warplanes
were circling overhead at the
time of the blast, indicating
a possible missile strike." - ], - [ - " WASHINGTON (Reuters) - A
former Fannie Mae <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=FNM.N
target=/stocks/quickinfo/fullq
uote\">FNM.N</A>
employee who gave U.S.
officials information about
what he saw as accounting
irregularities will not
testify as planned before a
congressional hearing next
week, a House committee said
on Friday." - ], - [ - "Beijing, Oct. 25 (PTI): China
and the US today agreed to
work jointly to re-energise
the six-party talks mechanism
aimed at dismantling North
Korea #39;s nuclear programmes
while Washington urged Beijing
to resume" - ], - [ - "AFP - Sporadic gunfire and
shelling took place overnight
in the disputed Georgian
region of South Ossetia in
violation of a fragile
ceasefire, wounding seven
Georgian servicemen." - ], - [ - "PARIS, Nov 4 (AFP) - The
European Aeronautic Defence
and Space Company reported
Thursday that its nine-month
net profit more than doubled,
thanks largely to sales of
Airbus aircraft, and raised
its full-year forecast." - ], - [ - "AP - Eric Hinske and Vernon
Wells homered, and the Toronto
Blue Jays completed a three-
game sweep of the Baltimore
Orioles with an 8-5 victory
Sunday." - ], - [ - "SiliconValley.com - When
\"Halo\" became a smash video
game hit following Microsoft's
launch of the Xbox console in
2001, it was a no-brainer that
there would be a sequel to the
science fiction shoot-em-up." - ], - [ - "The number of people claiming
unemployment benefit last
month fell by 6,100 to
830,200, according to the
Office for National
Statistics." - ], - [ - " NEW YORK (Reuters) - Todd
Walker homered, had three hits
and drove in four runs to lead
the Chicago Cubs to a 12-5 win
over the Cincinnati Reds in
National League play at
Wrigley Field on Monday." - ], - [ - "PARIS -- The city of Paris
intends to reduce its
dependence on software
suppliers with \"de facto
monopolies,\" but considers an
immediate switch of its 17,000
desktops to open source
software too costly, it said
Wednesday." - ], - [ - " FALLUJA, Iraq (Reuters) -
U.S. forces hit Iraq's rebel
stronghold of Falluja with the
fiercest air and ground
bombardment in months, as
insurgents struck back on
Saturday with attacks that
killed up to 37 people in
Samarra." - ], - [ - "MIAMI (Sports Network) -
Shaquille O #39;Neal made his
home debut, but once again it
was Dwyane Wade stealing the
show with 28 points as the
Miami Heat downed the
Cleveland Cavaliers, 92-86, in
front of a record crowd at
AmericanAirlines Arena." - ], - [ - "AP - The San Diego Chargers
looked sharp #151; and played
the same way. Wearing their
powder-blue throwback jerseys
and white helmets from the
1960s, the Chargers did almost
everything right in beating
the Jacksonville Jaguars 34-21
on Sunday." - ], - [ - "The vast majority of consumers
are unaware that an Apple iPod
digital music player only
plays proprietary iTunes
files, while a smaller
majority agree that it is
within RealNetworks #39;
rights to develop a program
that will make its music files
compatible" - ], - [ - "Tyler airlines are gearing up
for the beginning of holiday
travel, as officials offer
tips to help travelers secure
tickets and pass through
checkpoints with ease." - ], - [ - " NAJAF, Iraq (Reuters) - The
fate of a radical Shi'ite
rebellion in the holy city of
Najaf was uncertain Friday
amid disputed reports that
Iraqi police had gained
control of the Imam Ali
Mosque." - ], - [ - " PROVIDENCE, R.I. (Reuters) -
You change the oil in your car
every 5,000 miles or so. You
clean your house every week or
two. Your PC needs regular
maintenance as well --
especially if you're using
Windows and you spend a lot of
time on the Internet." - ], - [ - "NERVES - no problem. That
#39;s the verdict of Jose
Mourinho today after his
Chelsea side gave a resolute
display of character at
Highbury." - ], - [ - "AP - The latest low point in
Ron Zook's tenure at Florida
even has the coach wondering
what went wrong. Meanwhile,
Sylvester Croom's first big
win at Mississippi State has
given the Bulldogs and their
fans a reason to believe in
their first-year leader.
Jerious Norwood's 37-yard
touchdown run with 32 seconds
remaining lifted the Bulldogs
to a 38-31 upset of the 20th-
ranked Gators on Saturday." - ], - [ - "A criminal trial scheduled to
start Monday involving former
Enron Corp. executives may
shine a rare and potentially
harsh spotlight on the inner
workings" - ], - [ - "The Motley Fool - Here's
something you don't see every
day -- the continuing brouhaha
between Oracle (Nasdaq: ORCL -
News) and PeopleSoft (Nasdaq:
PSFT - News) being a notable
exception. South Africa's
Harmony Gold Mining Company
(NYSE: HMY - News) has
announced a hostile takeover
bid to acquire fellow South
African miner Gold Fields
Limited (NYSE: GFI - News).
The transaction, if it takes
place, would be an all-stock
acquisition, with Harmony
issuing 1.275 new shares in
payment for each share of Gold
Fields. The deal would value
Gold Fields at more than
#36;8 billion. ..." - ], - [ - "Someone forgot to inform the
US Olympic basketball team
that it was sent to Athens to
try to win a gold medal, not
to embarrass its country." - ], - [ - "SPACE.com - NASA's Mars \\rover
Opportunity nbsp;will back its
\\way out of a nbsp;crater it
has spent four months
exploring after reaching
terrain nbsp;that appears \\too
treacherous to tread. nbsp;" - ], - [ - "Sony Corp. announced Tuesday a
new 20 gigabyte digital music
player with MP3 support that
will be available in Great
Britain and Japan before
Christmas and elsewhere in
Europe in early 2005." - ], - [ - "Wal-Mart Stores Inc. #39;s
Asda, the UK #39;s second
biggest supermarket chain,
surpassed Marks amp; Spencer
Group Plc as Britain #39;s
largest clothing retailer in
the last three months,
according to the Sunday
Telegraph." - ], - [ - "Ten-man Paris St Germain
clinched their seventh
consecutive victory over arch-
rivals Olympique Marseille
with a 2-1 triumph in Ligue 1
on Sunday thanks to a second-
half winner by substitute
Edouard Cisse." - ], - [ - "Until this week, only a few
things about the strange,
long-ago disappearance of
Charles Robert Jenkins were
known beyond a doubt. In the
bitter cold of Jan. 5, 1965,
the 24-year-old US Army
sergeant was leading" - ], - [ - "Roy Oswalt wasn #39;t
surprised to hear the Astros
were flying Sunday night
through the remnants of a
tropical depression that
dumped several inches of rain
in Louisiana and could bring
showers today in Atlanta." - ], - [ - "AP - This hardly seemed
possible when Pitt needed
frantic rallies to overcome
Division I-AA Furman or Big
East cellar dweller Temple. Or
when the Panthers could barely
move the ball against Ohio
#151; not Ohio State, but Ohio
U." - ], - [ - "Everyone is moaning about the
fallout from last weekend but
they keep on reporting the
aftermath. #39;The fall-out
from the so-called
quot;Battle of Old Trafford
quot; continues to settle over
the nation and the debate" - ], - [ - "Oil supply concerns and broker
downgrades of blue-chip
companies left stocks mixed
yesterday, raising doubts that
Wall Street #39;s year-end
rally would continue." - ], - [ - "Genentech Inc. said the
marketing of Rituxan, a cancer
drug that is the company #39;s
best-selling product, is the
subject of a US criminal
investigation." - ], - [ - "American Lindsay Davenport
regained the No. 1 ranking in
the world for the first time
since early 2002 by defeating
Dinara Safina of Russia 6-4,
6-2 in the second round of the
Kremlin Cup on Thursday." - ], - [ - " The world's No. 2 soft drink
company said on Thursday
quarterly profit rose due to
tax benefits." - ], - [ - "TOKYO (AP) -- The electronics
and entertainment giant Sony
Corp. (SNE) is talking with
Wal-Mart Stores Inc..." - ], - [ - "After an unprecedented span of
just five days, SpaceShipOne
is ready for a return trip to
space on Monday, its final
flight to clinch a \\$10
million prize." - ], - [ - "The United States on Tuesday
modified slightly a threat of
sanctions on Sudan #39;s oil
industry in a revised text of
its UN resolution on
atrocities in the country
#39;s Darfur region." - ], - [ - "Freshman Jeremy Ito kicked
four field goals and Ryan
Neill scored on a 31-yard
interception return to lead
improving Rutgers to a 19-14
victory on Saturday over
visiting Michigan State." - ], - [ - "Hi-tech monitoring of
livestock at pig farms could
help improve the animal growth
process and reduce costs." - ], - [ - "Third-seeded Guillermo Canas
defeated Guillermo Garcia-
Lopez of Spain 7-6 (1), 6-3
Monday on the first day of the
Shanghai Open on Monday." - ], - [ - "AP - France intensified
efforts Tuesday to save the
lives of two journalists held
hostage in Iraq, and the Arab
League said the militants'
deadline for France to revoke
a ban on Islamic headscarves
in schools had been extended." - ], - [ - "Cable amp; Wireless plc
(NYSE: CWP - message board) is
significantly ramping up its
investment in local loop
unbundling (LLU) in the UK,
and it plans to spend up to 85
million (\\$152." - ], - [ - "USATODAY.com - Personal
finance software programs are
the computer industry's
version of veggies: Everyone
knows they're good for you,
but it's just hard to get
anyone excited about them." - ], - [ - " NEW YORK (Reuters) - The
dollar rebounded on Monday
after last week's heavy
selloff, but analysts were
uncertain if the rally would
hold after fresh economic data
suggested the December U.S.
jobs report due Friday might
not live up to expectations." - ], - [ - "AFP - Microsoft said that it
had launched a new desktop
search tool that allows
personal computer users to
find documents or messages on
their PCs." - ], - [ - "At least 12 people die in an
explosion at a fuel pipeline
on the outskirts of Nigeria's
biggest city, Lagos." - ], - [ - "The three largest computer
makers spearheaded a program
today designed to standardize
working conditions for their
non-US workers." - ], - [ - "Description: Illinois Gov. Rod
Blagojevich is backing state
legislation that would ban
sales or rentals of video
games with graphic sexual or
violent content to children
under 18." - ], - [ - "Volkswagen demanded a two-year
wage freeze for the
170,000-strong workforce at
Europe #39;s biggest car maker
yesterday, provoking union
warnings of imminent conflict
at key pay and conditions
negotiations." - ], - [ - " NEW YORK (Reuters) - U.S.
stock futures pointed to a
lower open on Wall Street on
Thursday, extending the
previous session's sharp
fall, with rising energy
prices feeding investor
concerns about corporate
profits and slower growth." - ], - [ - "But to play as feebly as it
did for about 35 minutes last
night in Game 1 of the WNBA
Finals and lose by only four
points -- on the road, no less
-- has to be the best
confidence builder since Cindy
St." - ], - [ - "MILAN General Motors and Fiat
on Wednesday edged closer to
initiating a legal battle that
could pit the two carmakers
against each other in a New
York City court room as early
as next month." - ], - [ - "Are you bidding on keywords
through Overture's Precision
Match, Google's AdWords or
another pay-for-placement
service? If so, you're
eligible to participate in
their contextual advertising
programs." - ], - [ - "Two of the Ford Motor Company
#39;s most senior executives
retired on Thursday in a sign
that the company #39;s deep
financial crisis has abated,
though serious challenges
remain." - ], - [ - "Citing security concerns, the
U.S. Embassy on Thursday
banned its employees from
using the highway linking the
embassy area to the
international airport, a
10-mile stretch of road
plagued by frequent suicide
car-bomb attacks." - ], - [ - "Nobel Laureate Wilkins, 87,
played an important role in
the discovery of the double
helix structure of DNA, the
molecule that carries our
quot;life code quot;,
Kazinform refers to BBC News." - ], - [ - "With yesterday #39;s report on
its athletic department
violations completed, the
University of Washington says
it is pleased to be able to
move forward." - ], - [ - " LONDON (Reuters) - Wall
Street was expected to start
little changed on Friday as
investors continue to fret
over the impact of high oil
prices on earnings, while
Boeing <A HREF=\"http://www.
investor.reuters.com/FullQuote
.aspx?ticker=BA.N target=/stoc
ks/quickinfo/fullquote\">BA.
N</A> will be eyed
after it reiterated its
earnings forecast." - ], - [ - "AP - Tom Daschle bade his
fellow Senate Democrats
farewell Tuesday with a plea
that they seek common ground
with Republicans yet continue
to fight for the less
fortunate." - ], - [ - "Sammy Sosa was fined \\$87,400
-- one day's salary -- for
arriving late to the Cubs'
regular-season finale at
Wrigley Field and leaving the
game early. The slugger's
agent, Adam Katz , said
yesterday Sosa most likely
will file a grievance. Sosa
arrived 70 minutes before
Sunday's first pitch, and he
apparently left 15 minutes
after the game started without
..." - ], - [ - "Having an always-on, fast net
connection is changing the way
Britons use the internet,
research suggests." - ], - [ - "AP - Police defused a bomb in
a town near Prime Minister
Silvio Berlusconi's villa on
the island of Sardinia on
Wednesday shortly after
British Prime Minister Tony
Blair finished a visit there
with the Italian leader." - ], - [ - "Is the Oklahoma defense a
notch below its predecessors?
Is Texas #39; offense a step-
ahead? Why is Texas Tech
feeling good about itself
despite its recent loss?" - ], - [ - "The coffin of Yasser Arafat,
draped with the Palestinian
flag, was bound for Ramallah
in the West Bank Friday,
following a formal funeral on
a military compound near
Cairo." - ], - [ - "US Ambassador to the United
Nations John Danforth resigned
on Thursday after serving in
the post for less than six
months. Danforth, 68, said in
a letter released Thursday" - ], - [ - "Crude oil futures prices
dropped below \\$51 a barrel
yesterday as supply concerns
ahead of the Northern
Hemisphere winter eased after
an unexpectedly high rise in
US inventories." - ], - [ - "New York gets 57 combined
points from its starting
backcourt of Jamal Crawford
and Stephon Marbury and tops
Denver, 107-96." - ], - [ - "ISLAMABAD, Pakistan -- Photos
were published yesterday in
newspapers across Pakistan of
six terror suspects, including
a senior Al Qaeda operative,
the government says were
behind attempts to assassinate
the nation's president." - ], - [ - "AP - Shawn Marion had a
season-high 33 points and 15
rebounds, leading the Phoenix
Suns on a fourth-quarter
comeback despite the absence
of Steve Nash in a 95-86 win
over the New Orleans Hornets
on Friday night." - ], - [ - "By Lilly Vitorovich Of DOW
JONES NEWSWIRES SYDNEY (Dow
Jones)--Rupert Murdoch has
seven weeks to convince News
Corp. (NWS) shareholders a
move to the US will make the
media conglomerate more
attractive to" - ], - [ - "A number of signs point to
increasing demand for tech
workers, but not all the
clouds have been driven away." - ], - [ - "Messina upset defending
champion AC Milan 2-1
Wednesday, while Juventus won
its third straight game to
stay alone atop the Italian
league standings." - ], - [ - "Microsoft Corp. (MSFT.O:
Quote, Profile, Research)
filed nine new lawsuits
against spammers who send
unsolicited e-mail, including
an e-mail marketing Web
hosting company, the world
#39;s largest software maker
said on Thursday." - ], - [ - "AP - Padraig Harrington
rallied to a three-stroke
victory in the German Masters
on a windy Sunday, closing
with a 2-under-par 70 and
giving his game a big boost
before the Ryder Cup." - ], - [ - " ATHENS (Reuters) - The Athens
Paralympics canceled
celebrations at its closing
ceremony after seven
schoolchildren traveling to
watch the event died in a bus
crash on Monday." - ], - [ - "The rocket plane SpaceShipOne
is just one flight away from
claiming the Ansari X-Prize, a
\\$10m award designed to kick-
start private space travel." - ], - [ - "Reuters - Three American
scientists won the\\2004 Nobel
physics prize on Tuesday for
showing how tiny
quark\\particles interact,
helping to explain everything
from how a\\coin spins to how
the universe was built." - ], - [ - "Ironically it was the first
regular season game for the
Carolina Panthers that not
only began the history of the
franchise, but also saw the
beginning of a rivalry that
goes on to this day." - ], - [ - "Baltimore Ravens running back
Jamal Lewis did not appear at
his arraignment Friday, but
his lawyers entered a not
guilty plea on charges in an
expanded drug conspiracy
indictment." - ], - [ - "AP - Sharp Electronics Corp.
plans to stop selling its
Linux-based handheld computer
in the United States, another
sign of the slowing market for
personal digital assistants." - ], - [ - "After serving a five-game
suspension, Milton Bradley
worked out with the Dodgers as
they prepared for Tuesday's
opener against the St. Louis
Cardinals." - ], - [ - "AP - Prime Time won't be
playing in prime time this
time. Deion Sanders was on the
inactive list and missed a
chance to strut his stuff on
\"Monday Night Football.\"" - ], - [ - "Reuters - Glaciers once held
up by a floating\\ice shelf off
Antarctica are now sliding off
into the sea --\\and they are
going fast, scientists said on
Tuesday." - ], - [ - "DUBAI : An Islamist group has
threatened to kill two Italian
women held hostage in Iraq if
Rome does not withdraw its
troops from the war-torn
country within 24 hours,
according to an internet
statement." - ], - [ - "AP - Warning lights flashed
atop four police cars as the
caravan wound its way up the
driveway in a procession fit
for a presidential candidate.
At long last, Azy and Indah
had arrived. They even flew
through a hurricane to get
here." - ], - [ - "The man who delivered the
knockout punch was picked up
from the Seattle scrap heap
just after the All-Star Game.
Before that, John Olerud
certainly hadn't figured on
facing Pedro Martinez in
Yankee Stadium in October." - ], - [ - "\\Female undergraduates work
harder and are more open-
minded than males, leading to
better results, say
scientists." - ], - [ - "A heavy quake rocked Indonesia
#39;s Papua province killing
at least 11 people and
wounding 75. The quake
destroyed 150 buildings,
including churches, mosques
and schools." - ], - [ - "LONDON : A consortium,
including former world
champion Nigel Mansell, claims
it has agreed terms to ensure
Silverstone remains one of the
venues for the 2005 Formula
One world championship." - ], - [ - " BATON ROUGE, La. (Sports
Network) - LSU has named Les
Miles its new head football
coach, replacing Nick Saban." - ], - [ - "The United Nations annual
World Robotics Survey predicts
the use of robots around the
home will surge seven-fold by
2007. The boom is expected to
be seen in robots that can mow
lawns and vacuum floors, among
other chores." - ], - [ - "The long-term economic health
of the United States is
threatened by \\$53 trillion in
government debts and
liabilities that start to come
due in four years when baby
boomers begin to retire." - ], - [ - "Reuters - A small group of
suspected\\gunmen stormed
Uganda's Water Ministry
Wednesday and took
three\\people hostage to
protest against proposals to
allow President\\Yoweri
Museveni for a third
term.\\Police and soldiers with
assault rifles cordoned off
the\\three-story building, just
328 feet from Uganda's
parliament\\building in the
capital Kampala." - ], - [ - "The Moscow Arbitration Court
ruled on Monday that the YUKOS
oil company must pay RUR
39.113bn (about \\$1.34bn) as
part of its back tax claim for
2001." - ], - [ - "NOVEMBER 11, 2004 -- Bankrupt
US Airways this morning said
it had reached agreements with
lenders and lessors to
continue operating nearly all
of its mainline and US Airways
Express fleets." - ], - [ - "Venezuela suggested Friday
that exiles living in Florida
may have masterminded the
assassination of a prosecutor
investigating a short-lived
coup against leftist President
Hugo Chvez" - ], - [ - "Want to dive deep -- really
deep -- into the technical
literature about search
engines? Here's a road map to
some of the best web
information retrieval
resources available online." - ], - [ - "Reuters - Ancel Keys, a
pioneer in public health\\best
known for identifying the
connection between
a\\cholesterol-rich diet and
heart disease, has died." - ], - [ - "The US government asks the
World Trade Organisation to
step in to stop EU member
states from \"subsidising\"
planemaker Airbus." - ], - [ - "Reuters - T-Mobile USA, the
U.S. wireless unit\\of Deutsche
Telekom AG (DTEGn.DE), does
not expect to offer\\broadband
mobile data services for at
least the next two years,\\its
chief executive said on
Thursday." - ], - [ - "Verizon Communications is
stepping further into video as
a way to compete against cable
companies." - ], - [ - "Facing a popular outcry at
home and stern warnings from
Europe, the Turkish government
discreetly stepped back
Tuesday from a plan to
introduce a motion into a
crucial penal reform bill to
make adultery a crime
punishable by prison." - ], - [ - "Boston Scientific Corp.
(BSX.N: Quote, Profile,
Research) said on Wednesday it
received US regulatory
approval for a device to treat
complications that arise in
patients with end-stage kidney
disease who need dialysis." - ], - [ - "North-west Norfolk MP Henry
Bellingham has called for the
release of an old college
friend accused of plotting a
coup in Equatorial Guinea." - ], - [ - "With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." - ], - [ - "AP - The Chicago Blackhawks
re-signed goaltender Michael
Leighton to a one-year
contract Wednesday." - ], - [ - "Oracle Corp. plans to release
the latest version of its CRM
(customer relationship
management) applications
within the next two months, as
part of an ongoing update of
its E-Business Suite." - ], - [ - "Toyota Motor Corp. #39;s
shares fell for a second day,
after the world #39;s second-
biggest automaker had an
unexpected quarterly profit
drop." - ], - [ - "AFP - Want to buy a castle?
Head for the former East
Germany." - ], - [ - "Hosted CRM service provider
Salesforce.com took another
step forward last week in its
strategy to build an online
ecosystem of vendors that
offer software as a service." - ], - [ - "Britain-based HBOS says it
will file a complaint to the
European Commission against
Spanish bank Santander Central
Hispano (SCH) in connection
with SCH #39;s bid to acquire
British bank Abbey National" - ], - [ - "AFP - Steven Gerrard has moved
to allay Liverpool fans' fears
that he could be out until
Christmas after breaking a
metatarsal bone in his left
foot." - ], - [ - "Verizon Wireless on Thursday
announced an agreement to
acquire all the PCS spectrum
licenses of NextWave Telecom
Inc. in 23 markets for \\$3
billion." - ], - [ - "washingtonpost.com -
Technology giants IBM and
Hewlett-Packard are injecting
hundreds of millions of
dollars into radio-frequency
identification technology,
which aims to advance the
tracking of items from ho-hum
bar codes to smart tags packed
with data." - ], - [ - "ATHENS -- She won her first
Olympic gold medal in kayaking
when she was 18, the youngest
paddler to do so in Games
history. Yesterday, at 42,
Germany #39;s golden girl
Birgit Fischer won her eighth
Olympic gold in the four-woman
500-metre kayak race." - ], - [ - "England boss Sven Goran
Eriksson has defended
goalkeeper David James after
last night #39;s 2-2 draw in
Austria. James allowed Andreas
Ivanschitz #39;s shot to slip
through his fingers to
complete Austria comeback from
two goals down." - ], - [ - "MINSK - Legislative elections
in Belarus held at the same
time as a referendum on
whether President Alexander
Lukashenko should be allowed
to seek a third term fell
significantly short of
democratic standards, foreign
observers said here Monday." - ], - [ - "An Olympic sailor is charged
with the manslaughter of a
Briton who died after being
hit by a car in Athens." - ], - [ - "The Norfolk Broads are on
their way to getting a clean
bill of ecological health
after a century of stagnation." - ], - [ - "AP - Secretary of State Colin
Powell on Friday praised the
peace deal that ended fighting
in Iraq's holy city of Najaf
and said the presence of U.S.
forces in the area helped make
it possible." - ], - [ - "The quot;future quot; is
getting a chance to revive the
presently struggling New York
Giants. Two other teams also
decided it was time for a
change at quarterback, but the
Buffalo Bills are not one of
them." - ], - [ - "For the second time this year,
an alliance of major Internet
providers - including Atlanta-
based EarthLink -iled a
coordinated group of lawsuits
aimed at stemming the flood of
online junk mail." - ], - [ - " WASHINGTON (Reuters) - The
PIMCO mutual fund group has
agreed to pay \\$50 million to
settle fraud charges involving
improper rapid dealing in
mutual fund shares, the U.S.
Securities and Exchange
Commission said on Monday." - ], - [ - "Via Technologies has released
a version of the open-source
Xine media player that is
designed to take advantage of
hardware digital video
acceleration capabilities in
two of the company #39;s PC
chipsets, the CN400 and
CLE266." - ], - [ - "The Conference Board reported
Thursday that the Leading
Economic Index fell for a
third consecutive month in
August, suggesting slower
economic growth ahead amid
rising oil prices." - ], - [ - " SAN FRANCISCO (Reuters) -
Software maker Adobe Systems
Inc.<A HREF=\"http://www.inv
estor.reuters.com/FullQuote.as
px?ticker=ADBE.O target=/stock
s/quickinfo/fullquote\">ADBE
.O</A> on Thursday
posted a quarterly profit that
rose more than one-third from
a year ago, but shares fell 3
percent after the maker of
Photoshop and Acrobat software
did not raise forecasts for
fiscal 2005." - ], - [ - "William Morrison Supermarkets
has agreed to sell 114 small
Safeway stores and a
distribution centre for 260.2
million pounds. Morrison
bought these stores as part of
its 3 billion pound" - ], - [ - "SCO Group has a plan to keep
itself fit enough to continue
its legal battles against
Linux and to develop its Unix-
on-Intel operating systems." - ], - [ - "Flushing Meadows, NY (Sports
Network) - The men #39;s
semifinals at the 2004 US Open
will be staged on Saturday,
with three of the tournament
#39;s top-five seeds ready for
action at the USTA National
Tennis Center." - ], - [ - "Pepsi pushes a blue version of
Mountain Dew only at Taco
Bell. Is this a winning
strategy?" - ], - [ - "New software helps corporate
travel managers track down
business travelers who
overspend. But it also poses a
dilemma for honest travelers
who are only trying to save
money." - ], - [ - "NATO Secretary-General Jaap de
Hoop Scheffer has called a
meeting of NATO states and
Russia on Tuesday to discuss
the siege of a school by
Chechen separatists in which
more than 335 people died, a
NATO spokesman said." - ], - [ - "26 August 2004 -- Iraq #39;s
top Shi #39;ite cleric, Grand
Ayatollah Ali al-Sistani,
arrived in the city of Al-
Najaf today in a bid to end a
weeks-long conflict between US
forces and militiamen loyal to
Shi #39;ite cleric Muqtada al-
Sadr." - ], - [ - "AFP - Senior executives at
business software group
PeopleSoft unanimously
recommended that its
shareholders reject a 8.8
billion dollar takeover bid
from Oracle Corp, PeopleSoft
said in a statement Wednesday." - ], - [ - "Reuters - Neolithic people in
China may have\\been the first
in the world to make wine,
according to\\scientists who
have found the earliest
evidence of winemaking\\from
pottery shards dating from
7,000 BC in northern China." - ], - [ - "Given nearly a week to examine
the security issues raised by
the now-infamous brawl between
players and fans in Auburn
Hills, Mich., Nov. 19, the
Celtics returned to the
FleetCenter last night with
two losses and few concerns
about their on-court safety." - ], - [ - " TOKYO (Reuters) - Electronics
conglomerate Sony Corp.
unveiled eight new flat-screen
televisions on Thursday in a
product push it hopes will
help it secure a leading 35
percent of the domestic
market in the key month of
December." - ], - [ - "As the election approaches,
Congress abandons all pretense
of fiscal responsibility,
voting tax cuts that would
drive 10-year deficits past
\\$3 trillion." - ], - [ - "PARIS : French trade unions
called on workers at France
Telecom to stage a 24-hour
strike September 7 to protest
government plans to privatize
the public telecommunications
operator, union sources said." - ], - [ - "ServiceMaster profitably
bundles services and pays a
healthy 3.5 dividend." - ], - [ - "The Indonesian tourism
industry has so far not been
affected by last week #39;s
bombing outside the Australian
embassy in Jakarta and
officials said they do not
expect a significant drop in
visitor numbers as a result of
the attack." - ], - [ - "\\$222.5 million -- in an
ongoing securities class
action lawsuit against Enron
Corp. The settlement,
announced Friday and" - ], - [ - "Arsenals Thierry Henry today
missed out on the European
Footballer of the Year award
as Andriy Shevchenko took the
honour. AC Milan frontman
Shevchenko held off
competition from Barcelona
pair Deco and" - ], - [ - "Donald Halsted, one target of
a class-action suit alleging
financial improprieties at
bankrupt Polaroid, officially
becomes CFO." - ], - [ - "Neil Mellor #39;s sensational
late winner for Liverpool
against Arsenal on Sunday has
earned the back-up striker the
chance to salvage a career
that had appeared to be
drifting irrevocably towards
the lower divisions." - ], - [ - "ABOUT 70,000 people were
forced to evacuate Real Madrid
#39;s Santiago Bernabeu
stadium minutes before the end
of a Primera Liga match
yesterday after a bomb threat
in the name of ETA Basque
separatist guerillas." - ], - [ - "The team learned on Monday
that full-back Jon Ritchie
will miss the rest of the
season with a torn anterior
cruciate ligament in his left
knee." - ], - [ - " NEW YORK (Reuters) -
Lifestyle guru Martha Stewart
said on Wednesday she wants
to start serving her prison
sentence for lying about a
suspicious stock sale as soon
as possible, so she can put
her \"nightmare\" behind her." - ], - [ - "Apple Computer's iPod remains
the king of digital music
players, but robust pretenders
to the throne have begun to
emerge in the Windows
universe. One of them is the
Zen Touch, from Creative Labs." - ], - [ - "The 7710 model features a
touch screen, pen input, a
digital camera, an Internet
browser, a radio, video
playback and streaming and
recording capabilities, the
company said." - ], - [ - "SAN FRANCISCO (CBS.MW) --
Crude futures closed under
\\$46 a barrel Wednesday for
the first time since late
September and heating-oil and
unleaded gasoline prices
dropped more than 6 percent
following an across-the-board
climb in US petroleum
inventories." - ], - [ - "The University of Iowa #39;s
market for US presidential
futures, founded 16-years ago,
has been overtaken by a
Dublin-based exchange that is
now 25 times larger." - ], - [ - "Venus Williams barely kept
alive her hopes of qualifying
for next week #39;s WTA Tour
Championships. Williams,
seeded fifth, survived a
third-set tiebreaker to
outlast Yuilana Fedak of the
Ukraine, 6-4 2-6 7-6" - ], - [ - " SYDNEY (Reuters) - Arnold
Palmer has taken a swing at
America's top players,
criticizing their increasing
reluctance to travel abroad
to play in tournaments." - ], - [ - "MARK Thatcher will have to
wait until at least next April
to face trial on allegations
he helped bankroll a coup
attempt in oil-rich Equatorial
Guinea." - ], - [ - "A top Red Hat executive has
attacked the open-source
credentials of its sometime
business partner Sun
Microsystems. In a Web log
posting Thursday, Michael
Tiemann, Red Hat #39;s vice
president of open-source
affairs" - ], - [ - "President Bush #39;s drive to
deploy a multibillion-dollar
shield against ballistic
missiles was set back on
Wednesday by what critics
called a stunning failure of
its first full flight test in
two years." - ], - [ - "Although he was well-beaten by
Retief Goosen in Sunday #39;s
final round of The Tour
Championship in Atlanta, there
has been some compensation for
the former world number one,
Tiger Woods." - ], - [ - "WAYNE Rooney and Henrik
Larsson are among the players
nominated for FIFAs
prestigious World Player of
the Year award. Rooney is one
of four Manchester United
players on a list which is
heavily influenced by the
Premiership." - ], - [ - "It didn #39;t look good when
it happened on the field, and
it looked worse in the
clubhouse. Angels second
baseman Adam Kennedy left the
Angels #39; 5-2 win over the
Seattle Mariners" - ], - [ - "Air travelers moved one step
closer to being able to talk
on cell phones and surf the
Internet from laptops while in
flight, thanks to votes by the
Federal Communications
Commission yesterday." - ], - [ - "MySQL developers turn to an
unlikely source for database
tool: Microsoft. Also: SGI
visualizes Linux, and the
return of Java veteran Kim
Polese." - ], - [ - "DESPITE the budget deficit,
continued increases in oil and
consumer prices, the economy,
as measured by gross domestic
product, grew by 6.3 percent
in the third" - ], - [ - "NEW YORK - A drop in oil
prices and upbeat outlooks
from Wal-Mart and Lowe's
helped send stocks sharply
higher Monday on Wall Street,
with the swing exaggerated by
thin late summer trading. The
Dow Jones industrials surged
nearly 130 points..." - ], - [ - "Freshman Darius Walker ran for
115 yards and scored two
touchdowns, helping revive an
Irish offense that had managed
just one touchdown in the
season's first six quarters." - ], - [ - "Consumers who cut it close by
paying bills from their
checking accounts a couple of
days before depositing funds
will be out of luck under a
new law that takes effect Oct.
28." - ], - [ - "Dell Inc. said its profit
surged 25 percent in the third
quarter as the world's largest
personal computer maker posted
record sales due to rising
technology spending in the
corporate and government
sectors in the United States
and abroad." - ], - [ - "AP - NBC is adding a 5-second
delay to its NASCAR telecasts
after Dale Earnhardt Jr. used
a vulgarity during a postrace
TV interview last weekend." - ], - [ - " BOSTON (Sports Network) - The
New York Yankees will start
Orlando \"El Duque\" Hernandez
in Game 4 of the American
League Championship Series on
Saturday against the Boston
Red Sox." - ], - [ - "The future of Sven-Goran
Eriksson as England coach is
the subject of intense
discussion after the draw in
Austria. Has the Swede lost
the confidence of the nation
or does he remain the best man
for the job?" - ], - [ - "Component problems meant
Brillian's new big screens
missed the NFL's kickoff
party." - ], - [ - "Spain begin their third final
in five seasons at the Olympic
stadium hoping to secure their
second title since their first
in Barcelona against Australia
in 2000." - ], - [ - "Second-seeded David Nalbandian
of Argentina lost at the Japan
Open on Thursday, beaten by
Gilles Muller of Luxembourg
7-6 (4), 3-6, 6-4 in the third
round." - ], - [ - "Thursday #39;s unexpected
resignation of Memphis
Grizzlies coach Hubie Brown
left a lot of questions
unanswered. In his unique way
of putting things, the
71-year-old Brown seemed to
indicate he was burned out and
had some health concerns." - ], - [ - "RUDI Voller had quit as coach
of Roma after a 3-1 defeat
away to Bologna, the Serie A
club said today. Under the
former Germany coach, Roma had
taken just four league points
from a possible 12." - ], - [ - "A Russian court on Thursday
rejected an appeal by the
Yukos oil company seeking to
overturn a freeze on the
accounts of the struggling oil
giant #39;s core subsidiaries." - ], - [ - "ONE by one, the players #39;
faces had flashed up on the
giant Ibrox screens offering
season #39;s greetings to the
Rangers fans. But the main
presents were reserved for
Auxerre." - ], - [ - "Switzerland #39;s struggling
national airline reported a
second-quarter profit of 45
million Swiss francs (\\$35.6
million) Tuesday, although its
figures were boosted by a
legal settlement in France." - ], - [ - "ROSTOV-ON-DON, Russia --
Hundreds of protesters
ransacked and occupied the
regional administration
building in a southern Russian
province Tuesday, demanding
the resignation of the region
#39;s president, whose former
son-in-law has been linked to
a multiple" - ], - [ - "SIPTU has said it is strongly
opposed to any privatisation
of Aer Lingus as pressure
mounts on the Government to
make a decision on the future
funding of the airline." - ], - [ - "Reuters - SBC Communications
said on Monday it\\would offer
a television set-top box that
can handle music,\\photos and
Internet downloads, part of
SBC's efforts to expand\\into
home entertainment." - ], - [ - "Molson Inc. Chief Executive
Officer Daniel O #39;Neill
said he #39;ll provide
investors with a positive #39;
#39; response to their
concerns over the company
#39;s plan to let stock-
option holders vote on its
planned merger with Adolph
Coors Co." - ], - [ - "South Korea #39;s Grace Park
shot a seven-under-par 65 to
triumph at the CJ Nine Bridges
Classic on Sunday. Park #39;s
victory made up her final-
round collapse at the Samsung
World Championship two weeks
ago." - ], - [ - " WASHINGTON (Reuters) - Hopes
-- and worries -- that U.S.
regulators will soon end the
ban on using wireless phones
during U.S. commercial flights
are likely at least a year or
two early, government
officials and analysts say." - ], - [ - "AFP - Iraqi Foreign Minister
Hoshyar Zebari arrived
unexpectedly in the holy city
of Mecca Wednesday where he
met Crown Prince Abdullah bin
Abdul Aziz, the official SPA
news agency reported." - ], - [ - "Titans QB Steve McNair was
released from a Nashville
hospital after a two-night
stay for treatment of a
bruised sternum. McNair was
injured during the fourth
quarter of the Titans #39;
15-12 loss to Jacksonville on
Sunday." - ], - [ - "Keith Miller, Australia #39;s
most prolific all-rounder in
Test cricket, died today at a
nursing home, Cricket
Australia said. He was 84." - ], - [ - "Haitian police and UN troops
moved into a slum neighborhood
on Sunday and cleared street
barricades that paralyzed a
part of the capital." - ], - [ - "TORONTO Former Toronto pitcher
John Cerutti (seh-ROO
#39;-tee) was found dead in
his hotel room today,
according to the team. He was
44." - ], - [ - "withdrawal of troops and
settlers from occupied Gaza
next year. Militants seek to
claim any pullout as a
victory. quot;Islamic Jihad
will not be broken by this
martyrdom, quot; said Khaled
al-Batsh, a senior political
leader in Gaza." - ], - [ - " NEW YORK (Reuters) - The
world's largest gold producer,
Newmont Mining Corp. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=NEM
.N target=/stocks/quickinfo/fu
llquote\">NEM.N</A>,
on Wednesday said higher gold
prices drove up quarterly
profit by 12.5 percent, even
though it sold less of the
precious metal." - ], - [ - "The U.S. military has found
nearly 20 houses where
intelligence officers believe
hostages were tortured or
killed in this city, including
the house with the cage that
held a British contractor who
was beheaded last month." - ], - [ - "AFP - Opponents of the Lao
government may be plotting
bomb attacks in Vientiane and
other areas of Laos timed to
coincide with a summit of
Southeast Asian leaders the
country is hosting next month,
the United States said." - ], - [ - "After a year of pilots and
trials, Siebel Systems jumped
with both feet into the SMB
market Tuesday, announcing a
new approach to offer Siebel
Professional CRM applications
to SMBs (small and midsize
businesses) -- companies with
revenues up to about \\$500
million." - ], - [ - "AP - Russia agreed Thursday to
send warships to help NATO
naval patrols that monitor
suspicious vessels in the
Mediterranean, part of a push
for closer counterterrorism
cooperation between Moscow and
the western alliance." - ], - [ - "Intel won't release a 4-GHz
version of its flagship
Pentium 4 product, having
decided instead to realign its
engineers around the company's
new design priorities, an
Intel spokesman said today.
\\\\" - ], - [ - "AP - A Soyuz spacecraft
carrying two Russians and an
American rocketed closer
Friday to its docking with the
international space station,
where the current three-man
crew made final departure
preparations." - ], - [ - "Defense: Ken Lucas. His
biggest play was his first
one. The fourth-year
cornerback intercepted a Ken
Dorsey pass that kissed off
the hands of wide receiver
Rashaun Woods and returned it
25 yards to set up the
Seahawks #39; first score." - ], - [ - "Scientists have manipulated
carbon atoms to create a
material that could be used to
create light-based, versus
electronic, switches. The
material could lead to a
supercharged Internet based
entirely on light, scientists
say." - ], - [ - "A military plane crashed in
the mountains near Caracas,
killing all 16 persons on
board, including two high-
ranking military officers,
officials said." - ], - [ - "The powerful St. Louis trio of
Albert Pujols, Scott Rolen and
Jim Edmonds is 4 for 23 with
one RBI in the series and with
runners on base, they are 1
for 13." - ], - [ - "A voice recording said to be
that of suspected Al Qaeda
commander Abu Mussab al-
Zarqawi, claims Iraq #39;s
Prime Minister Iyad Allawi is
the militant network #39;s
number one target." - ], - [ - "BEIJING -- More than a year
after becoming China's
president, Hu Jintao was
handed the full reins of power
yesterday when his
predecessor, Jiang Zemin, gave
up the nation's most powerful
military post." - ], - [ - "LOS ANGELES (Reuters)
Qualcomm has dropped an \\$18
million claim for monetary
damages from rival Texas
Instruments for publicly
discussing terms of a
licensing pact, a TI
spokeswoman confirmed Tuesday." - ], - [ - "Hewlett-Packard is the latest
IT vendor to try blogging. But
analysts wonder if the weblog
trend is the 21st century
equivalent of CB radios, which
made a big splash in the 1970s
before fading." - ], - [ - " WASHINGTON (Reuters) - Fannie
Mae executives and their
regulator squared off on
Wednesday, with executives
denying any accounting
irregularity and the regulator
saying the housing finance
company's management may need
to go." - ], - [ - "The scientists behind Dolly
the sheep apply for a license
to clone human embryos. They
want to take stem cells from
the embryos to study Lou
Gehrig's disease." - ], - [ - "As the first criminal trial
stemming from the financial
deals at Enron opened in
Houston on Monday, it is
notable as much for who is not
among the six defendants as
who is - and for how little
money was involved compared
with how much in other Enron" - ], - [ - "LONDON (CBS.MW) -- British
bank Barclays on Thursday said
it is in talks to buy a
majority stake in South
African bank ABSA. Free!" - ], - [ - "The Jets signed 33-year-old
cornerback Terrell Buckley,
who was released by New
England on Sunday, after
putting nickel back Ray
Mickens on season-ending
injured reserve yesterday with
a torn ACL in his left knee." - ], - [ - "Some of the silly tunes
Japanese pay to download to
use as the ring tone for their
mobile phones sure have their
knockers, but it #39;s for
precisely that reason that a
well-known counselor is raking
it in at the moment, according
to Shukan Gendai (10/2)." - ], - [ - "WEST INDIES thrilling victory
yesterday in the International
Cricket Council Champions
Trophy meant the world to the
five million people of the
Caribbean." - ], - [ - "AP - Greenpeace activists
scaled the walls of Ford Motor
Co.'s Norwegian headquarters
Tuesday to protest plans to
destroy hundreds of non-
polluting electric cars." - ], - [ - "Investors sent stocks sharply
lower yesterday as oil prices
continued their climb higher
and new questions about the
safety of arthritis drugs
pressured pharmaceutical
stocks." - ], - [ - "Scotland manager Berti Vogts
insists he is expecting
nothing but victory against
Moldova on Wednesday. The game
certainly is a must-win affair
if the Scots are to have any
chance of qualifying for the
2006 World Cup finals." - ], - [ - "IBM announced yesterday that
it will invest US\\$250 million
(S\\$425 million) over the next
five years and employ 1,000
people in a new business unit
to support products and
services related to sensor
networks." - ], - [ - "AFP - The chances of Rupert
Murdoch's News Corp relocating
from Australia to the United
States have increased after
one of its biggest
institutional investors has
chosen to abstain from a vote
next week on the move." - ], - [ - "AFP - An Indian minister said
a school text-book used in the
violence-prone western state
of Gujarat portrayed Adolf
Hitler as a role model." - ], - [ - "Reuters - The head of UAL
Corp.'s United\\Airlines said
on Thursday the airline's
restructuring plan\\would lead
to a significant number of job
losses, but it was\\not clear
how many." - ], - [ - "DOVER, N.H. (AP) -- Democrat
John Kerry is seizing on the
Bush administration's failure
to secure hundreds of tons of
explosives now missing in
Iraq." - ], - [ - "AP - Microsoft Corp. goes into
round two Friday of its battle
to get the European Union's
sweeping antitrust ruling
lifted having told a judge
that it had been prepared
during settlement talks to
share more software code with
its rivals than the EU
ultimately demanded." - ], - [ - " INDIANAPOLIS (Reuters) -
Jenny Thompson will take the
spotlight from injured U.S.
team mate Michael Phelps at
the world short course
championships Saturday as she
brings down the curtain on a
spectacular swimming career." - ], - [ - "Martin Brodeur made 27 saves,
and Brad Richards, Kris Draper
and Joe Sakic scored goals to
help Canada beat Russia 3-1
last night in the World Cup of
Hockey, giving the Canadians a
3-0 record in round-robin
play." - ], - [ - "AP - Sears, Roebuck and Co.,
which has successfully sold
its tools and appliances on
the Web, is counting on having
the same magic with bedspreads
and sweaters, thanks in part
to expertise gained by its
purchase of Lands' End Inc." - ], - [ - "com September 14, 2004, 9:12
AM PT. With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." - ], - [ - " NEW YORK (Reuters) -
Children's Place Retail Stores
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=PLCE.O target=/sto
cks/quickinfo/fullquote\">PL
CE.O</A> said on
Wednesday it will buy 313
retail stores from Walt
Disney Co., and its stock rose
more than 14 percent in early
morning trade." - ], - [ - "ALBANY, N.Y. -- A California-
based company that brokers
life, accident, and disability
policies for leading US
companies pocketed millions of
dollars a year in hidden
payments from insurers and
from charges on clients'
unsuspecting workers, New York
Attorney General Eliot Spitzer
charged yesterday." - ], - [ - "NORTEL Networks plans to slash
its workforce by 3500, or ten
per cent, as it struggles to
recover from an accounting
scandal that toppled three top
executives and led to a
criminal investigation and
lawsuits." - ], - [ - "Ebay Inc. (EBAY.O: Quote,
Profile, Research) said on
Friday it would buy Rent.com,
an Internet housing rental
listing service, for \\$415
million in a deal that gives
it access to a new segment of
the online real estate market." - ], - [ - "Austin police are working with
overseas officials to bring
charges against an English man
for sexual assault of a child,
a second-degree felony." - ], - [ - "United Nations officials
report security breaches in
internally displaced people
and refugee camps in Sudan
#39;s embattled Darfur region
and neighboring Chad." - ], - [ - "Noranda Inc., Canada #39;s
biggest mining company, began
exclusive talks on a takeover
proposal from China Minmetals
Corp. that would lead to the
spinoff of Noranda #39;s
aluminum business to
shareholders." - ], - [ - "San Francisco
developer/publisher lands
coveted Paramount sci-fi
license, \\$6.5 million in
funding on same day. Although
it is less than two years old,
Perpetual Entertainment has
acquired one of the most
coveted sci-fi licenses on the
market." - ], - [ - "ST. LOUIS -- Mike Martz #39;s
week of anger was no empty
display. He saw the defending
NFC West champions slipping
and thought taking potshots at
his players might be his best
shot at turning things around." - ], - [ - "Google warned Thursday that
increased competition and the
maturing of the company would
result in an quot;inevitable
quot; slowing of its growth." - ], - [ - "By KELLY WIESE JEFFERSON
CITY, Mo. (AP) -- Missouri
will allow members of the
military stationed overseas to
return absentee ballots via
e-mail, raising concerns from
Internet security experts
about fraud and ballot
secrecy..." - ], - [ - "Avis Europe PLC has dumped a
new ERP system based on
software from PeopleSoft Inc.
before it was even rolled out,
citing substantial delays and
higher-than-expected costs." - ], - [ - " TOKYO (Reuters) - The Nikkei
average rose 0.55 percent by
midsession on Wednesday as
some techs including Advantest
Corp. gained ground after
Wall Street reacted positively
to results from Intel Corp.
released after the U.S. market
close." - ], - [ - "Yahoo #39;s (Quote, Chart)
public embrace of the RSS
content syndication format
took a major leap forward with
the release of a revamped My
Yahoo portal seeking to
introduce the technology to
mainstream consumers." - ], - [ - "KINGSTON, Jamaica - Hurricane
Ivan's deadly winds and
monstrous waves bore down on
Jamaica on Friday, threatening
a direct hit on its densely
populated capital after
ravaging Grenada and killing
at least 33 people. The
Jamaican government ordered
the evacuation of half a
million people from coastal
areas, where rains on Ivan's
outer edges were already
flooding roads..." - ], - [ - "North Korea has denounced as
quot;wicked terrorists quot;
the South Korean officials who
orchestrated last month #39;s
airlift to Seoul of 468 North
Korean defectors." - ], - [ - "The Black Watch regiment has
returned to its base in Basra
in southern Iraq after a
month-long mission standing in
for US troops in a more
violent part of the country,
the Ministry of Defence says." - ], - [ - "Romanian soccer star Adrian
Mutu as he arrives at the
British Football Association
in London, ahead of his
disciplinary hearing, Thursday
Nov. 4, 2004." - ], - [ - "Australia completed an
emphatic Test series sweep
over New Zealand with a
213-run win Tuesday, prompting
a caution from Black Caps
skipper Stephen Fleming for
other cricket captains around
the globe." - ], - [ - "AP - A senior Congolese
official said Tuesday his
nation had been invaded by
neighboring Rwanda, and U.N.
officials said they were
investigating claims of
Rwandan forces clashing with
militias in the east." - ], - [ - "Liverpool, England (Sports
Network) - Former English
international and Liverpool
great Emlyn Hughes passed away
Tuesday from a brain tumor." - ], - [ - " ATLANTA (Reuters) - Soft
drink giant Coca-Cola Co.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=KO.N target=/stocks/quic
kinfo/fullquote\">KO.N</A
>, stung by a prolonged
downturn in North America,
Germany and other major
markets, on Thursday lowered
its key long-term earnings
and sales targets." - ], - [ - "JACKSONVILLE, Fla. -- They
were singing in the Colts #39;
locker room today, singing
like a bunch of wounded
songbirds. Never mind that
Marcus Pollard, Dallas Clark
and Ben Hartsock won #39;t be
recording a remake of Kenny
Chesney #39;s song,
quot;Young, quot; any time
soon." - ], - [ - "TOKYO (AP) -- Japanese
electronics and entertainment
giant Sony Corp. (SNE) plans
to begin selling a camcorder
designed for consumers that
takes video at digital high-
definition quality and is
being priced at about
\\$3,600..." - ], - [ - "As the close-knit NASCAR
community mourns the loss of
team owner Rick Hendrick #39;s
son, brother, twin nieces and
six others in a plane crash
Sunday, perhaps no one outside
of the immediate family
grieves more deeply than
Darrell Waltrip." - ], - [ - "AP - Purdue quarterback Kyle
Orton has no trouble
remembering how he felt after
last year's game at Michigan." - ], - [ - "UNITED NATIONS - The United
Nations #39; nuclear agency
says it is concerned about the
disappearance of equipment and
materials from Iraq that could
be used to make nuclear
weapons." - ], - [ - " BRUSSELS (Reuters) - The EU's
historic deal with Turkey to
open entry talks with the vast
Muslim country was hailed by
supporters as a bridge builder
between Europe and the Islamic
world." - ], - [ - "Iraqi President Ghazi al-
Yawar, who was due in Paris on
Sunday to start a European
tour, has postponed his visit
to France due to the ongoing
hostage drama involving two
French journalists, Arab
diplomats said Friday." - ], - [ - " SAO PAULO, Brazil (Reuters) -
President Luiz Inacio Lula da
Silva's Workers' Party (PT)
won the mayoralty of six state
capitals in Sunday's municipal
vote but was forced into a
run-off to defend its hold on
the race's biggest prize, the
city of Sao Paulo." - ], - [ - "ATHENS, Greece - They are
America's newest golden girls
- powerful and just a shade
from perfection. The U.S..." - ], - [ - "AMMAN, Sept. 15. - The owner
of a Jordanian truck company
announced today that he had
ordered its Iraq operations
stopped in a bid to save the
life of a driver held hostage
by a militant group." - ], - [ - "AP - U.S. State Department
officials learned that seven
American children had been
abandoned at a Nigerian
orphanage but waited more than
a week to check on the youths,
who were suffering from
malnutrition, malaria and
typhoid, a newspaper reported
Saturday." - ], - [ - "\\Angry mobs in Ivory Coast's
main city, Abidjan, marched on
the airport, hours after it
came under French control." - ], - [ - "Several workers are believed
to have been killed and others
injured after a contruction
site collapsed at Dubai
airport. The workers were
trapped under rubble at the
site of a \\$4." - ], - [ - "Talks between Sudan #39;s
government and two rebel
groups to resolve the nearly
two-year battle resume Friday.
By Abraham McLaughlin Staff
writer of The Christian
Science Monitor." - ], - [ - "In a meeting of the cinematic
with the scientific, Hollywood
helicopter stunt pilots will
try to snatch a returning NASA
space probe out of the air
before it hits the ground." - ], - [ - "Legend has it (incorrectly, it
seems) that infamous bank
robber Willie Sutton, when
asked why banks were his
favorite target, responded,
quot;Because that #39;s where
the money is." - ], - [ - "Brown is a second year player
from Memphis and has spent the
2004 season on the Steelers
#39; practice squad. He played
in two games last year." - ], - [ - "A Canadian court approved Air
Canada #39;s (AC.TO: Quote,
Profile, Research) plan of
arrangement with creditors on
Monday, clearing the way for
the world #39;s 11th largest
airline to emerge from
bankruptcy protection at the
end of next month" - ], - [ - "Pfizer, GlaxoSmithKline and
Purdue Pharma are the first
drugmakers willing to take the
plunge and use radio frequency
identification technology to
protect their US drug supply
chains from counterfeiters." - ], - [ - "Barret Jackman, the last of
the Blues left to sign before
the league #39;s probable
lockout on Wednesday,
finalized a deal Monday that
is rare in the current
economic climate but fitting
for him." - ], - [ - " LONDON (Reuters) - Oil prices
eased on Monday after rebels
in Nigeria withdrew a threat
to target oil operations, but
lingering concerns over
stretched supplies ahead of
winter kept prices close to
\\$50." - ], - [ - "AP - In the tumult of the
visitors' clubhouse at Yankee
Stadium, champagne pouring all
around him, Theo Epstein held
a beer. \"I came in and there
was no champagne left,\" he
said this week. \"I said, 'I'll
have champagne if we win it
all.'\" Get ready to pour a
glass of bubbly for Epstein.
No I.D. necessary." - ], - [ - "Search any fee-based digital
music service for the best-
loved musical artists of the
20th century and most of the
expected names show up." - ], - [ - "Barcelona held on from an
early Deco goal to edge game
local rivals Espanyol 1-0 and
carve out a five point
tabletop cushion. Earlier,
Ronaldo rescued a point for
Real Madrid, who continued
their middling form with a 1-1
draw at Real Betis." - ], - [ - "MONTREAL (CP) - The Expos may
be history, but their demise
has heated up the market for
team memorabilia. Vintage
1970s and 1980s shirts are
already sold out, but
everything from caps, beer
glasses and key-chains to
dolls of mascot Youppi!" - ], - [ - "Stansted airport is the
designated emergency landing
ground for planes in British
airspace hit by in-flight
security alerts. Emergency
services at Stansted have
successfully dealt" - ], - [ - "The massive military operation
to retake Fallujah has been
quot;accomplished quot;, a
senior Iraqi official said.
Fierce fighting continued in
the war-torn city where
pockets of resistance were
still holding out against US
forces." - ], - [ - "There are some signs of
progress in resolving the
Nigerian conflict that is
riling global oil markets. The
leader of militia fighters
threatening to widen a battle
for control of Nigeria #39;s
oil-rich south has" - ], - [ - "A strong earthquake hit Taiwan
on Monday, shaking buildings
in the capital Taipei for
several seconds. No casualties
were reported." - ], - [ - "America Online Inc. is
packaging new features to
combat viruses, spam and
spyware in response to growing
online security threats.
Subscribers will be able to
get the free tools" - ], - [ - "A 76th minute goal from
European Footballer of the
Year Pavel Nedved gave
Juventus a 1-0 win over Bayern
Munich on Tuesday handing the
Italians clear control at the
top of Champions League Group
C." - ], - [ - " LONDON (Reuters) - Oil prices
climbed above \\$42 a barrel on
Wednesday, rising for the
third day in a row as cold
weather gripped the U.S.
Northeast, the world's biggest
heating fuel market." - ], - [ - "A policeman ran amok at a
security camp in Indian-
controlled Kashmir after an
argument and shot dead seven
colleagues before he was
gunned down, police said on
Sunday." - ], - [ - "ANN ARBOR, Mich. -- Some NHL
players who took part in a
charity hockey game at the
University of Michigan on
Thursday were hopeful the news
that the NHL and the players
association will resume talks
next week" - ], - [ - "New York police have developed
a pre-emptive strike policy,
cutting off demonstrations
before they grow large." - ], - [ - "CAPE CANAVERAL-- NASA aims to
launch its first post-Columbia
shuttle mission during a
shortened nine-day window
March, and failure to do so
likely would delay a planned
return to flight until at
least May." - ], - [ - "Travelers headed home for
Thanksgiving were greeted
Wednesday with snow-covered
highways in the Midwest, heavy
rain and tornadoes in parts of
the South, and long security
lines at some of the nation
#39;s airports." - ], - [ - "BOULDER, Colo. -- Vernand
Morency ran for 165 yards and
two touchdowns and Donovan
Woods threw for three more
scores, lifting No. 22
Oklahoma State to a 42-14
victory over Colorado
yesterday." - ], - [ - "The Chinese city of Beijing
has cancelled an order for
Microsoft software, apparently
bowing to protectionist
sentiment. The deal has come
under fire in China, which is
trying to build a domestic
software industry." - ], - [ - "Apple says it will deliver its
iTunes music service to more
European countries next month.
Corroborating several reports
in recent months, Reuters is
reporting today that Apple
Computer is planning the next" - ], - [ - "Reuters - Motorola Inc., the
world's\\second-largest mobile
phone maker, said on Tuesday
it expects\\to sustain strong
sales growth in the second
half of 2004\\thanks to new
handsets with innovative
designs and features." - ], - [ - "PULLMAN - Last week, in
studying USC game film, Cougar
coaches thought they found a
chink in the national
champions armor. And not just
any chink - one with the
potential, right from the get
go, to" - ], - [ - "The union representing flight
attendants on Friday said it
mailed more than 5,000 strike
authorization ballots to its
members employed by US Airways
as both sides continued talks
that are expected to stretch
through the weekend." - ], - [ - "AP - Matt Leinart was quite a
baseball prospect growing up,
showing so much promise as a
left-handed pitcher that
scouts took notice before high
school." - ], - [ - "PRAGUE, Czech Republic --
Eugene Cernan, the last man to
walk on the moon during the
final Apollo landing, said
Thursday he doesn't expect
space tourism to become
reality in the near future,
despite a strong demand.
Cernan, now 70, who was
commander of NASA's Apollo 17
mission and set foot on the
lunar surface in December 1972
during his third space flight,
acknowledged that \"there are
many people interested in
space tourism.\" But the
former astronaut said he
believed \"we are a long way
away from the day when we can
send a bus of tourists to the
moon.\" He spoke to reporters
before being awarded a medal
by the Czech Academy of
Sciences for his contribution
to science..." - ], - [ - "Never shy about entering a
market late, Microsoft Corp.
is planning to open the
virtual doors of its long-
planned Internet music store
next week. <FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</B></FONT>" - ], - [ - "AP - On his first birthday
Thursday, giant panda cub Mei
Sheng delighted visitors by
playing for the first time in
snow delivered to him at the
San Diego Zoo. He also sat on
his ice cake, wrestled with
his mom, got his coat
incredibly dirty, and didn't
read any of the more than 700
birthday wishes sent him via
e-mail from as far away as
Ireland and Argentina." - ], - [ - "AP - Researchers put a
satellite tracking device on a
15-foot shark that appeared to
be lost in shallow water off
Cape Cod, the first time a
great white has been tagged
that way in the Atlantic." - ], - [ - "LSU will stick with a two-
quarterback rotation Saturday
at Auburn, according to Tigers
coach Nick Saban, who seemed
to have some fun telling the
media what he will and won
#39;t discuss Monday." - ], - [ - "Bulgaria has started its first
co-mission with the EU in
Bosnia and Herzegovina, along
with some 30 countries,
including Canada and Turkey." - ], - [ - "The Windows Future Storage
(WinFS) technology that got
cut out of Windows
quot;Longhorn quot; is in
serious trouble, and not just
the hot water a feature might
encounter for missing its
intended production vehicle." - ], - [ - "Seattle -- - Not so long ago,
the 49ers were inflicting on
other teams the kind of pain
and embarrassment they felt in
their 34-0 loss to the
Seahawks on Sunday." - ], - [ - "AP - The pileup of events in
the city next week, including
the Republican National
Convention, will add to the
security challenge for the New
York Police Department, but
commissioner Ray Kelly says,
\"With a big, experienced
police force, we can do it.\"" - ], - [ - "washingtonpost.com - Let the
games begin. Not the Olympics
again, but the all-out battle
between video game giants Sony
Corp. and Nintendo Co. Ltd.
The two Japanese companies are
rolling out new gaming
consoles, but Nintendo has
beaten Sony to the punch by
announcing an earlier launch
date for its new hand-held
game player." - ], - [ - "London - Manchester City held
fierce crosstown rivals
Manchester United to a 0-0
draw on Sunday, keeping the
Red Devils eleven points
behind leaders Chelsea." - ], - [ - "LONDON, Dec 11 (IranMania) -
Iraqi Vice-President Ibrahim
al-Jaafari refused to believe
in remarks published Friday
that Iran was attempting to
influence Iraqi polls with the
aim of creating a
quot;crescent quot; dominated
by Shiites in the region." - ], - [ - "LOS ANGELES (CBS.MW) - The US
Securities and Exchange
Commission is probing
transactions between Delphi
Corp and EDS, which supplies
the automotive parts and
components giant with
technology services, Delphi
said late Wednesday." - ], - [ - "MONTREAL (CP) - Molson Inc.
and Adolph Coors Co. are
sweetening their brewery
merger plan with a special
dividend to Molson
shareholders worth \\$381
million." - ], - [ - "AP - Echoing what NASA
officials said a day earlier,
a Russian space official on
Friday said the two-man crew
on the international space
station could be forced to
return to Earth if a planned
resupply flight cannot reach
them with food supplies later
this month." - ], - [ - "InfoWorld - SANTA CLARA,
CALIF. -- Accommodating large
patch sets in Linux is
expected to mean forking off
of the 2.7 version of the
platform to accommodate these
changes, according to Andrew
Morton, lead maintainer of the
Linux kernel for Open Source
Development Labs (OSDL)." - ], - [ - "AMSTERDAM The mobile phone
giants Vodafone and Nokia
teamed up on Thursday to
simplify cellphone software
written with the Java computer
language." - ], - [ - "WELLINGTON: National carrier
Air New Zealand said yesterday
the Australian Competition
Tribunal has approved a
proposed alliance with Qantas
Airways Ltd, despite its
rejection in New Zealand." - ], - [ - "The late Princess Dianas
former bodyguard, Ken Wharfe,
dismisses her suspicions that
one of her lovers was bumped
off. Princess Diana had an
affair with Barry Mannakee, a
policeman who was assigned to
protect her." - ], - [ - "Long considered beyond the
reach of mainland mores, the
Florida city is trying to
limit blatant displays of
sexual behavior." - ], - [ - "The overall Linux market is
far larger than previous
estimates show, a new study
says. In an analysis of the
Linux market released late
Tuesday, market research firm
IDC estimated that the Linux
market -- including" - ], - [ - "By PAUL ELIAS SAN FRANCISCO
(AP) -- Several California
cities and counties, including
San Francisco and Los Angeles,
sued Microsoft Corp. (MSFT) on
Friday, accusing the software
giant of illegally charging
inflated prices for its
products because of monopoly
control of the personal
computer operating system
market..." - ], - [ - "New Ole Miss head coach Ed
Orgeron, speaking for the
first time since his hiring,
made clear the goal of his
football program. quot;The
goal of this program will be
to go to the Sugar Bowl, quot;
Orgeron said." - ], - [ - "\"Everyone's nervous,\" Acting
Undersecretary of Defense
Michael W. Wynne warned in a
confidential e-mail to Air
Force Secretary James G. Roche
on July 8, 2003." - ], - [ - "Reuters - Alpharma Inc. on
Friday began\\selling a cheaper
generic version of Pfizer
Inc.'s #36;3\\billion a year
epilepsy drug Neurontin
without waiting for a\\court
ruling on Pfizer's request to
block the copycat medicine." - ], - [ - "Public opinion of the database
giant sinks to 12-year low, a
new report indicates." - ], - [ - "Opinion: Privacy hysterics
bring old whine in new bottles
to the Internet party. The
desktop search beta from this
Web search leader doesn #39;t
do anything you can #39;t do
already." - ], - [ - "It is much too easy to call
Pedro Martinez the selfish
one, to say he is walking out
on the Red Sox, his baseball
family, for the extra year of
the Mets #39; crazy money." - ], - [ - "It is impossible for young
tennis players today to know
what it was like to be Althea
Gibson and not to be able to
quot;walk in the front door,
quot; Garrison said." - ], - [ - "Senator John Kerry said today
that the war in Iraq was a
\"profound diversion\" from the
war on terror and Osama bin
Laden." - ], - [ - "For spammers, it #39;s been a
summer of love. Two newly
issued reports tracking the
circulation of unsolicited
e-mails say pornographic spam
dominated this summer, nearly
all of it originating from
Internet addresses in North
America." - ], - [ - "The Nordics fared well because
of their long-held ideals of
keeping corruption clamped
down and respect for
contracts, rule of law and
dedication to one-on-one
business relationships." - ], - [ - "Microsoft portrayed its
Longhorn decision as a
necessary winnowing to hit the
2006 timetable. The
announcement on Friday,
Microsoft executives insisted,
did not point to a setback in
software" - ], - [ - "Here #39;s an obvious word of
advice to Florida athletic
director Jeremy Foley as he
kicks off another search for
the Gators football coach: Get
Steve Spurrier on board." - ], - [ - "Don't bother with the small
stuff. Here's what really
matters to your lender." - ], - [ - "A problem in the Service Pack
2 update for Windows XP may
keep owners of AMD-based
computers from using the long-
awaited security package,
according to Microsoft." - ], - [ - "Five years ago, running a
telephone company was an
immensely profitable
proposition. Since then, those
profits have inexorably
declined, and now that decline
has taken another gut-
wrenching dip." - ], - [ - "NEW YORK - The litigious
Recording Industry Association
of America (RIAA) is involved
in another legal dispute with
a P-to-P (peer-to-peer)
technology maker, but this
time, the RIAA is on defense.
Altnet Inc. filed a lawsuit
Wednesday accusing the RIAA
and several of its partners of
infringing an Altnet patent
covering technology for
identifying requested files on
a P-to-P network." - ], - [ - "A group claiming to have
captured two Indonesian women
in Iraq has said it will
release them if Jakarta frees
Muslim cleric Abu Bakar Bashir
being held for alleged
terrorist links." - ], - [ - "Amid the stormy gloom in
Gotham, the rain-idled Yankees
last night had plenty of time
to gather in front of their
televisions and watch the Red
Sox Express roar toward them.
The national telecast might
have been enough to send a
jittery Boss Steinbrenner
searching his Bartlett's
Familiar Quotations for some
quot;Little Engine That Could
quot; metaphor." - ], - [ - "FULHAM fans would have been
singing the late Elvis #39;
hit #39;The wonder of you
#39; to their player Elvis
Hammond. If not for Frank
Lampard spoiling the party,
with his dedication to his
late grandfather." - ], - [ - "Indonesian police said
yesterday that DNA tests had
identified a suicide bomber
involved in a deadly attack
this month on the Australian
embassy in Jakarta." - ], - [ - "NEW YORK - Wal-Mart Stores
Inc.'s warning of
disappointing sales sent
stocks fluctuating Monday as
investors' concerns about a
slowing economy offset their
relief over a drop in oil
prices. October contracts
for a barrel of light crude
were quoted at \\$46.48, down
24 cents, on the New York
Mercantile Exchange..." - ], - [ - "Iraq #39;s top Shi #39;ite
cleric made a sudden return to
the country on Wednesday and
said he had a plan to end an
uprising in the quot;burning
city quot; of Najaf, where
fighting is creeping ever
closer to its holiest shrine." - ], - [ - "For two weeks before MTV
debuted U2 #39;s video for the
new single quot;Vertigo,
quot; fans had a chance to see
the band perform the song on
TV -- in an iPod commercial." - ], - [ - "A bird #39;s eye view of the
circuit at Shanghai shows what
an event Sunday #39;s Chinese
Grand Prix will be. The course
is arguably one of the best
there is, and so it should be
considering the amount of
money that has been spent on
it." - ], - [ - "KABUL, Afghanistan Aug. 22,
2004 - US soldiers sprayed a
pickup truck with bullets
after it failed to stop at a
roadblock in central
Afghanistan, killing two women
and a man and critically
wounding two other" - ], - [ - "Oct. 26, 2004 - The US-
European spacecraft Cassini-
Huygens on Tuesday made a
historic flyby of Titan,
Saturn #39;s largest moon,
passing so low as to almost
touch the fringes of its
atmosphere." - ], - [ - "Reuters - A volcano in central
Japan sent smoke and\\ash high
into the sky and spat out
molten rock as it erupted\\for
a fourth straight day on
Friday, but experts said the
peak\\appeared to be quieting
slightly." - ], - [ - "Shares of Google Inc. made
their market debut on Thursday
and quickly traded up 19
percent at \\$101.28. The Web
search company #39;s initial
public offering priced at \\$85" - ], - [ - "SYDNEY -- Prime Minister John
Howard of Australia, a key US
ally and supporter of the Iraq
war, celebrated his election
win over opposition Labor
after voters enjoying the
fruits of a strong economy
gave him another term." - ], - [ - "Reuters - Global warming is
melting\\Ecuador's cherished
mountain glaciers and could
cause several\\of them to
disappear over the next two
decades, Ecuadorean and\\French
scientists said on Wednesday." - ], - [ - "AP - Sirius Satellite Radio
signed a deal to air the men's
NCAA basketball tournament
through 2007, the latest move
made in an attempt to draw
customers through sports
programming." - ], - [ - "Rather than tell you, Dan
Kranzler chooses instead to
show you how he turned Mforma
into a worldwide publisher of
video games, ringtones and
other hot downloads for mobile
phones." - ], - [ - "UK interest rates have been
kept on hold at 4.75 following
the latest meeting of the Bank
of England #39;s rate-setting
committee." - ], - [ - "BAGHDAD, Iraq - Two rockets
hit a downtown Baghdad hotel
housing foreigners and
journalists Thursday, and
gunfire erupted in the
neighborhood across the Tigris
River from the U.S. Embassy
compound..." - ], - [ - "The Prevention of Terrorism
Act 2002 (Pota) polarised the
country, not just by the
manner in which it was pushed
through by the NDA government
through a joint session of
Parliament but by the shabby
and often biased manner in
which it was enforced." - ], - [ - "Not being part of a culture
with a highly developed
language, could limit your
thoughts, at least as far as
numbers are concerned, reveals
a new study conducted by a
psychologist at the Columbia
University in New York." - ], - [ - "CAMBRIDGE, Mass. A native of
Red Oak, Iowa, who was a
pioneer in astronomy who
proposed the quot;dirty
snowball quot; theory for the
substance of comets, has died." - ], - [ - "Resurgent oil prices paused
for breath as the United
States prepared to draw on its
emergency reserves to ease
supply strains caused by
Hurricane Ivan." - ], - [ - "(Sports Network) - The
inconsistent San Diego Padres
will try for consecutive wins
for the first time since
August 28-29 tonight, when
they begin a huge four-game
set against the Los Angeles
Dodgers at Dodger Stadium." - ], - [ - "A Portuguese-sounding version
of the virus has appeared in
the wild. Be wary of mail from
Manaus." - ], - [ - " NEW YORK (Reuters) - Top seed
Roger Federer survived a
stirring comeback from twice
champion Andre Agassi to reach
the semifinals of the U.S.
Open for the first time on
Thursday, squeezing through
6-3, 2-6, 7-5, 3-6, 6-3." - ], - [ - "President Bush, who credits
three years of tax relief
programs with helping
strengthen the slow economy,
said Saturday he would sign
into law the Working Families
Tax Relief Act to preserve tax
cuts." - ], - [ - "HEN investors consider the
bond market these days, the
low level of interest rates
should be more cause for worry
than for gratitude." - ], - [ - "Reuters - Hunters soon may be
able to sit at\\their computers
and blast away at animals on a
Texas ranch via\\the Internet,
a prospect that has state
wildlife officials up\\in arms." - ], - [ - "The Bedminster-based company
yesterday said it was pushing
into 21 new markets with the
service, AT amp;T CallVantage,
and extending an introductory
rate offer until Sept. 30. In
addition, the company is
offering in-home installation
of up to five ..." - ], - [ - "Samsung Electronics Co., Ltd.
has developed a new LCD
(liquid crystal display)
technology that builds a touch
screen into the display, a
development that could lead to
thinner and cheaper display
panels for mobile phones, the
company said Tuesday." - ], - [ - "The US military says marines
in Fallujah shot and killed an
insurgent who engaged them as
he was faking being dead, a
week after footage of a marine
killing an apparently unarmed
and wounded Iraqi caused a
stir in the region." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks rallied on Monday after
software maker PeopleSoft Inc.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=PSFT.O target=/stocks/qu
ickinfo/fullquote\">PSFT.O&l
t;/A> accepted a sweetened
\\$10.3 billion buyout by rival
Oracle Corp.'s <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=ORCL.O ta
rget=/stocks/quickinfo/fullquo
te\">ORCL.O</A> and
other big deals raised
expectations of more
takeovers." - ], - [ - "SAN FRANCISCO - What Babe Ruth
was to the first half of the
20th century and Hank Aaron
was to the second, Barry Bonds
has become for the home run
generation." - ], - [ - "Description: NPR #39;s Alex
Chadwick talks to Colin Brown,
deputy political editor for
the United Kingdom #39;s
Independent newspaper,
currently covering the British
Labour Party Conference." - ], - [ - "Birgit Fischer settled for
silver, leaving the 42-year-
old Olympian with two medals
in two days against decidedly
younger competition." - ], - [ - "The New Jersey-based Accoona
Corporation, an industry
pioneer in artificial
intelligence search
technology, announced on
Monday the launch of Accoona." - ], - [ - "Hamas vowed revenge yesterday
after an Israeli airstrike in
Gaza killed one of its senior
commanders - the latest
assassination to have weakened
the militant group." - ], - [ - "There was no mystery ... no
secret strategy ... no baited
trap that snapped shut and
changed the course of history
#39;s most lucrative non-
heavyweight fight." - ], - [ - "Notre Dame accepted an
invitation Sunday to play in
the Insight Bowl in Phoenix
against a Pac-10 team on Dec.
28. The Irish (6-5) accepted
the bid a day after losing to
Southern California" - ], - [ - "Greg Anderson has so dominated
Pro Stock this season that his
championship quest has evolved
into a pursuit of NHRA
history. By Bob Hesser, Racers
Edge Photography." - ], - [ - "Goldman Sachs Group Inc. on
Thursday said fourth-quarter
profit rose as its fixed-
income, currency and
commodities business soared
while a rebounding stock
market boosted investment
banking." - ], - [ - " NEW YORK (Reuters) - The
dollar rose on Monday in a
retracement from last week's
steep losses, but dealers said
the bias toward a weaker
greenback remained intact." - ], - [ - "Michael Powell, chairman of
the FCC, said Wednesday he was
disappointed with ABC for
airing a sexually suggestive
opening to \"Monday Night
Football.\"" - ], - [ - "The message against illegally
copying CDs for uses such as
in file-sharing over the
Internet has widely sunk in,
said the company in it #39;s
recent announcement to drop
the Copy-Control program." - ], - [ - "Former Washington football
coach Rick Neuheisel looked
forward to a return to
coaching Wednesday after being
cleared by the NCAA of
wrongdoing related to his
gambling on basketball games." - ], - [ - "Reuters - California will
become hotter and\\drier by the
end of the century, menacing
the valuable wine and\\dairy
industries, even if dramatic
steps are taken to curb\\global
warming, researchers said on
Monday." - ], - [ - "A senior member of the
Palestinian resistance group
Hamas has been released from
an Israeli prison after
completing a two-year
sentence." - ], - [ - "IBM said Monday that it won a
500 million (AUD\\$1.25
billion), seven-year services
contract to help move UK bank
Lloyds TBS from its
traditional voice
infrastructure to a converged
voice and data network." - ], - [ - "MPs have announced a new
inquiry into family courts and
whether parents are treated
fairly over issues such as
custody or contact with their
children." - ], - [ - "Canadian Press - MELBOURNE,
Australia (AP) - A 36-year-old
businesswoman was believed to
be the first woman to walk
around Australia on Friday
after striding into her
hometown of Melbourne to
complete her 16,700-kilometre
trek in 365 days." - ], - [ - "Most remaining Pakistani
prisoners held at the US
Guantanamo Bay prison camp are
freed, officials say." - ], - [ - "Space shuttle astronauts will
fly next year without the
ability to repair in orbit the
type of damage that destroyed
the Columbia vehicle in
February 2003." - ], - [ - "Moscow - Russia plans to
combine Gazprom, the world
#39;s biggest natural gas
producer, with state-owned oil
producer Rosneft, easing rules
for trading Gazprom shares and
creating a company that may
dominate the country #39;s
energy industry." - ], - [ - "AP - Rutgers basketball player
Shalicia Hurns was suspended
from the team after pleading
guilty to punching and tying
up her roommate during a
dispute over painkilling
drugs." - ], - [ - "By cutting WinFS from Longhorn
and indefinitely delaying the
storage system, Microsoft
Corp. has also again delayed
the Microsoft Business
Framework (MBF), a new Windows
programming layer that is
closely tied to WinFS." - ], - [ - "French police are
investigating an arson-caused
fire at a Jewish Social Center
that might have killed dozens
without the quick response of
firefighters." - ], - [ - "Rodney King, whose videotaped
beating led to riots in Los
Angeles in 1992, is out of
jail now and talking frankly
for the first time about the
riots, himself and the
American way of life." - ], - [ - "AFP - Radical Islamic cleric
Abu Hamza al-Masri was set to
learn Thursday whether he
would be charged under
Britain's anti-terrorism law,
thus delaying his possible
extradition to the United
States to face terrorism-
related charges." - ], - [ - "Diversified manufacturer
Honeywell International Inc.
(HON.N: Quote, Profile,
Research) posted a rise in
quarterly profit as strong
demand for aerospace equipment
and automobile components" - ], - [ - "This was the event Michael
Phelps didn't really need to
compete in if his goal was to
win eight golds. He probably
would have had a better chance
somewhere else." - ], - [ - "Samsung's new SPH-V5400 mobile
phone sports a built-in
1-inch, 1.5-gigabyte hard disk
that can store about 15 times
more data than conventional
handsets, Samsung said." - ], - [ - "Reuters - U.S. housing starts
jumped a\\larger-than-expected
6.4 percent in October to the
busiest pace\\since December as
buyers took advantage of low
mortgage rates,\\a government
report showed on Wednesday." - ], - [ - "Gabe Kapler became the first
player to leave the World
Series champion Boston Red
Sox, agreeing to a one-year
contract with the Yomiuri
Giants in Tokyo." - ], - [ - "Louisen Louis, 30, walked
Monday in the middle of a
street that resembled a small
river with brown rivulets and
waves. He wore sandals and had
a cut on one of his big toes." - ], - [ - "BALI, Indonesia - Svetlana
Kuznetsova, fresh off her
championship at the US Open,
defeated Australian qualifier
Samantha Stosur 6-4, 6-4
Thursday to reach the
quarterfinals of the Wismilak
International." - ], - [ - "The Securities and Exchange
Commission ordered mutual
funds to stop paying higher
commissions to brokers who
promote the companies' funds
and required portfolio
managers to reveal investments
in funds they supervise." - ], - [ - "A car bomb exploded outside
the main hospital in Chechny
#39;s capital, Grozny, on
Sunday, injuring 17 people in
an attack apparently targeting
members of a Chechen security
force bringing in wounded from
an earlier explosion" - ], - [ - "AP - Just like the old days in
Dallas, Emmitt Smith made life
miserable for the New York
Giants on Sunday." - ], - [ - "Sumitomo Mitsui Financial
Group (SMFG), Japans second
largest bank, today put
forward a 3,200 billion (\\$29
billion) takeover bid for
United Financial Group (UFJ),
the countrys fourth biggest
lender, in an effort to regain
initiative in its bidding" - ], - [ - "AP - Gay marriage is emerging
as a big enough issue in
several states to influence
races both for Congress and
the presidency." - ], - [ - "TAMPA, Fla. - Chris Simms
first NFL start lasted 19
plays, and it might be a while
before he plays again for the
Tampa Bay Buccaneers." - ], - [ - "Vendor says it #39;s
developing standards-based
servers in various form
factors for the telecom
market. By Darrell Dunn.
Hewlett-Packard on Thursday
unveiled plans to create a
portfolio of products and
services" - ], - [ - "Jarno Trulli made the most of
the conditions in qualifying
to claim pole ahead of Michael
Schumacher, while Fernando
finished third." - ], - [ - "More than 30 aid workers have
been airlifted to safety from
a town in Sudan #39;s troubled
Darfur region after fighting
broke out and their base was
bombed, a British charity
says." - ], - [ - " NEW YORK (Reuters) - U.S.
chain store retail sales
slipped during the
Thanksgiving holiday week, as
consumers took advantage of
discounted merchandise, a
retail report said on
Tuesday." - ], - [ - "Ziff Davis - The company this
week will unveil more programs
and technologies designed to
ease users of its high-end
servers onto its Integrity
line, which uses Intel's
64-bit Itanium processor." - ], - [ - "The Mac maker says it will
replace about 28,000 batteries
in one model of PowerBook G4
and tells people to stop using
the notebook." - ], - [ - "It #39;s the mildest of mild
winters down here in the south
of Italy and, last weekend at
Bcoli, a pretty suburb by the
seaside west of Naples, the
customers of Pizzeria quot;Da
Enrico quot; were making the
most of it." - ], - [ - "By George Chamberlin , Daily
Transcript Financial
Correspondent. Concerns about
oil production leading into
the winter months sent shivers
through the stock market
Wednesday." - ], - [ - "Airbus has withdrawn a filing
that gave support for
Microsoft in an antitrust case
before the European Union
#39;s Court of First Instance,
a source close to the
situation said on Friday." - ], - [ - "WASHINGTON - A spotty job
market and stagnant paychecks
cloud this Labor Day holiday
for many workers, highlighting
the importance of pocketbook
issues in the presidential
election. \"Working harder
and enjoying it less,\" said
economist Ken Mayland,
president of ClearView
Economics, summing up the
state of working America..." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks rose on Wednesday
lifted by a merger between
retailers Kmart and Sears,
better-than-expected earnings
from Hewlett-Packard and data
showing a slight rise in core
inflation." - ], - [ - "AP - Authorities are
investigating whether bettors
at New York's top thoroughbred
tracks were properly informed
when jockeys came in
overweight at races, a source
familiar with the probe told
The Associated Press." - ], - [ - "European Commission president
Romano Prodi has unveiled
proposals to loosen the
deficit rules under the EU
Stability Pact. The loosening
was drafted by monetary
affairs commissioner Joaquin
Almunia, who stood beside the
president at the announcement." - ], - [ - "Canadian Press - MONTREAL (CP)
- A 19-year-old man charged in
a firebombing at a Jewish
elementary school pleaded
guilty Thursday to arson." - ], - [ - "Retail sales in Britain saw
the fastest growth in
September since January,
casting doubts on the view
that the economy is slowing
down, according to official
figures released Thursday." - ], - [ - " NEW YORK (Reuters) -
Interstate Bakeries Corp.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=IBC.N target=/stocks/qui
ckinfo/fullquote\">IBC.N<
/A>, maker of Hostess
Twinkies and Wonder Bread,
filed for bankruptcy on
Wednesday after struggling
with more than \\$1.3 billion
in debt and high costs." - ], - [ - "Delta Air Lines (DAL.N: Quote,
Profile, Research) on Thursday
said it reached a deal with
FedEx Express to sell eight
McDonnell Douglas MD11
aircraft and four spare
engines for delivery in 2004." - ], - [ - "Michael Owen scored his first
goal for Real Madrid in a 1-0
home victory over Dynamo Kiev
in the Champions League. The
England striker toe-poked home
Ronaldo #39;s cross in the
35th minute to join the
Russians" - ], - [ - " quot;Resuming uranium
enrichment is not in our
agenda. We are still committed
to the suspension, quot;
Foreign Ministry spokesman
Hamid Reza." - ], - [ - "Leading OPEC producer Saudi
Arabia said on Monday in
Vienna, Austria, that it had
made a renewed effort to
deflate record high world oil
prices by upping crude output
again." - ], - [ - "The U.S. military presence in
Iraq will grow to 150,000
troops by next month, the
highest level since the
invasion last year." - ], - [ - "UPDATE, SUN 9PM: More than a
million people have left their
homes in Cuba, as Hurricane
Ivan approaches. The ferocious
storm is headed that way,
after ripping through the
Cayman Islands, tearing off
roofs, flooding homes and
causing general havoc." - ], - [ - "Jason Giambi has returned to
the New York Yankees'
clubhouse but is still
clueless as to when he will be
able to play again." - ], - [ - "Seventy-five National Hockey
League players met with union
leaders yesterday to get an
update on a lockout that shows
no sign of ending." - ], - [ - "Howard, at 6-4 overall and 3-3
in the Mid-Eastern Athletic
Conference, can clinch a
winning record in the MEAC
with a win over Delaware State
on Saturday." - ], - [ - "Millions of casual US anglers
are having are larger than
appreciated impact on sea fish
stocks, scientists claim." - ], - [ - "The founders of the Pilgrim
Baxter amp; Associates money-
management firm agreed
yesterday to personally fork
over \\$160 million to settle
charges they allowed a friend
to" - ], - [ - " ATHENS (Reuters) - Top-ranked
Argentina booked their berth
in the women's hockey semi-
finals at the Athens Olympics
on Friday but defending
champions Australia now face
an obstacle course to qualify
for the medal matches." - ], - [ - "IBM Corp. Tuesday announced
plans to acquire software
vendor Systemcorp ALG for an
undisclosed amount. Systemcorp
of Montreal makes project
portfolio management software
aimed at helping companies
better manage their IT
projects." - ], - [ - "The Notre Dame message boards
are no longer discussing
whether Tyrone Willingham
should be fired. Theyre
already arguing about whether
the next coach should be Barry
Alvarez or Steve Spurrier." - ], - [ - "Forbes.com - By now you
probably know that earnings of
Section 529 college savings
accounts are free of federal
tax if used for higher
education. But taxes are only
part of the problem. What if
your investments tank? Just
ask Laurence and Margo
Williams of Alexandria, Va. In
2000 they put #36;45,000 into
the Virginia Education Savings
Trust to open accounts for
daughters Lea, now 5, and
Anne, now 3. Since then their
investment has shrunk 5 while
the average private college
tuition has climbed 18 to
#36;18,300." - ], - [ - "German Chancellor Gerhard
Schroeder said Sunday that
there was quot;no problem
quot; with Germany #39;s
support to the start of
negotiations on Turkey #39;s
entrance into EU." - ], - [ - "Coca-Cola Amatil Ltd.,
Australia #39;s biggest soft-
drink maker, offered A\\$500
million (\\$382 million) in
cash and stock for fruit
canner SPC Ardmona Ltd." - ], - [ - "US technology shares tumbled
on Friday after technology
bellwether Intel Corp.
(INTC.O: Quote, Profile,
Research) slashed its revenue
forecast, but blue chips were
only moderately lower as drug
and industrial stocks made
solid gains." - ], - [ - " WASHINGTON (Reuters) - Final
U.S. government tests on an
animal suspected of having mad
cow disease were not yet
complete, the U.S. Agriculture
Department said, with no
announcement on the results
expected on Monday." - ], - [ - "MANILA Fernando Poe Jr., the
popular actor who challenged
President Gloria Macapagal
Arroyo in the presidential
elections this year, died
early Tuesday." - ], - [ - " THOMASTOWN, Ireland (Reuters)
- World number three Ernie
Els overcame difficult weather
conditions to fire a sparkling
eight-under-par 64 and move
two shots clear after two
rounds of the WGC-American
Express Championship Friday." - ], - [ - "Lamar Odom supplemented 20
points with 13 rebounds and
Kobe Bryant added 19 points to
overcome a big night from Yao
Ming as the Los Angeles Lakers
ground out an 84-79 win over
the Rockets in Houston
Saturday." - ], - [ - "AMSTERDAM, NETHERLANDS - A
Dutch filmmaker who outraged
members of the Muslim
community by making a film
critical of the mistreatment
of women in Islamic society
was gunned down and stabbed to
death Tuesday on an Amsterdam
street." - ], - [ - "Microsoft Xbox Live traffic on
service provider networks
quadrupled following the
November 9th launch of Halo-II
-- which set entertainment
industry records by selling
2.4-million units in the US
and Canada on the first day of
availability, driving cash" - ], - [ - "Lawyers in a California class
action suit against Microsoft
will get less than half the
payout they had hoped for. A
judge in San Francisco ruled
that the attorneys will
collect only \\$112." - ], - [ - "Google Browser May Become
Reality\\\\There has been much
fanfare in the Mozilla fan
camps about the possibility of
Google using Mozilla browser
technology to produce a
GBrowser - the Google Browser.
Over the past two weeks, the
news and speculation has
escalated to the point where
even Google itself is ..." - ], - [ - "Metro, Germany's biggest
retailer, turns in weaker-
than-expected profits as sales
at its core supermarkets
division dip lower." - ], - [ - "It rained Sunday, of course,
and but another soppy, sloppy
gray day at Westside Tennis
Club did nothing to deter
Roger Federer from his
appointed rounds." - ], - [ - "Hewlett-Packard has joined
with Brocade to integrate
Brocade #39;s storage area
network switching technology
into HP Bladesystem servers to
reduce the amount of fabric
infrastructure needed in a
datacentre." - ], - [ - "Zimbabwe #39;s most persecuted
white MP began a year of hard
labour last night after
parliament voted to jail him
for shoving the Justice
Minister during a debate over
land seizures." - ], - [ - "BOSTON (CBS.MW) -- First
Command has reached a \\$12
million settlement with
federal regulators for making
misleading statements and
omitting important information
when selling mutual funds to
US military personnel." - ], - [ - "A smashing blow is being dealt
to thousands of future
pensioners by a law that has
just been brought into force
by the Federal Government." - ], - [ - "The US Senate Commerce
Committee on Wednesday
approved a measure that would
provide up to \\$1 billion to
ensure consumers can still
watch television when
broadcasters switch to new,
crisp digital signals." - ], - [ - "Amelie Mauresmo was handed a
place in the Advanta
Championships final after
Maria Sharapova withdrew from
their semi-final because of
injury." - ], - [ - "AP - An Israeli helicopter
fired two missiles in Gaza
City after nightfall
Wednesday, one at a building
in the Zeitoun neighborhood,
witnesses said, setting a
fire." - ], - [ - "Reuters - Internet stocks
are\\as volatile as ever, with
growth-starved investors
flocking to\\the sector in the
hope they've bought shares in
the next online\\blue chip." - ], - [ - "The federal government, banks
and aircraft lenders are
putting the clamps on
airlines, particularly those
operating under bankruptcy
protection." - ], - [ - "EURO DISNEY, the financially
crippled French theme park
operator, has admitted that
its annual losses more than
doubled last financial year as
it was hit by a surge in
costs." - ], - [ - " EAST RUTHERFORD, New Jersey
(Sports Network) - Retired NBA
center and seven-time All-
Star Alonzo Mourning is going
to give his playing career
one more shot." - ], - [ - "NASA has released an inventory
of the scientific devices to
be put on board the Mars
Science Laboratory rover
scheduled to land on the
surface of Mars in 2009, NASAs
news release reads." - ], - [ - "The U.S. Congress needs to
invest more in the U.S.
education system and do more
to encourage broadband
adoption, the chief executive
of Cisco said Wednesday.<p&
gt;ADVERTISEMENT</p><
p><img src=\"http://ad.do
ubleclick.net/ad/idg.us.ifw.ge
neral/sbcspotrssfeed;sz=1x1;or
d=200301151450?\" width=\"1\"
height=\"1\"
border=\"0\"/><a href=\"htt
p://ad.doubleclick.net/clk;922
8975;9651165;a?http://www.info
world.com/spotlights/sbc/main.
html?lpid0103035400730000idlp\"
>SBC Case Study: Crate Ba
rrel</a><br/>What
sold them on improving their
network? A system that could
cut management costs from the
get-go. Find out
more.</p>" - ], - [ - "The Philippines put the toll
at more than 1,000 dead or
missing in four storms in two
weeks but, even with a break
in the weather on Saturday" - ], - [ - "Reuters - Four explosions were
reported at petrol\\stations in
the Madrid area on Friday,
Spanish radio stations\\said,
following a phone warning in
the name of the armed
Basque\\separatist group ETA to
a Basque newspaper." - ], - [ - "Thirty-two countries and
regions will participate the
Fifth China International
Aviation and Aerospace
Exhibition, opening Nov. 1 in
Zhuhai, a city in south China
#39;s Guangdong Province." - ], - [ - "Jordan have confirmed that
Timo Glock will replace
Giorgio Pantano for this
weekend #39;s Chinese GP as
the team has terminated its
contract with Pantano." - ], - [ - "WEST PALM BEACH, Fla. -
Hurricane Jeanne got stronger,
bigger and faster as it
battered the Bahamas and bore
down on Florida Saturday,
sending huge waves crashing
onto beaches and forcing
thousands into shelters just
weeks after Frances ravaged
this area..." - ], - [ - "p2pnet.net News:- Virgin
Electronics has joined the mp3
race with a \\$250, five gig
player which also handles
Microsoft #39;s WMA format." - ], - [ - "WASHINGTON The idea of a no-
bid contract for maintaining
airport security equipment has
turned into a non-starter for
the Transportation Security
Administration." - ], - [ - "Eyetech (EYET:Nasdaq - news -
research) did not open for
trading Friday because a Food
and Drug Administration
advisory committee is meeting
to review the small New York-
based biotech #39;s
experimental eye disease drug." - ], - [ - "The continuing heartache of
Wake Forest #39;s ACC football
season was best described by
fifth-ranked Florida State
coach Bobby Bowden, after his
Seminoles had edged the
Deacons 20-17 Saturday at
Groves Stadium." - ], - [ - "On September 13, 2001, most
Americans were still reeling
from the shock of the
terrorist attacks on New York
and the Pentagon two days
before." - ], - [ - "Microsoft has suspended the
beta testing of the next
version of its MSN Messenger
client because of a potential
security problem, a company
spokeswoman said Wednesday." - ], - [ - "AP - Business software maker
Oracle Corp. attacked the
credibility and motives of
PeopleSoft Inc.'s board of
directors Monday, hoping to
rally investor support as the
17-month takeover battle
between the bitter business
software rivals nears a
climactic showdown." - ], - [ - "NEW YORK - Elena Dementieva
shook off a subpar serve that
produced 15 double-faults, an
aching left thigh and an upset
stomach to advance to the
semifinals at the U.S. Open
with a 4-6, 6-4, 7-6 (1)
victory Tuesday over Amelie
Mauresmo..." - ], - [ - "THE glory days have returned
to White Hart Lane. When Spurs
new first-team coach Martin
Jol promised a return to the
traditions of the 1960s,
nobody could have believed he
was so determined to act so
quickly and so literally." - ], - [ - "A new worm has been discovered
in the wild that #39;s not
just settling for invading
users #39; PCs--it wants to
invade their homes too." - ], - [ - "Domestic air travelers could
be surfing the Web by 2006
with government-approved
technology that allows people
access to high-speed Internet
connections while they fly." - ], - [ - "GENEVA: Cross-border
investment is set to bounce in
2004 after three years of deep
decline, reflecting a stronger
world economy and more
international merger activity,
the United Nations (UN) said
overnight." - ], - [ - "Researchers have for the first
time established the existence
of odd-parity superconductors,
materials that can carry
electric current without any
resistance." - ], - [ - "Chewing gum giant Wm. Wrigley
Jr. Co. on Thursday said it
plans to phase out production
of its Eclipse breath strips
at a plant in Phoenix, Arizona
and shift manufacturing to
Poznan, Poland." - ], - [ - "Prime Minister Dr Manmohan
Singh inaugurated a research
centre in the Capital on
Thursday to mark 400 years of
compilation of Sikh holy book
the Guru Granth Sahib." - ], - [ - "com September 16, 2004, 7:58
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." - ], - [ - "BRUSSELS, Belgium (AP) --
European antitrust regulators
said Monday they have extended
their review of a deal between
Microsoft Corp. (MSFT) and
Time Warner Inc..." - ], - [ - "AP - When Paula Radcliffe
dropped out of the Olympic
marathon miles from the
finish, she sobbed
uncontrollably. Margaret Okayo
knew the feeling. Okayo pulled
out of the marathon at the
15th mile with a left leg
injury, and she cried, too.
When she watched Radcliffe
quit, Okayo thought, \"Let's
cry together.\"" - ], - [ - "Tightness in the labour market
notwithstanding, the prospects
for hiring in the third
quarter are down from the
second quarter, according to
the new Manpower Employment
Outlook Survey." - ], - [ - "Fans who can't get enough of
\"The Apprentice\" can visit a
new companion Web site each
week and watch an extra 40
minutes of video not broadcast
on the Thursday
show.<br><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</b></font>" - ], - [ - " ATLANTA (Sports Network) -
The Atlanta Hawks signed free
agent Kevin Willis on
Wednesday, nearly a decade
after the veteran big man
ended an 11- year stint with
the team." - ], - [ - "An adult Web site publisher is
suing Google, saying the
search engine company made it
easier for users to see the
site #39;s copyrighted nude
photographs without paying or
gaining access through the
proper channels." - ], - [ - "When his right-front tire went
flying off early in the Ford
400, the final race of the
NASCAR Nextel Cup Series
season, Kurt Busch, it seemed,
was destined to spend his
offseason" - ], - [ - "A Washington-based public
opinion firm has released the
results of an election day
survey of Nevada voters
showing 81 support for the
issuance of paper receipts
when votes are cast
electronically." - ], - [ - "NAPSTER creator SHAWN FANNING
has revealed his plans for a
new licensed file-sharing
service with an almost
unlimited selection of tracks." - ], - [ - " NEW YORK (Reuters) - The
dollar rose on Friday, after a
U.S. report showed consumer
prices in line with
expections, reminding
investors that the Federal
Reserve was likely to
continue raising interest
rates, analysts said." - ], - [ - "Brandon Backe and Woody
Williams pitched well last
night even though neither
earned a win. But their
outings will show up in the
record books." - ], - [ - "President George W. Bush
pledged Friday to spend some
of the political capital from
his re-election trying to
secure a lasting Middle East
peace, and he envisioned the
establishment" - ], - [ - "The Football Association today
decided not to charge David
Beckham with bringing the game
into disrepute. The FA made
the surprise announcement
after their compliance unit
ruled" - ], - [ - "Last year some election
watchers made a bold
prediction that this
presidential election would
set a record: the first half
billion dollar campaign in
hard money alone." - ], - [ - "It #39;s one more blow to
patients who suffer from
arthritis. Pfizer, the maker
of Celebrex, says it #39;s
painkiller poses an increased
risk of heart attacks to
patients using the drugs." - ], - [ - "NEW DELHI - A bomb exploded
during an Independence Day
parade in India's remote
northeast on Sunday, killing
at least 15 people, officials
said, just an hour after Prime
Minister Manmohan Singh
pledged to fight terrorism.
The outlawed United Liberation
Front of Asom was suspected of
being behind the attack in
Assam state and a second one
later in the area, said Assam
Inspector General of Police
Khagen Sharma..." - ], - [ - "Two separate studies by U.S.
researchers find that super
drug-resistant strains of
tuberculosis are at the
tipping point of a global
epidemic, and only small
changes could help them spread
quickly." - ], - [ - " CRANS-SUR-SIERRE, Switzerland
(Reuters) - World number
three Ernie Els says he feels
a failure after narrowly
missing out on three of the
year's four major
championships." - ], - [ - "A UN envoy to Sudan will visit
Darfur tomorrow to check on
the government #39;s claim
that some 70,000 people
displaced by conflict there
have voluntarily returned to
their homes, a spokesman said." - ], - [ - "Dell cut prices on some
servers and PCs by as much as
22 percent because it #39;s
paying less for parts. The
company will pass the savings
on components such as memory
and liquid crystal displays" - ], - [ - "AP - Most of the presidential
election provisional ballots
rejected so far in Ohio came
from people who were not even
registered to vote, election
officials said after spending
nearly two weeks poring over
thousands of disputed votes." - ], - [ - "Striker Bonaventure Kalou
netted twice to send AJ
Auxerre through to the first
knockout round of the UEFA Cup
at the expense of Rangers on
Wednesday." - ], - [ - "AP - Rival inmates fought each
other with knives and sticks
Wednesday at a San Salvador
prison, leaving at least 31
people dead and two dozen
injured, officials said." - ], - [ - "WASHINGTON: The European-
American Cassini-Huygens space
probe has detected traces of
ice flowing on the surface of
Saturn #39;s largest moon,
Titan, suggesting the
existence of an ice volcano,
NASA said Tuesday." - ], - [ - "The economic growth rate in
the July-September period was
revised slightly downward from
an already weak preliminary
report, the government said
Wednesday." - ], - [ - "All ISS systems continue to
function nominally, except
those noted previously or
below. Day 7 of joint
Exp.9/Exp.10 operations and
last full day before 8S
undocking." - ], - [ - "BAGHDAD - Two Egyptian
employees of a mobile phone
company were seized when
gunmen stormed into their
Baghdad office, the latest in
a series of kidnappings in the
country." - ], - [ - "BRISBANE, Australia - The body
of a whale resembling a giant
dolphin that washed up on an
eastern Australian beach has
intrigued local scientists,
who agreed Wednesday that it
is rare but are not sure just
how rare." - ], - [ - " quot;Magic can happen. quot;
Sirius Satellite Radio
(nasdaq: SIRI - news - people
) may have signed Howard Stern
and the men #39;s NCAA
basketball tournaments, but XM
Satellite Radio (nasdaq: XMSR
- news - people ) has its
sights on your cell phone." - ], - [ - "Trick-or-treaters can expect
an early Halloween treat on
Wednesday night, when a total
lunar eclipse makes the moon
look like a glowing pumpkin." - ], - [ - "THIS weekend sees the
quot;other quot; showdown
between New York and New
England as the Jets and
Patriots clash in a battle of
the unbeaten teams." - ], - [ - "Sifting through millions of
documents to locate a valuable
few is tedious enough, but
what happens when those files
are scattered across different
repositories?" - ], - [ - "President Bush aims to
highlight American drug-
fighting aid in Colombia and
boost a conservative Latin
American leader with a stop in
the Andean nation where
thousands of security forces
are deployed to safeguard his
brief stay." - ], - [ - "Dubai - Former Palestinian
security minister Mohammed
Dahlan said on Monday that a
quot;gang of mercenaries quot;
known to the Palestinian
police were behind the
shooting that resulted in two
deaths in a mourning tent for
Yasser Arafat in Gaza." - ], - [ - "A drug company executive who
spoke out in support of
Montgomery County's proposal
to import drugs from Canada
and similar legislation before
Congress said that his company
has launched an investigation
into his political activities." - ], - [ - "Nicolas Anelka is fit for
Manchester City #39;s
Premiership encounter against
Tottenham at Eastlands, but
the 13million striker will
have to be content with a
place on the bench." - ], - [ - " PITTSBURGH (Reuters) - Ben
Roethlisberger passed for 183
yards and two touchdowns,
Hines Ward scored twice and
the Pittsburgh Steelers
rolled to a convincing 27-3
victory over Philadelphia on
Sunday for their second
straight win against an
undefeated opponent." - ], - [ - " ATHENS (Reuters) - The U.S.
men's basketball team got
their first comfortable win
at the Olympic basketball
tournament Monday, routing
winless Angola 89-53 in their
final preliminary round game." - ], - [ - "A Frenchman working for Thales
SA, Europe #39;s biggest maker
of military electronics, was
shot dead while driving home
at night in the Saudi Arabian
city of Jeddah." - ], - [ - "Often, the older a pitcher
becomes, the less effective he
is on the mound. Roger Clemens
apparently didn #39;t get that
memo. On Tuesday, the 42-year-
old Clemens won an
unprecedented" - ], - [ - "NASA #39;s Mars rovers have
uncovered more tantalizing
evidence of a watery past on
the Red Planet, scientists
said Wednesday. And the
rovers, Spirit and
Opportunity, are continuing to
do their jobs months after
they were expected to ..." - ], - [ - "SYDNEY (AFP) - Australia #39;s
commodity exports are forecast
to increase by 15 percent to a
record 95 billion dollars (71
million US), the government
#39;s key economic forecaster
said." - ], - [ - "Google won a major legal
victory when a federal judge
ruled that the search engines
advertising policy does not
violate federal trademark
laws." - ], - [ - "Intel Chief Technology Officer
Pat Gelsinger said on
Thursday, Sept. 9, that the
Internet needed to be upgraded
in order to deal with problems
that will become real issues
soon." - ], - [ - "China will take tough measures
this winter to improve the
country #39;s coal mine safety
and prevent accidents. State
Councilor Hua Jianmin said
Thursday the industry should
take" - ], - [ - "AT amp;T Corp. on Thursday
said it is reducing one fifth
of its workforce this year and
will record a non-cash charge
of approximately \\$11." - ], - [ - "BAGHDAD (Iraq): As the
intensity of skirmishes
swelled on the soils of Iraq,
dozens of people were put to
death with toxic shots by the
US helicopter gunship, which
targeted the civilians,
milling around a burning
American vehicle in a Baghdad
street on" - ], - [ - " LONDON (Reuters) - Television
junkies of the world, get
ready for \"Friends,\" \"Big
Brother\" and \"The Simpsons\" to
phone home." - ], - [ - "A rift appeared within Canada
#39;s music industry yesterday
as prominent artists called on
the CRTC to embrace satellite
radio and the industry warned
of lost revenue and job
losses." - ], - [ - "Reuters - A key Iranian
nuclear facility which
the\\U.N.'s nuclear watchdog
has urged Tehran to shut down
is\\nearing completion, a
senior Iranian nuclear
official said on\\Sunday." - ], - [ - "Spain's Football Federation
launches an investigation into
racist comments made by
national coach Luis Aragones." - ], - [ - "Bricks and plaster blew inward
from the wall, as the windows
all shattered and I fell to
the floorwhether from the
shock wave, or just fright, it
wasn #39;t clear." - ], - [ - "Surfersvillage Global Surf
News, 13 September 2004: - -
Hurricane Ivan, one of the
most powerful storms to ever
hit the Caribbean, killed at
least 16 people in Jamaica,
where it wrecked houses and
washed away roads on Saturday,
but appears to have spared" - ], - [ - "I #39;M FEELING a little bit
better about the hundreds of
junk e-mails I get every day
now that I #39;ve read that
someone else has much bigger
e-mail troubles." - ], - [ - "NEW DELHI: India and Pakistan
agreed on Monday to step up
cooperation in the energy
sector, which could lead to
Pakistan importing large
amounts of diesel fuel from
its neighbour, according to
Pakistani Foreign Minister
Khurshid Mehmood Kasuri." - ], - [ - "LONDON, England -- A US
scientist is reported to have
observed a surprising jump in
the amount of carbon dioxide,
the main greenhouse gas." - ], - [ - "Microsoft's antispam Sender ID
technology continues to get
the cold shoulder. Now AOL
adds its voice to a growing
chorus of businesses and
organizations shunning the
proprietary e-mail
authentication system." - ], - [ - "PSV Eindhoven faces Arsenal at
Highbury tomorrow night on the
back of a free-scoring start
to the season. Despite losing
Mateja Kezman to Chelsea in
the summer, the Dutch side has
scored 12 goals in the first" - ], - [ - "Through the World Community
Grid, your computer could help
address the world's health and
social problems." - ], - [ - "Zimbabwe #39;s ruling Zanu-PF
old guard has emerged on top
after a bitter power struggle
in the deeply divided party
during its five-yearly
congress, which ended
yesterday." - ], - [ - "PLAYER OF THE GAME: Playing
with a broken nose, Seattle
point guard Sue Bird set a
WNBA playoff record for
assists with 14, also pumping
in 10 points as the Storm
claimed the Western Conference
title last night." - ], - [ - "Reuters - Thousands of
demonstrators pressing
to\\install Ukraine's
opposition leader as president
after a\\disputed election
launched fresh street rallies
in the capital\\for the third
day Wednesday." - ], - [ - "Michael Jackson wishes he had
fought previous child
molestation claims instead of
trying to \"buy peace\", his
lawyer says." - ], - [ - "North Korea says it will not
abandon its weapons programme
after the South admitted
nuclear activities." - ], - [ - "While there is growing
attention to ongoing genocide
in Darfur, this has not
translated into either a
meaningful international
response or an accurate
rendering of the scale and
evident course of the
catastrophe." - ], - [ - "A planned component for
Microsoft #39;s next version
of Windows is causing
consternation among antivirus
experts, who say that the new
module, a scripting platform
called Microsoft Shell, could
give birth to a whole new
generation of viruses and
remotely" - ], - [ - "THE prosecution on terrorism
charges of extremist Islamic
cleric and accused Jemaah
Islamiah leader Abu Bakar
Bashir will rely heavily on
the potentially tainted
testimony of at least two
convicted Bali bombers, his
lawyers have said." - ], - [ - "SAN JOSE, California Yahoo
will likely have a tough time
getting American courts to
intervene in a dispute over
the sale of Nazi memorabilia
in France after a US appeals
court ruling." - ], - [ - "TORONTO (CP) - Glamis Gold of
Reno, Nev., is planning a
takeover bid for Goldcorp Inc.
of Toronto - but only if
Goldcorp drops its
\\$2.4-billion-Cdn offer for
another Canadian firm, made in
early December." - ], - [ - "Clashes between US troops and
Sadr militiamen escalated
Thursday, as the US surrounded
Najaf for possible siege." - ], - [ - "eBay Style director Constance
White joins Post fashion
editor Robin Givhan and host
Janet Bennett to discuss how
to find trends and bargains
and pull together a wardrobe
online." - ], - [ - "This week will see the release
of October new and existing
home sales, a measure of
strength in the housing
industry. But the short
holiday week will also leave
investors looking ahead to the
holiday travel season." - ], - [ - "Frankfurt - World Cup winners
Brazil were on Monday drawn to
meet European champions
Greece, Gold Cup winners
Mexico and Asian champions
Japan at the 2005
Confederations Cup." - ], - [ - "Third baseman Vinny Castilla
said he fits fine with the
Colorado youth movement, even
though he #39;ll turn 38 next
season and the Rockies are
coming off the second-worst" - ], - [ - "With a sudden shudder, the
ground collapsed and the pipe
pushed upward, buckling into a
humped shape as Cornell
University scientists produced
the first simulated earthquake" - ], - [ - "(Sports Network) - Two of the
top teams in the American
League tangle in a possible
American League Division
Series preview tonight, as the
West-leading Oakland Athletics
host the wild card-leading
Boston Red Sox for the first
of a three-game set at the" - ], - [ - "over half the children in the
world - suffer extreme
deprivation because of war,
HIV/AIDS or poverty, according
to a report released yesterday
by the United Nations Children
#39;s Fund." - ], - [ - "Microsoft (Quote, Chart) has
fired another salvo in its
ongoing spam battle, this time
against porn peddlers who don
#39;t keep their smut inside
the digital equivalent of a
quot;Brown Paper Wrapper." - ], - [ - " BETHESDA, Md. (Reuters) - The
use of some antidepressant
drugs appears linked to an
increase in suicidal behavior
in some children and teen-
agers, a U.S. advisory panel
concluded on Tuesday." - ], - [ - " SEATTLE (Reuters) - The next
version of the Windows
operating system, Microsoft
Corp.'s <A HREF=\"http://www
.reuters.co.uk/financeQuoteLoo
kup.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
flagship product, will ship
in 2006, the world's largest
software maker said on
Friday." - ], - [ - "Reuters - Philippine rescue
teams\\evacuated thousands of
people from the worst flooding
in the\\central Luzon region
since the 1970s as hungry
victims hunted\\rats and birds
for food." - ], - [ - "Inverness Caledonian Thistle
appointed Craig Brewster as
its new manager-player
Thursday although he #39;s
unable to play for the team
until January." - ], - [ - "The Afghan president expresses
deep concern after a bomb
attack which left at least
seven people dead." - ], - [ - " NEW YORK (Reuters) - U.S.
technology stocks opened lower
on Thursday after a sales
warning from Applied Materials
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AMAT.O target=/sto
cks/quickinfo/fullquote\">AM
AT.O</A>, while weekly
jobless claims data met Wall
Street's expectations,
leaving the Dow and S P 500
market measures little
changed." - ], - [ - "ATHENS, Greece -- Alan Shearer
converted an 87th-minute
penalty to give Newcastle a
1-0 win over Panionios in
their UEFA Cup Group D match." - ], - [ - "Fossil remains of the oldest
and smallest known ancestor of
Tyrannosaurus rex, the world
#39;s favorite ferocious
dinosaur, have been discovered
in China with evidence that
its body was cloaked in downy
quot;protofeathers." - ], - [ - "Derek Jeter turned a season
that started with a terrible
slump into one of the best in
his accomplished 10-year
career. quot;I don #39;t
think there is any question,
quot; the New York Yankees
manager said." - ], - [ - "Gardez (Afghanistan), Sept. 16
(Reuters): Afghan President
Hamid Karzai escaped an
assassination bid today when a
rocket was fired at his US
military helicopter as it was
landing in the southeastern
town of Gardez." - ], - [ - "The Jets came up with four
turnovers by Dolphins
quarterback Jay Fiedler in the
second half, including an
interception returned 66 yards
for a touchdown." - ], - [ - "China's Guo Jingjing easily
won the women's 3-meter
springboard last night, and Wu
Minxia made it a 1-2 finish
for the world's diving
superpower, taking the silver." - ], - [ - "GREEN BAY, Wisconsin (Ticker)
-- Brett Favre will be hoping
his 200th consecutive start
turns out better than his last
two have against the St." - ], - [ - "People fishing for sport are
doing far more damage to US
marine fish stocks than anyone
thought, accounting for nearly
a quarter of the" - ], - [ - "When an NFL team opens with a
prolonged winning streak,
former Miami Dolphins coach
Don Shula and his players from
the 17-0 team of 1972 root
unabashedly for the next
opponent." - ], - [ - "MIANNE Bagger, the transsexual
golfer who prompted a change
in the rules to allow her to
compete on the professional
circuit, made history
yesterday by qualifying to
play full-time on the Ladies
European Tour." - ], - [ - "Great Britain #39;s gold medal
tally now stands at five after
Leslie Law was handed the
individual three day eventing
title - in a courtroom." - ], - [ - "This particular index is
produced by the University of
Michigan Business School, in
partnership with the American
Society for Quality and CFI
Group, and is supported in
part by ForeSee Results" - ], - [ - "CHICAGO : Interstate Bakeries
Corp., the maker of popular,
old-style snacks Twinkies and
Hostess Cakes, filed for
bankruptcy, citing rising
costs and falling sales." - ], - [ - "Delta Air Lines (DAL:NYSE -
commentary - research) will
cut employees and benefits but
give a bigger-than-expected
role to Song, its low-cost
unit, in a widely anticipated
but still unannounced
overhaul, TheStreet.com has
learned." - ], - [ - "PC World - Symantec, McAfee
hope raising virus-definition
fees will move users to\\
suites." - ], - [ - "By byron kho. A consortium of
movie and record companies
joined forces on Friday to
request that the US Supreme
Court take another look at
peer-to-peer file-sharing
programs." - ], - [ - "DUBLIN -- Prime Minister
Bertie Ahern urged Irish
Republican Army commanders
yesterday to meet what he
acknowledged was ''a heavy
burden quot;: disarming and
disbanding their organization
in support of Northern
Ireland's 1998 peace accord." - ], - [ - "Mayor Tom Menino must be
proud. His Boston Red Sox just
won their first World Series
in 86 years and his Hyde Park
Blue Stars yesterday clinched
their first Super Bowl berth
in 32 years, defeating
O'Bryant, 14-0. Who would have
thought?" - ], - [ - "While reproductive planning
and women #39;s equality have
improved substantially over
the past decade, says a United
Nations report, world
population will increase from
6.4 billion today to 8.9
billion by 2050, with the 50
poorest countries tripling in" - ], - [ - "Instead of the skinny black
line, showing a hurricane
#39;s forecast track,
forecasters have drafted a
couple of alternative graphics
to depict where the storms
might go -- and they want your
opinion." - ], - [ - "South Korea have appealed to
sport #39;s supreme legal body
in an attempt to award Yang
Tae-young the Olympic
gymnastics all-round gold
medal after a scoring error
robbed him of the title in
Athens." - ], - [ - "BERLIN - Volkswagen AG #39;s
announcement this week that it
has forged a new partnership
deal with Malaysian carmaker
Proton comes as a strong euro
and Europe #39;s weak economic
performance triggers a fresh
wave of German investment in
Asia." - ], - [ - "AP - Johan Santana had an
early lead and was well on his
way to his 10th straight win
when the rain started to fall." - ], - [ - "ATHENS-In one of the biggest
shocks in Olympic judo
history, defending champion
Kosei Inoue was defeated by
Dutchman Elco van der Geest in
the men #39;s 100-kilogram
category Thursday." - ], - [ - "Consumers in Dublin pay more
for basic goods and services
that people elsewhere in the
country, according to figures
released today by the Central
Statistics Office." - ], - [ - "LIBERTY Media #39;s move last
week to grab up to 17.1 per
cent of News Corporation
voting stock has prompted the
launch of a defensive
shareholder rights plan." - ], - [ - "NBC is adding a 5-second delay
to its Nascar telecasts after
Dale Earnhardt Jr. used a
vulgarity during a postrace
interview last weekend." - ], - [ - "LONDON - Wild capuchin monkeys
can understand cause and
effect well enough to use
rocks to dig for food,
scientists have found.
Capuchin monkeys often use
tools and solve problems in
captivity and sometimes" - ], - [ - "San Francisco Giants
outfielder Barry Bonds, who
became the third player in
Major League Baseball history
to hit 700 career home runs,
won the National League Most
Valuable Player Award" - ], - [ - "The blue-chip Hang Seng Index
rose 171.88 points, or 1.22
percent, to 14,066.91. On
Friday, the index had slipped
31.58 points, or 0.2 percent." - ], - [ - "BAR's Anthony Davidson and
Jenson Button set the pace at
the first Chinese Grand Prix." - ], - [ - "Shares plunge after company
says its vein graft treatment
failed to show benefit in
late-stage test. CHICAGO
(Reuters) - Biotechnology
company Corgentech Inc." - ], - [ - "WASHINGTON - Contradicting the
main argument for a war that
has cost more than 1,000
American lives, the top U.S.
arms inspector reported
Wednesday that he found no
evidence that Iraq produced
any weapons of mass
destruction after 1991..." - ], - [ - "The key to hidden treasure
lies in your handheld GPS
unit. GPS-based \"geocaching\"
is a high-tech sport being
played by thousands of people
across the globe." - ], - [ - "AFP - Style mavens will be
scanning the catwalks in Paris
this week for next spring's
must-have handbag, as a
sweeping exhibition at the
French capital's fashion and
textile museum reveals the bag
in all its forms." - ], - [ - "Canadian Press - SAINT-
QUENTIN, N.B. (CP) - A major
highway in northern New
Brunswick remained closed to
almost all traffic Monday, as
local residents protested
planned health care cuts." - ], - [ - "The U.S. information tech
sector lost 403,300 jobs
between March 2001 and April
2004, and the market for tech
workers remains bleak,
according to a new report." - ], - [ - " NAJAF, Iraq (Reuters) - A
radical Iraqi cleric leading a
Shi'ite uprising agreed on
Wednesday to disarm his
militia and leave one of the
country's holiest Islamic
shrines after warnings of an
onslaught by government
forces." - ], - [ - "Saudi security forces have
killed a wanted militant near
the scene of a deadly shootout
Thursday. Officials say the
militant was killed in a
gunbattle Friday in the
northern town of Buraida,
hours after one" - ], - [ - "Portsmouth chairman Milan
Mandaric said on Tuesday that
Harry Redknapp, who resigned
as manager last week, was
innocent of any wrong-doing
over agent or transfer fees." - ], - [ - "This record is for all the
little guys, for all the
players who have to leg out
every hit instead of taking a
relaxing trot around the
bases, for all the batters
whose muscles aren #39;t" - ], - [ - "Two South Africans acquitted
by a Zimbabwean court of
charges related to the alleged
coup plot in Equatorial Guinea
are to be questioned today by
the South African authorities." - ], - [ - "Charlie Hodgson #39;s record-
equalling performance against
South Africa was praised by
coach Andy Robinson after the
Sale flyhalf scored 27 points
in England #39;s 32-16 victory
here at Twickenham on
Saturday." - ], - [ - "com September 30, 2004, 11:11
AM PT. SanDisk announced
Thursday increased capacities
for several different flash
memory cards. The Sunnyvale,
Calif." - ], - [ - "MOSCOW (CP) - Russia mourned
89 victims of a double air
disaster today as debate
intensified over whether the
two passenger liners could
have plunged almost
simultaneously from the sky by
accident." - ], - [ - "US blue-chip stocks rose
slightly on Friday as
government data showed better-
than-expected demand in August
for durable goods other than
transportation equipment, but
climbing oil prices limited
gains." - ], - [ - "BASEBALL Atlanta (NL):
Optioned P Roman Colon to
Greenville (Southern);
recalled OF Dewayne Wise from
Richmond (IL). Boston (AL):
Purchased C Sandy Martinez
from Cleveland (AL) and
assigned him to Pawtucket
(IL). Cleveland (AL): Recalled
OF Ryan Ludwick from Buffalo
(IL). Chicago (NL): Acquired
OF Ben Grieve from Milwaukee
(NL) for player to be named
and cash; acquired C Mike ..." - ], - [ - "Australia #39;s prime minister
says a body found in Fallujah
is likely that of kidnapped
aid worker Margaret Hassan.
John Howard told Parliament a
videotape of an Iraqi
terrorist group executing a
Western woman appears to have
been genuine." - ], - [ - "roundup Plus: Tech firms rally
against copyright bill...Apple
.Mac customers suffer e-mail
glitches...Alvarion expands
wireless broadband in China." - ], - [ - "BRONX, New York (Ticker) --
Kelvim Escobar was the latest
Anaheim Angels #39; pitcher to
subdue the New York Yankees.
Escobar pitched seven strong
innings and Bengie Molina tied
a career-high with four hits,
including" - ], - [ - "Business software maker
PeopleSoft Inc. said Monday
that it expects third-quarter
revenue to range between \\$680
million and \\$695 million,
above average Wall Street
estimates of \\$651." - ], - [ - "Sep 08 - Vijay Singh revelled
in his status as the new world
number one after winning the
Deutsche Bank Championship by
three shots in Boston on
Monday." - ], - [ - "Reuters - Enron Corp. ,
desperate to\\meet profit
targets, \"parked\" unwanted
power generating barges\\at
Merrill Lynch in a sham sale
designed to be reversed,
a\\prosecutor said on Tuesday
in the first criminal trial
of\\former executives at the
fallen energy company." - ], - [ - " NEW YORK (Reuters) - The
dollar rebounded on Monday
after a heavy selloff last
week, but analysts were
uncertain if the rally could
hold as the drumbeat of
expectation began for to the
December U.S. jobs report due
Friday." - ], - [ - "AP - Their first debate less
than a week away, President
Bush and Democrat John Kerry
kept their public schedules
clear on Saturday and began to
focus on their prime-time
showdown." - ], - [ - "Many people in golf are asking
that today. He certainly wasn
#39;t A-list and he wasn #39;t
Larry Nelson either. But you
couldn #39;t find a more solid
guy to lead the United States
into Ireland for the 2006
Ryder Cup Matches." - ], - [ - "Coles Myer Ltd. Australia
#39;s biggest retailer,
increased second-half profit
by 26 percent after opening
fuel and convenience stores,
selling more-profitable
groceries and cutting costs." - ], - [ - "MOSCOW: Us oil major
ConocoPhillips is seeking to
buy up to 25 in Russian oil
giant Lukoil to add billions
of barrels of reserves to its
books, an industry source
familiar with the matter said
on Friday." - ], - [ - "Australian Stuart Appleby, who
was the joint second-round
leader, returned a two-over 74
to drop to third at three-
under while American Chris
DiMarco moved into fourth with
a round of 69." - ], - [ - "PARIS Getting to the bottom of
what killed Yassar Arafat
could shape up to be an ugly
family tug-of-war. Arafat
#39;s half-brother and nephew
want copies of Arafat #39;s
medical records from the
suburban Paris hospital" - ], - [ - "Red Hat is acquiring security
and authentication tools from
Netscape Security Solutions to
bolster its software arsenal.
Red Hat #39;s CEO and chairman
Matthew Szulik spoke about the
future strategy of the Linux
supplier." - ], - [ - "With a doubleheader sweep of
the Minnesota Twins, the New
York Yankees moved to the
verge of clinching their
seventh straight AL East
title." - ], - [ - "Global Web portal Yahoo! Inc.
Wednesday night made available
a beta version of a new search
service for videos. Called
Yahoo! Video Search, the
search engine crawls the Web
for different types of media
files" - ], - [ - "Interactive posters at 25
underground stations are
helping Londoners travel
safely over Christmas." - ], - [ - "Athens, Greece (Sports
Network) - The first official
track event took place this
morning and Italy #39;s Ivano
Brugnetti won the men #39;s
20km walk at the Summer
Olympics in Athens." - ], - [ - " THE HAGUE (Reuters) - Former
Yugoslav President Slobodan
Milosevic condemned his war
crimes trial as a \"pure farce\"
on Wednesday in a defiant
finish to his opening defense
statement against charges of
ethnic cleansing in the
Balkans." - ], - [ - "US Airways Group (otc: UAIRQ -
news - people ) on Thursday
said it #39;ll seek a court
injunction to prohibit a
strike by disaffected unions." - ], - [ - "Shares in Unilever fall after
the Anglo-Dutch consumer goods
giant issued a surprise
profits warning." - ], - [ - "SAN FRANCISCO (CBS.MW) - The
Canadian government will sell
its 19 percent stake in Petro-
Canada for \\$2.49 billion,
according to the final
prospectus filed with the US
Securities and Exchange
Commission Thursday." - ], - [ - "Champions Arsenal opened a
five-point lead at the top of
the Premier League after a 4-0
thrashing of Charlton Athletic
at Highbury Saturday." - ], - [ - "The Redskins and Browns have
traded field goals and are
tied, 3-3, in the first
quarter in Cleveland." - ], - [ - " HYDERABAD, India (Reuters) -
Microsoft Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=MSFT.O t
arget=/stocks/quickinfo/fullqu
ote\">MSFT.O</A> will
hire several hundred new staff
at its new Indian campus in
the next year, its chief
executive said on Monday, in a
move aimed at strengthening
its presence in Asia's fourth-
biggest economy." - ], - [ - "According to Swiss
authorities, history was made
Sunday when 2723 people in
four communities in canton
Geneva, Switzerland, voted
online in a national federal
referendum." - ], - [ - " GUWAHATI, India (Reuters) -
People braved a steady drizzle
to come out to vote in a
remote northeast Indian state
on Thursday, as troops
guarded polling stations in an
election being held under the
shadow of violence." - ], - [ - "AFP - Three of the nine
Canadian sailors injured when
their newly-delivered,
British-built submarine caught
fire in the North Atlantic
were airlifted Wednesday to
hospital in northwest Ireland,
officials said." - ], - [ - "Alitalia SpA, Italy #39;s
largest airline, reached an
agreement with its flight
attendants #39; unions to cut
900 jobs, qualifying the
company for a government
bailout that will keep it in
business for another six
months." - ], - [ - "BAGHDAD, Iraq - A series of
strong explosions shook
central Baghdad near dawn
Sunday, and columns of thick
black smoke rose from the
Green Zone where U.S. and
Iraqi government offices are
located..." - ], - [ - "Forget September call-ups. The
Red Sox may tap their minor
league system for an extra
player or two when the rules
allow them to expand their
25-man roster Wednesday, but
any help from the farm is
likely to pale against the
abundance of talent they gain
from the return of numerous
players, including Trot Nixon
, from the disabled list." - ], - [ - "P amp;Os cutbacks announced
today are the result of the
waves of troubles that have
swamped the ferry industry of
late. Some would say the
company has done well to
weather the storms for as long
as it has." - ], - [ - "Sven-Goran Eriksson may gamble
by playing goalkeeper Paul
Robinson and striker Jermain
Defoe in Poland." - ], - [ - "Foreign Secretary Jack Straw
has flown to Khartoum on a
mission to pile the pressure
on the Sudanese government to
tackle the humanitarian
catastrophe in Darfur." - ], - [ - " Nextel was the big story in
telecommunications yesterday,
thanks to the Reston company's
mega-merger with Sprint, but
the future of wireless may be
percolating in dozens of
Washington area start-ups." - ], - [ - "Reuters - A senior U.S.
official said on
Wednesday\\deals should not be
done with hostage-takers ahead
of the\\latest deadline set by
Afghan Islamic militants who
have\\threatened to kill three
kidnapped U.N. workers." - ], - [ - "I have been anticipating this
day like a child waits for
Christmas. Today, PalmOne
introduces the Treo 650, the
answer to my quot;what smart
phone will I buy?" - ], - [ - "THOUSAND OAKS -- Anonymity is
only a problem if you want it
to be, and it is obvious Vijay
Singh doesn #39;t want it to
be. Let others chase fame." - ], - [ - "Wikipedia has surprised Web
watchers by growing fast and
maturing into one of the most
popular reference sites." - ], - [ - "It only takes 20 minutes on
the Internet for an
unprotected computer running
Microsoft Windows to be taken
over by a hacker. Any personal
or financial information
stored" - ], - [ - "TORONTO (CP) - Russia #39;s
Severstal has made an offer to
buy Stelco Inc., in what #39;s
believed to be one of several
competing offers emerging for
the restructuring but
profitable Hamilton steel
producer." - ], - [ - "Prices for flash memory cards
-- the little modules used by
digital cameras, handheld
organizers, MP3 players and
cell phones to store pictures,
music and other data -- are
headed down -- way down. Past
trends suggest that prices
will drop 35 percent a year,
but industry analysts think
that rate will be more like 40
or 50 percent this year and
next, due to more
manufacturers entering the
market." - ], - [ - "Walt Disney Co. #39;s
directors nominated Michael
Ovitz to serve on its board
for another three years at a
meeting just weeks before
forcing him out of his job as" - ], - [ - "The European Union, Japan,
Brazil and five other
countries won World Trade
Organization approval to
impose tariffs worth more than
\\$150 million a year on
imports from the United" - ], - [ - "Industrial conglomerate
Honeywell International on
Wednesday said it has filed a
lawsuit against 34 electronics
companies including Apple
Computer and Eastman Kodak,
claiming patent infringement
of its liquid crystal display
technology." - ], - [ - "Sinn Fein leader Gerry Adams
has put the pressure for the
success or failure of the
Northern Ireland assembly
talks firmly on the shoulders
of Ian Paisley." - ], - [ - "Australia #39;s Computershare
has agreed to buy EquiServe of
the United States for US\\$292
million (\\$423 million),
making it the largest US share
registrar and driving its
shares up by a third." - ], - [ - "David Coulthard #39;s season-
long search for a Formula One
drive next year is almost
over. Negotiations between Red
Bull Racing and Coulthard, who
tested for the Austrian team
for the first time" - ], - [ - "Interest rates on short-term
Treasury securities were mixed
in yesterday's auction. The
Treasury Department sold \\$18
billion in three-month bills
at a discount rate of 1.640
percent, up from 1.635 percent
last week. An additional \\$16
billion was sold in six-month
bills at a rate of 1.840
percent, down from 1.860
percent." - ], - [ - "Two top executives of scandal-
tarred insurance firm Marsh
Inc. were ousted yesterday,
the company said, the latest
casualties of an industry
probe by New York's attorney
general." - ], - [ - "AP - Southern California
tailback LenDale White
remembers Justin Holland from
high school. The Colorado
State quarterback made quite
an impression." - ], - [ - "TIM HENMAN last night admitted
all of his energy has been
drained away as he bowed out
of the Madrid Masters. The top
seed, who had a blood test on
Wednesday to get to the bottom
of his fatigue, went down" - ], - [ - "USDA #39;s Animal Plant Health
Inspection Service (APHIS)
this morning announced it has
confirmed a detection of
soybean rust from two test
plots at Louisiana State
University near Baton Rouge,
Louisiana." - ], - [ - " JAKARTA (Reuters) - President
Megawati Sukarnoputri urged
Indonesians on Thursday to
accept the results of the
country's first direct
election of a leader, but
stopped short of conceding
defeat." - ], - [ - "ISLAMABAD: Pakistan early
Monday test-fired its
indigenously developed short-
range nuclear-capable Ghaznavi
missile, the Inter Services
Public Relations (ISPR) said
in a statement." - ], - [ - "While the US software giant
Microsoft has achieved almost
sweeping victories in
government procurement
projects in several Chinese
provinces and municipalities,
the process" - ], - [ - "Mexican Cemex, being the third
largest cement maker in the
world, agreed to buy its
British competitor - RMC Group
- for \\$5.8 billion, as well
as their debts in order to
expand their activity on the
building materials market of
the USA and Europe." - ], - [ - "Microsoft Corp. has delayed
automated distribution of a
major security upgrade to its
Windows XP Professional
operating system, citing a
desire to give companies more
time to test it." - ], - [ - "The trial of a man accused of
murdering York backpacker
Caroline Stuttle begins in
Australia." - ], - [ - "Gateway computers will be more
widely available at Office
Depot, in the PC maker #39;s
latest move to broaden
distribution at retail stores
since acquiring rival
eMachines this year." - ], - [ - "ATHENS -- US sailors needed a
big day to bring home gold and
bronze medals from the sailing
finale here yesterday. But
rolling the dice on windshifts
and starting tactics backfired
both in Star and Tornado
classes, and the Americans had
to settle for a single silver
medal." - ], - [ - "Intel Corp. is refreshing its
64-bit Itanium 2 processor
line with six new chips based
on the Madison core. The new
processors represent the last
single-core Itanium chips that
the Santa Clara, Calif." - ], - [ - "The world's largest insurance
group pays \\$126m in fines as
part of a settlement with US
regulators over its dealings
with two firms." - ], - [ - "BRUSSELS: The EU sought
Wednesday to keep pressure on
Turkey over its bid to start
talks on joining the bloc, as
last-minute haggling seemed
set to go down to the wire at
a summit poised to give a
green light to Ankara." - ], - [ - "AP - J. Cofer Black, the State
Department official in charge
of counterterrorism, is
leaving government in the next
few weeks." - ], - [ - "For the first time, broadband
connections are reaching more
than half (51 percent) of the
American online population at
home, according to measurement
taken in July by
Nielsen/NetRatings, a
Milpitas-based Internet
audience measurement and
research ..." - ], - [ - "AP - Cavaliers forward Luke
Jackson was activated
Wednesday after missing five
games because of tendinitis in
his right knee. Cleveland also
placed forward Sasha Pavlovic
on the injured list." - ], - [ - "The tobacco firm John Player
amp; Sons has announced plans
to lay off 90 workers at its
cigarette factory in Dublin.
The company said it was
planning a phased closure of
the factory between now and
February as part of a review
of its global operations." - ], - [ - "AP - Consumers borrowed more
freely in September,
especially when it came to
racking up charges on their
credit cards, the Federal
Reserve reported Friday." - ], - [ - "AFP - The United States
presented a draft UN
resolution that steps up the
pressure on Sudan over the
crisis in Darfur, including
possible international
sanctions against its oil
sector." - ], - [ - "AFP - At least 33 people were
killed and dozens others
wounded when two bombs ripped
through a congregation of
Sunni Muslims in Pakistan's
central city of Multan, police
said." - ], - [ - "Bold, innovative solutions are
key to addressing the rapidly
rising costs of higher
education and the steady
reduction in government-
subsidized help to finance
such education." - ], - [ - "Just as the PhD crowd emerge
with different interpretations
of today's economy, everyday
Americans battling to balance
the checkbook hold diverse
opinions about where things
stand now and in the future." - ], - [ - "The Brisbane Lions #39;
football manager stepped out
of the changerooms just before
six o #39;clock last night and
handed one of the milling
supporters a six-pack of beer." - ], - [ - "Authorities here are always
eager to show off their
accomplishments, so when
Beijing hosted the World
Toilet Organization conference
last week, delegates were
given a grand tour of the
city's toilets." - ], - [ - "Cavaliers owner Gordon Gund is
in quot;serious quot;
negotiations to sell the NBA
franchise, which has enjoyed a
dramatic financial turnaround
since the arrival of star
LeBron James." - ], - [ - "WASHINGTON Trying to break a
deadlock on energy policy, a
diverse group of
environmentalists, academics
and former government
officials were to publish a
report on Wednesday that
presents strategies for making
the United States cleaner,
more competitive" - ], - [ - "After two days of gloom, China
was back on the winning rails
on Thursday with Liu Chunhong
winning a weightlifting title
on her record-shattering binge
and its shuttlers contributing
two golds in the cliff-hanging
finals." - ], - [ - "One question that arises
whenever a player is linked to
steroids is, \"What would he
have done without them?\"
Baseball history whispers an
answer." - ], - [ - "AFP - A series of torchlight
rallies and vigils were held
after darkness fell on this
central Indian city as victims
and activists jointly
commemorated a night of horror
20 years ago when lethal gas
leaked from a pesticide plant
and killed thousands." - ], - [ - "Consider the New World of
Information - stuff that,
unlike the paper days of the
past, doesn't always
physically exist. You've got
notes, scrawlings and
snippets, Web graphics, photos
and sounds. Stuff needs to be
cut, pasted, highlighted,
annotated, crossed out,
dragged away. And, as Ross
Perot used to say (or maybe it
was Dana Carvey impersonating
him), don't forget the graphs
and charts." - ], - [ - "The second round of the
Canadian Open golf tournament
continues Saturday Glenn Abbey
Golf Club in Oakville,
Ontario, after play was
suspended late Friday due to
darkness." - ], - [ - "A consortium led by Royal
Dutch/Shell Group that is
developing gas reserves off
Russia #39;s Sakhalin Island
said Thursday it has struck a
US\\$6 billion (euro4." - ], - [ - "Major Hollywood studios on
Tuesday announced scores of
lawsuits against computer
server operators worldwide,
including eDonkey, BitTorrent
and DirectConnect networks,
for allowing trading of
pirated movies." - ], - [ - "A massive plan to attract the
2012 Summer Olympics to New
York, touting the city's
diversity, financial and media
power, was revealed Wednesday." - ], - [ - "A Zimbabwe court Friday
convicted a British man
accused of leading a coup plot
against the government of oil-
rich Equatorial Guinea on
weapons charges, but acquitted
most of the 69 other men held
with him." - ], - [ - "But will Wi-Fi, high-
definition broadcasts, mobile
messaging and other
enhancements improve the game,
or wreck it?\\<br />
Photos of tech-friendly parks\\" - ], - [ - "An audit by international
observers supported official
elections results that gave
President Hugo Chavez a
victory over a recall vote
against him, the secretary-
general of the Organisation of
American States announced." - ], - [ - "Canadian Press - TORONTO (CP)
- The fatal stabbing of a
young man trying to eject
unwanted party guests from his
family home, the third such
knifing in just weeks, has
police worried about a
potentially fatal holiday
recipe: teens, alcohol and
knives." - ], - [ - "NICK Heidfeld #39;s test with
Williams has been brought
forward after BAR blocked
plans for Anthony Davidson to
drive its Formula One rival
#39;s car." - ], - [ - "MOSCOW - A female suicide
bomber set off a shrapnel-
filled explosive device
outside a busy Moscow subway
station on Tuesday night,
officials said, killing 10
people and injuring more than
50." - ], - [ - "Grace Park closed with an
eagle and two birdies for a
7-under-par 65 and a two-
stroke lead after three rounds
of the Wachovia LPGA Classic
on Saturday." - ], - [ - "ABIDJAN (AFP) - Two Ivory
Coast military aircraft
carried out a second raid on
Bouake, the stronghold of the
former rebel New Forces (FN)
in the divided west African
country, a French military
source told AFP." - ], - [ - "Carlos Beltran drives in five
runs to carry the Astros to a
12-3 rout of the Braves in
Game 5 of their first-round NL
playoff series." - ], - [ - "On-demand viewing isn't just
for TiVo owners anymore.
Television over internet
protocol, or TVIP, offers
custom programming over
standard copper wires." - ], - [ - "Apple is recalling 28,000
faulty batteries for its
15-inch Powerbook G4 laptops." - ], - [ - "Since Lennox Lewis #39;s
retirement, the heavyweight
division has been knocked for
having more quantity than
quality. Eight heavyweights on
Saturday night #39;s card at
Madison Square Garden hope to
change that perception, at
least for one night." - ], - [ - "PalmSource #39;s European
developer conference is going
on now in Germany, and this
company is using this
opportunity to show off Palm
OS Cobalt 6.1, the latest
version of its operating
system." - ], - [ - "The former Chief Executive
Officer of Computer Associates
was indicted by a federal
grand jury in New York
Wednesday for allegedly
participating in a massive
fraud conspiracy and an
elaborate cover up of a scheme
that cost investors" - ], - [ - "Speaking to members of the
Massachusetts Software
Council, Microsoft CEO Steve
Ballmer touted a bright future
for technology but warned his
listeners to think twice
before adopting open-source
products like Linux." - ], - [ - "MIAMI - The Trillian instant
messaging (IM) application
will feature several
enhancements in its upcoming
version 3.0, including new
video and audio chat
capabilities, enhanced IM
session logs and integration
with the Wikipedia online
encyclopedia, according to
information posted Friday on
the product developer's Web
site." - ], - [ - "Honeywell International Inc.,
the world #39;s largest
supplier of building controls,
agreed to buy Novar Plc for
798 million pounds (\\$1.53
billion) to expand its
security, fire and
ventilation-systems business
in Europe." - ], - [ - "San Francisco investment bank
Thomas Weisel Partners on
Thursday agreed to pay \\$12.5
million to settle allegations
that some of the stock
research the bank published
during the Internet boom was
tainted by conflicts of
interest." - ], - [ - "AFP - A state of civil
emergency in the rebellion-hit
Indonesian province of Aceh
has been formally extended by
six month, as the country's
president pledged to end
violence there without foreign
help." - ], - [ - "Forbes.com - Peter Frankling
tapped an unusual source to
fund his new business, which
makes hot-dog-shaped ice cream
treats known as Cool Dogs: Two
investors, one a friend and
the other a professional
venture capitalist, put in
more than #36;100,000 each
from their Individual
Retirement Accounts. Later
Franklin added #36;150,000
from his own IRA." - ], - [ - "Reuters - Online DVD rental
service Netflix Inc.\\and TiVo
Inc., maker of a digital video
recorder, on Thursday\\said
they have agreed to develop a
joint entertainment\\offering,
driving shares of both
companies higher." - ], - [ - "A San Diego insurance
brokerage has been sued by New
York Attorney General Elliot
Spitzer for allegedly
soliciting payoffs in exchange
for steering business to
preferred insurance companies." - ], - [ - "The European Union agreed
Monday to lift penalties that
have cost American exporters
\\$300 million, following the
repeal of a US corporate tax
break deemed illegal under
global trade rules." - ], - [ - "US Secretary of State Colin
Powell on Monday said he had
spoken to both Indian Foreign
Minister K Natwar Singh and
his Pakistani counterpart
Khurshid Mahmud Kasuri late
last week before the two met
in New Delhi this week for
talks." - ], - [ - "NEW YORK - Victor Diaz hit a
tying, three-run homer with
two outs in the ninth inning,
and Craig Brazell's first
major league home run in the
11th gave the New York Mets a
stunning 4-3 victory over the
Chicago Cubs on Saturday.
The Cubs had much on the
line..." - ], - [ - "AFP - At least 54 people have
died and more than a million
have fled their homes as
torrential rains lashed parts
of India and Bangladesh,
officials said." - ], - [ - "LOS ANGELES - California has
adopted the world's first
rules to reduce greenhouse
emissions for autos, taking
what supporters see as a
dramatic step toward cleaning
up the environment but also
ensuring higher costs for
drivers. The rules may lead
to sweeping changes in
vehicles nationwide,
especially if other states opt
to follow California's
example..." - ], - [ - " LONDON (Reuters) - European
stock markets scaled
near-2-1/2 year highs on
Friday as oil prices held
below \\$48 a barrel, and the
euro held off from mounting
another assault on \\$1.30 but
hovered near record highs
against the dollar." - ], - [ - "Tim Duncan had 17 points and
10 rebounds, helping the San
Antonio Spurs to a 99-81
victory over the New York
Kicks. This was the Spurs
fourth straight win this
season." - ], - [ - "Nagpur: India suffered a
double blow even before the
first ball was bowled in the
crucial third cricket Test
against Australia on Tuesday
when captain Sourav Ganguly
and off spinner Harbhajan
Singh were ruled out of the
match." - ], - [ - "AFP - Republican and
Democratic leaders each
declared victory after the
first head-to-head sparring
match between President George
W. Bush and Democratic
presidential hopeful John
Kerry." - ], - [ - "THIS YULE is all about console
supply and there #39;s
precious little units around,
it has emerged. Nintendo has
announced that it is going to
ship another 400,000 units of
its DS console to the United
States to meet the shortfall
there." - ], - [ - "Annual global semiconductor
sales growth will probably
fall by half in 2005 and
memory chip sales could
collapse as a supply glut saps
prices, world-leading memory
chip maker Samsung Electronics
said on Monday." - ], - [ - "NEW YORK - Traditional phone
systems may be going the way
of the Pony Express. Voice-
over-Internet Protocol,
technology that allows users
to make and receive phone
calls using the Internet, is
giving the old circuit-
switched system a run for its
money." - ], - [ - "AP - Former New York Yankees
hitting coach Rick Down was
hired for the same job by the
Mets on Friday, reuniting him
with new manager Willie
Randolph." - ], - [ - "Last night in New York, the UN
secretary-general was given a
standing ovation - a robust
response to a series of
attacks in past weeks." - ], - [ - "FILDERSTADT (Germany) - Amelie
Mauresmo and Lindsay Davenport
took their battle for the No.
1 ranking and Porsche Grand
Prix title into the semi-
finals with straight-sets
victories on Friday." - ], - [ - "Reuters - The company behind
the Atkins Diet\\on Friday
shrugged off a recent decline
in interest in low-carb\\diets
as a seasonal blip, and its
marketing chief said\\consumers
would cut out starchy foods
again after picking up\\pounds
over the holidays." - ], - [ - "There #39;s something to be
said for being the quot;first
mover quot; in an industry
trend. Those years of extra
experience in tinkering with a
new idea can be invaluable in
helping the first" - ], - [ - "JOHANNESBURG -- Meeting in
Nigeria four years ago,
African leaders set a goal
that 60 percent of children
and pregnant women in malaria-
affected areas around the
continent would be sleeping
under bed nets by the end of
2005." - ], - [ - "AP - The first U.S. cases of
the fungus soybean rust, which
hinders plant growth and
drastically cuts crop
production, were found at two
research sites in Louisiana,
officials said Wednesday." - ], - [ - "Carter returned, but it was
running back Curtis Martin and
the offensive line that put
the Jets ahead. Martin rushed
for all but 10 yards of a
45-yard drive that stalled at
the Cardinals 10." - ], - [ - " quot;There #39;s no way
anyone would hire them to
fight viruses, quot; said
Sophos security analyst Gregg
Mastoras. quot;For one, no
security firm could maintain
its reputation by employing
hackers." - ], - [ - "Symantec has revoked its
decision to blacklist a
program that allows Web
surfers in China to browse
government-blocked Web sites.
The move follows reports that
the firm labelled the Freegate
program, which" - ], - [ - " NEW YORK (Reuters) - Shares
of Chiron Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=CHIR.O t
arget=/stocks/quickinfo/fullqu
ote\">CHIR.O</A> fell
7 percent before the market
open on Friday, a day after
the biopharmaceutical company
said it is delaying shipment
of its flu vaccine, Fluvirin,
because lots containing 4
million vaccines do not meet
product sterility standards." - ], - [ - "The nation's top
telecommunications regulator
said yesterday he will push --
before the next president is
inaugurated -- to protect
fledgling Internet telephone
services from getting taxed
and heavily regulated by the
50 state governments." - ], - [ - "Microsoft has signed a pact to
work with the United Nations
Educational, Scientific and
Cultural Organization (UNESCO)
to increase computer use,
Internet access and teacher
training in developing
countries." - ], - [ - "DENVER (Ticker) -- Jake
Plummer more than made up for
a lack of a running game.
Plummer passed for 294 yards
and two touchdowns as the
Denver Broncos posted a 23-13
victory over the San Diego
Chargers in a battle of AFC
West Division rivals." - ], - [ - "DALLAS -- Belo Corp. said
yesterday that it would cut
250 jobs, more than half of
them at its flagship
newspaper, The Dallas Morning
News, and that an internal
investigation into circulation
overstatements" - ], - [ - "AP - Duke Bainum outspent Mufi
Hannemann in Honolulu's most
expensive mayoral race, but
apparently failed to garner
enough votes in Saturday's
primary to claim the office
outright." - ], - [ - "roundup Plus: Microsoft tests
Windows Marketplace...Nortel
delays financials
again...Microsoft updates
SharePoint." - ], - [ - "The Federal Reserve still has
some way to go to restore US
interest rates to more normal
levels, Philadelphia Federal
Reserve President Anthony
Santomero said on Monday." - ], - [ - "It took all of about five
minutes of an introductory
press conference Wednesday at
Heritage Hall for USC
basketball to gain something
it never really had before." - ], - [ - "Delta Air Lines (DAL.N: Quote,
Profile, Research) said on
Wednesday its auditors have
expressed doubt about the
airline #39;s financial
viability." - ], - [ - "POLITICIANS and aid agencies
yesterday stressed the
importance of the media in
keeping the spotlight on the
appalling human rights abuses
taking place in the Darfur
region of Sudan." - ], - [ - "AP - The Boston Red Sox looked
at the out-of-town scoreboard
and could hardly believe what
they saw. The New York Yankees
were trailing big at home
against the Cleveland Indians
in what would be the worst
loss in the 101-year history
of the storied franchise." - ], - [ - "The Red Sox will either
complete an amazing comeback
as the first team to rebound
from a 3-0 deficit in
postseason history, or the
Yankees will stop them." - ], - [ - "\\Children who have a poor diet
are more likely to become
aggressive and anti-social, US
researchers believe." - ], - [ - "OPEN SOURCE champion Microsoft
is expanding its programme to
give government organisations
some of its source code. In a
communique from the lair of
the Vole, in Redmond,
spinsters have said that
Microsoft" - ], - [ - "The Red Sox have reached
agreement with free agent
pitcher Matt Clement yesterday
on a three-year deal that will
pay him around \\$25 million,
his agent confirmed yesterday." - ], - [ - "Takeover target Ronin Property
Group said it would respond to
an offer by Multiplex Group
for all the securities in the
company in about three weeks." - ], - [ - "Canadian Press - OTTAWA (CP) -
Contrary to Immigration
Department claims, there is no
shortage of native-borne
exotic dancers in Canada, says
a University of Toronto law
professor who has studied the
strip club business." - ], - [ - "HEN Manny Ramirez and David
Ortiz hit consecutive home
runs Sunday night in Chicago
to put the Red Sox ahead,
there was dancing in the
streets in Boston." - ], - [ - "Google Inc. is trying to
establish an online reading
room for five major libraries
by scanning stacks of hard-to-
find books into its widely
used Internet search engine." - ], - [ - "HOUSTON--(BUSINESS
WIRE)--Sept. 1, 2004-- L
#39;operazione crea una
centrale globale per l
#39;analisi strategica el
#39;approfondimento del
settore energetico IHS Energy,
fonte globale leader di
software, analisi e
informazioni" - ], - [ - "The European Union presidency
yesterday expressed optimism
that a deal could be struck
over Turkey #39;s refusal to
recognize Cyprus in the lead-
up to next weekend #39;s EU
summit, which will decide
whether to give Ankara a date
for the start of accession
talks." - ], - [ - "WASHINGTON Can you always tell
when somebody #39;s lying? If
so, you might be a wizard of
the fib. A California
psychology professor says
there #39;s a tiny subculture
of people that can pick out a
lie nearly every time they
hear one." - ], - [ - " KHARTOUM (Reuters) - Sudan on
Saturday questioned U.N.
estimates that up to 70,000
people have died from hunger
and disease in its remote
Darfur region since a
rebellion began 20 months
ago." - ], - [ - "Type design was once the
province of skilled artisans.
With the help of new computer
programs, neophytes have
flooded the Internet with
their creations." - ], - [ - "RCN Inc., co-owner of
Starpower Communications LLC,
the Washington area
television, telephone and
Internet provider, filed a
plan of reorganization
yesterday that it said puts
the company" - ], - [ - "MIAMI -- Bryan Randall grabbed
a set of Mardi Gras beads and
waved them aloft, while his
teammates exalted in the
prospect of a trip to New
Orleans." - ], - [ - "<strong>Letters</stro
ng> Reports of demise
premature" - ], - [ - "TORONTO (CP) - With an injured
Vince Carter on the bench, the
Toronto Raptors dropped their
sixth straight game Friday,
101-87 to the Denver Nuggets." - ], - [ - "The US airline industry,
riddled with excess supply,
will see a significant drop in
capacity, or far fewer seats,
as a result of at least one
airline liquidating in the
next year, according to
AirTran Airways Chief
Executive Joe Leonard." - ], - [ - "Boeing (nyse: BA - news -
people ) Chief Executive Harry
Stonecipher is keeping the
faith. On Monday, the head of
the aerospace and military
contractor insists he #39;s
confident his firm will
ultimately win out" - ], - [ - "While not quite a return to
glory, Monday represents the
Redskins' return to the
national consciousness." - ], - [ - "Australia #39;s biggest
supplier of fresh milk,
National Foods, has posted a
net profit of \\$68.7 million,
an increase of 14 per cent on
last financial year." - ], - [ - "Lawyers for customers suing
Merck amp; Co. want to
question CEO Raymond Gilmartin
about what he knew about the
dangers of Vioxx before the
company withdrew the drug from
the market because of health
hazards." - ], - [ - "Vijay Singh has won the US PGA
Tour player of the year award
for the first time, ending
Tiger Woods #39;s five-year
hold on the honour." - ], - [ - "New York; September 23, 2004 -
The Department of Justice
(DoJ), FBI and US Attorney
#39;s Office handed down a
10-count indictment against
former Computer Associates
(CA) chairman and CEO Sanjay
Kumar and Stephen Richards,
former CA head of worldwide
sales." - ], - [ - "AFP - At least four Georgian
soldiers were killed and five
wounded in overnight clashes
in Georgia's separatist, pro-
Russian region of South
Ossetia, Georgian officers
near the frontline with
Ossetian forces said early
Thursday." - ], - [ - "Intel, the world #39;s largest
chip maker, scrapped a plan
Thursday to enter the digital
television chip business,
marking a major retreat from
its push into consumer
electronics." - ], - [ - "PACIFIC Hydro shares yesterday
caught an updraught that sent
them more than 20 per cent
higher after the wind farmer
moved to flush out a bidder." - ], - [ - "The European Commission is
expected later this week to
recommend EU membership talks
with Turkey. Meanwhile, German
Chancellor Gerhard Schroeder
and Turkish Prime Minister
Tayyip Erdogan are
anticipating a quot;positive
report." - ], - [ - "The US is the originator of
over 42 of the worlds
unsolicited commercial e-mail,
making it the worst offender
in a league table of the top
12 spam producing countries
published yesterday by anti-
virus firm Sophos." - ], - [ - " NEW YORK (Reuters) - U.S.
consumer confidence retreated
in August while Chicago-area
business activity slowed,
according to reports on
Tuesday that added to worries
the economy's patch of slow
growth may last beyond the
summer." - ], - [ - "Intel is drawing the curtain
on some of its future research
projects to continue making
transistors smaller, faster,
and less power-hungry out as
far as 2020." - ], - [ - "England got strikes from
sparkling debut starter
Jermain Defoe and Michael Owen
to defeat Poland in a Uefa
World Cup qualifier in
Chorzow." - ], - [ - "The Canadian government
signalled its intention
yesterday to reintroduce
legislation to decriminalise
the possession of small
amounts of marijuana." - ], - [ - "A screensaver targeting spam-
related websites appears to
have been too successful." - ], - [ - "Titleholder Ernie Els moved
within sight of a record sixth
World Match Play title on
Saturday by solving a putting
problem to overcome injured
Irishman Padraig Harrington 5
and 4." - ], - [ - "If the Washington Nationals
never win a pennant, they have
no reason to ever doubt that
DC loves them. Yesterday, the
District City Council
tentatively approved a tab for
a publicly financed ballpark
that could amount to as much
as \\$630 million." - ], - [ - "Plus: Experts fear Check 21
could lead to massive bank
fraud." - ], - [ - "By SIOBHAN McDONOUGH
WASHINGTON (AP) -- Fewer
American youths are using
marijuana, LSD and Ecstasy,
but more are abusing
prescription drugs, says a
government report released
Thursday. The 2003 National
Survey on Drug Use and Health
also found youths and young
adults are more aware of the
risks of using pot once a
month or more frequently..." - ], - [ - " ABIDJAN (Reuters) - Ivory
Coast warplanes killed nine
French soldiers on Saturday in
a bombing raid during the
fiercest clashes with rebels
for 18 months and France hit
back by destroying most of
the West African country's
small airforce." - ], - [ - "Unilever has reported a three
percent rise in third-quarter
earnings but warned it is
reviewing its targets up to
2010, after issuing a shock
profits warning last month." - ], - [ - "A TNO engineer prepares to
start capturing images for the
world's biggest digital photo." - ], - [ - "Defensive back Brandon
Johnson, who had two
interceptions for Tennessee at
Mississippi, was suspended
indefinitely Monday for
violation of team rules." - ], - [ - "Points leader Kurt Busch spun
out and ran out of fuel, and
his misfortune was one of the
reasons crew chief Jimmy
Fennig elected not to pit with
20 laps to go." - ], - [ - " LONDON (Reuters) - Oil prices
extended recent heavy losses
on Wednesday ahead of weekly
U.S. data expected to show
fuel stocks rising in time
for peak winter demand." - ], - [ - "(CP) - The NHL all-star game
hasn #39;t been cancelled
after all. It #39;s just been
moved to Russia. The agent for
New York Rangers winger
Jaromir Jagr confirmed Monday
that the Czech star had joined
Omsk Avangard" - ], - [ - "PalmOne is aiming to sharpen
up its image with the launch
of the Treo 650 on Monday. As
previously reported, the smart
phone update has a higher-
resolution screen and a faster
processor than the previous
top-of-the-line model, the
Treo 600." - ], - [ - "GAZA CITY, Gaza Strip --
Islamic militant groups behind
many suicide bombings
dismissed yesterday a call
from Mahmoud Abbas, the
interim Palestinian leader, to
halt attacks in the run-up to
a Jan. 9 election to replace
Yasser Arafat." - ], - [ - "Secretary of State Colin
Powell is wrapping up an East
Asia trip focused on prodding
North Korea to resume talks
aimed at ending its nuclear-
weapons program." - ], - [ - "HERE in the land of myth, that
familiar god of sports --
karma -- threw a bolt of
lightning into the Olympic
stadium yesterday. Marion
Jones lunged desperately with
her baton in the 4 x 100m
relay final, but couldn #39;t
reach her target." - ], - [ - "kinrowan writes quot;MIT,
inventor of Kerberos, has
announced a pair of
vulnerabities in the software
that will allow an attacker to
either execute a DOS attack or
execute code on the machine." - ], - [ - "The risk of intestinal damage
from common painkillers may be
higher than thought, research
suggests." - ], - [ - "AN earthquake measuring 7.3 on
the Richter Scale hit western
Japan this morning, just hours
after another strong quake
rocked the area." - ], - [ - "Vodafone has increased the
competition ahead of Christmas
with plans to launch 10
handsets before the festive
season. The Newbury-based
group said it will begin
selling the phones in
November." - ], - [ - "Reuters - Former Pink Floyd
mainman Roger\\Waters released
two new songs, both inspired
by the U.S.-led\\invasion of
Iraq, via online download
outlets Tuesday." - ], - [ - "A former assistant treasurer
at Enron Corp. (ENRNQ.PK:
Quote, Profile, Research)
agreed to plead guilty to
conspiracy to commit
securities fraud on Tuesday
and will cooperate with" - ], - [ - "Britain #39;s Prince Harry,
struggling to shed a growing
quot;wild child quot; image,
won #39;t apologize to a
photographer he scuffled with
outside an exclusive London
nightclub, a royal spokesman
said on Saturday." - ], - [ - "UK house prices fell by 1.1 in
October, confirming a
softening of the housing
market, Halifax has said. The
UK #39;s biggest mortgage
lender said prices rose 18." - ], - [ - "Pakistan #39;s interim Prime
Minister Chaudhry Shaujaat
Hussain has announced his
resignation, paving the way
for his successor Shauket
Aziz." - ], - [ - "A previously unknown group
calling itself Jamaat Ansar
al-Jihad al-Islamiya says it
set fire to a Jewish soup
kitchen in Paris, according to
an Internet statement." - ], - [ - "More than six newspaper
companies have received
letters from the Securities
and Exchange Commission
seeking information about
their circulation practices." - ], - [ - "THE 64,000 dollar -
correction, make that 500
million dollar -uestion
hanging over Shire
Pharmaceuticals is whether the
5 per cent jump in the
companys shares yesterday
reflects relief that US
regulators have finally
approved its drug for" - ], - [ - "The deadliest attack on
Americans in Iraq since May
came as Iraqi officials
announced that Saddam
Hussein's deputy had not been
captured on Sunday." - ], - [ - "AP - Kenny Rogers lost at the
Coliseum for the first time in
more than 10 years, with Bobby
Crosby's three-run double in
the fifth inning leading the
Athletics to a 5-4 win over
the Texas Rangers on Thursday." - ], - [ - "A fundraising concert will be
held in London in memory of
the hundreds of victims of the
Beslan school siege." - ], - [ - "Dambulla, Sri Lanka - Kumar
Sangakkara and Avishka
Gunawardene slammed impressive
half-centuries to help an
under-strength Sri Lanka crush
South Africa by seven wickets
in the fourth one-day
international here on
Saturday." - ], - [ - "Fresh off being the worst team
in baseball, the Arizona
Diamondbacks set a new record
this week: fastest team to
both hire and fire a manager." - ], - [ - "Nebraska head coach Bill
Callahan runs off the field at
halftime of the game against
Baylor in Lincoln, Neb.,
Saturday, Oct. 16, 2004." - ], - [ - "NASA has been working on a
test flight project for the
last few years involving
hypersonic flight. Hypersonic
flight is fight at speeds
greater than Mach 5, or five
times the speed of sound." - ], - [ - "AFP - The landmark trial of a
Rwandan Roman Catholic priest
accused of supervising the
massacre of 2,000 of his Tutsi
parishioners during the
central African country's 1994
genocide opens at a UN court
in Tanzania." - ], - [ - "The Irish government has
stepped up its efforts to free
the British hostage in Iraq,
Ken Bigley, whose mother is
from Ireland, by talking to
diplomats from Iran and
Jordan." - ], - [ - "AP - Republican Sen. Lincoln
Chafee, who flirted with
changing political parties in
the wake of President Bush's
re-election victory, says he
will stay in the GOP." - ], - [ - "AP - Microsoft Corp. announced
Wednesday that it would offer
a low-cost, localized version
of its Windows XP operating
system in India to tap the
large market potential in this
country of 1 billion people,
most of whom do not speak
English." - ], - [ - "Businesses saw inventories
rise in July and sales picked
up, the government reported
Wednesday. The Commerce
Department said that stocks of
unsold goods increased 0.9 in
July, down from a 1.1 rise in
June." - ], - [ - " EAST RUTHERFORD, N.J. (Sports
Network) - The Toronto
Raptors have traded All-Star
swingman Vince Carter to the
New Jersey Nets in exchange
for center Alonzo Mourning,
forward Eric Williams,
center/forward Aaron Williams
and two first- round draft
picks." - ], - [ - "AP - Utah State University has
secured a #36;40 million
contract with NASA to build an
orbiting telescope that will
examine galaxies and try to
find new stars." - ], - [ - "South Korean President Roh
Moo-hyun pays a surprise visit
to troops in Iraq, after his
government decided to extend
their mandate." - ], - [ - "As the European Union
approaches a contentious
decision - whether to let
Turkey join the club - the
Continent #39;s rulers seem to
have left their citizens
behind." - ], - [ - "What riot? quot;An Argentine
friend of mine was a little
derisive of the Pacers-Pistons
eruption, quot; says reader
Mike Gaynes. quot;He snorted,
#39;Is that what Americans
call a riot?" - ], - [ - "All season, Chris Barnicle
seemed prepared for just about
everything, but the Newton
North senior was not ready for
the hot weather he encountered
yesterday in San Diego at the
Footlocker Cross-Country
National Championships. Racing
in humid conditions with
temperatures in the 70s, the
Massachusetts Division 1 state
champion finished sixth in 15
minutes 34 seconds in the
5-kilometer race. ..." - ], - [ - "Shares of Genta Inc. (GNTA.O:
Quote, Profile, Research)
soared nearly 50 percent on
Monday after the biotechnology
company presented promising
data on an experimental
treatment for blood cancers." - ], - [ - "I spend anywhere from three to
eight hours every week
sweating along with a motley
crew of local misfits,
shelving, sorting, and hauling
ton after ton of written
matter in a rowhouse basement
in Baltimore. We have no heat
nor air conditioning, but
still, every week, we come and
work. Volunteer night is
Wednesday, but many of us also
work on the weekends, when
we're open to the public.
There are times when we're
freezing and we have to wear
coats and gloves inside,
making handling books somewhat
tricky; other times, we're all
soaked with sweat, since it's
90 degrees out and the
basement is thick with bodies.
One learns to forget about
personal space when working at
The Book Thing, since you can
scarcely breathe without
bumping into someone, and we
are all so accustomed to
having to scrape by each other
that most of us no longer
bother to say \"excuse me\"
unless some particularly
dramatic brushing occurs." - ], - [ - " BAGHDAD (Reuters) - A
deadline set by militants who
have threatened to kill two
Americans and a Briton seized
in Iraq was due to expire
Monday, and more than two
dozen other hostages were
also facing death unless rebel
demands were met." - ], - [ - "Alarmed by software glitches,
security threats and computer
crashes with ATM-like voting
machines, officials from
Washington, D.C., to
California are considering an
alternative from an unlikely
place: Nevada." - ], - [ - "Some Venezuelan television
channels began altering their
programs Thursday, citing
fears of penalties under a new
law restricting violence and
sexual content over the
airwaves." - ], - [ - "SBC Communications Inc. plans
to cut at least 10,000 jobs,
or 6 percent of its work
force, by the end of next year
to compensate for a drop in
the number of local-telephone
customers." - ], - [ - "afrol News, 4 October -
Hundred years of conservation
efforts have lifted the
southern black rhino
population from about hundred
to 11,000 animals." - ], - [ - "THE death of Philippine movie
star and defeated presidential
candidate Fernando Poe has
drawn some political backlash,
with some people seeking to
use his sudden demise as a
platform to attack President
Gloria Arroyo." - ], - [ - " WASHINGTON (Reuters) - Major
cigarette makers go on trial
on Tuesday in the U.S.
government's \\$280 billion
racketeering case that
charges the tobacco industry
with deliberately deceiving
the public about the risks of
smoking since the 1950s." - ], - [ - "More Americans are quitting
their jobs and taking the risk
of starting a business despite
a still-lackluster job market." - ], - [ - "AP - Coach Tyrone Willingham
was fired by Notre Dame on
Tuesday after three seasons in
which he failed to return one
of the nation's most storied
football programs to
prominence." - ], - [ - "COLLEGE PARK, Md. -- Joel
Statham completed 18 of 25
passes for 268 yards and two
touchdowns in No. 23
Maryland's 45-22 victory over
Temple last night, the
Terrapins' 12th straight win
at Byrd Stadium." - ], - [ - "Manchester United boss Sir
Alex Ferguson wants the FA to
punish Arsenal good guy Dennis
Bergkamp for taking a swing at
Alan Smith last Sunday." - ], - [ - "VIENTIANE, Laos China moved
yet another step closer in
cementing its economic and
diplomatic relationships with
Southeast Asia today when
Prime Minister Wen Jiabao
signed a trade accord at a
regional summit that calls for
zero tariffs on a wide range
of" - ], - [ - "SPACE.com - With nbsp;food
stores nbsp;running low, the
two \\astronauts living aboard
the International Space
Station (ISS) are cutting back
\\their meal intake and
awaiting a critical cargo
nbsp;delivery expected to
arrive \\on Dec. 25." - ], - [ - "British judges in London
Tuesday ordered radical Muslim
imam Abu Hamza to stand trial
for soliciting murder and
inciting racial hatred." - ], - [ - "Federal Reserve policy-makers
raised the benchmark US
interest rate a quarter point
to 2.25 per cent and restated
a plan to carry out" - ], - [ - " DETROIT (Reuters) - A
Canadian law firm on Tuesday
said it had filed a lawsuit
against Ford Motor Co. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=F
.N target=/stocks/quickinfo/fu
llquote\">F.N</A> over
what it claims are defective
door latches on about 400,000
of the automaker's popular
pickup trucks and SUVs." - ], - [ - "Published reports say Barry
Bonds has testified that he
used a clear substance and a
cream given to him by a
trainer who was indicted in a
steroid-distribution ring." - ], - [ - "SARASOTA, Fla. - The
devastation brought on by
Hurricane Charley has been
especially painful for an
elderly population that is
among the largest in the
nation..." - ], - [ - " ATHENS (Reuters) - Christos
Angourakis added his name to
Greece's list of Paralympic
medal winners when he claimed
a bronze in the T53 shot put
competition Thursday." - ], - [ - " quot;He is charged for having
a part in the Bali incident,
quot; state prosecutor Andi
Herman told Reuters on
Saturday. bombing attack at
the US-run JW." - ], - [ - "Jay Fiedler threw for one
touchdown, Sage Rosenfels
threw for another and the
Miami Dolphins got a victory
in a game they did not want to
play, beating the New Orleans
Saints 20-19 Friday night." - ], - [ - " NEW YORK (Reuters) - Terrell
Owens scored three touchdowns
and the Philadelphia Eagles
amassed 35 first-half points
on the way to a 49-21
drubbing of the Dallas Cowboys
in Irving, Texas, Monday." - ], - [ - "AstraZeneca Plc suffered its
third setback in two months on
Friday as lung cancer drug
Iressa failed to help patients
live longer" - ], - [ - "Virgin Electronics hopes its
slim Virgin Player, which
debuts today and is smaller
than a deck of cards, will
rise as a lead competitor to
Apple's iPod." - ], - [ - "Researchers train a monkey to
feed itself by guiding a
mechanical arm with its mind.
It could be a big step forward
for prosthetics. By David
Cohn." - ], - [ - "Bruce Wasserstein, the
combative chief executive of
investment bank Lazard, is
expected to agree this week
that he will quit the group
unless he can pull off a
successful" - ], - [ - "A late strike by Salomon Kalou
sealed a 2-1 win for Feyenoord
over NEC Nijmegen, while
second placed AZ Alkmaar
defeated ADO Den Haag 2-0 in
the Dutch first division on
Sunday." - ], - [ - "The United Nations called on
Monday for an immediate
ceasefire in eastern Congo as
fighting between rival army
factions flared for a third
day." - ], - [ - "What a disgrace Ron Artest has
become. And the worst part is,
the Indiana Pacers guard just
doesn #39;t get it. Four days
after fueling one of the
biggest brawls in the history
of pro sports, Artest was on
national" - ], - [ - "Allowing dozens of casinos to
be built in the UK would bring
investment and thousands of
jobs, Tony Blair says." - ], - [ - "The shock here was not just
from the awful fact itself,
that two vibrant young Italian
women were kidnapped in Iraq,
dragged from their office by
attackers who, it seems, knew
their names." - ], - [ - "Tehran/Vianna, Sept. 19 (NNN):
Iran on Sunday rejected the
International Atomic Energy
Agency (IAEA) call to suspend
of all its nuclear activities,
saying that it will not agree
to halt uranium enrichment." - ], - [ - "A 15-year-old Argentine
student opened fire at his
classmates on Tuesday in a
middle school in the south of
the Buenos Aires province,
leaving at least four dead and
five others wounded, police
said." - ], - [ - "Dr. David J. Graham, the FDA
drug safety reviewer who
sounded warnings over five
drugs he felt could become the
next Vioxx has turned to a
Whistleblower protection group
for legal help." - ], - [ - "AP - Scientists in 17
countries will scout waterways
to locate and study the
world's largest freshwater
fish species, many of which
are declining in numbers,
hoping to learn how to better
protect them, researchers
announced Thursday." - ], - [ - "AP - Google Inc.'s plans to
move ahead with its initial
public stock offering ran into
a roadblock when the
Securities and Exchange
Commission didn't approve the
Internet search giant's
regulatory paperwork as
requested." - ], - [ - "Jenson Button has revealed
dissatisfaction with the way
his management handled a
fouled switch to Williams. Not
only did the move not come
off, his reputation may have
been irreparably damaged amid
news headline" - ], - [ - "The Kmart purchase of Sears,
Roebuck may be the ultimate
expression of that old saying
in real estate: location,
location, location." - ], - [ - "Citing security risks, a state
university is urging students
to drop Internet Explorer in
favor of alternative Web
browsers such as Firefox and
Safari." - ], - [ - "Redknapp and his No2 Jim Smith
resigned from Portsmouth
yesterday, leaving
controversial new director
Velimir Zajec in temporary
control." - ], - [ - "Despite being ranked eleventh
in the world in broadband
penetration, the United States
is rolling out high-speed
services on a quot;reasonable
and timely basis to all
Americans, quot; according to
a new report narrowly approved
today by the Federal
Communications" - ], - [ - "Sprint Corp. (FON.N: Quote,
Profile, Research) on Friday
said it plans to cut up to 700
jobs as it realigns its
business to focus on wireless
and Internet services and
takes a non-cash network
impairment charge." - ], - [ - "As the season winds down for
the Frederick Keys, Manager
Tom Lawless is starting to
enjoy the progress his
pitching staff has made this
season." - ], - [ - "Britain #39;s Bradley Wiggins
won the gold medal in men
#39;s individual pursuit
Saturday, finishing the
4,000-meter final in 4:16." - ], - [ - "And when David Akers #39;
50-yard field goal cleared the
crossbar in overtime, they did
just that. They escaped a
raucous Cleveland Browns
Stadium relieved but not
broken, tested but not
cracked." - ], - [ - "NEW YORK - A cable pay-per-
view company has decided not
to show a three-hour election
eve special with filmmaker
Michael Moore that included a
showing of his documentary
\"Fahrenheit 9/11,\" which is
sharply critical of President
Bush. The company, iN
DEMAND, said Friday that its
decision is due to \"legitimate
business and legal concerns.\"
A spokesman would not
elaborate..." - ], - [ - "Democracy candidates picked up
at least one more seat in
parliament, according to exit
polls." - ], - [ - "The IOC wants suspended
Olympic member Ivan Slavkov to
be thrown out of the
organisation." - ], - [ - " BANGKOK (Reuters) - The
ouster of Myanmar's prime
minister, architect of a
tentative \"roadmap to
democracy,\" has dashed faint
hopes for an end to military
rule and leaves Southeast
Asia's policy of constructive
engagement in tatters." - ], - [ - "San Antonio, TX (Sports
Network) - Dean Wilson shot a
five-under 65 on Friday to
move into the lead at the
halfway point of the Texas
Open." - ], - [ - "Now that Chelsea have added
Newcastle United to the list
of clubs that they have given
what for lately, what price
Jose Mourinho covering the
Russian-funded aristocrats of
west London in glittering
glory to the tune of four
trophies?" - ], - [ - "AP - Former Seattle Seahawks
running back Chris Warren has
been arrested in Virginia on a
federal warrant, accused of
failing to pay #36;137,147 in
child support for his two
daughters in Washington state." - ], - [ - "The anguish of hostage Kenneth
Bigley in Iraq hangs over
Prime Minister Tony Blair
today as he faces the twin
test of a local election and a
debate by his Labour Party
about the divisive war." - ], - [ - "Says that amount would have
been earned for the first 9
months of 2004, before AT
amp;T purchase. LOS ANGELES,
(Reuters) - Cingular Wireless
would have posted a net profit
of \\$650 million for the first
nine months" - ], - [ - "NewsFactor - Taking an
innovative approach to the
marketing of high-performance
\\computing, Sun Microsystems
(Nasdaq: SUNW) is offering its
N1 Grid program in a pay-for-
use pricing model that mimics
the way such commodities as
electricity and wireless phone
plans are sold." - ], - [ - " CHICAGO (Reuters) - Goodyear
Tire Rubber Co. <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=GT.N t
arget=/stocks/quickinfo/fullqu
ote\">GT.N</A> said
on Friday it will cut 340 jobs
in its engineered products and
chemical units as part of its
cost-reduction efforts,
resulting in a third-quarter
charge." - ], - [ - " quot;There were 16 people
travelling aboard. ... It
crashed into a mountain, quot;
Col. Antonio Rivero, head of
the Civil Protection service,
told." - ], - [ - "Reuters - Shares of long-
distance phone\\companies AT T
Corp. and MCI Inc. have
plunged\\about 20 percent this
year, but potential buyers
seem to be\\holding out for
clearance sales." - ], - [ - "Reuters - Madonna and m-Qube
have\\made it possible for the
star's North American fans to
download\\polyphonic ring tones
and other licensed mobile
content from\\her official Web
site, across most major
carriers and without\\the need
for a credit card." - ], - [ - "President Bush is reveling in
winning the popular vote and
feels he can no longer be
considered a one-term accident
of history." - ], - [ - "Russian oil giant Yukos files
for bankruptcy protection in
the US in a last ditch effort
to stop the Kremlin auctioning
its main production unit." - ], - [ - "British Airways #39; (BA)
chief executive Rod Eddington
has admitted that the company
quot;got it wrong quot; after
staff shortages led to three
days of travel chaos for
passengers." - ], - [ - "It #39;s official: US Open had
never gone into the third
round with only two American
men, including the defending
champion, Andy Roddick." - ], - [ - "Canadian Press - TORONTO (CP)
- Thousands of Royal Bank
clerks are being asked to
display rainbow stickers at
their desks and cubicles to
promote a safe work
environment for gays,
lesbians, and bisexuals." - ], - [ - "The chipmaker is back on a
buying spree, having scooped
up five other companies this
year." - ], - [ - "The issue of drug advertising
directly aimed at consumers is
becoming political." - ], - [ - "WASHINGTON, Aug. 17
(Xinhuanet) -- England coach
Sven-Goran Eriksson has urged
the international soccer
authorities to preserve the
health of the world superstar
footballers for major
tournaments, who expressed his
will in Slaley of England on
Tuesday ..." - ], - [ - " BAGHDAD (Reuters) - A car
bomb killed two American
soldiers and wounded eight
when it exploded in Baghdad on
Saturday, the U.S. military
said in a statement." - ], - [ - "Juventus coach Fabio Capello
has ordered his players not to
kick the ball out of play when
an opponent falls to the
ground apparently hurt because
he believes some players fake
injury to stop the match." - ], - [ - "AP - China's economic boom is
still roaring despite efforts
to cool sizzling growth, with
the gross domestic product
climbing 9.5 percent in the
first three quarters of this
year, the government reported
Friday." - ], - [ - "Soaring petroleum prices
pushed the cost of goods
imported into the U.S. much
higher than expected in
August, the government said
today." - ], - [ - "Anheuser-Busch teams up with
Vietnam's largest brewer,
laying the groundwork for
future growth in the region." - ], - [ - "You #39;re angry. You want to
lash out. The Red Sox are
doing it to you again. They
#39;re blowing a playoff
series, and to the Yankees no
less." - ], - [ - "TORONTO -- There is no
mystique to it anymore,
because after all, the
Russians have become commoners
in today's National Hockey
League, and Finns, Czechs,
Slovaks, and Swedes also have
been entrenched in the
Original 30 long enough to
turn the ongoing World Cup of
Hockey into a protracted
trailer for the NHL season." - ], - [ - "Sudanese authorities have
moved hundreds of pro-
government fighters from the
crisis-torn Darfur region to
other parts of the country to
keep them out of sight of
foreign military observers
demanding the militia #39;s
disarmament, a rebel leader
charged" - ], - [ - "CHARLOTTE, NC - Shares of
Krispy Kreme Doughnuts Inc.
#39;s fell sharply Monday as a
79 percent plunge in third-
quarter earnings and an
intensifying accounting
investigation overshadowed the
pastrymaker #39;s statement
that the low-carb craze might
be easing." - ], - [ - "The company hopes to lure
software partners by promising
to save them from
infrastructure headaches." - ], - [ - "BAGHDAD, Iraq - A car bomb
Tuesday ripped through a busy
market near a Baghdad police
headquarters where Iraqis were
waiting to apply for jobs on
the force, and gunmen opened
fire on a van carrying police
home from work in Baqouba,
killing at least 59 people
total and wounding at least
114. The attacks were the
latest attempts by militants
to wreck the building of a
strong Iraqi security force, a
keystone of the U.S..." - ], - [ - "The Israeli prime minister
said today that he wanted to
begin withdrawing settlers
from Gaza next May or June." - ], - [ - "Indianapolis, IN (Sports
Network) - The Indiana Pacers
try to win their second
straight game tonight, as they
host Kevin Garnett and the
Minnesota Timberwolves in the
third of a four-game homestand
at Conseco Fieldhouse." - ], - [ - "OXNARD - A leak of explosive
natural gas forced dozens of
workers to evacuate an
offshore oil platform for
hours Thursday but no damage
or injuries were reported." - ], - [ - "AP - Assets of the nation's
retail money market mutual
funds rose by #36;2.85
billion in the latest week to
#36;845.69 billion, the
Investment Company Institute
said Thursday." - ], - [ - "Peter Mandelson provoked fresh
Labour in-fighting yesterday
with an implied attack on
Gordon Brown #39;s
quot;exaggerated gloating
quot; about the health of the
British economy." - ], - [ - "Queen Elizabeth II stopped
short of apologizing for the
Allies #39; bombing of Dresden
during her first state visit
to Germany in 12 years and
instead acknowledged quot;the
appalling suffering of war on
both sides." - ], - [ - "JC Penney said yesterday that
Allen I. Questrom, the chief
executive who has restyled the
once-beleaguered chain into a
sleeker and more profitable
entity, would be succeeded by
Myron E. Ullman III, another
longtime retail executive." - ], - [ - " In the cosmetics department
at Hecht's in downtown
Washington, construction crews
have ripped out the
traditional glass display
cases, replacing them with a
system of open shelves stacked
high with fragrances from
Chanel, Burberry and Armani,
now easily within arm's reach
of the impulse buyer." - ], - [ - " LONDON (Reuters) - Oil prices
slid from record highs above
\\$50 a barrel Wednesday as the
U.S. government reported a
surprise increase in crude
stocks and rebels in Nigeria's
oil-rich delta region agreed
to a preliminary cease-fire." - ], - [ - "Rocky Shoes and Boots makes an
accretive acquisition -- and
gets Dickies and John Deere as
part of the deal." - ], - [ - "AP - Fugitive Taliban leader
Mullah Mohammed Omar has
fallen out with some of his
lieutenants, who blame him for
the rebels' failure to disrupt
the landmark Afghan
presidential election, the
U.S. military said Wednesday." - ], - [ - "HAVANA -- Cuban President
Fidel Castro's advancing age
-- and ultimately his
mortality -- were brought home
yesterday, a day after he
fractured a knee and arm when
he tripped and fell at a
public event." - ], - [ - " BRASILIA, Brazil (Reuters) -
The United States and Brazil
predicted on Tuesday Latin
America's largest country
would resolve a dispute with
the U.N. nuclear watchdog over
inspections of a uranium
enrichment plant." - ], - [ - "Call it the Maximus factor.
Archaeologists working at the
site of an old Roman temple
near Guy #39;s hospital in
London have uncovered a pot of
cosmetic cream dating back to
AD2." - ], - [ - "It is a team game, this Ryder
Cup stuff that will commence
Friday at Oakland Hills
Country Club. So what are the
teams? For the Americans,
captain Hal Sutton isn't
saying." - ], - [ - "Two bombs exploded today near
a tea shop and wounded 20
people in southern Thailand,
police said, as violence
continued unabated in the
Muslim-majority region where
residents are seething over
the deaths of 78 detainees
while in military custody." - ], - [ - "BONN: Deutsche Telekom is
bidding 2.9 bn for the 26 it
does not own in T-Online
International, pulling the
internet service back into the
fold four years after selling
stock to the public." - ], - [ - "Motorola Inc. says it #39;s
ready to make inroads in the
cell-phone market after
posting a third straight
strong quarter and rolling out
a series of new handsets in
time for the holiday selling
season." - ], - [ - "Prime Minister Junichiro
Koizumi, back in Tokyo after
an 11-day diplomatic mission
to the Americas, hunkered down
with senior ruling party
officials on Friday to focus
on a major reshuffle of
cabinet and top party posts." - ], - [ - "Costs of employer-sponsored
health plans are expected to
climb an average of 8 percent
in 2005, the first time in
five years increases have been
in single digits." - ], - [ - "NEW YORK - A sluggish gross
domestic product reading was
nonetheless better than
expected, prompting investors
to send stocks slightly higher
Friday on hopes that the
economic slowdown would not be
as bad as first thought.
The 2.8 percent GDP growth in
the second quarter, a revision
from the 3 percent preliminary
figure reported in July, is a
far cry from the 4.5 percent
growth in the first quarter..." - ], - [ - "After again posting record
earnings for the third
quarter, Taiwan Semiconductor
Manufacturing Company (TSMC)
expects to see its first
sequential drop in fourth-
quarter revenues, coupled with
a sharp drop in capacity
utilization rates." - ], - [ - " SEOUL (Reuters) - A huge
explosion in North Korea last
week may have been due to a
combination of demolition work
for a power plant and
atmospheric clouds, South
Korea's spy agency said on
Wednesday." - ], - [ - "The deal comes as Cisco pushes
to develop a market for CRS-1,
a new line of routers aimed at
telephone, wireless and cable
companies." - ], - [ - "Many people who have never
bounced a check in their life
could soon bounce their first
check if they write checks to
pay bills a couple of days
before their paycheck is
deposited into their checking
account." - ], - [ - " LUXEMBOURG (Reuters) -
Microsoft Corp told a judge on
Thursday that the European
Commission must be stopped
from ordering it to give up
secret technology to
competitors." - ], - [ - "SEPTEMBER 14, 2004 (IDG NEWS
SERVICE) - Sun Microsystems
Inc. and Microsoft Corp. next
month plan to provide more
details on the work they are
doing to make their products
interoperable, a Sun executive
said yesterday." - ], - [ - "MANCHESTER United today
dramatically rejected the
advances of Malcolm Glazer,
the US sports boss who is
mulling an 825m bid for the
football club." - ], - [ - "Dow Jones Industrial Average
futures declined amid concern
an upcoming report on
manufacturing may point to
slowing economic growth." - ], - [ - "AP - Astronomy buffs and
amateur stargazers turned out
to watch a total lunar eclipse
Wednesday night #151; the
last one Earth will get for
nearly two and a half years." - ], - [ - "Reuters - U.S. industrial
output advanced in\\July, as
American factories operated at
their highest capacity\\in more
than three years, a Federal
Reserve report on
Tuesday\\showed." - ], - [ - "As usual, the Big Ten coaches
were out in full force at
today #39;s Big Ten
Teleconference. Both OSU head
coach Jim Tressel and Iowa
head coach Kirk Ferentz
offered some thoughts on the
upcoming game between OSU" - ], - [ - "Sir Martin Sorrell, chief
executive of WPP, yesterday
declared he was quot;very
impressed quot; with Grey
Global, stoking speculation
WPP will bid for the US
advertising company." - ], - [ - "The compact disc has at least
another five years as the most
popular music format before
online downloads chip away at
its dominance, a new study
said on Tuesday." - ], - [ - "New York Knicks #39; Stephon
Marbury (3) fires over New
Orleans Hornets #39; Dan
Dickau (2) during the second
half in New Orleans Wednesday
night, Dec. 8, 2004." - ], - [ - "AP - Sudan's U.N. ambassador
challenged the United States
to send troops to the Darfur
region if it really believes a
genocide is taking place as
the U.S. Congress and
President Bush's
administration have
determined." - ], - [ - "At the very moment when the
Red Sox desperately need
someone slightly larger than
life to rally around, they
suddenly have the man for the
job: Thrilling Schilling." - ], - [ - "Does Your Site Need a Custom
Search Engine
Toolbar?\\\\Today's surfers
aren't always too comfortable
installing software on their
computers. Especially free
software that they don't
necessarily understand. With
all the horror stories of
viruses, spyware, and adware
that make the front page these
days, it's no wonder. So is
there ..." - ], - [ - "Dale Earnhardt Jr, right,
talks with Matt Kenseth, left,
during a break in practice at
Lowe #39;s Motor Speedway in
Concord, NC, Thursday Oct. 14,
2004 before qualifying for
Saturday #39;s UAW-GM Quality
500 NASCAR Nextel Cup race." - ], - [ - "Several starting spots may
have been usurped or at least
threatened after relatively
solid understudy showings
Sunday, but few players
welcome the kind of shot
delivered to Oakland" - ], - [ - "TEMPE, Ariz. -- Neil Rackers
kicked four field goals and
the Arizona Cardinals stifled
rookie Chris Simms and the
rest of the Tampa Bay offense
for a 12-7 victory yesterday
in a matchup of two sputtering
teams out of playoff
contention. Coach Jon Gruden's
team lost its fourth in a row
to finish 5-11, Tampa Bay's
worst record since going ..." - ], - [ - "AFP - A junior British
minister said that people
living in camps after fleeing
their villages because of
conflict in Sudan's Darfur
region lived in fear of
leaving their temporary homes
despite a greater presence now
of aid workers." - ], - [ - "US Deputy Secretary of State
Richard Armitage (L) shakes
hands with Pakistani Foreign
Minister Khurshid Kasuri prior
to their meeting in Islamabad,
09 November 2004." - ], - [ - "Spammers aren't ducking
antispam laws by operating
offshore, they're just
ignoring it." - ], - [ - "AP - Biologists plan to use
large nets and traps this week
in Chicago's Burnham Harbor to
search for the northern
snakehead #151; a type of
fish known for its voracious
appetite and ability to wreak
havoc on freshwater
ecosystems." - ], - [ - "BAE Systems PLC and Northrop
Grumman Corp. won \\$45 million
contracts yesterday to develop
prototypes of anti-missile
technology that could protect
commercial aircraft from
shoulder-fired missiles." - ], - [ - "AP - Democrat John Edwards
kept up a long-distance debate
over his \"two Americas\"
campaign theme with Vice
President Dick Cheney on
Tuesday, saying it was no
illusion to thousands of laid-
off workers in Ohio." - ], - [ - "Like introductory credit card
rates and superior customer
service, some promises just
aren #39;t built to last. And
so it is that Bank of America
- mere months after its pledge
to preserve" - ], - [ - "A group of 76 Eritreans on a
repatriation flight from Libya
Friday forced their plane to
change course and land in the
Sudanese capital, Khartoum,
where they" - ], - [ - "Reuters - Accounting firm KPMG
will pay #36;10\\million to
settle charges of improper
professional conduct\\while
acting as auditor for Gemstar-
TV Guide International Inc.\\,
the U.S. Securities and
Exchange Commission said
on\\Wednesday." - ], - [ - "Family matters made public: As
eager cousins wait for a slice
of the \\$15 billion cake that
is the Pritzker Empire,
Circuit Court Judge David
Donnersberger has ruled that
the case will be conducted in
open court." - ], - [ - "Users of Microsoft #39;s
Hotmail, most of whom are
accustomed to getting regular
sales pitches for premium
e-mail accounts, got a
pleasant surprise in their
inboxes this week: extra
storage for free." - ], - [ - "AP - They say that opposites
attract, and in the case of
Sen. John Kerry and Teresa
Heinz Kerry, that may be true
#151; at least in their public
personas." - ], - [ - "The weekly survey from
mortgage company Freddie Mac
had rates on 30-year fixed-
rate mortgages inching higher
this week, up to an average
5.82 percent from last week
#39;s 5.81 percent." - ], - [ - "The classic power struggle
between Walt Disney Co. CEO
Michael Eisner and former
feared talent agent Michael
Ovitz makes for high drama in
the courtroom - and apparently
on cable." - ], - [ - "ATHENS France, Britain and the
United States issued a joint
challenge Thursday to Germany
#39;s gold medal in equestrian
team three-day eventing." - ], - [ - "LONDON (CBS.MW) -- Elan
(UK:ELA) (ELN) and partner
Biogen (BIIB) said the FDA has
approved new drug Tysabri to
treat relapsing forms of
multiple sclerosis." - ], - [ - "IT services provider
Electronic Data Systems
yesterday reported a net loss
of \\$153 million for the third
quarter, with earnings hit in
part by an asset impairment
charge of \\$375 million
connected with EDS's N/MCI
project." - ], - [ - "ATLANTA - Who could have
imagined Tommy Tuberville in
this position? Yet there he
was Friday, standing alongside
the SEC championship trophy,
posing for pictures and
undoubtedly chuckling a bit on
the inside." - ], - [ - "AP - Impoverished North Korea
might resort to selling
weapons-grade plutonium to
terrorists for much-needed
cash, and that would be
\"disastrous for the world,\"
the top U.S. military
commander in South Korea said
Friday." - ], - [ - "International Business
Machines Corp. said Wednesday
it had agreed to settle most
of the issues in a suit over
changes in its pension plans
on terms that allow the
company to continue to appeal
a key question while capping
its liability at \\$1.7
billion. <BR><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-The Washington
Post</B></FONT>" - ], - [ - "Dubai: Osama bin Laden on
Thursday called on his
fighters to strike Gulf oil
supplies and warned Saudi
leaders they risked a popular
uprising in an audio message
said to be by the Western
world #39;s most wanted terror
mastermind." - ], - [ - "Bank of New Zealand has frozen
all accounts held in the name
of Access Brokerage, which was
yesterday placed in
liquidation after a client
fund shortfall of around \\$5
million was discovered." - ], - [ - "Edward Kozel, Cisco's former
chief technology officer,
joins the board of Linux
seller Red Hat." - ], - [ - "Reuters - International
Business Machines\\Corp. late
on Wednesday rolled out a new
version of its\\database
software aimed at users of
Linux and Unix
operating\\systems that it
hopes will help the company
take away market\\share from
market leader Oracle Corp. ." - ], - [ - "NASA #39;s Mars Odyssey
mission, originally scheduled
to end on Tuesday, has been
granted a stay of execution
until at least September 2006,
reveal NASA scientists." - ], - [ - "ZDNet #39;s survey of IT
professionals in August kept
Wired amp; Wireless on top
for the 18th month in a row.
Telecommunications equipment
maker Motorola said Tuesday
that it would cut 1,000 jobs
and take related" - ], - [ - "John Thomson threw shutout
ball for seven innings
Wednesday night in carrying
Atlanta to a 2-0 blanking of
the New York Mets. New York
lost for the 21st time in 25
games on the" - ], - [ - "BEIJING -- Chinese authorities
have arrested or reprimanded
more than 750 officials in
recent months in connection
with billions of dollars in
financial irregularities
ranging from unpaid taxes to
embezzlement, according to a
report made public yesterday." - ], - [ - "Canadian Press - GAUHATI,
India (AP) - Residents of
northeastern India were
bracing for more violence
Friday, a day after bombs
ripped through two buses and a
grenade was hurled into a
crowded market in attacks that
killed four people and wounded
54." - ], - [ - "Vikram Solanki beat the rain
clouds to register his second
one-day international century
as England won the third one-
day international to wrap up a
series victory." - ], - [ - "Some 28 people are charged
with trying to overthrow
Sudan's President Bashir,
reports say." - ], - [ - "Hong Kong #39;s Beijing-backed
chief executive yesterday
ruled out any early moves to
pass a controversial national
security law which last year
sparked a street protest by
half a million people." - ], - [ - "BRITAIN #39;S largest
financial institutions are
being urged to take lead roles
in lawsuits seeking hundreds
of millions of dollars from
the scandal-struck US
insurance industry." - ], - [ - "Bankrupt UAL Corp. (UALAQ.OB:
Quote, Profile, Research) on
Thursday reported a narrower
third-quarter net loss. The
parent of United Airlines
posted a loss of \\$274
million, or" - ], - [ - "A three-touchdown point spread
and a recent history of late-
season collapses had many
thinking the UCLA football
team would provide little
opposition to rival USC #39;s
march to the BCS-championship
game at the Orange Bowl." - ], - [ - " PHOENIX (Sports Network) -
Free agent third baseman Troy
Glaus is reportedly headed to
the Arizona Diamondbacks." - ], - [ - "The European Union #39;s head
office issued a bleak economic
report Tuesday, warning that
the sharp rise in oil prices
will quot;take its toll quot;
on economic growth next year
while the euro #39;s renewed
climb could threaten crucial
exports." - ], - [ - "Third-string tailback Chris
Markey ran for 131 yards to
lead UCLA to a 34-26 victory
over Oregon on Saturday.
Markey, playing because of an
injury to starter Maurice
Drew, also caught five passes
for 84 yards" - ], - [ - "Though Howard Stern's
defection from broadcast to
satellite radio is still 16
months off, the industry is
already trying to figure out
what will fill the crater in
ad revenue and listenership
that he is expected to leave
behind." - ], - [ - " WASHINGTON (Reuters) - The
United States set final anti-
dumping duties of up to 112.81
percent on shrimp imported
from China and up to 25.76
percent on shrimp from Vietnam
to offset unfair pricing, the
Commerce Department said on
Tuesday." - ], - [ - "Jay Payton #39;s three-run
homer led the San Diego Padres
to a 5-1 win over the San
Francisco Giants in National
League play Saturday, despite
a 701st career blast from
Barry Bonds." - ], - [ - "The optimists among Rutgers
fans were delighted, but the
Scarlet Knights still gave the
pessimists something to worry
about." - ], - [ - "Beer consumption has doubled
over the past five years,
prompting legislators to
implement new rules." - ], - [ - "BusinessWeek Online - The
jubilation that swept East
Germany after the fall of the
Berlin Wall in 1989 long ago
gave way to the sober reality
of globalization and market
forces. Now a decade of
resentment seems to be boiling
over. In Eastern cities such
as Leipzig or Chemnitz,
thousands have taken to the
streets since July to protest
cuts in unemployment benefits,
the main source of livelihood
for 1.6 million East Germans.
Discontent among
reunification's losers fueled
big gains by the far left and
far right in Brandenburg and
Saxony state elections Sept.
19. ..." - ], - [ - "NEW YORK -- When Office Depot
Inc. stores ran an electronics
recycling drive last summer
that accepted everything from
cellphones to televisions,
some stores were overwhelmed
by the amount of e-trash they
received." - ], - [ - "In a discovery sure to set off
a firestorm of debate over
human migration to the western
hemisphere, archaeologists in
South Carolina say they have
uncovered evidence that people
lived in eastern North America
at least 50,000 years ago -
far earlier than" - ], - [ - "AP - Former president Bill
Clinton on Monday helped
launch a new Internet search
company backed by the Chinese
government which says its
technology uses artificial
intelligence to produce better
results than Google Inc." - ], - [ - "The International Monetary
Fund expressed concern Tuesday
about the impact of the
troubles besetting oil major
Yukos on Russia #39;s standing
as a place to invest." - ], - [ - "Perhaps the sight of Maria
Sharapova opposite her tonight
will jog Serena Williams #39;
memory. Wimbledon. The final.
You and Maria." - ], - [ - "At least 79 people were killed
and 74 were missing after some
of the worst rainstorms in
recent years triggered
landslides and flash floods in
southwest China, disaster
relief officials said
yesterday." - ], - [ - "LinuxWorld Conference amp;
Expo will come to Boston for
the first time in February,
underscoring the area's
standing as a hub for the
open-source software being
adopted by thousands of
businesses." - ], - [ - "BANGKOK Thai military aircraft
dropped 100 million paper
birds over southern Thailand
on Sunday in a gesture
intended to promote peace in
mainly Muslim provinces, where
more than 500 people have died
this year in attacks by
separatist militants and" - ], - [ - "Insurgents threatened on
Saturday to cut the throats of
two Americans and a Briton
seized in Baghdad, and
launched a suicide car bomb
attack on Iraqi security
forces in Kirkuk that killed
at least 23 people." - ], - [ - "Five days after making the
putt that won the Ryder Cup,
Colin Montgomerie looked set
to miss the cut at a European
PGA tour event." - ], - [ - "Sun Microsystems plans to
release its Sun Studio 10
development tool in the fourth
quarter of this year,
featuring support for 64-bit
applications running on AMD
Opteron and Nocona processors,
Sun officials said on Tuesday." - ], - [ - "Karachi - Captain Inzamam ul-
Haq and coach Bob Woolmer came
under fire on Thursday for
choosing to bat first on a
tricky pitch after Pakistan
#39;s humiliating defeat in
the ICC Champions Trophy semi-
final." - ], - [ - "Scottish champions Celtic saw
their three-year unbeaten home
record in Europe broken
Tuesday as they lost 3-1 to
Barcelona in the Champions
League Group F opener." - ], - [ - "Interest rates on short-term
Treasury securities rose in
yesterday's auction. The
Treasury Department sold \\$19
billion in three-month bills
at a discount rate of 1.710
percent, up from 1.685 percent
last week. An additional \\$17
billion was sold in six-month
bills at a rate of 1.950
percent, up from 1.870
percent." - ], - [ - "Most IT Managers won #39;t
question the importance of
security, but this priority
has been sliding between the
third and fourth most
important focus for companies." - ], - [ - "AP - Andy Roddick searched out
Carlos Moya in the throng of
jumping, screaming Spanish
tennis players, hoping to
shake hands." - ], - [ - "AP - The Federal Trade
Commission on Thursday filed
the first case in the country
against software companies
accused of infecting computers
with intrusive \"spyware\" and
then trying to sell people the
solution." - ], - [ - "While shares of Apple have
climbed more than 10 percent
this week, reaching 52-week
highs, two research firms told
investors Friday they continue
to remain highly bullish about
the stock." - ], - [ - "Moody #39;s Investors Service
on Wednesday said it may cut
its bond ratings on HCA Inc.
(HCA.N: Quote, Profile,
Research) deeper into junk,
citing the hospital operator
#39;s plan to buy back about
\\$2." - ], - [ - "States are now barred from
imposing telecommunications
regulations on Net phone
providers." - ], - [ - "Telekom Austria AG, the
country #39;s biggest phone
operator, won the right to buy
Bulgaria #39;s largest mobile
phone company, MobilTel EAD,
for 1.6 billion euros (\\$2.1
billion), an acquisition that
would add 3 million
subscribers." - ], - [ - "Shares of ID Biomedical jumped
after the company reported
Monday that it signed long-
term agreements with the three
largest flu vaccine
wholesalers in the United
States in light of the
shortage of vaccine for the
current flu season." - ], - [ - "ENGLAND captain and Real
Madrid midfielder David
Beckham has played down
speculation that his club are
moving for England manager
Sven-Goran Eriksson." - ], - [ - "The federal government closed
its window on the oil industry
Thursday, saying that it is
selling its last 19 per cent
stake in Calgary-based Petro-
Canada." - ], - [ - "Strong sales of new mobile
phone models boosts profits at
Carphone Warehouse but the
retailer's shares fall on
concerns at a decline in
profits from pre-paid phones." - ], - [ - " NEW YORK (Reuters) - IBM and
top scientific research
organizations are joining
forces in a humanitarian
effort to tap the unused
power of millions of computers
and help solve complex social
problems." - ], - [ - " NEW YORK, Nov. 11 -- The 40
percent share price slide in
Merck #38; Co. in the five
weeks after it pulled the
painkiller Vioxx off the
market highlighted larger
problems in the pharmaceutical
industry that may depress
performance for years,
according to academics and
stock analysts who follow the
sector." - ], - [ - "Low-fare carrier Southwest
Airlines Co. said Thursday
that its third-quarter profit
rose 12.3 percent to beat Wall
Street expectations despite
higher fuel costs." - ], - [ - "Another shock hit the drug
sector Friday when
pharmaceutical giant Pfizer
Inc. announced that it found
an increased heart risk to
patients for its blockbuster
arthritis drug Celebrex." - ], - [ - "AFP - National Basketball
Association players trying to
win a fourth consecutive
Olympic gold medal for the
United States have gotten the
wake-up call that the \"Dream
Team\" days are done even if
supporters have not." - ], - [ - " BEIJING (Reuters) - Secretary
of State Colin Powell will
urge China Monday to exert its
influence over North Korea to
resume six-party negotiations
on scrapping its suspected
nuclear weapons program." - ], - [ - "Reuters - An Israeli missile
strike killed at least\\two
Hamas gunmen in Gaza City
Monday, a day after a
top\\commander of the Islamic
militant group was killed in a
similar\\attack, Palestinian
witnesses and security sources
said." - ], - [ - "England will be seeking their
third clean sweep of the
summer when the NatWest
Challenge against India
concludes at Lord #39;s. After
beating New Zealand and West
Indies 3-0 and 4-0 in Tests,
they have come alive" - ], - [ - "AP - The Pentagon has restored
access to a Web site that
assists soldiers and other
Americans living overseas in
voting, after receiving
complaints that its security
measures were preventing
legitimate voters from using
it." - ], - [ - "The number of US information
technology workers rose 2
percent to 10.5 million in the
first quarter of this year,
but demand for them is
dropping, according to a new
report." - ], - [ - "Montreal - The British-built
Canadian submarine HMCS
Chicoutimi, crippled since a
fire at sea that killed one of
its crew, is under tow and is
expected in Faslane, Scotland,
early next week, Canadian
naval commander Tyrone Pile
said on Friday." - ], - [ - "AP - Grizzly and black bears
killed a majority of elk
calves in northern Yellowstone
National Park for the second
year in a row, preliminary
study results show." - ], - [ - "Thirty-five Pakistanis freed
from the US Guantanamo Bay
prison camp arrived home on
Saturday and were taken
straight to prison for further
interrogation, the interior
minister said." - ], - [ - "AP - Mark Richt knows he'll
have to get a little creative
when he divvies up playing
time for Georgia's running
backs next season. Not so on
Saturday. Thomas Brown is the
undisputed starter for the
biggest game of the season." - ], - [ - "Witnesses in the trial of a US
soldier charged with abusing
prisoners at Abu Ghraib have
told the court that the CIA
sometimes directed abuse and
orders were received from
military command to toughen
interrogations." - ], - [ - "Insurance firm says its board
now consists of its new CEO
Michael Cherkasky and 10
outside members. NEW YORK
(Reuters) - Marsh amp;
McLennan Cos." - ], - [ - "Michael Phelps, the six-time
Olympic champion, issued an
apology yesterday after being
arrested and charged with
drunken driving in the United
States." - ], - [ - "PC World - The one-time World
Class Product of the Year PDA
gets a much-needed upgrade." - ], - [ - "AP - Italian and Lebanese
authorities have arrested 10
suspected terrorists who
planned to blow up the Italian
Embassy in Beirut, an Italian
news agency and the Defense
Ministry said Tuesday." - ], - [ - "CORAL GABLES, Fla. -- John F.
Kerry and President Bush
clashed sharply over the war
in Iraq last night during the
first debate of the
presidential campaign season,
with the senator from
Massachusetts accusing" - ], - [ - "GONAIVES, Haiti -- While
desperately hungry flood
victims wander the streets of
Gonaives searching for help,
tons of food aid is stacking
up in a warehouse guarded by
United Nations peacekeepers." - ], - [ - "As part of its much-touted new
MSN Music offering, Microsoft
Corp. (MSFT) is testing a Web-
based radio service that
mimics nearly 1,000 local
radio stations, allowing users
to hear a version of their
favorite radio station with
far fewer interruptions." - ], - [ - "AFP - The resignation of
disgraced Fiji Vice-President
Ratu Jope Seniloli failed to
quell anger among opposition
leaders and the military over
his surprise release from
prison after serving just
three months of a four-year
jail term for his role in a
failed 2000 coup." - ], - [ - "ATHENS Larry Brown, the US
coach, leaned back against the
scorer #39;s table, searching
for support on a sinking ship.
His best player, Tim Duncan,
had just fouled out, and the
options for an American team
that" - ], - [ - "Sourav Ganguly files an appeal
against a two-match ban
imposed for time-wasting." - ], - [ - "Ziff Davis - A quick
resolution to the Mambo open-
source copyright dispute seems
unlikely now that one of the
parties has rejected an offer
for mediation." - ], - [ - "Greek police surrounded a bus
full of passengers seized by
armed hijackers along a
highway from an Athens suburb
Wednesday, police said." - ], - [ - "Spain have named an unchanged
team for the Davis Cup final
against the United States in
Seville on 3-5 December.
Carlos Moya, Juan Carlos
Ferrero, Rafael Nadal and
Tommy Robredo will take on the
US in front of 22,000 fans at
the converted Olympic stadium." - ], - [ - "BAGHDAD: A suicide car bomber
attacked a police convoy in
Baghdad yesterday as guerillas
kept pressure on Iraq #39;s
security forces despite a
bloody rout of insurgents in
Fallujah." - ], - [ - "Slot machine maker
International Game Technology
(IGT.N: Quote, Profile,
Research) on Tuesday posted
better-than-expected quarterly
earnings, as casinos bought" - ], - [ - "Fixtures and fittings from
Damien Hirst's restaurant
Pharmacy sell for 11.1, 8m
more than expected." - ], - [ - "Last Tuesday night, Harvard
knocked off rival Boston
College, which was ranked No.
1 in the country. Last night,
the Crimson knocked off
another local foe, Boston
University, 2-1, at Walter
Brown Arena, which marked the
first time since 1999 that
Harvard had beaten them both
in the same season." - ], - [ - "Venezuelan election officials
say they expect to announce
Saturday, results of a partial
audit of last Sunday #39;s
presidential recall
referendum." - ], - [ - "About 500 prospective jurors
will be in an Eagle, Colorado,
courtroom Friday, answering an
82-item questionnaire in
preparation for the Kobe
Bryant sexual assault trial." - ], - [ - "Students take note - endless
journeys to the library could
become a thing of the past
thanks to a new multimillion-
pound scheme to make classic
texts available at the click
of a mouse." - ], - [ - "Moscow - The next crew of the
International Space Station
(ISS) is to contribute to the
Russian search for a vaccine
against Aids, Russian
cosmonaut Salijan Sharipovthe
said on Thursday." - ], - [ - "Locked away in fossils is
evidence of a sudden solar
cooling. Kate Ravilious meets
the experts who say it could
explain a 3,000-year-old mass
migration - and today #39;s
global warming." - ], - [ - "By ANICK JESDANUN NEW YORK
(AP) -- Taran Rampersad didn't
complain when he failed to
find anything on his hometown
in the online encyclopedia
Wikipedia. Instead, he simply
wrote his own entry for San
Fernando, Trinidad and
Tobago..." - ], - [ - "Five years after catastrophic
floods and mudslides killed
thousands along Venezuela's
Caribbean coast, survivors in
this town still see the signs
of destruction - shattered
concrete walls and tall weeds
growing atop streets covered
in dried mud." - ], - [ - "How do you top a battle
between Marines and an alien
religious cult fighting to the
death on a giant corona in
outer space? The next logical
step is to take that battle to
Earth, which is exactly what
Microsoft" - ], - [ - "Sony Ericsson Mobile
Communications Ltd., the
mobile-phone venture owned by
Sony Corp. and Ericsson AB,
said third-quarter profit rose
45 percent on camera phone
demand and forecast this
quarter will be its strongest." - ], - [ - "NEW YORK, September 17
(newratings.com) - Alcatel
(ALA.NYS) has expanded its
operations and presence in the
core North American
telecommunication market with
two separate acquisitions for
about \\$277 million." - ], - [ - "Four U.S. agencies yesterday
announced a coordinated attack
to stem the global trade in
counterfeit merchandise and
pirated music and movies, an
underground industry that law-
enforcement officials estimate
to be worth \\$500 billion each
year." - ], - [ - "BEIJING The NBA has reached
booming, basketball-crazy
China _ but the league doesn
#39;t expect to make any money
soon. The NBA flew more than
100 people halfway around the
world for its first games in
China, featuring" - ], - [ - "The UN nuclear watchdog
confirmed Monday that nearly
400 tons of powerful
explosives that could be used
in conventional or nuclear
missiles disappeared from an
unguarded military
installation in Iraq." - ], - [ - " NEW YORK (Reuters) - Curt
Schilling pitched 6 2/3
innings and Manny Ramirez hit
a three-run homer in a seven-
run fourth frame to lead the
Boston Red Sox to a 9-3 win
over the host Anaheim Angels
in their American League
Divisional Series opener
Tuesday." - ], - [ - "AP - The game between the
Minnesota Twins and the New
York Yankees on Tuesday night
was postponed by rain." - ], - [ - "Oil giant Shell swept aside
nearly 100 years of history
today when it unveiled plans
to merge its UK and Dutch
parent companies. Shell said
scrapping its twin-board
structure" - ], - [ - "Caesars Entertainment Inc. on
Thursday posted a rise in
third-quarter profit as Las
Vegas hotels filled up and
Atlantic City properties
squeaked out a profit that was
unexpected by the company." - ], - [ - "Half of all U.S. Secret
Service agents are dedicated
to protecting President
Washington #151;and all the
other Presidents on U.S.
currency #151;from
counterfeiters." - ], - [ - "One of India #39;s leading
telecommunications providers
will use Cisco Systems #39;
gear to build its new
Ethernet-based broadband
network." - ], - [ - "Militants in Iraq have killed
the second of two US civilians
they were holding hostage,
according to a statement on an
Islamist website." - ], - [ - "PARIS The verdict is in: The
world #39;s greatest race car
driver, the champion of
champions - all disciplines
combined - is Heikki
Kovalainen." - ], - [ - "Pakistan won the toss and
unsurprisingly chose to bowl
first as they and West Indies
did battle at the Rose Bowl
today for a place in the ICC
Champions Trophy final against
hosts England." - ], - [ - "Shares of Beacon Roofing
Suppler Inc. shot up as much
as 26 percent in its trading
debut Thursday, edging out
bank holding company Valley
Bancorp as the biggest gainer
among a handful of new stocks
that went public this week." - ], - [ - "More than 4,000 American and
Iraqi soldiers mounted a
military assault on the
insurgent-held city of
Samarra." - ], - [ - "Maxime Faget conceived and
proposed the development of
the one-man spacecraft used in
Project Mercury, which put the
first American astronauts into
suborbital flight, then
orbital flight" - ], - [ - "The first weekend of holiday
shopping went from red-hot to
dead white, as a storm that
delivered freezing, snowy
weather across Colorado kept
consumers at home." - ], - [ - " MEXICO CITY (Reuters) -
Mexican President Vicente Fox
said on Monday he hoped
President Bush's re-election
meant a bilateral accord on
migration would be reached
before his own term runs out
at the end of 2006." - ], - [ - " LONDON (Reuters) - The dollar
fell within half a cent of
last week's record low against
the euro on Thursday after
capital inflows data added to
worries the United States may
struggle to fund its current
account deficit." - ], - [ - " BRUSSELS (Reuters) - French
President Jacques Chirac said
on Friday he had not refused
to meet Iraqi interim Prime
Minister Iyad Allawi after
reports he was snubbing the
Iraqi leader by leaving an EU
meeting in Brussels early." - ], - [ - "China has pledged to invest
\\$20 billion in Argentina in
the next 10 years, La Nacion
reported Wednesday. The
announcement came during the
first day of a two-day visit" - ], - [ - "NEW YORK - Former President
Bill Clinton was in good
spirits Saturday, walking
around his hospital room in
street clothes and buoyed by
thousands of get-well messages
as he awaited heart bypass
surgery early this coming
week, people close to the
family said. Clinton was
expected to undergo surgery as
early as Monday but probably
Tuesday, said Democratic Party
Chairman Terry McAuliffe, who
said the former president was
\"upbeat\" when he spoke to him
by phone Friday..." - ], - [ - "Toyota Motor Corp., the world
#39;s biggest carmaker by
value, will invest 3.8 billion
yuan (\\$461 million) with its
partner Guangzhou Automobile
Group to boost manufacturing
capacity in" - ], - [ - "Poland will cut its troops in
Iraq early next year and won
#39;t stay in the country
quot;an hour longer quot; than
needed, the country #39;s
prime minister said Friday." - ], - [ - "AP - Boston Red Sox center
fielder Johnny Damon is having
a recurrence of migraine
headaches that first bothered
him after a collision in last
year's playoffs." - ], - [ - "The patch fixes a flaw in the
e-mail server software that
could be used to get access to
in-boxes and information." - ], - [ - "Felix Cardenas of Colombia won
the 17th stage of the Spanish
Vuelta cycling race Wednesday,
while defending champion
Roberto Heras held onto the
overall leader #39;s jersey
for the sixth day in a row." - ], - [ - "AP - Being the biggest dog may
pay off at feeding time, but
species that grow too large
may be more vulnerable to
extinction, new research
suggests." - ], - [ - "A car bomb exploded at a US
military convoy in the
northern Iraqi city of Mosul,
causing several casualties,
the army and Iraqi officials
said." - ], - [ - "Newest Efficeon processor also
offers higher frequency using
less power." - ], - [ - "BAE Systems has launched a
search for a senior American
businessman to become a non-
executive director. The high-
profile appointment is
designed to strengthen the
board at a time when the
defence giant is" - ], - [ - "AP - In what it calls a first
in international air travel,
Finnair says it will let its
frequent fliers check in using
text messages on mobile
phones." - ], - [ - "Computer Associates
International is expected to
announce that its new chief
executive will be John
Swainson, an I.B.M. executive
with strong technical and
sales credentials." - ], - [ - "Established star Landon
Donovan and rising sensation
Eddie Johnson carried the
United States into the
regional qualifying finals for
the 2006 World Cup in emphatic
fashion Wednesday night." - ], - [ - "STOCKHOLM (Dow
Jones)--Expectations for
Telefon AB LM Ericsson #39;s
(ERICY) third-quarter
performance imply that while
sales of mobile telephony
equipment are expected to have
dipped, the company" - ], - [ - "ATHENS, Greece - Michael
Phelps took care of qualifying
for the Olympic 200-meter
freestyle semifinals Sunday,
and then found out he had been
added to the American team for
the evening's 400 freestyle
relay final. Phelps' rivals
Ian Thorpe and Pieter van den
Hoogenband and teammate Klete
Keller were faster than the
teenager in the 200 free
preliminaries..." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
intends to present a timetable
for withdrawal from Gaza to
lawmakers from his Likud Party
Tuesday despite a mutiny in
the right-wing bloc over the
plan." - ], - [ - "Search Engine for Programming
Code\\\\An article at Newsforge
pointed me to Koders (
http://www.koders.com ) a
search engine for finding
programming code. Nifty.\\\\The
front page allows you to
specify keywords, sixteen
languages (from ASP to VB.NET)
and sixteen licenses (from AFL
to ZPL -- fortunately there's
an information page to ..." - ], - [ - " #147;Apple once again was the
star of the show at the annual
MacUser awards, #148; reports
MacUser, #147;taking away six
Maxine statues including
Product of the Year for the
iTunes Music Store. #148;
Other Apple award winners:
Power Mac G5, AirPort Express,
20-inch Apple Cinema Display,
GarageBand and DVD Studio Pro
3. Nov 22" - ], - [ - " KABUL (Reuters) - Three U.N.
workers held by militants in
Afghanistan were in their
third week of captivity on
Friday after calls from both
sides for the crisis to be
resolved ahead of this
weekend's Muslim festival of
Eid al-Fitr." - ], - [ - "AP - A sweeping wildlife
preserve in southwestern
Arizona is among the nation's
10 most endangered refuges,
due in large part to illegal
drug and immigrant traffic and
Border Patrol operations, a
conservation group said
Friday." - ], - [ - "ROME, Oct 29 (AFP) - French
President Jacques Chirac urged
the head of the incoming
European Commission Friday to
take quot;the appropriate
decisions quot; to resolve a
row over his EU executive team
which has left the EU in
limbo." - ], - [ - "The Anglo-Dutch oil giant
Shell today sought to draw a
line under its reserves
scandal by announcing plans to
spend \\$15bn (8.4bn) a year to
replenish reserves and develop
production in its oil and gas
business." - ], - [ - "SEPTEMBER 20, 2004
(COMPUTERWORLD) - At
PeopleSoft Inc. #39;s Connect
2004 conference in San
Francisco this week, the
software vendor is expected to
face questions from users
about its ability to fend off
Oracle Corp." - ], - [ - "Russia's parliament will
launch an inquiry into a
school siege that killed over
300 hostages, President
Vladimir Putin said on Friday,
but analysts doubt it will
satisfy those who blame the
carnage on security services." - ], - [ - "Tony Blair talks to business
leaders about new proposals
for a major shake-up of the
English exam system." - ], - [ - "KUALA LUMPUR, Malaysia A
Malaysian woman has claimed a
new world record after living
with over six-thousand
scorpions for 36 days
straight." - ], - [ - "PBS's Charlie Rose quizzes Sun
co-founder Bill Joy,
Monster.com chief Jeff Taylor,
and venture capitalist John
Doerr." - ], - [ - "Circulation declined at most
major US newspapers in the
last half year, the latest
blow for an industry already
rocked by a scandal involving
circulation misstatements that
has undermined the confidence
of investors and advertisers." - ], - [ - "Skype Technologies is teaming
with Siemens to offer cordless
phone users the ability to
make Internet telephony calls,
in addition to traditional
calls, from their handsets." - ], - [ - "AP - After years of
resistance, the U.S. trucking
industry says it will not try
to impede or delay a new
federal rule aimed at cutting
diesel pollution." - ], - [ - "INDIANAPOLIS - ATA Airlines
has accepted a \\$117 million
offer from Southwest Airlines
that would forge close ties
between two of the largest US
discount carriers." - ], - [ - "could take drastic steps if
the talks did not proceed as
Tehran wants. Mehr news agency
quoted him as saying on
Wednesday. that could be used" - ], - [ - "Wizards coach Eddie Jordan
says the team is making a
statement that immaturity will
not be tolerated by suspending
Kwame Brown one game for not
taking part in a team huddle
during a loss to Denver." - ], - [ - "EVERETT Fire investigators
are still trying to determine
what caused a two-alarm that
destroyed a portion of a South
Everett shopping center this
morning." - ], - [ - "Umesh Patel, a 36-year old
software engineer from
California, debated until the
last minute." - ], - [ - "Sure, the PeopleSoft board
told shareholders to just say
no. This battle will go down
to the wire, and even
afterward Ellison could
prevail." - ], - [ - "Investors cheered by falling
oil prices and an improving
job picture sent stocks higher
Tuesday, hoping that the news
signals a renewal of economic
strength and a fall rally in
stocks." - ], - [ - "AP - Andy Roddick has yet to
face a challenge in his U.S.
Open title defense. He beat
No. 18 Tommy Robredo of Spain
6-3, 6-2, 6-4 Tuesday night to
move into the quarterfinals
without having lost a set." - ], - [ - "Now that hell froze over in
Boston, New England braces for
its rarest season -- a winter
of content. Red Sox fans are
adrift from the familiar
torture of the past." - ], - [ - " CHICAGO (Reuters) - Wal-Mart
Stores Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=WMT.N target
=/stocks/quickinfo/fullquote\"&
gt;WMT.N</A>, the
world's largest retailer, said
on Saturday it still
anticipates September U.S.
sales to be up 2 percent to 4
percent at stores open at
least a year." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei stock average opened
down 0.15 percent on
Wednesday as investors took a
breather from the market's
recent rises and sold shares
of gainers such as Sharp
Corp." - ], - [ - "AP - From now until the start
of winter, male tarantulas are
roaming around, searching for
female mates, an ideal time to
find out where the spiders
flourish in Arkansas." - ], - [ - "Israeli authorities have
launched an investigation into
death threats against Israeli
Prime Minister Ariel Sharon
and other officials supporting
his disengagement plan from
Gaza and parts of the West
Bank, Jerusalem police said
Tuesday." - ], - [ - "NewsFactor - While a U.S.
District Court continues to
weigh the legality of
\\Oracle's (Nasdaq: ORCL)
attempted takeover of
\\PeopleSoft (Nasdaq: PSFT),
Oracle has taken the necessary
steps to ensure the offer does
not die on the vine with
PeopleSoft's shareholders." - ], - [ - "NEW YORK : World oil prices
fell, capping a drop of more
than 14 percent in a two-and-
a-half-week slide triggered by
a perception of growing US
crude oil inventories." - ], - [ - "SUFFOLK -- Virginia Tech
scientists are preparing to
protect the state #39;s
largest crop from a disease
with strong potential to do
damage." - ], - [ - "It was a carefully scripted
moment when Russian President
Vladimir Putin began quoting
Taras Shevchenko, this country
#39;s 19th-century bard,
during a live television" - ], - [ - "China says it welcomes Russia
#39;s ratification of the
Kyoto Protocol that aims to
stem global warming by
reducing greenhouse-gas
emissions." - ], - [ - "Analysts said the smartphone
enhancements hold the
potential to bring PalmSource
into an expanding market that
still has room despite early
inroads by Symbian and
Microsoft." - ], - [ - "The Metropolitan
Transportation Authority is
proposing a tax increase to
raise \\$900 million a year to
help pay for a five-year
rebuilding program." - ], - [ - "AFP - The Canadian armed
forces chief of staff on was
elected to take over as head
of NATO's Military Committee,
the alliance's highest
military authority, military
and diplomatic sources said." - ], - [ - "AFP - Two proposed resolutions
condemning widespread rights
abuses in Sudan and Zimbabwe
failed to pass a UN committee,
mired in debate between
African and western nations." - ], - [ - " NEW YORK (Reuters) -
Citigroup Inc. <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=C.N targe
t=/stocks/quickinfo/fullquote\"
>C.N</A> the world's
largest financial services
company, said on Tuesday it
will acquire First American
Bank in the quickly-growing
Texas market." - ], - [ - " SINGAPORE (Reuters) - U.S.
oil prices hovered just below
\\$50 a barrel on Tuesday,
holding recent gains on a rash
of crude supply outages and
fears over thin heating oil
tanks." - ], - [ - "THE South Africans have called
the Wallabies scrum cheats as
a fresh round of verbal
warfare opened in the Republic
last night." - ], - [ - "The return of noted reformer
Nabil Amr to Palestinian
politics comes at a crucial
juncture." - ], - [ - "An overwhelming majority of
NHL players who expressed
their opinion in a poll said
they would not support a
salary cap even if it meant
saving a season that was
supposed to have started Oct.
13." - ], - [ - "Tony Eury Sr. oversees Dale
Earnhardt Jr. on the
racetrack, but Sunday he
extended his domain to Victory
Lane. Earnhardt was unbuckling
to crawl out of the No." - ], - [ - "AP - After two debates, voters
have seen President Bush look
peevish and heard him pass the
buck. They've watched Sen.
John Kerry deny he's a flip-
flopper and then argue that
Saddam Hussein was a threat
#151; and wasn't. It's no
wonder so few minds have
changed." - ], - [ - "(Sports Network) - The New
York Yankees try to move one
step closer to a division
title when they conclude their
critical series with the
Boston Red Sox at Fenway Park." - ], - [ - "US retail sales fell 0.3 in
August as rising energy costs
and bad weather persuaded
shoppers to reduce their
spending." - ], - [ - "The United States says the
Lebanese parliament #39;s
decision Friday to extend the
term of pro-Syrian President
Emile Lahoud made a
quot;crude mockery of
democratic principles." - ], - [ - "Kurt Busch claimed a stake in
the points lead in the NASCAR
Chase for the Nextel Cup
yesterday, winning the
Sylvania 300 at New Hampshire
International Speedway." - ], - [ - "TOKYO (AFP) - Japan #39;s
Fujitsu and Cisco Systems of
the US said they have agreed
to form a strategic alliance
focusing on routers and
switches that will enable
businesses to build advanced
Internet Protocol (IP)
networks." - ], - [ - "GEORGETOWN, Del., Oct. 28 --
Plaintiffs in a shareholder
lawsuit over former Walt
Disney Co. president Michael
Ovitz's \\$140 million
severance package attempted
Thursday to portray Ovitz as a
dishonest bumbler who botched
the hiring of a major
television executive and
pushed the release of a movie
that angered the Chinese
government, damaging Disney's
business prospects in the
country." - ], - [ - "US stocks gained ground in
early trading Thursday after
tame inflation reports and
better than expected jobless
news. Oil prices held steady
as Hurricane Ivan battered the
Gulf coast, where oil
operations have halted." - ], - [ - "It #39;s a new internet
browser. The first full
version, Firefox 1.0, was
launched earlier this month.
In the sense it #39;s a
browser, yes, but the
differences are larger than
the similarities." - ], - [ - "LOUDON, NH - As this
newfangled stretch drive for
the Nextel Cup championship
ensues, Jeff Gordon has to be
considered the favorite for a
fifth title." - ], - [ - "PepsiCo. Inc., the world #39;s
No. 2 soft- drink maker, plans
to buy General Mills Inc.
#39;s stake in their European
joint venture for \\$750
million in cash, giving it
complete ownership of Europe
#39;s largest snack-food
company." - ], - [ - "Microsoft will accelerate SP2
distribution to meet goal of
100 million downloads in two
months." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks opened little changed
on Friday, after third-
quarter gross domestic product
data showed the U.S. economy
grew at a slower-than-expected
pace." - ], - [ - "International Business
Machines Corp., taken to court
by workers over changes it
made to its traditional
pension plan, has decided to
stop offering any such plan to
new employees." - ], - [ - "NEW YORK - With four of its
executives pleading guilty to
price-fixing charges today,
Infineon will have a hard time
arguing that it didn #39;t fix
prices in its ongoing
litigation with Rambus." - ], - [ - "By nick giongco. YOU KNOW you
have reached the status of a
boxing star when ring
announcer extraordinaire
Michael Buffer calls out your
name in his trademark booming
voice during a high-profile
event like yesterday" - ], - [ - "Canadian Press - OTTAWA (CP) -
The prime minister says he's
been assured by President
George W. Bush that the U.S.
missile defence plan does not
necessarily involve the
weaponization of space." - ], - [ - "AP - Even with a big lead,
Eric Gagne wanted to pitch in
front of his hometown fans one
last time." - ], - [ - " NEW YORK (Reuters) - Limited
Brands Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=LTD.N target
=/stocks/quickinfo/fullquote\"&
gt;LTD.N</A> on
Thursday reported higher
quarterly operating profit as
cost controls and strong
lingerie sales offset poor
results at the retailer's
Express apparel stores." - ], - [ - "General Motors and
DaimlerChrysler are
collaborating on development
of fuel- saving hybrid engines
in hopes of cashing in on an
expanding market dominated by
hybrid leaders Toyota and
Honda." - ], - [ - "ATHENS, Greece -- Larry Brown
was despondent, the head of
the US selection committee was
defensive and an irritated
Allen Iverson was hanging up
on callers who asked what went
wrong." - ], - [ - "Fifty-six miners are dead and
another 92 are still stranded
underground after a gas blast
at the Daping coal mine in
Central China #39;s Henan
Province, safety officials
said Thursday." - ], - [ - "BEIRUT, Lebanon - Three
Lebanese men and their Iraqi
driver have been kidnapped in
Iraq, the Lebanese Foreign
Ministry said Sunday, as
Iraq's prime minister said his
government was working for the
release of two Americans and a
Briton also being held
hostage. Gunmen snatched
the three Lebanese, who worked
for a travel agency with a
branch in Baghdad, as they
drove on the highway between
the capital and Fallujah on
Friday night, a ministry
official said..." - ], - [ - "PORTLAND, Ore. - It's been
almost a year since singer-
songwriter Elliott Smith
committed suicide, and fans
and friends will be looking
for answers as the posthumous
\"From a Basement on the Hill\"
is released..." - ], - [ - "AP - Courtney Brown refuses to
surrender to injuries. He's
already planning another
comeback." - ], - [ - " GAZA (Reuters) - Israel
expanded its military
offensive in northern Gaza,
launching two air strikes
early on Monday that killed
at least three Palestinians
and wounded two, including a
senior Hamas leader, witnesses
and medics said." - ], - [ - " TOKYO (Reuters) - Nintendo
Co. Ltd. raised its 2004
shipment target for its DS
handheld video game device by
40 percent to 2.8 million
units on Thursday after many
stores in Japan and the
United States sold out in the
first week of sales." - ], - [ - "It took an off-the-cuff
reference to a serial
murderer/cannibal to punctuate
the Robby Gordon storyline.
Gordon has been vilified by
his peers and put on probation" - ], - [ - "By BOBBY ROSS JR. Associated
Press Writer. play the Atlanta
Hawks. They will be treated to
free food and drink and have.
their pictures taken with
Mavericks players, dancers and
team officials." - ], - [ - "ARSENAL #39;S Brazilian World
Cup winning midfielder
Gilberto Silva is set to be
out for at least a month with
a back injury, the Premiership
leaders said." - ], - [ - "INDIANAPOLIS -- With a package
of academic reforms in place,
the NCAA #39;s next crusade
will address what its
president calls a dangerous
drift toward professionalism
and sports entertainment." - ], - [ - "Autodesk this week unwrapped
an updated version of its
hosted project collaboration
service targeted at the
construction and manufacturing
industries. Autodesk Buzzsaw
lets multiple, dispersed
project participants --
including building owners,
developers, architects,
construction teams, and
facility managers -- share and
manage data throughout the
life of a project, according
to Autodesk officials." - ], - [ - "AP - It was difficult for
Southern California's Pete
Carroll and Oklahoma's Bob
Stoops to keep from repeating
each other when the two
coaches met Thursday." - ], - [ - "Reuters - Sudan's government
resumed\\talks with rebels in
the oil-producing south on
Thursday while\\the United
Nations set up a panel to
investigate charges
of\\genocide in the west of
Africa's largest country." - ], - [ - "Andy Roddick, along with
Olympic silver medalist Mardy
Fish and the doubles pair of
twins Bob and Mike Bryan will
make up the US team to compete
with Belarus in the Davis Cup,
reported CRIENGLISH." - ], - [ - "NEW YORK - Optimism that the
embattled technology sector
was due for a recovery sent
stocks modestly higher Monday
despite a new revenue warning
from semiconductor company
Broadcom Inc. While
Broadcom, which makes chips
for television set-top boxes
and other electronics, said
high inventories resulted in
delayed shipments, investors
were encouraged as it said
future quarters looked
brighter..." - ], - [ - "A report indicates that many
giants of the industry have
been able to capture lasting
feelings of customer loyalty." - ], - [ - "It is the news that Internet
users do not want to hear: the
worldwide web is in danger of
collapsing around us. Patrick
Gelsinger, the chief
technology officer for
computer chip maker Intel,
told a conference" - ], - [ - " WASHINGTON (Reuters) - U.S.
employers hired just 96,000
workers in September, the
government said on Friday in a
weak jobs snapshot, the final
one ahead of presidential
elections that also fueled
speculation about a pause in
interest-rate rises." - ], - [ - "Cisco Systems CEO John
Chambers said today that his
company plans to offer twice
as many new products this year
as ever before." - ], - [ - "While the world #39;s best
athletes fight the noble
Olympic battle in stadiums and
pools, their fans storm the
streets of Athens, turning the
Greek capital into a huge
international party every
night." - ], - [ - "Carlos Barrios Orta squeezed
himself into his rubber diving
suit, pulled on an 18-pound
helmet that made him look like
an astronaut, then lowered
himself into the sewer. He
disappeared into the filthy
water, which looked like some
cauldron of rancid beef stew,
until the only sign of him was
air bubbles breaking the
surface." - ], - [ - "The mutilated body of a woman
of about sixty years,
amputated of arms and legs and
with the sliced throat, has
been discovered this morning
in a street of the south of
Faluya by the Marines,
according to a statement by
AFP photographer." - ], - [ - "Every day, somewhere in the
universe, there #39;s an
explosion that puts the power
of the Sun in the shade. Steve
Connor investigates the riddle
of gamma-ray bursts." - ], - [ - "Ten months after NASA #39;s
twin rovers landed on Mars,
scientists reported this week
that both robotic vehicles are
still navigating their rock-
studded landscapes with all
instruments operating" - ], - [ - "EVERTON showed they would not
be bullied into selling Wayne
Rooney last night by rejecting
a 23.5million bid from
Newcastle - as Manchester
United gamble on Goodison
#39;s resolve to keep the
striker." - ], - [ - "SAN FRANCISCO (CBS.MW) -- The
magazine known for evaluating
cars and electronics is
setting its sights on finding
the best value and quality of
prescription drugs on the
market." - ], - [ - "After months of legal
wrangling, the case of
<em>People v. Kobe Bean
Bryant</em> commences on
Friday in Eagle, Colo., with
testimony set to begin next
month." - ], - [ - "(SH) - In Afghanistan, Hamid
Karzai defeated a raft of
candidates to win his historic
election. In Iraq, more than
200 political parties have
registered for next month
#39;s elections." - ], - [ - "Lyon coach Paul le Guen has
admitted his side would be
happy with a draw at Old
Trafford on Tuesday night. The
three-times French champions
have assured themselves of
qualification for the
Champions League" - ], - [ - "British scientists say they
have found a new, greener way
to power cars and homes using
sunflower oil, a commodity
more commonly used for cooking
fries." - ], - [ - "A mouse, a house, and your
tax-planning spouse all factor
huge in the week of earnings
that lies ahead." - ], - [ - "The United States #39; top
computer-security official has
resigned after a little more
than a year on the job, the US
Department of Homeland
Security said on Friday." - ], - [ - "Reuters - Barry Bonds failed
to collect a hit in\\his bid to
join the 700-homer club, but
he did score a run to\\help the
San Francisco Giants edge the
host Milwaukee Brewers\\3-2 in
National League action on
Tuesday." - ], - [ - " SAN FRANCISCO (Reuters) -
Intel Corp. <A HREF=\"http:/
/www.reuters.co.uk/financeQuot
eLookup.jhtml?ticker=INTC.O
qtype=sym infotype=info
qcat=news\">INTC.O</A>
on Thursday outlined its
vision of the Internet of the
future, one in which millions
of computer servers would
analyze and direct network
traffic to make the Web safer
and more efficient." - ], - [ - "Margaret Hassan, who works for
charity Care International,
was taken hostage while on her
way to work in Baghdad. Here
are the main events since her
kidnapping." - ], - [ - "Check out new gadgets as
holiday season approaches." - ], - [ - "DALLAS (CBS.MW) -- Royal
Dutch/Shell Group will pay a
\\$120 million penalty to
settle a Securities and
Exchange Commission
investigation of its
overstatement of nearly 4.5
billion barrels of proven
reserves, the federal agency
said Tuesday." - ], - [ - "After waiting an entire summer
for the snow to fall and
Beaver Creek to finally open,
skiers from around the planet
are coming to check out the
Birds of Prey World Cup action
Dec. 1 - 5. Although everyones" - ], - [ - "TOKYO, Sep 08, 2004 (AFX-UK
via COMTEX) -- Sony Corp will
launch its popular projection
televisions with large liquid
crystal display (LCD) screens
in China early next year, a
company spokeswoman said." - ], - [ - "I confess that I am a complete
ignoramus when it comes to
women #39;s beach volleyball.
In fact, I know only one thing
about the sport - that it is
the supreme aesthetic
experience available on planet
Earth." - ], - [ - "Manchester United Plc may
offer US billionaire Malcolm
Glazer a seat on its board if
he agrees to drop a takeover
bid for a year, the Observer
said, citing an unidentified
person in the soccer industry." - ], - [ - "PHOENIX America West Airlines
has backed away from a
potential bidding war for
bankrupt ATA Airlines, paving
the way for AirTran to take
over ATA operations." - ], - [ - "US stock-index futures
declined. Dow Jones Industrial
Average shares including
General Electric Co. slipped
in Europe. Citigroup Inc." - ], - [ - "For a guy who spent most of
his first four professional
seasons on the disabled list,
Houston Astros reliever Brad
Lidge has developed into quite
the ironman these past two
days." - ], - [ - "AFP - Outgoing EU competition
commissioner Mario Monti wants
to reach a decision on
Oracle's proposed takeover of
business software rival
PeopleSoft by the end of next
month, an official said." - ], - [ - "Former Bengal Corey Dillon
found his stride Sunday in his
second game for the New
England Patriots. Dillon
gained 158 yards on 32 carries
as the Patriots beat the
Arizona Cardinals, 23-12, for
their 17th victory in a row." - ], - [ - "If legend is to be believed,
the end of a victorious war
was behind Pheidippides #39;
trek from Marathon to Athens
2,500 years ago." - ], - [ - " WASHINGTON (Reuters) -
Satellite companies would be
able to retransmit
broadcasters' television
signals for another five
years but would have to offer
those signals on a single
dish, under legislation
approved by Congress on
Saturday." - ], - [ - " BEIJING (Reuters) - Wimbledon
champion Maria Sharapova
demolished fellow Russian
Tatiana Panova 6-1, 6-1 to
advance to the quarter-finals
of the China Open on
Wednesday." - ], - [ - "Sven Jaschan, who may face
five years in prison for
spreading the Netsky and
Sasser worms, is now working
in IT security. Photo: AFP." - ], - [ - "Padres general manager Kevin
Towers called Expos general
manager Omar Minaya on
Thursday afternoon and told
him he needed a shortstop
because Khalil Greene had
broken his" - ], - [ - "Motorsport.com. Nine of the
ten Formula One teams have
united to propose cost-cutting
measures for the future, with
the notable exception of
Ferrari." - ], - [ - "Reuters - The United States
modified its\\call for U.N.
sanctions against Sudan on
Tuesday but still kept\\up the
threat of punitive measures if
Khartoum did not
stop\\atrocities in its Darfur
region." - ], - [ - "Human rights and environmental
activists have hailed the
award of the 2004 Nobel Peace
Prize to Wangari Maathai of
Kenya as fitting recognition
of the growing role of civil
society" - ], - [ - "Federal Reserve Chairman Alan
Greenspan has done it again.
For at least the fourth time
this year, he has touched the
electrified third rail of
American politics - Social
Security." - ], - [ - "An Al Qaeda-linked militant
group beheaded an American
hostage in Iraq and threatened
last night to kill another two
Westerners in 24 hours unless
women prisoners were freed
from Iraqi jails." - ], - [ - "TechWeb - An Indian Institute
of Technology professor--and
open-source evangelist--
discusses the role of Linux
and open source in India." - ], - [ - "Here are some of the latest
health and medical news
developments, compiled by
editors of HealthDay: -----
Contaminated Fish in Many U.S.
Lakes and Rivers Fish
that may be contaminated with
dioxin, mercury, PCBs and
pesticides are swimming in
more than one-third of the
United States' lakes and
nearly one-quarter of its
rivers, according to a list of
advisories released by the
Environmental Protection
Agency..." - ], - [ - "Argentina, Denmark, Greece,
Japan and Tanzania on Friday
won coveted two-year terms on
the UN Security Council at a
time when pressure is mounting
to expand the powerful
15-nation body." - ], - [ - "Description: Scientists say
the arthritis drug Bextra may
pose increased risk of
cardiovascular troubles.
Bextra is related to Vioxx,
which was pulled off the
market in September for the
same reason." - ], - [ - "Steve Francis and Shaquille O
#39;Neal enjoyed big debuts
with their new teams. Kobe
Bryant found out he can #39;t
carry the Lakers all by
himself." - ], - [ - "The trial of a lawsuit by Walt
Disney Co. shareholders who
accuse the board of directors
of rubberstamping a deal to
hire Michael Ovitz" - ], - [ - "Australia, by winning the
third Test at Nagpur on
Friday, also won the four-
match series 2-0 with one
match to go. Australia had
last won a Test series in
India way back in December
1969 when Bill Lawry #39;s
team beat Nawab Pataudi #39;s
Indian team 3-1." - ], - [ - "LOS ANGELES Grocery giant
Albertsons says it has
purchased Bristol Farms, which
operates eleven upscale stores
in Southern California." - ], - [ - "ISLAMABAD: Pakistan and India
agreed on Friday to re-open
the Khokhropar-Munabao railway
link, which was severed nearly
40 years ago." - ], - [ - "Final Score: Connecticut 61,
New York 51 New York, NY
(Sports Network) - Nykesha
Sales scored 15 points to lead
Connecticut to a 61-51 win
over New York in Game 1 of
their best-of-three Eastern
Conference Finals series at
Madison Square Garden." - ], - [ - "NEW YORK - Stocks moved higher
Friday as a stronger than
expected retail sales report
showed that higher oil prices
aren't scaring consumers away
from spending. Federal Reserve
Chairman Alan Greenspan's
positive comments on oil
prices also encouraged
investors..." - ], - [ - "The figures from a survey
released today are likely to
throw more people into the
ranks of the uninsured,
analysts said." - ], - [ - "Bill Gates is giving his big
speech right now at Microsofts
big Digital Entertainment
Anywhere event in Los Angeles.
Well have a proper report for
you soon, but in the meantime
we figured wed" - ], - [ - "Spinal and non-spinal
fractures are reduced by
almost a third in women age 80
or older who take a drug
called strontium ranelate,
European investigators
announced" - ], - [ - "JB Oxford Holdings Inc., a
Beverly Hills-based discount
brokerage firm, was sued by
the Securities and Exchange
Commission for allegedly
allowing thousands of improper
trades in more than 600 mutual
funds." - ], - [ - "BERLIN: Apple Computer Inc is
planning the next wave of
expansion for its popular
iTunes online music store with
a multi-country European
launch in October, the
services chief architect said
on Wednesday." - ], - [ - "Charlotte, NC -- LeBron James
poured in a game-high 19
points and Jeff McInnis scored
18 as the Cleveland Cavaliers
routed the Charlotte Bobcats,
106-89, at the Charlotte
Coliseum." - ], - [ - "Goldman Sachs reported strong
fourth quarter and full year
earnings of \\$1.19bn, up 36
per cent on the previous
quarter. Full year profit rose
52 per cent from the previous
year to \\$4.55bn." - ], - [ - "Saddam Hussein lives in an
air-conditioned 10-by-13 foot
cell on the grounds of one of
his former palaces, tending
plants and proclaiming himself
Iraq's lawful ruler." - ], - [ - "Lehmann, who was at fault in
two matches in the tournament
last season, was blundering
again with the German set to
take the rap for both Greek
goals." - ], - [ - "Calls to 13 other countries
will be blocked to thwart
auto-dialer software." - ], - [ - "AP - Brazilian U.N.
peacekeepers will remain in
Haiti until presidential
elections are held in that
Caribbean nation sometime next
year, President Luiz Inacio
Lula da Silva said Monday." - ], - [ - "com September 27, 2004, 5:00
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." - ], - [ - "Jeju Island, South Korea
(Sports Network) - Grace Park
and Carin Koch posted matching
rounds of six-under-par 66 on
Friday to share the lead after
the first round of the CJ Nine
Bridges Classic." - ], - [ - "SPACE.com - The Zero Gravity
Corporation \\ has been given
the thumbs up by the Federal
Aviation Administration (FAA)
to \\ conduct quot;weightless
flights quot; for the general
public, providing the \\
sensation of floating in
space." - ], - [ - "A 32-year-old woman in Belgium
has become the first woman
ever to give birth after
having ovarian tissue removed,
frozen and then implanted back
in her body." - ], - [ - "Argosy Gaming (AGY:NYSE - news
- research) jumped in early
trading Thursday, after the
company agreed to be acquired
by Penn National Gaming
(PENN:Nasdaq - news -
research) in a \\$1." - ], - [ - "No sweat, says the new CEO,
who's imported from IBM. He
also knows this will be the
challenge of a lifetime." - ], - [ - "Iraq is quot;working 24 hours
a day to ... stop the
terrorists, quot; interim
Iraqi Prime Minister Ayad
Allawi said Monday. Iraqis are
pushing ahead with reforms and
improvements, Allawi told" - ], - [ - "AP - Their discoveries may be
hard for many to comprehend,
but this year's Nobel Prize
winners still have to explain
what they did and how they did
it." - ], - [ - "The DuPont Co. has agreed to
pay up to \\$340 million to
settle a lawsuit that it
contaminated water supplies in
West Virginia and Ohio with a
chemical used to make Teflon,
one of its best-known brands." - ], - [ - "Benfica and Real Madrid set
the standard for soccer
success in Europe in the late
1950s and early #39;60s. The
clubs have evolved much
differently, but both have
struggled" - ], - [ - "AP - Musicians, composers and
authors were among the more
than two dozen people
Wednesday honored with
National Medal of Arts and
National Humanities awards at
the White House." - ], - [ - "More than 15 million homes in
the UK will be able to get on-
demand movies by 2008, say
analysts." - ], - [ - "Wynton Marsalis, the trumpet-
playing star and artistic
director of Jazz at Lincoln
Center, has become an entity
above and around the daily
jazz world." - ], - [ - "A well-researched study by the
National Academy of Sciences
makes a persuasive case for
refurbishing the Hubble Space
Telescope. NASA, which is
reluctant to take this
mission, should rethink its
position." - ], - [ - "BUENOS AIRES: Pakistan has
ratified the Kyoto Protocol on
Climatic Change, Environment
Minister Malik Khan said on
Thursday. He said that
Islamabad has notified UN
authorities of ratification,
which formally comes into
effect in February 2005." - ], - [ - "Reuters - Jeffrey Greenberg,
chairman and chief\\executive
of embattled insurance broker
Marsh McLennan Cos.\\, is
expected to step down within
hours, a newspaper\\reported on
Friday, citing people close to
the discussions." - ], - [ - "Staff Sgt. Johnny Horne, Jr.,
and Staff Sgt. Cardenas Alban
have been charged with murder
in the death of an Iraqi, the
1st Cavalry Division announced
Monday." - ], - [ - "LONDON -- In a deal that
appears to buck the growing
trend among governments to
adopt open-source
alternatives, the U.K.'s
Office of Government Commerce
(OGC) is negotiating a renewal
of a three-year agreement with
Microsoft Corp." - ], - [ - "WASHINGTON - A Pennsylvania
law requiring Internet service
providers (ISPs) to block Web
sites the state's prosecuting
attorneys deem to be child
pornography has been reversed
by a U.S. federal court, with
the judge ruling the law
violated free speech rights." - ], - [ - "The Microsoft Corp. chairman
receives four million e-mails
a day, but practically an
entire department at the
company he founded is
dedicated to ensuring that
nothing unwanted gets into his
inbox, the company #39;s chief
executive said Thursday." - ], - [ - "The 7100t has a mobile phone,
e-mail, instant messaging, Web
browsing and functions as an
organiser. The device looks
like a mobile phone and has
the features of the other
BlackBerry models, with a
large screen" - ], - [ - "President Vladimir Putin makes
a speech as he hosts Russia
#39;s Olympic athletes at a
Kremlin banquet in Moscow,
Thursday, Nov. 4, 2004." - ], - [ - "Apple Computer has unveiled
two new versions of its hugely
successful iPod: the iPod
Photo and the U2 iPod. Apple
also has expanded" - ], - [ - "Pakistan are closing in fast
on Sri Lanka #39;s first
innings total after impressing
with both ball and bat on the
second day of the opening Test
in Faisalabad." - ], - [ - "A state regulatory board
yesterday handed a five-year
suspension to a Lawrence
funeral director accused of
unprofessional conduct and
deceptive practices, including
one case where he refused to
complete funeral arrangements
for a client because she had
purchased a lower-priced
casket elsewhere." - ], - [ - "AP - This year's hurricanes
spread citrus canker to at
least 11,000 trees in
Charlotte County, one of the
largest outbreaks of the
fruit-damaging infection to
ever affect Florida's citrus
industry, state officials
said." - ], - [ - "AFP - Hundreds of Buddhists in
southern Russia marched
through snow to see and hear
the Dalai Lama as he continued
a long-awaited visit to the
country in spite of Chinese
protests." - ], - [ - "British officials were on
diplomatic tenterhooks as they
awaited the arrival on
Thursday of French President
Jacques Chirac for a two-day
state visit to Britain." - ], - [ - " SYDNEY (Reuters) - A group of
women on Pitcairn Island in
the South Pacific are standing
by their men, who face
underage sex charges, saying
having sex at age 12 is a
tradition dating back to 18th
century mutineers who settled
on the island." - ], - [ - "Two aircraft are flying out
from the UK on Sunday to
deliver vital aid and supplies
to Haiti, which has been
devastated by tropical storm
Jeanne." - ], - [ - "A scare triggered by a
vibrating sex toy shut down a
major Australian regional
airport for almost an hour
Monday, police said. The
vibrating object was
discovered Monday morning" - ], - [ - "IBM moved back into the iSCSI
(Internet SCSI) market Friday
with a new array priced at
US\\$3,000 and aimed at the
small and midsize business
market." - ], - [ - "Ron Artest has been hit with a
season long suspension,
unprecedented for the NBA
outside doping cases; Stephen
Jackson banned for 30 games;
Jermaine O #39;Neal for 25
games and Anthony Johnson for
five." - ], - [ - "The California Public
Utilities Commission on
Thursday upheld a \\$12.1
million fine against Cingular
Wireless, related to a two-
year investigation into the
cellular telephone company
#39;s business practices." - ], - [ - "Barclays, the British bank
that left South Africa in 1986
after apartheid protests, may
soon resume retail operations
in the nation." - ], - [ - "AP - An Indiana congressional
candidate abruptly walked off
the set of a debate because
she got stage fright." - ], - [ - "Italian-based Parmalat is
suing its former auditors --
Grant Thornton International
and Deloitte Touche Tohmatsu
-- for billions of dollars in
damages. Parmalat blames its
demise on the two companies
#39; mismanagement of its
finances." - ], - [ - "Reuters - Having reached out
to Kashmiris during a two-day
visit to the region, the prime
minister heads this weekend to
the country's volatile
northeast, where public anger
is high over alleged abuses by
Indian soldiers." - ], - [ - "SAN JUAN IXTAYOPAN, Mexico --
A pair of wooden crosses
outside the elementary school
are all that mark the macabre
site where, just weeks ago, an
angry mob captured two federal
police officers, beat them
unconscious, and set them on
fire." - ], - [ - "Three shots behind Grace Park
with five holes to go, six-
time LPGA player of the year
Sorenstam rallied with an
eagle, birdie and three pars
to win her fourth Samsung
World Championship by three
shots over Park on Sunday." - ], - [ - "update An alliance of
technology workers on Tuesday
accused conglomerate Honeywell
International of planning to
move thousands of jobs to low-
cost regions over the next
five years--a charge that
Honeywell denies." - ], - [ - "NSW Rugby CEO Fraser Neill
believes Waratah star Mat
Rogers has learned his lesson
after he was fined and ordered
to do community service
following his controversial
comments about the club rugby
competition." - ], - [ - "American Technology Research
analyst Shaw Wu has initiated
coverage of Apple Computer
(AAPL) with a #39;buy #39;
recommendation, and a 12-month
target of US\\$78 per share." - ], - [ - "washingtonpost.com - Cell
phone shoppers looking for new
deals and features didn't find
them yesterday, a day after
Sprint Corp. and Nextel
Communications Inc. announced
a merger that the phone
companies promised would shake
up the wireless business." - ], - [ - "ATHENS: China, the dominant
force in world diving for the
best part of 20 years, won six
out of eight Olympic titles in
Athens and prompted
speculation about a clean
sweep when they stage the
Games in Beijing in 2008." - ], - [ - " NEW YORK (Reuters) - Northrop
Grumman Corp. <A HREF=\"http
://www.investor.reuters.com/Fu
llQuote.aspx?ticker=NOC.N targ
et=/stocks/quickinfo/fullquote
\">NOC.N</A> reported
higher third-quarter earnings
on Wednesday and an 11
percent increase in sales on
strength in its mission
systems, integrated systems,
ships and space technology
businesses." - ], - [ - "JAPANESE GIANT Sharp has
pulled the plug on its Linux-
based PDAs in the United
States as no-one seems to want
them. The company said that it
will continue to sell them in
Japan where they sell like hot
cakes" - ], - [ - "DUBLIN : An Olympic Airlines
plane diverted to Ireland
following a bomb alert, the
second faced by the Greek
carrier in four days, resumed
its journey to New York after
no device was found on board,
airport officials said." - ], - [ - "New NavOne offers handy PDA
and Pocket PC connectivity,
but fails to impress on
everything else." - ], - [ - "TORONTO (CP) - Shares of
Iamgold fell more than 10 per
cent after its proposed merger
with Gold Fields Ltd. was
thrown into doubt Monday as
South Africa #39;s Harmony
Gold Mining Company Ltd." - ], - [ - "The Marvel deal calls for
Mforma to work with the comic
book giant to develop a
variety of mobile applications
based on Marvel content." - ], - [ - " LONDON (Reuters) - Oil prices
tumbled again on Monday to an
8-week low under \\$46 a
barrel, as growing fuel stocks
in the United States eased
fears of a winter supply
crunch." - ], - [ - " WASHINGTON (Reuters) - The
United States said on Friday
it is preparing a new U.N.
resolution on Darfur and that
Secretary of State Colin
Powell might address next week
whether the violence in
western Sudan constitutes
genocide." - ], - [ - "The Houston Astros won their
19th straight game at home and
are one game from winning
their first playoff series in
42 years." - ], - [ - "washingtonpost.com - The
Portable Media Center -- a
new, Microsoft-conceived
handheld device that presents
video and photos as well as
music -- would be a decent
idea if there weren't such a
thing as lampposts. Or street
signs. Or trees. Or other
cars." - ], - [ - "Alcoa Inc., one of the world
#39;s top producers of
aluminum, said Monday that it
received an unsolicited
quot;mini-tender quot; offer
from Toronto-based TRC Capital
Corp." - ], - [ - "The European Commission is to
warn Greece about publishing
false information about its
public finances." - ], - [ - "The Air Force Reserve #39;s
Hurricane Hunters, those
fearless crews who dive into
the eyewalls of hurricanes to
relay critical data on
tropical systems, were chased
from their base on the
Mississippi Gulf Coast by
Hurricane Ivan." - ], - [ - " LONDON (Reuters) - U.S.
shares were expected to open
lower on Wednesday after
crude oil pushed to a fresh
high overnight, while Web
search engine Google Inc.
dented sentiment as it
slashed the price range on its
initial public offering." - ], - [ - "The eighth-seeded American
fell to sixth-seeded Elena
Dementieva of Russia, 0-6 6-2
7-6 (7-5), on Friday - despite
being up a break on four
occasions in the third set." - ], - [ - "TUCSON, Arizona (Ticker) --
No. 20 Arizona State tries to
post its first three-game
winning streak over Pac-10
Conference rival Arizona in 26
years when they meet Friday." - ], - [ - "NAJAF, Iraq : Iraq #39;s top
Shiite Muslim clerics, back in
control of Najaf #39;s Imam
Ali shrine after a four-month
militia occupation, met amid
the ruins of the city as life
spluttered back to normality." - ], - [ - "Embargo or not, Fidel Castro's
socialist paradise has quietly
become a pharmaceutical
powerhouse. (They're still
working on the capitalism
thing.) By Douglas Starr from
Wired magazine." - ], - [ - "AP - Phillip Fulmer kept his
cool when starting center
Jason Respert drove off in the
coach's golf cart at practice." - ], - [ - "GOALS from Wayne Rooney and
Ruud van Nistelrooy gave
Manchester United the win at
Newcastle. Alan Shearer
briefly levelled matters for
the Magpies but United managed
to scrape through." - ], - [ - "The telemarketer at the other
end of Orlando Castelblanco
#39;s line promised to reduce
the consumer #39;s credit card
debt by at least \\$2,500 and
get his 20 percent -- and
growing -- interest rates down
to single digits." - ], - [ - " NEW YORK (Reuters) - Blue-
chip stocks fell slightly on
Monday after No. 1 retailer
Wal-Mart Stores Inc. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=WMT
.N target=/stocks/quickinfo/fu
llquote\">WMT.N</A>
reported lower-than-expected
Thanksgiving sales, while
technology shares were lifted
by a rally in Apple Computer
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AAPL.O target=/sto
cks/quickinfo/fullquote\">AA
PL.O</A>." - ], - [ - "Reuters - House of
Representatives
Majority\\Leader Tom DeLay,
admonished twice in six days
by his chamber's\\ethics
committee, withstood calls on
Thursday by rival\\Democrats
and citizen groups that he
step aside." - ], - [ - "WPP Group Inc., the world
#39;s second- largest
marketing and advertising
company, said it won the
bidding for Grey Global Group
Inc." - ], - [ - "AFP - Australia has accounted
for all its nationals known to
be working in Iraq following a
claim by a radical Islamic
group to have kidnapped two
Australians, Foreign Affairs
Minister Alexander Downer
said." - ], - [ - "A new worm can spy on users by
hijacking their Web cameras, a
security firm warned Monday.
The Rbot.gr worm -- the latest
in a long line of similar
worms; one security firm
estimates that more than 4,000
variations" - ], - [ - " NEW YORK (Reuters) - Stocks
were slightly lower on
Tuesday, as concerns about
higher oil prices cutting into
corporate profits and
consumer demand weighed on
sentiment, while retail sales
posted a larger-than-expected
decline in August." - ], - [ - "The separation of PalmOne and
PalmSource will be complete
with Eric Benhamou's
resignation as the latter's
chairman." - ], - [ - "AP - Two-time U.S. Open
doubles champion Max Mirnyi
will lead the Belarus team
that faces the United States
in the Davis Cup semifinals in
Charleston later this month." - ], - [ - "The hurricane appeared to be
strengthening and forecasters
warned of an increased threat
of serious flooding and wind
damage." - ], - [ - "AP - New York Jets safety Erik
Coleman got his souvenir
football from the equipment
manager and held it tightly." - ], - [ - " SINGAPORE (Reuters) - Oil
prices climbed above \\$42 a
barrel on Wednesday, rising
for the third day in a row as
the heavy consuming U.S.
Northeast feels the first
chills of winter." - ], - [ - "\\\\\"(CNN) -- A longtime
associate of al Qaeda leader
Osama bin Laden surrendered
to\\Saudi Arabian officials
Tuesday, a Saudi Interior
Ministry official said.\"\\\\\"But
it is unclear what role, if
any, Khaled al-Harbi may have
had in any terror\\attacks
because no public charges have
been filed against him.\"\\\\\"The
Saudi government -- in a
statement released by its
embassy in Washington
--\\called al-Harbi's surrender
\"the latest direct result\" of
its limited, one-month\\offer
of leniency to terror
suspects.\"\\\\This is great! I
hope this really starts to pay
off. Creative solutions
to\\terrorism that don't
involve violence. \\\\How
refreshing! \\\\Are you paying
attention Bush
administration?\\\\" - ], - [ - " NEW YORK (Reuters) - Merck
Co Inc. <A HREF=\"http://www
.investor.reuters.com/FullQuot
e.aspx?ticker=MRK.N target=/st
ocks/quickinfo/fullquote\">M
RK.N</A> on Thursday
pulled its arthritis drug
Vioxx off the market after a
study showed it doubled the
risk of heart attack and
stroke, a move that sent its
shares plunging and erased
\\$25 billion from its market
value." - ], - [ - "AT T Corp. is cutting 7,400
more jobs and slashing the
book value of its assets by
\\$11.4 billion, drastic moves
prompted by the company's plan
to retreat from the
traditional consumer telephone
business following a lost
court battle." - ], - [ - "The government on Wednesday
defended its decision to
radically revise the country
#39;s deficit figures, ahead
of a European Commission
meeting to consider possible
disciplinary action against
Greece for submitting faulty
figures." - ], - [ - "Ivan Hlinka coached the Czech
Republic to the hockey gold
medal at the 1998 Nagano
Olympics and became the coach
of the Pittsburgh Penguins two
years later." - ], - [ - "Four homeless men were
bludgeoned to death and six
were in critical condition on
Friday following early morning
attacks by unknown assailants
in downtown streets of Sao
Paulo, a police spokesman
said." - ], - [ - "OPEC ministers yesterday
agreed to increase their
ceiling for oil production to
help bring down stubbornly
high prices in a decision that
traders and analysts dismissed
as symbolic because the cartel
already is pumping more than
its new target." - ], - [ - "Williams-Sonoma Inc. said
second- quarter profit rose 55
percent, boosted by the
addition of Pottery Barn
stores and sale of outdoor
furniture." - ], - [ - "Celerons form the basis of
Intel #39;s entry-level
platform which includes
integrated/value motherboards
as well. The Celeron 335D is
the fastest - until the
Celeron 340D - 2.93GHz -
becomes mainstream - of the" - ], - [ - "The entertainment industry
asks the Supreme Court to
reverse the Grokster decision,
which held that peer-to-peer
networks are not liable for
copyright abuses of their
users. By Michael Grebb." - ], - [ - " HONG KONG/SAN FRANCISCO
(Reuters) - China's largest
personal computer maker,
Lenovo Group Ltd., said on
Tuesday it was in acquisition
talks with a major technology
company, which a source
familiar with the situation
said was IBM." - ], - [ - "Phone companies are not doing
enough to warn customers about
internet \"rogue-dialling\"
scams, watchdog Icstis warns." - ], - [ - "Reuters - Oil prices stayed
close to #36;49 a\\barrel on
Thursday, supported by a
forecast for an early
cold\\snap in the United States
that could put a strain on a
thin\\supply cushion of winter
heating fuel." - ], - [ - "AUBURN - Ah, easy street. No
game this week. Light
practices. And now Auburn is
being touted as the No. 3 team
in the Bowl Championship
Series standings." - ], - [ - "Portsmouth #39;s Harry
Redknapp has been named as the
Barclays manager of the month
for October. Redknapp #39;s
side were unbeaten during the
month and maintained an
impressive climb to ninth in
the Premiership." - ], - [ - "India's main opposition party
takes action against senior
party member Uma Bharti after
a public row." - ], - [ - "Hewlett-Packard will shell out
\\$16.1 billion for chips in
2005, but Dell's wallet is
wide open, too." - ], - [ - "WASHINGTON (CBS.MW) --
President Bush announced
Monday that Kellogg chief
executive Carlos Gutierrez
would replace Don Evans as
Commerce secretary, naming the
first of many expected changes
to his economic team." - ], - [ - "Iron Mountain moved further
into the backup and recovery
space Tuesday with the
acquisition of Connected Corp.
for \\$117 million. Connected
backs up desktop data for more
than 600 corporations, with
more than" - ], - [ - "Three directors of Manchester
United have been ousted from
the board after US tycoon
Malcolm Glazer, who is
attempting to buy the club,
voted against their re-
election." - ], - [ - "The Russian military yesterday
extended its offer of a \\$10
million reward for information
leading to the capture of two
separatist leaders who, the
Kremlin claims, were behind
the Beslan massacre." - ], - [ - "AP - Manny Ramirez singled and
scored before leaving with a
bruised knee, and the
streaking Boston Red Sox beat
the Detroit Tigers 5-3 Friday
night for their 10th victory
in 11 games." - ], - [ - "A remote attacker could take
complete control over
computers running many
versions of Microsoft software
by inserting malicious code in
a JPEG image that executes
through an unchecked buffer" - ], - [ - "Montgomery County (website -
news) is a big step closer to
shopping for prescription
drugs north of the border. On
a 7-2 vote, the County Council
is approving a plan that would
give county" - ], - [ - "Israels Shin Bet security
service has tightened
protection of the prime
minister, MPs and parliament
ahead of next weeks crucial
vote on a Gaza withdrawal." - ], - [ - "The news comes fast and
furious. Pedro Martinez goes
to Tampa to visit George
Steinbrenner. Theo Epstein and
John Henry go to Florida for
their turn with Pedro. Carl
Pavano comes to Boston to
visit Curt Schilling. Jason
Varitek says he's not a goner.
Derek Lowe is a goner, but he
says he wishes it could be
different. Orlando Cabrera ..." - ], - [ - "The disclosure this week that
a Singapore-listed company
controlled by a Chinese state-
owned enterprise lost \\$550
million in derivatives
transactions" - ], - [ - "Reuters - Iraq's interim
defense minister
accused\\neighbors Iran and
Syria on Wednesday of aiding
al Qaeda\\Islamist Abu Musab
al-Zarqawi and former agents
of Saddam\\Hussein to promote a
\"terrorist\" insurgency in
Iraq." - ], - [ - "AP - The Senate race in
Kentucky stayed at fever pitch
on Thursday as Democratic
challenger Daniel Mongiardo
stressed his opposition to gay
marriage while accusing
Republican incumbent Jim
Bunning of fueling personal
attacks that seemed to suggest
Mongiardo is gay." - ], - [ - "The lawsuit claims the
companies use a patented
Honeywell technology for
brightening images and
reducing interference on
displays." - ], - [ - "The co-president of Oracle
testified that her company was
serious about its takeover
offer for PeopleSoft and was
not trying to scare off its
customers." - ], - [ - "VANCOUVER - A Vancouver-based
firm won #39;t sell 1.2
million doses of influenza
vaccine to the United States
after all, announcing Tuesday
that it will sell the doses
within Canada instead." - ], - [ - "An extremely rare Hawaiian
bird dies in captivity,
possibly marking the
extinction of its entire
species only 31 years after it
was first discovered." - ], - [ - "Does Geico's trademark lawsuit
against Google have merit? How
will the case be argued?
What's the likely outcome of
the trial? A mock court of
trademark experts weighs in
with their verdict." - ], - [ - "Treo 650 boasts a high-res
display, an improved keyboard
and camera, a removable
battery, and more. PalmOne
this week is announcing the
Treo 650, a hybrid PDA/cell-
phone device that addresses
many of the shortcomings" - ], - [ - "FRED Hale Sr, documented as
the worlds oldest man, has
died at the age of 113. Hale
died in his sleep on Friday at
a hospital in Syracuse, New
York, while trying to recover
from a bout of pneumonia, his
grandson, Fred Hale III said." - ], - [ - "The Oakland Raiders have
traded Jerry Rice to the
Seattle Seahawks in a move
expected to grant the most
prolific receiver in National
Football League history his
wish to get more playing time." - ], - [ - "consortium led by the Sony
Corporation of America reached
a tentative agreement today to
buy Metro-Goldwyn-Mayer, the
Hollywood studio famous for
James Bond and the Pink
Panther, for" - ], - [ - "International Business
Machines Corp.'s possible exit
from the personal computer
business would be the latest
move in what amounts to a long
goodbye from a field it
pioneered and revolutionized." - ], - [ - "Leipzig Game Convention in
Germany, the stage for price-
slash revelations. Sony has
announced that it #39;s
slashing the cost of PS2 in
the UK and Europe to 104.99
GBP." - ], - [ - "AP - Florida coach Ron Zook
was fired Monday but will be
allowed to finish the season,
athletic director Jeremy Foley
told The Gainesville Sun." - ], - [ - "The country-cooking restaurant
chain has agreed to pay \\$8.7
million over allegations that
it segregated black customers,
subjected them to racial slurs
and gave black workers
inferior jobs." - ], - [ - "Troy Brown has had to make a
lot of adjustments while
playing both sides of the
football. quot;You always
want to score when you get the
ball -- offense or defense" - ], - [ - " PORT LOUIS, Aug. 17
(Xinhuanet) -- Southern
African countries Tuesday
pledged better trade and
investment relations with
China as well as India in the
final communique released at
the end of their two-day
summit." - ], - [ - "In yet another devastating
body blow to the company,
Intel (Nasdaq: INTC) announced
it would be canceling its
4-GHz Pentium chip. The
semiconductor bellwether said
it was switching" - ], - [ - "Jenson Button will tomorrow
discover whether he is allowed
to quit BAR and move to
Williams for 2005. The
Englishman has signed
contracts with both teams but
prefers a switch to Williams,
where he began his Formula One
career in 2000." - ], - [ - "Seagate #39;s native SATA
interface technology with
Native Command Queuing (NCQ)
allows the Barracuda 7200.8 to
match the performance of
10,000-rpm SATA drives without
sacrificing capacity" - ], - [ - "ARSENAL boss Arsene Wenger
last night suffered a
Champions League setback as
Brazilian midfielder Gilberto
Silva (above) was left facing
a long-term injury absence." - ], - [ - "BAGHDAD - A militant group has
released a video saying it
kidnapped a missing journalist
in Iraq and would kill him
unless US forces left Najaf
within 48 hours." - ], - [ - "18 August 2004 -- There has
been renewed fighting in the
Iraqi city of Al-Najaf between
US and Iraqi troops and Shi
#39;a militiamen loyal to
radical cleric Muqtada al-
Sadr." - ], - [ - "You #39;re probably already
familiar with one of the most
common questions we hear at
iPodlounge: quot;how can I
load my iPod up with free
music?" - ], - [ - " SINGAPORE (Reuters) -
Investors bought shares in
Asian exporters and
electronics firms such as
Fujitsu Ltd. on Tuesday,
buoyed by a favorable outlook
from U.S. technology
bellwethers and a slide in oil
prices." - ], - [ - "Ace Ltd. will stop paying
brokers for steering business
its way, becoming the third
company to make concessions in
the five days since New York
Attorney General Eliot Spitzer
unveiled a probe of the
insurance industry." - ], - [ - "Vice chairman of the office of
the chief executive officer in
Novell Inc, Chris Stone is
leaving the company to pursue
other opportunities in life." - ], - [ - "Wm. Wrigley Jr. Co., the world
#39;s largest maker of chewing
gum, agreed to buy candy
businesses including Altoids
mints and Life Savers from
Kraft Foods Inc." - ], - [ - "American improves to 3-1 on
the season with a hard-fought
overtime win, 74-63, against
Loyala at Bender Arena on
Friday night." - ], - [ - "Australia tighten their grip
on the third Test and the
series after dominating India
on day two in Nagpur." - ], - [ - " #39;Reaching a preliminary
pilot agreement is the single
most important hurdle they
have to clear, but certainly
not the only one." - ], - [ - "Bee Staff Writers. SAN
FRANCISCO - As Eric Johnson
drove to the stadium Sunday
morning, his bruised ribs were
so sore, he wasn #39;t sure he
#39;d be able to suit up for
the game." - ], - [ - "The New England Patriots are
so single-minded in pursuing
their third Super Bowl triumph
in four years that they almost
have no room for any other
history." - ], - [ - "TORONTO (CP) - Canada #39;s
big banks are increasing
mortgage rates following a
decision by the Bank of Canada
to raise its overnight rate by
one-quarter of a percentage
point to 2.25 per cent." - ], - [ - " SEOUL (Reuters) - North
Korea's two-year-old nuclear
crisis has taxed the world's
patience, the chief United
Nations nuclear regulator
said on Wednesday, urging
communist Pyongyang to return
to its disarmament treaty
obligations." - ], - [ - "washingtonpost.com - Microsoft
is going to Tinseltown today
to announce plans for its
revamped Windows XP Media
Center, part of an aggressive
push to get ahead in the
digital entertainment race." - ], - [ - "GROZNY, Russia - The Russian
government's choice for
president of war-ravaged
Chechnya appeared to be the
victor Sunday in an election
tainted by charges of fraud
and shadowed by last week's
terrorist destruction of two
airliners. Little more than
two hours after polls closed,
acting Chechen president
Sergei Abramov said
preliminary results showed
Maj..." - ], - [ - "Because, while the Eagles are
certain to stumble at some
point during the regular
season, it seems inconceivable
that they will falter against
a team with as many offensive
problems as Baltimore has
right now." - ], - [ - "AP - J.J. Arrington ran for 84
of his 121 yards in the second
half and Aaron Rodgers shook
off a slow start to throw two
touchdown passes to help No. 5
California beat Washington
42-12 on Saturday." - ], - [ - "BAGHDAD, Sept 5 (AFP) - Izzat
Ibrahim al-Duri, Saddam
Hussein #39;s deputy whose
capture was announced Sunday,
is 62 and riddled with cancer,
but was public enemy number
two in Iraq for the world
#39;s most powerful military." - ], - [ - "AP - An explosion targeted the
Baghdad governor's convoy as
he was traveling through the
capital Tuesday, killing two
people but leaving him
uninjured, the Interior
Ministry said." - ], - [ - "GENEVA: Rescuers have found
the bodies of five Swiss
firemen who died after the
ceiling of an underground car
park collapsed during a fire,
a police spokesman said last
night." - ], - [ - "John Kerry has held 10 \"front
porch visit\" events an actual
front porch is optional where
perhaps 100 people ask
questions in a low-key
campaigning style." - ], - [ - "AP - Most of the turkeys
gracing the nation's dinner
tables Thursday have been
selectively bred for their
white meat for so many
generations that simply
walking can be a problem for
many of the big-breasted birds
and sex is no longer possible." - ], - [ - "OTTAWA (CP) - The economy
created another 43,000 jobs
last month, pushing the
unemployment rate down to 7.1
per cent from 7.2 per cent in
August, Statistics Canada said
Friday." - ], - [ - "The right-win opposition
Conservative Party and Liberal
Center Union won 43 seats in
the 141-member Lithuanian
parliament, after more than 99
percent of the votes were
counted" - ], - [ - " TOKYO (Reuters) - Japan's
Nikkei average rose 0.39
percent by midsession on
Friday, bolstered by solid
gains in stocks dependent on
domestic business such as Kao
Corp. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=4452.T target=/sto
cks/quickinfo/fullquote\">44
52.T</A>." - ], - [ - " FRANKFURT (Reuters) -
DaimlerChrysler and General
Motors will jointly develop
new hybrid motors to compete
against Japanese rivals on
the fuel-saving technology
that reduces harmful
emissions, the companies said
on Monday." - ], - [ - " SEATTLE (Reuters) - Microsoft
Corp. <A HREF=\"http://www.r
euters.co.uk/financeQuoteLooku
p.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
is making a renewed push this
week to get its software into
living rooms with the launch
of a new version of its
Windows XP Media Center, a
personal computer designed for
viewing movies, listening to
music and scrolling through
digital pictures." - ], - [ - "KHARTOUM, Aug 18 (Reuters) -
The United Nations said on
Wednesday it was very
concerned by Sudan #39;s lack
of practical progress in
bringing security to Darfur,
where more than a million
people have fled their homes
for fear of militia ..." - ], - [ - "SHANGHAI, China The Houston
Rockets have arrived in
Shanghai with hometown
favorite Yao Ming declaring
himself quot;here on
business." - ], - [ - "Charleston, SC (Sports
Network) - Andy Roddick and
Mardy Fish will play singles
for the United States in this
weekend #39;s Davis Cup
semifinal matchup against
Belarus." - ], - [ - "AFP - With less than two
months until the November 2
election, President George W.
Bush is working to shore up
support among his staunchest
supporters even as he courts
undecided voters." - ], - [ - "The bass should be in your
face. That's what Matt Kelly,
of Boston's popular punk rock
band Dropkick Murphys, thinks
is the mark of a great stereo
system. And he should know.
Kelly, 29, is the drummer for
the band that likes to think
of itself as a bit of an Irish
lucky charm for the Red Sox." - ], - [ - "Chile's government says it
will build a prison for
officers convicted of human
rights abuses in the Pinochet
era." - ], - [ - "Slumping corporate spending
and exports caused the economy
to slow to a crawl in the
July-September period, with
real gross domestic product
expanding just 0.1 percent
from the previous quarter,
Cabinet Office data showed
Friday." - ], - [ - "US President George W. Bush
signed into law a bill
replacing an export tax
subsidy that violated
international trade rules with
a \\$145 billion package of new
corporate tax cuts and a
buyout for tobacco farmers." - ], - [ - "The Nikkei average was up 0.37
percent in mid-morning trade
on Thursday as a recovery in
the dollar helped auto makers
among other exporters, but
trade was slow as investors
waited for important Japanese
economic data." - ], - [ - "Jennifer Canada knew she was
entering a boy's club when she
enrolled in Southern Methodist
University's Guildhall school
of video-game making." - ], - [ - "RICKY PONTING believes the
game #39;s watchers have
fallen for the quot;myth
quot; that New Zealand know
how to rattle Australia." - ], - [ - "MILWAUKEE (SportsTicker) -
Barry Bonds tries to go where
just two players have gone
before when the San Francisco
Giants visit the Milwaukee
Brewers on Tuesday." - ], - [ - "Palestinian leader Mahmoud
Abbas reiterated calls for his
people to drop their weapons
in the struggle for a state. a
clear change of strategy for
peace with Israel after Yasser
Arafat #39;s death." - ], - [ - "The new software is designed
to simplify the process of
knitting together back-office
business applications." - ], - [ - "SYDNEY (Dow Jones)--Colorado
Group Ltd. (CDO.AU), an
Australian footwear and
clothing retailer, said Monday
it expects net profit for the
fiscal year ending Jan. 29 to
be over 30 higher than that of
a year earlier." - ], - [ - "NEW YORK - What are the odds
that a tiny nation like
Antigua and Barbuda could take
on the United States in an
international dispute and win?" - ], - [ - "AP - With Tom Brady as their
quarterback and a stingy,
opportunistic defense, it's
difficult to imagine when the
New England Patriots might
lose again. Brady and
defensive end Richard Seymour
combined to secure the
Patriots' record-tying 18th
straight victory, 31-17 over
the Buffalo Bills on Sunday." - ], - [ - "FRANKFURT, GERMANY -- The
German subsidiaries of
Hewlett-Packard Co. (HP) and
Novell Inc. are teaming to
offer Linux-based products to
the country's huge public
sector." - ], - [ - "SBC Communications expects to
cut 10,000 or more jobs by the
end of next year through
layoffs and attrition. That
#39;s about six percent of the
San Antonio-based company
#39;s work force." - ], - [ - " BAGHDAD (Reuters) - Iraq's
U.S.-backed government said on
Tuesday that \"major neglect\"
by its American-led military
allies led to a massacre of 49
army recruits at the weekend." - ], - [ - "SiliconValley.com - \"I'm
back,\" declared Apple
Computer's Steve Jobs on
Thursday morning in his first
public appearance before
reporters since cancer surgery
in late July." - ], - [ - "BEIJING -- Police have
detained a man accused of
slashing as many as nine boys
to death as they slept in
their high school dormitory in
central China, state media
reported today." - ], - [ - "Health India: London, Nov 4 :
Cosmetic face cream used by
fashionable Roman women was
discovered at an ongoing
archaeological dig in London,
in a metal container, complete
with the lid and contents." - ], - [ - "Israeli Prime Minister Ariel
Sharon #39;s Likud party
agreed on Thursday to a
possible alliance with
opposition Labour in a vote
that averted a snap election
and strengthened his Gaza
withdrawal plan." - ], - [ - "Another United Airlines union
is seeking to oust senior
management at the troubled
airline, saying its strategies
are reckless and incompetent." - ], - [ - "Approaching Hurricane Ivan has
led to postponement of the
game Thursday night between
10th-ranked California and
Southern Mississippi in
Hattiesburg, Cal #39;s
athletic director said Monday." - ], - [ - "Global oil prices boomed on
Wednesday, spreading fear that
energy prices will restrain
economic activity, as traders
worried about a heating oil
supply crunch in the American
winter." - ], - [ - "Custom-designed imported
furniture was once an
exclusive realm. Now, it's the
economical alternative for
commercial developers and
designers needing everything
from seats to beds to desks
for their projects." - ], - [ - "SAN DIEGO (Ticker) - The San
Diego Padres lacked speed and
an experienced bench last
season, things veteran
infielder Eric Young is
capable of providing." - ], - [ - "This is an eye chart,
reprinted as a public service
to the New York Mets so they
may see from what they suffer:
myopia. Has ever a baseball
franchise been so shortsighted
for so long?" - ], - [ - "Short-term interest rate
futures struggled on Thursday
after a government report
showing US core inflation for
August below market
expectations failed to alter
views on Federal Reserve rate
policy." - ], - [ - "MacCentral - Microsoft's
Macintosh Business Unit on
Tuesday issued a patch for
Virtual PC 7 that fixes a
problem that occurred when
running the software on Power
Mac G5 computers with more
than 2GB of RAM installed.
Previously, Virtual PC 7 would
not run on those computers,
causing a fatal error that
crashed the application.
Microsoft also noted that
Virtual PC 7.0.1 also offers
stability improvements,
although it wasn't more
specific than that -- some
users have reported problems
with using USB devices and
other issues." - ], - [ - "Samsung is now the world #39;s
second-largest mobile phone
maker, behind Nokia. According
to market watcher Gartner, the
South Korean company has
finally knocked Motorola into
third place." - ], - [ - "Don't bother buying Star Wars:
Battlefront if you're looking
for a first-class shooter --
there are far better games out
there. But if you're a Star
Wars freak and need a fix,
this title will suffice. Lore
Sjberg reviews Battlefront." - ], - [ - "While the American forces are
preparing to launch a large-
scale attack against Falluja
and al-Ramadi, one of the
chieftains of Falluja said
that contacts are still
continuous yesterday between
members representing the city
#39;s delegation and the
interim" - ], - [ - "UN Security Council
ambassadors were still
quibbling over how best to
pressure Sudan and rebels to
end two different wars in the
country even as they left for
Kenya on Tuesday for a meeting
on the crisis." - ], - [ - "HENDALA, Sri Lanka -- Day
after day, locked in a cement
room somewhere in Iraq, the
hooded men beat him. They told
him he would be beheaded.
''Ameriqi! quot; they shouted,
even though he comes from this
poor Sri Lankan fishing
village." - ], - [ - "THE kidnappers of British aid
worker Margaret Hassan last
night threatened to turn her
over to the group which
beheaded Ken Bigley if the
British Government refuses to
pull its troops out of Iraq." - ], - [ - " TOKYO (Reuters) - Tokyo
stocks climbed to a two week
high on Friday after Tokyo
Electron Ltd. and other chip-
related stocks were boosted
by a bullish revenue outlook
from industry leader Intel
Corp." - ], - [ - "More than by brain size or
tool-making ability, the human
species was set apart from its
ancestors by the ability to
jog mile after lung-stabbing
mile with greater endurance
than any other primate,
according to research
published today in the journal" - ], - [ - " LONDON (Reuters) - A medical
product used to treat both
male hair loss and prostate
problems has been added to the
list of banned drugs for
athletes." - ], - [ - "AP - Curtis Martin and Jerome
Bettis have the opportunity to
go over 13,000 career yards
rushing in the same game when
Bettis and the Pittsburgh
Steelers play Martin and the
New York Jets in a big AFC
matchup Sunday." - ], - [ - "<p></p><p>
BOGOTA, Colombia (Reuters) -
Ten Colombian police
officerswere killed on Tuesday
in an ambush by the National
LiberationArmy, or ELN, in the
worst attack by the Marxist
group inyears, authorities
told Reuters.</p>" - ], - [ - "Woodland Hills-based Brilliant
Digital Entertainment and its
subsidiary Altnet announced
yesterday that they have filed
a patent infringement suit
against the Recording Industry
Association of America (RIAA)." - ], - [ - "AFP - US President George W.
Bush called his Philippines
counterpart Gloria Arroyo and
said their countries should
keep strong ties, a spokesman
said after a spat over
Arroyo's handling of an Iraq
kidnapping." - ], - [ - "A scathing judgment from the
UK #39;s highest court
condemning the UK government
#39;s indefinite detention of
foreign terror suspects as a
threat to the life of the
nation, left anti-terrorist
laws in the UK in tatters on
Thursday." - ], - [ - "Sony Ericsson and Cingular
provide Z500a phones and
service for military families
to stay connected on today
#39;s quot;Dr. Phil Show
quot;." - ], - [ - "Reuters - Australia's
conservative Prime
Minister\\John Howard, handed
the most powerful mandate in a
generation,\\got down to work
on Monday with reform of
telecommunications,\\labor and
media laws high on his agenda." - ], - [ - " TOKYO (Reuters) - Typhoon
Megi killed one person as it
slammed ashore in northern
Japan on Friday, bringing the
death toll to at least 13 and
cutting power to thousands of
homes before heading out into
the Pacific." - ], - [ - "Cairo: Egyptian President
Hosni Mubarak held telephone
talks with Palestinian leader
Yasser Arafat about Israel
#39;s plan to pull troops and
the 8,000 Jewish settlers out
of the Gaza Strip next year,
the Egyptian news agency Mena
said." - ], - [ - " NEW YORK (Reuters) - Adobe
Systems Inc. <A HREF=\"http:
//www.investor.reuters.com/Ful
lQuote.aspx?ticker=ADBE.O targ
et=/stocks/quickinfo/fullquote
\">ADBE.O</A> on
Monday reported a sharp rise
in quarterly profit, driven by
robust demand for its
Photoshop and document-sharing
software." - ], - [ - "Dow Jones amp; Co., publisher
of The Wall Street Journal,
has agreed to buy online
financial news provider
MarketWatch Inc. for about
\\$463 million in a bid to
boost its revenue from the
fast-growing Internet
advertising market." - ], - [ - "The first American military
intelligence soldier to be
court-martialed over the Abu
Ghraib abuse scandal was
sentenced Saturday to eight
months in jail, a reduction in
rank and a bad-conduct
discharge." - ], - [ - " TOKYO (Reuters) - Japan will
seek an explanation at weekend
talks with North Korea on
activity indicating Pyongyang
may be preparing a missile
test, although Tokyo does not
think a launch is imminent,
Japan's top government
spokesman said." - ], - [ - "Michigan Stadium was mostly
filled with empty seats. The
only cheers were coming from
near one end zone -he Iowa
section. By Carlos Osorio, AP." - ], - [ - "The International Rugby Board
today confirmed that three
countries have expressed an
interest in hosting the 2011
World Cup. New Zealand, South
Africa and Japan are leading
the race to host rugby union
#39;s global spectacular in
seven years #39; time." - ], - [ - "Officials at EarthLink #39;s
(Quote, Chart) R amp;D
facility have quietly released
a proof-of-concept file-
sharing application based on
the Session Initiated Protocol
(define)." - ], - [ - "Low-fare airline ATA has
announced plans to lay off
hundreds of employees and to
drop most of its flights out
of Midway Airport in Chicago." - ], - [ - " BEIJING (Reuters) - Iran will
never be prepared to
dismantle its nuclear program
entirely but remains committed
to the non-proliferation
treaty (NPT), its chief
delegate to the International
Atomic Energy Agency said on
Wednesday." - ], - [ - " WASHINGTON (Reuters) -
Germany's Bayer AG <A HREF=
\"http://www.investor.reuters.c
om/FullQuote.aspx?ticker=BAYG.
DE target=/stocks/quickinfo/fu
llquote\">BAYG.DE</A>
has agreed to plead guilty
and pay a \\$4.7 million fine
for taking part in a
conspiracy to fix the prices
of synthetic rubber, the U.S.
Justice Department said on
Wednesday." - ], - [ - "MIAMI (Ticker) -- In its first
season in the Atlantic Coast
Conference, No. 11 Virginia
Tech is headed to the BCS.
Bryan Randall threw two
touchdown passes and the
Virginia Tech defense came up
big all day as the Hokies
knocked off No." - ], - [ - "(CP) - Somehow, in the span of
half an hour, the Detroit
Tigers #39; pitching went from
brutal to brilliant. Shortly
after being on the wrong end
of several records in a 26-5
thrashing from to the Kansas
City" - ], - [ - "<p></p><p>
SANTIAGO, Chile (Reuters) -
President Bush on
Saturdayreached into a throng
of squabbling bodyguards and
pulled aSecret Service agent
away from Chilean security
officers afterthey stopped the
U.S. agent from accompanying
the president ata
dinner.</p>" - ], - [ - " quot;It #39;s your mail,
quot; the Google Web site
said. quot;You should be able
to choose how and where you
read it. You can even switch
to other e-mail services
without having to worry about
losing access to your
messages." - ], - [ - "The US oil giant got a good
price, Russia #39;s No. 1 oil
company acquired a savvy
partner, and Putin polished
Russia #39;s image." - ], - [ - "With the Huskies reeling at
0-4 - the only member of a
Bowl Championship Series
conference left without a win
-an Jose State suddenly looms
as the only team left on the
schedule that UW will be
favored to beat." - ], - [ - "Darryl Sutter, who coached the
Calgary Flames to the Stanley
Cup finals last season, had an
emergency appendectomy and was
recovering Friday." - ], - [ - "The maker of Hostess Twinkies,
a cake bar and a piece of
Americana children have
snacked on for almost 75
years, yesterday raised
concerns about the company
#39;s ability to stay in
business." - ], - [ - "Iranian deputy foreign
minister Gholamali Khoshrou
denied Tuesday that his
country #39;s top leaders were
at odds over whether nuclear
weapons were un-Islamic,
insisting that it will
quot;never quot; make the
bomb." - ], - [ - " quot;Israel mercenaries
assisting the Ivory Coast army
operated unmanned aircraft
that aided aerial bombings of
a French base in the country,
quot; claimed" - ], - [ - "Athens, Greece (Sports
Network) - For the second
straight day a Briton captured
gold at the Olympic Velodrome.
Bradley Wiggins won the men
#39;s individual 4,000-meter
pursuit Saturday, one day
after teammate" - ], - [ - "AFP - SAP, the world's leading
maker of business software,
may need an extra year to
achieve its medium-term profit
target of an operating margin
of 30 percent, its chief
financial officer said." - ], - [ - "Tenet Healthcare Corp., the
second- largest US hospital
chain, said fourth-quarter
charges may exceed \\$1 billion
and its loss from continuing
operations will widen from the
third quarter #39;s because of
increased bad debt." - ], - [ - "AFP - The airline Swiss said
it had managed to cut its
first-half net loss by about
90 percent but warned that
spiralling fuel costs were
hampering a turnaround despite
increasing passenger travel." - ], - [ - "Vulnerable youngsters expelled
from schools in England are
being let down by the system,
say inspectors." - ], - [ - "Yasser Arafat was undergoing
tests for possible leukaemia
at a military hospital outside
Paris last night after being
airlifted from his Ramallah
headquarters to an anxious
farewell from Palestinian
well-wishers." - ], - [ - "The world #39;s only captive
great white shark made history
this week when she ate several
salmon fillets, marking the
first time that a white shark
in captivity" - ], - [ - "Nepal #39;s main opposition
party urged the government on
Monday to call a unilateral
ceasefire with Maoist rebels
and seek peace talks to end a
road blockade that has cut the
capital off from the rest of
the country." - ], - [ - "Communications aggregator
iPass said Monday that it is
adding in-flight Internet
access to its access
portfolio. Specifically, iPass
said it will add access
offered by Connexion by Boeing
to its list of access
providers." - ], - [ - "The London-based brokerage
Collins Stewart Tullett placed
55m of new shares yesterday to
help fund the 69.5m purchase
of the money and futures
broker Prebon." - ], - [ - "BOSTON - The New York Yankees
and Boston were tied 4-4 after
13 innings Monday night with
the Red Sox trying to stay
alive in the AL championship
series. Boston tied the
game with two runs in the
eighth inning on David Ortiz's
solo homer, a walk to Kevin
Millar, a single by Trot Nixon
and a sacrifice fly by Jason
Varitek..." - ], - [ - "A Steffen Iversen penalty was
sufficient to secure the
points for Norway at Hampden
on Saturday. James McFadden
was ordered off after 53
minutes for deliberate
handball as he punched Claus
Lundekvam #39;s header off the
line." - ], - [ - "Reuters - The country may be
more\\or less evenly divided
along partisan lines when it
comes to\\the presidential
race, but the Republican Party
prevailed in\\the Nielsen
polling of this summer's
nominating conventions." - ], - [ - " PARIS (Reuters) - European
equities flirted with 5-month
peaks as hopes that economic
growth was sustainable and a
small dip in oil prices
helped lure investors back to
recent underperformers such
as technology and insurance
stocks." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks looked to open higher
on Friday, as the fourth
quarter begins on Wall Street
with oil prices holding below
\\$50 a barrel." - ], - [ - "LONDON : World oil prices
stormed above 54 US dollars
for the first time Tuesday as
strikes in Nigeria and Norway
raised worries about possible
supply shortages during the
northern hemisphere winter." - ], - [ - "AP - Ailing St. Louis reliever
Steve Kline was unavailable
for Game 3 of the NL
championship series on
Saturday, but Cardinals
manager Tony LaRussa hopes the
left-hander will pitch later
this postseason." - ], - [ - "Company launches free test
version of service that
fosters popular Internet
activity." - ], - [ - "Outdated computer systems are
hampering the work of
inspectors, says the UN
nuclear agency." - ], - [ - " In Vice President Cheney's
final push before next
Tuesday's election, talk of
nuclear annihilation and
escalating war rhetoric have
blended with balloon drops,
confetti cannons and the other
trappings of modern
campaigning with such ferocity
that it is sometimes tough to
tell just who the enemy is." - ], - [ - "MADRID: A stunning first-half
free kick from David Beckham
gave Real Madrid a 1-0 win
over newly promoted Numancia
at the Bernabeu last night." - ], - [ - "MacCentral - You Software Inc.
announced on Tuesday the
availability of You Control:
iTunes, a free\\download that
places iTunes controls in the
Mac OS X menu bar.
Without\\leaving the current
application, you can pause,
play, rewind or skip songs,\\as
well as control iTunes' volume
and even browse your entire
music library\\by album, artist
or genre. Each time a new song
plays, You Control:
iTunes\\also pops up a window
that displays the artist and
song name and the
album\\artwork, if it's in the
library. System requirements
call for Mac OS X\\v10.2.6 and
10MB free hard drive space.
..." - ], - [ - "Favourites Argentina beat
Italy 3-0 this morning to
claim their place in the final
of the men #39;s Olympic
football tournament. Goals by
leading goalscorer Carlos
Tevez, with a smart volley
after 16 minutes, and" - ], - [ - "Shortly after Steve Spurrier
arrived at Florida in 1990,
the Gators were placed on NCAA
probation for a year stemming
from a child-support payment
former coach Galen Hall made
for a player." - ], - [ - "The US Secret Service Thursday
announced arrests in eight
states and six foreign
countries of 28 suspected
cybercrime gangsters on
charges of identity theft,
computer fraud, credit-card
fraud, and conspiracy." - ], - [ - "US stocks were little changed
on Thursday as an upbeat
earnings report from chip
maker National Semiconductor
Corp. (NSM) sparked some
buying, but higher oil prices
limited gains." - ] - ], - "hovertemplate": "label=Other
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Other", - "marker": { - "color": "#636efa", - "size": 5, - "symbol": "circle" - }, - "mode": "markers", - "name": "Other", - "showlegend": true, - "type": "scattergl", - "x": [ - -50.70537, - 16.660423, - -47.977715, - -51.65402, - 17.206654, - -23.452963, - 28.167477, - 14.671119, - -37.6373, - 21.907679, - 49.959976, - -36.581165, - -19.791555, - 11.003371, - 12.786125, - 54.445854, - -20.928211, - 28.192938, - 27.511831, - 30.389194, - 31.907536, - 27.685726, - -0.48236108, - -53.210773, - 24.631432, - -39.491558, - 19.87161, - -22.567608, - 13.9476, - 42.217842, - 23.463217, - -19.96729, - -49.124306, - 15.450646, - -7.3474283, - -28.231606, - 22.48473, - 47.958393, - -22.541676, - -52.717, - 47.226242, - 51.068775, - 50.124294, - -19.31264, - -28.148346, - 44.945942, - -42.825386, - -47.410145, - -29.877638, - 7.788443, - 46.406788, - 48.53827, - -5.8827424, - -35.965088, - 31.687206, - 26.455547, - -10.623615, - -40.76841, - -4.8219795, - -18.956379, - 40.537342, - 3.2403526, - -5.107883, - 63.37852, - 56.515934, - 45.10189, - -42.131943, - -8.153443, - 48.401085, - 0.8158772, - -29.768171, - 7.324227, - 36.802402, - -32.52056, - 24.88585, - -39.654697, - 12.912951, - -18.497515, - 54.15107, - -31.347673, - -48.55776, - 38.79396, - -53.367012, - -27.265852, - -6.4607854, - 13.661437, - 30.355759, - 58.71805, - -25.208595, - 3.1252713, - -41.868053, - 38.756367, - 59.531124, - 47.890396, - -17.98721, - 36.084118, - -13.634508, - 39.42093, - 18.820461, - -30.356327, - -49.554066, - -29.197483, - 55.4732, - -43.75864, - 60.896523, - 56.4989, - -33.627903, - 48.16754, - -28.459074, - 13.827141, - -11.594272, - 47.840588, - -33.894855, - -5.484721, - 1.4320391, - 60.467564, - -13.830222, - -26.233013, - 31.210938, - -36.616104, - 12.191131, - 49.345882, - -53.822376, - -44.628685, - -2.3659778, - -19.861258, - 58.657722, - -44.997097, - -37.276833, - 25.89013, - -10.061741, - -32.693943, - -1.0874362, - -19.60824, - -38.45829, - -15.162608, - 16.015558, - -38.214413, - -18.354656, - 20.328302, - 37.406326, - 45.95487, - 27.38124, - 46.569256, - 15.950565, - 11.055151, - 14.368044, - 40.19783, - -38.585472, - -46.83205, - 15.940281, - 48.277, - -38.63723, - 48.961315, - -7.0522246, - 28.371725, - -23.330465, - -50.323666, - 46.0185, - -44.325756, - -35.855865, - -18.20647, - -7.6315646, - 59.281708, - -53.65577, - -33.435104, - 17.925577, - -48.104885, - 15.851762, - 58.10119, - 41.329796, - 60.929493, - -56.60167, - 49.080627, - 1.6593695, - -1.9387143, - -40.07204, - -32.65333, - -11.705121, - 18.05479, - 52.442997, - 25.193996, - 28.471008, - -23.656853, - 13.627039, - 10.705277, - -8.970166, - 18.345266, - 49.169395, - -44.30361, - -36.139923, - 31.360231, - 50.538635, - 50.209198, - -48.951195, - 29.55601, - -21.136202, - -21.265085, - -41.619125, - -34.370987, - -33.846046, - -16.407732, - 4.6381655, - 16.084637, - 38.928047, - -41.55955, - -33.503048, - 57.835648, - 25.167212, - -31.4103, - 51.287056, - -41.654114, - -43.58004, - -40.584736, - 54.942364, - 12.821454, - 24.011929, - 31.13371, - -20.22472, - 17.79019, - -24.227406, - 15.120078, - -41.80841, - -43.47724, - -39.450546, - 19.99747, - 15.529904, - 45.565693, - -28.54648, - 56.076347, - 19.791918, - -55.67927, - -41.094902, - -27.870377, - -41.256504, - -11.352515, - -46.313496, - -46.637367, - -18.041924, - -18.929783, - -38.786, - -18.44551, - -45.789707, - -9.525661, - 12.873272, - 47.481384, - 16.33122, - 22.366423, - -35.619858, - 54.362545, - 2.4414933, - 25.421625, - 53.08307, - -29.50228, - -34.186226, - -37.5602, - -36.813686, - 20.893494, - 19.51864, - -5.632542, - 11.30494, - 2.9794881, - -45.209023, - -31.500145, - 12.285144, - 55.395947, - 32.21779, - 3.3651476, - -51.390118, - 36.722115, - 58.705086, - 30.618706, - -16.802534, - 2.6427522, - -42.6501, - 18.079544, - -6.2927465, - -46.257027, - -23.344019, - -44.347576, - 23.218187, - 48.1655, - -8.361857, - 2.7036908, - 6.7110343, - -1.3755705, - -17.157036, - 39.944633, - 51.72719, - -18.672327, - 57.632236, - 18.106745, - 10.5324745, - 40.575005, - -23.71305, - -55.86017, - 18.966919, - 31.810251, - -34.78124, - 49.25058, - 52.102192, - 17.243034, - 45.8365, - 4.507162, - 41.15665, - 21.953882, - 45.06367, - 50.470036, - 12.895756, - 13.899155, - -18.132378, - -35.732872, - -8.266737, - 55.406406, - -34.572502, - -5.21674, - 34.643066, - 9.645002, - -39.5287, - -21.22969, - 8.84193, - -6.6976542, - -26.256298, - -42.24456, - 44.60481, - -34.82042, - -45.549995, - -48.911743, - 7.766448, - 40.65631, - -7.7231383, - -46.522186, - 49.13915, - 10.753302, - -36.61156, - 60.835865, - 29.961258, - -37.112934, - -10.257471, - 31.980536, - 27.892096, - 45.230713, - -16.951935, - 29.803865, - -5.4085217, - 25.779589, - -19.065458, - -22.693665, - 56.531708, - -44.68482, - 23.273722, - 2.6259706, - 37.571487, - -29.42919, - -30.674974, - 65.59185, - -16.563955, - 38.63354, - 3.1031818, - -43.12336, - -57.728573, - 13.034079, - 46.481388, - -48.13403, - -27.2014, - -10.165841, - 46.68557, - 49.08016, - -15.232134, - -53.29632, - -7.2577205, - 14.032702, - -31.430248, - 23.928396, - 12.880273, - -27.285727, - -42.180077, - -15.3164215, - -6.620774, - -47.9015, - 11.016033, - -37.857563, - 45.88543, - 35.342182, - -30.674488, - -23.828165, - -37.931133, - -31.504562, - -47.091602, - 17.860275, - -6.3850884, - -16.122215, - -3.119768, - 47.523766, - -28.833778, - 12.732019, - -7.503495, - 47.32294, - -26.526276, - 16.701687, - 34.786186, - -42.6552, - 14.009928, - 18.774673, - -37.64758, - 43.796356, - -14.742917, - 49.697426, - -19.793585, - -53.133896, - 37.340225, - -22.841238, - 2.979671, - -51.962658, - 54.74676, - 41.444393, - -15.730567, - 30.604837, - -44.145668, - 8.863162, - 60.995483, - -44.98284, - 9.040379, - 24.042429, - 25.076736, - 30.519775, - -47.514927, - -40.143944, - -29.305222, - 12.896586, - -30.795404, - 25.85091, - 19.948092, - 20.974108, - -19.33182, - -50.141148, - 66.21081, - -49.376717, - 35.24333, - 18.678154, - -43.173016, - 57.111607, - 48.019886, - -4.780475, - 49.229675, - 52.86177, - -32.70729, - -13.887877, - 19.741331, - 52.435543, - -34.81799, - -22.524883, - -12.82885, - -46.24378, - -29.501112, - -5.0456986, - 31.022472, - -36.60279, - -47.141144, - -11.186273, - -36.80437, - 18.250782, - -8.335074, - -34.644447, - -11.501718, - -9.537627, - 24.104692, - 51.07264, - 61.549442, - 1.9518297, - -42.394825, - 42.282997, - -11.57566, - 15.377659, - -29.24355, - -47.198746, - 33.828228, - 14.915583, - 56.65421, - -53.3065, - 19.173527, - 43.26525, - 62.865932, - -37.63518, - -42.896793, - -15.898649, - 1.5646982, - -46.367523, - -51.349506, - -47.68558, - -10.65396, - -49.790844, - 39.05222, - -27.663815, - -7.4475813, - 41.410755, - 38.42368, - -45.67376, - -20.76551, - -29.445877, - -23.031208, - -37.19868, - 4.37751, - -31.336668, - 8.212411, - -45.453674, - 5.134725, - 14.638772, - -6.1798644, - 1.3949758, - 15.7138605, - -50.869877, - 56.985188, - -44.030884, - -44.016224, - 57.739395, - -24.544922, - 17.170551, - 50.66318, - 48.850945, - -50.093754, - -24.092436, - 17.347712, - -19.335144, - 36.300686, - 39.32889, - -6.3253975, - -25.326218, - 31.211935, - -6.78382, - -50.14104, - -49.846096, - 39.84183, - 24.028929, - 40.939545, - 48.37629, - 25.452175, - -37.92231, - -20.485857, - 51.559708, - -52.251915, - -33.647213, - -9.488163, - 5.1724906, - 46.41191, - -14.337521, - 52.77315, - 27.314432, - -29.272839, - -42.127716, - -45.652252, - -43.886417, - 12.80685, - -32.545635, - 4.3835373, - -26.112938, - -39.891853, - 55.190277, - -24.499039, - -43.219402, - -9.765382, - 12.646676, - 17.87465, - 38.03023, - -42.07617, - 9.909096, - 59.759964, - -46.420776, - 22.181377, - 48.908253, - 29.485273, - -1.1384851, - -36.030876, - 49.40172, - 24.128727, - 49.71215, - -16.348925, - -31.277052, - 40.702366, - -16.921326, - 57.916637, - 16.221085, - -9.602789, - -13.009839, - 51.67792, - 30.202019, - -35.251965, - 57.654526, - 34.585587, - -9.096614, - 25.097984, - 2.5471764, - 61.278408, - -16.914656, - 8.574884, - 23.422081, - 39.786133, - -44.31037, - -28.402678, - 21.295237, - -18.734745, - -28.94602, - 25.800558, - -42.23089, - 33.389534, - -23.825924, - -37.813118, - -45.167847, - 5.8968763, - -22.952112, - 24.184378, - -45.96224, - 54.46313, - -36.155823, - -37.039566, - -54.980534, - -33.989525, - -15.194927, - 46.068615, - -28.049438, - -30.086702, - 48.63991, - 35.595135, - 20.787077, - 29.313784, - -1.0110728, - 8.255305, - 50.553566, - -17.443365, - -12.567652, - -42.189774, - -40.062756, - -52.438923, - -24.236567, - -35.002357, - 54.101334, - -54.033554, - -24.72687, - 31.629358, - 15.709119, - -25.166414, - -7.6725793, - 51.79524, - -51.84964, - -27.801344, - -26.129557, - -52.71393, - 10.710161, - -13.981046, - 42.513103, - -6.9841313, - 39.504147, - 20.738512, - 47.29459, - -10.861444, - -48.87469, - -50.175316, - -35.826897, - -31.892145, - 51.208797, - 61.06654, - 23.318872, - -35.0245, - -3.1801224, - 59.059566, - 41.391323, - 56.262905, - -31.190088, - -35.842033, - -44.238426, - -46.75248, - 46.51155, - -24.869604, - -48.693672, - 27.989025, - 57.75085, - 35.379726, - 34.078384, - 40.129883, - 51.36741, - 44.124233, - 38.459312, - -19.088882, - 42.09723, - -32.190376, - 20.918581, - -25.71062, - -44.228165, - -22.265373, - 54.85633, - -31.057253, - 11.446291, - 48.645084, - 33.95719, - 10.624376, - 34.440483, - 13.654009, - -52.42964, - 23.009165, - 0.9160498, - -33.267147, - 1.6248351, - -36.05716, - -7.2311153, - 45.367435, - -12.550289, - 15.268804, - 57.216434, - 20.570063, - 16.519796, - -20.162544, - -39.967007, - -7.045784, - -13.788036, - -6.436385, - -21.87981, - 49.150986, - -31.409056, - 40.020714, - 1.7450867, - -37.63322, - 36.398586, - -31.830273, - -16.392036, - 19.578056, - -30.145031, - 9.273592, - -22.6749, - 49.763367, - 44.87128, - -31.353453, - -45.900715, - -54.722725, - -9.91315, - -33.41616, - -29.682985, - 27.913883, - 5.769484, - -21.902475, - -28.206575, - 34.063007, - -38.86974, - -19.676825, - -27.176733, - -49.362682, - -44.44146, - 5.3805246, - 2.333401, - 14.209792, - 29.130596, - 40.65309, - 7.33986, - 33.885105, - -38.69257, - 42.74865, - -51.24567, - 46.39128, - 63.1983, - -1.2716205, - 2.7853732, - 26.978062, - 18.276062, - 20.191584, - 25.01299, - 4.6009235, - 9.839586, - 11.750173, - 7.9382405, - -10.997008, - 21.737896, - 49.79338, - -29.136082, - -29.750324, - 54.410885, - -35.14978, - 63.605362, - -51.056225, - 39.934895, - 17.519335, - 17.778656, - 15.492881, - 7.7321296, - 8.952756, - -19.130821, - 40.63617, - -37.452244, - 20.371246, - 30.811249, - -9.127035, - -5.5860677, - 1.1558152, - 47.465935, - -24.740665, - -47.064148, - -54.69983, - 47.272655, - -27.990711, - 63.177612, - -7.06102, - -43.44754, - 24.795843, - -4.7836714, - 41.66488, - 1.8769449, - -24.956768, - 51.543095, - 12.356418, - -53.22971, - 38.820065, - 4.2263513, - -7.9959974, - -23.705156, - -6.0662427, - -37.926384, - -41.1264, - -27.350927, - 31.053217, - -9.149289, - -37.36757, - -16.533398, - 40.088383, - 7.0387945, - -22.092422, - -30.736622, - -44.570576, - 60.45724, - 52.433907, - 9.723743, - -15.802876, - -49.361073, - -25.432766, - 38.667847, - -28.812906, - -22.672857, - -35.77931, - -16.137821, - 27.65755, - 57.766346, - 42.41823, - 26.112234, - -39.176956, - 16.072603, - -48.2029, - 19.677572, - 17.410772, - -6.2585354, - 7.9719267, - -53.251343, - 12.662249, - -9.297528, - -36.831997, - -44.267094, - -42.660313, - 18.940567, - 20.549877, - -19.017382, - 33.992294, - -34.603184, - 56.381645, - -15.977553, - 53.579098, - 7.4309235, - -35.853523, - -15.548051, - -44.87483, - -51.507732, - 19.506048, - -52.502518, - 59.620773, - 8.936648, - 48.37667, - -32.07786, - 14.902041, - 35.445507, - 46.157833, - 49.838924, - -48.87661, - -45.17925, - 29.182852, - -22.362936, - 38.542347, - -10.216267, - 22.10423, - -31.37848, - -2.6893454, - 51.905163, - 21.657618, - -5.704888, - -20.502497, - 30.625587, - -24.823088, - 13.691204, - 28.035511, - 23.045893, - -50.661304, - 43.841885, - -50.370255, - -47.05539, - 56.74711, - 30.591192, - 51.738125, - -11.594388, - 17.440117, - 51.774147, - -23.063238, - -9.929121, - 43.796253, - -38.724506, - 47.406204, - 7.212067, - -41.108536, - -38.19549, - -21.799944, - 14.5572, - -36.380856, - -22.186844, - -33.03146, - -47.564026, - -6.8207917, - -0.203547, - 26.660809, - -45.418346, - -32.97913, - -29.1495, - 41.08887, - 2.4019308, - -34.859055, - 14.605348, - 5.080946, - 62.321815, - 30.915781, - 49.839912, - -13.132145, - -12.614871, - 48.11404, - -33.125538, - 37.93922, - -30.265446, - 4.331932, - -24.302145, - -38.971054, - -6.6933794, - 32.7655, - 58.07306, - 50.09836, - 23.97021, - -44.289158, - -16.34018, - -42.824986, - -37.11219, - 54.922394, - -38.334126, - 22.242004, - -12.324585, - -28.60194, - -35.730442, - 52.352432, - 14.265632, - -36.50344, - -27.018137, - -30.541101, - 53.529724, - -7.2380333, - -40.239014, - 7.0784307, - 20.74278, - 2.5284033, - 25.636118, - 4.454403, - -49.050774, - -23.530384, - -23.313187, - 38.338932, - 9.910433, - -22.21815, - -25.737848, - 51.55675, - 37.103165, - -17.621637, - -31.606474, - -46.921032, - -12.631271, - -34.711258, - 14.978659, - -43.354763, - -22.281115, - 45.54423, - -33.235416, - -43.594814, - 53.86991, - -15.313636, - 47.012283, - -21.579958, - -46.839928, - -45.437263, - 60.093002, - 11.213355, - 32.56739, - -27.061964, - -20.385843, - 15.526145, - -8.932405, - 60.606064, - 9.335806, - -38.67932, - -8.953644, - 39.772743, - 18.62211, - -6.674851, - -41.675705, - -6.503544, - 23.033293, - -5.5465455, - -36.837105, - -4.2590623, - 48.95457, - -42.01228, - 10.529721, - 13.965547, - -3.9804885, - 44.68764, - 48.906673, - 47.63983, - 21.258057, - 62.788, - -6.2482433, - -48.024345, - -12.530503, - -39.613857, - 10.181149, - -34.855972, - 17.598188, - -46.561874, - -17.363302, - 1.3672223, - 32.536667, - 10.24864, - 5.8206697, - -45.638084, - -0.31910038, - -10.62197, - -21.206648, - 38.030407, - -34.547794, - 21.86292, - 56.60054, - 20.400032, - 27.48848, - 2.2426317, - 5.0682087, - -18.876629, - 27.914957, - -17.48441, - -20.422543, - 16.509165, - -27.667318, - -48.115654, - 40.073948, - 60.232296, - 9.352251, - 56.806816, - 2.808305, - -16.641712, - -19.632275, - -41.143227, - 6.707939, - 45.64992, - 19.51436, - -41.17226, - 39.266872, - -6.392582, - 62.91453, - 18.935217, - 46.280994, - 50.306213, - 53.805332, - -13.137335, - 50.443317, - 53.03957, - 44.309578, - -30.403149, - -33.03263, - -30.970875, - -50.138874, - -14.373312, - 8.379798, - 54.42772, - 2.4920332, - 1.7612854, - 34.023724, - -28.959257, - 61.473892, - 50.651646, - -42.69843, - -18.173891, - 27.97626, - -11.489995, - 59.39454, - -50.46992, - 47.18665, - -22.095016, - -0.99369574, - -48.586517, - -28.31348, - 2.79127, - -32.614243, - 16.340908, - 20.619595, - 32.39917, - 59.94177, - 23.400663, - 42.23158, - -40.497093, - 14.445518, - -43.79571, - 56.222717, - 26.900372, - -34.05016, - 59.36177, - -48.04673, - 57.550297, - -10.504851, - -45.725693, - 12.496445, - 60.801098, - -49.58257, - -20.070473, - 57.966537, - 28.753572, - -35.82806, - 55.964886, - -44.020023, - -23.90992, - 45.870426, - 21.319304, - -27.236769, - -37.01328, - -19.117485, - 38.638237, - 49.729176, - -39.115543, - 17.625916, - 11.094263, - 7.11425, - -29.740028, - 18.546873, - 58.080826, - -34.482994, - 37.20064, - 9.897873, - -27.855904, - 24.480858, - -52.830154, - 58.289707, - -48.07056, - -19.067713, - -21.63138, - -40.71425, - -4.696033, - -4.852559, - -17.729515, - 8.527567, - -29.865084, - 25.88273, - -46.45139, - -9.0318775, - 63.36231, - 50.890648, - -8.188348, - 16.88663, - 13.06387, - -25.576069, - -26.325634, - -23.095638, - 29.025854, - -40.87854, - 45.88053, - -38.34742, - -13.60535, - 3.984353, - -1.1919637, - -50.887096, - 50.78542, - -34.409237, - -46.677288, - 5.320594, - 14.373686, - -45.882183, - -32.426746, - 43.456127, - 2.8495433, - 28.731657, - -2.2277532, - 50.339493, - 61.357586, - 11.930037, - -42.132465, - 56.755314, - -18.868166, - -14.928126, - 13.779188, - 23.310764, - -42.33495, - 19.120626, - 18.960714, - 25.783823, - 17.941885, - 55.462612, - 10.820086, - 58.314003, - -45.8806, - -21.790516, - 53.49091, - -51.873066, - -8.934254, - -35.644184, - -43.46856, - -26.787775, - -12.61641, - 11.278602, - -12.760466, - -35.958366, - -9.973649, - -5.3010283, - 8.342169, - 58.012913, - 7.6059, - -7.4377956, - -46.84005, - 49.449314, - 37.930157, - 12.515653, - -54.239532, - -39.886326, - -43.70516, - 57.86416, - 8.195707, - 52.26376, - -40.78544, - -46.046387, - 1.8771796, - -8.241421, - 47.072803, - -12.890557, - -23.360226, - -23.913462, - -10.10402, - -33.456993, - 48.17513, - -34.200912, - 22.029692, - -34.14632, - -40.844006, - 44.906193, - -29.91782, - 4.4929285, - 56.61765, - 56.60834, - -17.537066, - -30.420895, - 56.066643, - -19.92304, - -2.2965894, - 56.162464, - -41.66086, - -57.68235, - 3.6962993, - -19.05618, - 5.52023, - -48.503033, - -18.99317, - 53.77512, - -14.034199, - 47.758217, - -29.327738, - -27.266224, - 50.96032, - -49.10616, - -4.6537275, - 58.05466, - -8.695738, - 15.926025, - -35.493626, - -52.898724, - 4.0713243, - -16.14875, - -40.76337, - -36.11573, - 41.446438, - -3.7340183, - -15.154654, - 58.41072, - 11.970405, - -16.320389, - -19.673914, - 11.040503, - -36.72977, - -9.829185, - 35.8429, - 47.047108, - -37.2606, - 54.494556, - -52.1362, - 13.273838, - 7.288217, - 47.79968, - -20.01322, - -18.065994, - 8.75742, - -54.428818, - 18.142248, - -9.159126, - 29.14241, - -46.200623, - 17.28087, - 13.877883, - -13.831901, - -21.605253, - 21.1013, - 59.32574, - 13.981468, - 40.920834, - 55.53207, - 44.559975, - -10.860374, - 10.2113, - 28.748735, - 10.333969, - -37.78618, - -45.533035, - 53.77833, - -8.867661, - 12.468114, - 3.0369818, - 32.079, - 47.351242, - -55.4472, - 5.742987, - 24.300056, - -21.27348, - -8.906268, - -34.02309, - -0.9226989, - 32.861256, - -5.918376, - -30.542126, - 38.30059, - 48.4094, - 33.499294, - 1.5139743, - -5.9578004, - 22.857521, - -42.396126, - -16.095537, - 29.347134, - 4.3284388, - 45.721344, - 26.680521, - 45.999187, - 49.048878, - -21.678917, - -48.91647, - -11.771681, - -10.15981, - 39.29256, - 8.132189, - 32.81585, - 11.17274, - 22.79567, - 2.0400486, - 19.547178, - -4.0862207, - -9.854177, - -23.889015, - 26.376568, - -54.596252, - -22.090435, - 32.12724, - -50.986782, - -34.252632, - 59.9222, - 45.969334, - 47.935875, - -4.5817585, - 17.717125, - 32.523216, - 19.772266, - 57.007023, - 34.043217, - 30.42877, - 10.665481, - -16.827753, - -38.59416, - -32.974155, - 15.195456, - -36.174, - -45.269844, - 11.543438, - -19.309122, - -28.692097, - 53.714108, - -18.300999, - -49.752243, - -10.5037985, - 34.008293, - 18.401154, - 33.648438, - -44.20961, - -39.52826, - -27.136961, - 59.613667, - 31.749115, - 7.0795293, - -34.181965, - -37.106304, - 19.923655, - 14.908174, - 52.849945, - 10.556734, - -48.20029, - 9.239984, - 15.951407, - -7.4418893, - -28.779457, - -35.19683, - -54.1994, - 20.179276, - 31.14273, - 0.258186, - -2.1609035, - 61.664543, - 14.35011, - -26.758255, - -54.634964, - 14.368874, - -43.92253, - -42.005432, - -39.611347, - 9.892005, - -39.611637, - -24.87918, - -22.471472, - -38.19811, - 30.838337, - -36.996124, - -4.4758306, - -46.204945, - 43.08786, - -24.678703, - -50.613956, - 49.605602, - 6.150114, - 63.165108, - -20.649567, - 47.894882, - 51.314476, - 44.60029, - 6.031961, - 8.659726, - -15.612729, - -9.729161, - -28.362564, - 10.755605, - -36.588448, - 8.7123785, - -12.811854, - -0.94040644, - -45.534595, - 12.619259, - -44.44866, - -4.227074, - 44.015842, - -22.860474, - -30.753082, - 39.41502, - 0.080250725, - -15.496077, - 20.854275, - -10.390649, - -35.993237, - -36.425526, - -5.6656046, - -36.567635, - 59.81665, - -11.675889, - 14.897927, - 41.209156, - 8.117931, - 6.539579, - -12.951042, - -30.48289, - 47.579025, - 56.48261, - -38.7589, - 46.025146, - -36.49073, - -6.355229, - 58.74744, - 46.206974, - -52.00866, - -31.978811, - -43.13706, - -7.6462755, - -31.936037, - -19.532629, - 53.145702, - 7.7298007, - -36.42381, - 12.733178, - 23.083542, - 60.687424, - -38.00677, - 38.102413, - 39.646805, - -46.434704, - -42.961407, - 52.38563, - -16.082205, - -50.200237, - -29.59413, - -10.404932, - -27.002981, - -20.752146, - 34.14185, - -18.822731, - -38.39936, - -34.192577, - -23.879477, - -49.73623, - -20.585733, - 31.320894, - 6.8278956, - 14.610548, - 40.573475, - -19.3257, - -32.563313, - 7.079915, - -7.734347, - 21.593582, - 41.94092, - 22.709345, - -8.220228, - 30.75048, - -50.261745, - 23.351994, - 10.662044, - 6.3287606, - -44.1901, - 20.248484, - 39.690254, - 34.33151, - -21.206255, - 17.894573, - 53.560726, - 18.915913, - -50.147823, - -56.14451, - 50.696335, - 19.135786, - 0.011293956, - -41.132812, - -7.490298, - -6.7789235, - 21.208382, - 5.4172053, - -44.169758, - -47.881756, - -28.388693, - -12.397968, - 29.16581, - -0.9005222, - 58.507614, - 40.03086, - -17.01861, - -49.997864, - -11.5951185, - -38.691113, - 24.29299, - 48.50645, - 38.79774, - -53.174366, - 15.59622, - -8.326396, - 0.79674417, - 10.643132, - -44.02579, - 5.560217, - 0.5841107, - 24.635311, - -28.108793, - 13.113659, - 62.77733, - -20.166492, - 47.435825, - -15.611658, - 18.401346, - -38.040787, - -8.663238, - -30.962019, - -8.084352, - 43.003845, - -39.750137, - 46.27362, - 14.899461, - -45.082096, - -47.16861, - 24.252523, - -4.7970433, - 5.36986, - -16.89367, - -26.904469, - 31.625498, - 10.970106, - 51.867313, - -17.731619, - -34.483925, - -43.073074, - -6.7949033, - -27.989662, - 2.5174408, - 34.368248, - 12.8087, - 35.39813, - -25.524998, - -46.526306, - 53.752186, - 55.804855, - -54.849133, - -40.10975, - -11.253943, - 15.975605, - -24.282412, - -36.69884, - -9.612953, - 27.581682, - 1.6741688, - -53.5042, - -27.687584, - 16.295555, - 3.6958573, - -28.30938, - -35.854397, - 26.508045, - 17.794357, - 30.6338, - 47.806313, - 10.886147, - 56.805237, - -40.808376, - 18.907486, - 49.249695, - -38.4294, - -5.0891867, - -45.114822, - -46.690304, - 49.522606, - -25.18432, - -36.175987, - -41.517033, - -33.290382, - -15.035485, - 61.757652, - 3.8529873, - 61.630924, - -54.139446, - -25.219833, - 39.668633, - 10.995691, - 23.637348, - 33.6961, - 51.79226, - 14.72486, - -53.989174, - 28.194004, - 53.427227, - 45.15016, - 36.015182, - -34.2908, - 43.020264, - 7.9172506, - 54.577732, - -48.755344, - -49.55056, - -39.571285, - -40.278057, - -51.21703, - 18.002365, - -3.3571925, - 19.580015, - -8.731081, - -6.0078135, - 31.860546, - -28.372087, - -0.10420398, - 19.054085, - 37.094307, - -11.813869, - -28.535112, - -1.1245881, - 58.735332, - -40.870914, - 26.428055, - -52.076916, - -16.299625, - 12.898047, - -51.801567, - 35.940807, - 30.280912, - -27.921608, - -36.991142, - 63.004868, - -23.981367, - 47.676117, - 43.803253, - -35.73722, - 52.02361, - 0.08228831, - 57.569775, - -31.23627, - 4.8372564, - 2.4959075, - 6.9097495, - 24.6171, - -36.47172, - -11.448383, - -3.8125634, - -20.261177, - 51.3331, - -4.775729, - 40.77166, - -24.145273, - -0.46443436, - 48.259228, - -45.570045, - -29.613533, - -40.73366, - -19.412077, - -11.283554, - -47.05097, - 49.969627, - -48.772636, - 25.599476, - 36.618427, - -10.298156, - 14.019283, - -43.35723, - 55.361397, - -10.978807, - 51.953743, - -53.829735, - -8.411927, - 15.602155, - -13.247851, - -15.053305, - 40.71827, - -13.399857, - -47.515026, - 62.178337, - -4.658773, - 1.1374025, - -8.963649, - 25.336575, - -29.961985, - -12.003402, - -17.52331, - -50.23115, - 27.973917, - -48.06655, - 39.666965, - -9.277499, - -7.6838555, - 23.369677, - 6.171623, - 26.484608, - 7.0410976, - 19.369898, - -33.914284, - 33.43409, - -15.22937, - -21.86168, - 20.71207, - -7.6405187, - 12.614038, - 17.452501, - 55.207115, - -31.572515, - 32.183567, - -50.991158, - -38.40225, - 16.695406, - -52.86785, - -35.325897, - 18.572897, - -51.80827, - -35.83179, - -41.270184, - -36.710674, - 6.0333753, - 55.5641, - -49.167038, - -21.823997, - -1.3200667, - 5.044943, - -40.638805, - 51.27627, - 47.339336, - 16.012442, - -27.684992, - 63.347527, - 39.062187, - -12.411886, - -41.362526, - 9.572269, - -24.7866, - 26.459038, - -17.990955, - -40.258007, - -2.3985894, - 54.67712, - 2.9941561, - 65.51466, - -37.48171, - 17.726252, - -23.877874, - -34.57765, - -0.9994553, - 45.10427, - 17.785444, - -34.842422, - -51.40557, - -39.0015, - -14.16722, - -31.760511, - -16.35767, - -36.74732, - 47.36583, - 35.328148, - 20.736986, - -50.34398, - -5.775708, - -32.659416, - 30.716692, - 24.382677, - 58.147617, - -19.314493, - -3.8920984, - 16.1644, - 64.86492, - -10.574449, - 19.621206, - 8.682678, - -17.94723, - -24.707636, - -20.651194, - -5.6782784, - -13.584895, - -52.063236, - 32.677113, - 55.061314, - -26.427645, - -33.76177, - -50.93683, - 38.546684, - -14.214475, - 43.151165, - 1.4400622, - -35.708652, - 26.161028, - -41.237144, - 44.980778, - 25.263475, - 16.929596, - -50.64484, - -48.196377, - -10.817454, - -2.0928724, - -25.303522, - 47.840103, - 39.76294, - -23.521646, - 49.251343, - 52.69105, - -43.41168, - 0.50536364, - -41.631573, - 19.154146, - 49.939175, - 46.95092, - 26.26559, - 19.381176, - 12.624142, - 13.547113, - -15.368924, - -44.33141, - 17.735638, - -49.86946, - -25.189764, - -41.6564, - 5.6944747, - 28.887644, - 54.523384, - 11.9049635, - 64.17483, - 19.661798, - -40.866665, - 7.287593, - -48.861267, - 22.103119, - 27.097654, - 58.47151, - 12.937629, - -37.111736, - -49.37285, - -0.5269812, - 50.23622, - -37.09859, - -33.893284, - 18.126286, - -41.025192, - 19.819803, - -2.1707618, - 14.775703, - -27.523653, - 39.812546, - -37.509644, - -48.43532, - 59.636997, - 57.34273, - -37.623196, - -40.202778, - -55.58907, - -41.903214, - 16.772926, - 3.6852949, - 25.670559, - 26.078526, - -49.322422, - -9.049681, - -18.721113, - 48.26851, - 17.1552, - -16.408047, - 9.536311, - 21.02507, - -42.958614, - 12.836097, - 6.9077144, - 13.885367, - -52.688995, - -29.522964, - 25.294838, - 0.9785223, - 42.70037, - 15.134995, - -42.372177, - -30.956533, - 1.8828108, - -15.489649, - 49.12623, - 59.67211, - 10.278181, - -45.431026, - -21.36634, - 47.292377, - 47.805153, - -32.821945, - 3.350846, - 10.675423, - 46.018627, - -27.676836, - -30.13521, - -31.987688, - 2.7699895, - 29.804829, - -4.7174063, - 8.834031, - -30.901245, - -20.815348, - 57.51465, - 37.074707, - 14.13684, - -47.19078, - -45.82224, - -36.344696, - 64.22567, - -46.568104, - -2.3207862, - 10.008406, - 40.90623, - -45.59506, - 42.02211, - 36.001675, - -13.1443, - -43.422806 - ], - "xaxis": "x", - "y": [ - -10.254759, - -20.803589, - -22.326504, - -8.559602, - 22.728033, - 7.8286805, - 23.284092, - 21.800117, - -20.467894, - 22.159718, - -3.7095485, - -16.367886, - 34.67725, - 29.896206, - 6.133116, - -10.627376, - 0.20705454, - 8.674217, - 25.905638, - -3.9541492, - 9.192532, - 25.749458, - 39.722248, - 13.600263, - 7.8999453, - 18.938295, - -7.791385, - -2.1101115, - -17.816854, - -27.949192, - 10.707973, - -5.9476533, - -0.62792206, - 21.421028, - 17.02401, - 3.4177885, - 23.633503, - 9.072081, - 2.5558534, - -10.392384, - -21.783358, - -12.137919, - 8.247171, - -23.29184, - -18.170088, - -27.218586, - -29.326565, - 12.92886, - 7.4292397, - 11.001149, - -35.47235, - 0.2254613, - 26.266212, - -12.614066, - -32.542274, - 6.058426, - 14.819815, - 25.03953, - 4.199548, - -0.6994232, - -29.048313, - 8.901868, - -3.3929446, - -18.953293, - -16.318848, - 1.29799, - 24.970749, - 14.150794, - 0.27214336, - 7.6804514, - -22.118223, - 4.9480743, - -22.427275, - 0.9335098, - 26.755693, - 26.929127, - -26.440922, - 9.1828, - 1.5617585, - -19.087698, - -14.938796, - -2.3146381, - 13.022359, - -21.471975, - 25.554472, - 2.532362, - 23.373753, - -13.859794, - -25.318462, - 22.522005, - -32.54127, - -10.261337, - 2.3437028, - -11.51763, - 24.106895, - -28.920532, - -2.2139447, - -14.537146, - 9.316687, - 9.6741905, - 12.820546, - 4.9088416, - -28.30168, - -17.468342, - -17.185091, - 10.214211, - -20.446613, - -11.397742, - 8.161042, - 27.62886, - 26.281322, - -29.872732, - 5.007877, - 6.2455893, - 5.951754, - -19.64737, - -1.2898456, - -8.973769, - 0.16790108, - 12.575957, - 15.638516, - 9.72588, - 13.993413, - 22.678474, - -17.482075, - 14.391562, - -19.213398, - 17.073126, - 30.481924, - 20.813839, - 32.03464, - -26.55557, - 12.0553255, - -16.22469, - -18.176107, - -3.6315196, - -19.35426, - 20.519714, - 4.681029, - -24.165535, - -17.097263, - -23.540205, - -22.659904, - -30.161833, - 28.830767, - 14.3665, - 0.061168052, - -32.789925, - 4.0007343, - -27.456821, - 23.813591, - -30.553509, - -24.490698, - -19.611755, - 29.56973, - 21.227903, - 7.7406225, - -10.1600275, - 8.52158, - -10.852377, - 5.442065, - 9.661537, - 22.864084, - 4.1424494, - 7.6243353, - 4.249151, - 31.043804, - -10.734537, - 3.844936, - 1.4751459, - -12.852704, - -20.392239, - -6.189112, - -4.5837173, - 1.4175098, - -21.713743, - -13.330445, - 12.867583, - -10.440891, - 3.6453905, - 8.166061, - -11.337284, - 16.828537, - -8.8150835, - -16.43065, - 9.330847, - 20.16529, - 9.5765, - -22.28117, - -9.1425705, - -23.877768, - -10.817816, - 5.1117396, - 7.9826016, - 11.228575, - 17.663988, - 2.542931, - -1.3406546, - -23.981632, - 12.972686, - 4.730411, - 30.063469, - 3.6004014, - 18.804445, - -13.418971, - 30.71818, - -14.152756, - -24.45379, - -11.355663, - 6.520791, - -9.840589, - 21.164257, - -8.373115, - -8.409088, - 9.545558, - 4.881303, - 30.134317, - -32.61165, - -17.390278, - 32.50385, - 19.963877, - 8.090675, - 31.114712, - 30.646704, - 21.478413, - 14.554468, - 20.755419, - 11.230936, - 6.923768, - -13.468946, - 23.0764, - 20.141148, - -15.70016, - 8.499566, - -19.558147, - -10.837732, - 7.830664, - 24.00407, - 6.959669, - -17.884281, - 24.8098, - -24.985989, - -12.053112, - 8.462659, - 18.15951, - -5.462048, - 2.4064643, - 8.999294, - -12.727203, - -13.069021, - -6.154228, - 14.864804, - 1.5735915, - -25.217607, - -11.249722, - 27.957365, - -0.7906725, - 19.460798, - -2.3412774, - 6.4599543, - 2.4203362, - 32.717518, - 28.99686, - -18.920874, - -7.624435, - -23.937035, - -15.694869, - 2.3350112, - 9.491719, - -25.943512, - 0.82049704, - -3.9954906, - -16.211517, - -10.548126, - 33.583965, - 22.352716, - -0.7140172, - 28.585188, - 20.132593, - 10.375427, - -18.380714, - -21.956186, - 18.302557, - 8.7813, - 27.98141, - 5.231712, - -1.274212, - -17.928478, - -17.166925, - 5.588625, - 1.8213869, - -20.784616, - -9.940092, - -11.329836, - 1.3020672, - -5.6699047, - 2.9951952, - 7.513018, - 18.828894, - -8.567718, - -11.798271, - -2.4976819, - -25.911339, - 22.716187, - -10.770047, - 15.819128, - -15.446808, - -32.171726, - 5.0620914, - 12.743932, - 7.1431947, - 20.908062, - 27.65378, - -29.32608, - -12.216588, - 3.5037541, - -35.429436, - -8.023369, - 19.798025, - -4.302394, - 16.329193, - -23.965172, - 8.796663, - 16.477135, - -11.357406, - 32.09736, - 26.441679, - 21.586815, - 30.292624, - -14.503349, - 19.197943, - -14.683218, - -3.407611, - 23.7153, - -14.726069, - -17.214022, - 15.711783, - -8.98979, - -22.324871, - 0.59863055, - 16.493795, - -27.750652, - -28.93897, - -5.3719177, - -23.418943, - -9.659326, - -23.277813, - 16.425425, - -19.531103, - 18.54026, - 0.31460643, - 31.197924, - -14.720505, - -0.26035935, - -21.057713, - -27.277906, - -7.310227, - -15.416589, - -1.605775, - -8.874298, - -13.5169735, - -26.390093, - 0.7872089, - -7.2581453, - 22.63779, - 28.57203, - -23.089176, - -19.599855, - -21.929888, - -10.379873, - -11.895842, - -17.141865, - -16.003376, - -14.515779, - 10.840164, - -26.575148, - 3.1463404, - -3.7059593, - -8.936446, - -23.257317, - 30.278105, - 15.54324, - -31.523523, - -15.298813, - -29.652391, - -9.050367, - 18.134205, - -14.212201, - 10.717227, - 19.883846, - 21.597916, - -19.211506, - 28.315454, - -11.721406, - 16.122732, - -6.269737, - -14.575271, - -20.626392, - -9.711501, - 20.470428, - -8.267473, - 33.287487, - 25.027699, - 15.167711, - 12.847039, - -22.223913, - -13.995945, - -28.966488, - 14.344031, - 7.419209, - -21.779205, - 24.548212, - 23.27041, - -17.763275, - -27.218397, - -36.186253, - 5.0752234, - 0.31401816, - -0.48519766, - 9.704817, - -22.044197, - 28.721743, - 14.702273, - 18.21779, - 16.7961, - 9.027207, - 21.439281, - 25.772839, - 5.9104095, - 18.049044, - 11.854107, - 25.408955, - -1.7761685, - 7.837817, - -11.143075, - -11.487356, - -25.348227, - 20.674139, - -15.303513, - 34.420277, - -6.806543, - 2.799256, - -27.043676, - 32.15406, - 6.988793, - -29.502745, - 5.2307787, - 24.185543, - 17.168627, - -6.9711366, - 28.700588, - -16.839674, - -6.9957857, - 19.155857, - 22.57425, - 4.2664466, - 10.645888, - -2.8677607, - 17.716654, - 33.268223, - 13.592724, - 35.533974, - 35.79897, - -9.217092, - -7.505608, - 16.755838, - 19.649885, - -13.013833, - 2.553211, - 5.488912, - 25.960653, - -14.678428, - -6.362675, - 15.933173, - -25.562366, - -7.9709535, - -19.333553, - 5.761818, - 5.2738123, - 14.799318, - 0.9805258, - -30.191147, - -8.254407, - -9.329842, - 24.331854, - -1.1096494, - -27.81828, - -23.302309, - 10.189425, - -0.9053779, - 14.969123, - -12.578425, - -16.734713, - -25.194714, - 34.912987, - -36.29533, - -0.7015533, - -21.124685, - 33.794212, - -20.977274, - -19.704374, - 23.483368, - -15.128482, - 8.0363655, - 2.2579987, - -16.33133, - 31.233051, - 22.297411, - -11.6483135, - 3.5171926, - 23.886812, - 12.337329, - -19.59588, - -30.116133, - 27.538383, - -19.748474, - -4.7339125, - 19.465944, - -18.429428, - -24.985508, - -24.043522, - 26.484413, - 16.774218, - 5.9628015, - -14.398376, - -23.032887, - -16.154268, - -11.766295, - -27.591204, - 20.015493, - -20.486948, - 7.6020126, - -13.656402, - 14.815331, - -33.948692, - -33.920197, - -9.174384, - 20.629124, - 16.143784, - 8.925708, - 7.7047353, - -21.596968, - 16.84247, - 11.881365, - -22.970503, - 24.66648, - 1.9238061, - 25.418554, - -17.758942, - 3.5172246, - 23.261137, - -8.986503, - 28.923544, - -7.5245304, - -15.130549, - 5.0646152, - 21.07103, - -5.8668604, - -14.940109, - -6.4981833, - -20.06512, - 23.290081, - -11.591567, - -27.786598, - 20.645449, - -5.3597302, - -11.159512, - -13.735753, - 18.798145, - -32.18803, - 8.9016, - -22.157974, - 26.788364, - -16.650103, - 18.377977, - -18.147429, - -24.88111, - 21.901451, - -14.823587, - -0.6368593, - 3.2132275, - 31.100603, - 16.802742, - 20.371767, - -28.899687, - 0.73946625, - 0.94949424, - -14.675726, - -24.362509, - 31.862827, - 23.13797, - 35.12017, - -18.907366, - 24.827017, - 31.66899, - -18.148087, - -24.660992, - 9.816621, - 16.572128, - 25.328583, - -15.456796, - 1.9859632, - 5.658062, - -5.2393093, - 9.180699, - 7.721218, - 3.9763682, - -14.759153, - 8.72019, - -12.5096655, - 4.320076, - 2.0307107, - -12.368451, - -11.865506, - 16.297318, - 0.7318651, - -13.755454, - -21.899122, - -11.081378, - -19.075409, - -13.679028, - 10.51185, - -10.045945, - -2.6716044, - 13.364902, - 20.333702, - 5.9486156, - -30.512154, - -1.8922254, - -14.551722, - -13.595177, - 24.951237, - 15.502925, - -26.033178, - -15.84722, - -0.48769227, - 5.509095, - 25.674028, - 23.005444, - 12.414623, - -7.935221, - 24.642124, - -22.191689, - -19.237648, - 16.660208, - 5.5806613, - 9.362999, - 16.740986, - -14.059228, - -9.914337, - -20.576859, - -10.982109, - 31.096636, - -11.43558, - -17.933233, - -22.175861, - -14.856947, - 26.15921, - -23.924995, - 6.894826, - 4.1693807, - 5.6076837, - -17.656506, - 15.090964, - 1.2161766, - -9.937122, - -27.618727, - -3.5818095, - -14.13704, - 25.846468, - 19.352674, - -22.007416, - 23.278618, - 11.748135, - -22.37126, - -22.028944, - -10.037108, - -25.306404, - -7.7222157, - 3.5807598, - -6.6086307, - -19.699232, - -15.10728, - -17.251148, - 10.148522, - -0.68818355, - 7.5768538, - -17.733555, - -23.194473, - 9.637636, - -2.6014824, - 9.428179, - -10.8705435, - 8.272561, - 18.622755, - 8.240764, - 7.8728004, - 13.976609, - 21.211613, - 10.388335, - -13.317306, - -0.20468314, - -0.7534798, - 16.867065, - -22.69967, - 22.19843, - 29.903488, - -29.479254, - 14.083497, - 0.6598771, - -8.660773, - -7.2729115, - -11.945698, - 23.76637, - -16.428364, - -28.303225, - -11.955685, - -30.203144, - -4.9588523, - 26.250034, - 19.381159, - -16.469437, - -14.535694, - -24.852484, - 12.103588, - 7.8694215, - -8.026257, - -6.199936, - 9.750696, - -14.905879, - -22.042368, - 2.0052595, - 15.873175, - -11.668809, - 7.235856, - -21.42294, - 14.838855, - 16.791052, - -21.904455, - -23.169117, - -20.787516, - 9.315685, - 34.738625, - 10.819606, - 20.726511, - -10.898081, - 31.885904, - 11.005908, - 15.028398, - -3.1344242, - -3.9499974, - 14.654819, - 8.201109, - 17.144817, - -19.819767, - -19.525257, - -4.076858, - -24.730019, - 11.900147, - -1.3390135, - 26.11797, - -2.478072, - -23.535704, - 27.143415, - 17.844543, - 19.694197, - 30.822157, - 11.223421, - 17.761076, - 13.325627, - -13.261404, - 2.2092547, - -13.576142, - -11.716383, - 27.541485, - -18.290712, - -25.388409, - -15.495678, - -32.85601, - 34.832695, - 15.818021, - 12.122141, - 33.150494, - -0.5336322, - -13.886067, - 28.821224, - 20.72354, - -33.77542, - 3.162032, - 17.181808, - 34.996464, - -22.37821, - -4.1373553, - -20.077517, - -16.791988, - -33.790863, - 4.8909636, - -23.158052, - 13.435741, - -22.73552, - -0.6918705, - 27.578976, - -23.911886, - -0.9915625, - 0.41720697, - -28.11098, - -15.606873, - -21.062717, - -15.843517, - 7.1253057, - -12.007193, - -23.275118, - 15.710144, - -13.556541, - -15.989742, - 1.5220636, - 15.600531, - 3.0372694, - -13.601137, - -7.148113, - -24.879805, - -0.8274632, - -11.567605, - 19.323282, - -7.7168093, - -27.03218, - 5.8135962, - -7.6383777, - 1.1989386, - 3.9182017, - -0.47444645, - -25.135891, - 22.896002, - 0.94497335, - 9.556583, - -4.4569497, - 21.02248, - -25.89945, - -18.168903, - 17.865675, - 22.459995, - 12.360714, - -24.076357, - -15.80312, - 21.917862, - 21.659195, - 33.719093, - 19.704102, - -2.2529974, - 31.99901, - -29.042156, - -26.121319, - 33.52397, - 23.902458, - 7.067429, - 26.534893, - 9.6071, - 29.210163, - -23.639217, - 3.7444665, - 1.8415234, - -4.9220414, - 22.216219, - 21.501694, - -17.915682, - -17.60881, - 19.686275, - 16.870352, - -16.338673, - -2.4079158, - 10.431047, - -11.452592, - 20.084156, - -34.98855, - -30.50168, - -1.8533841, - 13.475318, - 22.79436, - -23.127438, - -2.6888435, - -26.898434, - 32.299854, - 9.865102, - -15.889842, - -7.1564, - 14.4235935, - 10.5956135, - 16.942707, - -17.442066, - -6.0696855, - 0.2748501, - 33.509598, - 2.4050539, - 7.209693, - 12.352939, - -0.83113074, - -16.57776, - 26.730667, - -13.937987, - 5.5682783, - 8.4994335, - -12.461162, - 24.32622, - -25.814455, - -19.692043, - 8.181132, - -25.507462, - -16.080286, - -1.2937344, - 18.989775, - 16.529331, - -10.464009, - 11.700205, - -25.712864, - 24.65294, - -5.132745, - 24.787573, - 19.01329, - -9.251707, - -2.7055879, - 14.609039, - 27.475252, - 14.475491, - 0.96339697, - -11.8820095, - 7.1217036, - 31.858027, - 16.848389, - 32.03336, - -13.837845, - -33.480656, - -20.987251, - 30.462563, - -16.143095, - 6.7093077, - -15.854709, - -24.921698, - 16.484713, - -1.7420386, - -23.097334, - 18.896671, - 34.8398, - 10.520301, - 3.5488389, - -18.068623, - 30.076416, - -29.86582, - -8.282391, - -8.46684, - 13.576438, - 3.0699391, - -16.238358, - 2.9773757, - -14.182415, - 17.441216, - -25.85015, - 9.083556, - 22.073168, - 19.385956, - 8.168441, - 13.999631, - -13.918425, - 19.32553, - -19.83609, - 29.535501, - 31.019588, - -6.5198464, - -16.273378, - 31.29178, - -20.836182, - 8.972529, - 14.504229, - -22.65874, - 24.289896, - 0.45974386, - -8.057026, - 7.783574, - -12.477235, - 3.8825731, - -3.5055225, - 15.380986, - 22.033895, - 3.7059414, - -1.0848922, - 0.16963075, - -5.582006, - 11.250292, - 21.913166, - -23.06022, - -13.376665, - -5.6566067, - -5.0115275, - 33.256733, - -27.384535, - 22.36791, - -23.036457, - 3.1787782, - -11.463062, - 16.85544, - 17.925854, - 26.127491, - 34.042473, - 3.7194152, - 11.578919, - -3.056115, - 8.806574, - -12.564382, - 26.605755, - 21.529955, - 25.043688, - 17.78518, - 25.579552, - 27.044067, - -29.090658, - 21.886444, - -29.44567, - -3.69288, - 7.423554, - 19.89922, - -13.892162, - -9.352621, - -23.756565, - -17.759132, - 21.111221, - -15.3389635, - 20.052608, - 8.306711, - -6.695091, - -0.2840251, - 29.565565, - 6.3890157, - 20.825033, - -15.78091, - -3.9792998, - 8.250312, - -4.315795, - 33.91667, - 31.587502, - -4.7497973, - 0.70931256, - 22.03959, - -1.3183376, - -13.819872, - -8.057265, - 2.5191355, - -6.09211, - -1.2537154, - 1.041188, - 6.271001, - 15.563097, - 3.0869732, - 19.476908, - -7.959283, - -20.58928, - 17.528534, - -34.817635, - 26.520325, - -7.863438, - -13.616495, - 34.081158, - -23.899826, - 19.227066, - -0.1847365, - 21.436638, - -21.634756, - 27.98984, - -9.426962, - 17.888885, - 18.802984, - -12.24037, - 25.563747, - -18.85435, - 20.995552, - -25.321373, - 11.024011, - -19.68378, - 30.48236, - -26.103676, - 10.497953, - 3.9857144, - -11.662108, - 14.603634, - 7.0568976, - -9.688497, - -4.4100275, - 2.030001, - -22.706993, - 19.098873, - 31.796051, - -2.4754145, - -26.096392, - -21.39815, - 3.600532, - 28.98958, - -24.192507, - -22.364601, - 24.713762, - -16.769764, - 21.682661, - -1.3566388, - 16.40951, - 8.210962, - -15.716439, - -34.972008, - 26.949068, - 21.239998, - 12.173473, - 20.502365, - -12.030829, - -28.317688, - 4.4826207, - -4.760545, - -10.980467, - 30.730364, - 20.87726, - -17.78651, - 22.801989, - -5.3119135, - -20.541088, - 12.556309, - 1.3681566, - -15.915366, - 23.323511, - -7.8275642, - 1.0861593, - 8.230685, - -17.60057, - 4.390221, - 9.649646, - -16.683647, - -22.447065, - -10.756376, - 27.087646, - 2.2553952, - 5.474195, - 6.01643, - 14.907442, - -19.740395, - -14.250181, - -28.855429, - -21.907415, - -6.474749, - 26.200584, - 23.3236, - 5.0985155, - 23.742764, - -23.47392, - 10.961509, - -9.009804, - 10.729193, - -16.08439, - 24.293411, - -14.420636, - -0.6379835, - -7.351985, - 4.601816, - -21.606695, - 10.600249, - -19.460848, - -1.0193497, - -5.6577854, - 1.2037258, - -19.941338, - -17.019722, - 32.26841, - -20.533716, - -23.794706, - 2.3137836, - 35.702885, - -2.6479704, - 21.060795, - -4.315942, - -22.034695, - 0.85024196, - 13.542582, - -8.745571, - 6.832896, - -10.188763, - -13.390235, - -0.5990197, - -23.021431, - -5.876716, - -11.976225, - 4.2575808, - 27.501059, - 11.98244, - 26.565365, - -1.931646, - 24.216267, - -16.869408, - -8.099275, - -14.887161, - 2.2845645, - 11.149261, - -15.141055, - 27.739674, - -3.0804467, - 5.0789285, - -17.30228, - -3.2769468, - -17.239506, - 4.583181, - -19.281757, - -3.5722063, - 28.793531, - -16.723783, - 25.030203, - 9.832679, - 20.863323, - -19.392942, - -15.235338, - 11.71164, - -24.406261, - -15.53886, - -16.890417, - -19.303434, - -12.302218, - -21.589676, - -14.588415, - 15.091036, - -17.137983, - -23.051016, - -11.65064, - -1.327813, - 4.7823358, - -19.877468, - 29.76316, - -3.8284235, - 21.326263, - -17.971964, - -2.6890767, - -8.098414, - -20.775913, - 11.0288925, - -15.161179, - -13.708067, - 6.9839473, - 9.420364, - 15.523962, - -1.839738, - 18.062141, - 35.796543, - -26.4286, - 4.53065, - -3.197111, - 15.938968, - -5.59304, - -9.126152, - -23.904675, - 8.384921, - -3.4012072, - -5.3693423, - 32.041183, - -33.521553, - 9.530565, - 15.937399, - -27.414234, - -24.713099, - 24.769993, - -8.645808, - -13.032957, - -23.740261, - 8.2281, - -20.86936, - -5.3864436, - -13.534582, - -1.0408515, - 26.929934, - 16.484713, - -3.2705798, - -22.339233, - -17.725012, - 6.1994753, - -13.713904, - 8.064646, - -8.887762, - -27.97785, - 8.281391, - -14.383507, - 1.1649175, - -17.226963, - 23.824167, - -0.03827765, - 21.001068, - -1.6157911, - 1.0350281, - 23.757103, - -2.2386749, - -12.003306, - -5.807004, - 5.4682074, - 3.4183521, - -18.329607, - 10.1421995, - 21.500256, - 20.873947, - 14.622503, - 20.323536, - -22.500238, - -5.1817036, - 26.616285, - -10.172258, - -14.895687, - 7.471235, - 4.855366, - 8.929348, - 3.4454656, - 24.15315, - 33.191727, - -17.779476, - 13.368094, - -16.79903, - -1.2212269, - 29.02862, - 1.353517, - 33.686493, - -9.61028, - -10.290435, - 17.499424, - -18.92016, - 10.638852, - -4.0155163, - -29.874123, - -23.89452, - 17.025469, - 12.36343, - 25.982975, - -5.359385, - -20.511335, - 26.314108, - -14.478729, - 20.105099, - 10.390779, - -2.7448454, - -21.707514, - 2.8463974, - 20.082417, - 39.494793, - 23.544054, - 33.45021, - 1.2731425, - 7.00291, - 20.49504, - 11.026453, - -14.920918, - 21.672586, - -24.179169, - -22.502762, - -18.470171, - -5.233647, - 15.536683, - 7.5924697, - 31.43023, - -10.685339, - -5.5552483, - 30.057226, - 2.6354103, - 17.865553, - -25.625107, - -23.603718, - 16.79463, - -21.343506, - 24.513098, - -22.31949, - -13.1784725, - 14.572172, - -21.927172, - -0.43766883, - 26.446459, - 7.797492, - 27.607801, - -14.08771, - 28.953205, - -1.2875158, - -17.776453, - 1.3350589, - -0.14630945, - -12.744574, - -5.8219385, - 6.380316, - -24.39855, - 1.6557639, - -25.33089, - -10.88375, - -5.4497714, - -3.2008982, - 3.516546, - 3.7044208, - -14.088412, - 1.8123101, - -2.0853994, - -12.914869, - -14.570528, - 6.286185, - 29.915886, - 18.577192, - -19.750566, - -4.8032465, - -14.996935, - 9.808406, - 3.1115727, - 10.539988, - -0.25747964, - 7.8065777, - -9.5224, - 22.650063, - -8.527657, - 25.720367, - 27.335323, - -27.719013, - -27.493273, - -28.8183, - 16.676228, - 15.222469, - -6.1142654, - 23.31772, - 6.885112, - -21.120987, - 31.183216, - 16.581377, - -1.3270321, - -3.024608, - -24.535004, - -35.037914, - 27.32407, - 2.2356973, - 16.557335, - 8.043718, - 4.2089057, - 24.168753, - -0.42459357, - 26.167639, - -19.28855, - -16.932995, - 0.031842478, - 11.079847, - 23.264338, - 11.247658, - 28.108557, - -17.26478, - 23.26528, - -5.613793, - -12.292187, - -13.964472, - 21.349566, - 21.782167, - 26.02513, - -30.554207, - -20.807219, - -22.266432, - -16.260057, - 13.463569, - -20.409258, - 5.911049, - -3.838907, - -30.899261, - -25.502863, - 17.450424, - 4.5370917, - 7.3130083, - 29.060263, - -1.2906566, - -9.992426, - 9.496942, - 19.615667, - -15.057436, - -14.524883, - -5.6858554, - -8.944074, - 30.993462, - -18.399357, - 4.312004, - -12.452006, - 11.88096, - -26.893, - 10.486003, - -14.269513, - 13.904057, - -14.193346, - -17.597988, - -13.744734, - 19.081799, - 7.1376367, - -20.63535, - 0.17712176, - 26.276295, - -4.0243726, - 18.80954, - 8.85504, - -11.71116, - 10.333615, - -33.28943, - -13.433018, - 25.406893, - -21.37861, - -30.53585, - -0.6449607, - -17.676962, - -33.109673, - 6.502574, - 25.979095, - 13.889341, - 24.452019, - -11.330729, - -14.508683, - 7.7211857, - 30.14757, - -15.281551, - 25.445856, - 23.137957, - 2.9930232, - -11.392148, - -3.4584122, - -17.335155, - 32.249325, - 1.1835473, - 0.4309157, - -1.922125, - 18.76773, - 12.763572, - -5.1183553, - -19.383118, - -11.329933, - -9.979049, - -19.62514, - 14.371391, - -9.079416, - 17.039936, - 12.198028, - 17.744976, - -27.767008, - 4.7606173, - 20.943676, - -2.7953665, - 34.946663, - 21.359537, - 23.354967, - 32.181087, - 10.895949, - -23.63617, - 16.164768, - -21.386267, - -0.20407373, - -10.61583, - 18.747564, - -8.708449, - 26.564816, - -20.358099, - 3.6623113, - 2.833431, - -2.406363, - -7.6430187, - 30.990358, - -1.6160171, - 22.291674, - 14.2712755, - 8.649531, - -22.09123, - -3.9283407, - -15.144995, - -5.257486, - 16.290205, - 24.053005, - -5.443865, - 29.637974, - -30.894657, - 10.8514185, - -19.329512, - -1.7249132, - -27.617838, - 12.135396, - -20.576097, - -32.521618, - -17.759117, - 14.102587, - -1.4501517, - -17.441105, - 22.34238, - -1.5771652, - -3.4706712, - 19.873198, - 17.654528, - 14.297588, - 35.126564, - 3.530811, - 22.92706, - 1.305536, - -5.8584995, - -3.4917607, - -25.70212, - 15.667845, - -13.110925, - 1.5236746, - 1.27955, - 26.836803, - 22.695467, - -7.542444, - -24.459936, - -4.085233, - -24.834877, - -13.123537, - 13.346765, - 3.3096304, - 5.8128743, - -9.243302, - -22.380308, - 24.534492, - 32.18937, - 0.7944149, - -17.298498, - -7.3226933, - 23.025293, - -0.33986145, - 14.641378, - -32.17766, - 9.108203, - -15.654366, - -3.2708795, - 1.7839518, - 4.667992, - -21.404385, - 33.032204, - 0.07473384, - -8.874142, - 19.918457, - 2.485261, - -26.038076, - 13.791234, - 19.88417, - 26.989523, - 6.4794717, - -8.599584, - 26.08512, - 35.79187, - -3.0957053, - 1.5328475, - -15.78256, - 14.641849, - 0.75382006, - 13.353416, - -20.758772, - 27.588259, - -8.591754, - 7.6756034, - -32.257572, - -3.6816385, - -8.807442, - -23.705658, - 26.69215, - 5.574528, - -3.3590631, - -16.991213, - -18.813177, - 20.353582, - -8.202672, - -2.241037, - -22.663652, - -10.86935, - 22.6146, - 0.538039, - -11.617886, - -7.3185177, - 5.459471, - -20.510658, - 14.793362, - -15.245933, - 2.8498745, - 30.176495, - 25.41137, - 12.340705, - -14.110134, - 20.984993, - -20.736963, - -21.078281, - -16.38932, - -10.101326, - -29.059853, - -14.522557, - -31.21759, - 11.320027, - -1.3346295, - 19.095402, - 3.5003624, - -0.27149853, - 23.530079, - -1.4504046, - -20.799906, - 26.357058, - 25.323908, - 21.914633, - 19.832611, - -14.345478, - -12.780764, - -15.090428, - 0.40740138, - -16.226871, - 22.365917, - 24.898293, - -22.19336, - -17.027992, - -19.892523, - 23.981928, - -11.016326, - -16.473738, - -20.647305, - -18.943878, - -34.179035, - -14.075991, - 1.9298484, - 20.942158, - -15.682211, - -9.76076, - -23.77744, - 2.101532, - -25.935007, - 8.422051, - -21.395668, - -12.298222, - 2.824297, - 12.158624, - 15.439734, - -5.986609, - 22.680363, - -19.286484, - 30.605867, - -0.7899231, - 18.014528, - -18.204716, - -18.893454, - -2.6403008, - -26.197563, - 0.6461262, - -17.935425, - 21.006203, - 19.50926, - -25.124516, - 19.076454, - -13.34786, - -20.217596, - -18.721956, - 13.471852, - 10.719515, - -6.343975, - -4.427436, - 2.1415112, - 0.124456935, - 9.154357, - 15.850318, - 14.106509, - 18.979578, - -25.880474, - 15.075585, - 20.326845, - -15.592323, - -16.127396, - 19.439365, - -18.178284, - -7.721521, - 18.546848, - 1.3289208, - -21.118057, - 15.136754, - -8.462077, - -6.078381, - 0.24295494, - -14.893564, - -3.098876, - -22.965818, - -2.973772, - -10.412807, - 36.82579, - 0.043326903, - -0.730605, - 20.569399, - 20.47704, - 34.56152, - -12.61784, - -22.44099, - -13.279965, - -28.35139, - -9.076381, - -14.49968, - 11.381456, - 27.552359, - 10.113919, - 4.322983, - -16.923988, - 18.366398, - 4.072649, - -18.502573, - 14.2359915, - 1.2205616, - 34.52153, - -13.276994, - 16.888266, - -17.09381, - 26.655972, - 12.712044, - -22.337847, - -18.344118, - -21.796993, - -2.695157, - 33.12794, - 20.795307, - 5.892835, - -30.008844, - 13.092032, - -12.617298, - -26.583797, - -12.331805, - -25.788994, - 18.527788, - -5.358728, - -20.973848, - 21.975595, - 3.6332028, - 21.49163, - -24.02265, - -1.2270886, - 31.648344, - -26.34871, - 28.852188, - 11.337199, - 16.580437, - 6.917111, - -2.6463892, - -13.808859, - 27.402872, - 31.668863, - 10.09489, - -9.203751, - -4.5927486, - -19.010218, - 7.268004, - 27.96568, - -32.725826, - -12.638194, - -9.072612, - 0.687024, - -24.00849, - -16.797096, - -13.887938, - 21.008837, - -20.714098, - 4.003382, - -5.864986, - 6.308118, - -18.954786, - -14.093458, - 14.5252905, - -10.20566, - -5.714998, - -7.6352305, - -11.445573, - 28.259352, - -7.4210625, - -14.774667, - 8.2712965, - -14.246153, - -23.317041, - 0.21726441, - -20.630865, - -24.174063, - -15.430166, - -22.63233, - -5.336508, - -0.4162142, - -17.627256, - -12.0516205, - -10.120339, - 22.627249, - 17.18417, - -24.923342, - 20.119074, - -11.128392, - -23.75025, - -22.75563, - -18.194794, - -2.677447, - 5.6336102, - -8.593113, - -27.35188, - 30.831476, - 6.842084, - -23.006275, - -2.1064568, - -31.873516, - -21.917208, - 11.057577, - 21.760345, - 31.105818, - -7.2484465, - 27.442217, - 27.198599, - -5.4786696, - 20.937487, - -15.238694, - -22.516329, - 16.441422, - -27.548603, - -0.95101047, - -5.9703918, - -20.764137, - 9.63625, - 25.318214, - -4.7924676, - 22.43602, - -29.857277, - -8.804195, - -16.590578, - 6.1655693, - -6.229835, - 9.825396, - 3.6917143, - -25.044327, - -15.101339, - 7.166533, - 18.591246, - -25.983875, - 27.819729, - 24.170658, - 5.3510475, - 6.549803, - -32.0242, - 27.198914, - -3.37324, - -14.339118, - -28.126497, - 22.221628, - -13.358003, - -16.78678, - -32.53302, - 15.152627, - 13.393224, - 19.411095, - 23.425772, - 20.027725, - 24.710947, - 17.26326, - -27.410538, - 26.30866, - -4.510418, - 5.3038287, - 7.526191, - -15.999681, - -2.2162335, - 31.378555, - 6.302167, - 15.184932, - -21.060045, - 14.10122, - 5.90295, - -27.716919, - -16.625145, - -10.241354, - 6.1585164, - 7.223655, - -11.634907, - -21.870625, - -21.870728, - 6.634295, - -6.066459, - -17.1438, - -32.103767, - -10.273103, - 15.137199, - 7.232844, - 21.119562, - 1.9282136, - 12.128642, - 12.653392, - 9.936496, - 21.916615, - 9.071596, - 27.73088, - 14.497267, - -4.162361, - -25.22734, - -22.694798, - -10.849964, - -8.824205, - 20.774977, - 20.526009, - 28.81767, - -15.895552, - -11.81379, - 11.597373, - -10.619046, - -12.564632, - -21.738821, - -13.048038, - -23.010983, - -1.3630763, - 19.897066 - ], - "yaxis": "y" - }, - { - "customdata": [ - [ - "AP - President Bush declared
Friday that charges of voter
fraud have cast doubt on the
Ukrainian election, and warned
that any European-negotiated
pact on Iran's nuclear program
must ensure the world can
verify Tehran's compliance." - ], - [ - "Israel is prepared to back a
Middle East conference
convened by Tony Blair early
next year despite having
expressed fears that the
British plans were over-
ambitious and designed" - ], - [ - "THE re-election of British
Prime Minister Tony Blair
would be seen as an
endorsement of the military
action in Iraq, Prime Minister
John Howard said today." - ], - [ - "AFP - A battle group of
British troops rolled out of
southern Iraq on a US-
requested mission to deadlier
areas near Baghdad, in a major
political gamble for British
Prime Minister Tony Blair." - ], - [ - " WASHINGTON (Reuters) -
President Bush on Friday set a
four-year goal of seeing a
Palestinian state established
and he and British Prime
Minister Tony Blair vowed to
mobilize international
support to help make it happen
now that Yasser Arafat is
dead." - ] - ], - "hovertemplate": "label=Nearest neighbor (top 5)
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Nearest neighbor (top 5)", - "marker": { - "color": "#EF553B", - "size": 5, - "symbol": "diamond" - }, - "mode": "markers", - "name": "Nearest neighbor (top 5)", - "showlegend": true, - "type": "scattergl", - "x": [ - 4.836007, - 10.248553, - 11.338411, - 14.155432, - 11.67213 - ], - "xaxis": "x", - "y": [ - 5.3443413, - 0.81385136, - -1.632514, - 14.279812, - 3.6773765 - ], - "yaxis": "y" - }, - { - "customdata": [ - [ - "BRITAIN: BLAIR WARNS OF
CLIMATE THREAT Prime Minister
Tony Blair urged the
international community to
consider global warming a dire
threat and agree on a plan of
action to curb the
quot;alarming quot; growth of
greenhouse gases." - ] - ], - "hovertemplate": "label=Source
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Source", - "marker": { - "color": "#00cc96", - "size": 5, - "symbol": "square" - }, - "mode": "markers", - "name": "Source", - "showlegend": true, - "type": "scattergl", - "x": [ - 9.787297 - ], - "xaxis": "x", - "y": [ - -0.25818 - ], - "yaxis": "y" - } - ], - "layout": { - "height": 500, - "legend": { - "title": { - "text": "label" - }, - "tracegroupgap": 0 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Nearest neighbors of the Tony Blair article" - }, - "width": 600, - "xaxis": { - "anchor": "y", - "domain": [ - 0, - 1 - ], - "title": { - "text": "Component 0" - } - }, - "yaxis": { - "anchor": "x", - "domain": [ - 0, - 1 - ], - "title": { - "text": "Component 1" - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# a 2D chart of nearest neighbors of the Tony Blair article\n", - "chart_from_components(\n", - " components=tsne_components,\n", - " labels=tony_blair_labels,\n", - " strings=article_descriptions,\n", - " width=600,\n", - " height=500,\n", - " title=\"Nearest neighbors of the Tony Blair article\",\n", - " category_orders={\"label\": [\"Other\", \"Nearest neighbor (top 5)\", \"Source\"]},\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Looking at the 2D chart above, we can see that the articles about Tony Blair are somewhat close together inside of the World news cluster. Interestingly, although the 5 nearest neighbors (red) were closest in high dimensional space, they are not the closest points in this compressed 2D space. Compressing the embeddings from 2048 dimensions to 2 dimensions discards much of their information, and the nearest neighbors in the 2D space don't seem to be as relevant as those in the full embedding space." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly/" - }, - "data": [ - { - "customdata": [ - [ - "BRITAIN: BLAIR WARNS OF
CLIMATE THREAT Prime Minister
Tony Blair urged the
international community to
consider global warming a dire
threat and agree on a plan of
action to curb the
quot;alarming quot; growth of
greenhouse gases." - ], - [ - "Newspapers in Greece reflect a
mixture of exhilaration that
the Athens Olympics proved
successful, and relief that
they passed off without any
major setback." - ], - [ - "SAN JOSE, Calif. -- Apple
Computer (Quote, Chart)
unveiled a batch of new iPods,
iTunes software and promos
designed to keep it atop the
heap of digital music players." - ], - [ - "Any product, any shape, any
size -- manufactured on your
desktop! The future is the
fabricator. By Bruce Sterling
from Wired magazine." - ], - [ - "KABUL, Sept 22 (AFP): Three US
soldiers were killed and 14
wounded in a series of fierce
clashes with suspected Taliban
fighters in south and eastern
Afghanistan this week, the US
military said Wednesday." - ], - [ - "The EU and US moved closer to
an aviation trade war after
failing to reach agreement
during talks Thursday on
subsidies to Airbus Industrie." - ], - [ - "AUSTRALIAN journalist John
Martinkus is lucky to be alive
after spending 24 hours in the
hands of Iraqi militants at
the weekend. Martinkus was in
Baghdad working for the SBS
Dateline TV current affairs
program" - ], - [ - " GAZA (Reuters) - An Israeli
helicopter fired a missile
into a town in the southern
Gaza Strip late on Wednesday,
witnesses said, hours after a
Palestinian suicide bomber
blew herself up in Jerusalem,
killing two Israeli border
policemen." - ], - [ - "The Microsoft CEO says one way
to stem growing piracy of
Windows and Office in emerging
markets is to offer low-cost
computers." - ], - [ - "RIYADH, Saudi Arabia -- Saudi
police are seeking two young
men in the killing of a Briton
in a Riyadh parking lot, the
Interior Ministry said today,
and the British ambassador
called it a terrorist attack." - ], - [ - "Loudon, NH -- As the rain
began falling late Friday
afternoon at New Hampshire
International Speedway, the
rich in the Nextel Cup garage
got richer." - ], - [ - "PalmSource surprised the
mobile vendor community today
with the announcement that it
will acquire China MobileSoft
(CMS), ostensibly to leverage
that company's expertise in
building a mobile version of
the Linux operating system." - ], - [ - "JEFFERSON CITY - An election
security expert has raised
questions about Missouri
Secretary of State Matt Blunt
#39;s plan to let soldiers at
remote duty stations or in
combat areas cast their
ballots with the help of
e-mail." - ], - [ - "A gas explosion at a coal mine
in northern China killed 33
workers in the 10th deadly
mine blast reported in three
months. The explosion occurred
yesterday at 4:20 pm at Nanlou
township" - ], - [ - "Reuters - Palestinian leader
Mahmoud Abbas called\\Israel
\"the Zionist enemy\" Tuesday,
unprecedented language for\\the
relative moderate who is
expected to succeed Yasser
Arafat." - ], - [ - "AP - Ottawa Senators right
wing Marian Hossa is switching
European teams during the NHL
lockout." - ], - [ - "A new, optional log-on service
from America Online that makes
it harder for scammers to
access a persons online
account will not be available
for Macintosh" - ], - [ - "Nasser al-Qidwa, Palestinian
representative at the United
Nations and nephew of late
leader Yasser Arafat, handed
Arafat #39;s death report to
the Palestinian National
Authority (PNA) on Saturday." - ], - [ - "CAIRO, Egypt - France's
foreign minister appealed
Monday for the release of two
French journalists abducted in
Baghdad, saying the French
respect all religions. He did
not rule out traveling to
Baghdad..." - ], - [ - "Chelsea terminated Romania
striker Adrian Mutu #39;s
contract, citing gross
misconduct after the player
failed a doping test for
cocaine and admitted taking
the drug, the English soccer
club said in a statement." - ], - [ - "United Arab Emirates President
and ruler of Abu Dhabi Sheik
Zayed bin Sultan al-Nayhan
died Tuesday, official
television reports. He was 86." - ], - [ - "PALESTINIAN leader Yasser
Arafat today issued an urgent
call for the immediate release
of two French journalists
taken hostage in Iraq." - ], - [ - "The al-Qaida terrorist network
spent less than \\$50,000 on
each of its major attacks
except for the Sept. 11, 2001,
suicide hijackings, and one of
its hallmarks is using" - ], - [ - " SAN FRANCISCO (Reuters) -
Nike Inc. <A HREF=\"http://w
ww.investor.reuters.com/FullQu
ote.aspx?ticker=NKE.N target=/
stocks/quickinfo/fullquote\">
;NKE.N</A>, the world's
largest athletic shoe company,
on Monday reported a 25
percent rise in quarterly
profit, beating analysts'
estimates, on strong demand
for high-end running and
basketball shoes in the
United States." - ], - [ - "A FATHER who scaled the walls
of a Cardiff court dressed as
superhero Robin said the
Buckingham Palace protester
posed no threat. Fathers 4
Justice activist Jim Gibson,
who earlier this year staged
an eye-catching" - ], - [ - "NEW YORK (CBS.MW) - US stocks
traded mixed Thurday as Merck
shares lost a quarter of their
value, dragging blue chips
lower, but the Nasdaq moved
higher, buoyed by gains in the
semiconductor sector." - ], - [ - "Julia Gillard has reportedly
bowed out of the race to
become shadow treasurer,
taking enormous pressure off
Opposition Leader Mark Latham." - ], - [ - "It #39;s official. Microsoft
recently stated
definitivelyand contrary to
rumorsthat there will be no
new versions of Internet
Explorer for users of older
versions of Windows." - ], - [ - "The success of Paris #39; bid
for Olympic Games 2012 would
bring an exceptional
development for France for at
least 6 years, said Jean-
Francois Lamour, French
minister for Youth and Sports
on Tuesday." - ], - [ - "AFP - Maybe it's something to
do with the fact that the
playing area is so vast that
you need a good pair of
binoculars to see the action
if it's not taking place right
in front of the stands." - ], - [ - "Egypt #39;s release of accused
Israeli spy Azzam Azzam in an
apparent swap for six Egyptian
students held on suspicion of
terrorism is expected to melt
the ice and perhaps result" - ], - [ - "But fresh antitrust suit is in
the envelope, says Novell" - ], - [ - "Chips that help a computer's
main microprocessors perform
specific types of math
problems are becoming a big
business once again.\\" - ], - [ - "GAZA CITY, Gaza Strip: Hamas
militants killed an Israeli
soldier and wounded four with
an explosion in a booby-
trapped chicken coop on
Tuesday, in what the Islamic
group said was an elaborate
scheme to lure troops to the
area with the help of a double" - ], - [ - "A rocket carrying two Russian
cosmonauts and an American
astronaut to the international
space station streaked into
orbit on Thursday, the latest
flight of a Russian space
vehicle to fill in for
grounded US shuttles." - ], - [ - "Bankrupt ATA Airlines #39;
withdrawal from Chicago #39;s
Midway International Airport
presents a juicy opportunity
for another airline to beef up
service in the Midwest" - ], - [ - "AP - The 300 men filling out
forms in the offices of an
Iranian aid group were offered
three choices: Train for
suicide attacks against U.S.
troops in Iraq, for suicide
attacks against Israelis or to
assassinate British author
Salman Rushdie." - ], - [ - "ATHENS, Greece - Gail Devers,
the most talented yet star-
crossed hurdler of her
generation, was unable to
complete even one hurdle in
100-meter event Sunday -
failing once again to win an
Olympic hurdling medal.
Devers, 37, who has three
world championships in the
hurdles but has always flopped
at the Olympics, pulled up
short and screamed as she slid
under the first hurdle..." - ], - [ - "OCTOBER 12, 2004
(COMPUTERWORLD) - Microsoft
Corp. #39;s monthly patch
release cycle may be making it
more predictable for users to
deploy software updates, but
there appears to be little
letup in the number of holes
being discovered in the
company #39;s software" - ], - [ - "Wearable tech goes mainstream
as the Gap introduces jacket
with built-in radio.\\" - ], - [ - "Madison, WI (Sports Network) -
Anthony Davis ran for 122
yards and two touchdowns to
lead No. 6 Wisconsin over
Northwestern, 24-12, to
celebrate Homecoming weekend
at Camp Randall Stadium." - ], - [ - "LaVar Arrington participated
in his first practice since
Oct. 25, when he aggravated a
knee injury that sidelined him
for 10 games." - ], - [ - " NEW YORK (Reuters) - Billie
Jean King cut her final tie
with the U.S. Fed Cup team
Tuesday when she retired as
coach." - ], - [ - "The Instinet Group, owner of
one of the largest electronic
stock trading systems, is for
sale, executives briefed on
the plan say." - ], - [ - "Funding round of \\$105 million
brings broadband Internet
telephony company's total haul
to \\$208 million." - ], - [ - "The Miami Dolphins arrived for
their final exhibition game
last night in New Orleans
short nine players." - ], - [ - "washingtonpost.com - Anthony
Casalena was 17 when he got
his first job as a programmer
for a start-up called
HyperOffice, a software
company that makes e-mail and
contact management
applications for the Web.
Hired as an intern, he became
a regular programmer after two
weeks and rewrote the main
product line." - ], - [ - "The Auburn Hills-based
Chrysler Group made a profit
of \\$269 million in the third
quarter, even though worldwide
sales and revenues declined,
contributing to a \\$1." - ], - [ - "SAN FRANCISCO (CBS.MW) -- UAL
Corp., parent of United
Airlines, said Wednesday it
will overhaul its route
structure to reduce costs and
offset rising fuel costs." - ], - [ - " NAIROBI (Reuters) - The
Sudanese government and its
southern rebel opponents have
agreed to sign a pledge in the
Kenyan capital on Friday to
formally end a brutal 21-year-
old civil war, with U.N.
Security Council ambassadors
as witnesses." - ], - [ - "AP - From LSU at No. 1 to Ohio
State at No. 10, The AP
women's basketball poll had no
changes Monday." - ], - [ - "nother stage victory for race
leader Petter Solberg, his
fifth since the start of the
rally. The Subaru driver is
not pulling away at a fast
pace from Gronholm and Loeb
but the gap is nonetheless
increasing steadily." - ], - [ - "The fossilized neck bones of a
230-million-year-old sea
creature have features
suggesting that the animal
#39;s snakelike throat could
flare open and create suction
that would pull in prey." - ], - [ - "IBM late on Tuesday announced
the biggest update of its
popular WebSphere business
software in two years, adding
features such as automatically
detecting and fixing problems." - ], - [ - "April 1980 -- Players strike
the last eight days of spring
training. Ninety-two
exhibition games are canceled.
June 1981 -- Players stage
first midseason strike in
history." - ], - [ - "AP - Former Guatemalan
President Alfonso Portillo
#151; suspected of corruption
at home #151; is living and
working part-time in the same
Mexican city he fled two
decades ago to avoid arrest on
murder charges, his close
associates told The Associated
Press on Sunday." - ], - [ - "AP - British entrepreneur
Richard Branson said Monday
that his company plans to
launch commercial space
flights over the next few
years." - ], - [ - "Annual economic growth in
China has slowed for the third
quarter in a row, falling to
9.1 per cent, third-quarter
data shows. The slowdown shows
the economy is responding to
Beijing #39;s efforts to rein
in break-neck investment and
lending." - ], - [ - "washingtonpost.com - BRUSSELS,
Aug. 26 -- The United States
will have to wait until next
year to see its fight with the
European Union over biotech
foods resolved, as the World
Trade Organization agreed to
an E.U. request to bring
scientists into the debate,
officials said Thursday." - ], - [ - "A group of Internet security
and instant messaging
providers have teamed up to
detect and thwart the growing
threat of IM and peer-to-peer
viruses and worms, they said
this week." - ], - [ - "On Sunday, the day after Ohio
State dropped to 0-3 in the
Big Ten with a 33-7 loss at
Iowa, the Columbus Dispatch
ran a single word above its
game story on the Buckeyes:
quot;Embarrassing." - ], - [ - "Insisting that Hurriyat
Conference is the real
representative of Kashmiris,
Pakistan has claimed that
India is not ready to accept
ground realities in Kashmir." - ], - [ - "THE All-India Motor Transport
Congress (AIMTC) on Saturday
called off its seven-day
strike after finalising an
agreement with the Government
on the contentious issue of
service tax and the various
demands including tax deducted
at source (TDS), scrapping" - ], - [ - "BOSTON - Curt Schilling got
his 20th win on the eve of
Boston #39;s big series with
the New York Yankees. Now he
wants much more. quot;In a
couple of weeks, hopefully, it
will get a lot better, quot;
he said after becoming" - ], - [ - "Shooed the ghosts of the
Bambino and the Iron Horse and
the Yankee Clipper and the
Mighty Mick, on his 73rd
birthday, no less, and turned
Yankee Stadium into a morgue." - ], - [ - "Gold medal-winning Marlon
Devonish says the men #39;s
4x100m Olympic relay triumph
puts British sprinting back on
the map. Devonish, Darren
Campbell, Jason Gardener and
Mark Lewis-Francis edged out
the American" - ], - [ - "AP - The euro-zone economy
grew by 0.5 percent in the
second quarter of 2004, a
touch slower than in the first
three months of the year,
according to initial estimates
released Tuesday by the
European Union." - ], - [ - "His first space flight was in
1965 when he piloted the first
manned Gemini mission. Later
he made two trips to the moon
-- orbiting during a 1969
flight and then walking on the
lunar surface during a mission
in 1972." - ], - [ - "he difficult road conditions
created a few incidents in the
first run of the Epynt stage.
Francois Duval takes his
second stage victory since the
start of the rally, nine
tenths better than Sebastien
Loeb #39;s performance in
second position." - ], - [ - "VIENNA -- After two years of
investigating Iran's atomic
program, the UN nuclear
watchdog still cannot rule out
that Tehran has a secret atom
bomb project as Washington
insists, the agency's chief
said yesterday." - ], - [ - "By RACHEL KONRAD SAN
FRANCISCO (AP) -- EBay Inc.,
which has been aggressively
expanding in Asia, plans to
increase its stake in South
Korea's largest online auction
company. The Internet
auction giant said Tuesday
night that it would purchase
nearly 3 million publicly held
shares of Internet Auction
Co..." - ], - [ - "AFP - US Secretary of State
Colin Powell wrapped up a
three-nation tour of Asia
after winning pledges from
Japan, China and South Korea
to press North Korea to resume
stalled talks on its nuclear
weapons programs." - ], - [ - "Tallahassee, FL (Sports
Network) - Florida State head
coach Bobby Bowden has
suspended senior wide receiver
Craphonso Thorpe for the
Seminoles #39; game against
fellow Atlantic Coast
Conference member Duke on
Saturday." - ], - [ - "A few years ago, casinos
across the United States were
closing their poker rooms to
make space for more popular
and lucrative slot machines." - ], - [ - "CAIRO, Egypt An Egyptian
company says one of its four
workers who had been kidnapped
in Iraq has been freed. It
says it can #39;t give the
status of the others being
held hostage but says it is
quot;doing its best to secure
quot; their release." - ], - [ - "WASHINGTON -- Consumers were
tightfisted amid soaring
gasoline costs in August and
hurricane-related disruptions
last week sent applications
for jobless benefits to their
highest level in seven months." - ], - [ - "Talking kitchens and vanities.
Musical jump ropes and potty
seats. Blusterous miniature
leaf blowers and vacuum
cleaners -- almost as loud as
the real things." - ], - [ - "Online merchants in the United
States have become better at
weeding out fraudulent credit
card orders, a new survey
indicates. But shipping
overseas remains a risky
venture. By Joanna Glasner." - ], - [ - "Former champion Lleyton Hewitt
bristled, battled and
eventually blossomed as he
took another step towards a
second US Open title with a
second-round victory over
Moroccan Hicham Arazi on
Friday." - ], - [ - "AP - China's biggest computer
maker, Lenovo Group, said
Wednesday it has acquired a
majority stake in
International Business
Machines Corp.'s personal
computer business for
#36;1.75 billion, one of the
biggest Chinese overseas
acquisitions ever." - ], - [ - "Popping a gadget into a cradle
to recharge it used to mean
downtime, but these days
chargers are doing double
duty, keeping your portable
devices playing even when
they're charging." - ], - [ - "AFP - Hosts India braced
themselves for a harrowing
chase on a wearing wicket in
the first Test after Australia
declined to enforce the
follow-on here." - ], - [ - " SAN FRANCISCO (Reuters) -
Texas Instruments Inc. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=T
XN.N target=/stocks/quickinfo/
fullquote\">TXN.N</A>,
the largest maker of chips for
cellular phones, on Monday
said record demand for its
handset and television chips
boosted quarterly profit by
26 percent, even as it
struggles with a nagging
inventory problem." - ], - [ - "LONDON ARM Holdings, a British
semiconductor designer, said
on Monday that it would buy
Artisan Components for \\$913
million to broaden its product
range." - ], - [ - "Big evolutionary insights
sometimes come in little
packages. Witness the
startling discovery, in a cave
on the eastern Indonesian
island of Flores, of the
partial skeleton of a half-
size Homo species that" - ], - [ - "Prime Minister Paul Martin of
Canada urged Haitian leaders
on Sunday to allow the
political party of the deposed
president, Jean-Bertrand
Aristide, to take part in new
elections." - ], - [ - "Hostage takers holding up to
240 people at a school in
southern Russia have refused
to talk with a top Islamic
leader and demanded to meet
with regional leaders instead,
ITAR-TASS reported on
Wednesday." - ], - [ - "As the Mets round out their
search for a new manager, the
club is giving a last-minute
nod to its past. Wally
Backman, an infielder for the
Mets from 1980-88 who played
second base on the 1986" - ], - [ - "MELBOURNE: Global shopping
mall owner Westfield Group
will team up with Australian
developer Multiplex Group to
bid 585mil (US\\$1." - ], - [ - "Three children from a care
home are missing on the
Lancashire moors after they
are separated from a group." - ], - [ - "Luke Skywalker and Darth Vader
may get all the glory, but a
new Star Wars video game
finally gives credit to the
everyday grunts who couldn
#39;t summon the Force for
help." - ], - [ - "AMSTERDAM, Aug 18 (Reuters) -
Midfielder Edgar Davids #39;s
leadership qualities and
never-say-die attitude have
earned him the captaincy of
the Netherlands under new
coach Marco van Basten." - ], - [ - "COUNTY KILKENNY, Ireland (PA)
-- Hurricane Jeanne has led to
world No. 1 Vijay Singh
pulling out of this week #39;s
WGC-American Express
Championship at Mount Juliet." - ], - [ - "Green Bay, WI (Sports Network)
- The Green Bay Packers will
be without the services of Pro
Bowl center Mike Flanagan for
the remainder of the season,
as he will undergo left knee
surgery." - ], - [ - "Diabetics should test their
blood sugar levels more
regularly to reduce the risk
of cardiovascular disease, a
study says." - ], - [ - "COLUMBUS, Ohio -- NCAA
investigators will return to
Ohio State University Monday
to take another look at the
football program after the
latest round of allegations
made by former players,
according to the Akron Beacon
Journal." - ], - [ - " LOS ANGELES (Reuters) -
Federal authorities raided
three Washington, D.C.-area
video game stores and arrested
two people for modifying
video game consoles to play
pirated video games, a video
game industry group said on
Wednesday." - ], - [ - "Manchester United gave Alex
Ferguson a 1,000th game
anniversary present by
reaching the last 16 of the
Champions League yesterday,
while four-time winners Bayern
Munich romped into the second
round with a 5-1 beating of
Maccabi Tel Aviv." - ], - [ - "Iraq's interim Prime Minister
Ayad Allawi announced that
proceedings would begin
against former Baath Party
leaders." - ], - [ - "Reuters - Delta Air Lines Inc.
, which is\\racing to cut costs
to avoid bankruptcy, on
Wednesday reported\\a wider
quarterly loss amid soaring
fuel prices and weak\\domestic
airfares." - ], - [ - "Energy utility TXU Corp. on
Monday more than quadrupled
its quarterly dividend payment
and raised its projected
earnings for 2004 and 2005
after a companywide
performance review." - ], - [ - "Northwest Airlines Corp., the
fourth- largest US airline,
and its pilots union reached
tentative agreement on a
contract that would cut pay
and benefits, saving the
company \\$265 million a year." - ], - [ - "The last time the Angels
played the Texas Rangers, they
dropped two consecutive
shutouts at home in their most
agonizing lost weekend of the
season." - ], - [ - "Microsoft Corp. MSFT.O and
cable television provider
Comcast Corp. CMCSA.O said on
Monday that they would begin
deploying set-top boxes
powered by Microsoft software
starting next week." - ], - [ - "SEATTLE - Chasing a nearly
forgotten ghost of the game,
Ichiro Suzuki broke one of
baseball #39;s oldest records
Friday night, smoking a single
up the middle for his 258th
hit of the year and breaking
George Sisler #39;s record for
the most hits in a season" - ], - [ - "Grace Park, her caddie - and
fans - were poking around in
the desert brush alongside the
18th fairway desperately
looking for her ball." - ], - [ - "Philippines mobile phone
operator Smart Communications
Inc. is in talks with
Singapore #39;s Mobile One for
a possible tie-up,
BusinessWorld reported Monday." - ], - [ - "Washington Redskins kicker
John Hall strained his right
groin during practice
Thursday, his third leg injury
of the season. Hall will be
held out of practice Friday
and is questionable for Sunday
#39;s game against the Chicago
Bears." - ], - [ - "Airline warns it may file for
bankruptcy if too many senior
pilots take early retirement
option. Delta Air LInes #39;
CEO says it faces bankruptcy
if it can #39;t slow the pace
of pilots taking early
retirement." - ], - [ - "A toxic batch of home-brewed
alcohol has killed 31 people
in several towns in central
Pakistan, police and hospital
officials say." - ], - [ - "Thornbugs communicate by
vibrating the branches they
live on. Now scientists are
discovering just what the bugs
are \"saying.\"" - ], - [ - "The Florida Gators and
Arkansas Razorbacks meet for
just the sixth time Saturday.
The Gators hold a 4-1
advantage in the previous five
meetings, including last year
#39;s 33-28 win." - ], - [ - "Kodak Versamark #39;s parent
company, Eastman Kodak Co.,
reported Tuesday it plans to
eliminate almost 900 jobs this
year in a restructuring of its
overseas operations." - ], - [ - "A top official of the US Food
and Drug Administration said
Friday that he and his
colleagues quot;categorically
reject quot; earlier
Congressional testimony that
the agency has failed to
protect the public against
dangerous drugs." - ], - [ - " BEIJING (Reuters) - North
Korea is committed to holding
six-party talks aimed at
resolving the crisis over its
nuclear weapons program, but
has not indicated when, a top
British official said on
Tuesday." - ], - [ - "AP - 1941 #151; Brooklyn
catcher Mickey Owen dropped a
third strike on Tommy Henrich
of what would have been the
last out of a Dodgers victory
against the New York Yankees.
Given the second chance, the
Yankees scored four runs for a
7-4 victory and a 3-1 lead in
the World Series, which they
ended up winning." - ], - [ - "Federal prosecutors cracked a
global cartel that had
illegally fixed prices of
memory chips in personal
computers and servers for
three years." - ], - [ - "AP - Oracle Corp. CEO Larry
Ellison reiterated his
determination to prevail in a
long-running takeover battle
with rival business software
maker PeopleSoft Inc.,
predicting the proposed deal
will create a more competitive
company with improved customer
service." - ], - [ - "Braves shortstop Rafael Furcal
was arrested on drunken
driving charges Friday, his
second D.U.I. arrest in four
years." - ], - [ - "AFP - British retailer Marks
and Spencer announced a major
management shake-up as part of
efforts to revive its
fortunes, saying trading has
become tougher ahead of the
crucial Christmas period." - ], - [ - " BAGHDAD (Reuters) - Iraq's
interim government extended
the closure of Baghdad
international airport
indefinitely on Saturday
under emergency rule imposed
ahead of this week's U.S.-led
offensive on Falluja." - ], - [ - " ATHENS (Reuters) - Natalie
Coughlin's run of bad luck
finally took a turn for the
better when she won the gold
medal in the women's 100
meters backstroke at the
Athens Olympics Monday." - ], - [ - " ATLANTA (Reuters) - Home
Depot Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=HD.N target=/
stocks/quickinfo/fullquote\">
;HD.N</A>, the top home
improvement retailer, on
Tuesday reported a 15 percent
rise in third-quarter profit,
topping estimates, aided by
installed services and sales
to contractors." - ], - [ - " LONDON (Reuters) - The dollar
fought back from one-month
lows against the euro and
Swiss franc on Wednesday as
investors viewed its sell-off
in the wake of the Federal
Reserve's verdict on interest
rates as overdone." - ], - [ - "Rivaling Bush vs. Kerry for
bitterness, doctors and trial
lawyers are squaring off this
fall in an unprecedented four-
state struggle over limiting
malpractice awards..." - ], - [ - "Boston Scientific Corp said on
Friday it has recalled an ear
implant the company acquired
as part of its purchase of
Advanced Bionics in June." - ], - [ - "AP - Pedro Feliz hit a
tiebreaking grand slam with
two outs in the eighth inning
for his fourth hit of the day,
and the Giants helped their
playoff chances with a 9-5
victory over the Los Angeles
Dodgers on Saturday." - ], - [ - "MarketWatch.com. Richemont
sees significant H1 lift,
unclear on FY (2:53 AM ET)
LONDON (CBS.MW) -- Swiss
luxury goods maker
Richemont(zz:ZZ:001273145:
news, chart, profile), which
also is a significant" - ], - [ - "Crude oil climbed more than
\\$1 in New York on the re-
election of President George
W. Bush, who has been filling
the US Strategic Petroleum
Reserve." - ], - [ - "AP - Hundreds of tribesmen
gathered Tuesday near the area
where suspected al-Qaida-
linked militants are holding
two Chinese engineers and
demanding safe passage to
their reputed leader, a former
U.S. prisoner from Guantanamo
Bay, Cuba, officials and
residents said." - ], - [ - "AP - Scientists warned Tuesday
that a long-term increase in
global temperature of 3.5
degrees could threaten Latin
American water supplies,
reduce food yields in Asia and
result in a rise in extreme
weather conditions in the
Caribbean." - ], - [ - "AP - Further proof New York's
real estate market is
inflated: The city plans to
sell space on top of lampposts
to wireless phone companies
for #36;21.6 million a year." - ], - [ - "In an alarming development,
high-precision equipment and
materials which could be used
for making nuclear bombs have
disappeared from some Iraqi
facilities, the United Nations
watchdog agency has said." - ], - [ - "Yukos #39; largest oil-
producing unit regained power
supplies from Tyumenenergo, a
Siberia-based electricity
generator, Friday after the
subsidiary pledged to pay 440
million rubles (\\$15 million)
in debt by Oct. 3." - ], - [ - "The rollout of new servers and
networking switches in stores
is part of a five-year
agreement supporting 7-Eleven
#39;s Retail Information
System." - ], - [ - "Top Hollywood studios have
launched a wave of court cases
against internet users who
illegally download film files.
The Motion Picture Association
of America, which acts as the
representative of major film" - ], - [ - "AUSTRALIANS went into a
television-buying frenzy the
run-up to the Athens Olympics,
suggesting that as a nation we
could easily have scored a
gold medal for TV purchasing." - ], - [ - "US STOCKS vacillated yesterday
as rising oil prices muted
Wall Streets excitement over
strong results from Lehman
Brothers and Sprints \\$35
billion acquisition of Nextel
Communications." - ], - [ - "The next time you are in your
bedroom with your PC plus
Webcam switched on, don #39;t
think that your privacy is all
intact. If you have a Webcam
plugged into an infected
computer, there is a
possibility that" - ], - [ - "At the head of the class,
Rosabeth Moss Kanter is an
intellectual whirlwind: loud,
expansive, in constant motion." - ], - [ - "LEVERKUSEN/ROME, Dec 7 (SW) -
Dynamo Kiev, Bayer Leverkusen,
and Real Madrid all have a
good chance of qualifying for
the Champions League Round of
16 if they can get the right
results in Group F on
Wednesday night." - ], - [ - "Ed Hinkel made a diving,
fingertip catch for a key
touchdown and No. 16 Iowa
stiffened on defense when it
needed to most to beat Iowa
State 17-10 Saturday." - ], - [ - "During last Sunday #39;s
Nextel Cup race, amid the
ongoing furor over Dale
Earnhardt Jr. #39;s salty
language, NBC ran a commercial
for a show coming on later
that night called quot;Law
amp; Order: Criminal Intent." - ], - [ - "AP - After playing in hail,
fog and chill, top-ranked
Southern California finishes
its season in familiar
comfort. The Trojans (9-0, 6-0
Pacific-10) have two games at
home #151; against Arizona on
Saturday and Notre Dame on
Nov. 27 #151; before their
rivalry game at UCLA." - ], - [ - "A US airman dies and two are
hurt as a helicopter crashes
due to technical problems in
western Afghanistan." - ], - [ - "Jacques Chirac has ruled out
any withdrawal of French
troops from Ivory Coast,
despite unrest and anti-French
attacks, which have forced the
evacuation of thousands of
Westerners." - ], - [ - "Japanese Prime Minister
Junichiro Koizumi reshuffled
his cabinet yesterday,
replacing several top
ministers in an effort to
boost his popularity,
consolidate political support
and quicken the pace of
reforms in the world #39;s
second-largest economy." - ], - [ - "The remnants of Hurricane
Jeanne rained out Monday's
game between the Mets and the
Atlanta Braves. It will be
made up Tuesday as part of a
doubleheader." - ], - [ - "AP - NASCAR is not expecting
any immediate changes to its
top-tier racing series
following the merger between
telecommunications giant
Sprint Corp. and Nextel
Communications Inc." - ], - [ - "AP - Shawn Fanning's Napster
software enabled countless
music fans to swap songs on
the Internet for free, turning
him into the recording
industry's enemy No. 1 in the
process." - ], - [ - "TBILISI (Reuters) - At least
two Georgian soldiers were
killed and five wounded in
artillery fire with
separatists in the breakaway
region of South Ossetia,
Georgian officials said on
Wednesday." - ], - [ - "Like wide-open races? You
#39;ll love the Big 12 North.
Here #39;s a quick morning
line of the Big 12 North as it
opens conference play this
weekend." - ], - [ - "Reuters - Walt Disney Co.
is\\increasing investment in
video games for hand-held
devices and\\plans to look for
acquisitions of small game
publishers and\\developers,
Disney consumer products
Chairman Andy Mooney said\\on
Monday." - ], - [ - "Taquan Dean scored 22 points,
Francisco Garcia added 19 and
No. 13 Louisville withstood a
late rally to beat Florida
74-70 Saturday." - ], - [ - "BANGKOK - A UN conference last
week banned commercial trade
in the rare Irrawaddy dolphin,
a move environmentalists said
was needed to save the
threatened species." - ], - [ - "Laksamana.Net - Two Indonesian
female migrant workers freed
by militants in Iraq are
expected to arrive home within
a day or two, the Foreign
Affairs Ministry said
Wednesday (6/10/04)." - ], - [ - "A bitter row between America
and the European Union over
alleged subsidies to rival
aircraft makers Boeing and
Airbus intensified yesterday." - ], - [ - "NEW YORK -- This was all about
Athens, about redemption for
one and validation for the
other. Britain's Paula
Radcliffe, the fastest female
marathoner in history, failed
to finish either of her
Olympic races last summer.
South Africa's Hendrik Ramaala
was a five-ringed dropout,
too, reinforcing his
reputation as a man who could
go only half the distance." - ], - [ - "Reuters - Online media company
Yahoo Inc.\\ late on Monday
rolled out tests of redesigned
start\\pages for its popular
Yahoo.com and My.Yahoo.com
sites." - ], - [ - "Amsterdam (pts) - Dutch
electronics company Philips
http://www.philips.com has
announced today, Friday, that
it has cut its stake in Atos
Origin by more than a half." - ], - [ - "TORONTO (CP) - Two-thirds of
banks around the world have
reported an increase in the
volume of suspicious
activities that they report to
police, a new report by KPMG
suggests." - ], - [ - "The Sun may have captured
thousands or even millions of
asteroids from another
planetary system during an
encounter more than four
billion years ago, astronomers
are reporting." - ], - [ - "LONDON -- Ernie Els has set
his sights on an improved
putting display this week at
the World Golf Championships
#39; NEC Invitational in
Akron, Ohio, after the
disappointment of tying for
fourth place at the PGA
Championship last Sunday." - ], - [ - "The Atkins diet frenzy slowed
growth briefly, but the
sandwich business is booming,
with \\$105 billion in sales
last year." - ], - [ - "Luxury carmaker Jaguar said
Friday it was stopping
production at a factory in
central England, resulting in
a loss of 1,100 jobs,
following poor sales in the
key US market." - ], - [ - "A bus was hijacked today and
shots were fired at police who
surrounded it on the outskirts
of Athens. Police did not know
how many passengers were
aboard the bus." - ], - [ - "Thumb through the book - then
buy a clean copy from Amazon" - ], - [ - "AP - President Bashar Assad
shuffled his Cabinet on
Monday, just weeks after the
United States and the United
Nations challenged Syria over
its military presence in
Lebanon and the security
situation along its border
with Iraq." - ], - [ - "Fiji #39;s Vijay Singh
replaced Tiger Woods as the
world #39;s No.1 ranked golfer
today by winning the PGA
Deutsche Bank Championship." - ], - [ - "LEIPZIG, Germany : Jurgen
Klinsmann enjoyed his first
home win as German manager
with his team defeating ten-
man Cameroon 3-0 in a friendly
match." - ], - [ - "AP - Kevin Brown had a chance
to claim a place in Yankees
postseason history with his
start in Game 7 of the AL
championship series." - ], - [ - "Reuters - High-definition
television can\\show the sweat
beading on an athlete's brow,
but the cost of\\all the
necessary electronic equipment
can get a shopper's own\\pulse
racing." - ], - [ - "HOMESTEAD, Fla. -- Kurt Busch
got his first big break in
NASCAR by winning a 1999
talent audition nicknamed
quot;The Gong Show. quot; He
was selected from dozens of
unknown, young race drivers to
work for one of the sport
#39;s most famous team owners,
Jack Roush." - ], - [ - "AP - President Vladimir Putin
has signed a bill confirming
Russia's ratification of the
Kyoto Protocol, the Kremlin
said Friday, clearing the way
for the global climate pact to
come into force early next
year." - ], - [ - "John Gibson said Friday that
he decided to resign as chief
executive officer of
Halliburton Energy Services
when it became apparent he
couldn #39;t become the CEO of
the entire corporation, after
getting a taste of the No." - ], - [ - "MacCentral - Apple Computer
Inc. on Monday released an
update for Apple Remote
Desktop (ARD), the company's
software solution to assist
Mac system administrators and
computer managers with asset
management, software
distribution and help desk
support. ARD 2.1 includes
several enhancements and bug
fixes." - ], - [ - "NEW YORK (Reuters) - Outback
Steakhouse Inc. said Tuesday
it lost about 130 operating
days and up to \\$2 million in
revenue because it had to
close some restaurants in the
South due to Hurricane
Charley." - ], - [ - "State insurance commissioners
from across the country have
proposed new rules governing
insurance brokerage fees,
winning praise from an
industry group and criticism
from" - ], - [ - "AP - The authenticity of newly
unearthed memos stating that
George W. Bush failed to meet
standards of the Texas Air
National Guard during the
Vietnam War was questioned
Thursday by the son of the
late officer who reportedly
wrote the memos." - ], - [ - "Zurich, Switzerland (Sports
Network) - Former world No. 1
Venus Williams advanced on
Thursday and will now meet
Wimbledon champion Maria
Sharapova in the quarterfinals
at the \\$1." - ], - [ - "INDIA #39;S cricket chiefs
began a frenetic search today
for a broadcaster to show next
month #39;s home series
against world champion
Australia after cancelling a
controversial \\$US308 million
(\\$440 million) television
deal." - ], - [ - "Canadian Press - OAKVILLE,
Ont. (CP) - The body of a
missing autistic man was
pulled from a creek Monday,
just metres from where a key
piece of evidence was
uncovered but originally
overlooked because searchers
had the wrong information." - ], - [ - "SOFTWARE giant Oracle #39;s
stalled \\$7.7bn (4.2bn) bid to
take over competitor
PeopleSoft has received a huge
boost after a US judge threw
out an anti-trust lawsuit
filed by the Department of
Justice to block the
acquisition." - ], - [ - "The International Olympic
Committee (IOC) has urged
Beijing to ensure the city is
ready to host the 2008 Games
well in advance, an official
said on Wednesday." - ], - [ - "AFP - German Chancellor
Gerhard Schroeder arrived in
Libya for an official visit
during which he is to hold
talks with Libyan leader
Moamer Kadhafi." - ], - [ - "The fastest-swiveling space
science observatory ever built
rocketed into orbit on
Saturday to scan the universe
for celestial explosions." - ], - [ - "The government will examine
claims 100,000 Iraqi civilians
have been killed since the US-
led invasion, Jack Straw says." - ], - [ - "Virginia Tech scores 24 points
off four first-half turnovers
in a 55-6 wipeout of Maryland
on Thursday to remain alone
atop the ACC." - ], - [ - "Copernic Unleashes Desktop
Search Tool\\\\Copernic
Technologies Inc. today
announced Copernic Desktop
Search(TM) (CDS(TM)), \"The
Search Engine For Your PC
(TM).\" Copernic has used the
experience gained from over 30
million downloads of its
Windows-based Web search
software to develop CDS, a
desktop search product that
users are saying is far ..." - ], - [ - "The DVD Forum moved a step
further toward the advent of
HD DVD media and drives with
the approval of key physical
specifications at a meeting of
the organisations steering
committee last week." - ], - [ - "Eton College and Clarence
House joined forces yesterday
to deny allegations due to be
made at an employment tribunal
today by a former art teacher
that she improperly helped
Prince Harry secure an A-level
pass in art two years ago." - ], - [ - "AFP - Great Britain's chances
of qualifying for the World
Group of the Davis Cup were
evaporating rapidly after
Austria moved into a 2-1 lead
following the doubles." - ], - [ - "AP - Martina Navratilova's
long, illustrious career will
end without an Olympic medal.
The 47-year-old Navratilova
and Lisa Raymond lost 6-4,
4-6, 6-4 on Thursday night to
fifth-seeded Shinobu Asagoe
and Ai Sugiyama of Japan in
the quarterfinals, one step
shy of the medal round." - ], - [ - "Often pigeonholed as just a
seller of televisions and DVD
players, Royal Philips
Electronics said third-quarter
profit surged despite a slide
into the red by its consumer
electronics division." - ], - [ - "AP - Google, the Internet
search engine, has done
something that law enforcement
officials and their computer
tools could not: Identify a
man who died in an apparent
hit-and-run accident 11 years
ago in this small town outside
Yakima." - ], - [ - "We are all used to IE getting
a monthly new security bug
found, but Winamp? In fact,
this is not the first security
flaw found in the application." - ], - [ - "The Apache Software Foundation
and the Debian Project said
they won't support the Sender
ID e-mail authentication
standard in their products." - ], - [ - "USATODAY.com - The newly
restored THX 1138 arrives on
DVD today with Star Wars
creator George Lucas' vision
of a Brave New World-like
future." - ], - [ - "Office Depot Inc. (ODP.N:
Quote, Profile, Research) on
Tuesday warned of weaker-than-
expected profits for the rest
of the year because of
disruptions from the string of
hurricanes" - ], - [ - "THE photo-equipment maker
Kodak yesterday announced
plans to axe 600 jobs in the
UK and close a factory in
Nottinghamshire, in a move in
line with the giants global
restructuring strategy
unveiled last January." - ], - [ - "The chances of scientists
making any one of five
discoveries by 2010 have been
hugely underestimated,
according to bookmakers." - ], - [ - "Asia-Pacific leaders meet in
Australia to discuss how to
keep nuclear weapons out of
the hands of extremists." - ], - [ - " TALL AFAR, Iraq -- A three-
foot-high coil of razor wire,
21-ton armored vehicles and
American soldiers with black
M-4 assault rifles stood
between tens of thousands of
people and their homes last
week." - ], - [ - "PSV Eindhoven re-established
their five-point lead at the
top of the Dutch Eredivisie
today with a 2-0 win at
Vitesse Arnhem. Former
Sheffield Wednesday striker
Gerald Sibon put PSV ahead in
the 56th minute" - ], - [ - "China's central bank on
Thursday raised interest rates
for the first time in nearly a
decade, signaling deepening
unease with the breakneck pace
of development and an intent
to reign in a construction
boom now sowing fears of
runaway inflation." - ], - [ - "Deepening its commitment to
help corporate users create
SOAs (service-oriented
architectures) through the use
of Web services, IBM's Global
Services unit on Thursday
announced the formation of an
SOA Management Practice." - ], - [ - "TODAY AUTO RACING 3 p.m. --
NASCAR Nextel Cup Sylvania 300
qualifying at N.H.
International Speedway,
Loudon, N.H., TNT PRO BASEBALL
7 p.m. -- Red Sox at New York
Yankees, Ch. 38, WEEI (850)
(on cable systems where Ch. 38
is not available, the game
will air on NESN); Chicago
Cubs at Cincinnati, ESPN 6
p.m. -- Eastern League finals:
..." - ], - [ - "MUMBAI, SEPTEMBER 21: The
Board of Control for Cricket
in India (BCCI) today informed
the Bombay High Court that it
was cancelling the entire
tender process regarding
cricket telecast rights as
also the conditional deal with
Zee TV." - ], - [ - "CHICAGO - Delta Air Lines
(DAL) said Wednesday it plans
to eliminate between 6,000 and
6,900 jobs during the next 18
months, implement a 10 across-
the-board pay reduction and
reduce employee benefits." - ], - [ - "LAKE GEORGE, N.Y. - Even
though he's facing double hip
replacement surgery, Bill
Smith is more than happy to
struggle out the door each
morning, limp past his brand
new P.T..." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks were likely to open
flat on Wednesday, with high
oil prices and profit warnings
weighing on the market before
earnings reports start and key
jobs data is released this
week." - ], - [ - "Best known for its popular
search engine, Google is
rapidly rolling out new
products and muscling into
Microsoft's stronghold: the
computer desktop. The
competition means consumers
get more choices and better
products." - ], - [ - "Toshiba Corp. #39;s new
desktop-replacement multimedia
notebooks, introduced on
Tuesday, are further evidence
that US consumers still have
yet to embrace the mobility
offered by Intel Corp." - ], - [ - "JEJU, South Korea : Grace Park
of South Korea won an LPGA
Tour tournament, firing a
seven-under-par 65 in the
final round of the
1.35-million dollar CJ Nine
Bridges Classic." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
poured cold water on Tuesday
on recent international
efforts to restart stalled
peace talks with Syria, saying
there was \"no possibility\" of
returning to previous
discussions." - ], - [ - "Dutch smugness was slapped
hard during the past
fortnight. The rude awakening
began with the barbaric
slaying of controversial
filmmaker Theo van Gogh on
November 2. Then followed a
reciprocal cycle of some" - ], - [ - "AP - The NHL will lock out its
players Thursday, starting a
work stoppage that threatens
to keep the sport off the ice
for the entire 2004-05 season." - ], - [ - " MOSCOW (Reuters) - Russia's
Gazprom said on Tuesday it
will bid for embattled oil
firm YUKOS' main unit next
month, as the Kremlin seeks
to turn the world's biggest
gas producer into a major oil
player." - ], - [ - "pee writes quot;A passenger
on a commuter plane in
northern Norway attacked both
pilots and at least one
passenger with an axe as the
aircraft was coming in to
land." - ], - [ - "Aregular Amazon customer,
Yvette Thompson has found
shopping online to be mostly
convenient and trouble-free.
But last month, after ordering
two CDs on Amazon.com, the
Silver Spring reader
discovered on her bank
statement that she was double-
charged for the \\$26.98 order.
And there was a \\$25 charge
that was a mystery." - ], - [ - "Prime Minister Ariel Sharon
pledged Sunday to escalate a
broad Israeli offensive in
northern Gaza, saying troops
will remain until Palestinian
rocket attacks are halted.
Israeli officials said the
offensive -- in which 58
Palestinians and three
Israelis have been killed --
will help clear the way for an
Israeli withdrawal." - ], - [ - "Federal Reserve officials
raised a key short-term
interest rate Tuesday for the
fifth time this year, and
indicated they will gradually
move rates higher next year to
keep inflation under control
as the economy expands." - ], - [ - "Canadians are paying more to
borrow money for homes, cars
and other purchases today
after a quarter-point
interest-rate increase by the
Bank of Canada yesterday was
quickly matched by the
chartered banks." - ], - [ - "NEW YORK - Wall Street
professionals know to keep
their expectations in check in
September, historically the
worst month of the year for
stocks. As summertime draws to
a close, money managers are
getting back to business,
cleaning house, and often
sending the market lower in
the process..." - ], - [ - "A group linked to al Qaeda
ally Abu Musab al-Zarqawi said
it had tried to kill Iraq
#39;s environment minister on
Tuesday and warned it would
not miss next time, according
to an Internet statement." - ], - [ - "The Israeli military killed
four Palestinian militants on
Wednesday as troops in tanks
and armored vehicles pushed
into another town in the
northern Gaza Strip, extending" - ], - [ - "When Paula Radcliffe dropped
out of the Olympic marathon
miles from the finish, she
sobbed uncontrollably.
Margaret Okayo knew the
feeling." - ], - [ - "Delta Air Lines is to issue
millions of new shares without
shareholder consent as part of
moves to ensure its survival." - ], - [ - "First baseman Richie Sexson
agrees to a four-year contract
with the Seattle Mariners on
Wednesday." - ], - [ - "KIRKUK, Iraq - A suicide
attacker detonated a car bomb
Saturday outside a police
academy in the northern Iraqi
city of Kirkuk as hundreds of
trainees and civilians were
leaving for the day, killing
at least 20 people and
wounding 36, authorities said.
Separately, U.S and Iraqi
forces clashed with insurgents
in another part of northern
Iraq after launching an
operation to destroy an
alleged militant cell in the
town of Tal Afar, the U.S..." - ], - [ - "Genta (GNTA:Nasdaq - news -
research) is never boring!
Monday night, the company
announced that its phase III
Genasense study in chronic
lymphocytic leukemia (CLL) met
its primary endpoint, which
was tumor shrinkage." - ], - [ - "Finnish mobile giant Nokia has
described its new Preminet
solution, which it launched
Monday (Oct. 25), as a
quot;major worldwide
initiative." - ], - [ - "While the entire airline
industry #39;s finances are
under water, ATA Airlines will
have to hold its breath longer
than its competitors to keep
from going belly up." - ], - [ - " SAN FRANCISCO (Reuters) - At
virtually every turn, Intel
Corp. executives are heaping
praise on an emerging long-
range wireless technology
known as WiMAX, which can
blanket entire cities with
high-speed Internet access." - ], - [ - "One day after ousting its
chief executive, the nation's
largest insurance broker said
it will tell clients exactly
how much they are paying for
services and renounce back-
door payments from carriers." - ], - [ - "LONDON (CBS.MW) -- Outlining
an expectation for higher oil
prices and increasing demand,
Royal Dutch/Shell on Wednesday
said it #39;s lifting project
spending to \\$45 billion over
the next three years." - ], - [ - "Tuesday #39;s meeting could
hold clues to whether it
#39;ll be a November or
December pause in rate hikes.
By Chris Isidore, CNN/Money
senior writer." - ], - [ - "Phishing is one of the
fastest-growing forms of
personal fraud in the world.
While consumers are the most
obvious victims, the damage
spreads far wider--hurting
companies #39; finances and
reputations and potentially" - ], - [ - "Reuters - The U.S. Interior
Department on\\Friday gave
final approval to a plan by
ConocoPhillips and\\partner
Anadarko Petroleum Corp. to
develop five tracts around\\the
oil-rich Alpine field on
Alaska's North Slope." - ], - [ - "The dollar may fall against
the euro for a third week in
four on concern near-record
crude oil prices will temper
the pace of expansion in the
US economy, a survey by
Bloomberg News indicates." - ], - [ - "The battle for the British-
based Chelsfield plc hotted up
at the weekend, with reports
from London that the property
giant #39;s management was
working on its own bid to
thwart the 585 million (\\$A1." - ], - [ - "Atari has opened the initial
sign-up phase of the closed
beta for its Dungeons amp;
Dragons real-time-strategy
title, Dragonshard." - ], - [ - "AP - Many states are facing
legal challenges over possible
voting problems Nov. 2. A look
at some of the developments
Thursday:" - ], - [ - "Israeli troops withdrew from
the southern Gaza Strip town
of Khan Yunis on Tuesday
morning, following a 30-hour
operation that left 17
Palestinians dead." - ], - [ - "Notes and quotes from various
drivers following California
Speedway #39;s Pop Secret 500.
Jeff Gordon slipped to second
in points following an engine
failure while Jimmie Johnson
moved back into first." - ], - [ - "PM-designate Omar Karameh
forms a new 30-member cabinet
which includes women for the
first time." - ], - [ - "Bahrain #39;s king pardoned a
human rights activist who
convicted of inciting hatred
of the government and
sentenced to one year in
prison Sunday in a case linked
to criticism of the prime
minister." - ], - [ - "Big Blue adds features, beefs
up training efforts in China;
rival Unisys debuts new line
and pricing plan." - ], - [ - " MEMPHIS, Tenn. (Sports
Network) - The Memphis
Grizzlies signed All-Star
forward Pau Gasol to a multi-
year contract on Friday.
Terms of the deal were not
announced." - ], - [ - "Leaders from 38 Asian and
European nations are gathering
in Vietnam for a summit of the
Asia-Europe Meeting, know as
ASEM. One thousand delegates
are to discuss global trade
and regional politics during
the two-day forum." - ], - [ - "A US soldier has pleaded
guilty to murdering a wounded
16-year-old Iraqi boy. Staff
Sergeant Johnny Horne was
convicted Friday of the
unpremeditated murder" - ], - [ - "Andre Agassi brushed past
Jonas Bjorkman 6-3 6-4 at the
Stockholm Open on Thursday to
set up a quarter-final meeting
with Spanish sixth seed
Fernando Verdasco." - ], - [ - "South Korea's Hynix
Semiconductor Inc. and Swiss-
based STMicroelectronics NV
signed a joint-venture
agreement on Tuesday to
construct a memory chip
manufacturing plant in Wuxi,
about 100 kilometers west of
Shanghai, in China." - ], - [ - "SAN DIEGO --(Business Wire)--
Oct. 11, 2004 -- Breakthrough
PeopleSoft EnterpriseOne 8.11
Applications Enable
Manufacturers to Become
Demand-Driven." - ], - [ - "Reuters - Oil prices rose on
Friday as tight\\supplies of
distillate fuel, including
heating oil, ahead of\\the
northern hemisphere winter
spurred buying." - ], - [ - "Well, Intel did say -
dismissively of course - that
wasn #39;t going to try to
match AMD #39;s little dual-
core Opteron demo coup of last
week and show off a dual-core
Xeon at the Intel Developer
Forum this week and - as good
as its word - it didn #39;t." - ], - [ - "Guinea-Bissau #39;s army chief
of staff and former interim
president, General Verissimo
Correia Seabra, was killed
Wednesday during unrest by
mutinous soldiers in the
former Portuguese" - ], - [ - "31 October 2004 -- Exit polls
show that Prime Minister
Viktor Yanukovich and
challenger Viktor Yushchenko
finished on top in Ukraine
#39;s presidential election
today and will face each other
in a run-off next month." - ], - [ - "Nov. 18, 2004 - An FDA
scientist says the FDA, which
is charged with protecting
America #39;s prescription
drug supply, is incapable of
doing so." - ], - [ - "Rock singer Bono pledges to
spend the rest of his life
trying to eradicate extreme
poverty around the world." - ], - [ - "AP - Just when tourists
thought it was safe to go back
to the Princess Diana memorial
fountain, the mud has struck." - ], - [ - "The UK's inflation rate fell
in September, thanks in part
to a fall in the price of
airfares, increasing the
chance that interest rates
will be kept on hold." - ], - [ - " HONG KONG/SAN FRANCISCO
(Reuters) - IBM is selling its
PC-making business to China's
largest personal computer
company, Lenovo Group Ltd.,
for \\$1.25 billion, marking
the U.S. firm's retreat from
an industry it helped pioneer
in 1981." - ], - [ - "AP - Three times a week, The
Associated Press picks an
issue and asks President Bush
and Democratic presidential
candidate John Kerry a
question about it. Today's
question and their responses:" - ], - [ - " BOSTON (Reuters) - Boston was
tingling with anticipation on
Saturday as the Red Sox
prepared to host Game One of
the World Series against the
St. Louis Cardinals and take a
step toward ridding
themselves of a hex that has
hung over the team for eight
decades." - ], - [ - "FOR the first time since his
appointment as Newcastle
United manager, Graeme Souness
has been required to display
the strong-arm disciplinary
qualities that, to Newcastle
directors, made" - ], - [ - "In an apparent damage control
exercise, Russian President
Vladimir Putin on Saturday
said he favored veto rights
for India as new permanent
member of the UN Security
Council." - ], - [ - "Nordstrom reported a strong
second-quarter profit as it
continued to select more
relevant inventory and sell
more items at full price." - ], - [ - "WHY IT HAPPENED Tom Coughlin
won his first game as Giants
coach and immediately
announced a fine amnesty for
all Giants. Just kidding." - ], - [ - "A second-place finish in his
first tournament since getting
married was good enough to
take Tiger Woods from third to
second in the world rankings." - ], - [ - " COLORADO SPRINGS, Colorado
(Reuters) - World 400 meters
champion Jerome Young has been
given a lifetime ban from
athletics for a second doping
offense, the U.S. Anti-Doping
Agency (USADA) said Wednesday." - ], - [ - "AP - Nigeria's Senate has
ordered a subsidiary of
petroleum giant Royal/Dutch
Shell to pay a Nigerian ethnic
group #36;1.5 billion for oil
spills in their homelands, but
the legislative body can't
enforce the resolution, an
official said Wednesday." - ], - [ - "IT #39;S BEEN a heck of an
interesting two days here in
Iceland. I #39;ve seen some
interesting technology, heard
some inventive speeches and
met some people with different
ideas." - ], - [ - "The Bank of England is set to
keep interest rates on hold
following the latest meeting
of the its Monetary Policy
Committee." - ], - [ - "Australian troops in Baghdad
came under attack today for
the first time since the end
of the Iraq war when a car
bomb exploded injuring three
soldiers and damaging an
Australian armoured convoy." - ], - [ - "The Bush administration upheld
yesterday the imposition of
penalty tariffs on shrimp
imports from China and
Vietnam, handing a victory to
beleaguered US shrimp
producers." - ], - [ - "House prices rose 0.2 percent
in September compared with the
month before to stand up 17.8
percent on a year ago, the
Nationwide Building Society
says." - ], - [ - "Reuters - Two clients of
Germany's Postbank\\(DPBGn.DE)
fell for an e-mail fraud that
led them to reveal\\money
transfer codes to a bogus Web
site -- the first case of\\this
scam in German, prosecutors
said on Thursday." - ], - [ - "US spending on information
technology goods, services,
and staff will grow seven
percent in 2005 and continue
at a similar pace through
2008, according to a study
released Monday by Forrester
Research." - ], - [ - "LONDON - In two years, Arsenal
will play their home matches
in the Emirates stadium. That
is what their new stadium at
Ashburton Grove will be called
after the Premiership
champions yesterday agreed to
the" - ], - [ - "KNOXVILLE, Tenn. -- Jason
Campbell threw for 252 yards
and two touchdowns, and No. 8
Auburn proved itself as a
national title contender by
overwhelming No. 10 Tennessee,
34-10, last night." - ], - [ - "Look, Ma, no hands! The U.S.
space agency's latest
spacecraft can run an entire
mission by itself. By Amit
Asaravala." - ], - [ - "Pakistans decision to refuse
the International Atomic
Energy Agency to have direct
access to Dr AQ Khan is
correct on both legal and
political counts." - ], - [ - "MANILA, 4 December 2004 - With
floods receding, rescuers
raced to deliver food to
famished survivors in
northeastern Philippine
villages isolated by back-to-
back storms that left more
than 650 people dead and
almost 400 missing." - ], - [ - "Talks on where to build the
world #39;s first nuclear
fusion reactor ended without a
deal on Tuesday but the
European Union said Japan and
the United States no longer
firmly opposed its bid to put
the plant in France." - ], - [ - "Joining America Online,
EarthLink and Yahoo against
spamming, Microsoft Corp.
today announced the filing of
three new anti-Spam lawsuits
under the CAN-SPAM federal law
as part of its initiative in
solving the Spam problem for
Internet users worldwide." - ], - [ - "WASHINGTON -- Another
Revolution season concluded
with an overtime elimination.
Last night, the Revolution
thrice rallied from deficits
for a 3-3 tie with D.C. United
in the Eastern Conference
final, then lost in the first-
ever Major League Soccer match
decided by penalty kicks." - ], - [ - "Dwyane Wade calls himself a
quot;sidekick, quot; gladly
accepting the role Kobe Bryant
never wanted in Los Angeles.
And not only does second-year
Heat point" - ], - [ - "A nationwide inspection shows
Internet users are not as safe
online as they believe. The
inspections found most
consumers have no firewall
protection, outdated antivirus
software and dozens of spyware
programs secretly running on
their computers." - ], - [ - "World number one golfer Vijay
Singh of Fiji has captured his
eighth PGA Tour event of the
year with a win at the 84
Lumber Classic in Farmington,
Pennsylvania." - ], - [ - "The noise was deafening and
potentially unsettling in the
minutes before the start of
the men #39;s Olympic
200-meter final. The Olympic
Stadium crowd chanted
quot;Hellas!" - ], - [ - "CLEVELAND - The White House
said Vice President Dick
Cheney faces a \"master
litigator\" when he debates
Sen. John Edwards Tuesday
night, a backhanded compliment
issued as the Republican
administration defended itself
against criticism that it has
not acknowledged errors in
waging war in Iraq..." - ], - [ - "Brazilian forward Ronaldinho
scored a sensational goal for
Barcelona against Milan,
making for a last-gasp 2-1.
The 24-year-old #39;s
brilliant left-foot hit at the
Nou Camp Wednesday night sent
Barcelona atop of Group F." - ], - [ - "Nortel Networks says it will
again delay the release of its
restated financial results.
The Canadian telecom vendor
originally promised to release
the restated results in
September." - ], - [ - " CHICAGO (Reuters) - At first
glance, paying \\$13 or \\$14
for a hard-wired Internet
laptop connection in a hotel
room might seem expensive." - ], - [ - "SEOUL (Reuters) - The chairman
of South Korea #39;s ruling
Uri Party resigned on Thursday
after saying his father had
served as a military police
officer during Japan #39;s
1910-1945 colonial rule on the
peninsula." - ], - [ - "ALERE, Uganda -- Kasmiro
Bongonyinge remembers sitting
up suddenly in his bed. It was
just after sunrise on a summer
morning two years ago, and the
old man, 87 years old and
blind, knew something was
wrong." - ], - [ - "Reuters - An investigation
into U.S. insurers\\and brokers
rattled insurance industry
stocks for a second day\\on
Friday as investors, shaken
further by subpoenas
delivered\\to the top U.S. life
insurer, struggled to gauge
how deep the\\probe might
reach." - ], - [ - "Bee Staff Writer. SANTA CLARA
- Andre Carter #39;s back
injury has kept him out of the
49ers #39; lineup since Week
1. It #39;s also interfering
with him rooting on his alma
mater in person." - ], - [ - "AP - Kellen Winslow Jr. ended
his second NFL holdout Friday." - ], - [ - "JAKARTA - Official results
have confirmed former army
general Susilo Bambang
Yudhoyono as the winner of
Indonesia #39;s first direct
presidential election, while
incumbent Megawati
Sukarnoputri urged her nation
Thursday to wait for the
official announcement" - ], - [ - "HOUSTON - With only a few
seconds left in a certain
victory for Miami, Peyton
Manning threw a meaningless
6-yard touchdown pass to
Marvin Harrison to cut the
score to 24-15." - ], - [ - "Reuters - A ragged band of
children\\emerges ghost-like
from mists in Ethiopia's
highlands,\\thrusting bunches
of carrots at a car full of
foreigners." - ], - [ - "DEADLY SCORER: Manchester
United #39;s Wayne Rooney
celebrating his three goals
against Fenerbahce this week
at Old Trafford. (AP)." - ], - [ - "AP - A U.N. human rights
expert criticized the U.S.-led
coalition forces in
Afghanistan for violating
international law by allegedly
beating Afghans to death and
forcing some to remove their
clothes or wear hoods." - ], - [ - "You can feel the confidence
level, not just with Team
Canada but with all of Canada.
There #39;s every expectation,
from one end of the bench to
the other, that Martin Brodeur
is going to hold the fort." - ], - [ - "Heading into the first game of
a new season, every team has
question marks. But in 2004,
the Denver Broncos seemed to
have more than normal." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
said on Thursday Yasser
Arafat's death could be a
turning point for peacemaking
but he would pursue a
unilateral plan that would
strip Palestinians of some
land they want for a state." - ], - [ - " AL-ASAD AIRBASE, Iraq
(Reuters) - Defense Secretary
Donald Rumsfeld swept into an
airbase in Iraq's western
desert Sunday to make a
first-hand evaluation of
operations to quell a raging
Iraqi insurgency in his first
such visit in five months." - ], - [ - "More than three out of four
(76 percent) consumers are
experiencing an increase in
spoofing and phishing
incidents, and 35 percent
receive fake e-mails at least
once a week, according to a
recent national study." - ], - [ - "The Dow Jones Industrial
Average failed three times
this year to exceed its
previous high and fell to
about 10,000 each time, most
recently a week ago." - ], - [ - "AP - Deep in the Atlantic
Ocean, undersea explorers are
living a safer life thanks to
germ-fighting clothing made in
Kinston." - ], - [ - "Anaheim, Calif. - There is a
decidedly right lean to the
three-time champions of the
American League Central. In a
span of 26 days, the Minnesota
Twins lost the left side of
their infield to free agency." - ], - [ - "Computer Associates Monday
announced the general
availability of three
Unicenter performance
management products for
mainframe data management." - ], - [ - "Reuters - The European Union
approved on\\Wednesday the
first biotech seeds for
planting and sale across\\EU
territory, flying in the face
of widespread
consumer\\resistance to
genetically modified (GMO)
crops and foods." - ], - [ - "With the NFL trading deadline
set for 4 p.m. Tuesday,
Patriots coach Bill Belichick
said there didn't seem to be
much happening on the trade
front around the league." - ], - [ - "WASHINGTON - Democrat John
Kerry accused President Bush
on Monday of sending U.S.
troops to the \"wrong war in
the wrong place at the wrong
time\" and said he'd try to
bring them all home in four
years..." - ], - [ - " SINGAPORE (Reuters) - Asian
share markets staged a broad-
based retreat on Wednesday,
led by steelmakers amid
warnings of price declines,
but also enveloping technology
and financial stocks on
worries that earnings may
disappoint." - ], - [ - "p2pnet.net News:- A Microsoft
UK quot;WEIGHING THE COST OF
LINUX VS. WINDOWS? LET #39;S
REVIEW THE FACTS quot;
magazine ad has been nailed as
misleading by Britain #39;s
Advertising Standards
Authority (ASA)." - ], - [ - "More lorry drivers are
bringing supplies to Nepal's
capital in defiance of an
indefinite blockade by Maoist
rebels." - ], - [ - "NEW YORK - CNN has a new boss
for the second time in 14
months: former CBS News
executive Jonathan Klein, who
will oversee programming and
editorial direction at the
second-ranked cable news
network." - ], - [ - "Cut-price carrier Virgin Blue
said Tuesday the cost of using
Australian airports is
spiraling upward and asked the
government to review the
deregulated system of charges." - ], - [ - "The retail sector overall may
be reporting a sluggish start
to the season, but holiday
shoppers are scooping up tech
goods at a brisk pace -- and
they're scouring the Web for
bargains more than ever.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\" color=\"#666666\">&
lt;B>-washingtonpost.com<
;/B></FONT>" - ], - [ - "AP - David Beckham broke his
rib moments after scoring
England's second goal in
Saturday's 2-0 win over Wales
in a World Cup qualifying
game." - ], - [ - "Saudi Arabia, Kuwait and the
United Arab Emirates, which
account for almost half of
OPEC #39;s oil output, said
they #39;re committed to
boosting capacity to meet
soaring demand that has driven
prices to a record." - ], - [ - "The US Commerce Department
said Thursday personal income
posted its biggest increase in
three months in August. The
government agency also said
personal spending was
unchanged after rising
strongly in July." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei average opened up 0.54
percent on Monday with banks
and exporters leading the way
as a stronger finish on Wall
Street and declining oil
prices soothed worries over
the global economic outlook." - ], - [ - " BEIJING (Reuters) - Floods
and landslides have killed 76
people in southwest China in
the past four days and washed
away homes and roads, knocked
down power lines and cut off
at least one city, state
media said on Monday." - ], - [ - "Nothing changed at the top of
Serie A as all top teams won
their games to keep the
distance between one another
unaltered. Juventus came back
from behind against Lazio to
win thanks to another goal by
Ibrahimovic" - ], - [ - "The team behind the Beagle 2
mission has unveiled its
design for a successor to the
British Mars lander." - ], - [ - "Survey points to popularity in
Europe, the Middle East and
Asia of receivers that skip
the pay TV and focus on free
programming." - ], - [ - "RICHMOND, Va. Jeremy Mayfield
won his first race in over
four years, taking the
Chevrolet 400 at Richmond
International Raceway after
leader Kurt Busch ran out of
gas eight laps from the
finish." - ], - [ - "AP - Victims of the Sept. 11
attacks were mourned worldwide
Saturday, but in the Middle
East, amid sympathy for the
dead, Arabs said Washington's
support for Israel and the war
on terror launched in the
aftermath of the World Trade
Center's collapse have only
fueled anger and violence." - ], - [ - "Linux publisher Red Hat Inc.
said Tuesday that information-
technology consulting firm
Unisys Corp. will begin
offering a business version of
the company #39;s open-source
operating system on its
servers." - ], - [ - "SEATTLE - Ichiro Suzuki set
the major league record for
hits in a season with 258,
breaking George Sisler's
84-year-old mark with a pair
of singles Friday night. The
Seattle star chopped a leadoff
single in the first inning,
then made history with a
grounder up the middle in the
third..." - ], - [ - "The intruder who entered
British Queen Elizabeth II
#39;s official Scottish
residence and caused a
security scare was a reporter
from the London-based Sunday
Times newspaper, local media
reported Friday." - ], - [ - "IBM's p5-575, a specialized
server geared for high-
performance computing, has
eight 1.9GHz Power5
processors." - ], - [ - "Bruce Wasserstein, head of
Lazard LLC, is asking partners
to take a one-third pay cut as
he readies the world #39;s
largest closely held
investment bank for a share
sale, people familiar with the
situation said." - ], - [ - "Canadian Press - FREDERICTON
(CP) - A New Brunswick truck
driver arrested in Ontario
this week has been accused by
police of stealing 50,000 cans
of Moosehead beer." - ], - [ - "Reuters - British police said
on Monday they had\\charged a
man with sending hoax emails
to relatives of people\\missing
since the Asian tsunami,
saying their loved ones
had\\been confirmed dead." - ], - [ - "The Lemon Bay Manta Rays were
not going to let a hurricane
get in the way of football. On
Friday, they headed to the
practice field for the first
time in eight" - ], - [ - "Microsoft Corp. Chairman Bill
Gates has donated \\$400,000 to
a campaign in California
trying to win approval of a
measure calling for the state
to sell \\$3 billion in bonds
to fund stem-cell research." - ], - [ - "AP - Track star Marion Jones
filed a defamation lawsuit
Wednesday against the man
whose company is at the center
of a federal investigation
into illegal steroid use among
some of the nation's top
athletes." - ], - [ - "LOS ANGELES - On Sept. 1,
former secretary of
Agriculture Dan Glickman
replaced the legendary Jack
Valenti as president and CEO
of Hollywood #39;s trade
group, the Motion Picture
Association of America." - ], - [ - "England #39;s players hit out
at cricket #39;s authorities
tonight and claimed they had
been used as quot;political
pawns quot; after the Zimbabwe
government produced a
spectacular U-turn to ensure
the controversial one-day
series will go ahead." - ], - [ - "Newspaper publisher Pulitzer
Inc. said Sunday that company
officials are considering a
possible sale of the firm to
boost shareholder value." - ], - [ - "Shares of Merck amp; Co.
plunged almost 10 percent
yesterday after a media report
said that documents show the
pharmaceutical giant hid or
denied" - ], - [ - "AP - The Japanese won the
pregame home run derby. Then
the game started and the major
league All-Stars put their
bats to work. Back-to-back
home runs by Moises Alou and
Vernon Wells in the fourth
inning and by Johnny Estrada
and Brad Wilkerson in the
ninth powered the major
leaguers past the Japanese
stars 7-3 Sunday for a 3-0
lead in the eight-game series." - ], - [ - "Reuters - Wall Street was
expected to dip at\\Thursday's
opening, but shares of Texas
Instruments Inc.\\, may climb
after it gave upbeat earnings
guidance." - ], - [ - "Chinese authorities detained a
prominent, U.S.-based Buddhist
leader in connection with his
plans to reopen an ancient
temple complex in the Chinese
province of Inner Mongolia
last week and have forced
dozens of his American
followers to leave the region,
local officials said
Wednesday." - ], - [ - "The director of the National
Hurricane Center stays calm in
the midst of a storm, but
wants everyone in hurricane-
prone areas to get the message
from his media advisories:
Respect the storm's power and
make proper response plans." - ], - [ - "With Chelsea losing their
unbeaten record and Manchester
United failing yet again to
win, William Hill now make
Arsenal red-hot 2/5 favourites
to retain the title." - ], - [ - "Late in August, Boeing #39;s
top sales execs flew to
Singapore for a crucial sales
pitch. They were close to
persuading Singapore Airlines,
one of the world #39;s leading
airlines, to buy the American
company #39;s new jet, the
mid-sized 7E7." - ], - [ - "SBC Communications and
BellSouth will acquire
YellowPages.com with the goal
of building the site into a
nationwide online business
index, the companies said
Thursday." - ], - [ - "Theresa special bookcase in Al
Grohs office completely full
of game plans from his days in
the NFL. Green ones are from
the Jets." - ], - [ - "SAN FRANCISCO Several
California cities and
counties, including Los
Angeles and San Francisco, are
suing Microsoft for what could
amount to billions of dollars." - ], - [ - "Newcastle ensured their place
as top seeds in Friday #39;s
third round UEFA Cup draw
after holding Sporting Lisbon
to a 1-1 draw at St James #39;
Park." - ], - [ - "Adorned with Turkish and EU
flags, Turkey #39;s newspapers
hailed Thursday an official EU
report recommending the
country start talks to join
the bloc, while largely
ignoring the stringent
conditions attached to the
announcement." - ], - [ - "Google plans to release a
version of its desktop search
tool for computers that run
Apple Computer #39;s Mac
operating system, Google #39;s
chief executive, Eric Schmidt,
said Friday." - ], - [ - "AMD : sicurezza e prestazioni
ottimali con il nuovo
processore mobile per notebook
leggeri e sottili; Acer Inc.
preme sull #39;acceleratore
con il nuovo notebook a
marchio Ferrari." - ], - [ - "The sounds of tinkling bells
could be heard above the
bustle of the Farmers Market
on the Long Beach Promenade,
leading shoppers to a row of
bright red tin kettles dotting
a pathway Friday." - ], - [ - "CBC SPORTS ONLINE - Bode
Miller continued his
impressive 2004-05 World Cup
skiing season by winning a
night slalom race in
Sestriere, Italy on Monday." - ], - [ - "Firefox use around the world
climbed 34 percent in the last
month, according to a report
published by Web analytics
company WebSideStory Monday." - ], - [ - "If a plastic card that gives
you credit for something you
don't want isn't your idea of
a great gift, you can put it
up for sale or swap." - ], - [ - "WASHINGTON Aug. 17, 2004
Scientists are planning to
take the pulse of the planet
and more in an effort to
improve weather forecasts,
predict energy needs months in
advance, anticipate disease
outbreaks and even tell
fishermen where the catch will
be ..." - ], - [ - "Damien Rhodes scored on a
2-yard run in the second
overtime, then Syracuse's
defense stopped Pittsburgh on
fourth and 1, sending the
Orange to a 38-31 victory
yesterday in Syracuse, N.Y." - ], - [ - "AP - Anthony Harris scored 18
of his career-high 23 points
in the second half to help
Miami upset No. 19 Florida
72-65 Saturday and give first-
year coach Frank Haith his
biggest victory." - ], - [ - "LONDON Santander Central
Hispano of Spain looked
certain to clinch its bid for
the British mortgage lender
Abbey National, after HBOS,
Britain #39;s biggest home-
loan company, said Wednesday
it would not counterbid, and
after the European Commission
cleared" - ], - [ - "New communications technology
could spawn future products.
Could your purse tell you to
bring an umbrella?" - ], - [ - " WASHINGTON (Reuters) - The
Justice Department is
investigating possible
accounting fraud at Fannie
Mae, bringing greater
government scrutiny to bear on
the mortgage finance company,
already facing a parallel
inquiry by the SEC, a source
close to the matter said on
Thursday." - ], - [ - "AP - The five cities looking
to host the 2012 Summer Games
submitted bids to the
International Olympic
Committee on Monday, entering
the final stage of a long
process in hopes of landing
one of the biggest prizes in
sports." - ], - [ - "SAP has won a \\$35 million
contract to install its human
resources software for the US
Postal Service. The NetWeaver-
based system will replace the
Post Office #39;s current
25-year-old legacy application" - ], - [ - "The FIA has already cancelled
todays activities at Suzuka as
Super Typhoon Ma-On heads
towards the 5.807km circuit.
Saturday practice has been
cancelled altogether while
pre-qualifying and final
qualifying" - ], - [ - "Thailand's prime minister
visits the southern town where
scores of Muslims died in army
custody after a rally." - ], - [ - "Indian industrial group Tata
agrees to invest \\$2bn in
Bangladesh, the biggest single
deal agreed by a firm in the
south Asian country." - ], - [ - "NewsFactor - For years,
companies large and small have
been convinced that if they
want the sophisticated
functionality of enterprise-
class software like ERP and
CRM systems, they must buy
pre-packaged applications.
And, to a large extent, that
remains true." - ], - [ - "Following in the footsteps of
the RIAA, the MPAA (Motion
Picture Association of
America) announced that they
have began filing lawsuits
against people who use peer-
to-peer software to download
copyrighted movies off the
Internet." - ], - [ - " GRAND PRAIRIE, Texas
(Reuters) - Betting on horses
was banned in Texas until as
recently as 1987. Times have
changed rapidly since.
Saturday, Lone Star Park race
track hosts the \\$14 million
Breeders Cup, global racing's
end-of-season extravaganza." - ], - [ - "MacCentral - At a special
music event featuring Bono and
The Edge from rock group U2
held on Tuesday, Apple took
the wraps off the iPod Photo,
a color iPod available in 40GB
or 60GB storage capacities.
The company also introduced
the iPod U2, a special edition
of Apple's 20GB player clad in
black, equipped with a red
Click Wheel and featuring
engraved U2 band member
signatures. The iPod Photo is
available immediately, and
Apple expects the iPod U2 to
ship in mid-November." - ], - [ - "Beijing: At least 170 miners
were trapped underground after
a gas explosion on Sunday
ignited a fire in a coalmine
in north-west China #39;s
Shaanxi province, reports
said." - ], - [ - "The steel tubing company
reports sharply higher
earnings, but the stock is
falling." - ], - [ - "It might be a stay of
execution for Coach P, or it
might just be a Christmas
miracle come early. SU #39;s
upset win over BC has given
hope to the Orange playing in
a post season Bowl game." - ], - [ - "PHIL Neville insists
Manchester United don #39;t
fear anyone in the Champions
League last 16 and declared:
quot;Bring on the Italians." - ], - [ - "Playboy Enterprises, the adult
entertainment company, has
announced plans to open a
private members club in
Shanghai even though the
company #39;s flagship men
#39;s magazine is still banned
in China." - ], - [ - "Reuters - Oracle Corp is
likely to win clearance\\from
the European Commission for
its hostile #36;7.7
billion\\takeover of rival
software firm PeopleSoft Inc.,
a source close\\to the
situation said on Friday." - ], - [ - "TORONTO (CP) - Earnings
warnings from Celestica and
Coca-Cola along with a
slowdown in US industrial
production sent stock markets
lower Wednesday." - ], - [ - "IBM (Quote, Chart) said it
would spend a quarter of a
billion dollars over the next
year and a half to grow its
RFID (define) business." - ], - [ - "Eastman Kodak Co., the world
#39;s largest maker of
photographic film, said
Wednesday it expects sales of
digital products and services
to grow at an annual rate of
36 percent between 2003 and
2007, above prior growth rate
estimates of 26 percent
between 2002" - ], - [ - "SAMARRA (Iraq): With renewe d
wave of skirmishes between the
Iraqi insurgents and the US-
led coalition marines, several
people including top police
officers were put to death on
Saturday." - ], - [ - "SPACE.com - NASA released one
of the best pictures ever made
of Saturn's moon Titan as the
Cassini spacecraft begins a
close-up inspection of the
satellite today. Cassini is
making the nearest flyby ever
of the smog-shrouded moon." - ], - [ - "AFP - The Iraqi government
plans to phase out slowly
subsidies on basic products,
such as oil and electricity,
which comprise 50 percent of
public spending, equal to 15
billion dollars, the planning
minister said." - ], - [ - "ANNAPOLIS ROYAL, NS - Nova
Scotia Power officials
continued to keep the sluice
gates open at one of the
utility #39;s hydroelectric
plants Wednesday in hopes a
wayward whale would leave the
area and head for the open
waters of the Bay of Fundy." - ], - [ - "TORONTO -- Toronto Raptors
point guard Alvin Williams
will miss the rest of the
season after undergoing
surgery on his right knee
Monday." - ], - [ - "The federal agency that
insures pension plans said
that its deficit, already at
the highest in its history,
had doubled in its last fiscal
year, to \\$23.3 billion." - ], - [ - "AFP - Like most US Latinos,
members of the extended
Rodriguez family say they will
cast their votes for Democrat
John Kerry in next month's
presidential polls." - ], - [ - "A Milan judge on Tuesday opens
hearings into whether to put
on trial 32 executives and
financial institutions over
the collapse of international
food group Parmalat in one of
Europe #39;s biggest fraud
cases." - ], - [ - "AP - Tennessee's two freshmen
quarterbacks have Volunteers
fans fantasizing about the
next four years. Brent
Schaeffer and Erik Ainge
surprised many with the nearly
seamless way they rotated
throughout a 42-17 victory
over UNLV on Sunday night." - ], - [ - "In fact, Larry Ellison
compares himself to the
warlord, according to
PeopleSoft's former CEO,
defending previous remarks he
made." - ], - [ - "FALLUJAH, Iraq -- Four Iraqi
fighters huddled in a trench,
firing rocket-propelled
grenades at Lieutenant Eric
Gregory's Bradley Fighting
Vehicle and the US tanks and
Humvees that were lumbering
through tight streets between
boxlike beige houses." - ], - [ - "MADRID, Aug 18 (Reuters) -
Portugal captain Luis Figo
said on Wednesday he was
taking an indefinite break
from international football,
but would not confirm whether
his decision was final." - ], - [ - "The Bank of England on
Thursday left its benchmark
interest rate unchanged, at
4.75 percent, as policy makers
assessed whether borrowing
costs, already the highest in
the Group of Seven, are
constraining consumer demand." - ], - [ - "AP - Several thousand
Christians who packed a
cathedral compound in the
Egyptian capital hurled stones
at riot police Wednesday to
protest a woman's alleged
forced conversion to Islam. At
least 30 people were injured." - ], - [ - "A group of Saudi religious
scholars have signed an open
letter urging Iraqis to
support jihad against US-led
forces. quot;Fighting the
occupiers is a duty for all
those who are able, quot; they
said in a statement posted on
the internet at the weekend." - ], - [ - "Fashion retailers Austin Reed
and Ted Baker have reported
contrasting fortunes on the
High Street. Austin Reed
reported interim losses of 2." - ], - [ - "AP - Shaun Rogers is in the
backfield as often as some
running backs. Whether teams
dare to block Detroit's star
defensive tackle with one
player or follow the trend of
double-teaming him, he often
rips through offensive lines
with a rare combination of
size, speed, strength and
nimble footwork." - ], - [ - " NEW YORK (Reuters) - A
federal judge on Friday
approved Citigroup Inc.'s
\\$2.6 billion settlement with
WorldCom Inc. investors who
lost billions when an
accounting scandal plunged
the telecommunications company
into bankruptcy protection." - ], - [ - "The Lions and Eagles entered
Sunday #39;s game at Ford
Field in the same place --
atop their respective
divisions -- and with
identical 2-0 records." - ], - [ - "An unspecified number of
cochlear implants to help
people with severe hearing
loss are being recalled
because they may malfunction
due to ear moisture, the US
Food and Drug Administration
announced." - ], - [ - "Profits triple at McDonald's
Japan after the fast-food
chain starts selling larger
burgers." - ], - [ - "After Marcos Moreno threw four
more interceptions in last
week's 14-13 overtime loss at
N.C. A T, Bison Coach Ray
Petty will start Antoine
Hartfield against Norfolk
State on Saturday." - ], - [ - "You can empty your pockets of
change, take off your belt and
shoes and stick your keys in
the little tray. But if you've
had radiation therapy
recently, you still might set
off Homeland Security alarms." - ], - [ - "Mountaineers retrieve three
bodies believed to have been
buried for 22 years on an
Indian glacier." - ], - [ - "SEOUL, Oct 19 Asia Pulse -
LG.Philips LCD Co.
(KSE:034220), the world #39;s
second-largest maker of liquid
crystal display (LCD), said
Tuesday it has developed the
world #39;s largest organic
light emitting diode" - ], - [ - "SOUTH WILLIAMSPORT, Pa. --
Looking ahead to the US
championship game almost cost
Conejo Valley in the
semifinals of the Little
League World Series." - ], - [ - "The Cubs didn #39;t need to
fly anywhere near Florida to
be in the eye of the storm.
For a team that is going on
100 years since last winning a
championship, the only thing
they never are at a loss for
is controversy." - ], - [ - "Security experts warn of
banner ads with a bad attitude
--and a link to malicious
code. Also: Phishers, be gone." - ], - [ - "KETTERING, Ohio Oct. 12, 2004
- Cincinnati Bengals defensive
end Justin Smith pleaded not
guilty to a driving under the
influence charge." - ], - [ - "com October 15, 2004, 5:11 AM
PT. Wood paneling and chrome
made your dad #39;s station
wagon look like a million
bucks, and they might also be
just the ticket for Microsoft
#39;s fledgling" - ], - [ - "President Thabo Mbeki met with
Ivory Coast Prime Minister
Seydou Diarra for three hours
yesterday as part of talks
aimed at bringing peace to the
conflict-wracked Ivory Coast." - ], - [ - "MINNEAPOLIS -- For much of the
2004 season, Twins pitcher
Johan Santana didn #39;t just
beat opposing hitters. Often,
he overwhelmed and owned them
in impressive fashion." - ], - [ - "Britain #39;s inflation rate
fell in August further below
its 2.0 percent government-set
upper limit target with
clothing and footwear prices
actually falling, official
data showed on Tuesday." - ], - [ - " KATHMANDU (Reuters) - Nepal's
Maoist rebels have
temporarily suspended a
crippling economic blockade of
the capital from Wednesday,
saying the move was in
response to popular appeals." - ], - [ - "Reuters - An Algerian
suspected of being a leader\\of
the Madrid train bombers has
been identified as one of
seven\\people who blew
themselves up in April to
avoid arrest, Spain's\\Interior
Ministry said on Friday." - ], - [ - "KABUL: An Afghan man was found
guilty on Saturday of killing
four journalists in 2001,
including two from Reuters,
and sentenced to death." - ], - [ - "Yasser Arafat, the leader for
decades of a fight for
Palestinian independence from
Israel, has died at a military
hospital in Paris, according
to news reports." - ], - [ - " LONDON (Reuters) - European
shares shrugged off a spike in
the euro to a fresh all-time
high Wednesday, with telecoms
again leading the way higher
after interim profits at
Britain's mm02 beat
expectations." - ], - [ - "WASHINGTON - Weighed down by
high energy prices, the US
economy grew slower than the
government estimated in the
April-June quarter, as higher
oil prices limited consumer
spending and contributed to a
record trade deficit." - ], - [ - "CHICAGO United Airlines says
it will need even more labor
cuts than anticipated to get
out of bankruptcy. United told
a bankruptcy court judge in
Chicago today that it intends
to start talks with unions
next month on a new round of
cost savings." - ], - [ - " JABALYA, Gaza Strip (Reuters)
- Israel pulled most of its
forces out of the northern
Gaza Strip Saturday after a
four-day incursion it said
was staged to halt Palestinian
rocket attacks on southern
Israeli towns." - ], - [ - "Computer Associates
International yesterday
reported a 6 increase in
revenue during its second
fiscal quarter, but posted a
\\$94 million loss after paying
to settle government
investigations into the
company, it said yesterday." - ], - [ - "THE Turkish embassy in Baghdad
was investigating a television
report that two Turkish
hostages had been killed in
Iraq, but no confirmation was
available so far, a senior
Turkish diplomat said today." - ], - [ - "Reuters - Thousands of
supporters of
Ukraine's\\opposition leader,
Viktor Yushchenko, celebrated
on the streets\\in the early
hours on Monday after an exit
poll showed him\\winner of a
bitterly fought presidential
election." - ], - [ - "LONDON : The United States
faced rare criticism over
human rights from close ally
Britain, with an official
British government report
taking Washington to task over
concerns about Iraq and the
Guantanamo Bay jail." - ], - [ - "The University of California,
Berkeley, has signed an
agreement with the Samoan
government to isolate, from a
tree, the gene for a promising
anti- Aids drug and to share
any royalties from the sale of
a gene-derived drug with the
people of Samoa." - ], - [ - "At a charity auction in New
Jersey last weekend, baseball
memorabilia dealer Warren
Heller was approached by a man
with an unusual but topical
request." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei average jumped 2.5
percent by mid-afternoon on
Monday as semiconductor-
related stocks such as
Advantest Corp. mirrored a
rally by their U.S. peers
while banks and brokerages
extended last week's gains." - ], - [ - "INTER Milan coach Roberto
Mancini believes the club
#39;s lavish (northern) summer
signings will enable them to
mount a serious Serie A
challenge this season." - ], - [ - "LONDON - A bomb threat that
mentioned Iraq forced a New
York-bound Greek airliner to
make an emergency landing
Sunday at London's Stansted
Airport escorted by military
jets, authorities said. An
airport spokeswoman said an
Athens newspaper had received
a phone call saying there was
a bomb on board the Olympic
Airlines plane..." - ], - [ - "Links to this week's topics
from search engine forums
across the web: New MSN Search
Goes LIVE in Beta - Microsoft
To Launch New Search Engine -
Google Launches 'Google
Advertising Professionals' -
Organic vs Paid Traffic ROI? -
Making Money With AdWords? -
Link Building 101" - ], - [ - "AP - Brad Ott shot an 8-under
64 on Sunday to win the
Nationwide Tour's Price Cutter
Charity Championship for his
first Nationwide victory." - ], - [ - "AP - New York Jets running
back Curtis Martin passed Eric
Dickerson and Jerome Bettis on
the NFL career rushing list
Sunday against the St. Louis
Rams, moving to fourth all-
time." - ], - [ - "Eight conservation groups are
fighting the US government
over a plan to poison
thousands of prairie dogs in
the grasslands of South
Dakota, saying wildlife should
not take a backseat to
ranching interests." - ], - [ - "ATHENS, Greece - Sheryl
Swoopes made three big plays
at the end - two baskets and
another on defense - to help
the United States squeeze out
a 66-62 semifinal victory over
Russia on Friday. Now, only
one game stands between the
U.S..." - ], - [ - "Instead of standing for ante
meridian and post meridian,
though, fans will remember the
time periods of pre-Mia and
after-Mia. After playing for
18 years and shattering nearly
every record" - ], - [ - "General Motors (GM) plans to
announce a massive
restructuring Thursday that
will eliminate as many as
12,000 jobs in Europe in a
move to stem the five-year
flow of red ink from its auto
operations in the region." - ], - [ - "Scientists are developing a
device which could improve the
lives of kidney dialysis
patients." - ], - [ - "KABUL, Afghanistan The Afghan
government is blaming drug
smugglers for yesterday #39;s
attack on the leading vice
presidential candidate ." - ], - [ - "Stephon Marbury, concerned
about his lousy shooting in
Athens, used an off day to go
to the gym and work on his
shot. By finding his range, he
saved the United States #39;
hopes for a basketball gold
medal." - ], - [ - " LONDON (Reuters) - Oil prices
held firm on Wednesday as
Hurricane Ivan closed off
crude output and shut
refineries in the Gulf of
Mexico, while OPEC's Gulf
producers tried to reassure
traders by recommending an
output hike." - ], - [ - "State-owned, running a
monopoly on imports of jet
fuel to China #39;s fast-
growing aviation industry and
a prized member of Singapore
#39;s Stock Exchange." - ], - [ - "Google has won a trade mark
dispute, with a District Court
judge finding that the search
engines sale of sponsored
search terms Geico and Geico
Direct did not breach car
insurance firm GEICOs rights
in the trade marked terms." - ], - [ - "Wall Street bounded higher for
the second straight day
yesterday as investors reveled
in sharply falling oil prices
and the probusiness agenda of
the second Bush
administration. The Dow Jones
industrials gained more than
177 points for its best day of
2004, while the Standard amp;
Poor's 500 closed at its
highest level since early
2002." - ], - [ - "Key factors help determine if
outsourcing benefits or hurts
Americans." - ], - [ - "The US Trade Representative on
Monday rejected the European
Union #39;s assertion that its
ban on beef from hormone-
treated cattle is now
justified by science and that
US and Canadian retaliatory
sanctions should be lifted." - ], - [ - "One of the leading figures in
the Greek Orthodox Church, the
Patriarch of Alexandria Peter
VII, has been killed in a
helicopter crash in the Aegean
Sea." - ], - [ - "Siding with chip makers,
Microsoft said it won't charge
double for its per-processor
licenses when dual-core chips
come to market next year." - ], - [ - "NEW YORK -- Wall Street's
fourth-quarter rally gave
stock mutual funds a solid
performance for 2004, with
small-cap equity funds and
real estate funds scoring some
of the biggest returns. Large-
cap growth equities and
technology-focused funds had
the slimmest gains." - ], - [ - "CANBERRA, Australia -- The
sweat-stained felt hats worn
by Australian cowboys, as much
a part of the Outback as
kangaroos and sun-baked soil,
may be heading for the history
books. They fail modern
industrial safety standards." - ], - [ - "Big Food Group Plc, the UK
owner of the Iceland grocery
chain, said second-quarter
sales at stores open at least
a year dropped 3.3 percent,
the second consecutive
decline, after competitors cut
prices." - ], - [ - "A London-to-Washington flight
is diverted after a security
alert involving the singer
formerly known as Cat Stevens." - ], - [ - " WASHINGTON (Reuters) - The
first case of soybean rust has
been found on the mainland
United States and could affect
U.S. crops for the near
future, costing farmers
millions of dollars, the
Agriculture Department said on
Wednesday." - ], - [ - "IBM and the Spanish government
have introduced a new
supercomputer they hope will
be the most powerful in
Europe, and one of the 10 most
powerful in the world." - ], - [ - "The Supreme Court today
overturned a five-figure
damage award to an Alexandria
man for a local auto dealer
#39;s alleged loan scam,
ruling that a Richmond-based
federal appeals court had
wrongly" - ], - [ - "AP - President Bush declared
Friday that charges of voter
fraud have cast doubt on the
Ukrainian election, and warned
that any European-negotiated
pact on Iran's nuclear program
must ensure the world can
verify Tehran's compliance." - ], - [ - "TheSpaceShipOne team is handed
the \\$10m cheque and trophy it
won for claiming the Ansari
X-Prize." - ], - [ - "Security officials have
identified six of the
militants who seized a school
in southern Russia as being
from Chechnya, drawing a
strong connection to the
Chechen insurgents who have
been fighting Russian forces
for years." - ], - [ - "AP - Randy Moss is expected to
play a meaningful role for the
Minnesota Vikings this weekend
against the Giants, even
without a fully healed right
hamstring." - ], - [ - "Pros: Fits the recent profile
(44, past PGA champion, fiery
Ryder Cup player); the job is
his if he wants it. Cons:
Might be too young to be
willing to burn two years of
play on tour." - ], - [ - "SEOUL -- North Korea set three
conditions yesterday to be met
before it would consider
returning to six-party talks
on its nuclear programs." - ], - [ - "Official figures show the
12-nation eurozone economy
continues to grow, but there
are warnings it may slow down
later in the year." - ], - [ - "Elmer Santos scored in the
second half, lifting East
Boston to a 1-0 win over
Brighton yesterday afternoon
and giving the Jets an early
leg up in what is shaping up
to be a tight Boston City
League race." - ], - [ - "In upholding a lower court
#39;s ruling, the Supreme
Court rejected arguments that
the Do Not Call list violates
telemarketers #39; First
Amendment rights." - ], - [ - "US-backed Iraqi commandos were
poised Friday to storm rebel
strongholds in the northern
city of Mosul, as US military
commanders said they had
quot;broken the back quot; of
the insurgency with their
assault on the former rebel
bastion of Fallujah." - ], - [ - "Infineon Technologies, the
second-largest chip maker in
Europe, said Wednesday that it
planned to invest about \\$1
billion in a new factory in
Malaysia to expand its
automotive chip business and
be closer to customers in the
region." - ], - [ - "Mozilla's new web browser is
smart, fast and user-friendly
while offering a slew of
advanced, customizable
functions. By Michelle Delio." - ], - [ - "Saints special teams captain
Steve Gleason expects to be
fined by the league after
being ejected from Sunday's
game against the Carolina
Panthers for throwing a punch." - ], - [ - "JERUSALEM (Reuters) - Prime
Minister Ariel Sharon, facing
a party mutiny over his plan
to quit the Gaza Strip, has
approved 1,000 more Israeli
settler homes in the West Bank
in a move that drew a cautious
response on Tuesday from ..." - ], - [ - "Play has begun in the
Australian Masters at
Huntingdale in Melbourne with
around half the field of 120
players completing their first
rounds." - ], - [ - " NEW YORK (Reuters) -
Washington Post Co. <A HREF
=\"http://www.investor.reuters.
com/FullQuote.aspx?ticker=WPO.
N target=/stocks/quickinfo/ful
lquote\">WPO.N</A>
said on Friday that quarterly
profit jumped, beating
analysts' forecasts, boosted
by results at its Kaplan
education unit and television
broadcasting operations." - ], - [ - "GHAZNI, Afghanistan, 6 October
2004 - Wartime security was
rolled out for Afghanistans
interim President Hamid Karzai
as he addressed his first
election campaign rally
outside the capital yesterday
amid spiraling violence." - ], - [ - "LOUISVILLE, Ky. - Louisville
men #39;s basketball head
coach Rick Pitino and senior
forward Ellis Myles met with
members of the media on Friday
to preview the Cardinals #39;
home game against rival
Kentucky on Satursday." - ], - [ - "AP - Sounds like David
Letterman is as big a \"Pops\"
fan as most everyone else." - ], - [ - "originally offered on notebook
PCs -- to its Opteron 32- and
64-bit x86 processors for
server applications. The
technology will help servers
to run" - ], - [ - "New orders for US-made durable
goods increased 0.2pc in
September, held back by a big
drop in orders for
transportation goods, the US
Commerce Department said
today." - ], - [ - "Siblings are the first ever to
be convicted for sending
boatloads of junk e-mail
pushing bogus products. Also:
Microsoft takes MSN music
download on a Euro trip....
Nokia begins legal battle
against European
counterparts.... and more." - ], - [ - "I always get a kick out of the
annual list published by
Forbes singling out the
richest people in the country.
It #39;s almost as amusing as
those on the list bickering
over their placement." - ], - [ - "MacCentral - After Apple
unveiled the iMac G5 in Paris
this week, Vice President of
Hardware Product Marketing
Greg Joswiak gave Macworld
editors a guided tour of the
desktop's new design. Among
the topics of conversation:
the iMac's cooling system, why
pre-installed Bluetooth
functionality and FireWire 800
were left out, and how this
new model fits in with Apple's
objectives." - ], - [ - "Williams-Sonoma Inc., operator
of home furnishing chains
including Pottery Barn, said
third-quarter earnings rose 19
percent, boosted by store
openings and catalog sales." - ], - [ - "We #39;ve known about
quot;strained silicon quot;
for a while--but now there
#39;s a better way to do it.
Straining silicon improves
chip performance." - ], - [ - "This week, Sir Richard Branson
announced his new company,
Virgin Galactic, has the
rights to the first commercial
flights into space." - ], - [ - "71-inch HDTV comes with a home
stereo system and components
painted in 24-karat gold." - ], - [ - "Arsenal was held to a 1-1 tie
by struggling West Bromwich
Albion on Saturday, failing to
pick up a Premier League
victory when Rob Earnshaw
scored with 11 minutes left." - ], - [ - "TOKYO - Mitsubishi Heavy
Industries said today it #39;s
in talks to buy a plot of land
in central Japan #39;s Nagoya
city from Mitsubishi Motors
for building aircraft parts." - ], - [ - "China has confirmed that it
found a deadly strain of bird
flu in pigs as early as two
years ago. China #39;s
Agriculture Ministry said two
cases had been discovered, but
it did not say exactly where
the samples had been taken." - ], - [ - "Baseball #39;s executive vice
president Sandy Alderson
insisted last month that the
Cubs, disciplined for an
assortment of run-ins with
umpires, would not be targeted
the rest of the season by
umpires who might hold a
grudge." - ], - [ - "As Superman and Batman would
no doubt reflect during their
cigarette breaks, the really
draining thing about being a
hero was that you have to keep
riding to the rescue." - ], - [ - "MacCentral - RealNetworks Inc.
said on Tuesday that it has
sold more than a million songs
at its online music store
since slashing prices last
week as part of a limited-time
sale aimed at growing the user
base of its new digital media
software." - ], - [ - "With the presidential election
less than six weeks away,
activists and security experts
are ratcheting up concern over
the use of touch-screen
machines to cast votes.
<FONT face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-washingtonpost.com&l
t;/B></FONT>" - ], - [ - "NEW YORK, September 14 (New
Ratings) - Yahoo! Inc
(YHOO.NAS) has agreed to
acquire Musicmatch Inc, a
privately held digital music
software company, for about
\\$160 million in cash." - ], - [ - "Japan #39;s Sumitomo Mitsui
Financial Group Inc. said
Tuesday it proposed to UFJ
Holdings Inc. that the two
banks merge on an equal basis
in its latest attempt to woo
UFJ away from a rival suitor." - ], - [ - "Oil futures prices were little
changed Thursday as traders
anxiously watched for
indications that the supply or
demand picture would change in
some way to add pressure to
the market or take some away." - ], - [ - "Gov. Rod Blagojevich plans to
propose a ban Thursday on the
sale of violent and sexually
explicit video games to
minors, something other states
have tried with little
success." - ], - [ - " CHICAGO (Reuters) - Delta Air
Lines Inc. <A HREF=\"http://
www.investor.reuters.com/FullQ
uote.aspx?ticker=DAL.N target=
/stocks/quickinfo/fullquote\"&g
t;DAL.N</A> said on
Tuesday it will cut wages by
10 percent and its chief
executive will go unpaid for
the rest of the year, but it
still warned of bankruptcy
within weeks unless more cuts
are made." - ], - [ - "AP - Ten years after the Irish
Republican Army's momentous
cease-fire, negotiations
resumed Wednesday in hope of
reviving a Catholic-Protestant
administration, an elusive
goal of Northern Ireland's
hard-fought peace process." - ], - [ - "A cable channel plans to
resurrect each of the 1,230
regular-season games listed on
the league's defunct 2004-2005
schedule by setting them in
motion on a video game
console." - ], - [ - " SANTO DOMINGO, Dominican
Republic, Sept. 18 -- Tropical
Storm Jeanne headed for the
Bahamas on Saturday after an
assault on the Dominican
Republic that killed 10
people, destroyed hundreds of
houses and forced thousands
from their homes." - ], - [ - "An explosion tore apart a car
in Gaza City Monday, killing
at least one person,
Palestinian witnesses said.
They said Israeli warplanes
were circling overhead at the
time of the blast, indicating
a possible missile strike." - ], - [ - " WASHINGTON (Reuters) - A
former Fannie Mae <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=FNM.N
target=/stocks/quickinfo/fullq
uote\">FNM.N</A>
employee who gave U.S.
officials information about
what he saw as accounting
irregularities will not
testify as planned before a
congressional hearing next
week, a House committee said
on Friday." - ], - [ - "Beijing, Oct. 25 (PTI): China
and the US today agreed to
work jointly to re-energise
the six-party talks mechanism
aimed at dismantling North
Korea #39;s nuclear programmes
while Washington urged Beijing
to resume" - ], - [ - "AFP - Sporadic gunfire and
shelling took place overnight
in the disputed Georgian
region of South Ossetia in
violation of a fragile
ceasefire, wounding seven
Georgian servicemen." - ], - [ - "PARIS, Nov 4 (AFP) - The
European Aeronautic Defence
and Space Company reported
Thursday that its nine-month
net profit more than doubled,
thanks largely to sales of
Airbus aircraft, and raised
its full-year forecast." - ], - [ - "AP - Eric Hinske and Vernon
Wells homered, and the Toronto
Blue Jays completed a three-
game sweep of the Baltimore
Orioles with an 8-5 victory
Sunday." - ], - [ - "SiliconValley.com - When
\"Halo\" became a smash video
game hit following Microsoft's
launch of the Xbox console in
2001, it was a no-brainer that
there would be a sequel to the
science fiction shoot-em-up." - ], - [ - "The number of people claiming
unemployment benefit last
month fell by 6,100 to
830,200, according to the
Office for National
Statistics." - ], - [ - " NEW YORK (Reuters) - Todd
Walker homered, had three hits
and drove in four runs to lead
the Chicago Cubs to a 12-5 win
over the Cincinnati Reds in
National League play at
Wrigley Field on Monday." - ], - [ - "PARIS -- The city of Paris
intends to reduce its
dependence on software
suppliers with \"de facto
monopolies,\" but considers an
immediate switch of its 17,000
desktops to open source
software too costly, it said
Wednesday." - ], - [ - " FALLUJA, Iraq (Reuters) -
U.S. forces hit Iraq's rebel
stronghold of Falluja with the
fiercest air and ground
bombardment in months, as
insurgents struck back on
Saturday with attacks that
killed up to 37 people in
Samarra." - ], - [ - "MIAMI (Sports Network) -
Shaquille O #39;Neal made his
home debut, but once again it
was Dwyane Wade stealing the
show with 28 points as the
Miami Heat downed the
Cleveland Cavaliers, 92-86, in
front of a record crowd at
AmericanAirlines Arena." - ], - [ - "AP - The San Diego Chargers
looked sharp #151; and played
the same way. Wearing their
powder-blue throwback jerseys
and white helmets from the
1960s, the Chargers did almost
everything right in beating
the Jacksonville Jaguars 34-21
on Sunday." - ], - [ - "The vast majority of consumers
are unaware that an Apple iPod
digital music player only
plays proprietary iTunes
files, while a smaller
majority agree that it is
within RealNetworks #39;
rights to develop a program
that will make its music files
compatible" - ], - [ - "Tyler airlines are gearing up
for the beginning of holiday
travel, as officials offer
tips to help travelers secure
tickets and pass through
checkpoints with ease." - ], - [ - " NAJAF, Iraq (Reuters) - The
fate of a radical Shi'ite
rebellion in the holy city of
Najaf was uncertain Friday
amid disputed reports that
Iraqi police had gained
control of the Imam Ali
Mosque." - ], - [ - " PROVIDENCE, R.I. (Reuters) -
You change the oil in your car
every 5,000 miles or so. You
clean your house every week or
two. Your PC needs regular
maintenance as well --
especially if you're using
Windows and you spend a lot of
time on the Internet." - ], - [ - "NERVES - no problem. That
#39;s the verdict of Jose
Mourinho today after his
Chelsea side gave a resolute
display of character at
Highbury." - ], - [ - "AP - The latest low point in
Ron Zook's tenure at Florida
even has the coach wondering
what went wrong. Meanwhile,
Sylvester Croom's first big
win at Mississippi State has
given the Bulldogs and their
fans a reason to believe in
their first-year leader.
Jerious Norwood's 37-yard
touchdown run with 32 seconds
remaining lifted the Bulldogs
to a 38-31 upset of the 20th-
ranked Gators on Saturday." - ], - [ - "A criminal trial scheduled to
start Monday involving former
Enron Corp. executives may
shine a rare and potentially
harsh spotlight on the inner
workings" - ], - [ - "The Motley Fool - Here's
something you don't see every
day -- the continuing brouhaha
between Oracle (Nasdaq: ORCL -
News) and PeopleSoft (Nasdaq:
PSFT - News) being a notable
exception. South Africa's
Harmony Gold Mining Company
(NYSE: HMY - News) has
announced a hostile takeover
bid to acquire fellow South
African miner Gold Fields
Limited (NYSE: GFI - News).
The transaction, if it takes
place, would be an all-stock
acquisition, with Harmony
issuing 1.275 new shares in
payment for each share of Gold
Fields. The deal would value
Gold Fields at more than
#36;8 billion. ..." - ], - [ - "Someone forgot to inform the
US Olympic basketball team
that it was sent to Athens to
try to win a gold medal, not
to embarrass its country." - ], - [ - "SPACE.com - NASA's Mars \\rover
Opportunity nbsp;will back its
\\way out of a nbsp;crater it
has spent four months
exploring after reaching
terrain nbsp;that appears \\too
treacherous to tread. nbsp;" - ], - [ - "Sony Corp. announced Tuesday a
new 20 gigabyte digital music
player with MP3 support that
will be available in Great
Britain and Japan before
Christmas and elsewhere in
Europe in early 2005." - ], - [ - "Wal-Mart Stores Inc. #39;s
Asda, the UK #39;s second
biggest supermarket chain,
surpassed Marks amp; Spencer
Group Plc as Britain #39;s
largest clothing retailer in
the last three months,
according to the Sunday
Telegraph." - ], - [ - "Ten-man Paris St Germain
clinched their seventh
consecutive victory over arch-
rivals Olympique Marseille
with a 2-1 triumph in Ligue 1
on Sunday thanks to a second-
half winner by substitute
Edouard Cisse." - ], - [ - "Until this week, only a few
things about the strange,
long-ago disappearance of
Charles Robert Jenkins were
known beyond a doubt. In the
bitter cold of Jan. 5, 1965,
the 24-year-old US Army
sergeant was leading" - ], - [ - "Roy Oswalt wasn #39;t
surprised to hear the Astros
were flying Sunday night
through the remnants of a
tropical depression that
dumped several inches of rain
in Louisiana and could bring
showers today in Atlanta." - ], - [ - "AP - This hardly seemed
possible when Pitt needed
frantic rallies to overcome
Division I-AA Furman or Big
East cellar dweller Temple. Or
when the Panthers could barely
move the ball against Ohio
#151; not Ohio State, but Ohio
U." - ], - [ - "Everyone is moaning about the
fallout from last weekend but
they keep on reporting the
aftermath. #39;The fall-out
from the so-called
quot;Battle of Old Trafford
quot; continues to settle over
the nation and the debate" - ], - [ - "Oil supply concerns and broker
downgrades of blue-chip
companies left stocks mixed
yesterday, raising doubts that
Wall Street #39;s year-end
rally would continue." - ], - [ - "Genentech Inc. said the
marketing of Rituxan, a cancer
drug that is the company #39;s
best-selling product, is the
subject of a US criminal
investigation." - ], - [ - "American Lindsay Davenport
regained the No. 1 ranking in
the world for the first time
since early 2002 by defeating
Dinara Safina of Russia 6-4,
6-2 in the second round of the
Kremlin Cup on Thursday." - ], - [ - " The world's No. 2 soft drink
company said on Thursday
quarterly profit rose due to
tax benefits." - ], - [ - "TOKYO (AP) -- The electronics
and entertainment giant Sony
Corp. (SNE) is talking with
Wal-Mart Stores Inc..." - ], - [ - "After an unprecedented span of
just five days, SpaceShipOne
is ready for a return trip to
space on Monday, its final
flight to clinch a \\$10
million prize." - ], - [ - "The United States on Tuesday
modified slightly a threat of
sanctions on Sudan #39;s oil
industry in a revised text of
its UN resolution on
atrocities in the country
#39;s Darfur region." - ], - [ - "Freshman Jeremy Ito kicked
four field goals and Ryan
Neill scored on a 31-yard
interception return to lead
improving Rutgers to a 19-14
victory on Saturday over
visiting Michigan State." - ], - [ - "Hi-tech monitoring of
livestock at pig farms could
help improve the animal growth
process and reduce costs." - ], - [ - "Third-seeded Guillermo Canas
defeated Guillermo Garcia-
Lopez of Spain 7-6 (1), 6-3
Monday on the first day of the
Shanghai Open on Monday." - ], - [ - "AP - France intensified
efforts Tuesday to save the
lives of two journalists held
hostage in Iraq, and the Arab
League said the militants'
deadline for France to revoke
a ban on Islamic headscarves
in schools had been extended." - ], - [ - "Cable amp; Wireless plc
(NYSE: CWP - message board) is
significantly ramping up its
investment in local loop
unbundling (LLU) in the UK,
and it plans to spend up to 85
million (\\$152." - ], - [ - "USATODAY.com - Personal
finance software programs are
the computer industry's
version of veggies: Everyone
knows they're good for you,
but it's just hard to get
anyone excited about them." - ], - [ - " NEW YORK (Reuters) - The
dollar rebounded on Monday
after last week's heavy
selloff, but analysts were
uncertain if the rally would
hold after fresh economic data
suggested the December U.S.
jobs report due Friday might
not live up to expectations." - ], - [ - "AFP - Microsoft said that it
had launched a new desktop
search tool that allows
personal computer users to
find documents or messages on
their PCs." - ], - [ - "At least 12 people die in an
explosion at a fuel pipeline
on the outskirts of Nigeria's
biggest city, Lagos." - ], - [ - "The three largest computer
makers spearheaded a program
today designed to standardize
working conditions for their
non-US workers." - ], - [ - "Description: Illinois Gov. Rod
Blagojevich is backing state
legislation that would ban
sales or rentals of video
games with graphic sexual or
violent content to children
under 18." - ], - [ - "Volkswagen demanded a two-year
wage freeze for the
170,000-strong workforce at
Europe #39;s biggest car maker
yesterday, provoking union
warnings of imminent conflict
at key pay and conditions
negotiations." - ], - [ - " NEW YORK (Reuters) - U.S.
stock futures pointed to a
lower open on Wall Street on
Thursday, extending the
previous session's sharp
fall, with rising energy
prices feeding investor
concerns about corporate
profits and slower growth." - ], - [ - "But to play as feebly as it
did for about 35 minutes last
night in Game 1 of the WNBA
Finals and lose by only four
points -- on the road, no less
-- has to be the best
confidence builder since Cindy
St." - ], - [ - "MILAN General Motors and Fiat
on Wednesday edged closer to
initiating a legal battle that
could pit the two carmakers
against each other in a New
York City court room as early
as next month." - ], - [ - "Are you bidding on keywords
through Overture's Precision
Match, Google's AdWords or
another pay-for-placement
service? If so, you're
eligible to participate in
their contextual advertising
programs." - ], - [ - "Two of the Ford Motor Company
#39;s most senior executives
retired on Thursday in a sign
that the company #39;s deep
financial crisis has abated,
though serious challenges
remain." - ], - [ - "Citing security concerns, the
U.S. Embassy on Thursday
banned its employees from
using the highway linking the
embassy area to the
international airport, a
10-mile stretch of road
plagued by frequent suicide
car-bomb attacks." - ], - [ - "Nobel Laureate Wilkins, 87,
played an important role in
the discovery of the double
helix structure of DNA, the
molecule that carries our
quot;life code quot;,
Kazinform refers to BBC News." - ], - [ - "With yesterday #39;s report on
its athletic department
violations completed, the
University of Washington says
it is pleased to be able to
move forward." - ], - [ - " LONDON (Reuters) - Wall
Street was expected to start
little changed on Friday as
investors continue to fret
over the impact of high oil
prices on earnings, while
Boeing <A HREF=\"http://www.
investor.reuters.com/FullQuote
.aspx?ticker=BA.N target=/stoc
ks/quickinfo/fullquote\">BA.
N</A> will be eyed
after it reiterated its
earnings forecast." - ], - [ - "AP - Tom Daschle bade his
fellow Senate Democrats
farewell Tuesday with a plea
that they seek common ground
with Republicans yet continue
to fight for the less
fortunate." - ], - [ - "Sammy Sosa was fined \\$87,400
-- one day's salary -- for
arriving late to the Cubs'
regular-season finale at
Wrigley Field and leaving the
game early. The slugger's
agent, Adam Katz , said
yesterday Sosa most likely
will file a grievance. Sosa
arrived 70 minutes before
Sunday's first pitch, and he
apparently left 15 minutes
after the game started without
..." - ], - [ - "Having an always-on, fast net
connection is changing the way
Britons use the internet,
research suggests." - ], - [ - "AP - Police defused a bomb in
a town near Prime Minister
Silvio Berlusconi's villa on
the island of Sardinia on
Wednesday shortly after
British Prime Minister Tony
Blair finished a visit there
with the Italian leader." - ], - [ - "Is the Oklahoma defense a
notch below its predecessors?
Is Texas #39; offense a step-
ahead? Why is Texas Tech
feeling good about itself
despite its recent loss?" - ], - [ - "The coffin of Yasser Arafat,
draped with the Palestinian
flag, was bound for Ramallah
in the West Bank Friday,
following a formal funeral on
a military compound near
Cairo." - ], - [ - "US Ambassador to the United
Nations John Danforth resigned
on Thursday after serving in
the post for less than six
months. Danforth, 68, said in
a letter released Thursday" - ], - [ - "Crude oil futures prices
dropped below \\$51 a barrel
yesterday as supply concerns
ahead of the Northern
Hemisphere winter eased after
an unexpectedly high rise in
US inventories." - ], - [ - "New York gets 57 combined
points from its starting
backcourt of Jamal Crawford
and Stephon Marbury and tops
Denver, 107-96." - ], - [ - "ISLAMABAD, Pakistan -- Photos
were published yesterday in
newspapers across Pakistan of
six terror suspects, including
a senior Al Qaeda operative,
the government says were
behind attempts to assassinate
the nation's president." - ], - [ - "AP - Shawn Marion had a
season-high 33 points and 15
rebounds, leading the Phoenix
Suns on a fourth-quarter
comeback despite the absence
of Steve Nash in a 95-86 win
over the New Orleans Hornets
on Friday night." - ], - [ - "By Lilly Vitorovich Of DOW
JONES NEWSWIRES SYDNEY (Dow
Jones)--Rupert Murdoch has
seven weeks to convince News
Corp. (NWS) shareholders a
move to the US will make the
media conglomerate more
attractive to" - ], - [ - "A number of signs point to
increasing demand for tech
workers, but not all the
clouds have been driven away." - ], - [ - "Messina upset defending
champion AC Milan 2-1
Wednesday, while Juventus won
its third straight game to
stay alone atop the Italian
league standings." - ], - [ - "Microsoft Corp. (MSFT.O:
Quote, Profile, Research)
filed nine new lawsuits
against spammers who send
unsolicited e-mail, including
an e-mail marketing Web
hosting company, the world
#39;s largest software maker
said on Thursday." - ], - [ - "AP - Padraig Harrington
rallied to a three-stroke
victory in the German Masters
on a windy Sunday, closing
with a 2-under-par 70 and
giving his game a big boost
before the Ryder Cup." - ], - [ - " ATHENS (Reuters) - The Athens
Paralympics canceled
celebrations at its closing
ceremony after seven
schoolchildren traveling to
watch the event died in a bus
crash on Monday." - ], - [ - "The rocket plane SpaceShipOne
is just one flight away from
claiming the Ansari X-Prize, a
\\$10m award designed to kick-
start private space travel." - ], - [ - "Reuters - Three American
scientists won the\\2004 Nobel
physics prize on Tuesday for
showing how tiny
quark\\particles interact,
helping to explain everything
from how a\\coin spins to how
the universe was built." - ], - [ - "Ironically it was the first
regular season game for the
Carolina Panthers that not
only began the history of the
franchise, but also saw the
beginning of a rivalry that
goes on to this day." - ], - [ - "Baltimore Ravens running back
Jamal Lewis did not appear at
his arraignment Friday, but
his lawyers entered a not
guilty plea on charges in an
expanded drug conspiracy
indictment." - ], - [ - "AP - Sharp Electronics Corp.
plans to stop selling its
Linux-based handheld computer
in the United States, another
sign of the slowing market for
personal digital assistants." - ], - [ - "After serving a five-game
suspension, Milton Bradley
worked out with the Dodgers as
they prepared for Tuesday's
opener against the St. Louis
Cardinals." - ], - [ - "AP - Prime Time won't be
playing in prime time this
time. Deion Sanders was on the
inactive list and missed a
chance to strut his stuff on
\"Monday Night Football.\"" - ], - [ - "Reuters - Glaciers once held
up by a floating\\ice shelf off
Antarctica are now sliding off
into the sea --\\and they are
going fast, scientists said on
Tuesday." - ], - [ - "DUBAI : An Islamist group has
threatened to kill two Italian
women held hostage in Iraq if
Rome does not withdraw its
troops from the war-torn
country within 24 hours,
according to an internet
statement." - ], - [ - "AP - Warning lights flashed
atop four police cars as the
caravan wound its way up the
driveway in a procession fit
for a presidential candidate.
At long last, Azy and Indah
had arrived. They even flew
through a hurricane to get
here." - ], - [ - "The man who delivered the
knockout punch was picked up
from the Seattle scrap heap
just after the All-Star Game.
Before that, John Olerud
certainly hadn't figured on
facing Pedro Martinez in
Yankee Stadium in October." - ], - [ - "\\Female undergraduates work
harder and are more open-
minded than males, leading to
better results, say
scientists." - ], - [ - "A heavy quake rocked Indonesia
#39;s Papua province killing
at least 11 people and
wounding 75. The quake
destroyed 150 buildings,
including churches, mosques
and schools." - ], - [ - "LONDON : A consortium,
including former world
champion Nigel Mansell, claims
it has agreed terms to ensure
Silverstone remains one of the
venues for the 2005 Formula
One world championship." - ], - [ - " BATON ROUGE, La. (Sports
Network) - LSU has named Les
Miles its new head football
coach, replacing Nick Saban." - ], - [ - "The United Nations annual
World Robotics Survey predicts
the use of robots around the
home will surge seven-fold by
2007. The boom is expected to
be seen in robots that can mow
lawns and vacuum floors, among
other chores." - ], - [ - "The long-term economic health
of the United States is
threatened by \\$53 trillion in
government debts and
liabilities that start to come
due in four years when baby
boomers begin to retire." - ], - [ - "Reuters - A small group of
suspected\\gunmen stormed
Uganda's Water Ministry
Wednesday and took
three\\people hostage to
protest against proposals to
allow President\\Yoweri
Museveni for a third
term.\\Police and soldiers with
assault rifles cordoned off
the\\three-story building, just
328 feet from Uganda's
parliament\\building in the
capital Kampala." - ], - [ - "The Moscow Arbitration Court
ruled on Monday that the YUKOS
oil company must pay RUR
39.113bn (about \\$1.34bn) as
part of its back tax claim for
2001." - ], - [ - "NOVEMBER 11, 2004 -- Bankrupt
US Airways this morning said
it had reached agreements with
lenders and lessors to
continue operating nearly all
of its mainline and US Airways
Express fleets." - ], - [ - "Venezuela suggested Friday
that exiles living in Florida
may have masterminded the
assassination of a prosecutor
investigating a short-lived
coup against leftist President
Hugo Chvez" - ], - [ - "Want to dive deep -- really
deep -- into the technical
literature about search
engines? Here's a road map to
some of the best web
information retrieval
resources available online." - ], - [ - "Reuters - Ancel Keys, a
pioneer in public health\\best
known for identifying the
connection between
a\\cholesterol-rich diet and
heart disease, has died." - ], - [ - "The US government asks the
World Trade Organisation to
step in to stop EU member
states from \"subsidising\"
planemaker Airbus." - ], - [ - "Reuters - T-Mobile USA, the
U.S. wireless unit\\of Deutsche
Telekom AG (DTEGn.DE), does
not expect to offer\\broadband
mobile data services for at
least the next two years,\\its
chief executive said on
Thursday." - ], - [ - "Verizon Communications is
stepping further into video as
a way to compete against cable
companies." - ], - [ - "Facing a popular outcry at
home and stern warnings from
Europe, the Turkish government
discreetly stepped back
Tuesday from a plan to
introduce a motion into a
crucial penal reform bill to
make adultery a crime
punishable by prison." - ], - [ - "Boston Scientific Corp.
(BSX.N: Quote, Profile,
Research) said on Wednesday it
received US regulatory
approval for a device to treat
complications that arise in
patients with end-stage kidney
disease who need dialysis." - ], - [ - "North-west Norfolk MP Henry
Bellingham has called for the
release of an old college
friend accused of plotting a
coup in Equatorial Guinea." - ], - [ - "With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." - ], - [ - "AP - The Chicago Blackhawks
re-signed goaltender Michael
Leighton to a one-year
contract Wednesday." - ], - [ - "Oracle Corp. plans to release
the latest version of its CRM
(customer relationship
management) applications
within the next two months, as
part of an ongoing update of
its E-Business Suite." - ], - [ - "Toyota Motor Corp. #39;s
shares fell for a second day,
after the world #39;s second-
biggest automaker had an
unexpected quarterly profit
drop." - ], - [ - "AFP - Want to buy a castle?
Head for the former East
Germany." - ], - [ - "Hosted CRM service provider
Salesforce.com took another
step forward last week in its
strategy to build an online
ecosystem of vendors that
offer software as a service." - ], - [ - "Britain-based HBOS says it
will file a complaint to the
European Commission against
Spanish bank Santander Central
Hispano (SCH) in connection
with SCH #39;s bid to acquire
British bank Abbey National" - ], - [ - "AFP - Steven Gerrard has moved
to allay Liverpool fans' fears
that he could be out until
Christmas after breaking a
metatarsal bone in his left
foot." - ], - [ - "Verizon Wireless on Thursday
announced an agreement to
acquire all the PCS spectrum
licenses of NextWave Telecom
Inc. in 23 markets for \\$3
billion." - ], - [ - "washingtonpost.com -
Technology giants IBM and
Hewlett-Packard are injecting
hundreds of millions of
dollars into radio-frequency
identification technology,
which aims to advance the
tracking of items from ho-hum
bar codes to smart tags packed
with data." - ], - [ - "ATHENS -- She won her first
Olympic gold medal in kayaking
when she was 18, the youngest
paddler to do so in Games
history. Yesterday, at 42,
Germany #39;s golden girl
Birgit Fischer won her eighth
Olympic gold in the four-woman
500-metre kayak race." - ], - [ - "England boss Sven Goran
Eriksson has defended
goalkeeper David James after
last night #39;s 2-2 draw in
Austria. James allowed Andreas
Ivanschitz #39;s shot to slip
through his fingers to
complete Austria comeback from
two goals down." - ], - [ - "MINSK - Legislative elections
in Belarus held at the same
time as a referendum on
whether President Alexander
Lukashenko should be allowed
to seek a third term fell
significantly short of
democratic standards, foreign
observers said here Monday." - ], - [ - "An Olympic sailor is charged
with the manslaughter of a
Briton who died after being
hit by a car in Athens." - ], - [ - "The Norfolk Broads are on
their way to getting a clean
bill of ecological health
after a century of stagnation." - ], - [ - "AP - Secretary of State Colin
Powell on Friday praised the
peace deal that ended fighting
in Iraq's holy city of Najaf
and said the presence of U.S.
forces in the area helped make
it possible." - ], - [ - "The quot;future quot; is
getting a chance to revive the
presently struggling New York
Giants. Two other teams also
decided it was time for a
change at quarterback, but the
Buffalo Bills are not one of
them." - ], - [ - "For the second time this year,
an alliance of major Internet
providers - including Atlanta-
based EarthLink -iled a
coordinated group of lawsuits
aimed at stemming the flood of
online junk mail." - ], - [ - " WASHINGTON (Reuters) - The
PIMCO mutual fund group has
agreed to pay \\$50 million to
settle fraud charges involving
improper rapid dealing in
mutual fund shares, the U.S.
Securities and Exchange
Commission said on Monday." - ], - [ - "Via Technologies has released
a version of the open-source
Xine media player that is
designed to take advantage of
hardware digital video
acceleration capabilities in
two of the company #39;s PC
chipsets, the CN400 and
CLE266." - ], - [ - "The Conference Board reported
Thursday that the Leading
Economic Index fell for a
third consecutive month in
August, suggesting slower
economic growth ahead amid
rising oil prices." - ], - [ - " SAN FRANCISCO (Reuters) -
Software maker Adobe Systems
Inc.<A HREF=\"http://www.inv
estor.reuters.com/FullQuote.as
px?ticker=ADBE.O target=/stock
s/quickinfo/fullquote\">ADBE
.O</A> on Thursday
posted a quarterly profit that
rose more than one-third from
a year ago, but shares fell 3
percent after the maker of
Photoshop and Acrobat software
did not raise forecasts for
fiscal 2005." - ], - [ - "William Morrison Supermarkets
has agreed to sell 114 small
Safeway stores and a
distribution centre for 260.2
million pounds. Morrison
bought these stores as part of
its 3 billion pound" - ], - [ - "SCO Group has a plan to keep
itself fit enough to continue
its legal battles against
Linux and to develop its Unix-
on-Intel operating systems." - ], - [ - "Flushing Meadows, NY (Sports
Network) - The men #39;s
semifinals at the 2004 US Open
will be staged on Saturday,
with three of the tournament
#39;s top-five seeds ready for
action at the USTA National
Tennis Center." - ], - [ - "Pepsi pushes a blue version of
Mountain Dew only at Taco
Bell. Is this a winning
strategy?" - ], - [ - "New software helps corporate
travel managers track down
business travelers who
overspend. But it also poses a
dilemma for honest travelers
who are only trying to save
money." - ], - [ - "NATO Secretary-General Jaap de
Hoop Scheffer has called a
meeting of NATO states and
Russia on Tuesday to discuss
the siege of a school by
Chechen separatists in which
more than 335 people died, a
NATO spokesman said." - ], - [ - "26 August 2004 -- Iraq #39;s
top Shi #39;ite cleric, Grand
Ayatollah Ali al-Sistani,
arrived in the city of Al-
Najaf today in a bid to end a
weeks-long conflict between US
forces and militiamen loyal to
Shi #39;ite cleric Muqtada al-
Sadr." - ], - [ - "AFP - Senior executives at
business software group
PeopleSoft unanimously
recommended that its
shareholders reject a 8.8
billion dollar takeover bid
from Oracle Corp, PeopleSoft
said in a statement Wednesday." - ], - [ - "Reuters - Neolithic people in
China may have\\been the first
in the world to make wine,
according to\\scientists who
have found the earliest
evidence of winemaking\\from
pottery shards dating from
7,000 BC in northern China." - ], - [ - "Given nearly a week to examine
the security issues raised by
the now-infamous brawl between
players and fans in Auburn
Hills, Mich., Nov. 19, the
Celtics returned to the
FleetCenter last night with
two losses and few concerns
about their on-court safety." - ], - [ - " TOKYO (Reuters) - Electronics
conglomerate Sony Corp.
unveiled eight new flat-screen
televisions on Thursday in a
product push it hopes will
help it secure a leading 35
percent of the domestic
market in the key month of
December." - ], - [ - "As the election approaches,
Congress abandons all pretense
of fiscal responsibility,
voting tax cuts that would
drive 10-year deficits past
\\$3 trillion." - ], - [ - "PARIS : French trade unions
called on workers at France
Telecom to stage a 24-hour
strike September 7 to protest
government plans to privatize
the public telecommunications
operator, union sources said." - ], - [ - "ServiceMaster profitably
bundles services and pays a
healthy 3.5 dividend." - ], - [ - "The Indonesian tourism
industry has so far not been
affected by last week #39;s
bombing outside the Australian
embassy in Jakarta and
officials said they do not
expect a significant drop in
visitor numbers as a result of
the attack." - ], - [ - "\\$222.5 million -- in an
ongoing securities class
action lawsuit against Enron
Corp. The settlement,
announced Friday and" - ], - [ - "Arsenals Thierry Henry today
missed out on the European
Footballer of the Year award
as Andriy Shevchenko took the
honour. AC Milan frontman
Shevchenko held off
competition from Barcelona
pair Deco and" - ], - [ - "Donald Halsted, one target of
a class-action suit alleging
financial improprieties at
bankrupt Polaroid, officially
becomes CFO." - ], - [ - "Neil Mellor #39;s sensational
late winner for Liverpool
against Arsenal on Sunday has
earned the back-up striker the
chance to salvage a career
that had appeared to be
drifting irrevocably towards
the lower divisions." - ], - [ - "ABOUT 70,000 people were
forced to evacuate Real Madrid
#39;s Santiago Bernabeu
stadium minutes before the end
of a Primera Liga match
yesterday after a bomb threat
in the name of ETA Basque
separatist guerillas." - ], - [ - "The team learned on Monday
that full-back Jon Ritchie
will miss the rest of the
season with a torn anterior
cruciate ligament in his left
knee." - ], - [ - " NEW YORK (Reuters) -
Lifestyle guru Martha Stewart
said on Wednesday she wants
to start serving her prison
sentence for lying about a
suspicious stock sale as soon
as possible, so she can put
her \"nightmare\" behind her." - ], - [ - "Apple Computer's iPod remains
the king of digital music
players, but robust pretenders
to the throne have begun to
emerge in the Windows
universe. One of them is the
Zen Touch, from Creative Labs." - ], - [ - "The 7710 model features a
touch screen, pen input, a
digital camera, an Internet
browser, a radio, video
playback and streaming and
recording capabilities, the
company said." - ], - [ - "SAN FRANCISCO (CBS.MW) --
Crude futures closed under
\\$46 a barrel Wednesday for
the first time since late
September and heating-oil and
unleaded gasoline prices
dropped more than 6 percent
following an across-the-board
climb in US petroleum
inventories." - ], - [ - "The University of Iowa #39;s
market for US presidential
futures, founded 16-years ago,
has been overtaken by a
Dublin-based exchange that is
now 25 times larger." - ], - [ - "Venus Williams barely kept
alive her hopes of qualifying
for next week #39;s WTA Tour
Championships. Williams,
seeded fifth, survived a
third-set tiebreaker to
outlast Yuilana Fedak of the
Ukraine, 6-4 2-6 7-6" - ], - [ - " SYDNEY (Reuters) - Arnold
Palmer has taken a swing at
America's top players,
criticizing their increasing
reluctance to travel abroad
to play in tournaments." - ], - [ - "MARK Thatcher will have to
wait until at least next April
to face trial on allegations
he helped bankroll a coup
attempt in oil-rich Equatorial
Guinea." - ], - [ - "A top Red Hat executive has
attacked the open-source
credentials of its sometime
business partner Sun
Microsystems. In a Web log
posting Thursday, Michael
Tiemann, Red Hat #39;s vice
president of open-source
affairs" - ], - [ - "President Bush #39;s drive to
deploy a multibillion-dollar
shield against ballistic
missiles was set back on
Wednesday by what critics
called a stunning failure of
its first full flight test in
two years." - ], - [ - "Although he was well-beaten by
Retief Goosen in Sunday #39;s
final round of The Tour
Championship in Atlanta, there
has been some compensation for
the former world number one,
Tiger Woods." - ], - [ - "WAYNE Rooney and Henrik
Larsson are among the players
nominated for FIFAs
prestigious World Player of
the Year award. Rooney is one
of four Manchester United
players on a list which is
heavily influenced by the
Premiership." - ], - [ - "It didn #39;t look good when
it happened on the field, and
it looked worse in the
clubhouse. Angels second
baseman Adam Kennedy left the
Angels #39; 5-2 win over the
Seattle Mariners" - ], - [ - "Air travelers moved one step
closer to being able to talk
on cell phones and surf the
Internet from laptops while in
flight, thanks to votes by the
Federal Communications
Commission yesterday." - ], - [ - "MySQL developers turn to an
unlikely source for database
tool: Microsoft. Also: SGI
visualizes Linux, and the
return of Java veteran Kim
Polese." - ], - [ - "DESPITE the budget deficit,
continued increases in oil and
consumer prices, the economy,
as measured by gross domestic
product, grew by 6.3 percent
in the third" - ], - [ - "NEW YORK - A drop in oil
prices and upbeat outlooks
from Wal-Mart and Lowe's
helped send stocks sharply
higher Monday on Wall Street,
with the swing exaggerated by
thin late summer trading. The
Dow Jones industrials surged
nearly 130 points..." - ], - [ - "Freshman Darius Walker ran for
115 yards and scored two
touchdowns, helping revive an
Irish offense that had managed
just one touchdown in the
season's first six quarters." - ], - [ - "Consumers who cut it close by
paying bills from their
checking accounts a couple of
days before depositing funds
will be out of luck under a
new law that takes effect Oct.
28." - ], - [ - "Dell Inc. said its profit
surged 25 percent in the third
quarter as the world's largest
personal computer maker posted
record sales due to rising
technology spending in the
corporate and government
sectors in the United States
and abroad." - ], - [ - "AP - NBC is adding a 5-second
delay to its NASCAR telecasts
after Dale Earnhardt Jr. used
a vulgarity during a postrace
TV interview last weekend." - ], - [ - " BOSTON (Sports Network) - The
New York Yankees will start
Orlando \"El Duque\" Hernandez
in Game 4 of the American
League Championship Series on
Saturday against the Boston
Red Sox." - ], - [ - "The future of Sven-Goran
Eriksson as England coach is
the subject of intense
discussion after the draw in
Austria. Has the Swede lost
the confidence of the nation
or does he remain the best man
for the job?" - ], - [ - "Component problems meant
Brillian's new big screens
missed the NFL's kickoff
party." - ], - [ - "Spain begin their third final
in five seasons at the Olympic
stadium hoping to secure their
second title since their first
in Barcelona against Australia
in 2000." - ], - [ - "Second-seeded David Nalbandian
of Argentina lost at the Japan
Open on Thursday, beaten by
Gilles Muller of Luxembourg
7-6 (4), 3-6, 6-4 in the third
round." - ], - [ - "Thursday #39;s unexpected
resignation of Memphis
Grizzlies coach Hubie Brown
left a lot of questions
unanswered. In his unique way
of putting things, the
71-year-old Brown seemed to
indicate he was burned out and
had some health concerns." - ], - [ - "RUDI Voller had quit as coach
of Roma after a 3-1 defeat
away to Bologna, the Serie A
club said today. Under the
former Germany coach, Roma had
taken just four league points
from a possible 12." - ], - [ - "A Russian court on Thursday
rejected an appeal by the
Yukos oil company seeking to
overturn a freeze on the
accounts of the struggling oil
giant #39;s core subsidiaries." - ], - [ - "ONE by one, the players #39;
faces had flashed up on the
giant Ibrox screens offering
season #39;s greetings to the
Rangers fans. But the main
presents were reserved for
Auxerre." - ], - [ - "Switzerland #39;s struggling
national airline reported a
second-quarter profit of 45
million Swiss francs (\\$35.6
million) Tuesday, although its
figures were boosted by a
legal settlement in France." - ], - [ - "ROSTOV-ON-DON, Russia --
Hundreds of protesters
ransacked and occupied the
regional administration
building in a southern Russian
province Tuesday, demanding
the resignation of the region
#39;s president, whose former
son-in-law has been linked to
a multiple" - ], - [ - "SIPTU has said it is strongly
opposed to any privatisation
of Aer Lingus as pressure
mounts on the Government to
make a decision on the future
funding of the airline." - ], - [ - "Reuters - SBC Communications
said on Monday it\\would offer
a television set-top box that
can handle music,\\photos and
Internet downloads, part of
SBC's efforts to expand\\into
home entertainment." - ], - [ - "Molson Inc. Chief Executive
Officer Daniel O #39;Neill
said he #39;ll provide
investors with a positive #39;
#39; response to their
concerns over the company
#39;s plan to let stock-
option holders vote on its
planned merger with Adolph
Coors Co." - ], - [ - "South Korea #39;s Grace Park
shot a seven-under-par 65 to
triumph at the CJ Nine Bridges
Classic on Sunday. Park #39;s
victory made up her final-
round collapse at the Samsung
World Championship two weeks
ago." - ], - [ - " WASHINGTON (Reuters) - Hopes
-- and worries -- that U.S.
regulators will soon end the
ban on using wireless phones
during U.S. commercial flights
are likely at least a year or
two early, government
officials and analysts say." - ], - [ - "AFP - Iraqi Foreign Minister
Hoshyar Zebari arrived
unexpectedly in the holy city
of Mecca Wednesday where he
met Crown Prince Abdullah bin
Abdul Aziz, the official SPA
news agency reported." - ], - [ - "Titans QB Steve McNair was
released from a Nashville
hospital after a two-night
stay for treatment of a
bruised sternum. McNair was
injured during the fourth
quarter of the Titans #39;
15-12 loss to Jacksonville on
Sunday." - ], - [ - "Keith Miller, Australia #39;s
most prolific all-rounder in
Test cricket, died today at a
nursing home, Cricket
Australia said. He was 84." - ], - [ - "Haitian police and UN troops
moved into a slum neighborhood
on Sunday and cleared street
barricades that paralyzed a
part of the capital." - ], - [ - "TORONTO Former Toronto pitcher
John Cerutti (seh-ROO
#39;-tee) was found dead in
his hotel room today,
according to the team. He was
44." - ], - [ - "withdrawal of troops and
settlers from occupied Gaza
next year. Militants seek to
claim any pullout as a
victory. quot;Islamic Jihad
will not be broken by this
martyrdom, quot; said Khaled
al-Batsh, a senior political
leader in Gaza." - ], - [ - " NEW YORK (Reuters) - The
world's largest gold producer,
Newmont Mining Corp. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=NEM
.N target=/stocks/quickinfo/fu
llquote\">NEM.N</A>,
on Wednesday said higher gold
prices drove up quarterly
profit by 12.5 percent, even
though it sold less of the
precious metal." - ], - [ - "The U.S. military has found
nearly 20 houses where
intelligence officers believe
hostages were tortured or
killed in this city, including
the house with the cage that
held a British contractor who
was beheaded last month." - ], - [ - "AFP - Opponents of the Lao
government may be plotting
bomb attacks in Vientiane and
other areas of Laos timed to
coincide with a summit of
Southeast Asian leaders the
country is hosting next month,
the United States said." - ], - [ - "After a year of pilots and
trials, Siebel Systems jumped
with both feet into the SMB
market Tuesday, announcing a
new approach to offer Siebel
Professional CRM applications
to SMBs (small and midsize
businesses) -- companies with
revenues up to about \\$500
million." - ], - [ - "AP - Russia agreed Thursday to
send warships to help NATO
naval patrols that monitor
suspicious vessels in the
Mediterranean, part of a push
for closer counterterrorism
cooperation between Moscow and
the western alliance." - ], - [ - "Intel won't release a 4-GHz
version of its flagship
Pentium 4 product, having
decided instead to realign its
engineers around the company's
new design priorities, an
Intel spokesman said today.
\\\\" - ], - [ - "AP - A Soyuz spacecraft
carrying two Russians and an
American rocketed closer
Friday to its docking with the
international space station,
where the current three-man
crew made final departure
preparations." - ], - [ - "Defense: Ken Lucas. His
biggest play was his first
one. The fourth-year
cornerback intercepted a Ken
Dorsey pass that kissed off
the hands of wide receiver
Rashaun Woods and returned it
25 yards to set up the
Seahawks #39; first score." - ], - [ - "Scientists have manipulated
carbon atoms to create a
material that could be used to
create light-based, versus
electronic, switches. The
material could lead to a
supercharged Internet based
entirely on light, scientists
say." - ], - [ - "A military plane crashed in
the mountains near Caracas,
killing all 16 persons on
board, including two high-
ranking military officers,
officials said." - ], - [ - "The powerful St. Louis trio of
Albert Pujols, Scott Rolen and
Jim Edmonds is 4 for 23 with
one RBI in the series and with
runners on base, they are 1
for 13." - ], - [ - "A voice recording said to be
that of suspected Al Qaeda
commander Abu Mussab al-
Zarqawi, claims Iraq #39;s
Prime Minister Iyad Allawi is
the militant network #39;s
number one target." - ], - [ - "BEIJING -- More than a year
after becoming China's
president, Hu Jintao was
handed the full reins of power
yesterday when his
predecessor, Jiang Zemin, gave
up the nation's most powerful
military post." - ], - [ - "LOS ANGELES (Reuters)
Qualcomm has dropped an \\$18
million claim for monetary
damages from rival Texas
Instruments for publicly
discussing terms of a
licensing pact, a TI
spokeswoman confirmed Tuesday." - ], - [ - "Hewlett-Packard is the latest
IT vendor to try blogging. But
analysts wonder if the weblog
trend is the 21st century
equivalent of CB radios, which
made a big splash in the 1970s
before fading." - ], - [ - " WASHINGTON (Reuters) - Fannie
Mae executives and their
regulator squared off on
Wednesday, with executives
denying any accounting
irregularity and the regulator
saying the housing finance
company's management may need
to go." - ], - [ - "The scientists behind Dolly
the sheep apply for a license
to clone human embryos. They
want to take stem cells from
the embryos to study Lou
Gehrig's disease." - ], - [ - "As the first criminal trial
stemming from the financial
deals at Enron opened in
Houston on Monday, it is
notable as much for who is not
among the six defendants as
who is - and for how little
money was involved compared
with how much in other Enron" - ], - [ - "LONDON (CBS.MW) -- British
bank Barclays on Thursday said
it is in talks to buy a
majority stake in South
African bank ABSA. Free!" - ], - [ - "The Jets signed 33-year-old
cornerback Terrell Buckley,
who was released by New
England on Sunday, after
putting nickel back Ray
Mickens on season-ending
injured reserve yesterday with
a torn ACL in his left knee." - ], - [ - "Some of the silly tunes
Japanese pay to download to
use as the ring tone for their
mobile phones sure have their
knockers, but it #39;s for
precisely that reason that a
well-known counselor is raking
it in at the moment, according
to Shukan Gendai (10/2)." - ], - [ - "WEST INDIES thrilling victory
yesterday in the International
Cricket Council Champions
Trophy meant the world to the
five million people of the
Caribbean." - ], - [ - "AP - Greenpeace activists
scaled the walls of Ford Motor
Co.'s Norwegian headquarters
Tuesday to protest plans to
destroy hundreds of non-
polluting electric cars." - ], - [ - "Investors sent stocks sharply
lower yesterday as oil prices
continued their climb higher
and new questions about the
safety of arthritis drugs
pressured pharmaceutical
stocks." - ], - [ - "Scotland manager Berti Vogts
insists he is expecting
nothing but victory against
Moldova on Wednesday. The game
certainly is a must-win affair
if the Scots are to have any
chance of qualifying for the
2006 World Cup finals." - ], - [ - "IBM announced yesterday that
it will invest US\\$250 million
(S\\$425 million) over the next
five years and employ 1,000
people in a new business unit
to support products and
services related to sensor
networks." - ], - [ - "AFP - The chances of Rupert
Murdoch's News Corp relocating
from Australia to the United
States have increased after
one of its biggest
institutional investors has
chosen to abstain from a vote
next week on the move." - ], - [ - "AFP - An Indian minister said
a school text-book used in the
violence-prone western state
of Gujarat portrayed Adolf
Hitler as a role model." - ], - [ - "Reuters - The head of UAL
Corp.'s United\\Airlines said
on Thursday the airline's
restructuring plan\\would lead
to a significant number of job
losses, but it was\\not clear
how many." - ], - [ - "DOVER, N.H. (AP) -- Democrat
John Kerry is seizing on the
Bush administration's failure
to secure hundreds of tons of
explosives now missing in
Iraq." - ], - [ - "AP - Microsoft Corp. goes into
round two Friday of its battle
to get the European Union's
sweeping antitrust ruling
lifted having told a judge
that it had been prepared
during settlement talks to
share more software code with
its rivals than the EU
ultimately demanded." - ], - [ - " INDIANAPOLIS (Reuters) -
Jenny Thompson will take the
spotlight from injured U.S.
team mate Michael Phelps at
the world short course
championships Saturday as she
brings down the curtain on a
spectacular swimming career." - ], - [ - "Martin Brodeur made 27 saves,
and Brad Richards, Kris Draper
and Joe Sakic scored goals to
help Canada beat Russia 3-1
last night in the World Cup of
Hockey, giving the Canadians a
3-0 record in round-robin
play." - ], - [ - "AP - Sears, Roebuck and Co.,
which has successfully sold
its tools and appliances on
the Web, is counting on having
the same magic with bedspreads
and sweaters, thanks in part
to expertise gained by its
purchase of Lands' End Inc." - ], - [ - "com September 14, 2004, 9:12
AM PT. With the economy slowly
turning up, upgrading hardware
has been on businesses radar
in the past 12 months as their
number two priority." - ], - [ - " NEW YORK (Reuters) -
Children's Place Retail Stores
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=PLCE.O target=/sto
cks/quickinfo/fullquote\">PL
CE.O</A> said on
Wednesday it will buy 313
retail stores from Walt
Disney Co., and its stock rose
more than 14 percent in early
morning trade." - ], - [ - "ALBANY, N.Y. -- A California-
based company that brokers
life, accident, and disability
policies for leading US
companies pocketed millions of
dollars a year in hidden
payments from insurers and
from charges on clients'
unsuspecting workers, New York
Attorney General Eliot Spitzer
charged yesterday." - ], - [ - "NORTEL Networks plans to slash
its workforce by 3500, or ten
per cent, as it struggles to
recover from an accounting
scandal that toppled three top
executives and led to a
criminal investigation and
lawsuits." - ], - [ - "Ebay Inc. (EBAY.O: Quote,
Profile, Research) said on
Friday it would buy Rent.com,
an Internet housing rental
listing service, for \\$415
million in a deal that gives
it access to a new segment of
the online real estate market." - ], - [ - "Austin police are working with
overseas officials to bring
charges against an English man
for sexual assault of a child,
a second-degree felony." - ], - [ - "United Nations officials
report security breaches in
internally displaced people
and refugee camps in Sudan
#39;s embattled Darfur region
and neighboring Chad." - ], - [ - "Noranda Inc., Canada #39;s
biggest mining company, began
exclusive talks on a takeover
proposal from China Minmetals
Corp. that would lead to the
spinoff of Noranda #39;s
aluminum business to
shareholders." - ], - [ - "San Francisco
developer/publisher lands
coveted Paramount sci-fi
license, \\$6.5 million in
funding on same day. Although
it is less than two years old,
Perpetual Entertainment has
acquired one of the most
coveted sci-fi licenses on the
market." - ], - [ - "ST. LOUIS -- Mike Martz #39;s
week of anger was no empty
display. He saw the defending
NFC West champions slipping
and thought taking potshots at
his players might be his best
shot at turning things around." - ], - [ - "Google warned Thursday that
increased competition and the
maturing of the company would
result in an quot;inevitable
quot; slowing of its growth." - ], - [ - "By KELLY WIESE JEFFERSON
CITY, Mo. (AP) -- Missouri
will allow members of the
military stationed overseas to
return absentee ballots via
e-mail, raising concerns from
Internet security experts
about fraud and ballot
secrecy..." - ], - [ - "Avis Europe PLC has dumped a
new ERP system based on
software from PeopleSoft Inc.
before it was even rolled out,
citing substantial delays and
higher-than-expected costs." - ], - [ - " TOKYO (Reuters) - The Nikkei
average rose 0.55 percent by
midsession on Wednesday as
some techs including Advantest
Corp. gained ground after
Wall Street reacted positively
to results from Intel Corp.
released after the U.S. market
close." - ], - [ - "Yahoo #39;s (Quote, Chart)
public embrace of the RSS
content syndication format
took a major leap forward with
the release of a revamped My
Yahoo portal seeking to
introduce the technology to
mainstream consumers." - ], - [ - "KINGSTON, Jamaica - Hurricane
Ivan's deadly winds and
monstrous waves bore down on
Jamaica on Friday, threatening
a direct hit on its densely
populated capital after
ravaging Grenada and killing
at least 33 people. The
Jamaican government ordered
the evacuation of half a
million people from coastal
areas, where rains on Ivan's
outer edges were already
flooding roads..." - ], - [ - "North Korea has denounced as
quot;wicked terrorists quot;
the South Korean officials who
orchestrated last month #39;s
airlift to Seoul of 468 North
Korean defectors." - ], - [ - "The Black Watch regiment has
returned to its base in Basra
in southern Iraq after a
month-long mission standing in
for US troops in a more
violent part of the country,
the Ministry of Defence says." - ], - [ - "Romanian soccer star Adrian
Mutu as he arrives at the
British Football Association
in London, ahead of his
disciplinary hearing, Thursday
Nov. 4, 2004." - ], - [ - "Australia completed an
emphatic Test series sweep
over New Zealand with a
213-run win Tuesday, prompting
a caution from Black Caps
skipper Stephen Fleming for
other cricket captains around
the globe." - ], - [ - "AP - A senior Congolese
official said Tuesday his
nation had been invaded by
neighboring Rwanda, and U.N.
officials said they were
investigating claims of
Rwandan forces clashing with
militias in the east." - ], - [ - "Liverpool, England (Sports
Network) - Former English
international and Liverpool
great Emlyn Hughes passed away
Tuesday from a brain tumor." - ], - [ - " ATLANTA (Reuters) - Soft
drink giant Coca-Cola Co.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=KO.N target=/stocks/quic
kinfo/fullquote\">KO.N</A
>, stung by a prolonged
downturn in North America,
Germany and other major
markets, on Thursday lowered
its key long-term earnings
and sales targets." - ], - [ - "JACKSONVILLE, Fla. -- They
were singing in the Colts #39;
locker room today, singing
like a bunch of wounded
songbirds. Never mind that
Marcus Pollard, Dallas Clark
and Ben Hartsock won #39;t be
recording a remake of Kenny
Chesney #39;s song,
quot;Young, quot; any time
soon." - ], - [ - "TOKYO (AP) -- Japanese
electronics and entertainment
giant Sony Corp. (SNE) plans
to begin selling a camcorder
designed for consumers that
takes video at digital high-
definition quality and is
being priced at about
\\$3,600..." - ], - [ - "As the close-knit NASCAR
community mourns the loss of
team owner Rick Hendrick #39;s
son, brother, twin nieces and
six others in a plane crash
Sunday, perhaps no one outside
of the immediate family
grieves more deeply than
Darrell Waltrip." - ], - [ - "AP - Purdue quarterback Kyle
Orton has no trouble
remembering how he felt after
last year's game at Michigan." - ], - [ - "UNITED NATIONS - The United
Nations #39; nuclear agency
says it is concerned about the
disappearance of equipment and
materials from Iraq that could
be used to make nuclear
weapons." - ], - [ - " BRUSSELS (Reuters) - The EU's
historic deal with Turkey to
open entry talks with the vast
Muslim country was hailed by
supporters as a bridge builder
between Europe and the Islamic
world." - ], - [ - "Iraqi President Ghazi al-
Yawar, who was due in Paris on
Sunday to start a European
tour, has postponed his visit
to France due to the ongoing
hostage drama involving two
French journalists, Arab
diplomats said Friday." - ], - [ - " SAO PAULO, Brazil (Reuters) -
President Luiz Inacio Lula da
Silva's Workers' Party (PT)
won the mayoralty of six state
capitals in Sunday's municipal
vote but was forced into a
run-off to defend its hold on
the race's biggest prize, the
city of Sao Paulo." - ], - [ - "ATHENS, Greece - They are
America's newest golden girls
- powerful and just a shade
from perfection. The U.S..." - ], - [ - "AMMAN, Sept. 15. - The owner
of a Jordanian truck company
announced today that he had
ordered its Iraq operations
stopped in a bid to save the
life of a driver held hostage
by a militant group." - ], - [ - "Israel is prepared to back a
Middle East conference
convened by Tony Blair early
next year despite having
expressed fears that the
British plans were over-
ambitious and designed" - ], - [ - "AP - U.S. State Department
officials learned that seven
American children had been
abandoned at a Nigerian
orphanage but waited more than
a week to check on the youths,
who were suffering from
malnutrition, malaria and
typhoid, a newspaper reported
Saturday." - ], - [ - "\\Angry mobs in Ivory Coast's
main city, Abidjan, marched on
the airport, hours after it
came under French control." - ], - [ - "Several workers are believed
to have been killed and others
injured after a contruction
site collapsed at Dubai
airport. The workers were
trapped under rubble at the
site of a \\$4." - ], - [ - "Talks between Sudan #39;s
government and two rebel
groups to resolve the nearly
two-year battle resume Friday.
By Abraham McLaughlin Staff
writer of The Christian
Science Monitor." - ], - [ - "In a meeting of the cinematic
with the scientific, Hollywood
helicopter stunt pilots will
try to snatch a returning NASA
space probe out of the air
before it hits the ground." - ], - [ - "Legend has it (incorrectly, it
seems) that infamous bank
robber Willie Sutton, when
asked why banks were his
favorite target, responded,
quot;Because that #39;s where
the money is." - ], - [ - "Brown is a second year player
from Memphis and has spent the
2004 season on the Steelers
#39; practice squad. He played
in two games last year." - ], - [ - "A Canadian court approved Air
Canada #39;s (AC.TO: Quote,
Profile, Research) plan of
arrangement with creditors on
Monday, clearing the way for
the world #39;s 11th largest
airline to emerge from
bankruptcy protection at the
end of next month" - ], - [ - "Pfizer, GlaxoSmithKline and
Purdue Pharma are the first
drugmakers willing to take the
plunge and use radio frequency
identification technology to
protect their US drug supply
chains from counterfeiters." - ], - [ - "Barret Jackman, the last of
the Blues left to sign before
the league #39;s probable
lockout on Wednesday,
finalized a deal Monday that
is rare in the current
economic climate but fitting
for him." - ], - [ - " LONDON (Reuters) - Oil prices
eased on Monday after rebels
in Nigeria withdrew a threat
to target oil operations, but
lingering concerns over
stretched supplies ahead of
winter kept prices close to
\\$50." - ], - [ - "AP - In the tumult of the
visitors' clubhouse at Yankee
Stadium, champagne pouring all
around him, Theo Epstein held
a beer. \"I came in and there
was no champagne left,\" he
said this week. \"I said, 'I'll
have champagne if we win it
all.'\" Get ready to pour a
glass of bubbly for Epstein.
No I.D. necessary." - ], - [ - "Search any fee-based digital
music service for the best-
loved musical artists of the
20th century and most of the
expected names show up." - ], - [ - "Barcelona held on from an
early Deco goal to edge game
local rivals Espanyol 1-0 and
carve out a five point
tabletop cushion. Earlier,
Ronaldo rescued a point for
Real Madrid, who continued
their middling form with a 1-1
draw at Real Betis." - ], - [ - "MONTREAL (CP) - The Expos may
be history, but their demise
has heated up the market for
team memorabilia. Vintage
1970s and 1980s shirts are
already sold out, but
everything from caps, beer
glasses and key-chains to
dolls of mascot Youppi!" - ], - [ - "Stansted airport is the
designated emergency landing
ground for planes in British
airspace hit by in-flight
security alerts. Emergency
services at Stansted have
successfully dealt" - ], - [ - "The massive military operation
to retake Fallujah has been
quot;accomplished quot;, a
senior Iraqi official said.
Fierce fighting continued in
the war-torn city where
pockets of resistance were
still holding out against US
forces." - ], - [ - "There are some signs of
progress in resolving the
Nigerian conflict that is
riling global oil markets. The
leader of militia fighters
threatening to widen a battle
for control of Nigeria #39;s
oil-rich south has" - ], - [ - "A strong earthquake hit Taiwan
on Monday, shaking buildings
in the capital Taipei for
several seconds. No casualties
were reported." - ], - [ - "America Online Inc. is
packaging new features to
combat viruses, spam and
spyware in response to growing
online security threats.
Subscribers will be able to
get the free tools" - ], - [ - "A 76th minute goal from
European Footballer of the
Year Pavel Nedved gave
Juventus a 1-0 win over Bayern
Munich on Tuesday handing the
Italians clear control at the
top of Champions League Group
C." - ], - [ - " LONDON (Reuters) - Oil prices
climbed above \\$42 a barrel on
Wednesday, rising for the
third day in a row as cold
weather gripped the U.S.
Northeast, the world's biggest
heating fuel market." - ], - [ - "A policeman ran amok at a
security camp in Indian-
controlled Kashmir after an
argument and shot dead seven
colleagues before he was
gunned down, police said on
Sunday." - ], - [ - "ANN ARBOR, Mich. -- Some NHL
players who took part in a
charity hockey game at the
University of Michigan on
Thursday were hopeful the news
that the NHL and the players
association will resume talks
next week" - ], - [ - "New York police have developed
a pre-emptive strike policy,
cutting off demonstrations
before they grow large." - ], - [ - "CAPE CANAVERAL-- NASA aims to
launch its first post-Columbia
shuttle mission during a
shortened nine-day window
March, and failure to do so
likely would delay a planned
return to flight until at
least May." - ], - [ - "Travelers headed home for
Thanksgiving were greeted
Wednesday with snow-covered
highways in the Midwest, heavy
rain and tornadoes in parts of
the South, and long security
lines at some of the nation
#39;s airports." - ], - [ - "BOULDER, Colo. -- Vernand
Morency ran for 165 yards and
two touchdowns and Donovan
Woods threw for three more
scores, lifting No. 22
Oklahoma State to a 42-14
victory over Colorado
yesterday." - ], - [ - "The Chinese city of Beijing
has cancelled an order for
Microsoft software, apparently
bowing to protectionist
sentiment. The deal has come
under fire in China, which is
trying to build a domestic
software industry." - ], - [ - "Apple says it will deliver its
iTunes music service to more
European countries next month.
Corroborating several reports
in recent months, Reuters is
reporting today that Apple
Computer is planning the next" - ], - [ - "Reuters - Motorola Inc., the
world's\\second-largest mobile
phone maker, said on Tuesday
it expects\\to sustain strong
sales growth in the second
half of 2004\\thanks to new
handsets with innovative
designs and features." - ], - [ - "PULLMAN - Last week, in
studying USC game film, Cougar
coaches thought they found a
chink in the national
champions armor. And not just
any chink - one with the
potential, right from the get
go, to" - ], - [ - "The union representing flight
attendants on Friday said it
mailed more than 5,000 strike
authorization ballots to its
members employed by US Airways
as both sides continued talks
that are expected to stretch
through the weekend." - ], - [ - "AP - Matt Leinart was quite a
baseball prospect growing up,
showing so much promise as a
left-handed pitcher that
scouts took notice before high
school." - ], - [ - "PRAGUE, Czech Republic --
Eugene Cernan, the last man to
walk on the moon during the
final Apollo landing, said
Thursday he doesn't expect
space tourism to become
reality in the near future,
despite a strong demand.
Cernan, now 70, who was
commander of NASA's Apollo 17
mission and set foot on the
lunar surface in December 1972
during his third space flight,
acknowledged that \"there are
many people interested in
space tourism.\" But the
former astronaut said he
believed \"we are a long way
away from the day when we can
send a bus of tourists to the
moon.\" He spoke to reporters
before being awarded a medal
by the Czech Academy of
Sciences for his contribution
to science..." - ], - [ - "Never shy about entering a
market late, Microsoft Corp.
is planning to open the
virtual doors of its long-
planned Internet music store
next week. <FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</B></FONT>" - ], - [ - "AP - On his first birthday
Thursday, giant panda cub Mei
Sheng delighted visitors by
playing for the first time in
snow delivered to him at the
San Diego Zoo. He also sat on
his ice cake, wrestled with
his mom, got his coat
incredibly dirty, and didn't
read any of the more than 700
birthday wishes sent him via
e-mail from as far away as
Ireland and Argentina." - ], - [ - "AP - Researchers put a
satellite tracking device on a
15-foot shark that appeared to
be lost in shallow water off
Cape Cod, the first time a
great white has been tagged
that way in the Atlantic." - ], - [ - "LSU will stick with a two-
quarterback rotation Saturday
at Auburn, according to Tigers
coach Nick Saban, who seemed
to have some fun telling the
media what he will and won
#39;t discuss Monday." - ], - [ - "Bulgaria has started its first
co-mission with the EU in
Bosnia and Herzegovina, along
with some 30 countries,
including Canada and Turkey." - ], - [ - "The Windows Future Storage
(WinFS) technology that got
cut out of Windows
quot;Longhorn quot; is in
serious trouble, and not just
the hot water a feature might
encounter for missing its
intended production vehicle." - ], - [ - "Seattle -- - Not so long ago,
the 49ers were inflicting on
other teams the kind of pain
and embarrassment they felt in
their 34-0 loss to the
Seahawks on Sunday." - ], - [ - "AP - The pileup of events in
the city next week, including
the Republican National
Convention, will add to the
security challenge for the New
York Police Department, but
commissioner Ray Kelly says,
\"With a big, experienced
police force, we can do it.\"" - ], - [ - "washingtonpost.com - Let the
games begin. Not the Olympics
again, but the all-out battle
between video game giants Sony
Corp. and Nintendo Co. Ltd.
The two Japanese companies are
rolling out new gaming
consoles, but Nintendo has
beaten Sony to the punch by
announcing an earlier launch
date for its new hand-held
game player." - ], - [ - "London - Manchester City held
fierce crosstown rivals
Manchester United to a 0-0
draw on Sunday, keeping the
Red Devils eleven points
behind leaders Chelsea." - ], - [ - "LONDON, Dec 11 (IranMania) -
Iraqi Vice-President Ibrahim
al-Jaafari refused to believe
in remarks published Friday
that Iran was attempting to
influence Iraqi polls with the
aim of creating a
quot;crescent quot; dominated
by Shiites in the region." - ], - [ - "LOS ANGELES (CBS.MW) - The US
Securities and Exchange
Commission is probing
transactions between Delphi
Corp and EDS, which supplies
the automotive parts and
components giant with
technology services, Delphi
said late Wednesday." - ], - [ - "MONTREAL (CP) - Molson Inc.
and Adolph Coors Co. are
sweetening their brewery
merger plan with a special
dividend to Molson
shareholders worth \\$381
million." - ], - [ - "AP - Echoing what NASA
officials said a day earlier,
a Russian space official on
Friday said the two-man crew
on the international space
station could be forced to
return to Earth if a planned
resupply flight cannot reach
them with food supplies later
this month." - ], - [ - "InfoWorld - SANTA CLARA,
CALIF. -- Accommodating large
patch sets in Linux is
expected to mean forking off
of the 2.7 version of the
platform to accommodate these
changes, according to Andrew
Morton, lead maintainer of the
Linux kernel for Open Source
Development Labs (OSDL)." - ], - [ - "AMSTERDAM The mobile phone
giants Vodafone and Nokia
teamed up on Thursday to
simplify cellphone software
written with the Java computer
language." - ], - [ - "WELLINGTON: National carrier
Air New Zealand said yesterday
the Australian Competition
Tribunal has approved a
proposed alliance with Qantas
Airways Ltd, despite its
rejection in New Zealand." - ], - [ - "The late Princess Dianas
former bodyguard, Ken Wharfe,
dismisses her suspicions that
one of her lovers was bumped
off. Princess Diana had an
affair with Barry Mannakee, a
policeman who was assigned to
protect her." - ], - [ - "Long considered beyond the
reach of mainland mores, the
Florida city is trying to
limit blatant displays of
sexual behavior." - ], - [ - "The overall Linux market is
far larger than previous
estimates show, a new study
says. In an analysis of the
Linux market released late
Tuesday, market research firm
IDC estimated that the Linux
market -- including" - ], - [ - "By PAUL ELIAS SAN FRANCISCO
(AP) -- Several California
cities and counties, including
San Francisco and Los Angeles,
sued Microsoft Corp. (MSFT) on
Friday, accusing the software
giant of illegally charging
inflated prices for its
products because of monopoly
control of the personal
computer operating system
market..." - ], - [ - "New Ole Miss head coach Ed
Orgeron, speaking for the
first time since his hiring,
made clear the goal of his
football program. quot;The
goal of this program will be
to go to the Sugar Bowl, quot;
Orgeron said." - ], - [ - "\"Everyone's nervous,\" Acting
Undersecretary of Defense
Michael W. Wynne warned in a
confidential e-mail to Air
Force Secretary James G. Roche
on July 8, 2003." - ], - [ - "Reuters - Alpharma Inc. on
Friday began\\selling a cheaper
generic version of Pfizer
Inc.'s #36;3\\billion a year
epilepsy drug Neurontin
without waiting for a\\court
ruling on Pfizer's request to
block the copycat medicine." - ], - [ - "Public opinion of the database
giant sinks to 12-year low, a
new report indicates." - ], - [ - "Opinion: Privacy hysterics
bring old whine in new bottles
to the Internet party. The
desktop search beta from this
Web search leader doesn #39;t
do anything you can #39;t do
already." - ], - [ - "It is much too easy to call
Pedro Martinez the selfish
one, to say he is walking out
on the Red Sox, his baseball
family, for the extra year of
the Mets #39; crazy money." - ], - [ - "It is impossible for young
tennis players today to know
what it was like to be Althea
Gibson and not to be able to
quot;walk in the front door,
quot; Garrison said." - ], - [ - "Senator John Kerry said today
that the war in Iraq was a
\"profound diversion\" from the
war on terror and Osama bin
Laden." - ], - [ - "For spammers, it #39;s been a
summer of love. Two newly
issued reports tracking the
circulation of unsolicited
e-mails say pornographic spam
dominated this summer, nearly
all of it originating from
Internet addresses in North
America." - ], - [ - "The Nordics fared well because
of their long-held ideals of
keeping corruption clamped
down and respect for
contracts, rule of law and
dedication to one-on-one
business relationships." - ], - [ - "Microsoft portrayed its
Longhorn decision as a
necessary winnowing to hit the
2006 timetable. The
announcement on Friday,
Microsoft executives insisted,
did not point to a setback in
software" - ], - [ - "Here #39;s an obvious word of
advice to Florida athletic
director Jeremy Foley as he
kicks off another search for
the Gators football coach: Get
Steve Spurrier on board." - ], - [ - "Don't bother with the small
stuff. Here's what really
matters to your lender." - ], - [ - "A problem in the Service Pack
2 update for Windows XP may
keep owners of AMD-based
computers from using the long-
awaited security package,
according to Microsoft." - ], - [ - "Five years ago, running a
telephone company was an
immensely profitable
proposition. Since then, those
profits have inexorably
declined, and now that decline
has taken another gut-
wrenching dip." - ], - [ - "NEW YORK - The litigious
Recording Industry Association
of America (RIAA) is involved
in another legal dispute with
a P-to-P (peer-to-peer)
technology maker, but this
time, the RIAA is on defense.
Altnet Inc. filed a lawsuit
Wednesday accusing the RIAA
and several of its partners of
infringing an Altnet patent
covering technology for
identifying requested files on
a P-to-P network." - ], - [ - "A group claiming to have
captured two Indonesian women
in Iraq has said it will
release them if Jakarta frees
Muslim cleric Abu Bakar Bashir
being held for alleged
terrorist links." - ], - [ - "Amid the stormy gloom in
Gotham, the rain-idled Yankees
last night had plenty of time
to gather in front of their
televisions and watch the Red
Sox Express roar toward them.
The national telecast might
have been enough to send a
jittery Boss Steinbrenner
searching his Bartlett's
Familiar Quotations for some
quot;Little Engine That Could
quot; metaphor." - ], - [ - "FULHAM fans would have been
singing the late Elvis #39;
hit #39;The wonder of you
#39; to their player Elvis
Hammond. If not for Frank
Lampard spoiling the party,
with his dedication to his
late grandfather." - ], - [ - "Indonesian police said
yesterday that DNA tests had
identified a suicide bomber
involved in a deadly attack
this month on the Australian
embassy in Jakarta." - ], - [ - "NEW YORK - Wal-Mart Stores
Inc.'s warning of
disappointing sales sent
stocks fluctuating Monday as
investors' concerns about a
slowing economy offset their
relief over a drop in oil
prices. October contracts
for a barrel of light crude
were quoted at \\$46.48, down
24 cents, on the New York
Mercantile Exchange..." - ], - [ - "Iraq #39;s top Shi #39;ite
cleric made a sudden return to
the country on Wednesday and
said he had a plan to end an
uprising in the quot;burning
city quot; of Najaf, where
fighting is creeping ever
closer to its holiest shrine." - ], - [ - "For two weeks before MTV
debuted U2 #39;s video for the
new single quot;Vertigo,
quot; fans had a chance to see
the band perform the song on
TV -- in an iPod commercial." - ], - [ - "A bird #39;s eye view of the
circuit at Shanghai shows what
an event Sunday #39;s Chinese
Grand Prix will be. The course
is arguably one of the best
there is, and so it should be
considering the amount of
money that has been spent on
it." - ], - [ - "KABUL, Afghanistan Aug. 22,
2004 - US soldiers sprayed a
pickup truck with bullets
after it failed to stop at a
roadblock in central
Afghanistan, killing two women
and a man and critically
wounding two other" - ], - [ - "Oct. 26, 2004 - The US-
European spacecraft Cassini-
Huygens on Tuesday made a
historic flyby of Titan,
Saturn #39;s largest moon,
passing so low as to almost
touch the fringes of its
atmosphere." - ], - [ - "Reuters - A volcano in central
Japan sent smoke and\\ash high
into the sky and spat out
molten rock as it erupted\\for
a fourth straight day on
Friday, but experts said the
peak\\appeared to be quieting
slightly." - ], - [ - "Shares of Google Inc. made
their market debut on Thursday
and quickly traded up 19
percent at \\$101.28. The Web
search company #39;s initial
public offering priced at \\$85" - ], - [ - "SYDNEY -- Prime Minister John
Howard of Australia, a key US
ally and supporter of the Iraq
war, celebrated his election
win over opposition Labor
after voters enjoying the
fruits of a strong economy
gave him another term." - ], - [ - "Reuters - Global warming is
melting\\Ecuador's cherished
mountain glaciers and could
cause several\\of them to
disappear over the next two
decades, Ecuadorean and\\French
scientists said on Wednesday." - ], - [ - "AP - Sirius Satellite Radio
signed a deal to air the men's
NCAA basketball tournament
through 2007, the latest move
made in an attempt to draw
customers through sports
programming." - ], - [ - "Rather than tell you, Dan
Kranzler chooses instead to
show you how he turned Mforma
into a worldwide publisher of
video games, ringtones and
other hot downloads for mobile
phones." - ], - [ - "UK interest rates have been
kept on hold at 4.75 following
the latest meeting of the Bank
of England #39;s rate-setting
committee." - ], - [ - "BAGHDAD, Iraq - Two rockets
hit a downtown Baghdad hotel
housing foreigners and
journalists Thursday, and
gunfire erupted in the
neighborhood across the Tigris
River from the U.S. Embassy
compound..." - ], - [ - "The Prevention of Terrorism
Act 2002 (Pota) polarised the
country, not just by the
manner in which it was pushed
through by the NDA government
through a joint session of
Parliament but by the shabby
and often biased manner in
which it was enforced." - ], - [ - "Not being part of a culture
with a highly developed
language, could limit your
thoughts, at least as far as
numbers are concerned, reveals
a new study conducted by a
psychologist at the Columbia
University in New York." - ], - [ - "CAMBRIDGE, Mass. A native of
Red Oak, Iowa, who was a
pioneer in astronomy who
proposed the quot;dirty
snowball quot; theory for the
substance of comets, has died." - ], - [ - "Resurgent oil prices paused
for breath as the United
States prepared to draw on its
emergency reserves to ease
supply strains caused by
Hurricane Ivan." - ], - [ - "(Sports Network) - The
inconsistent San Diego Padres
will try for consecutive wins
for the first time since
August 28-29 tonight, when
they begin a huge four-game
set against the Los Angeles
Dodgers at Dodger Stadium." - ], - [ - "A Portuguese-sounding version
of the virus has appeared in
the wild. Be wary of mail from
Manaus." - ], - [ - " NEW YORK (Reuters) - Top seed
Roger Federer survived a
stirring comeback from twice
champion Andre Agassi to reach
the semifinals of the U.S.
Open for the first time on
Thursday, squeezing through
6-3, 2-6, 7-5, 3-6, 6-3." - ], - [ - "President Bush, who credits
three years of tax relief
programs with helping
strengthen the slow economy,
said Saturday he would sign
into law the Working Families
Tax Relief Act to preserve tax
cuts." - ], - [ - "HEN investors consider the
bond market these days, the
low level of interest rates
should be more cause for worry
than for gratitude." - ], - [ - "Reuters - Hunters soon may be
able to sit at\\their computers
and blast away at animals on a
Texas ranch via\\the Internet,
a prospect that has state
wildlife officials up\\in arms." - ], - [ - "The Bedminster-based company
yesterday said it was pushing
into 21 new markets with the
service, AT amp;T CallVantage,
and extending an introductory
rate offer until Sept. 30. In
addition, the company is
offering in-home installation
of up to five ..." - ], - [ - "Samsung Electronics Co., Ltd.
has developed a new LCD
(liquid crystal display)
technology that builds a touch
screen into the display, a
development that could lead to
thinner and cheaper display
panels for mobile phones, the
company said Tuesday." - ], - [ - "The US military says marines
in Fallujah shot and killed an
insurgent who engaged them as
he was faking being dead, a
week after footage of a marine
killing an apparently unarmed
and wounded Iraqi caused a
stir in the region." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks rallied on Monday after
software maker PeopleSoft Inc.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=PSFT.O target=/stocks/qu
ickinfo/fullquote\">PSFT.O&l
t;/A> accepted a sweetened
\\$10.3 billion buyout by rival
Oracle Corp.'s <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=ORCL.O ta
rget=/stocks/quickinfo/fullquo
te\">ORCL.O</A> and
other big deals raised
expectations of more
takeovers." - ], - [ - "SAN FRANCISCO - What Babe Ruth
was to the first half of the
20th century and Hank Aaron
was to the second, Barry Bonds
has become for the home run
generation." - ], - [ - "Description: NPR #39;s Alex
Chadwick talks to Colin Brown,
deputy political editor for
the United Kingdom #39;s
Independent newspaper,
currently covering the British
Labour Party Conference." - ], - [ - "Birgit Fischer settled for
silver, leaving the 42-year-
old Olympian with two medals
in two days against decidedly
younger competition." - ], - [ - "The New Jersey-based Accoona
Corporation, an industry
pioneer in artificial
intelligence search
technology, announced on
Monday the launch of Accoona." - ], - [ - "Hamas vowed revenge yesterday
after an Israeli airstrike in
Gaza killed one of its senior
commanders - the latest
assassination to have weakened
the militant group." - ], - [ - "There was no mystery ... no
secret strategy ... no baited
trap that snapped shut and
changed the course of history
#39;s most lucrative non-
heavyweight fight." - ], - [ - "Notre Dame accepted an
invitation Sunday to play in
the Insight Bowl in Phoenix
against a Pac-10 team on Dec.
28. The Irish (6-5) accepted
the bid a day after losing to
Southern California" - ], - [ - "Greg Anderson has so dominated
Pro Stock this season that his
championship quest has evolved
into a pursuit of NHRA
history. By Bob Hesser, Racers
Edge Photography." - ], - [ - "Goldman Sachs Group Inc. on
Thursday said fourth-quarter
profit rose as its fixed-
income, currency and
commodities business soared
while a rebounding stock
market boosted investment
banking." - ], - [ - " NEW YORK (Reuters) - The
dollar rose on Monday in a
retracement from last week's
steep losses, but dealers said
the bias toward a weaker
greenback remained intact." - ], - [ - "Michael Powell, chairman of
the FCC, said Wednesday he was
disappointed with ABC for
airing a sexually suggestive
opening to \"Monday Night
Football.\"" - ], - [ - "The message against illegally
copying CDs for uses such as
in file-sharing over the
Internet has widely sunk in,
said the company in it #39;s
recent announcement to drop
the Copy-Control program." - ], - [ - "Former Washington football
coach Rick Neuheisel looked
forward to a return to
coaching Wednesday after being
cleared by the NCAA of
wrongdoing related to his
gambling on basketball games." - ], - [ - "Reuters - California will
become hotter and\\drier by the
end of the century, menacing
the valuable wine and\\dairy
industries, even if dramatic
steps are taken to curb\\global
warming, researchers said on
Monday." - ], - [ - "A senior member of the
Palestinian resistance group
Hamas has been released from
an Israeli prison after
completing a two-year
sentence." - ], - [ - "IBM said Monday that it won a
500 million (AUD\\$1.25
billion), seven-year services
contract to help move UK bank
Lloyds TBS from its
traditional voice
infrastructure to a converged
voice and data network." - ], - [ - "MPs have announced a new
inquiry into family courts and
whether parents are treated
fairly over issues such as
custody or contact with their
children." - ], - [ - "Canadian Press - MELBOURNE,
Australia (AP) - A 36-year-old
businesswoman was believed to
be the first woman to walk
around Australia on Friday
after striding into her
hometown of Melbourne to
complete her 16,700-kilometre
trek in 365 days." - ], - [ - "Most remaining Pakistani
prisoners held at the US
Guantanamo Bay prison camp are
freed, officials say." - ], - [ - "Space shuttle astronauts will
fly next year without the
ability to repair in orbit the
type of damage that destroyed
the Columbia vehicle in
February 2003." - ], - [ - "Moscow - Russia plans to
combine Gazprom, the world
#39;s biggest natural gas
producer, with state-owned oil
producer Rosneft, easing rules
for trading Gazprom shares and
creating a company that may
dominate the country #39;s
energy industry." - ], - [ - "AP - Rutgers basketball player
Shalicia Hurns was suspended
from the team after pleading
guilty to punching and tying
up her roommate during a
dispute over painkilling
drugs." - ], - [ - "By cutting WinFS from Longhorn
and indefinitely delaying the
storage system, Microsoft
Corp. has also again delayed
the Microsoft Business
Framework (MBF), a new Windows
programming layer that is
closely tied to WinFS." - ], - [ - "French police are
investigating an arson-caused
fire at a Jewish Social Center
that might have killed dozens
without the quick response of
firefighters." - ], - [ - "Rodney King, whose videotaped
beating led to riots in Los
Angeles in 1992, is out of
jail now and talking frankly
for the first time about the
riots, himself and the
American way of life." - ], - [ - "AFP - Radical Islamic cleric
Abu Hamza al-Masri was set to
learn Thursday whether he
would be charged under
Britain's anti-terrorism law,
thus delaying his possible
extradition to the United
States to face terrorism-
related charges." - ], - [ - "Diversified manufacturer
Honeywell International Inc.
(HON.N: Quote, Profile,
Research) posted a rise in
quarterly profit as strong
demand for aerospace equipment
and automobile components" - ], - [ - "This was the event Michael
Phelps didn't really need to
compete in if his goal was to
win eight golds. He probably
would have had a better chance
somewhere else." - ], - [ - "Samsung's new SPH-V5400 mobile
phone sports a built-in
1-inch, 1.5-gigabyte hard disk
that can store about 15 times
more data than conventional
handsets, Samsung said." - ], - [ - "Reuters - U.S. housing starts
jumped a\\larger-than-expected
6.4 percent in October to the
busiest pace\\since December as
buyers took advantage of low
mortgage rates,\\a government
report showed on Wednesday." - ], - [ - "Gabe Kapler became the first
player to leave the World
Series champion Boston Red
Sox, agreeing to a one-year
contract with the Yomiuri
Giants in Tokyo." - ], - [ - "Louisen Louis, 30, walked
Monday in the middle of a
street that resembled a small
river with brown rivulets and
waves. He wore sandals and had
a cut on one of his big toes." - ], - [ - "BALI, Indonesia - Svetlana
Kuznetsova, fresh off her
championship at the US Open,
defeated Australian qualifier
Samantha Stosur 6-4, 6-4
Thursday to reach the
quarterfinals of the Wismilak
International." - ], - [ - "The Securities and Exchange
Commission ordered mutual
funds to stop paying higher
commissions to brokers who
promote the companies' funds
and required portfolio
managers to reveal investments
in funds they supervise." - ], - [ - "A car bomb exploded outside
the main hospital in Chechny
#39;s capital, Grozny, on
Sunday, injuring 17 people in
an attack apparently targeting
members of a Chechen security
force bringing in wounded from
an earlier explosion" - ], - [ - "AP - Just like the old days in
Dallas, Emmitt Smith made life
miserable for the New York
Giants on Sunday." - ], - [ - "Sumitomo Mitsui Financial
Group (SMFG), Japans second
largest bank, today put
forward a 3,200 billion (\\$29
billion) takeover bid for
United Financial Group (UFJ),
the countrys fourth biggest
lender, in an effort to regain
initiative in its bidding" - ], - [ - "AP - Gay marriage is emerging
as a big enough issue in
several states to influence
races both for Congress and
the presidency." - ], - [ - "TAMPA, Fla. - Chris Simms
first NFL start lasted 19
plays, and it might be a while
before he plays again for the
Tampa Bay Buccaneers." - ], - [ - "Vendor says it #39;s
developing standards-based
servers in various form
factors for the telecom
market. By Darrell Dunn.
Hewlett-Packard on Thursday
unveiled plans to create a
portfolio of products and
services" - ], - [ - "Jarno Trulli made the most of
the conditions in qualifying
to claim pole ahead of Michael
Schumacher, while Fernando
finished third." - ], - [ - "More than 30 aid workers have
been airlifted to safety from
a town in Sudan #39;s troubled
Darfur region after fighting
broke out and their base was
bombed, a British charity
says." - ], - [ - " NEW YORK (Reuters) - U.S.
chain store retail sales
slipped during the
Thanksgiving holiday week, as
consumers took advantage of
discounted merchandise, a
retail report said on
Tuesday." - ], - [ - "The Mac maker says it will
replace about 28,000 batteries
in one model of PowerBook G4
and tells people to stop using
the notebook." - ], - [ - "It #39;s the mildest of mild
winters down here in the south
of Italy and, last weekend at
Bcoli, a pretty suburb by the
seaside west of Naples, the
customers of Pizzeria quot;Da
Enrico quot; were making the
most of it." - ], - [ - "By George Chamberlin , Daily
Transcript Financial
Correspondent. Concerns about
oil production leading into
the winter months sent shivers
through the stock market
Wednesday." - ], - [ - "Airbus has withdrawn a filing
that gave support for
Microsoft in an antitrust case
before the European Union
#39;s Court of First Instance,
a source close to the
situation said on Friday." - ], - [ - "WASHINGTON - A spotty job
market and stagnant paychecks
cloud this Labor Day holiday
for many workers, highlighting
the importance of pocketbook
issues in the presidential
election. \"Working harder
and enjoying it less,\" said
economist Ken Mayland,
president of ClearView
Economics, summing up the
state of working America..." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks rose on Wednesday
lifted by a merger between
retailers Kmart and Sears,
better-than-expected earnings
from Hewlett-Packard and data
showing a slight rise in core
inflation." - ], - [ - "AP - Authorities are
investigating whether bettors
at New York's top thoroughbred
tracks were properly informed
when jockeys came in
overweight at races, a source
familiar with the probe told
The Associated Press." - ], - [ - "European Commission president
Romano Prodi has unveiled
proposals to loosen the
deficit rules under the EU
Stability Pact. The loosening
was drafted by monetary
affairs commissioner Joaquin
Almunia, who stood beside the
president at the announcement." - ], - [ - "Canadian Press - MONTREAL (CP)
- A 19-year-old man charged in
a firebombing at a Jewish
elementary school pleaded
guilty Thursday to arson." - ], - [ - "Retail sales in Britain saw
the fastest growth in
September since January,
casting doubts on the view
that the economy is slowing
down, according to official
figures released Thursday." - ], - [ - " NEW YORK (Reuters) -
Interstate Bakeries Corp.
<A HREF=\"http://www.investo
r.reuters.com/FullQuote.aspx?t
icker=IBC.N target=/stocks/qui
ckinfo/fullquote\">IBC.N<
/A>, maker of Hostess
Twinkies and Wonder Bread,
filed for bankruptcy on
Wednesday after struggling
with more than \\$1.3 billion
in debt and high costs." - ], - [ - "Delta Air Lines (DAL.N: Quote,
Profile, Research) on Thursday
said it reached a deal with
FedEx Express to sell eight
McDonnell Douglas MD11
aircraft and four spare
engines for delivery in 2004." - ], - [ - "Michael Owen scored his first
goal for Real Madrid in a 1-0
home victory over Dynamo Kiev
in the Champions League. The
England striker toe-poked home
Ronaldo #39;s cross in the
35th minute to join the
Russians" - ], - [ - " quot;Resuming uranium
enrichment is not in our
agenda. We are still committed
to the suspension, quot;
Foreign Ministry spokesman
Hamid Reza." - ], - [ - "Leading OPEC producer Saudi
Arabia said on Monday in
Vienna, Austria, that it had
made a renewed effort to
deflate record high world oil
prices by upping crude output
again." - ], - [ - "The U.S. military presence in
Iraq will grow to 150,000
troops by next month, the
highest level since the
invasion last year." - ], - [ - "UPDATE, SUN 9PM: More than a
million people have left their
homes in Cuba, as Hurricane
Ivan approaches. The ferocious
storm is headed that way,
after ripping through the
Cayman Islands, tearing off
roofs, flooding homes and
causing general havoc." - ], - [ - "Jason Giambi has returned to
the New York Yankees'
clubhouse but is still
clueless as to when he will be
able to play again." - ], - [ - "Seventy-five National Hockey
League players met with union
leaders yesterday to get an
update on a lockout that shows
no sign of ending." - ], - [ - "Howard, at 6-4 overall and 3-3
in the Mid-Eastern Athletic
Conference, can clinch a
winning record in the MEAC
with a win over Delaware State
on Saturday." - ], - [ - "Millions of casual US anglers
are having are larger than
appreciated impact on sea fish
stocks, scientists claim." - ], - [ - "The founders of the Pilgrim
Baxter amp; Associates money-
management firm agreed
yesterday to personally fork
over \\$160 million to settle
charges they allowed a friend
to" - ], - [ - " ATHENS (Reuters) - Top-ranked
Argentina booked their berth
in the women's hockey semi-
finals at the Athens Olympics
on Friday but defending
champions Australia now face
an obstacle course to qualify
for the medal matches." - ], - [ - "IBM Corp. Tuesday announced
plans to acquire software
vendor Systemcorp ALG for an
undisclosed amount. Systemcorp
of Montreal makes project
portfolio management software
aimed at helping companies
better manage their IT
projects." - ], - [ - "The Notre Dame message boards
are no longer discussing
whether Tyrone Willingham
should be fired. Theyre
already arguing about whether
the next coach should be Barry
Alvarez or Steve Spurrier." - ], - [ - "Forbes.com - By now you
probably know that earnings of
Section 529 college savings
accounts are free of federal
tax if used for higher
education. But taxes are only
part of the problem. What if
your investments tank? Just
ask Laurence and Margo
Williams of Alexandria, Va. In
2000 they put #36;45,000 into
the Virginia Education Savings
Trust to open accounts for
daughters Lea, now 5, and
Anne, now 3. Since then their
investment has shrunk 5 while
the average private college
tuition has climbed 18 to
#36;18,300." - ], - [ - "German Chancellor Gerhard
Schroeder said Sunday that
there was quot;no problem
quot; with Germany #39;s
support to the start of
negotiations on Turkey #39;s
entrance into EU." - ], - [ - "Coca-Cola Amatil Ltd.,
Australia #39;s biggest soft-
drink maker, offered A\\$500
million (\\$382 million) in
cash and stock for fruit
canner SPC Ardmona Ltd." - ], - [ - "US technology shares tumbled
on Friday after technology
bellwether Intel Corp.
(INTC.O: Quote, Profile,
Research) slashed its revenue
forecast, but blue chips were
only moderately lower as drug
and industrial stocks made
solid gains." - ], - [ - " WASHINGTON (Reuters) - Final
U.S. government tests on an
animal suspected of having mad
cow disease were not yet
complete, the U.S. Agriculture
Department said, with no
announcement on the results
expected on Monday." - ], - [ - "MANILA Fernando Poe Jr., the
popular actor who challenged
President Gloria Macapagal
Arroyo in the presidential
elections this year, died
early Tuesday." - ], - [ - " THOMASTOWN, Ireland (Reuters)
- World number three Ernie
Els overcame difficult weather
conditions to fire a sparkling
eight-under-par 64 and move
two shots clear after two
rounds of the WGC-American
Express Championship Friday." - ], - [ - "Lamar Odom supplemented 20
points with 13 rebounds and
Kobe Bryant added 19 points to
overcome a big night from Yao
Ming as the Los Angeles Lakers
ground out an 84-79 win over
the Rockets in Houston
Saturday." - ], - [ - "AMSTERDAM, NETHERLANDS - A
Dutch filmmaker who outraged
members of the Muslim
community by making a film
critical of the mistreatment
of women in Islamic society
was gunned down and stabbed to
death Tuesday on an Amsterdam
street." - ], - [ - "Microsoft Xbox Live traffic on
service provider networks
quadrupled following the
November 9th launch of Halo-II
-- which set entertainment
industry records by selling
2.4-million units in the US
and Canada on the first day of
availability, driving cash" - ], - [ - "Lawyers in a California class
action suit against Microsoft
will get less than half the
payout they had hoped for. A
judge in San Francisco ruled
that the attorneys will
collect only \\$112." - ], - [ - "Google Browser May Become
Reality\\\\There has been much
fanfare in the Mozilla fan
camps about the possibility of
Google using Mozilla browser
technology to produce a
GBrowser - the Google Browser.
Over the past two weeks, the
news and speculation has
escalated to the point where
even Google itself is ..." - ], - [ - "Metro, Germany's biggest
retailer, turns in weaker-
than-expected profits as sales
at its core supermarkets
division dip lower." - ], - [ - "It rained Sunday, of course,
and but another soppy, sloppy
gray day at Westside Tennis
Club did nothing to deter
Roger Federer from his
appointed rounds." - ], - [ - "Hewlett-Packard has joined
with Brocade to integrate
Brocade #39;s storage area
network switching technology
into HP Bladesystem servers to
reduce the amount of fabric
infrastructure needed in a
datacentre." - ], - [ - "Zimbabwe #39;s most persecuted
white MP began a year of hard
labour last night after
parliament voted to jail him
for shoving the Justice
Minister during a debate over
land seizures." - ], - [ - "BOSTON (CBS.MW) -- First
Command has reached a \\$12
million settlement with
federal regulators for making
misleading statements and
omitting important information
when selling mutual funds to
US military personnel." - ], - [ - "A smashing blow is being dealt
to thousands of future
pensioners by a law that has
just been brought into force
by the Federal Government." - ], - [ - "The US Senate Commerce
Committee on Wednesday
approved a measure that would
provide up to \\$1 billion to
ensure consumers can still
watch television when
broadcasters switch to new,
crisp digital signals." - ], - [ - "Amelie Mauresmo was handed a
place in the Advanta
Championships final after
Maria Sharapova withdrew from
their semi-final because of
injury." - ], - [ - "AP - An Israeli helicopter
fired two missiles in Gaza
City after nightfall
Wednesday, one at a building
in the Zeitoun neighborhood,
witnesses said, setting a
fire." - ], - [ - "Reuters - Internet stocks
are\\as volatile as ever, with
growth-starved investors
flocking to\\the sector in the
hope they've bought shares in
the next online\\blue chip." - ], - [ - "The federal government, banks
and aircraft lenders are
putting the clamps on
airlines, particularly those
operating under bankruptcy
protection." - ], - [ - "EURO DISNEY, the financially
crippled French theme park
operator, has admitted that
its annual losses more than
doubled last financial year as
it was hit by a surge in
costs." - ], - [ - " EAST RUTHERFORD, New Jersey
(Sports Network) - Retired NBA
center and seven-time All-
Star Alonzo Mourning is going
to give his playing career
one more shot." - ], - [ - "NASA has released an inventory
of the scientific devices to
be put on board the Mars
Science Laboratory rover
scheduled to land on the
surface of Mars in 2009, NASAs
news release reads." - ], - [ - "The U.S. Congress needs to
invest more in the U.S.
education system and do more
to encourage broadband
adoption, the chief executive
of Cisco said Wednesday.<p&
gt;ADVERTISEMENT</p><
p><img src=\"http://ad.do
ubleclick.net/ad/idg.us.ifw.ge
neral/sbcspotrssfeed;sz=1x1;or
d=200301151450?\" width=\"1\"
height=\"1\"
border=\"0\"/><a href=\"htt
p://ad.doubleclick.net/clk;922
8975;9651165;a?http://www.info
world.com/spotlights/sbc/main.
html?lpid0103035400730000idlp\"
>SBC Case Study: Crate Ba
rrel</a><br/>What
sold them on improving their
network? A system that could
cut management costs from the
get-go. Find out
more.</p>" - ], - [ - "The Philippines put the toll
at more than 1,000 dead or
missing in four storms in two
weeks but, even with a break
in the weather on Saturday" - ], - [ - "Reuters - Four explosions were
reported at petrol\\stations in
the Madrid area on Friday,
Spanish radio stations\\said,
following a phone warning in
the name of the armed
Basque\\separatist group ETA to
a Basque newspaper." - ], - [ - "Thirty-two countries and
regions will participate the
Fifth China International
Aviation and Aerospace
Exhibition, opening Nov. 1 in
Zhuhai, a city in south China
#39;s Guangdong Province." - ], - [ - "Jordan have confirmed that
Timo Glock will replace
Giorgio Pantano for this
weekend #39;s Chinese GP as
the team has terminated its
contract with Pantano." - ], - [ - "WEST PALM BEACH, Fla. -
Hurricane Jeanne got stronger,
bigger and faster as it
battered the Bahamas and bore
down on Florida Saturday,
sending huge waves crashing
onto beaches and forcing
thousands into shelters just
weeks after Frances ravaged
this area..." - ], - [ - "p2pnet.net News:- Virgin
Electronics has joined the mp3
race with a \\$250, five gig
player which also handles
Microsoft #39;s WMA format." - ], - [ - "WASHINGTON The idea of a no-
bid contract for maintaining
airport security equipment has
turned into a non-starter for
the Transportation Security
Administration." - ], - [ - "Eyetech (EYET:Nasdaq - news -
research) did not open for
trading Friday because a Food
and Drug Administration
advisory committee is meeting
to review the small New York-
based biotech #39;s
experimental eye disease drug." - ], - [ - "The continuing heartache of
Wake Forest #39;s ACC football
season was best described by
fifth-ranked Florida State
coach Bobby Bowden, after his
Seminoles had edged the
Deacons 20-17 Saturday at
Groves Stadium." - ], - [ - "On September 13, 2001, most
Americans were still reeling
from the shock of the
terrorist attacks on New York
and the Pentagon two days
before." - ], - [ - "Microsoft has suspended the
beta testing of the next
version of its MSN Messenger
client because of a potential
security problem, a company
spokeswoman said Wednesday." - ], - [ - "AP - Business software maker
Oracle Corp. attacked the
credibility and motives of
PeopleSoft Inc.'s board of
directors Monday, hoping to
rally investor support as the
17-month takeover battle
between the bitter business
software rivals nears a
climactic showdown." - ], - [ - "NEW YORK - Elena Dementieva
shook off a subpar serve that
produced 15 double-faults, an
aching left thigh and an upset
stomach to advance to the
semifinals at the U.S. Open
with a 4-6, 6-4, 7-6 (1)
victory Tuesday over Amelie
Mauresmo..." - ], - [ - "THE glory days have returned
to White Hart Lane. When Spurs
new first-team coach Martin
Jol promised a return to the
traditions of the 1960s,
nobody could have believed he
was so determined to act so
quickly and so literally." - ], - [ - "A new worm has been discovered
in the wild that #39;s not
just settling for invading
users #39; PCs--it wants to
invade their homes too." - ], - [ - "Domestic air travelers could
be surfing the Web by 2006
with government-approved
technology that allows people
access to high-speed Internet
connections while they fly." - ], - [ - "GENEVA: Cross-border
investment is set to bounce in
2004 after three years of deep
decline, reflecting a stronger
world economy and more
international merger activity,
the United Nations (UN) said
overnight." - ], - [ - "Researchers have for the first
time established the existence
of odd-parity superconductors,
materials that can carry
electric current without any
resistance." - ], - [ - "Chewing gum giant Wm. Wrigley
Jr. Co. on Thursday said it
plans to phase out production
of its Eclipse breath strips
at a plant in Phoenix, Arizona
and shift manufacturing to
Poznan, Poland." - ], - [ - "Prime Minister Dr Manmohan
Singh inaugurated a research
centre in the Capital on
Thursday to mark 400 years of
compilation of Sikh holy book
the Guru Granth Sahib." - ], - [ - "com September 16, 2004, 7:58
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." - ], - [ - "BRUSSELS, Belgium (AP) --
European antitrust regulators
said Monday they have extended
their review of a deal between
Microsoft Corp. (MSFT) and
Time Warner Inc..." - ], - [ - "AP - When Paula Radcliffe
dropped out of the Olympic
marathon miles from the
finish, she sobbed
uncontrollably. Margaret Okayo
knew the feeling. Okayo pulled
out of the marathon at the
15th mile with a left leg
injury, and she cried, too.
When she watched Radcliffe
quit, Okayo thought, \"Let's
cry together.\"" - ], - [ - "Tightness in the labour market
notwithstanding, the prospects
for hiring in the third
quarter are down from the
second quarter, according to
the new Manpower Employment
Outlook Survey." - ], - [ - "THE re-election of British
Prime Minister Tony Blair
would be seen as an
endorsement of the military
action in Iraq, Prime Minister
John Howard said today." - ], - [ - "Fans who can't get enough of
\"The Apprentice\" can visit a
new companion Web site each
week and watch an extra 40
minutes of video not broadcast
on the Thursday
show.<br><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-Leslie
Walker</b></font>" - ], - [ - " ATLANTA (Sports Network) -
The Atlanta Hawks signed free
agent Kevin Willis on
Wednesday, nearly a decade
after the veteran big man
ended an 11- year stint with
the team." - ], - [ - "An adult Web site publisher is
suing Google, saying the
search engine company made it
easier for users to see the
site #39;s copyrighted nude
photographs without paying or
gaining access through the
proper channels." - ], - [ - "When his right-front tire went
flying off early in the Ford
400, the final race of the
NASCAR Nextel Cup Series
season, Kurt Busch, it seemed,
was destined to spend his
offseason" - ], - [ - "A Washington-based public
opinion firm has released the
results of an election day
survey of Nevada voters
showing 81 support for the
issuance of paper receipts
when votes are cast
electronically." - ], - [ - "NAPSTER creator SHAWN FANNING
has revealed his plans for a
new licensed file-sharing
service with an almost
unlimited selection of tracks." - ], - [ - " NEW YORK (Reuters) - The
dollar rose on Friday, after a
U.S. report showed consumer
prices in line with
expections, reminding
investors that the Federal
Reserve was likely to
continue raising interest
rates, analysts said." - ], - [ - "Brandon Backe and Woody
Williams pitched well last
night even though neither
earned a win. But their
outings will show up in the
record books." - ], - [ - "President George W. Bush
pledged Friday to spend some
of the political capital from
his re-election trying to
secure a lasting Middle East
peace, and he envisioned the
establishment" - ], - [ - "The Football Association today
decided not to charge David
Beckham with bringing the game
into disrepute. The FA made
the surprise announcement
after their compliance unit
ruled" - ], - [ - "Last year some election
watchers made a bold
prediction that this
presidential election would
set a record: the first half
billion dollar campaign in
hard money alone." - ], - [ - "It #39;s one more blow to
patients who suffer from
arthritis. Pfizer, the maker
of Celebrex, says it #39;s
painkiller poses an increased
risk of heart attacks to
patients using the drugs." - ], - [ - "NEW DELHI - A bomb exploded
during an Independence Day
parade in India's remote
northeast on Sunday, killing
at least 15 people, officials
said, just an hour after Prime
Minister Manmohan Singh
pledged to fight terrorism.
The outlawed United Liberation
Front of Asom was suspected of
being behind the attack in
Assam state and a second one
later in the area, said Assam
Inspector General of Police
Khagen Sharma..." - ], - [ - "Two separate studies by U.S.
researchers find that super
drug-resistant strains of
tuberculosis are at the
tipping point of a global
epidemic, and only small
changes could help them spread
quickly." - ], - [ - " CRANS-SUR-SIERRE, Switzerland
(Reuters) - World number
three Ernie Els says he feels
a failure after narrowly
missing out on three of the
year's four major
championships." - ], - [ - "A UN envoy to Sudan will visit
Darfur tomorrow to check on
the government #39;s claim
that some 70,000 people
displaced by conflict there
have voluntarily returned to
their homes, a spokesman said." - ], - [ - "Dell cut prices on some
servers and PCs by as much as
22 percent because it #39;s
paying less for parts. The
company will pass the savings
on components such as memory
and liquid crystal displays" - ], - [ - "AP - Most of the presidential
election provisional ballots
rejected so far in Ohio came
from people who were not even
registered to vote, election
officials said after spending
nearly two weeks poring over
thousands of disputed votes." - ], - [ - "Striker Bonaventure Kalou
netted twice to send AJ
Auxerre through to the first
knockout round of the UEFA Cup
at the expense of Rangers on
Wednesday." - ], - [ - "AP - Rival inmates fought each
other with knives and sticks
Wednesday at a San Salvador
prison, leaving at least 31
people dead and two dozen
injured, officials said." - ], - [ - "WASHINGTON: The European-
American Cassini-Huygens space
probe has detected traces of
ice flowing on the surface of
Saturn #39;s largest moon,
Titan, suggesting the
existence of an ice volcano,
NASA said Tuesday." - ], - [ - "The economic growth rate in
the July-September period was
revised slightly downward from
an already weak preliminary
report, the government said
Wednesday." - ], - [ - "All ISS systems continue to
function nominally, except
those noted previously or
below. Day 7 of joint
Exp.9/Exp.10 operations and
last full day before 8S
undocking." - ], - [ - "BAGHDAD - Two Egyptian
employees of a mobile phone
company were seized when
gunmen stormed into their
Baghdad office, the latest in
a series of kidnappings in the
country." - ], - [ - "BRISBANE, Australia - The body
of a whale resembling a giant
dolphin that washed up on an
eastern Australian beach has
intrigued local scientists,
who agreed Wednesday that it
is rare but are not sure just
how rare." - ], - [ - " quot;Magic can happen. quot;
Sirius Satellite Radio
(nasdaq: SIRI - news - people
) may have signed Howard Stern
and the men #39;s NCAA
basketball tournaments, but XM
Satellite Radio (nasdaq: XMSR
- news - people ) has its
sights on your cell phone." - ], - [ - "Trick-or-treaters can expect
an early Halloween treat on
Wednesday night, when a total
lunar eclipse makes the moon
look like a glowing pumpkin." - ], - [ - "THIS weekend sees the
quot;other quot; showdown
between New York and New
England as the Jets and
Patriots clash in a battle of
the unbeaten teams." - ], - [ - "Sifting through millions of
documents to locate a valuable
few is tedious enough, but
what happens when those files
are scattered across different
repositories?" - ], - [ - "President Bush aims to
highlight American drug-
fighting aid in Colombia and
boost a conservative Latin
American leader with a stop in
the Andean nation where
thousands of security forces
are deployed to safeguard his
brief stay." - ], - [ - "Dubai - Former Palestinian
security minister Mohammed
Dahlan said on Monday that a
quot;gang of mercenaries quot;
known to the Palestinian
police were behind the
shooting that resulted in two
deaths in a mourning tent for
Yasser Arafat in Gaza." - ], - [ - "A drug company executive who
spoke out in support of
Montgomery County's proposal
to import drugs from Canada
and similar legislation before
Congress said that his company
has launched an investigation
into his political activities." - ], - [ - "Nicolas Anelka is fit for
Manchester City #39;s
Premiership encounter against
Tottenham at Eastlands, but
the 13million striker will
have to be content with a
place on the bench." - ], - [ - " PITTSBURGH (Reuters) - Ben
Roethlisberger passed for 183
yards and two touchdowns,
Hines Ward scored twice and
the Pittsburgh Steelers
rolled to a convincing 27-3
victory over Philadelphia on
Sunday for their second
straight win against an
undefeated opponent." - ], - [ - " ATHENS (Reuters) - The U.S.
men's basketball team got
their first comfortable win
at the Olympic basketball
tournament Monday, routing
winless Angola 89-53 in their
final preliminary round game." - ], - [ - "A Frenchman working for Thales
SA, Europe #39;s biggest maker
of military electronics, was
shot dead while driving home
at night in the Saudi Arabian
city of Jeddah." - ], - [ - "Often, the older a pitcher
becomes, the less effective he
is on the mound. Roger Clemens
apparently didn #39;t get that
memo. On Tuesday, the 42-year-
old Clemens won an
unprecedented" - ], - [ - "NASA #39;s Mars rovers have
uncovered more tantalizing
evidence of a watery past on
the Red Planet, scientists
said Wednesday. And the
rovers, Spirit and
Opportunity, are continuing to
do their jobs months after
they were expected to ..." - ], - [ - "SYDNEY (AFP) - Australia #39;s
commodity exports are forecast
to increase by 15 percent to a
record 95 billion dollars (71
million US), the government
#39;s key economic forecaster
said." - ], - [ - "Google won a major legal
victory when a federal judge
ruled that the search engines
advertising policy does not
violate federal trademark
laws." - ], - [ - "Intel Chief Technology Officer
Pat Gelsinger said on
Thursday, Sept. 9, that the
Internet needed to be upgraded
in order to deal with problems
that will become real issues
soon." - ], - [ - "China will take tough measures
this winter to improve the
country #39;s coal mine safety
and prevent accidents. State
Councilor Hua Jianmin said
Thursday the industry should
take" - ], - [ - "AT amp;T Corp. on Thursday
said it is reducing one fifth
of its workforce this year and
will record a non-cash charge
of approximately \\$11." - ], - [ - "BAGHDAD (Iraq): As the
intensity of skirmishes
swelled on the soils of Iraq,
dozens of people were put to
death with toxic shots by the
US helicopter gunship, which
targeted the civilians,
milling around a burning
American vehicle in a Baghdad
street on" - ], - [ - " LONDON (Reuters) - Television
junkies of the world, get
ready for \"Friends,\" \"Big
Brother\" and \"The Simpsons\" to
phone home." - ], - [ - "A rift appeared within Canada
#39;s music industry yesterday
as prominent artists called on
the CRTC to embrace satellite
radio and the industry warned
of lost revenue and job
losses." - ], - [ - "Reuters - A key Iranian
nuclear facility which
the\\U.N.'s nuclear watchdog
has urged Tehran to shut down
is\\nearing completion, a
senior Iranian nuclear
official said on\\Sunday." - ], - [ - "Spain's Football Federation
launches an investigation into
racist comments made by
national coach Luis Aragones." - ], - [ - "Bricks and plaster blew inward
from the wall, as the windows
all shattered and I fell to
the floorwhether from the
shock wave, or just fright, it
wasn #39;t clear." - ], - [ - "Surfersvillage Global Surf
News, 13 September 2004: - -
Hurricane Ivan, one of the
most powerful storms to ever
hit the Caribbean, killed at
least 16 people in Jamaica,
where it wrecked houses and
washed away roads on Saturday,
but appears to have spared" - ], - [ - "I #39;M FEELING a little bit
better about the hundreds of
junk e-mails I get every day
now that I #39;ve read that
someone else has much bigger
e-mail troubles." - ], - [ - "NEW DELHI: India and Pakistan
agreed on Monday to step up
cooperation in the energy
sector, which could lead to
Pakistan importing large
amounts of diesel fuel from
its neighbour, according to
Pakistani Foreign Minister
Khurshid Mehmood Kasuri." - ], - [ - "LONDON, England -- A US
scientist is reported to have
observed a surprising jump in
the amount of carbon dioxide,
the main greenhouse gas." - ], - [ - "Microsoft's antispam Sender ID
technology continues to get
the cold shoulder. Now AOL
adds its voice to a growing
chorus of businesses and
organizations shunning the
proprietary e-mail
authentication system." - ], - [ - "PSV Eindhoven faces Arsenal at
Highbury tomorrow night on the
back of a free-scoring start
to the season. Despite losing
Mateja Kezman to Chelsea in
the summer, the Dutch side has
scored 12 goals in the first" - ], - [ - "Through the World Community
Grid, your computer could help
address the world's health and
social problems." - ], - [ - "Zimbabwe #39;s ruling Zanu-PF
old guard has emerged on top
after a bitter power struggle
in the deeply divided party
during its five-yearly
congress, which ended
yesterday." - ], - [ - "PLAYER OF THE GAME: Playing
with a broken nose, Seattle
point guard Sue Bird set a
WNBA playoff record for
assists with 14, also pumping
in 10 points as the Storm
claimed the Western Conference
title last night." - ], - [ - "Reuters - Thousands of
demonstrators pressing
to\\install Ukraine's
opposition leader as president
after a\\disputed election
launched fresh street rallies
in the capital\\for the third
day Wednesday." - ], - [ - "Michael Jackson wishes he had
fought previous child
molestation claims instead of
trying to \"buy peace\", his
lawyer says." - ], - [ - "North Korea says it will not
abandon its weapons programme
after the South admitted
nuclear activities." - ], - [ - "While there is growing
attention to ongoing genocide
in Darfur, this has not
translated into either a
meaningful international
response or an accurate
rendering of the scale and
evident course of the
catastrophe." - ], - [ - "A planned component for
Microsoft #39;s next version
of Windows is causing
consternation among antivirus
experts, who say that the new
module, a scripting platform
called Microsoft Shell, could
give birth to a whole new
generation of viruses and
remotely" - ], - [ - "THE prosecution on terrorism
charges of extremist Islamic
cleric and accused Jemaah
Islamiah leader Abu Bakar
Bashir will rely heavily on
the potentially tainted
testimony of at least two
convicted Bali bombers, his
lawyers have said." - ], - [ - "SAN JOSE, California Yahoo
will likely have a tough time
getting American courts to
intervene in a dispute over
the sale of Nazi memorabilia
in France after a US appeals
court ruling." - ], - [ - "TORONTO (CP) - Glamis Gold of
Reno, Nev., is planning a
takeover bid for Goldcorp Inc.
of Toronto - but only if
Goldcorp drops its
\\$2.4-billion-Cdn offer for
another Canadian firm, made in
early December." - ], - [ - "Clashes between US troops and
Sadr militiamen escalated
Thursday, as the US surrounded
Najaf for possible siege." - ], - [ - "eBay Style director Constance
White joins Post fashion
editor Robin Givhan and host
Janet Bennett to discuss how
to find trends and bargains
and pull together a wardrobe
online." - ], - [ - "This week will see the release
of October new and existing
home sales, a measure of
strength in the housing
industry. But the short
holiday week will also leave
investors looking ahead to the
holiday travel season." - ], - [ - "Frankfurt - World Cup winners
Brazil were on Monday drawn to
meet European champions
Greece, Gold Cup winners
Mexico and Asian champions
Japan at the 2005
Confederations Cup." - ], - [ - "Third baseman Vinny Castilla
said he fits fine with the
Colorado youth movement, even
though he #39;ll turn 38 next
season and the Rockies are
coming off the second-worst" - ], - [ - "With a sudden shudder, the
ground collapsed and the pipe
pushed upward, buckling into a
humped shape as Cornell
University scientists produced
the first simulated earthquake" - ], - [ - "AFP - A battle group of
British troops rolled out of
southern Iraq on a US-
requested mission to deadlier
areas near Baghdad, in a major
political gamble for British
Prime Minister Tony Blair." - ], - [ - "(Sports Network) - Two of the
top teams in the American
League tangle in a possible
American League Division
Series preview tonight, as the
West-leading Oakland Athletics
host the wild card-leading
Boston Red Sox for the first
of a three-game set at the" - ], - [ - "over half the children in the
world - suffer extreme
deprivation because of war,
HIV/AIDS or poverty, according
to a report released yesterday
by the United Nations Children
#39;s Fund." - ], - [ - "Microsoft (Quote, Chart) has
fired another salvo in its
ongoing spam battle, this time
against porn peddlers who don
#39;t keep their smut inside
the digital equivalent of a
quot;Brown Paper Wrapper." - ], - [ - " BETHESDA, Md. (Reuters) - The
use of some antidepressant
drugs appears linked to an
increase in suicidal behavior
in some children and teen-
agers, a U.S. advisory panel
concluded on Tuesday." - ], - [ - " SEATTLE (Reuters) - The next
version of the Windows
operating system, Microsoft
Corp.'s <A HREF=\"http://www
.reuters.co.uk/financeQuoteLoo
kup.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
flagship product, will ship
in 2006, the world's largest
software maker said on
Friday." - ], - [ - "Reuters - Philippine rescue
teams\\evacuated thousands of
people from the worst flooding
in the\\central Luzon region
since the 1970s as hungry
victims hunted\\rats and birds
for food." - ], - [ - "Inverness Caledonian Thistle
appointed Craig Brewster as
its new manager-player
Thursday although he #39;s
unable to play for the team
until January." - ], - [ - "The Afghan president expresses
deep concern after a bomb
attack which left at least
seven people dead." - ], - [ - " NEW YORK (Reuters) - U.S.
technology stocks opened lower
on Thursday after a sales
warning from Applied Materials
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AMAT.O target=/sto
cks/quickinfo/fullquote\">AM
AT.O</A>, while weekly
jobless claims data met Wall
Street's expectations,
leaving the Dow and S P 500
market measures little
changed." - ], - [ - "ATHENS, Greece -- Alan Shearer
converted an 87th-minute
penalty to give Newcastle a
1-0 win over Panionios in
their UEFA Cup Group D match." - ], - [ - "Fossil remains of the oldest
and smallest known ancestor of
Tyrannosaurus rex, the world
#39;s favorite ferocious
dinosaur, have been discovered
in China with evidence that
its body was cloaked in downy
quot;protofeathers." - ], - [ - "Derek Jeter turned a season
that started with a terrible
slump into one of the best in
his accomplished 10-year
career. quot;I don #39;t
think there is any question,
quot; the New York Yankees
manager said." - ], - [ - "Gardez (Afghanistan), Sept. 16
(Reuters): Afghan President
Hamid Karzai escaped an
assassination bid today when a
rocket was fired at his US
military helicopter as it was
landing in the southeastern
town of Gardez." - ], - [ - "The Jets came up with four
turnovers by Dolphins
quarterback Jay Fiedler in the
second half, including an
interception returned 66 yards
for a touchdown." - ], - [ - "China's Guo Jingjing easily
won the women's 3-meter
springboard last night, and Wu
Minxia made it a 1-2 finish
for the world's diving
superpower, taking the silver." - ], - [ - "GREEN BAY, Wisconsin (Ticker)
-- Brett Favre will be hoping
his 200th consecutive start
turns out better than his last
two have against the St." - ], - [ - "People fishing for sport are
doing far more damage to US
marine fish stocks than anyone
thought, accounting for nearly
a quarter of the" - ], - [ - "When an NFL team opens with a
prolonged winning streak,
former Miami Dolphins coach
Don Shula and his players from
the 17-0 team of 1972 root
unabashedly for the next
opponent." - ], - [ - "MIANNE Bagger, the transsexual
golfer who prompted a change
in the rules to allow her to
compete on the professional
circuit, made history
yesterday by qualifying to
play full-time on the Ladies
European Tour." - ], - [ - "Great Britain #39;s gold medal
tally now stands at five after
Leslie Law was handed the
individual three day eventing
title - in a courtroom." - ], - [ - "This particular index is
produced by the University of
Michigan Business School, in
partnership with the American
Society for Quality and CFI
Group, and is supported in
part by ForeSee Results" - ], - [ - "CHICAGO : Interstate Bakeries
Corp., the maker of popular,
old-style snacks Twinkies and
Hostess Cakes, filed for
bankruptcy, citing rising
costs and falling sales." - ], - [ - "Delta Air Lines (DAL:NYSE -
commentary - research) will
cut employees and benefits but
give a bigger-than-expected
role to Song, its low-cost
unit, in a widely anticipated
but still unannounced
overhaul, TheStreet.com has
learned." - ], - [ - "By byron kho. A consortium of
movie and record companies
joined forces on Friday to
request that the US Supreme
Court take another look at
peer-to-peer file-sharing
programs." - ], - [ - "DUBLIN -- Prime Minister
Bertie Ahern urged Irish
Republican Army commanders
yesterday to meet what he
acknowledged was ''a heavy
burden quot;: disarming and
disbanding their organization
in support of Northern
Ireland's 1998 peace accord." - ], - [ - "Mayor Tom Menino must be
proud. His Boston Red Sox just
won their first World Series
in 86 years and his Hyde Park
Blue Stars yesterday clinched
their first Super Bowl berth
in 32 years, defeating
O'Bryant, 14-0. Who would have
thought?" - ], - [ - "While reproductive planning
and women #39;s equality have
improved substantially over
the past decade, says a United
Nations report, world
population will increase from
6.4 billion today to 8.9
billion by 2050, with the 50
poorest countries tripling in" - ], - [ - "Instead of the skinny black
line, showing a hurricane
#39;s forecast track,
forecasters have drafted a
couple of alternative graphics
to depict where the storms
might go -- and they want your
opinion." - ], - [ - "South Korea have appealed to
sport #39;s supreme legal body
in an attempt to award Yang
Tae-young the Olympic
gymnastics all-round gold
medal after a scoring error
robbed him of the title in
Athens." - ], - [ - "BERLIN - Volkswagen AG #39;s
announcement this week that it
has forged a new partnership
deal with Malaysian carmaker
Proton comes as a strong euro
and Europe #39;s weak economic
performance triggers a fresh
wave of German investment in
Asia." - ], - [ - "AP - Johan Santana had an
early lead and was well on his
way to his 10th straight win
when the rain started to fall." - ], - [ - "ATHENS-In one of the biggest
shocks in Olympic judo
history, defending champion
Kosei Inoue was defeated by
Dutchman Elco van der Geest in
the men #39;s 100-kilogram
category Thursday." - ], - [ - "Consumers in Dublin pay more
for basic goods and services
that people elsewhere in the
country, according to figures
released today by the Central
Statistics Office." - ], - [ - "LIBERTY Media #39;s move last
week to grab up to 17.1 per
cent of News Corporation
voting stock has prompted the
launch of a defensive
shareholder rights plan." - ], - [ - "NBC is adding a 5-second delay
to its Nascar telecasts after
Dale Earnhardt Jr. used a
vulgarity during a postrace
interview last weekend." - ], - [ - "LONDON - Wild capuchin monkeys
can understand cause and
effect well enough to use
rocks to dig for food,
scientists have found.
Capuchin monkeys often use
tools and solve problems in
captivity and sometimes" - ], - [ - "San Francisco Giants
outfielder Barry Bonds, who
became the third player in
Major League Baseball history
to hit 700 career home runs,
won the National League Most
Valuable Player Award" - ], - [ - "The blue-chip Hang Seng Index
rose 171.88 points, or 1.22
percent, to 14,066.91. On
Friday, the index had slipped
31.58 points, or 0.2 percent." - ], - [ - "BAR's Anthony Davidson and
Jenson Button set the pace at
the first Chinese Grand Prix." - ], - [ - "Shares plunge after company
says its vein graft treatment
failed to show benefit in
late-stage test. CHICAGO
(Reuters) - Biotechnology
company Corgentech Inc." - ], - [ - "WASHINGTON - Contradicting the
main argument for a war that
has cost more than 1,000
American lives, the top U.S.
arms inspector reported
Wednesday that he found no
evidence that Iraq produced
any weapons of mass
destruction after 1991..." - ], - [ - "The key to hidden treasure
lies in your handheld GPS
unit. GPS-based \"geocaching\"
is a high-tech sport being
played by thousands of people
across the globe." - ], - [ - "AFP - Style mavens will be
scanning the catwalks in Paris
this week for next spring's
must-have handbag, as a
sweeping exhibition at the
French capital's fashion and
textile museum reveals the bag
in all its forms." - ], - [ - "Canadian Press - SAINT-
QUENTIN, N.B. (CP) - A major
highway in northern New
Brunswick remained closed to
almost all traffic Monday, as
local residents protested
planned health care cuts." - ], - [ - "The U.S. information tech
sector lost 403,300 jobs
between March 2001 and April
2004, and the market for tech
workers remains bleak,
according to a new report." - ], - [ - " NAJAF, Iraq (Reuters) - A
radical Iraqi cleric leading a
Shi'ite uprising agreed on
Wednesday to disarm his
militia and leave one of the
country's holiest Islamic
shrines after warnings of an
onslaught by government
forces." - ], - [ - "Saudi security forces have
killed a wanted militant near
the scene of a deadly shootout
Thursday. Officials say the
militant was killed in a
gunbattle Friday in the
northern town of Buraida,
hours after one" - ], - [ - "Portsmouth chairman Milan
Mandaric said on Tuesday that
Harry Redknapp, who resigned
as manager last week, was
innocent of any wrong-doing
over agent or transfer fees." - ], - [ - "This record is for all the
little guys, for all the
players who have to leg out
every hit instead of taking a
relaxing trot around the
bases, for all the batters
whose muscles aren #39;t" - ], - [ - "Two South Africans acquitted
by a Zimbabwean court of
charges related to the alleged
coup plot in Equatorial Guinea
are to be questioned today by
the South African authorities." - ], - [ - "Charlie Hodgson #39;s record-
equalling performance against
South Africa was praised by
coach Andy Robinson after the
Sale flyhalf scored 27 points
in England #39;s 32-16 victory
here at Twickenham on
Saturday." - ], - [ - "com September 30, 2004, 11:11
AM PT. SanDisk announced
Thursday increased capacities
for several different flash
memory cards. The Sunnyvale,
Calif." - ], - [ - "MOSCOW (CP) - Russia mourned
89 victims of a double air
disaster today as debate
intensified over whether the
two passenger liners could
have plunged almost
simultaneously from the sky by
accident." - ], - [ - "US blue-chip stocks rose
slightly on Friday as
government data showed better-
than-expected demand in August
for durable goods other than
transportation equipment, but
climbing oil prices limited
gains." - ], - [ - "BASEBALL Atlanta (NL):
Optioned P Roman Colon to
Greenville (Southern);
recalled OF Dewayne Wise from
Richmond (IL). Boston (AL):
Purchased C Sandy Martinez
from Cleveland (AL) and
assigned him to Pawtucket
(IL). Cleveland (AL): Recalled
OF Ryan Ludwick from Buffalo
(IL). Chicago (NL): Acquired
OF Ben Grieve from Milwaukee
(NL) for player to be named
and cash; acquired C Mike ..." - ], - [ - "Australia #39;s prime minister
says a body found in Fallujah
is likely that of kidnapped
aid worker Margaret Hassan.
John Howard told Parliament a
videotape of an Iraqi
terrorist group executing a
Western woman appears to have
been genuine." - ], - [ - "roundup Plus: Tech firms rally
against copyright bill...Apple
.Mac customers suffer e-mail
glitches...Alvarion expands
wireless broadband in China." - ], - [ - "BRONX, New York (Ticker) --
Kelvim Escobar was the latest
Anaheim Angels #39; pitcher to
subdue the New York Yankees.
Escobar pitched seven strong
innings and Bengie Molina tied
a career-high with four hits,
including" - ], - [ - "Business software maker
PeopleSoft Inc. said Monday
that it expects third-quarter
revenue to range between \\$680
million and \\$695 million,
above average Wall Street
estimates of \\$651." - ], - [ - "Sep 08 - Vijay Singh revelled
in his status as the new world
number one after winning the
Deutsche Bank Championship by
three shots in Boston on
Monday." - ], - [ - "Reuters - Enron Corp. ,
desperate to\\meet profit
targets, \"parked\" unwanted
power generating barges\\at
Merrill Lynch in a sham sale
designed to be reversed,
a\\prosecutor said on Tuesday
in the first criminal trial
of\\former executives at the
fallen energy company." - ], - [ - " NEW YORK (Reuters) - The
dollar rebounded on Monday
after a heavy selloff last
week, but analysts were
uncertain if the rally could
hold as the drumbeat of
expectation began for to the
December U.S. jobs report due
Friday." - ], - [ - "AP - Their first debate less
than a week away, President
Bush and Democrat John Kerry
kept their public schedules
clear on Saturday and began to
focus on their prime-time
showdown." - ], - [ - "Many people in golf are asking
that today. He certainly wasn
#39;t A-list and he wasn #39;t
Larry Nelson either. But you
couldn #39;t find a more solid
guy to lead the United States
into Ireland for the 2006
Ryder Cup Matches." - ], - [ - "Coles Myer Ltd. Australia
#39;s biggest retailer,
increased second-half profit
by 26 percent after opening
fuel and convenience stores,
selling more-profitable
groceries and cutting costs." - ], - [ - "MOSCOW: Us oil major
ConocoPhillips is seeking to
buy up to 25 in Russian oil
giant Lukoil to add billions
of barrels of reserves to its
books, an industry source
familiar with the matter said
on Friday." - ], - [ - "Australian Stuart Appleby, who
was the joint second-round
leader, returned a two-over 74
to drop to third at three-
under while American Chris
DiMarco moved into fourth with
a round of 69." - ], - [ - "PARIS Getting to the bottom of
what killed Yassar Arafat
could shape up to be an ugly
family tug-of-war. Arafat
#39;s half-brother and nephew
want copies of Arafat #39;s
medical records from the
suburban Paris hospital" - ], - [ - "Red Hat is acquiring security
and authentication tools from
Netscape Security Solutions to
bolster its software arsenal.
Red Hat #39;s CEO and chairman
Matthew Szulik spoke about the
future strategy of the Linux
supplier." - ], - [ - "With a doubleheader sweep of
the Minnesota Twins, the New
York Yankees moved to the
verge of clinching their
seventh straight AL East
title." - ], - [ - "Global Web portal Yahoo! Inc.
Wednesday night made available
a beta version of a new search
service for videos. Called
Yahoo! Video Search, the
search engine crawls the Web
for different types of media
files" - ], - [ - "Interactive posters at 25
underground stations are
helping Londoners travel
safely over Christmas." - ], - [ - "Athens, Greece (Sports
Network) - The first official
track event took place this
morning and Italy #39;s Ivano
Brugnetti won the men #39;s
20km walk at the Summer
Olympics in Athens." - ], - [ - " THE HAGUE (Reuters) - Former
Yugoslav President Slobodan
Milosevic condemned his war
crimes trial as a \"pure farce\"
on Wednesday in a defiant
finish to his opening defense
statement against charges of
ethnic cleansing in the
Balkans." - ], - [ - "US Airways Group (otc: UAIRQ -
news - people ) on Thursday
said it #39;ll seek a court
injunction to prohibit a
strike by disaffected unions." - ], - [ - "Shares in Unilever fall after
the Anglo-Dutch consumer goods
giant issued a surprise
profits warning." - ], - [ - "SAN FRANCISCO (CBS.MW) - The
Canadian government will sell
its 19 percent stake in Petro-
Canada for \\$2.49 billion,
according to the final
prospectus filed with the US
Securities and Exchange
Commission Thursday." - ], - [ - "Champions Arsenal opened a
five-point lead at the top of
the Premier League after a 4-0
thrashing of Charlton Athletic
at Highbury Saturday." - ], - [ - "The Redskins and Browns have
traded field goals and are
tied, 3-3, in the first
quarter in Cleveland." - ], - [ - " HYDERABAD, India (Reuters) -
Microsoft Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=MSFT.O t
arget=/stocks/quickinfo/fullqu
ote\">MSFT.O</A> will
hire several hundred new staff
at its new Indian campus in
the next year, its chief
executive said on Monday, in a
move aimed at strengthening
its presence in Asia's fourth-
biggest economy." - ], - [ - "According to Swiss
authorities, history was made
Sunday when 2723 people in
four communities in canton
Geneva, Switzerland, voted
online in a national federal
referendum." - ], - [ - " GUWAHATI, India (Reuters) -
People braved a steady drizzle
to come out to vote in a
remote northeast Indian state
on Thursday, as troops
guarded polling stations in an
election being held under the
shadow of violence." - ], - [ - "AFP - Three of the nine
Canadian sailors injured when
their newly-delivered,
British-built submarine caught
fire in the North Atlantic
were airlifted Wednesday to
hospital in northwest Ireland,
officials said." - ], - [ - "Alitalia SpA, Italy #39;s
largest airline, reached an
agreement with its flight
attendants #39; unions to cut
900 jobs, qualifying the
company for a government
bailout that will keep it in
business for another six
months." - ], - [ - "BAGHDAD, Iraq - A series of
strong explosions shook
central Baghdad near dawn
Sunday, and columns of thick
black smoke rose from the
Green Zone where U.S. and
Iraqi government offices are
located..." - ], - [ - "Forget September call-ups. The
Red Sox may tap their minor
league system for an extra
player or two when the rules
allow them to expand their
25-man roster Wednesday, but
any help from the farm is
likely to pale against the
abundance of talent they gain
from the return of numerous
players, including Trot Nixon
, from the disabled list." - ], - [ - "P amp;Os cutbacks announced
today are the result of the
waves of troubles that have
swamped the ferry industry of
late. Some would say the
company has done well to
weather the storms for as long
as it has." - ], - [ - "Sven-Goran Eriksson may gamble
by playing goalkeeper Paul
Robinson and striker Jermain
Defoe in Poland." - ], - [ - "Foreign Secretary Jack Straw
has flown to Khartoum on a
mission to pile the pressure
on the Sudanese government to
tackle the humanitarian
catastrophe in Darfur." - ], - [ - " Nextel was the big story in
telecommunications yesterday,
thanks to the Reston company's
mega-merger with Sprint, but
the future of wireless may be
percolating in dozens of
Washington area start-ups." - ], - [ - "Reuters - A senior U.S.
official said on
Wednesday\\deals should not be
done with hostage-takers ahead
of the\\latest deadline set by
Afghan Islamic militants who
have\\threatened to kill three
kidnapped U.N. workers." - ], - [ - "I have been anticipating this
day like a child waits for
Christmas. Today, PalmOne
introduces the Treo 650, the
answer to my quot;what smart
phone will I buy?" - ], - [ - "THOUSAND OAKS -- Anonymity is
only a problem if you want it
to be, and it is obvious Vijay
Singh doesn #39;t want it to
be. Let others chase fame." - ], - [ - "Wikipedia has surprised Web
watchers by growing fast and
maturing into one of the most
popular reference sites." - ], - [ - "It only takes 20 minutes on
the Internet for an
unprotected computer running
Microsoft Windows to be taken
over by a hacker. Any personal
or financial information
stored" - ], - [ - "TORONTO (CP) - Russia #39;s
Severstal has made an offer to
buy Stelco Inc., in what #39;s
believed to be one of several
competing offers emerging for
the restructuring but
profitable Hamilton steel
producer." - ], - [ - "Prices for flash memory cards
-- the little modules used by
digital cameras, handheld
organizers, MP3 players and
cell phones to store pictures,
music and other data -- are
headed down -- way down. Past
trends suggest that prices
will drop 35 percent a year,
but industry analysts think
that rate will be more like 40
or 50 percent this year and
next, due to more
manufacturers entering the
market." - ], - [ - "Walt Disney Co. #39;s
directors nominated Michael
Ovitz to serve on its board
for another three years at a
meeting just weeks before
forcing him out of his job as" - ], - [ - "The European Union, Japan,
Brazil and five other
countries won World Trade
Organization approval to
impose tariffs worth more than
\\$150 million a year on
imports from the United" - ], - [ - "Industrial conglomerate
Honeywell International on
Wednesday said it has filed a
lawsuit against 34 electronics
companies including Apple
Computer and Eastman Kodak,
claiming patent infringement
of its liquid crystal display
technology." - ], - [ - "Sinn Fein leader Gerry Adams
has put the pressure for the
success or failure of the
Northern Ireland assembly
talks firmly on the shoulders
of Ian Paisley." - ], - [ - "Australia #39;s Computershare
has agreed to buy EquiServe of
the United States for US\\$292
million (\\$423 million),
making it the largest US share
registrar and driving its
shares up by a third." - ], - [ - "David Coulthard #39;s season-
long search for a Formula One
drive next year is almost
over. Negotiations between Red
Bull Racing and Coulthard, who
tested for the Austrian team
for the first time" - ], - [ - "Interest rates on short-term
Treasury securities were mixed
in yesterday's auction. The
Treasury Department sold \\$18
billion in three-month bills
at a discount rate of 1.640
percent, up from 1.635 percent
last week. An additional \\$16
billion was sold in six-month
bills at a rate of 1.840
percent, down from 1.860
percent." - ], - [ - "Two top executives of scandal-
tarred insurance firm Marsh
Inc. were ousted yesterday,
the company said, the latest
casualties of an industry
probe by New York's attorney
general." - ], - [ - "AP - Southern California
tailback LenDale White
remembers Justin Holland from
high school. The Colorado
State quarterback made quite
an impression." - ], - [ - "TIM HENMAN last night admitted
all of his energy has been
drained away as he bowed out
of the Madrid Masters. The top
seed, who had a blood test on
Wednesday to get to the bottom
of his fatigue, went down" - ], - [ - "USDA #39;s Animal Plant Health
Inspection Service (APHIS)
this morning announced it has
confirmed a detection of
soybean rust from two test
plots at Louisiana State
University near Baton Rouge,
Louisiana." - ], - [ - " JAKARTA (Reuters) - President
Megawati Sukarnoputri urged
Indonesians on Thursday to
accept the results of the
country's first direct
election of a leader, but
stopped short of conceding
defeat." - ], - [ - "ISLAMABAD: Pakistan early
Monday test-fired its
indigenously developed short-
range nuclear-capable Ghaznavi
missile, the Inter Services
Public Relations (ISPR) said
in a statement." - ], - [ - "While the US software giant
Microsoft has achieved almost
sweeping victories in
government procurement
projects in several Chinese
provinces and municipalities,
the process" - ], - [ - "Mexican Cemex, being the third
largest cement maker in the
world, agreed to buy its
British competitor - RMC Group
- for \\$5.8 billion, as well
as their debts in order to
expand their activity on the
building materials market of
the USA and Europe." - ], - [ - "Microsoft Corp. has delayed
automated distribution of a
major security upgrade to its
Windows XP Professional
operating system, citing a
desire to give companies more
time to test it." - ], - [ - "The trial of a man accused of
murdering York backpacker
Caroline Stuttle begins in
Australia." - ], - [ - "Gateway computers will be more
widely available at Office
Depot, in the PC maker #39;s
latest move to broaden
distribution at retail stores
since acquiring rival
eMachines this year." - ], - [ - "ATHENS -- US sailors needed a
big day to bring home gold and
bronze medals from the sailing
finale here yesterday. But
rolling the dice on windshifts
and starting tactics backfired
both in Star and Tornado
classes, and the Americans had
to settle for a single silver
medal." - ], - [ - "Intel Corp. is refreshing its
64-bit Itanium 2 processor
line with six new chips based
on the Madison core. The new
processors represent the last
single-core Itanium chips that
the Santa Clara, Calif." - ], - [ - "The world's largest insurance
group pays \\$126m in fines as
part of a settlement with US
regulators over its dealings
with two firms." - ], - [ - "BRUSSELS: The EU sought
Wednesday to keep pressure on
Turkey over its bid to start
talks on joining the bloc, as
last-minute haggling seemed
set to go down to the wire at
a summit poised to give a
green light to Ankara." - ], - [ - "AP - J. Cofer Black, the State
Department official in charge
of counterterrorism, is
leaving government in the next
few weeks." - ], - [ - "For the first time, broadband
connections are reaching more
than half (51 percent) of the
American online population at
home, according to measurement
taken in July by
Nielsen/NetRatings, a
Milpitas-based Internet
audience measurement and
research ..." - ], - [ - "AP - Cavaliers forward Luke
Jackson was activated
Wednesday after missing five
games because of tendinitis in
his right knee. Cleveland also
placed forward Sasha Pavlovic
on the injured list." - ], - [ - "The tobacco firm John Player
amp; Sons has announced plans
to lay off 90 workers at its
cigarette factory in Dublin.
The company said it was
planning a phased closure of
the factory between now and
February as part of a review
of its global operations." - ], - [ - "AP - Consumers borrowed more
freely in September,
especially when it came to
racking up charges on their
credit cards, the Federal
Reserve reported Friday." - ], - [ - "AFP - The United States
presented a draft UN
resolution that steps up the
pressure on Sudan over the
crisis in Darfur, including
possible international
sanctions against its oil
sector." - ], - [ - "AFP - At least 33 people were
killed and dozens others
wounded when two bombs ripped
through a congregation of
Sunni Muslims in Pakistan's
central city of Multan, police
said." - ], - [ - "Bold, innovative solutions are
key to addressing the rapidly
rising costs of higher
education and the steady
reduction in government-
subsidized help to finance
such education." - ], - [ - "Just as the PhD crowd emerge
with different interpretations
of today's economy, everyday
Americans battling to balance
the checkbook hold diverse
opinions about where things
stand now and in the future." - ], - [ - "The Brisbane Lions #39;
football manager stepped out
of the changerooms just before
six o #39;clock last night and
handed one of the milling
supporters a six-pack of beer." - ], - [ - "Authorities here are always
eager to show off their
accomplishments, so when
Beijing hosted the World
Toilet Organization conference
last week, delegates were
given a grand tour of the
city's toilets." - ], - [ - "Cavaliers owner Gordon Gund is
in quot;serious quot;
negotiations to sell the NBA
franchise, which has enjoyed a
dramatic financial turnaround
since the arrival of star
LeBron James." - ], - [ - "WASHINGTON Trying to break a
deadlock on energy policy, a
diverse group of
environmentalists, academics
and former government
officials were to publish a
report on Wednesday that
presents strategies for making
the United States cleaner,
more competitive" - ], - [ - "After two days of gloom, China
was back on the winning rails
on Thursday with Liu Chunhong
winning a weightlifting title
on her record-shattering binge
and its shuttlers contributing
two golds in the cliff-hanging
finals." - ], - [ - "One question that arises
whenever a player is linked to
steroids is, \"What would he
have done without them?\"
Baseball history whispers an
answer." - ], - [ - "AFP - A series of torchlight
rallies and vigils were held
after darkness fell on this
central Indian city as victims
and activists jointly
commemorated a night of horror
20 years ago when lethal gas
leaked from a pesticide plant
and killed thousands." - ], - [ - "Consider the New World of
Information - stuff that,
unlike the paper days of the
past, doesn't always
physically exist. You've got
notes, scrawlings and
snippets, Web graphics, photos
and sounds. Stuff needs to be
cut, pasted, highlighted,
annotated, crossed out,
dragged away. And, as Ross
Perot used to say (or maybe it
was Dana Carvey impersonating
him), don't forget the graphs
and charts." - ], - [ - "The second round of the
Canadian Open golf tournament
continues Saturday Glenn Abbey
Golf Club in Oakville,
Ontario, after play was
suspended late Friday due to
darkness." - ], - [ - "A consortium led by Royal
Dutch/Shell Group that is
developing gas reserves off
Russia #39;s Sakhalin Island
said Thursday it has struck a
US\\$6 billion (euro4." - ], - [ - "Major Hollywood studios on
Tuesday announced scores of
lawsuits against computer
server operators worldwide,
including eDonkey, BitTorrent
and DirectConnect networks,
for allowing trading of
pirated movies." - ], - [ - "A massive plan to attract the
2012 Summer Olympics to New
York, touting the city's
diversity, financial and media
power, was revealed Wednesday." - ], - [ - "A Zimbabwe court Friday
convicted a British man
accused of leading a coup plot
against the government of oil-
rich Equatorial Guinea on
weapons charges, but acquitted
most of the 69 other men held
with him." - ], - [ - "But will Wi-Fi, high-
definition broadcasts, mobile
messaging and other
enhancements improve the game,
or wreck it?\\<br />
Photos of tech-friendly parks\\" - ], - [ - "An audit by international
observers supported official
elections results that gave
President Hugo Chavez a
victory over a recall vote
against him, the secretary-
general of the Organisation of
American States announced." - ], - [ - "Canadian Press - TORONTO (CP)
- The fatal stabbing of a
young man trying to eject
unwanted party guests from his
family home, the third such
knifing in just weeks, has
police worried about a
potentially fatal holiday
recipe: teens, alcohol and
knives." - ], - [ - "NICK Heidfeld #39;s test with
Williams has been brought
forward after BAR blocked
plans for Anthony Davidson to
drive its Formula One rival
#39;s car." - ], - [ - "MOSCOW - A female suicide
bomber set off a shrapnel-
filled explosive device
outside a busy Moscow subway
station on Tuesday night,
officials said, killing 10
people and injuring more than
50." - ], - [ - "Grace Park closed with an
eagle and two birdies for a
7-under-par 65 and a two-
stroke lead after three rounds
of the Wachovia LPGA Classic
on Saturday." - ], - [ - "ABIDJAN (AFP) - Two Ivory
Coast military aircraft
carried out a second raid on
Bouake, the stronghold of the
former rebel New Forces (FN)
in the divided west African
country, a French military
source told AFP." - ], - [ - "Carlos Beltran drives in five
runs to carry the Astros to a
12-3 rout of the Braves in
Game 5 of their first-round NL
playoff series." - ], - [ - "On-demand viewing isn't just
for TiVo owners anymore.
Television over internet
protocol, or TVIP, offers
custom programming over
standard copper wires." - ], - [ - "Apple is recalling 28,000
faulty batteries for its
15-inch Powerbook G4 laptops." - ], - [ - "Since Lennox Lewis #39;s
retirement, the heavyweight
division has been knocked for
having more quantity than
quality. Eight heavyweights on
Saturday night #39;s card at
Madison Square Garden hope to
change that perception, at
least for one night." - ], - [ - "PalmSource #39;s European
developer conference is going
on now in Germany, and this
company is using this
opportunity to show off Palm
OS Cobalt 6.1, the latest
version of its operating
system." - ], - [ - "The former Chief Executive
Officer of Computer Associates
was indicted by a federal
grand jury in New York
Wednesday for allegedly
participating in a massive
fraud conspiracy and an
elaborate cover up of a scheme
that cost investors" - ], - [ - "Speaking to members of the
Massachusetts Software
Council, Microsoft CEO Steve
Ballmer touted a bright future
for technology but warned his
listeners to think twice
before adopting open-source
products like Linux." - ], - [ - "MIAMI - The Trillian instant
messaging (IM) application
will feature several
enhancements in its upcoming
version 3.0, including new
video and audio chat
capabilities, enhanced IM
session logs and integration
with the Wikipedia online
encyclopedia, according to
information posted Friday on
the product developer's Web
site." - ], - [ - "Honeywell International Inc.,
the world #39;s largest
supplier of building controls,
agreed to buy Novar Plc for
798 million pounds (\\$1.53
billion) to expand its
security, fire and
ventilation-systems business
in Europe." - ], - [ - "San Francisco investment bank
Thomas Weisel Partners on
Thursday agreed to pay \\$12.5
million to settle allegations
that some of the stock
research the bank published
during the Internet boom was
tainted by conflicts of
interest." - ], - [ - "AFP - A state of civil
emergency in the rebellion-hit
Indonesian province of Aceh
has been formally extended by
six month, as the country's
president pledged to end
violence there without foreign
help." - ], - [ - "Forbes.com - Peter Frankling
tapped an unusual source to
fund his new business, which
makes hot-dog-shaped ice cream
treats known as Cool Dogs: Two
investors, one a friend and
the other a professional
venture capitalist, put in
more than #36;100,000 each
from their Individual
Retirement Accounts. Later
Franklin added #36;150,000
from his own IRA." - ], - [ - "Reuters - Online DVD rental
service Netflix Inc.\\and TiVo
Inc., maker of a digital video
recorder, on Thursday\\said
they have agreed to develop a
joint entertainment\\offering,
driving shares of both
companies higher." - ], - [ - "A San Diego insurance
brokerage has been sued by New
York Attorney General Elliot
Spitzer for allegedly
soliciting payoffs in exchange
for steering business to
preferred insurance companies." - ], - [ - "The European Union agreed
Monday to lift penalties that
have cost American exporters
\\$300 million, following the
repeal of a US corporate tax
break deemed illegal under
global trade rules." - ], - [ - "US Secretary of State Colin
Powell on Monday said he had
spoken to both Indian Foreign
Minister K Natwar Singh and
his Pakistani counterpart
Khurshid Mahmud Kasuri late
last week before the two met
in New Delhi this week for
talks." - ], - [ - "NEW YORK - Victor Diaz hit a
tying, three-run homer with
two outs in the ninth inning,
and Craig Brazell's first
major league home run in the
11th gave the New York Mets a
stunning 4-3 victory over the
Chicago Cubs on Saturday.
The Cubs had much on the
line..." - ], - [ - "AFP - At least 54 people have
died and more than a million
have fled their homes as
torrential rains lashed parts
of India and Bangladesh,
officials said." - ], - [ - "LOS ANGELES - California has
adopted the world's first
rules to reduce greenhouse
emissions for autos, taking
what supporters see as a
dramatic step toward cleaning
up the environment but also
ensuring higher costs for
drivers. The rules may lead
to sweeping changes in
vehicles nationwide,
especially if other states opt
to follow California's
example..." - ], - [ - " LONDON (Reuters) - European
stock markets scaled
near-2-1/2 year highs on
Friday as oil prices held
below \\$48 a barrel, and the
euro held off from mounting
another assault on \\$1.30 but
hovered near record highs
against the dollar." - ], - [ - "Tim Duncan had 17 points and
10 rebounds, helping the San
Antonio Spurs to a 99-81
victory over the New York
Kicks. This was the Spurs
fourth straight win this
season." - ], - [ - "Nagpur: India suffered a
double blow even before the
first ball was bowled in the
crucial third cricket Test
against Australia on Tuesday
when captain Sourav Ganguly
and off spinner Harbhajan
Singh were ruled out of the
match." - ], - [ - "AFP - Republican and
Democratic leaders each
declared victory after the
first head-to-head sparring
match between President George
W. Bush and Democratic
presidential hopeful John
Kerry." - ], - [ - "THIS YULE is all about console
supply and there #39;s
precious little units around,
it has emerged. Nintendo has
announced that it is going to
ship another 400,000 units of
its DS console to the United
States to meet the shortfall
there." - ], - [ - "Annual global semiconductor
sales growth will probably
fall by half in 2005 and
memory chip sales could
collapse as a supply glut saps
prices, world-leading memory
chip maker Samsung Electronics
said on Monday." - ], - [ - "NEW YORK - Traditional phone
systems may be going the way
of the Pony Express. Voice-
over-Internet Protocol,
technology that allows users
to make and receive phone
calls using the Internet, is
giving the old circuit-
switched system a run for its
money." - ], - [ - "AP - Former New York Yankees
hitting coach Rick Down was
hired for the same job by the
Mets on Friday, reuniting him
with new manager Willie
Randolph." - ], - [ - "Last night in New York, the UN
secretary-general was given a
standing ovation - a robust
response to a series of
attacks in past weeks." - ], - [ - "FILDERSTADT (Germany) - Amelie
Mauresmo and Lindsay Davenport
took their battle for the No.
1 ranking and Porsche Grand
Prix title into the semi-
finals with straight-sets
victories on Friday." - ], - [ - "Reuters - The company behind
the Atkins Diet\\on Friday
shrugged off a recent decline
in interest in low-carb\\diets
as a seasonal blip, and its
marketing chief said\\consumers
would cut out starchy foods
again after picking up\\pounds
over the holidays." - ], - [ - "There #39;s something to be
said for being the quot;first
mover quot; in an industry
trend. Those years of extra
experience in tinkering with a
new idea can be invaluable in
helping the first" - ], - [ - "JOHANNESBURG -- Meeting in
Nigeria four years ago,
African leaders set a goal
that 60 percent of children
and pregnant women in malaria-
affected areas around the
continent would be sleeping
under bed nets by the end of
2005." - ], - [ - "AP - The first U.S. cases of
the fungus soybean rust, which
hinders plant growth and
drastically cuts crop
production, were found at two
research sites in Louisiana,
officials said Wednesday." - ], - [ - "Carter returned, but it was
running back Curtis Martin and
the offensive line that put
the Jets ahead. Martin rushed
for all but 10 yards of a
45-yard drive that stalled at
the Cardinals 10." - ], - [ - " quot;There #39;s no way
anyone would hire them to
fight viruses, quot; said
Sophos security analyst Gregg
Mastoras. quot;For one, no
security firm could maintain
its reputation by employing
hackers." - ], - [ - "Symantec has revoked its
decision to blacklist a
program that allows Web
surfers in China to browse
government-blocked Web sites.
The move follows reports that
the firm labelled the Freegate
program, which" - ], - [ - " NEW YORK (Reuters) - Shares
of Chiron Corp. <A HREF=\"ht
tp://www.investor.reuters.com/
FullQuote.aspx?ticker=CHIR.O t
arget=/stocks/quickinfo/fullqu
ote\">CHIR.O</A> fell
7 percent before the market
open on Friday, a day after
the biopharmaceutical company
said it is delaying shipment
of its flu vaccine, Fluvirin,
because lots containing 4
million vaccines do not meet
product sterility standards." - ], - [ - "The nation's top
telecommunications regulator
said yesterday he will push --
before the next president is
inaugurated -- to protect
fledgling Internet telephone
services from getting taxed
and heavily regulated by the
50 state governments." - ], - [ - "Microsoft has signed a pact to
work with the United Nations
Educational, Scientific and
Cultural Organization (UNESCO)
to increase computer use,
Internet access and teacher
training in developing
countries." - ], - [ - "DENVER (Ticker) -- Jake
Plummer more than made up for
a lack of a running game.
Plummer passed for 294 yards
and two touchdowns as the
Denver Broncos posted a 23-13
victory over the San Diego
Chargers in a battle of AFC
West Division rivals." - ], - [ - "DALLAS -- Belo Corp. said
yesterday that it would cut
250 jobs, more than half of
them at its flagship
newspaper, The Dallas Morning
News, and that an internal
investigation into circulation
overstatements" - ], - [ - "AP - Duke Bainum outspent Mufi
Hannemann in Honolulu's most
expensive mayoral race, but
apparently failed to garner
enough votes in Saturday's
primary to claim the office
outright." - ], - [ - "roundup Plus: Microsoft tests
Windows Marketplace...Nortel
delays financials
again...Microsoft updates
SharePoint." - ], - [ - "The Federal Reserve still has
some way to go to restore US
interest rates to more normal
levels, Philadelphia Federal
Reserve President Anthony
Santomero said on Monday." - ], - [ - "It took all of about five
minutes of an introductory
press conference Wednesday at
Heritage Hall for USC
basketball to gain something
it never really had before." - ], - [ - "Delta Air Lines (DAL.N: Quote,
Profile, Research) said on
Wednesday its auditors have
expressed doubt about the
airline #39;s financial
viability." - ], - [ - "POLITICIANS and aid agencies
yesterday stressed the
importance of the media in
keeping the spotlight on the
appalling human rights abuses
taking place in the Darfur
region of Sudan." - ], - [ - "AP - The Boston Red Sox looked
at the out-of-town scoreboard
and could hardly believe what
they saw. The New York Yankees
were trailing big at home
against the Cleveland Indians
in what would be the worst
loss in the 101-year history
of the storied franchise." - ], - [ - "The Red Sox will either
complete an amazing comeback
as the first team to rebound
from a 3-0 deficit in
postseason history, or the
Yankees will stop them." - ], - [ - "\\Children who have a poor diet
are more likely to become
aggressive and anti-social, US
researchers believe." - ], - [ - "OPEN SOURCE champion Microsoft
is expanding its programme to
give government organisations
some of its source code. In a
communique from the lair of
the Vole, in Redmond,
spinsters have said that
Microsoft" - ], - [ - "The Red Sox have reached
agreement with free agent
pitcher Matt Clement yesterday
on a three-year deal that will
pay him around \\$25 million,
his agent confirmed yesterday." - ], - [ - "Takeover target Ronin Property
Group said it would respond to
an offer by Multiplex Group
for all the securities in the
company in about three weeks." - ], - [ - "Canadian Press - OTTAWA (CP) -
Contrary to Immigration
Department claims, there is no
shortage of native-borne
exotic dancers in Canada, says
a University of Toronto law
professor who has studied the
strip club business." - ], - [ - "HEN Manny Ramirez and David
Ortiz hit consecutive home
runs Sunday night in Chicago
to put the Red Sox ahead,
there was dancing in the
streets in Boston." - ], - [ - "Google Inc. is trying to
establish an online reading
room for five major libraries
by scanning stacks of hard-to-
find books into its widely
used Internet search engine." - ], - [ - "HOUSTON--(BUSINESS
WIRE)--Sept. 1, 2004-- L
#39;operazione crea una
centrale globale per l
#39;analisi strategica el
#39;approfondimento del
settore energetico IHS Energy,
fonte globale leader di
software, analisi e
informazioni" - ], - [ - "The European Union presidency
yesterday expressed optimism
that a deal could be struck
over Turkey #39;s refusal to
recognize Cyprus in the lead-
up to next weekend #39;s EU
summit, which will decide
whether to give Ankara a date
for the start of accession
talks." - ], - [ - " WASHINGTON (Reuters) -
President Bush on Friday set a
four-year goal of seeing a
Palestinian state established
and he and British Prime
Minister Tony Blair vowed to
mobilize international
support to help make it happen
now that Yasser Arafat is
dead." - ], - [ - "WASHINGTON Can you always tell
when somebody #39;s lying? If
so, you might be a wizard of
the fib. A California
psychology professor says
there #39;s a tiny subculture
of people that can pick out a
lie nearly every time they
hear one." - ], - [ - " KHARTOUM (Reuters) - Sudan on
Saturday questioned U.N.
estimates that up to 70,000
people have died from hunger
and disease in its remote
Darfur region since a
rebellion began 20 months
ago." - ], - [ - "Type design was once the
province of skilled artisans.
With the help of new computer
programs, neophytes have
flooded the Internet with
their creations." - ], - [ - "RCN Inc., co-owner of
Starpower Communications LLC,
the Washington area
television, telephone and
Internet provider, filed a
plan of reorganization
yesterday that it said puts
the company" - ], - [ - "MIAMI -- Bryan Randall grabbed
a set of Mardi Gras beads and
waved them aloft, while his
teammates exalted in the
prospect of a trip to New
Orleans." - ], - [ - "<strong>Letters</stro
ng> Reports of demise
premature" - ], - [ - "TORONTO (CP) - With an injured
Vince Carter on the bench, the
Toronto Raptors dropped their
sixth straight game Friday,
101-87 to the Denver Nuggets." - ], - [ - "The US airline industry,
riddled with excess supply,
will see a significant drop in
capacity, or far fewer seats,
as a result of at least one
airline liquidating in the
next year, according to
AirTran Airways Chief
Executive Joe Leonard." - ], - [ - "Boeing (nyse: BA - news -
people ) Chief Executive Harry
Stonecipher is keeping the
faith. On Monday, the head of
the aerospace and military
contractor insists he #39;s
confident his firm will
ultimately win out" - ], - [ - "While not quite a return to
glory, Monday represents the
Redskins' return to the
national consciousness." - ], - [ - "Australia #39;s biggest
supplier of fresh milk,
National Foods, has posted a
net profit of \\$68.7 million,
an increase of 14 per cent on
last financial year." - ], - [ - "Lawyers for customers suing
Merck amp; Co. want to
question CEO Raymond Gilmartin
about what he knew about the
dangers of Vioxx before the
company withdrew the drug from
the market because of health
hazards." - ], - [ - "Vijay Singh has won the US PGA
Tour player of the year award
for the first time, ending
Tiger Woods #39;s five-year
hold on the honour." - ], - [ - "New York; September 23, 2004 -
The Department of Justice
(DoJ), FBI and US Attorney
#39;s Office handed down a
10-count indictment against
former Computer Associates
(CA) chairman and CEO Sanjay
Kumar and Stephen Richards,
former CA head of worldwide
sales." - ], - [ - "AFP - At least four Georgian
soldiers were killed and five
wounded in overnight clashes
in Georgia's separatist, pro-
Russian region of South
Ossetia, Georgian officers
near the frontline with
Ossetian forces said early
Thursday." - ], - [ - "Intel, the world #39;s largest
chip maker, scrapped a plan
Thursday to enter the digital
television chip business,
marking a major retreat from
its push into consumer
electronics." - ], - [ - "PACIFIC Hydro shares yesterday
caught an updraught that sent
them more than 20 per cent
higher after the wind farmer
moved to flush out a bidder." - ], - [ - "The European Commission is
expected later this week to
recommend EU membership talks
with Turkey. Meanwhile, German
Chancellor Gerhard Schroeder
and Turkish Prime Minister
Tayyip Erdogan are
anticipating a quot;positive
report." - ], - [ - "The US is the originator of
over 42 of the worlds
unsolicited commercial e-mail,
making it the worst offender
in a league table of the top
12 spam producing countries
published yesterday by anti-
virus firm Sophos." - ], - [ - " NEW YORK (Reuters) - U.S.
consumer confidence retreated
in August while Chicago-area
business activity slowed,
according to reports on
Tuesday that added to worries
the economy's patch of slow
growth may last beyond the
summer." - ], - [ - "Intel is drawing the curtain
on some of its future research
projects to continue making
transistors smaller, faster,
and less power-hungry out as
far as 2020." - ], - [ - "England got strikes from
sparkling debut starter
Jermain Defoe and Michael Owen
to defeat Poland in a Uefa
World Cup qualifier in
Chorzow." - ], - [ - "The Canadian government
signalled its intention
yesterday to reintroduce
legislation to decriminalise
the possession of small
amounts of marijuana." - ], - [ - "A screensaver targeting spam-
related websites appears to
have been too successful." - ], - [ - "Titleholder Ernie Els moved
within sight of a record sixth
World Match Play title on
Saturday by solving a putting
problem to overcome injured
Irishman Padraig Harrington 5
and 4." - ], - [ - "If the Washington Nationals
never win a pennant, they have
no reason to ever doubt that
DC loves them. Yesterday, the
District City Council
tentatively approved a tab for
a publicly financed ballpark
that could amount to as much
as \\$630 million." - ], - [ - "Plus: Experts fear Check 21
could lead to massive bank
fraud." - ], - [ - "By SIOBHAN McDONOUGH
WASHINGTON (AP) -- Fewer
American youths are using
marijuana, LSD and Ecstasy,
but more are abusing
prescription drugs, says a
government report released
Thursday. The 2003 National
Survey on Drug Use and Health
also found youths and young
adults are more aware of the
risks of using pot once a
month or more frequently..." - ], - [ - " ABIDJAN (Reuters) - Ivory
Coast warplanes killed nine
French soldiers on Saturday in
a bombing raid during the
fiercest clashes with rebels
for 18 months and France hit
back by destroying most of
the West African country's
small airforce." - ], - [ - "Unilever has reported a three
percent rise in third-quarter
earnings but warned it is
reviewing its targets up to
2010, after issuing a shock
profits warning last month." - ], - [ - "A TNO engineer prepares to
start capturing images for the
world's biggest digital photo." - ], - [ - "Defensive back Brandon
Johnson, who had two
interceptions for Tennessee at
Mississippi, was suspended
indefinitely Monday for
violation of team rules." - ], - [ - "Points leader Kurt Busch spun
out and ran out of fuel, and
his misfortune was one of the
reasons crew chief Jimmy
Fennig elected not to pit with
20 laps to go." - ], - [ - " LONDON (Reuters) - Oil prices
extended recent heavy losses
on Wednesday ahead of weekly
U.S. data expected to show
fuel stocks rising in time
for peak winter demand." - ], - [ - "(CP) - The NHL all-star game
hasn #39;t been cancelled
after all. It #39;s just been
moved to Russia. The agent for
New York Rangers winger
Jaromir Jagr confirmed Monday
that the Czech star had joined
Omsk Avangard" - ], - [ - "PalmOne is aiming to sharpen
up its image with the launch
of the Treo 650 on Monday. As
previously reported, the smart
phone update has a higher-
resolution screen and a faster
processor than the previous
top-of-the-line model, the
Treo 600." - ], - [ - "GAZA CITY, Gaza Strip --
Islamic militant groups behind
many suicide bombings
dismissed yesterday a call
from Mahmoud Abbas, the
interim Palestinian leader, to
halt attacks in the run-up to
a Jan. 9 election to replace
Yasser Arafat." - ], - [ - "Secretary of State Colin
Powell is wrapping up an East
Asia trip focused on prodding
North Korea to resume talks
aimed at ending its nuclear-
weapons program." - ], - [ - "HERE in the land of myth, that
familiar god of sports --
karma -- threw a bolt of
lightning into the Olympic
stadium yesterday. Marion
Jones lunged desperately with
her baton in the 4 x 100m
relay final, but couldn #39;t
reach her target." - ], - [ - "kinrowan writes quot;MIT,
inventor of Kerberos, has
announced a pair of
vulnerabities in the software
that will allow an attacker to
either execute a DOS attack or
execute code on the machine." - ], - [ - "The risk of intestinal damage
from common painkillers may be
higher than thought, research
suggests." - ], - [ - "AN earthquake measuring 7.3 on
the Richter Scale hit western
Japan this morning, just hours
after another strong quake
rocked the area." - ], - [ - "Vodafone has increased the
competition ahead of Christmas
with plans to launch 10
handsets before the festive
season. The Newbury-based
group said it will begin
selling the phones in
November." - ], - [ - "Reuters - Former Pink Floyd
mainman Roger\\Waters released
two new songs, both inspired
by the U.S.-led\\invasion of
Iraq, via online download
outlets Tuesday." - ], - [ - "A former assistant treasurer
at Enron Corp. (ENRNQ.PK:
Quote, Profile, Research)
agreed to plead guilty to
conspiracy to commit
securities fraud on Tuesday
and will cooperate with" - ], - [ - "Britain #39;s Prince Harry,
struggling to shed a growing
quot;wild child quot; image,
won #39;t apologize to a
photographer he scuffled with
outside an exclusive London
nightclub, a royal spokesman
said on Saturday." - ], - [ - "UK house prices fell by 1.1 in
October, confirming a
softening of the housing
market, Halifax has said. The
UK #39;s biggest mortgage
lender said prices rose 18." - ], - [ - "Pakistan #39;s interim Prime
Minister Chaudhry Shaujaat
Hussain has announced his
resignation, paving the way
for his successor Shauket
Aziz." - ], - [ - "A previously unknown group
calling itself Jamaat Ansar
al-Jihad al-Islamiya says it
set fire to a Jewish soup
kitchen in Paris, according to
an Internet statement." - ], - [ - "More than six newspaper
companies have received
letters from the Securities
and Exchange Commission
seeking information about
their circulation practices." - ], - [ - "THE 64,000 dollar -
correction, make that 500
million dollar -uestion
hanging over Shire
Pharmaceuticals is whether the
5 per cent jump in the
companys shares yesterday
reflects relief that US
regulators have finally
approved its drug for" - ], - [ - "The deadliest attack on
Americans in Iraq since May
came as Iraqi officials
announced that Saddam
Hussein's deputy had not been
captured on Sunday." - ], - [ - "AP - Kenny Rogers lost at the
Coliseum for the first time in
more than 10 years, with Bobby
Crosby's three-run double in
the fifth inning leading the
Athletics to a 5-4 win over
the Texas Rangers on Thursday." - ], - [ - "A fundraising concert will be
held in London in memory of
the hundreds of victims of the
Beslan school siege." - ], - [ - "Dambulla, Sri Lanka - Kumar
Sangakkara and Avishka
Gunawardene slammed impressive
half-centuries to help an
under-strength Sri Lanka crush
South Africa by seven wickets
in the fourth one-day
international here on
Saturday." - ], - [ - "Fresh off being the worst team
in baseball, the Arizona
Diamondbacks set a new record
this week: fastest team to
both hire and fire a manager." - ], - [ - "Nebraska head coach Bill
Callahan runs off the field at
halftime of the game against
Baylor in Lincoln, Neb.,
Saturday, Oct. 16, 2004." - ], - [ - "NASA has been working on a
test flight project for the
last few years involving
hypersonic flight. Hypersonic
flight is fight at speeds
greater than Mach 5, or five
times the speed of sound." - ], - [ - "AFP - The landmark trial of a
Rwandan Roman Catholic priest
accused of supervising the
massacre of 2,000 of his Tutsi
parishioners during the
central African country's 1994
genocide opens at a UN court
in Tanzania." - ], - [ - "The Irish government has
stepped up its efforts to free
the British hostage in Iraq,
Ken Bigley, whose mother is
from Ireland, by talking to
diplomats from Iran and
Jordan." - ], - [ - "AP - Republican Sen. Lincoln
Chafee, who flirted with
changing political parties in
the wake of President Bush's
re-election victory, says he
will stay in the GOP." - ], - [ - "AP - Microsoft Corp. announced
Wednesday that it would offer
a low-cost, localized version
of its Windows XP operating
system in India to tap the
large market potential in this
country of 1 billion people,
most of whom do not speak
English." - ], - [ - "Businesses saw inventories
rise in July and sales picked
up, the government reported
Wednesday. The Commerce
Department said that stocks of
unsold goods increased 0.9 in
July, down from a 1.1 rise in
June." - ], - [ - " EAST RUTHERFORD, N.J. (Sports
Network) - The Toronto
Raptors have traded All-Star
swingman Vince Carter to the
New Jersey Nets in exchange
for center Alonzo Mourning,
forward Eric Williams,
center/forward Aaron Williams
and two first- round draft
picks." - ], - [ - "AP - Utah State University has
secured a #36;40 million
contract with NASA to build an
orbiting telescope that will
examine galaxies and try to
find new stars." - ], - [ - "South Korean President Roh
Moo-hyun pays a surprise visit
to troops in Iraq, after his
government decided to extend
their mandate." - ], - [ - "As the European Union
approaches a contentious
decision - whether to let
Turkey join the club - the
Continent #39;s rulers seem to
have left their citizens
behind." - ], - [ - "What riot? quot;An Argentine
friend of mine was a little
derisive of the Pacers-Pistons
eruption, quot; says reader
Mike Gaynes. quot;He snorted,
#39;Is that what Americans
call a riot?" - ], - [ - "All season, Chris Barnicle
seemed prepared for just about
everything, but the Newton
North senior was not ready for
the hot weather he encountered
yesterday in San Diego at the
Footlocker Cross-Country
National Championships. Racing
in humid conditions with
temperatures in the 70s, the
Massachusetts Division 1 state
champion finished sixth in 15
minutes 34 seconds in the
5-kilometer race. ..." - ], - [ - "Shares of Genta Inc. (GNTA.O:
Quote, Profile, Research)
soared nearly 50 percent on
Monday after the biotechnology
company presented promising
data on an experimental
treatment for blood cancers." - ], - [ - "I spend anywhere from three to
eight hours every week
sweating along with a motley
crew of local misfits,
shelving, sorting, and hauling
ton after ton of written
matter in a rowhouse basement
in Baltimore. We have no heat
nor air conditioning, but
still, every week, we come and
work. Volunteer night is
Wednesday, but many of us also
work on the weekends, when
we're open to the public.
There are times when we're
freezing and we have to wear
coats and gloves inside,
making handling books somewhat
tricky; other times, we're all
soaked with sweat, since it's
90 degrees out and the
basement is thick with bodies.
One learns to forget about
personal space when working at
The Book Thing, since you can
scarcely breathe without
bumping into someone, and we
are all so accustomed to
having to scrape by each other
that most of us no longer
bother to say \"excuse me\"
unless some particularly
dramatic brushing occurs." - ], - [ - " BAGHDAD (Reuters) - A
deadline set by militants who
have threatened to kill two
Americans and a Briton seized
in Iraq was due to expire
Monday, and more than two
dozen other hostages were
also facing death unless rebel
demands were met." - ], - [ - "Alarmed by software glitches,
security threats and computer
crashes with ATM-like voting
machines, officials from
Washington, D.C., to
California are considering an
alternative from an unlikely
place: Nevada." - ], - [ - "Some Venezuelan television
channels began altering their
programs Thursday, citing
fears of penalties under a new
law restricting violence and
sexual content over the
airwaves." - ], - [ - "SBC Communications Inc. plans
to cut at least 10,000 jobs,
or 6 percent of its work
force, by the end of next year
to compensate for a drop in
the number of local-telephone
customers." - ], - [ - "afrol News, 4 October -
Hundred years of conservation
efforts have lifted the
southern black rhino
population from about hundred
to 11,000 animals." - ], - [ - "THE death of Philippine movie
star and defeated presidential
candidate Fernando Poe has
drawn some political backlash,
with some people seeking to
use his sudden demise as a
platform to attack President
Gloria Arroyo." - ], - [ - " WASHINGTON (Reuters) - Major
cigarette makers go on trial
on Tuesday in the U.S.
government's \\$280 billion
racketeering case that
charges the tobacco industry
with deliberately deceiving
the public about the risks of
smoking since the 1950s." - ], - [ - "More Americans are quitting
their jobs and taking the risk
of starting a business despite
a still-lackluster job market." - ], - [ - "AP - Coach Tyrone Willingham
was fired by Notre Dame on
Tuesday after three seasons in
which he failed to return one
of the nation's most storied
football programs to
prominence." - ], - [ - "COLLEGE PARK, Md. -- Joel
Statham completed 18 of 25
passes for 268 yards and two
touchdowns in No. 23
Maryland's 45-22 victory over
Temple last night, the
Terrapins' 12th straight win
at Byrd Stadium." - ], - [ - "Manchester United boss Sir
Alex Ferguson wants the FA to
punish Arsenal good guy Dennis
Bergkamp for taking a swing at
Alan Smith last Sunday." - ], - [ - "VIENTIANE, Laos China moved
yet another step closer in
cementing its economic and
diplomatic relationships with
Southeast Asia today when
Prime Minister Wen Jiabao
signed a trade accord at a
regional summit that calls for
zero tariffs on a wide range
of" - ], - [ - "SPACE.com - With nbsp;food
stores nbsp;running low, the
two \\astronauts living aboard
the International Space
Station (ISS) are cutting back
\\their meal intake and
awaiting a critical cargo
nbsp;delivery expected to
arrive \\on Dec. 25." - ], - [ - "British judges in London
Tuesday ordered radical Muslim
imam Abu Hamza to stand trial
for soliciting murder and
inciting racial hatred." - ], - [ - "Federal Reserve policy-makers
raised the benchmark US
interest rate a quarter point
to 2.25 per cent and restated
a plan to carry out" - ], - [ - " DETROIT (Reuters) - A
Canadian law firm on Tuesday
said it had filed a lawsuit
against Ford Motor Co. <A H
REF=\"http://www.investor.reute
rs.com/FullQuote.aspx?ticker=F
.N target=/stocks/quickinfo/fu
llquote\">F.N</A> over
what it claims are defective
door latches on about 400,000
of the automaker's popular
pickup trucks and SUVs." - ], - [ - "Published reports say Barry
Bonds has testified that he
used a clear substance and a
cream given to him by a
trainer who was indicted in a
steroid-distribution ring." - ], - [ - "SARASOTA, Fla. - The
devastation brought on by
Hurricane Charley has been
especially painful for an
elderly population that is
among the largest in the
nation..." - ], - [ - " ATHENS (Reuters) - Christos
Angourakis added his name to
Greece's list of Paralympic
medal winners when he claimed
a bronze in the T53 shot put
competition Thursday." - ], - [ - " quot;He is charged for having
a part in the Bali incident,
quot; state prosecutor Andi
Herman told Reuters on
Saturday. bombing attack at
the US-run JW." - ], - [ - "Jay Fiedler threw for one
touchdown, Sage Rosenfels
threw for another and the
Miami Dolphins got a victory
in a game they did not want to
play, beating the New Orleans
Saints 20-19 Friday night." - ], - [ - " NEW YORK (Reuters) - Terrell
Owens scored three touchdowns
and the Philadelphia Eagles
amassed 35 first-half points
on the way to a 49-21
drubbing of the Dallas Cowboys
in Irving, Texas, Monday." - ], - [ - "AstraZeneca Plc suffered its
third setback in two months on
Friday as lung cancer drug
Iressa failed to help patients
live longer" - ], - [ - "Virgin Electronics hopes its
slim Virgin Player, which
debuts today and is smaller
than a deck of cards, will
rise as a lead competitor to
Apple's iPod." - ], - [ - "Researchers train a monkey to
feed itself by guiding a
mechanical arm with its mind.
It could be a big step forward
for prosthetics. By David
Cohn." - ], - [ - "Bruce Wasserstein, the
combative chief executive of
investment bank Lazard, is
expected to agree this week
that he will quit the group
unless he can pull off a
successful" - ], - [ - "A late strike by Salomon Kalou
sealed a 2-1 win for Feyenoord
over NEC Nijmegen, while
second placed AZ Alkmaar
defeated ADO Den Haag 2-0 in
the Dutch first division on
Sunday." - ], - [ - "The United Nations called on
Monday for an immediate
ceasefire in eastern Congo as
fighting between rival army
factions flared for a third
day." - ], - [ - "What a disgrace Ron Artest has
become. And the worst part is,
the Indiana Pacers guard just
doesn #39;t get it. Four days
after fueling one of the
biggest brawls in the history
of pro sports, Artest was on
national" - ], - [ - "Allowing dozens of casinos to
be built in the UK would bring
investment and thousands of
jobs, Tony Blair says." - ], - [ - "The shock here was not just
from the awful fact itself,
that two vibrant young Italian
women were kidnapped in Iraq,
dragged from their office by
attackers who, it seems, knew
their names." - ], - [ - "Tehran/Vianna, Sept. 19 (NNN):
Iran on Sunday rejected the
International Atomic Energy
Agency (IAEA) call to suspend
of all its nuclear activities,
saying that it will not agree
to halt uranium enrichment." - ], - [ - "A 15-year-old Argentine
student opened fire at his
classmates on Tuesday in a
middle school in the south of
the Buenos Aires province,
leaving at least four dead and
five others wounded, police
said." - ], - [ - "Dr. David J. Graham, the FDA
drug safety reviewer who
sounded warnings over five
drugs he felt could become the
next Vioxx has turned to a
Whistleblower protection group
for legal help." - ], - [ - "AP - Scientists in 17
countries will scout waterways
to locate and study the
world's largest freshwater
fish species, many of which
are declining in numbers,
hoping to learn how to better
protect them, researchers
announced Thursday." - ], - [ - "AP - Google Inc.'s plans to
move ahead with its initial
public stock offering ran into
a roadblock when the
Securities and Exchange
Commission didn't approve the
Internet search giant's
regulatory paperwork as
requested." - ], - [ - "Jenson Button has revealed
dissatisfaction with the way
his management handled a
fouled switch to Williams. Not
only did the move not come
off, his reputation may have
been irreparably damaged amid
news headline" - ], - [ - "The Kmart purchase of Sears,
Roebuck may be the ultimate
expression of that old saying
in real estate: location,
location, location." - ], - [ - "Citing security risks, a state
university is urging students
to drop Internet Explorer in
favor of alternative Web
browsers such as Firefox and
Safari." - ], - [ - "Redknapp and his No2 Jim Smith
resigned from Portsmouth
yesterday, leaving
controversial new director
Velimir Zajec in temporary
control." - ], - [ - "Despite being ranked eleventh
in the world in broadband
penetration, the United States
is rolling out high-speed
services on a quot;reasonable
and timely basis to all
Americans, quot; according to
a new report narrowly approved
today by the Federal
Communications" - ], - [ - "Sprint Corp. (FON.N: Quote,
Profile, Research) on Friday
said it plans to cut up to 700
jobs as it realigns its
business to focus on wireless
and Internet services and
takes a non-cash network
impairment charge." - ], - [ - "As the season winds down for
the Frederick Keys, Manager
Tom Lawless is starting to
enjoy the progress his
pitching staff has made this
season." - ], - [ - "Britain #39;s Bradley Wiggins
won the gold medal in men
#39;s individual pursuit
Saturday, finishing the
4,000-meter final in 4:16." - ], - [ - "And when David Akers #39;
50-yard field goal cleared the
crossbar in overtime, they did
just that. They escaped a
raucous Cleveland Browns
Stadium relieved but not
broken, tested but not
cracked." - ], - [ - "NEW YORK - A cable pay-per-
view company has decided not
to show a three-hour election
eve special with filmmaker
Michael Moore that included a
showing of his documentary
\"Fahrenheit 9/11,\" which is
sharply critical of President
Bush. The company, iN
DEMAND, said Friday that its
decision is due to \"legitimate
business and legal concerns.\"
A spokesman would not
elaborate..." - ], - [ - "Democracy candidates picked up
at least one more seat in
parliament, according to exit
polls." - ], - [ - "The IOC wants suspended
Olympic member Ivan Slavkov to
be thrown out of the
organisation." - ], - [ - " BANGKOK (Reuters) - The
ouster of Myanmar's prime
minister, architect of a
tentative \"roadmap to
democracy,\" has dashed faint
hopes for an end to military
rule and leaves Southeast
Asia's policy of constructive
engagement in tatters." - ], - [ - "San Antonio, TX (Sports
Network) - Dean Wilson shot a
five-under 65 on Friday to
move into the lead at the
halfway point of the Texas
Open." - ], - [ - "Now that Chelsea have added
Newcastle United to the list
of clubs that they have given
what for lately, what price
Jose Mourinho covering the
Russian-funded aristocrats of
west London in glittering
glory to the tune of four
trophies?" - ], - [ - "AP - Former Seattle Seahawks
running back Chris Warren has
been arrested in Virginia on a
federal warrant, accused of
failing to pay #36;137,147 in
child support for his two
daughters in Washington state." - ], - [ - "The anguish of hostage Kenneth
Bigley in Iraq hangs over
Prime Minister Tony Blair
today as he faces the twin
test of a local election and a
debate by his Labour Party
about the divisive war." - ], - [ - "Says that amount would have
been earned for the first 9
months of 2004, before AT
amp;T purchase. LOS ANGELES,
(Reuters) - Cingular Wireless
would have posted a net profit
of \\$650 million for the first
nine months" - ], - [ - "NewsFactor - Taking an
innovative approach to the
marketing of high-performance
\\computing, Sun Microsystems
(Nasdaq: SUNW) is offering its
N1 Grid program in a pay-for-
use pricing model that mimics
the way such commodities as
electricity and wireless phone
plans are sold." - ], - [ - " CHICAGO (Reuters) - Goodyear
Tire Rubber Co. <A HREF=\"
http://www.investor.reuters.co
m/FullQuote.aspx?ticker=GT.N t
arget=/stocks/quickinfo/fullqu
ote\">GT.N</A> said
on Friday it will cut 340 jobs
in its engineered products and
chemical units as part of its
cost-reduction efforts,
resulting in a third-quarter
charge." - ], - [ - " quot;There were 16 people
travelling aboard. ... It
crashed into a mountain, quot;
Col. Antonio Rivero, head of
the Civil Protection service,
told." - ], - [ - "Reuters - Shares of long-
distance phone\\companies AT T
Corp. and MCI Inc. have
plunged\\about 20 percent this
year, but potential buyers
seem to be\\holding out for
clearance sales." - ], - [ - "Reuters - Madonna and m-Qube
have\\made it possible for the
star's North American fans to
download\\polyphonic ring tones
and other licensed mobile
content from\\her official Web
site, across most major
carriers and without\\the need
for a credit card." - ], - [ - "President Bush is reveling in
winning the popular vote and
feels he can no longer be
considered a one-term accident
of history." - ], - [ - "Russian oil giant Yukos files
for bankruptcy protection in
the US in a last ditch effort
to stop the Kremlin auctioning
its main production unit." - ], - [ - "British Airways #39; (BA)
chief executive Rod Eddington
has admitted that the company
quot;got it wrong quot; after
staff shortages led to three
days of travel chaos for
passengers." - ], - [ - "It #39;s official: US Open had
never gone into the third
round with only two American
men, including the defending
champion, Andy Roddick." - ], - [ - "Canadian Press - TORONTO (CP)
- Thousands of Royal Bank
clerks are being asked to
display rainbow stickers at
their desks and cubicles to
promote a safe work
environment for gays,
lesbians, and bisexuals." - ], - [ - "The chipmaker is back on a
buying spree, having scooped
up five other companies this
year." - ], - [ - "The issue of drug advertising
directly aimed at consumers is
becoming political." - ], - [ - "WASHINGTON, Aug. 17
(Xinhuanet) -- England coach
Sven-Goran Eriksson has urged
the international soccer
authorities to preserve the
health of the world superstar
footballers for major
tournaments, who expressed his
will in Slaley of England on
Tuesday ..." - ], - [ - " BAGHDAD (Reuters) - A car
bomb killed two American
soldiers and wounded eight
when it exploded in Baghdad on
Saturday, the U.S. military
said in a statement." - ], - [ - "Juventus coach Fabio Capello
has ordered his players not to
kick the ball out of play when
an opponent falls to the
ground apparently hurt because
he believes some players fake
injury to stop the match." - ], - [ - "AP - China's economic boom is
still roaring despite efforts
to cool sizzling growth, with
the gross domestic product
climbing 9.5 percent in the
first three quarters of this
year, the government reported
Friday." - ], - [ - "Soaring petroleum prices
pushed the cost of goods
imported into the U.S. much
higher than expected in
August, the government said
today." - ], - [ - "Anheuser-Busch teams up with
Vietnam's largest brewer,
laying the groundwork for
future growth in the region." - ], - [ - "You #39;re angry. You want to
lash out. The Red Sox are
doing it to you again. They
#39;re blowing a playoff
series, and to the Yankees no
less." - ], - [ - "TORONTO -- There is no
mystique to it anymore,
because after all, the
Russians have become commoners
in today's National Hockey
League, and Finns, Czechs,
Slovaks, and Swedes also have
been entrenched in the
Original 30 long enough to
turn the ongoing World Cup of
Hockey into a protracted
trailer for the NHL season." - ], - [ - "Sudanese authorities have
moved hundreds of pro-
government fighters from the
crisis-torn Darfur region to
other parts of the country to
keep them out of sight of
foreign military observers
demanding the militia #39;s
disarmament, a rebel leader
charged" - ], - [ - "CHARLOTTE, NC - Shares of
Krispy Kreme Doughnuts Inc.
#39;s fell sharply Monday as a
79 percent plunge in third-
quarter earnings and an
intensifying accounting
investigation overshadowed the
pastrymaker #39;s statement
that the low-carb craze might
be easing." - ], - [ - "The company hopes to lure
software partners by promising
to save them from
infrastructure headaches." - ], - [ - "BAGHDAD, Iraq - A car bomb
Tuesday ripped through a busy
market near a Baghdad police
headquarters where Iraqis were
waiting to apply for jobs on
the force, and gunmen opened
fire on a van carrying police
home from work in Baqouba,
killing at least 59 people
total and wounding at least
114. The attacks were the
latest attempts by militants
to wreck the building of a
strong Iraqi security force, a
keystone of the U.S..." - ], - [ - "The Israeli prime minister
said today that he wanted to
begin withdrawing settlers
from Gaza next May or June." - ], - [ - "Indianapolis, IN (Sports
Network) - The Indiana Pacers
try to win their second
straight game tonight, as they
host Kevin Garnett and the
Minnesota Timberwolves in the
third of a four-game homestand
at Conseco Fieldhouse." - ], - [ - "OXNARD - A leak of explosive
natural gas forced dozens of
workers to evacuate an
offshore oil platform for
hours Thursday but no damage
or injuries were reported." - ], - [ - "AP - Assets of the nation's
retail money market mutual
funds rose by #36;2.85
billion in the latest week to
#36;845.69 billion, the
Investment Company Institute
said Thursday." - ], - [ - "Peter Mandelson provoked fresh
Labour in-fighting yesterday
with an implied attack on
Gordon Brown #39;s
quot;exaggerated gloating
quot; about the health of the
British economy." - ], - [ - "Queen Elizabeth II stopped
short of apologizing for the
Allies #39; bombing of Dresden
during her first state visit
to Germany in 12 years and
instead acknowledged quot;the
appalling suffering of war on
both sides." - ], - [ - "JC Penney said yesterday that
Allen I. Questrom, the chief
executive who has restyled the
once-beleaguered chain into a
sleeker and more profitable
entity, would be succeeded by
Myron E. Ullman III, another
longtime retail executive." - ], - [ - " In the cosmetics department
at Hecht's in downtown
Washington, construction crews
have ripped out the
traditional glass display
cases, replacing them with a
system of open shelves stacked
high with fragrances from
Chanel, Burberry and Armani,
now easily within arm's reach
of the impulse buyer." - ], - [ - " LONDON (Reuters) - Oil prices
slid from record highs above
\\$50 a barrel Wednesday as the
U.S. government reported a
surprise increase in crude
stocks and rebels in Nigeria's
oil-rich delta region agreed
to a preliminary cease-fire." - ], - [ - "Rocky Shoes and Boots makes an
accretive acquisition -- and
gets Dickies and John Deere as
part of the deal." - ], - [ - "AP - Fugitive Taliban leader
Mullah Mohammed Omar has
fallen out with some of his
lieutenants, who blame him for
the rebels' failure to disrupt
the landmark Afghan
presidential election, the
U.S. military said Wednesday." - ], - [ - "HAVANA -- Cuban President
Fidel Castro's advancing age
-- and ultimately his
mortality -- were brought home
yesterday, a day after he
fractured a knee and arm when
he tripped and fell at a
public event." - ], - [ - " BRASILIA, Brazil (Reuters) -
The United States and Brazil
predicted on Tuesday Latin
America's largest country
would resolve a dispute with
the U.N. nuclear watchdog over
inspections of a uranium
enrichment plant." - ], - [ - "Call it the Maximus factor.
Archaeologists working at the
site of an old Roman temple
near Guy #39;s hospital in
London have uncovered a pot of
cosmetic cream dating back to
AD2." - ], - [ - "It is a team game, this Ryder
Cup stuff that will commence
Friday at Oakland Hills
Country Club. So what are the
teams? For the Americans,
captain Hal Sutton isn't
saying." - ], - [ - "Two bombs exploded today near
a tea shop and wounded 20
people in southern Thailand,
police said, as violence
continued unabated in the
Muslim-majority region where
residents are seething over
the deaths of 78 detainees
while in military custody." - ], - [ - "BONN: Deutsche Telekom is
bidding 2.9 bn for the 26 it
does not own in T-Online
International, pulling the
internet service back into the
fold four years after selling
stock to the public." - ], - [ - "Motorola Inc. says it #39;s
ready to make inroads in the
cell-phone market after
posting a third straight
strong quarter and rolling out
a series of new handsets in
time for the holiday selling
season." - ], - [ - "Prime Minister Junichiro
Koizumi, back in Tokyo after
an 11-day diplomatic mission
to the Americas, hunkered down
with senior ruling party
officials on Friday to focus
on a major reshuffle of
cabinet and top party posts." - ], - [ - "Costs of employer-sponsored
health plans are expected to
climb an average of 8 percent
in 2005, the first time in
five years increases have been
in single digits." - ], - [ - "NEW YORK - A sluggish gross
domestic product reading was
nonetheless better than
expected, prompting investors
to send stocks slightly higher
Friday on hopes that the
economic slowdown would not be
as bad as first thought.
The 2.8 percent GDP growth in
the second quarter, a revision
from the 3 percent preliminary
figure reported in July, is a
far cry from the 4.5 percent
growth in the first quarter..." - ], - [ - "After again posting record
earnings for the third
quarter, Taiwan Semiconductor
Manufacturing Company (TSMC)
expects to see its first
sequential drop in fourth-
quarter revenues, coupled with
a sharp drop in capacity
utilization rates." - ], - [ - " SEOUL (Reuters) - A huge
explosion in North Korea last
week may have been due to a
combination of demolition work
for a power plant and
atmospheric clouds, South
Korea's spy agency said on
Wednesday." - ], - [ - "The deal comes as Cisco pushes
to develop a market for CRS-1,
a new line of routers aimed at
telephone, wireless and cable
companies." - ], - [ - "Many people who have never
bounced a check in their life
could soon bounce their first
check if they write checks to
pay bills a couple of days
before their paycheck is
deposited into their checking
account." - ], - [ - " LUXEMBOURG (Reuters) -
Microsoft Corp told a judge on
Thursday that the European
Commission must be stopped
from ordering it to give up
secret technology to
competitors." - ], - [ - "SEPTEMBER 14, 2004 (IDG NEWS
SERVICE) - Sun Microsystems
Inc. and Microsoft Corp. next
month plan to provide more
details on the work they are
doing to make their products
interoperable, a Sun executive
said yesterday." - ], - [ - "MANCHESTER United today
dramatically rejected the
advances of Malcolm Glazer,
the US sports boss who is
mulling an 825m bid for the
football club." - ], - [ - "Dow Jones Industrial Average
futures declined amid concern
an upcoming report on
manufacturing may point to
slowing economic growth." - ], - [ - "AP - Astronomy buffs and
amateur stargazers turned out
to watch a total lunar eclipse
Wednesday night #151; the
last one Earth will get for
nearly two and a half years." - ], - [ - "Reuters - U.S. industrial
output advanced in\\July, as
American factories operated at
their highest capacity\\in more
than three years, a Federal
Reserve report on
Tuesday\\showed." - ], - [ - "As usual, the Big Ten coaches
were out in full force at
today #39;s Big Ten
Teleconference. Both OSU head
coach Jim Tressel and Iowa
head coach Kirk Ferentz
offered some thoughts on the
upcoming game between OSU" - ], - [ - "Sir Martin Sorrell, chief
executive of WPP, yesterday
declared he was quot;very
impressed quot; with Grey
Global, stoking speculation
WPP will bid for the US
advertising company." - ], - [ - "The compact disc has at least
another five years as the most
popular music format before
online downloads chip away at
its dominance, a new study
said on Tuesday." - ], - [ - "New York Knicks #39; Stephon
Marbury (3) fires over New
Orleans Hornets #39; Dan
Dickau (2) during the second
half in New Orleans Wednesday
night, Dec. 8, 2004." - ], - [ - "AP - Sudan's U.N. ambassador
challenged the United States
to send troops to the Darfur
region if it really believes a
genocide is taking place as
the U.S. Congress and
President Bush's
administration have
determined." - ], - [ - "At the very moment when the
Red Sox desperately need
someone slightly larger than
life to rally around, they
suddenly have the man for the
job: Thrilling Schilling." - ], - [ - "Does Your Site Need a Custom
Search Engine
Toolbar?\\\\Today's surfers
aren't always too comfortable
installing software on their
computers. Especially free
software that they don't
necessarily understand. With
all the horror stories of
viruses, spyware, and adware
that make the front page these
days, it's no wonder. So is
there ..." - ], - [ - "Dale Earnhardt Jr, right,
talks with Matt Kenseth, left,
during a break in practice at
Lowe #39;s Motor Speedway in
Concord, NC, Thursday Oct. 14,
2004 before qualifying for
Saturday #39;s UAW-GM Quality
500 NASCAR Nextel Cup race." - ], - [ - "Several starting spots may
have been usurped or at least
threatened after relatively
solid understudy showings
Sunday, but few players
welcome the kind of shot
delivered to Oakland" - ], - [ - "TEMPE, Ariz. -- Neil Rackers
kicked four field goals and
the Arizona Cardinals stifled
rookie Chris Simms and the
rest of the Tampa Bay offense
for a 12-7 victory yesterday
in a matchup of two sputtering
teams out of playoff
contention. Coach Jon Gruden's
team lost its fourth in a row
to finish 5-11, Tampa Bay's
worst record since going ..." - ], - [ - "AFP - A junior British
minister said that people
living in camps after fleeing
their villages because of
conflict in Sudan's Darfur
region lived in fear of
leaving their temporary homes
despite a greater presence now
of aid workers." - ], - [ - "US Deputy Secretary of State
Richard Armitage (L) shakes
hands with Pakistani Foreign
Minister Khurshid Kasuri prior
to their meeting in Islamabad,
09 November 2004." - ], - [ - "Spammers aren't ducking
antispam laws by operating
offshore, they're just
ignoring it." - ], - [ - "AP - Biologists plan to use
large nets and traps this week
in Chicago's Burnham Harbor to
search for the northern
snakehead #151; a type of
fish known for its voracious
appetite and ability to wreak
havoc on freshwater
ecosystems." - ], - [ - "BAE Systems PLC and Northrop
Grumman Corp. won \\$45 million
contracts yesterday to develop
prototypes of anti-missile
technology that could protect
commercial aircraft from
shoulder-fired missiles." - ], - [ - "AP - Democrat John Edwards
kept up a long-distance debate
over his \"two Americas\"
campaign theme with Vice
President Dick Cheney on
Tuesday, saying it was no
illusion to thousands of laid-
off workers in Ohio." - ], - [ - "Like introductory credit card
rates and superior customer
service, some promises just
aren #39;t built to last. And
so it is that Bank of America
- mere months after its pledge
to preserve" - ], - [ - "A group of 76 Eritreans on a
repatriation flight from Libya
Friday forced their plane to
change course and land in the
Sudanese capital, Khartoum,
where they" - ], - [ - "Reuters - Accounting firm KPMG
will pay #36;10\\million to
settle charges of improper
professional conduct\\while
acting as auditor for Gemstar-
TV Guide International Inc.\\,
the U.S. Securities and
Exchange Commission said
on\\Wednesday." - ], - [ - "Family matters made public: As
eager cousins wait for a slice
of the \\$15 billion cake that
is the Pritzker Empire,
Circuit Court Judge David
Donnersberger has ruled that
the case will be conducted in
open court." - ], - [ - "Users of Microsoft #39;s
Hotmail, most of whom are
accustomed to getting regular
sales pitches for premium
e-mail accounts, got a
pleasant surprise in their
inboxes this week: extra
storage for free." - ], - [ - "AP - They say that opposites
attract, and in the case of
Sen. John Kerry and Teresa
Heinz Kerry, that may be true
#151; at least in their public
personas." - ], - [ - "The weekly survey from
mortgage company Freddie Mac
had rates on 30-year fixed-
rate mortgages inching higher
this week, up to an average
5.82 percent from last week
#39;s 5.81 percent." - ], - [ - "The classic power struggle
between Walt Disney Co. CEO
Michael Eisner and former
feared talent agent Michael
Ovitz makes for high drama in
the courtroom - and apparently
on cable." - ], - [ - "ATHENS France, Britain and the
United States issued a joint
challenge Thursday to Germany
#39;s gold medal in equestrian
team three-day eventing." - ], - [ - "LONDON (CBS.MW) -- Elan
(UK:ELA) (ELN) and partner
Biogen (BIIB) said the FDA has
approved new drug Tysabri to
treat relapsing forms of
multiple sclerosis." - ], - [ - "IT services provider
Electronic Data Systems
yesterday reported a net loss
of \\$153 million for the third
quarter, with earnings hit in
part by an asset impairment
charge of \\$375 million
connected with EDS's N/MCI
project." - ], - [ - "ATLANTA - Who could have
imagined Tommy Tuberville in
this position? Yet there he
was Friday, standing alongside
the SEC championship trophy,
posing for pictures and
undoubtedly chuckling a bit on
the inside." - ], - [ - "AP - Impoverished North Korea
might resort to selling
weapons-grade plutonium to
terrorists for much-needed
cash, and that would be
\"disastrous for the world,\"
the top U.S. military
commander in South Korea said
Friday." - ], - [ - "International Business
Machines Corp. said Wednesday
it had agreed to settle most
of the issues in a suit over
changes in its pension plans
on terms that allow the
company to continue to appeal
a key question while capping
its liability at \\$1.7
billion. <BR><FONT
face=\"verdana,MS Sans
Serif,arial,helvetica\"
size=\"-2\"\\ color=\"#666666\">
<B>-The Washington
Post</B></FONT>" - ], - [ - "Dubai: Osama bin Laden on
Thursday called on his
fighters to strike Gulf oil
supplies and warned Saudi
leaders they risked a popular
uprising in an audio message
said to be by the Western
world #39;s most wanted terror
mastermind." - ], - [ - "Bank of New Zealand has frozen
all accounts held in the name
of Access Brokerage, which was
yesterday placed in
liquidation after a client
fund shortfall of around \\$5
million was discovered." - ], - [ - "Edward Kozel, Cisco's former
chief technology officer,
joins the board of Linux
seller Red Hat." - ], - [ - "Reuters - International
Business Machines\\Corp. late
on Wednesday rolled out a new
version of its\\database
software aimed at users of
Linux and Unix
operating\\systems that it
hopes will help the company
take away market\\share from
market leader Oracle Corp. ." - ], - [ - "NASA #39;s Mars Odyssey
mission, originally scheduled
to end on Tuesday, has been
granted a stay of execution
until at least September 2006,
reveal NASA scientists." - ], - [ - "ZDNet #39;s survey of IT
professionals in August kept
Wired amp; Wireless on top
for the 18th month in a row.
Telecommunications equipment
maker Motorola said Tuesday
that it would cut 1,000 jobs
and take related" - ], - [ - "John Thomson threw shutout
ball for seven innings
Wednesday night in carrying
Atlanta to a 2-0 blanking of
the New York Mets. New York
lost for the 21st time in 25
games on the" - ], - [ - "BEIJING -- Chinese authorities
have arrested or reprimanded
more than 750 officials in
recent months in connection
with billions of dollars in
financial irregularities
ranging from unpaid taxes to
embezzlement, according to a
report made public yesterday." - ], - [ - "Canadian Press - GAUHATI,
India (AP) - Residents of
northeastern India were
bracing for more violence
Friday, a day after bombs
ripped through two buses and a
grenade was hurled into a
crowded market in attacks that
killed four people and wounded
54." - ], - [ - "Vikram Solanki beat the rain
clouds to register his second
one-day international century
as England won the third one-
day international to wrap up a
series victory." - ], - [ - "Some 28 people are charged
with trying to overthrow
Sudan's President Bashir,
reports say." - ], - [ - "Hong Kong #39;s Beijing-backed
chief executive yesterday
ruled out any early moves to
pass a controversial national
security law which last year
sparked a street protest by
half a million people." - ], - [ - "BRITAIN #39;S largest
financial institutions are
being urged to take lead roles
in lawsuits seeking hundreds
of millions of dollars from
the scandal-struck US
insurance industry." - ], - [ - "Bankrupt UAL Corp. (UALAQ.OB:
Quote, Profile, Research) on
Thursday reported a narrower
third-quarter net loss. The
parent of United Airlines
posted a loss of \\$274
million, or" - ], - [ - "A three-touchdown point spread
and a recent history of late-
season collapses had many
thinking the UCLA football
team would provide little
opposition to rival USC #39;s
march to the BCS-championship
game at the Orange Bowl." - ], - [ - " PHOENIX (Sports Network) -
Free agent third baseman Troy
Glaus is reportedly headed to
the Arizona Diamondbacks." - ], - [ - "The European Union #39;s head
office issued a bleak economic
report Tuesday, warning that
the sharp rise in oil prices
will quot;take its toll quot;
on economic growth next year
while the euro #39;s renewed
climb could threaten crucial
exports." - ], - [ - "Third-string tailback Chris
Markey ran for 131 yards to
lead UCLA to a 34-26 victory
over Oregon on Saturday.
Markey, playing because of an
injury to starter Maurice
Drew, also caught five passes
for 84 yards" - ], - [ - "Though Howard Stern's
defection from broadcast to
satellite radio is still 16
months off, the industry is
already trying to figure out
what will fill the crater in
ad revenue and listenership
that he is expected to leave
behind." - ], - [ - " WASHINGTON (Reuters) - The
United States set final anti-
dumping duties of up to 112.81
percent on shrimp imported
from China and up to 25.76
percent on shrimp from Vietnam
to offset unfair pricing, the
Commerce Department said on
Tuesday." - ], - [ - "Jay Payton #39;s three-run
homer led the San Diego Padres
to a 5-1 win over the San
Francisco Giants in National
League play Saturday, despite
a 701st career blast from
Barry Bonds." - ], - [ - "The optimists among Rutgers
fans were delighted, but the
Scarlet Knights still gave the
pessimists something to worry
about." - ], - [ - "Beer consumption has doubled
over the past five years,
prompting legislators to
implement new rules." - ], - [ - "BusinessWeek Online - The
jubilation that swept East
Germany after the fall of the
Berlin Wall in 1989 long ago
gave way to the sober reality
of globalization and market
forces. Now a decade of
resentment seems to be boiling
over. In Eastern cities such
as Leipzig or Chemnitz,
thousands have taken to the
streets since July to protest
cuts in unemployment benefits,
the main source of livelihood
for 1.6 million East Germans.
Discontent among
reunification's losers fueled
big gains by the far left and
far right in Brandenburg and
Saxony state elections Sept.
19. ..." - ], - [ - "NEW YORK -- When Office Depot
Inc. stores ran an electronics
recycling drive last summer
that accepted everything from
cellphones to televisions,
some stores were overwhelmed
by the amount of e-trash they
received." - ], - [ - "In a discovery sure to set off
a firestorm of debate over
human migration to the western
hemisphere, archaeologists in
South Carolina say they have
uncovered evidence that people
lived in eastern North America
at least 50,000 years ago -
far earlier than" - ], - [ - "AP - Former president Bill
Clinton on Monday helped
launch a new Internet search
company backed by the Chinese
government which says its
technology uses artificial
intelligence to produce better
results than Google Inc." - ], - [ - "The International Monetary
Fund expressed concern Tuesday
about the impact of the
troubles besetting oil major
Yukos on Russia #39;s standing
as a place to invest." - ], - [ - "Perhaps the sight of Maria
Sharapova opposite her tonight
will jog Serena Williams #39;
memory. Wimbledon. The final.
You and Maria." - ], - [ - "At least 79 people were killed
and 74 were missing after some
of the worst rainstorms in
recent years triggered
landslides and flash floods in
southwest China, disaster
relief officials said
yesterday." - ], - [ - "LinuxWorld Conference amp;
Expo will come to Boston for
the first time in February,
underscoring the area's
standing as a hub for the
open-source software being
adopted by thousands of
businesses." - ], - [ - "BANGKOK Thai military aircraft
dropped 100 million paper
birds over southern Thailand
on Sunday in a gesture
intended to promote peace in
mainly Muslim provinces, where
more than 500 people have died
this year in attacks by
separatist militants and" - ], - [ - "Insurgents threatened on
Saturday to cut the throats of
two Americans and a Briton
seized in Baghdad, and
launched a suicide car bomb
attack on Iraqi security
forces in Kirkuk that killed
at least 23 people." - ], - [ - "Five days after making the
putt that won the Ryder Cup,
Colin Montgomerie looked set
to miss the cut at a European
PGA tour event." - ], - [ - "Sun Microsystems plans to
release its Sun Studio 10
development tool in the fourth
quarter of this year,
featuring support for 64-bit
applications running on AMD
Opteron and Nocona processors,
Sun officials said on Tuesday." - ], - [ - "Karachi - Captain Inzamam ul-
Haq and coach Bob Woolmer came
under fire on Thursday for
choosing to bat first on a
tricky pitch after Pakistan
#39;s humiliating defeat in
the ICC Champions Trophy semi-
final." - ], - [ - "Scottish champions Celtic saw
their three-year unbeaten home
record in Europe broken
Tuesday as they lost 3-1 to
Barcelona in the Champions
League Group F opener." - ], - [ - "Interest rates on short-term
Treasury securities rose in
yesterday's auction. The
Treasury Department sold \\$19
billion in three-month bills
at a discount rate of 1.710
percent, up from 1.685 percent
last week. An additional \\$17
billion was sold in six-month
bills at a rate of 1.950
percent, up from 1.870
percent." - ], - [ - "Most IT Managers won #39;t
question the importance of
security, but this priority
has been sliding between the
third and fourth most
important focus for companies." - ], - [ - "AP - Andy Roddick searched out
Carlos Moya in the throng of
jumping, screaming Spanish
tennis players, hoping to
shake hands." - ], - [ - "AP - The Federal Trade
Commission on Thursday filed
the first case in the country
against software companies
accused of infecting computers
with intrusive \"spyware\" and
then trying to sell people the
solution." - ], - [ - "While shares of Apple have
climbed more than 10 percent
this week, reaching 52-week
highs, two research firms told
investors Friday they continue
to remain highly bullish about
the stock." - ], - [ - "Moody #39;s Investors Service
on Wednesday said it may cut
its bond ratings on HCA Inc.
(HCA.N: Quote, Profile,
Research) deeper into junk,
citing the hospital operator
#39;s plan to buy back about
\\$2." - ], - [ - "States are now barred from
imposing telecommunications
regulations on Net phone
providers." - ], - [ - "Telekom Austria AG, the
country #39;s biggest phone
operator, won the right to buy
Bulgaria #39;s largest mobile
phone company, MobilTel EAD,
for 1.6 billion euros (\\$2.1
billion), an acquisition that
would add 3 million
subscribers." - ], - [ - "Shares of ID Biomedical jumped
after the company reported
Monday that it signed long-
term agreements with the three
largest flu vaccine
wholesalers in the United
States in light of the
shortage of vaccine for the
current flu season." - ], - [ - "ENGLAND captain and Real
Madrid midfielder David
Beckham has played down
speculation that his club are
moving for England manager
Sven-Goran Eriksson." - ], - [ - "The federal government closed
its window on the oil industry
Thursday, saying that it is
selling its last 19 per cent
stake in Calgary-based Petro-
Canada." - ], - [ - "Strong sales of new mobile
phone models boosts profits at
Carphone Warehouse but the
retailer's shares fall on
concerns at a decline in
profits from pre-paid phones." - ], - [ - " NEW YORK (Reuters) - IBM and
top scientific research
organizations are joining
forces in a humanitarian
effort to tap the unused
power of millions of computers
and help solve complex social
problems." - ], - [ - " NEW YORK, Nov. 11 -- The 40
percent share price slide in
Merck #38; Co. in the five
weeks after it pulled the
painkiller Vioxx off the
market highlighted larger
problems in the pharmaceutical
industry that may depress
performance for years,
according to academics and
stock analysts who follow the
sector." - ], - [ - "Low-fare carrier Southwest
Airlines Co. said Thursday
that its third-quarter profit
rose 12.3 percent to beat Wall
Street expectations despite
higher fuel costs." - ], - [ - "Another shock hit the drug
sector Friday when
pharmaceutical giant Pfizer
Inc. announced that it found
an increased heart risk to
patients for its blockbuster
arthritis drug Celebrex." - ], - [ - "AFP - National Basketball
Association players trying to
win a fourth consecutive
Olympic gold medal for the
United States have gotten the
wake-up call that the \"Dream
Team\" days are done even if
supporters have not." - ], - [ - " BEIJING (Reuters) - Secretary
of State Colin Powell will
urge China Monday to exert its
influence over North Korea to
resume six-party negotiations
on scrapping its suspected
nuclear weapons program." - ], - [ - "Reuters - An Israeli missile
strike killed at least\\two
Hamas gunmen in Gaza City
Monday, a day after a
top\\commander of the Islamic
militant group was killed in a
similar\\attack, Palestinian
witnesses and security sources
said." - ], - [ - "England will be seeking their
third clean sweep of the
summer when the NatWest
Challenge against India
concludes at Lord #39;s. After
beating New Zealand and West
Indies 3-0 and 4-0 in Tests,
they have come alive" - ], - [ - "AP - The Pentagon has restored
access to a Web site that
assists soldiers and other
Americans living overseas in
voting, after receiving
complaints that its security
measures were preventing
legitimate voters from using
it." - ], - [ - "The number of US information
technology workers rose 2
percent to 10.5 million in the
first quarter of this year,
but demand for them is
dropping, according to a new
report." - ], - [ - "Montreal - The British-built
Canadian submarine HMCS
Chicoutimi, crippled since a
fire at sea that killed one of
its crew, is under tow and is
expected in Faslane, Scotland,
early next week, Canadian
naval commander Tyrone Pile
said on Friday." - ], - [ - "AP - Grizzly and black bears
killed a majority of elk
calves in northern Yellowstone
National Park for the second
year in a row, preliminary
study results show." - ], - [ - "Thirty-five Pakistanis freed
from the US Guantanamo Bay
prison camp arrived home on
Saturday and were taken
straight to prison for further
interrogation, the interior
minister said." - ], - [ - "AP - Mark Richt knows he'll
have to get a little creative
when he divvies up playing
time for Georgia's running
backs next season. Not so on
Saturday. Thomas Brown is the
undisputed starter for the
biggest game of the season." - ], - [ - "Witnesses in the trial of a US
soldier charged with abusing
prisoners at Abu Ghraib have
told the court that the CIA
sometimes directed abuse and
orders were received from
military command to toughen
interrogations." - ], - [ - "Insurance firm says its board
now consists of its new CEO
Michael Cherkasky and 10
outside members. NEW YORK
(Reuters) - Marsh amp;
McLennan Cos." - ], - [ - "Michael Phelps, the six-time
Olympic champion, issued an
apology yesterday after being
arrested and charged with
drunken driving in the United
States." - ], - [ - "AP - Italian and Lebanese
authorities have arrested 10
suspected terrorists who
planned to blow up the Italian
Embassy in Beirut, an Italian
news agency and the Defense
Ministry said Tuesday." - ], - [ - "CORAL GABLES, Fla. -- John F.
Kerry and President Bush
clashed sharply over the war
in Iraq last night during the
first debate of the
presidential campaign season,
with the senator from
Massachusetts accusing" - ], - [ - "GONAIVES, Haiti -- While
desperately hungry flood
victims wander the streets of
Gonaives searching for help,
tons of food aid is stacking
up in a warehouse guarded by
United Nations peacekeepers." - ], - [ - "As part of its much-touted new
MSN Music offering, Microsoft
Corp. (MSFT) is testing a Web-
based radio service that
mimics nearly 1,000 local
radio stations, allowing users
to hear a version of their
favorite radio station with
far fewer interruptions." - ], - [ - "AFP - The resignation of
disgraced Fiji Vice-President
Ratu Jope Seniloli failed to
quell anger among opposition
leaders and the military over
his surprise release from
prison after serving just
three months of a four-year
jail term for his role in a
failed 2000 coup." - ], - [ - "ATHENS Larry Brown, the US
coach, leaned back against the
scorer #39;s table, searching
for support on a sinking ship.
His best player, Tim Duncan,
had just fouled out, and the
options for an American team
that" - ], - [ - "Sourav Ganguly files an appeal
against a two-match ban
imposed for time-wasting." - ], - [ - "Ziff Davis - A quick
resolution to the Mambo open-
source copyright dispute seems
unlikely now that one of the
parties has rejected an offer
for mediation." - ], - [ - "Greek police surrounded a bus
full of passengers seized by
armed hijackers along a
highway from an Athens suburb
Wednesday, police said." - ], - [ - "Spain have named an unchanged
team for the Davis Cup final
against the United States in
Seville on 3-5 December.
Carlos Moya, Juan Carlos
Ferrero, Rafael Nadal and
Tommy Robredo will take on the
US in front of 22,000 fans at
the converted Olympic stadium." - ], - [ - "BAGHDAD: A suicide car bomber
attacked a police convoy in
Baghdad yesterday as guerillas
kept pressure on Iraq #39;s
security forces despite a
bloody rout of insurgents in
Fallujah." - ], - [ - "Slot machine maker
International Game Technology
(IGT.N: Quote, Profile,
Research) on Tuesday posted
better-than-expected quarterly
earnings, as casinos bought" - ], - [ - "Fixtures and fittings from
Damien Hirst's restaurant
Pharmacy sell for 11.1, 8m
more than expected." - ], - [ - "Last Tuesday night, Harvard
knocked off rival Boston
College, which was ranked No.
1 in the country. Last night,
the Crimson knocked off
another local foe, Boston
University, 2-1, at Walter
Brown Arena, which marked the
first time since 1999 that
Harvard had beaten them both
in the same season." - ], - [ - "Venezuelan election officials
say they expect to announce
Saturday, results of a partial
audit of last Sunday #39;s
presidential recall
referendum." - ], - [ - "About 500 prospective jurors
will be in an Eagle, Colorado,
courtroom Friday, answering an
82-item questionnaire in
preparation for the Kobe
Bryant sexual assault trial." - ], - [ - "Students take note - endless
journeys to the library could
become a thing of the past
thanks to a new multimillion-
pound scheme to make classic
texts available at the click
of a mouse." - ], - [ - "Moscow - The next crew of the
International Space Station
(ISS) is to contribute to the
Russian search for a vaccine
against Aids, Russian
cosmonaut Salijan Sharipovthe
said on Thursday." - ], - [ - "Locked away in fossils is
evidence of a sudden solar
cooling. Kate Ravilious meets
the experts who say it could
explain a 3,000-year-old mass
migration - and today #39;s
global warming." - ], - [ - "By ANICK JESDANUN NEW YORK
(AP) -- Taran Rampersad didn't
complain when he failed to
find anything on his hometown
in the online encyclopedia
Wikipedia. Instead, he simply
wrote his own entry for San
Fernando, Trinidad and
Tobago..." - ], - [ - "Five years after catastrophic
floods and mudslides killed
thousands along Venezuela's
Caribbean coast, survivors in
this town still see the signs
of destruction - shattered
concrete walls and tall weeds
growing atop streets covered
in dried mud." - ], - [ - "How do you top a battle
between Marines and an alien
religious cult fighting to the
death on a giant corona in
outer space? The next logical
step is to take that battle to
Earth, which is exactly what
Microsoft" - ], - [ - "Sony Ericsson Mobile
Communications Ltd., the
mobile-phone venture owned by
Sony Corp. and Ericsson AB,
said third-quarter profit rose
45 percent on camera phone
demand and forecast this
quarter will be its strongest." - ], - [ - "NEW YORK, September 17
(newratings.com) - Alcatel
(ALA.NYS) has expanded its
operations and presence in the
core North American
telecommunication market with
two separate acquisitions for
about \\$277 million." - ], - [ - "Four U.S. agencies yesterday
announced a coordinated attack
to stem the global trade in
counterfeit merchandise and
pirated music and movies, an
underground industry that law-
enforcement officials estimate
to be worth \\$500 billion each
year." - ], - [ - "BEIJING The NBA has reached
booming, basketball-crazy
China _ but the league doesn
#39;t expect to make any money
soon. The NBA flew more than
100 people halfway around the
world for its first games in
China, featuring" - ], - [ - "The UN nuclear watchdog
confirmed Monday that nearly
400 tons of powerful
explosives that could be used
in conventional or nuclear
missiles disappeared from an
unguarded military
installation in Iraq." - ], - [ - " NEW YORK (Reuters) - Curt
Schilling pitched 6 2/3
innings and Manny Ramirez hit
a three-run homer in a seven-
run fourth frame to lead the
Boston Red Sox to a 9-3 win
over the host Anaheim Angels
in their American League
Divisional Series opener
Tuesday." - ], - [ - "AP - The game between the
Minnesota Twins and the New
York Yankees on Tuesday night
was postponed by rain." - ], - [ - "Oil giant Shell swept aside
nearly 100 years of history
today when it unveiled plans
to merge its UK and Dutch
parent companies. Shell said
scrapping its twin-board
structure" - ], - [ - "Caesars Entertainment Inc. on
Thursday posted a rise in
third-quarter profit as Las
Vegas hotels filled up and
Atlantic City properties
squeaked out a profit that was
unexpected by the company." - ], - [ - "Half of all U.S. Secret
Service agents are dedicated
to protecting President
Washington #151;and all the
other Presidents on U.S.
currency #151;from
counterfeiters." - ], - [ - "One of India #39;s leading
telecommunications providers
will use Cisco Systems #39;
gear to build its new
Ethernet-based broadband
network." - ], - [ - "Militants in Iraq have killed
the second of two US civilians
they were holding hostage,
according to a statement on an
Islamist website." - ], - [ - "PARIS The verdict is in: The
world #39;s greatest race car
driver, the champion of
champions - all disciplines
combined - is Heikki
Kovalainen." - ], - [ - "Pakistan won the toss and
unsurprisingly chose to bowl
first as they and West Indies
did battle at the Rose Bowl
today for a place in the ICC
Champions Trophy final against
hosts England." - ], - [ - "Shares of Beacon Roofing
Suppler Inc. shot up as much
as 26 percent in its trading
debut Thursday, edging out
bank holding company Valley
Bancorp as the biggest gainer
among a handful of new stocks
that went public this week." - ], - [ - "More than 4,000 American and
Iraqi soldiers mounted a
military assault on the
insurgent-held city of
Samarra." - ], - [ - "Maxime Faget conceived and
proposed the development of
the one-man spacecraft used in
Project Mercury, which put the
first American astronauts into
suborbital flight, then
orbital flight" - ], - [ - "The first weekend of holiday
shopping went from red-hot to
dead white, as a storm that
delivered freezing, snowy
weather across Colorado kept
consumers at home." - ], - [ - " MEXICO CITY (Reuters) -
Mexican President Vicente Fox
said on Monday he hoped
President Bush's re-election
meant a bilateral accord on
migration would be reached
before his own term runs out
at the end of 2006." - ], - [ - " LONDON (Reuters) - The dollar
fell within half a cent of
last week's record low against
the euro on Thursday after
capital inflows data added to
worries the United States may
struggle to fund its current
account deficit." - ], - [ - " BRUSSELS (Reuters) - French
President Jacques Chirac said
on Friday he had not refused
to meet Iraqi interim Prime
Minister Iyad Allawi after
reports he was snubbing the
Iraqi leader by leaving an EU
meeting in Brussels early." - ], - [ - "China has pledged to invest
\\$20 billion in Argentina in
the next 10 years, La Nacion
reported Wednesday. The
announcement came during the
first day of a two-day visit" - ], - [ - "NEW YORK - Former President
Bill Clinton was in good
spirits Saturday, walking
around his hospital room in
street clothes and buoyed by
thousands of get-well messages
as he awaited heart bypass
surgery early this coming
week, people close to the
family said. Clinton was
expected to undergo surgery as
early as Monday but probably
Tuesday, said Democratic Party
Chairman Terry McAuliffe, who
said the former president was
\"upbeat\" when he spoke to him
by phone Friday..." - ], - [ - "Toyota Motor Corp., the world
#39;s biggest carmaker by
value, will invest 3.8 billion
yuan (\\$461 million) with its
partner Guangzhou Automobile
Group to boost manufacturing
capacity in" - ], - [ - "Poland will cut its troops in
Iraq early next year and won
#39;t stay in the country
quot;an hour longer quot; than
needed, the country #39;s
prime minister said Friday." - ], - [ - "AP - Boston Red Sox center
fielder Johnny Damon is having
a recurrence of migraine
headaches that first bothered
him after a collision in last
year's playoffs." - ], - [ - "The patch fixes a flaw in the
e-mail server software that
could be used to get access to
in-boxes and information." - ], - [ - "Felix Cardenas of Colombia won
the 17th stage of the Spanish
Vuelta cycling race Wednesday,
while defending champion
Roberto Heras held onto the
overall leader #39;s jersey
for the sixth day in a row." - ], - [ - "AP - Being the biggest dog may
pay off at feeding time, but
species that grow too large
may be more vulnerable to
extinction, new research
suggests." - ], - [ - "A car bomb exploded at a US
military convoy in the
northern Iraqi city of Mosul,
causing several casualties,
the army and Iraqi officials
said." - ], - [ - "Newest Efficeon processor also
offers higher frequency using
less power." - ], - [ - "BAE Systems has launched a
search for a senior American
businessman to become a non-
executive director. The high-
profile appointment is
designed to strengthen the
board at a time when the
defence giant is" - ], - [ - "AP - In what it calls a first
in international air travel,
Finnair says it will let its
frequent fliers check in using
text messages on mobile
phones." - ], - [ - "Computer Associates
International is expected to
announce that its new chief
executive will be John
Swainson, an I.B.M. executive
with strong technical and
sales credentials." - ], - [ - "Established star Landon
Donovan and rising sensation
Eddie Johnson carried the
United States into the
regional qualifying finals for
the 2006 World Cup in emphatic
fashion Wednesday night." - ], - [ - "STOCKHOLM (Dow
Jones)--Expectations for
Telefon AB LM Ericsson #39;s
(ERICY) third-quarter
performance imply that while
sales of mobile telephony
equipment are expected to have
dipped, the company" - ], - [ - "ATHENS, Greece - Michael
Phelps took care of qualifying
for the Olympic 200-meter
freestyle semifinals Sunday,
and then found out he had been
added to the American team for
the evening's 400 freestyle
relay final. Phelps' rivals
Ian Thorpe and Pieter van den
Hoogenband and teammate Klete
Keller were faster than the
teenager in the 200 free
preliminaries..." - ], - [ - " JERUSALEM (Reuters) - Israeli
Prime Minister Ariel Sharon
intends to present a timetable
for withdrawal from Gaza to
lawmakers from his Likud Party
Tuesday despite a mutiny in
the right-wing bloc over the
plan." - ], - [ - "Search Engine for Programming
Code\\\\An article at Newsforge
pointed me to Koders (
http://www.koders.com ) a
search engine for finding
programming code. Nifty.\\\\The
front page allows you to
specify keywords, sixteen
languages (from ASP to VB.NET)
and sixteen licenses (from AFL
to ZPL -- fortunately there's
an information page to ..." - ], - [ - " #147;Apple once again was the
star of the show at the annual
MacUser awards, #148; reports
MacUser, #147;taking away six
Maxine statues including
Product of the Year for the
iTunes Music Store. #148;
Other Apple award winners:
Power Mac G5, AirPort Express,
20-inch Apple Cinema Display,
GarageBand and DVD Studio Pro
3. Nov 22" - ], - [ - " KABUL (Reuters) - Three U.N.
workers held by militants in
Afghanistan were in their
third week of captivity on
Friday after calls from both
sides for the crisis to be
resolved ahead of this
weekend's Muslim festival of
Eid al-Fitr." - ], - [ - "AP - A sweeping wildlife
preserve in southwestern
Arizona is among the nation's
10 most endangered refuges,
due in large part to illegal
drug and immigrant traffic and
Border Patrol operations, a
conservation group said
Friday." - ], - [ - "ROME, Oct 29 (AFP) - French
President Jacques Chirac urged
the head of the incoming
European Commission Friday to
take quot;the appropriate
decisions quot; to resolve a
row over his EU executive team
which has left the EU in
limbo." - ], - [ - "The Anglo-Dutch oil giant
Shell today sought to draw a
line under its reserves
scandal by announcing plans to
spend \\$15bn (8.4bn) a year to
replenish reserves and develop
production in its oil and gas
business." - ], - [ - "SEPTEMBER 20, 2004
(COMPUTERWORLD) - At
PeopleSoft Inc. #39;s Connect
2004 conference in San
Francisco this week, the
software vendor is expected to
face questions from users
about its ability to fend off
Oracle Corp." - ], - [ - "Russia's parliament will
launch an inquiry into a
school siege that killed over
300 hostages, President
Vladimir Putin said on Friday,
but analysts doubt it will
satisfy those who blame the
carnage on security services." - ], - [ - "Tony Blair talks to business
leaders about new proposals
for a major shake-up of the
English exam system." - ], - [ - "KUALA LUMPUR, Malaysia A
Malaysian woman has claimed a
new world record after living
with over six-thousand
scorpions for 36 days
straight." - ], - [ - "PBS's Charlie Rose quizzes Sun
co-founder Bill Joy,
Monster.com chief Jeff Taylor,
and venture capitalist John
Doerr." - ], - [ - "Circulation declined at most
major US newspapers in the
last half year, the latest
blow for an industry already
rocked by a scandal involving
circulation misstatements that
has undermined the confidence
of investors and advertisers." - ], - [ - "Skype Technologies is teaming
with Siemens to offer cordless
phone users the ability to
make Internet telephony calls,
in addition to traditional
calls, from their handsets." - ], - [ - "AP - After years of
resistance, the U.S. trucking
industry says it will not try
to impede or delay a new
federal rule aimed at cutting
diesel pollution." - ], - [ - "INDIANAPOLIS - ATA Airlines
has accepted a \\$117 million
offer from Southwest Airlines
that would forge close ties
between two of the largest US
discount carriers." - ], - [ - "could take drastic steps if
the talks did not proceed as
Tehran wants. Mehr news agency
quoted him as saying on
Wednesday. that could be used" - ], - [ - "Wizards coach Eddie Jordan
says the team is making a
statement that immaturity will
not be tolerated by suspending
Kwame Brown one game for not
taking part in a team huddle
during a loss to Denver." - ], - [ - "EVERETT Fire investigators
are still trying to determine
what caused a two-alarm that
destroyed a portion of a South
Everett shopping center this
morning." - ], - [ - "Umesh Patel, a 36-year old
software engineer from
California, debated until the
last minute." - ], - [ - "Sure, the PeopleSoft board
told shareholders to just say
no. This battle will go down
to the wire, and even
afterward Ellison could
prevail." - ], - [ - "Investors cheered by falling
oil prices and an improving
job picture sent stocks higher
Tuesday, hoping that the news
signals a renewal of economic
strength and a fall rally in
stocks." - ], - [ - "AP - Andy Roddick has yet to
face a challenge in his U.S.
Open title defense. He beat
No. 18 Tommy Robredo of Spain
6-3, 6-2, 6-4 Tuesday night to
move into the quarterfinals
without having lost a set." - ], - [ - "Now that hell froze over in
Boston, New England braces for
its rarest season -- a winter
of content. Red Sox fans are
adrift from the familiar
torture of the past." - ], - [ - " CHICAGO (Reuters) - Wal-Mart
Stores Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=WMT.N target
=/stocks/quickinfo/fullquote\"&
gt;WMT.N</A>, the
world's largest retailer, said
on Saturday it still
anticipates September U.S.
sales to be up 2 percent to 4
percent at stores open at
least a year." - ], - [ - " TOKYO (Reuters) - Tokyo's
Nikkei stock average opened
down 0.15 percent on
Wednesday as investors took a
breather from the market's
recent rises and sold shares
of gainers such as Sharp
Corp." - ], - [ - "AP - From now until the start
of winter, male tarantulas are
roaming around, searching for
female mates, an ideal time to
find out where the spiders
flourish in Arkansas." - ], - [ - "Israeli authorities have
launched an investigation into
death threats against Israeli
Prime Minister Ariel Sharon
and other officials supporting
his disengagement plan from
Gaza and parts of the West
Bank, Jerusalem police said
Tuesday." - ], - [ - "NewsFactor - While a U.S.
District Court continues to
weigh the legality of
\\Oracle's (Nasdaq: ORCL)
attempted takeover of
\\PeopleSoft (Nasdaq: PSFT),
Oracle has taken the necessary
steps to ensure the offer does
not die on the vine with
PeopleSoft's shareholders." - ], - [ - "NEW YORK : World oil prices
fell, capping a drop of more
than 14 percent in a two-and-
a-half-week slide triggered by
a perception of growing US
crude oil inventories." - ], - [ - "SUFFOLK -- Virginia Tech
scientists are preparing to
protect the state #39;s
largest crop from a disease
with strong potential to do
damage." - ], - [ - "It was a carefully scripted
moment when Russian President
Vladimir Putin began quoting
Taras Shevchenko, this country
#39;s 19th-century bard,
during a live television" - ], - [ - "China says it welcomes Russia
#39;s ratification of the
Kyoto Protocol that aims to
stem global warming by
reducing greenhouse-gas
emissions." - ], - [ - "Analysts said the smartphone
enhancements hold the
potential to bring PalmSource
into an expanding market that
still has room despite early
inroads by Symbian and
Microsoft." - ], - [ - "The Metropolitan
Transportation Authority is
proposing a tax increase to
raise \\$900 million a year to
help pay for a five-year
rebuilding program." - ], - [ - "AFP - The Canadian armed
forces chief of staff on was
elected to take over as head
of NATO's Military Committee,
the alliance's highest
military authority, military
and diplomatic sources said." - ], - [ - "AFP - Two proposed resolutions
condemning widespread rights
abuses in Sudan and Zimbabwe
failed to pass a UN committee,
mired in debate between
African and western nations." - ], - [ - " NEW YORK (Reuters) -
Citigroup Inc. <A HREF=\"htt
p://www.investor.reuters.com/F
ullQuote.aspx?ticker=C.N targe
t=/stocks/quickinfo/fullquote\"
>C.N</A> the world's
largest financial services
company, said on Tuesday it
will acquire First American
Bank in the quickly-growing
Texas market." - ], - [ - " SINGAPORE (Reuters) - U.S.
oil prices hovered just below
\\$50 a barrel on Tuesday,
holding recent gains on a rash
of crude supply outages and
fears over thin heating oil
tanks." - ], - [ - "THE South Africans have called
the Wallabies scrum cheats as
a fresh round of verbal
warfare opened in the Republic
last night." - ], - [ - "The return of noted reformer
Nabil Amr to Palestinian
politics comes at a crucial
juncture." - ], - [ - "An overwhelming majority of
NHL players who expressed
their opinion in a poll said
they would not support a
salary cap even if it meant
saving a season that was
supposed to have started Oct.
13." - ], - [ - "Tony Eury Sr. oversees Dale
Earnhardt Jr. on the
racetrack, but Sunday he
extended his domain to Victory
Lane. Earnhardt was unbuckling
to crawl out of the No." - ], - [ - "AP - After two debates, voters
have seen President Bush look
peevish and heard him pass the
buck. They've watched Sen.
John Kerry deny he's a flip-
flopper and then argue that
Saddam Hussein was a threat
#151; and wasn't. It's no
wonder so few minds have
changed." - ], - [ - "(Sports Network) - The New
York Yankees try to move one
step closer to a division
title when they conclude their
critical series with the
Boston Red Sox at Fenway Park." - ], - [ - "US retail sales fell 0.3 in
August as rising energy costs
and bad weather persuaded
shoppers to reduce their
spending." - ], - [ - "The United States says the
Lebanese parliament #39;s
decision Friday to extend the
term of pro-Syrian President
Emile Lahoud made a
quot;crude mockery of
democratic principles." - ], - [ - "Kurt Busch claimed a stake in
the points lead in the NASCAR
Chase for the Nextel Cup
yesterday, winning the
Sylvania 300 at New Hampshire
International Speedway." - ], - [ - "TOKYO (AFP) - Japan #39;s
Fujitsu and Cisco Systems of
the US said they have agreed
to form a strategic alliance
focusing on routers and
switches that will enable
businesses to build advanced
Internet Protocol (IP)
networks." - ], - [ - "GEORGETOWN, Del., Oct. 28 --
Plaintiffs in a shareholder
lawsuit over former Walt
Disney Co. president Michael
Ovitz's \\$140 million
severance package attempted
Thursday to portray Ovitz as a
dishonest bumbler who botched
the hiring of a major
television executive and
pushed the release of a movie
that angered the Chinese
government, damaging Disney's
business prospects in the
country." - ], - [ - "US stocks gained ground in
early trading Thursday after
tame inflation reports and
better than expected jobless
news. Oil prices held steady
as Hurricane Ivan battered the
Gulf coast, where oil
operations have halted." - ], - [ - "It #39;s a new internet
browser. The first full
version, Firefox 1.0, was
launched earlier this month.
In the sense it #39;s a
browser, yes, but the
differences are larger than
the similarities." - ], - [ - "LOUDON, NH - As this
newfangled stretch drive for
the Nextel Cup championship
ensues, Jeff Gordon has to be
considered the favorite for a
fifth title." - ], - [ - "PepsiCo. Inc., the world #39;s
No. 2 soft- drink maker, plans
to buy General Mills Inc.
#39;s stake in their European
joint venture for \\$750
million in cash, giving it
complete ownership of Europe
#39;s largest snack-food
company." - ], - [ - "Microsoft will accelerate SP2
distribution to meet goal of
100 million downloads in two
months." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks opened little changed
on Friday, after third-
quarter gross domestic product
data showed the U.S. economy
grew at a slower-than-expected
pace." - ], - [ - "International Business
Machines Corp., taken to court
by workers over changes it
made to its traditional
pension plan, has decided to
stop offering any such plan to
new employees." - ], - [ - "NEW YORK - With four of its
executives pleading guilty to
price-fixing charges today,
Infineon will have a hard time
arguing that it didn #39;t fix
prices in its ongoing
litigation with Rambus." - ], - [ - "By nick giongco. YOU KNOW you
have reached the status of a
boxing star when ring
announcer extraordinaire
Michael Buffer calls out your
name in his trademark booming
voice during a high-profile
event like yesterday" - ], - [ - "Canadian Press - OTTAWA (CP) -
The prime minister says he's
been assured by President
George W. Bush that the U.S.
missile defence plan does not
necessarily involve the
weaponization of space." - ], - [ - "AP - Even with a big lead,
Eric Gagne wanted to pitch in
front of his hometown fans one
last time." - ], - [ - " NEW YORK (Reuters) - Limited
Brands Inc. <A HREF=\"http:/
/www.investor.reuters.com/Full
Quote.aspx?ticker=LTD.N target
=/stocks/quickinfo/fullquote\"&
gt;LTD.N</A> on
Thursday reported higher
quarterly operating profit as
cost controls and strong
lingerie sales offset poor
results at the retailer's
Express apparel stores." - ], - [ - "General Motors and
DaimlerChrysler are
collaborating on development
of fuel- saving hybrid engines
in hopes of cashing in on an
expanding market dominated by
hybrid leaders Toyota and
Honda." - ], - [ - "ATHENS, Greece -- Larry Brown
was despondent, the head of
the US selection committee was
defensive and an irritated
Allen Iverson was hanging up
on callers who asked what went
wrong." - ], - [ - "Fifty-six miners are dead and
another 92 are still stranded
underground after a gas blast
at the Daping coal mine in
Central China #39;s Henan
Province, safety officials
said Thursday." - ], - [ - "BEIRUT, Lebanon - Three
Lebanese men and their Iraqi
driver have been kidnapped in
Iraq, the Lebanese Foreign
Ministry said Sunday, as
Iraq's prime minister said his
government was working for the
release of two Americans and a
Briton also being held
hostage. Gunmen snatched
the three Lebanese, who worked
for a travel agency with a
branch in Baghdad, as they
drove on the highway between
the capital and Fallujah on
Friday night, a ministry
official said..." - ], - [ - "PORTLAND, Ore. - It's been
almost a year since singer-
songwriter Elliott Smith
committed suicide, and fans
and friends will be looking
for answers as the posthumous
\"From a Basement on the Hill\"
is released..." - ], - [ - "AP - Courtney Brown refuses to
surrender to injuries. He's
already planning another
comeback." - ], - [ - " GAZA (Reuters) - Israel
expanded its military
offensive in northern Gaza,
launching two air strikes
early on Monday that killed
at least three Palestinians
and wounded two, including a
senior Hamas leader, witnesses
and medics said." - ], - [ - " TOKYO (Reuters) - Nintendo
Co. Ltd. raised its 2004
shipment target for its DS
handheld video game device by
40 percent to 2.8 million
units on Thursday after many
stores in Japan and the
United States sold out in the
first week of sales." - ], - [ - "It took an off-the-cuff
reference to a serial
murderer/cannibal to punctuate
the Robby Gordon storyline.
Gordon has been vilified by
his peers and put on probation" - ], - [ - "By BOBBY ROSS JR. Associated
Press Writer. play the Atlanta
Hawks. They will be treated to
free food and drink and have.
their pictures taken with
Mavericks players, dancers and
team officials." - ], - [ - "ARSENAL #39;S Brazilian World
Cup winning midfielder
Gilberto Silva is set to be
out for at least a month with
a back injury, the Premiership
leaders said." - ], - [ - "INDIANAPOLIS -- With a package
of academic reforms in place,
the NCAA #39;s next crusade
will address what its
president calls a dangerous
drift toward professionalism
and sports entertainment." - ], - [ - "Autodesk this week unwrapped
an updated version of its
hosted project collaboration
service targeted at the
construction and manufacturing
industries. Autodesk Buzzsaw
lets multiple, dispersed
project participants --
including building owners,
developers, architects,
construction teams, and
facility managers -- share and
manage data throughout the
life of a project, according
to Autodesk officials." - ], - [ - "AP - It was difficult for
Southern California's Pete
Carroll and Oklahoma's Bob
Stoops to keep from repeating
each other when the two
coaches met Thursday." - ], - [ - "Reuters - Sudan's government
resumed\\talks with rebels in
the oil-producing south on
Thursday while\\the United
Nations set up a panel to
investigate charges
of\\genocide in the west of
Africa's largest country." - ], - [ - "Andy Roddick, along with
Olympic silver medalist Mardy
Fish and the doubles pair of
twins Bob and Mike Bryan will
make up the US team to compete
with Belarus in the Davis Cup,
reported CRIENGLISH." - ], - [ - "NEW YORK - Optimism that the
embattled technology sector
was due for a recovery sent
stocks modestly higher Monday
despite a new revenue warning
from semiconductor company
Broadcom Inc. While
Broadcom, which makes chips
for television set-top boxes
and other electronics, said
high inventories resulted in
delayed shipments, investors
were encouraged as it said
future quarters looked
brighter..." - ], - [ - "A report indicates that many
giants of the industry have
been able to capture lasting
feelings of customer loyalty." - ], - [ - "It is the news that Internet
users do not want to hear: the
worldwide web is in danger of
collapsing around us. Patrick
Gelsinger, the chief
technology officer for
computer chip maker Intel,
told a conference" - ], - [ - " WASHINGTON (Reuters) - U.S.
employers hired just 96,000
workers in September, the
government said on Friday in a
weak jobs snapshot, the final
one ahead of presidential
elections that also fueled
speculation about a pause in
interest-rate rises." - ], - [ - "Cisco Systems CEO John
Chambers said today that his
company plans to offer twice
as many new products this year
as ever before." - ], - [ - "While the world #39;s best
athletes fight the noble
Olympic battle in stadiums and
pools, their fans storm the
streets of Athens, turning the
Greek capital into a huge
international party every
night." - ], - [ - "Carlos Barrios Orta squeezed
himself into his rubber diving
suit, pulled on an 18-pound
helmet that made him look like
an astronaut, then lowered
himself into the sewer. He
disappeared into the filthy
water, which looked like some
cauldron of rancid beef stew,
until the only sign of him was
air bubbles breaking the
surface." - ], - [ - "The mutilated body of a woman
of about sixty years,
amputated of arms and legs and
with the sliced throat, has
been discovered this morning
in a street of the south of
Faluya by the Marines,
according to a statement by
AFP photographer." - ], - [ - "Every day, somewhere in the
universe, there #39;s an
explosion that puts the power
of the Sun in the shade. Steve
Connor investigates the riddle
of gamma-ray bursts." - ], - [ - "Ten months after NASA #39;s
twin rovers landed on Mars,
scientists reported this week
that both robotic vehicles are
still navigating their rock-
studded landscapes with all
instruments operating" - ], - [ - "EVERTON showed they would not
be bullied into selling Wayne
Rooney last night by rejecting
a 23.5million bid from
Newcastle - as Manchester
United gamble on Goodison
#39;s resolve to keep the
striker." - ], - [ - "SAN FRANCISCO (CBS.MW) -- The
magazine known for evaluating
cars and electronics is
setting its sights on finding
the best value and quality of
prescription drugs on the
market." - ], - [ - "After months of legal
wrangling, the case of
<em>People v. Kobe Bean
Bryant</em> commences on
Friday in Eagle, Colo., with
testimony set to begin next
month." - ], - [ - "(SH) - In Afghanistan, Hamid
Karzai defeated a raft of
candidates to win his historic
election. In Iraq, more than
200 political parties have
registered for next month
#39;s elections." - ], - [ - "Lyon coach Paul le Guen has
admitted his side would be
happy with a draw at Old
Trafford on Tuesday night. The
three-times French champions
have assured themselves of
qualification for the
Champions League" - ], - [ - "British scientists say they
have found a new, greener way
to power cars and homes using
sunflower oil, a commodity
more commonly used for cooking
fries." - ], - [ - "A mouse, a house, and your
tax-planning spouse all factor
huge in the week of earnings
that lies ahead." - ], - [ - "The United States #39; top
computer-security official has
resigned after a little more
than a year on the job, the US
Department of Homeland
Security said on Friday." - ], - [ - "Reuters - Barry Bonds failed
to collect a hit in\\his bid to
join the 700-homer club, but
he did score a run to\\help the
San Francisco Giants edge the
host Milwaukee Brewers\\3-2 in
National League action on
Tuesday." - ], - [ - " SAN FRANCISCO (Reuters) -
Intel Corp. <A HREF=\"http:/
/www.reuters.co.uk/financeQuot
eLookup.jhtml?ticker=INTC.O
qtype=sym infotype=info
qcat=news\">INTC.O</A>
on Thursday outlined its
vision of the Internet of the
future, one in which millions
of computer servers would
analyze and direct network
traffic to make the Web safer
and more efficient." - ], - [ - "Margaret Hassan, who works for
charity Care International,
was taken hostage while on her
way to work in Baghdad. Here
are the main events since her
kidnapping." - ], - [ - "Check out new gadgets as
holiday season approaches." - ], - [ - "DALLAS (CBS.MW) -- Royal
Dutch/Shell Group will pay a
\\$120 million penalty to
settle a Securities and
Exchange Commission
investigation of its
overstatement of nearly 4.5
billion barrels of proven
reserves, the federal agency
said Tuesday." - ], - [ - "After waiting an entire summer
for the snow to fall and
Beaver Creek to finally open,
skiers from around the planet
are coming to check out the
Birds of Prey World Cup action
Dec. 1 - 5. Although everyones" - ], - [ - "TOKYO, Sep 08, 2004 (AFX-UK
via COMTEX) -- Sony Corp will
launch its popular projection
televisions with large liquid
crystal display (LCD) screens
in China early next year, a
company spokeswoman said." - ], - [ - "I confess that I am a complete
ignoramus when it comes to
women #39;s beach volleyball.
In fact, I know only one thing
about the sport - that it is
the supreme aesthetic
experience available on planet
Earth." - ], - [ - "Manchester United Plc may
offer US billionaire Malcolm
Glazer a seat on its board if
he agrees to drop a takeover
bid for a year, the Observer
said, citing an unidentified
person in the soccer industry." - ], - [ - "PHOENIX America West Airlines
has backed away from a
potential bidding war for
bankrupt ATA Airlines, paving
the way for AirTran to take
over ATA operations." - ], - [ - "US stock-index futures
declined. Dow Jones Industrial
Average shares including
General Electric Co. slipped
in Europe. Citigroup Inc." - ], - [ - "For a guy who spent most of
his first four professional
seasons on the disabled list,
Houston Astros reliever Brad
Lidge has developed into quite
the ironman these past two
days." - ], - [ - "AFP - Outgoing EU competition
commissioner Mario Monti wants
to reach a decision on
Oracle's proposed takeover of
business software rival
PeopleSoft by the end of next
month, an official said." - ], - [ - "Former Bengal Corey Dillon
found his stride Sunday in his
second game for the New
England Patriots. Dillon
gained 158 yards on 32 carries
as the Patriots beat the
Arizona Cardinals, 23-12, for
their 17th victory in a row." - ], - [ - "If legend is to be believed,
the end of a victorious war
was behind Pheidippides #39;
trek from Marathon to Athens
2,500 years ago." - ], - [ - " WASHINGTON (Reuters) -
Satellite companies would be
able to retransmit
broadcasters' television
signals for another five
years but would have to offer
those signals on a single
dish, under legislation
approved by Congress on
Saturday." - ], - [ - " BEIJING (Reuters) - Wimbledon
champion Maria Sharapova
demolished fellow Russian
Tatiana Panova 6-1, 6-1 to
advance to the quarter-finals
of the China Open on
Wednesday." - ], - [ - "Sven Jaschan, who may face
five years in prison for
spreading the Netsky and
Sasser worms, is now working
in IT security. Photo: AFP." - ], - [ - "Padres general manager Kevin
Towers called Expos general
manager Omar Minaya on
Thursday afternoon and told
him he needed a shortstop
because Khalil Greene had
broken his" - ], - [ - "Motorsport.com. Nine of the
ten Formula One teams have
united to propose cost-cutting
measures for the future, with
the notable exception of
Ferrari." - ], - [ - "Reuters - The United States
modified its\\call for U.N.
sanctions against Sudan on
Tuesday but still kept\\up the
threat of punitive measures if
Khartoum did not
stop\\atrocities in its Darfur
region." - ], - [ - "Human rights and environmental
activists have hailed the
award of the 2004 Nobel Peace
Prize to Wangari Maathai of
Kenya as fitting recognition
of the growing role of civil
society" - ], - [ - "Federal Reserve Chairman Alan
Greenspan has done it again.
For at least the fourth time
this year, he has touched the
electrified third rail of
American politics - Social
Security." - ], - [ - "An Al Qaeda-linked militant
group beheaded an American
hostage in Iraq and threatened
last night to kill another two
Westerners in 24 hours unless
women prisoners were freed
from Iraqi jails." - ], - [ - "TechWeb - An Indian Institute
of Technology professor--and
open-source evangelist--
discusses the role of Linux
and open source in India." - ], - [ - "Here are some of the latest
health and medical news
developments, compiled by
editors of HealthDay: -----
Contaminated Fish in Many U.S.
Lakes and Rivers Fish
that may be contaminated with
dioxin, mercury, PCBs and
pesticides are swimming in
more than one-third of the
United States' lakes and
nearly one-quarter of its
rivers, according to a list of
advisories released by the
Environmental Protection
Agency..." - ], - [ - "Argentina, Denmark, Greece,
Japan and Tanzania on Friday
won coveted two-year terms on
the UN Security Council at a
time when pressure is mounting
to expand the powerful
15-nation body." - ], - [ - "Description: Scientists say
the arthritis drug Bextra may
pose increased risk of
cardiovascular troubles.
Bextra is related to Vioxx,
which was pulled off the
market in September for the
same reason." - ], - [ - "Steve Francis and Shaquille O
#39;Neal enjoyed big debuts
with their new teams. Kobe
Bryant found out he can #39;t
carry the Lakers all by
himself." - ], - [ - "The trial of a lawsuit by Walt
Disney Co. shareholders who
accuse the board of directors
of rubberstamping a deal to
hire Michael Ovitz" - ], - [ - "Australia, by winning the
third Test at Nagpur on
Friday, also won the four-
match series 2-0 with one
match to go. Australia had
last won a Test series in
India way back in December
1969 when Bill Lawry #39;s
team beat Nawab Pataudi #39;s
Indian team 3-1." - ], - [ - "LOS ANGELES Grocery giant
Albertsons says it has
purchased Bristol Farms, which
operates eleven upscale stores
in Southern California." - ], - [ - "ISLAMABAD: Pakistan and India
agreed on Friday to re-open
the Khokhropar-Munabao railway
link, which was severed nearly
40 years ago." - ], - [ - "Final Score: Connecticut 61,
New York 51 New York, NY
(Sports Network) - Nykesha
Sales scored 15 points to lead
Connecticut to a 61-51 win
over New York in Game 1 of
their best-of-three Eastern
Conference Finals series at
Madison Square Garden." - ], - [ - "NEW YORK - Stocks moved higher
Friday as a stronger than
expected retail sales report
showed that higher oil prices
aren't scaring consumers away
from spending. Federal Reserve
Chairman Alan Greenspan's
positive comments on oil
prices also encouraged
investors..." - ], - [ - "The figures from a survey
released today are likely to
throw more people into the
ranks of the uninsured,
analysts said." - ], - [ - "Bill Gates is giving his big
speech right now at Microsofts
big Digital Entertainment
Anywhere event in Los Angeles.
Well have a proper report for
you soon, but in the meantime
we figured wed" - ], - [ - "Spinal and non-spinal
fractures are reduced by
almost a third in women age 80
or older who take a drug
called strontium ranelate,
European investigators
announced" - ], - [ - "JB Oxford Holdings Inc., a
Beverly Hills-based discount
brokerage firm, was sued by
the Securities and Exchange
Commission for allegedly
allowing thousands of improper
trades in more than 600 mutual
funds." - ], - [ - "BERLIN: Apple Computer Inc is
planning the next wave of
expansion for its popular
iTunes online music store with
a multi-country European
launch in October, the
services chief architect said
on Wednesday." - ], - [ - "Charlotte, NC -- LeBron James
poured in a game-high 19
points and Jeff McInnis scored
18 as the Cleveland Cavaliers
routed the Charlotte Bobcats,
106-89, at the Charlotte
Coliseum." - ], - [ - "Goldman Sachs reported strong
fourth quarter and full year
earnings of \\$1.19bn, up 36
per cent on the previous
quarter. Full year profit rose
52 per cent from the previous
year to \\$4.55bn." - ], - [ - "Saddam Hussein lives in an
air-conditioned 10-by-13 foot
cell on the grounds of one of
his former palaces, tending
plants and proclaiming himself
Iraq's lawful ruler." - ], - [ - "Lehmann, who was at fault in
two matches in the tournament
last season, was blundering
again with the German set to
take the rap for both Greek
goals." - ], - [ - "Calls to 13 other countries
will be blocked to thwart
auto-dialer software." - ], - [ - "AP - Brazilian U.N.
peacekeepers will remain in
Haiti until presidential
elections are held in that
Caribbean nation sometime next
year, President Luiz Inacio
Lula da Silva said Monday." - ], - [ - "com September 27, 2004, 5:00
AM PT. This fourth priority
#39;s main focus has been
improving or obtaining CRM and
ERP software for the past year
and a half." - ], - [ - "Jeju Island, South Korea
(Sports Network) - Grace Park
and Carin Koch posted matching
rounds of six-under-par 66 on
Friday to share the lead after
the first round of the CJ Nine
Bridges Classic." - ], - [ - "SPACE.com - The Zero Gravity
Corporation \\ has been given
the thumbs up by the Federal
Aviation Administration (FAA)
to \\ conduct quot;weightless
flights quot; for the general
public, providing the \\
sensation of floating in
space." - ], - [ - "A 32-year-old woman in Belgium
has become the first woman
ever to give birth after
having ovarian tissue removed,
frozen and then implanted back
in her body." - ], - [ - "Argosy Gaming (AGY:NYSE - news
- research) jumped in early
trading Thursday, after the
company agreed to be acquired
by Penn National Gaming
(PENN:Nasdaq - news -
research) in a \\$1." - ], - [ - "No sweat, says the new CEO,
who's imported from IBM. He
also knows this will be the
challenge of a lifetime." - ], - [ - "Iraq is quot;working 24 hours
a day to ... stop the
terrorists, quot; interim
Iraqi Prime Minister Ayad
Allawi said Monday. Iraqis are
pushing ahead with reforms and
improvements, Allawi told" - ], - [ - "AP - Their discoveries may be
hard for many to comprehend,
but this year's Nobel Prize
winners still have to explain
what they did and how they did
it." - ], - [ - "The DuPont Co. has agreed to
pay up to \\$340 million to
settle a lawsuit that it
contaminated water supplies in
West Virginia and Ohio with a
chemical used to make Teflon,
one of its best-known brands." - ], - [ - "Benfica and Real Madrid set
the standard for soccer
success in Europe in the late
1950s and early #39;60s. The
clubs have evolved much
differently, but both have
struggled" - ], - [ - "AP - Musicians, composers and
authors were among the more
than two dozen people
Wednesday honored with
National Medal of Arts and
National Humanities awards at
the White House." - ], - [ - "More than 15 million homes in
the UK will be able to get on-
demand movies by 2008, say
analysts." - ], - [ - "Wynton Marsalis, the trumpet-
playing star and artistic
director of Jazz at Lincoln
Center, has become an entity
above and around the daily
jazz world." - ], - [ - "A well-researched study by the
National Academy of Sciences
makes a persuasive case for
refurbishing the Hubble Space
Telescope. NASA, which is
reluctant to take this
mission, should rethink its
position." - ], - [ - "BUENOS AIRES: Pakistan has
ratified the Kyoto Protocol on
Climatic Change, Environment
Minister Malik Khan said on
Thursday. He said that
Islamabad has notified UN
authorities of ratification,
which formally comes into
effect in February 2005." - ], - [ - "Reuters - Jeffrey Greenberg,
chairman and chief\\executive
of embattled insurance broker
Marsh McLennan Cos.\\, is
expected to step down within
hours, a newspaper\\reported on
Friday, citing people close to
the discussions." - ], - [ - "Staff Sgt. Johnny Horne, Jr.,
and Staff Sgt. Cardenas Alban
have been charged with murder
in the death of an Iraqi, the
1st Cavalry Division announced
Monday." - ], - [ - "LONDON -- In a deal that
appears to buck the growing
trend among governments to
adopt open-source
alternatives, the U.K.'s
Office of Government Commerce
(OGC) is negotiating a renewal
of a three-year agreement with
Microsoft Corp." - ], - [ - "WASHINGTON - A Pennsylvania
law requiring Internet service
providers (ISPs) to block Web
sites the state's prosecuting
attorneys deem to be child
pornography has been reversed
by a U.S. federal court, with
the judge ruling the law
violated free speech rights." - ], - [ - "The Microsoft Corp. chairman
receives four million e-mails
a day, but practically an
entire department at the
company he founded is
dedicated to ensuring that
nothing unwanted gets into his
inbox, the company #39;s chief
executive said Thursday." - ], - [ - "The 7100t has a mobile phone,
e-mail, instant messaging, Web
browsing and functions as an
organiser. The device looks
like a mobile phone and has
the features of the other
BlackBerry models, with a
large screen" - ], - [ - "President Vladimir Putin makes
a speech as he hosts Russia
#39;s Olympic athletes at a
Kremlin banquet in Moscow,
Thursday, Nov. 4, 2004." - ], - [ - "Apple Computer has unveiled
two new versions of its hugely
successful iPod: the iPod
Photo and the U2 iPod. Apple
also has expanded" - ], - [ - "Pakistan are closing in fast
on Sri Lanka #39;s first
innings total after impressing
with both ball and bat on the
second day of the opening Test
in Faisalabad." - ], - [ - "A state regulatory board
yesterday handed a five-year
suspension to a Lawrence
funeral director accused of
unprofessional conduct and
deceptive practices, including
one case where he refused to
complete funeral arrangements
for a client because she had
purchased a lower-priced
casket elsewhere." - ], - [ - "AP - This year's hurricanes
spread citrus canker to at
least 11,000 trees in
Charlotte County, one of the
largest outbreaks of the
fruit-damaging infection to
ever affect Florida's citrus
industry, state officials
said." - ], - [ - "AFP - Hundreds of Buddhists in
southern Russia marched
through snow to see and hear
the Dalai Lama as he continued
a long-awaited visit to the
country in spite of Chinese
protests." - ], - [ - "British officials were on
diplomatic tenterhooks as they
awaited the arrival on
Thursday of French President
Jacques Chirac for a two-day
state visit to Britain." - ], - [ - " SYDNEY (Reuters) - A group of
women on Pitcairn Island in
the South Pacific are standing
by their men, who face
underage sex charges, saying
having sex at age 12 is a
tradition dating back to 18th
century mutineers who settled
on the island." - ], - [ - "Two aircraft are flying out
from the UK on Sunday to
deliver vital aid and supplies
to Haiti, which has been
devastated by tropical storm
Jeanne." - ], - [ - "A scare triggered by a
vibrating sex toy shut down a
major Australian regional
airport for almost an hour
Monday, police said. The
vibrating object was
discovered Monday morning" - ], - [ - "IBM moved back into the iSCSI
(Internet SCSI) market Friday
with a new array priced at
US\\$3,000 and aimed at the
small and midsize business
market." - ], - [ - "Ron Artest has been hit with a
season long suspension,
unprecedented for the NBA
outside doping cases; Stephen
Jackson banned for 30 games;
Jermaine O #39;Neal for 25
games and Anthony Johnson for
five." - ], - [ - "The California Public
Utilities Commission on
Thursday upheld a \\$12.1
million fine against Cingular
Wireless, related to a two-
year investigation into the
cellular telephone company
#39;s business practices." - ], - [ - "Barclays, the British bank
that left South Africa in 1986
after apartheid protests, may
soon resume retail operations
in the nation." - ], - [ - "AP - An Indiana congressional
candidate abruptly walked off
the set of a debate because
she got stage fright." - ], - [ - "Italian-based Parmalat is
suing its former auditors --
Grant Thornton International
and Deloitte Touche Tohmatsu
-- for billions of dollars in
damages. Parmalat blames its
demise on the two companies
#39; mismanagement of its
finances." - ], - [ - "Reuters - Having reached out
to Kashmiris during a two-day
visit to the region, the prime
minister heads this weekend to
the country's volatile
northeast, where public anger
is high over alleged abuses by
Indian soldiers." - ], - [ - "SAN JUAN IXTAYOPAN, Mexico --
A pair of wooden crosses
outside the elementary school
are all that mark the macabre
site where, just weeks ago, an
angry mob captured two federal
police officers, beat them
unconscious, and set them on
fire." - ], - [ - "Three shots behind Grace Park
with five holes to go, six-
time LPGA player of the year
Sorenstam rallied with an
eagle, birdie and three pars
to win her fourth Samsung
World Championship by three
shots over Park on Sunday." - ], - [ - "update An alliance of
technology workers on Tuesday
accused conglomerate Honeywell
International of planning to
move thousands of jobs to low-
cost regions over the next
five years--a charge that
Honeywell denies." - ], - [ - "NSW Rugby CEO Fraser Neill
believes Waratah star Mat
Rogers has learned his lesson
after he was fined and ordered
to do community service
following his controversial
comments about the club rugby
competition." - ], - [ - "American Technology Research
analyst Shaw Wu has initiated
coverage of Apple Computer
(AAPL) with a #39;buy #39;
recommendation, and a 12-month
target of US\\$78 per share." - ], - [ - "washingtonpost.com - Cell
phone shoppers looking for new
deals and features didn't find
them yesterday, a day after
Sprint Corp. and Nextel
Communications Inc. announced
a merger that the phone
companies promised would shake
up the wireless business." - ], - [ - "ATHENS: China, the dominant
force in world diving for the
best part of 20 years, won six
out of eight Olympic titles in
Athens and prompted
speculation about a clean
sweep when they stage the
Games in Beijing in 2008." - ], - [ - " NEW YORK (Reuters) - Northrop
Grumman Corp. <A HREF=\"http
://www.investor.reuters.com/Fu
llQuote.aspx?ticker=NOC.N targ
et=/stocks/quickinfo/fullquote
\">NOC.N</A> reported
higher third-quarter earnings
on Wednesday and an 11
percent increase in sales on
strength in its mission
systems, integrated systems,
ships and space technology
businesses." - ], - [ - "JAPANESE GIANT Sharp has
pulled the plug on its Linux-
based PDAs in the United
States as no-one seems to want
them. The company said that it
will continue to sell them in
Japan where they sell like hot
cakes" - ], - [ - "DUBLIN : An Olympic Airlines
plane diverted to Ireland
following a bomb alert, the
second faced by the Greek
carrier in four days, resumed
its journey to New York after
no device was found on board,
airport officials said." - ], - [ - "New NavOne offers handy PDA
and Pocket PC connectivity,
but fails to impress on
everything else." - ], - [ - "TORONTO (CP) - Shares of
Iamgold fell more than 10 per
cent after its proposed merger
with Gold Fields Ltd. was
thrown into doubt Monday as
South Africa #39;s Harmony
Gold Mining Company Ltd." - ], - [ - "The Marvel deal calls for
Mforma to work with the comic
book giant to develop a
variety of mobile applications
based on Marvel content." - ], - [ - " LONDON (Reuters) - Oil prices
tumbled again on Monday to an
8-week low under \\$46 a
barrel, as growing fuel stocks
in the United States eased
fears of a winter supply
crunch." - ], - [ - " WASHINGTON (Reuters) - The
United States said on Friday
it is preparing a new U.N.
resolution on Darfur and that
Secretary of State Colin
Powell might address next week
whether the violence in
western Sudan constitutes
genocide." - ], - [ - "The Houston Astros won their
19th straight game at home and
are one game from winning
their first playoff series in
42 years." - ], - [ - "washingtonpost.com - The
Portable Media Center -- a
new, Microsoft-conceived
handheld device that presents
video and photos as well as
music -- would be a decent
idea if there weren't such a
thing as lampposts. Or street
signs. Or trees. Or other
cars." - ], - [ - "Alcoa Inc., one of the world
#39;s top producers of
aluminum, said Monday that it
received an unsolicited
quot;mini-tender quot; offer
from Toronto-based TRC Capital
Corp." - ], - [ - "The European Commission is to
warn Greece about publishing
false information about its
public finances." - ], - [ - "The Air Force Reserve #39;s
Hurricane Hunters, those
fearless crews who dive into
the eyewalls of hurricanes to
relay critical data on
tropical systems, were chased
from their base on the
Mississippi Gulf Coast by
Hurricane Ivan." - ], - [ - " LONDON (Reuters) - U.S.
shares were expected to open
lower on Wednesday after
crude oil pushed to a fresh
high overnight, while Web
search engine Google Inc.
dented sentiment as it
slashed the price range on its
initial public offering." - ], - [ - "The eighth-seeded American
fell to sixth-seeded Elena
Dementieva of Russia, 0-6 6-2
7-6 (7-5), on Friday - despite
being up a break on four
occasions in the third set." - ], - [ - "TUCSON, Arizona (Ticker) --
No. 20 Arizona State tries to
post its first three-game
winning streak over Pac-10
Conference rival Arizona in 26
years when they meet Friday." - ], - [ - "NAJAF, Iraq : Iraq #39;s top
Shiite Muslim clerics, back in
control of Najaf #39;s Imam
Ali shrine after a four-month
militia occupation, met amid
the ruins of the city as life
spluttered back to normality." - ], - [ - "Embargo or not, Fidel Castro's
socialist paradise has quietly
become a pharmaceutical
powerhouse. (They're still
working on the capitalism
thing.) By Douglas Starr from
Wired magazine." - ], - [ - "AP - Phillip Fulmer kept his
cool when starting center
Jason Respert drove off in the
coach's golf cart at practice." - ], - [ - "GOALS from Wayne Rooney and
Ruud van Nistelrooy gave
Manchester United the win at
Newcastle. Alan Shearer
briefly levelled matters for
the Magpies but United managed
to scrape through." - ], - [ - "The telemarketer at the other
end of Orlando Castelblanco
#39;s line promised to reduce
the consumer #39;s credit card
debt by at least \\$2,500 and
get his 20 percent -- and
growing -- interest rates down
to single digits." - ], - [ - " NEW YORK (Reuters) - Blue-
chip stocks fell slightly on
Monday after No. 1 retailer
Wal-Mart Stores Inc. <A HRE
F=\"http://www.investor.reuters
.com/FullQuote.aspx?ticker=WMT
.N target=/stocks/quickinfo/fu
llquote\">WMT.N</A>
reported lower-than-expected
Thanksgiving sales, while
technology shares were lifted
by a rally in Apple Computer
Inc. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=AAPL.O target=/sto
cks/quickinfo/fullquote\">AA
PL.O</A>." - ], - [ - "Reuters - House of
Representatives
Majority\\Leader Tom DeLay,
admonished twice in six days
by his chamber's\\ethics
committee, withstood calls on
Thursday by rival\\Democrats
and citizen groups that he
step aside." - ], - [ - "WPP Group Inc., the world
#39;s second- largest
marketing and advertising
company, said it won the
bidding for Grey Global Group
Inc." - ], - [ - "AFP - Australia has accounted
for all its nationals known to
be working in Iraq following a
claim by a radical Islamic
group to have kidnapped two
Australians, Foreign Affairs
Minister Alexander Downer
said." - ], - [ - "A new worm can spy on users by
hijacking their Web cameras, a
security firm warned Monday.
The Rbot.gr worm -- the latest
in a long line of similar
worms; one security firm
estimates that more than 4,000
variations" - ], - [ - " NEW YORK (Reuters) - Stocks
were slightly lower on
Tuesday, as concerns about
higher oil prices cutting into
corporate profits and
consumer demand weighed on
sentiment, while retail sales
posted a larger-than-expected
decline in August." - ], - [ - "The separation of PalmOne and
PalmSource will be complete
with Eric Benhamou's
resignation as the latter's
chairman." - ], - [ - "AP - Two-time U.S. Open
doubles champion Max Mirnyi
will lead the Belarus team
that faces the United States
in the Davis Cup semifinals in
Charleston later this month." - ], - [ - "The hurricane appeared to be
strengthening and forecasters
warned of an increased threat
of serious flooding and wind
damage." - ], - [ - "AP - New York Jets safety Erik
Coleman got his souvenir
football from the equipment
manager and held it tightly." - ], - [ - " SINGAPORE (Reuters) - Oil
prices climbed above \\$42 a
barrel on Wednesday, rising
for the third day in a row as
the heavy consuming U.S.
Northeast feels the first
chills of winter." - ], - [ - "\\\\\"(CNN) -- A longtime
associate of al Qaeda leader
Osama bin Laden surrendered
to\\Saudi Arabian officials
Tuesday, a Saudi Interior
Ministry official said.\"\\\\\"But
it is unclear what role, if
any, Khaled al-Harbi may have
had in any terror\\attacks
because no public charges have
been filed against him.\"\\\\\"The
Saudi government -- in a
statement released by its
embassy in Washington
--\\called al-Harbi's surrender
\"the latest direct result\" of
its limited, one-month\\offer
of leniency to terror
suspects.\"\\\\This is great! I
hope this really starts to pay
off. Creative solutions
to\\terrorism that don't
involve violence. \\\\How
refreshing! \\\\Are you paying
attention Bush
administration?\\\\" - ], - [ - " NEW YORK (Reuters) - Merck
Co Inc. <A HREF=\"http://www
.investor.reuters.com/FullQuot
e.aspx?ticker=MRK.N target=/st
ocks/quickinfo/fullquote\">M
RK.N</A> on Thursday
pulled its arthritis drug
Vioxx off the market after a
study showed it doubled the
risk of heart attack and
stroke, a move that sent its
shares plunging and erased
\\$25 billion from its market
value." - ], - [ - "AT T Corp. is cutting 7,400
more jobs and slashing the
book value of its assets by
\\$11.4 billion, drastic moves
prompted by the company's plan
to retreat from the
traditional consumer telephone
business following a lost
court battle." - ], - [ - "The government on Wednesday
defended its decision to
radically revise the country
#39;s deficit figures, ahead
of a European Commission
meeting to consider possible
disciplinary action against
Greece for submitting faulty
figures." - ], - [ - "Ivan Hlinka coached the Czech
Republic to the hockey gold
medal at the 1998 Nagano
Olympics and became the coach
of the Pittsburgh Penguins two
years later." - ], - [ - "Four homeless men were
bludgeoned to death and six
were in critical condition on
Friday following early morning
attacks by unknown assailants
in downtown streets of Sao
Paulo, a police spokesman
said." - ], - [ - "OPEC ministers yesterday
agreed to increase their
ceiling for oil production to
help bring down stubbornly
high prices in a decision that
traders and analysts dismissed
as symbolic because the cartel
already is pumping more than
its new target." - ], - [ - "Williams-Sonoma Inc. said
second- quarter profit rose 55
percent, boosted by the
addition of Pottery Barn
stores and sale of outdoor
furniture." - ], - [ - "Celerons form the basis of
Intel #39;s entry-level
platform which includes
integrated/value motherboards
as well. The Celeron 335D is
the fastest - until the
Celeron 340D - 2.93GHz -
becomes mainstream - of the" - ], - [ - "The entertainment industry
asks the Supreme Court to
reverse the Grokster decision,
which held that peer-to-peer
networks are not liable for
copyright abuses of their
users. By Michael Grebb." - ], - [ - " HONG KONG/SAN FRANCISCO
(Reuters) - China's largest
personal computer maker,
Lenovo Group Ltd., said on
Tuesday it was in acquisition
talks with a major technology
company, which a source
familiar with the situation
said was IBM." - ], - [ - "Phone companies are not doing
enough to warn customers about
internet \"rogue-dialling\"
scams, watchdog Icstis warns." - ], - [ - "Reuters - Oil prices stayed
close to #36;49 a\\barrel on
Thursday, supported by a
forecast for an early
cold\\snap in the United States
that could put a strain on a
thin\\supply cushion of winter
heating fuel." - ], - [ - "AUBURN - Ah, easy street. No
game this week. Light
practices. And now Auburn is
being touted as the No. 3 team
in the Bowl Championship
Series standings." - ], - [ - "Portsmouth #39;s Harry
Redknapp has been named as the
Barclays manager of the month
for October. Redknapp #39;s
side were unbeaten during the
month and maintained an
impressive climb to ninth in
the Premiership." - ], - [ - "India's main opposition party
takes action against senior
party member Uma Bharti after
a public row." - ], - [ - "Hewlett-Packard will shell out
\\$16.1 billion for chips in
2005, but Dell's wallet is
wide open, too." - ], - [ - "WASHINGTON (CBS.MW) --
President Bush announced
Monday that Kellogg chief
executive Carlos Gutierrez
would replace Don Evans as
Commerce secretary, naming the
first of many expected changes
to his economic team." - ], - [ - "Iron Mountain moved further
into the backup and recovery
space Tuesday with the
acquisition of Connected Corp.
for \\$117 million. Connected
backs up desktop data for more
than 600 corporations, with
more than" - ], - [ - "Three directors of Manchester
United have been ousted from
the board after US tycoon
Malcolm Glazer, who is
attempting to buy the club,
voted against their re-
election." - ], - [ - "The Russian military yesterday
extended its offer of a \\$10
million reward for information
leading to the capture of two
separatist leaders who, the
Kremlin claims, were behind
the Beslan massacre." - ], - [ - "AP - Manny Ramirez singled and
scored before leaving with a
bruised knee, and the
streaking Boston Red Sox beat
the Detroit Tigers 5-3 Friday
night for their 10th victory
in 11 games." - ], - [ - "A remote attacker could take
complete control over
computers running many
versions of Microsoft software
by inserting malicious code in
a JPEG image that executes
through an unchecked buffer" - ], - [ - "Montgomery County (website -
news) is a big step closer to
shopping for prescription
drugs north of the border. On
a 7-2 vote, the County Council
is approving a plan that would
give county" - ], - [ - "Israels Shin Bet security
service has tightened
protection of the prime
minister, MPs and parliament
ahead of next weeks crucial
vote on a Gaza withdrawal." - ], - [ - "The news comes fast and
furious. Pedro Martinez goes
to Tampa to visit George
Steinbrenner. Theo Epstein and
John Henry go to Florida for
their turn with Pedro. Carl
Pavano comes to Boston to
visit Curt Schilling. Jason
Varitek says he's not a goner.
Derek Lowe is a goner, but he
says he wishes it could be
different. Orlando Cabrera ..." - ], - [ - "The disclosure this week that
a Singapore-listed company
controlled by a Chinese state-
owned enterprise lost \\$550
million in derivatives
transactions" - ], - [ - "Reuters - Iraq's interim
defense minister
accused\\neighbors Iran and
Syria on Wednesday of aiding
al Qaeda\\Islamist Abu Musab
al-Zarqawi and former agents
of Saddam\\Hussein to promote a
\"terrorist\" insurgency in
Iraq." - ], - [ - "AP - The Senate race in
Kentucky stayed at fever pitch
on Thursday as Democratic
challenger Daniel Mongiardo
stressed his opposition to gay
marriage while accusing
Republican incumbent Jim
Bunning of fueling personal
attacks that seemed to suggest
Mongiardo is gay." - ], - [ - "The lawsuit claims the
companies use a patented
Honeywell technology for
brightening images and
reducing interference on
displays." - ], - [ - "The co-president of Oracle
testified that her company was
serious about its takeover
offer for PeopleSoft and was
not trying to scare off its
customers." - ], - [ - "VANCOUVER - A Vancouver-based
firm won #39;t sell 1.2
million doses of influenza
vaccine to the United States
after all, announcing Tuesday
that it will sell the doses
within Canada instead." - ], - [ - "An extremely rare Hawaiian
bird dies in captivity,
possibly marking the
extinction of its entire
species only 31 years after it
was first discovered." - ], - [ - "Does Geico's trademark lawsuit
against Google have merit? How
will the case be argued?
What's the likely outcome of
the trial? A mock court of
trademark experts weighs in
with their verdict." - ], - [ - "Treo 650 boasts a high-res
display, an improved keyboard
and camera, a removable
battery, and more. PalmOne
this week is announcing the
Treo 650, a hybrid PDA/cell-
phone device that addresses
many of the shortcomings" - ], - [ - "FRED Hale Sr, documented as
the worlds oldest man, has
died at the age of 113. Hale
died in his sleep on Friday at
a hospital in Syracuse, New
York, while trying to recover
from a bout of pneumonia, his
grandson, Fred Hale III said." - ], - [ - "The Oakland Raiders have
traded Jerry Rice to the
Seattle Seahawks in a move
expected to grant the most
prolific receiver in National
Football League history his
wish to get more playing time." - ], - [ - "consortium led by the Sony
Corporation of America reached
a tentative agreement today to
buy Metro-Goldwyn-Mayer, the
Hollywood studio famous for
James Bond and the Pink
Panther, for" - ], - [ - "International Business
Machines Corp.'s possible exit
from the personal computer
business would be the latest
move in what amounts to a long
goodbye from a field it
pioneered and revolutionized." - ], - [ - "Leipzig Game Convention in
Germany, the stage for price-
slash revelations. Sony has
announced that it #39;s
slashing the cost of PS2 in
the UK and Europe to 104.99
GBP." - ], - [ - "AP - Florida coach Ron Zook
was fired Monday but will be
allowed to finish the season,
athletic director Jeremy Foley
told The Gainesville Sun." - ], - [ - "The country-cooking restaurant
chain has agreed to pay \\$8.7
million over allegations that
it segregated black customers,
subjected them to racial slurs
and gave black workers
inferior jobs." - ], - [ - "Troy Brown has had to make a
lot of adjustments while
playing both sides of the
football. quot;You always
want to score when you get the
ball -- offense or defense" - ], - [ - " PORT LOUIS, Aug. 17
(Xinhuanet) -- Southern
African countries Tuesday
pledged better trade and
investment relations with
China as well as India in the
final communique released at
the end of their two-day
summit." - ], - [ - "In yet another devastating
body blow to the company,
Intel (Nasdaq: INTC) announced
it would be canceling its
4-GHz Pentium chip. The
semiconductor bellwether said
it was switching" - ], - [ - "Jenson Button will tomorrow
discover whether he is allowed
to quit BAR and move to
Williams for 2005. The
Englishman has signed
contracts with both teams but
prefers a switch to Williams,
where he began his Formula One
career in 2000." - ], - [ - "Seagate #39;s native SATA
interface technology with
Native Command Queuing (NCQ)
allows the Barracuda 7200.8 to
match the performance of
10,000-rpm SATA drives without
sacrificing capacity" - ], - [ - "ARSENAL boss Arsene Wenger
last night suffered a
Champions League setback as
Brazilian midfielder Gilberto
Silva (above) was left facing
a long-term injury absence." - ], - [ - "BAGHDAD - A militant group has
released a video saying it
kidnapped a missing journalist
in Iraq and would kill him
unless US forces left Najaf
within 48 hours." - ], - [ - "18 August 2004 -- There has
been renewed fighting in the
Iraqi city of Al-Najaf between
US and Iraqi troops and Shi
#39;a militiamen loyal to
radical cleric Muqtada al-
Sadr." - ], - [ - "You #39;re probably already
familiar with one of the most
common questions we hear at
iPodlounge: quot;how can I
load my iPod up with free
music?" - ], - [ - " SINGAPORE (Reuters) -
Investors bought shares in
Asian exporters and
electronics firms such as
Fujitsu Ltd. on Tuesday,
buoyed by a favorable outlook
from U.S. technology
bellwethers and a slide in oil
prices." - ], - [ - "Ace Ltd. will stop paying
brokers for steering business
its way, becoming the third
company to make concessions in
the five days since New York
Attorney General Eliot Spitzer
unveiled a probe of the
insurance industry." - ], - [ - "Vice chairman of the office of
the chief executive officer in
Novell Inc, Chris Stone is
leaving the company to pursue
other opportunities in life." - ], - [ - "Wm. Wrigley Jr. Co., the world
#39;s largest maker of chewing
gum, agreed to buy candy
businesses including Altoids
mints and Life Savers from
Kraft Foods Inc." - ], - [ - "American improves to 3-1 on
the season with a hard-fought
overtime win, 74-63, against
Loyala at Bender Arena on
Friday night." - ], - [ - "Australia tighten their grip
on the third Test and the
series after dominating India
on day two in Nagpur." - ], - [ - " #39;Reaching a preliminary
pilot agreement is the single
most important hurdle they
have to clear, but certainly
not the only one." - ], - [ - "Bee Staff Writers. SAN
FRANCISCO - As Eric Johnson
drove to the stadium Sunday
morning, his bruised ribs were
so sore, he wasn #39;t sure he
#39;d be able to suit up for
the game." - ], - [ - "The New England Patriots are
so single-minded in pursuing
their third Super Bowl triumph
in four years that they almost
have no room for any other
history." - ], - [ - "TORONTO (CP) - Canada #39;s
big banks are increasing
mortgage rates following a
decision by the Bank of Canada
to raise its overnight rate by
one-quarter of a percentage
point to 2.25 per cent." - ], - [ - " SEOUL (Reuters) - North
Korea's two-year-old nuclear
crisis has taxed the world's
patience, the chief United
Nations nuclear regulator
said on Wednesday, urging
communist Pyongyang to return
to its disarmament treaty
obligations." - ], - [ - "washingtonpost.com - Microsoft
is going to Tinseltown today
to announce plans for its
revamped Windows XP Media
Center, part of an aggressive
push to get ahead in the
digital entertainment race." - ], - [ - "GROZNY, Russia - The Russian
government's choice for
president of war-ravaged
Chechnya appeared to be the
victor Sunday in an election
tainted by charges of fraud
and shadowed by last week's
terrorist destruction of two
airliners. Little more than
two hours after polls closed,
acting Chechen president
Sergei Abramov said
preliminary results showed
Maj..." - ], - [ - "Because, while the Eagles are
certain to stumble at some
point during the regular
season, it seems inconceivable
that they will falter against
a team with as many offensive
problems as Baltimore has
right now." - ], - [ - "AP - J.J. Arrington ran for 84
of his 121 yards in the second
half and Aaron Rodgers shook
off a slow start to throw two
touchdown passes to help No. 5
California beat Washington
42-12 on Saturday." - ], - [ - "BAGHDAD, Sept 5 (AFP) - Izzat
Ibrahim al-Duri, Saddam
Hussein #39;s deputy whose
capture was announced Sunday,
is 62 and riddled with cancer,
but was public enemy number
two in Iraq for the world
#39;s most powerful military." - ], - [ - "AP - An explosion targeted the
Baghdad governor's convoy as
he was traveling through the
capital Tuesday, killing two
people but leaving him
uninjured, the Interior
Ministry said." - ], - [ - "GENEVA: Rescuers have found
the bodies of five Swiss
firemen who died after the
ceiling of an underground car
park collapsed during a fire,
a police spokesman said last
night." - ], - [ - "John Kerry has held 10 \"front
porch visit\" events an actual
front porch is optional where
perhaps 100 people ask
questions in a low-key
campaigning style." - ], - [ - "AP - Most of the turkeys
gracing the nation's dinner
tables Thursday have been
selectively bred for their
white meat for so many
generations that simply
walking can be a problem for
many of the big-breasted birds
and sex is no longer possible." - ], - [ - "OTTAWA (CP) - The economy
created another 43,000 jobs
last month, pushing the
unemployment rate down to 7.1
per cent from 7.2 per cent in
August, Statistics Canada said
Friday." - ], - [ - "The right-win opposition
Conservative Party and Liberal
Center Union won 43 seats in
the 141-member Lithuanian
parliament, after more than 99
percent of the votes were
counted" - ], - [ - " TOKYO (Reuters) - Japan's
Nikkei average rose 0.39
percent by midsession on
Friday, bolstered by solid
gains in stocks dependent on
domestic business such as Kao
Corp. <A HREF=\"http://www.i
nvestor.reuters.com/FullQuote.
aspx?ticker=4452.T target=/sto
cks/quickinfo/fullquote\">44
52.T</A>." - ], - [ - " FRANKFURT (Reuters) -
DaimlerChrysler and General
Motors will jointly develop
new hybrid motors to compete
against Japanese rivals on
the fuel-saving technology
that reduces harmful
emissions, the companies said
on Monday." - ], - [ - " SEATTLE (Reuters) - Microsoft
Corp. <A HREF=\"http://www.r
euters.co.uk/financeQuoteLooku
p.jhtml?ticker=MSFT.O
qtype=sym infotype=info
qcat=news\">MSFT.O</A>
is making a renewed push this
week to get its software into
living rooms with the launch
of a new version of its
Windows XP Media Center, a
personal computer designed for
viewing movies, listening to
music and scrolling through
digital pictures." - ], - [ - "KHARTOUM, Aug 18 (Reuters) -
The United Nations said on
Wednesday it was very
concerned by Sudan #39;s lack
of practical progress in
bringing security to Darfur,
where more than a million
people have fled their homes
for fear of militia ..." - ], - [ - "SHANGHAI, China The Houston
Rockets have arrived in
Shanghai with hometown
favorite Yao Ming declaring
himself quot;here on
business." - ], - [ - "Charleston, SC (Sports
Network) - Andy Roddick and
Mardy Fish will play singles
for the United States in this
weekend #39;s Davis Cup
semifinal matchup against
Belarus." - ], - [ - "AFP - With less than two
months until the November 2
election, President George W.
Bush is working to shore up
support among his staunchest
supporters even as he courts
undecided voters." - ], - [ - "The bass should be in your
face. That's what Matt Kelly,
of Boston's popular punk rock
band Dropkick Murphys, thinks
is the mark of a great stereo
system. And he should know.
Kelly, 29, is the drummer for
the band that likes to think
of itself as a bit of an Irish
lucky charm for the Red Sox." - ], - [ - "Chile's government says it
will build a prison for
officers convicted of human
rights abuses in the Pinochet
era." - ], - [ - "Slumping corporate spending
and exports caused the economy
to slow to a crawl in the
July-September period, with
real gross domestic product
expanding just 0.1 percent
from the previous quarter,
Cabinet Office data showed
Friday." - ], - [ - "US President George W. Bush
signed into law a bill
replacing an export tax
subsidy that violated
international trade rules with
a \\$145 billion package of new
corporate tax cuts and a
buyout for tobacco farmers." - ], - [ - "The Nikkei average was up 0.37
percent in mid-morning trade
on Thursday as a recovery in
the dollar helped auto makers
among other exporters, but
trade was slow as investors
waited for important Japanese
economic data." - ], - [ - "Jennifer Canada knew she was
entering a boy's club when she
enrolled in Southern Methodist
University's Guildhall school
of video-game making." - ], - [ - "RICKY PONTING believes the
game #39;s watchers have
fallen for the quot;myth
quot; that New Zealand know
how to rattle Australia." - ], - [ - "MILWAUKEE (SportsTicker) -
Barry Bonds tries to go where
just two players have gone
before when the San Francisco
Giants visit the Milwaukee
Brewers on Tuesday." - ], - [ - "Palestinian leader Mahmoud
Abbas reiterated calls for his
people to drop their weapons
in the struggle for a state. a
clear change of strategy for
peace with Israel after Yasser
Arafat #39;s death." - ], - [ - "The new software is designed
to simplify the process of
knitting together back-office
business applications." - ], - [ - "SYDNEY (Dow Jones)--Colorado
Group Ltd. (CDO.AU), an
Australian footwear and
clothing retailer, said Monday
it expects net profit for the
fiscal year ending Jan. 29 to
be over 30 higher than that of
a year earlier." - ], - [ - "NEW YORK - What are the odds
that a tiny nation like
Antigua and Barbuda could take
on the United States in an
international dispute and win?" - ], - [ - "AP - With Tom Brady as their
quarterback and a stingy,
opportunistic defense, it's
difficult to imagine when the
New England Patriots might
lose again. Brady and
defensive end Richard Seymour
combined to secure the
Patriots' record-tying 18th
straight victory, 31-17 over
the Buffalo Bills on Sunday." - ], - [ - "FRANKFURT, GERMANY -- The
German subsidiaries of
Hewlett-Packard Co. (HP) and
Novell Inc. are teaming to
offer Linux-based products to
the country's huge public
sector." - ], - [ - "SBC Communications expects to
cut 10,000 or more jobs by the
end of next year through
layoffs and attrition. That
#39;s about six percent of the
San Antonio-based company
#39;s work force." - ], - [ - " BAGHDAD (Reuters) - Iraq's
U.S.-backed government said on
Tuesday that \"major neglect\"
by its American-led military
allies led to a massacre of 49
army recruits at the weekend." - ], - [ - "SiliconValley.com - \"I'm
back,\" declared Apple
Computer's Steve Jobs on
Thursday morning in his first
public appearance before
reporters since cancer surgery
in late July." - ], - [ - "BEIJING -- Police have
detained a man accused of
slashing as many as nine boys
to death as they slept in
their high school dormitory in
central China, state media
reported today." - ], - [ - "Health India: London, Nov 4 :
Cosmetic face cream used by
fashionable Roman women was
discovered at an ongoing
archaeological dig in London,
in a metal container, complete
with the lid and contents." - ], - [ - "Israeli Prime Minister Ariel
Sharon #39;s Likud party
agreed on Thursday to a
possible alliance with
opposition Labour in a vote
that averted a snap election
and strengthened his Gaza
withdrawal plan." - ], - [ - "Another United Airlines union
is seeking to oust senior
management at the troubled
airline, saying its strategies
are reckless and incompetent." - ], - [ - "Approaching Hurricane Ivan has
led to postponement of the
game Thursday night between
10th-ranked California and
Southern Mississippi in
Hattiesburg, Cal #39;s
athletic director said Monday." - ], - [ - "Global oil prices boomed on
Wednesday, spreading fear that
energy prices will restrain
economic activity, as traders
worried about a heating oil
supply crunch in the American
winter." - ], - [ - "Custom-designed imported
furniture was once an
exclusive realm. Now, it's the
economical alternative for
commercial developers and
designers needing everything
from seats to beds to desks
for their projects." - ], - [ - "SAN DIEGO (Ticker) - The San
Diego Padres lacked speed and
an experienced bench last
season, things veteran
infielder Eric Young is
capable of providing." - ], - [ - "This is an eye chart,
reprinted as a public service
to the New York Mets so they
may see from what they suffer:
myopia. Has ever a baseball
franchise been so shortsighted
for so long?" - ], - [ - "Short-term interest rate
futures struggled on Thursday
after a government report
showing US core inflation for
August below market
expectations failed to alter
views on Federal Reserve rate
policy." - ], - [ - "MacCentral - Microsoft's
Macintosh Business Unit on
Tuesday issued a patch for
Virtual PC 7 that fixes a
problem that occurred when
running the software on Power
Mac G5 computers with more
than 2GB of RAM installed.
Previously, Virtual PC 7 would
not run on those computers,
causing a fatal error that
crashed the application.
Microsoft also noted that
Virtual PC 7.0.1 also offers
stability improvements,
although it wasn't more
specific than that -- some
users have reported problems
with using USB devices and
other issues." - ], - [ - "Samsung is now the world #39;s
second-largest mobile phone
maker, behind Nokia. According
to market watcher Gartner, the
South Korean company has
finally knocked Motorola into
third place." - ], - [ - "Don't bother buying Star Wars:
Battlefront if you're looking
for a first-class shooter --
there are far better games out
there. But if you're a Star
Wars freak and need a fix,
this title will suffice. Lore
Sjberg reviews Battlefront." - ], - [ - "While the American forces are
preparing to launch a large-
scale attack against Falluja
and al-Ramadi, one of the
chieftains of Falluja said
that contacts are still
continuous yesterday between
members representing the city
#39;s delegation and the
interim" - ], - [ - "UN Security Council
ambassadors were still
quibbling over how best to
pressure Sudan and rebels to
end two different wars in the
country even as they left for
Kenya on Tuesday for a meeting
on the crisis." - ], - [ - "HENDALA, Sri Lanka -- Day
after day, locked in a cement
room somewhere in Iraq, the
hooded men beat him. They told
him he would be beheaded.
''Ameriqi! quot; they shouted,
even though he comes from this
poor Sri Lankan fishing
village." - ], - [ - "THE kidnappers of British aid
worker Margaret Hassan last
night threatened to turn her
over to the group which
beheaded Ken Bigley if the
British Government refuses to
pull its troops out of Iraq." - ], - [ - " TOKYO (Reuters) - Tokyo
stocks climbed to a two week
high on Friday after Tokyo
Electron Ltd. and other chip-
related stocks were boosted
by a bullish revenue outlook
from industry leader Intel
Corp." - ], - [ - "More than by brain size or
tool-making ability, the human
species was set apart from its
ancestors by the ability to
jog mile after lung-stabbing
mile with greater endurance
than any other primate,
according to research
published today in the journal" - ], - [ - " LONDON (Reuters) - A medical
product used to treat both
male hair loss and prostate
problems has been added to the
list of banned drugs for
athletes." - ], - [ - "AP - Curtis Martin and Jerome
Bettis have the opportunity to
go over 13,000 career yards
rushing in the same game when
Bettis and the Pittsburgh
Steelers play Martin and the
New York Jets in a big AFC
matchup Sunday." - ], - [ - "<p></p><p>
BOGOTA, Colombia (Reuters) -
Ten Colombian police
officerswere killed on Tuesday
in an ambush by the National
LiberationArmy, or ELN, in the
worst attack by the Marxist
group inyears, authorities
told Reuters.</p>" - ], - [ - "Woodland Hills-based Brilliant
Digital Entertainment and its
subsidiary Altnet announced
yesterday that they have filed
a patent infringement suit
against the Recording Industry
Association of America (RIAA)." - ], - [ - "AFP - US President George W.
Bush called his Philippines
counterpart Gloria Arroyo and
said their countries should
keep strong ties, a spokesman
said after a spat over
Arroyo's handling of an Iraq
kidnapping." - ], - [ - "A scathing judgment from the
UK #39;s highest court
condemning the UK government
#39;s indefinite detention of
foreign terror suspects as a
threat to the life of the
nation, left anti-terrorist
laws in the UK in tatters on
Thursday." - ], - [ - "Sony Ericsson and Cingular
provide Z500a phones and
service for military families
to stay connected on today
#39;s quot;Dr. Phil Show
quot;." - ], - [ - "Reuters - Australia's
conservative Prime
Minister\\John Howard, handed
the most powerful mandate in a
generation,\\got down to work
on Monday with reform of
telecommunications,\\labor and
media laws high on his agenda." - ], - [ - " TOKYO (Reuters) - Typhoon
Megi killed one person as it
slammed ashore in northern
Japan on Friday, bringing the
death toll to at least 13 and
cutting power to thousands of
homes before heading out into
the Pacific." - ], - [ - "Cairo: Egyptian President
Hosni Mubarak held telephone
talks with Palestinian leader
Yasser Arafat about Israel
#39;s plan to pull troops and
the 8,000 Jewish settlers out
of the Gaza Strip next year,
the Egyptian news agency Mena
said." - ], - [ - " NEW YORK (Reuters) - Adobe
Systems Inc. <A HREF=\"http:
//www.investor.reuters.com/Ful
lQuote.aspx?ticker=ADBE.O targ
et=/stocks/quickinfo/fullquote
\">ADBE.O</A> on
Monday reported a sharp rise
in quarterly profit, driven by
robust demand for its
Photoshop and document-sharing
software." - ], - [ - "Dow Jones amp; Co., publisher
of The Wall Street Journal,
has agreed to buy online
financial news provider
MarketWatch Inc. for about
\\$463 million in a bid to
boost its revenue from the
fast-growing Internet
advertising market." - ], - [ - "The first American military
intelligence soldier to be
court-martialed over the Abu
Ghraib abuse scandal was
sentenced Saturday to eight
months in jail, a reduction in
rank and a bad-conduct
discharge." - ], - [ - " TOKYO (Reuters) - Japan will
seek an explanation at weekend
talks with North Korea on
activity indicating Pyongyang
may be preparing a missile
test, although Tokyo does not
think a launch is imminent,
Japan's top government
spokesman said." - ], - [ - "Michigan Stadium was mostly
filled with empty seats. The
only cheers were coming from
near one end zone -he Iowa
section. By Carlos Osorio, AP." - ], - [ - "The International Rugby Board
today confirmed that three
countries have expressed an
interest in hosting the 2011
World Cup. New Zealand, South
Africa and Japan are leading
the race to host rugby union
#39;s global spectacular in
seven years #39; time." - ], - [ - "Officials at EarthLink #39;s
(Quote, Chart) R amp;D
facility have quietly released
a proof-of-concept file-
sharing application based on
the Session Initiated Protocol
(define)." - ], - [ - "Low-fare airline ATA has
announced plans to lay off
hundreds of employees and to
drop most of its flights out
of Midway Airport in Chicago." - ], - [ - " BEIJING (Reuters) - Iran will
never be prepared to
dismantle its nuclear program
entirely but remains committed
to the non-proliferation
treaty (NPT), its chief
delegate to the International
Atomic Energy Agency said on
Wednesday." - ], - [ - " WASHINGTON (Reuters) -
Germany's Bayer AG <A HREF=
\"http://www.investor.reuters.c
om/FullQuote.aspx?ticker=BAYG.
DE target=/stocks/quickinfo/fu
llquote\">BAYG.DE</A>
has agreed to plead guilty
and pay a \\$4.7 million fine
for taking part in a
conspiracy to fix the prices
of synthetic rubber, the U.S.
Justice Department said on
Wednesday." - ], - [ - "MIAMI (Ticker) -- In its first
season in the Atlantic Coast
Conference, No. 11 Virginia
Tech is headed to the BCS.
Bryan Randall threw two
touchdown passes and the
Virginia Tech defense came up
big all day as the Hokies
knocked off No." - ], - [ - "(CP) - Somehow, in the span of
half an hour, the Detroit
Tigers #39; pitching went from
brutal to brilliant. Shortly
after being on the wrong end
of several records in a 26-5
thrashing from to the Kansas
City" - ], - [ - "<p></p><p>
SANTIAGO, Chile (Reuters) -
President Bush on
Saturdayreached into a throng
of squabbling bodyguards and
pulled aSecret Service agent
away from Chilean security
officers afterthey stopped the
U.S. agent from accompanying
the president ata
dinner.</p>" - ], - [ - " quot;It #39;s your mail,
quot; the Google Web site
said. quot;You should be able
to choose how and where you
read it. You can even switch
to other e-mail services
without having to worry about
losing access to your
messages." - ], - [ - "The US oil giant got a good
price, Russia #39;s No. 1 oil
company acquired a savvy
partner, and Putin polished
Russia #39;s image." - ], - [ - "With the Huskies reeling at
0-4 - the only member of a
Bowl Championship Series
conference left without a win
-an Jose State suddenly looms
as the only team left on the
schedule that UW will be
favored to beat." - ], - [ - "Darryl Sutter, who coached the
Calgary Flames to the Stanley
Cup finals last season, had an
emergency appendectomy and was
recovering Friday." - ], - [ - "The maker of Hostess Twinkies,
a cake bar and a piece of
Americana children have
snacked on for almost 75
years, yesterday raised
concerns about the company
#39;s ability to stay in
business." - ], - [ - "Iranian deputy foreign
minister Gholamali Khoshrou
denied Tuesday that his
country #39;s top leaders were
at odds over whether nuclear
weapons were un-Islamic,
insisting that it will
quot;never quot; make the
bomb." - ], - [ - " quot;Israel mercenaries
assisting the Ivory Coast army
operated unmanned aircraft
that aided aerial bombings of
a French base in the country,
quot; claimed" - ], - [ - "Athens, Greece (Sports
Network) - For the second
straight day a Briton captured
gold at the Olympic Velodrome.
Bradley Wiggins won the men
#39;s individual 4,000-meter
pursuit Saturday, one day
after teammate" - ], - [ - "AFP - SAP, the world's leading
maker of business software,
may need an extra year to
achieve its medium-term profit
target of an operating margin
of 30 percent, its chief
financial officer said." - ], - [ - "Tenet Healthcare Corp., the
second- largest US hospital
chain, said fourth-quarter
charges may exceed \\$1 billion
and its loss from continuing
operations will widen from the
third quarter #39;s because of
increased bad debt." - ], - [ - "AFP - The airline Swiss said
it had managed to cut its
first-half net loss by about
90 percent but warned that
spiralling fuel costs were
hampering a turnaround despite
increasing passenger travel." - ], - [ - "Vulnerable youngsters expelled
from schools in England are
being let down by the system,
say inspectors." - ], - [ - "Yasser Arafat was undergoing
tests for possible leukaemia
at a military hospital outside
Paris last night after being
airlifted from his Ramallah
headquarters to an anxious
farewell from Palestinian
well-wishers." - ], - [ - "The world #39;s only captive
great white shark made history
this week when she ate several
salmon fillets, marking the
first time that a white shark
in captivity" - ], - [ - "Nepal #39;s main opposition
party urged the government on
Monday to call a unilateral
ceasefire with Maoist rebels
and seek peace talks to end a
road blockade that has cut the
capital off from the rest of
the country." - ], - [ - "Communications aggregator
iPass said Monday that it is
adding in-flight Internet
access to its access
portfolio. Specifically, iPass
said it will add access
offered by Connexion by Boeing
to its list of access
providers." - ], - [ - "The London-based brokerage
Collins Stewart Tullett placed
55m of new shares yesterday to
help fund the 69.5m purchase
of the money and futures
broker Prebon." - ], - [ - "BOSTON - The New York Yankees
and Boston were tied 4-4 after
13 innings Monday night with
the Red Sox trying to stay
alive in the AL championship
series. Boston tied the
game with two runs in the
eighth inning on David Ortiz's
solo homer, a walk to Kevin
Millar, a single by Trot Nixon
and a sacrifice fly by Jason
Varitek..." - ], - [ - "A Steffen Iversen penalty was
sufficient to secure the
points for Norway at Hampden
on Saturday. James McFadden
was ordered off after 53
minutes for deliberate
handball as he punched Claus
Lundekvam #39;s header off the
line." - ], - [ - "Reuters - The country may be
more\\or less evenly divided
along partisan lines when it
comes to\\the presidential
race, but the Republican Party
prevailed in\\the Nielsen
polling of this summer's
nominating conventions." - ], - [ - " PARIS (Reuters) - European
equities flirted with 5-month
peaks as hopes that economic
growth was sustainable and a
small dip in oil prices
helped lure investors back to
recent underperformers such
as technology and insurance
stocks." - ], - [ - " NEW YORK (Reuters) - U.S.
stocks looked to open higher
on Friday, as the fourth
quarter begins on Wall Street
with oil prices holding below
\\$50 a barrel." - ], - [ - "LONDON : World oil prices
stormed above 54 US dollars
for the first time Tuesday as
strikes in Nigeria and Norway
raised worries about possible
supply shortages during the
northern hemisphere winter." - ], - [ - "AP - Ailing St. Louis reliever
Steve Kline was unavailable
for Game 3 of the NL
championship series on
Saturday, but Cardinals
manager Tony LaRussa hopes the
left-hander will pitch later
this postseason." - ], - [ - "Company launches free test
version of service that
fosters popular Internet
activity." - ], - [ - "Outdated computer systems are
hampering the work of
inspectors, says the UN
nuclear agency." - ], - [ - " In Vice President Cheney's
final push before next
Tuesday's election, talk of
nuclear annihilation and
escalating war rhetoric have
blended with balloon drops,
confetti cannons and the other
trappings of modern
campaigning with such ferocity
that it is sometimes tough to
tell just who the enemy is." - ], - [ - "MADRID: A stunning first-half
free kick from David Beckham
gave Real Madrid a 1-0 win
over newly promoted Numancia
at the Bernabeu last night." - ], - [ - "MacCentral - You Software Inc.
announced on Tuesday the
availability of You Control:
iTunes, a free\\download that
places iTunes controls in the
Mac OS X menu bar.
Without\\leaving the current
application, you can pause,
play, rewind or skip songs,\\as
well as control iTunes' volume
and even browse your entire
music library\\by album, artist
or genre. Each time a new song
plays, You Control:
iTunes\\also pops up a window
that displays the artist and
song name and the
album\\artwork, if it's in the
library. System requirements
call for Mac OS X\\v10.2.6 and
10MB free hard drive space.
..." - ], - [ - "Favourites Argentina beat
Italy 3-0 this morning to
claim their place in the final
of the men #39;s Olympic
football tournament. Goals by
leading goalscorer Carlos
Tevez, with a smart volley
after 16 minutes, and" - ], - [ - "Shortly after Steve Spurrier
arrived at Florida in 1990,
the Gators were placed on NCAA
probation for a year stemming
from a child-support payment
former coach Galen Hall made
for a player." - ], - [ - "The US Secret Service Thursday
announced arrests in eight
states and six foreign
countries of 28 suspected
cybercrime gangsters on
charges of identity theft,
computer fraud, credit-card
fraud, and conspiracy." - ], - [ - "US stocks were little changed
on Thursday as an upbeat
earnings report from chip
maker National Semiconductor
Corp. (NSM) sparked some
buying, but higher oil prices
limited gains." - ] - ], - "hovertemplate": "label=Other
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Other", - "marker": { - "color": "#636efa", - "size": 5, - "symbol": "circle" - }, - "mode": "markers", - "name": "Other", - "showlegend": true, - "type": "scattergl", - "x": [ - 9.787297, - 16.660423, - -47.977715, - -51.65402, - 17.206654, - -23.452963, - 28.167477, - 14.671119, - -37.6373, - 21.907679, - 49.959976, - -36.581165, - -19.791555, - 11.003371, - 12.786125, - 54.445854, - -20.928211, - 28.192938, - 27.511831, - 30.389194, - 31.907536, - 27.685726, - -0.48236108, - -53.210773, - 24.631432, - -39.491558, - 19.87161, - -22.567608, - 13.9476, - 42.217842, - 23.463217, - -19.96729, - -49.124306, - 15.450646, - -7.3474283, - -28.231606, - 22.48473, - 47.958393, - -22.541676, - -52.717, - 47.226242, - 51.068775, - 50.124294, - -19.31264, - -28.148346, - 44.945942, - -42.825386, - -47.410145, - -29.877638, - 7.788443, - 46.406788, - 48.53827, - -5.8827424, - -35.965088, - 31.687206, - 26.455547, - -10.623615, - -40.76841, - -4.8219795, - -18.956379, - 40.537342, - 3.2403526, - -5.107883, - 63.37852, - 56.515934, - 45.10189, - -42.131943, - -8.153443, - 48.401085, - 0.8158772, - -29.768171, - 7.324227, - 36.802402, - -32.52056, - 24.88585, - -39.654697, - 12.912951, - -18.497515, - 54.15107, - -31.347673, - -48.55776, - 38.79396, - -53.367012, - -27.265852, - -6.4607854, - 13.661437, - 30.355759, - 58.71805, - -25.208595, - 3.1252713, - -41.868053, - 38.756367, - 59.531124, - 47.890396, - -17.98721, - 36.084118, - -13.634508, - 39.42093, - 18.820461, - -30.356327, - -49.554066, - -29.197483, - 55.4732, - -43.75864, - 60.896523, - 56.4989, - -33.627903, - 48.16754, - -28.459074, - 13.827141, - -11.594272, - 47.840588, - -33.894855, - -5.484721, - 1.4320391, - 60.467564, - -13.830222, - -26.233013, - 31.210938, - -36.616104, - 12.191131, - 49.345882, - -53.822376, - -44.628685, - -2.3659778, - -19.861258, - 58.657722, - -44.997097, - -37.276833, - 25.89013, - -10.061741, - -32.693943, - -1.0874362, - -19.60824, - -38.45829, - -15.162608, - 16.015558, - -38.214413, - -18.354656, - 20.328302, - 37.406326, - 45.95487, - 27.38124, - 46.569256, - 15.950565, - 11.055151, - 14.368044, - 40.19783, - -38.585472, - -46.83205, - 15.940281, - 48.277, - -38.63723, - 48.961315, - -7.0522246, - 28.371725, - -23.330465, - 46.0185, - -44.325756, - -35.855865, - -18.20647, - -7.6315646, - 59.281708, - -53.65577, - -33.435104, - 17.925577, - -48.104885, - 15.851762, - 58.10119, - 41.329796, - 60.929493, - -56.60167, - 49.080627, - 1.6593695, - -1.9387143, - -40.07204, - -32.65333, - -11.705121, - 18.05479, - 52.442997, - 25.193996, - 28.471008, - -23.656853, - 13.627039, - 10.705277, - -8.970166, - 18.345266, - 49.169395, - -44.30361, - -36.139923, - 31.360231, - 50.538635, - 50.209198, - -48.951195, - 29.55601, - -21.136202, - -21.265085, - -41.619125, - -34.370987, - -33.846046, - -16.407732, - 4.6381655, - 16.084637, - 38.928047, - -41.55955, - -33.503048, - 57.835648, - 25.167212, - -31.4103, - 51.287056, - -41.654114, - -43.58004, - -40.584736, - 54.942364, - 12.821454, - 24.011929, - 31.13371, - -20.22472, - 17.79019, - -24.227406, - 15.120078, - -41.80841, - -43.47724, - -39.450546, - 19.99747, - 15.529904, - 45.565693, - -28.54648, - 56.076347, - 19.791918, - -55.67927, - -41.094902, - -27.870377, - -41.256504, - -11.352515, - -46.313496, - -46.637367, - -18.041924, - -18.929783, - -38.786, - -18.44551, - -45.789707, - -9.525661, - 12.873272, - 47.481384, - 16.33122, - 22.366423, - -35.619858, - 54.362545, - 2.4414933, - 25.421625, - 53.08307, - -29.50228, - -34.186226, - -37.5602, - -36.813686, - 20.893494, - 19.51864, - -5.632542, - 11.30494, - 2.9794881, - -45.209023, - -31.500145, - 12.285144, - 55.395947, - 32.21779, - 3.3651476, - -51.390118, - 36.722115, - 58.705086, - 30.618706, - -16.802534, - 2.6427522, - -42.6501, - 18.079544, - -6.2927465, - -46.257027, - -23.344019, - -44.347576, - 23.218187, - 48.1655, - -8.361857, - 2.7036908, - 6.7110343, - -1.3755705, - -17.157036, - 39.944633, - 51.72719, - -18.672327, - 57.632236, - 18.106745, - 10.5324745, - 40.575005, - -23.71305, - -55.86017, - 18.966919, - 31.810251, - -34.78124, - 49.25058, - 52.102192, - 17.243034, - 45.8365, - 4.507162, - 41.15665, - 21.953882, - 45.06367, - 50.470036, - 12.895756, - 13.899155, - -18.132378, - -35.732872, - -8.266737, - 55.406406, - -34.572502, - -5.21674, - 34.643066, - 9.645002, - -39.5287, - -21.22969, - 8.84193, - -6.6976542, - -26.256298, - -42.24456, - 44.60481, - -34.82042, - -45.549995, - -48.911743, - 7.766448, - 40.65631, - -7.7231383, - -46.522186, - 49.13915, - 10.753302, - -36.61156, - 60.835865, - 29.961258, - -37.112934, - -10.257471, - 31.980536, - 27.892096, - 45.230713, - -16.951935, - 29.803865, - -5.4085217, - 25.779589, - -19.065458, - -22.693665, - 56.531708, - -44.68482, - 23.273722, - 2.6259706, - 37.571487, - -29.42919, - -30.674974, - 65.59185, - -16.563955, - 38.63354, - 3.1031818, - -43.12336, - -57.728573, - 13.034079, - 46.481388, - -48.13403, - -27.2014, - -10.165841, - 46.68557, - 49.08016, - -15.232134, - -53.29632, - -7.2577205, - 14.032702, - -31.430248, - 23.928396, - 12.880273, - -27.285727, - -42.180077, - -15.3164215, - -6.620774, - -47.9015, - 11.016033, - -37.857563, - 45.88543, - 35.342182, - -30.674488, - -23.828165, - -37.931133, - -31.504562, - -47.091602, - 17.860275, - -6.3850884, - -16.122215, - -3.119768, - 47.523766, - -28.833778, - 12.732019, - -7.503495, - 47.32294, - -26.526276, - 16.701687, - 34.786186, - -42.6552, - 14.009928, - 18.774673, - -37.64758, - 43.796356, - -14.742917, - 49.697426, - -19.793585, - -53.133896, - 37.340225, - -22.841238, - 2.979671, - -51.962658, - 54.74676, - 41.444393, - -15.730567, - 30.604837, - -44.145668, - 8.863162, - 60.995483, - -44.98284, - 9.040379, - 24.042429, - 25.076736, - 30.519775, - -47.514927, - -40.143944, - -29.305222, - 12.896586, - -30.795404, - 25.85091, - 19.948092, - 20.974108, - -19.33182, - 66.21081, - -49.376717, - 35.24333, - 18.678154, - -43.173016, - 57.111607, - 48.019886, - -4.780475, - 49.229675, - 52.86177, - -32.70729, - -13.887877, - 19.741331, - 52.435543, - -34.81799, - -22.524883, - -12.82885, - -46.24378, - -29.501112, - -5.0456986, - 31.022472, - -36.60279, - -47.141144, - -11.186273, - -36.80437, - 18.250782, - -8.335074, - -34.644447, - -11.501718, - 4.836007, - -9.537627, - 24.104692, - 51.07264, - 61.549442, - 1.9518297, - -42.394825, - 42.282997, - -11.57566, - 15.377659, - -29.24355, - -47.198746, - 33.828228, - 14.915583, - 56.65421, - -53.3065, - 19.173527, - 43.26525, - 62.865932, - -37.63518, - -42.896793, - -15.898649, - 1.5646982, - -46.367523, - -51.349506, - -47.68558, - -10.65396, - -49.790844, - 39.05222, - -27.663815, - -7.4475813, - 41.410755, - 38.42368, - -45.67376, - -20.76551, - -29.445877, - -23.031208, - -37.19868, - 4.37751, - -31.336668, - 8.212411, - -45.453674, - 5.134725, - 14.638772, - -6.1798644, - 1.3949758, - 15.7138605, - -50.869877, - 56.985188, - -44.030884, - -44.016224, - 57.739395, - -24.544922, - 17.170551, - 50.66318, - 48.850945, - -50.093754, - -24.092436, - 17.347712, - -19.335144, - 36.300686, - 39.32889, - -6.3253975, - -25.326218, - 31.211935, - -6.78382, - -50.14104, - -49.846096, - 39.84183, - 24.028929, - 40.939545, - 48.37629, - 25.452175, - -37.92231, - -20.485857, - 51.559708, - -52.251915, - -33.647213, - -9.488163, - 5.1724906, - 46.41191, - -14.337521, - 52.77315, - 27.314432, - -29.272839, - -42.127716, - -45.652252, - -43.886417, - 12.80685, - -32.545635, - 4.3835373, - -26.112938, - -39.891853, - 55.190277, - -24.499039, - -43.219402, - -9.765382, - 12.646676, - 17.87465, - 38.03023, - -42.07617, - 9.909096, - 59.759964, - -46.420776, - 22.181377, - 48.908253, - 29.485273, - -1.1384851, - -36.030876, - 49.40172, - 24.128727, - 49.71215, - -16.348925, - -31.277052, - 40.702366, - -16.921326, - 57.916637, - 16.221085, - -9.602789, - -13.009839, - 51.67792, - 30.202019, - -35.251965, - 57.654526, - 34.585587, - -9.096614, - 25.097984, - 2.5471764, - 61.278408, - -16.914656, - 8.574884, - 23.422081, - 39.786133, - -44.31037, - -28.402678, - 21.295237, - -18.734745, - -28.94602, - 25.800558, - -42.23089, - 33.389534, - -23.825924, - -37.813118, - -45.167847, - 5.8968763, - -22.952112, - 24.184378, - -45.96224, - 54.46313, - -36.155823, - -37.039566, - -54.980534, - -33.989525, - -15.194927, - 46.068615, - -28.049438, - -30.086702, - 48.63991, - 35.595135, - 20.787077, - 29.313784, - -1.0110728, - 8.255305, - 50.553566, - -17.443365, - -12.567652, - -42.189774, - -40.062756, - -52.438923, - -24.236567, - -35.002357, - 54.101334, - -54.033554, - -24.72687, - 31.629358, - 15.709119, - -25.166414, - -7.6725793, - 51.79524, - -51.84964, - -27.801344, - -26.129557, - -52.71393, - 10.710161, - -13.981046, - 42.513103, - -6.9841313, - 39.504147, - 20.738512, - 47.29459, - -10.861444, - -48.87469, - -50.175316, - -35.826897, - -31.892145, - 51.208797, - 61.06654, - 23.318872, - -35.0245, - -3.1801224, - 59.059566, - 41.391323, - 56.262905, - -31.190088, - -35.842033, - -44.238426, - -46.75248, - 46.51155, - -24.869604, - -48.693672, - 27.989025, - 57.75085, - 35.379726, - 34.078384, - 40.129883, - 51.36741, - 44.124233, - 38.459312, - -19.088882, - 42.09723, - -32.190376, - 20.918581, - -25.71062, - -44.228165, - -22.265373, - 54.85633, - -31.057253, - 11.446291, - 48.645084, - 33.95719, - 10.624376, - 34.440483, - 13.654009, - -52.42964, - 23.009165, - 0.9160498, - -33.267147, - 1.6248351, - -36.05716, - -7.2311153, - 45.367435, - -12.550289, - 15.268804, - 57.216434, - 20.570063, - 16.519796, - -20.162544, - -39.967007, - -7.045784, - -13.788036, - -6.436385, - -21.87981, - 49.150986, - -31.409056, - 40.020714, - 1.7450867, - -37.63322, - 36.398586, - -31.830273, - -16.392036, - 19.578056, - -30.145031, - 9.273592, - -22.6749, - 49.763367, - 44.87128, - -31.353453, - -45.900715, - -54.722725, - -9.91315, - -33.41616, - -29.682985, - 27.913883, - 5.769484, - -21.902475, - -28.206575, - 34.063007, - -38.86974, - -19.676825, - -27.176733, - -49.362682, - -44.44146, - 5.3805246, - 2.333401, - 14.209792, - 29.130596, - 40.65309, - 7.33986, - 33.885105, - -38.69257, - 42.74865, - -51.24567, - 46.39128, - 63.1983, - -1.2716205, - 2.7853732, - 26.978062, - 18.276062, - 20.191584, - 25.01299, - 10.248553, - 4.6009235, - 9.839586, - 11.750173, - 7.9382405, - -10.997008, - 21.737896, - 49.79338, - -29.136082, - -29.750324, - 54.410885, - -35.14978, - 63.605362, - -51.056225, - 39.934895, - 17.519335, - 17.778656, - 15.492881, - 7.7321296, - 8.952756, - -19.130821, - 40.63617, - -37.452244, - 20.371246, - 30.811249, - -9.127035, - -5.5860677, - 1.1558152, - 47.465935, - -24.740665, - -47.064148, - -54.69983, - 47.272655, - -27.990711, - 63.177612, - -7.06102, - -43.44754, - 24.795843, - -4.7836714, - 41.66488, - 1.8769449, - -24.956768, - 51.543095, - 12.356418, - -53.22971, - 38.820065, - 4.2263513, - -7.9959974, - -23.705156, - -6.0662427, - -37.926384, - -41.1264, - -27.350927, - 31.053217, - -9.149289, - -37.36757, - -16.533398, - 40.088383, - 7.0387945, - -22.092422, - -30.736622, - -44.570576, - 60.45724, - 52.433907, - 9.723743, - -15.802876, - -49.361073, - -25.432766, - 38.667847, - -28.812906, - -22.672857, - -35.77931, - -16.137821, - 27.65755, - 57.766346, - 42.41823, - 26.112234, - -39.176956, - 16.072603, - -48.2029, - 19.677572, - 17.410772, - -6.2585354, - 7.9719267, - -53.251343, - 12.662249, - -9.297528, - -36.831997, - -44.267094, - -42.660313, - 18.940567, - 20.549877, - -19.017382, - 33.992294, - -34.603184, - 56.381645, - -15.977553, - 53.579098, - 7.4309235, - -35.853523, - -15.548051, - -44.87483, - -51.507732, - 19.506048, - -52.502518, - 59.620773, - 8.936648, - 48.37667, - -32.07786, - 14.902041, - 35.445507, - 46.157833, - 49.838924, - -48.87661, - -45.17925, - 29.182852, - -22.362936, - 38.542347, - -10.216267, - 22.10423, - -31.37848, - -2.6893454, - 51.905163, - 21.657618, - -5.704888, - -20.502497, - 30.625587, - -24.823088, - 13.691204, - 28.035511, - 23.045893, - -50.661304, - 43.841885, - -50.370255, - -47.05539, - 56.74711, - 30.591192, - 51.738125, - -11.594388, - 17.440117, - 51.774147, - -23.063238, - -9.929121, - 43.796253, - -38.724506, - 47.406204, - 7.212067, - -41.108536, - -21.799944, - 14.5572, - -36.380856, - -22.186844, - -33.03146, - -47.564026, - -6.8207917, - -0.203547, - 26.660809, - -45.418346, - -32.97913, - -29.1495, - 41.08887, - 2.4019308, - -34.859055, - 14.605348, - 5.080946, - 62.321815, - 30.915781, - 49.839912, - -13.132145, - -12.614871, - 48.11404, - -33.125538, - 37.93922, - -30.265446, - 4.331932, - -24.302145, - -38.971054, - -6.6933794, - 32.7655, - 58.07306, - 50.09836, - 23.97021, - -44.289158, - -16.34018, - -42.824986, - -37.11219, - 54.922394, - -38.334126, - 22.242004, - -12.324585, - -28.60194, - -35.730442, - 52.352432, - 14.265632, - -36.50344, - -27.018137, - -30.541101, - 53.529724, - -7.2380333, - -40.239014, - 7.0784307, - 20.74278, - 2.5284033, - 25.636118, - 4.454403, - -49.050774, - -23.530384, - -23.313187, - 38.338932, - 9.910433, - -22.21815, - -25.737848, - 51.55675, - 37.103165, - -17.621637, - -31.606474, - -46.921032, - -12.631271, - -34.711258, - 14.978659, - -43.354763, - -22.281115, - 45.54423, - -33.235416, - 11.338411, - -43.594814, - 53.86991, - -15.313636, - 47.012283, - -21.579958, - -46.839928, - -45.437263, - 60.093002, - 11.213355, - 32.56739, - -27.061964, - -20.385843, - 15.526145, - -8.932405, - 60.606064, - 9.335806, - -38.67932, - -8.953644, - 39.772743, - 18.62211, - -6.674851, - -41.675705, - -6.503544, - 23.033293, - -5.5465455, - -36.837105, - -4.2590623, - 48.95457, - -42.01228, - 10.529721, - 13.965547, - -3.9804885, - 44.68764, - 48.906673, - 47.63983, - 21.258057, - 62.788, - -6.2482433, - -48.024345, - -12.530503, - -39.613857, - 10.181149, - -34.855972, - 17.598188, - -46.561874, - -17.363302, - 1.3672223, - 32.536667, - 10.24864, - 5.8206697, - -45.638084, - -0.31910038, - -10.62197, - -21.206648, - 38.030407, - -34.547794, - 21.86292, - 56.60054, - 20.400032, - 27.48848, - 2.2426317, - 5.0682087, - -18.876629, - 27.914957, - -17.48441, - -20.422543, - 16.509165, - -27.667318, - -48.115654, - 40.073948, - 60.232296, - 9.352251, - 14.155432, - 56.806816, - 2.808305, - -16.641712, - -19.632275, - -41.143227, - 6.707939, - 45.64992, - 19.51436, - -41.17226, - 39.266872, - -6.392582, - 62.91453, - 18.935217, - 46.280994, - 50.306213, - 53.805332, - -13.137335, - 50.443317, - 53.03957, - 44.309578, - -30.403149, - -33.03263, - -30.970875, - -14.373312, - 8.379798, - 54.42772, - 2.4920332, - 1.7612854, - 34.023724, - -28.959257, - 61.473892, - 50.651646, - -42.69843, - -18.173891, - 27.97626, - -11.489995, - 59.39454, - -50.46992, - 47.18665, - -22.095016, - -0.99369574, - -48.586517, - -28.31348, - 2.79127, - -32.614243, - 16.340908, - 20.619595, - 32.39917, - 59.94177, - 23.400663, - 42.23158, - -40.497093, - 14.445518, - -43.79571, - 56.222717, - 26.900372, - -34.05016, - 59.36177, - -48.04673, - 57.550297, - -10.504851, - -45.725693, - 12.496445, - 60.801098, - -49.58257, - -20.070473, - 57.966537, - 28.753572, - -35.82806, - 55.964886, - -44.020023, - -23.90992, - 45.870426, - 21.319304, - -27.236769, - -37.01328, - -19.117485, - 38.638237, - 49.729176, - -39.115543, - 17.625916, - 11.094263, - 7.11425, - -29.740028, - 18.546873, - 58.080826, - -34.482994, - 37.20064, - 9.897873, - -27.855904, - 24.480858, - -52.830154, - 58.289707, - -48.07056, - -19.067713, - -21.63138, - -40.71425, - -4.696033, - -4.852559, - -17.729515, - 8.527567, - -29.865084, - 25.88273, - -46.45139, - -9.0318775, - 63.36231, - 50.890648, - -8.188348, - 16.88663, - 13.06387, - -25.576069, - -26.325634, - -23.095638, - 29.025854, - -40.87854, - 45.88053, - -38.34742, - -13.60535, - 3.984353, - -1.1919637, - -50.887096, - 50.78542, - -34.409237, - -46.677288, - 5.320594, - 14.373686, - -45.882183, - -32.426746, - 43.456127, - 2.8495433, - 28.731657, - -2.2277532, - 50.339493, - 61.357586, - 11.930037, - -42.132465, - 56.755314, - -18.868166, - -14.928126, - 13.779188, - 23.310764, - -42.33495, - 19.120626, - 18.960714, - 25.783823, - 17.941885, - 55.462612, - 10.820086, - 58.314003, - -45.8806, - -21.790516, - 53.49091, - -51.873066, - -8.934254, - -35.644184, - -43.46856, - -26.787775, - -12.61641, - 11.278602, - -12.760466, - -35.958366, - -9.973649, - -5.3010283, - 8.342169, - 58.012913, - 7.6059, - -7.4377956, - -46.84005, - 49.449314, - 37.930157, - 12.515653, - -54.239532, - -39.886326, - -43.70516, - 57.86416, - 8.195707, - 52.26376, - -40.78544, - -46.046387, - 1.8771796, - -8.241421, - 47.072803, - -12.890557, - -23.360226, - -23.913462, - -10.10402, - -33.456993, - 48.17513, - -34.200912, - 22.029692, - -34.14632, - -40.844006, - 44.906193, - -29.91782, - 4.4929285, - 56.61765, - 56.60834, - -17.537066, - -30.420895, - 56.066643, - -19.92304, - -2.2965894, - 56.162464, - -41.66086, - -57.68235, - 3.6962993, - 11.67213, - -19.05618, - 5.52023, - -48.503033, - -18.99317, - 53.77512, - -14.034199, - 47.758217, - -29.327738, - -27.266224, - 50.96032, - -49.10616, - -4.6537275, - 58.05466, - -8.695738, - 15.926025, - -35.493626, - -52.898724, - 4.0713243, - -16.14875, - -40.76337, - -36.11573, - 41.446438, - -3.7340183, - -15.154654, - 58.41072, - 11.970405, - -16.320389, - -19.673914, - 11.040503, - -36.72977, - -9.829185, - 35.8429, - 47.047108, - -37.2606, - 54.494556, - -52.1362, - 13.273838, - 7.288217, - 47.79968, - -20.01322, - -18.065994, - 8.75742, - -54.428818, - 18.142248, - -9.159126, - 29.14241, - -46.200623, - 17.28087, - 13.877883, - -13.831901, - -21.605253, - 21.1013, - 59.32574, - 13.981468, - 40.920834, - 55.53207, - 44.559975, - -10.860374, - 10.2113, - 28.748735, - 10.333969, - -37.78618, - -45.533035, - 53.77833, - -8.867661, - 12.468114, - 3.0369818, - 32.079, - 47.351242, - -55.4472, - 5.742987, - 24.300056, - -21.27348, - -8.906268, - -34.02309, - -0.9226989, - 32.861256, - -5.918376, - -30.542126, - 38.30059, - 48.4094, - 33.499294, - 1.5139743, - -5.9578004, - 22.857521, - -42.396126, - -16.095537, - 29.347134, - 4.3284388, - 45.721344, - 26.680521, - 45.999187, - 49.048878, - -21.678917, - -48.91647, - -11.771681, - -10.15981, - 39.29256, - 8.132189, - 32.81585, - 11.17274, - 22.79567, - 2.0400486, - 19.547178, - -4.0862207, - -9.854177, - -23.889015, - 26.376568, - -54.596252, - -22.090435, - 32.12724, - -50.986782, - -34.252632, - 59.9222, - 45.969334, - 47.935875, - -4.5817585, - 17.717125, - 32.523216, - 19.772266, - 57.007023, - 34.043217, - 30.42877, - 10.665481, - -16.827753, - -38.59416, - -32.974155, - 15.195456, - -36.174, - -45.269844, - 11.543438, - -19.309122, - -28.692097, - 53.714108, - -18.300999, - -49.752243, - -10.5037985, - 34.008293, - 18.401154, - 33.648438, - -44.20961, - -39.52826, - -27.136961, - 59.613667, - 31.749115, - 7.0795293, - -34.181965, - -37.106304, - 19.923655, - 14.908174, - 52.849945, - 10.556734, - -48.20029, - 9.239984, - 15.951407, - -7.4418893, - -28.779457, - -35.19683, - -54.1994, - 20.179276, - 31.14273, - 0.258186, - -2.1609035, - 61.664543, - 14.35011, - -26.758255, - -54.634964, - 14.368874, - -43.92253, - -42.005432, - -39.611347, - 9.892005, - -39.611637, - -24.87918, - -22.471472, - -38.19811, - 30.838337, - -36.996124, - -4.4758306, - -46.204945, - 43.08786, - -24.678703, - -50.613956, - 49.605602, - 6.150114, - 63.165108, - -20.649567, - 47.894882, - 51.314476, - 44.60029, - 6.031961, - 8.659726, - -15.612729, - -9.729161, - -28.362564, - 10.755605, - -36.588448, - 8.7123785, - -12.811854, - -0.94040644, - -45.534595, - 12.619259, - -44.44866, - -4.227074, - 44.015842, - -22.860474, - -30.753082, - 39.41502, - 0.080250725, - -15.496077, - 20.854275, - -10.390649, - -35.993237, - -36.425526, - -5.6656046, - -36.567635, - 59.81665, - -11.675889, - 14.897927, - 41.209156, - 8.117931, - 6.539579, - -12.951042, - -30.48289, - 47.579025, - 56.48261, - -38.7589, - 46.025146, - -36.49073, - -6.355229, - 58.74744, - 46.206974, - -52.00866, - -31.978811, - -43.13706, - -7.6462755, - -31.936037, - -19.532629, - 53.145702, - 7.7298007, - -36.42381, - 12.733178, - 23.083542, - 60.687424, - -38.00677, - 38.102413, - 39.646805, - -46.434704, - -42.961407, - 52.38563, - -16.082205, - -50.200237, - -29.59413, - -10.404932, - -27.002981, - -20.752146, - 34.14185, - -18.822731, - -38.39936, - -34.192577, - -23.879477, - -49.73623, - -20.585733, - 31.320894, - 6.8278956, - 14.610548, - 40.573475, - -19.3257, - -32.563313, - 7.079915, - -7.734347, - 21.593582, - 41.94092, - 22.709345, - -8.220228, - 30.75048, - 23.351994, - 10.662044, - 6.3287606, - -44.1901, - 20.248484, - 39.690254, - 34.33151, - -21.206255, - 17.894573, - 53.560726, - 18.915913, - -50.147823, - -56.14451, - 50.696335, - 19.135786, - 0.011293956, - -41.132812, - -7.490298, - -6.7789235, - 21.208382, - 5.4172053, - -44.169758, - -47.881756, - -28.388693, - -12.397968, - 29.16581, - -0.9005222, - 58.507614, - 40.03086, - -17.01861, - -49.997864, - -11.5951185, - -38.691113, - 24.29299, - 48.50645, - 38.79774, - -53.174366, - 15.59622, - -8.326396, - 0.79674417, - 10.643132, - -44.02579, - 5.560217, - 0.5841107, - 24.635311, - -28.108793, - 13.113659, - 62.77733, - -20.166492, - 47.435825, - -15.611658, - 18.401346, - -38.040787, - -8.663238, - -30.962019, - -8.084352, - 43.003845, - -39.750137, - 46.27362, - 14.899461, - -45.082096, - -47.16861, - 24.252523, - -4.7970433, - 5.36986, - -16.89367, - -26.904469, - 31.625498, - 10.970106, - 51.867313, - -17.731619, - -34.483925, - -43.073074, - -6.7949033, - -27.989662, - 2.5174408, - 34.368248, - 12.8087, - 35.39813, - -25.524998, - -46.526306, - 53.752186, - 55.804855, - -54.849133, - -40.10975, - -11.253943, - 15.975605, - -24.282412, - -36.69884, - -9.612953, - 27.581682, - 1.6741688, - -53.5042, - -27.687584, - 16.295555, - 3.6958573, - -28.30938, - -35.854397, - 26.508045, - 17.794357, - 30.6338, - 47.806313, - 10.886147, - 56.805237, - -40.808376, - 18.907486, - 49.249695, - -38.4294, - -5.0891867, - -45.114822, - -46.690304, - 49.522606, - -25.18432, - -36.175987, - -41.517033, - -33.290382, - -15.035485, - 61.757652, - 3.8529873, - 61.630924, - -54.139446, - -25.219833, - 39.668633, - 10.995691, - 23.637348, - 33.6961, - 51.79226, - 14.72486, - -53.989174, - 28.194004, - 53.427227, - 45.15016, - 36.015182, - -34.2908, - 43.020264, - 7.9172506, - 54.577732, - -48.755344, - -49.55056, - -39.571285, - -40.278057, - -51.21703, - 18.002365, - -3.3571925, - 19.580015, - -8.731081, - -6.0078135, - 31.860546, - -28.372087, - -0.10420398, - 19.054085, - 37.094307, - -11.813869, - -28.535112, - -1.1245881, - 58.735332, - -40.870914, - 26.428055, - -52.076916, - -16.299625, - 12.898047, - -51.801567, - 35.940807, - 30.280912, - -27.921608, - -36.991142, - 63.004868, - -23.981367, - 47.676117, - 43.803253, - -35.73722, - 52.02361, - 0.08228831, - 57.569775, - -31.23627, - 4.8372564, - 2.4959075, - 6.9097495, - 24.6171, - -36.47172, - -11.448383, - -3.8125634, - -20.261177, - 51.3331, - -4.775729, - 40.77166, - -24.145273, - -0.46443436, - 48.259228, - -45.570045, - -29.613533, - -40.73366, - -19.412077, - -11.283554, - -47.05097, - 49.969627, - -48.772636, - 25.599476, - 36.618427, - -10.298156, - 14.019283, - -43.35723, - 55.361397, - -10.978807, - 51.953743, - -53.829735, - -8.411927, - 15.602155, - -13.247851, - -15.053305, - 40.71827, - -13.399857, - -47.515026, - 62.178337, - -4.658773, - 1.1374025, - -8.963649, - 25.336575, - -29.961985, - -12.003402, - -17.52331, - -50.23115, - 27.973917, - -48.06655, - 39.666965, - -9.277499, - -7.6838555, - 23.369677, - 6.171623, - 26.484608, - 7.0410976, - 19.369898, - -33.914284, - 33.43409, - -15.22937, - -21.86168, - 20.71207, - -7.6405187, - 12.614038, - 17.452501, - 55.207115, - -31.572515, - 32.183567, - -50.991158, - -38.40225, - 16.695406, - -52.86785, - -35.325897, - 18.572897, - -51.80827, - -35.83179, - -41.270184, - -36.710674, - 6.0333753, - 55.5641, - -49.167038, - -21.823997, - -1.3200667, - 5.044943, - -40.638805, - 51.27627, - 47.339336, - 16.012442, - -27.684992, - 63.347527, - 39.062187, - -12.411886, - -41.362526, - 9.572269, - -24.7866, - 26.459038, - -17.990955, - -40.258007, - -2.3985894, - 54.67712, - 2.9941561, - 65.51466, - -37.48171, - 17.726252, - -23.877874, - -34.57765, - -0.9994553, - 45.10427, - 17.785444, - -34.842422, - -51.40557, - -39.0015, - -14.16722, - -31.760511, - -16.35767, - -36.74732, - 47.36583, - 35.328148, - 20.736986, - -50.34398, - -5.775708, - -32.659416, - 30.716692, - 24.382677, - 58.147617, - -19.314493, - -3.8920984, - 16.1644, - 64.86492, - -10.574449, - 19.621206, - 8.682678, - -17.94723, - -24.707636, - -20.651194, - -5.6782784, - -13.584895, - -52.063236, - 32.677113, - 55.061314, - -26.427645, - -33.76177, - -50.93683, - 38.546684, - -14.214475, - 43.151165, - 1.4400622, - -35.708652, - 26.161028, - -41.237144, - 44.980778, - 25.263475, - 16.929596, - -50.64484, - -48.196377, - -10.817454, - -2.0928724, - -25.303522, - 47.840103, - 39.76294, - -23.521646, - 49.251343, - 52.69105, - -43.41168, - 0.50536364, - -41.631573, - 19.154146, - 49.939175, - 46.95092, - 26.26559, - 19.381176, - 12.624142, - 13.547113, - -15.368924, - -44.33141, - 17.735638, - -49.86946, - -25.189764, - -41.6564, - 5.6944747, - 28.887644, - 54.523384, - 11.9049635, - 64.17483, - 19.661798, - -40.866665, - 7.287593, - -48.861267, - 22.103119, - 27.097654, - 58.47151, - 12.937629, - -37.111736, - -49.37285, - -0.5269812, - 50.23622, - -37.09859, - -33.893284, - 18.126286, - -41.025192, - 19.819803, - -2.1707618, - 14.775703, - -27.523653, - 39.812546, - -37.509644, - -48.43532, - 59.636997, - 57.34273, - -37.623196, - -40.202778, - -55.58907, - -41.903214, - 16.772926, - 3.6852949, - 25.670559, - 26.078526, - -49.322422, - -9.049681, - -18.721113, - 48.26851, - 17.1552, - -16.408047, - 9.536311, - 21.02507, - -42.958614, - 12.836097, - 6.9077144, - 13.885367, - -52.688995, - -29.522964, - 25.294838, - 0.9785223, - 42.70037, - 15.134995, - -42.372177, - -30.956533, - 1.8828108, - -15.489649, - 49.12623, - 59.67211, - 10.278181, - -45.431026, - -21.36634, - 47.292377, - 47.805153, - -32.821945, - 3.350846, - 10.675423, - 46.018627, - -27.676836, - -30.13521, - -31.987688, - 2.7699895, - 29.804829, - -4.7174063, - 8.834031, - -30.901245, - -20.815348, - 57.51465, - 37.074707, - 14.13684, - -47.19078, - -45.82224, - -36.344696, - 64.22567, - -46.568104, - -2.3207862, - 10.008406, - 40.90623, - -45.59506, - 42.02211, - 36.001675, - -13.1443, - -43.422806 - ], - "xaxis": "x", - "y": [ - -0.25818, - -20.803589, - -22.326504, - -8.559602, - 22.728033, - 7.8286805, - 23.284092, - 21.800117, - -20.467894, - 22.159718, - -3.7095485, - -16.367886, - 34.67725, - 29.896206, - 6.133116, - -10.627376, - 0.20705454, - 8.674217, - 25.905638, - -3.9541492, - 9.192532, - 25.749458, - 39.722248, - 13.600263, - 7.8999453, - 18.938295, - -7.791385, - -2.1101115, - -17.816854, - -27.949192, - 10.707973, - -5.9476533, - -0.62792206, - 21.421028, - 17.02401, - 3.4177885, - 23.633503, - 9.072081, - 2.5558534, - -10.392384, - -21.783358, - -12.137919, - 8.247171, - -23.29184, - -18.170088, - -27.218586, - -29.326565, - 12.92886, - 7.4292397, - 11.001149, - -35.47235, - 0.2254613, - 26.266212, - -12.614066, - -32.542274, - 6.058426, - 14.819815, - 25.03953, - 4.199548, - -0.6994232, - -29.048313, - 8.901868, - -3.3929446, - -18.953293, - -16.318848, - 1.29799, - 24.970749, - 14.150794, - 0.27214336, - 7.6804514, - -22.118223, - 4.9480743, - -22.427275, - 0.9335098, - 26.755693, - 26.929127, - -26.440922, - 9.1828, - 1.5617585, - -19.087698, - -14.938796, - -2.3146381, - 13.022359, - -21.471975, - 25.554472, - 2.532362, - 23.373753, - -13.859794, - -25.318462, - 22.522005, - -32.54127, - -10.261337, - 2.3437028, - -11.51763, - 24.106895, - -28.920532, - -2.2139447, - -14.537146, - 9.316687, - 9.6741905, - 12.820546, - 4.9088416, - -28.30168, - -17.468342, - -17.185091, - 10.214211, - -20.446613, - -11.397742, - 8.161042, - 27.62886, - 26.281322, - -29.872732, - 5.007877, - 6.2455893, - 5.951754, - -19.64737, - -1.2898456, - -8.973769, - 0.16790108, - 12.575957, - 15.638516, - 9.72588, - 13.993413, - 22.678474, - -17.482075, - 14.391562, - -19.213398, - 17.073126, - 30.481924, - 20.813839, - 32.03464, - -26.55557, - 12.0553255, - -16.22469, - -18.176107, - -3.6315196, - -19.35426, - 20.519714, - 4.681029, - -24.165535, - -17.097263, - -23.540205, - -22.659904, - -30.161833, - 28.830767, - 14.3665, - 0.061168052, - -32.789925, - 4.0007343, - -27.456821, - 23.813591, - -30.553509, - -24.490698, - -19.611755, - 29.56973, - 21.227903, - 7.7406225, - 8.52158, - -10.852377, - 5.442065, - 9.661537, - 22.864084, - 4.1424494, - 7.6243353, - 4.249151, - 31.043804, - -10.734537, - 3.844936, - 1.4751459, - -12.852704, - -20.392239, - -6.189112, - -4.5837173, - 1.4175098, - -21.713743, - -13.330445, - 12.867583, - -10.440891, - 3.6453905, - 8.166061, - -11.337284, - 16.828537, - -8.8150835, - -16.43065, - 9.330847, - 20.16529, - 9.5765, - -22.28117, - -9.1425705, - -23.877768, - -10.817816, - 5.1117396, - 7.9826016, - 11.228575, - 17.663988, - 2.542931, - -1.3406546, - -23.981632, - 12.972686, - 4.730411, - 30.063469, - 3.6004014, - 18.804445, - -13.418971, - 30.71818, - -14.152756, - -24.45379, - -11.355663, - 6.520791, - -9.840589, - 21.164257, - -8.373115, - -8.409088, - 9.545558, - 4.881303, - 30.134317, - -32.61165, - -17.390278, - 32.50385, - 19.963877, - 8.090675, - 31.114712, - 30.646704, - 21.478413, - 14.554468, - 20.755419, - 11.230936, - 6.923768, - -13.468946, - 23.0764, - 20.141148, - -15.70016, - 8.499566, - -19.558147, - -10.837732, - 7.830664, - 24.00407, - 6.959669, - -17.884281, - 24.8098, - -24.985989, - -12.053112, - 8.462659, - 18.15951, - -5.462048, - 2.4064643, - 8.999294, - -12.727203, - -13.069021, - -6.154228, - 14.864804, - 1.5735915, - -25.217607, - -11.249722, - 27.957365, - -0.7906725, - 19.460798, - -2.3412774, - 6.4599543, - 2.4203362, - 32.717518, - 28.99686, - -18.920874, - -7.624435, - -23.937035, - -15.694869, - 2.3350112, - 9.491719, - -25.943512, - 0.82049704, - -3.9954906, - -16.211517, - -10.548126, - 33.583965, - 22.352716, - -0.7140172, - 28.585188, - 20.132593, - 10.375427, - -18.380714, - -21.956186, - 18.302557, - 8.7813, - 27.98141, - 5.231712, - -1.274212, - -17.928478, - -17.166925, - 5.588625, - 1.8213869, - -20.784616, - -9.940092, - -11.329836, - 1.3020672, - -5.6699047, - 2.9951952, - 7.513018, - 18.828894, - -8.567718, - -11.798271, - -2.4976819, - -25.911339, - 22.716187, - -10.770047, - 15.819128, - -15.446808, - -32.171726, - 5.0620914, - 12.743932, - 7.1431947, - 20.908062, - 27.65378, - -29.32608, - -12.216588, - 3.5037541, - -35.429436, - -8.023369, - 19.798025, - -4.302394, - 16.329193, - -23.965172, - 8.796663, - 16.477135, - -11.357406, - 32.09736, - 26.441679, - 21.586815, - 30.292624, - -14.503349, - 19.197943, - -14.683218, - -3.407611, - 23.7153, - -14.726069, - -17.214022, - 15.711783, - -8.98979, - -22.324871, - 0.59863055, - 16.493795, - -27.750652, - -28.93897, - -5.3719177, - -23.418943, - -9.659326, - -23.277813, - 16.425425, - -19.531103, - 18.54026, - 0.31460643, - 31.197924, - -14.720505, - -0.26035935, - -21.057713, - -27.277906, - -7.310227, - -15.416589, - -1.605775, - -8.874298, - -13.5169735, - -26.390093, - 0.7872089, - -7.2581453, - 22.63779, - 28.57203, - -23.089176, - -19.599855, - -21.929888, - -10.379873, - -11.895842, - -17.141865, - -16.003376, - -14.515779, - 10.840164, - -26.575148, - 3.1463404, - -3.7059593, - -8.936446, - -23.257317, - 30.278105, - 15.54324, - -31.523523, - -15.298813, - -29.652391, - -9.050367, - 18.134205, - -14.212201, - 10.717227, - 19.883846, - 21.597916, - -19.211506, - 28.315454, - -11.721406, - 16.122732, - -6.269737, - -14.575271, - -20.626392, - -9.711501, - 20.470428, - -8.267473, - 33.287487, - 25.027699, - 15.167711, - 12.847039, - -22.223913, - -13.995945, - -28.966488, - 14.344031, - 7.419209, - -21.779205, - 24.548212, - 23.27041, - -17.763275, - -27.218397, - -36.186253, - 5.0752234, - 0.31401816, - -0.48519766, - 9.704817, - -22.044197, - 28.721743, - 14.702273, - 18.21779, - 16.7961, - 9.027207, - 21.439281, - 25.772839, - 5.9104095, - 18.049044, - 11.854107, - 25.408955, - -1.7761685, - 7.837817, - -11.143075, - -25.348227, - 20.674139, - -15.303513, - 34.420277, - -6.806543, - 2.799256, - -27.043676, - 32.15406, - 6.988793, - -29.502745, - 5.2307787, - 24.185543, - 17.168627, - -6.9711366, - 28.700588, - -16.839674, - -6.9957857, - 19.155857, - 22.57425, - 4.2664466, - 10.645888, - -2.8677607, - 17.716654, - 33.268223, - 13.592724, - 35.533974, - 35.79897, - -9.217092, - -7.505608, - 5.3443413, - 16.755838, - 19.649885, - -13.013833, - 2.553211, - 5.488912, - 25.960653, - -14.678428, - -6.362675, - 15.933173, - -25.562366, - -7.9709535, - -19.333553, - 5.761818, - 5.2738123, - 14.799318, - 0.9805258, - -30.191147, - -8.254407, - -9.329842, - 24.331854, - -1.1096494, - -27.81828, - -23.302309, - 10.189425, - -0.9053779, - 14.969123, - -12.578425, - -16.734713, - -25.194714, - 34.912987, - -36.29533, - -0.7015533, - -21.124685, - 33.794212, - -20.977274, - -19.704374, - 23.483368, - -15.128482, - 8.0363655, - 2.2579987, - -16.33133, - 31.233051, - 22.297411, - -11.6483135, - 3.5171926, - 23.886812, - 12.337329, - -19.59588, - -30.116133, - 27.538383, - -19.748474, - -4.7339125, - 19.465944, - -18.429428, - -24.985508, - -24.043522, - 26.484413, - 16.774218, - 5.9628015, - -14.398376, - -23.032887, - -16.154268, - -11.766295, - -27.591204, - 20.015493, - -20.486948, - 7.6020126, - -13.656402, - 14.815331, - -33.948692, - -33.920197, - -9.174384, - 20.629124, - 16.143784, - 8.925708, - 7.7047353, - -21.596968, - 16.84247, - 11.881365, - -22.970503, - 24.66648, - 1.9238061, - 25.418554, - -17.758942, - 3.5172246, - 23.261137, - -8.986503, - 28.923544, - -7.5245304, - -15.130549, - 5.0646152, - 21.07103, - -5.8668604, - -14.940109, - -6.4981833, - -20.06512, - 23.290081, - -11.591567, - -27.786598, - 20.645449, - -5.3597302, - -11.159512, - -13.735753, - 18.798145, - -32.18803, - 8.9016, - -22.157974, - 26.788364, - -16.650103, - 18.377977, - -18.147429, - -24.88111, - 21.901451, - -14.823587, - -0.6368593, - 3.2132275, - 31.100603, - 16.802742, - 20.371767, - -28.899687, - 0.73946625, - 0.94949424, - -14.675726, - -24.362509, - 31.862827, - 23.13797, - 35.12017, - -18.907366, - 24.827017, - 31.66899, - -18.148087, - -24.660992, - 9.816621, - 16.572128, - 25.328583, - -15.456796, - 1.9859632, - 5.658062, - -5.2393093, - 9.180699, - 7.721218, - 3.9763682, - -14.759153, - 8.72019, - -12.5096655, - 4.320076, - 2.0307107, - -12.368451, - -11.865506, - 16.297318, - 0.7318651, - -13.755454, - -21.899122, - -11.081378, - -19.075409, - -13.679028, - 10.51185, - -10.045945, - -2.6716044, - 13.364902, - 20.333702, - 5.9486156, - -30.512154, - -1.8922254, - -14.551722, - -13.595177, - 24.951237, - 15.502925, - -26.033178, - -15.84722, - -0.48769227, - 5.509095, - 25.674028, - 23.005444, - 12.414623, - -7.935221, - 24.642124, - -22.191689, - -19.237648, - 16.660208, - 5.5806613, - 9.362999, - 16.740986, - -14.059228, - -9.914337, - -20.576859, - -10.982109, - 31.096636, - -11.43558, - -17.933233, - -22.175861, - -14.856947, - 26.15921, - -23.924995, - 6.894826, - 4.1693807, - 5.6076837, - -17.656506, - 15.090964, - 1.2161766, - -9.937122, - -27.618727, - -3.5818095, - -14.13704, - 25.846468, - 19.352674, - -22.007416, - 23.278618, - 11.748135, - -22.37126, - -22.028944, - -10.037108, - -25.306404, - -7.7222157, - 3.5807598, - -6.6086307, - -19.699232, - -15.10728, - -17.251148, - 10.148522, - -0.68818355, - 7.5768538, - -17.733555, - -23.194473, - 9.637636, - -2.6014824, - 9.428179, - -10.8705435, - 8.272561, - 18.622755, - 8.240764, - 7.8728004, - 13.976609, - 21.211613, - 10.388335, - -13.317306, - -0.20468314, - -0.7534798, - 16.867065, - -22.69967, - 22.19843, - 29.903488, - -29.479254, - 14.083497, - 0.6598771, - -8.660773, - -7.2729115, - -11.945698, - 23.76637, - -16.428364, - -28.303225, - -11.955685, - -30.203144, - -4.9588523, - 26.250034, - 19.381159, - -16.469437, - -14.535694, - -24.852484, - 12.103588, - 7.8694215, - -8.026257, - -6.199936, - 9.750696, - -14.905879, - -22.042368, - 2.0052595, - 15.873175, - -11.668809, - 7.235856, - -21.42294, - 14.838855, - 16.791052, - -21.904455, - -23.169117, - -20.787516, - 9.315685, - 34.738625, - 10.819606, - 20.726511, - -10.898081, - 31.885904, - 11.005908, - 15.028398, - -3.1344242, - -3.9499974, - 14.654819, - 8.201109, - 17.144817, - -19.819767, - -19.525257, - -4.076858, - -24.730019, - 11.900147, - -1.3390135, - 26.11797, - -2.478072, - -23.535704, - 27.143415, - 0.81385136, - 17.844543, - 19.694197, - 30.822157, - 11.223421, - 17.761076, - 13.325627, - -13.261404, - 2.2092547, - -13.576142, - -11.716383, - 27.541485, - -18.290712, - -25.388409, - -15.495678, - -32.85601, - 34.832695, - 15.818021, - 12.122141, - 33.150494, - -0.5336322, - -13.886067, - 28.821224, - 20.72354, - -33.77542, - 3.162032, - 17.181808, - 34.996464, - -22.37821, - -4.1373553, - -20.077517, - -16.791988, - -33.790863, - 4.8909636, - -23.158052, - 13.435741, - -22.73552, - -0.6918705, - 27.578976, - -23.911886, - -0.9915625, - 0.41720697, - -28.11098, - -15.606873, - -21.062717, - -15.843517, - 7.1253057, - -12.007193, - -23.275118, - 15.710144, - -13.556541, - -15.989742, - 1.5220636, - 15.600531, - 3.0372694, - -13.601137, - -7.148113, - -24.879805, - -0.8274632, - -11.567605, - 19.323282, - -7.7168093, - -27.03218, - 5.8135962, - -7.6383777, - 1.1989386, - 3.9182017, - -0.47444645, - -25.135891, - 22.896002, - 0.94497335, - 9.556583, - -4.4569497, - 21.02248, - -25.89945, - -18.168903, - 17.865675, - 22.459995, - 12.360714, - -24.076357, - -15.80312, - 21.917862, - 21.659195, - 33.719093, - 19.704102, - -2.2529974, - 31.99901, - -29.042156, - -26.121319, - 33.52397, - 23.902458, - 7.067429, - 26.534893, - 9.6071, - 29.210163, - -23.639217, - 3.7444665, - 1.8415234, - -4.9220414, - 22.216219, - 21.501694, - -17.915682, - -17.60881, - 19.686275, - 16.870352, - -16.338673, - -2.4079158, - 10.431047, - -11.452592, - 20.084156, - -34.98855, - -30.50168, - -1.8533841, - 13.475318, - 22.79436, - -23.127438, - -2.6888435, - -26.898434, - 32.299854, - 9.865102, - -15.889842, - -7.1564, - 14.4235935, - 10.5956135, - 16.942707, - -17.442066, - -6.0696855, - 0.2748501, - 33.509598, - 2.4050539, - 7.209693, - 12.352939, - -0.83113074, - -16.57776, - 26.730667, - -13.937987, - 5.5682783, - 8.4994335, - -12.461162, - 24.32622, - -25.814455, - -19.692043, - 8.181132, - -25.507462, - -16.080286, - -1.2937344, - 18.989775, - 16.529331, - 11.700205, - -25.712864, - 24.65294, - -5.132745, - 24.787573, - 19.01329, - -9.251707, - -2.7055879, - 14.609039, - 27.475252, - 14.475491, - 0.96339697, - -11.8820095, - 7.1217036, - 31.858027, - 16.848389, - 32.03336, - -13.837845, - -33.480656, - -20.987251, - 30.462563, - -16.143095, - 6.7093077, - -15.854709, - -24.921698, - 16.484713, - -1.7420386, - -23.097334, - 18.896671, - 34.8398, - 10.520301, - 3.5488389, - -18.068623, - 30.076416, - -29.86582, - -8.282391, - -8.46684, - 13.576438, - 3.0699391, - -16.238358, - 2.9773757, - -14.182415, - 17.441216, - -25.85015, - 9.083556, - 22.073168, - 19.385956, - 8.168441, - 13.999631, - -13.918425, - 19.32553, - -19.83609, - 29.535501, - 31.019588, - -6.5198464, - -16.273378, - 31.29178, - -20.836182, - 8.972529, - 14.504229, - -22.65874, - 24.289896, - 0.45974386, - -8.057026, - 7.783574, - -12.477235, - 3.8825731, - -3.5055225, - 15.380986, - 22.033895, - 3.7059414, - -1.0848922, - 0.16963075, - -5.582006, - 11.250292, - 21.913166, - -1.632514, - -23.06022, - -13.376665, - -5.6566067, - -5.0115275, - 33.256733, - -27.384535, - 22.36791, - -23.036457, - 3.1787782, - -11.463062, - 16.85544, - 17.925854, - 26.127491, - 34.042473, - 3.7194152, - 11.578919, - -3.056115, - 8.806574, - -12.564382, - 26.605755, - 21.529955, - 25.043688, - 17.78518, - 25.579552, - 27.044067, - -29.090658, - 21.886444, - -29.44567, - -3.69288, - 7.423554, - 19.89922, - -13.892162, - -9.352621, - -23.756565, - -17.759132, - 21.111221, - -15.3389635, - 20.052608, - 8.306711, - -6.695091, - -0.2840251, - 29.565565, - 6.3890157, - 20.825033, - -15.78091, - -3.9792998, - 8.250312, - -4.315795, - 33.91667, - 31.587502, - -4.7497973, - 0.70931256, - 22.03959, - -1.3183376, - -13.819872, - -8.057265, - 2.5191355, - -6.09211, - -1.2537154, - 1.041188, - 6.271001, - 15.563097, - 3.0869732, - 19.476908, - -7.959283, - -20.58928, - 17.528534, - -34.817635, - 26.520325, - -7.863438, - -13.616495, - 34.081158, - 14.279812, - -23.899826, - 19.227066, - -0.1847365, - 21.436638, - -21.634756, - 27.98984, - -9.426962, - 17.888885, - 18.802984, - -12.24037, - 25.563747, - -18.85435, - 20.995552, - -25.321373, - 11.024011, - -19.68378, - 30.48236, - -26.103676, - 10.497953, - 3.9857144, - -11.662108, - 14.603634, - 7.0568976, - -4.4100275, - 2.030001, - -22.706993, - 19.098873, - 31.796051, - -2.4754145, - -26.096392, - -21.39815, - 3.600532, - 28.98958, - -24.192507, - -22.364601, - 24.713762, - -16.769764, - 21.682661, - -1.3566388, - 16.40951, - 8.210962, - -15.716439, - -34.972008, - 26.949068, - 21.239998, - 12.173473, - 20.502365, - -12.030829, - -28.317688, - 4.4826207, - -4.760545, - -10.980467, - 30.730364, - 20.87726, - -17.78651, - 22.801989, - -5.3119135, - -20.541088, - 12.556309, - 1.3681566, - -15.915366, - 23.323511, - -7.8275642, - 1.0861593, - 8.230685, - -17.60057, - 4.390221, - 9.649646, - -16.683647, - -22.447065, - -10.756376, - 27.087646, - 2.2553952, - 5.474195, - 6.01643, - 14.907442, - -19.740395, - -14.250181, - -28.855429, - -21.907415, - -6.474749, - 26.200584, - 23.3236, - 5.0985155, - 23.742764, - -23.47392, - 10.961509, - -9.009804, - 10.729193, - -16.08439, - 24.293411, - -14.420636, - -0.6379835, - -7.351985, - 4.601816, - -21.606695, - 10.600249, - -19.460848, - -1.0193497, - -5.6577854, - 1.2037258, - -19.941338, - -17.019722, - 32.26841, - -20.533716, - -23.794706, - 2.3137836, - 35.702885, - -2.6479704, - 21.060795, - -4.315942, - -22.034695, - 0.85024196, - 13.542582, - -8.745571, - 6.832896, - -10.188763, - -13.390235, - -0.5990197, - -23.021431, - -5.876716, - -11.976225, - 4.2575808, - 27.501059, - 11.98244, - 26.565365, - -1.931646, - 24.216267, - -16.869408, - -8.099275, - -14.887161, - 2.2845645, - 11.149261, - -15.141055, - 27.739674, - -3.0804467, - 5.0789285, - -17.30228, - -3.2769468, - -17.239506, - 4.583181, - -19.281757, - -3.5722063, - 28.793531, - -16.723783, - 25.030203, - 9.832679, - 20.863323, - -19.392942, - -15.235338, - 11.71164, - -24.406261, - -15.53886, - -16.890417, - -19.303434, - -12.302218, - -21.589676, - -14.588415, - 15.091036, - -17.137983, - -23.051016, - -11.65064, - -1.327813, - 4.7823358, - -19.877468, - 29.76316, - -3.8284235, - 21.326263, - -17.971964, - -2.6890767, - -8.098414, - -20.775913, - 11.0288925, - -15.161179, - -13.708067, - 6.9839473, - 9.420364, - 15.523962, - -1.839738, - 18.062141, - 35.796543, - -26.4286, - 4.53065, - -3.197111, - 15.938968, - -5.59304, - -9.126152, - -23.904675, - 8.384921, - -3.4012072, - -5.3693423, - 32.041183, - -33.521553, - 9.530565, - 15.937399, - -27.414234, - -24.713099, - 24.769993, - -8.645808, - -13.032957, - -23.740261, - 8.2281, - -20.86936, - -5.3864436, - -13.534582, - -1.0408515, - 3.6773765, - 26.929934, - 16.484713, - -3.2705798, - -22.339233, - -17.725012, - 6.1994753, - -13.713904, - 8.064646, - -8.887762, - -27.97785, - 8.281391, - -14.383507, - 1.1649175, - -17.226963, - 23.824167, - -0.03827765, - 21.001068, - -1.6157911, - 1.0350281, - 23.757103, - -2.2386749, - -12.003306, - -5.807004, - 5.4682074, - 3.4183521, - -18.329607, - 10.1421995, - 21.500256, - 20.873947, - 14.622503, - 20.323536, - -22.500238, - -5.1817036, - 26.616285, - -10.172258, - -14.895687, - 7.471235, - 4.855366, - 8.929348, - 3.4454656, - 24.15315, - 33.191727, - -17.779476, - 13.368094, - -16.79903, - -1.2212269, - 29.02862, - 1.353517, - 33.686493, - -9.61028, - -10.290435, - 17.499424, - -18.92016, - 10.638852, - -4.0155163, - -29.874123, - -23.89452, - 17.025469, - 12.36343, - 25.982975, - -5.359385, - -20.511335, - 26.314108, - -14.478729, - 20.105099, - 10.390779, - -2.7448454, - -21.707514, - 2.8463974, - 20.082417, - 39.494793, - 23.544054, - 33.45021, - 1.2731425, - 7.00291, - 20.49504, - 11.026453, - -14.920918, - 21.672586, - -24.179169, - -22.502762, - -18.470171, - -5.233647, - 15.536683, - 7.5924697, - 31.43023, - -10.685339, - -5.5552483, - 30.057226, - 2.6354103, - 17.865553, - -25.625107, - -23.603718, - 16.79463, - -21.343506, - 24.513098, - -22.31949, - -13.1784725, - 14.572172, - -21.927172, - -0.43766883, - 26.446459, - 7.797492, - 27.607801, - -14.08771, - 28.953205, - -1.2875158, - -17.776453, - 1.3350589, - -0.14630945, - -12.744574, - -5.8219385, - 6.380316, - -24.39855, - 1.6557639, - -25.33089, - -10.88375, - -5.4497714, - -3.2008982, - 3.516546, - 3.7044208, - -14.088412, - 1.8123101, - -2.0853994, - -12.914869, - -14.570528, - 6.286185, - 29.915886, - 18.577192, - -19.750566, - -4.8032465, - -14.996935, - 9.808406, - 3.1115727, - 10.539988, - -0.25747964, - 7.8065777, - -9.5224, - 22.650063, - -8.527657, - 25.720367, - 27.335323, - -27.719013, - -27.493273, - -28.8183, - 16.676228, - 15.222469, - -6.1142654, - 23.31772, - 6.885112, - -21.120987, - 31.183216, - 16.581377, - -1.3270321, - -3.024608, - -24.535004, - -35.037914, - 27.32407, - 2.2356973, - 16.557335, - 8.043718, - 4.2089057, - 24.168753, - -0.42459357, - 26.167639, - -19.28855, - -16.932995, - 0.031842478, - 11.079847, - 23.264338, - 11.247658, - 28.108557, - -17.26478, - 23.26528, - -5.613793, - -12.292187, - -13.964472, - 21.349566, - 21.782167, - 26.02513, - -30.554207, - -20.807219, - -22.266432, - -16.260057, - 13.463569, - -20.409258, - 5.911049, - -3.838907, - -30.899261, - -25.502863, - 17.450424, - 4.5370917, - 7.3130083, - 29.060263, - -1.2906566, - -9.992426, - 9.496942, - 19.615667, - -15.057436, - -14.524883, - -5.6858554, - -8.944074, - 30.993462, - -18.399357, - 4.312004, - -12.452006, - 11.88096, - -26.893, - 10.486003, - -14.269513, - 13.904057, - -14.193346, - -17.597988, - -13.744734, - 19.081799, - 7.1376367, - -20.63535, - 0.17712176, - 26.276295, - -4.0243726, - 18.80954, - 8.85504, - -11.71116, - 10.333615, - -33.28943, - -13.433018, - 25.406893, - -21.37861, - -30.53585, - -0.6449607, - -17.676962, - -33.109673, - 6.502574, - 25.979095, - 13.889341, - 24.452019, - -11.330729, - -14.508683, - 7.7211857, - 30.14757, - -15.281551, - 25.445856, - 23.137957, - 2.9930232, - -11.392148, - -3.4584122, - -17.335155, - 32.249325, - 1.1835473, - 0.4309157, - -1.922125, - 18.76773, - 12.763572, - -5.1183553, - -19.383118, - -11.329933, - -9.979049, - -19.62514, - 14.371391, - -9.079416, - 17.039936, - 12.198028, - 17.744976, - -27.767008, - 4.7606173, - 20.943676, - -2.7953665, - 34.946663, - 21.359537, - 23.354967, - 32.181087, - 10.895949, - -23.63617, - 16.164768, - -21.386267, - -0.20407373, - 18.747564, - -8.708449, - 26.564816, - -20.358099, - 3.6623113, - 2.833431, - -2.406363, - -7.6430187, - 30.990358, - -1.6160171, - 22.291674, - 14.2712755, - 8.649531, - -22.09123, - -3.9283407, - -15.144995, - -5.257486, - 16.290205, - 24.053005, - -5.443865, - 29.637974, - -30.894657, - 10.8514185, - -19.329512, - -1.7249132, - -27.617838, - 12.135396, - -20.576097, - -32.521618, - -17.759117, - 14.102587, - -1.4501517, - -17.441105, - 22.34238, - -1.5771652, - -3.4706712, - 19.873198, - 17.654528, - 14.297588, - 35.126564, - 3.530811, - 22.92706, - 1.305536, - -5.8584995, - -3.4917607, - -25.70212, - 15.667845, - -13.110925, - 1.5236746, - 1.27955, - 26.836803, - 22.695467, - -7.542444, - -24.459936, - -4.085233, - -24.834877, - -13.123537, - 13.346765, - 3.3096304, - 5.8128743, - -9.243302, - -22.380308, - 24.534492, - 32.18937, - 0.7944149, - -17.298498, - -7.3226933, - 23.025293, - -0.33986145, - 14.641378, - -32.17766, - 9.108203, - -15.654366, - -3.2708795, - 1.7839518, - 4.667992, - -21.404385, - 33.032204, - 0.07473384, - -8.874142, - 19.918457, - 2.485261, - -26.038076, - 13.791234, - 19.88417, - 26.989523, - 6.4794717, - -8.599584, - 26.08512, - 35.79187, - -3.0957053, - 1.5328475, - -15.78256, - 14.641849, - 0.75382006, - 13.353416, - -20.758772, - 27.588259, - -8.591754, - 7.6756034, - -32.257572, - -3.6816385, - -8.807442, - -23.705658, - 26.69215, - 5.574528, - -3.3590631, - -16.991213, - -18.813177, - 20.353582, - -8.202672, - -2.241037, - -22.663652, - -10.86935, - 22.6146, - 0.538039, - -11.617886, - -7.3185177, - 5.459471, - -20.510658, - 14.793362, - -15.245933, - 2.8498745, - 30.176495, - 25.41137, - 12.340705, - -14.110134, - 20.984993, - -20.736963, - -21.078281, - -16.38932, - -10.101326, - -29.059853, - -14.522557, - -31.21759, - 11.320027, - -1.3346295, - 19.095402, - 3.5003624, - -0.27149853, - 23.530079, - -1.4504046, - -20.799906, - 26.357058, - 25.323908, - 21.914633, - 19.832611, - -14.345478, - -12.780764, - -15.090428, - 0.40740138, - -16.226871, - 22.365917, - 24.898293, - -22.19336, - -17.027992, - -19.892523, - 23.981928, - -11.016326, - -16.473738, - -20.647305, - -18.943878, - -34.179035, - -14.075991, - 1.9298484, - 20.942158, - -15.682211, - -9.76076, - -23.77744, - 2.101532, - -25.935007, - 8.422051, - -21.395668, - -12.298222, - 2.824297, - 12.158624, - 15.439734, - -5.986609, - 22.680363, - -19.286484, - 30.605867, - -0.7899231, - 18.014528, - -18.204716, - -18.893454, - -2.6403008, - -26.197563, - 0.6461262, - -17.935425, - 21.006203, - 19.50926, - -25.124516, - 19.076454, - -13.34786, - -20.217596, - -18.721956, - 13.471852, - 10.719515, - -6.343975, - -4.427436, - 2.1415112, - 0.124456935, - 9.154357, - 15.850318, - 14.106509, - 18.979578, - -25.880474, - 15.075585, - 20.326845, - -15.592323, - -16.127396, - 19.439365, - -18.178284, - -7.721521, - 18.546848, - 1.3289208, - -21.118057, - 15.136754, - -8.462077, - -6.078381, - 0.24295494, - -14.893564, - -3.098876, - -22.965818, - -2.973772, - -10.412807, - 36.82579, - 0.043326903, - -0.730605, - 20.569399, - 20.47704, - 34.56152, - -12.61784, - -22.44099, - -13.279965, - -28.35139, - -9.076381, - -14.49968, - 11.381456, - 27.552359, - 10.113919, - 4.322983, - -16.923988, - 18.366398, - 4.072649, - -18.502573, - 14.2359915, - 1.2205616, - 34.52153, - -13.276994, - 16.888266, - -17.09381, - 26.655972, - 12.712044, - -22.337847, - -18.344118, - -21.796993, - -2.695157, - 33.12794, - 20.795307, - 5.892835, - -30.008844, - 13.092032, - -12.617298, - -26.583797, - -12.331805, - -25.788994, - 18.527788, - -5.358728, - -20.973848, - 21.975595, - 3.6332028, - 21.49163, - -24.02265, - -1.2270886, - 31.648344, - -26.34871, - 28.852188, - 11.337199, - 16.580437, - 6.917111, - -2.6463892, - -13.808859, - 27.402872, - 31.668863, - 10.09489, - -9.203751, - -4.5927486, - -19.010218, - 7.268004, - 27.96568, - -32.725826, - -12.638194, - -9.072612, - 0.687024, - -24.00849, - -16.797096, - -13.887938, - 21.008837, - -20.714098, - 4.003382, - -5.864986, - 6.308118, - -18.954786, - -14.093458, - 14.5252905, - -10.20566, - -5.714998, - -7.6352305, - -11.445573, - 28.259352, - -7.4210625, - -14.774667, - 8.2712965, - -14.246153, - -23.317041, - 0.21726441, - -20.630865, - -24.174063, - -15.430166, - -22.63233, - -5.336508, - -0.4162142, - -17.627256, - -12.0516205, - -10.120339, - 22.627249, - 17.18417, - -24.923342, - 20.119074, - -11.128392, - -23.75025, - -22.75563, - -18.194794, - -2.677447, - 5.6336102, - -8.593113, - -27.35188, - 30.831476, - 6.842084, - -23.006275, - -2.1064568, - -31.873516, - -21.917208, - 11.057577, - 21.760345, - 31.105818, - -7.2484465, - 27.442217, - 27.198599, - -5.4786696, - 20.937487, - -15.238694, - -22.516329, - 16.441422, - -27.548603, - -0.95101047, - -5.9703918, - -20.764137, - 9.63625, - 25.318214, - -4.7924676, - 22.43602, - -29.857277, - -8.804195, - -16.590578, - 6.1655693, - -6.229835, - 9.825396, - 3.6917143, - -25.044327, - -15.101339, - 7.166533, - 18.591246, - -25.983875, - 27.819729, - 24.170658, - 5.3510475, - 6.549803, - -32.0242, - 27.198914, - -3.37324, - -14.339118, - -28.126497, - 22.221628, - -13.358003, - -16.78678, - -32.53302, - 15.152627, - 13.393224, - 19.411095, - 23.425772, - 20.027725, - 24.710947, - 17.26326, - -27.410538, - 26.30866, - -4.510418, - 5.3038287, - 7.526191, - -15.999681, - -2.2162335, - 31.378555, - 6.302167, - 15.184932, - -21.060045, - 14.10122, - 5.90295, - -27.716919, - -16.625145, - -10.241354, - 6.1585164, - 7.223655, - -11.634907, - -21.870625, - -21.870728, - 6.634295, - -6.066459, - -17.1438, - -32.103767, - -10.273103, - 15.137199, - 7.232844, - 21.119562, - 1.9282136, - 12.128642, - 12.653392, - 9.936496, - 21.916615, - 9.071596, - 27.73088, - 14.497267, - -4.162361, - -25.22734, - -22.694798, - -10.849964, - -8.824205, - 20.774977, - 20.526009, - 28.81767, - -15.895552, - -11.81379, - 11.597373, - -10.619046, - -12.564632, - -21.738821, - -13.048038, - -23.010983, - -1.3630763, - 19.897066 - ], - "yaxis": "y" - }, - { - "customdata": [ - [ - "PC World - Updated antivirus
software for businesses adds
intrusion prevention features." - ], - [ - "PC World - Send your video
throughout your house--
wirelessly--with new gateways
and media adapters." - ], - [ - "Ziff Davis - The company this
week will unveil more programs
and technologies designed to
ease users of its high-end
servers onto its Integrity
line, which uses Intel's
64-bit Itanium processor." - ], - [ - "PC World - Symantec, McAfee
hope raising virus-definition
fees will move users to\\
suites." - ], - [ - "PC World - The one-time World
Class Product of the Year PDA
gets a much-needed upgrade." - ] - ], - "hovertemplate": "label=Nearest neighbor (top 5)
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Nearest neighbor (top 5)", - "marker": { - "color": "#EF553B", - "size": 5, - "symbol": "diamond" - }, - "mode": "markers", - "name": "Nearest neighbor (top 5)", - "showlegend": true, - "type": "scattergl", - "x": [ - -50.323666, - -50.141148, - -38.19549, - -50.138874, - -50.261745 - ], - "xaxis": "x", - "y": [ - -10.1600275, - -11.487356, - -10.464009, - -9.688497, - -10.61583 - ], - "yaxis": "y" - }, - { - "customdata": [ - [ - "PC World - Upcoming chip set
will include built-in security
features for your PC." - ] - ], - "hovertemplate": "label=Source
Component 0=%{x}
Component 1=%{y}
string=%{customdata[0]}", - "legendgroup": "Source", - "marker": { - "color": "#00cc96", - "size": 5, - "symbol": "square" - }, - "mode": "markers", - "name": "Source", - "showlegend": true, - "type": "scattergl", - "x": [ - -50.70537 - ], - "xaxis": "x", - "y": [ - -10.254759 - ], - "yaxis": "y" - } - ], - "layout": { - "height": 500, - "legend": { - "title": { - "text": "label" - }, - "tracegroupgap": 0 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Nearest neighbors of the chipset security article" - }, - "width": 600, - "xaxis": { - "anchor": "y", - "domain": [ - 0, - 1 - ], - "title": { - "text": "Component 0" - } - }, - "yaxis": { - "anchor": "x", - "domain": [ - 0, - 1 - ], - "title": { - "text": "Component 1" - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# a 2D chart of nearest neighbors of the chipset security article\n", - "chart_from_components(\n", - " components=tsne_components,\n", - " labels=chipset_security_labels,\n", - " strings=article_descriptions,\n", - " width=600,\n", - " height=500,\n", - " title=\"Nearest neighbors of the chipset security article\",\n", - " category_orders={\"label\": [\"Other\", \"Nearest neighbor (top 5)\", \"Source\"]},\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For the chipset security example, the 4 closest nearest neighbors in the full embedding space remain nearest neighbors in this compressed 2D visualization. The fifth is displayed as more distant, despite being closer in the full embedding space." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Should you want to, you can also make an interactive 3D plot of the embeddings with the function `chart_from_components_3D`. (Doing so will require recomputing the t-SNE components with `n_components=3`.)" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Recommendation_using_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Recommendation_using_embeddings.ipynb)." ] } ], diff --git a/examples/embeddings/Regression.ipynb b/examples/embeddings/Regression.ipynb index cf99894aa7..8d44cb97b4 100644 --- a/examples/embeddings/Regression.ipynb +++ b/examples/embeddings/Regression.ipynb @@ -4,90 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Regression using the embeddings\n", - "\n", - "Regression means predicting a number, rather than one of the categories. We will predict the score based on the embedding of the review's text. We split the dataset into a training and a testing set for all of the following tasks, so we can realistically evaluate performance on unseen data. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb).\n", - "\n", - "We're predicting the score of the review, which is a number between 1 and 5 (1-star being negative and 5-star positive)." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Babbage similarity embedding performance on 1k Amazon reviews: mse=0.38, mae=0.39\n" - ] - } - ], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "from sklearn.ensemble import RandomForestRegressor\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.metrics import mean_squared_error, mean_absolute_error\n", - "\n", - "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", - "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", - "\n", - "X_train, X_test, y_train, y_test = train_test_split(list(df.babbage_similarity.values), df.Score, test_size = 0.2, random_state=42)\n", - "\n", - "rfr = RandomForestRegressor(n_estimators=100)\n", - "rfr.fit(X_train, y_train)\n", - "preds = rfr.predict(X_test)\n", - "\n", - "\n", - "mse = mean_squared_error(y_test, preds)\n", - "mae = mean_absolute_error(y_test, preds)\n", - "\n", - "print(f\"Babbage similarity embedding performance on 1k Amazon reviews: mse={mse:.2f}, mae={mae:.2f}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dummy mean prediction performance on Amazon reviews: mse=1.77, mae=1.04\n" - ] - } - ], - "source": [ - "bmse = mean_squared_error(y_test, np.repeat(y_test.mean(), len(y_test)))\n", - "bmae = mean_absolute_error(y_test, np.repeat(y_test.mean(), len(y_test)))\n", - "print(f\"Dummy mean prediction performance on Amazon reviews: mse={bmse:.2f}, mae={bmae:.2f}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the embeddings are able to predict the scores with an average error of 0.39 per score prediction. This is roughly equivalent to predicting 2 out of 3 reviews perfectly, and 1 out of three reviews by a one star error." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You could also train a classifier to predict the label, or use the embeddings within an existing ML model to encode free text features." + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Regression_using_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Regression_using_embeddings.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -100,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb index 259355a987..78dbc35f35 100644 --- a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb +++ b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb @@ -4,166 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Semantic text search using embeddings\n", - "\n", - "We can search through all our reviews semantically in a very efficient manner and at very low cost, by simply embedding our search query, and then finding the most similar reviews. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "\n", - "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", - "df['babbage_search'] = df.babbage_search.apply(eval).apply(np.array)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Remember to use the documents embedding engine for documents (in this case reviews), and query embedding engine for queries. Note that here we just compare the cosine similarity of the embeddings of the query and the documents, and show top_n best matches." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Jamaican Blue beans: Excellent coffee bean for roasting. Our family just purchased another 5 pounds for more roasting. Plenty of flavor and mild on acidity when roasted to a dark brown bean and befor\n", - "\n", - "Good Buy: I liked the beans. They were vacuum sealed, plump and moist. Would recommend them for any use. I personally split and stuck them in some vodka to make vanilla extract. Yum!\n", - "\n", - "Fantastic Instant Refried beans: Fantastic Instant Refried Beans have been a staple for my family now for nearly 20 years. All 7 of us love it and my grown kids are passing on the tradition.\n", - "\n" - ] - } - ], - "source": [ - "from openai.embeddings_utils import get_embedding, cosine_similarity\n", - "\n", - "# search through the reviews for a specific product\n", - "def search_reviews(df, product_description, n=3, pprint=True):\n", - " embedding = get_embedding(product_description, engine='text-search-babbage-query-001')\n", - " df['similarities'] = df.babbage_search.apply(lambda x: cosine_similarity(x, embedding))\n", - "\n", - " res = df.sort_values('similarities', ascending=False).head(n).combined.str.replace('Title: ','').str.replace('; Content:', ': ')\n", - " if pprint:\n", - " for r in res:\n", - " print(r[:200])\n", - " print()\n", - " return res\n", - "res = search_reviews(df, 'delicious beans', n=3)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rustichella ROCKS!: Anything this company makes is worthwhile eating! My favorite is their Trenne.
Their whole wheat pasta is the best I have ever had.\n", - "\n", - "sooo good: tastes so good. Worth the money. My boyfriend hates wheat pasta and LOVES this. cooks fast tastes great.I love this brand and started buying more of their pastas. Bulk is best.\n", - "\n", - "Wonderful: Came quickly. Was plentiful and delicious and cheaper than in the store. You will enjoy it if you like thick pasta.\n", - "\n" - ] - } - ], - "source": [ - "res = search_reviews(df, 'whole wheat pasta', n=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can search through these reviews easily. To speed up computation, we can use a special algorithm, aimed at faster search through embeddings." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "great product, poor delivery: The coffee is excellent and I am a repeat buyer. Problem this time was with the UPS delivery. They left the box in front of my garage door in the middle of the drivewa\n", - "\n" - ] - } - ], - "source": [ - "res = search_reviews(df, 'bad delivery', n=1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we can see, this can immediately deliver a lot of value. In this example we show being able to quickly find the examples of delivery failures." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Extremely dissapointed: Hi,
I am very disappointed with the past shipment I received of the ONE coconut water. 3 of the boxes were leaking and the coconut water was spoiled.

Thanks." - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import statsmodels.api as sm\n", - "\n", - "\n", - "correlation = X_test[['percentile_cosine_similarity', 'Score']].corr().values[0,1]\n", - "print('Correlation between user & vector similarity percentile metric and review number of stars (score): %.2f%%' % (100*correlation))\n", - "\n", - "# boxplot of cosine similarity for each score\n", - "X_test.boxplot(column='percentile_cosine_similarity', by='Score')\n", - "plt.title('')\n", - "plt.show()\n", - "plt.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can observe a weak trend, showing that the higher the similarity score between the user and the product embedding, the higher the review score. Therefore, the user and product embeddings can weakly predict the review score - even before the user receives the product!\n", - "\n", - "Because this signal works in a different way than the more commonly used collaborative filtering, it can act as an additional feature to slightly improve the performance on existing problems." + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/User_and_product_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/User_and_product_embeddings.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -165,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/Visualize_in_2d.ipynb b/examples/embeddings/Visualize_in_2d.ipynb index acb6b886b7..4638b58e95 100644 --- a/examples/embeddings/Visualize_in_2d.ipynb +++ b/examples/embeddings/Visualize_in_2d.ipynb @@ -4,123 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Visualizing the embeddings in 2D\n", - "\n", - "We will use t-SNE to reduce the dimensionality of the embeddings from 2048 to 2. Once the embeddings are reduced to two dimensions, we can plot them in a 2D scatter plot. The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 1. Reduce dimensionality\n", - "\n", - "We reduce the dimensionality to 2 dimensions using t-SNE decomposition." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(1000, 2)" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import pandas as pd\n", - "from sklearn.manifold import TSNE\n", - "\n", - "# Load the embeddings\n", - "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", - "\n", - "# Convert to a list of lists of floats\n", - "matrix = df.babbage_similarity.apply(eval).to_list()\n", - "\n", - "# Create a t-SNE model and transform the data\n", - "tsne = TSNE(n_components=2, perplexity=15, random_state=42, init='random', learning_rate=200)\n", - "vis_dims = tsne.fit_transform(matrix)\n", - "vis_dims.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2. Plotting the embeddings\n", - "\n", - "We colour each review by its star rating, ranging from red to green." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can observe a decent data separation even in the reduced 2 dimensions. There seems to be a cluster of mostly negative reviews." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Text(0.5, 1.0, 'Amazon ratings visualized in language using t-SNE')" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import matplotlib\n", - "import numpy as np\n", - "\n", - "colors = [\"red\", \"darkorange\", \"gold\", \"turquoise\", \"darkgreen\"]\n", - "x = [x for x,y in vis_dims]\n", - "y = [y for x,y in vis_dims]\n", - "color_indices = df.Score.values - 1\n", - "\n", - "colormap = matplotlib.colors.ListedColormap(colors)\n", - "plt.scatter(x, y, c=color_indices, cmap=colormap, alpha=0.3)\n", - "for score in [0,1,2,3,4]:\n", - " avg_x = np.array(x)[df.Score-1==score].mean()\n", - " avg_y = np.array(y)[df.Score-1==score].mean()\n", - " color = colors[score]\n", - " plt.scatter(avg_x, avg_y, marker='x', color=color, s=100)\n", - "plt.title(\"Amazon ratings visualized in language using t-SNE\")" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Visualizing_embeddings_in_2D.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Visualizing_embeddings_in_2D.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -133,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/Visualize_in_3d.ipynb b/examples/embeddings/Visualize_in_3d.ipynb index ebbfdf635e..df79b02e9b 100644 --- a/examples/embeddings/Visualize_in_3d.ipynb +++ b/examples/embeddings/Visualize_in_3d.ipynb @@ -2,251 +2,16 @@ "cells": [ { "cell_type": "markdown", - "id": "983ef639-fbf4-4912-b593-9cf08aeb11cd", + "id": "b87d69b2", "metadata": {}, "source": [ - "# Visualizing the embeddings in 3D" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Visualizing_embeddings_in_3D.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Visualizing_embeddings_in_3D.ipynb)." ] - }, - { - "cell_type": "markdown", - "id": "9c9ea9a8-675d-4e3a-a8f7-6f4563df84ad", - "metadata": {}, - "source": [ - "The example uses [PCA](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html) to reduce the dimensionality fo the embeddings from 2048 to 3. Then we can visualize the data points in a 3D plot. The small dataset `dbpedia_samples.jsonl` is curated by randomly sampling 200 samples from [DBpedia validation dataset](https://www.kaggle.com/danofer/dbpedia-classes?select=DBPEDIA_val.csv)." - ] - }, - { - "cell_type": "markdown", - "id": "8df5f2c3-ddbb-4cc4-9205-4c0af1670562", - "metadata": {}, - "source": [ - "### 1. Load the dataset and query embeddings" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "133dfc2a-9dbd-4a5a-96fa-477272f7af5a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Categories of DBpedia samples: Artist 21\n", - "Film 19\n", - "Plant 19\n", - "OfficeHolder 18\n", - "Company 17\n", - "NaturalPlace 16\n", - "Athlete 16\n", - "Village 12\n", - "WrittenWork 11\n", - "Building 11\n", - "Album 11\n", - "Animal 11\n", - "EducationalInstitution 10\n", - "MeanOfTransportation 8\n", - "Name: category, dtype: int64\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
textcategory
0Morada Limited is a textile company based in ...Company
1The Armenian Mirror-Spectator is a newspaper ...WrittenWork
2Mt. Kinka (金華山 Kinka-zan) also known as Kinka...NaturalPlace
3Planning the Play of a Bridge Hand is a book ...WrittenWork
4Wang Yuanping (born 8 December 1976) is a ret...Athlete
\n", - "
" - ], - "text/plain": [ - " text category\n", - "0 Morada Limited is a textile company based in ... Company\n", - "1 The Armenian Mirror-Spectator is a newspaper ... WrittenWork\n", - "2 Mt. Kinka (金華山 Kinka-zan) also known as Kinka... NaturalPlace\n", - "3 Planning the Play of a Bridge Hand is a book ... WrittenWork\n", - "4 Wang Yuanping (born 8 December 1976) is a ret... Athlete" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import pandas as pd\n", - "samples = pd.read_json(\"dbpedia_samples.jsonl\", lines=True)\n", - "categories = sorted(samples[\"category\"].unique())\n", - "print(\"Categories of DBpedia samples:\", samples[\"category\"].value_counts())\n", - "samples.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "19874e3e-a216-48cc-a27b-acb73854d832", - "metadata": {}, - "outputs": [], - "source": [ - "from openai.embeddings_utils import get_embeddings\n", - "# NOTE: The following code will send a query of batch size 200 to /embeddings, cost about $0.2\n", - "matrix = get_embeddings(samples[\"text\"].to_list(), engine=\"text-similarity-babbage-001\")" - ] - }, - { - "cell_type": "markdown", - "id": "d410c268-d8a7-4979-887c-45b1d382dda9", - "metadata": {}, - "source": [ - "### 2. Reduce the embedding dimensionality" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "f5410068-f3da-490c-8576-48e84a8728de", - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.decomposition import PCA\n", - "pca = PCA(n_components=3)\n", - "vis_dims = pca.fit_transform(matrix)\n", - "samples[\"embed_vis\"] = vis_dims.tolist()" - ] - }, - { - "cell_type": "markdown", - "id": "b6565f57-59c6-4d36-a094-3cbbd9ddeb4c", - "metadata": {}, - "source": [ - "### 3. Plot the embeddings of lower dimensionality" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b17caad3-f0de-4115-83eb-55434a132acc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "864488447fdd46b4ae1f338d3b0afded", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%matplotlib widget\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "fig = plt.figure(figsize=(10, 5))\n", - "ax = fig.add_subplot(projection='3d')\n", - "cmap = plt.get_cmap(\"tab20\")\n", - "\n", - "# Plot each sample category individually such that we can set label name.\n", - "for i, cat in enumerate(categories):\n", - " sub_matrix = np.array(samples[samples[\"category\"] == cat][\"embed_vis\"].to_list())\n", - " x=sub_matrix[:, 0]\n", - " y=sub_matrix[:, 1]\n", - " z=sub_matrix[:, 2]\n", - " colors = [cmap(i/len(categories))] * len(sub_matrix)\n", - " ax.scatter(x, y, zs=z, zdir='z', c=colors, label=cat)\n", - "\n", - "ax.set_xlabel('x')\n", - "ax.set_ylabel('y')\n", - "ax.set_zlabel('z')\n", - "ax.legend(bbox_to_anchor=(1.1, 1))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a8868043-9889-4a0b-b23d-79bb3823bdc7", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3.9.9 ('openai')", "language": "python", "name": "python3" }, @@ -261,6 +26,11 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.9" + }, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } } }, "nbformat": 4, diff --git a/examples/embeddings/Zero-shot_classification.ipynb b/examples/embeddings/Zero-shot_classification.ipynb index d539b256b6..d63561879a 100644 --- a/examples/embeddings/Zero-shot_classification.ipynb +++ b/examples/embeddings/Zero-shot_classification.ipynb @@ -4,205 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Zero-shot classification using the embeddings\n", - "\n", - "In this notebook we will classify the sentiment of reviews using embeddings and zero labeled data! The dataset is created in the [Obtain_dataset Notebook](Obtain_dataset.ipynb).\n", - "\n", - "We'll define positive sentiment to be 4 and 5-star reviews, and negative sentiment to be 1 and 2-star reviews. 3-star reviews are considered neutral and we won't use them for this example.\n", - "\n", - "We will perform zero-shot classification by embedding descriptions of each class and then comparing new samples to those class embeddings." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "from sklearn.metrics import classification_report\n", - "\n", - "df = pd.read_csv('output/embedded_1k_reviews.csv')\n", - "df['babbage_similarity'] = df.babbage_similarity.apply(eval).apply(np.array)\n", - "df['babbage_search'] = df.babbage_search.apply(eval).apply(np.array)\n", - "\n", - "df= df[df.Score!=3]\n", - "df['sentiment'] = df.Score.replace({1:'negative', 2:'negative', 4:'positive', 5:'positive'})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Zero-Shot Classification\n", - "To perform zero shot classification, we want to predict labels for our samples without any training. To do this, we can simply embed short descriptions of each label, such as positive and negative, and then compare the cosine distance between embeddings of samples and label descriptions. \n", - "\n", - "The highest similarity label to the sample input is the predicted label. We can also define a prediction score to be the difference between the cosine distance to the positive and to the negative label. This score can be used for plotting a precision-recall curve, which can be used to select a different tradeoff between precision and recall, by selecting a different threshold." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " precision recall f1-score support\n", - "\n", - " negative 0.67 0.88 0.76 136\n", - " positive 0.98 0.93 0.95 789\n", - "\n", - " accuracy 0.92 925\n", - " macro avg 0.82 0.90 0.86 925\n", - "weighted avg 0.93 0.92 0.92 925\n", - "\n" - ] - }, - { - "data": { - "image/png": "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FzscsyqVfP0tAHO/WcZWrVrW+PkbdBPT0qVLad26tZODZSaJ1q1bu9Zp9cI7M+YCMGzylwU5f4NOEICTg601/5ux+uLRc/cp6PkbfIIwM2uoWjQr7Ee4E0SBff7555xyyil06dKFPffck6OOOoopU6Ywc+ZMdt111xq7Tv/+/XnttdcAGDlyJN26daNHjx6UlJRw0kknrde5I4KDDjqIhQsXrlr37LPPIokPP/xw1bqZM2ey4YYb0qNHD7p27cpFF11EWVnZel37zTffZI899qBZs2YMHjy40v3GjBnDD37wA3bccUcuv/xyIpJZcefNm8ehhx7KTjvtxKGHHsrXX38NwIsvvkj//v3XKzazhs4JooAiguOPP57evXszffp0xowZw0033cQXX3xR49caMGAAhxxyCACPPfYY1157LePGjaN9+/ZVfrBWtHLlmg/Nv/zyy3Tv3p1NN/3u2chBgwax//77M2jQoNX27dKlC+PGjWPChAl88MEHPPvss+tWoFTHjh156KGHOO2006rc7+KLL+Yvf/kLU6dOZerUqbz66qsA3HzzzRx88MFMnTqVgw8+mJtvvhmAPn368MILL7BkyZL1is+sIWvQdzHluuGFSXzw2cLqd1wLXbfdlF8d3a3S7cOHD6d58+ZcdNFFq9Z1794dSL5tl5s5cyZnnnkm33yTTLdx11130atXL2bPns3JJ5/MwoULWblyJffccw+9evXi3HPPZfTo0UjinHPO4Re/+AX9+vWjb9++zJ8/n6eeeoohQ4bwyiuvcOONN9K3b18mTpxIaWkp11xzDSNGjGDZsmVceumlXHjhhYwYMYLrr7+eLbbYgg8//JApU6asVo7HHnuMCy64YNXy4sWLeeuttxg+fDhHH300N9xwwxplb9asGb169WLatGnr9N6W69SpEwBNmlT+XWb27NksXLiQH/7whwCcddZZPPvssxx55JE899xzjBgxAoCzzz6b3r17c8sttyCJ3r178+KLL/LTn/50vWI0a6gaTYIohokTJ7LnnntWu99WW23F0KFDadmyJVOnTuXUU09l9OjRPP744xx++OFcd911lJaWsmTJEsaNG0dJSQkTJ04EYP78+aud67zzzuOtt96ib9++nHTSSaslogceeIDNNtuMUaNGsWzZMvbbbz8OO+wwAMaOHcvEiRPz3tr59ttvc999961afu655zjiiCPYeeedad26NWPGjFmjnEuWLGHYsGEMGDBgjfMdcMABLFq0aI31t91226pa0NooKSmhQ4cOq5Y7dOhASUkJAF988QXt2rUDYJtttlmt9tazZ09GjhzpBGFWiUaTIKr6pl9sK1as4LLLLmPcuHE0bdp01Tf4vfbai3POOYcVK1Zw3HHH0aNHD3bYYQdmzJjBz3/+c/r06bPqAz6Lf/zjH0yYMGFVk9OCBQuYOnUqLVq0YO+99670vv958+bRqlWrVcuDBg3iiiuuAOCUU05h0KBBqxLE9OnT6dGjB5I49thjOfLII9c438iRIzPHXJMkrXaH0lZbbcVnn31WlFjM6oNGkyCKoVu3bpna///whz+w9dZbM378eMrKymjZMnng5Uc/+hFvvvkmL730Ev369ePKK6/krLPOYvz48QwZMoR7772Xp556igcffDBTPBHBn/70Jw4//PDV1o8YMYKNN9640uOaNWtGWVkZTZo0Yd68ebz++uu8//77SKK0tBRJ3HrrrcB3fRBVqekaRPv27Zk1a9aq5VmzZtG+fXsAtt56a2bPnk27du2YPXs2W2211ar9li5dyoYbbrjW1zNrLNxJXUAHHXQQy5Yt4/7771+1bsKECWt8g16wYAHt2rWjSZMmPPLII5SWlgLw8ccfs/XWW3P++edz3nnnMXbsWL766ivKyso48cQT+e1vf8vYsWMzx3P44Ydzzz33sGJFMpL7lClTVvV7VOV73/seM2bMAGDw4MGceeaZfPzxx8ycOZNPP/2Uzp07r1WtYOTIkYwbN26Nn3VJDgDt2rVj00035d133yUiePjhhzn22GMBOOaYYxg4cCAAAwcOXLUekvLX5J1kZsXyzbLCzMjgBFFAkvj73//Oa6+9RpcuXejWrRvXXnst22yzzWr7XXLJJQwcOJDu3bvz4Ycfrvo2P2LECLp3787uu+/Ok08+yRVXXEFJSQm9e/emR48enHHGGdx0002Z4znvvPPo2rUre+yxB7vuuisXXnhh3ruWKurTp8+qjt5BgwZx/PHHr7b9xBNPXONuppoyatQoOnTowNNPP82FF15It27fNRX26NFj1eu7776b8847jx133JEuXbqsatq65pprGDp0KDvttBOvvfYa11xzzapjhg8fTp8+fQoSt1ltWLI8+f/725cmF+T8Kr9fvL7r2bNnjB49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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "from openai.embeddings_utils import cosine_similarity, get_embedding\n", - "from sklearn.metrics import PrecisionRecallDisplay\n", - "\n", - "def evaluate_emeddings_approach(\n", - " labels = ['negative', 'positive'], \n", - " engine = 'text-similarity-babbage-001',\n", - "):\n", - " label_embeddings = [get_embedding(label, engine=engine) for label in labels]\n", - "\n", - " def label_score(review_embedding, label_embeddings):\n", - " return cosine_similarity(review_embedding, label_embeddings[1]) - cosine_similarity(review_embedding, label_embeddings[0])\n", - "\n", - " engine_col_name = engine.replace('-','_').replace('_query','')\n", - " probas = df[engine_col_name].apply(lambda x: label_score(x, label_embeddings))\n", - " preds = probas.apply(lambda x: 'positive' if x>0 else 'negative')\n", - "\n", - " report = classification_report(df.sentiment, preds)\n", - " print(report)\n", - "\n", - " display = PrecisionRecallDisplay.from_predictions(df.sentiment, probas, pos_label='positive')\n", - " _ = display.ax_.set_title(\"2-class Precision-Recall curve\")\n", - "\n", - "evaluate_emeddings_approach(labels=['negative', 'positive'], engine='text-similarity-babbage-001')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that this classifier already performs extremely well. We used similarity embeddings, and the simplest possible label name. Let's try to improve on this by using more descriptive label names, and search embeddings." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " precision recall f1-score support\n", - "\n", - " negative 0.65 0.93 0.76 136\n", - " positive 0.99 0.91 0.95 789\n", - "\n", - " accuracy 0.92 925\n", - " macro avg 0.82 0.92 0.86 925\n", - "weighted avg 0.94 0.92 0.92 925\n", - "\n" - ] - }, - { - "data": { - "image/png": "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3fn+97/Pe++9x+zZs9lpp53q7DxXXnklzzzzDADjx4+nT58+9OvXj3nz5nH88cfX6tgRwf7778/SpUsryh5++GEk8c4771SUzZ49m9atW9OvXz969+7NueeeS1nZN+Z5WicvvPACu+22G82bN2fUqKpHfp84cSI777wz2267LRdeeCERyay4Cxcu5KCDDmK77bbjoIMOYtGiRQA8/vjjXHnllVUez8ycIAoqIjjmmGMYNGgQs2bNYuLEiVxzzTV88skndX6u4cOHc+CBBwJw7733ctlllzF58mS6dOlS7QdrZWvWfPOh+SeffJK+ffvStu3Xz0aOHDmSfffdl5EjR661b69evZg8eTJTp07lrbfe4uGHH16/CqW6devGXXfdxcknn1ztfueddx5/+ctfmDFjBjNmzODpp58G4Nprr+WAAw5gxowZHHDAAVx77bUADB48mMcee4zly5fXKj6zpqxJ38WU6+rHpvPWR0tr3nEd9N6qLb88ok+V28eOHUuLFi0499xzK8r69u0LJN+2y82ePZtTTz2VL79Mptu4+eabGTBgAPPnz+fEE09k6dKlrFmzhltuuYUBAwbwox/9iAkTJiCJM888k5/97GcMHTqUww8/nMWLF/PAAw8wevRonnrqKX7zm99w+OGHM23aNEpLS7n00ksZN24cq1at4ic/+QnnnHMO48aN44orrmCzzTbjnXfe4b333lurHvfeey9nn312xfqyZct48cUXGTt2LEcccQRXX331N+revHlzBgwYwMyZM9frvS3Xo0cPAJo1q/q7zPz581m6dCl77bUXAKeddhoPP/wwhx12GI888gjjxo0D4PTTT2fQoEFcd911SGLQoEE8/vjjnHDCCbWK0ayp2mASRDFMmzaN3Xffvcb9tthiC8aMGUOrVq2YMWMGQ4YMYcKECfztb3/jkEMO4fLLL6e0tJTly5czefJk5s2bx7Rp0wBYvHjxWsc666yzePHFFzn88MM5/vjj10pEd9xxB+3ateP1119n1apV7LPPPhx88MEATJo0iWnTpuW9tfOll17itttuq1h/5JFHOPTQQ9l+++3p0KEDEydO/EY9ly9fzrPPPsvw4cO/cbyBAwfyxRdffKP8hhtuqLgKWhfz5s2ja9euFetdu3Zl3rxkdttPPvmEzp07A7DllluudfXWv39/xo8f7wRhVoUNJkFU902/2FavXs3555/P5MmTKSkpqfgGv8cee3DmmWeyevVqjj76aPr168c222zD+++/zwUXXMDgwYMrPuCz+Oc//8nUqVMrmpyWLFnCjBkzaNmyJXvuuWeV9/0vXLiQTTfdtGJ95MiR/PSnPwXgpJNOYuTIkRUJYtasWfTr1w9JHHXUURx22GHfON748eMzx1yXJK11h9IWW2zBRx99VJRYzBqDDSZBFEOfPn0ytf//4Q9/oFOnTkyZMoWysjJatWoFwHe/+11eeOEFnnjiCYYOHcpFF13EaaedxpQpUxg9ejS33norDzzwAHfeeWemeCKCP/7xjxxyyCFrlY8bN442bdpU+brmzZtTVlZGs2bNWLhwIc899xxvvvkmkigtLUUS119/PfB1H0R16voKokuXLsydO7dife7cuXTp0gWATp06MX/+fDp37sz8+fPZYostKvZbuXIlrVu3XufzmTUUf/vXHADGvvMpB/buVOfHdyd1Ae2///6sWrWK22+/vaJs6tSp3/gGvWTJEjp37kyzZs245557KC0tBeCDDz6gU6dODBs2jLPOOotJkybx2WefUVZWxnHHHcevf/1rJk2alDmeQw45hFtuuYXVq5OR3N97772Kfo/qfPvb3+b9998HYNSoUZx66ql88MEHzJ49mw8//JCePXuu01XB+PHjmTx58jd+1ic5AHTu3Jm2bdvy6quvEhHcfffdHHXUUQAceeSRjBgxAoARI0ZUlENS/7q8k8ysvp2//7YAzP685v/H68MJooAk8Y9//INnnnmGXr160adPHy677DK23HLLtfb78Y9/zIgRI+jbty/vvPNOxbf5cePG0bdvX3bddVfuv/9+fvrTnzJv3jwGDRpEv379OOWUU7jmmmsyx3PWWWfRu3dvdtttN3baaSfOOeecvHctVTZ48OCKjt6RI0dyzDHHrLX9uOOO+8bdTHXl9ddfp2vXrjz44IOcc8459OnzdVNhv379Kpb//Oc/c9ZZZ7HtttvSq1eviqatSy+9lDFjxrDddtvxzDPPcOmll1a8ZuzYsQwePLggcZvVhwG9OgDw6yfeLsjxVX6/eGPXv3//mDBhwlplb7/9NjvuuGORImo65s+fz2mnncaYMWOKHUqd+eSTTzj55JN59tln8273vx1rDMrKgm3+60mGDezJ5YN7r9cxJE2MiP75trkPwmrUuXNn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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "evaluate_emeddings_approach(labels=['An Amazon review with a negative sentiment.', 'An Amazon review with a positive sentiment.'], engine='text-similarity-babbage-001')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the search embeddings and descriptive names leads to an additional improvement in performance." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " precision recall f1-score support\n", - "\n", - " negative 0.77 0.79 0.78 136\n", - " positive 0.96 0.96 0.96 789\n", - "\n", - " accuracy 0.94 925\n", - " macro avg 0.87 0.88 0.87 925\n", - "weighted avg 0.94 0.94 0.94 925\n", - "\n" - ] - }, - { - "data": { - "image/png": "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oZhVafbCpYx+77Oinb9eJ4hZs2bRokULOnXqhKRSh2N1QETwxRdfMGvWLDp37lzqcMwKmv/1ClavCRo3qv7PuHrdxLRs2TJatWrl5GCZSaJVq1audVqdcMCubYt6/nqdIAAnB1tv/puxuqLndlsV9fz1PkGYmdmGcYIosk8++YSTTz6ZLl26sOeee3LEEUcwdepUZs6cyW677VZt17nuuut44YUXABg7dizdunWjZ8+ezJ49mxNOOGGjzh0RHHDAASxevLh82xNPPIEk3n333fJtM2fOZLPNNqNnz5507dqVAQMGsGbNxg1F/PLLL7PHHnvQpEkThg6tfFiu8ePHs/vuu7PTTjtx+eWXExEAzJ8/n4MPPpidd96Zgw8+mAULFgDw9NNPc91111V6PjNzgiiqiODYY4+lb9++vP/++4wfP55bbrmFTz/9tNqvNXDgQA466CAAHnnkEa655homTJhAhw4dqvxgrWjVqlXrbBs+fDg9evSgZcuW5duGDBnC97//fYYMGbLWsV26dGHChAlMmjSJt99+myeeeGLDCpTafvvtefDBBzn11FOrPO6iiy7ij3/8I9OmTWPatGk899xzANx6660ceOCBTJs2jQMPPJBbb70VgH79+vHUU0+xZMmSjYrPrD6r13cx5brxqSm8PWdx4QPXQ9dtW3L9kd0q3T969GiaNm3KgAEDyrf16NEDSL5tl5k5cyZnnHEGX3/9NQC/+93v6NOnD3PnzuWkk05i8eLFrFq1ij/84Q/06dOHc889l3HjxiGJc845h5/85CecddZZ9O/fn4ULF/LYY48xYsQInn32WX7xi1/Qv39/Jk+ezOrVq7n66qsZM2YMy5cv55JLLuHCCy9kzJgxXHvttWy99da8++67TJ06da1yPPLII1xwwQXl61999RWvvPIKo0eP5sgjj+TGG29cp+xNmjShT58+TJ8+fYPe2zKdOnUCoFGjyr/LzJ07l8WLF7P33nsDcOaZZ/LEE09w+OGH8+STTzJmzBgAfvzjH9O3b19uu+02JNG3b1+efvppTjzxxI2K0ay+ajAJohQmT57MnnvuWfC4tm3bMnLkSJo1a8a0adM45ZRTGDduHH/5y1849NBD+fnPf87q1atZsmQJEyZMYPbs2UyePBmAhQsXrnWu8847j1deeYX+/ftzwgknrJWIHnjgAbbccktef/11li9fzr777sshhxwCwBtvvMHkyZPz3tr5z3/+k/vuu698/cknn+Swww5jl112oVWrVowfP36dci5ZsoQXX3yRgQMHrnO+/fbbjy+//HKd7XfccUd5LWh9zJ49m44dO5avd+zYkdmzk6lHPv30U9q3bw/ANttss1btrVevXowdO9YJwqwSDSZBVPVNv9RWrlzJpZdeyoQJE2jcuHH5N/i99tqLc845h5UrV3LMMcfQs2dPdtxxR2bMmMFll11Gv379yj/gs3j++eeZNGlSeZPTokWLmDZtGptssgm9e/eu9L7/+fPn06JFi/L1IUOGcMUVVwBw8sknM2TIkPIE8f7779OzZ08kcfTRR3P44Yevc76xY8dmjrk6SVrrDqW2bdsyZ86cksRiVhc0mARRCt26dcvU/v/rX/+adu3aMXHiRNasWUOzZskUgj/4wQ94+eWXeeaZZzjrrLO48sorOfPMM5k4cSIjRozg3nvv5bHHHmPQoEGZ4okI7r77bg499NC1to8ZM4Ytttii0tc1adKENWvW0KhRI+bPn8+oUaN46623kMTq1auRxO233w580wdRlequQXTo0IFZs2aVr8+aNYsOHZLpz9u1a8fcuXNp3749c+fOpW3bb+4bX7ZsGZttttl6X8+soXAndREdcMABLF++nPvvv79826RJk9b5Br1o0SLat29Po0aNePjhh1m9OpmW+8MPP6Rdu3acf/75nHfeebzxxht8/vnnrFmzhuOPP56bb76ZN954I3M8hx56KH/4wx9YuXIlAFOnTi3v96jKt7/9bWbMmAHA0KFDOeOMM/jwww+ZOXMmH3/8MZ07d16vWsHYsWOZMGHCOj8bkhwA2rdvT8uWLXn11VeJCB566CGOPvpoAI466igGDx4MwODBg8u3Q1L+6ryTzKxUxn+4oCjndYIoIkn84x//4IUXXqBLly5069aNa665hm222Wat4y6++GIGDx5Mjx49ePfdd8u/zY8ZM4YePXrw3e9+l0cffZQrrriC2bNn07dvX3r27Mnpp5/OLbfckjme8847j65du7LHHnuw2267ceGFF+a9a6mifv36lXf0DhkyhGOPPXat/ccff/w6dzNVl9dff52OHTvyt7/9jQsvvJBu3b5pKuzZs2f58u9//3vOO+88dtppJ7p06VLetHX11VczcuRIdt55Z1544QWuvvrq8teM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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "evaluate_emeddings_approach(labels=['An Amazon review with a negative sentiment.', 'An Amazon review with a positive sentiment.'], engine='text-similarity-babbage-001')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As shown above, zero-shot classification with embeddings can lead to great results, especially when the labels are more descriptive than just simple words." + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Zero-shot_classification_with_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Zero-shot_classification_with_embeddings.ipynb)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -215,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/embeddings/dbpedia_samples.jsonl b/examples/embeddings/dbpedia_samples.jsonl deleted file mode 100644 index 0e022faa1b..0000000000 --- a/examples/embeddings/dbpedia_samples.jsonl +++ /dev/null @@ -1,200 +0,0 @@ -{"text": " Morada Limited is a textile company based in Altham Lancashire. Morada specializes in curtains.", "category": "Company"} -{"text": " The Armenian Mirror-Spectator is a newspaper published by the Baikar Association in Watertown Massachusetts.", "category": "WrittenWork"} -{"text": " Mt. Kinka (\u91d1\u83ef\u5c71 Kinka-zan) also known as Kinkazan is located in the heart of the city of Gifu Gifu Prefecture Japan and rises to a height of 329 m (1079 ft). Previously called Mt. Inaba (\u7a32\u8449\u5c71 Inaba-yama) it has long served as the representative symbol of Gifu. It stands along the Nagara River creating bountiful nature within the city. Though it is the most famous mountain in the city Mount Dodo to the north is the tallest.", "category": "NaturalPlace"} -{"text": " Planning the Play of a Bridge Hand is a book on contract bridge co-written by Canadian teacher and author Barbara Seagram and British author David Bird. It was published by Master Point Press in 2009.The book teaches novice bridge players some basic techniques of declarer play including suit establishment ruffing losers and the finesse.", "category": "WrittenWork"} -{"text": " Wang Yuanping (born 8 December 1976) is a retired Chinese athlete who specialised in the 800 metres. She won several medals at the regional level.Her personal bests in the event are 2:00.63 seconds outdoors (Jinzhou 2000) and 2:03.41 seconds indoors (Yokohama 2004).", "category": "Athlete"} -{"text": " The Incorporated VIllage of Westhampton Beach is an incorporated village in the Town of Southampton Suffolk County New York United States. As of the 2010 census the village population was 1721.", "category": "Village"} -{"text": " Andersons Creek is a creek in Warrandyte and Park Orchards east of Melbourne Victoria Australia. It is a tributary of the Yarra River.", "category": "NaturalPlace"} -{"text": " The Three Horseshoes is a public house in Drybridge Street in the Overmonnow area of Monmouth Wales. The pub has also been used as an Inn and also known as The Three Horse Shoes Inn. The building has been a Grade II Listed building since 15 August 1974. 19th century 2 storeys roughcast as stone with a hooded doorway", "category": "Building"} -{"text": " The Brewer's Art is a Baltimore Maryland brewpub and restaurant. Opened on Friday September 13 1996. In 2008 it was named by Esquire magazine as the #1 Best Bar in America.", "category": "Company"} -{"text": " The P\u00e2r\u00e2ul S\u0103r\u0103\u021bii is a tributary of the Cibin River in Romania.", "category": "NaturalPlace"} -{"text": " Jean-Fran\u00e7ois Imbernon (born October 17 1951 in Perpignan France is a retired French international rugby union player.He played as a Lock for USA Perpignan. He earned his first cap with the French national team on 7 February 1976 against Ireland at Parc des Princes.", "category": "Athlete"} -{"text": " Le Cadeau released in Italy as Il regalo is a 1982 French and Italian film. It stars Claudia Cardinale.", "category": "Film"} -{"text": " Mykola Kanevets (Ukrainian: \u041c\u0438\u043a\u043e\u043b\u0430 \u041a\u0430\u043d\u0456\u0432\u0435\u0446\u044c) is the Artistic Director and Ballet Master of the Cheremosh Ukrainian Dance Company in Edmonton Alberta Canada.A native of Kiev Ukraine Mykola attended the National University of Culture and Performing Arts in Kiev Ukraine where he graduated from the Faculty of Choreography with the distinction of Ballet Master and Choreographer.", "category": "Artist"} -{"text": " Jenna Rose Swerdlow (born September 28 1998) is an American teenage singer who gained media attention as a pre-teen with her single My Jeans. After the video went viral on YouTube and received 14 million views Swerdlow is considered a semi-viral star.", "category": "Artist"} -{"text": " The Spice of Life is a smooth jazz studio album by Earl Klugh released in April 2008. The album received a Grammy nomination for Best Pop Instrumental Album at the 51st Grammy Awards in 2009.", "category": "Album"} -{"text": " Lomatium macrocarpum is a perennial flowering plant in the carrot family known by the common names bigseed lomatium biscuit root or even bigseed biscuitroot. It is native to much of western North America where it can be found in various types of habitat including the grasslands of the Great Plains. It is spreading or erect perennial herb growing up to about half a meter long with hairy gray-green herbage.", "category": "Plant"} -{"text": " Physena is the sole genus of the flowering plant family Physenaceae. It contains two species of shrubs and small trees which are endemic to Madagascar. The APG II system of 2003 (unchanged from the APG system of 1998) does recognize this family and assigns it to the order Caryophyllales in the clade core eudicots.", "category": "Plant"} -{"text": " David John Weatherley (born 1 March 1939) is a New Zealander actor known for his roles as Spencer the butler and the voice of Benglo the Fearcat in Power Rangers Operation Overdrive and Barliman Butterbur in The Lord of the Rings: The Fellowship of the Ring.Weatherley was born in London England and moved to Canada for a military career. He eventually moved to New Zealand to engage in a theatre acting career.", "category": "Artist"} -{"text": " Draba incrassata is an uncommon species of flowering plant in the mustard family known by the common name Sweetwater Mountains draba. It is endemic to California where it is known mainly from the Sweetwater Mountains of Mono County. It grows in alpine rock fields on the barren high mountain peaks. Draba incrassata is a small perennial herb forming mats of thick oval-shaped leaves.", "category": "Plant"} -{"text": " Pimelea ferruginea is a small shrub native to southwest Western Australia. It was described by Labillardiere in 1805.", "category": "Plant"} -{"text": " Lindsay Ell is a country music singer songwriter and guitarist from Calgary Alberta.She performed at the South by Southwest music festival held in Austin Texas in March 2009 the welcome reception of the 2009 Juno Awards held in Vancouver British Columbia and was also a featured artist at the 2010 Winter Olympics.", "category": "Artist"} -{"text": " Scopula fuscata is a moth of the Geometridae family. It is found from south-western Saskatchewan west to British Columbia and south to California and Arizona. The habitat consists of montane areas including foothills.The wingspan is 24-28 mm. The wings and body are light tan sprinkled with darker yellow-brown or grey-brown scales. There is one generation per year with adults on wing in late June and early July in the northern part of the range.", "category": "Animal"} -{"text": " Oxmoor Center is a Louisville Kentucky shopping mall located at 7900 Shelbyville Road in eastern Louisville.", "category": "Building"} -{"text": " Ghostquake (also known as Haunted High) is a 2012 American made-for-television horror film produced by Syfy. The film was directed by Jeffrey Lando and written by Paul A. Birkett and Anthony C. Ferrante. The film stars Danny Trejo and MC Gainey. It follows a group of high school students trying to escape the wrath of a few ghastly spirits following an earthquake at their school Holloman High School.", "category": "Film"} -{"text": " The Masonic Temple in Great Falls Montana is a building from 1914. It was listed on the National Register of Historic Places in 2000.Address is 821 Central Avenue Great Falls Motana 59401 Phone number is 453-9080.Cascade No. 34 meets 2nd and 4th Tuesdays at 7:30pm Sept-June.Euclid No. 58 meets year-round 1st and 3rd Tuesdays at 7:30pm Sept-May 3rd Tuesdays at 7:30pm June-Aug. Delta No. 128 meets 2nd Wednesdays at 7:30pm Sept-June.", "category": "Building"} -{"text": " Harold Frederick Weaver Hawkins (1893-1977) was an English painter who specialized in ambitious sometimes mural-sized modernist allegories of morality for an age of atomic warfare and global over-population.", "category": "Artist"} -{"text": " Robert Murray Waddington (24 October 1927 \u2013 15 March 2007) was Dean of Manchester in the last quarter of the 20th century.Born in Bognor Regis on 24 October 1927 he was educated at Dulwich College Selwyn College Cambridge and Ely Theological College. Ordained in 1954 he began his career at St John\u2019s Bethnal Green. Afterwards he was Chaplain at Slade School in Warwick Queensland. He returned to England in 1959 to join the Oratory of the Good Shepherd an order of celibate priests.", "category": "OfficeHolder"} -{"text": " Jason Gary King (born 13 April 1985 in Maidstone England) is a speedway rider who was formerly the club captain of Newcastle Diamonds in the British Premier League. His brother Daniel is also a speedway rider.", "category": "Athlete"} -{"text": " The African Queen is a 1951 adventure film adapted from the 1935 novel of the same name by C. S. Forester. The film was directed by John Huston and produced by Sam Spiegel and John Woolf. The screenplay was adapted by James Agee John Huston John Collier and Peter Viertel. It was photographed in Technicolor by Jack Cardiff and had a music score by Allan Gray.", "category": "Film"} -{"text": " The Fiat Barchetta (Italian pronunciation: [\u02c8fiat bar\u02c8ketta]) (Type 183) is a roadster produced by the Italian manufacturer Fiat from 1995 to 2005 (though production was paused between May 2002 and 2004). Barchetta in Italian means 'little boat'.", "category": "MeanOfTransportation"} -{"text": " Sardar Vallabhbhai Patel National Memorial is a museum and exhibition centre dedicated to Sardar Vallabhbhai Patel at Moti Shahi Mahal located in Shahibaug Ahmedabad Gujarat. Moti Shahi Mahal was constructed by Mughal emperor Shahjahan between 1618 and 1622. It is surrounded by a garden.", "category": "Building"} -{"text": " Under Cover 2 is the 5th solo album of Joe Lynn Turner released in 1999. Just like Under Cover the album consists mainly of covers of Turner's favourite artists.", "category": "Album"} -{"text": " The Atakora River is a tributary of Lake Volta in Ghana it flows about 60 km east to the Lake Volta. Its entire course is in south Ghana.", "category": "NaturalPlace"} -{"text": " Death from Above is a 2011 horror film by director Bruce Koehler. The film features professional wrestling stars Kurt Angle Sid Eudy James Storm Matt Morgan Terry Gerin and Jessica Kresa.", "category": "Film"} -{"text": " Portraits of Cuba is an album by Cuban musician Paquito D'Rivera released through Chesky Records in 1996. In 1997 the album won D'Rivera the Grammy Award for Best Latin Jazz Performance.", "category": "Album"} -{"text": " Jimmy Cross (17 November 1938 - 8 October 1978) was an American radio producer and singer who attained a minor Billboard Hot 100 hit with the novelty song I Want My Baby Back in 1965. He was born in Dothan Alabama[citation needed] and became the producer of the syndicated radio series Country Concert.I Want My Baby Back was originally issued on the Tollie label and reached #92 on the Billboard Hot 100 in February 1965.", "category": "Artist"} -{"text": " Timothy Floyd Tim Burchett (born August 25 1964) is an American Republican politician currently the mayor of Knox County Tennessee. He previously served in Tennessee General Assembly first in the Tennessee House of Representatives and later in the Tennessee State Senate in which he represented Tennessee's District 7 part of Knox County. On August 5 2010 Burchett was elected mayor of Knox County replacing Mike Ragsdale.", "category": "OfficeHolder"} -{"text": " Daniel Lawrence Dan Whitney (born February 17 1963) best known by his stage name and character Larry the Cable Guy is an American stand-up comedian actor voice actor and former radio personality.", "category": "Artist"} -{"text": " Renealmia is a plant genus in the family Zingiberaceae. Species include: Renealmia alpinia Renealmia aurantifera Renealmia cernua Renealmia dolichocalyx Renealmia oligotricha Renealmia sessilifolia Renealmia thrysoidesE.g. Alpinia nutans was formerly placed herein too.", "category": "Plant"} -{"text": " Jeff Chapman (born July 17 1969 in Brunswick Georgia) is the bass singer for the Kingdom Heirs. He has been a member of the group since 2002. He has previously traveled with Bob Wills The Sound The Anchormen and The Blackwoods.He has twice been nominated as favorite bass in the Singing News fan awards.Chapman has a wife Angie two sons Justin and Sean and daughter Taylor.", "category": "Artist"} -{"text": " Arenaria ursina is a species of flowering plant in the pink family known by the common name Bear Valley sandwort.", "category": "Plant"} -{"text": " Living Fossil is a classic science fiction story on the concepts of human extinction and future evolution by L. Sprague de Camp. It was first published in the magazine Astounding Science-Fiction for February 1939. It first appeared in book form in the anthology A Treasury of Science Fiction (Crown Publishers 1948); it later appeared in the anthologies Gates to Tomorrow (Atheneum 1973) and The SFWA Grand Masters Volume 1 (Tor Books 1999).", "category": "WrittenWork"} -{"text": " Brachyglottis huntii commonly called rautini or Chatham Island Christmas tree is a species in the Asteraceae family and is found only on the Chatham Islands in New Zealand.", "category": "Plant"} -{"text": " Luktvatnet is a lake that lies in the northern part of the municipality of Vefsn in Nordland county Norway. The lake lies between the mountains Korgfjellet and Lukttinden about 5 kilometres (3.1 mi) south of Elsfjord. The European route E06 highway passes along the northern shore of the lake.", "category": "NaturalPlace"} -{"text": " The IAR 79 is a bi-engine bomber military reconnaissance aircraft with a wood and metal structure that saw service in World War II built under licence in Brasov Romania by Industria Aeronautic\u0103 Rom\u00e2n\u0103", "category": "MeanOfTransportation"} -{"text": " Enrico Perucconi (born 4 January 1925 in Morazzone Varese Italy) was an Italian athlete who competed mainly in the 100 metres.", "category": "Athlete"} -{"text": " Central National-Gottesman Inc. is one of the world's largest distributors of pulp paper paperboard and newsprint. The firm's products are sold in over 75 countries through a network of 43 offices located in the United States and abroad. With annual revenues exceeding $3 billion Forbes ranked Central National-Gottesman 137th in its annual list of The Largest Private Companies.", "category": "Company"} -{"text": " The Kout Food Group is a Kuwaiti-based conglomerate founded in 1982.In Kuwait it operates franchises of Burger King Pizza Hut and Taco Bell.Its UK arm Kout Food Group Restaurants UK it operates under brands such as Burger King KFC and Maison Blanc. In August 2013 it acquired the Little Chef chain for \u00a315 million.", "category": "Company"} -{"text": " Fab Five: The Texas Cheerleader Scandal is a Lifetime Television made-for-TV drama film starring Jenna Dewan Ashley Benson and Tatum O'Neal and directed by Tom McLoughlin. The film premiered on August 2 2008. It is based on a true story which occurred at McKinney North High School in McKinney Texas in 2006 in which five teenage cheerleaders became notorious for bullying truancies violations of the school dress code and general disrespect to the school community and authority.", "category": "Film"} -{"text": " Qadi Mahalleh (Persian: \u0642\u0627\u062f\u064a \u0645\u062d\u0644\u0647\u200e also Romanized as Q\u0101d\u012b Ma\u1e29alleh) is a village in Pazevar Rural District Rudbast District Babolsar County Mazandaran Province Iran. At the 2006 census its population was 228 in 59 families.", "category": "Village"} -{"text": " Eungella Dam is one of Queensland's more established freshwater fisheries. Eungella has made a name for producing extra oversized Sooty grunter and more recently Barramundi.Eungella Dam was constructed in 1969 to meet the requirements of a thermal power station at Collinsville and the town water requirement of Collinsville and Scottsville.", "category": "NaturalPlace"} -{"text": " The American Motor Car Company was a short-lived company in the automotive industry founded in 1906 lasting until 1913. It was based in Indianapolis Indiana United States. The American Motor Car Company pioneered the underslung design.", "category": "Company"} -{"text": " Hawkeye & Mockingbird was a comic book ongoing series published by Marvel Comics starring superheroes Hawkeye and Mockingbird.", "category": "WrittenWork"} -{"text": " Margaret Anderson Kelliher (born March 11 1968) is a Minnesota politician and a former member of the Minnesota House of Representatives. A member of the Minnesota Democratic\u2013Farmer\u2013Labor Party she represented District 60A which includes portions of the city of Minneapolis in Hennepin County located in the Twin Cities metropolitan area. First elected in 1999 she served until 2011 also serving as the Speaker from 2007 to 2011.", "category": "OfficeHolder"} -{"text": " John Whitlow Wyatt (September 27 1907 \u2013 July 16 1999) was a professional baseball pitcher. He played all or part of sixteen seasons in Major League Baseball for the Detroit Tigers (1929\u201333) Chicago White Sox (1933\u201336) Cleveland Indians (1937) Brooklyn Dodgers (1939\u201344) and Philadelphia Phillies (1945). While injuries sidetracked much of Wyatt's early career he is most famous for his performance in 1941 when his team (the Dodgers) won the National League pennant.", "category": "Athlete"} -{"text": " William Thomas Burton (31 January 1878 in Black Rock St Michael Barbados \u2013 22 August 1946 St Michael Barbados) was a coloured West Indian cricketer best known as a member of the 1900 and 1906 West Indian tourists to England. He is generally known as Tommie Burton.He was the son of a black mother and a white father. He was brought up in Barbados and served for some years there as a practice bowler and in trial matches.", "category": "Athlete"} -{"text": " Tulemalu Lake is a lake in Kivalliq Region Nunavut Canada.", "category": "NaturalPlace"} -{"text": " Sten Stjernqvist is a Swedish former footballer who played as a forward.", "category": "Athlete"} -{"text": " David Parlett (born 1939) is a games scholar from South London who has studied both card games and board games. His published works include many popular books on games and the more academic volumes The Oxford Guide to Card Games and The Oxford History of Board Games both now out of print. Parlett also invented a number of board games the most successful of which is Hare and Tortoise (1974). The German edition was awarded Spiel des Jahres (Game of the Year) in 1979.", "category": "Artist"} -{"text": " Karl Nabersberg (sometimes written as Carl Nabersberg) was a German youth leader.Nabersberg was the son of a Crefeld shopkeeper. In 1923 he joined the Jugendorganisation the forerunner of the Hitler Youth in his home town. On 28 December 1925 he was admitted as a member of the National Socialist German Workers' Party (member number 26269) and as a member of the Sturmabteilung.", "category": "OfficeHolder"} -{"text": " \u0160etonje is a village situated in Petrovac na Mlavi municipality in Serbia.", "category": "Village"} -{"text": " Dr. Joseph de Graft-Johnson (1933\u20131999) was an engineer academic and politician. He became the Vice-President of Ghana between 1979 and 1981.", "category": "OfficeHolder"} -{"text": " Patties Foods (previously Patties Bakery) is an Australian food manufacturing company that produces meat pies baked goods frozen fruits and pre-made desserts. Headquartered in Bairnsdale Victoria Australia Patties Foods is represented in the Australian market by the Four'N Twenty Patties Herbert Adams Creative Gourmet Nanna's and Chefs Pride brands. Patties is the largest meat pie producing company in Australia and the world.", "category": "Company"} -{"text": " Double Butte is the 2579-foot (786 m) mountain summit distinguished by two buttes (the other at abou 2480 feet or 756 metres) in Riverside County California. It is the western most summit of a mountain range north of Winchester California east of Perris Valley and west of the San Jacinto Valley. The eastern ridge is composed primarily of metamorphic rock of the Triassic - Jurassic French Valley formation.", "category": "NaturalPlace"} -{"text": " Mount Carmel \u2013 Blytheswood Public School is an elementary school in the north end of Leamington Ontario Canada. It is part of the Greater Essex County District School Board and serves students from JK to Grade 8 from the communities of Blytheswood and Mount Carmel and surrounding areas.", "category": "EducationalInstitution"} -{"text": " La combi asesina (The Killer Combination) is a 1982 Mexican film. It was directed by Gustavo Alatriste.", "category": "Film"} -{"text": " Halimium ocymoides (basil-leaved rock rose) syn. Cistus algarvensis is a species of flowering plant in the family Cistaceae native to southern Portugal and southern Spain. It is an erect evergreen shrub growing to 60 cm (24 in) tall by 100 cm (3 ft) wide with woolly grey-green leaves and bright yellow flowers in spring. The flowers may have a dark brown blotch at the base of each petal.In cultivation this plant requires a sandy soil and full sun.", "category": "Plant"} -{"text": " Kaala Patthar (English: Black Stone) is a 1979 Indian Bollywood action/drama film. It was produced and directed by Yash Chopra. The story was written by Salim-Javed. This film is the fourth collaboration between Amitabh Bachchan Shashi Kapoor and director Yash Chopra after the hugely successful Deewaar (1975) Kabhie Kabhie (1976) and Trishul (1978). However this film did average business at the box office. It was nominated for Filmfare awards.", "category": "Film"} -{"text": " Martin G.S. Mansergh (born 31 December 1946) is a former Irish Fianna F\u00e1il politician and historian. He was a Teachta D\u00e1la (TD) for the Tipperary South constituency from 2007 until 2011. He was previously a Senator from 2002 to 2007. He played a leading role in formulating Fianna F\u00e1il policy on Northern Ireland.", "category": "OfficeHolder"} -{"text": " Shriniwas Ganesh Sardesai (1907-1996) popularly known as S.G. Sardesai was an Indian freedom fighter from Maharashtra and one of the great communist leaders produced by the communist movement in India. He is author of the book Progress and conservatism in ancient India famous for his profound theoretical analysis. He was the Central Executive Committee of pre-split Communist Party of India during the Indo-china conflict.", "category": "OfficeHolder"} -{"text": " USS Tuluran (AG-46) \u2013 also known as USS Lake Superior (ID-2995) \u2013 was a commercial cargo ship acquired by the U.S. Navy for service during both World War I when she was known as USS Lake Superior and also during World War II when she was known as USS Tuluran.", "category": "MeanOfTransportation"} -{"text": " The American Journal of Gastroenterology is a peer-reviewed medical journal published for the American College of Gastroenterology by the Nature Publishing Group.", "category": "WrittenWork"} -{"text": " William Lindsay (September 4 1835 \u2013 October 15 1909) was a Democratic U.S. Senator from Kentucky from 1893 to 1901.Born near Lexington Virginia Lindsay attended the common schools and settled in Clinton Kentucky in 1854. There he taught school and studied law. He was admitted to the bar and commenced practice in Clinton in 1858.", "category": "OfficeHolder"} -{"text": " Brian Schroeder (a.k.a. Pushead) is an artist record label owner and writer within the hardcore punk and heavy metal field. He has created artwork for many bands artists and athletes including Metallica The Misfits Dr. Dre Travis Barker Craig Johnson and Kool Keith. He has designed many record covers T-shirts skateboards and a pair of Nike SB Dunks. His record label Pusmort Records has released albums by Negative Gain Poison Idea and Final Conflict.", "category": "Artist"} -{"text": " Panicum anceps is a species of grass known by the common name beaked panicgrass. It is native to the southeastern United States where it occurs as far north as New Jersey and as far west as Kansas and Texas.This species is a rhizomatous perennial grass with stems growing up to 1.3 meters tall. The leaves have erect blades up to half a meter tall. The inflorescence is a panicle up to 40 centimeters long bearing pale green or yellowish spikelets. The grass produces an abundance of seed.", "category": "Plant"} -{"text": " Shukan ST is a weekly newspaper published by The Japan Times for learners of English language. It is originally titled as Student Times but changed to Shukan ST since a significant portion of its readers are not students. It has articles on news movie lifestyle in English-speaking countries opinions and other kinds attracting learners of English and helping them with notes on terms.", "category": "Company"} -{"text": " The Tiger Hotel is a hotel in Columbia Missouri. Built as a hotel in 1928 the building later housed a retirement home and banquet center. In 2012 the building was fully restored and reopened as a boutique hotel. It was listed on the National Register of Historic Places in 1980.", "category": "Building"} -{"text": " Emi Motoi (\u672c\u4e95 \u3048\u307f Motoi Emi born October 11 in Kanagawa) is a Japanese voice actress.", "category": "Artist"} -{"text": " The Hudson River is a 49.5-mile-long (79.7 km) tributary of the Broad River in the U.S. state of Georgia. Via the Broad River it is part of the Savannah River watershed.The headwaters are in Banks County near the city of Homer. Grove Creek feeds into the Hudson near the Franklin County line. The river then constitutes most of the southern border of Franklin County separating it from Madison County.", "category": "NaturalPlace"} -{"text": " This article details Car Nos. 10\u201313 of the Manx Electric Railway on the Isle of Man.This was the third batch of motorcars delivered to the railway in 1895 at the same time as the cars for the new Snaefell Mountain Railway were delivered. They were constructed to a very similar design to those provided for the mountain line.", "category": "MeanOfTransportation"} -{"text": " Catharanthus roseus commonly known as the Madagascar rosy periwinkle is a species of Catharanthus native and endemic to Madagascar. Other English names occasionally used include Cape periwinkle rose periwinkle rosy periwinkle and old-maid.", "category": "Plant"} -{"text": " Thapanzeik is a village in Homalin Township Hkamti District in the Sagaing Region of northwestern Burma.", "category": "Village"} -{"text": " USS Spiegel Grove (LSD-32) was a Thomaston-class dock landing ship of the United States Navy. She was named for Spiegel Grove the home and estate in Fremont Ohio of Rutherford B. Hayes the 19th President of the United States.", "category": "MeanOfTransportation"} -{"text": " Acmella is a genus of thirty species of plants in the aster family Asteraceae. It is native to the Americas and has been introduced to Asia Africa the Pacific islands and Australia.One familiar species is Acmella oleracea which has been widely cultivated for centuries. It is used for food and medicine and as an insecticide and an ornamental plant.", "category": "Plant"} -{"text": " Mirbelia is a plant genus belonging to the Fabaceae family. It is endemic to Australia occurring in every mainland state except South Australia.", "category": "Plant"} -{"text": " Nigma puella is a species of spider belonging to the family Dictynidae. It is found in Europe Azores Madeira Canary Islands and parts of North Africa.Like most members of the family this is a small spider but the female is striking with a light green abdomen marked with a bold maroon blotch and a variable amount of barring in the same colour. The male is reddish-brown. This species makes a horizontal web over the top surface of a leaf.", "category": "Animal"} -{"text": " The Madrisa (or Madrisahorn) is a mountain in the R\u00e4tikon mountain range overlooking Klosters in the Swiss canton of Graub\u00fcnden. Its summit (2826 metres) is located near the Austrian border.The Madrisa is constituted by several secondary summits notably the Gargeller Madrisa (2770 metres) overlooking Gargellen in Austria.Ski lifts up to 2600 metres are located on the Klosters side.", "category": "NaturalPlace"} -{"text": " Temporary Temple is a live album by Psychic TV. The album was recorded on July 28 1984 in London and released on 12 vinyl. It was later coupled with another concert and released on CD as Temporary Temple & Atonal.", "category": "Album"} -{"text": " La hija de Juan Sim\u00f3n (Juan Sim\u00f3n's Daughter) is a musical play by Nemesio M. Sobrevila which has been made into two Spanish films. It is also the name of the title track and the song has been recorded by numerous artists such as Leonardo Favio.The first film directed by Jos\u00e9 Luis S\u00e1enz de Heredia was released in 1935 and starred Angelillo Pilar Mu\u00f1oz and Manuel Arb\u00f3. Luis Bu\u00f1uel was the executive producer for Film\u00f3fono and had a small role as an actor.", "category": "Film"} -{"text": " Book Of Matches is a poetry book written by Simon Armitage first published in 1993 by Faber and Faber. Several poems featured in the book are studied as part of the GCSE English Literature examination in the UK.The book is written in three sections the first (Book of Matches) containing 30 short sonnets. Each is meant to be read within 20 seconds the amount of time it would take for a match to be lit and burn out.", "category": "WrittenWork"} -{"text": " The Last Supper is the fourth album released by American stand-up comedian Jim Gaffigan. It focuses largely on his love of food.", "category": "Album"} -{"text": " The Miami Center is a skyscraper in downtown Miami Florida. Although Miami Center is not the city's tallest building it is a symbol of early downtown. Built in 1983 it is older compared with most of the taller buildings in Miami which have been built in the last decade. In addition the Miami Center is immediately adjacent to Bayfront Park and is unobstructed when looking at the skyline from Miami Beach to the east. The building is 484 ft (148 m) tall and has 34 floors.", "category": "Building"} -{"text": " Duboisia hopwoodii is a shrub native to the arid interior region of Australia. Common names include pituri pitchuri thornapple or pitcheri. It has an erect habit usually growing to between 1 and 3 metres in height and has long narrow leaves. Flowers are white and bell-shaped with violet-striped throats. These appear between June and November in the species native range followed by purple-black rounded berries which are 3 to 6 mm in diameter.", "category": "Plant"} -{"text": " Jelenin svet (Jelena's World) is a 2008 independent documentary film written and directed by Tanja Brzakovi\u0107 about former World No. 1 female tennis player Jelena Jankovi\u0107.", "category": "Film"} -{"text": " Jay Cashman Inc. is an American heavy-construction company based in Quincy Massachusetts with satellite offices in Boston Jupiter Florida and Staten Island New York. As of 2006 the company has about 1000 employees. The company was one of the major contractors on the Boston's Central Artery/Tunnel Project. In 2004 Jay Cashman Inc.", "category": "Company"} -{"text": " Hashemanli (Persian: \u0647\u0627\u0634\u0645\u0646\u0644\u064a\u200e also Romanized as H\u0101shemanl\u012b; also known as H\u0101shem El\u00e1) is a village in Jafarbay-ye Jonubi Rural District in the Central District of Torkaman County Golestan Province Iran. At the 2006 census its population was 135 in 27 families.", "category": "Village"} -{"text": " Rani Kasula Rangamma is a Telugu film starring Chiranjeevi.", "category": "Film"} -{"text": " The 20/20 Experience \u2013 The Complete Experience is a compilation album by American singer-songwriter Justin Timberlake. It was released on September 27 2013 by RCA Records.", "category": "Album"} -{"text": " R.C. Bigelow Inc better known as the Bigelow Tea Company is an American tea company based in Fairfield Connecticut. The company was founded by Ruth C. Bigelow in the late 1940s based on a recipe she marketed as Constant Comment tea. Bigelow is still a 100% family-owned business that markets over 50 varieties of tea including black and green as well as herbal teas all of which are still blended in Fairfield. They also own America's only tea plantation in Charleston South Carolina.", "category": "Company"} -{"text": " Thomas Eyre(fl. 1890s) was a footballer who made 65 appearances in the Football League playing for Lincoln City. He played at left back. Either side of Lincoln he played for Ashfield and Hamilton Academical in Scotland.", "category": "Athlete"} -{"text": " Malleable Iron Range Company was a company that existed from 1896 to 1985 and primarily produced kitchen ranges made of malleable iron but also produced a variety of other related products. The company's primary trademark was 'Monarch' and was colloquially often referred to as the Monarch Company or just Monarch.", "category": "Company"} -{"text": " The Chiltern School is a coeducational special school located over two sites in Dunstable and Houghton Regis in Bedfordshire England. The school accepts pupils from all over the Central Bedfordshire area.The school was formed in 2012 from the merger of Glenwood School in Dunstable and Hillcrest School in Houghton Regis.", "category": "EducationalInstitution"} -{"text": " Kim Dae-Eun (born September 17 1984) is a South Korean gymnast. He is the 2004 Olympic All-around silver medalist. He won the gold medal on the parallel bars at the 2007 World Artistic Gymnastics Championships.Kim was part of the South Korean team that won the bronze medal in the team event at the 2006 Asian Games.", "category": "Athlete"} -{"text": " Arayik Vladimirovich Harutyunyan (Armenian: \u0531\u0580\u0561\u0575\u056b\u056f \u0540\u0561\u0580\u0578\u0582\u0569\u0575\u0578\u0582\u0576\u0575\u0561\u0576 Russian: \u0410\u0440\u0430\u0438\u043a \u0410\u0440\u0443\u0442\u044e\u043d\u044f\u043d) (born 14 December 1973) is the current Prime Minister of the Nagorno-Karabakh Republic. He was suggested by the President of Nagorno-Karabakh Bako Sahakyan and was unanimously approved by the Parliament of Karabakh on 14 September 2007 by 32 out of 32 present parliamentarians.", "category": "OfficeHolder"} -{"text": " Shelton Hank Williams also known as Hank Williams III and Hank 3 (born December 12 1972) is an American musician singer and multi-instrumentalist including guitar bass drums banjo and vocals. In addition to his honky tonk recordings Williams' style alternates between country punk and metal.", "category": "Artist"} -{"text": " Helicella orzai is a species of air-breathing land snails terrestrial pulmonate gastropod mollusks in the family Hygromiidae the hairy snails and their allies. This species is endemic to Spain.", "category": "Animal"} -{"text": " Gro\u00dfe Schmalenau is a river of North Rhine-Westphalia Germany.", "category": "NaturalPlace"} -{"text": " The Tupolev ANT-29 (military designation DIP \u2013 Dvukhmotorny istrebitel pushechny twin-engined cannon fighter) was a 1930s twin-engined cannon-armed fighter designed by Alexander Arkhangelsky and built by Tupolev.Design work started in 1932 on a twin-engined aircraft capable of carrying two APK-100 cannons. The resulting design was the ANT-29 and it first flew in February 1935. A monoplane with a tall and narrow fuselage and powered by two Hispano-Suiza 12Ybrs engines.", "category": "MeanOfTransportation"} -{"text": " Charles Corm (1894-1963) was a Lebanese writer businessman and philanthropist. He is considered to be the leader of the Phoenicianism movement in Lebanon which ignited a surge of nationalism that led to Lebanon's independence. In a country torn by sectarian conflicts Corm's intention was to find a common root shared by all Lebanese beyond their religious beliefs (the Phoenicians were pagans).", "category": "Artist"} -{"text": " Joseph Hubert Ruetz (October 21 1916 \u2013 January 2 2003) was a professional football player in the All-America Football Conference for the Chicago Rockets in 1946 and 1948. Prior to that he played at the collegiate level while attending the University of Notre Dame. He played guard for the Irish with the exception of playing one season at quarterback. In 1938 he graduated from Notre Dame with cum laude honors.", "category": "Athlete"} -{"text": " The Reef House is a historic house located at 411 S. Poplar St. in Carbondale Illinois. William A. Reef built the house for his family circa 1892. The Queen Anne-style cottage may have been designed by local carpenter A. M. Etherton though records of its designer do not exist. The house features fishscale shingle siding on its second floor and clapboard siding on its first; the clapboard siding is adorned with stickwork.", "category": "Building"} -{"text": " MAKO Surgical Corp. (Stryker Medical) is a publicly traded medical device company based in Florida. On September 25 2013 the Board of Directors of Mako Surgical accepted a deal to merge with Stryker Medical for $1.65B subject to shareholder approval.", "category": "Company"} -{"text": " Pop Carn is a 2003 Indian Tamil film written and directed by actor-cum-director Nassar and starring Mohanlal and Simran Bagga in lead roles and introducing newcomers Kunal Shah and Jyothi Nawal. The film which had music scored by Yuvan Shankar Raja was released on 30 January 2003 but flopped at the box office. Nonetheless the film was dubbed into Malayalam and released in 2007 under the same name.", "category": "Film"} -{"text": " USNS Mount Baker (T-AE-34) is the seventh of eight Kilauea-class ammunition ships to serve with the Military Sealift Command. She is the second U.S. Navy ship to bear the name and is named for Mount Baker a 10781-foot volcano in the Cascade Range of Washington. Ammunition ships operated by Military Sealift Command provide logistic support to US Navy ships at sea.Mount Baker was built by Ingalls Shipbuilding Pascagoula Mississippi.", "category": "MeanOfTransportation"} -{"text": " Dansere is an album by Jan Garbarek. The album was recorded in November 1975 and features the Bobo Stenson Quartet.", "category": "Album"} -{"text": " Divraz (Persian: \u062f\u064a\u0648\u0631\u0632\u200e also Romanized as D\u012bvraz) is a village in Bala Khiyaban-e Litkuh Rural District in the Central District of Amol County Mazandaran Province Iran. At the 2006 census its population was 393 in 95 families.", "category": "Village"} -{"text": " The D\u0103ih\u0103\u021ba\u0219u River is a tributary of the Dumbr\u0103vanu River in Romania.", "category": "NaturalPlace"} -{"text": " Zeisters also known as Fat Guy Goes Nutzoid is a 1986 comedy film produced by Troma Entertainment. Troma was originally set to title the film Fat Boy Goes Nutzoid but at the request of the lawyers of the hip-hop group The Fat Boys it was changed to Fat Guy.", "category": "Film"} -{"text": " Paul Gobeil (born March 1 1942 in Saint-R\u00e9mi-de-Tingwick Quebec) is a former Canadian politician and businessman.From 1985 to 1989 Mr. Gobeil was a Liberal member of the National Assembly for the riding of Verdun and served as Minister assigned to Administration President of the Treasury Board and as Minister of International Affairs for the Government of Quebec.", "category": "OfficeHolder"} -{"text": " Ruff Ryders: Past Present Future is the fifth compilation album from American hip hop record label Ruff Ryders Entertainment released on November 21 2011.", "category": "Album"} -{"text": " Ridi Viharaya (Sinhala: \u0dbb\u0dd2\u0daf\u0dd3 \u0dc0\u0dd2\u0dc4\u0dcf\u0dbb\u0dba) or Silver Temple is a 2nd-century BCE Theravada Buddhist temple in the village of Ridigama Sri Lanka. Built during the reign of Dutthagamani of Anuradhapura the temple is considered as the place where the silver ore which provided silver to complete Ruwanwelisaya; one of the largest stupa in Sri Lanka was discovered.", "category": "Building"} -{"text": " Grand Canyon Preparatory Academy is a public charter college preparatory school in Tempe Arizona.", "category": "EducationalInstitution"} -{"text": " Aricoceras is an extinct genus of the Adrianitidae family. They are an extinct group of ammonoid which are shelled cephalopods related to squids belemnites octopuses and cuttlefish and more distantly to the nautiloids.", "category": "Animal"} -{"text": " Blackburn High School is a public secondary school for girls and boys in years 7 to 12 in Blackburn a suburb of Melbourne Victoria Australia. Blackburn High School is an outstanding secondary school for aspiring young men and women. It aims to educate tomorrow's minds today. Started in 1956 the school has a proud tradition of academic excellence and exceptional music achievement.The school is nationally recognised as a leading educational institution for music education.", "category": "EducationalInstitution"} -{"text": " Chris Nieratko (born February 19 1976) is an American humorist and author. Nieratko is a past editor of Big Brother Magazine and currently reviews pornographic films for Vice magazine as well as being the author of the related Skinema book. He also appeared on MTV's Jackass.", "category": "Artist"} -{"text": " Warlock is a 1959 film released by Twentieth Century Fox and shot in DeLuxe Color and CinemaScope. It is a Western adapted from the novel by Oakley Hall (screenplay written by Robert Alan Aurthur).", "category": "Film"} -{"text": " Sieniawa [\u0255e\u02c8\u0272ava] is a village in the administrative district of Gmina Raba Wy\u017cna within Nowy Targ County Lesser Poland Voivodeship in southern Poland. It lies approximately 5 kilometres (3 mi) south-east of Raba Wy\u017cna 10 km (6 mi) north-west of Nowy Targ and 59 km (37 mi) south of the regional capital Krak\u00f3w.The village has a population of 1900.", "category": "Village"} -{"text": " Michael Adam (born 9 December 1984) is a German politician. He has been District Administrator (Landrat) of Regen since 2011.", "category": "OfficeHolder"} -{"text": " Thunderbird High School is a public high school located in northwestern Phoenix Arizona. The school is a part of the Glendale Union High School District.", "category": "EducationalInstitution"} -{"text": " Nayef Al Khater (born May 10 1978) is a Qatari football player. He currently plays for Al Wakrah as a defender.", "category": "Athlete"} -{"text": " Black Cobra Woman (Italian: Eva nera) also known as Black Cobra is an Italian 1976 exploitation movie written and directed by Joe D'Amato.", "category": "Film"} -{"text": " Joe Cuba a.k.a Sonny (April 22 1931 \u2013 February 15 2009) was a musician of Puerto Rican descent who was known as the Father of Latin Boogaloo.", "category": "Artist"} -{"text": " Jacob LeBlanc (born February 2 1981 in Auburn California) is an American retired professional soccer player.", "category": "Athlete"} -{"text": " Kevin B. Kamenetz is the 12th and current County Executive of Baltimore County Maryland serving since 2010. He is a member of the Democratic Party. He previously served as a four-term County Councilman representing the Second District of Baltimore County.", "category": "OfficeHolder"} -{"text": " Thomas Frederick Fred Peart Baron Peart PC (30 April 1914 \u2013 26 August 1988) was a British Labour politician who served in the Labour governments of the 1960s and 1970s and was a candidate for Deputy Leader of the Party.", "category": "OfficeHolder"} -{"text": " Grand Lake is a large lake in the interior of Newfoundland of the Canadian province of Newfoundland and Labrador. It has an area of 534 km\u00b2 making it the largest lake on Newfoundland. Consequently it is one of if not the deepest.", "category": "NaturalPlace"} -{"text": " A Colossal Failure of Common Sense: The Inside Story of the Collapse of Lehman Brothers is a 2009 non-fiction book written by Lawrence G. McDonald and Patrick Robinson which chronicles the events surrounding the bankruptcy of Lehman Brothers in the context of the financial crisis of 2007\u20132010 and the subprime mortgage crisis.", "category": "WrittenWork"} -{"text": " Thatching (31 May 1975 \u2013 1999) was an Irish Thoroughbred racehorse and sire. The horse's early career was delayed and disrupted by injury and he did not show his best form until switched to sprinting distances in the spring of 1979 when he won the Duke of York Stakes. He improved further when equipped with blinkers that summer recording impressive victories in both the Cork and Orrery Stakes and the July Cup.", "category": "Animal"} -{"text": " Ya\u015far Kurt (Armenian: \u0545\u0561\u0577\u0561\u0580 \u053f\u0578\u0582\u0580\u0569 b.August 16 1968 in Istanbul Turkey) is a Turkish-Armenian rock artist.", "category": "Artist"} -{"text": " Then and Now is a historical novel by W. Somerset Maugham. Set in Florence Italy during the Renaissance the story focuses on three months in the life of Niccolo Machiavelli the Florentine politician diplomat philosopher and writer in the early years of the 16th century. The book was first published by Heinemann in 1946.", "category": "WrittenWork"} -{"text": " Abdollah Masud-e Sofla (Persian: \u0639\u0628\u062f\u0627\u0644\u0647 \u0645\u0633\u0639\u0648\u062f\u0633\u0641\u0644\u064a\u200e also Romanized as \u2018Abdoll\u0101h Mas\u2018\u016bd-e Sofl\u00e1; also known as Abdollah Mas\u2019ood \u2018Abdoll\u0101h Mas\u2018\u016bd and Abdull\u0101h Mas\u016bd) is a village in Hesar-e Valiyeasr Rural District Avaj District Buin Zahra County Qazvin Province Iran. At the 2006 census its population was 72 in 15 families.", "category": "Village"} -{"text": " Springhill High School (SHS) is a secondary school in Springhill Nova Scotia Canada. SHS is part of the Chignecto-Central Regional School Board and is the only high school in the town of Springhill. The school is home to many sports teams and clubs. These include: basketball soccer badminton track and field softball Students Against Destructive Decisions drama club homework club and book club.", "category": "EducationalInstitution"} -{"text": " Charniele L. Herring (/\u0283\u0251r\u02c8n\u025bl \u02c8h\u025br\u026a\u014b/ shar-NEL HERR-ing; born September 25 1969) is an American politician. She has served in the Virginia House of Delegates since 2009 representing the 46th district made up the city of Alexandria and part of Fairfax County near Washington D.C. Herring is a member of the Democratic Party. She has been the House minority whip since 2012 and in December 2012 she was the first African-American to be elected chair of the Democratic Party of Virginia.", "category": "OfficeHolder"} -{"text": " Symmoca dodecatella is a moth of the Symmocidae family. It is found in Portugal and Spain.The wingspan is about 18\u201319 mm. The forewings are grey sprinkled with black mainly along the margin. The hindwings are grey.", "category": "Animal"} -{"text": " Ali Abbasov Mammad oglu (Azerbaijani: \u018fli Abbasov M\u0259mm\u0259d o\u011flu) (born 1953 in Azerbaijan) is the current Minister of Communications and Information Technologies of the Republic of Azerbaijan.", "category": "OfficeHolder"} -{"text": " Worlds Beyond was an American digest magazine of science fiction and fantasy fiction in 1950 and 1951. The magazine only issued three monthly issues from December 1950 to February 1951 but is notable for having printed stories by Cyril M. Kornbluth Jack Vance Mack Reynolds Graham Greene John Christopher Lester del Rey Judith Merril and others.Worlds Beyond was published by Hillman Periodicals and was edited by Damon Knight.", "category": "Company"} -{"text": " The Daily News Journal commonly abbreviated to DNJ is a newspaper serving Murfreesboro Tennessee Rutherford County and surrounding communities. Published in Murfreesboro it serves as the primary local newspaper with competition from The Murfreesboro Post and other publications. The newspaper is not in competition with The Tennessean of Nashville as both are owned by Gannett Company.The roots the DNJ date back to 1849 and the founding of Murfreesboro News.", "category": "WrittenWork"} -{"text": " Echinocereus fendleri is a species of cactus known by the common names pinkflower hedgehog cactus and Fendler's hedgehog cactus. It grows in deserts and woodlands in the Southwestern United States and Northeastern Mexico. It is most common in New Mexico.The taxonomy of the species is uncertain with authors recognizing up to eight varieties.", "category": "Plant"} -{"text": " Michael F. Kitt Snr (13 September 1914 \u2013 24 December 1974) was an Irish Fianna F\u00e1il politician and long-serving Teachta D\u00e1la (TD).He was elected to D\u00e1il \u00c9ireann for the first time at the 1948 general election for the Galway North constituency but lost his seat at the 1951 general election and failed to be elected again at the 1954 general election.", "category": "OfficeHolder"} -{"text": " Epidendrum mancum is an epiphytic orchid that grows in the tropical low elfin cloud forests of Ecuador and Amazonas Peru at altitudes of 2\u20143 km .", "category": "Plant"} -{"text": " Salempur Masanda is a village in Jalandhar District near the Jalandhar Cantonment in Punjab India.", "category": "Village"} -{"text": " Yaleh Gonbad (Persian: \u064a\u0644\u0647 \u06af\u0646\u0628\u062f\u200e; also known as Em\u0101mz\u0101deh Imamzade-Ele-Geumbez and Im\u0101mz\u0101deh) is a village in Ilat-e Qaqazan-e Gharbi Rural District Kuhin District Qazvin County Qazvin Province Iran. At the 2006 census its population was 429 in 105 families.", "category": "Village"} -{"text": " Popeyes Louisiana Kitchen is an American chain of fried chicken fast food restaurants founded in 1972 in New Orleans Louisiana. Often referred to as Popeyes and sometimes as Popeyes Chicken & Biscuits or Popeyes Chicken & Seafood[citation needed] it was acquired by Sandy Springs Georgia-based AFC Enterprises originally America's Favorite Chicken Company in 1993.", "category": "Company"} -{"text": " The White Umfolozi River originates just south of Vryheid KwaZulu-Natal South Africa and joins the Black Umfolozi River at 28\u00b020\u203258\u2033S 31\u00b058\u203246\u2033E to form the Umfolozi River before it flows east towards the Indian Ocean.", "category": "NaturalPlace"} -{"text": " The Albatros L 74 was a two-seated German training biplane produced by Albatros Flugzeugwerke. Only two were produced.", "category": "MeanOfTransportation"} -{"text": " The University of Nevada School of Medicine is an academic division of the University of Nevada Reno and grants the Doctor of Medicine (MD) degree. The School of Medicine was founded in 1969 as the first medical school in the state of Nevada. More than 1500 MDs have graduated from the School of Medicine. The pre-clinical campus is located in Reno but the third and fourth years can be spent in hospitals and clinics throughout Nevada.", "category": "EducationalInstitution"} -{"text": " Leon Kroll (December 6 1884 \u2013 October 25 1974) was an American painter and lithographer. Known as a figurative artist Life Magazine described him as the dean of U.S. nude painters yet he was an exceptional landscape painter and also produced an exceptional body of still life compositions.Born into a musical family on lower Second Avenue in New York City Kroll's father was a violinist and his cousin was William Kroll.", "category": "Artist"} -{"text": " Michael E. DeBakey High School for Health Professions at Qatar (DHSHP@Q in short) is a private international middle and secondary school in Doha Qatar. The school is a branch campus of Michael E. DeBakey High School for Health Professions of Houston Texas United States. Charlesetta Deason is the CEO and President.Named after Michael E. DeBakey the school opened in September 2008 with grades 8 through 10 with 100 students per grade; the school will ultimately cover grades 7-12.", "category": "EducationalInstitution"} -{"text": " The Richleighs of Tantamount is a children\u2019s historical novel written by British author Barbara Willard. It was originally published in the United Kingdom in 1966 by the publishers Constable before being published in the United States by Harcourt Brace & World in June 1967. C. Walter Hodges drew the line illustrations and painted the cover portrait for the original edition.", "category": "WrittenWork"} -{"text": " Ennea is a genus of air-breathing land snails terrestrial pulmonate gastropod mollusks in the family Streptaxidae.Ennea is the type genus of the subfamily Enneinae.", "category": "Animal"} -{"text": " Come Live with Me is a 1941 American romantic comedy film produced and directed by Clarence Brown and starring James Stewart and Hedy Lamarr. Based on a story by Virginia Van Upp the film is about a beautiful Viennese refugee seeking United States citizenship who arranges a marriage of convenience with a struggling writer.", "category": "Film"} -{"text": " St. Thomas Episcopal Church is a parish church in the Episcopal Diocese of Iowa. The church is located in Sioux City Iowa United States at 1200 Douglas Street. The church building is listed on the National Register of Historic Places.", "category": "Building"} -{"text": " Nuno Daniel Costeira Valente (born 22 November 1991 in Ada\u00fafe - Braga ) is a Portuguese footballer who plays for Vizela on loan from S.C. Braga as a midfielder.", "category": "Athlete"} -{"text": " Jaagoo (Bengali: \u099c\u09be\u0997\u09cb) is a Bangladeshi sports based romantic Movies. Its writer and director Khijir Hayat Khan. Adnan Karim sponsored youth football game built this film was released in 2010. The film is produced by Sharjeel Karim and Adnan Karim and directed by Khijir Hayat Khan who has written the story screenplay and dialogues of the film. The film features Ferdous Ahmed and Afsana Ara Bindu in lead roles and with supporting Arefin Shuvo Tariq Anam Ronok Hasaan and many more.", "category": "Film"} -{"text": " John Edward Hatton AO (born 29 May 1933) is former Australian politician and an National Trust of Australia nominated Australian Living Treasure. He was the independent member of the Legislative Assembly of the New South Wales parliament for the seat of South Coast from 1973 to 1995. Notably the allegations about police corruption Hatton raised in Parliament resulted in the Wood Royal Commission. He is currently a social activist in his local community.", "category": "OfficeHolder"} -{"text": " Trichoptilus subtilis is a moth of the Pterophoridae family that is known from South Africa.", "category": "Animal"} -{"text": " Sin\u00e9ad Madden (born in Galway Ireland) is an Irish singer-songwriter and fiddle player best known as a member of the Moya Brennan band. She also teaches at Waltons New School of Music in Dublin.", "category": "Artist"} -{"text": " Philip Sprint is a German footballer who currently plays for Hertha BSC.Sprint made his professional debut for Hertha BSC on 12 August 2012 in a 2. Bundesliga match against FSV Frankfurt coming on in the 50th minute for Marvin Knoll after starting goalkeeper Sascha Burchert had been sent off.", "category": "Athlete"} -{"text": " River Roads Mall was an enclosed shopping mall located in the city of Jennings a suburb of St. Louis Missouri United States. Opened in 1962 as one of the nation's first shopping malls the mall declined in the 1990s becoming a dead mall and eventually being shuttered in 1995. Demolition of the long-vacant mall began in 2006.", "category": "Building"} -{"text": " The Brown-patched Kangaroo lizard (Otocryptis wiegmanni) also called Wiegmann's Agama or Sri Lankan Kangaroo Lizard is a small ground dwelling agamid lizard endemic to the wet zone forests and lower mountain forests (up to 1300 metres) of Sri Lanka. It is commonly seen in the leaf litter of shady rain forests. When perceiving danger it spurts away quickly on its large hind legs and might eventually climb up a sapling or tree.", "category": "Animal"} -{"text": " Shiho Kawaragi (\u6cb3\u539f\u6728 \u5fd7\u7a42 Kawaragi Shiho born April 29 1976 in Tokyo) is a Japanese voice actress who works for Kenyu-Office. When voicing adult games and hentai OVAs she is also known as Kaname Yuzuki (\u67da\u6728\u304b\u306a\u3081 Yuzuki Kaname) She is currently married since March 2012.", "category": "Artist"} -{"text": " Down in the Shacks Where the Satellite Dishes Grow is the second album by the Judybats released in 1992.", "category": "Album"} -{"text": " Turn of Faith is a 2001 film directed by Charles Jarrott. It stars Ray Mancini and Mia Sara.", "category": "Film"} -{"text": " Frederick William Seward (July 8 1830 \u2013 April 25 1915) was the Assistant Secretary of State during the American Civil War serving in Abraham Lincoln's administration as well as under Andrew Johnson during Reconstruction and for over two years under Rutherford B. Hayes.", "category": "OfficeHolder"} -{"text": " Ivoprop Corporation founded in 1984 by Ivo Zdarsky is an American manufacturer of composite propellers for homebuilt and ultralight aircraft as well as airboats. The company headquarters is located in Bellflower California.Zdarsky started the company after carving his own propeller for a homebuilt ultralight trike that he flew from Cold War Czechoslovakia over the Iron Curtain to Vienna in 1984.", "category": "Company"} -{"text": " Wave Broadband is a provider of residential business and enterprise class cable TV broadband internet and telephone services on the West Coast currently serving about 400000 customers within communities in western Washington state Oregon Sacramento California and the San Francisco Bay Area. Wave Broadband provides services via their fiber-optic network and uses Northwest Open Access Network as the backbone for most of their service areas in Washington.", "category": "Company"} -{"text": " Andie Tong is a comic book artist known for his work on books such as Spectacular Spider-Man UK The Batman Strikes! and Tangent: Superman's Reign.", "category": "Artist"} -{"text": " Merdani is a village in the municipality of Busova\u010da Bosnia and Herzegovina.", "category": "Village"} -{"text": " Kamam (Persian: \u0643\u0627\u0645\u0645\u200e also Romanized as K\u0101mam) is a village in Mangur-e Sharqi Rural District Khalifan District Mahabad County West Azerbaijan Province Iran. At the 2006 census its population was 98 in 16 families.", "category": "Village"} -{"text": " Ficus greiffiana is a species of plant in the Moraceae family. It is found in Argentina Brazil Colombia and Guyana.", "category": "Plant"} -{"text": " Toni Amboaje is a Spanish singer who currently works for metal band Sauze which formed on early 2008.", "category": "Artist"} -{"text": " Mount Whittier is a mountain in Carroll County New Hampshire in the northern Ossipee Mountains. Named after John Greenleaf Whittier the peak is not to be confused with nearby Nickerson Mountain which was once known as Mount Whittier.There are no hiking trails on Mount Whittier. There was once a CCC alpine ski trail on the northern face.", "category": "NaturalPlace"} -{"text": " El Rompe Discoteka: The Mix Album is a 2007 album by Hector El Father.", "category": "Album"} -{"text": " e-Spirit is a commercial software company that develops and markets the FirstSpirit CMS Web content management system. The company was founded in 1999 in Dortmund Germany and established a US presence in 2011. The company's FirstSpirit CMS is a Java-based offering now in its fifth major release.[citation needed]", "category": "Company"} -{"text": " The Valley is the first novel by Barry Pilton published in 2005 by Bloomsbury. It is a humorous account of the effect of outsiders on the rural status quo in a fictional mid-Wales valley during the 1980s and is being adapted for television.", "category": "WrittenWork"} -{"text": " Sema Group plc was an Anglo-French IT services company. It was listed on the London Stock Exchange and was a constituent of the FTSE 100 Index but was acquired by Schlumberger in 2001.", "category": "Company"} -{"text": " Bent Hansen (born 1954) is a retired Danish ice hockey forward. He played for 18 years in Denmark for the R\u00f8dovre SIK and KSF. He also competed for the Danish national team. His son Jannik Hansen also played for the R\u00f8dovre team and was drafted into the NHL by the Vancouver Canucks in 2004. During his hockey career Hansen also worked as a carpenter.", "category": "Athlete"} -{"text": " Behind the Sun is a 2004 album by Dive.", "category": "Album"} -{"text": " Mungaru Male (English: Pre Monsoon Rain) is a 2006 Kannada language movie directed by Yograj Bhat and produced by E Krishnappa. The film stars Ganesh Pooja Gandhi Anant Nag Padmaja Rao in lead roles.", "category": "Film"} -{"text": " Megachile perihirta commonly known as the Western leafcutting bee is a bee in the genus Megachile. The bee is native to western North America ranging from Nebraska to Texas and Mexico west to California and north to British Columbia and Alberta and often inhabits meadows and orchards. The bee is black with long whitish-yellow hair more so below the thorax and abdomen. The abdomen however is mostly bare although each segment has scattered whitish hair.", "category": "Animal"} -{"text": " Sukeban Deka The Movie (\u30b9\u30b1\u30d0\u30f3\u5211\u4e8b) is a live action Japanese film that was released in 1987. The movie closely follows a TV and manga series Sukeban Deka written and illustrated by Shinji Wada. The movie stars Yoko Minamino and Yui Asaka who were also in the TV series. The movie was followed by Sukeban Deka II in 1988.", "category": "Film"} -{"text": " The Maple School District is a public school district in Douglas County Wisconsin United States based in Maple Wisconsin.", "category": "EducationalInstitution"} -{"text": " Mount Waverley Secondary College is a public secondary school located in the Melbourne suburb of Mount Waverley. The school consists of roughly 1900 students and is one of the largest in the state.The school consists of two campuses (Junior & Senior) both situated on Stephensons Road in Mount Waverley. The Junior site holds years 7 and 8 with year levels 9 to 12 at the Senior Campus. The campuses are a short walking distance apart.", "category": "EducationalInstitution"} -{"text": " Jon-Paul Roger JP Pietersen (born 12 July 1986 in Stellenbosch South Africa) is a South African rugby union footballer. He generally plays fullback or wing for the Sharks (in the Super Rugby competition) and the Natal Sharks in the Currie Cup. He played in more than 50 tests for the Springboks.", "category": "Athlete"} -{"text": " Deltocolpodes is a genus of beetles in the family Carabidae containing the following species: Deltocolpodes brendelli Morvan 1992 Deltocolpodes championi Morvan 1992 Deltocolpodes duluchus Morvan 1992 Deltocolpodes heinigeri Morvan 1992 Deltocolpodes jalepensis Morvan 1992 Deltocolpodes kirschenhoferi Morvan 1992 Deltocolpodes nepalensis Morvan 1992 Deltocolpodes perreaui Deuve 1985 Deltocolpodes rectangulus Morvan 1992 Deltocolpodes rolex Morvan 1992 Deltocolpodes salpensis Deuve 1985 Deltocolpodes sikkimensis Morvan 1992\u2191", "category": "Animal"} -{"text": " Stanhopea martiana is a species of orchid endemic to southwestern Mexico.", "category": "Plant"} -{"text": " Yawarmayu (Quechua yawar blood mayu river blood river hispanicized spelling Yahuarmayo) is a river in Peru located in the Puno Region Carabaya Province Ayapata District. It originates near the border of the districts Ayapata and Coasa. Its direction is mainly to the northwest where it meets Inambari River as a right affluent. The confluence is north of the village Yawarmayu (Yahuarmayo).", "category": "NaturalPlace"} -{"text": " The Charles Granke House at 406 S. Seventh St. in Hamilton Montana is a historic house that was built in 1906. It includes Colonial Revival and Queen Anne architecture. It was listed on the National Register of Historic Places in 1988. The listing included two contributing buildings.It was built in approximately 1906 by the Anaconda Copper Mining Company as a worker cottage for workers at the sawmill that operated in Hamilton until 1915. Charles W.", "category": "Building"} -{"text": " Passiflora monadelpha is a species of plant in the Passifloraceae family. It is endemic to Ecuador.", "category": "Plant"} -{"text": " Mangifera persiciformis or Peach Mango is a species of plant in the Anacardiaceae family. It is endemic to China.", "category": "Plant"} diff --git a/examples/finetuning/answers_with_ft.py b/examples/finetuning/answers_with_ft.py index 32507e82ff..43061f4c1b 100644 --- a/examples/finetuning/answers_with_ft.py +++ b/examples/finetuning/answers_with_ft.py @@ -1,150 +1,2 @@ -import argparse - -import openai - - -def create_context( - question, search_file_id, max_len=1800, search_model="ada", max_rerank=10 -): - """ - Create a context for a question by finding the most similar context from the search file. - :param question: The question - :param search_file_id: The file id of the search file - :param max_len: The maximum length of the returned context (in tokens) - :param search_model: The search model to use - :param max_rerank: The maximum number of reranking - :return: The context - """ - results = openai.Engine(search_model).search( - search_model=search_model, - query=question, - max_rerank=max_rerank, - file=search_file_id, - return_metadata=True, - ) - returns = [] - cur_len = 0 - for result in results["data"]: - cur_len += int(result["metadata"]) + 4 - if cur_len > max_len: - break - returns.append(result["text"]) - return "\n\n###\n\n".join(returns) - - -def answer_question( - search_file_id="", - fine_tuned_qa_model="", - question="Which country won the European Football championship in 2021?", - max_len=1800, - search_model="ada", - max_rerank=10, - debug=False, - stop_sequence=["\n", "."], - max_tokens=100, -): - """ - Answer a question based on the most similar context from the search file, using your fine-tuned model. - :param question: The question - :param fine_tuned_qa_model: The fine tuned QA model - :param search_file_id: The file id of the search file - :param max_len: The maximum length of the returned context (in tokens) - :param search_model: The search model to use - :param max_rerank: The maximum number of reranking - :param debug: Whether to output debug information - :param stop_sequence: The stop sequence for Q&A model - :param max_tokens: The maximum number of tokens to return - :return: The answer - """ - context = create_context( - question, - search_file_id, - max_len=max_len, - search_model=search_model, - max_rerank=max_rerank, - ) - if debug: - print("Context:\n" + context) - print("\n\n") - try: - # fine-tuned models requires model parameter, whereas other models require engine parameter - model_param = ( - {"model": fine_tuned_qa_model} - if ":" in fine_tuned_qa_model - and fine_tuned_qa_model.split(":")[1].startswith("ft") - else {"engine": fine_tuned_qa_model} - ) - response = openai.Completion.create( - prompt=f"Answer the question based on the context below\n\nText: {context}\n\n---\n\nQuestion: {question}\nAnswer:", - temperature=0, - max_tokens=max_tokens, - top_p=1, - frequency_penalty=0, - presence_penalty=0, - stop=stop_sequence, - **model_param, - ) - return response["choices"][0]["text"] - except Exception as e: - print(e) - return "" - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Rudimentary functionality of the answers endpoint with a fine-tuned Q&A model.", - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - ) - parser.add_argument( - "--search_file_id", help="Search file id", required=True, type=str - ) - parser.add_argument( - "--fine_tuned_qa_model", help="Fine-tuned QA model id", required=True, type=str - ) - parser.add_argument( - "--question", help="Question to answer", required=True, type=str - ) - parser.add_argument( - "--max_len", - help="Maximum length of the returned context (in tokens)", - default=1800, - type=int, - ) - parser.add_argument( - "--search_model", help="Search model to use", default="ada", type=str - ) - parser.add_argument( - "--max_rerank", - help="Maximum number of reranking for the search", - default=10, - type=int, - ) - parser.add_argument( - "--debug", help="Print debug information (context used)", action="http://github.com/store_true" - ) - parser.add_argument( - "--stop_sequence", - help="Stop sequences for the Q&A model", - default=["\n", "."], - nargs="+", - type=str, - ) - parser.add_argument( - "--max_tokens", - help="Maximum number of tokens to return", - default=100, - type=int, - ) - args = parser.parse_args() - response = answer_question( - search_file_id=args.search_file_id, - fine_tuned_qa_model=args.fine_tuned_qa_model, - question=args.question, - max_len=args.max_len, - search_model=args.search_model, - max_rerank=args.max_rerank, - debug=args.debug, - stop_sequence=args.stop_sequence, - max_tokens=args.max_tokens, - ) - print(f"Answer:{response}") +# This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) +# at [examples/fine-tuned_qa](https://github.com/openai/openai-cookbook/tree/main/examples/fine-tuned_qa) diff --git a/examples/finetuning/finetuning-classification.ipynb b/examples/finetuning/finetuning-classification.ipynb index 60b8896ecc..e5ece174d9 100644 --- a/examples/finetuning/finetuning-classification.ipynb +++ b/examples/finetuning/finetuning-classification.ipynb @@ -2,737 +2,35 @@ "cells": [ { "cell_type": "markdown", + "metadata": {}, "source": [ - "# Fine tuning classification example\n", - "\n", - "We will fine-tune an ada classifier to distinguish between the two sports: Baseball and Hockey." - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 1, - "source": [ - "from sklearn.datasets import fetch_20newsgroups\n", - "import pandas as pd\n", - "import openai\n", - "\n", - "categories = ['rec.sport.baseball', 'rec.sport.hockey']\n", - "sports_dataset = fetch_20newsgroups(subset='train', shuffle=True, random_state=42, categories=categories)" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - " ## Data exploration\n", - " The newsgroup dataset can be loaded using sklearn. First we will look at the data itself:" - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 2, - "source": [ - "print(sports_dataset['data'][0])" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "From: dougb@comm.mot.com (Doug Bank)\n", - "Subject: Re: Info needed for Cleveland tickets\n", - "Reply-To: dougb@ecs.comm.mot.com\n", - "Organization: Motorola Land Mobile Products Sector\n", - "Distribution: usa\n", - "Nntp-Posting-Host: 145.1.146.35\n", - "Lines: 17\n", - "\n", - "In article <1993Apr1.234031.4950@leland.Stanford.EDU>, bohnert@leland.Stanford.EDU (matthew bohnert) writes:\n", - "\n", - "|> I'm going to be in Cleveland Thursday, April 15 to Sunday, April 18.\n", - "|> Does anybody know if the Tribe will be in town on those dates, and\n", - "|> if so, who're they playing and if tickets are available?\n", - "\n", - "The tribe will be in town from April 16 to the 19th.\n", - "There are ALWAYS tickets available! (Though they are playing Toronto,\n", - "and many Toronto fans make the trip to Cleveland as it is easier to\n", - "get tickets in Cleveland than in Toronto. Either way, I seriously\n", - "doubt they will sell out until the end of the season.)\n", - "\n", - "-- \n", - "Doug Bank Private Systems Division\n", - "dougb@ecs.comm.mot.com Motorola Communications Sector\n", - "dougb@nwu.edu Schaumburg, Illinois\n", - "dougb@casbah.acns.nwu.edu 708-576-8207 \n", - "\n" - ] - } - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 3, - "source": [ - "sports_dataset.target_names[sports_dataset['target'][0]]\n" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "'rec.sport.baseball'" - ] - }, - "metadata": {}, - "execution_count": 3 - } - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 4, - "source": [ - "len_all, len_baseball, len_hockey = len(sports_dataset.data), len([e for e in sports_dataset.target if e == 0]), len([e for e in sports_dataset.target if e == 1])\n", - "print(f\"Total examples: {len_all}, Baseball examples: {len_baseball}, Hockey examples: {len_hockey}\")" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Total examples: 1197, Baseball examples: 597, Hockey examples: 600\n" - ] - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "One sample from the baseball category can be seen above. It is an email to a mailing list. We can observe that we have 1197 examples in total, which are evenly split between the two sports." - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "## Data Preparation\n", - "We transform the dataset into a pandas dataframe, with a column for prompt and completion. The prompt contains the email from the mailing list, and the completion is a name of the sport, either hockey or baseball. For demonstration purposes only and speed of fine-tuning we take only 300 examples. In a real use case the more examples the better the performance." - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 5, - "source": [ - "import pandas as pd\n", - "\n", - "labels = [sports_dataset.target_names[x].split('.')[-1] for x in sports_dataset['target']]\n", - "texts = [text.strip() for text in sports_dataset['data']]\n", - "df = pd.DataFrame(zip(texts, labels), columns = ['prompt','completion']) #[:300]\n", - "df.head()" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - " prompt completion\n", - "0 From: dougb@comm.mot.com (Doug Bank)\\nSubject:... baseball\n", - "1 From: gld@cunixb.cc.columbia.edu (Gary L Dare)... hockey\n", - "2 From: rudy@netcom.com (Rudy Wade)\\nSubject: Re... baseball\n", - "3 From: monack@helium.gas.uug.arizona.edu (david... hockey\n", - "4 Subject: Let it be Known\\nFrom: \n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
promptcompletion
0From: dougb@comm.mot.com (Doug Bank)\\nSubject:...baseball
1From: gld@cunixb.cc.columbia.edu (Gary L Dare)...hockey
2From: rudy@netcom.com (Rudy Wade)\\nSubject: Re...baseball
3From: monack@helium.gas.uug.arizona.edu (david...hockey
4Subject: Let it be Known\\nFrom: <ISSBTL@BYUVM....baseball
\n", - "" - ] - }, - "metadata": {}, - "execution_count": 5 - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "Both baseball and hockey are single tokens. We save the dataset as a jsonl file." - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 6, - "source": [ - "df.to_json(\"sport2.jsonl\", orient='records', lines=True)" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "### Data Preparation tool\n", - "We can now use a data preparation tool which will suggest a few improvements to our dataset before fine-tuning. Before launching the tool we update the openai library to ensure we're using the latest data preparation tool. We additionally specify `-q` which auto-accepts all suggestions." - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 7, - "source": [ - "!pip install --upgrade openai" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 8, - "source": [ - "!openai tools fine_tunes.prepare_data -f sport2.jsonl -q" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Analyzing...\n", - "\n", - "- Your file contains 1197 prompt-completion pairs\n", - "- Based on your data it seems like you're trying to fine-tune a model for classification\n", - "- For classification, we recommend you try one of the faster and cheaper models, such as `ada`\n", - "- For classification, you can estimate the expected model performance by keeping a held out dataset, which is not used for training\n", - "- There are 11 examples that are very long. These are rows: [134, 200, 281, 320, 404, 595, 704, 838, 1113, 1139, 1174]\n", - "For conditional generation, and for classification the examples shouldn't be longer than 2048 tokens.\n", - "- Your data does not contain a common separator at the end of your prompts. Having a separator string appended to the end of the prompt makes it clearer to the fine-tuned model where the completion should begin. See https://beta.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples. If you intend to do open-ended generation, then you should leave the prompts empty\n", - "- The completion should start with a whitespace character (` `). This tends to produce better results due to the tokenization we use. See https://beta.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details\n", - "\n", - "Based on the analysis we will perform the following actions:\n", - "- [Recommended] Remove 11 long examples [Y/n]: Y\n", - "- [Recommended] Add a suffix separator `\\n\\n###\\n\\n` to all prompts [Y/n]: Y\n", - "- [Recommended] Add a whitespace character to the beginning of the completion [Y/n]: Y\n", - "- [Recommended] Would you like to split into training and validation set? [Y/n]: Y\n", - "\n", - "\n", - "Your data will be written to a new JSONL file. Proceed [Y/n]: Y\n", - "\n", - "Wrote modified files to `sport2_prepared_train.jsonl` and `sport2_prepared_valid.jsonl`\n", - "Feel free to take a look!\n", - "\n", - "Now use that file when fine-tuning:\n", - "> openai api fine_tunes.create -t \"sport2_prepared_train.jsonl\" -v \"sport2_prepared_valid.jsonl\" --compute_classification_metrics --classification_positive_class \" baseball\"\n", - "\n", - "After you’ve fine-tuned a model, remember that your prompt has to end with the indicator string `\\n\\n###\\n\\n` for the model to start generating completions, rather than continuing with the prompt.\n", - "Once your model starts training, it'll approximately take 30.8 minutes to train a `curie` model, and less for `ada` and `babbage`. Queue will approximately take half an hour per job ahead of you.\n" - ] - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "The tool helpfully suggests a few improvements to the dataset and splits the dataset into training and validation set.\n", - "\n", - "A suffix between a prompt and a completion is necessary to tell the model that the input text has stopped, and that it now needs to predict the class. Since we use the same separator in each example, the model is able to learn that it is meant to predict either baseball or hockey following the separator.\n", - "A whitespace prefix in completions is useful, as most word tokens are tokenized with a space prefix.\n", - "The tool also recognized that this is likely a classification task, so it suggested to split the dataset into training and validation datasets. This will allow us to easily measure expected performance on new data." - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "## Fine-tuning\n", - "The tool suggests we run the following command to train the dataset. Since this is a classification task, we would like to know what the generalization performance on the provided validation set is for our classification use case. The tool suggests to add `--compute_classification_metrics --classification_positive_class \" baseball\"` in order to compute the classification metrics.\n", - "\n", - "We can simply copy the suggested command from the CLI tool. We specifically add `-m ada` to fine-tune a cheaper and faster ada model, which is usually comperable in performance to slower and more expensive models on classification use cases. " - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 9, - "source": [ - "!openai api fine_tunes.create -t \"sport2_prepared_train.jsonl\" -v \"sport2_prepared_valid.jsonl\" --compute_classification_metrics --classification_positive_class \" baseball\" -m ada" - ], - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Upload progress: 100%|████████████████████| 1.52M/1.52M [00:00<00:00, 1.81Mit/s]\n", - "Uploaded file from sport2_prepared_train.jsonl: file-Dxx2xJqyjcwlhfDHpZdmCXlF\n", - "Upload progress: 100%|███████████████████████| 388k/388k [00:00<00:00, 507kit/s]\n", - "Uploaded file from sport2_prepared_valid.jsonl: file-Mvb8YAeLnGdneSAFcfiVcgcN\n", - "Created fine-tune: ft-2zaA7qi0rxJduWQpdvOvmGn3\n", - "Streaming events until fine-tuning is complete...\n", - "\n", - "(Ctrl-C will interrupt the stream, but not cancel the fine-tune)\n", - "[2021-07-30 13:15:50] Created fine-tune: ft-2zaA7qi0rxJduWQpdvOvmGn3\n", - "[2021-07-30 13:15:52] Fine-tune enqueued. Queue number: 0\n", - "[2021-07-30 13:15:56] Fine-tune started\n", - "[2021-07-30 13:18:55] Completed epoch 1/4\n", - "[2021-07-30 13:20:47] Completed epoch 2/4\n", - "[2021-07-30 13:22:40] Completed epoch 3/4\n", - "[2021-07-30 13:24:31] Completed epoch 4/4\n", - "[2021-07-30 13:26:22] Uploaded model: ada:ft-openai-2021-07-30-12-26-20\n", - "[2021-07-30 13:26:27] Uploaded result file: file-6Ki9RqLQwkChGsr9CHcr1ncg\n", - "[2021-07-30 13:26:28] Fine-tune succeeded\n", - "\n", - "Job complete! Status: succeeded 🎉\n", - "Try out your fine-tuned model:\n", - "\n", - "openai api completions.create -m ada:ft-openai-2021-07-30-12-26-20 -p \n" - ] - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "The model is successfully trained in about ten minutes. We can see the model name is `ada:ft-openai-2021-07-30-12-26-20`, which we can use for doing inference." - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "### [Advanced] Results and expected model performance\n", - "We can now download the results file to observe the expected performance on a held out validation set." - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 10, - "source": [ - "!openai api fine_tunes.results -i ft-2zaA7qi0rxJduWQpdvOvmGn3 > result.csv" - ], - "outputs": [], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 11, - "source": [ - "results = pd.read_csv('result.csv')\n", - "results[results['classification/accuracy'].notnull()].tail(1)" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - " step elapsed_tokens elapsed_examples training_loss \\\n", - "929 930 3027688 3720 0.044408 \n", - "\n", - " training_sequence_accuracy training_token_accuracy \\\n", - "929 1.0 1.0 \n", - "\n", - " classification/accuracy classification/precision classification/recall \\\n", - "929 0.991597 0.983471 1.0 \n", - "\n", - " classification/auroc classification/auprc classification/f1.0 \\\n", - "929 1.0 1.0 0.991667 \n", - "\n", - " validation_loss validation_sequence_accuracy validation_token_accuracy \n", - "929 NaN NaN NaN " - ], - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
stepelapsed_tokenselapsed_examplestraining_losstraining_sequence_accuracytraining_token_accuracyclassification/accuracyclassification/precisionclassification/recallclassification/aurocclassification/auprcclassification/f1.0validation_lossvalidation_sequence_accuracyvalidation_token_accuracy
929930302768837200.0444081.01.00.9915970.9834711.01.01.00.991667NaNNaNNaN
\n", - "
" - ] - }, - "metadata": {}, - "execution_count": 11 - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "The accuracy reaches 99.6%. On the plot below we can see how accuracy on the validation set increases during the training run. " - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 12, - "source": [ - "results[results['classification/accuracy'].notnull()]['classification/accuracy'].plot()" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 12 - }, - { - "output_type": "display_data", - "data": { - "text/plain": [ - "
" - ], - "image/png": "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" - }, - "metadata": { - "needs_background": "light" - } - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "## Using the model\n", - "We can now call the model to get the predictions." - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 13, - "source": [ - "test = pd.read_json('sport2_prepared_valid.jsonl', lines=True)\n", - "test.head()" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - " prompt completion\n", - "0 From: gld@cunixb.cc.columbia.edu (Gary L Dare)... hockey\n", - "1 From: smorris@venus.lerc.nasa.gov (Ron Morris ... hockey\n", - "2 From: golchowy@alchemy.chem.utoronto.ca (Geral... hockey\n", - "3 From: krattige@hpcc01.corp.hp.com (Kim Krattig... baseball\n", - "4 From: warped@cs.montana.edu (Doug Dolven)\\nSub... baseball" - ], - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
promptcompletion
0From: gld@cunixb.cc.columbia.edu (Gary L Dare)...hockey
1From: smorris@venus.lerc.nasa.gov (Ron Morris ...hockey
2From: golchowy@alchemy.chem.utoronto.ca (Geral...hockey
3From: krattige@hpcc01.corp.hp.com (Kim Krattig...baseball
4From: warped@cs.montana.edu (Doug Dolven)\\nSub...baseball
\n", - "
" - ] - }, - "metadata": {}, - "execution_count": 13 - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "We need to use the same separator following the prompt which we used during fine-tuning. In this case it is `\\n\\n###\\n\\n`. Since we're concerned with classification, we want the temperature to be as low as possible, and we only require one token completion to determine the prediction of the model." - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 14, - "source": [ - "ft_model = 'ada:ft-openai-2021-07-30-12-26-20'\n", - "res = openai.Completion.create(model=ft_model, prompt=test['prompt'][0] + '\\n\\n###\\n\\n', max_tokens=1, temperature=0)\n", - "res['choices'][0]['text']\n" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "' hockey'" - ] - }, - "metadata": {}, - "execution_count": 14 - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "To get the log probabilities, we can specify logprobs parameter on the completion request" - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 15, - "source": [ - "res = openai.Completion.create(model=ft_model, prompt=test['prompt'][0] + '\\n\\n###\\n\\n', max_tokens=1, temperature=0, logprobs=2)\n", - "res['choices'][0]['logprobs']['top_logprobs'][0]" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - " JSON: {\n", - " \" baseball\": -7.6311407,\n", - " \" hockey\": -0.0006307676\n", - "}" - ] - }, - "metadata": {}, - "execution_count": 15 - } - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "We can see that the model predicts hockey as a lot more likely than baseball, which is the correct prediction. By requesting log_probs, we can see the prediction (log) probability for each class." - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "### Generalization\n", - "Interestingly, our fine-tuned classifier is quite versatile. Despite being trained on emails to different mailing lists, it also successfully predicts tweets." - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 16, - "source": [ - "sample_hockey_tweet = \"\"\"Thank you to the \n", - "@Canes\n", - " and all you amazing Caniacs that have been so supportive! You guys are some of the best fans in the NHL without a doubt! Really excited to start this new chapter in my career with the \n", - "@DetroitRedWings\n", - " !!\"\"\"\n", - "res = openai.Completion.create(model=ft_model, prompt=sample_hockey_tweet + '\\n\\n###\\n\\n', max_tokens=1, temperature=0, logprobs=2)\n", - "res['choices'][0]['text']" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "' hockey'" - ] - }, - "metadata": {}, - "execution_count": 16 - } - ], - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 17, - "source": [ - "sample_baseball_tweet=\"\"\"BREAKING: The Tampa Bay Rays are finalizing a deal to acquire slugger Nelson Cruz from the Minnesota Twins, sources tell ESPN.\"\"\"\n", - "res = openai.Completion.create(model=ft_model, prompt=sample_baseball_tweet + '\\n\\n###\\n\\n', max_tokens=1, temperature=0, logprobs=2)\n", - "res['choices'][0]['text']" - ], - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "' baseball'" - ] - }, - "metadata": {}, - "execution_count": 17 - } - ], - "metadata": {} + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Fine-tuned_classification.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Fine-tuned_classification.ipynb)." + ] } ], "metadata": { - "orig_nbformat": 4, + "kernelspec": { + "display_name": "Python 3.9.9 ('openai')", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python", - "version": "3.7.3", - "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, - "pygments_lexer": "ipython3", + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", "nbconvert_exporter": "python", - "file_extension": ".py" - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3.7.3 64-bit ('base': conda)" + "pygments_lexer": "ipython3", + "version": "3.9.9" }, - "interpreter": { - "hash": "3b138a8faad971cc852f62bcf00f59ea0e31721743ea2c5a866ca26adf572e75" + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } } }, "nbformat": 4, diff --git a/examples/finetuning/olympics-1-collect-data.ipynb b/examples/finetuning/olympics-1-collect-data.ipynb index 7a88051bbf..a0c55d438e 100644 --- a/examples/finetuning/olympics-1-collect-data.ipynb +++ b/examples/finetuning/olympics-1-collect-data.ipynb @@ -4,494 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# 1. Collect Wikipedia data about Olympic Games 2020\n", - "\n", - "The idea of this project is to create a question answering model, based on a few paragraphs of provided text. Base GPT-3 models do a good job at answering questions when the answer is contained within the paragraph, however if the answer isn't contained, the base models tend to try their best to answer anyway, often leading to confabulated answers. \n", - "\n", - "To create a model which answers questions only if there is sufficient context for doing so, we first create a dataset of questions and answers based on paragraphs of text. In order to train the model to answer only when the answer is present, we also add adversarial examples, where the question doesn't match the context. In those cases, we ask the model to output \"No sufficient context for answering the question\". \n", - "\n", - "We will perform this task in three notebooks:\n", - "1. The first (this) notebook focuses on collecting recent data, which GPT-3 didn't see during it's pre-training. We picked the topic of Olympic Games 2020 (which actually took place in the summer of 2021), and downloaded 713 unique pages. We organized the dataset by individual sections, which will serve as context for asking and answering the questions.\n", - "2. The [second notebook](olympics-2-create-qa.ipynb) will utilize Davinci-instruct to ask a few questions based on a Wikipedia section, as well as answer those questions, based on that section.\n", - "3. The [third notebook](olympics-3-train-qa.ipynb) will utilize the dataset of context, question and answer pairs to additionally create adversarial questions and context pairs, where the question was not generated on that context. In those cases the model will be prompted to answer \"No sufficient context for answering the question\". We will also train a discriminator model, which predicts whether the question can be answered based on the context or not." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1.1 Data extraction using the wikipedia API\n", - "Extracting the data will take about half an hour, and processing will likely take about as much." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "909" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import pandas as pd\n", - "import wikipedia\n", - "\n", - "\n", - "def filter_olympic_2020_titles(titles):\n", - " \"\"\"\n", - " Get the titles which are related to Olympic games hosted in 2020, given a list of titles\n", - " \"\"\"\n", - " titles = [title for title in titles if '2020' in title and 'olympi' in title.lower()]\n", - " \n", - " return titles\n", - "\n", - "def get_wiki_page(title):\n", - " \"\"\"\n", - " Get the wikipedia page given a title\n", - " \"\"\"\n", - " try:\n", - " return wikipedia.page(title)\n", - " except wikipedia.exceptions.DisambiguationError as e:\n", - " return wikipedia.page(e.options[0])\n", - " except wikipedia.exceptions.PageError as e:\n", - " return None\n", - "\n", - "def recursively_find_all_pages(titles, titles_so_far=set()):\n", - " \"\"\"\n", - " Recursively find all the pages that are linked to the Wikipedia titles in the list\n", - " \"\"\"\n", - " all_pages = []\n", - " \n", - " titles = list(set(titles) - titles_so_far)\n", - " titles = filter_olympic_2020_titles(titles)\n", - " titles_so_far.update(titles)\n", - " for title in titles:\n", - " page = get_wiki_page(title)\n", - " if page is None:\n", - " continue\n", - " all_pages.append(page)\n", - "\n", - " new_pages = recursively_find_all_pages(page.links, titles_so_far)\n", - " for pg in new_pages:\n", - " if pg.title not in [p.title for p in all_pages]:\n", - " all_pages.append(pg)\n", - " titles_so_far.update(page.links)\n", - " return all_pages\n", - "\n", - "\n", - "pages = recursively_find_all_pages([\"2020 Summer Olympics\"])\n", - "len(pages)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1.2 Filtering the Wikipedia pages and splitting them into sections by headings\n", - "We remove sections unlikely to contain textual information, and ensure that each section is not longer than the token limit" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('Bermuda at the 2020 Summer Olympics',\n", - " 'Equestrian',\n", - " \"Bermuda entered one dressage rider into the Olympic competition by finishing in the top four, outside the group selection, of the individual FEI Olympic Rankings for Groups D and E (North, Central, and South America), marking the country's recurrence to the sport after an eight-year absence. The quota was later withdrawn, following an injury of Annabelle Collins' main horse Joyero and a failure to obtain minimum eligibility requirements (MER) aboard a new horse Chuppy Checker.\",\n", - " 104)" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\n", - "import re\n", - "from typing import Set\n", - "from transformers import GPT2TokenizerFast\n", - "\n", - "import numpy as np\n", - "from nltk.tokenize import sent_tokenize\n", - "\n", - "tokenizer = GPT2TokenizerFast.from_pretrained(\"gpt2\")\n", - "\n", - "def count_tokens(text: str) -> int:\n", - " \"\"\"count the number of tokens in a string\"\"\"\n", - " return len(tokenizer.encode(text))\n", - "\n", - "def reduce_long(\n", - " long_text: str, long_text_tokens: bool = False, max_len: int = 590\n", - ") -> str:\n", - " \"\"\"\n", - " Reduce a long text to a maximum of `max_len` tokens by potentially cutting at a sentence end\n", - " \"\"\"\n", - " if not long_text_tokens:\n", - " long_text_tokens = count_tokens(long_text)\n", - " if long_text_tokens > max_len:\n", - " sentences = sent_tokenize(long_text.replace(\"\\n\", \" \"))\n", - " ntokens = 0\n", - " for i, sentence in enumerate(sentences):\n", - " ntokens += 1 + count_tokens(sentence)\n", - " if ntokens > max_len:\n", - " return \". \".join(sentences[:i][:-1]) + \".\"\n", - "\n", - " return long_text\n", - "\n", - "discard_categories = ['See also', 'References', 'External links', 'Further reading', \"Footnotes\",\n", - " \"Bibliography\", \"Sources\", \"Citations\", \"Literature\", \"Footnotes\", \"Notes and references\",\n", - " \"Photo gallery\", \"Works cited\", \"Photos\", \"Gallery\", \"Notes\", \"References and sources\",\n", - " \"References and notes\",]\n", - "\n", - "\n", - "def extract_sections(\n", - " wiki_text: str,\n", - " title: str,\n", - " max_len: int = 1500,\n", - " discard_categories: Set[str] = discard_categories,\n", - ") -> str:\n", - " \"\"\"\n", - " Extract the sections of a Wikipedia page, discarding the the references and other low information sections\n", - " \"\"\"\n", - " if len(wiki_text) == 0:\n", - " return []\n", - "\n", - " # find all headings and the coresponding contents\n", - " headings = re.findall(\"==+ .* ==+\", wiki_text)\n", - " for heading in headings:\n", - " wiki_text = wiki_text.replace(heading, \"==+ !! ==+\")\n", - " contents = wiki_text.split(\"==+ !! ==+\")\n", - " contents = [c.strip() for c in contents]\n", - " assert len(headings) == len(contents) - 1\n", - "\n", - " cont = contents.pop(0).strip()\n", - " outputs = [(title, \"Summary\", cont, count_tokens(cont)+4)]\n", - " \n", - " # discard the discard categories, accounting for a tree structure\n", - " max_level = 100\n", - " keep_group_level = max_level\n", - " remove_group_level = max_level\n", - " nheadings, ncontents = [], []\n", - " for heading, content in zip(headings, contents):\n", - " plain_heading = \" \".join(heading.split(\" \")[1:-1])\n", - " num_equals = len(heading.split(\" \")[0])\n", - " if num_equals <= keep_group_level:\n", - " keep_group_level = max_level\n", - "\n", - " if num_equals > remove_group_level:\n", - " if (\n", - " num_equals <= keep_group_level\n", - " ):\n", - " continue\n", - " keep_group_level = max_level\n", - " if plain_heading in discard_categories:\n", - " remove_group_level = num_equals\n", - " keep_group_level = max_level\n", - " continue\n", - " nheadings.append(heading.replace(\"=\", \"\").strip())\n", - " ncontents.append(content)\n", - " remove_group_level = max_level\n", - "\n", - " # count the tokens of each section\n", - " ncontent_ntokens = [\n", - " count_tokens(c)\n", - " + 3\n", - " + count_tokens(\" \".join(h.split(\" \")[1:-1]))\n", - " - (1 if len(c) == 0 else 0)\n", - " for h, c in zip(nheadings, ncontents)\n", - " ]\n", - "\n", - " # Create a tuple of (title, section_name, content, number of tokens)\n", - " outputs += [(title, h, c, t) if t 1024). Running this sequence through the model will result in indexing errors\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titleheadingcontenttokens
02020 Summer OlympicsSummaryThe 2020 Summer Olympics (Japanese: 2020年夏季オリン...713
12020 Summer OlympicsHost city selectionThe International Olympic Committee (IOC) vote...126
22020 Summer OlympicsImpact of the COVID-19 pandemicIn January 2020, concerns were raised about th...369
32020 Summer OlympicsQualifying event cancellation and postponementConcerns about the pandemic began to affect qu...298
42020 Summer OlympicsEffect on doping testsMandatory doping tests were being severely res...163
\n", - "
" - ], - "text/plain": [ - " title heading \\\n", - "0 2020 Summer Olympics Summary \n", - "1 2020 Summer Olympics Host city selection \n", - "2 2020 Summer Olympics Impact of the COVID-19 pandemic \n", - "3 2020 Summer Olympics Qualifying event cancellation and postponement \n", - "4 2020 Summer Olympics Effect on doping tests \n", - "\n", - " content tokens \n", - "0 The 2020 Summer Olympics (Japanese: 2020年夏季オリン... 713 \n", - "1 The International Olympic Committee (IOC) vote... 126 \n", - "2 In January 2020, concerns were raised about th... 369 \n", - "3 Concerns about the pandemic began to affect qu... 298 \n", - "4 Mandatory doping tests were being severely res... 163 " - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res = []\n", - "for page in pages:\n", - " res += extract_sections(page.content, page.title)\n", - "df = pd.DataFrame(res, columns=[\"title\", \"heading\", \"content\", \"tokens\"])\n", - "df = df[df.tokens>40]\n", - "df = df.drop_duplicates(['title','heading'])\n", - "df = df.reset_index().drop('index',axis=1) # reset index\n", - "df.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Save the section dataset\n", - "We will save the section dataset, for the [next notebook](olympics-2-create-qa.ipynb)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "df.to_csv('olympics-data/olympics_sections.csv', index=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1.3 (Optional) Exploring the data " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Concerns and controversies at the 2020 Summer Olympics 51\n", - "United States at the 2020 Summer Olympics 46\n", - "Great Britain at the 2020 Summer Olympics 42\n", - "Canada at the 2020 Summer Olympics 39\n", - "Olympic Games 39\n", - "Name: title, dtype: int64" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df.title.value_counts().head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There appear to be winter and summer Olympics 2020. We chose to leave a little ambiguity and noise in the dataset, even though we were interested in only Summer Olympics 2020." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True 3567\n", - "False 305\n", - "Name: title, dtype: int64" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df.title.str.contains('Summer').value_counts()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False 3774\n", - "True 98\n", - "Name: title, dtype: int64" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df.title.str.contains('Winter').value_counts()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "import pandas as pd\n", - "from matplotlib import pyplot as plt\n", - "\n", - "df = pd.read_csv('olympics-data/olympics_sections.csv')\n", - "df[['tokens']].hist()\n", - "# add axis descriptions and title\n", - "plt.xlabel('Number of tokens')\n", - "plt.ylabel('Number of Wikipedia sections')\n", - "plt.title('Distribution of number of tokens in Wikipedia sections')\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the majority of section are fairly short (less than 500 tokens)." + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/fine-tuned_qa/](https://github.com/openai/openai-cookbook/tree/main/examples/fine-tuned_qa)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -504,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/finetuning/olympics-2-create-qa.ipynb b/examples/finetuning/olympics-2-create-qa.ipynb index 9834cec85b..a0c55d438e 100644 --- a/examples/finetuning/olympics-2-create-qa.ipynb +++ b/examples/finetuning/olympics-2-create-qa.ipynb @@ -4,732 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# 2. Creating a synthetic Q&A dataset\n", - "We use [`davinci-instruct-beta-v2`](https://beta.openai.com/docs/engines/instruct-series-beta), a model specialized in following instructions, to create questions based on the given context. Then we also use [`davinci-instruct-beta-v2`](https://beta.openai.com/docs/engines/instruct-series-beta) to answer those questions, given the same context. \n", - "\n", - "This is expensive, and will also take a long time, as we call the davinci engine for each section. You can simply download the final dataset instead.\n", - "\n", - "We're using the dataset created using the [previous notebook](olympics-1-collect-data.ipynb)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2.1 Read in the data, and create a context\n", - "Create a context by concatenating the title, the heading and the content of that section" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titleheadingcontenttokenscontext
02020 Summer OlympicsSummaryThe 2020 Summer Olympics (Japanese: 2020年夏季オリン...7132020 Summer Olympics\\nSummary\\n\\nThe 2020 Summ...
12020 Summer OlympicsHost city selectionThe International Olympic Committee (IOC) vote...1262020 Summer Olympics\\nHost city selection\\n\\nT...
22020 Summer OlympicsImpact of the COVID-19 pandemicIn January 2020, concerns were raised about th...3692020 Summer Olympics\\nImpact of the COVID-19 p...
32020 Summer OlympicsQualifying event cancellation and postponementConcerns about the pandemic began to affect qu...2982020 Summer Olympics\\nQualifying event cancell...
42020 Summer OlympicsEffect on doping testsMandatory doping tests were being severely res...1632020 Summer Olympics\\nEffect on doping tests\\n...
\n", - "
" - ], - "text/plain": [ - " title heading \\\n", - "0 2020 Summer Olympics Summary \n", - "1 2020 Summer Olympics Host city selection \n", - "2 2020 Summer Olympics Impact of the COVID-19 pandemic \n", - "3 2020 Summer Olympics Qualifying event cancellation and postponement \n", - "4 2020 Summer Olympics Effect on doping tests \n", - "\n", - " content tokens \\\n", - "0 The 2020 Summer Olympics (Japanese: 2020年夏季オリン... 713 \n", - "1 The International Olympic Committee (IOC) vote... 126 \n", - "2 In January 2020, concerns were raised about th... 369 \n", - "3 Concerns about the pandemic began to affect qu... 298 \n", - "4 Mandatory doping tests were being severely res... 163 \n", - "\n", - " context \n", - "0 2020 Summer Olympics\\nSummary\\n\\nThe 2020 Summ... \n", - "1 2020 Summer Olympics\\nHost city selection\\n\\nT... \n", - "2 2020 Summer Olympics\\nImpact of the COVID-19 p... \n", - "3 2020 Summer Olympics\\nQualifying event cancell... \n", - "4 2020 Summer Olympics\\nEffect on doping tests\\n... " - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import pandas as pd\n", - "df = pd.read_csv('olympics-data/olympics_sections.csv')\n", - "df['context'] = df.title + \"\\n\" + df.heading + \"\\n\\n\" + df.content\n", - "df.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2.2 Create questions based on the context\n", - "Use davinci-instruct to generate a number of plausible questions relating to the Wikipedia section contents.\n", - "\n", - "Note: We have used temperature=0, but it may be beneficial to experiment with a higher temperature to get a higher diversity of questions.\n", - "\n", - "**WARNING: This step will last a long time, and consume a lot of tokens, as it calls davinci-instruct for every section to generate a number of questions.**" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1. What is the 2020 Summer Olympics?\n", - "2. When did the 2020 Summer Olympics take place?\n", - "3. Who won the most medals at the 2020 Summer Olympics?\n", - "4. Who won the most gold medals at the 2020 Summer Olympics?\n", - "5. Who won the most medals at the 2020 Summer Olympics?\n" - ] - } - ], - "source": [ - "import openai\n", - "\n", - "def get_questions(context):\n", - " try:\n", - " response = openai.Completion.create(\n", - " engine=\"davinci-instruct-beta-v2\",\n", - " prompt=f\"Write questions based on the text below\\n\\nText: {context}\\n\\nQuestions:\\n1.\",\n", - " temperature=0,\n", - " max_tokens=257,\n", - " top_p=1,\n", - " frequency_penalty=0,\n", - " presence_penalty=0,\n", - " stop=[\"\\n\\n\"]\n", - " )\n", - " return response['choices'][0]['text']\n", - " except:\n", - " return \"\"\n", - "\n", - "\n", - "df['questions']= df.context.apply(get_questions)\n", - "df['questions'] = \"1.\" + df.questions\n", - "print(df[['questions']].values[0][0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The prompt is designed to generate a number of questions. Example questions above were generated based on the summary section of the 2020 Summer Olympics page.\n", - "\n", - "We can observe that the questions 3 and 5 above repeat. Sometimes the generated questions could be ambiguous without the context. We will show that even despite these limitations we can create a successful model." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The 2020 Summer Olympics (Japanese: 2020年夏季オリンピック, Hepburn: Nisen Nijū-nen Kaki Orinpikku), officially the Games of the XXXII Olympiad (第三十二回オリンピック競技大会, Dai Sanjūni-kai Orinpikku Kyōgi Taikai) and branded as Tokyo 2020 (東京2020, Tōkyō Nii Zero Nii Zero), was an international multi-sport event held from 23 July to 8 August 2021 in Tokyo, Japan, with some preliminary events that began on 21 July.\n", - "Tokyo was selected as the host city during the 125th IOC Session in Buenos Aires, Argentina, on 7 September 2013. Originally scheduled to take place from 24 July to 9 August 2020, the event was postponed to 2021 in March 2020 as a result of the COVID-19 pandemic, the first such instance in the history of the Olympic Games (previous games had been cancelled but not rescheduled). However, the event retained the Tokyo 2020 name for marketing and branding purposes. It was largely held behind closed doors with no public spectators permitted due to the declaration of a state of emergency in the Greater Tokyo Area in response to the pandemic. The Summer Paralympics were held between 24 August and 5 September 2021, 16 days after the completion of the Olympics.The 2020 Games were the fourth Olympic Games to be held in Japan, following the Tokyo 1964 (Summer), Sapporo 1972 (Winter) and Nagano 1998 (Winter) games. Tokyo is the first city in Asia to hold the Summer Games twice. The 2020 Games were the second of three consecutive Olympics to be held in East Asia, following the 2018 Winter Olympics in Pyeongchang, South Korea and preceding the 2022 Winter Olympics in Beijing, China.\n", - "New events were introduced in existing sports for 2020, including 3x3 basketball, freestyle BMX and mixed gender team events in a number of existing sports, as well as the return of madison cycling for men and an introduction of the same event for women. New IOC policies also allowed the host organizing committee to add new sports to the Olympic program for just one Games. The disciplines added by the Japanese Olympic Committee were baseball and softball, karate, sport climbing, surfing and skateboarding, the last four of which made their Olympic debuts, and the last three of which will remain on the Olympic program.The United States topped the medal count by both total golds (39) and total medals (113), with China finishing second by both respects (38 and 88). Host nation Japan finished third, setting a record for the most gold medals and total medals ever won by their delegation at an Olympic Games with 27 and 58. Great Britain finished fourth, with a total of 22 gold and 65 medals, becoming the first nation at the Summer Olympics to increase or equal their total medals won in the two Games subsequent to hosting them. The Russian delegation competing as the ROC (not to be confused with the Republic of China (Taiwan) which competed as Chinese Taipei, not ROC) finished fifth with 20 gold medals and third in the overall medal count, with 71 medals. Bermuda, the Philippines and Qatar won their first-ever Olympic gold medals. Burkina Faso, San Marino and Turkmenistan won their first-ever Olympic medals.\n" - ] - } - ], - "source": [ - "print(df.content.values[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2.3 Create answers based on the context\n", - "Use davinci-instruct to answer the questions given the relevant Wikipedia section contents\n", - "\n", - "Note: We have used temperature=0, but it may be beneficial to experiment with a higher temperature to get a higher diversity of questions.\n", - "\n", - "**WARNING: This step will last a long time, and consume a lot of tokens, as it calls davinci-instruct for every section to answer all the questions.**" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1. The 2020 Summer Olympics is an international multi-sport event held from 23 July to 8 August 2021 in Tokyo, Japan.\n", - "2. The 2020 Summer Olympics took place from 23 July to 8 August 2021.\n", - "3. The United States topped the medal count by both total golds (39) and total medals (113), with China finishing second by both respects (38 and 88).\n", - "4. The United States topped the medal count by both total golds (39) and total medals (113), with China finishing second by both respects (38 and 88).\n", - "5. The United States topped the medal count by both total golds (39) and total medals (113), with China finishing second by both respects (38 and 88).\n" - ] - } - ], - "source": [ - "def get_answers(row):\n", - " try:\n", - " response = openai.Completion.create(\n", - " engine=\"davinci-instruct-beta-v2\",\n", - " prompt=f\"Write questions based on the text below\\n\\nText: {row.context}\\n\\nQuestions:\\n{row.questions}\\n\\nAnswers:\\n1.\",\n", - " temperature=0,\n", - " max_tokens=257,\n", - " top_p=1,\n", - " frequency_penalty=0,\n", - " presence_penalty=0\n", - " )\n", - " return response['choices'][0]['text']\n", - " except Exception as e:\n", - " print (e)\n", - " return \"\"\n", - "\n", - "\n", - "df['answers']= df.apply(get_answers, axis=1)\n", - "df['answers'] = \"1.\" + df.answers\n", - "df = df.dropna().reset_index().drop('index',axis=1)\n", - "print(df[['answers']].values[0][0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These are the answers to the questions above based on the context around the host city selection. \n", - "\n", - "We can see that answers 3-5 contain the correct answer, but instead of answering the question directly, the answer is a verbatim extraction. Despite these occasional lower quality answers, we will show that the model can learn the task reasonably well, given a high number of examples." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2.4 Save the Olympics Q&A dataset based on Wikipedia sections\n", - "We save the file for use in the [next notebook](olympics-3-train-qa.ipynb)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "df.to_csv('olympics-data/olympics_qa.csv', index=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2.5 Search file\n", - "We create a search file ([API reference](https://beta.openai.com/docs/api-reference/files/list)), which can be used to retrieve the relevant context when a question is asked.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "df = df[df.tokens<2000]\n", - "df[['context', 'tokens']].rename(columns={'context':'text','tokens':'metadata'}).to_json('olympics-data/olympics_search.jsonl', orient='records', lines=True)\n", - "\n", - "search_file = openai.File.create(\n", - " file=open(\"olympics-data/olympics_search.jsonl\"),\n", - " purpose='search'\n", - ")\n", - "olympics_search_fileid = search_file['id']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2.6 Answer questions based on the context provided\n", - "\n", - "We will use a simple implementation of the answers endpoint. This works by simply using the [/search endpoint](https://beta.openai.com/docs/api-reference/searches), which searches over an indexed file to obtain the relevant sections which can be included in the context, following by a question and answering prompt given a specified model." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Athletics at the 2020 Summer Olympics – Women's 4 × 100 metres relay\n", - "Summary\n", - "\n", - "The women's 4 × 100 metres relay event at the 2020 Summer Olympics took place on 5 and 6 August 2021 at the Japan National Stadium. There were 16 competing relay teams, with each team having 5 members from which 4 were selected in each round.\n", - "\n", - "###\n", - "\n", - "Athletics at the 2020 Summer Olympics – Men's 4 × 100 metres relay\n", - "Qualification\n", - "\n", - "National Olympic Committees (NOCs) could qualify one relay team in one of three following ways:\n", - "The top 8 NOCs at the 2019 World Athletics Championships qualified a relay team.\n", - "The top 8 NOCs at the 2021 World Athletics Relays qualified a relay team.\n", - "Where an NOC placed in the top 8 at both the 2019 World Championships and the 2021 World Relays, the quota place was allocated to the world top list as of 29 June 2021. In this case, 4 teams did so, so there are 4 places available through the world rankings.A total of five athletes may be entered for a relay team. Should a NOC have also entered individual athletes in the corresponding individual event (100 m), the entered individual athletes must be included in the total of five (5) athletes entered for the relay event. In addition of five, NOCs can nominate a maximum of one alternate athlete for each team.\n", - "The qualifying period was originally from 1 May 2019 to 29 June 2020. Due to the COVID-19 pandemic, the period was suspended from 6 April 2020 to 30 November 2020, with the end date extended to 29 June 2021. The qualifying time standards could be obtained in various meets during the given period that have the approval of the IAAF. Both indoor and outdoor meets are eligible. The most recent Area Championships may be counted in the ranking, even if not during the qualifying period.\n" - ] - } - ], - "source": [ - "from answers_with_ft import create_context, answer_question\n", - "print(create_context(\"Where did women's 4 x 100 metres relay event take place during the 2020 Summer Olympics?\", olympics_search_fileid, max_len=400))" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' Japan National Stadium'" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "answer_question(olympics_search_fileid, \"davinci-instruct-beta-v2\", \n", - " \"Where did women's 4 x 100 metres relay event take place during the 2020 Summer Olympics?\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After we fine-tune the model for Q&A we'll be able to use it instead of [`davinci-instruct-beta-v2`](https://beta.openai.com/docs/engines/instruct-series-beta), to obtain better answers when the question can't be answered based on the context. We see a downside of [`davinci-instruct-beta-v2`](https://beta.openai.com/docs/engines/instruct-series-beta), which always attempts to answer the question, regardless of the relevant context being present or not. (Note the second question is asking about a future event, set in 2024.)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' Japan National Stadium'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "answer_question(olympics_search_fileid, \"davinci-instruct-beta-v2\", \n", - " \"Where did women's 4 x 100 metres relay event take place during the 2048 Summer Olympics?\", max_len=1000)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that davinci has a tendency to answer the question, even if the question can't be answered given the context provided. Note the question asked regarding 2048 Summer Olympics, which didn't happen yet, and the retrieved content has only returned results for 2020." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2.7 (Optional) Investigation into how likely the search endpoint is to return the relevant context" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(0, 58)\n" - ] - } - ], - "source": [ - "def check_context(title, heading, question, max_len=1800, search_model='ada', max_rerank=10):\n", - " \"\"\"\n", - " Evaluate the performance of the search model in retrieving the correct context\n", - "\n", - " Parameters\n", - " ----------\n", - " title: str\n", - " The title of the Wikipedia page\n", - " heading: str\n", - " The heading of the Wikipedia section\n", - " qusetion: str\n", - " The question\n", - " max_len: int\n", - " The maximum length of the context\n", - " search_model: str\n", - " The search model to use - `ada` is most cost effective\n", - " max_rerank: int\n", - " The maximum number of reranking documents to use the search model on\n", - "\n", - " Returns\n", - " -------\n", - " rank: int\n", - " The rank of the correct context\n", - " token_length: int\n", - " The number of tokens needed to obtain the correct context\n", - " \"\"\"\n", - " \n", - " try:\n", - " results = openai.Engine(search_model).search(\n", - " search_model=search_model, \n", - " query=question, \n", - " max_rerank=max_rerank,\n", - " file=olympics_search_fileid,\n", - " return_metadata=True\n", - " )\n", - " index=-1\n", - " returns = []\n", - " cur_len = 0\n", - " for result in results['data']:\n", - " cur_len += int(result['metadata']) + 4 # we add 4 tokens for the separator `\\n\\n###\\n\\n`\n", - " if cur_len > max_len:\n", - " break\n", - " returns.append(result['text'])\n", - " res = result['text'].split('\\n')\n", - " if res[0] == title and res[1] == heading:\n", - " index = len(returns) - 1\n", - " break\n", - " return index, cur_len\n", - " except Exception as e:\n", - " #print (e)\n", - " return []\n", - "print(check_context(\"Athletics at the 2020 Summer Olympics – Women's 4 × 100 metres relay\", \"Summary\", \"Where did women's 4 x 100 metres relay event take place during the 2020 Summer Olympics?\", max_len=10000))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We utilize the generated questions based on context to estimate how often we can retrieve the original context. These questions are noisy, so this is not a perfect estimate.\n", - "\n", - "Our questions and answers are prefixed with numbered bullet points, however due to the way they were generated, they are missing the first number, hence we add \"1.\" to the list of questions (and answers).\n", - "\n", - "We calculate the rank of the section retrieved using ada search, and the number of tokens in the context needed to retrieve the relevant section in full." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0 [(132, 27104), (-1, 22939), (8, 2151), (2, 121...\n", - "1 [(4, 1737), (0, 130), (8, 744), (96, 17208), (...\n", - "2 [(0, 373), (0, 373), (-1, 40610), (1, 570)]\n", - "3 [(0, 302), (0, 302), (5, 968), (8, 1425)]\n", - "4 [(0, 167), (0, 167), (2, 1442)]\n", - "Name: ada, dtype: object" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ada_results = df.apply(lambda x: [\n", - " check_context( x.title, \n", - " x.heading, \n", - " q[3:], # remove the number prefix\n", - " max_len=1000000, # set a large number to get the full context \n", - " search_model='ada', \n", - " max_rerank=200,\n", - " ) \n", - " for q in (x.questions).split('\\n') # split the questions\n", - " if len(q) >10 # remove the empty questions\n", - " ], axis=1)\n", - "ada_results.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "out = pd.concat([ada_results], axis=1)\n", - "out.columns = ['ada']\n", - "out.to_csv('olympics-data/search_engine_results.csv')" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def expand_lists(out):\n", - " \"\"\"\n", - " Expand a pandas series containing lists into a series, where each list element becomes a value on its own\n", - "\n", - " Input is a row per paragraph, which has multiple questions\n", - " Output is a row per question\n", - " \"\"\"\n", - " cols = [pd.DataFrame(out[name].tolist()).stack().reset_index(level=1, drop=True).rename(name) for name in out.columns] \n", - " return pd.concat(cols, axis=1)\n", - "\n", - "out_expanded = expand_lists(out)\n", - "out_expanded['rank'] = out_expanded.ada.apply(lambda x: x[0] if x != [] else -2)\n", - "out_expanded['tokens'] = out_expanded.ada.apply(lambda x: x[1] if x != [] else -2)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "74.3% of relevant paragraphs are retrieved within the first 2k tokens\n" - ] - } - ], - "source": [ - "within_2k = (out_expanded.tokens < 2000).mean()\n", - "print(f\"{within_2k*100:.1f}% of relevant paragraphs are retrieved within the first 2k tokens\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The relevant context can be obtained 74% of the time on this dataset" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "7.4% of relevant paragraphs are not retrieved within the first 200 results\n" - ] - } - ], - "source": [ - "outside_200 = (out_expanded['rank'] == -1).mean()\n", - "print(f\"{outside_200*100:.1f}% of relevant paragraphs are not retrieved within the first 200 results\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "7.4% of the time, this is due to the keyword search part of the search algorithm not retrieving the relevant context within the first 200 results.\n", - "18.3% of the time this is due to the semantic search not placing the relevant context within the first 2000 tokens." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "# plot a histogram, and add axis descriptions and title\n", - "out_expanded[(out_expanded['rank'] >=0)&(out_expanded['rank'] <30)]['rank'].hist(bins=29)\n", - "plt.xlabel('rank')\n", - "plt.ylabel('count')\n", - "plt.title('Histogram of ranks of retrieved paragraphs')\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "out_expanded[(out_expanded.tokens>=0)&(out_expanded.tokens < 2000)]['tokens'].hist(bins=29)\n", - "plt.xlabel('tokens')\n", - "plt.ylabel('count')\n", - "plt.title('Histogram of the number of minimum tokens needed')\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can observe that the context is most likely to be returned as one of the first results, and most likely to be returned within the first 200-500 tokens." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "-2 0.000063\n", - "-1 0.074428\n", - " 0 0.453420\n", - " 1 0.089515\n", - " 2 0.047146\n", - " 3 0.032437\n", - " 4 0.024139\n", - " 5 0.019676\n", - " 6 0.015967\n", - " 7 0.013452\n", - " 8 0.011189\n", - " 9 0.009869\n", - " 10 0.009178\n", - "Name: rank, dtype: float64" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# normalized value_counts\n", - "out_expanded['rank'].value_counts(normalize=True).sort_index()[:13]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "probabilities of the relevant context being returned at each rank. (-2 means a processing error, -1 means the rank is >200)" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/fine-tuned_qa/](https://github.com/openai/openai-cookbook/tree/main/examples/fine-tuned_qa)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -742,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/finetuning/olympics-3-train-qa.ipynb b/examples/finetuning/olympics-3-train-qa.ipynb index 76fb3f3ca8..a0c55d438e 100644 --- a/examples/finetuning/olympics-3-train-qa.ipynb +++ b/examples/finetuning/olympics-3-train-qa.ipynb @@ -4,618 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# 3. Train a fine-tuning model specialized for Q&A\n", - "This notebook will utilize the dataset of context, question and answer pairs to additionally create adversarial questions and context pairs, where the question was not generated on that context. In those cases the model will be prompted to answer \"No sufficient context for answering the question\". We will also train a discriminator model, which predicts whether the question can be answered based on the context or not.\n", - "\n", - "We will add hard adversarial examples as well, which will be based either on semantically similar sections, or neighbouring sections, originating from the same article." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titleheadingcontenttokenscontextquestionsanswers
02020 Summer OlympicsSummaryThe 2020 Summer Olympics (Japanese: 2020年夏季オリン...7132020 Summer Olympics\\nSummary\\n\\nThe 2020 Summ...1. What is the 2020 Summer Olympics?\\n2. When ...1. The 2020 Summer Olympics is an internationa...
12020 Summer OlympicsHost city selectionThe International Olympic Committee (IOC) vote...1262020 Summer Olympics\\nHost city selection\\n\\nT...1. \\n2. \\n3. \\n4.1. What is the International Olympic Committee...
22020 Summer OlympicsImpact of the COVID-19 pandemicIn January 2020, concerns were raised about th...3692020 Summer Olympics\\nImpact of the COVID-19 p...1. What was the COVID-19 pandemic?\\n2. How did...1. The COVID-19 pandemic was a pandemic that o...
32020 Summer OlympicsQualifying event cancellation and postponementConcerns about the pandemic began to affect qu...2982020 Summer Olympics\\nQualifying event cancell...1. What was the original location of the Asia ...1. The original location of the Asia & Oceania...
42020 Summer OlympicsEffect on doping testsMandatory doping tests were being severely res...1632020 Summer Olympics\\nEffect on doping tests\\n...1. What was the COVID-19 pandemic?\\n2. What di...1. The COVID-19 pandemic was a pandemic that o...
\n", - "
" - ], - "text/plain": [ - " title heading \\\n", - "0 2020 Summer Olympics Summary \n", - "1 2020 Summer Olympics Host city selection \n", - "2 2020 Summer Olympics Impact of the COVID-19 pandemic \n", - "3 2020 Summer Olympics Qualifying event cancellation and postponement \n", - "4 2020 Summer Olympics Effect on doping tests \n", - "\n", - " content tokens \\\n", - "0 The 2020 Summer Olympics (Japanese: 2020年夏季オリン... 713 \n", - "1 The International Olympic Committee (IOC) vote... 126 \n", - "2 In January 2020, concerns were raised about th... 369 \n", - "3 Concerns about the pandemic began to affect qu... 298 \n", - "4 Mandatory doping tests were being severely res... 163 \n", - "\n", - " context \\\n", - "0 2020 Summer Olympics\\nSummary\\n\\nThe 2020 Summ... \n", - "1 2020 Summer Olympics\\nHost city selection\\n\\nT... \n", - "2 2020 Summer Olympics\\nImpact of the COVID-19 p... \n", - "3 2020 Summer Olympics\\nQualifying event cancell... \n", - "4 2020 Summer Olympics\\nEffect on doping tests\\n... \n", - "\n", - " questions \\\n", - "0 1. What is the 2020 Summer Olympics?\\n2. When ... \n", - "1 1. \\n2. \\n3. \\n4. \n", - "2 1. What was the COVID-19 pandemic?\\n2. How did... \n", - "3 1. What was the original location of the Asia ... \n", - "4 1. What was the COVID-19 pandemic?\\n2. What di... \n", - "\n", - " answers \n", - "0 1. The 2020 Summer Olympics is an internationa... \n", - "1 1. What is the International Olympic Committee... \n", - "2 1. The COVID-19 pandemic was a pandemic that o... \n", - "3 1. The original location of the Asia & Oceania... \n", - "4 1. The COVID-19 pandemic was a pandemic that o... " - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import openai\n", - "import pandas as pd\n", - "df = pd.read_csv('olympics-data/olympics_qa.csv')\n", - "olympics_search_fileid = \"file-c3shd8wqF3vSCKaukW4Jr1TT\"\n", - "df.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Split the sections into a training and testing set" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(3014, 754)" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from sklearn.model_selection import train_test_split\n", - "train_df, test_df = train_test_split(df, test_size=0.2, random_state=42)\n", - "len(train_df), len(test_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "we check that he separator we intend to use isn't present within the contexts" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df.context.str.contains('->').sum()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.1 Create the fine-tuning datasets for Q&A and discriminator models\n", - "The fine-tuning dataset is created in the following way. For every corresponding question, answer and context pair we create:\n", - "- Positive example: correct question, answer, context pair\n", - "- Negative examples:\n", - " - random negative example, where the random context is paired with the question \n", - " - two hard negative examples\n", - " - one originating from the same wikipedia article\n", - " - another, which is most similar to the correct context\n", - "\n", - "This process is noisy, as sometimes the question might be answerable given a different context, but on average we hope this won't affect the peformance too much.\n", - "\n", - "We apply the same process of dataset creation for both the discriminator, and the Q&A answering model. We apply the process separately for the training and testing set, to ensure that the examples from the traing set don't feature within the test set." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "import random\n", - "\n", - "def get_random_similar_contexts(question, context, file_id=olympics_search_fileid, search_model='ada', max_rerank=10):\n", - " \"\"\"\n", - " Find similar contexts to the given context using the search file\n", - " \"\"\"\n", - " try:\n", - " results = openai.Engine(search_model).search(\n", - " search_model=search_model, \n", - " query=question, \n", - " max_rerank=max_rerank,\n", - " file=file_id\n", - " )\n", - " candidates = []\n", - " for result in results['data'][:3]:\n", - " if result['text'] == context:\n", - " continue\n", - " candidates.append(result['text'])\n", - " random_candidate = random.choice(candidates)\n", - " return random_candidate\n", - " except Exception as e:\n", - " print(e)\n", - " return \"\"\n", - "\n", - "def create_fine_tuning_dataset(df, discriminator=False, n_negative=1, add_related=False):\n", - " \"\"\"\n", - " Create a dataset for fine tuning the OpenAI model; either for a discriminator model, \n", - " or a model specializing in Q&A, where it says if no relevant context is found.\n", - "\n", - " Parameters\n", - " ----------\n", - " df: pd.DataFrame\n", - " The dataframe containing the question, answer and context pairs\n", - " discriminator: bool\n", - " Whether to create a dataset for the discriminator\n", - " n_negative: int\n", - " The number of random negative samples to add (using a random context)\n", - " add_related: bool\n", - " Whether to add the related contexts to the correct context. These are hard negative examples\n", - "\n", - " Returns\n", - " -------\n", - " pd.DataFrame\n", - " The dataframe containing the prompts and completions, ready for fine-tuning\n", - " \"\"\"\n", - " rows = []\n", - " for i, row in df.iterrows():\n", - " for q, a in zip((\"1.\" + row.questions).split('\\n'), (\"1.\" + row.answers).split('\\n')):\n", - " if len(q) >10 and len(a) >10:\n", - " if discriminator:\n", - " rows.append({\"prompt\":f\"{row.context}\\nQuestion: {q[2:].strip()}\\n Related:\", \"completion\":f\" yes\"})\n", - " else:\n", - " rows.append({\"prompt\":f\"{row.context}\\nQuestion: {q[2:].strip()}\\nAnswer:\", \"completion\":f\" {a[2:].strip()}\"})\n", - "\n", - " for i, row in df.iterrows():\n", - " for q in (\"1.\" + row.questions).split('\\n'):\n", - " if len(q) >10:\n", - " for j in range(n_negative + (2 if add_related else 0)):\n", - " random_context = \"\"\n", - " if j == 0 and add_related:\n", - " # add the related contexts based on originating from the same wikipedia page\n", - " subset = df[(df.title == row.title) & (df.context != row.context)]\n", - " \n", - " if len(subset) < 1:\n", - " continue\n", - " random_context = subset.sample(1).iloc[0].context\n", - " if j == 1 and add_related:\n", - " # add the related contexts based on the most similar contexts according to the search\n", - " random_context = get_random_similar_contexts(q[2:].strip(), row.context, search_model='ada', max_rerank=10)\n", - " else:\n", - " while True:\n", - " # add random context, which isn't the correct context\n", - " random_context = df.sample(1).iloc[0].context\n", - " if random_context != row.context:\n", - " break\n", - " if discriminator:\n", - " rows.append({\"prompt\":f\"{random_context}\\nQuestion: {q[2:].strip()}\\n Related:\", \"completion\":f\" no\"})\n", - " else:\n", - " rows.append({\"prompt\":f\"{random_context}\\nQuestion: {q[2:].strip()}\\nAnswer:\", \"completion\":f\" No appropriate context found to answer the question.\"})\n", - "\n", - " return pd.DataFrame(rows) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We apply the same process of dataset creation for both the discriminator, and the Q&A answering model. We apply the process separately for the training and testing set, to ensure that the examples from the traing set don't feature within the test set." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "for name, is_disc in [('discriminator', True), ('qa', False)]:\n", - " for train_test, dt in [('train', train_df), ('test', test_df)]:\n", - " ft = create_fine_tuning_dataset(dt, discriminator=is_disc, n_negative=1, add_related=True)\n", - " ft.to_json(f'{name}_{train_test}.jsonl', orient='records', lines=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We formatted the data according to the recommendations from the fine-tuning tool, which is available using\n", - "> openai tools fine_tunes.prepare_data -f qa_train.jsonl\n", - "\n", - "We highly recommend that you use this tool, which suggests improvements in your data formatting for fine-tuning.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.2 Submit the datasets for fine-tuning" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "!openai api fine_tunes.create -t \"olympics-data/discriminator_train.jsonl\" -v \"olympics-data/discriminator_test.jsonl\" --batch_size 16 --compute_classification_metrics --classification_positive_class \" yes\" --model ada" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "!openai api fine_tunes.create -t \"olympics-data/qa_train.jsonl\" -v \"olympics-data/qa_test.jsonl\" --batch_size 16" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.3 Using the fine-tuned models\n", - "\n", - "We will now use the fine-tuned discriminator and the fine-tuned Q&A model. By requesting logprobs, we can see how certain the discriminator is in a `yes` vs `no` answer." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[ JSON: {\n", - " \" no\": -10.819577,\n", - " \" yes\": -2.045765e-05\n", - " }]" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ft_discriminator = \"curie:ft-openai-internal-2021-08-23-23-58-57\"\n", - "ft_qa = \"curie:ft-openai-internal-2021-08-23-17-54-10\"\n", - "\n", - "def apply_ft_discriminator(context, question, discriminator_model):\n", - " \"\"\"\n", - " Apply the fine tuned discriminator to a question, to assess whether it can be answered from the context.\n", - " \"\"\"\n", - " prompt = f\"{context}\\nQuestion: {question}\\n Related:\"\n", - " result = openai.Completion.create(model=discriminator_model, prompt=prompt, max_tokens=1, temperature=0, top_p=1, n=1, logprobs=2)\n", - " return result['choices'][0]['logprobs']['top_logprobs']\n", - "\n", - "apply_ft_discriminator('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.', \n", - " 'What was the first human-made object in space?', ft_discriminator)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the model can generalize well to different contexts and questions. " - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def apply_ft_qa_answer(context, question, answering_model):\n", - " \"\"\"\n", - " Apply the fine tuned discriminator to a question\n", - " \"\"\"\n", - " prompt = f\"{context}\\nQuestion: {question}\\nAnswer:\"\n", - " result = openai.Completion.create(model=answering_model, prompt=prompt, max_tokens=30, temperature=0, top_p=1, n=1, stop=['.','\\n'])\n", - " return result['choices'][0]['text']\n", - "\n", - "apply_ft_qa_answer('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.', \n", - " 'What was the first human-made object in space?', ft_qa)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the model can answer the question, when the context is appropriate." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' The Soviet Union was the first country to successfully launch a satellite into space'" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "apply_ft_qa_answer('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.',\n", - " 'What is impressive about the Soviet Union?', ft_qa)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' No appropriate context found to answer the question'" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "apply_ft_qa_answer('The first human-made object in space was the Soviet Union satellite Sputnik 1 on 4 October 1957.',\n", - " 'How many cars were produced in the Soviet Union in 1970?', ft_qa)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the model knows when to answer the question, and when to say that insufficient context is present to answer the question." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also combine a discriminator and a base model, or a fine-tuned Q&A model. Discriminator can essentially serve as a decision whether the question can be answered given the context or not." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' Weather could cause a sport event to have no crowd'" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def answer_question_conditionally(answering_model, discriminator_model, context, question, discriminator_logprob_yes_modifier=0):\n", - " logprobs = apply_ft_discriminator(context, question, discriminator_model)\n", - " yes_logprob = logprobs[' yes'] if ' yes' in logprobs else -100\n", - " no_logprob = logprobs[' no'] if ' no' in logprobs else -100\n", - " if yes_logprob + discriminator_logprob_yes_modifier < no_logprob:\n", - " return \" No appropriate context found to answer the question based on the discriminator.\"\n", - " return apply_ft_qa_answer(context, question, answering_model)\n", - "answer_question_conditionally(ft_qa, ft_discriminator, \n", - " \"Crowdless games are a rare although not unheard-of occurrence in sports. \\\n", - " When they do occur, it is usually the result of events beyond the control \\\n", - " of the teams or fans, such as weather-related concerns, public health concerns, \\\n", - " or wider civil disturbances unrelated to the game. For instance, \\\n", - " the COVID-19 pandemic caused many sports leagues around the world \\\n", - " to be played behind closed doors.\",\n", - " \"Could weather cause a sport event to have no crowd?\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The above function illustrates how to potentially combine a discriminator and a fine-tuned Q&A model. This gives a more fine-grained control over how certain we want the model to be before it answers the question.\n", - "\n", - "We'll now take a look on how answers endpoint works - combining search to retrieve the relevant context from a knowledge base, and then using the fine-tuned Q&A model to answer the question." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3.4 Answering the question based on a knowledge base\n", - "Finally we can use a logic similar to the [/answers](https://beta.openai.com/docs/api-reference/answers) endpoint, where we first search for the relevant context, and then ask a Q&A model to answer the question given that context. If you'd like to see the implementation details, check out the [`answers_with_ft.py`](answers_with_ft.py) file." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\" Canada won the Women's football tournament at the 2020 Olympic games\"" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from answers_with_ft import answer_question\n", - "answer_question(olympics_search_fileid, ft_qa, \"Which country won the Women's football tournament at the 2020 Olympic games?\")" + "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/fine-tuned_qa/](https://github.com/openai/openai-cookbook/tree/main/examples/fine-tuned_qa)." ] } ], "metadata": { - "interpreter": { - "hash": "be4b5d5b73a21c599de40d6deb1129796d12dc1cc33a738f7bac13269cfcafe8" - }, "kernelspec": { - "display_name": "Python 3.7.3 64-bit ('base': conda)", + "display_name": "Python 3.9.9 ('openai')", + "language": "python", "name": "python3" }, "language_info": { @@ -628,9 +24,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.9" }, - "orig_nbformat": 4 + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" + } + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/semanticsearch/README.md b/examples/semanticsearch/README.md deleted file mode 100644 index b9a2d880d3..0000000000 --- a/examples/semanticsearch/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# semanticsearch - -A client-side implementation of our semantic search endpoint (https://beta.openai.com/docs/api-reference/search). - -Our endpoint has a special fast implementation of this logic which -makes it very fast for calls involving many documents, so we recommend -using our implementation rather than this one for latency-sensitive -workloads. - -We encourage you to try different variants of this client-side logic --- we don't think our setup is likely optimal at all! - -## Sample usage - -The following usage will run a client-side semantic search. This -formats each document into a prompt asking the API for the document's -relevance, and then post-processes the logprobs to derive relevance -scores: - -``` -$ ./semanticsearch.py -q 'positive emotion' -d happy -d sad -[client-side semantic search] {'object': 'list', 'data': [{'object': 'search_result', 'document': 0, 'score': 204.448}, {'object': 'search_result', 'document': 1, 'score': 108.208}], 'model': 'ada:2020-05-03'} -``` - -We run the exact same logic server-side: - -``` -$ ./semanticsearch.py -q 'positive emotion' -d happy -d sad -s -[server-side semantic search] {'object': 'list', 'data': [{'object': 'search_result', 'document': 0, 'score': 204.448}, {'object': 'search_result', 'document': 1, 'score': 108.208}], 'model': 'ada:2020-05-03'} -``` diff --git a/examples/semanticsearch/semanticsearch.py b/examples/semanticsearch/semanticsearch.py deleted file mode 100755 index bceb4c55d2..0000000000 --- a/examples/semanticsearch/semanticsearch.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -import argparse -import logging -import sys -from typing import List - -import openai - -logger = logging.getLogger() -formatter = logging.Formatter("[%(asctime)s] [%(process)d] %(message)s") -handler = logging.StreamHandler(sys.stderr) -handler.setFormatter(formatter) -logger.addHandler(handler) - -DEFAULT_COND_LOGP_TEMPLATE = ( - "<|endoftext|>{document}\n\n---\n\nThe above passage is related to: {query}" -) -SCORE_MULTIPLIER = 100.0 - - -class SearchScorer: - def __init__( - self, *, document, query, cond_logp_template=DEFAULT_COND_LOGP_TEMPLATE - ): - self.document = document - self.query = query - self.cond_logp_template = cond_logp_template - self.context = self.cond_logp_template.format( - document=self.document, query=self.query - ) - - def get_context(self): - return self.context - - def get_score(self, choice) -> float: - assert choice.text == self.context - logprobs: List[float] = choice.logprobs.token_logprobs - text = choice.logprobs.tokens - text_len = sum(len(token) for token in text) - if text_len != len(self.context): - raise RuntimeError( - f"text_len={text_len}, len(self.context)={len(self.context)}" - ) - total_len = 0 - last_used = len(text) - while total_len < len(self.query): - assert last_used > 0 - total_len += len(text[last_used - 1]) - last_used -= 1 - max_len = len(self.context) - self.cond_logp_template.index("{document}") - assert total_len + len(self.document) <= max_len - logits: List[float] = logprobs[last_used:] - return sum(logits) / len(logits) * SCORE_MULTIPLIER - - -def semantic_search(engine, query, documents): - # add empty document as baseline - scorers = [ - SearchScorer(document=document, query=query) for document in [""] + documents - ] - completion = openai.Completion.create( - engine=engine, - prompt=[scorer.get_context() for scorer in scorers], - max_tokens=0, - logprobs=0, - echo=True, - ) - # put the documents back in order so we can easily normalize by the empty document 0 - data = sorted(completion.choices, key=lambda choice: choice.index) - assert len(scorers) == len( - data - ), f"len(scorers)={len(scorers)} len(data)={len(data)}" - scores = [scorer.get_score(choice) for scorer, choice in zip(scorers, data)] - # subtract score for empty document - scores = [score - scores[0] for score in scores][1:] - data = { - "object": "list", - "data": [ - { - "object": "search_result", - "document": document_idx, - "score": round(score, 3), - } - for document_idx, score in enumerate(scores) - ], - "model": completion.model, - } - return data - - -def main(): - parser = argparse.ArgumentParser(description=None) - parser.add_argument( - "-v", - "--verbose", - action="http://github.com/count", - dest="verbosity", - default=0, - help="Set verbosity.", - ) - parser.add_argument("-e", "--engine", default="ada") - parser.add_argument("-q", "--query", required=True) - parser.add_argument("-d", "--document", action="http://github.com/append", required=True) - parser.add_argument("-s", "--server-side", action="http://github.com/store_true") - args = parser.parse_args() - - if args.verbosity == 1: - logger.setLevel(logging.INFO) - elif args.verbosity >= 2: - logger.setLevel(logging.DEBUG) - - if args.server_side: - resp = openai.Engine(id=args.engine).search( - query=args.query, documents=args.document - ) - resp = resp.to_dict_recursive() - print(f"[server-side semantic search] {resp}") - else: - resp = semantic_search(args.engine, query=args.query, documents=args.document) - print(f"[client-side semantic search] {resp}") - - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/openai/__init__.py b/openai/__init__.py index 554af337ab..091e4830dd 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -32,8 +32,9 @@ organization = os.environ.get("OPENAI_ORGANIZATION") api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") -api_version = "2022-03-01-preview" if api_type in ( - "azure", "azure_ad", "azuread") else None +api_version = ( + "2022-03-01-preview" if api_type in ("azure", "azure_ad", "azuread") else None +) verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None app_info = None diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index c1091554ef..dd13fcbf0e 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -62,7 +62,7 @@ def instance_url(http://github.com/self,%20operation=None): self.azure_api_prefix, base, extn, - api_version + api_version, ) return "/%s/%s/%s/%s?api-version=%s" % ( @@ -70,7 +70,7 @@ def instance_url(http://github.com/self,%20operation=None): self.azure_deployments_prefix, extn, operation, - api_version + api_version, ) elif self.typed_api_type == ApiType.OPEN_AI: @@ -78,8 +78,7 @@ def instance_url(http://github.com/self,%20operation=None): return "%s/%s" % (base, extn) else: - raise error.InvalidAPIType( - "Unsupported API type %s" % self.api_type) + raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) # The `method_` and `url_` arguments are suffixed with an underscore to # avoid conflicting with actual request parameters in `params`. @@ -101,7 +100,7 @@ def _static_request( api_version=api_version, organization=organization, api_base=api_base, - api_type=api_type + api_type=api_type, ) response, _, api_key = requestor.request( method_, url_, params, request_id=request_id @@ -111,8 +110,13 @@ def _static_request( ) @classmethod - def _get_api_type_and_version(cls, api_type: Optional[str] = None, api_version: Optional[str] = None): - typed_api_type = ApiType.from_str( - api_type) if api_type else ApiType.from_str(openai.api_type) + def _get_api_type_and_version( + cls, api_type: Optional[str] = None, api_version: Optional[str] = None + ): + typed_api_type = ( + ApiType.from_str(api_type) + if api_type + else ApiType.from_str(openai.api_type) + ) typed_api_version = api_version or openai.api_version return (typed_api_type, typed_api_version) diff --git a/openai/api_resources/abstract/createable_api_resource.py b/openai/api_resources/abstract/createable_api_resource.py index 57889b24e9..39d3e4f504 100644 --- a/openai/api_resources/abstract/createable_api_resource.py +++ b/openai/api_resources/abstract/createable_api_resource.py @@ -25,16 +25,16 @@ def create( organization=organization, ) typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version) + api_type, api_version + ) if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, - base, api_version) + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) elif typed_api_type == ApiType.OPEN_AI: url = cls.class_url() else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType("Unsupported API type %s" % api_type) response, _, api_key = requestor.request( "post", url, params, request_id=request_id diff --git a/openai/api_resources/abstract/deletable_api_resource.py b/openai/api_resources/abstract/deletable_api_resource.py index f1235c4a4f..220375ca2f 100644 --- a/openai/api_resources/abstract/deletable_api_resource.py +++ b/openai/api_resources/abstract/deletable_api_resource.py @@ -9,20 +9,26 @@ class DeletableAPIResource(APIResource): @classmethod def delete(cls, sid, api_type=None, api_version=None, **params): if isinstance(cls, APIResource): - raise ValueError( - ".delete may only be called as a class method now.") + raise ValueError(".delete may only be called as a class method now.") base = cls.class_url() extn = quote_plus(sid) typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version) + api_type, api_version + ) if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): url = "/%s%s/%s?api-version=%s" % ( - cls.azure_api_prefix, base, extn, api_version) + cls.azure_api_prefix, + base, + extn, + api_version, + ) elif typed_api_type == ApiType.OPEN_AI: url = "%s/%s" % (base, extn) else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType("Unsupported API type %s" % api_type) - return cls._static_request("delete", url, api_type=api_type, api_version=api_version, **params) + return cls._static_request( + "delete", url, api_type=api_type, api_version=api_version, **params + ) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index b480060255..9d4eda1a45 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -29,7 +29,8 @@ def class_url( # with forward slashes (/), so replace the former with the latter. base = cls.OBJECT_NAME.replace(".", "/") # type: ignore typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version) + api_type, api_version + ) if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): if not api_version: @@ -46,7 +47,7 @@ def class_url( cls.azure_deployments_prefix, extn, base, - api_version + api_version, ) elif typed_api_type == ApiType.OPEN_AI: @@ -81,12 +82,16 @@ def create( if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): if deployment_id is None and engine is None: raise error.InvalidRequestError( - "Must provide an 'engine' or 'deployment_id' parameter to create a %s" % cls, "engine" + "Must provide an 'engine' or 'deployment_id' parameter to create a %s" + % cls, + "engine", ) else: if model is None and engine is None: raise error.InvalidRequestError( - "Must provide an 'engine' or 'model' parameter to create a %s" % cls, "engine" + "Must provide an 'engine' or 'model' parameter to create a %s" + % cls, + "engine", ) if timeout is None: @@ -155,7 +160,7 @@ def instance_url(http://github.com/self): ) extn = quote_plus(id) - params_connector = '?' + params_connector = "?" if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): api_version = self.api_version or openai.api_version @@ -170,17 +175,16 @@ def instance_url(http://github.com/self): self.engine, base, extn, - api_version + api_version, ) - params_connector = '&' + params_connector = "&" elif self.typed_api_type == ApiType.OPEN_AI: base = self.class_url(http://github.com/self.engine,%20self.api_type,%20self.api_version) url = "%s/%s" % (base, extn) else: - raise error.InvalidAPIType( - "Unsupported API type %s" % self.api_type) + raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) timeout = self.get("timeout") if timeout is not None: diff --git a/openai/api_resources/abstract/listable_api_resource.py b/openai/api_resources/abstract/listable_api_resource.py index 18e49b887b..adbf4e8df9 100644 --- a/openai/api_resources/abstract/listable_api_resource.py +++ b/openai/api_resources/abstract/listable_api_resource.py @@ -28,16 +28,16 @@ def list( ) typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version) + api_type, api_version + ) if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, - base, api_version) + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) elif typed_api_type == ApiType.OPEN_AI: url = cls.class_url() else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType("Unsupported API type %s" % api_type) response, _, api_key = requestor.request( "get", url, params, request_id=request_id diff --git a/openai/api_resources/deployment.py b/openai/api_resources/deployment.py index dbc1df765e..5850e0c9fb 100644 --- a/openai/api_resources/deployment.py +++ b/openai/api_resources/deployment.py @@ -1,5 +1,9 @@ from openai import util -from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource, CreateableAPIResource +from openai.api_resources.abstract import ( + DeletableAPIResource, + ListableAPIResource, + CreateableAPIResource, +) from openai.error import InvalidRequestError, APIError @@ -12,10 +16,12 @@ def create(cls, *args, **kwargs): Creates a new deployment for the provided prompt and parameters. """ typed_api_type, _ = cls._get_api_type_and_version( - kwargs.get("api_type", None), None) + kwargs.get("api_type", None), None + ) if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): raise APIError( - "Deployment operations are only available for the Azure API type.") + "Deployment operations are only available for the Azure API type." + ) if kwargs.get("model", None) is None: raise InvalidRequestError( @@ -30,8 +36,10 @@ def create(cls, *args, **kwargs): param="scale_settings", ) - if "scale_type" not in scale_settings or \ - (scale_settings["scale_type"].lower() == 'manual' and "capacity" not in scale_settings): + if "scale_type" not in scale_settings or ( + scale_settings["scale_type"].lower() == "manual" + and "capacity" not in scale_settings + ): raise InvalidRequestError( "The 'scale_settings' parameter contains invalid or incomplete values.", param="scale_settings", @@ -42,29 +50,35 @@ def create(cls, *args, **kwargs): @classmethod def list(cls, *args, **kwargs): typed_api_type, _ = cls._get_api_type_and_version( - kwargs.get("api_type", None), None) + kwargs.get("api_type", None), None + ) if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): raise APIError( - "Deployment operations are only available for the Azure API type.") + "Deployment operations are only available for the Azure API type." + ) return super().list(*args, **kwargs) @classmethod def delete(cls, *args, **kwargs): typed_api_type, _ = cls._get_api_type_and_version( - kwargs.get("api_type", None), None) + kwargs.get("api_type", None), None + ) if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): raise APIError( - "Deployment operations are only available for the Azure API type.") + "Deployment operations are only available for the Azure API type." + ) return super().delete(*args, **kwargs) @classmethod def retrieve(cls, *args, **kwargs): typed_api_type, _ = cls._get_api_type_and_version( - kwargs.get("api_type", None), None) + kwargs.get("api_type", None), None + ) if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): raise APIError( - "Deployment operations are only available for the Azure API type.") + "Deployment operations are only available for the Azure API type." + ) return super().retrieve(*args, **kwargs) diff --git a/openai/api_resources/edit.py b/openai/api_resources/edit.py index 61f1c36aa5..fe66b6f0f4 100644 --- a/openai/api_resources/edit.py +++ b/openai/api_resources/edit.py @@ -20,7 +20,8 @@ def create(cls, *args, **kwargs): typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): raise error.InvalidAPIType( - "This operation is not supported by the Azure OpenAI API yet.") + "This operation is not supported by the Azure OpenAI API yet." + ) while True: try: diff --git a/openai/api_resources/error_object.py b/openai/api_resources/error_object.py index e329fa25e4..555dc35237 100644 --- a/openai/api_resources/error_object.py +++ b/openai/api_resources/error_object.py @@ -10,6 +10,7 @@ def refresh_from( values, api_key=None, api_version=None, + api_type=None, organization=None, response_ms: Optional[int] = None, ): @@ -21,6 +22,7 @@ def refresh_from( values=values, api_key=api_key, api_version=api_version, + api_type=api_type, organization=organization, response_ms=response_ms, ) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 3bf2afbe65..aba7117fea 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -25,8 +25,7 @@ def create( user_provided_filename=None, ): if purpose != "search" and model is not None: - raise ValueError( - "'model' is only meaningful if 'purpose' is 'search'") + raise ValueError("'model' is only meaningful if 'purpose' is 'search'") requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, @@ -35,16 +34,16 @@ def create( organization=organization, ) typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version) + api_type, api_version + ) if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, - base, api_version) + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) elif typed_api_type == ApiType.OPEN_AI: url = cls.class_url() else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType("Unsupported API type %s" % api_type) # Set the filename on 'purpose' and 'model' to None so they are # interpreted as form data. @@ -53,9 +52,10 @@ def create( files.append(("model", (None, model))) if user_provided_filename is not None: files.append( - ("file", (user_provided_filename, file, 'application/octet-stream'))) + ("file", (user_provided_filename, file, "application/octet-stream")) + ) else: - files.append(("file", ("file", file, 'application/octet-stream'))) + files.append(("file", ("file", file, "application/octet-stream"))) response, _, api_key = requestor.request("post", url, files=files) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -69,7 +69,7 @@ def download( api_base=None, api_type=None, api_version=None, - organization=None + organization=None, ): requestor = api_requestor.APIRequestor( api_key, @@ -79,16 +79,21 @@ def download( organization=organization, ) typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version) + api_type, api_version + ) if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() url = "/%s%s/%s/content?api-version=%s" % ( - cls.azure_api_prefix, base, id, api_version) + cls.azure_api_prefix, + base, + id, + api_version, + ) elif typed_api_type == ApiType.OPEN_AI: url = f"{cls.class_url()}/{id}/content" else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType("Unsupported API type %s" % api_type) result = requestor.request_raw("get", url) if not 200 <= result.status_code < 300: diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index bfdecbf8cd..1b5d92d861 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -23,20 +23,25 @@ def cancel( api_type=None, request_id=None, api_version=None, - **params + **params, ): base = cls.class_url() extn = quote_plus(id) typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version) + api_type, api_version + ) if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): url = "/%s%s/%s/cancel?api-version=%s" % ( - cls.azure_api_prefix, base, extn, api_version) + cls.azure_api_prefix, + base, + extn, + api_version, + ) elif typed_api_type == ApiType.OPEN_AI: url = "%s/%s/cancel" % (base, extn) else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType("Unsupported API type %s" % api_type) instance = cls(id, api_key, **params) return instance.request("post", url, request_id=request_id) @@ -65,15 +70,20 @@ def stream_events( ) typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version) + api_type, api_version + ) if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): url = "/%s%s/%s/events?stream=true&api-version=%s" % ( - cls.azure_api_prefix, base, extn, api_version) + cls.azure_api_prefix, + base, + extn, + api_version, + ) elif typed_api_type == ApiType.OPEN_AI: url = "%s/%s/events?stream=true" % (base, extn) else: - raise error.InvalidAPIType('Unsupported API type %s' % api_type) + raise error.InvalidAPIType("Unsupported API type %s" % api_type) response, _, api_key = requestor.request( "get", url, params, stream=True, request_id=request_id diff --git a/openai/cli.py b/openai/cli.py index fd9c8469ad..09ede89131 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -320,7 +320,9 @@ def _maybe_upload_file( sys.stdout.write( "Found potentially duplicated files with name '{name}', purpose 'fine-tune' and size {size} bytes\n".format( name=os.path.basename(matching_files[0]["filename"]), - size=matching_files[0]["bytes"] if "bytes" in matching_files[0] else matching_files[0]["size"], + size=matching_files[0]["bytes"] + if "bytes" in matching_files[0] + else matching_files[0]["size"], ) ) sys.stdout.write("\n".join(file_ids)) diff --git a/openai/tests/test_url_composition.py b/openai/tests/test_url_composition.py index 5d3da919bf..5034354a05 100644 --- a/openai/tests/test_url_composition.py +++ b/openai/tests/test_url_composition.py @@ -102,8 +102,7 @@ def test_completions_url_composition_instance_url_open_ai() -> None: @pytest.mark.url def test_completions_url_composition_instance_url_invalid() -> None: - completion = Completion( - id="test_id", engine="test_engine", api_type="invalid") + completion = Completion(id="test_id", engine="test_engine", api_type="invalid") with pytest.raises(Exception): url = completion.instance_url() @@ -126,8 +125,7 @@ def test_completions_url_composition_instance_url_timeout_azure() -> None: @pytest.mark.url def test_completions_url_composition_instance_url_timeout_openai() -> None: - completion = Completion( - id="test_id", engine="test_engine", api_type="open_ai") + completion = Completion(id="test_id", engine="test_engine", api_type="open_ai") completion["timeout"] = 12 url = completion.instance_url() assert url == "/engines/test_engine/completions/test_id?timeout=12" @@ -135,8 +133,7 @@ def test_completions_url_composition_instance_url_timeout_openai() -> None: @pytest.mark.url def test_engine_search_url_composition_azure() -> None: - engine = Engine(id="test_id", api_type="azure", - api_version="2021-11-01-preview") + engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") assert engine.api_type == "azure" assert engine.typed_api_type == ApiType.AZURE url = engine.instance_url("http://github.com/test_operation") @@ -148,8 +145,7 @@ def test_engine_search_url_composition_azure() -> None: @pytest.mark.url def test_engine_search_url_composition_azure_ad() -> None: - engine = Engine(id="test_id", api_type="azure_ad", - api_version="2021-11-01-preview") + engine = Engine(id="test_id", api_type="azure_ad", api_version="2021-11-01-preview") assert engine.api_type == "azure_ad" assert engine.typed_api_type == ApiType.AZURE_AD url = engine.instance_url("http://github.com/test_operation") @@ -170,11 +166,13 @@ def test_engine_search_url_composition_azure_no_version() -> None: @pytest.mark.url def test_engine_search_url_composition_azure_no_operation() -> None: - engine = Engine(id="test_id", api_type="azure", - api_version="2021-11-01-preview") + engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") assert engine.api_type == "azure" assert engine.typed_api_type == ApiType.AZURE - assert engine.instance_url() == "/openai/engines/test_id?api-version=2021-11-01-preview" + assert ( + engine.instance_url() + == "/openai/engines/test_id?api-version=2021-11-01-preview" + ) @pytest.mark.url diff --git a/openai/upload_progress.py b/openai/upload_progress.py index 1d0a1fe6a3..e4da62a4e0 100644 --- a/openai/upload_progress.py +++ b/openai/upload_progress.py @@ -49,4 +49,4 @@ def incr(progress): def MB(i): - return int(i // 1024 ** 2) + return int(i // 1024**2) diff --git a/openai/util.py b/openai/util.py index 9cc0ba233e..56dfc2e677 100644 --- a/openai/util.py +++ b/openai/util.py @@ -178,8 +178,7 @@ def default_api_key() -> str: with open(openai.api_key_path, "rt") as k: api_key = k.read().strip() if not api_key.startswith("sk-"): - raise ValueError( - f"Malformed API key in {openai.api_key_path}.") + raise ValueError(f"Malformed API key in {openai.api_key_path}.") return api_key elif openai.api_key is not None: return openai.api_key diff --git a/setup.py b/setup.py index d1110c6c19..707e8f299b 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,8 @@ "tqdm", # Needed for progress bars "pandas>=1.2.3", # Needed for CLI fine-tuning data preparation tool "pandas-stubs>=1.1.0.11", # Needed for type hints for mypy - "openpyxl>=3.0.7" # Needed for CLI fine-tuning data preparation tool xlsx format + "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format + "numpy>=1.22.0", # To address a vuln in <1.21.6 ], extras_require={ "dev": ["black~=21.6b0", "pytest==6.*"], From f3e3083c221ffa564784fa39296733b17b1c456e Mon Sep 17 00:00:00 2001 From: hallacy Date: Tue, 2 Aug 2022 15:48:48 -0700 Subject: [PATCH 041/914] v0.22.1 (#114) 0.22.0 was deployed incorrectly with extra breakpoint statements. 0.22.1 fixes this --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index a31140d7da..dae64cb9e3 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.22.0" +VERSION = "0.22.1" From 3edecbee24102299dd6e4a35af031780e9ad0f9a Mon Sep 17 00:00:00 2001 From: hallacy Date: Tue, 23 Aug 2022 20:06:43 -0700 Subject: [PATCH 042/914] Hallacy/v23 (#116) * overload output type depending on stream literal (#142) * Bump to v22 * [numpy] change version (#143) * [numpy] change version * update comments * no version for numpy * Fix timeouts (#137) * Fix timeouts * Rename to request_timeout and add to readme * Dev/hallacy/request timeout takes tuples (#144) * Add tuple typing for request_timeout * imports * [api_requestor] Log request_id with response (#145) * Only import wandb as needed (#146) Co-authored-by: Felipe Petroski Such Co-authored-by: Henrique Oliveira Pinto Co-authored-by: Rachel Lim --- README.md | 4 + openai/api_requestor.py | 76 ++++++++++++++++++- openai/api_resources/abstract/api_resource.py | 15 +++- .../abstract/engine_api_resource.py | 3 +- openai/cli.py | 3 +- openai/error.py | 4 + openai/openai_object.py | 4 +- openai/tests/test_endpoints.py | 24 ++++++ openai/tests/test_exceptions.py | 1 + openai/version.py | 2 +- setup.py | 3 +- 11 files changed, 126 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ab98ec4403..2a1bb52f31 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,10 @@ completion = openai.Completion.create(engine="ada", prompt="Hello world") print(completion.choices[0].text) ``` + +### Params +All endpoints have a `.create` method that support a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise a `openai.error.TimeoutError` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). + ### Microsoft Azure Endpoints In order to use the library with Microsoft Azure endpoints, you need to set the api_type, api_base and api_version in addition to the api_key. The api_type must be set to 'azure' and the others correspond to the properties of your endpoint. diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 954afc05e0..5970510434 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -3,10 +3,11 @@ import threading import warnings from json import JSONDecodeError -from typing import Dict, Iterator, Optional, Tuple, Union +from typing import Dict, Iterator, Optional, Tuple, Union, overload from urllib.parse import urlencode, urlsplit, urlunsplit import requests +from typing_extensions import Literal import openai from openai import error, util, version @@ -99,6 +100,63 @@ def format_app_info(cls, info): str += " (%s)" % (info["url"],) return str + @overload + def request( + self, + method, + url, + params, + headers, + files, + stream: Literal[True], + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[Iterator[OpenAIResponse], bool, str]: + pass + + @overload + def request( + self, + method, + url, + params=..., + headers=..., + files=..., + *, + stream: Literal[True], + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[Iterator[OpenAIResponse], bool, str]: + pass + + @overload + def request( + self, + method, + url, + params=..., + headers=..., + files=..., + stream: Literal[False] = ..., + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[OpenAIResponse, bool, str]: + pass + + @overload + def request( + self, + method, + url, + params=..., + headers=..., + files=..., + stream: bool = ..., + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool, str]: + pass + def request( self, method, @@ -106,8 +164,9 @@ def request( params=None, headers=None, files=None, - stream=False, + stream: bool = False, request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool, str]: result = self.request_raw( method.lower(), @@ -117,6 +176,7 @@ def request( files=files, stream=stream, request_id=request_id, + request_timeout=request_timeout, ) resp, got_stream = self._interpret_response(result, stream) return resp, got_stream, self.api_key @@ -179,7 +239,11 @@ def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False return error.APIError(message, rbody, rcode, resp, rheaders) else: return error.APIError( - error_data.get("message"), rbody, rcode, resp, rheaders + f"{error_data.get('message')} {rbody} {rcode} {resp} {rheaders}", + rbody, + rcode, + resp, + rheaders, ) def request_headers( @@ -256,6 +320,7 @@ def request_raw( files=None, stream: bool = False, request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, ) -> requests.Response: abs_url = "%s%s" % (self.api_base, url) headers = self._validate_headers(supplied_headers) @@ -295,8 +360,10 @@ def request_raw( data=data, files=files, stream=stream, - timeout=TIMEOUT_SECS, + timeout=request_timeout if request_timeout else TIMEOUT_SECS, ) + except requests.exceptions.Timeout as e: + raise error.Timeout("Request timed out") from e except requests.exceptions.RequestException as e: raise error.APIConnectionError("Error communicating with OpenAI") from e util.log_info( @@ -304,6 +371,7 @@ def request_raw( path=abs_url, response_code=result.status_code, processing_ms=result.headers.get("OpenAI-Processing-Ms"), + request_id=result.headers.get("X-Request-Id"), ) # Don't read the whole stream for debug logging unless necessary. if openai.log == "debug": diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index dd13fcbf0e..aa7cfe88e1 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -13,14 +13,21 @@ class APIResource(OpenAIObject): azure_deployments_prefix = "deployments" @classmethod - def retrieve(cls, id, api_key=None, request_id=None, **params): + def retrieve( + cls, id, api_key=None, request_id=None, request_timeout=None, **params + ): instance = cls(id, api_key, **params) - instance.refresh(request_id=request_id) + instance.refresh(request_id=request_id, request_timeout=request_timeout) return instance - def refresh(self, request_id=None): + def refresh(self, request_id=None, request_timeout=None): self.refresh_from( - self.request("get", self.instance_url(), request_id=request_id) + self.request( + "get", + self.instance_url(), + request_id=request_id, + request_timeout=request_timeout, + ) ) return self diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index 9d4eda1a45..152313c202 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -77,7 +77,7 @@ def create( timeout = params.pop("timeout", None) stream = params.get("stream", False) headers = params.pop("headers", None) - + request_timeout = params.pop("request_timeout", None) typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): if deployment_id is None and engine is None: @@ -119,6 +119,7 @@ def create( headers=headers, stream=stream, request_id=request_id, + request_timeout=request_timeout, ) if stream: diff --git a/openai/cli.py b/openai/cli.py index 09ede89131..dbde7e19cf 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -9,7 +9,6 @@ import requests import openai -import openai.wandb_logger from openai.upload_progress import BufferReader from openai.validators import ( apply_necessary_remediation, @@ -542,6 +541,8 @@ def prepare_data(cls, args): class WandbLogger: @classmethod def sync(cls, args): + import openai.wandb_logger + resp = openai.wandb_logger.WandbLogger.sync( id=args.id, n_fine_tunes=args.n_fine_tunes, diff --git a/openai/error.py b/openai/error.py index 47f9aab6bc..d22e71c902 100644 --- a/openai/error.py +++ b/openai/error.py @@ -76,6 +76,10 @@ class TryAgain(OpenAIError): pass +class Timeout(OpenAIError): + pass + + class APIConnectionError(OpenAIError): def __init__( self, diff --git a/openai/openai_object.py b/openai/openai_object.py index 58e458dfed..5bfa29e45f 100644 --- a/openai/openai_object.py +++ b/openai/openai_object.py @@ -1,6 +1,6 @@ import json from copy import deepcopy -from typing import Optional +from typing import Optional, Tuple, Union import openai from openai import api_requestor, util @@ -165,6 +165,7 @@ def request( stream=False, plain_old_data=False, request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, ): if params is None: params = self._retrieve_params @@ -182,6 +183,7 @@ def request( stream=stream, headers=headers, request_id=request_id, + request_timeout=request_timeout, ) if stream: diff --git a/openai/tests/test_endpoints.py b/openai/tests/test_endpoints.py index f590328eec..e5c466add1 100644 --- a/openai/tests/test_endpoints.py +++ b/openai/tests/test_endpoints.py @@ -1,7 +1,10 @@ import io import json +import pytest + import openai +from openai import error # FILE TESTS @@ -34,3 +37,24 @@ def test_completions_model(): result = openai.Completion.create(prompt="This was a test", n=5, model="ada") assert len(result.choices) == 5 assert result.model.startswith("ada") + + +def test_timeout_raises_error(): + # A query that should take awhile to return + with pytest.raises(error.Timeout): + openai.Completion.create( + prompt="test" * 1000, + n=10, + model="ada", + max_tokens=100, + request_timeout=0.01, + ) + + +def test_timeout_does_not_error(): + # A query that should be fast + openai.Completion.create( + prompt="test", + model="ada", + request_timeout=10, + ) diff --git a/openai/tests/test_exceptions.py b/openai/tests/test_exceptions.py index e97b4cb386..7760cdc5f6 100644 --- a/openai/tests/test_exceptions.py +++ b/openai/tests/test_exceptions.py @@ -21,6 +21,7 @@ openai.error.SignatureVerificationError("message", "sig_header?"), openai.error.APIConnectionError("message!", should_retry=True), openai.error.TryAgain(), + openai.error.Timeout(), openai.error.APIError( message="message", code=400, diff --git a/openai/version.py b/openai/version.py index dae64cb9e3..68c325024b 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.22.1" +VERSION = "0.23.0" diff --git a/setup.py b/setup.py index 707e8f299b..03bd6c3b6a 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,8 @@ "pandas>=1.2.3", # Needed for CLI fine-tuning data preparation tool "pandas-stubs>=1.1.0.11", # Needed for type hints for mypy "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format - "numpy>=1.22.0", # To address a vuln in <1.21.6 + "numpy", + "typing_extensions", # Needed for type hints for mypy ], extras_require={ "dev": ["black~=21.6b0", "pytest==6.*"], From e51ae9153eda7dc308a78e5f51d06ae50a535f85 Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 26 Aug 2022 10:31:23 -0700 Subject: [PATCH 043/914] Add an = to <= check in embedding_utils (#118) --- openai/embeddings_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index 2db5514c64..47a04e6582 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -27,7 +27,7 @@ def get_embedding(text: str, engine="text-similarity-davinci-001") -> List[float def get_embeddings( list_of_text: List[str], engine="text-similarity-babbage-001" ) -> List[List[float]]: - assert len(list_of_text) < 2048, "The batch size should not be larger than 2048." + assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048." # replace newlines, which can negatively affect performance. list_of_text = [text.replace("\n", " ") for text in list_of_text] From 09dc7ef3e17e3367fc5168a701f0e5dfd461f4d8 Mon Sep 17 00:00:00 2001 From: Zafer Cavdar Date: Tue, 20 Sep 2022 18:56:06 +0300 Subject: [PATCH 044/914] Add api_key parameter to Moderation.create (#123) * Add api_key parameter to Moderation.create * Reverted auto-formatted linting --- openai/api_resources/moderation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/api_resources/moderation.py b/openai/api_resources/moderation.py index 12a4e19d7e..52f997fb26 100644 --- a/openai/api_resources/moderation.py +++ b/openai/api_resources/moderation.py @@ -11,14 +11,14 @@ def get_url(http://github.com/self): return "/moderations" @classmethod - def create(cls, input: Union[str, List[str]], model: Optional[str] = None): + def create(cls, input: Union[str, List[str]], model: Optional[str] = None, api_key: Optional[str] = None): if model is not None and model not in cls.VALID_MODEL_NAMES: raise ValueError( f"The parameter model should be chosen from {cls.VALID_MODEL_NAMES} " f"and it is default to be None." ) - instance = cls() + instance = cls(api_key=api_key) params = {"input": input} if model is not None: params["model"] = model From 15b6354fc5f805c1f9361d99bb35b251fdfb33e9 Mon Sep 17 00:00:00 2001 From: Michelle Pokrass Date: Wed, 28 Sep 2022 11:22:21 -0700 Subject: [PATCH 045/914] Add missing dependencies (#126) * Add missing dependencies * bump patch version --- openai/version.py | 2 +- setup.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/openai/version.py b/openai/version.py index 68c325024b..cbc6a7bea5 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.23.0" +VERSION = "0.23.1" diff --git a/setup.py b/setup.py index 03bd6c3b6a..0b6956ef0e 100644 --- a/setup.py +++ b/setup.py @@ -24,9 +24,13 @@ ], extras_require={ "dev": ["black~=21.6b0", "pytest==6.*"], + "wandb": ["wandb"], "embeddings": [ - "scikit-learn>=1.0.2", # Needed for embedding utils, versions >= 1.1 require python 3.8 - "tenacity>=8.0.1" + "scikit-learn>=1.0.2", # Needed for embedding utils, versions >= 1.1 require python 3.8 + "tenacity>=8.0.1", + "matplotlib", + "sklearn", + "plotly", ], }, python_requires=">=3.7.1", From 34a12097548ae07dbfe1363bb683fe98646aa723 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 30 Sep 2022 22:47:16 -0200 Subject: [PATCH 046/914] Update README.md (#127) Minor changes to sentence structure to avoid repetitious language. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2a1bb52f31..d777bc392a 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ For more information on embeddings and the types of embeddings OpenAI offers, re ### Fine tuning -Fine tuning a model on training data can both improve the results (by giving the model more examples to learn from) and reduce the cost & latency of API calls (by reducing the need to include training examples in prompts). +Fine tuning a model on training data can both improve the results (by giving the model more examples to learn from) and reduce the cost/latency of API calls (chiefly through reducing the need to include training examples in prompts). Examples of fine tuning are shared in the following Jupyter notebooks: @@ -194,8 +194,8 @@ For more information on fine tuning, read the [fine-tuning guide](https://beta.o - Python 3.7.1+ -In general we want to support the versions of Python that our -customers are using, so if you run into issues with any version +In general, we want to support the versions of Python that our +customers are using. If you run into problems with any version issues, please let us know at support@openai.com. ## Credit From d1769c155a3671c2c0153badd810931465427cdb Mon Sep 17 00:00:00 2001 From: Serina Grill <42048900+serinamarie@users.noreply.github.com> Date: Sun, 16 Oct 2022 16:01:52 -0700 Subject: [PATCH 047/914] Fix KeyError occurring using fine_tunes.prepare_data (#125) * Initial commit * Add fix * Reinstate reset_index() * Add suggestions * Remove print stmt * punctuation * Add test for fine_tunes.prepare_data * Renamed file, added docstrings * Move comment placement --- openai/tests/test_long_examples_validator.py | 48 ++++++++++++++++++++ openai/validators.py | 17 +++++-- 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 openai/tests/test_long_examples_validator.py diff --git a/openai/tests/test_long_examples_validator.py b/openai/tests/test_long_examples_validator.py new file mode 100644 index 0000000000..7f3e4c8cf1 --- /dev/null +++ b/openai/tests/test_long_examples_validator.py @@ -0,0 +1,48 @@ +import json +import subprocess +from tempfile import NamedTemporaryFile + + +def test_long_examples_validator() -> None: + + """ + Ensures that long_examples_validator() handles previously applied recommendations, + namely dropped duplicates, without resulting in a KeyError. + """ + + # data + short_prompt = "a prompt " + long_prompt = short_prompt * 500 + + short_completion = "a completion " + long_completion = short_completion * 500 + + # the order of these matters + unprepared_training_data = [ + {"prompt": long_prompt, "completion": long_completion}, # 1 of 2 duplicates + {"prompt": short_prompt, "completion": short_completion}, + {"prompt": long_prompt, "completion": long_completion}, # 2 of 2 duplicates + + ] + + with NamedTemporaryFile(suffix="jsonl", mode="w") as training_data: + for prompt_completion_row in unprepared_training_data: + training_data.write(json.dumps(prompt_completion_row) + "\n") + training_data.flush() + + prepared_data_cmd_output = subprocess.run( + [f"openai tools fine_tunes.prepare_data -f {training_data.name}"], + stdout=subprocess.PIPE, + text=True, + input="y\ny\ny\ny\ny", # apply all recommendations, one at a time + stderr=subprocess.PIPE, + encoding="utf-8", + shell=True + ) + + # validate data was prepared successfully + assert prepared_data_cmd_output.stderr == "" + # validate get_long_indexes() applied during optional_fn() call in long_examples_validator() + assert "indices of the long examples has changed" in prepared_data_cmd_output.stdout + + return prepared_data_cmd_output.stdout \ No newline at end of file diff --git a/openai/validators.py b/openai/validators.py index 0d4d85d4f2..23ff525495 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -158,17 +158,24 @@ def long_examples_validator(df): ft_type = infer_task_type(df) if ft_type != "open-ended generation": - long_examples = df.apply( - lambda x: len(x.prompt) + len(x.completion) > 10000, axis=1 - ) - long_indexes = df.reset_index().index[long_examples].tolist() + def get_long_indexes(d): + long_examples = d.apply( + lambda x: len(x.prompt) + len(x.completion) > 10000, axis=1 + ) + return d.reset_index().index[long_examples].tolist() + + long_indexes = get_long_indexes(df) if len(long_indexes) > 0: immediate_msg = f"\n- There are {len(long_indexes)} examples that are very long. These are rows: {long_indexes}\nFor conditional generation, and for classification the examples shouldn't be longer than 2048 tokens." optional_msg = f"Remove {len(long_indexes)} long examples" def optional_fn(x): - return x.drop(long_indexes) + + long_indexes_to_drop = get_long_indexes(x) + if long_indexes != long_indexes_to_drop: + sys.stdout.write(f"The indices of the long examples has changed as a result of a previously applied recommendation.\nThe {len(long_indexes_to_drop)} long examples to be dropped are now at the following indices: {long_indexes_to_drop}\n") + return x.drop(long_indexes_to_drop) return Remediation( name="long_examples", From d59672b3b6c03b78eaa78f9ccbf92ea6a681dc50 Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 21 Oct 2022 10:29:27 -0700 Subject: [PATCH 048/914] Add Dalle Support (#147) (#131) * Add Dalle Support (#147) --- openai/__init__.py | 2 + openai/api_resources/__init__.py | 3 +- openai/api_resources/dalle.py | 90 ++++++++++++++++++++++++++++++ openai/cli.py | 94 ++++++++++++++++++++++++++++++++ openai/version.py | 2 +- 5 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 openai/api_resources/dalle.py diff --git a/openai/__init__.py b/openai/__init__.py index 091e4830dd..c7ceee3c8e 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -6,6 +6,7 @@ from typing import Optional from openai.api_resources import ( + DALLE, Answer, Classification, Completion, @@ -50,6 +51,7 @@ "Completion", "Customer", "Edit", + "DALLE", "Deployment", "Embedding", "Engine", diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index c01690945e..28bea88ad3 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -2,8 +2,9 @@ from openai.api_resources.classification import Classification # noqa: F401 from openai.api_resources.completion import Completion # noqa: F401 from openai.api_resources.customer import Customer # noqa: F401 -from openai.api_resources.edit import Edit # noqa: F401 +from openai.api_resources.dalle import DALLE # noqa: F401 from openai.api_resources.deployment import Deployment # noqa: F401 +from openai.api_resources.edit import Edit # noqa: F401 from openai.api_resources.embedding import Embedding # noqa: F401 from openai.api_resources.engine import Engine # noqa: F401 from openai.api_resources.error_object import ErrorObject # noqa: F401 diff --git a/openai/api_resources/dalle.py b/openai/api_resources/dalle.py new file mode 100644 index 0000000000..119860666b --- /dev/null +++ b/openai/api_resources/dalle.py @@ -0,0 +1,90 @@ +# WARNING: This interface is considered experimental and may changed in the future without warning. +from typing import Any, List + +import openai +from openai import api_requestor, util +from openai.api_resources.abstract import APIResource + + +class DALLE(APIResource): + OBJECT_NAME = "images" + + @classmethod + def _get_url(http://github.com/cls,%20action): + return cls.class_url() + f"/{action}" + + @classmethod + def generations( + cls, + **params, + ): + instance = cls() + return instance.request("post", cls._get_url("http://github.com/generations"), params) + + @classmethod + def variations( + cls, + image, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + _, api_version = cls._get_api_type_and_version(api_type, api_version) + + url = cls._get_url("http://github.com/variations") + + files: List[Any] = [] + for key, value in params.items(): + files.append((key, (None, value))) + files.append(("image", ("image", image, "application/octet-stream"))) + + response, _, api_key = requestor.request("post", url, files=files) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def edits( + cls, + image, + mask, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + _, api_version = cls._get_api_type_and_version(api_type, api_version) + + url = cls._get_url("http://github.com/edits") + + files: List[Any] = [] + for key, value in params.items(): + files.append((key, (None, value))) + files.append(("image", ("image", image, "application/octet-stream"))) + files.append(("mask", ("mask", mask, "application/octet-stream"))) + + response, _, api_key = requestor.request("post", url, files=files) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) diff --git a/openai/cli.py b/openai/cli.py index dbde7e19cf..3c77bfe103 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -229,6 +229,49 @@ def list(cls, args): print(file) +class DALLE: + @classmethod + def generations(cls, args): + resp = openai.DALLE.generations( + prompt=args.prompt, + model=args.model, + size=args.size, + num_images=args.num_images, + response_format=args.response_format, + ) + print(resp) + + @classmethod + def variations(cls, args): + with open(args.image, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") + resp = openai.DALLE.variations( + image=buffer_reader, + model=args.model, + size=args.size, + num_images=args.num_images, + response_format=args.response_format, + ) + print(resp) + + @classmethod + def edits(cls, args): + with open(args.image, "rb") as file_reader: + image_reader = BufferReader(file_reader.read(), desc="Upload progress") + with open(args.mask, "rb") as file_reader: + mask_reader = BufferReader(file_reader.read(), desc="Upload progress") + resp = openai.DALLE.edits( + image=image_reader, + mask=mask_reader, + prompt=args.prompt, + model=args.model, + size=args.size, + num_images=args.num_images, + response_format=args.response_format, + ) + print(resp) + + class Search: @classmethod def prepare_data(cls, args, purpose): @@ -983,6 +1026,57 @@ def help(args): sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") sub.set_defaults(func=FineTune.cancel) + # DALLE + sub = subparsers.add_parser("dalle.generations") + sub.add_argument("-m", "--model", type=str, default="image-alpha-001") + 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=DALLE.generations) + + sub = subparsers.add_parser("dalle.edits") + sub.add_argument("-m", "--model", type=str, default="image-alpha-001") + 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=True, + 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=DALLE.edits) + + sub = subparsers.add_parser("dalle.variations") + sub.add_argument("-m", "--model", type=str, default="image-alpha-001") + 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=DALLE.variations) + def wandb_register(parser): subparsers = parser.add_subparsers( diff --git a/openai/version.py b/openai/version.py index cbc6a7bea5..595cfe2965 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.23.1" +VERSION = "0.24.0" From 5f60acacf25dbb384b045ba777d3654623a5612b Mon Sep 17 00:00:00 2001 From: Michelle Pokrass Date: Wed, 2 Nov 2022 12:45:32 -0700 Subject: [PATCH 049/914] =?UTF-8?q?Rename=20DALL=C2=B7E=20to=20Image=20(#1?= =?UTF-8?q?34)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename DALL·E to Image * bump version --- openai/__init__.py | 4 +-- openai/api_resources/__init__.py | 2 +- openai/api_resources/{dalle.py => image.py} | 8 ++--- openai/cli.py | 40 +++++++++------------ openai/version.py | 2 +- 5 files changed, 25 insertions(+), 31 deletions(-) rename openai/api_resources/{dalle.py => image.py} (96%) diff --git a/openai/__init__.py b/openai/__init__.py index c7ceee3c8e..b65b86d003 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -6,7 +6,6 @@ from typing import Optional from openai.api_resources import ( - DALLE, Answer, Classification, Completion, @@ -18,6 +17,7 @@ ErrorObject, File, FineTune, + Image, Model, Moderation, Search, @@ -51,7 +51,7 @@ "Completion", "Customer", "Edit", - "DALLE", + "Image", "Deployment", "Embedding", "Engine", diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index 28bea88ad3..26fa63c525 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -2,7 +2,6 @@ from openai.api_resources.classification import Classification # noqa: F401 from openai.api_resources.completion import Completion # noqa: F401 from openai.api_resources.customer import Customer # noqa: F401 -from openai.api_resources.dalle import DALLE # noqa: F401 from openai.api_resources.deployment import Deployment # noqa: F401 from openai.api_resources.edit import Edit # noqa: F401 from openai.api_resources.embedding import Embedding # noqa: F401 @@ -10,6 +9,7 @@ from openai.api_resources.error_object import ErrorObject # noqa: F401 from openai.api_resources.file import File # noqa: F401 from openai.api_resources.fine_tune import FineTune # noqa: F401 +from openai.api_resources.image import Image # noqa: F401 from openai.api_resources.model import Model # noqa: F401 from openai.api_resources.moderation import Moderation # noqa: F401 from openai.api_resources.search import Search # noqa: F401 diff --git a/openai/api_resources/dalle.py b/openai/api_resources/image.py similarity index 96% rename from openai/api_resources/dalle.py rename to openai/api_resources/image.py index 119860666b..ebb77676df 100644 --- a/openai/api_resources/dalle.py +++ b/openai/api_resources/image.py @@ -6,7 +6,7 @@ from openai.api_resources.abstract import APIResource -class DALLE(APIResource): +class Image(APIResource): OBJECT_NAME = "images" @classmethod @@ -14,7 +14,7 @@ def _get_url(http://github.com/cls,%20action): return cls.class_url() + f"/{action}" @classmethod - def generations( + def create( cls, **params, ): @@ -22,7 +22,7 @@ def generations( return instance.request("post", cls._get_url("http://github.com/generations"), params) @classmethod - def variations( + def create_variation( cls, image, api_key=None, @@ -55,7 +55,7 @@ def variations( ) @classmethod - def edits( + def create_edit( cls, image, mask, diff --git a/openai/cli.py b/openai/cli.py index 3c77bfe103..de2aca8bd6 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -229,44 +229,41 @@ def list(cls, args): print(file) -class DALLE: +class Image: @classmethod - def generations(cls, args): - resp = openai.DALLE.generations( + def create(cls, args): + resp = openai.Image.create( prompt=args.prompt, - model=args.model, size=args.size, - num_images=args.num_images, + n=args.num_images, response_format=args.response_format, ) print(resp) @classmethod - def variations(cls, args): + def create_variation(cls, args): with open(args.image, "rb") as file_reader: buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") - resp = openai.DALLE.variations( + resp = openai.Image.create_variation( image=buffer_reader, - model=args.model, size=args.size, - num_images=args.num_images, + n=args.num_images, response_format=args.response_format, ) print(resp) @classmethod - def edits(cls, args): + def create_edit(cls, args): with open(args.image, "rb") as file_reader: image_reader = BufferReader(file_reader.read(), desc="Upload progress") with open(args.mask, "rb") as file_reader: mask_reader = BufferReader(file_reader.read(), desc="Upload progress") - resp = openai.DALLE.edits( + resp = openai.Image.create_edit( image=image_reader, mask=mask_reader, prompt=args.prompt, - model=args.model, size=args.size, - num_images=args.num_images, + n=args.num_images, response_format=args.response_format, ) print(resp) @@ -1026,19 +1023,17 @@ def help(args): sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") sub.set_defaults(func=FineTune.cancel) - # DALLE - sub = subparsers.add_parser("dalle.generations") - sub.add_argument("-m", "--model", type=str, default="image-alpha-001") + # Image + sub = subparsers.add_parser("image.create") 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=DALLE.generations) + sub.set_defaults(func=Image.create) - sub = subparsers.add_parser("dalle.edits") - sub.add_argument("-m", "--model", type=str, default="image-alpha-001") + sub = subparsers.add_parser("image.create_edit") sub.add_argument("-p", "--prompt", type=str, required=True) sub.add_argument("-n", "--num-images", type=int, default=1) sub.add_argument( @@ -1059,10 +1054,9 @@ def help(args): required=True, 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=DALLE.edits) + sub.set_defaults(func=Image.create_edit) - sub = subparsers.add_parser("dalle.variations") - sub.add_argument("-m", "--model", type=str, default="image-alpha-001") + sub = subparsers.add_parser("image.create_variation") sub.add_argument("-n", "--num-images", type=int, default=1) sub.add_argument( "-I", @@ -1075,7 +1069,7 @@ def help(args): "-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=DALLE.variations) + sub.set_defaults(func=Image.create_variation) def wandb_register(parser): diff --git a/openai/version.py b/openai/version.py index 595cfe2965..1ae872fda0 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.24.0" +VERSION = "0.25.0" From dc33cb9d303eeecce484ac87bd6870db8de3f4a6 Mon Sep 17 00:00:00 2001 From: Michelle Pokrass Date: Thu, 3 Nov 2022 16:02:00 -0700 Subject: [PATCH 050/914] Update readme with image information (#135) --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index d777bc392a..85955bdab2 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,9 @@ openai api engines.list # create a completion openai api completions.create -e ada -p "Hello world" + +# generate images via DALL·E API +openai api image.create -p "two dogs playing chess, cartoon" -n 1 ``` ## Example code @@ -190,6 +193,18 @@ openai wandb sync For more information on fine tuning, read the [fine-tuning guide](https://beta.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. +## Image generation (DALL·E) + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +image_resp = openai.Image.create(prompt="two dogs playing chess, oil painting", n=4, size="512x512") + +``` + +See the [usage guide](https://beta.openai.com/docs/guides/images) for more details. + ## Requirements - Python 3.7.1+ From 3c00e856ad71fa69ac40a228a070b59688dfdee2 Mon Sep 17 00:00:00 2001 From: hallacy Date: Thu, 17 Nov 2022 14:57:04 -0800 Subject: [PATCH 051/914] Add Moderation endpoint to readme (#151) (#138) * Add Moderation endpoint to readme * add basic example Co-authored-by: Todor Markov --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 85955bdab2..1dad8c0a96 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,19 @@ openai wandb sync For more information on fine tuning, read the [fine-tuning guide](https://beta.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. +### Moderation + +OpenAI provides a Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://beta.openai.com/docs/usage-policies) + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +moderation_resp = openai.Moderation.create(input="Here is some perfectly innocuous text that follows all OpenAI content policies.") +``` + +See the [moderation guide](https://beta.openai.com/docs/guides/moderation) for more details. + ## Image generation (DALL·E) ```python From af59037ab47478103fa02f02e167d8bab1ef5f7c Mon Sep 17 00:00:00 2001 From: Boris Dayma Date: Wed, 7 Dec 2022 11:29:30 -0600 Subject: [PATCH 052/914] fix(wandb): encoding on Windows (#148) fix #143 --- openai/wandb_logger.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openai/wandb_logger.py b/openai/wandb_logger.py index bc6696fb39..6dd7614ca2 100644 --- a/openai/wandb_logger.py +++ b/openai/wandb_logger.py @@ -242,7 +242,9 @@ def _log_artifacts(cls, fine_tune, project, entity): type="fine_tune_details", metadata=fine_tune, ) - with artifact.new_file("fine_tune_details.json") as f: + with artifact.new_file( + "fine_tune_details.json", mode="w", encoding="utf-8" + ) as f: json.dump(fine_tune, f, indent=2) wandb.run.log_artifact( artifact, @@ -276,7 +278,7 @@ def _log_artifact_inputs(cls, file, prefix, artifact_type, project, entity): ) return artifact = wandb.Artifact(artifact_name, type=artifact_type, metadata=file) - with artifact.new_file(filename, mode="w") as f: + with artifact.new_file(filename, mode="w", encoding="utf-8") as f: f.write(file_content) # create a Table From bbea3dc0da251754d1e32e9dba47fb6640b06eaf Mon Sep 17 00:00:00 2001 From: Michael Weiss Date: Tue, 20 Dec 2022 18:03:41 +0100 Subject: [PATCH 053/914] Include long description (#157) --- setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.py b/setup.py index 0b6956ef0e..67721f029c 100644 --- a/setup.py +++ b/setup.py @@ -8,10 +8,15 @@ ) with open(version_path, "rt") as f: exec(f.read(), version_contents) + +with open("README.md", "r") as fh: + long_description = fh.read() setup( name="openai", description="Python client library for the OpenAI API", + long_description=long_description, + long_description_content_type="text/markdown", version=version_contents["VERSION"], install_requires=[ "requests>=2.20", # to get the patch for CVE-2018-18074 From 528a5ba196d14cfd3cfa242933c159ecd93ffc3e Mon Sep 17 00:00:00 2001 From: Michal Vasilek Date: Tue, 20 Dec 2022 18:07:26 +0100 Subject: [PATCH 054/914] Do not require typing_extensions with Python 3.8+ (#154) typing_extensions are only used for Literal which is available in the standard library since Python 3.8 --- openai/api_requestor.py | 7 ++++++- setup.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 5970510434..99a392c148 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -7,7 +7,12 @@ from urllib.parse import urlencode, urlsplit, urlunsplit import requests -from typing_extensions import Literal + +# Literal is available from Python 3.8 +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal import openai from openai import error, util, version diff --git a/setup.py b/setup.py index 67721f029c..9b318d326e 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ "pandas-stubs>=1.1.0.11", # Needed for type hints for mypy "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format "numpy", - "typing_extensions", # Needed for type hints for mypy + 'typing_extensions;python_version<"3.8"', # Needed for type hints for mypy ], extras_require={ "dev": ["black~=21.6b0", "pytest==6.*"], From cb120bf4ad6f9164192cc1bf1bfc06cca0c53155 Mon Sep 17 00:00:00 2001 From: Michal Vasilek Date: Wed, 21 Dec 2022 21:46:28 +0100 Subject: [PATCH 055/914] Fix mypy (#158) mypy doesn't correctly handle try except blocks, so it's necessary to import from the correct module based on the python version. --- openai/api_requestor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 99a392c148..2ae0cbe034 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -1,5 +1,6 @@ import json import platform +import sys import threading import warnings from json import JSONDecodeError @@ -8,10 +9,9 @@ import requests -# Literal is available from Python 3.8 -try: +if sys.version_info >= (3, 8): from typing import Literal -except ImportError: +else: from typing_extensions import Literal import openai From ec4943f93c34a34e74a52ba428575bb5b3efceb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCrtz?= <120655914+cmurtz-msft@users.noreply.github.com> Date: Thu, 22 Dec 2022 19:19:38 +0100 Subject: [PATCH 056/914] Azure OpenAI 2022-12-01 (#159) * Update api_version * Update README.md * Fix README * Fix README --- README.md | 19 +++++++------------ openai/__init__.py | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1dad8c0a96..5bd40a7919 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ All endpoints have a `.create` method that support a `request_timeout` param. T ### Microsoft Azure Endpoints -In order to use the library with Microsoft Azure endpoints, you need to set the api_type, api_base and api_version in addition to the api_key. The api_type must be set to 'azure' and the others correspond to the properties of your endpoint. +In order to use the library with Microsoft Azure endpoints, you need to set the `api_type`, `api_base` and `api_version` in addition to the `api_key`. The `api_type` must be set to 'azure' and the others correspond to the properties of your endpoint. In addition, the deployment name must be passed as the engine parameter. ```python @@ -66,29 +66,24 @@ import openai openai.api_type = "azure" openai.api_key = "..." openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2021-11-01-preview" +openai.api_version = "2022-12-01" # create a completion completion = openai.Completion.create(engine="deployment-name", prompt="Hello world") # print the completion print(completion.choices[0].text) - -# create a search and pass the deployment-name as the engine Id. -search = openai.Engine(id="deployment-name").search(documents=["White House", "hospital", "school"], query ="the president") - -# print the search -print(search) ``` -Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, search and fine-tuning operations. +Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. For a detailed example on how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: +* [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) * [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) * [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) ### Microsoft Azure Active Directory Authentication -In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the api_type to "azure_ad" and pass the acquired credential token to api_key. The rest of the parameters need to be set as specified in the previous section. +In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the `api_type` to "azure_ad" and pass the acquired credential token to `api_key`. The rest of the parameters need to be set as specified in the previous section. ```python @@ -97,13 +92,13 @@ import openai # Request credential default_credential = DefaultAzureCredential() -token = default_credential.get_token("https://cognitiveservices.azure.com/") +token = default_credential.get_token("https://cognitiveservices.azure.com/.default") # Setup parameters openai.api_type = "azure_ad" openai.api_key = token.token openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2022-03-01-preview" +openai.api_version = "2022-12-01" # ... ``` diff --git a/openai/__init__.py b/openai/__init__.py index b65b86d003..d935ea8ca5 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -34,7 +34,7 @@ api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") api_version = ( - "2022-03-01-preview" if api_type in ("azure", "azure_ad", "azuread") else None + "2022-12-01" if api_type in ("azure", "azure_ad", "azuread") else None ) verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None From 0abf64137c18b45925d5015bae80429adb46fac6 Mon Sep 17 00:00:00 2001 From: Andrew Chen Wang <60190294+Andrew-Chen-Wang@users.noreply.github.com> Date: Wed, 4 Jan 2023 19:39:57 -0500 Subject: [PATCH 057/914] Add async support (#146) * Add async support * Fix aiohttp requests * Fix some syntax errors * Close aiohttp session properly * This is due to a lack of an async __del__ method * Fix code per review * Fix async tests and some mypy errors * Run black * Add todo for multipart form generation * Fix more mypy * Fix exception type * Don't yield twice Co-authored-by: Damien Deville --- README.md | 26 ++ openai/__init__.py | 11 +- openai/api_requestor.py | 255 ++++++++++++++++-- openai/api_resources/abstract/api_resource.py | 43 +++ .../abstract/createable_api_resource.py | 55 +++- .../abstract/deletable_api_resource.py | 16 +- .../abstract/engine_api_resource.py | 121 ++++++++- .../abstract/listable_api_resource.py | 52 +++- .../abstract/nested_resource_class_methods.py | 58 +++- .../abstract/updateable_api_resource.py | 6 + openai/api_resources/answer.py | 5 + openai/api_resources/classification.py | 5 + openai/api_resources/completion.py | 20 ++ openai/api_resources/customer.py | 5 + openai/api_resources/deployment.py | 49 +++- openai/api_resources/edit.py | 24 ++ openai/api_resources/embedding.py | 39 +++ openai/api_resources/engine.py | 25 ++ openai/api_resources/file.py | 157 +++++++++-- openai/api_resources/fine_tune.py | 71 ++++- openai/api_resources/image.py | 111 +++++++- openai/api_resources/moderation.py | 22 +- openai/api_resources/search.py | 21 ++ openai/embeddings_utils.py | 27 ++ openai/openai_object.py | 51 ++++ openai/tests/__init__.py | 0 openai/tests/asyncio/__init__.py | 0 openai/tests/asyncio/test_endpoints.py | 65 +++++ openai/tests/test_long_examples_validator.py | 19 +- setup.py | 3 +- 30 files changed, 1288 insertions(+), 74 deletions(-) create mode 100644 openai/tests/__init__.py create mode 100644 openai/tests/asyncio/__init__.py create mode 100644 openai/tests/asyncio/test_endpoints.py diff --git a/README.md b/README.md index 5bd40a7919..53bab3ab2a 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,32 @@ image_resp = openai.Image.create(prompt="two dogs playing chess, oil painting", ``` +## Async API + +Async support is available in the API by prepending `a` to a network-bound method: + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +async def create_completion(): + completion_resp = await openai.Completion.acreate(prompt="This is a test", engine="davinci") + +``` + +To make async requests more efficient, you can pass in your own +``aiohttp.ClientSession``, but you must manually close the client session at the end +of your program/event loop: + +```python +import openai +from aiohttp import ClientSession + +openai.aiosession.set(ClientSession()) +# At the end of your program, close the http session +await openai.aiosession.get().close() +``` + See the [usage guide](https://beta.openai.com/docs/guides/images) for more details. ## Requirements diff --git a/openai/__init__.py b/openai/__init__.py index d935ea8ca5..ef6da5ba58 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -3,7 +3,8 @@ # Originally forked from the MIT-licensed Stripe Python bindings. import os -from typing import Optional +from contextvars import ContextVar +from typing import Optional, TYPE_CHECKING from openai.api_resources import ( Answer, @@ -24,6 +25,9 @@ ) from openai.error import APIError, InvalidRequestError, OpenAIError +if TYPE_CHECKING: + from aiohttp import ClientSession + api_key = os.environ.get("OPENAI_API_KEY") # Path of a file with an API key, whose contents can change. Supercedes # `api_key` if set. The main use case is volume-mounted Kubernetes secrets, @@ -44,6 +48,11 @@ debug = False log = None # Set to either 'debug' or 'info', controls console logging +aiosession: ContextVar[Optional["ClientSession"]] = ContextVar( + "aiohttp-session", default=None +) # Acts as a global aiohttp ClientSession that reuses connections. +# This is user-supplied; otherwise, a session is remade for each request. + __all__ = [ "APIError", "Answer", diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 2ae0cbe034..b10730216d 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -1,12 +1,14 @@ +import asyncio import json import platform import sys import threading import warnings from json import JSONDecodeError -from typing import Dict, Iterator, Optional, Tuple, Union, overload +from typing import AsyncGenerator, Dict, Iterator, Optional, Tuple, Union, overload from urllib.parse import urlencode, urlsplit, urlunsplit +import aiohttp import requests if sys.version_info >= (3, 8): @@ -49,6 +51,20 @@ def _requests_proxies_arg(proxy) -> Optional[Dict[str, str]]: ) +def _aiohttp_proxies_arg(proxy) -> Optional[str]: + """Returns a value suitable for the 'proxies' argument to 'aiohttp.ClientSession.request.""" + if proxy is None: + return None + elif isinstance(proxy, str): + return proxy + elif isinstance(proxy, dict): + return proxy["https"] if "https" in proxy else proxy["http"] + else: + raise ValueError( + "'openai.proxy' must be specified as either a string URL or a dict with string URL under the https and/or http keys." + ) + + def _make_session() -> requests.Session: if not openai.verify_ssl_certs: warnings.warn("verify_ssl_certs is ignored; openai always verifies.") @@ -63,18 +79,32 @@ def _make_session() -> requests.Session: return s +def parse_stream_helper(line): + if line: + if line == b"data: [DONE]": + # return here will cause GeneratorExit exception in urllib3 + # and it will close http connection with TCP Reset + return None + if hasattr(line, "decode"): + line = line.decode("utf-8") + if line.startswith("data: "): + line = line[len("data: ") :] + return line + return None + + def parse_stream(rbody): for line in rbody: - if line: - if line == b"data: [DONE]": - # return here will cause GeneratorExit exception in urllib3 - # and it will close http connection with TCP Reset - continue - if hasattr(line, "decode"): - line = line.decode("utf-8") - if line.startswith("data: "): - line = line[len("data: ") :] - yield line + _line = parse_stream_helper(line) + if _line is not None: + yield _line + + +async def parse_stream_async(rbody: aiohttp.StreamReader): + async for line in rbody: + _line = parse_stream_helper(line) + if _line is not None: + yield _line class APIRequestor: @@ -186,6 +216,86 @@ def request( resp, got_stream = self._interpret_response(result, stream) return resp, got_stream, self.api_key + @overload + async def arequest( + self, + method, + url, + params, + headers, + files, + stream: Literal[True], + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[AsyncGenerator[OpenAIResponse, None], bool, str]: + pass + + @overload + async def arequest( + self, + method, + url, + params=..., + headers=..., + files=..., + *, + stream: Literal[True], + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[AsyncGenerator[OpenAIResponse, None], bool, str]: + pass + + @overload + async def arequest( + self, + method, + url, + params=..., + headers=..., + files=..., + stream: Literal[False] = ..., + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[OpenAIResponse, bool, str]: + pass + + @overload + async def arequest( + self, + method, + url, + params=..., + headers=..., + files=..., + stream: bool = ..., + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: + pass + + async def arequest( + self, + method, + url, + params=None, + headers=None, + files=None, + stream: bool = False, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: + result = await self.arequest_raw( + method.lower(), + url, + params=params, + supplied_headers=headers, + files=files, + request_id=request_id, + request_timeout=request_timeout, + ) + resp, got_stream = await self._interpret_async_response(result, stream) + return resp, got_stream, self.api_key + def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): try: error_data = resp["error"] @@ -315,18 +425,15 @@ def _validate_headers( return headers - def request_raw( + def _prepare_request_raw( self, - method, url, - *, - params=None, - supplied_headers: Dict[str, str] = None, - files=None, - stream: bool = False, - request_id: Optional[str] = None, - request_timeout: Optional[Union[float, Tuple[float, float]]] = None, - ) -> requests.Response: + supplied_headers, + method, + params, + files, + request_id: Optional[str], + ) -> Tuple[str, Dict[str, str], Optional[bytes]]: abs_url = "%s%s" % (self.api_base, url) headers = self._validate_headers(supplied_headers) @@ -355,6 +462,24 @@ def request_raw( util.log_info("Request to OpenAI API", method=method, path=abs_url) util.log_debug("Post details", data=data, api_version=self.api_version) + return abs_url, headers, data + + def request_raw( + self, + method, + url, + *, + params=None, + supplied_headers: Optional[Dict[str, str]] = None, + files=None, + stream: bool = False, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ) -> requests.Response: + abs_url, headers, data = self._prepare_request_raw( + url, supplied_headers, method, params, files, request_id + ) + if not hasattr(_thread_context, "session"): _thread_context.session = _make_session() try: @@ -385,6 +510,71 @@ def request_raw( ) return result + async def arequest_raw( + self, + method, + url, + *, + params=None, + supplied_headers: Optional[Dict[str, str]] = None, + files=None, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ) -> aiohttp.ClientResponse: + abs_url, headers, data = self._prepare_request_raw( + url, supplied_headers, method, params, files, request_id + ) + + if isinstance(request_timeout, tuple): + timeout = aiohttp.ClientTimeout( + connect=request_timeout[0], + total=request_timeout[1], + ) + else: + timeout = aiohttp.ClientTimeout( + total=request_timeout if request_timeout else TIMEOUT_SECS + ) + user_set_session = openai.aiosession.get() + + if files: + # TODO: Use `aiohttp.MultipartWriter` to create the multipart form data here. + # For now we use the private `requests` method that is known to have worked so far. + data, content_type = requests.models.RequestEncodingMixin._encode_files( # type: ignore + files, data + ) + headers["Content-Type"] = content_type + request_kwargs = { + "method": method, + "url": abs_url, + "headers": headers, + "data": data, + "proxy": _aiohttp_proxies_arg(openai.proxy), + "timeout": timeout, + } + try: + if user_set_session: + result = await user_set_session.request(**request_kwargs) + else: + async with aiohttp.ClientSession() as session: + result = await session.request(**request_kwargs) + util.log_info( + "OpenAI API response", + path=abs_url, + response_code=result.status, + processing_ms=result.headers.get("OpenAI-Processing-Ms"), + request_id=result.headers.get("X-Request-Id"), + ) + # Don't read the whole stream for debug logging unless necessary. + if openai.log == "debug": + util.log_debug( + "API response body", body=result.content, headers=result.headers + ) + return result + except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as e: + raise error.Timeout("Request timed out") from e + except aiohttp.ClientError as e: + raise error.APIConnectionError("Error communicating with OpenAI") from e + def _interpret_response( self, result: requests.Response, stream: bool ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool]: @@ -404,6 +594,29 @@ def _interpret_response( False, ) + async def _interpret_async_response( + self, result: aiohttp.ClientResponse, stream: bool + ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool]: + """Returns the response(s) and a bool indicating whether it is a stream.""" + if stream and "text/event-stream" in result.headers.get("Content-Type", ""): + return ( + self._interpret_response_line( + line, result.status, result.headers, stream=True + ) + async for line in parse_stream_async(result.content) + ), True + else: + try: + await result.read() + except aiohttp.ClientError as e: + util.log_warn(e, body=result.content) + return ( + self._interpret_response_line( + await result.read(), result.status, result.headers, stream=False + ), + False, + ) + def _interpret_response_line( self, rbody, rcode, rheaders, stream: bool ) -> OpenAIResponse: diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index aa7cfe88e1..53a7dec799 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -20,6 +20,13 @@ def retrieve( instance.refresh(request_id=request_id, request_timeout=request_timeout) return instance + @classmethod + def aretrieve( + cls, id, api_key=None, request_id=None, request_timeout=None, **params + ): + instance = cls(id, api_key, **params) + return instance.arefresh(request_id=request_id, request_timeout=request_timeout) + def refresh(self, request_id=None, request_timeout=None): self.refresh_from( self.request( @@ -31,6 +38,17 @@ def refresh(self, request_id=None, request_timeout=None): ) return self + async def arefresh(self, request_id=None, request_timeout=None): + self.refresh_from( + await self.arequest( + "get", + self.instance_url(http://github.com/operation=%22refresh"), + request_id=request_id, + request_timeout=request_timeout, + ) + ) + return self + @classmethod def class_url(http://github.com/cls): if cls == APIResource: @@ -116,6 +134,31 @@ def _static_request( response, api_key, api_version, organization ) + @classmethod + async def _astatic_request( + cls, + method_, + url_, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_version=api_version, + organization=organization, + api_base=api_base, + api_type=api_type, + ) + response, _, api_key = await requestor.arequest( + method_, url_, params, request_id=request_id + ) + return response + @classmethod def _get_api_type_and_version( cls, api_type: Optional[str] = None, api_version: Optional[str] = None diff --git a/openai/api_resources/abstract/createable_api_resource.py b/openai/api_resources/abstract/createable_api_resource.py index 39d3e4f504..1361c02627 100644 --- a/openai/api_resources/abstract/createable_api_resource.py +++ b/openai/api_resources/abstract/createable_api_resource.py @@ -7,15 +7,13 @@ class CreateableAPIResource(APIResource): plain_old_data = False @classmethod - def create( + def __prepare_create_requestor( cls, api_key=None, api_base=None, api_type=None, - request_id=None, api_version=None, organization=None, - **params, ): requestor = api_requestor.APIRequestor( api_key, @@ -35,6 +33,26 @@ def create( url = cls.class_url() else: raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return requestor, url + + @classmethod + def create( + cls, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor, url = cls.__prepare_create_requestor( + api_key, + api_base, + api_type, + api_version, + organization, + ) response, _, api_key = requestor.request( "post", url, params, request_id=request_id @@ -47,3 +65,34 @@ def create( organization, plain_old_data=cls.plain_old_data, ) + + @classmethod + async def acreate( + cls, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor, url = cls.__prepare_create_requestor( + api_key, + api_base, + api_type, + api_version, + organization, + ) + + response, _, api_key = await requestor.arequest( + "post", url, params, request_id=request_id + ) + + return util.convert_to_openai_object( + response, + api_key, + api_version, + organization, + plain_old_data=cls.plain_old_data, + ) diff --git a/openai/api_resources/abstract/deletable_api_resource.py b/openai/api_resources/abstract/deletable_api_resource.py index 220375ca2f..a800ceb812 100644 --- a/openai/api_resources/abstract/deletable_api_resource.py +++ b/openai/api_resources/abstract/deletable_api_resource.py @@ -1,4 +1,5 @@ from urllib.parse import quote_plus +from typing import Awaitable from openai import error from openai.api_resources.abstract.api_resource import APIResource @@ -7,7 +8,7 @@ class DeletableAPIResource(APIResource): @classmethod - def delete(cls, sid, api_type=None, api_version=None, **params): + def __prepare_delete(cls, sid, api_type=None, api_version=None): if isinstance(cls, APIResource): raise ValueError(".delete may only be called as a class method now.") @@ -28,7 +29,20 @@ def delete(cls, sid, api_type=None, api_version=None, **params): url = "%s/%s" % (base, extn) else: raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return url + + @classmethod + def delete(cls, sid, api_type=None, api_version=None, **params): + url = cls.__prepare_delete(sid, api_type, api_version) return cls._static_request( "delete", url, api_type=api_type, api_version=api_version, **params ) + + @classmethod + def adelete(cls, sid, api_type=None, api_version=None, **params) -> Awaitable: + url = cls.__prepare_delete(sid, api_type, api_version) + + return cls._astatic_request( + "delete", url, api_type=api_type, api_version=api_version, **params + ) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index 152313c202..d6fe0d39a9 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -61,12 +61,11 @@ def class_url( raise error.InvalidAPIType("Unsupported API type %s" % api_type) @classmethod - def create( + def __prepare_create_request( cls, api_key=None, api_base=None, api_type=None, - request_id=None, api_version=None, organization=None, **params, @@ -112,6 +111,45 @@ def create( organization=organization, ) url = cls.class_url(http://github.com/engine,%20api_type,%20api_version) + return ( + deployment_id, + engine, + timeout, + stream, + headers, + request_timeout, + typed_api_type, + requestor, + url, + params, + ) + + @classmethod + def create( + cls, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + ( + deployment_id, + engine, + timeout, + stream, + headers, + request_timeout, + typed_api_type, + requestor, + url, + params, + ) = cls.__prepare_create_request( + api_key, api_base, api_type, api_version, organization, **params + ) + response, _, api_key = requestor.request( "post", url, @@ -151,6 +189,70 @@ def create( return obj + @classmethod + async def acreate( + cls, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + ( + deployment_id, + engine, + timeout, + stream, + headers, + request_timeout, + typed_api_type, + requestor, + url, + params, + ) = cls.__prepare_create_request( + api_key, api_base, api_type, api_version, organization, **params + ) + response, _, api_key = await requestor.arequest( + "post", + url, + params=params, + headers=headers, + stream=stream, + request_id=request_id, + request_timeout=request_timeout, + ) + + if stream: + # must be an iterator + assert not isinstance(response, OpenAIResponse) + return ( + util.convert_to_openai_object( + line, + api_key, + api_version, + organization, + engine=engine, + plain_old_data=cls.plain_old_data, + ) + for line in response + ) + else: + obj = util.convert_to_openai_object( + response, + api_key, + api_version, + organization, + engine=engine, + plain_old_data=cls.plain_old_data, + ) + + if timeout is not None: + await obj.await_(timeout=timeout or None) + + return obj + def instance_url(http://github.com/self): id = self.get("id") @@ -206,3 +308,18 @@ def wait(self, timeout=None): break self.refresh() return self + + async def await_(self, timeout=None): + """Async version of `EngineApiResource.wait`""" + start = time.time() + while self.status != "complete": + self.timeout = ( + min(timeout + start - time.time(), MAX_TIMEOUT) + if timeout is not None + else MAX_TIMEOUT + ) + if self.timeout < 0: + del self.timeout + break + await self.arefresh() + return self diff --git a/openai/api_resources/abstract/listable_api_resource.py b/openai/api_resources/abstract/listable_api_resource.py index adbf4e8df9..3e59979f13 100644 --- a/openai/api_resources/abstract/listable_api_resource.py +++ b/openai/api_resources/abstract/listable_api_resource.py @@ -9,15 +9,13 @@ def auto_paging_iter(cls, *args, **params): return cls.list(*args, **params).auto_paging_iter() @classmethod - def list( + def __prepare_list_requestor( cls, api_key=None, - request_id=None, api_version=None, organization=None, api_base=None, api_type=None, - **params, ): requestor = api_requestor.APIRequestor( api_key, @@ -38,6 +36,26 @@ def list( url = cls.class_url() else: raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return requestor, url + + @classmethod + def list( + cls, + api_key=None, + request_id=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + **params, + ): + requestor, url = cls.__prepare_list_requestor( + api_key, + api_version, + organization, + api_base, + api_type, + ) response, _, api_key = requestor.request( "get", url, params, request_id=request_id @@ -47,3 +65,31 @@ def list( ) openai_object._retrieve_params = params return openai_object + + @classmethod + async def alist( + cls, + api_key=None, + request_id=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + **params, + ): + requestor, url = cls.__prepare_list_requestor( + api_key, + api_version, + organization, + api_base, + api_type, + ) + + response, _, api_key = await requestor.arequest( + "get", url, params, request_id=request_id + ) + openai_object = util.convert_to_openai_object( + response, api_key, api_version, organization + ) + openai_object._retrieve_params = params + return openai_object diff --git a/openai/api_resources/abstract/nested_resource_class_methods.py b/openai/api_resources/abstract/nested_resource_class_methods.py index c86e59fbf6..bfa5bcd873 100644 --- a/openai/api_resources/abstract/nested_resource_class_methods.py +++ b/openai/api_resources/abstract/nested_resource_class_methods.py @@ -3,8 +3,12 @@ from openai import api_requestor, util -def nested_resource_class_methods( - resource, path=None, operations=None, resource_plural=None +def _nested_resource_class_methods( + resource, + path=None, + operations=None, + resource_plural=None, + async_=False, ): if resource_plural is None: resource_plural = "%ss" % resource @@ -43,8 +47,34 @@ def nested_resource_request( response, api_key, api_version, organization ) + async def anested_resource_request( + cls, + method, + url, + api_key=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, api_version=api_version, organization=organization + ) + response, _, api_key = await requestor.arequest( + method, url, params, request_id=request_id + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + resource_request_method = "%ss_request" % resource - setattr(cls, resource_request_method, classmethod(nested_resource_request)) + setattr( + cls, + resource_request_method, + classmethod( + anested_resource_request if async_ else nested_resource_request + ), + ) for operation in operations: if operation == "create": @@ -100,3 +130,25 @@ def list_nested_resources(cls, id, **params): return cls return wrapper + + +def nested_resource_class_methods( + resource, + path=None, + operations=None, + resource_plural=None, +): + return _nested_resource_class_methods( + resource, path, operations, resource_plural, async_=False + ) + + +def anested_resource_class_methods( + resource, + path=None, + operations=None, + resource_plural=None, +): + return _nested_resource_class_methods( + resource, path, operations, resource_plural, async_=True + ) diff --git a/openai/api_resources/abstract/updateable_api_resource.py b/openai/api_resources/abstract/updateable_api_resource.py index e7289d12d3..245f9b80b3 100644 --- a/openai/api_resources/abstract/updateable_api_resource.py +++ b/openai/api_resources/abstract/updateable_api_resource.py @@ -1,4 +1,5 @@ from urllib.parse import quote_plus +from typing import Awaitable from openai.api_resources.abstract.api_resource import APIResource @@ -8,3 +9,8 @@ class UpdateableAPIResource(APIResource): def modify(cls, sid, **params): url = "%s/%s" % (cls.class_url(), quote_plus(sid)) return cls._static_request("post", url, **params) + + @classmethod + def amodify(cls, sid, **params) -> Awaitable: + url = "%s/%s" % (cls.class_url(), quote_plus(sid)) + return cls._astatic_request("patch", url, **params) diff --git a/openai/api_resources/answer.py b/openai/api_resources/answer.py index 33de3cb7e9..be8c4f1ac8 100644 --- a/openai/api_resources/answer.py +++ b/openai/api_resources/answer.py @@ -10,3 +10,8 @@ def get_url(http://github.com/self): def create(cls, **params): instance = cls() return instance.request("post", cls.get_url(), params) + + @classmethod + def acreate(cls, **params): + instance = cls() + return instance.arequest("post", cls.get_url(), params) diff --git a/openai/api_resources/classification.py b/openai/api_resources/classification.py index 6423c6946a..823f521b96 100644 --- a/openai/api_resources/classification.py +++ b/openai/api_resources/classification.py @@ -10,3 +10,8 @@ def get_url(http://github.com/self): def create(cls, **params): instance = cls() return instance.request("post", cls.get_url(), params) + + @classmethod + def acreate(cls, **params): + instance = cls() + return instance.arequest("post", cls.get_url(), params) diff --git a/openai/api_resources/completion.py b/openai/api_resources/completion.py index 429597b46e..6912b4b730 100644 --- a/openai/api_resources/completion.py +++ b/openai/api_resources/completion.py @@ -28,3 +28,23 @@ def create(cls, *args, **kwargs): raise util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new completion for the provided prompt and parameters. + + See https://beta.openai.com/docs/api-reference/completions/create for a list + of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + while True: + try: + return await super().acreate(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/customer.py b/openai/api_resources/customer.py index 571adf8eac..cb9779a2f1 100644 --- a/openai/api_resources/customer.py +++ b/openai/api_resources/customer.py @@ -10,3 +10,8 @@ def get_url(http://github.com/self,%20customer,%20endpoint): def create(cls, customer, endpoint, **params): instance = cls() return instance.request("post", cls.get_url(http://github.com/customer,%20endpoint), params) + + @classmethod + def acreate(cls, customer, endpoint, **params): + instance = cls() + return instance.arequest("post", cls.get_url(http://github.com/customer,%20endpoint), params) diff --git a/openai/api_resources/deployment.py b/openai/api_resources/deployment.py index 5850e0c9fb..2f3fcd1307 100644 --- a/openai/api_resources/deployment.py +++ b/openai/api_resources/deployment.py @@ -11,10 +11,7 @@ class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResourc OBJECT_NAME = "deployments" @classmethod - def create(cls, *args, **kwargs): - """ - Creates a new deployment for the provided prompt and parameters. - """ + def _check_create(cls, *args, **kwargs): typed_api_type, _ = cls._get_api_type_and_version( kwargs.get("api_type", None), None ) @@ -45,10 +42,24 @@ def create(cls, *args, **kwargs): param="scale_settings", ) + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new deployment for the provided prompt and parameters. + """ + cls._check_create(*args, **kwargs) return super().create(*args, **kwargs) @classmethod - def list(cls, *args, **kwargs): + def acreate(cls, *args, **kwargs): + """ + Creates a new deployment for the provided prompt and parameters. + """ + cls._check_create(*args, **kwargs) + return super().acreate(*args, **kwargs) + + @classmethod + def _check_list(cls, *args, **kwargs): typed_api_type, _ = cls._get_api_type_and_version( kwargs.get("api_type", None), None ) @@ -57,10 +68,18 @@ def list(cls, *args, **kwargs): "Deployment operations are only available for the Azure API type." ) + @classmethod + def list(cls, *args, **kwargs): + cls._check_list(*args, **kwargs) return super().list(*args, **kwargs) @classmethod - def delete(cls, *args, **kwargs): + def alist(cls, *args, **kwargs): + cls._check_list(*args, **kwargs) + return super().alist(*args, **kwargs) + + @classmethod + def _check_delete(cls, *args, **kwargs): typed_api_type, _ = cls._get_api_type_and_version( kwargs.get("api_type", None), None ) @@ -69,10 +88,18 @@ def delete(cls, *args, **kwargs): "Deployment operations are only available for the Azure API type." ) + @classmethod + def delete(cls, *args, **kwargs): + cls._check_delete(*args, **kwargs) return super().delete(*args, **kwargs) @classmethod - def retrieve(cls, *args, **kwargs): + def adelete(cls, *args, **kwargs): + cls._check_delete(*args, **kwargs) + return super().adelete(*args, **kwargs) + + @classmethod + def _check_retrieve(cls, *args, **kwargs): typed_api_type, _ = cls._get_api_type_and_version( kwargs.get("api_type", None), None ) @@ -81,4 +108,12 @@ def retrieve(cls, *args, **kwargs): "Deployment operations are only available for the Azure API type." ) + @classmethod + def retrieve(cls, *args, **kwargs): + cls._check_retrieve(*args, **kwargs) return super().retrieve(*args, **kwargs) + + @classmethod + def aretrieve(cls, *args, **kwargs): + cls._check_retrieve(*args, **kwargs) + return super().aretrieve(*args, **kwargs) diff --git a/openai/api_resources/edit.py b/openai/api_resources/edit.py index fe66b6f0f4..985f062ddb 100644 --- a/openai/api_resources/edit.py +++ b/openai/api_resources/edit.py @@ -31,3 +31,27 @@ def create(cls, *args, **kwargs): raise util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new edit for the provided input, instruction, and parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + api_type = kwargs.pop("api_type", None) + typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] + if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType( + "This operation is not supported by the Azure OpenAI API yet." + ) + + while True: + try: + return await super().acreate(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py index 85ede2c088..679f97973b 100644 --- a/openai/api_resources/embedding.py +++ b/openai/api_resources/embedding.py @@ -50,3 +50,42 @@ def create(cls, *args, **kwargs): raise util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new embedding for the provided input and parameters. + + See https://beta.openai.com/docs/api-reference/embeddings for a list + of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + user_provided_encoding_format = kwargs.get("encoding_format", None) + + # If encoding format was not explicitly specified, we opaquely use base64 for performance + if not user_provided_encoding_format: + kwargs["encoding_format"] = "base64" + + while True: + try: + response = await super().acreate(*args, **kwargs) + + # If a user specifies base64, we'll just return the encoded string. + # This is only for the default case. + if not user_provided_encoding_format: + for data in response.data: + + # If an engine isn't using this optimization, don't do anything + if type(data["embedding"]) == str: + data["embedding"] = np.frombuffer( + base64.b64decode(data["embedding"]), dtype="float32" + ).tolist() + + return response + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py index 11c8ec9ec9..93140819a9 100644 --- a/openai/api_resources/engine.py +++ b/openai/api_resources/engine.py @@ -27,6 +27,23 @@ def generate(self, timeout=None, **params): util.log_info("Waiting for model to warm up", error=e) + async def agenerate(self, timeout=None, **params): + start = time.time() + while True: + try: + return await self.arequest( + "post", + self.instance_url() + "/generate", + params, + stream=params.get("stream"), + plain_old_data=True, + ) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) + def search(self, **params): if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): return self.request("post", self.instance_url("http://github.com/search"), params) @@ -35,6 +52,14 @@ def search(self, **params): else: raise InvalidAPIType("Unsupported API type %s" % self.api_type) + def asearch(self, **params): + if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + return self.arequest("post", self.instance_url("http://github.com/search"), params) + elif self.typed_api_type == ApiType.OPEN_AI: + return self.arequest("post", self.instance_url() + "/search", params) + else: + raise InvalidAPIType("Unsupported API type %s" % self.api_type) + def embeddings(self, **params): warnings.warn( "Engine.embeddings is deprecated, use Embedding.create", DeprecationWarning diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index aba7117fea..3654dd2d2e 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -12,7 +12,7 @@ class File(ListableAPIResource, DeletableAPIResource): OBJECT_NAME = "files" @classmethod - def create( + def __prepare_file_create( cls, file, purpose, @@ -56,13 +56,69 @@ def create( ) else: files.append(("file", ("file", file, "application/octet-stream"))) + + return requestor, url, files + + @classmethod + def create( + cls, + file, + purpose, + model=None, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + user_provided_filename=None, + ): + requestor, url, files = cls.__prepare_file_create( + file, + purpose, + model, + api_key, + api_base, + api_type, + api_version, + organization, + user_provided_filename, + ) response, _, api_key = requestor.request("post", url, files=files) return util.convert_to_openai_object( response, api_key, api_version, organization ) @classmethod - def download( + async def acreate( + cls, + file, + purpose, + model=None, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + user_provided_filename=None, + ): + requestor, url, files = cls.__prepare_file_create( + file, + purpose, + model, + api_key, + api_base, + api_type, + api_version, + organization, + user_provided_filename, + ) + response, _, api_key = await requestor.arequest("post", url, files=files) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def __prepare_file_download( cls, id, api_key=None, @@ -84,17 +140,33 @@ def download( if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s/%s/content?api-version=%s" % ( + url = "/%s%s/%s?api-version=%s" % ( cls.azure_api_prefix, base, id, api_version, ) elif typed_api_type == ApiType.OPEN_AI: - url = f"{cls.class_url()}/{id}/content" + url = "%s/%s" % (cls.class_url(), id) else: raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return requestor, url + + @classmethod + def download( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + requestor, url = cls.__prepare_file_download( + id, api_key, api_base, api_type, api_version, organization + ) + result = requestor.request_raw("get", url) if not 200 <= result.status_code < 300: raise requestor.handle_error_response( @@ -107,25 +179,32 @@ def download( return result.content @classmethod - def find_matching_files( + async def adownload( cls, - name, - bytes, - purpose, + id, api_key=None, api_base=None, api_type=None, api_version=None, organization=None, ): - """Find already uploaded files with the same name, size, and purpose.""" - all_files = cls.list( - api_key=api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ).get("data", []) + requestor, url = cls.__prepare_file_download( + id, api_key, api_base, api_type, api_version, organization + ) + + result = await requestor.arequest_raw("get", url) + if not 200 <= result.status < 300: + raise requestor.handle_error_response( + result.content, + result.status, + json.loads(cast(bytes, result.content)), + result.headers, + stream_error=False, + ) + return result.content + + @classmethod + def __find_matching_files(cls, name, all_files, purpose): matching_files = [] basename = os.path.basename(name) for f in all_files: @@ -140,3 +219,49 @@ def find_matching_files( continue matching_files.append(f) return matching_files + + @classmethod + def find_matching_files( + cls, + name, + bytes, + purpose, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + """Find already uploaded files with the same name, size, and purpose.""" + all_files = cls.list( + api_key=api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ).get("data", []) + return cls.__find_matching_files(name, all_files, purpose) + + @classmethod + async def afind_matching_files( + cls, + name, + bytes, + purpose, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + """Find already uploaded files with the same name, size, and purpose.""" + all_files = ( + await cls.alist( + api_key=api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + ).get("data", []) + return cls.__find_matching_files(name, all_files, purpose) diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index 1b5d92d861..65dba48836 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -16,7 +16,7 @@ class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource) OBJECT_NAME = "fine-tunes" @classmethod - def cancel( + def _prepare_cancel( cls, id, api_key=None, @@ -44,10 +44,50 @@ def cancel( raise error.InvalidAPIType("Unsupported API type %s" % api_type) instance = cls(id, api_key, **params) + return instance, url + + @classmethod + def cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + instance, url = cls._prepare_cancel( + id, + api_key, + api_type, + request_id, + api_version, + **params, + ) return instance.request("post", url, request_id=request_id) @classmethod - def stream_events( + def acancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + instance, url = cls._prepare_cancel( + id, + api_key, + api_type, + request_id, + api_version, + **params, + ) + return instance.arequest("post", url, request_id=request_id) + + @classmethod + def _prepare_stream_events( cls, id, api_key=None, @@ -85,7 +125,32 @@ def stream_events( else: raise error.InvalidAPIType("Unsupported API type %s" % api_type) - response, _, api_key = requestor.request( + return requestor, url + + @classmethod + async def stream_events( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor, url = cls._prepare_stream_events( + id, + api_key, + api_base, + api_type, + request_id, + api_version, + organization, + **params, + ) + + response, _, api_key = await requestor.arequest( "get", url, params, stream=True, request_id=request_id ) diff --git a/openai/api_resources/image.py b/openai/api_resources/image.py index ebb77676df..089200015d 100644 --- a/openai/api_resources/image.py +++ b/openai/api_resources/image.py @@ -22,7 +22,12 @@ def create( return instance.request("post", cls._get_url("http://github.com/generations"), params) @classmethod - def create_variation( + def acreate(cls, **params): + instance = cls() + return instance.arequest("post", cls._get_url("http://github.com/generations"), params) + + @classmethod + def _prepare_create_variation( cls, image, api_key=None, @@ -47,6 +52,28 @@ def create_variation( for key, value in params.items(): files.append((key, (None, value))) files.append(("image", ("image", image, "application/octet-stream"))) + return requestor, url, files + + @classmethod + def create_variation( + cls, + image, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, url, files = cls._prepare_create_variation( + image, + api_key, + api_base, + api_type, + api_version, + organization, + **params, + ) response, _, api_key = requestor.request("post", url, files=files) @@ -55,7 +82,34 @@ def create_variation( ) @classmethod - def create_edit( + async def acreate_variation( + cls, + image, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, url, files = cls._prepare_create_variation( + image, + api_key, + api_base, + api_type, + api_version, + organization, + **params, + ) + + response, _, api_key = await requestor.arequest("post", url, files=files) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def _prepare_create_edit( cls, image, mask, @@ -82,9 +136,62 @@ def create_edit( files.append((key, (None, value))) files.append(("image", ("image", image, "application/octet-stream"))) files.append(("mask", ("mask", mask, "application/octet-stream"))) + return requestor, url, files + + @classmethod + def create_edit( + cls, + image, + mask, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, url, files = cls._prepare_create_edit( + image, + mask, + api_key, + api_base, + api_type, + api_version, + organization, + **params, + ) response, _, api_key = requestor.request("post", url, files=files) return util.convert_to_openai_object( response, api_key, api_version, organization ) + + @classmethod + async def acreate_edit( + cls, + image, + mask, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, url, files = cls._prepare_create_edit( + image, + mask, + api_key, + api_base, + api_type, + api_version, + organization, + **params, + ) + + response, _, api_key = await requestor.arequest("post", url, files=files) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) diff --git a/openai/api_resources/moderation.py b/openai/api_resources/moderation.py index 52f997fb26..4b8b58c6d9 100644 --- a/openai/api_resources/moderation.py +++ b/openai/api_resources/moderation.py @@ -11,7 +11,7 @@ def get_url(http://github.com/self): return "/moderations" @classmethod - def create(cls, input: Union[str, List[str]], model: Optional[str] = None, api_key: Optional[str] = None): + def _prepare_create(cls, input, model, api_key): if model is not None and model not in cls.VALID_MODEL_NAMES: raise ValueError( f"The parameter model should be chosen from {cls.VALID_MODEL_NAMES} " @@ -22,4 +22,24 @@ def create(cls, input: Union[str, List[str]], model: Optional[str] = None, api_k params = {"input": input} if model is not None: params["model"] = model + return instance, params + + @classmethod + def create( + cls, + input: Union[str, List[str]], + model: Optional[str] = None, + api_key: Optional[str] = None, + ): + instance, params = cls._prepare_create(input, model, api_key) return instance.request("post", cls.get_url(), params) + + @classmethod + def acreate( + cls, + input: Union[str, List[str]], + model: Optional[str] = None, + api_key: Optional[str] = None, + ): + instance, params = cls._prepare_create(input, model, api_key) + return instance.arequest("post", cls.get_url(), params) diff --git a/openai/api_resources/search.py b/openai/api_resources/search.py index adc113c1c4..0f9cdab604 100644 --- a/openai/api_resources/search.py +++ b/openai/api_resources/search.py @@ -28,3 +28,24 @@ def create(cls, *args, **kwargs): raise util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new search for the provided input and parameters. + + See https://beta.openai.com/docs/api-reference/search for a list + of valid parameters. + """ + + start = time.time() + timeout = kwargs.pop("timeout", None) + + while True: + try: + return await super().acreate(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index 47a04e6582..c4e8a2f448 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -23,6 +23,19 @@ def get_embedding(text: str, engine="text-similarity-davinci-001") -> List[float return openai.Embedding.create(input=[text], engine=engine)["data"][0]["embedding"] +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) +async def aget_embedding( + text: str, engine="text-similarity-davinci-001" +) -> List[float]: + + # replace newlines, which can negatively affect performance. + text = text.replace("\n", " ") + + return (await openai.Embedding.acreate(input=[text], engine=engine))["data"][0][ + "embedding" + ] + + @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) def get_embeddings( list_of_text: List[str], engine="text-similarity-babbage-001" @@ -37,6 +50,20 @@ def get_embeddings( return [d["embedding"] for d in data] +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) +async def aget_embeddings( + list_of_text: List[str], engine="text-similarity-babbage-001" +) -> List[List[float]]: + assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048." + + # replace newlines, which can negatively affect performance. + list_of_text = [text.replace("\n", " ") for text in list_of_text] + + data = (await openai.Embedding.acreate(input=list_of_text, engine=engine)).data + data = sorted(data, key=lambda x: x["index"]) # maintain the same order as input. + return [d["embedding"] for d in data] + + def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) diff --git a/openai/openai_object.py b/openai/openai_object.py index 5bfa29e45f..c0af6bbc2a 100644 --- a/openai/openai_object.py +++ b/openai/openai_object.py @@ -207,6 +207,57 @@ def request( plain_old_data=plain_old_data, ) + async def arequest( + self, + method, + url, + params=None, + headers=None, + stream=False, + plain_old_data=False, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ): + if params is None: + params = self._retrieve_params + requestor = api_requestor.APIRequestor( + key=self.api_key, + api_base=self.api_base_override or self.api_base(), + api_type=self.api_type, + api_version=self.api_version, + organization=self.organization, + ) + response, stream, api_key = await requestor.arequest( + method, + url, + params=params, + stream=stream, + headers=headers, + request_id=request_id, + request_timeout=request_timeout, + ) + + if stream: + assert not isinstance(response, OpenAIResponse) # must be an iterator + return ( + util.convert_to_openai_object( + line, + api_key, + self.api_version, + self.organization, + plain_old_data=plain_old_data, + ) + for line in response + ) + else: + return util.convert_to_openai_object( + response, + api_key, + self.api_version, + self.organization, + plain_old_data=plain_old_data, + ) + def __repr__(self): ident_parts = [type(self).__name__] diff --git a/openai/tests/__init__.py b/openai/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openai/tests/asyncio/__init__.py b/openai/tests/asyncio/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openai/tests/asyncio/test_endpoints.py b/openai/tests/asyncio/test_endpoints.py new file mode 100644 index 0000000000..e5c6d012cd --- /dev/null +++ b/openai/tests/asyncio/test_endpoints.py @@ -0,0 +1,65 @@ +import io +import json + +import pytest + +import openai +from openai import error + + +pytestmark = [pytest.mark.asyncio] + + +# FILE TESTS +async def test_file_upload(): + result = await openai.File.acreate( + file=io.StringIO(json.dumps({"text": "test file data"})), + purpose="search", + ) + assert result.purpose == "search" + assert "id" in result + + result = await openai.File.aretrieve(id=result.id) + assert result.status == "uploaded" + + +# COMPLETION TESTS +async def test_completions(): + result = await openai.Completion.acreate( + prompt="This was a test", n=5, engine="ada" + ) + assert len(result.choices) == 5 + + +async def test_completions_multiple_prompts(): + result = await openai.Completion.acreate( + prompt=["This was a test", "This was another test"], n=5, engine="ada" + ) + assert len(result.choices) == 10 + + +async def test_completions_model(): + result = await openai.Completion.acreate(prompt="This was a test", n=5, model="ada") + assert len(result.choices) == 5 + assert result.model.startswith("ada") + + +async def test_timeout_raises_error(): + # A query that should take awhile to return + with pytest.raises(error.Timeout): + await openai.Completion.acreate( + prompt="test" * 1000, + n=10, + model="ada", + max_tokens=100, + request_timeout=0.01, + ) + + +async def test_timeout_does_not_error(): + # A query that should be fast + await openai.Completion.acreate( + prompt="test", + model="ada", + request_timeout=10, + ) diff --git a/openai/tests/test_long_examples_validator.py b/openai/tests/test_long_examples_validator.py index 7f3e4c8cf1..6346b25a02 100644 --- a/openai/tests/test_long_examples_validator.py +++ b/openai/tests/test_long_examples_validator.py @@ -20,29 +20,28 @@ def test_long_examples_validator() -> None: # the order of these matters unprepared_training_data = [ {"prompt": long_prompt, "completion": long_completion}, # 1 of 2 duplicates - {"prompt": short_prompt, "completion": short_completion}, + {"prompt": short_prompt, "completion": short_completion}, {"prompt": long_prompt, "completion": long_completion}, # 2 of 2 duplicates - ] with NamedTemporaryFile(suffix="jsonl", mode="w") as training_data: for prompt_completion_row in unprepared_training_data: training_data.write(json.dumps(prompt_completion_row) + "\n") training_data.flush() - + prepared_data_cmd_output = subprocess.run( - [f"openai tools fine_tunes.prepare_data -f {training_data.name}"], - stdout=subprocess.PIPE, - text=True, + [f"openai tools fine_tunes.prepare_data -f {training_data.name}"], + stdout=subprocess.PIPE, + text=True, input="y\ny\ny\ny\ny", # apply all recommendations, one at a time stderr=subprocess.PIPE, encoding="utf-8", - shell=True + shell=True, ) # validate data was prepared successfully - assert prepared_data_cmd_output.stderr == "" + assert prepared_data_cmd_output.stderr == "" # validate get_long_indexes() applied during optional_fn() call in long_examples_validator() assert "indices of the long examples has changed" in prepared_data_cmd_output.stdout - - return prepared_data_cmd_output.stdout \ No newline at end of file + + return prepared_data_cmd_output.stdout diff --git a/setup.py b/setup.py index 9b318d326e..aa112f7931 100644 --- a/setup.py +++ b/setup.py @@ -26,9 +26,10 @@ "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format "numpy", 'typing_extensions;python_version<"3.8"', # Needed for type hints for mypy + "aiohttp", # Needed for async support ], extras_require={ - "dev": ["black~=21.6b0", "pytest==6.*"], + "dev": ["black~=21.6b0", "pytest==6.*", "pytest-asyncio", "pytest-mock"], "wandb": ["wandb"], "embeddings": [ "scikit-learn>=1.0.2", # Needed for embedding utils, versions >= 1.1 require python 3.8 From fb4b672ac9c85568d1fe0e18c3e424b3614c7782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCrtz?= <120655914+cmurtz-msft@users.noreply.github.com> Date: Fri, 6 Jan 2023 20:32:02 +0100 Subject: [PATCH 058/914] Add deployments cli cmd (#165) --- openai/cli.py | 39 +++++++++++++++++++++++++++++++++++++++ openai/object_classes.py | 1 + 2 files changed, 40 insertions(+) diff --git a/openai/cli.py b/openai/cli.py index de2aca8bd6..208bc27c5f 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -183,6 +183,28 @@ def create(cls, args): sys.stdout.flush() +class Deployment: + @classmethod + def get(cls, args): + resp = openai.Deployment.retrieve(id=args.id) + print(resp) + + @classmethod + def delete(cls, args): + model = openai.Deployment.delete(args.id) + print(model) + + @classmethod + def list(cls, args): + models = openai.Deployment.list() + print(models) + + @classmethod + def create(cls, args): + models = openai.Deployment.create(model=args.model, scale_settings={"scale_type": args.scale_type}) + print(models) + + class Model: @classmethod def get(cls, args): @@ -824,6 +846,23 @@ def help(args): ) sub.set_defaults(func=Completion.create) + # Deployments + sub = subparsers.add_parser("deployments.list") + sub.set_defaults(func=Deployment.list) + + sub = subparsers.add_parser("deployments.get") + sub.add_argument("-i", "--id", required=True, help="The deployment ID") + sub.set_defaults(func=Deployment.get) + + sub = subparsers.add_parser("deployments.delete") + sub.add_argument("-i", "--id", required=True, help="The deployment ID") + sub.set_defaults(func=Deployment.delete) + + sub = subparsers.add_parser("deployments.create") + sub.add_argument("-m", "--model", required=True, help="The model ID") + sub.add_argument("-s", "--scale_type", required=True, help="The scale type. Either 'manual' or 'standard'") + sub.set_defaults(func=Deployment.create) + # Models sub = subparsers.add_parser("models.list") sub.set_defaults(func=Model.list) diff --git a/openai/object_classes.py b/openai/object_classes.py index 76e8c0fe1b..5f72bd7cf8 100644 --- a/openai/object_classes.py +++ b/openai/object_classes.py @@ -7,4 +7,5 @@ "file": api_resources.File, "fine-tune": api_resources.FineTune, "model": api_resources.Model, + "deployment": api_resources.Deployment, } From 7af43ce02c199cdadd4944d151ce32d0a0fc3e56 Mon Sep 17 00:00:00 2001 From: Damien Deville Date: Fri, 6 Jan 2023 12:15:42 -0800 Subject: [PATCH 059/914] Fix API requestor hanging when not using a global session (#167) --- openai/api_requestor.py | 54 ++++++++++++++++++++++++------------ openai/api_resources/file.py | 21 +++++++------- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index b10730216d..1961e1d093 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -4,8 +4,18 @@ import sys import threading import warnings +from contextlib import asynccontextmanager from json import JSONDecodeError -from typing import AsyncGenerator, Dict, Iterator, Optional, Tuple, Union, overload +from typing import ( + AsyncGenerator, + AsyncIterator, + Dict, + Iterator, + Optional, + Tuple, + Union, + overload, +) from urllib.parse import urlencode, urlsplit, urlunsplit import aiohttp @@ -284,17 +294,19 @@ async def arequest( request_id: Optional[str] = None, request_timeout: Optional[Union[float, Tuple[float, float]]] = None, ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: - result = await self.arequest_raw( - method.lower(), - url, - params=params, - supplied_headers=headers, - files=files, - request_id=request_id, - request_timeout=request_timeout, - ) - resp, got_stream = await self._interpret_async_response(result, stream) - return resp, got_stream, self.api_key + async with aiohttp_session() as session: + result = await self.arequest_raw( + method.lower(), + url, + session, + params=params, + supplied_headers=headers, + files=files, + request_id=request_id, + request_timeout=request_timeout, + ) + resp, got_stream = await self._interpret_async_response(result, stream) + return resp, got_stream, self.api_key def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): try: @@ -514,6 +526,7 @@ async def arequest_raw( self, method, url, + session, *, params=None, supplied_headers: Optional[Dict[str, str]] = None, @@ -534,7 +547,6 @@ async def arequest_raw( timeout = aiohttp.ClientTimeout( total=request_timeout if request_timeout else TIMEOUT_SECS ) - user_set_session = openai.aiosession.get() if files: # TODO: Use `aiohttp.MultipartWriter` to create the multipart form data here. @@ -552,11 +564,7 @@ async def arequest_raw( "timeout": timeout, } try: - if user_set_session: - result = await user_set_session.request(**request_kwargs) - else: - async with aiohttp.ClientSession() as session: - result = await session.request(**request_kwargs) + result = await session.request(**request_kwargs) util.log_info( "OpenAI API response", path=abs_url, @@ -648,3 +656,13 @@ def _interpret_response_line( rbody, rcode, resp.data, rheaders, stream_error=stream_error ) return resp + + +@asynccontextmanager +async def aiohttp_session() -> AsyncIterator[aiohttp.ClientSession]: + user_set_session = openai.aiosession.get() + if user_set_session: + yield user_set_session + else: + async with aiohttp.ClientSession() as session: + yield session diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 3654dd2d2e..80b989ada1 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -192,16 +192,17 @@ async def adownload( id, api_key, api_base, api_type, api_version, organization ) - result = await requestor.arequest_raw("get", url) - if not 200 <= result.status < 300: - raise requestor.handle_error_response( - result.content, - result.status, - json.loads(cast(bytes, result.content)), - result.headers, - stream_error=False, - ) - return result.content + async with api_requestor.aiohttp_session() as session: + result = await requestor.arequest_raw("get", url, session) + if not 200 <= result.status < 300: + raise requestor.handle_error_response( + result.content, + result.status, + json.loads(cast(bytes, result.content)), + result.headers, + stream_error=False, + ) + return result.content @classmethod def __find_matching_files(cls, name, all_files, purpose): From 9678e1547a069760645f02de62cca442de0d7af7 Mon Sep 17 00:00:00 2001 From: Greg Brockman Date: Fri, 6 Jan 2023 12:19:38 -0800 Subject: [PATCH 060/914] Including the underlying errors in the error message for Timeout & APIConnectionError (#137) Many libraries will only show the error string of the raised error, not displaying its cause. Prior to this path, this would mean that the many different kinds of connection errors get translated into a single opaque "Error communicating with OpenAI". --- openai/api_requestor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 1961e1d093..a34ee281ec 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -505,9 +505,9 @@ def request_raw( timeout=request_timeout if request_timeout else TIMEOUT_SECS, ) except requests.exceptions.Timeout as e: - raise error.Timeout("Request timed out") from e + raise error.Timeout("Request timed out: {}".format(e)) from e except requests.exceptions.RequestException as e: - raise error.APIConnectionError("Error communicating with OpenAI") from e + raise error.APIConnectionError("Error communicating with OpenAI: {}".format(e)) from e util.log_info( "OpenAI API response", path=abs_url, From ede0882939656ce4289cb4f61142e7658bb2dec7 Mon Sep 17 00:00:00 2001 From: Jakub Roztocil Date: Fri, 6 Jan 2023 12:23:29 -0800 Subject: [PATCH 061/914] Make `numpy` and `pandas` optional for ~7 times smaller deps (#153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make `numpy` and `pandas` optional dependencies * Cleanup * Cleanup * Cleanup * Cleanup * Cleanup * Cleanup * Move `openpyxl` to `datalib` extras * Improve errors and instructions * Add “Optional dependencies” to README * Polish README.md * Polish README.md Co-authored-by: hallacy --- README.md | 20 +++++++ openai/api_resources/embedding.py | 4 +- openai/datalib.py | 56 ++++++++++++++++++++ openai/embeddings_utils.py | 4 +- openai/tests/test_long_examples_validator.py | 9 +++- openai/validators.py | 3 +- openai/wandb_logger.py | 5 +- setup.py | 20 +++++-- 8 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 openai/datalib.py diff --git a/README.md b/README.md index 53bab3ab2a..61f38a2afb 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,26 @@ Install from source with: python setup.py install ``` +### Optional dependencies + +Install dependencies for [`openapi.embeddings_utils`](openai/embeddings_utils.py): + +```sh +pip install openai[embeddings] +``` + +Install support for [Weights & Biases](https://wandb.me/openai-docs): + +``` +pip install openai[wandb] +``` + +Data libraries like `numpy` and `pandas` are not installed by default due to their size. They’re needed for some functionality of this library, but generally not for talking to the API. If you encounter a `MissingDependencyError`, install them with: + +```sh +pip install openai[datalib] +```` + ## Usage The library needs to be configured with your account's secret key which is available on the [website](https://beta.openai.com/account/api-keys). Either set it as the `OPENAI_API_KEY` environment variable before using the library: diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py index 679f97973b..5f1cfe5609 100644 --- a/openai/api_resources/embedding.py +++ b/openai/api_resources/embedding.py @@ -1,11 +1,10 @@ import base64 import time -import numpy as np from openai import util -from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.datalib import numpy as np, assert_has_numpy from openai.error import TryAgain @@ -40,6 +39,7 @@ def create(cls, *args, **kwargs): # If an engine isn't using this optimization, don't do anything if type(data["embedding"]) == str: + assert_has_numpy() data["embedding"] = np.frombuffer( base64.b64decode(data["embedding"]), dtype="float32" ).tolist() diff --git a/openai/datalib.py b/openai/datalib.py new file mode 100644 index 0000000000..2781cfc4db --- /dev/null +++ b/openai/datalib.py @@ -0,0 +1,56 @@ +""" +This module helps make data libraries like `numpy` and `pandas` optional dependencies. + +The libraries add up to 130MB+, which makes it challenging to deploy applications +using this library in environments with code size constraints, like AWS Lambda. + +This module serves as an import proxy and provides a few utilities for dealing with the optionality. + +Since the primary use case of this library (talking to the OpenAI API) doesn’t generally require data libraries, +it’s safe to make them optional. The rare case when data libraries are needed in the client is handled through +assertions with instructive error messages. + +See also `setup.py`. + +""" +try: + import numpy +except ImportError: + numpy = None + +try: + import pandas +except ImportError: + pandas = None + +HAS_NUMPY = bool(numpy) +HAS_PANDAS = bool(pandas) + +INSTRUCTIONS = """ + +OpenAI error: + + missing `{library}` + +This feature requires additional dependencies: + + $ pip install openai[datalib] + +""" + +NUMPY_INSTRUCTIONS = INSTRUCTIONS.format(library="numpy") +PANDAS_INSTRUCTIONS = INSTRUCTIONS.format(library="pandas") + + +class MissingDependencyError(Exception): + pass + + +def assert_has_numpy(): + if not HAS_NUMPY: + raise MissingDependencyError(NUMPY_INSTRUCTIONS) + + +def assert_has_pandas(): + if not HAS_PANDAS: + raise MissingDependencyError(PANDAS_INSTRUCTIONS) diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index c4e8a2f448..056c2065c1 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -2,8 +2,6 @@ from typing import List, Optional import matplotlib.pyplot as plt -import numpy as np -import pandas as pd import plotly.express as px from scipy import spatial from sklearn.decomposition import PCA @@ -12,6 +10,8 @@ from tenacity import retry, stop_after_attempt, wait_random_exponential import openai +from openai.datalib import numpy as np +from openai.datalib import pandas as pd @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) diff --git a/openai/tests/test_long_examples_validator.py b/openai/tests/test_long_examples_validator.py index 6346b25a02..a9334d4f75 100644 --- a/openai/tests/test_long_examples_validator.py +++ b/openai/tests/test_long_examples_validator.py @@ -2,9 +2,14 @@ import subprocess from tempfile import NamedTemporaryFile +import pytest + +from openai.datalib import HAS_PANDAS, HAS_NUMPY, NUMPY_INSTRUCTIONS, PANDAS_INSTRUCTIONS -def test_long_examples_validator() -> None: +@pytest.mark.skipif(not HAS_PANDAS, reason=PANDAS_INSTRUCTIONS) +@pytest.mark.skipif(not HAS_NUMPY, reason=NUMPY_INSTRUCTIONS) +def test_long_examples_validator() -> None: """ Ensures that long_examples_validator() handles previously applied recommendations, namely dropped duplicates, without resulting in a KeyError. @@ -43,5 +48,5 @@ def test_long_examples_validator() -> None: assert prepared_data_cmd_output.stderr == "" # validate get_long_indexes() applied during optional_fn() call in long_examples_validator() assert "indices of the long examples has changed" in prepared_data_cmd_output.stdout - + return prepared_data_cmd_output.stdout diff --git a/openai/validators.py b/openai/validators.py index 23ff525495..0329ed5c7d 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -2,7 +2,7 @@ import sys from typing import Any, Callable, NamedTuple, Optional -import pandas as pd +from openai.datalib import pandas as pd, assert_has_pandas class Remediation(NamedTuple): @@ -474,6 +474,7 @@ def read_any_format(fname, fields=["prompt", "completion"]): - for .xlsx it will read the first sheet - for .txt it will assume completions and split on newline """ + assert_has_pandas() remediation = None necessary_msg = None immediate_msg = None diff --git a/openai/wandb_logger.py b/openai/wandb_logger.py index 6dd7614ca2..ba650d1fe4 100644 --- a/openai/wandb_logger.py +++ b/openai/wandb_logger.py @@ -13,10 +13,9 @@ import re from pathlib import Path - import numpy as np - import pandas as pd - from openai import File, FineTune + from openai.datalib import numpy as np + from openai.datalib import pandas as pd class WandbLogger: diff --git a/setup.py b/setup.py index aa112f7931..e431d26ccd 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,15 @@ with open("README.md", "r") as fh: long_description = fh.read() + +DATA_LIBRARIES = [ + # These libraries are optional because of their size. See `openai/datalib.py`. + "numpy", + "pandas>=1.2.3", # Needed for CLI fine-tuning data preparation tool + "pandas-stubs>=1.1.0.11", # Needed for type hints for mypy + "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format +] + setup( name="openai", description="Python client library for the OpenAI API", @@ -21,22 +30,23 @@ install_requires=[ "requests>=2.20", # to get the patch for CVE-2018-18074 "tqdm", # Needed for progress bars - "pandas>=1.2.3", # Needed for CLI fine-tuning data preparation tool - "pandas-stubs>=1.1.0.11", # Needed for type hints for mypy - "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format - "numpy", 'typing_extensions;python_version<"3.8"', # Needed for type hints for mypy "aiohttp", # Needed for async support ], extras_require={ "dev": ["black~=21.6b0", "pytest==6.*", "pytest-asyncio", "pytest-mock"], - "wandb": ["wandb"], + "datalib": DATA_LIBRARIES, + "wandb": [ + "wandb", + *DATA_LIBRARIES, + ], "embeddings": [ "scikit-learn>=1.0.2", # Needed for embedding utils, versions >= 1.1 require python 3.8 "tenacity>=8.0.1", "matplotlib", "sklearn", "plotly", + *DATA_LIBRARIES, ], }, python_requires=">=3.7.1", From 3afcfb276dabd309fd3f6a05e60d6c89a130f5fb Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 6 Jan 2023 12:27:49 -0800 Subject: [PATCH 062/914] Allow passing of api_key in image.create (#163) * Allow passing of api_key in image.create * Add async support --- openai/api_resources/image.py | 54 +++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/openai/api_resources/image.py b/openai/api_resources/image.py index 089200015d..abd32c3f8b 100644 --- a/openai/api_resources/image.py +++ b/openai/api_resources/image.py @@ -16,15 +16,59 @@ def _get_url(http://github.com/cls,%20action): @classmethod def create( cls, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, **params, ): - instance = cls() - return instance.request("post", cls._get_url("http://github.com/generations"), params) + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + + _, api_version = cls._get_api_type_and_version(api_type, api_version) + + response, _, api_key = requestor.request( + "post", cls._get_url("http://github.com/generations"), params + ) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) @classmethod - def acreate(cls, **params): - instance = cls() - return instance.arequest("post", cls._get_url("http://github.com/generations"), params) + async def acreate( + cls, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + + _, api_version = cls._get_api_type_and_version(api_type, api_version) + + response, _, api_key = await requestor.arequest( + "post", cls._get_url("http://github.com/generations"), params + ) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) @classmethod def _prepare_create_variation( From a7305833040804f9d071c401e57557ae5ed0d691 Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 6 Jan 2023 12:40:33 -0800 Subject: [PATCH 063/914] Remove search, answers, classifications (#154) (#168) * Remove search, answers, classifications * mypy * Update test with new format --- openai/__init__.py | 6 - openai/api_resources/__init__.py | 3 - openai/api_resources/answer.py | 17 --- openai/api_resources/classification.py | 17 --- openai/api_resources/engine.py | 19 +-- openai/api_resources/file.py | 2 - openai/api_resources/search.py | 51 ------- openai/cli.py | 197 ------------------------- openai/tests/test_endpoints.py | 8 +- openai/validators.py | 41 ----- 10 files changed, 6 insertions(+), 355 deletions(-) delete mode 100644 openai/api_resources/answer.py delete mode 100644 openai/api_resources/classification.py delete mode 100644 openai/api_resources/search.py diff --git a/openai/__init__.py b/openai/__init__.py index ef6da5ba58..879fe33d04 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -7,8 +7,6 @@ from typing import Optional, TYPE_CHECKING from openai.api_resources import ( - Answer, - Classification, Completion, Customer, Edit, @@ -21,7 +19,6 @@ Image, Model, Moderation, - Search, ) from openai.error import APIError, InvalidRequestError, OpenAIError @@ -55,8 +52,6 @@ __all__ = [ "APIError", - "Answer", - "Classification", "Completion", "Customer", "Edit", @@ -71,7 +66,6 @@ "Model", "Moderation", "OpenAIError", - "Search", "api_base", "api_key", "api_type", diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index 26fa63c525..4692b9ac3f 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -1,5 +1,3 @@ -from openai.api_resources.answer import Answer # noqa: F401 -from openai.api_resources.classification import Classification # noqa: F401 from openai.api_resources.completion import Completion # noqa: F401 from openai.api_resources.customer import Customer # noqa: F401 from openai.api_resources.deployment import Deployment # noqa: F401 @@ -12,4 +10,3 @@ from openai.api_resources.image import Image # noqa: F401 from openai.api_resources.model import Model # noqa: F401 from openai.api_resources.moderation import Moderation # noqa: F401 -from openai.api_resources.search import Search # noqa: F401 diff --git a/openai/api_resources/answer.py b/openai/api_resources/answer.py deleted file mode 100644 index be8c4f1ac8..0000000000 --- a/openai/api_resources/answer.py +++ /dev/null @@ -1,17 +0,0 @@ -from openai.openai_object import OpenAIObject - - -class Answer(OpenAIObject): - @classmethod - def get_url(http://github.com/self): - return "/answers" - - @classmethod - def create(cls, **params): - instance = cls() - return instance.request("post", cls.get_url(), params) - - @classmethod - def acreate(cls, **params): - instance = cls() - return instance.arequest("post", cls.get_url(), params) diff --git a/openai/api_resources/classification.py b/openai/api_resources/classification.py deleted file mode 100644 index 823f521b96..0000000000 --- a/openai/api_resources/classification.py +++ /dev/null @@ -1,17 +0,0 @@ -from openai.openai_object import OpenAIObject - - -class Classification(OpenAIObject): - @classmethod - def get_url(http://github.com/self): - return "/classifications" - - @classmethod - def create(cls, **params): - instance = cls() - return instance.request("post", cls.get_url(), params) - - @classmethod - def acreate(cls, **params): - instance = cls() - return instance.arequest("post", cls.get_url(), params) diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py index 93140819a9..5a0c467c2f 100644 --- a/openai/api_resources/engine.py +++ b/openai/api_resources/engine.py @@ -3,8 +3,7 @@ from openai import util from openai.api_resources.abstract import ListableAPIResource, UpdateableAPIResource -from openai.error import InvalidAPIType, TryAgain -from openai.util import ApiType +from openai.error import TryAgain class Engine(ListableAPIResource, UpdateableAPIResource): @@ -44,22 +43,6 @@ async def agenerate(self, timeout=None, **params): util.log_info("Waiting for model to warm up", error=e) - def search(self, **params): - if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - return self.request("post", self.instance_url("http://github.com/search"), params) - elif self.typed_api_type == ApiType.OPEN_AI: - return self.request("post", self.instance_url() + "/search", params) - else: - raise InvalidAPIType("Unsupported API type %s" % self.api_type) - - def asearch(self, **params): - if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - return self.arequest("post", self.instance_url("http://github.com/search"), params) - elif self.typed_api_type == ApiType.OPEN_AI: - return self.arequest("post", self.instance_url() + "/search", params) - else: - raise InvalidAPIType("Unsupported API type %s" % self.api_type) - def embeddings(self, **params): warnings.warn( "Engine.embeddings is deprecated, use Embedding.create", DeprecationWarning diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 80b989ada1..55c733512b 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -24,8 +24,6 @@ def __prepare_file_create( organization=None, user_provided_filename=None, ): - if purpose != "search" and model is not None: - raise ValueError("'model' is only meaningful if 'purpose' is 'search'") requestor = api_requestor.APIRequestor( api_key, api_base=api_base or openai.api_base, diff --git a/openai/api_resources/search.py b/openai/api_resources/search.py deleted file mode 100644 index 0f9cdab604..0000000000 --- a/openai/api_resources/search.py +++ /dev/null @@ -1,51 +0,0 @@ -import time - -from openai import util -from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import TryAgain - - -class Search(EngineAPIResource): - OBJECT_NAME = "search" - - @classmethod - def create(cls, *args, **kwargs): - """ - Creates a new search for the provided input and parameters. - - See https://beta.openai.com/docs/api-reference/search for a list - of valid parameters. - """ - - start = time.time() - timeout = kwargs.pop("timeout", None) - - while True: - try: - return super().create(*args, **kwargs) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) - - @classmethod - async def acreate(cls, *args, **kwargs): - """ - Creates a new search for the provided input and parameters. - - See https://beta.openai.com/docs/api-reference/search for a list - of valid parameters. - """ - - start = time.time() - timeout = kwargs.pop("timeout", None) - - while True: - try: - return await super().acreate(*args, **kwargs) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/cli.py b/openai/cli.py index 208bc27c5f..eaa49c713a 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -3,7 +3,6 @@ import signal import sys import warnings -from functools import partial from typing import Optional import requests @@ -13,11 +12,9 @@ from openai.validators import ( apply_necessary_remediation, apply_validators, - get_search_validators, get_validators, read_any_format, write_out_file, - write_out_search_file, ) @@ -105,40 +102,6 @@ def generate(cls, args): sys.stdout.write("\n") sys.stdout.flush() - @classmethod - def search(cls, args): - params = { - "query": args.query, - "max_rerank": args.max_rerank, - "return_metadata": args.return_metadata, - } - if args.documents: - params["documents"] = args.documents - if args.file: - params["file"] = args.file - - if args.version: - params["version"] = args.version - - resp = openai.Engine(id=args.id).search(**params) - scores = [ - (search_result["score"], search_result["document"]) - for search_result in resp["data"] - ] - scores.sort(reverse=True) - dataset = ( - args.documents if args.documents else [x["text"] for x in resp["data"]] - ) - for score, document_idx in scores: - print("=== score {:.3f} ===".format(score)) - print(dataset[document_idx]) - if ( - args.return_metadata - and args.file - and "metadata" in resp["data"][document_idx] - ): - print(f"METADATA: {resp['data'][document_idx]['metadata']}") - @classmethod def list(cls, args): engines = openai.Engine.list() @@ -230,7 +193,6 @@ def create(cls, args): resp = openai.File.create( file=buffer_reader, purpose=args.purpose, - model=args.model, user_provided_filename=args.file, ) print(resp) @@ -291,51 +253,6 @@ def create_edit(cls, args): print(resp) -class Search: - @classmethod - def prepare_data(cls, args, purpose): - - sys.stdout.write("Analyzing...\n") - fname = args.file - auto_accept = args.quiet - - optional_fields = ["metadata"] - - if purpose == "classifications": - required_fields = ["text", "label"] - else: - required_fields = ["text"] - - df, remediation = read_any_format( - fname, fields=required_fields + optional_fields - ) - - if "metadata" not in df: - df["metadata"] = None - - apply_necessary_remediation(None, remediation) - validators = get_search_validators(required_fields, optional_fields) - - write_out_file_func = partial( - write_out_search_file, - purpose=purpose, - fields=required_fields + optional_fields, - ) - - apply_validators( - df, fname, remediation, validators, auto_accept, write_out_file_func - ) - - @classmethod - def create(cls, args): - resp = openai.Search.create( - query=args.query, - documents=args.documents, - model=args.model, - ) - print(resp) - - class FineTune: @classmethod def list(cls, args): @@ -642,57 +559,6 @@ def help(args): ) sub.set_defaults(func=FineTune.prepare_data) - sub = subparsers.add_parser("search.prepare_data") - sub.add_argument( - "-f", - "--file", - required=True, - help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing text examples to be analyzed." - "This should be the local file path.", - ) - sub.add_argument( - "-q", - "--quiet", - required=False, - action="http://github.com/store_true", - help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", - ) - sub.set_defaults(func=partial(Search.prepare_data, purpose="search")) - - sub = subparsers.add_parser("classifications.prepare_data") - sub.add_argument( - "-f", - "--file", - required=True, - help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing text-label examples to be analyzed." - "This should be the local file path.", - ) - sub.add_argument( - "-q", - "--quiet", - required=False, - action="http://github.com/store_true", - help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", - ) - sub.set_defaults(func=partial(Search.prepare_data, purpose="classifications")) - - sub = subparsers.add_parser("answers.prepare_data") - sub.add_argument( - "-f", - "--file", - required=True, - help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing text examples to be analyzed." - "This should be the local file path.", - ) - sub.add_argument( - "-q", - "--quiet", - required=False, - action="http://github.com/store_true", - help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", - ) - sub.set_defaults(func=partial(Search.prepare_data, purpose="answer")) - def api_register(parser): # Engine management @@ -760,41 +626,6 @@ def help(args): ) sub.set_defaults(func=Engine.generate) - sub = subparsers.add_parser("engines.search") - sub.add_argument("-i", "--id", required=True) - sub.add_argument( - "-d", - "--documents", - action="http://github.com/append", - help="List of documents to search over. Only one of `documents` or `file` may be supplied.", - required=False, - ) - sub.add_argument( - "-f", - "--file", - help="A file id to search over. Only one of `documents` or `file` may be supplied.", - required=False, - ) - sub.add_argument( - "--max_rerank", - help="The maximum number of documents to be re-ranked and returned by search. This flag only takes effect when `file` is set.", - type=int, - default=200, - ) - sub.add_argument( - "--return_metadata", - help="A special boolean flag for showing metadata. If set `true`, each document entry in the returned json will contain a 'metadata' field. Default to be `false`. This flag only takes effect when `file` is set.", - type=bool, - default=False, - ) - sub.add_argument( - "--version", - help="The version of the search routing to use", - ) - - sub.add_argument("-q", "--query", required=True, help="Search query") - sub.set_defaults(func=Engine.search) - # Completions sub = subparsers.add_parser("completions.create") sub.add_argument( @@ -890,11 +721,6 @@ def help(args): help="Why are you uploading this file? (see https://beta.openai.com/docs/api-reference/ for purposes)", required=True, ) - sub.add_argument( - "-m", - "--model", - help="Model for search indexing (e.g. 'ada'). Only meaningful if --purpose is 'search'.", - ) sub.set_defaults(func=File.create) sub = subparsers.add_parser("files.get") @@ -908,29 +734,6 @@ def help(args): sub = subparsers.add_parser("files.list") sub.set_defaults(func=File.list) - # Search - sub = subparsers.add_parser("search.create") - - sub.add_argument( - "-d", - "--documents", - help="Documents to search over", - type=str, - nargs="+", - ) - sub.add_argument( - "-q", - "--query", - required=True, - help="Search query", - ) - sub.add_argument( - "-m", - "--model", - help="The model to search with", - ) - sub.set_defaults(func=Search.create) - # Finetune sub = subparsers.add_parser("fine_tunes.list") sub.set_defaults(func=FineTune.list) diff --git a/openai/tests/test_endpoints.py b/openai/tests/test_endpoints.py index e5c466add1..565bd41880 100644 --- a/openai/tests/test_endpoints.py +++ b/openai/tests/test_endpoints.py @@ -10,10 +10,12 @@ # FILE TESTS def test_file_upload(): result = openai.File.create( - file=io.StringIO(json.dumps({"text": "test file data"})), - purpose="search", + file=io.StringIO( + json.dumps({"prompt": "test file data", "completion": "tada"}) + ), + purpose="fine-tune", ) - assert result.purpose == "search" + assert result.purpose == "fine-tune" assert "id" in result result = openai.File.retrieve(id=result.id) diff --git a/openai/validators.py b/openai/validators.py index 0329ed5c7d..146e97d9fb 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -718,30 +718,6 @@ def write_out_file(df, fname, any_remediations, auto_accept): sys.stdout.write("Aborting... did not write the file\n") -def write_out_search_file(df, fname, any_remediations, auto_accept, fields, purpose): - """ - This function will write out a dataframe to a file, if the user would like to proceed. - """ - input_text = "\n\nYour data will be written to a new JSONL file. Proceed [Y/n]: " - - if not any_remediations: - sys.stdout.write( - f'\nYou can upload your file:\n> openai api files.create -f "{fname}" -p {purpose}' - ) - - elif accept_suggestion(input_text, auto_accept): - fnames = get_outfnames(fname, split=False) - - assert len(fnames) == 1 - df[fields].to_json(fnames[0], lines=True, orient="records", force_ascii=False) - - sys.stdout.write( - f'\nWrote modified file to {fnames[0]}`\nFeel free to take a look!\n\nNow upload that file:\n> openai api files.create -f "{fnames[0]}" -p {purpose}' - ) - else: - sys.stdout.write("Aborting... did not write the file\n") - - def infer_task_type(df): """ Infer the likely fine-tuning task type from the data @@ -800,23 +776,6 @@ def get_validators(): ] -def get_search_validators(required_fields, optional_fields): - validators = [ - lambda x: necessary_column_validator(x, field) for field in required_fields - ] - validators += [ - lambda x: non_empty_field_validator(x, field) for field in required_fields - ] - validators += [lambda x: duplicated_rows_validator(x, required_fields)] - validators += [ - lambda x: additional_column_validator( - x, fields=required_fields + optional_fields - ), - ] - - return validators - - def apply_validators( df, fname, From 70c2a853d83d8d3028bb037d05cbae068d5f5689 Mon Sep 17 00:00:00 2001 From: YufeiG Date: Fri, 6 Jan 2023 17:11:37 -0500 Subject: [PATCH 064/914] Add optional mask for dalle's edit api (#157) (#169) * Add optional mask for dalle's edit api --- openai/api_resources/image.py | 9 +++++---- openai/cli.py | 8 +++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/openai/api_resources/image.py b/openai/api_resources/image.py index abd32c3f8b..39a5b6f616 100644 --- a/openai/api_resources/image.py +++ b/openai/api_resources/image.py @@ -156,7 +156,7 @@ async def acreate_variation( def _prepare_create_edit( cls, image, - mask, + mask=None, api_key=None, api_base=None, api_type=None, @@ -179,14 +179,15 @@ def _prepare_create_edit( for key, value in params.items(): files.append((key, (None, value))) files.append(("image", ("image", image, "application/octet-stream"))) - files.append(("mask", ("mask", mask, "application/octet-stream"))) + if mask is not None: + files.append(("mask", ("mask", mask, "application/octet-stream"))) return requestor, url, files @classmethod def create_edit( cls, image, - mask, + mask=None, api_key=None, api_base=None, api_type=None, @@ -215,7 +216,7 @@ def create_edit( async def acreate_edit( cls, image, - mask, + mask=None, api_key=None, api_base=None, api_type=None, diff --git a/openai/cli.py b/openai/cli.py index eaa49c713a..0ebb0537ce 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -240,8 +240,10 @@ def create_variation(cls, args): def create_edit(cls, args): with open(args.image, "rb") as file_reader: image_reader = BufferReader(file_reader.read(), desc="Upload progress") - with open(args.mask, "rb") as file_reader: - mask_reader = BufferReader(file_reader.read(), desc="Upload progress") + mask_reader = None + if args.mask is not None: + with open(args.mask, "rb") as file_reader: + mask_reader = BufferReader(file_reader.read(), desc="Upload progress") resp = openai.Image.create_edit( image=image_reader, mask=mask_reader, @@ -893,7 +895,7 @@ def help(args): "-M", "--mask", type=str, - required=True, + 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=Image.create_edit) From 4fee0da142f58307edb30111ed484d2d41e4811d Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 6 Jan 2023 15:35:09 -0800 Subject: [PATCH 065/914] Bump to 0.26.0 (#170) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 1ae872fda0..9cd44bb9ae 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.25.0" +VERSION = "0.26.0" From 777c1c3de1a9cfc0a33e6376cc09b9badbb9cdf9 Mon Sep 17 00:00:00 2001 From: Josh Bode Date: Wed, 11 Jan 2023 13:48:54 +0800 Subject: [PATCH 066/914] Fix documentation typo (#175) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61f38a2afb..7fdd0715e7 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ python setup.py install ### Optional dependencies -Install dependencies for [`openapi.embeddings_utils`](openai/embeddings_utils.py): +Install dependencies for [`openai.embeddings_utils`](openai/embeddings_utils.py): ```sh pip install openai[embeddings] From 71cee6a4ab54a1c26bc8f3570a435ef189c9aeff Mon Sep 17 00:00:00 2001 From: Damien Deville Date: Wed, 11 Jan 2023 21:35:30 -0800 Subject: [PATCH 067/914] Fix `FineTune.stream_events` (#176) We incorrectly changed the original method to be async rather than making a new async version. This PR fixes that. Fixes 173. --- openai/api_resources/fine_tune.py | 40 ++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index 65dba48836..d9c8359538 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -128,7 +128,45 @@ def _prepare_stream_events( return requestor, url @classmethod - async def stream_events( + def stream_events( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor, url = cls._prepare_stream_events( + id, + api_key, + api_base, + api_type, + request_id, + api_version, + organization, + **params, + ) + + response, _, api_key = requestor.request( + "get", url, params, stream=True, request_id=request_id + ) + + assert not isinstance(response, OpenAIResponse) # must be an iterator + return ( + util.convert_to_openai_object( + line, + api_key, + api_version, + organization, + ) + for line in response + ) + + @classmethod + async def astream_events( cls, id, api_key=None, From e7ee4daaa5b2f4b90c519faee993377ab691559d Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Thu, 12 Jan 2023 17:55:39 +0000 Subject: [PATCH 068/914] Several fixes to make `Completion.acreate(stream=True)` work (#172) * Added a failing test case for async completion stream * Consume async generator with async for * Consume the stream in chunks as sent by API, to avoid "empty" parts The api will send chunks like ``` b'data: {"id": "cmpl-6W18L0k1kFoHUoSsJOwcPq7DKBaGX", "object": "text_completion", "created": 1673088873, "choices": [{"text": "_", "index": 0, "logprobs": null, "finish_reason": null}], "model": "ada"}\n\n' ``` The default iterator will break on each `\n` character, whereas iter_chunks will just output parts as they arrive * Add another test using global aiosession * Manually consume aiohttp_session asyncontextmanager to ensure that session is only closed once the response stream is finished Previously we'd exit the with statement before the response stream is consumed by the caller, therefore, unless we're using a global ClientSession, the session is closed (and thus the request) before it should be. * Ensure we close the session even if the caller raises an exception while consuming the stream --- openai/api_requestor.py | 47 ++++++++++++------- .../abstract/engine_api_resource.py | 2 +- openai/tests/asyncio/test_endpoints.py | 24 ++++++++++ 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index a34ee281ec..eff7dd8a0a 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -89,9 +89,9 @@ def _make_session() -> requests.Session: return s -def parse_stream_helper(line): +def parse_stream_helper(line: bytes): if line: - if line == b"data: [DONE]": + if line.strip() == b"data: [DONE]": # return here will cause GeneratorExit exception in urllib3 # and it will close http connection with TCP Reset return None @@ -111,7 +111,7 @@ def parse_stream(rbody): async def parse_stream_async(rbody: aiohttp.StreamReader): - async for line in rbody: + async for line, _ in rbody.iter_chunks(): _line = parse_stream_helper(line) if _line is not None: yield _line @@ -294,18 +294,31 @@ async def arequest( request_id: Optional[str] = None, request_timeout: Optional[Union[float, Tuple[float, float]]] = None, ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: - async with aiohttp_session() as session: - result = await self.arequest_raw( - method.lower(), - url, - session, - params=params, - supplied_headers=headers, - files=files, - request_id=request_id, - request_timeout=request_timeout, - ) - resp, got_stream = await self._interpret_async_response(result, stream) + ctx = aiohttp_session() + session = await ctx.__aenter__() + result = await self.arequest_raw( + method.lower(), + url, + session, + params=params, + supplied_headers=headers, + files=files, + request_id=request_id, + request_timeout=request_timeout, + ) + resp, got_stream = await self._interpret_async_response(result, stream) + if got_stream: + + async def wrap_resp(): + try: + async for r in resp: + yield r + finally: + await ctx.__aexit__(None, None, None) + + return wrap_resp(), got_stream, self.api_key + else: + await ctx.__aexit__(None, None, None) return resp, got_stream, self.api_key def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): @@ -507,7 +520,9 @@ def request_raw( except requests.exceptions.Timeout as e: raise error.Timeout("Request timed out: {}".format(e)) from e except requests.exceptions.RequestException as e: - raise error.APIConnectionError("Error communicating with OpenAI: {}".format(e)) from e + raise error.APIConnectionError( + "Error communicating with OpenAI: {}".format(e) + ) from e util.log_info( "OpenAI API response", path=abs_url, diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index d6fe0d39a9..1f172d8cbd 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -236,7 +236,7 @@ async def acreate( engine=engine, plain_old_data=cls.plain_old_data, ) - for line in response + async for line in response ) else: obj = util.convert_to_openai_object( diff --git a/openai/tests/asyncio/test_endpoints.py b/openai/tests/asyncio/test_endpoints.py index e5c6d012cd..a0e38a5a04 100644 --- a/openai/tests/asyncio/test_endpoints.py +++ b/openai/tests/asyncio/test_endpoints.py @@ -5,6 +5,7 @@ import openai from openai import error +from aiohttp import ClientSession pytestmark = [pytest.mark.asyncio] @@ -63,3 +64,26 @@ async def test_timeout_does_not_error(): model="ada", request_timeout=10, ) + + +async def test_completions_stream_finishes_global_session(): + async with ClientSession() as session: + openai.aiosession.set(session) + + # A query that should be fast + parts = [] + async for part in await openai.Completion.acreate( + prompt="test", model="ada", request_timeout=3, stream=True + ): + parts.append(part) + assert len(parts) > 1 + + +async def test_completions_stream_finishes_local_session(): + # A query that should be fast + parts = [] + async for part in await openai.Completion.acreate( + prompt="test", model="ada", request_timeout=3, stream=True + ): + parts.append(part) + assert len(parts) > 1 From ef8f1f1af60d9c41f3c840513d087a88401c98a2 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Thu, 12 Jan 2023 20:53:45 +0000 Subject: [PATCH 069/914] Async iterator needs to be consumed with async for (#179) --- openai/api_resources/fine_tune.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py index d9c8359538..45e3cf2af3 100644 --- a/openai/api_resources/fine_tune.py +++ b/openai/api_resources/fine_tune.py @@ -200,5 +200,5 @@ async def astream_events( api_version, organization, ) - for line in response + async for line in response ) From 5d1a72688a8cc36b02ccc3d09bc577ec37ef4f27 Mon Sep 17 00:00:00 2001 From: Damien Deville Date: Thu, 12 Jan 2023 17:06:42 -0800 Subject: [PATCH 070/914] Fix some typing issues (#177) --- openai/api_requestor.py | 68 +++++++++++++++----------- openai/tests/asyncio/test_endpoints.py | 2 +- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index eff7dd8a0a..2148a0070f 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -89,21 +89,19 @@ def _make_session() -> requests.Session: return s -def parse_stream_helper(line: bytes): +def parse_stream_helper(line: bytes) -> Optional[str]: if line: if line.strip() == b"data: [DONE]": # return here will cause GeneratorExit exception in urllib3 # and it will close http connection with TCP Reset return None - if hasattr(line, "decode"): - line = line.decode("utf-8") - if line.startswith("data: "): - line = line[len("data: ") :] - return line + if line.startswith(b"data: "): + line = line[len(b"data: ") :] + return line.decode("utf-8") return None -def parse_stream(rbody): +def parse_stream(rbody: Iterator[bytes]) -> Iterator[str]: for line in rbody: _line = parse_stream_helper(line) if _line is not None: @@ -111,10 +109,13 @@ def parse_stream(rbody): async def parse_stream_async(rbody: aiohttp.StreamReader): - async for line, _ in rbody.iter_chunks(): - _line = parse_stream_helper(line) - if _line is not None: - yield _line + async for chunk, _ in rbody.iter_chunks(): + # While the `ChunkTupleAsyncStreamIterator` iterator is meant to iterate over chunks (and thus lines) it seems + # to still sometimes return multiple lines at a time, so let's split the chunk by lines again. + for line in chunk.splitlines(): + _line = parse_stream_helper(line) + if _line is not None: + yield _line class APIRequestor: @@ -296,20 +297,25 @@ async def arequest( ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: ctx = aiohttp_session() session = await ctx.__aenter__() - result = await self.arequest_raw( - method.lower(), - url, - session, - params=params, - supplied_headers=headers, - files=files, - request_id=request_id, - request_timeout=request_timeout, - ) - resp, got_stream = await self._interpret_async_response(result, stream) + try: + result = await self.arequest_raw( + method.lower(), + url, + session, + params=params, + supplied_headers=headers, + files=files, + request_id=request_id, + request_timeout=request_timeout, + ) + resp, got_stream = await self._interpret_async_response(result, stream) + except Exception: + await ctx.__aexit__(None, None, None) + raise if got_stream: async def wrap_resp(): + assert isinstance(resp, AsyncGenerator) try: async for r in resp: yield r @@ -612,7 +618,10 @@ def _interpret_response( else: return ( self._interpret_response_line( - result.content, result.status_code, result.headers, stream=False + result.content.decode("utf-8"), + result.status_code, + result.headers, + stream=False, ), False, ) @@ -635,13 +644,16 @@ async def _interpret_async_response( util.log_warn(e, body=result.content) return ( self._interpret_response_line( - await result.read(), result.status, result.headers, stream=False + (await result.read()).decode("utf-8"), + result.status, + result.headers, + stream=False, ), False, ) def _interpret_response_line( - self, rbody, rcode, rheaders, stream: bool + self, rbody: str, rcode: int, rheaders, stream: bool ) -> OpenAIResponse: # HTTP 204 response code does not have any content in the body. if rcode == 204: @@ -655,13 +667,11 @@ def _interpret_response_line( headers=rheaders, ) try: - if hasattr(rbody, "decode"): - rbody = rbody.decode("utf-8") data = json.loads(rbody) - except (JSONDecodeError, UnicodeDecodeError): + except (JSONDecodeError, UnicodeDecodeError) as e: raise error.APIError( f"HTTP code {rcode} from API ({rbody})", rbody, rcode, headers=rheaders - ) + ) from e resp = OpenAIResponse(data, rheaders) # In the future, we might add a "status" parameter to errors # to better handle the "error while streaming" case. diff --git a/openai/tests/asyncio/test_endpoints.py b/openai/tests/asyncio/test_endpoints.py index a0e38a5a04..3dc355b733 100644 --- a/openai/tests/asyncio/test_endpoints.py +++ b/openai/tests/asyncio/test_endpoints.py @@ -2,10 +2,10 @@ import json import pytest +from aiohttp import ClientSession import openai from openai import error -from aiohttp import ClientSession pytestmark = [pytest.mark.asyncio] From e5b7d1a2ef3cfa7bf0af96a3dcc9ca3056552838 Mon Sep 17 00:00:00 2001 From: Damien Deville Date: Thu, 12 Jan 2023 17:06:50 -0800 Subject: [PATCH 071/914] [openai-python] Change request logs to debug level (#152) (#178) Co-authored-by: andrewpeng@openai.com Co-authored-by: Andrew Peng --- openai/api_requestor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 2148a0070f..8647894ff9 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -490,7 +490,7 @@ def _prepare_request_raw( headers = self.request_headers(method, headers, request_id) - util.log_info("Request to OpenAI API", method=method, path=abs_url) + util.log_debug("Request to OpenAI API", method=method, path=abs_url) util.log_debug("Post details", data=data, api_version=self.api_version) return abs_url, headers, data @@ -529,7 +529,7 @@ def request_raw( raise error.APIConnectionError( "Error communicating with OpenAI: {}".format(e) ) from e - util.log_info( + util.log_debug( "OpenAI API response", path=abs_url, response_code=result.status_code, From 040f72efc461d747f04c32126ac4285d0f63b993 Mon Sep 17 00:00:00 2001 From: Damien Deville Date: Fri, 13 Jan 2023 12:51:26 -0800 Subject: [PATCH 072/914] Bump version (#182) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 9cd44bb9ae..10ea7670f6 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.26.0" +VERSION = "0.26.1" From 4a33f3f1a16c5be8e66a433bf2efd392ee787b6d Mon Sep 17 00:00:00 2001 From: Agnieszka Stec <51296866+Ygnys@users.noreply.github.com> Date: Mon, 16 Jan 2023 19:47:20 -1000 Subject: [PATCH 073/914] Update README.md (#188) --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7fdd0715e7..f1e68c1d0a 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ print(completion.choices[0].text) ### Params -All endpoints have a `.create` method that support a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise a `openai.error.TimeoutError` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). +All endpoints have a `.create` method that supports a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.TimeoutError` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). ### Microsoft Azure Endpoints @@ -96,7 +96,7 @@ print(completion.choices[0].text) ``` Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. -For a detailed example on how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: +For a detailed example of how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: * [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) * [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) * [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) @@ -188,14 +188,14 @@ Examples of how to use embeddings are shared in the following Jupyter notebooks: For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://beta.openai.com/docs/guides/embeddings) in the OpenAI documentation. -### Fine tuning +### Fine-tuning -Fine tuning a model on training data can both improve the results (by giving the model more examples to learn from) and reduce the cost/latency of API calls (chiefly through reducing the need to include training examples in prompts). +Fine-tuning a model on training data can both improve the results (by giving the model more examples to learn from) and reduce the cost/latency of API calls (chiefly through reducing the need to include training examples in prompts). -Examples of fine tuning are shared in the following Jupyter notebooks: +Examples of fine-tuning are shared in the following Jupyter notebooks: -- [Classification with fine tuning](https://github.com/openai/openai-cookbook/blob/main/examples/Fine-tuned_classification.ipynb) (a simple notebook that shows the steps required for fine tuning) -- Fine tuning a model that answers questions about the 2020 Olympics +- [Classification with fine-tuning](https://github.com/openai/openai-cookbook/blob/main/examples/Fine-tuned_classification.ipynb) (a simple notebook that shows the steps required for fine-tuning) +- Fine-tuning a model that answers questions about the 2020 Olympics - [Step 1: Collecting data](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-1-collect-data.ipynb) - [Step 2: Creating a synthetic Q&A dataset](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-2-create-qa.ipynb) - [Step 3: Train a fine-tuning model specialized for Q&A](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-3-train-qa.ipynb) @@ -206,7 +206,7 @@ Sync your fine-tunes to [Weights & Biases](https://wandb.me/openai-docs) to trac openai wandb sync ``` -For more information on fine tuning, read the [fine-tuning guide](https://beta.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. +For more information on fine-tuning, read the [fine-tuning guide](https://beta.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. ### Moderation From 48b69293a3dff649165c1d0e0f9d77d1b52436c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCrtz?= <120655914+cmurtz-msft@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:57:37 +0100 Subject: [PATCH 074/914] Add fine_tunes.delete cmd to cli (#187) --- openai/cli.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openai/cli.py b/openai/cli.py index 0ebb0537ce..a7f7654dc8 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -498,6 +498,11 @@ def cancel(cls, args): resp = openai.FineTune.cancel(id=args.id) print(resp) + @classmethod + def delete(cls, args): + resp = openai.FineTune.delete(sid=args.id) + print(resp) + @classmethod def prepare_data(cls, args): @@ -867,6 +872,10 @@ def help(args): sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") sub.set_defaults(func=FineTune.cancel) + sub = subparsers.add_parser("fine_tunes.delete") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTune.delete) + # Image sub = subparsers.add_parser("image.create") sub.add_argument("-p", "--prompt", type=str, required=True) From 634901591e35112b4649a28ab764db236e6f6113 Mon Sep 17 00:00:00 2001 From: joe-at-openai <117690718+joe-at-openai@users.noreply.github.com> Date: Sat, 21 Jan 2023 17:53:02 -0800 Subject: [PATCH 075/914] Improve error message in case of a misformatted file (#158) (#190) * add more descriptive error handling regarding poorly formatted files * update version * add dot prefix to json file extentions and ensure list of allowable file types is complete * cleanup error messages and add comments to explain jsonl/json loading logic * cleanup csv/tsv reading allowing use of elif for other file extensions, add comments, and remove unnecessary re-attempt to parse as json * run fillna immediately upon DataFrame creation so that an additional switch is not needed * use only 1 try-except block to catch parsing errors + cleanup error message * separate the json and jsonl cases while still maintaining the same functionality, also include a message to user if jsonl appears to be json or vice versa * fix bug in csv path * use index -1 to get extension from split * black formatting apply * fix black Co-authored-by: joe-at-openai Co-authored-by: Boris Power <81998504+BorisPower@users.noreply.github.com> --- openai/validators.py | 100 ++++++++++++++++++++++++------------------- openai/version.py | 2 +- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/openai/validators.py b/openai/validators.py index 146e97d9fb..c5a3dd7890 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -37,7 +37,7 @@ def necessary_column_validator(df, necessary_column): """ def lower_case_column(df, column): - cols = [c for c in df.columns if c.lower() == column] + cols = [c for c in df.columns if str(c).lower() == column] df.rename(columns={cols[0]: column.lower()}, inplace=True) return df @@ -47,7 +47,7 @@ def lower_case_column(df, column): error_msg = None if necessary_column not in df.columns: - if necessary_column in [c.lower() for c in df.columns]: + if necessary_column in [str(c).lower() for c in df.columns]: def lower_case_column_creator(df): return lower_case_column(df, necessary_column) @@ -482,51 +482,65 @@ def read_any_format(fname, fields=["prompt", "completion"]): df = None if os.path.isfile(fname): - for ending, separator in [(".csv", ","), (".tsv", "\t")]: - if fname.lower().endswith(ending): - immediate_msg = f"\n- Based on your file extension, your file is formatted as a {ending[1:].upper()} file" - necessary_msg = ( - f"Your format `{ending[1:].upper()}` will be converted to `JSONL`" + try: + if fname.lower().endswith(".csv") or fname.lower().endswith(".tsv"): + file_extension_str, separator = ( + ("CSV", ",") if fname.lower().endswith(".csv") else ("TSV", "\t") ) - df = pd.read_csv(fname, sep=separator, dtype=str) - if fname.lower().endswith(".xlsx"): - immediate_msg = "\n- Based on your file extension, your file is formatted as an Excel file" - necessary_msg = "Your format `XLSX` will be converted to `JSONL`" - xls = pd.ExcelFile(fname) - sheets = xls.sheet_names - if len(sheets) > 1: - immediate_msg += "\n- Your Excel file contains more than one sheet. Please either save as csv or ensure all data is present in the first sheet. WARNING: Reading only the first sheet..." - df = pd.read_excel(fname, dtype=str) - if fname.lower().endswith(".txt"): - immediate_msg = "\n- Based on your file extension, you provided a text file" - necessary_msg = "Your format `TXT` will be converted to `JSONL`" - with open(fname, "r") as f: - content = f.read() - df = pd.DataFrame( - [["", line] for line in content.split("\n")], - columns=fields, - dtype=str, + immediate_msg = f"\n- Based on your file extension, your file is formatted as a {file_extension_str} file" + necessary_msg = ( + f"Your format `{file_extension_str}` will be converted to `JSONL`" ) - if fname.lower().endswith("jsonl") or fname.lower().endswith("json"): - try: - df = pd.read_json(fname, lines=True, dtype=str) - except (ValueError, TypeError): - df = pd.read_json(fname, dtype=str) - immediate_msg = "\n- Your file appears to be in a .JSON format. Your file will be converted to JSONL format" - necessary_msg = "Your format `JSON` will be converted to `JSONL`" - - if df is None: - error_msg = ( - "Your file is not saved as a .CSV, .TSV, .XLSX, .TXT or .JSONL file." - ) - if "." in fname: - error_msg += ( - f" Your file `{fname}` appears to end with `.{fname.split('.')[1]}`" + df = pd.read_csv(fname, sep=separator, dtype=str).fillna("") + elif fname.lower().endswith(".xlsx"): + immediate_msg = "\n- Based on your file extension, your file is formatted as an Excel file" + necessary_msg = "Your format `XLSX` will be converted to `JSONL`" + xls = pd.ExcelFile(fname) + sheets = xls.sheet_names + if len(sheets) > 1: + immediate_msg += "\n- Your Excel file contains more than one sheet. Please either save as csv or ensure all data is present in the first sheet. WARNING: Reading only the first sheet..." + df = pd.read_excel(fname, dtype=str).fillna("") + elif fname.lower().endswith(".txt"): + immediate_msg = ( + "\n- Based on your file extension, you provided a text file" ) + necessary_msg = "Your format `TXT` will be converted to `JSONL`" + with open(fname, "r") as f: + content = f.read() + df = pd.DataFrame( + [["", line] for line in content.split("\n")], + columns=fields, + dtype=str, + ).fillna("") + elif fname.lower().endswith(".jsonl"): + df = pd.read_json(fname, lines=True, dtype=str).fillna("") + if len(df) == 1: + # this is NOT what we expect for a .jsonl file + immediate_msg = "\n- Your JSONL file appears to be in a JSON format. Your file will be converted to JSONL format" + necessary_msg = "Your format `JSON` will be converted to `JSONL`" + df = pd.read_json(fname, dtype=str).fillna("") + else: + pass # this is what we expect for a .jsonl file + elif fname.lower().endswith(".json"): + df = pd.read_json(fname, lines=True, dtype=str).fillna("") + if len(df) == 1: + # this is what we expect for a .json file + df = pd.read_json(fname, dtype=str).fillna("") + else: + # this is NOT what we expect for a .json file + immediate_msg = "\n- Your JSON file appears to be in a JSONL format. Your file will be converted to JSONL format" + necessary_msg = "Your format `JSON` will be converted to `JSONL`" else: - error_msg += f" Your file `{fname}` does not appear to have a file ending. Please ensure your filename ends with one of the supported file endings." - else: - df.fillna("", inplace=True) + error_msg = "Your file must have one of the following extensions: .CSV, .TSV, .XLSX, .TXT, .JSON or .JSONL" + if "." in fname: + error_msg += f" Your file `{fname}` ends with the extension `.{fname.split('.')[-1]}` which is not supported." + else: + error_msg += f" Your file `{fname}` is missing a file extension." + + except (ValueError, TypeError): + file_extension_str = fname.split(".")[-1].upper() + error_msg = f"Your file `{fname}` does not appear to be in valid {file_extension_str} format. Please ensure your file is formatted as a valid {file_extension_str} file." + else: error_msg = f"File {fname} does not exist." diff --git a/openai/version.py b/openai/version.py index 10ea7670f6..46ef7466b0 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.26.1" +VERSION = "0.26.2" \ No newline at end of file From bf51385a0623db39943753fdbcc6359e6efa7f92 Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Tue, 24 Jan 2023 11:35:30 -0600 Subject: [PATCH 076/914] Modify README to clarify documentation section (#193) --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index f1e68c1d0a..8c6f359e0d 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,7 @@ pre-defined set of classes for API resources that initialize themselves dynamically from API responses which makes it compatible with a wide range of versions of the OpenAI API. -## Documentation - -See the [OpenAI API docs](https://beta.openai.com/docs/api-reference?lang=python). +You can find usage examples for the OpenAI Python library in our [API reference](https://beta.openai.com/docs/api-reference?lang=python) and the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). ## Installation From 88b267b922b821d74d7b253f8ce2a56aa3e497ee Mon Sep 17 00:00:00 2001 From: James Chua <30519287+thejaminator@users.noreply.github.com> Date: Wed, 25 Jan 2023 12:21:00 +0800 Subject: [PATCH 077/914] fix(download): Fix download url by reverting change (#197) Co-authored-by: James Chua --- openai/api_resources/file.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 55c733512b..365cb2add8 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -138,14 +138,9 @@ def __prepare_file_download( if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): base = cls.class_url() - url = "/%s%s/%s?api-version=%s" % ( - cls.azure_api_prefix, - base, - id, - api_version, - ) + url = f"/{cls.azure_api_prefix}{base}/{id}/content?api-version={api_version}" elif typed_api_type == ApiType.OPEN_AI: - url = "%s/%s" % (cls.class_url(), id) + url = f"{cls.class_url()}/{id}/content" else: raise error.InvalidAPIType("Unsupported API type %s" % api_type) From 41f938fb902de90111c0d026b76d58f5f9eabeb6 Mon Sep 17 00:00:00 2001 From: hallacy Date: Wed, 25 Jan 2023 07:25:40 -0800 Subject: [PATCH 078/914] Bump to version 0.26.3 (#200) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 46ef7466b0..72ad5c9364 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.26.2" \ No newline at end of file +VERSION = "0.26.3" From 86dff298f759be51292b47efcff926eb361cee7a Mon Sep 17 00:00:00 2001 From: Jon Bringhurst Date: Wed, 25 Jan 2023 11:07:55 -0800 Subject: [PATCH 079/914] Add license metadata for pypi (#198) --- pyproject.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c745249e3d..65fe55ff7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,11 @@ +[project] +license = {file = "LICENSE"} +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + [tool.black] target-version = ['py36'] exclude = '.*\.ipynb' @@ -6,4 +14,4 @@ exclude = '.*\.ipynb' py_version = 36 include_trailing_comma = "true" line_length = 88 -multi_line_output = 3 \ No newline at end of file +multi_line_output = 3 From ca2fbacf4a26124c315e83077db636c6fdc27691 Mon Sep 17 00:00:00 2001 From: Damien Deville Date: Thu, 26 Jan 2023 13:34:42 -0800 Subject: [PATCH 080/914] Fix invalid pyproject.toml file and move to setup.cfg (#201) The `[project]` section of a `pyproject.toml` file *has* to include both a `name` and a `version`, that we were missing. However, rather than adding more stuff to `pyproject.toml`, I've moved the license info to the setup file and pointed `pyproject.toml` to use `setuptools` for building (the `pyproject.toml` file is really only needed to integrated with `black`, that doesn't support setup files...) While I was at it I decided to migrate to a `setup.cfg` file rather than `setup.py` since its declaritive config is usally preferred these days (and it's a bit easier to load from version/license files than with a `setup.py` file). --- pyproject.toml | 10 +++----- setup.cfg | 65 +++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 68 ++------------------------------------------------ 3 files changed, 70 insertions(+), 73 deletions(-) create mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml index 65fe55ff7d..6116c7fa2f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,6 @@ -[project] -license = {file = "LICENSE"} -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" [tool.black] target-version = ['py36'] diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000..a42b05ef01 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,65 @@ +[metadata] +name = openai +version = attr: openai.version.VERSION +description = Python client library for the OpenAI API +long_description = file: README.md +long_description_content_type = text/markdown +author = OpenAI +author_email = support@openai.com +url = https://github.com/openai/openai-python +license_files = LICENSE +classifiers = + Programming Language :: Python :: 3 + License :: OSI Approved :: MIT License + Operating System :: OS Independent + +[options] +packages = find: +python_requires = >=3.7.1 +zip_safe = True +include_package_data = True +install_requires = + requests >= 2.20 # to get the patch for CVE-2018-18074 + tqdm # Needed for progress bars + typing_extensions; python_version<"3.8" # Needed for type hints for mypy + aiohttp # Needed for async support + +[options.extras_require] +dev = + black ~= 21.6b0 + pytest == 6.* + pytest-asyncio + pytest-mock +datalib = + numpy + pandas >= 1.2.3 # Needed for CLI fine-tuning data preparation tool + pandas-stubs >= 1.1.0.11 # Needed for type hints for mypy + openpyxl >= 3.0.7 # Needed for CLI fine-tuning data preparation tool xlsx format +wandb = + wandb + numpy + pandas >= 1.2.3 # Needed for CLI fine-tuning data preparation tool + pandas-stubs >= 1.1.0.11 # Needed for type hints for mypy + openpyxl >= 3.0.7 # Needed for CLI fine-tuning data preparation tool xlsx format +embeddings = + scikit-learn >= 1.0.2 # Needed for embedding utils, versions >= 1.1 require python 3.8 + tenacity >= 8.0.1 + matplotlib + sklearn + plotly + numpy + pandas >= 1.2.3 # Needed for CLI fine-tuning data preparation tool + pandas-stubs >= 1.1.0.11 # Needed for type hints for mypy + openpyxl >= 3.0.7 # Needed for CLI fine-tuning data preparation tool xlsx format + +[options.entry_points] +console_scripts = + openai = openai._openai_scripts:main + +[options.package_data] + openai = py.typed + +[options.packages.find] +exclude = + tests + tests.* diff --git a/setup.py b/setup.py index e431d26ccd..606849326a 100644 --- a/setup.py +++ b/setup.py @@ -1,67 +1,3 @@ -import os +from setuptools import setup -from setuptools import find_packages, setup - -version_contents = {} -version_path = os.path.join( - os.path.abspath(os.path.dirname(__file__)), "openai/version.py" -) -with open(version_path, "rt") as f: - exec(f.read(), version_contents) - -with open("README.md", "r") as fh: - long_description = fh.read() - - -DATA_LIBRARIES = [ - # These libraries are optional because of their size. See `openai/datalib.py`. - "numpy", - "pandas>=1.2.3", # Needed for CLI fine-tuning data preparation tool - "pandas-stubs>=1.1.0.11", # Needed for type hints for mypy - "openpyxl>=3.0.7", # Needed for CLI fine-tuning data preparation tool xlsx format -] - -setup( - name="openai", - description="Python client library for the OpenAI API", - long_description=long_description, - long_description_content_type="text/markdown", - version=version_contents["VERSION"], - install_requires=[ - "requests>=2.20", # to get the patch for CVE-2018-18074 - "tqdm", # Needed for progress bars - 'typing_extensions;python_version<"3.8"', # Needed for type hints for mypy - "aiohttp", # Needed for async support - ], - extras_require={ - "dev": ["black~=21.6b0", "pytest==6.*", "pytest-asyncio", "pytest-mock"], - "datalib": DATA_LIBRARIES, - "wandb": [ - "wandb", - *DATA_LIBRARIES, - ], - "embeddings": [ - "scikit-learn>=1.0.2", # Needed for embedding utils, versions >= 1.1 require python 3.8 - "tenacity>=8.0.1", - "matplotlib", - "sklearn", - "plotly", - *DATA_LIBRARIES, - ], - }, - python_requires=">=3.7.1", - entry_points={ - "console_scripts": [ - "openai=openai._openai_scripts:main", - ], - }, - packages=find_packages(exclude=["tests", "tests.*"]), - package_data={ - "openai": [ - "py.typed", - ] - }, - author="OpenAI", - author_email="support@openai.com", - url="https://github.com/openai/openai-python", -) +setup() From b23ecafe49133da9812d96cb693f656e0a223050 Mon Sep 17 00:00:00 2001 From: Damien Deville Date: Thu, 26 Jan 2023 13:38:52 -0800 Subject: [PATCH 081/914] Bump version (#204) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 72ad5c9364..cbd0c64107 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.26.3" +VERSION = "0.26.4" From 33fd6267f895b8c928cd53ce61ebeb712d318d01 Mon Sep 17 00:00:00 2001 From: Thang Dang <31526669+tkppro@users.noreply.github.com> Date: Tue, 7 Feb 2023 03:40:29 +0900 Subject: [PATCH 082/914] Replace self with cls in classmethods for consistency (#211) --- openai/api_resources/customer.py | 2 +- openai/api_resources/moderation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/api_resources/customer.py b/openai/api_resources/customer.py index cb9779a2f1..8690d07b38 100644 --- a/openai/api_resources/customer.py +++ b/openai/api_resources/customer.py @@ -3,7 +3,7 @@ class Customer(OpenAIObject): @classmethod - def get_url(http://github.com/self,%20customer,%20endpoint): + def get_url(http://github.com/cls,%20customer,%20endpoint): return f"/customer/{customer}/{endpoint}" @classmethod diff --git a/openai/api_resources/moderation.py b/openai/api_resources/moderation.py index 4b8b58c6d9..bd19646b49 100644 --- a/openai/api_resources/moderation.py +++ b/openai/api_resources/moderation.py @@ -7,7 +7,7 @@ class Moderation(OpenAIObject): VALID_MODEL_NAMES: List[str] = ["text-moderation-stable", "text-moderation-latest"] @classmethod - def get_url(http://github.com/self): + def get_url(http://github.com/cls): return "/moderations" @classmethod From 5ac7946f20a92ca57d65fbd279fced95d92dcfff Mon Sep 17 00:00:00 2001 From: Sergey Kozlov Date: Tue, 7 Feb 2023 00:43:26 +0600 Subject: [PATCH 083/914] Add 'bytes' argument to __find_matching_files() (#207) --- openai/api_resources/file.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 365cb2add8..394417245f 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -198,7 +198,7 @@ async def adownload( return result.content @classmethod - def __find_matching_files(cls, name, all_files, purpose): + def __find_matching_files(cls, name, bytes, all_files, purpose): matching_files = [] basename = os.path.basename(name) for f in all_files: @@ -234,7 +234,7 @@ def find_matching_files( api_version=api_version, organization=organization, ).get("data", []) - return cls.__find_matching_files(name, all_files, purpose) + return cls.__find_matching_files(name, bytes, all_files, purpose) @classmethod async def afind_matching_files( @@ -258,4 +258,4 @@ async def afind_matching_files( organization=organization, ) ).get("data", []) - return cls.__find_matching_files(name, all_files, purpose) + return cls.__find_matching_files(name, bytes, all_files, purpose) From 341b85d8840376834855aa4e001c57b6f1ba669a Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Mon, 6 Feb 2023 16:06:06 -0600 Subject: [PATCH 084/914] Create issue templates (#214) * Create config.yml * Add * Create feature_request.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 56 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 7 +++ .github/ISSUE_TEMPLATE/feature_request.yml | 20 ++++++++ 3 files changed, 83 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..300ad9f0ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,56 @@ +name: Bug report +description: Create a report to help us improve +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! If you have questions about using the OpenAI Python library, please post on our [Community forum](https://community.openai.com). + - type: textarea + id: what-happened + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is, and any additional context. + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: repro-steps + attributes: + label: To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. Fetch a '...' + 2. Update the '....' + 3. See error + validations: + required: true + - type: textarea + id: code-snippets + attributes: + label: Code snippets + description: If applicable, add code snippets to help explain your problem. + render: Python + validations: + required: false + - type: input + id: os + attributes: + label: OS + placeholder: macOS + validations: + required: true + - type: input + id: language-version + attributes: + label: Python version + placeholder: Python v3.7.1 + validations: + required: true + - type: input + id: lib-version + attributes: + label: Library version + placeholder: openai-python v0.26.4 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..5bedf975eb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +blank_issues_enabled: false +contact_links: + - name: OpenAI support + url: https://help.openai.com/ + about: | + Please only file issues here that you believe represent actual bugs or feature requests for the OpenAI Python library. + If you're having general trouble with the OpenAI API, ChatGPT, etc, please visit our help center to get support. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..2bd1c635ba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,20 @@ +name: Feature request +description: Suggest an idea for this library +labels: ["feature-request"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request! Please note, we are not able to accommodate all feature requests given limited bandwidth but we appreciate you taking the time to share with us how to improve the OpenAI Python library. + - type: textarea + id: feature + attributes: + label: Describe the feature or improvement you're requesting + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context about the feature request here. From fb6232f7b91598c600dc638ac17f80719ae933cb Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Mon, 6 Feb 2023 16:12:42 -0600 Subject: [PATCH 085/914] Remove mention of support email (#215) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c6f359e0d..9976e1de10 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ See the [usage guide](https://beta.openai.com/docs/guides/images) for more detai In general, we want to support the versions of Python that our customers are using. If you run into problems with any version -issues, please let us know at support@openai.com. +issues, please let us know at on our [support page](https://help.openai.com/en/). ## Credit From 5386db105948b0bb63f82d1a7d46fbcac6189aa3 Mon Sep 17 00:00:00 2001 From: Atty Eleti Date: Tue, 7 Feb 2023 14:18:33 -0800 Subject: [PATCH 086/914] Fixes #184: JSON parsing issue when async streaming (#216) --- openai/api_requestor.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 8647894ff9..04b65fdcdb 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -96,8 +96,10 @@ def parse_stream_helper(line: bytes) -> Optional[str]: # and it will close http connection with TCP Reset return None if line.startswith(b"data: "): - line = line[len(b"data: ") :] - return line.decode("utf-8") + line = line[len(b"data: "):] + return line.decode("utf-8") + else: + return None return None @@ -109,13 +111,10 @@ def parse_stream(rbody: Iterator[bytes]) -> Iterator[str]: async def parse_stream_async(rbody: aiohttp.StreamReader): - async for chunk, _ in rbody.iter_chunks(): - # While the `ChunkTupleAsyncStreamIterator` iterator is meant to iterate over chunks (and thus lines) it seems - # to still sometimes return multiple lines at a time, so let's split the chunk by lines again. - for line in chunk.splitlines(): - _line = parse_stream_helper(line) - if _line is not None: - yield _line + async for line in rbody: + _line = parse_stream_helper(line) + if _line is not None: + yield _line class APIRequestor: From cf8a2751e6113d9fcc998525c88363273035dc9e Mon Sep 17 00:00:00 2001 From: Atty Eleti Date: Tue, 7 Feb 2023 14:24:53 -0800 Subject: [PATCH 087/914] Bump version to 0.26.5 (#217) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index cbd0c64107..9daab2e53f 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.26.4" +VERSION = "0.26.5" From 62ebb446d42af2ac41ab37165a7dd5a41cbbe1ee Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Wed, 1 Mar 2023 18:33:52 +0100 Subject: [PATCH 088/914] Fixes for embedding requirements install (#227) I noticed sklearn is in the install which is deprecated and scikit-learn is already in there as well, and scipy was missing. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index a42b05ef01..3729647b8d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,9 +45,9 @@ embeddings = scikit-learn >= 1.0.2 # Needed for embedding utils, versions >= 1.1 require python 3.8 tenacity >= 8.0.1 matplotlib - sklearn plotly numpy + scipy pandas >= 1.2.3 # Needed for CLI fine-tuning data preparation tool pandas-stubs >= 1.1.0.11 # Needed for type hints for mypy openpyxl >= 3.0.7 # Needed for CLI fine-tuning data preparation tool xlsx format From 62b73b9bd426d131910534ae6e0d23d7ae4f8fde Mon Sep 17 00:00:00 2001 From: Atty Eleti Date: Wed, 1 Mar 2023 09:38:18 -0800 Subject: [PATCH 089/914] Add ChatCompletions and Audio endpoints (#237) --- README.md | 52 +++-- openai/__init__.py | 6 +- openai/api_requestor.py | 4 +- openai/api_resources/__init__.py | 2 + openai/api_resources/audio.py | 205 +++++++++++++++++++ openai/api_resources/chat_completion.py | 50 +++++ openai/api_resources/completion.py | 4 +- openai/api_resources/embedding.py | 4 +- openai/cli.py | 160 ++++++++++++++- openai/tests/asyncio/test_endpoints.py | 9 +- openai/tests/test_endpoints.py | 26 +++ openai/tests/test_long_examples_validator.py | 10 +- openai/validators.py | 8 +- openai/version.py | 2 +- 14 files changed, 507 insertions(+), 35 deletions(-) create mode 100644 openai/api_resources/audio.py create mode 100644 openai/api_resources/chat_completion.py diff --git a/README.md b/README.md index 9976e1de10..5dbd51a627 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ pip install openai[datalib] ## Usage -The library needs to be configured with your account's secret key which is available on the [website](https://beta.openai.com/account/api-keys). Either set it as the `OPENAI_API_KEY` environment variable before using the library: +The library needs to be configured with your account's secret key which is available on the [website](https://platform.openai.com/account/api-keys). Either set it as the `OPENAI_API_KEY` environment variable before using the library: ```bash export OPENAI_API_KEY='sk-...' @@ -57,14 +57,14 @@ Or set `openai.api_key` to its value: import openai openai.api_key = "sk-..." -# list engines -engines = openai.Engine.list() +# list models +models = openai.Model.list() -# print the first engine's id -print(engines.data[0].id) +# print the first model's id +print(models.data[0].id) # create a completion -completion = openai.Completion.create(engine="ada", prompt="Hello world") +completion = openai.Completion.create(model="ada", prompt="Hello world") # print the completion print(completion.choices[0].text) @@ -127,11 +127,14 @@ which makes it easy to interact with the API from your terminal. Run `openai api -h` for usage. ```sh -# list engines -openai api engines.list +# list models +openai api models.list # create a completion -openai api completions.create -e ada -p "Hello world" +openai api completions.create -m ada -p "Hello world" + +# create a chat completion +openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world" # generate images via DALL·E API openai api image.create -p "two dogs playing chess, cartoon" -n 1 @@ -152,6 +155,18 @@ Examples of how to use this Python library to accomplish various tasks can be fo Prior to July 2022, this OpenAI Python library hosted code examples in its examples folder, but since then all examples have been migrated to the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). +### Chat + +Conversational models such as `gpt-3.5-turbo` can be called using the chat completions endpoint. + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world!"}]) +print(completion.choices[0].message.content) +``` + ### Embeddings In the OpenAI Python library, an embedding represents a text string as a fixed-length vector of floating point numbers. Embeddings are designed to measure the similarity or relevance between text strings. @@ -169,7 +184,7 @@ text_string = "sample text" model_id = "text-similarity-davinci-001" # compute the embedding of the text -embedding = openai.Embedding.create(input=text_string, engine=model_id)['data'][0]['embedding'] +embedding = openai.Embedding.create(input=text_string, model=model_id)['data'][0]['embedding'] ``` An example of how to call the embeddings method is shown in this [get embeddings notebook](https://github.com/openai/openai-cookbook/blob/main/examples/Get_embeddings.ipynb). @@ -208,7 +223,7 @@ For more information on fine-tuning, read the [fine-tuning guide](https://beta.o ### Moderation -OpenAI provides a Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://beta.openai.com/docs/usage-policies) +OpenAI provides a Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://platform.openai.com/docs/usage-policies) ```python import openai @@ -217,7 +232,7 @@ openai.api_key = "sk-..." # supply your API key however you choose moderation_resp = openai.Moderation.create(input="Here is some perfectly innocuous text that follows all OpenAI content policies.") ``` -See the [moderation guide](https://beta.openai.com/docs/guides/moderation) for more details. +See the [moderation guide](https://platform.openai.com/docs/guides/moderation) for more details. ## Image generation (DALL·E) @@ -229,6 +244,15 @@ image_resp = openai.Image.create(prompt="two dogs playing chess, oil painting", ``` +## Audio transcription (Whisper) +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose +f = open("path/to/file.mp3", "rb") +transcript = openai.Audio.transcribe("whisper-1", f) + +``` + ## Async API Async support is available in the API by prepending `a` to a network-bound method: @@ -238,7 +262,7 @@ import openai openai.api_key = "sk-..." # supply your API key however you choose async def create_completion(): - completion_resp = await openai.Completion.acreate(prompt="This is a test", engine="davinci") + completion_resp = await openai.Completion.acreate(prompt="This is a test", model="davinci") ``` @@ -255,7 +279,7 @@ openai.aiosession.set(ClientSession()) await openai.aiosession.get().close() ``` -See the [usage guide](https://beta.openai.com/docs/guides/images) for more details. +See the [usage guide](https://platform.openai.com/docs/guides/images) for more details. ## Requirements diff --git a/openai/__init__.py b/openai/__init__.py index 879fe33d04..2b184226df 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -7,6 +7,8 @@ from typing import Optional, TYPE_CHECKING from openai.api_resources import ( + Audio, + ChatCompletion, Completion, Customer, Edit, @@ -52,6 +54,8 @@ __all__ = [ "APIError", + "Audio", + "ChatCompletion", "Completion", "Customer", "Edit", @@ -74,7 +78,7 @@ "app_info", "ca_bundle_path", "debug", - "enable_elemetry", + "enable_telemetry", "log", "organization", "proxy", diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 04b65fdcdb..64e55e82ef 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -476,8 +476,8 @@ def _prepare_request_raw( abs_url = _build_api_url(http://github.com/abs_url,%20encoded_params) elif method in {"post", "put"}: if params and files: - raise ValueError("At most one of params and files may be specified.") - if params: + data = params + if params and not files: data = json.dumps(params).encode() headers["Content-Type"] = "application/json" else: diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index 4692b9ac3f..b06ebb4be9 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -1,3 +1,5 @@ +from openai.api_resources.audio import Audio # noqa: F401 +from openai.api_resources.chat_completion import ChatCompletion # noqa: F401 from openai.api_resources.completion import Completion # noqa: F401 from openai.api_resources.customer import Customer # noqa: F401 from openai.api_resources.deployment import Deployment # noqa: F401 diff --git a/openai/api_resources/audio.py b/openai/api_resources/audio.py new file mode 100644 index 0000000000..8ad6705680 --- /dev/null +++ b/openai/api_resources/audio.py @@ -0,0 +1,205 @@ +from typing import Any, List + +import openai +from openai import api_requestor, util +from openai.api_resources.abstract import APIResource + + +class Audio(APIResource): + OBJECT_NAME = "audio" + + @classmethod + def _get_url(http://github.com/cls,%20action): + return cls.class_url() + f"/{action}" + + @classmethod + def _prepare_request( + cls, + file, + filename, + model, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + files: List[Any] = [] + data = { + "model": model, + **params, + } + files.append(("file", (filename, file, "application/octet-stream"))) + return requestor, files, data + + @classmethod + def transcribe( + cls, + model, + file, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request(file, file.name, model, **params) + url = cls._get_url("http://github.com/transcriptions") + response, _, api_key = requestor.request("post", url, files=files, params=data) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def translate( + cls, + model, + file, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request(file, file.name, model, **params) + url = cls._get_url("http://github.com/translations") + response, _, api_key = requestor.request("post", url, files=files, params=data) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def transcribe_raw( + cls, + model, + file, + filename, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request(file, filename, model, **params) + url = cls._get_url("http://github.com/transcriptions") + response, _, api_key = requestor.request("post", url, files=files, params=data) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def translate_raw( + cls, + model, + file, + filename, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request(file, filename, model, **params) + url = cls._get_url("http://github.com/translations") + response, _, api_key = requestor.request("post", url, files=files, params=data) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def atranscribe( + cls, + model, + file, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request(file, file.name, model, **params) + url = cls._get_url("http://github.com/transcriptions") + response, _, api_key = await requestor.arequest( + "post", url, files=files, params=data + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def atranslate( + cls, + model, + file, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request(file, file.name, model, **params) + url = cls._get_url("http://github.com/translations") + response, _, api_key = await requestor.arequest( + "post", url, files=files, params=data + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def atranscribe_raw( + cls, + model, + file, + filename, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request(file, filename, model, **params) + url = cls._get_url("http://github.com/transcriptions") + response, _, api_key = await requestor.arequest( + "post", url, files=files, params=data + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def atranslate_raw( + cls, + model, + file, + filename, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request(file, filename, model, **params) + url = cls._get_url("http://github.com/translations") + response, _, api_key = await requestor.arequest( + "post", url, files=files, params=data + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) diff --git a/openai/api_resources/chat_completion.py b/openai/api_resources/chat_completion.py new file mode 100644 index 0000000000..39fb58b33a --- /dev/null +++ b/openai/api_resources/chat_completion.py @@ -0,0 +1,50 @@ +import time + +from openai import util +from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.error import TryAgain + + +class ChatCompletion(EngineAPIResource): + engine_required = False + OBJECT_NAME = "chat.completions" + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new chat completion for the provided messages and parameters. + + See https://platform.openai.com/docs/api-reference/chat-completions/create + for a list of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + while True: + try: + return super().create(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new chat completion for the provided messages and parameters. + + See https://platform.openai.com/docs/api-reference/chat-completions/create + for a list of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + while True: + try: + return await super().acreate(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/completion.py b/openai/api_resources/completion.py index 6912b4b730..7b9c44bd08 100644 --- a/openai/api_resources/completion.py +++ b/openai/api_resources/completion.py @@ -14,7 +14,7 @@ def create(cls, *args, **kwargs): """ Creates a new completion for the provided prompt and parameters. - See https://beta.openai.com/docs/api-reference/completions/create for a list + See https://platform.openai.com/docs/api-reference/completions/create for a list of valid parameters. """ start = time.time() @@ -34,7 +34,7 @@ async def acreate(cls, *args, **kwargs): """ Creates a new completion for the provided prompt and parameters. - See https://beta.openai.com/docs/api-reference/completions/create for a list + See https://platform.openai.com/docs/api-reference/completions/create for a list of valid parameters. """ start = time.time() diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py index 5f1cfe5609..4eb97c68bf 100644 --- a/openai/api_resources/embedding.py +++ b/openai/api_resources/embedding.py @@ -16,7 +16,7 @@ def create(cls, *args, **kwargs): """ Creates a new embedding for the provided input and parameters. - See https://beta.openai.com/docs/api-reference/embeddings for a list + See https://platform.openai.com/docs/api-reference/embeddings for a list of valid parameters. """ start = time.time() @@ -56,7 +56,7 @@ async def acreate(cls, *args, **kwargs): """ Creates a new embedding for the provided input and parameters. - See https://beta.openai.com/docs/api-reference/embeddings for a list + See https://platform.openai.com/docs/api-reference/embeddings for a list of valid parameters. """ start = time.time() diff --git a/openai/cli.py b/openai/cli.py index a7f7654dc8..e9201b18b9 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -108,6 +108,44 @@ def list(cls, args): display(engines) +class ChatCompletion: + @classmethod + def create(cls, args): + if args.n is not None and args.n > 1 and args.stream: + raise ValueError( + "Can't stream chat completions with n>1 with the current CLI" + ) + + messages = [ + {"role": role, "content": content} for role, content in args.message + ] + + resp = openai.ChatCompletion.create( + # Required + model=args.model, + messages=messages, + # Optional + n=args.n, + max_tokens=100, + temperature=args.temperature, + top_p=args.top_p, + stop=args.stop, + stream=args.stream, + ) + if not args.stream: + resp = [resp] + + for part in resp: + choices = part["choices"] + for c_idx, c in enumerate(sorted(choices, key=lambda s: s["index"])): + if len(choices) > 1: + sys.stdout.write("===== Chat Completion {} =====\n".format(c_idx)) + sys.stdout.write(c["message"]["content"]) + if len(choices) > 1: + sys.stdout.write("\n") + sys.stdout.flush() + + class Completion: @classmethod def create(cls, args): @@ -255,6 +293,43 @@ def create_edit(cls, args): print(resp) +class Audio: + @classmethod + def transcribe(cls, args): + with open(args.file, "rb") as r: + file_reader = BufferReader(r.read(), desc="Upload progress") + + resp = openai.Audio.transcribe_raw( + # Required + model=args.model, + file=file_reader, + filename=args.file, + # Optional + response_format=args.response_format, + language=args.language, + temperature=args.temperature, + prompt=args.prompt, + ) + print(resp) + + @classmethod + def translate(cls, args): + with open(args.file, "rb") as r: + file_reader = BufferReader(r.read(), desc="Upload progress") + resp = openai.Audio.translate_raw( + # Required + model=args.model, + file=file_reader, + filename=args.file, + # Optional + response_format=args.response_format, + language=args.language, + temperature=args.temperature, + prompt=args.prompt, + ) + print(resp) + + class FineTune: @classmethod def list(cls, args): @@ -505,7 +580,6 @@ def delete(cls, args): @classmethod def prepare_data(cls, args): - sys.stdout.write("Analyzing...\n") fname = args.file auto_accept = args.quiet @@ -633,12 +707,68 @@ def help(args): ) sub.set_defaults(func=Engine.generate) + # Chat Completions + sub = subparsers.add_parser("chat_completions.create") + + sub._action_groups.pop() + req = sub.add_argument_group("required arguments") + opt = sub.add_argument_group("optional arguments") + + req.add_argument( + "-m", + "--model", + help="The model to use.", + required=True, + ) + req.add_argument( + "-g", + "--message", + action="http://github.com/append", + nargs=2, + metavar=("ROLE", "CONTENT"), + help="A message in `{role} {content}` format. Use this argument multiple times to add multiple messages.", + required=True, + ) + opt.add_argument( + "-n", + "--n", + help="How many completions to generate for the conversation.", + type=int, + ) + opt.add_argument( + "-M", "--max-tokens", help="The maximum number of tokens to generate.", type=int + ) + opt.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, + ) + opt.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, + ) + opt.add_argument( + "--stop", + help="A stop sequence at which to stop generating tokens for the message.", + ) + opt.add_argument( + "--stream", help="Stream messages as they're ready.", action="http://github.com/store_true" + ) + sub.set_defaults(func=ChatCompletion.create) + # Completions sub = subparsers.add_parser("completions.create") sub.add_argument( "-e", "--engine", - help="The engine to use. See https://beta.openai.com/docs/engines for more about what engines are available.", + help="The engine to use. See https://platform.openai.com/docs/engines for more about what engines are available.", ) sub.add_argument( "-m", @@ -725,7 +855,7 @@ def help(args): sub.add_argument( "-p", "--purpose", - help="Why are you uploading this file? (see https://beta.openai.com/docs/api-reference/ for purposes)", + help="Why are you uploading this file? (see https://platform.openai.com/docs/api-reference/ for purposes)", required=True, ) sub.set_defaults(func=File.create) @@ -924,6 +1054,30 @@ def help(args): sub.add_argument("--response-format", type=str, default="url") sub.set_defaults(func=Image.create_variation) + # Audio + # transcriptions + sub = subparsers.add_parser("audio.transcribe") + # 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=Audio.transcribe) + # translations + sub = subparsers.add_parser("audio.translate") + # 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=Audio.translate) + def wandb_register(parser): subparsers = parser.add_subparsers( diff --git a/openai/tests/asyncio/test_endpoints.py b/openai/tests/asyncio/test_endpoints.py index 3dc355b733..1b146e6749 100644 --- a/openai/tests/asyncio/test_endpoints.py +++ b/openai/tests/asyncio/test_endpoints.py @@ -7,17 +7,18 @@ import openai from openai import error - pytestmark = [pytest.mark.asyncio] # FILE TESTS async def test_file_upload(): result = await openai.File.acreate( - file=io.StringIO(json.dumps({"text": "test file data"})), - purpose="search", + file=io.StringIO( + json.dumps({"prompt": "test file data", "completion": "tada"}) + ), + purpose="fine-tune", ) - assert result.purpose == "search" + assert result.purpose == "fine-tune" assert "id" in result result = await openai.File.aretrieve(id=result.id) diff --git a/openai/tests/test_endpoints.py b/openai/tests/test_endpoints.py index 565bd41880..c3fc1094bb 100644 --- a/openai/tests/test_endpoints.py +++ b/openai/tests/test_endpoints.py @@ -22,6 +22,32 @@ def test_file_upload(): assert result.status == "uploaded" +# CHAT COMPLETION TESTS +def test_chat_completions(): + result = openai.ChatCompletion.create( + model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello!"}] + ) + assert len(result.choices) == 1 + + +def test_chat_completions_multiple(): + result = openai.ChatCompletion.create( + model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello!"}], n=5 + ) + assert len(result.choices) == 5 + + +def test_chat_completions_streaming(): + result = None + events = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": "Hello!"}], + stream=True, + ) + for result in events: + assert len(result.choices) == 1 + + # COMPLETION TESTS def test_completions(): result = openai.Completion.create(prompt="This was a test", n=5, engine="ada") diff --git a/openai/tests/test_long_examples_validator.py b/openai/tests/test_long_examples_validator.py index a9334d4f75..0cac1360cd 100644 --- a/openai/tests/test_long_examples_validator.py +++ b/openai/tests/test_long_examples_validator.py @@ -4,7 +4,12 @@ import pytest -from openai.datalib import HAS_PANDAS, HAS_NUMPY, NUMPY_INSTRUCTIONS, PANDAS_INSTRUCTIONS +from openai.datalib import ( + HAS_NUMPY, + HAS_PANDAS, + NUMPY_INSTRUCTIONS, + PANDAS_INSTRUCTIONS, +) @pytest.mark.skipif(not HAS_PANDAS, reason=PANDAS_INSTRUCTIONS) @@ -29,7 +34,8 @@ def test_long_examples_validator() -> None: {"prompt": long_prompt, "completion": long_completion}, # 2 of 2 duplicates ] - with NamedTemporaryFile(suffix="jsonl", mode="w") as training_data: + with NamedTemporaryFile(suffix=".jsonl", mode="w") as training_data: + print(training_data.name) for prompt_completion_row in unprepared_training_data: training_data.write(json.dumps(prompt_completion_row) + "\n") training_data.flush() diff --git a/openai/validators.py b/openai/validators.py index c5a3dd7890..b15e59bee3 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -242,7 +242,7 @@ def add_suffix(x, suffix): immediate_msg += f"\n WARNING: Some of your prompts contain the suffix `{common_suffix}` more than once. We strongly suggest that you review your prompts and add a unique suffix" else: - immediate_msg = "\n- Your data does not contain a common separator at the end of your prompts. Having a separator string appended to the end of the prompt makes it clearer to the fine-tuned model where the completion should begin. See https://beta.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples. If you intend to do open-ended generation, then you should leave the prompts empty" + immediate_msg = "\n- Your data does not contain a common separator at the end of your prompts. Having a separator string appended to the end of the prompt makes it clearer to the fine-tuned model where the completion should begin. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples. If you intend to do open-ended generation, then you should leave the prompts empty" if common_suffix == "": optional_msg = ( @@ -393,7 +393,7 @@ def add_suffix(x, suffix): immediate_msg += f"\n WARNING: Some of your completions contain the suffix `{common_suffix}` more than once. We suggest that you review your completions and add a unique ending" else: - immediate_msg = "\n- Your data does not contain a common ending at the end of your completions. Having a common ending string appended to the end of the completion makes it clearer to the fine-tuned model where the completion should end. See https://beta.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples." + immediate_msg = "\n- Your data does not contain a common ending at the end of your completions. Having a common ending string appended to the end of the completion makes it clearer to the fine-tuned model where the completion should end. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples." if common_suffix == "": optional_msg = ( @@ -428,7 +428,7 @@ def add_space_start(x): immediate_msg = None if df.completion.str[:1].nunique() != 1 or df.completion.values[0][0] != " ": - immediate_msg = "\n- The completion should start with a whitespace character (` `). This tends to produce better results due to the tokenization we use. See https://beta.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details" + immediate_msg = "\n- The completion should start with a whitespace character (` `). This tends to produce better results due to the tokenization we use. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details" optional_msg = "Add a whitespace character to the beginning of the completion" optional_fn = add_space_start return Remediation( @@ -462,7 +462,7 @@ def lower_case(x): if count_upper * 2 > count_lower: return Remediation( name="lower_case", - immediate_msg=f"\n- More than a third of your `{column}` column/key is uppercase. Uppercase {column}s tends to perform worse than a mixture of case encountered in normal language. We recommend to lower case the data if that makes sense in your domain. See https://beta.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details", + immediate_msg=f"\n- More than a third of your `{column}` column/key is uppercase. Uppercase {column}s tends to perform worse than a mixture of case encountered in normal language. We recommend to lower case the data if that makes sense in your domain. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details", optional_msg=f"Lowercase all your data in column/key `{column}`", optional_fn=lower_case, ) diff --git a/openai/version.py b/openai/version.py index 9daab2e53f..0f467df874 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.26.5" +VERSION = "0.27.0" From 75c90a71e88e4194ce22c71edeb3d2dee7f6ac93 Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Wed, 1 Mar 2023 12:09:37 -0600 Subject: [PATCH 090/914] Create chatml.md (#238) * Create chatml.md * Update chatml.md --- chatml.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 chatml.md diff --git a/chatml.md b/chatml.md new file mode 100644 index 0000000000..21e2e315e9 --- /dev/null +++ b/chatml.md @@ -0,0 +1,87 @@ +Traditionally, GPT models consumed unstructured text. ChatGPT models +instead expect a structured format, called Chat Markup Language +(ChatML for short). +ChatML documents consists of a sequence of messages. Each message +contains a header (which today consists of who said it, but in the +future will contain other metadata) and contents (which today is a +text payload, but in the future will contain other datatypes). +We are still evolving ChatML, but the current version (ChatML v0) can +be represented with our upcoming "list of dicts" JSON format as +follows: +``` +[ + {"token": "<|im_start|>"}, + "system\nYou are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.\nKnowledge cutoff: 2021-09-01\nCurrent date: 2023-03-01", + {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, + "user\nHow are you", + {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, + "assistant\nI am doing well!", + {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, + "user\nHow are you now?", + {"token": "<|im_end|>"}, "\n" +] +``` +You could also represent it in the classic "unsafe raw string" +format. Note this format inherently allows injections from user input +containing special-token syntax, similar to a SQL injections: +``` +<|im_start|>system +You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible. +Knowledge cutoff: 2021-09-01 +Current date: 2023-03-01<|im_end|> +<|im_start|>user +How are you<|im_end|> +<|im_start|>assistant +I am doing well!<|im_end|> +<|im_start|>user +How are you now?<|im_end|> +``` +## Non-chat use-cases +ChatML can be applied to classic GPT use-cases that are not +traditionally thought of as chat. For example, instruction following +(where a user requests for the AI to complete an instruction) can be +implemented as a ChatML query like the following: +``` +[ + {"token": "<|im_start|>"}, + "user\nList off some good ideas:", + {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, + "assistant" +] +``` +We do not currently allow autocompleting of partial messages, +``` +[ + {"token": "<|im_start|>"}, + "system\nPlease autocomplete the user's message." + {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, + "user\nThis morning I decided to eat a giant" +] +``` +Note that ChatML makes explicit to the model the source of each piece +of text, and particularly shows the boundary between human and AI +text. This gives an opportunity to mitigate and eventually solve +injections, as the model can tell which instructions come from the +developer, the user, or its own input. +## Few-shot prompting +In general, we recommend adding few-shot examples using separate +`system` messages with a `name` field of `example_user` or +`example_assistant`. For example, here is a 1-shot prompt: +``` +<|im_start|>system +Translate from English to French +<|im_end|> +<|im_start|>system name=example_user +How are you? +<|im_end|> +<|im_start|>system name=example_assistant +Comment allez-vous? +<|im_end|> +<|im_start|>user +{{user input here}}<|im_end|> +``` +If adding instructions in the `system` message doesn't work, you can +also try putting them into a `user` message. (In the near future, we +will train our models to be much more steerable via the system +message. But to date, we have trained only on a few system messages, +so the models pay much most attention to user examples.) From 1165639843d1be71b009e17b9c29686d05299d4e Mon Sep 17 00:00:00 2001 From: Greg Brockman Date: Wed, 1 Mar 2023 15:12:41 -0800 Subject: [PATCH 091/914] Update chatml.md (#244) --- chatml.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chatml.md b/chatml.md index 21e2e315e9..02f22b9f1c 100644 --- a/chatml.md +++ b/chatml.md @@ -1,3 +1,9 @@ +(This document is a preview of the underlying format consumed by +ChatGPT models. As a developer, you can use our [higher-level +API](https://platform.openai.com/docs/guides/chat) and won't need to +iteract directly with this format today — but expect to have the +option in the future!) + Traditionally, GPT models consumed unstructured text. ChatGPT models instead expect a structured format, called Chat Markup Language (ChatML for short). From d008c3b7021e8b38701ff6bf7683ded37ca30f1d Mon Sep 17 00:00:00 2001 From: David Thayer Date: Thu, 2 Mar 2023 09:21:11 -0800 Subject: [PATCH 092/914] Typo, line 93 (#259) Changed "much most" to "much more" --- chatml.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatml.md b/chatml.md index 02f22b9f1c..c2f6131922 100644 --- a/chatml.md +++ b/chatml.md @@ -90,4 +90,4 @@ If adding instructions in the `system` message doesn't work, you can also try putting them into a `user` message. (In the near future, we will train our models to be much more steerable via the system message. But to date, we have trained only on a few system messages, -so the models pay much most attention to user examples.) +so the models pay much more attention to user examples.) From 81e624e8ba3b0d36e0c14a437c7ff4171d35b5c0 Mon Sep 17 00:00:00 2001 From: Kevin Huang <44907675+Ynng@users.noreply.github.com> Date: Thu, 2 Mar 2023 12:22:41 -0500 Subject: [PATCH 093/914] Fixed a tiny typo in chatml.md (#249) iteract -> interact --- chatml.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatml.md b/chatml.md index c2f6131922..ea53e22c27 100644 --- a/chatml.md +++ b/chatml.md @@ -1,7 +1,7 @@ (This document is a preview of the underlying format consumed by ChatGPT models. As a developer, you can use our [higher-level API](https://platform.openai.com/docs/guides/chat) and won't need to -iteract directly with this format today — but expect to have the +interact directly with this format today — but expect to have the option in the future!) Traditionally, GPT models consumed unstructured text. ChatGPT models From 81ad94e7455aa4db6b5f5285212b69aaa6d510c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kanta=20Yamaoka=20=28=E5=B1=B1=E5=B2=A1=E5=B9=B9=E5=A4=AA?= =?UTF-8?q?=29?= <49053649+kantasv@users.noreply.github.com> Date: Fri, 3 Mar 2023 23:00:40 +0900 Subject: [PATCH 094/914] fixed a minor typo in chatml.md (#260) --- chatml.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatml.md b/chatml.md index ea53e22c27..41f5bbdd9e 100644 --- a/chatml.md +++ b/chatml.md @@ -7,7 +7,7 @@ option in the future!) Traditionally, GPT models consumed unstructured text. ChatGPT models instead expect a structured format, called Chat Markup Language (ChatML for short). -ChatML documents consists of a sequence of messages. Each message +ChatML documents consist of a sequence of messages. Each message contains a header (which today consists of who said it, but in the future will contain other metadata) and contents (which today is a text payload, but in the future will contain other datatypes). From 8470953ce2dd96a47d7c5175f5ced2d05707edaf Mon Sep 17 00:00:00 2001 From: Pinxi <52986595+FizzyAgent@users.noreply.github.com> Date: Mon, 6 Mar 2023 19:13:41 -0800 Subject: [PATCH 095/914] Use kwargs (#236) --- openai/api_resources/abstract/api_resource.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py index 53a7dec799..5d54bb9fd8 100644 --- a/openai/api_resources/abstract/api_resource.py +++ b/openai/api_resources/abstract/api_resource.py @@ -16,7 +16,7 @@ class APIResource(OpenAIObject): def retrieve( cls, id, api_key=None, request_id=None, request_timeout=None, **params ): - instance = cls(id, api_key, **params) + instance = cls(id=id, api_key=api_key, **params) instance.refresh(request_id=request_id, request_timeout=request_timeout) return instance @@ -24,7 +24,7 @@ def retrieve( def aretrieve( cls, id, api_key=None, request_id=None, request_timeout=None, **params ): - instance = cls(id, api_key, **params) + instance = cls(id=id, api_key=api_key, **params) return instance.arefresh(request_id=request_id, request_timeout=request_timeout) def refresh(self, request_id=None, request_timeout=None): From fad5a24bb905fba5473007f3400c607277b04184 Mon Sep 17 00:00:00 2001 From: Ethan Knights Date: Tue, 7 Mar 2023 03:24:17 +0000 Subject: [PATCH 096/914] make SQL example pointer consistent (#273) --- examples/codex/backtranslation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/codex/backtranslation.py b/examples/codex/backtranslation.py index 6fff98bb2d..6390e5e174 100644 --- a/examples/codex/backtranslation.py +++ b/examples/codex/backtranslation.py @@ -1 +1,2 @@ -# this example has moved to https://github.com/openai/openai-cookbook/blob/main/examples/Backtranslation_of_SQL_queries.py +# This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) +# at [examples/Backtranslation_of_SQL_queries](https://github.com/openai/openai-cookbook/blob/main/examples/Backtranslation_of_SQL_queries.py) From 9b5f8dbd9fda64c997621704283b11bb747778dd Mon Sep 17 00:00:00 2001 From: guaguaguaxia Date: Thu, 9 Mar 2023 04:33:49 +0800 Subject: [PATCH 097/914] fix:fix response format bug (#282) * fix:fix bug * fix:fix response_format bug * fix:fix response_format bug * fix:fix response_format bug * fix:fix response_format bug * fix:fix response_format bug * fix:fix response_format bug * fix:fix response_format bug --- openai/api_requestor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 64e55e82ef..8a0e6cabc2 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -666,7 +666,10 @@ def _interpret_response_line( headers=rheaders, ) try: - data = json.loads(rbody) + if 'text/plain' in rheaders.get('Content-Type'): + data = rbody + else: + data = json.loads(rbody) except (JSONDecodeError, UnicodeDecodeError) as e: raise error.APIError( f"HTTP code {rcode} from API ({rbody})", rbody, rcode, headers=rheaders From 94428401b4f71596e4a1331102a6beee9d8f0bc4 Mon Sep 17 00:00:00 2001 From: Michelle Pokrass Date: Wed, 8 Mar 2023 12:37:21 -0800 Subject: [PATCH 098/914] Bump version (#289) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 0f467df874..e88778bd11 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.0" +VERSION = "0.27.1" From 6c23b7f62d92e0fabdd9db72bd38d2038caeb524 Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 10 Mar 2023 19:48:42 -0800 Subject: [PATCH 099/914] Create wheels (#297) --- Makefile | 10 ++++++---- openai/version.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index f80c2b0a51..b3ef11eea1 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,11 @@ .PHONY: build upload build: - python setup.py sdist + rm -rf dist/ build/ + python -m pip install build + python -m build . upload: - twine upload dist/openai-*.tar.gz - rm dist/openai-*.tar.gz - + python -m pip install twine + python -m twine upload dist/openai-* + rm -rf dist diff --git a/openai/version.py b/openai/version.py index e88778bd11..cba8d8927b 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.1" +VERSION = "0.27.2" From 1d6142f376067e401492ca92ff88a08deb47d6ba Mon Sep 17 00:00:00 2001 From: Anthony Chiu Date: Tue, 14 Mar 2023 07:19:13 +0800 Subject: [PATCH 100/914] Update README.md (#301) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dbd51a627..76f9e9b58e 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ print(completion.choices[0].text) ### Params -All endpoints have a `.create` method that supports a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.TimeoutError` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). +All endpoints have a `.create` method that supports a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.Timeout` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). ### Microsoft Azure Endpoints From f3e06c05c59cd526ac446b0099f3d4195a8e67db Mon Sep 17 00:00:00 2001 From: Muluken Hakim <91600753+MuleHakim@users.noreply.github.com> Date: Wed, 22 Mar 2023 06:03:07 +0300 Subject: [PATCH 101/914] 'let us know at on our support page' to 'let us know on our support page' (#308) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76f9e9b58e..49ae2efd22 100644 --- a/README.md +++ b/README.md @@ -287,7 +287,7 @@ See the [usage guide](https://platform.openai.com/docs/guides/images) for more d In general, we want to support the versions of Python that our customers are using. If you run into problems with any version -issues, please let us know at on our [support page](https://help.openai.com/en/). +issues, please let us know on our [support page](https://help.openai.com/en/). ## Credit From 62a208d93ad2e8ffdbd21c4adde5ad9e3b3daea5 Mon Sep 17 00:00:00 2001 From: Pinxi <52986595+FizzyAgent@users.noreply.github.com> Date: Wed, 22 Mar 2023 11:04:36 +0800 Subject: [PATCH 102/914] add cli version arg (#298) --- openai/_openai_scripts.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openai/_openai_scripts.py b/openai/_openai_scripts.py index d234256c62..aaf23b0cad 100755 --- a/openai/_openai_scripts.py +++ b/openai/_openai_scripts.py @@ -4,6 +4,7 @@ import sys import openai +from openai import version from openai.cli import api_register, display_error, tools_register, wandb_register logger = logging.getLogger() @@ -15,6 +16,12 @@ def main(): parser = argparse.ArgumentParser(description=None) + parser.add_argument( + "-V", + "--version", + action="http://github.com/version", + version="%(prog)s " + version.VERSION, + ) parser.add_argument( "-v", "--verbose", From 2b0bb90712f48246a7dce5a755a7e93b169da6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCrtz?= <120655914+cmurtz-msft@users.noreply.github.com> Date: Sat, 25 Mar 2023 16:25:07 +0100 Subject: [PATCH 103/914] 2023-03-15-preview as default (#335) --- README.md | 6 +++--- openai/__init__.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 49ae2efd22..fb5a3e32f9 100644 --- a/README.md +++ b/README.md @@ -84,10 +84,10 @@ import openai openai.api_type = "azure" openai.api_key = "..." openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2022-12-01" +openai.api_version = "2023-03-15-preview" # create a completion -completion = openai.Completion.create(engine="deployment-name", prompt="Hello world") +completion = openai.Completion.create(deployment_id="deployment-name", prompt="Hello world") # print the completion print(completion.choices[0].text) @@ -116,7 +116,7 @@ token = default_credential.get_token("https://cognitiveservices.azure.com/.defau%20openai.api_type%20="azure_ad" openai.api_key = token.token openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2022-12-01" +openai.api_version = "2023-03-15-preview" # ... ``` diff --git a/openai/__init__.py b/openai/__init__.py index 2b184226df..f80085eada 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -37,7 +37,7 @@ api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") api_version = ( - "2022-12-01" if api_type in ("azure", "azure_ad", "azuread") else None + "2023-03-15-preview" if api_type in ("azure", "azure_ad", "azuread") else None ) verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None From 237448dc072a2c062698da3f9f512fae38300c1c Mon Sep 17 00:00:00 2001 From: liuliu <44393673+liuliuOD@users.noreply.github.com> Date: Sat, 25 Mar 2023 23:26:16 +0800 Subject: [PATCH 104/914] doc: add symbol comma into chatml.md (#329) --- chatml.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chatml.md b/chatml.md index 41f5bbdd9e..5fe9fe4c03 100644 --- a/chatml.md +++ b/chatml.md @@ -59,7 +59,7 @@ We do not currently allow autocompleting of partial messages, ``` [ {"token": "<|im_start|>"}, - "system\nPlease autocomplete the user's message." + "system\nPlease autocomplete the user's message.", {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, "user\nThis morning I decided to eat a giant" ] @@ -87,7 +87,7 @@ Comment allez-vous? {{user input here}}<|im_end|> ``` If adding instructions in the `system` message doesn't work, you can -also try putting them into a `user` message. (In the near future, we +also try putting them into a `user` message. (In the near future, we will train our models to be much more steerable via the system message. But to date, we have trained only on a few system messages, so the models pay much more attention to user examples.) From 931b4d23c742349bf5f82a2a995dd46ae684cdd5 Mon Sep 17 00:00:00 2001 From: Lucas Pickup Date: Wed, 29 Mar 2023 14:27:12 -0700 Subject: [PATCH 105/914] Handle OpenAIErrors created with 'str' json_body (#356) `api_requestor.py` recently started respecting `text/plain` response `Content-Type`s. Azure OpenAI can sometimes return errors with that Content-Type that are still JSON strings with error info, which can break OpenAIErrors default behavior of checking for `error` being `in` the supplied `json_body`. Classic Python, `in` works for `dict` or `str` but `json_body["error"]` will only work if `json_body` is a `dict`. This is the minimal fix to now throw KeyError and let the unparsed json_body + message bubble up in the OpenAIError. --- openai/error.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openai/error.py b/openai/error.py index d22e71c902..16692569da 100644 --- a/openai/error.py +++ b/openai/error.py @@ -58,6 +58,7 @@ def __repr__(self): def construct_error_object(self): if ( self.json_body is None + or not isinstance(self.json_body, dict) or "error" not in self.json_body or not isinstance(self.json_body["error"], dict) ): From 6db251894cbe0c0698307f58e5a46e3ee0d471f6 Mon Sep 17 00:00:00 2001 From: Jhen-Jie Hong Date: Thu, 30 Mar 2023 06:47:28 +0800 Subject: [PATCH 106/914] CLI: Fix missing max_tokens arg of chat_completions.create (#317) --- openai/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/cli.py b/openai/cli.py index e9201b18b9..91daa1c5ff 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -126,7 +126,7 @@ def create(cls, args): messages=messages, # Optional n=args.n, - max_tokens=100, + max_tokens=args.max_tokens, temperature=args.temperature, top_p=args.top_p, stop=args.stop, From 15c6aebc6cda59e8d884e1fbc63fd1f6eddbc196 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Thu, 30 Mar 2023 11:41:12 +0700 Subject: [PATCH 107/914] Fix a couple of grammar mistakes in chatml.md (#316) --- chatml.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chatml.md b/chatml.md index 5fe9fe4c03..783e91d996 100644 --- a/chatml.md +++ b/chatml.md @@ -28,8 +28,8 @@ follows: ] ``` You could also represent it in the classic "unsafe raw string" -format. Note this format inherently allows injections from user input -containing special-token syntax, similar to a SQL injections: +format. However, this format inherently allows injections from user +input containing special-token syntax, similar to SQL injections: ``` <|im_start|>system You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible. From 47ce29542e7fc496c1cd0bb323293b7991f45bb0 Mon Sep 17 00:00:00 2001 From: Bojie Li Date: Thu, 30 Mar 2023 13:11:40 +0800 Subject: [PATCH 108/914] set openai.proxy explicitly to Requests lib to override environmental defaults (#339) By default, the Requests library use the proxy settings from the environment variable HTTPS_PROXY. It has higher priority than the "proxies" setting in the session, which may lead to confusing behaviors if both environment variables and the "openai.proxy" config are set. This patch explicitly informs the Requests library to use the "openai.proxy" config. --- openai/api_requestor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 8a0e6cabc2..827b73b78e 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -521,6 +521,7 @@ def request_raw( files=files, stream=stream, timeout=request_timeout if request_timeout else TIMEOUT_SECS, + proxies=_thread_context.session.proxies, ) except requests.exceptions.Timeout as e: raise error.Timeout("Request timed out: {}".format(e)) from e From 39cdc662350cadd2e6a4a382454394d22908004d Mon Sep 17 00:00:00 2001 From: Michelle Pokrass Date: Mon, 3 Apr 2023 14:03:22 -0700 Subject: [PATCH 109/914] [audio] Use supplied api key (#170) (#369) --- openai/api_resources/audio.py | 80 +++++++++++++++++++++++++++++++---- openai/version.py | 2 +- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/openai/api_resources/audio.py b/openai/api_resources/audio.py index 8ad6705680..33820c64a7 100644 --- a/openai/api_resources/audio.py +++ b/openai/api_resources/audio.py @@ -52,7 +52,15 @@ def transcribe( organization=None, **params, ): - requestor, files, data = cls._prepare_request(file, file.name, model, **params) + requestor, files, data = cls._prepare_request( + file=file, + filename=file.name, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + **params, + ) url = cls._get_url("http://github.com/transcriptions") response, _, api_key = requestor.request("post", url, files=files, params=data) return util.convert_to_openai_object( @@ -71,7 +79,15 @@ def translate( organization=None, **params, ): - requestor, files, data = cls._prepare_request(file, file.name, model, **params) + requestor, files, data = cls._prepare_request( + file=file, + filename=file.name, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + **params, + ) url = cls._get_url("http://github.com/translations") response, _, api_key = requestor.request("post", url, files=files, params=data) return util.convert_to_openai_object( @@ -91,7 +107,15 @@ def transcribe_raw( organization=None, **params, ): - requestor, files, data = cls._prepare_request(file, filename, model, **params) + requestor, files, data = cls._prepare_request( + file=file, + filename=filename, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + **params, + ) url = cls._get_url("http://github.com/transcriptions") response, _, api_key = requestor.request("post", url, files=files, params=data) return util.convert_to_openai_object( @@ -111,7 +135,15 @@ def translate_raw( organization=None, **params, ): - requestor, files, data = cls._prepare_request(file, filename, model, **params) + requestor, files, data = cls._prepare_request( + file=file, + filename=filename, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + **params, + ) url = cls._get_url("http://github.com/translations") response, _, api_key = requestor.request("post", url, files=files, params=data) return util.convert_to_openai_object( @@ -130,7 +162,15 @@ async def atranscribe( organization=None, **params, ): - requestor, files, data = cls._prepare_request(file, file.name, model, **params) + requestor, files, data = cls._prepare_request( + file=file, + filename=file.name, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + **params, + ) url = cls._get_url("http://github.com/transcriptions") response, _, api_key = await requestor.arequest( "post", url, files=files, params=data @@ -151,7 +191,15 @@ async def atranslate( organization=None, **params, ): - requestor, files, data = cls._prepare_request(file, file.name, model, **params) + requestor, files, data = cls._prepare_request( + file=file, + filename=file.name, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + **params, + ) url = cls._get_url("http://github.com/translations") response, _, api_key = await requestor.arequest( "post", url, files=files, params=data @@ -173,7 +221,15 @@ async def atranscribe_raw( organization=None, **params, ): - requestor, files, data = cls._prepare_request(file, filename, model, **params) + requestor, files, data = cls._prepare_request( + file=file, + filename=filename, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + **params, + ) url = cls._get_url("http://github.com/transcriptions") response, _, api_key = await requestor.arequest( "post", url, files=files, params=data @@ -195,7 +251,15 @@ async def atranslate_raw( organization=None, **params, ): - requestor, files, data = cls._prepare_request(file, filename, model, **params) + requestor, files, data = cls._prepare_request( + file=file, + filename=filename, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + **params, + ) url = cls._get_url("http://github.com/translations") response, _, api_key = await requestor.arequest( "post", url, files=files, params=data diff --git a/openai/version.py b/openai/version.py index cba8d8927b..cdf81e9c82 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.2" +VERSION = "0.27.3" From ed821c85c35f57d8083afe8fd27502fe8a342a75 Mon Sep 17 00:00:00 2001 From: iaalm Date: Tue, 4 Apr 2023 14:34:08 +0800 Subject: [PATCH 110/914] add support for azure openai api (#359) --- openai/cli.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/openai/cli.py b/openai/cli.py index 91daa1c5ff..e1bf3eac06 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -123,6 +123,7 @@ def create(cls, args): resp = openai.ChatCompletion.create( # Required model=args.model, + engine=args.engine, messages=messages, # Optional n=args.n, @@ -714,12 +715,6 @@ def help(args): req = sub.add_argument_group("required arguments") opt = sub.add_argument_group("optional arguments") - req.add_argument( - "-m", - "--model", - help="The model to use.", - required=True, - ) req.add_argument( "-g", "--message", @@ -729,6 +724,19 @@ def help(args): help="A message in `{role} {content}` format. Use this argument multiple times to add multiple messages.", required=True, ) + + group = opt.add_mutually_exclusive_group() + group.add_argument( + "-e", + "--engine", + help="The engine to use. See https://learn.microsoft.com/en-us/azure/cognitive-services/openai/chatgpt-quickstart?pivots=programming-language-python for more about what engines are available.", + ) + group.add_argument( + "-m", + "--model", + help="The model to use.", + ) + opt.add_argument( "-n", "--n", From cf03fe16a92cd01f2a8867537399c12e183ba58e Mon Sep 17 00:00:00 2001 From: Atty Eleti Date: Tue, 4 Apr 2023 10:41:28 -0700 Subject: [PATCH 111/914] Bump version to v0.27.4 (#373) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index cdf81e9c82..7fb4979dc6 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.3" +VERSION = "0.27.4" From bd269ab91501f3c7a80bc67ddb0269a3e0e498fc Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 7 Apr 2023 17:53:16 -0700 Subject: [PATCH 112/914] Speed up imports (#386) --- openai/__init__.py | 14 ++++++++++-- openai/api_resources/embedding.py | 4 ++-- openai/{datalib.py => datalib/common.py} | 24 -------------------- openai/datalib/numpy_helper.py | 15 ++++++++++++ openai/datalib/pandas_helper.py | 15 ++++++++++++ openai/embeddings_utils.py | 4 ++-- openai/tests/test_long_examples_validator.py | 10 +++----- openai/validators.py | 10 +++++--- openai/wandb_logger.py | 4 ++-- 9 files changed, 58 insertions(+), 42 deletions(-) rename openai/{datalib.py => datalib/common.py} (67%) create mode 100644 openai/datalib/numpy_helper.py create mode 100644 openai/datalib/pandas_helper.py diff --git a/openai/__init__.py b/openai/__init__.py index f80085eada..1a69e24485 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -3,16 +3,26 @@ # Originally forked from the MIT-licensed Stripe Python bindings. import os +import sys +from typing import TYPE_CHECKING, Optional + from contextvars import ContextVar -from typing import Optional, TYPE_CHECKING + +if "pkg_resources" not in sys.modules: + # workaround for the following: + # https://github.com/benoitc/gunicorn/pull/2539 + sys.modules["pkg_resources"] = object() # type: ignore[assignment] + import aiohttp + + del sys.modules["pkg_resources"] from openai.api_resources import ( Audio, ChatCompletion, Completion, Customer, - Edit, Deployment, + Edit, Embedding, Engine, ErrorObject, diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py index 4eb97c68bf..e937636404 100644 --- a/openai/api_resources/embedding.py +++ b/openai/api_resources/embedding.py @@ -1,10 +1,10 @@ import base64 import time - from openai import util from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.datalib import numpy as np, assert_has_numpy +from openai.datalib.numpy_helper import assert_has_numpy +from openai.datalib.numpy_helper import numpy as np from openai.error import TryAgain diff --git a/openai/datalib.py b/openai/datalib/common.py similarity index 67% rename from openai/datalib.py rename to openai/datalib/common.py index 2781cfc4db..678b3395fd 100644 --- a/openai/datalib.py +++ b/openai/datalib/common.py @@ -13,19 +13,6 @@ See also `setup.py`. """ -try: - import numpy -except ImportError: - numpy = None - -try: - import pandas -except ImportError: - pandas = None - -HAS_NUMPY = bool(numpy) -HAS_PANDAS = bool(pandas) - INSTRUCTIONS = """ OpenAI error: @@ -39,18 +26,7 @@ """ NUMPY_INSTRUCTIONS = INSTRUCTIONS.format(library="numpy") -PANDAS_INSTRUCTIONS = INSTRUCTIONS.format(library="pandas") class MissingDependencyError(Exception): pass - - -def assert_has_numpy(): - if not HAS_NUMPY: - raise MissingDependencyError(NUMPY_INSTRUCTIONS) - - -def assert_has_pandas(): - if not HAS_PANDAS: - raise MissingDependencyError(PANDAS_INSTRUCTIONS) diff --git a/openai/datalib/numpy_helper.py b/openai/datalib/numpy_helper.py new file mode 100644 index 0000000000..fb80f2ae54 --- /dev/null +++ b/openai/datalib/numpy_helper.py @@ -0,0 +1,15 @@ +from openai.datalib.common import INSTRUCTIONS, MissingDependencyError + +try: + import numpy +except ImportError: + numpy = None + +HAS_NUMPY = bool(numpy) + +NUMPY_INSTRUCTIONS = INSTRUCTIONS.format(library="numpy") + + +def assert_has_numpy(): + if not HAS_NUMPY: + raise MissingDependencyError(NUMPY_INSTRUCTIONS) diff --git a/openai/datalib/pandas_helper.py b/openai/datalib/pandas_helper.py new file mode 100644 index 0000000000..4e86d7b4f9 --- /dev/null +++ b/openai/datalib/pandas_helper.py @@ -0,0 +1,15 @@ +from openai.datalib.common import INSTRUCTIONS, MissingDependencyError + +try: + import pandas +except ImportError: + pandas = None + +HAS_PANDAS = bool(pandas) + +PANDAS_INSTRUCTIONS = INSTRUCTIONS.format(library="pandas") + + +def assert_has_pandas(): + if not HAS_PANDAS: + raise MissingDependencyError(PANDAS_INSTRUCTIONS) diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index 056c2065c1..08fa94c2ea 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -10,8 +10,8 @@ from tenacity import retry, stop_after_attempt, wait_random_exponential import openai -from openai.datalib import numpy as np -from openai.datalib import pandas as pd +from openai.datalib.numpy_helper import numpy as np +from openai.datalib.pandas_helper import pandas as pd @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) diff --git a/openai/tests/test_long_examples_validator.py b/openai/tests/test_long_examples_validator.py index 0cac1360cd..949a7cbbae 100644 --- a/openai/tests/test_long_examples_validator.py +++ b/openai/tests/test_long_examples_validator.py @@ -4,12 +4,8 @@ import pytest -from openai.datalib import ( - HAS_NUMPY, - HAS_PANDAS, - NUMPY_INSTRUCTIONS, - PANDAS_INSTRUCTIONS, -) +from openai.datalib.numpy_helper import HAS_NUMPY, NUMPY_INSTRUCTIONS +from openai.datalib.pandas_helper import HAS_PANDAS, PANDAS_INSTRUCTIONS @pytest.mark.skipif(not HAS_PANDAS, reason=PANDAS_INSTRUCTIONS) @@ -54,5 +50,5 @@ def test_long_examples_validator() -> None: assert prepared_data_cmd_output.stderr == "" # validate get_long_indexes() applied during optional_fn() call in long_examples_validator() assert "indices of the long examples has changed" in prepared_data_cmd_output.stdout - + return prepared_data_cmd_output.stdout diff --git a/openai/validators.py b/openai/validators.py index b15e59bee3..97083659df 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -2,7 +2,8 @@ import sys from typing import Any, Callable, NamedTuple, Optional -from openai.datalib import pandas as pd, assert_has_pandas +from openai.datalib.pandas_helper import assert_has_pandas +from openai.datalib.pandas_helper import pandas as pd class Remediation(NamedTuple): @@ -158,6 +159,7 @@ def long_examples_validator(df): ft_type = infer_task_type(df) if ft_type != "open-ended generation": + def get_long_indexes(d): long_examples = d.apply( lambda x: len(x.prompt) + len(x.completion) > 10000, axis=1 @@ -171,10 +173,12 @@ def get_long_indexes(d): optional_msg = f"Remove {len(long_indexes)} long examples" def optional_fn(x): - + long_indexes_to_drop = get_long_indexes(x) if long_indexes != long_indexes_to_drop: - sys.stdout.write(f"The indices of the long examples has changed as a result of a previously applied recommendation.\nThe {len(long_indexes_to_drop)} long examples to be dropped are now at the following indices: {long_indexes_to_drop}\n") + sys.stdout.write( + f"The indices of the long examples has changed as a result of a previously applied recommendation.\nThe {len(long_indexes_to_drop)} long examples to be dropped are now at the following indices: {long_indexes_to_drop}\n" + ) return x.drop(long_indexes_to_drop) return Remediation( diff --git a/openai/wandb_logger.py b/openai/wandb_logger.py index ba650d1fe4..fdd8c24adc 100644 --- a/openai/wandb_logger.py +++ b/openai/wandb_logger.py @@ -14,8 +14,8 @@ from pathlib import Path from openai import File, FineTune - from openai.datalib import numpy as np - from openai.datalib import pandas as pd + from openai.datalib.numpy_helper import numpy as np + from openai.datalib.pandas_helper import pandas as pd class WandbLogger: From 6572ef4f85371ec56397d9e125ae5f45ba231744 Mon Sep 17 00:00:00 2001 From: ZheNing Hu Date: Sat, 8 Apr 2023 09:09:55 +0800 Subject: [PATCH 113/914] Add proxy argument support for openai client (#313) Co-authored-by: huzhening.hzn --- README.md | 3 +++ openai/_openai_scripts.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/README.md b/README.md index fb5a3e32f9..c0ca2724a6 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,9 @@ openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world" # generate images via DALL·E API openai api image.create -p "two dogs playing chess, cartoon" -n 1 + +# using openai through a proxy +openai --proxy=http://proxy.com api models.list ``` ## Example code diff --git a/openai/_openai_scripts.py b/openai/_openai_scripts.py index aaf23b0cad..f2aa3ce2b9 100755 --- a/openai/_openai_scripts.py +++ b/openai/_openai_scripts.py @@ -32,6 +32,7 @@ def main(): ) 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", @@ -65,6 +66,13 @@ def help(args): openai.api_base = args.api_base if args.organization is not None: openai.organization = args.organization + if args.proxy is not None: + openai.proxy = {} + for proxy in args.proxy: + if proxy.startswith('https'): + openai.proxy['https'] = proxy + elif proxy.startswith('http'): + openai.proxy['http'] = proxy try: args.func(args) From aa133ce3a883e2957aa1e0111ee27dfef986e787 Mon Sep 17 00:00:00 2001 From: Harutaka Kawamura Date: Sat, 8 Apr 2023 13:38:25 +0900 Subject: [PATCH 114/914] Update __init__.py (#382) --- openai/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openai/__init__.py b/openai/__init__.py index 1a69e24485..3e2cb1e281 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -33,6 +33,7 @@ Moderation, ) from openai.error import APIError, InvalidRequestError, OpenAIError +from openai.version import VERSION if TYPE_CHECKING: from aiohttp import ClientSession @@ -62,6 +63,7 @@ ) # Acts as a global aiohttp ClientSession that reuses connections. # This is user-supplied; otherwise, a session is remade for each request. +__version__ = VERSION __all__ = [ "APIError", "Audio", From ff8d5cf1d4e696d7fcb6ff28857a2b5f8207e34c Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 10 Apr 2023 08:37:26 -0700 Subject: [PATCH 115/914] Fix grabbing value out of header (#388) --- openai/api_requestor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 827b73b78e..a56e16c417 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -667,7 +667,7 @@ def _interpret_response_line( headers=rheaders, ) try: - if 'text/plain' in rheaders.get('Content-Type'): + if 'text/plain' in rheaders.get('Content-Type', ''): data = rbody else: data = json.loads(rbody) From d6fa3bfaae69d639b0dd2e9251b375d7070bbef1 Mon Sep 17 00:00:00 2001 From: joe-at-openai <117690718+joe-at-openai@users.noreply.github.com> Date: Mon, 10 Apr 2023 08:41:17 -0700 Subject: [PATCH 116/914] add case to handle indented .json parsing (#175) (#389) --- openai/validators.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/openai/validators.py b/openai/validators.py index 97083659df..078179a44b 100644 --- a/openai/validators.py +++ b/openai/validators.py @@ -526,14 +526,21 @@ def read_any_format(fname, fields=["prompt", "completion"]): else: pass # this is what we expect for a .jsonl file elif fname.lower().endswith(".json"): - df = pd.read_json(fname, lines=True, dtype=str).fillna("") - if len(df) == 1: - # this is what we expect for a .json file + try: + # to handle case where .json file is actually a .jsonl file + df = pd.read_json(fname, lines=True, dtype=str).fillna("") + if len(df) == 1: + # this code path corresponds to a .json file that has one line + df = pd.read_json(fname, dtype=str).fillna("") + else: + # this is NOT what we expect for a .json file + immediate_msg = "\n- Your JSON file appears to be in a JSONL format. Your file will be converted to JSONL format" + necessary_msg = ( + "Your format `JSON` will be converted to `JSONL`" + ) + except ValueError: + # this code path corresponds to a .json file that has multiple lines (i.e. it is indented) df = pd.read_json(fname, dtype=str).fillna("") - else: - # this is NOT what we expect for a .json file - immediate_msg = "\n- Your JSON file appears to be in a JSONL format. Your file will be converted to JSONL format" - necessary_msg = "Your format `JSON` will be converted to `JSONL`" else: error_msg = "Your file must have one of the following extensions: .CSV, .TSV, .XLSX, .TXT, .JSON or .JSONL" if "." in fname: From c5f0cc41fb5480ecc2f7078be9749fe6a9b8f143 Mon Sep 17 00:00:00 2001 From: Harutaka Kawamura Date: Tue, 18 Apr 2023 02:59:15 +0900 Subject: [PATCH 117/914] Add openai/datalib/__init__.py (#404) Signed-off-by: harupy --- openai/datalib/__init__.py | 14 ++++++++++++++ openai/datalib/common.py | 19 ++----------------- 2 files changed, 16 insertions(+), 17 deletions(-) create mode 100644 openai/datalib/__init__.py diff --git a/openai/datalib/__init__.py b/openai/datalib/__init__.py new file mode 100644 index 0000000000..d02b49cfff --- /dev/null +++ b/openai/datalib/__init__.py @@ -0,0 +1,14 @@ +""" +This module helps make data libraries like `numpy` and `pandas` optional dependencies. + +The libraries add up to 130MB+, which makes it challenging to deploy applications +using this library in environments with code size constraints, like AWS Lambda. + +This module serves as an import proxy and provides a few utilities for dealing with the optionality. + +Since the primary use case of this library (talking to the OpenAI API) doesn't generally require data libraries, +it's safe to make them optional. The rare case when data libraries are needed in the client is handled through +assertions with instructive error messages. + +See also `setup.py`. +""" diff --git a/openai/datalib/common.py b/openai/datalib/common.py index 678b3395fd..96f9908a18 100644 --- a/openai/datalib/common.py +++ b/openai/datalib/common.py @@ -1,23 +1,8 @@ -""" -This module helps make data libraries like `numpy` and `pandas` optional dependencies. - -The libraries add up to 130MB+, which makes it challenging to deploy applications -using this library in environments with code size constraints, like AWS Lambda. - -This module serves as an import proxy and provides a few utilities for dealing with the optionality. - -Since the primary use case of this library (talking to the OpenAI API) doesn’t generally require data libraries, -it’s safe to make them optional. The rare case when data libraries are needed in the client is handled through -assertions with instructive error messages. - -See also `setup.py`. - -""" INSTRUCTIONS = """ -OpenAI error: +OpenAI error: - missing `{library}` + missing `{library}` This feature requires additional dependencies: From 794def37e51f9dbfd1fef7cd451a5d92d5323a43 Mon Sep 17 00:00:00 2001 From: Someet_Sahoo <32305867+Someet-Git@users.noreply.github.com> Date: Tue, 18 Apr 2023 01:40:12 +0530 Subject: [PATCH 118/914] updated the link for OpenAI API (#400) * just updated the url for OpenAi API * updated the docs --- openai/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/util.py b/openai/util.py index 56dfc2e677..f11dc08e8c 100644 --- a/openai/util.py +++ b/openai/util.py @@ -184,5 +184,5 @@ def default_api_key() -> str: return openai.api_key else: raise openai.error.AuthenticationError( - "No API key provided. You can set your API key in code using 'openai.api_key = ', or you can set the environment variable OPENAI_API_KEY=). If your API key is stored in a file, you can point the openai module at it with 'openai.api_key_path = '. You can generate API keys in the OpenAI web interface. See https://onboard.openai.com for details, or email support@openai.com if you have any questions." + "No API key provided. You can set your API key in code using 'openai.api_key = ', or you can set the environment variable OPENAI_API_KEY=). If your API key is stored in a file, you can point the openai module at it with 'openai.api_key_path = '. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details, or email support@openai.com if you have any questions." ) From db3f3526f16ea518bcd16308508e035017f9259e Mon Sep 17 00:00:00 2001 From: Liren Tu Date: Wed, 19 Apr 2023 14:04:20 -0700 Subject: [PATCH 119/914] Pass in keyword arguments in embedding utility functions (#405) * Pass in kwargs in embedding util functions * Remove debug code --- openai/embeddings_utils.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index 08fa94c2ea..1b65e7c8e9 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -15,51 +15,51 @@ @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) -def get_embedding(text: str, engine="text-similarity-davinci-001") -> List[float]: +def get_embedding(text: str, engine="text-similarity-davinci-001", **kwargs) -> List[float]: # replace newlines, which can negatively affect performance. text = text.replace("\n", " ") - return openai.Embedding.create(input=[text], engine=engine)["data"][0]["embedding"] + return openai.Embedding.create(input=[text], engine=engine, **kwargs)["data"][0]["embedding"] @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) async def aget_embedding( - text: str, engine="text-similarity-davinci-001" + text: str, engine="text-similarity-davinci-001", **kwargs ) -> List[float]: # replace newlines, which can negatively affect performance. text = text.replace("\n", " ") - return (await openai.Embedding.acreate(input=[text], engine=engine))["data"][0][ + return (await openai.Embedding.acreate(input=[text], engine=engine, **kwargs))["data"][0][ "embedding" ] @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) def get_embeddings( - list_of_text: List[str], engine="text-similarity-babbage-001" + list_of_text: List[str], engine="text-similarity-babbage-001", **kwargs ) -> List[List[float]]: assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048." # replace newlines, which can negatively affect performance. list_of_text = [text.replace("\n", " ") for text in list_of_text] - data = openai.Embedding.create(input=list_of_text, engine=engine).data + data = openai.Embedding.create(input=list_of_text, engine=engine, **kwargs).data data = sorted(data, key=lambda x: x["index"]) # maintain the same order as input. return [d["embedding"] for d in data] @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) async def aget_embeddings( - list_of_text: List[str], engine="text-similarity-babbage-001" + list_of_text: List[str], engine="text-similarity-babbage-001", **kwargs ) -> List[List[float]]: assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048." # replace newlines, which can negatively affect performance. list_of_text = [text.replace("\n", " ") for text in list_of_text] - data = (await openai.Embedding.acreate(input=list_of_text, engine=engine)).data + data = (await openai.Embedding.acreate(input=list_of_text, engine=engine, **kwargs)).data data = sorted(data, key=lambda x: x["index"]) # maintain the same order as input. return [d["embedding"] for d in data] From 96e7642a8e88260e33f486ea8aa38533602b984c Mon Sep 17 00:00:00 2001 From: hallacy Date: Thu, 27 Apr 2023 09:52:16 -0700 Subject: [PATCH 120/914] Bump to 0.27.5 (#417) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 7fb4979dc6..d1af62ba49 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.4" +VERSION = "0.27.5" From c556584eff3b36c92278e6af62cfe02ebb68fb65 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 27 Apr 2023 10:01:49 -0700 Subject: [PATCH 121/914] allow for user passed requests.Session (#390) --- openai/__init__.py | 7 ++++++- openai/api_requestor.py | 4 ++++ openai/tests/test_endpoints.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/openai/__init__.py b/openai/__init__.py index 3e2cb1e281..ecf663a3b0 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -4,7 +4,7 @@ import os import sys -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Optional, Union, Callable from contextvars import ContextVar @@ -36,6 +36,7 @@ from openai.version import VERSION if TYPE_CHECKING: + import requests from aiohttp import ClientSession api_key = os.environ.get("OPENAI_API_KEY") @@ -58,6 +59,10 @@ debug = False log = None # Set to either 'debug' or 'info', controls console logging +requestssession: Optional[ + Union["requests.Session", Callable[[], "requests.Session"]] +] = None # Provide a requests.Session or Session factory. + aiosession: ContextVar[Optional["ClientSession"]] = ContextVar( "aiohttp-session", default=None ) # Acts as a global aiohttp ClientSession that reuses connections. diff --git a/openai/api_requestor.py b/openai/api_requestor.py index a56e16c417..a8a1fe331e 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -76,6 +76,10 @@ def _aiohttp_proxies_arg(proxy) -> Optional[str]: def _make_session() -> requests.Session: + if openai.requestssession: + if isinstance(openai.requestssession, requests.Session): + return openai.requestssession + return openai.requestssession() if not openai.verify_ssl_certs: warnings.warn("verify_ssl_certs is ignored; openai always verifies.") s = requests.Session() diff --git a/openai/tests/test_endpoints.py b/openai/tests/test_endpoints.py index c3fc1094bb..958e07f091 100644 --- a/openai/tests/test_endpoints.py +++ b/openai/tests/test_endpoints.py @@ -2,6 +2,7 @@ import json import pytest +import requests import openai from openai import error @@ -86,3 +87,32 @@ def test_timeout_does_not_error(): model="ada", request_timeout=10, ) + + +def test_user_session(): + with requests.Session() as session: + openai.requestssession = session + + completion = openai.Completion.create( + prompt="hello world", + model="ada", + ) + assert completion + + +def test_user_session_factory(): + def factory(): + session = requests.Session() + session.mount( + "https://", + requests.adapters.HTTPAdapter(max_retries=4), + ) + return session + + openai.requestssession = factory + + completion = openai.Completion.create( + prompt="hello world", + model="ada", + ) + assert completion From 6fb0efde6fa984f1b666e35814cc3e8e861bbca3 Mon Sep 17 00:00:00 2001 From: Michelle Pokrass Date: Mon, 1 May 2023 15:50:21 -0700 Subject: [PATCH 122/914] fix error messages mentioning support@ (#427) Co-authored-by: Shyamal H Anadkat --- openai/api_requestor.py | 2 +- openai/cli.py | 2 +- openai/error.py | 2 +- openai/util.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index a8a1fe331e..964bbd84e7 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -487,7 +487,7 @@ def _prepare_request_raw( else: raise error.APIConnectionError( "Unrecognized HTTP method %r. This may indicate a bug in the " - "OpenAI bindings. Please contact support@openai.com for " + "OpenAI bindings. Please contact us through our help center at help.openai.com for " "assistance." % (method,) ) diff --git a/openai/cli.py b/openai/cli.py index e1bf3eac06..e924133d72 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -565,7 +565,7 @@ def signal_handler(sig, frame): ) elif status == "failed": sys.stdout.write( - "\nJob failed. Please contact support@openai.com if you need assistance." + "\nJob failed. Please contact us through our help center at help.openai.com if you need assistance." ) sys.stdout.write("\n") diff --git a/openai/error.py b/openai/error.py index 16692569da..2928ef6aa6 100644 --- a/openai/error.py +++ b/openai/error.py @@ -19,7 +19,7 @@ def __init__( except BaseException: http_body = ( "" + "Please contact us through our help center at help.openai.com.>" ) self._message = message diff --git a/openai/util.py b/openai/util.py index f11dc08e8c..5501d5b67e 100644 --- a/openai/util.py +++ b/openai/util.py @@ -184,5 +184,5 @@ def default_api_key() -> str: return openai.api_key else: raise openai.error.AuthenticationError( - "No API key provided. You can set your API key in code using 'openai.api_key = ', or you can set the environment variable OPENAI_API_KEY=). If your API key is stored in a file, you can point the openai module at it with 'openai.api_key_path = '. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details, or email support@openai.com if you have any questions." + "No API key provided. You can set your API key in code using 'openai.api_key = ', or you can set the environment variable OPENAI_API_KEY=). If your API key is stored in a file, you can point the openai module at it with 'openai.api_key_path = '. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details." ) From c975bce15e81f000a1c5130510928e92bfa04fa8 Mon Sep 17 00:00:00 2001 From: Michelle Pokrass Date: Mon, 1 May 2023 15:54:06 -0700 Subject: [PATCH 123/914] bump version (#428) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index d1af62ba49..53d320d921 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.5" +VERSION = "0.27.6" From f24d1931d3067234cf27319c6fa7250f133c8d90 Mon Sep 17 00:00:00 2001 From: Rafal Wojdyla Date: Wed, 3 May 2023 16:24:25 +0100 Subject: [PATCH 124/914] Embeddings util - remove unnecessary sort by index (#430) --- openai/embeddings_utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index 1b65e7c8e9..f1d438c9c0 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -46,7 +46,6 @@ def get_embeddings( list_of_text = [text.replace("\n", " ") for text in list_of_text] data = openai.Embedding.create(input=list_of_text, engine=engine, **kwargs).data - data = sorted(data, key=lambda x: x["index"]) # maintain the same order as input. return [d["embedding"] for d in data] @@ -60,7 +59,6 @@ async def aget_embeddings( list_of_text = [text.replace("\n", " ") for text in list_of_text] data = (await openai.Embedding.acreate(input=list_of_text, engine=engine, **kwargs)).data - data = sorted(data, key=lambda x: x["index"]) # maintain the same order as input. return [d["embedding"] for d in data] From 03f848e3c91e1cb6e1a7860ea91e219f82cc97f4 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Sat, 6 May 2023 09:26:23 -0700 Subject: [PATCH 125/914] allow api_version to be set by environment variable (#438) --- openai/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openai/__init__.py b/openai/__init__.py index ecf663a3b0..3ff85c2662 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -48,8 +48,9 @@ organization = os.environ.get("OPENAI_ORGANIZATION") api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") -api_version = ( - "2023-03-15-preview" if api_type in ("azure", "azure_ad", "azuread") else None +api_version = os.environ.get( + "OPENAI_API_VERSION", + ("2023-03-15-preview" if api_type in ("azure", "azure_ad", "azuread") else None), ) verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None From 91a63f2ccd6468a2b32d66f4ee657ac1a3acf4c6 Mon Sep 17 00:00:00 2001 From: Mike Amy Date: Tue, 9 May 2023 01:27:54 +0700 Subject: [PATCH 126/914] Fixed CLI streamed chat completions. (#319) * Fixed streamed chat completions. Streamed chat completions use a different response structure per returned token, also they may have roles and empty tokens at the end. Handle this sensibly. * Only render content --------- Co-authored-by: Atty Eleti --- openai/cli.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/openai/cli.py b/openai/cli.py index e924133d72..ad08ac3e7b 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -141,9 +141,14 @@ def create(cls, args): for c_idx, c in enumerate(sorted(choices, key=lambda s: s["index"])): if len(choices) > 1: sys.stdout.write("===== Chat Completion {} =====\n".format(c_idx)) - sys.stdout.write(c["message"]["content"]) - if len(choices) > 1: - sys.stdout.write("\n") + if args.stream: + delta = c["delta"] + if "content" in delta: + sys.stdout.write(delta["content"]) + else: + sys.stdout.write(c["message"]["content"]) + if len(choices) > 1: # not in streams + sys.stdout.write("\n") sys.stdout.flush() @@ -203,7 +208,9 @@ def list(cls, args): @classmethod def create(cls, args): - models = openai.Deployment.create(model=args.model, scale_settings={"scale_type": args.scale_type}) + models = openai.Deployment.create( + model=args.model, scale_settings={"scale_type": args.scale_type} + ) print(models) @@ -833,10 +840,15 @@ def help(args): sub = subparsers.add_parser("deployments.delete") sub.add_argument("-i", "--id", required=True, help="The deployment ID") sub.set_defaults(func=Deployment.delete) - + sub = subparsers.add_parser("deployments.create") sub.add_argument("-m", "--model", required=True, help="The model ID") - sub.add_argument("-s", "--scale_type", required=True, help="The scale type. Either 'manual' or 'standard'") + sub.add_argument( + "-s", + "--scale_type", + required=True, + help="The scale type. Either 'manual' or 'standard'", + ) sub.set_defaults(func=Deployment.create) # Models From 2b21516ee87cae266160023da678a82deb791c9b Mon Sep 17 00:00:00 2001 From: Atty Eleti Date: Tue, 9 May 2023 21:37:02 +0530 Subject: [PATCH 127/914] Update README to use gpt-3.5-turbo by default (#441) --- README.md | 76 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index c0ca2724a6..4ce2921287 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Data libraries like `numpy` and `pandas` are not installed by default due to the ```sh pip install openai[datalib] -```` +``` ## Usage @@ -63,16 +63,16 @@ models = openai.Model.list() # print the first model's id print(models.data[0].id) -# create a completion -completion = openai.Completion.create(model="ada", prompt="Hello world") +# create a chat completion +chat_completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) -# print the completion -print(completion.choices[0].text) +# print the chat completion +print(chat_completion.choices[0].message.content) ``` - ### Params -All endpoints have a `.create` method that supports a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.Timeout` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). + +All endpoints have a `.create` method that supports a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.Timeout` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). ### Microsoft Azure Endpoints @@ -86,24 +86,24 @@ openai.api_key = "..." openai.api_base = "https://example-endpoint.openai.azure.com/" openai.api_version = "2023-03-15-preview" -# create a completion -completion = openai.Completion.create(deployment_id="deployment-name", prompt="Hello world") +# create a chat completion +chat_completion = openai.ChatCompletion.create(deployment_id="deployment-name", model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) # print the completion -print(completion.choices[0].text) +print(completion.choices[0].message.content) ``` Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. For a detailed example of how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: -* [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) -* [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) -* [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) + +- [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) +- [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) +- [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) ### Microsoft Azure Active Directory Authentication In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the `api_type` to "azure_ad" and pass the acquired credential token to `api_key`. The rest of the parameters need to be set as specified in the previous section. - ```python from azure.identity import DefaultAzureCredential import openai @@ -120,6 +120,7 @@ openai.api_version = "2023-03-15-preview" # ... ``` + ### Command-line interface This library additionally provides an `openai` command-line utility @@ -130,12 +131,12 @@ which makes it easy to interact with the API from your terminal. Run # list models openai api models.list -# create a completion -openai api completions.create -m ada -p "Hello world" - -# create a chat completion +# create a chat completion (gpt-3.5-turbo, gpt-4, etc.) openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world" +# create a completion (text-davinci-003, text-davinci-002, ada, babbage, curie, davinci, etc.) +openai api completions.create -m ada -p "Hello world" + # generate images via DALL·E API openai api image.create -p "two dogs playing chess, cartoon" -n 1 @@ -147,18 +148,18 @@ openai --proxy=http://proxy.com api models.list Examples of how to use this Python library to accomplish various tasks can be found in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). It contains code examples for: -* Classification using fine-tuning -* Clustering -* Code search -* Customizing embeddings -* Question answering from a corpus of documents -* Recommendations -* Visualization of embeddings -* And more +- Classification using fine-tuning +- Clustering +- Code search +- Customizing embeddings +- Question answering from a corpus of documents +- Recommendations +- Visualization of embeddings +- And more Prior to July 2022, this OpenAI Python library hosted code examples in its examples folder, but since then all examples have been migrated to the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). -### Chat +### Chat Completions Conversational models such as `gpt-3.5-turbo` can be called using the chat completions endpoint. @@ -166,10 +167,22 @@ Conversational models such as `gpt-3.5-turbo` can be called using the chat compl import openai openai.api_key = "sk-..." # supply your API key however you choose -completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world!"}]) +completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) print(completion.choices[0].message.content) ``` +### Completions + +Text models such as `text-davinci-003`, `text-davinci-002` and earlier (`ada`, `babbage`, `curie`, `davinci`, etc.) can be called using the completions endpoint. + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +completion = openai.Completion.create(model="text-davinci-003", prompt="Hello world") +print(completion.choices[0].text) +``` + ### Embeddings In the OpenAI Python library, an embedding represents a text string as a fixed-length vector of floating point numbers. Embeddings are designed to measure the similarity or relevance between text strings. @@ -248,6 +261,7 @@ image_resp = openai.Image.create(prompt="two dogs playing chess, oil painting", ``` ## Audio transcription (Whisper) + ```python import openai openai.api_key = "sk-..." # supply your API key however you choose @@ -264,13 +278,13 @@ Async support is available in the API by prepending `a` to a network-bound metho import openai openai.api_key = "sk-..." # supply your API key however you choose -async def create_completion(): - completion_resp = await openai.Completion.acreate(prompt="This is a test", model="davinci") +async def create_chat_completion(): + chat_completion_resp = await openai.ChatCompletion.acreate(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) ``` To make async requests more efficient, you can pass in your own -``aiohttp.ClientSession``, but you must manually close the client session at the end +`aiohttp.ClientSession`, but you must manually close the client session at the end of your program/event loop: ```python From da828789387755c964c8816d1198d9a61df85b2e Mon Sep 17 00:00:00 2001 From: Gerardo Lecaros Date: Thu, 18 May 2023 15:26:57 -0700 Subject: [PATCH 128/914] Updating code and readme to reference the Azure's newest 2023-05-15 API version. (#452) --- README.md | 4 ++-- openai/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4ce2921287..d61fdee644 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ import openai openai.api_type = "azure" openai.api_key = "..." openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2023-03-15-preview" +openai.api_version = "2023-05-15" # create a chat completion chat_completion = openai.ChatCompletion.create(deployment_id="deployment-name", model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) @@ -116,7 +116,7 @@ token = default_credential.get_token("https://cognitiveservices.azure.com/.defau%20openai.api_type%20="azure_ad" openai.api_key = token.token openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2023-03-15-preview" +openai.api_version = "2023-05-15" # ... ``` diff --git a/openai/__init__.py b/openai/__init__.py index 3ff85c2662..537f12883e 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -50,7 +50,7 @@ api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") api_version = os.environ.get( "OPENAI_API_VERSION", - ("2023-03-15-preview" if api_type in ("azure", "azure_ad", "azuread") else None), + ("2023-05-15" if api_type in ("azure", "azure_ad", "azuread") else None), ) verify_ssl_certs = True # No effect. Certificates are always verified. proxy = None From fe3abd16b582ae784d8a73fd249bcdfebd5752c9 Mon Sep 17 00:00:00 2001 From: hallacy Date: Fri, 19 May 2023 10:28:16 -0700 Subject: [PATCH 129/914] Update version.py (#458) Bump for release --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index 53d320d921..aa89e032d8 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.6" +VERSION = "0.27.7" From 77e5cf97933235b5567a5c9af0eac39f470b1f94 Mon Sep 17 00:00:00 2001 From: John Allard Date: Mon, 5 Jun 2023 17:23:47 -0700 Subject: [PATCH 130/914] Periodically close open `request.Sessions` to avoid buggy interaction with Docker Desktop (#478) * Periodically refresh open `requests.Session`s to mitigate open filehandle issues (#179) As reported, we create a `requests.Session` object on first request to the servers and then reuse it indefinitely. This can leave some open file handles on the OS (not a big deal), but can interact poorly with a bug in Docker Desktop which causes the SDK to entierly break connections to the server. See https://github.com/openai/openai-python/issues/140 for more info. The order of items in the API responses is intentional, and this order is clobbered by the rendering of `OpenAIObject`. This change removes the alphabetic sort of response keys --- openai/api_requestor.py | 10 ++++++++++ openai/openai_object.py | 2 +- openai/tests/test_api_requestor.py | 32 ++++++++++++++++++++++++++++++ openai/tests/test_util.py | 25 +++++++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 964bbd84e7..cde4108bde 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -3,6 +3,7 @@ import platform import sys import threading +import time import warnings from contextlib import asynccontextmanager from json import JSONDecodeError @@ -32,6 +33,7 @@ from openai.util import ApiType TIMEOUT_SECS = 600 +MAX_SESSION_LIFETIME_SECS = 180 MAX_CONNECTION_RETRIES = 2 # Has one attribute per thread, 'session'. @@ -516,6 +518,14 @@ def request_raw( if not hasattr(_thread_context, "session"): _thread_context.session = _make_session() + _thread_context.session_create_time = time.time() + elif ( + time.time() - getattr(_thread_context, "session_create_time", 0) + >= MAX_SESSION_LIFETIME_SECS + ): + _thread_context.session.close() + _thread_context.session = _make_session() + _thread_context.session_create_time = time.time() try: result = _thread_context.session.request( method, diff --git a/openai/openai_object.py b/openai/openai_object.py index c0af6bbc2a..95f8829742 100644 --- a/openai/openai_object.py +++ b/openai/openai_object.py @@ -278,7 +278,7 @@ def __repr__(self): def __str__(self): obj = self.to_dict_recursive() - return json.dumps(obj, sort_keys=True, indent=2) + return json.dumps(obj, indent=2) def to_dict(self): return dict(self) diff --git a/openai/tests/test_api_requestor.py b/openai/tests/test_api_requestor.py index 4998a0ffb2..56e8ec89da 100644 --- a/openai/tests/test_api_requestor.py +++ b/openai/tests/test_api_requestor.py @@ -67,3 +67,35 @@ def test_requestor_azure_ad_headers() -> None: assert headers["Test_Header"] == "Unit_Test_Header" assert "Authorization" in headers assert headers["Authorization"] == "Bearer test_key" + + +@pytest.mark.requestor +def test_requestor_cycle_sessions(mocker: MockerFixture) -> None: + # HACK: we need to purge the _thread_context to not interfere + # with other tests + from openai.api_requestor import _thread_context + + delattr(_thread_context, "session") + + api_requestor = APIRequestor(key="test_key", api_type="azure_ad") + + mock_session = mocker.MagicMock() + mocker.patch("openai.api_requestor._make_session", lambda: mock_session) + + # We don't call `session.close()` if not enough time has elapsed + api_requestor.request_raw("get", "http://example.com/") + mock_session.request.assert_called() + api_requestor.request_raw("get", "http://example.com/") + mock_session.close.assert_not_called() + + mocker.patch("openai.api_requestor.MAX_SESSION_LIFETIME_SECS", 0) + + # Due to 0 lifetime, the original session will be closed before the next call + # and a new session will be created + mock_session_2 = mocker.MagicMock() + mocker.patch("openai.api_requestor._make_session", lambda: mock_session_2) + api_requestor.request_raw("get", "http://example.com/") + mock_session.close.assert_called() + mock_session_2.request.assert_called() + + delattr(_thread_context, "session") diff --git a/openai/tests/test_util.py b/openai/tests/test_util.py index d0ce0ac5c4..6220ccb7f4 100644 --- a/openai/tests/test_util.py +++ b/openai/tests/test_util.py @@ -1,3 +1,4 @@ +import json from tempfile import NamedTemporaryFile import pytest @@ -28,3 +29,27 @@ def test_openai_api_key_path_with_malformed_key(api_key_file) -> None: api_key_file.flush() with pytest.raises(ValueError, match="Malformed API key"): util.default_api_key() + + +def test_key_order_openai_object_rendering() -> None: + sample_response = { + "id": "chatcmpl-7NaPEA6sgX7LnNPyKPbRlsyqLbr5V", + "object": "chat.completion", + "created": 1685855844, + "model": "gpt-3.5-turbo-0301", + "usage": {"prompt_tokens": 57, "completion_tokens": 40, "total_tokens": 97}, + "choices": [ + { + "message": { + "role": "assistant", + "content": "The 2020 World Series was played at Globe Life Field in Arlington, Texas. It was the first time that the World Series was played at a neutral site because of the COVID-19 pandemic.", + }, + "finish_reason": "stop", + "index": 0, + } + ], + } + + oai_object = util.convert_to_openai_object(sample_response) + # The `__str__` method was sorting while dumping to json + assert list(json.loads(str(oai_object)).keys()) == list(sample_response.keys()) From 6e159bf0ba579b8feb2f24d02e4c4dab7afb3e57 Mon Sep 17 00:00:00 2001 From: Gerardo Lecaros Date: Tue, 6 Jun 2023 16:03:49 -0700 Subject: [PATCH 131/914] Support for Azure Dall-e (#439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * This PR updates #337 with updates for it to work with the latest API preview --------- Co-authored-by: Christian Mürtz --- openai/api_requestor.py | 66 +++++++++++++++++++++++++++++++++++ openai/api_resources/image.py | 53 ++++++++++++++++++++++------ openai/openai_response.py | 11 ++++++ 3 files changed, 119 insertions(+), 11 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index cde4108bde..a6565c0351 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -1,5 +1,6 @@ import asyncio import json +import time import platform import sys import threading @@ -10,6 +11,7 @@ from typing import ( AsyncGenerator, AsyncIterator, + Callable, Dict, Iterator, Optional, @@ -151,6 +153,70 @@ def format_app_info(cls, info): str += " (%s)" % (info["url"],) return str + def _check_polling_response(self, response: OpenAIResponse, predicate: Callable[[OpenAIResponse], bool]): + if not predicate(response): + return + error_data = response.data['error'] + message = error_data.get('message', 'Operation failed') + code = error_data.get('code') + raise error.OpenAIError(message=message, code=code) + + def _poll( + self, + method, + url, + until, + failed, + params = None, + headers = None, + interval = None, + delay = None + ) -> Tuple[Iterator[OpenAIResponse], bool, str]: + if delay: + time.sleep(delay) + + response, b, api_key = self.request(method, url, params, headers) + self._check_polling_response(response, failed) + start_time = time.time() + while not until(response): + if time.time() - start_time > TIMEOUT_SECS: + raise error.Timeout("Operation polling timed out.") + + time.sleep(interval or response.retry_after or 10) + response, b, api_key = self.request(method, url, params, headers) + self._check_polling_response(response, failed) + + response.data = response.data['result'] + return response, b, api_key + + async def _apoll( + self, + method, + url, + until, + failed, + params = None, + headers = None, + interval = None, + delay = None + ) -> Tuple[Iterator[OpenAIResponse], bool, str]: + if delay: + await asyncio.sleep(delay) + + response, b, api_key = await self.arequest(method, url, params, headers) + self._check_polling_response(response, failed) + start_time = time.time() + while not until(response): + if time.time() - start_time > TIMEOUT_SECS: + raise error.Timeout("Operation polling timed out.") + + await asyncio.sleep(interval or response.retry_after or 10) + response, b, api_key = await self.arequest(method, url, params, headers) + self._check_polling_response(response, failed) + + response.data = response.data['result'] + return response, b, api_key + @overload def request( self, diff --git a/openai/api_resources/image.py b/openai/api_resources/image.py index 39a5b6f616..1522923510 100644 --- a/openai/api_resources/image.py +++ b/openai/api_resources/image.py @@ -2,7 +2,7 @@ from typing import Any, List import openai -from openai import api_requestor, util +from openai import api_requestor, error, util from openai.api_resources.abstract import APIResource @@ -10,8 +10,11 @@ class Image(APIResource): OBJECT_NAME = "images" @classmethod - def _get_url(http://github.com/cls,%20action): - return cls.class_url() + f"/{action}" + def _get_url(http://github.com/cls,%20action,%20azure_action,%20api_type,%20api_version): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD) and azure_action is not None: + return f"/{cls.azure_api_prefix}{cls.class_url()}/{action}:{azure_action}?api-version={api_version}" + else: + return f"{cls.class_url()}/{action}" @classmethod def create( @@ -31,12 +34,20 @@ def create( organization=organization, ) - _, api_version = cls._get_api_type_and_version(api_type, api_version) + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) response, _, api_key = requestor.request( - "post", cls._get_url("http://github.com/generations"), params + "post", cls._get_url("http://github.com/generations%22,%20azure_action=%22/service/http://github.com/submit%22,%20api_type=api_type,%20api_version=api_version), params ) + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + requestor.api_base = "" # operation_location is a full url + response, _, api_key = requestor._poll( + "get", response.operation_location, + until=lambda response: response.data['status'] in [ 'succeeded' ], + failed=lambda response: response.data['status'] in [ 'failed' ] + ) + return util.convert_to_openai_object( response, api_key, api_version, organization ) @@ -60,12 +71,20 @@ async def acreate( organization=organization, ) - _, api_version = cls._get_api_type_and_version(api_type, api_version) + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) response, _, api_key = await requestor.arequest( - "post", cls._get_url("http://github.com/generations"), params + "post", cls._get_url("http://github.com/generations%22,%20azure_action=%22/service/http://github.com/submit%22,%20api_type=api_type,%20api_version=api_version), params ) + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + requestor.api_base = "" # operation_location is a full url + response, _, api_key = await requestor._apoll( + "get", response.operation_location, + until=lambda response: response.data['status'] in [ 'succeeded' ], + failed=lambda response: response.data['status'] in [ 'failed' ] + ) + return util.convert_to_openai_object( response, api_key, api_version, organization ) @@ -88,9 +107,9 @@ def _prepare_create_variation( api_version=api_version, organization=organization, ) - _, api_version = cls._get_api_type_and_version(api_type, api_version) + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/variations") + url = cls._get_url("http://github.com/variations%22,%20azure_action=None,%20api_type=api_type,%20api_version=api_version) files: List[Any] = [] for key, value in params.items(): @@ -109,6 +128,9 @@ def create_variation( organization=None, **params, ): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType("Variations are not supported by the Azure OpenAI API yet.") + requestor, url, files = cls._prepare_create_variation( image, api_key, @@ -136,6 +158,9 @@ async def acreate_variation( organization=None, **params, ): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType("Variations are not supported by the Azure OpenAI API yet.") + requestor, url, files = cls._prepare_create_variation( image, api_key, @@ -171,9 +196,9 @@ def _prepare_create_edit( api_version=api_version, organization=organization, ) - _, api_version = cls._get_api_type_and_version(api_type, api_version) + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/edits") + url = cls._get_url("http://github.com/edits%22,%20azure_action=None,%20api_type=api_type,%20api_version=api_version) files: List[Any] = [] for key, value in params.items(): @@ -195,6 +220,9 @@ def create_edit( organization=None, **params, ): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType("Edits are not supported by the Azure OpenAI API yet.") + requestor, url, files = cls._prepare_create_edit( image, mask, @@ -224,6 +252,9 @@ async def acreate_edit( organization=None, **params, ): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType("Edits are not supported by the Azure OpenAI API yet.") + requestor, url, files = cls._prepare_create_edit( image, mask, diff --git a/openai/openai_response.py b/openai/openai_response.py index 9954247319..d2230b1540 100644 --- a/openai/openai_response.py +++ b/openai/openai_response.py @@ -10,6 +10,17 @@ def __init__(self, data, headers): def request_id(self) -> Optional[str]: return self._headers.get("request-id") + @property + def retry_after(self) -> Optional[int]: + try: + return int(self._headers.get("retry-after")) + except TypeError: + return None + + @property + def operation_location(self) -> Optional[str]: + return self._headers.get("operation-location") + @property def organization(self) -> Optional[str]: return self._headers.get("OpenAI-Organization") From 7610c5adfaebe3ffdb9927a551a741a3fab1b62e Mon Sep 17 00:00:00 2001 From: John Allard Date: Tue, 6 Jun 2023 16:39:03 -0700 Subject: [PATCH 132/914] Bump version to 0.27.8 (#480) --- openai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai/version.py b/openai/version.py index aa89e032d8..3b506fe925 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.7" +VERSION = "0.27.8" From 041bf5a8ec54da19aad0169671793c2078bd6173 Mon Sep 17 00:00:00 2001 From: Vik Goel Date: Fri, 16 Jun 2023 09:53:37 -0700 Subject: [PATCH 133/914] catch asyncio.TimeoutError in _interpret_async_response (#180) (#489) --- openai/api_requestor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index a6565c0351..504f7c4411 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -720,6 +720,8 @@ async def _interpret_async_response( else: try: await result.read() + except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as e: + raise error.Timeout("Request timed out") from e except aiohttp.ClientError as e: util.log_warn(e, body=result.content) return ( From d1c36582e82cfa97568d7e9184454ee3b77975fc Mon Sep 17 00:00:00 2001 From: Yutian Liu <138078584+yutian-openai@users.noreply.github.com> Date: Wed, 5 Jul 2023 17:51:01 -0700 Subject: [PATCH 134/914] Add api_version and organization field in Audio API requests (#514) * Add api_version and organization field in Audio API requests * update version --- openai/api_resources/audio.py | 16 ++++++++++++++++ openai/version.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/openai/api_resources/audio.py b/openai/api_resources/audio.py index 33820c64a7..d5d906ed96 100644 --- a/openai/api_resources/audio.py +++ b/openai/api_resources/audio.py @@ -59,6 +59,8 @@ def transcribe( api_key=api_key, api_base=api_base, api_type=api_type, + api_version=api_version, + organization=organization, **params, ) url = cls._get_url("http://github.com/transcriptions") @@ -86,6 +88,8 @@ def translate( api_key=api_key, api_base=api_base, api_type=api_type, + api_version=api_version, + organization=organization, **params, ) url = cls._get_url("http://github.com/translations") @@ -114,6 +118,8 @@ def transcribe_raw( api_key=api_key, api_base=api_base, api_type=api_type, + api_version=api_version, + organization=organization, **params, ) url = cls._get_url("http://github.com/transcriptions") @@ -142,6 +148,8 @@ def translate_raw( api_key=api_key, api_base=api_base, api_type=api_type, + api_version=api_version, + organization=organization, **params, ) url = cls._get_url("http://github.com/translations") @@ -169,6 +177,8 @@ async def atranscribe( api_key=api_key, api_base=api_base, api_type=api_type, + api_version=api_version, + organization=organization, **params, ) url = cls._get_url("http://github.com/transcriptions") @@ -198,6 +208,8 @@ async def atranslate( api_key=api_key, api_base=api_base, api_type=api_type, + api_version=api_version, + organization=organization, **params, ) url = cls._get_url("http://github.com/translations") @@ -228,6 +240,8 @@ async def atranscribe_raw( api_key=api_key, api_base=api_base, api_type=api_type, + api_version=api_version, + organization=organization, **params, ) url = cls._get_url("http://github.com/transcriptions") @@ -258,6 +272,8 @@ async def atranslate_raw( api_key=api_key, api_base=api_base, api_type=api_type, + api_version=api_version, + organization=organization, **params, ) url = cls._get_url("http://github.com/translations") diff --git a/openai/version.py b/openai/version.py index 3b506fe925..51f3ce82ff 100644 --- a/openai/version.py +++ b/openai/version.py @@ -1 +1 @@ -VERSION = "0.27.8" +VERSION = "0.27.9" From b82a3f7e4c462a8a10fa445193301a3cefef9a4a Mon Sep 17 00:00:00 2001 From: Atty Eleti Date: Mon, 10 Jul 2023 15:38:09 -0700 Subject: [PATCH 135/914] Update docstring in chat_completion API resource (#527) Fixes a link to our docs. --- openai/api_resources/chat_completion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openai/api_resources/chat_completion.py b/openai/api_resources/chat_completion.py index 39fb58b33a..7e55f9e38f 100644 --- a/openai/api_resources/chat_completion.py +++ b/openai/api_resources/chat_completion.py @@ -14,7 +14,7 @@ def create(cls, *args, **kwargs): """ Creates a new chat completion for the provided messages and parameters. - See https://platform.openai.com/docs/api-reference/chat-completions/create + See https://platform.openai.com/docs/api-reference/chat/create for a list of valid parameters. """ start = time.time() @@ -34,7 +34,7 @@ async def acreate(cls, *args, **kwargs): """ Creates a new chat completion for the provided messages and parameters. - See https://platform.openai.com/docs/api-reference/chat-completions/create + See https://platform.openai.com/docs/api-reference/chat/create for a list of valid parameters. """ start = time.time() From 120d225b91a8453e15240a49fb1c6794d8119326 Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Tue, 22 Aug 2023 09:04:10 -0700 Subject: [PATCH 136/914] Update chatml.md (#580) --- chatml.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chatml.md b/chatml.md index 783e91d996..6689953adb 100644 --- a/chatml.md +++ b/chatml.md @@ -1,5 +1,8 @@ +> [!IMPORTANT] +> This page is not currently maintained and is intended to provide general insight into the ChatML format, not current up-to-date information. + (This document is a preview of the underlying format consumed by -ChatGPT models. As a developer, you can use our [higher-level +GPT models. As a developer, you can use our [higher-level API](https://platform.openai.com/docs/guides/chat) and won't need to interact directly with this format today — but expect to have the option in the future!) From fcec4dd0121472f60154932ab303b226c17dca38 Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Tue, 22 Aug 2023 09:06:51 -0700 Subject: [PATCH 137/914] Update README.md to show newer completions models (#578) * Update README.md to show newer completions models * Update README.md * Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d61fdee644..fd537888f3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ pre-defined set of classes for API resources that initialize themselves dynamically from API responses which makes it compatible with a wide range of versions of the OpenAI API. -You can find usage examples for the OpenAI Python library in our [API reference](https://beta.openai.com/docs/api-reference?lang=python) and the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). +You can find usage examples for the OpenAI Python library in our [API reference](https://platform.openai.com/docs/api-reference?lang=python) and the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). ## Installation @@ -173,13 +173,13 @@ print(completion.choices[0].message.content) ### Completions -Text models such as `text-davinci-003`, `text-davinci-002` and earlier (`ada`, `babbage`, `curie`, `davinci`, etc.) can be called using the completions endpoint. +Text models such as `babbage-002` or `davinci-002` (and our [legacy completions models](https://platform.openai.com/docs/deprecations/deprecation-history)) can be called using the completions endpoint. ```python import openai openai.api_key = "sk-..." # supply your API key however you choose -completion = openai.Completion.create(model="text-davinci-003", prompt="Hello world") +completion = openai.Completion.create(model="davinci-002", prompt="Hello world") print(completion.choices[0].text) ``` @@ -197,13 +197,13 @@ openai.api_key = "sk-..." # supply your API key however you choose text_string = "sample text" # choose an embedding -model_id = "text-similarity-davinci-001" +model_id = "text-embedding-ada-002" # compute the embedding of the text embedding = openai.Embedding.create(input=text_string, model=model_id)['data'][0]['embedding'] ``` -An example of how to call the embeddings method is shown in this [get embeddings notebook](https://github.com/openai/openai-cookbook/blob/main/examples/Get_embeddings.ipynb). +An example of how to call the embeddings method is shown in this [embeddings guide](https://platform.openai.com/docs/guides/embeddings/embeddings). Examples of how to use embeddings are shared in the following Jupyter notebooks: @@ -215,7 +215,7 @@ Examples of how to use embeddings are shared in the following Jupyter notebooks: - [Zero-shot classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Zero-shot_classification_with_embeddings.ipynb) - [Recommendation using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Recommendation_using_embeddings.ipynb) -For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://beta.openai.com/docs/guides/embeddings) in the OpenAI documentation. +For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://platform.openai.com/docs/guides/embeddings) in the OpenAI documentation. ### Fine-tuning @@ -235,7 +235,7 @@ Sync your fine-tunes to [Weights & Biases](https://wandb.me/openai-docs) to trac openai wandb sync ``` -For more information on fine-tuning, read the [fine-tuning guide](https://beta.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. +For more information on fine-tuning, read the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. ### Moderation From b8dfa35e242de26e9a0371bc2c18bf8661cdc034 Mon Sep 17 00:00:00 2001 From: whysage <67018871+whysage@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:13:03 +0300 Subject: [PATCH 138/914] doc: fix readme azure example code (#571) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd537888f3..7dab22d5a8 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ openai.api_version = "2023-05-15" chat_completion = openai.ChatCompletion.create(deployment_id="deployment-name", model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) # print the completion -print(completion.choices[0].message.content) +print(chat_completion.choices[0].message.content) ``` Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. From df8b0ba7853a2e7345d7506538ae0e57ec0d872b Mon Sep 17 00:00:00 2001 From: John Allard Date: Tue, 22 Aug 2023 09:19:06 -0700 Subject: [PATCH 139/914] Updates to the fine tuning SDK + addition of pagination primitives (#582) * Add support for new fine_tuning SDK + pagination primitives * typo --- openai/__init__.py | 2 + openai/api_resources/__init__.py | 1 + openai/api_resources/abstract/__init__.py | 3 + .../abstract/nested_resource_class_methods.py | 13 ++ .../abstract/paginatable_api_resource.py | 125 ++++++++++++++++++ openai/api_resources/fine_tuning.py | 88 ++++++++++++ openai/object_classes.py | 1 + 7 files changed, 233 insertions(+) create mode 100644 openai/api_resources/abstract/paginatable_api_resource.py create mode 100644 openai/api_resources/fine_tuning.py diff --git a/openai/__init__.py b/openai/__init__.py index 537f12883e..b44e50f97f 100644 --- a/openai/__init__.py +++ b/openai/__init__.py @@ -28,6 +28,7 @@ ErrorObject, File, FineTune, + FineTuningJob, Image, Model, Moderation, @@ -84,6 +85,7 @@ "ErrorObject", "File", "FineTune", + "FineTuningJob", "InvalidRequestError", "Model", "Moderation", diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py index b06ebb4be9..78bad1a22a 100644 --- a/openai/api_resources/__init__.py +++ b/openai/api_resources/__init__.py @@ -9,6 +9,7 @@ from openai.api_resources.error_object import ErrorObject # noqa: F401 from openai.api_resources.file import File # noqa: F401 from openai.api_resources.fine_tune import FineTune # noqa: F401 +from openai.api_resources.fine_tuning import FineTuningJob # noqa: F401 from openai.api_resources.image import Image # noqa: F401 from openai.api_resources.model import Model # noqa: F401 from openai.api_resources.moderation import Moderation # noqa: F401 diff --git a/openai/api_resources/abstract/__init__.py b/openai/api_resources/abstract/__init__.py index 32830e273c..48482bd87a 100644 --- a/openai/api_resources/abstract/__init__.py +++ b/openai/api_resources/abstract/__init__.py @@ -7,4 +7,7 @@ from openai.api_resources.abstract.nested_resource_class_methods import ( nested_resource_class_methods, ) +from openai.api_resources.abstract.paginatable_api_resource import ( + PaginatableAPIResource, +) from openai.api_resources.abstract.updateable_api_resource import UpdateableAPIResource diff --git a/openai/api_resources/abstract/nested_resource_class_methods.py b/openai/api_resources/abstract/nested_resource_class_methods.py index bfa5bcd873..2f2dd45e40 100644 --- a/openai/api_resources/abstract/nested_resource_class_methods.py +++ b/openai/api_resources/abstract/nested_resource_class_methods.py @@ -124,6 +124,19 @@ def list_nested_resources(cls, id, **params): list_method = "list_%s" % resource_plural setattr(cls, list_method, classmethod(list_nested_resources)) + elif operation == "paginated_list": + + def paginated_list_nested_resources( + cls, id, limit=None, cursor=None, **params + ): + url = getattr(cls, resource_url_method)(id) + return getattr(cls, resource_request_method)( + "get", url, limit=limit, cursor=cursor, **params + ) + + list_method = "list_%s" % resource_plural + setattr(cls, list_method, classmethod(paginated_list_nested_resources)) + else: raise ValueError("Unknown operation: %s" % operation) diff --git a/openai/api_resources/abstract/paginatable_api_resource.py b/openai/api_resources/abstract/paginatable_api_resource.py new file mode 100644 index 0000000000..2d75744f23 --- /dev/null +++ b/openai/api_resources/abstract/paginatable_api_resource.py @@ -0,0 +1,125 @@ +from openai import api_requestor, error, util +from openai.api_resources.abstract.listable_api_resource import ListableAPIResource +from openai.util import ApiType + + +class PaginatableAPIResource(ListableAPIResource): + @classmethod + def auto_paging_iter(cls, *args, **params): + next_cursor = None + has_more = True + if not params.get("limit"): + params["limit"] = 20 + while has_more: + if next_cursor: + params["after"] = next_cursor + response = cls.list(*args, **params) + + for item in response.data: + yield item + + if response.data: + next_cursor = response.data[-1].id + has_more = response.has_more + + @classmethod + def __prepare_list_requestor( + cls, + api_key=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or cls.api_base(), + api_version=api_version, + api_type=api_type, + organization=organization, + ) + + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return requestor, url + + @classmethod + def list( + cls, + limit=None, + starting_after=None, + api_key=None, + request_id=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + **params, + ): + requestor, url = cls.__prepare_list_requestor( + api_key, + api_version, + organization, + api_base, + api_type, + ) + + params = { + **params, + "limit": limit, + "starting_after": starting_after, + } + + response, _, api_key = requestor.request( + "get", url, params, request_id=request_id + ) + openai_object = util.convert_to_openai_object( + response, api_key, api_version, organization + ) + openai_object._retrieve_params = params + return openai_object + + @classmethod + async def alist( + cls, + limit=None, + starting_after=None, + api_key=None, + request_id=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + **params, + ): + requestor, url = cls.__prepare_list_requestor( + api_key, + api_version, + organization, + api_base, + api_type, + ) + + params = { + **params, + "limit": limit, + "starting_after": starting_after, + } + + response, _, api_key = await requestor.arequest( + "get", url, params, request_id=request_id + ) + openai_object = util.convert_to_openai_object( + response, api_key, api_version, organization + ) + openai_object._retrieve_params = params + return openai_object diff --git a/openai/api_resources/fine_tuning.py b/openai/api_resources/fine_tuning.py new file mode 100644 index 0000000000..f03be56ab7 --- /dev/null +++ b/openai/api_resources/fine_tuning.py @@ -0,0 +1,88 @@ +from urllib.parse import quote_plus + +from openai import error +from openai.api_resources.abstract import ( + CreateableAPIResource, + PaginatableAPIResource, + nested_resource_class_methods, +) +from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource +from openai.util import ApiType + + +@nested_resource_class_methods("event", operations=["paginated_list"]) +class FineTuningJob( + PaginatableAPIResource, CreateableAPIResource, DeletableAPIResource +): + OBJECT_NAME = "fine_tuning.jobs" + + @classmethod + def _prepare_cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + base = cls.class_url() + extn = quote_plus(id) + + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + url = "/%s%s/%s/cancel?api-version=%s" % ( + cls.azure_api_prefix, + base, + extn, + api_version, + ) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s/cancel" % (base, extn) + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + + instance = cls(id, api_key, **params) + return instance, url + + @classmethod + def cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + instance, url = cls._prepare_cancel( + id, + api_key, + api_type, + request_id, + api_version, + **params, + ) + return instance.request("post", url, request_id=request_id) + + @classmethod + def acancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + instance, url = cls._prepare_cancel( + id, + api_key, + api_type, + request_id, + api_version, + **params, + ) + return instance.arequest("post", url, request_id=request_id) diff --git a/openai/object_classes.py b/openai/object_classes.py index 5f72bd7cf8..08093650fd 100644 --- a/openai/object_classes.py +++ b/openai/object_classes.py @@ -8,4 +8,5 @@ "fine-tune": api_resources.FineTune, "model": api_resources.Model, "deployment": api_resources.Deployment, + "fine_tuning.job": api_resources.FineTuningJob, } From 2942bf4bb635b1e6722d6decb5e8619950fcddd1 Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Tue, 22 Aug 2023 11:43:22 -0700 Subject: [PATCH 140/914] Update README.md with fine-tuning examples (#583) * Update README.md with fine-tuning examples * Update README.md --- README.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7dab22d5a8..9b59ad5550 100644 --- a/README.md +++ b/README.md @@ -219,20 +219,26 @@ For more information on embeddings and the types of embeddings OpenAI offers, re ### Fine-tuning -Fine-tuning a model on training data can both improve the results (by giving the model more examples to learn from) and reduce the cost/latency of API calls (chiefly through reducing the need to include training examples in prompts). +Fine-tuning a model on training data can both improve the results (by giving the model more examples to learn from) and lower the cost/latency of API calls by reducing the need to include training examples in prompts. -Examples of fine-tuning are shared in the following Jupyter notebooks: +```python +# Create a fine-tuning job with an already uploaded file +openai.FineTuningJob.create(training_file="file-abc123", model="gpt-3.5-turbo") -- [Classification with fine-tuning](https://github.com/openai/openai-cookbook/blob/main/examples/Fine-tuned_classification.ipynb) (a simple notebook that shows the steps required for fine-tuning) -- Fine-tuning a model that answers questions about the 2020 Olympics - - [Step 1: Collecting data](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-1-collect-data.ipynb) - - [Step 2: Creating a synthetic Q&A dataset](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-2-create-qa.ipynb) - - [Step 3: Train a fine-tuning model specialized for Q&A](https://github.com/openai/openai-cookbook/blob/main/examples/fine-tuned_qa/olympics-3-train-qa.ipynb) +# List 10 fine-tuning jobs +openai.FineTuningJob.list(limit=10) -Sync your fine-tunes to [Weights & Biases](https://wandb.me/openai-docs) to track experiments, models, and datasets in your central dashboard with: +# Retrieve the state of a fine-tune +openai.FineTuningJob.retrieve("ft-abc123") -```bash -openai wandb sync +# Cancel a job +openai.FineTuningJob.cancel("ft-abc123") + +# List up to 10 events from a fine-tuning job +openai.FineTuningJob.list_events(id="ft-abc123", limit=10) + +# Delete a fine-tuned model (must be an owner of the org the model was created in) +openai.Model.delete("ft-abc123") ``` For more information on fine-tuning, read the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. From 9d559b064cda09d56c1ebf756be4a1f73d5a275a Mon Sep 17 00:00:00 2001 From: John Allard Date: Tue, 29 Aug 2023 17:19:23 -0700 Subject: [PATCH 141/914] [fine_tuning] Add CLI for fine_tuning.jobs (#592) --- openai/api_resources/file.py | 18 +++ openai/cli.py | 272 +++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+) diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py index 394417245f..dba2ee92e1 100644 --- a/openai/api_resources/file.py +++ b/openai/api_resources/file.py @@ -1,6 +1,7 @@ import json import os from typing import cast +import time import openai from openai import api_requestor, util, error @@ -259,3 +260,20 @@ async def afind_matching_files( ) ).get("data", []) return cls.__find_matching_files(name, bytes, all_files, purpose) + + @classmethod + def wait_for_processing(cls, id, max_wait_seconds=30 * 60): + TERMINAL_STATES = ["processed", "error", "deleted"] + + start = time.time() + file = cls.retrieve(id=id) + while file.status not in TERMINAL_STATES: + file = cls.retrieve(id=id) + time.sleep(5.0) + if time.time() - start > max_wait_seconds: + raise openai.error.OpenAIError( + message="Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds.".format( + id=id, max_wait_seconds=max_wait_seconds + ) + ) + return file.status diff --git a/openai/cli.py b/openai/cli.py index ad08ac3e7b..c272d0b8d8 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -606,6 +606,201 @@ def prepare_data(cls, args): ) +class FineTuningJob: + @classmethod + def list(cls, args): + has_ft_jobs = False + for fine_tune_job in openai.FineTuningJob.auto_paging_iter(): + has_ft_jobs = True + print(fine_tune_job) + if not has_ft_jobs: + print("No fine-tuning jobs found.") + + @classmethod + def _is_url(http://github.com/cls,%20file:%20str): + return file.lower().startswith("http") + + @classmethod + def _download_file_from_public_url(http://github.com/cls,%20url:%20str) -> Optional[bytes]: + resp = requests.get(url) + if resp.status_code == 200: + return resp.content + else: + return None + + @classmethod + def _maybe_upload_file( + cls, + file: Optional[str] = None, + content: Optional[bytes] = None, + user_provided_file: Optional[str] = None, + check_if_file_exists: bool = True, + ): + # Exactly one of `file` or `content` must be provided + if (file is None) == (content is None): + raise ValueError("Exactly one of `file` or `content` must be provided") + + if content is None: + assert file is not None + with open(file, "rb") as f: + content = f.read() + + if check_if_file_exists: + bytes = len(content) + matching_files = openai.File.find_matching_files( + name=user_provided_file or f.name, + bytes=bytes, + purpose="fine-tune", + ) + if len(matching_files) > 0: + file_ids = [f["id"] for f in matching_files] + sys.stdout.write( + "Found potentially duplicated files with name '{name}', purpose 'fine-tune', and size {size} bytes\n".format( + name=os.path.basename(matching_files[0]["filename"]), + size=matching_files[0]["bytes"] + if "bytes" in matching_files[0] + else matching_files[0]["size"], + ) + ) + sys.stdout.write("\n".join(file_ids)) + while True: + sys.stdout.write( + "\nEnter file ID to reuse an already uploaded file, or an empty string to upload this file anyway: " + ) + inp = sys.stdin.readline().strip() + if inp in file_ids: + sys.stdout.write( + "Reusing already uploaded file: {id}\n".format(id=inp) + ) + return inp + elif inp == "": + break + else: + sys.stdout.write( + "File id '{id}' is not among the IDs of the potentially duplicated files\n".format( + id=inp + ) + ) + + buffer_reader = BufferReader(content, desc="Upload progress") + resp = openai.File.create( + file=buffer_reader, + purpose="fine-tune", + user_provided_filename=user_provided_file or file, + ) + sys.stdout.write( + "Uploaded file from {file}: {id}\n".format( + file=user_provided_file or file, id=resp["id"] + ) + ) + sys.stdout.write("Waiting for file to finish processing before proceeding..\n") + sys.stdout.flush() + status = openai.File.wait_for_processing(resp["id"]) + if status != "processed": + raise openai.error.OpenAIError( + "File {id} failed to process, status={status}.".format( + id=resp["id"], status=status + ) + ) + + sys.stdout.write( + "File {id} finished processing and is ready for use in fine-tuning".format( + id=resp["id"] + ) + ) + sys.stdout.flush() + return resp["id"] + + @classmethod + def _get_or_upload(cls, file, check_if_file_exists=True): + try: + # 1. If it's a valid file, use it + openai.File.retrieve(file) + return file + except openai.error.InvalidRequestError: + pass + if os.path.isfile(file): + # 2. If it's a file on the filesystem, upload it + return cls._maybe_upload_file( + file=file, check_if_file_exists=check_if_file_exists + ) + if cls._is_url(http://github.com/file): + # 3. If it's a URL, download it temporarily + content = cls._download_file_from_public_url(http://github.com/file) + if content is not None: + return cls._maybe_upload_file( + content=content, + check_if_file_exists=check_if_file_exists, + user_provided_file=file, + ) + return file + + @classmethod + def create(cls, args): + create_args = { + "training_file": cls._get_or_upload( + args.training_file, args.check_if_files_exist + ), + } + if args.validation_file: + create_args["validation_file"] = cls._get_or_upload( + args.validation_file, args.check_if_files_exist + ) + + for param in ("model", "suffix"): + attr = getattr(args, param) + if attr is not None: + create_args[param] = attr + + if getattr(args, "n_epochs"): + create_args["hyperparameters"] = { + "n_epochs": args.n_epochs, + } + + resp = openai.FineTuningJob.create(**create_args) + print(resp) + return + + @classmethod + def get(cls, args): + resp = openai.FineTuningJob.retrieve(id=args.id) + print(resp) + + @classmethod + def results(cls, args): + fine_tune = openai.FineTuningJob.retrieve(id=args.id) + if "result_files" not in fine_tune or len(fine_tune["result_files"]) == 0: + raise openai.error.InvalidRequestError( + f"No results file available for fine-tune {args.id}", "id" + ) + result_file = openai.FineTuningJob.retrieve(id=args.id)["result_files"][0] + resp = openai.File.download(id=result_file) + print(resp.decode("utf-8")) + + @classmethod + def events(cls, args): + seen, has_more = 0, True + while has_more: + resp = openai.FineTuningJob.list_events(id=args.id) # type: ignore + for event in resp["data"]: + print(event) + seen += 1 + if args.limit is not None and seen >= args.limit: + return + has_more = resp["has_more"] + + @classmethod + def follow(cls, args): + raise openai.error.OpenAIError( + message="Event streaming is not yet supported for `fine_tuning.job` events" + ) + + @classmethod + def cancel(cls, args): + resp = openai.FineTuningJob.cancel(id=args.id) + print(resp) + + class WandbLogger: @classmethod def sync(cls, args): @@ -1098,6 +1293,83 @@ def help(args): sub.add_argument("--prompt", type=str) sub.set_defaults(func=Audio.translate) + # FineTuning Jobs + sub = subparsers.add_parser("fine_tuning.job.list") + sub.set_defaults(func=FineTuningJob.list) + + sub = subparsers.add_parser("fine_tuning.job.create") + sub.add_argument( + "-t", + "--training_file", + required=True, + help="JSONL file containing either chat-completion or prompt-completion examples for training. " + "This can be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " + 'a local file path, or a URL that starts with "http".', + ) + sub.add_argument( + "-v", + "--validation_file", + help="JSONL file containing either chat-completion or prompt-completion examples for validation. " + "This can be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " + 'a local file path, or a URL that starts with "http".', + ) + sub.add_argument( + "--no_check_if_files_exist", + dest="check_if_files_exist", + action="http://github.com/store_false", + help="If this argument is set and training_file or validation_file are file paths, immediately upload them. If this argument is not set, check if they may be duplicates of already uploaded files before uploading, based on file name and file size.", + ) + sub.add_argument( + "-m", + "--model", + help="The model to start fine-tuning from", + ) + sub.add_argument( + "--suffix", + help="If set, this argument can be used to customize the generated fine-tuned model name." + "All punctuation and whitespace in `suffix` will be replaced with a " + "single dash, and the string will be lower cased. The max " + "length of `suffix` is 18 chars. " + "The generated name will match the form `ft:{base_model}:{org-title}:{suffix}:{rstring}` where `rstring` " + "is a random string sortable as a timestamp. " + 'For example, `openai api fine_tuning.job.create -t test.jsonl -m gpt-3.5-turbo-0613 --suffix "first finetune!" ' + "could generate a model with the name " + "ft:gpt-3.5-turbo-0613:your-org:first-finetune:7p4PqAoY", + ) + sub.add_argument( + "--n_epochs", + type=int, + help="The number of epochs to train the model for. An epoch refers to one " + "full cycle through the training dataset.", + ) + sub.set_defaults(func=FineTuningJob.create) + + sub = subparsers.add_parser("fine_tuning.job.get") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTuningJob.get) + + sub = subparsers.add_parser("fine_tuning.job.results") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTuningJob.results) + + sub = subparsers.add_parser("fine_tuning.job.events") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.add_argument( + "--limit", + type=int, + required=False, + help="The number of events to return, starting from most recent. If not specified, all events will be returned.", + ) + sub.set_defaults(func=FineTuningJob.events) + + sub = subparsers.add_parser("fine_tuning.job.follow") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTuningJob.follow) + + sub = subparsers.add_parser("fine_tuning.job.cancel") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTuningJob.cancel) + def wandb_register(parser): subparsers = parser.add_subparsers( From 1be14ee34a0f8e42d3f9aa5451aa4cb161f1781f Mon Sep 17 00:00:00 2001 From: John Allard Date: Thu, 31 Aug 2023 15:56:09 -0700 Subject: [PATCH 142/914] [fine_tuning] fix pagination for auto-generated list_events (#188) (#597) --- .../abstract/nested_resource_class_methods.py | 4 ++-- openai/cli.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/openai/api_resources/abstract/nested_resource_class_methods.py b/openai/api_resources/abstract/nested_resource_class_methods.py index 2f2dd45e40..f171737b17 100644 --- a/openai/api_resources/abstract/nested_resource_class_methods.py +++ b/openai/api_resources/abstract/nested_resource_class_methods.py @@ -127,11 +127,11 @@ def list_nested_resources(cls, id, **params): elif operation == "paginated_list": def paginated_list_nested_resources( - cls, id, limit=None, cursor=None, **params + cls, id, limit=None, after=None, **params ): url = getattr(cls, resource_url_method)(id) return getattr(cls, resource_request_method)( - "get", url, limit=limit, cursor=cursor, **params + "get", url, limit=limit, after=after, **params ) list_method = "list_%s" % resource_plural diff --git a/openai/cli.py b/openai/cli.py index c272d0b8d8..99d171a849 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -779,15 +779,17 @@ def results(cls, args): @classmethod def events(cls, args): - seen, has_more = 0, True + seen, has_more, after = 0, True, None while has_more: - resp = openai.FineTuningJob.list_events(id=args.id) # type: ignore + resp = openai.FineTuningJob.list_events(id=args.id, after=after) # type: ignore for event in resp["data"]: print(event) seen += 1 if args.limit is not None and seen >= args.limit: return - has_more = resp["has_more"] + has_more = resp.get("has_more", False) + if resp["data"]: + after = resp["data"][-1]["id"] @classmethod def follow(cls, args): From 52f571a70c1ffe9d1991adbde434e645d45dabad Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Wed, 6 Sep 2023 17:33:34 -0500 Subject: [PATCH 143/914] Update README.md (#601) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b59ad5550..9fa7651ec0 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,7 @@ openai.FineTuningJob.cancel("ft-abc123") openai.FineTuningJob.list_events(id="ft-abc123", limit=10) # Delete a fine-tuned model (must be an owner of the org the model was created in) -openai.Model.delete("ft-abc123") +openai.Model.delete("ft:gpt-3.5-turbo:acemeco:suffix:abc123") ``` For more information on fine-tuning, read the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. From 376dd19955aa34100045cc0e5b6e768e96677093 Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Thu, 7 Sep 2023 09:26:55 -0500 Subject: [PATCH 144/914] Revamp README to make examples front and center (#603) * Revamp README * Add completions guides * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md --- README.md | 261 +++++++++++++++++++++--------------------------------- 1 file changed, 100 insertions(+), 161 deletions(-) diff --git a/README.md b/README.md index 9fa7651ec0..001b248e9c 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,20 @@ You can find usage examples for the OpenAI Python library in our [API reference] ## Installation -You don't need this source code unless you want to modify the package. If you just -want to use the package, just run: +To start, ensure you have Python 3.7.1 or newer. If you just +want to use the package, run: ```sh pip install --upgrade openai ``` -Install from source with: +After you have installed the package, import it at the top of a file: + +```python +import openai +``` + +To install this package from source to make modifications to it, run the following command from the root of the repository: ```sh python setup.py install @@ -33,7 +39,7 @@ pip install openai[embeddings] Install support for [Weights & Biases](https://wandb.me/openai-docs): -``` +```sh pip install openai[wandb] ``` @@ -54,168 +60,48 @@ export OPENAI_API_KEY='sk-...' Or set `openai.api_key` to its value: ```python -import openai openai.api_key = "sk-..." - -# list models -models = openai.Model.list() - -# print the first model's id -print(models.data[0].id) - -# create a chat completion -chat_completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) - -# print the chat completion -print(chat_completion.choices[0].message.content) -``` - -### Params - -All endpoints have a `.create` method that supports a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.Timeout` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). - -### Microsoft Azure Endpoints - -In order to use the library with Microsoft Azure endpoints, you need to set the `api_type`, `api_base` and `api_version` in addition to the `api_key`. The `api_type` must be set to 'azure' and the others correspond to the properties of your endpoint. -In addition, the deployment name must be passed as the engine parameter. - -```python -import openai -openai.api_type = "azure" -openai.api_key = "..." -openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2023-05-15" - -# create a chat completion -chat_completion = openai.ChatCompletion.create(deployment_id="deployment-name", model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) - -# print the completion -print(chat_completion.choices[0].message.content) -``` - -Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. -For a detailed example of how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: - -- [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) -- [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) -- [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) - -### Microsoft Azure Active Directory Authentication - -In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the `api_type` to "azure_ad" and pass the acquired credential token to `api_key`. The rest of the parameters need to be set as specified in the previous section. - -```python -from azure.identity import DefaultAzureCredential -import openai - -# Request credential -default_credential = DefaultAzureCredential() -token = default_credential.get_token("https://cognitiveservices.azure.com/.default") - -# Setup parameters -openai.api_type = "azure_ad" -openai.api_key = token.token -openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2023-05-15" - -# ... ``` -### Command-line interface +Examples of how to use this library to accomplish various tasks can be found in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). It contains code examples for: classification using fine-tuning, clustering, code search, customizing embeddings, question answering from a corpus of documents. recommendations, visualization of embeddings, and more. -This library additionally provides an `openai` command-line utility -which makes it easy to interact with the API from your terminal. Run -`openai api -h` for usage. +Most endpoints support a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.Timeout` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). -```sh -# list models -openai api models.list - -# create a chat completion (gpt-3.5-turbo, gpt-4, etc.) -openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world" - -# create a completion (text-davinci-003, text-davinci-002, ada, babbage, curie, davinci, etc.) -openai api completions.create -m ada -p "Hello world" - -# generate images via DALL·E API -openai api image.create -p "two dogs playing chess, cartoon" -n 1 +### Chat completions -# using openai through a proxy -openai --proxy=http://proxy.com api models.list -``` - -## Example code - -Examples of how to use this Python library to accomplish various tasks can be found in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). It contains code examples for: - -- Classification using fine-tuning -- Clustering -- Code search -- Customizing embeddings -- Question answering from a corpus of documents -- Recommendations -- Visualization of embeddings -- And more - -Prior to July 2022, this OpenAI Python library hosted code examples in its examples folder, but since then all examples have been migrated to the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). - -### Chat Completions - -Conversational models such as `gpt-3.5-turbo` can be called using the chat completions endpoint. +Chat models such as `gpt-3.5-turbo` and `gpt-4` can be called using the [chat completions endpoint](https://platform.openai.com/docs/api-reference/chat/create). ```python -import openai -openai.api_key = "sk-..." # supply your API key however you choose - completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) print(completion.choices[0].message.content) ``` +You can learn more in our [chat completions guide](https://platform.openai.com/docs/guides/gpt/chat-completions-api). + ### Completions Text models such as `babbage-002` or `davinci-002` (and our [legacy completions models](https://platform.openai.com/docs/deprecations/deprecation-history)) can be called using the completions endpoint. ```python -import openai -openai.api_key = "sk-..." # supply your API key however you choose - completion = openai.Completion.create(model="davinci-002", prompt="Hello world") print(completion.choices[0].text) ``` -### Embeddings +You can learn more in our [completions guide](https://platform.openai.com/docs/guides/gpt/completions-api). -In the OpenAI Python library, an embedding represents a text string as a fixed-length vector of floating point numbers. Embeddings are designed to measure the similarity or relevance between text strings. +### Embeddings -To get an embedding for a text string, you can use the embeddings method as follows in Python: +Embeddings are designed to measure the similarity or relevance between text strings. To get an embedding for a text string, you can use following: ```python -import openai -openai.api_key = "sk-..." # supply your API key however you choose - -# choose text to embed text_string = "sample text" -# choose an embedding model_id = "text-embedding-ada-002" -# compute the embedding of the text embedding = openai.Embedding.create(input=text_string, model=model_id)['data'][0]['embedding'] ``` -An example of how to call the embeddings method is shown in this [embeddings guide](https://platform.openai.com/docs/guides/embeddings/embeddings). - -Examples of how to use embeddings are shared in the following Jupyter notebooks: - -- [Classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Classification_using_embeddings.ipynb) -- [Clustering using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Clustering.ipynb) -- [Code search using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Code_search.ipynb) -- [Semantic text search using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Semantic_text_search_using_embeddings.ipynb) -- [User and product embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/User_and_product_embeddings.ipynb) -- [Zero-shot classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Zero-shot_classification_with_embeddings.ipynb) -- [Recommendation using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Recommendation_using_embeddings.ipynb) - -For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://platform.openai.com/docs/guides/embeddings) in the OpenAI documentation. +You can learn more in our [embeddings guide](https://platform.openai.com/docs/guides/embeddings/embeddings). ### Fine-tuning @@ -241,52 +127,48 @@ openai.FineTuningJob.list_events(id="ft-abc123", limit=10) openai.Model.delete("ft:gpt-3.5-turbo:acemeco:suffix:abc123") ``` -For more information on fine-tuning, read the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. +You can learn more in our [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning). ### Moderation -OpenAI provides a Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://platform.openai.com/docs/usage-policies) +OpenAI provides a free Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://platform.openai.com/docs/usage-policies). ```python -import openai -openai.api_key = "sk-..." # supply your API key however you choose - moderation_resp = openai.Moderation.create(input="Here is some perfectly innocuous text that follows all OpenAI content policies.") ``` -See the [moderation guide](https://platform.openai.com/docs/guides/moderation) for more details. +You can learn more in our [moderation guide](https://platform.openai.com/docs/guides/moderation). -## Image generation (DALL·E) +### Image generation (DALL·E) -```python -import openai -openai.api_key = "sk-..." # supply your API key however you choose +DALL·E is a generative image model that can create new images based on a prompt. +```python image_resp = openai.Image.create(prompt="two dogs playing chess, oil painting", n=4, size="512x512") - ``` -## Audio transcription (Whisper) +You can learn more in our [image generation guide](https://platform.openai.com/docs/guides/images). + +### Audio (Whisper) + +The speech to text API provides two endpoints, transcriptions and translations, based on our state-of-the-art [open source large-v2 Whisper model](https://github.com/openai/whisper). ```python -import openai -openai.api_key = "sk-..." # supply your API key however you choose f = open("path/to/file.mp3", "rb") transcript = openai.Audio.transcribe("whisper-1", f) +transcript = openai.Audio.translate("whisper-1", f) ``` -## Async API +You can learn more in our [speech to text guide](https://platform.openai.com/docs/guides/speech-to-text). + +### Async API Async support is available in the API by prepending `a` to a network-bound method: ```python -import openai -openai.api_key = "sk-..." # supply your API key however you choose - async def create_chat_completion(): chat_completion_resp = await openai.ChatCompletion.acreate(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) - ``` To make async requests more efficient, you can pass in your own @@ -294,23 +176,80 @@ To make async requests more efficient, you can pass in your own of your program/event loop: ```python -import openai from aiohttp import ClientSession - openai.aiosession.set(ClientSession()) + # At the end of your program, close the http session await openai.aiosession.get().close() ``` -See the [usage guide](https://platform.openai.com/docs/guides/images) for more details. +### Command-line interface + +This library additionally provides an `openai` command-line utility +which makes it easy to interact with the API from your terminal. Run +`openai api -h` for usage. + +```sh +# list models +openai api models.list + +# create a chat completion (gpt-3.5-turbo, gpt-4, etc.) +openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world" + +# create a completion (text-davinci-003, text-davinci-002, ada, babbage, curie, davinci, etc.) +openai api completions.create -m ada -p "Hello world" + +# generate images via DALL·E API +openai api image.create -p "two dogs playing chess, cartoon" -n 1 + +# using openai through a proxy +openai --proxy=http://proxy.com api models.list +``` + +### Microsoft Azure Endpoints -## Requirements +In order to use the library with Microsoft Azure endpoints, you need to set the `api_type`, `api_base` and `api_version` in addition to the `api_key`. The `api_type` must be set to 'azure' and the others correspond to the properties of your endpoint. +In addition, the deployment name must be passed as the engine parameter. -- Python 3.7.1+ +```python +import openai +openai.api_type = "azure" +openai.api_key = "..." +openai.api_base = "https://example-endpoint.openai.azure.com/" +openai.api_version = "2023-05-15" + +# create a chat completion +chat_completion = openai.ChatCompletion.create(deployment_id="deployment-name", model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) + +# print the completion +print(chat_completion.choices[0].message.content) +``` + +Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. +For a detailed example of how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: -In general, we want to support the versions of Python that our -customers are using. If you run into problems with any version -issues, please let us know on our [support page](https://help.openai.com/en/). +- [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) +- [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) +- [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) + +### Microsoft Azure Active Directory Authentication + +In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the `api_type` to "azure_ad" and pass the acquired credential token to `api_key`. The rest of the parameters need to be set as specified in the previous section. + +```python +from azure.identity import DefaultAzureCredential +import openai + +# Request credential +default_credential = DefaultAzureCredential() +token = default_credential.get_token("https://cognitiveservices.azure.com/.default") + +# Setup parameters +openai.api_type = "azure_ad" +openai.api_key = token.token +openai.api_base = "https://example-endpoint.openai.azure.com/" +openai.api_version = "2023-05-15" +``` ## Credit From 5d50e9e3b39540af782ca24e65c290343d86e1a9 Mon Sep 17 00:00:00 2001 From: Shapor Naghibzadeh Date: Thu, 7 Sep 2023 07:41:09 -0700 Subject: [PATCH 145/914] Update pointer to Jupyter notebooks as finetuning doesn't exist but chat does (#477) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 001b248e9c..a6c24bf18c 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ Please note that for the moment, the Microsoft Azure endpoints can only be used For a detailed example of how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: - [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) -- [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) +- [Using Azure chat](https://github.com/openai/openai-cookbook/tree/main/examples/azure/chat.ipynb) - [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) ### Microsoft Azure Active Directory Authentication From e389823ba013a24b4c32ce38fa0bd87e6bccae94 Mon Sep 17 00:00:00 2001 From: Morgan McGuire Date: Tue, 12 Sep 2023 19:54:15 +0200 Subject: [PATCH 146/914] Update the wandb logger (#590) * Update WandbLogger for new FineTuningJob api * remove prints * add docs link * remove pd * add pandas check * list all jobs * move pandas assert --------- Co-authored-by: Morgan McGuire Co-authored-by: Thomas Capelle Co-authored-by: John Allard --- README.md | 8 ++++++++ openai/_openai_scripts.py | 2 +- openai/cli.py | 14 ++++++++++---- openai/wandb_logger.py | 28 +++++++++++++++++++++------- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a6c24bf18c..e65a5d45b6 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,14 @@ openai.Model.delete("ft:gpt-3.5-turbo:acemeco:suffix:abc123") You can learn more in our [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning). +To log the training results from fine-tuning to Weights & Biases use: + +``` +openai wandb sync +``` + +For more information, read the [wandb documentation](https://docs.wandb.ai/guides/integrations/openai) on Weights & Biases. + ### Moderation OpenAI provides a free Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://platform.openai.com/docs/usage-policies). diff --git a/openai/_openai_scripts.py b/openai/_openai_scripts.py index f2aa3ce2b9..497de19fab 100755 --- a/openai/_openai_scripts.py +++ b/openai/_openai_scripts.py @@ -47,7 +47,7 @@ def help(args): subparsers = parser.add_subparsers() sub_api = subparsers.add_parser("api", help="Direct API calls") sub_tools = subparsers.add_parser("tools", help="Client side tools for convenience") - sub_wandb = subparsers.add_parser("wandb", help="Logging with Weights & Biases") + sub_wandb = subparsers.add_parser("wandb", help="Logging with Weights & Biases, see https://docs.wandb.ai/guides/integrations/openai for documentation") api_register(sub_api) tools_register(sub_tools) diff --git a/openai/cli.py b/openai/cli.py index 99d171a849..a6e99396ae 100644 --- a/openai/cli.py +++ b/openai/cli.py @@ -1375,7 +1375,7 @@ def help(args): def wandb_register(parser): subparsers = parser.add_subparsers( - title="wandb", help="Logging with Weights & Biases" + title="wandb", help="Logging with Weights & Biases, see https://docs.wandb.ai/guides/integrations/openai for documentation" ) def help(args): @@ -1394,17 +1394,23 @@ def help(args): ) sub.add_argument( "--project", - default="GPT-3", - help="""Name of the project where you're sending runs. By default, it is "GPT-3".""", + default="OpenAI-Fine-Tune", + help="""Name of the Weights & Biases project where you're sending runs. By default, it is "OpenAI-Fine-Tune".""", ) sub.add_argument( "--entity", - help="Username or team name where you're sending runs. By default, your default entity is used, which is usually your username.", + help="Weights & Biases username or team name where you're sending runs. By default, your default entity is used, which is usually your username.", ) sub.add_argument( "--force", action="http://github.com/store_true", help="Forces logging and overwrite existing wandb run of the same fine-tune.", ) + sub.add_argument( + "--legacy", + action="http://github.com/store_true", + help="Log results from legacy OpenAI /v1/fine-tunes api", + ) sub.set_defaults(force=False) + sub.set_defaults(legacy=False) sub.set_defaults(func=WandbLogger.sync) diff --git a/openai/wandb_logger.py b/openai/wandb_logger.py index fdd8c24adc..d8e060c41b 100644 --- a/openai/wandb_logger.py +++ b/openai/wandb_logger.py @@ -13,9 +13,9 @@ import re from pathlib import Path - from openai import File, FineTune + from openai import File, FineTune, FineTuningJob from openai.datalib.numpy_helper import numpy as np - from openai.datalib.pandas_helper import pandas as pd + from openai.datalib.pandas_helper import assert_has_pandas, pandas as pd class WandbLogger: @@ -34,9 +34,10 @@ def sync( cls, id=None, n_fine_tunes=None, - project="GPT-3", + project="OpenAI-Fine-Tune", entity=None, force=False, + legacy=False, **kwargs_wandb_init, ): """ @@ -47,18 +48,26 @@ def sync( :param entity: Username or team name where you're sending runs. By default, your default entity is used, which is usually your username. :param force: Forces logging and overwrite existing wandb run of the same fine-tune. """ + + assert_has_pandas() if not WANDB_AVAILABLE: return if id: - fine_tune = FineTune.retrieve(id=id) + print("Retrieving fine-tune job...") + if legacy: + fine_tune = FineTune.retrieve(id=id) + else: + fine_tune = FineTuningJob.retrieve(id=id) fine_tune.pop("events", None) fine_tunes = [fine_tune] - else: # get list of fine_tune to log - fine_tunes = FineTune.list() + if legacy: + fine_tunes = FineTune.list() + else: + fine_tunes = list(FineTuningJob.auto_paging_iter()) if not fine_tunes or fine_tunes.get("data") is None: print("No fine-tune has been retrieved") return @@ -76,6 +85,7 @@ def sync( project, entity, force, + legacy, show_individual_warnings, **kwargs_wandb_init, ) @@ -94,6 +104,7 @@ def _log_fine_tune( project, entity, force, + legacy, show_individual_warnings, **kwargs_wandb_init, ): @@ -110,7 +121,10 @@ def _log_fine_tune( # check results are present try: - results_id = fine_tune["result_files"][0]["id"] + if legacy: + results_id = fine_tune["result_files"][0]["id"] + else: + results_id = fine_tune["result_files"][0] results = File.download(id=results_id).decode("utf-8") except: if show_individual_warnings: From 3f6ef0604fb8ae2b705396e22be14fad0e80c1d2 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 21 Sep 2023 14:49:55 -0700 Subject: [PATCH 147/914] [azure] enable audio/whisper support (#613) * enable azure for audio * reorder overloads * add additional tests * add helper function to utils * simplify - azure users will just need to pass model and deployment_id --- openai/api_resources/audio.py | 44 ++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/openai/api_resources/audio.py b/openai/api_resources/audio.py index d5d906ed96..cb316f07f1 100644 --- a/openai/api_resources/audio.py +++ b/openai/api_resources/audio.py @@ -9,7 +9,9 @@ class Audio(APIResource): OBJECT_NAME = "audio" @classmethod - def _get_url(http://github.com/cls,%20action): + def _get_url(http://github.com/cls,%20action,%20deployment_id=None,%20api_type=None,%20api_version=None): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + return f"/{cls.azure_api_prefix}/deployments/{deployment_id}/audio/{action}?api-version={api_version}" return cls.class_url() + f"/{action}" @classmethod @@ -50,6 +52,8 @@ def transcribe( api_type=None, api_version=None, organization=None, + *, + deployment_id=None, **params, ): requestor, files, data = cls._prepare_request( @@ -63,7 +67,8 @@ def transcribe( organization=organization, **params, ) - url = cls._get_url("http://github.com/transcriptions") + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + url = cls._get_url("http://github.com/transcriptions%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) response, _, api_key = requestor.request("post", url, files=files, params=data) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -79,6 +84,8 @@ def translate( api_type=None, api_version=None, organization=None, + *, + deployment_id=None, **params, ): requestor, files, data = cls._prepare_request( @@ -92,7 +99,8 @@ def translate( organization=organization, **params, ) - url = cls._get_url("http://github.com/translations") + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + url = cls._get_url("http://github.com/translations%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) response, _, api_key = requestor.request("post", url, files=files, params=data) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -109,6 +117,8 @@ def transcribe_raw( api_type=None, api_version=None, organization=None, + *, + deployment_id=None, **params, ): requestor, files, data = cls._prepare_request( @@ -122,7 +132,8 @@ def transcribe_raw( organization=organization, **params, ) - url = cls._get_url("http://github.com/transcriptions") + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + url = cls._get_url("http://github.com/transcriptions%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) response, _, api_key = requestor.request("post", url, files=files, params=data) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -139,6 +150,8 @@ def translate_raw( api_type=None, api_version=None, organization=None, + *, + deployment_id=None, **params, ): requestor, files, data = cls._prepare_request( @@ -152,7 +165,8 @@ def translate_raw( organization=organization, **params, ) - url = cls._get_url("http://github.com/translations") + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + url = cls._get_url("http://github.com/translations%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) response, _, api_key = requestor.request("post", url, files=files, params=data) return util.convert_to_openai_object( response, api_key, api_version, organization @@ -168,6 +182,8 @@ async def atranscribe( api_type=None, api_version=None, organization=None, + *, + deployment_id=None, **params, ): requestor, files, data = cls._prepare_request( @@ -181,7 +197,8 @@ async def atranscribe( organization=organization, **params, ) - url = cls._get_url("http://github.com/transcriptions") + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + url = cls._get_url("http://github.com/transcriptions%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) response, _, api_key = await requestor.arequest( "post", url, files=files, params=data ) @@ -199,6 +216,8 @@ async def atranslate( api_type=None, api_version=None, organization=None, + *, + deployment_id=None, **params, ): requestor, files, data = cls._prepare_request( @@ -212,7 +231,8 @@ async def atranslate( organization=organization, **params, ) - url = cls._get_url("http://github.com/translations") + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + url = cls._get_url("http://github.com/translations%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) response, _, api_key = await requestor.arequest( "post", url, files=files, params=data ) @@ -231,6 +251,8 @@ async def atranscribe_raw( api_type=None, api_version=None, organization=None, + *, + deployment_id=None, **params, ): requestor, files, data = cls._prepare_request( @@ -244,7 +266,8 @@ async def atranscribe_raw( organization=organization, **params, ) - url = cls._get_url("http://github.com/transcriptions") + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + url = cls._get_url("http://github.com/transcriptions%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) response, _, api_key = await requestor.arequest( "post", url, files=files, params=data ) @@ -263,6 +286,8 @@ async def atranslate_raw( api_type=None, api_version=None, organization=None, + *, + deployment_id=None, **params, ): requestor, files, data = cls._prepare_request( @@ -276,7 +301,8 @@ async def atranslate_raw( organization=organization, **params, ) - url = cls._get_url("http://github.com/translations") + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + url = cls._get_url("http://github.com/translations%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) response, _, api_key = await requestor.arequest( "post", url, files=files, params=data ) From 7fba4dce4b7e797e708aac7dc1c73a7c66f006d3 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 22 Sep 2023 21:04:44 +0800 Subject: [PATCH 148/914] Update nested_resource_class_methods.py (#612) support api-base in _nested_resource_class_methods --- .../abstract/nested_resource_class_methods.py | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/openai/api_resources/abstract/nested_resource_class_methods.py b/openai/api_resources/abstract/nested_resource_class_methods.py index f171737b17..68197ab1fa 100644 --- a/openai/api_resources/abstract/nested_resource_class_methods.py +++ b/openai/api_resources/abstract/nested_resource_class_methods.py @@ -28,17 +28,18 @@ def nested_resource_url(http://github.com/cls,%20id,%20nested_id=None): setattr(cls, resource_url_method, classmethod(nested_resource_url)) def nested_resource_request( - cls, - method, - url, - api_key=None, - request_id=None, - api_version=None, - organization=None, - **params, + cls, + method, + url, + api_base=None, + api_key=None, + request_id=None, + api_version=None, + organization=None, + **params, ): requestor = api_requestor.APIRequestor( - api_key, api_version=api_version, organization=organization + api_key, api_base=api_base, api_version=api_version, organization=organization ) response, _, api_key = requestor.request( method, url, params, request_id=request_id @@ -48,17 +49,18 @@ def nested_resource_request( ) async def anested_resource_request( - cls, - method, - url, - api_key=None, - request_id=None, - api_version=None, - organization=None, - **params, + cls, + method, + url, + api_key=None, + api_base=None, + request_id=None, + api_version=None, + organization=None, + **params, ): requestor = api_requestor.APIRequestor( - api_key, api_version=api_version, organization=organization + api_key, api_base=api_base, api_version=api_version, organization=organization ) response, _, api_key = await requestor.arequest( method, url, params, request_id=request_id From 8944bd1609227d42e6283967a4eeb2ae800d6bb6 Mon Sep 17 00:00:00 2001 From: Michael Feil <63565275+michaelfeil@users.noreply.github.com> Date: Mon, 25 Sep 2023 02:12:24 +0200 Subject: [PATCH 149/914] Fix: SSE Stream parser expects additional space after colon "data:" (#559) * Update api_requestor.py * fix: SSE event for api_requestor.py --- openai/api_requestor.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 504f7c4411..0b44949839 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -98,16 +98,18 @@ def _make_session() -> requests.Session: def parse_stream_helper(line: bytes) -> Optional[str]: - if line: - if line.strip() == b"data: [DONE]": - # return here will cause GeneratorExit exception in urllib3 - # and it will close http connection with TCP Reset - return None + if line and line.startswith(b"data:"): if line.startswith(b"data: "): + # SSE event may be valid when it contain whitespace line = line[len(b"data: "):] - return line.decode("utf-8") else: + line = line[len(b"data:"):] + if line.strip() == b"[DONE]": + # return here will cause GeneratorExit exception in urllib3 + # and it will close http connection with TCP Reset return None + else: + return line.decode("utf-8") return None From 532d3df107e651deb95ca071a0ff7fb32588243f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hynek=20Kydl=C3=AD=C4=8Dek?= <39408646+hynky1999@users.noreply.github.com> Date: Tue, 26 Sep 2023 05:16:58 +0200 Subject: [PATCH 150/914] =?UTF-8?q?=F0=9F=90=9B=20fixed=20asyncio=20bugs?= =?UTF-8?q?=20(#584)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openai/api_requestor.py | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/openai/api_requestor.py b/openai/api_requestor.py index 0b44949839..c051bc64f2 100644 --- a/openai/api_requestor.py +++ b/openai/api_requestor.py @@ -6,11 +6,10 @@ import threading import time import warnings -from contextlib import asynccontextmanager from json import JSONDecodeError from typing import ( + AsyncContextManager, AsyncGenerator, - AsyncIterator, Callable, Dict, Iterator, @@ -368,8 +367,9 @@ async def arequest( request_id: Optional[str] = None, request_timeout: Optional[Union[float, Tuple[float, float]]] = None, ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: - ctx = aiohttp_session() + ctx = AioHTTPSession() session = await ctx.__aenter__() + result = None try: result = await self.arequest_raw( method.lower(), @@ -383,6 +383,9 @@ async def arequest( ) resp, got_stream = await self._interpret_async_response(result, stream) except Exception: + # Close the request before exiting session context. + if result is not None: + result.release() await ctx.__aexit__(None, None, None) raise if got_stream: @@ -393,10 +396,15 @@ async def wrap_resp(): async for r in resp: yield r finally: + # Close the request before exiting session context. Important to do it here + # as if stream is not fully exhausted, we need to close the request nevertheless. + result.release() await ctx.__aexit__(None, None, None) return wrap_resp(), got_stream, self.api_key else: + # Close the request before exiting session context. + result.release() await ctx.__aexit__(None, None, None) return resp, got_stream, self.api_key @@ -770,11 +778,22 @@ def _interpret_response_line( return resp -@asynccontextmanager -async def aiohttp_session() -> AsyncIterator[aiohttp.ClientSession]: - user_set_session = openai.aiosession.get() - if user_set_session: - yield user_set_session - else: - async with aiohttp.ClientSession() as session: - yield session +class AioHTTPSession(AsyncContextManager): + def __init__(self): + self._session = None + self._should_close_session = False + + async def __aenter__(self): + self._session = openai.aiosession.get() + if self._session is None: + self._session = await aiohttp.ClientSession().__aenter__() + self._should_close_session = True + + return self._session + + async def __aexit__(self, exc_type, exc_value, traceback): + if self._session is None: + raise RuntimeError("Session is not initialized") + + if self._should_close_session: + await self._session.__aexit__(exc_type, exc_value, traceback) \ No newline at end of file From 19d2444a14b9d558361eb4d5342852ab94e7d54b Mon Sep 17 00:00:00 2001 From: William Horton Date: Mon, 25 Sep 2023 23:18:32 -0400 Subject: [PATCH 151/914] Bugfix: Add param positional arg to InvalidRequestError (#573) I was testing some code and I got this error: ``` File "/usr/local/lib/python3.10/site-packages/openai/api_resources/abstract/engine_api_resource.py", line 37, in class_url raise error.InvalidRequestError( TypeError: InvalidRequestError.__init__() missing 1 required positional argument: 'param' ``` So I checked this file and saw that in a few cases, InvalidRequestError was missing the second positional argument `param` --- openai/api_resources/abstract/engine_api_resource.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py index 1f172d8cbd..bbef90e23e 100644 --- a/openai/api_resources/abstract/engine_api_resource.py +++ b/openai/api_resources/abstract/engine_api_resource.py @@ -35,11 +35,13 @@ def class_url( if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): if not api_version: raise error.InvalidRequestError( - "An API version is required for the Azure API type." + "An API version is required for the Azure API type.", + "api_version" ) if engine is None: raise error.InvalidRequestError( - "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service" + "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service", + "engine" ) extn = quote_plus(engine) return "/%s/%s/%s/%s?api-version=%s" % ( @@ -269,7 +271,8 @@ def instance_url(http://github.com/self): api_version = self.api_version or openai.api_version if not api_version: raise error.InvalidRequestError( - "An API version is required for the Azure API type." + "An API version is required for the Azure API type.", + "api_version" ) base = self.OBJECT_NAME.replace(".", "/") url = "/%s/%s/%s/%s/%s?api-version=%s" % ( From 2d711847c859e7c74da301701d0e0b1756d8af28 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Tue, 26 Sep 2023 08:26:34 -0700 Subject: [PATCH 152/914] Replace engine with deployment_id in README (#614) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e65a5d45b6..a348a47b33 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ openai --proxy=http://proxy.com api models.list ### Microsoft Azure Endpoints In order to use the library with Microsoft Azure endpoints, you need to set the `api_type`, `api_base` and `api_version` in addition to the `api_key`. The `api_type` must be set to 'azure' and the others correspond to the properties of your endpoint. -In addition, the deployment name must be passed as the engine parameter. +In addition, the deployment name must be passed as the `deployment_id` parameter. ```python import openai From 870534d39cb4a37a696efc2507cc1e89e18e6a07 Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Tue, 26 Sep 2023 10:34:18 -0500 Subject: [PATCH 153/914] Update embeddings_utils.py to set default model to text-embedding-ada-002 (#604) * Update embeddings_utils.py * Update max tokens for new embeddings model --- openai/embeddings_utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py index f1d438c9c0..dc26445c3c 100644 --- a/openai/embeddings_utils.py +++ b/openai/embeddings_utils.py @@ -15,7 +15,7 @@ @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) -def get_embedding(text: str, engine="text-similarity-davinci-001", **kwargs) -> List[float]: +def get_embedding(text: str, engine="text-embedding-ada-002", **kwargs) -> List[float]: # replace newlines, which can negatively affect performance. text = text.replace("\n", " ") @@ -25,7 +25,7 @@ def get_embedding(text: str, engine="text-similarity-davinci-001", **kwargs) -> @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) async def aget_embedding( - text: str, engine="text-similarity-davinci-001", **kwargs + text: str, engine="text-embedding-ada-002", **kwargs ) -> List[float]: # replace newlines, which can negatively affect performance. @@ -38,9 +38,9 @@ async def aget_embedding( @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) def get_embeddings( - list_of_text: List[str], engine="text-similarity-babbage-001", **kwargs + list_of_text: List[str], engine="text-embedding-ada-002", **kwargs ) -> List[List[float]]: - assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048." + assert len(list_of_text) <= 8191, "The batch size should not be larger than 8191." # replace newlines, which can negatively affect performance. list_of_text = [text.replace("\n", " ") for text in list_of_text] @@ -51,9 +51,9 @@ def get_embeddings( @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) async def aget_embeddings( - list_of_text: List[str], engine="text-similarity-babbage-001", **kwargs + list_of_text: List[str], engine="text-embedding-ada-002", **kwargs ) -> List[List[float]]: - assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048." + assert len(list_of_text) <= 8191, "The batch size should not be larger than 8191." # replace newlines, which can negatively affect performance. list_of_text = [text.replace("\n", " ") for text in list_of_text] From 5453a19efe6fa4395673782e5e3bd161572d383c Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Tue, 26 Sep 2023 10:34:40 -0500 Subject: [PATCH 154/914] Update README.md (#625) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a348a47b33..b2e0b1bf00 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Install dependencies for [`openai.embeddings_utils`](openai/embeddings_utils.py) pip install openai[embeddings] ``` -Install support for [Weights & Biases](https://wandb.me/openai-docs): +Install support for [Weights & Biases](https://wandb.me/openai-docs) which can be used for fine-tuning: ```sh pip install openai[wandb] @@ -51,7 +51,7 @@ pip install openai[datalib] ## Usage -The library needs to be configured with your account's secret key which is available on the [website](https://platform.openai.com/account/api-keys). Either set it as the `OPENAI_API_KEY` environment variable before using the library: +The library needs to be configured with your OpenAI account's private API key which is available on our [developer platform](https://platform.openai.com/account/api-keys). Either set it as the `OPENAI_API_KEY` environment variable before using the library: ```bash export OPENAI_API_KEY='sk-...' From 96225bdb16a0fa5fd472c8f54ae74fcf7d3e433d Mon Sep 17 00:00:00 2001 From: hallacy <1945079+hallacy@users.noreply.github.com> Date: Tue, 3 Oct 2023 15:39:12 -0700 Subject: [PATCH 155/914] Add note to readme about new beta (#634) * Add note to readme about new beeta * Update README.md Co-authored-by: Logan Kilpatrick * Update README.md * Update README.md --------- Co-authored-by: Logan Kilpatrick --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index b2e0b1bf00..6ec4b6ae82 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,19 @@ with a wide range of versions of the OpenAI API. You can find usage examples for the OpenAI Python library in our [API reference](https://platform.openai.com/docs/api-reference?lang=python) and the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). +## Beta Release + +> [!IMPORTANT] +> We're is preparing to release version 1.0 of the OpenAI Python library. + +This new version will be a major release and will include breaking changes. We're releasing this beta version to give you a chance to try out the new features and provide feedback before the official release. You can install the beta version with: + +```sh +pip install --pre openai +``` +And follow along with the [beta release notes](https://github.com/openai/openai-python/discussions/631). + + ## Installation To start, ensure you have Python 3.7.1 or newer. If you just From 284c1799070c723c6a553337134148a7ab088dd8 Mon Sep 17 00:00:00 2001 From: hallacy <1945079+hallacy@users.noreply.github.com> Date: Tue, 3 Oct 2023 16:21:15 -0700 Subject: [PATCH 156/914] Update README.md (#635) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ec4b6ae82..615160b3a4 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can find usage examples for the OpenAI Python library in our [API reference] ## Beta Release > [!IMPORTANT] -> We're is preparing to release version 1.0 of the OpenAI Python library. +> We're preparing to release version 1.0 of the OpenAI Python library. This new version will be a major release and will include breaking changes. We're releasing this beta version to give you a chance to try out the new features and provide feedback before the official release. You can install the beta version with: From 08b8179a6b3e46ca8eb117f819cc6563ae74e27d Mon Sep 17 00:00:00 2001 From: David Schnurr Date: Mon, 6 Nov 2023 08:19:00 -0800 Subject: [PATCH 157/914] V1 (#677) * cleanup * v1.0.0-beta.1 * docs: add basic manual azure example * docs: use chat completions instead of completions for demo example * test: rename `API_BASE_URL` to `TEST_API_BASE_URL` * feat(client): handle retry-after header with a date format * feat(api): remove `content_filter` stop_reason and update documentation * refactor(cli): rename internal types for improved auto complete * feat(client): add forwards-compatible pydantic methods * feat(api): move `n_epochs` under `hyperparameters` * feat(client): add support for passing in a httpx client * chore: update README * feat(cli): use http/2 if h2 is available * chore(docs): remove trailing spaces * feat(client): add logging setup * chore(internal): minor updates * v1.0.0-beta.2 * docs: use chat completions instead of completions for demo example * chore: add case insensitive get header function * fix(client): correctly handle errors during streaming * fix(streaming): add additional overload for ambiguous stream param * chore(internal): enable lint rule * chore(internal): cleanup some redundant code * fix(client): accept io.IOBase instances in file params * docs: improve error message for invalid file param type * 1.0.0-beta.3 * chore(internal): migrate from Poetry to Rye * feat(cli): add `tools fine_tunes.prepare_data` * feat(client): support passing httpx.URL instances to base_url * chore(internal): fix some latent type errors * feat(api): add embeddings encoding_format * feat: use numpy for faster embeddings decoding * chore(internal): bump pyright * chore(internal): bump deps * feat(client): improve file upload types * feat(client): adjust retry behavior to be exponential backoff * ci: add lint workflow * docs: improve to dictionary example * ci(lint): run ruff too * chore(internal): require explicit overrides * feat(client): support accessing raw response objects * test(qs): add an additional test case for array brackets * feat(client): add dedicated Azure client * feat(package): add classifiers * docs(readme): add Azure guide * 1.0.0-rc1 * docs: small cleanup * feat(github): include a devcontainer setup * chore: improve type names * feat(client): allow binary returns * feat(client): support passing BaseModels to request params at runtime * fix(binaries): don't synchronously block in astream_to_file * 1.0.0-rc2 * chore(internal): remove unused int/float conversion * docs(readme): improve example snippets * fix: prevent TypeError in Python 3.8 (ABC is not subscriptable) * 1.0.0-rc3 * docs: update streaming example * docs(readme): update opening * v1.0.0 --------- Co-authored-by: Robert Craigie Co-authored-by: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Co-authored-by: Stainless Bot Co-authored-by: Alex Rattray --- .devcontainer/Dockerfile | 27 + .devcontainer/devcontainer.json | 20 + .github/ISSUE_TEMPLATE/bug_report.yml | 56 - .github/ISSUE_TEMPLATE/config.yml | 7 - .github/ISSUE_TEMPLATE/feature_request.yml | 20 - .github/workflows/ci.yml | 41 + .gitignore | 24 +- .python-version | 1 + .stats.yml | 1 + LICENSE | 222 ++- Makefile | 11 - README.md | 534 +++-- api.md | 172 ++ bin/blacken-docs.py | 251 +++ bin/check-test-server | 50 + bin/test | 3 + chatml.md | 96 - examples/README.md | 7 - examples/async_demo.py | 22 + examples/azure.py | 43 + examples/azure/embeddings.ipynb | 38 - examples/azure/finetuning.ipynb | 38 - examples/azure_ad.py | 30 + examples/codex/backtranslation.py | 2 - examples/demo.py | 38 + examples/embeddings/Classification.ipynb | 38 - examples/embeddings/Clustering.ipynb | 38 - examples/embeddings/Code_search.ipynb | 38 - examples/embeddings/Get_embeddings.ipynb | 38 - examples/embeddings/Obtain_dataset.ipynb | 38 - examples/embeddings/Recommendation.ipynb | 36 - examples/embeddings/Regression.ipynb | 38 - ...emantic_text_search_using_embeddings.ipynb | 38 - .../User_and_product_embeddings.ipynb | 38 - examples/embeddings/Visualize_in_2d.ipynb | 38 - examples/embeddings/Visualize_in_3d.ipynb | 38 - .../embeddings/Zero-shot_classification.ipynb | 38 - examples/finetuning/answers_with_ft.py | 2 - .../finetuning-classification.ipynb | 38 - .../finetuning/olympics-1-collect-data.ipynb | 38 - .../finetuning/olympics-2-create-qa.ipynb | 38 - examples/finetuning/olympics-3-train-qa.ipynb | 38 - examples/module_client.py | 25 + examples/streaming.py | 56 + mypy.ini | 47 + noxfile.py | 9 + openai/__init__.py | 106 - openai/_openai_scripts.py | 89 - openai/api_requestor.py | 799 -------- openai/api_resources/__init__.py | 15 - openai/api_resources/abstract/__init__.py | 13 - openai/api_resources/abstract/api_resource.py | 172 -- .../abstract/createable_api_resource.py | 98 - .../abstract/deletable_api_resource.py | 48 - .../abstract/engine_api_resource.py | 328 --- .../abstract/listable_api_resource.py | 95 - .../abstract/nested_resource_class_methods.py | 169 -- .../abstract/paginatable_api_resource.py | 125 -- .../abstract/updateable_api_resource.py | 16 - openai/api_resources/audio.py | 311 --- openai/api_resources/chat_completion.py | 50 - openai/api_resources/completion.py | 50 - openai/api_resources/customer.py | 17 - openai/api_resources/deployment.py | 119 -- openai/api_resources/edit.py | 57 - openai/api_resources/embedding.py | 91 - openai/api_resources/engine.py | 50 - openai/api_resources/error_object.py | 28 - openai/api_resources/experimental/__init__.py | 3 - .../experimental/completion_config.py | 11 - openai/api_resources/file.py | 279 --- openai/api_resources/fine_tune.py | 204 -- openai/api_resources/fine_tuning.py | 88 - openai/api_resources/image.py | 273 --- openai/api_resources/model.py | 5 - openai/api_resources/moderation.py | 45 - openai/cli.py | 1416 ------------- openai/datalib/__init__.py | 14 - openai/datalib/common.py | 17 - openai/datalib/numpy_helper.py | 15 - openai/datalib/pandas_helper.py | 15 - openai/embeddings_utils.py | 252 --- openai/error.py | 169 -- openai/object_classes.py | 12 - openai/openai_object.py | 347 ---- openai/openai_response.py | 31 - openai/tests/__init__.py | 0 openai/tests/asyncio/__init__.py | 0 openai/tests/asyncio/test_endpoints.py | 90 - openai/tests/test_api_requestor.py | 101 - openai/tests/test_endpoints.py | 118 -- openai/tests/test_exceptions.py | 40 - openai/tests/test_file_cli.py | 39 - openai/tests/test_long_examples_validator.py | 54 - openai/tests/test_url_composition.py | 209 -- openai/tests/test_util.py | 55 - openai/upload_progress.py | 52 - openai/util.py | 188 -- openai/version.py | 1 - openai/wandb_logger.py | 314 --- public/Makefile | 7 - public/setup.py | 10 - pyproject.toml | 163 +- pytest.ini | 4 - requirements-dev.lock | 74 + requirements.lock | 32 + setup.cfg | 65 - setup.py | 3 - src/openai/__init__.py | 342 ++++ src/openai/__main__.py | 3 + src/openai/_base_client.py | 1768 +++++++++++++++++ src/openai/_client.py | 488 +++++ src/openai/_compat.py | 173 ++ src/openai/_constants.py | 10 + src/openai/_exceptions.py | 123 ++ src/openai/_extras/__init__.py | 3 + src/openai/_extras/_common.py | 21 + src/openai/_extras/numpy_proxy.py | 39 + src/openai/_extras/pandas_proxy.py | 30 + src/openai/_files.py | 122 ++ src/openai/_models.py | 460 +++++ src/openai/_module_client.py | 85 + src/openai/_qs.py | 150 ++ src/openai/_resource.py | 42 + src/openai/_response.py | 252 +++ src/openai/_streaming.py | 232 +++ src/openai/_types.py | 343 ++++ src/openai/_utils/__init__.py | 36 + src/openai/_utils/_logs.py | 25 + src/openai/_utils/_proxy.py | 61 + src/openai/_utils/_transform.py | 214 ++ src/openai/_utils/_utils.py | 408 ++++ src/openai/_version.py | 4 + src/openai/cli/__init__.py | 1 + src/openai/cli/_api/__init__.py | 1 + src/openai/cli/_api/_main.py | 16 + src/openai/cli/_api/audio.py | 94 + src/openai/cli/_api/chat/__init__.py | 13 + src/openai/cli/_api/chat/completions.py | 154 ++ src/openai/cli/_api/completions.py | 173 ++ src/openai/cli/_api/files.py | 75 + src/openai/cli/_api/image.py | 130 ++ src/openai/cli/_api/models.py | 45 + src/openai/cli/_cli.py | 234 +++ src/openai/cli/_errors.py | 23 + src/openai/cli/_models.py | 17 + src/openai/cli/_progress.py | 59 + src/openai/cli/_tools/__init__.py | 1 + src/openai/cli/_tools/_main.py | 17 + src/openai/cli/_tools/fine_tunes.py | 63 + src/openai/cli/_tools/migrate.py | 181 ++ src/openai/cli/_utils.py | 45 + .../openai/lib/_validators.py | 281 ++- src/openai/lib/azure.py | 439 ++++ src/openai/pagination.py | 95 + {openai => src/openai}/py.typed | 0 src/openai/resources/__init__.py | 95 + src/openai/resources/audio/__init__.py | 30 + src/openai/resources/audio/audio.py | 60 + src/openai/resources/audio/transcriptions.py | 206 ++ src/openai/resources/audio/translations.py | 192 ++ src/openai/resources/chat/__init__.py | 20 + src/openai/resources/chat/chat.py | 48 + src/openai/resources/chat/completions.py | 942 +++++++++ src/openai/resources/completions.py | 1117 +++++++++++ src/openai/resources/edits.py | 191 ++ src/openai/resources/embeddings.py | 221 +++ src/openai/resources/files.py | 471 +++++ src/openai/resources/fine_tunes.py | 820 ++++++++ src/openai/resources/fine_tuning/__init__.py | 20 + .../resources/fine_tuning/fine_tuning.py | 43 + src/openai/resources/fine_tuning/jobs.py | 567 ++++++ src/openai/resources/images.py | 479 +++++ src/openai/resources/models.py | 235 +++ src/openai/resources/moderations.py | 148 ++ src/openai/types/__init__.py | 42 + src/openai/types/audio/__init__.py | 12 + src/openai/types/audio/transcription.py | 9 + .../audio/transcription_create_params.py | 52 + src/openai/types/audio/translation.py | 9 + .../types/audio/translation_create_params.py | 44 + src/openai/types/chat/__init__.py | 12 + src/openai/types/chat/chat_completion.py | 50 + .../types/chat/chat_completion_chunk.py | 76 + .../types/chat/chat_completion_message.py | 35 + .../chat/chat_completion_message_param.py | 50 + src/openai/types/chat/chat_completion_role.py | 7 + .../types/chat/completion_create_params.py | 194 ++ src/openai/types/completion.py | 29 + src/openai/types/completion_choice.py | 35 + src/openai/types/completion_create_params.py | 184 ++ src/openai/types/completion_usage.py | 16 + src/openai/types/create_embedding_response.py | 30 + src/openai/types/edit.py | 40 + src/openai/types/edit_create_params.py | 44 + src/openai/types/embedding.py | 22 + src/openai/types/embedding_create_params.py | 43 + src/openai/types/file_content.py | 6 + src/openai/types/file_create_params.py | 26 + src/openai/types/file_deleted.py | 13 + src/openai/types/file_object.py | 40 + src/openai/types/fine_tune.py | 93 + src/openai/types/fine_tune_create_params.py | 140 ++ src/openai/types/fine_tune_event.py | 15 + .../types/fine_tune_events_list_response.py | 14 + .../types/fine_tune_list_events_params.py | 41 + src/openai/types/fine_tuning/__init__.py | 9 + .../types/fine_tuning/fine_tuning_job.py | 107 + .../fine_tuning/fine_tuning_job_event.py | 19 + .../types/fine_tuning/job_create_params.py | 65 + .../fine_tuning/job_list_events_params.py | 15 + .../types/fine_tuning/job_list_params.py | 15 + src/openai/types/image.py | 18 + .../types/image_create_variation_params.py | 40 + src/openai/types/image_edit_params.py | 54 + src/openai/types/image_generate_params.py | 38 + src/openai/types/images_response.py | 14 + src/openai/types/model.py | 19 + src/openai/types/model_deleted.py | 13 + src/openai/types/moderation.py | 120 ++ src/openai/types/moderation_create_params.py | 25 + .../types/moderation_create_response.py | 19 + src/openai/version.py | 3 + tests/__init__.py | 1 + tests/api_resources/__init__.py | 1 + tests/api_resources/audio/__init__.py | 1 + .../audio/test_transcriptions.py | 87 + .../api_resources/audio/test_translations.py | 85 + tests/api_resources/chat/__init__.py | 1 + tests/api_resources/chat/test_completions.py | 281 +++ tests/api_resources/fine_tuning/__init__.py | 1 + tests/api_resources/fine_tuning/test_jobs.py | 240 +++ tests/api_resources/test_completions.py | 185 ++ tests/api_resources/test_edits.py | 95 + tests/api_resources/test_embeddings.py | 83 + tests/api_resources/test_files.py | 184 ++ tests/api_resources/test_fine_tunes.py | 274 +++ tests/api_resources/test_images.py | 197 ++ tests/api_resources/test_models.py | 116 ++ tests/api_resources/test_moderations.py | 75 + tests/conftest.py | 16 + tests/lib/test_azure.py | 36 + tests/test_client.py | 1110 +++++++++++ tests/test_deepcopy.py | 59 + tests/test_extract_files.py | 64 + tests/test_files.py | 51 + tests/test_models.py | 573 ++++++ tests/test_module_client.py | 179 ++ tests/test_qs.py | 78 + tests/test_required_args.py | 111 ++ tests/test_streaming.py | 104 + tests/test_transform.py | 232 +++ tests/utils.py | 105 + 253 files changed, 21668 insertions(+), 8629 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .python-version create mode 100644 .stats.yml delete mode 100644 Makefile create mode 100644 api.md create mode 100644 bin/blacken-docs.py create mode 100755 bin/check-test-server create mode 100755 bin/test delete mode 100644 chatml.md delete mode 100644 examples/README.md create mode 100644 examples/async_demo.py create mode 100644 examples/azure.py delete mode 100644 examples/azure/embeddings.ipynb delete mode 100644 examples/azure/finetuning.ipynb create mode 100644 examples/azure_ad.py delete mode 100644 examples/codex/backtranslation.py create mode 100644 examples/demo.py delete mode 100644 examples/embeddings/Classification.ipynb delete mode 100644 examples/embeddings/Clustering.ipynb delete mode 100644 examples/embeddings/Code_search.ipynb delete mode 100644 examples/embeddings/Get_embeddings.ipynb delete mode 100644 examples/embeddings/Obtain_dataset.ipynb delete mode 100644 examples/embeddings/Recommendation.ipynb delete mode 100644 examples/embeddings/Regression.ipynb delete mode 100644 examples/embeddings/Semantic_text_search_using_embeddings.ipynb delete mode 100644 examples/embeddings/User_and_product_embeddings.ipynb delete mode 100644 examples/embeddings/Visualize_in_2d.ipynb delete mode 100644 examples/embeddings/Visualize_in_3d.ipynb delete mode 100644 examples/embeddings/Zero-shot_classification.ipynb delete mode 100644 examples/finetuning/answers_with_ft.py delete mode 100644 examples/finetuning/finetuning-classification.ipynb delete mode 100644 examples/finetuning/olympics-1-collect-data.ipynb delete mode 100644 examples/finetuning/olympics-2-create-qa.ipynb delete mode 100644 examples/finetuning/olympics-3-train-qa.ipynb create mode 100644 examples/module_client.py create mode 100755 examples/streaming.py create mode 100644 mypy.ini create mode 100644 noxfile.py delete mode 100644 openai/__init__.py delete mode 100755 openai/_openai_scripts.py delete mode 100644 openai/api_requestor.py delete mode 100644 openai/api_resources/__init__.py delete mode 100644 openai/api_resources/abstract/__init__.py delete mode 100644 openai/api_resources/abstract/api_resource.py delete mode 100644 openai/api_resources/abstract/createable_api_resource.py delete mode 100644 openai/api_resources/abstract/deletable_api_resource.py delete mode 100644 openai/api_resources/abstract/engine_api_resource.py delete mode 100644 openai/api_resources/abstract/listable_api_resource.py delete mode 100644 openai/api_resources/abstract/nested_resource_class_methods.py delete mode 100644 openai/api_resources/abstract/paginatable_api_resource.py delete mode 100644 openai/api_resources/abstract/updateable_api_resource.py delete mode 100644 openai/api_resources/audio.py delete mode 100644 openai/api_resources/chat_completion.py delete mode 100644 openai/api_resources/completion.py delete mode 100644 openai/api_resources/customer.py delete mode 100644 openai/api_resources/deployment.py delete mode 100644 openai/api_resources/edit.py delete mode 100644 openai/api_resources/embedding.py delete mode 100644 openai/api_resources/engine.py delete mode 100644 openai/api_resources/error_object.py delete mode 100644 openai/api_resources/experimental/__init__.py delete mode 100644 openai/api_resources/experimental/completion_config.py delete mode 100644 openai/api_resources/file.py delete mode 100644 openai/api_resources/fine_tune.py delete mode 100644 openai/api_resources/fine_tuning.py delete mode 100644 openai/api_resources/image.py delete mode 100644 openai/api_resources/model.py delete mode 100644 openai/api_resources/moderation.py delete mode 100644 openai/cli.py delete mode 100644 openai/datalib/__init__.py delete mode 100644 openai/datalib/common.py delete mode 100644 openai/datalib/numpy_helper.py delete mode 100644 openai/datalib/pandas_helper.py delete mode 100644 openai/embeddings_utils.py delete mode 100644 openai/error.py delete mode 100644 openai/object_classes.py delete mode 100644 openai/openai_object.py delete mode 100644 openai/openai_response.py delete mode 100644 openai/tests/__init__.py delete mode 100644 openai/tests/asyncio/__init__.py delete mode 100644 openai/tests/asyncio/test_endpoints.py delete mode 100644 openai/tests/test_api_requestor.py delete mode 100644 openai/tests/test_endpoints.py delete mode 100644 openai/tests/test_exceptions.py delete mode 100644 openai/tests/test_file_cli.py delete mode 100644 openai/tests/test_long_examples_validator.py delete mode 100644 openai/tests/test_url_composition.py delete mode 100644 openai/tests/test_util.py delete mode 100644 openai/upload_progress.py delete mode 100644 openai/util.py delete mode 100644 openai/version.py delete mode 100644 openai/wandb_logger.py delete mode 100644 public/Makefile delete mode 100644 public/setup.py delete mode 100644 pytest.ini create mode 100644 requirements-dev.lock create mode 100644 requirements.lock delete mode 100644 setup.cfg delete mode 100644 setup.py create mode 100644 src/openai/__init__.py create mode 100644 src/openai/__main__.py create mode 100644 src/openai/_base_client.py create mode 100644 src/openai/_client.py create mode 100644 src/openai/_compat.py create mode 100644 src/openai/_constants.py create mode 100644 src/openai/_exceptions.py create mode 100644 src/openai/_extras/__init__.py create mode 100644 src/openai/_extras/_common.py create mode 100644 src/openai/_extras/numpy_proxy.py create mode 100644 src/openai/_extras/pandas_proxy.py create mode 100644 src/openai/_files.py create mode 100644 src/openai/_models.py create mode 100644 src/openai/_module_client.py create mode 100644 src/openai/_qs.py create mode 100644 src/openai/_resource.py create mode 100644 src/openai/_response.py create mode 100644 src/openai/_streaming.py create mode 100644 src/openai/_types.py create mode 100644 src/openai/_utils/__init__.py create mode 100644 src/openai/_utils/_logs.py create mode 100644 src/openai/_utils/_proxy.py create mode 100644 src/openai/_utils/_transform.py create mode 100644 src/openai/_utils/_utils.py create mode 100644 src/openai/_version.py create mode 100644 src/openai/cli/__init__.py create mode 100644 src/openai/cli/_api/__init__.py create mode 100644 src/openai/cli/_api/_main.py create mode 100644 src/openai/cli/_api/audio.py create mode 100644 src/openai/cli/_api/chat/__init__.py create mode 100644 src/openai/cli/_api/chat/completions.py create mode 100644 src/openai/cli/_api/completions.py create mode 100644 src/openai/cli/_api/files.py create mode 100644 src/openai/cli/_api/image.py create mode 100644 src/openai/cli/_api/models.py create mode 100644 src/openai/cli/_cli.py create mode 100644 src/openai/cli/_errors.py create mode 100644 src/openai/cli/_models.py create mode 100644 src/openai/cli/_progress.py create mode 100644 src/openai/cli/_tools/__init__.py create mode 100644 src/openai/cli/_tools/_main.py create mode 100644 src/openai/cli/_tools/fine_tunes.py create mode 100644 src/openai/cli/_tools/migrate.py create mode 100644 src/openai/cli/_utils.py rename openai/validators.py => src/openai/lib/_validators.py (80%) create mode 100644 src/openai/lib/azure.py create mode 100644 src/openai/pagination.py rename {openai => src/openai}/py.typed (100%) create mode 100644 src/openai/resources/__init__.py create mode 100644 src/openai/resources/audio/__init__.py create mode 100644 src/openai/resources/audio/audio.py create mode 100644 src/openai/resources/audio/transcriptions.py create mode 100644 src/openai/resources/audio/translations.py create mode 100644 src/openai/resources/chat/__init__.py create mode 100644 src/openai/resources/chat/chat.py create mode 100644 src/openai/resources/chat/completions.py create mode 100644 src/openai/resources/completions.py create mode 100644 src/openai/resources/edits.py create mode 100644 src/openai/resources/embeddings.py create mode 100644 src/openai/resources/files.py create mode 100644 src/openai/resources/fine_tunes.py create mode 100644 src/openai/resources/fine_tuning/__init__.py create mode 100644 src/openai/resources/fine_tuning/fine_tuning.py create mode 100644 src/openai/resources/fine_tuning/jobs.py create mode 100644 src/openai/resources/images.py create mode 100644 src/openai/resources/models.py create mode 100644 src/openai/resources/moderations.py create mode 100644 src/openai/types/__init__.py create mode 100644 src/openai/types/audio/__init__.py create mode 100644 src/openai/types/audio/transcription.py create mode 100644 src/openai/types/audio/transcription_create_params.py create mode 100644 src/openai/types/audio/translation.py create mode 100644 src/openai/types/audio/translation_create_params.py create mode 100644 src/openai/types/chat/__init__.py create mode 100644 src/openai/types/chat/chat_completion.py create mode 100644 src/openai/types/chat/chat_completion_chunk.py create mode 100644 src/openai/types/chat/chat_completion_message.py create mode 100644 src/openai/types/chat/chat_completion_message_param.py create mode 100644 src/openai/types/chat/chat_completion_role.py create mode 100644 src/openai/types/chat/completion_create_params.py create mode 100644 src/openai/types/completion.py create mode 100644 src/openai/types/completion_choice.py create mode 100644 src/openai/types/completion_create_params.py create mode 100644 src/openai/types/completion_usage.py create mode 100644 src/openai/types/create_embedding_response.py create mode 100644 src/openai/types/edit.py create mode 100644 src/openai/types/edit_create_params.py create mode 100644 src/openai/types/embedding.py create mode 100644 src/openai/types/embedding_create_params.py create mode 100644 src/openai/types/file_content.py create mode 100644 src/openai/types/file_create_params.py create mode 100644 src/openai/types/file_deleted.py create mode 100644 src/openai/types/file_object.py create mode 100644 src/openai/types/fine_tune.py create mode 100644 src/openai/types/fine_tune_create_params.py create mode 100644 src/openai/types/fine_tune_event.py create mode 100644 src/openai/types/fine_tune_events_list_response.py create mode 100644 src/openai/types/fine_tune_list_events_params.py create mode 100644 src/openai/types/fine_tuning/__init__.py create mode 100644 src/openai/types/fine_tuning/fine_tuning_job.py create mode 100644 src/openai/types/fine_tuning/fine_tuning_job_event.py create mode 100644 src/openai/types/fine_tuning/job_create_params.py create mode 100644 src/openai/types/fine_tuning/job_list_events_params.py create mode 100644 src/openai/types/fine_tuning/job_list_params.py create mode 100644 src/openai/types/image.py create mode 100644 src/openai/types/image_create_variation_params.py create mode 100644 src/openai/types/image_edit_params.py create mode 100644 src/openai/types/image_generate_params.py create mode 100644 src/openai/types/images_response.py create mode 100644 src/openai/types/model.py create mode 100644 src/openai/types/model_deleted.py create mode 100644 src/openai/types/moderation.py create mode 100644 src/openai/types/moderation_create_params.py create mode 100644 src/openai/types/moderation_create_response.py create mode 100644 src/openai/version.py create mode 100644 tests/__init__.py create mode 100644 tests/api_resources/__init__.py create mode 100644 tests/api_resources/audio/__init__.py create mode 100644 tests/api_resources/audio/test_transcriptions.py create mode 100644 tests/api_resources/audio/test_translations.py create mode 100644 tests/api_resources/chat/__init__.py create mode 100644 tests/api_resources/chat/test_completions.py create mode 100644 tests/api_resources/fine_tuning/__init__.py create mode 100644 tests/api_resources/fine_tuning/test_jobs.py create mode 100644 tests/api_resources/test_completions.py create mode 100644 tests/api_resources/test_edits.py create mode 100644 tests/api_resources/test_embeddings.py create mode 100644 tests/api_resources/test_files.py create mode 100644 tests/api_resources/test_fine_tunes.py create mode 100644 tests/api_resources/test_images.py create mode 100644 tests/api_resources/test_models.py create mode 100644 tests/api_resources/test_moderations.py create mode 100644 tests/conftest.py create mode 100644 tests/lib/test_azure.py create mode 100644 tests/test_client.py create mode 100644 tests/test_deepcopy.py create mode 100644 tests/test_extract_files.py create mode 100644 tests/test_files.py create mode 100644 tests/test_models.py create mode 100644 tests/test_module_client.py create mode 100644 tests/test_qs.py create mode 100644 tests/test_required_args.py create mode 100644 tests/test_streaming.py create mode 100644 tests/test_transform.py create mode 100644 tests/utils.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..73f1b9f237 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,27 @@ +# syntax=docker/dockerfile:1 +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y \ + libxkbcommon0 \ + ca-certificates \ + make \ + curl \ + git \ + unzip \ + libc++1 \ + vim \ + termcap \ + && apt-get clean autoclean + +RUN curl -sSf https://rye-up.com/get | RYE_VERSION="0.15.2" RYE_INSTALL_OPTION="--yes" bash +ENV PATH=/root/.rye/shims:$PATH + +WORKDIR /workspace + +COPY README.md .python-version pyproject.toml requirements.lock requirements-dev.lock /workspace/ + +RUN rye sync --all-features + +COPY . /workspace + +CMD ["rye", "shell"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..d55fc4d671 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,20 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Debian", + "build": { + "dockerfile": "Dockerfile" + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 300ad9f0ae..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Bug report -description: Create a report to help us improve -labels: ["bug"] -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this bug report! If you have questions about using the OpenAI Python library, please post on our [Community forum](https://community.openai.com). - - type: textarea - id: what-happened - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is, and any additional context. - placeholder: Tell us what you see! - validations: - required: true - - type: textarea - id: repro-steps - attributes: - label: To Reproduce - description: Steps to reproduce the behavior. - placeholder: | - 1. Fetch a '...' - 2. Update the '....' - 3. See error - validations: - required: true - - type: textarea - id: code-snippets - attributes: - label: Code snippets - description: If applicable, add code snippets to help explain your problem. - render: Python - validations: - required: false - - type: input - id: os - attributes: - label: OS - placeholder: macOS - validations: - required: true - - type: input - id: language-version - attributes: - label: Python version - placeholder: Python v3.7.1 - validations: - required: true - - type: input - id: lib-version - attributes: - label: Library version - placeholder: openai-python v0.26.4 - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 5bedf975eb..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,7 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: OpenAI support - url: https://help.openai.com/ - about: | - Please only file issues here that you believe represent actual bugs or feature requests for the OpenAI Python library. - If you're having general trouble with the OpenAI API, ChatGPT, etc, please visit our help center to get support. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index 2bd1c635ba..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Feature request -description: Suggest an idea for this library -labels: ["feature-request"] -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this feature request! Please note, we are not able to accommodate all feature requests given limited bandwidth but we appreciate you taking the time to share with us how to improve the OpenAI Python library. - - type: textarea - id: feature - attributes: - label: Describe the feature or improvement you're requesting - description: A clear and concise description of what you want to happen. - validations: - required: true - - type: textarea - id: context - attributes: - label: Additional context - description: Add any other context about the feature request here. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..c031d9a1d1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: CI +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + if: github.repository == 'openai/openai-python' + + steps: + - uses: actions/checkout@v3 + + - name: Install Rye + run: | + curl -sSf https://rye-up.com/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: 0.15.2 + RYE_INSTALL_OPTION: "--yes" + + - name: Install dependencies + run: | + rye sync --all-features + + - name: Run ruff + run: | + rye run check:ruff + + - name: Run type checking + run: | + rye run typecheck + + - name: Ensure importable + run: | + rye run python -c 'import openai' diff --git a/.gitignore b/.gitignore index 7ad641a0c8..a4b2f8c0bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,14 @@ -*.egg-info -.idea -.python-version -/public/dist +.vscode +_dev + __pycache__ -build -*.egg -.vscode/settings.json -.ipynb_checkpoints -.vscode/launch.json -examples/azure/training.jsonl -examples/azure/validation.jsonl +.mypy_cache + +dist + +.venv +.idea + +.env +.envrc +codegen.log diff --git a/.python-version b/.python-version new file mode 100644 index 0000000000..43077b2460 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.9.18 diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 0000000000..f21eb8fef0 --- /dev/null +++ b/.stats.yml @@ -0,0 +1 @@ +configured_endpoints: 28 diff --git a/LICENSE b/LICENSE index 4f14854c32..7b1b36a644 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,201 @@ -The MIT License - -Copyright (c) OpenAI (https://openai.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 OpenAI + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile deleted file mode 100644 index b3ef11eea1..0000000000 --- a/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -.PHONY: build upload - -build: - rm -rf dist/ build/ - python -m pip install build - python -m build . - -upload: - python -m pip install twine - python -m twine upload dist/openai-* - rm -rf dist diff --git a/README.md b/README.md index 615160b3a4..a27375d598 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ -# OpenAI Python Library +# OpenAI Python API library -The OpenAI Python library provides convenient access to the OpenAI API -from applications written in the Python language. It includes a -pre-defined set of classes for API resources that initialize -themselves dynamically from API responses which makes it compatible -with a wide range of versions of the OpenAI API. +[![PyPI version](https://img.shields.io/pypi/v/openai.svg)](https://pypi.org/project/openai/) -You can find usage examples for the OpenAI Python library in our [API reference](https://platform.openai.com/docs/api-reference?lang=python) and the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). +The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.7+ +application. The library includes type definitions for all request params and response fields, +and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). + +It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi) with [Stainless](https://stainlessapi.com/). + +## Documentation + +The API documentation can be found [here](https://platform.openai.com/docs). ## Beta Release @@ -23,255 +27,483 @@ And follow along with the [beta release notes](https://github.com/openai/openai- ## Installation -To start, ensure you have Python 3.7.1 or newer. If you just -want to use the package, run: - ```sh -pip install --upgrade openai +pip install --pre openai ``` -After you have installed the package, import it at the top of a file: +## Usage + +The full API of this library can be found in [api.md](https://www.github.com/openai/openai-python/blob/main/api.md). ```python -import openai +from openai import OpenAI + +client = OpenAI( + # defaults to os.environ.get("OPENAI_API_KEY") + api_key="My API Key", +) + +chat_completion = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", +) ``` -To install this package from source to make modifications to it, run the following command from the root of the repository: +While you can provide an `api_key` keyword argument, +we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) +to add `OPENAI_API_KEY="My API Key"` to your `.env` file +so that your API Key is not stored in source control. -```sh -python setup.py install -``` +## Async usage -### Optional dependencies +Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call: -Install dependencies for [`openai.embeddings_utils`](openai/embeddings_utils.py): +```python +import asyncio +from openai import AsyncOpenAI -```sh -pip install openai[embeddings] -``` +client = AsyncOpenAI( + # defaults to os.environ.get("OPENAI_API_KEY") + api_key="My API Key", +) -Install support for [Weights & Biases](https://wandb.me/openai-docs) which can be used for fine-tuning: -```sh -pip install openai[wandb] -``` +async def main() -> None: + chat_completion = await client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ) -Data libraries like `numpy` and `pandas` are not installed by default due to their size. They’re needed for some functionality of this library, but generally not for talking to the API. If you encounter a `MissingDependencyError`, install them with: -```sh -pip install openai[datalib] +asyncio.run(main()) ``` -## Usage +Functionality between the synchronous and asynchronous clients is otherwise identical. + +## Streaming Responses + +We provide support for streaming responses using Server Side Events (SSE). -The library needs to be configured with your OpenAI account's private API key which is available on our [developer platform](https://platform.openai.com/account/api-keys). Either set it as the `OPENAI_API_KEY` environment variable before using the library: +```python +from openai import OpenAI + +client = OpenAI() -```bash -export OPENAI_API_KEY='sk-...' +stream = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Say this is a test"}], + stream=True, +) +for part in stream: + print(part.choices[0].delta.content or "") ``` -Or set `openai.api_key` to its value: +The async client uses the exact same interface. ```python -openai.api_key = "sk-..." +from openai import AsyncOpenAI + +client = AsyncOpenAI() + +stream = await client.chat.completions.create( + prompt="Say this is a test", + messages=[{"role": "user", "content": "Say this is a test"}], + stream=True, +) +async for part in stream: + print(part.choices[0].delta.content or "") ``` -Examples of how to use this library to accomplish various tasks can be found in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). It contains code examples for: classification using fine-tuning, clustering, code search, customizing embeddings, question answering from a corpus of documents. recommendations, visualization of embeddings, and more. +## Module-level client -Most endpoints support a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.Timeout` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). +> [!IMPORTANT] +> We highly recommend instantiating client instances instead of relying on the global client. -### Chat completions +We also expose a global client instance that is accessible in a similar fashion to versions prior to v1. -Chat models such as `gpt-3.5-turbo` and `gpt-4` can be called using the [chat completions endpoint](https://platform.openai.com/docs/api-reference/chat/create). +```py +import openai -```python -completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) +# optional; defaults to `os.environ['OPENAI_API_KEY']` +openai.api_key = '...' + +# all client options can be configured just like the `OpenAI` instantiation counterpart +openai.base_url = "https://.../" +openai.default_headers = {"x-foo": "true"} + +completion = openai.chat.completions.create( + model="gpt-4", + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], +) print(completion.choices[0].message.content) ``` -You can learn more in our [chat completions guide](https://platform.openai.com/docs/guides/gpt/chat-completions-api). +The API is the exact same as the standard client instance based API. -### Completions +This is intended to be used within REPLs or notebooks for faster iteration, **not** in application code. -Text models such as `babbage-002` or `davinci-002` (and our [legacy completions models](https://platform.openai.com/docs/deprecations/deprecation-history)) can be called using the completions endpoint. +We recommend that you always instantiate a client (e.g., with `client = OpenAI()`) in application code because: -```python -completion = openai.Completion.create(model="davinci-002", prompt="Hello world") -print(completion.choices[0].text) -``` +- It can be difficult to reason about where client options are configured +- It's not possible to change certain client options without potentially causing race conditions +- It's harder to mock for testing purposes +- It's not possible to control cleanup of network connections + +## Using types + +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev), which provide helper methods for things like serializing back into JSON ([v1](https://docs.pydantic.dev/1.10/usage/models/), [v2](https://docs.pydantic.dev/latest/usage/serialization/)). To get a dictionary, call `model.model_dump()`. -You can learn more in our [completions guide](https://platform.openai.com/docs/guides/gpt/completions-api). +Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. -### Embeddings +## Pagination -Embeddings are designed to measure the similarity or relevance between text strings. To get an embedding for a text string, you can use following: +List methods in the OpenAI API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: ```python -text_string = "sample text" +import openai -model_id = "text-embedding-ada-002" +client = OpenAI() -embedding = openai.Embedding.create(input=text_string, model=model_id)['data'][0]['embedding'] +all_jobs = [] +# Automatically fetches more pages as needed. +for job in client.fine_tuning.jobs.list( + limit=20, +): + # Do something with job here + all_jobs.append(job) +print(all_jobs) ``` -You can learn more in our [embeddings guide](https://platform.openai.com/docs/guides/embeddings/embeddings). +Or, asynchronously: -### Fine-tuning +```python +import asyncio +import openai -Fine-tuning a model on training data can both improve the results (by giving the model more examples to learn from) and lower the cost/latency of API calls by reducing the need to include training examples in prompts. +client = AsyncOpenAI() -```python -# Create a fine-tuning job with an already uploaded file -openai.FineTuningJob.create(training_file="file-abc123", model="gpt-3.5-turbo") -# List 10 fine-tuning jobs -openai.FineTuningJob.list(limit=10) +async def main() -> None: + all_jobs = [] + # Iterate through items across all pages, issuing requests as needed. + async for job in client.fine_tuning.jobs.list( + limit=20, + ): + all_jobs.append(job) + print(all_jobs) -# Retrieve the state of a fine-tune -openai.FineTuningJob.retrieve("ft-abc123") -# Cancel a job -openai.FineTuningJob.cancel("ft-abc123") +asyncio.run(main()) +``` -# List up to 10 events from a fine-tuning job -openai.FineTuningJob.list_events(id="ft-abc123", limit=10) +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: -# Delete a fine-tuned model (must be an owner of the org the model was created in) -openai.Model.delete("ft:gpt-3.5-turbo:acemeco:suffix:abc123") +```python +first_page = await client.fine_tuning.jobs.list( + limit=20, +) +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.data)}") + +# Remove `await` for non-async usage. ``` -You can learn more in our [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning). +Or just work directly with the returned data: -To log the training results from fine-tuning to Weights & Biases use: +```python +first_page = await client.fine_tuning.jobs.list( + limit=20, +) -``` -openai wandb sync -``` +print(f"next page cursor: {first_page.after}") # => "next page cursor: ..." +for job in first_page.data: + print(job.id) -For more information, read the [wandb documentation](https://docs.wandb.ai/guides/integrations/openai) on Weights & Biases. +# Remove `await` for non-async usage. +``` -### Moderation +## Nested params -OpenAI provides a free Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://platform.openai.com/docs/usage-policies). +Nested parameters are dictionaries, typed using `TypedDict`, for example: ```python -moderation_resp = openai.Moderation.create(input="Here is some perfectly innocuous text that follows all OpenAI content policies.") -``` +from openai import OpenAI -You can learn more in our [moderation guide](https://platform.openai.com/docs/guides/moderation). +client = OpenAI() + +page = client.files.list() +``` -### Image generation (DALL·E) +## File Uploads -DALL·E is a generative image model that can create new images based on a prompt. +Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. ```python -image_resp = openai.Image.create(prompt="two dogs playing chess, oil painting", n=4, size="512x512") +from pathlib import Path +from openai import OpenAI + +client = OpenAI() + +client.files.create( + file=Path("input.jsonl"), + purpose="fine-tune", +) ``` -You can learn more in our [image generation guide](https://platform.openai.com/docs/guides/images). +The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically. -### Audio (Whisper) +## Handling errors -The speech to text API provides two endpoints, transcriptions and translations, based on our state-of-the-art [open source large-v2 Whisper model](https://github.com/openai/whisper). +When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `openai.APIConnectionError` is raised. -```python -f = open("path/to/file.mp3", "rb") -transcript = openai.Audio.transcribe("whisper-1", f) +When the API returns a non-success status code (that is, 4xx or 5xx +response), a subclass of `openai.APIStatusError` is raised, containing `status_code` and `response` properties. -transcript = openai.Audio.translate("whisper-1", f) +All errors inherit from `openai.APIError`. + +```python +import openai +from openai import OpenAI + +client = OpenAI() + +try: + client.fine_tunes.create( + training_file="file-XGinujblHPwGLSztz8cPS8XY", + ) +except openai.APIConnectionError as e: + print("The server could not be reached") + print(e.__cause__) # an underlying Exception, likely raised within httpx. +except openai.RateLimitError as e: + print("A 429 status code was received; we should back off a bit.") +except openai.APIStatusError as e: + print("Another non-200-range status code was received") + print(e.status_code) + print(e.response) ``` -You can learn more in our [speech to text guide](https://platform.openai.com/docs/guides/speech-to-text). +Error codes are as followed: + +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | + +### Retries -### Async API +Certain errors are automatically retried 2 times by default, with a short exponential backoff. +Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, +429 Rate Limit, and >=500 Internal errors are all retried by default. -Async support is available in the API by prepending `a` to a network-bound method: +You can use the `max_retries` option to configure or disable retry settings: ```python -async def create_chat_completion(): - chat_completion_resp = await openai.ChatCompletion.acreate(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) +from openai import OpenAI + +# Configure the default for all requests: +client = OpenAI( + # default is 2 + max_retries=0, +) + +# Or, configure per-request: +client.with_options(max_retries=5).chat.completions.create( + messages=[ + { + "role": "user", + "content": "How can I get the name of the current day in Node.js?", + } + ], + model="gpt-3.5-turbo", +) ``` -To make async requests more efficient, you can pass in your own -`aiohttp.ClientSession`, but you must manually close the client session at the end -of your program/event loop: +### Timeouts -```python -from aiohttp import ClientSession -openai.aiosession.set(ClientSession()) +By default requests time out after 10 minutes. You can configure this with a `timeout` option, +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object: -# At the end of your program, close the http session -await openai.aiosession.get().close() +```python +from openai import OpenAI + +# Configure the default for all requests: +client = OpenAI( + # default is 60s + timeout=20.0, +) + +# More granular control: +client = OpenAI( + timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0), +) + +# Override per-request: +client.with_options(timeout=5 * 1000).chat.completions.create( + messages=[ + { + "role": "user", + "content": "How can I list all files in a directory using Python?", + } + ], + model="gpt-3.5-turbo", +) ``` -### Command-line interface +On timeout, an `APITimeoutError` is thrown. -This library additionally provides an `openai` command-line utility -which makes it easy to interact with the API from your terminal. Run -`openai api -h` for usage. +Note that requests that time out are [retried twice by default](#retries). -```sh -# list models -openai api models.list +## Advanced -# create a chat completion (gpt-3.5-turbo, gpt-4, etc.) -openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world" +### Logging -# create a completion (text-davinci-003, text-davinci-002, ada, babbage, curie, davinci, etc.) -openai api completions.create -m ada -p "Hello world" +We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. -# generate images via DALL·E API -openai api image.create -p "two dogs playing chess, cartoon" -n 1 +You can enable logging by setting the environment variable `OPENAI_LOG` to `debug`. -# using openai through a proxy -openai --proxy=http://proxy.com api models.list +```shell +$ export OPENAI_LOG=debug ``` -### Microsoft Azure Endpoints +### How to tell whether `None` means `null` or missing -In order to use the library with Microsoft Azure endpoints, you need to set the `api_type`, `api_base` and `api_version` in addition to the `api_key`. The `api_type` must be set to 'azure' and the others correspond to the properties of your endpoint. -In addition, the deployment name must be passed as the `deployment_id` parameter. +In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: -```python -import openai -openai.api_type = "azure" -openai.api_key = "..." -openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2023-05-15" +```py +if response.my_field is None: + if 'my_field' not in response.model_fields_set: + print('Got json like {}, without a "my_field" key present at all.') + else: + print('Got json like {"my_field": null}.') +``` -# create a chat completion -chat_completion = openai.ChatCompletion.create(deployment_id="deployment-name", model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) +### Accessing raw response data (e.g. headers) -# print the completion -print(chat_completion.choices[0].message.content) +The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call. + +```py +from openai import OpenAI + +client = OpenAI() +response = client.chat.completions.with_raw_response.create( + messages=[{ + "role": "user", + "content": "Say this is a test", + }], + model="gpt-3.5-turbo", +) +print(response.headers.get('X-My-Header')) + +completion = response.parse() # get the object that `chat.completions.create()` would have returned +print(completion) ``` -Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. -For a detailed example of how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: +These methods return an [`APIResponse`](https://github.com/openai/openai-python/tree/v1/src/openai/_response.py) object. -- [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) -- [Using Azure chat](https://github.com/openai/openai-cookbook/tree/main/examples/azure/chat.ipynb) -- [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) +### Configuring the HTTP client -### Microsoft Azure Active Directory Authentication +You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: -In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the `api_type` to "azure_ad" and pass the acquired credential token to `api_key`. The rest of the parameters need to be set as specified in the previous section. +- Support for proxies +- Custom transports +- Additional [advanced](https://www.python-httpx.org/advanced/#client-instances) functionality ```python -from azure.identity import DefaultAzureCredential -import openai +import httpx +from openai import OpenAI + +client = OpenAI( + base_url="http://my.test.server.example.com:8083/", + http_client=httpx.Client( + proxies="http://my.test.proxy.example.com/", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +### Managing HTTP resources + +By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. + +## Microsoft Azure OpenAI + +To use this library with [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview), use the `AzureOpenAI` +class instead of the `OpenAI` class. + +> [!IMPORTANT] +> The Azure API shape differs from the core API shape which means that the static types for responses / params +> won't always be correct. -# Request credential -default_credential = DefaultAzureCredential() -token = default_credential.get_token("https://cognitiveservices.azure.com/.default") +```py +from openai import AzureOpenAI -# Setup parameters -openai.api_type = "azure_ad" -openai.api_key = token.token -openai.api_base = "https://example-endpoint.openai.azure.com/" -openai.api_version = "2023-05-15" +# gets the API Key from environment variable AZURE_OPENAI_API_KEY +client = AzureOpenAI( + # https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning + api_version="2023-07-01-preview" + # https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource + azure_endpoint="https://example-endpoint.openai.azure.com/", +) + +completion = client.chat.completions.create( + model="deployment-name", # e.g. gpt-35-instant + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], +) +print(completion.model_dump_json(indent=2)) ``` -## Credit +In addition to the options provided in the base `OpenAI` client, the following options are provided: + +- `azure_endpoint` +- `azure_deployment` +- `api_version` +- `azure_ad_token` +- `azure_ad_token_provider` + +An example of using the client with Azure Active Directory can be found [here](https://github.com/openai/openai-python/blob/v1/examples/azure_ad.py). + +## Versioning + +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: + +1. Changes that only affect static types, without breaking runtime behavior. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals)_. +3. Changes that we do not expect to impact the vast majority of users in practice. + +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. + +We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions. + +## Requirements -This library is forked from the [Stripe Python Library](https://github.com/stripe/stripe-python). +Python 3.7 or higher. diff --git a/api.md b/api.md new file mode 100644 index 0000000000..915a05479a --- /dev/null +++ b/api.md @@ -0,0 +1,172 @@ +# Completions + +Types: + +```python +from openai.types import Completion, CompletionChoice, CompletionUsage +``` + +Methods: + +- client.completions.create(\*\*params) -> Completion + +# Chat + +## Completions + +Types: + +```python +from openai.types.chat import ( + ChatCompletion, + ChatCompletionChunk, + ChatCompletionMessage, + ChatCompletionMessageParam, + ChatCompletionRole, +) +``` + +Methods: + +- client.chat.completions.create(\*\*params) -> ChatCompletion + +# Edits + +Types: + +```python +from openai.types import Edit +``` + +Methods: + +- client.edits.create(\*\*params) -> Edit + +# Embeddings + +Types: + +```python +from openai.types import CreateEmbeddingResponse, Embedding +``` + +Methods: + +- client.embeddings.create(\*\*params) -> CreateEmbeddingResponse + +# Files + +Types: + +```python +from openai.types import FileContent, FileDeleted, FileObject +``` + +Methods: + +- client.files.create(\*\*params) -> FileObject +- client.files.retrieve(file_id) -> FileObject +- client.files.list() -> SyncPage[FileObject] +- client.files.delete(file_id) -> FileDeleted +- client.files.retrieve_content(file_id) -> str +- client.files.wait_for_processing(\*args) -> FileObject + +# Images + +Types: + +```python +from openai.types import Image, ImagesResponse +``` + +Methods: + +- client.images.create_variation(\*\*params) -> ImagesResponse +- client.images.edit(\*\*params) -> ImagesResponse +- client.images.generate(\*\*params) -> ImagesResponse + +# Audio + +## Transcriptions + +Types: + +```python +from openai.types.audio import Transcription +``` + +Methods: + +- client.audio.transcriptions.create(\*\*params) -> Transcription + +## Translations + +Types: + +```python +from openai.types.audio import Translation +``` + +Methods: + +- client.audio.translations.create(\*\*params) -> Translation + +# Moderations + +Types: + +```python +from openai.types import Moderation, ModerationCreateResponse +``` + +Methods: + +- client.moderations.create(\*\*params) -> ModerationCreateResponse + +# Models + +Types: + +```python +from openai.types import Model, ModelDeleted +``` + +Methods: + +- client.models.retrieve(model) -> Model +- client.models.list() -> SyncPage[Model] +- client.models.delete(model) -> ModelDeleted + +# FineTuning + +## Jobs + +Types: + +```python +from openai.types.fine_tuning import FineTuningJob, FineTuningJobEvent +``` + +Methods: + +- client.fine_tuning.jobs.create(\*\*params) -> FineTuningJob +- client.fine_tuning.jobs.retrieve(fine_tuning_job_id) -> FineTuningJob +- client.fine_tuning.jobs.list(\*\*params) -> SyncCursorPage[FineTuningJob] +- client.fine_tuning.jobs.cancel(fine_tuning_job_id) -> FineTuningJob +- client.fine_tuning.jobs.list_events(fine_tuning_job_id, \*\*params) -> SyncCursorPage[FineTuningJobEvent] + +# FineTunes + +Types: + +```python +from openai.types import FineTune, FineTuneEvent, FineTuneEventsListResponse +``` + +Methods: + +- client.fine_tunes.create(\*\*params) -> FineTune +- client.fine_tunes.retrieve(fine_tune_id) -> FineTune +- client.fine_tunes.list() -> SyncPage[FineTune] +- client.fine_tunes.cancel(fine_tune_id) -> FineTune +- client.fine_tunes.list_events(fine_tune_id, \*\*params) -> FineTuneEventsListResponse diff --git a/bin/blacken-docs.py b/bin/blacken-docs.py new file mode 100644 index 0000000000..45d0ad1225 --- /dev/null +++ b/bin/blacken-docs.py @@ -0,0 +1,251 @@ +# fork of https://github.com/asottile/blacken-docs implementing https://github.com/asottile/blacken-docs/issues/170 +from __future__ import annotations + +import re +import argparse +import textwrap +import contextlib +from typing import Match, Optional, Sequence, Generator, NamedTuple, cast + +import black +from black.mode import TargetVersion +from black.const import DEFAULT_LINE_LENGTH + +MD_RE = re.compile( + r"(?P^(?P *)```\s*python\n)" r"(?P.*?)" r"(?P^(?P=indent)```\s*$)", + re.DOTALL | re.MULTILINE, +) +MD_PYCON_RE = re.compile( + r"(?P^(?P *)```\s*pycon\n)" r"(?P.*?)" r"(?P^(?P=indent)```.*$)", + re.DOTALL | re.MULTILINE, +) +RST_PY_LANGS = frozenset(("python", "py", "sage", "python3", "py3", "numpy")) +BLOCK_TYPES = "(code|code-block|sourcecode|ipython)" +DOCTEST_TYPES = "(testsetup|testcleanup|testcode)" +RST_RE = re.compile( + rf"(?P" + rf"^(?P *)\.\. (" + rf"jupyter-execute::|" + rf"{BLOCK_TYPES}:: (?P\w+)|" + rf"{DOCTEST_TYPES}::.*" + rf")\n" + rf"((?P=indent) +:.*\n)*" + rf"\n*" + rf")" + rf"(?P(^((?P=indent) +.*)?\n)+)", + re.MULTILINE, +) +RST_PYCON_RE = re.compile( + r"(?P" + r"(?P *)\.\. ((code|code-block):: pycon|doctest::.*)\n" + r"((?P=indent) +:.*\n)*" + r"\n*" + r")" + r"(?P(^((?P=indent) +.*)?(\n|$))+)", + re.MULTILINE, +) +PYCON_PREFIX = ">>> " +PYCON_CONTINUATION_PREFIX = "..." +PYCON_CONTINUATION_RE = re.compile( + rf"^{re.escape(PYCON_CONTINUATION_PREFIX)}( |$)", +) +LATEX_RE = re.compile( + r"(?P^(?P *)\\begin{minted}{python}\n)" + r"(?P.*?)" + r"(?P^(?P=indent)\\end{minted}\s*$)", + re.DOTALL | re.MULTILINE, +) +LATEX_PYCON_RE = re.compile( + r"(?P^(?P *)\\begin{minted}{pycon}\n)" r"(?P.*?)" r"(?P^(?P=indent)\\end{minted}\s*$)", + re.DOTALL | re.MULTILINE, +) +PYTHONTEX_LANG = r"(?Ppyblock|pycode|pyconsole|pyverbatim)" +PYTHONTEX_RE = re.compile( + rf"(?P^(?P *)\\begin{{{PYTHONTEX_LANG}}}\n)" + rf"(?P.*?)" + rf"(?P^(?P=indent)\\end{{(?P=lang)}}\s*$)", + re.DOTALL | re.MULTILINE, +) +INDENT_RE = re.compile("^ +(?=[^ ])", re.MULTILINE) +TRAILING_NL_RE = re.compile(r"\n+\Z", re.MULTILINE) + + +class CodeBlockError(NamedTuple): + offset: int + exc: Exception + + +def format_str( + src: str, + black_mode: black.FileMode, +) -> tuple[str, Sequence[CodeBlockError]]: + errors: list[CodeBlockError] = [] + + @contextlib.contextmanager + def _collect_error(match: Match[str]) -> Generator[None, None, None]: + try: + yield + except Exception as e: + errors.append(CodeBlockError(match.start(), e)) + + def _md_match(match: Match[str]) -> str: + code = textwrap.dedent(match["code"]) + with _collect_error(match): + code = black.format_str(code, mode=black_mode) + code = textwrap.indent(code, match["indent"]) + return f'{match["before"]}{code}{match["after"]}' + + def _rst_match(match: Match[str]) -> str: + lang = match["lang"] + if lang is not None and lang not in RST_PY_LANGS: + return match[0] + min_indent = min(INDENT_RE.findall(match["code"])) + trailing_ws_match = TRAILING_NL_RE.search(match["code"]) + assert trailing_ws_match + trailing_ws = trailing_ws_match.group() + code = textwrap.dedent(match["code"]) + with _collect_error(match): + code = black.format_str(code, mode=black_mode) + code = textwrap.indent(code, min_indent) + return f'{match["before"]}{code.rstrip()}{trailing_ws}' + + def _pycon_match(match: Match[str]) -> str: + code = "" + fragment = cast(Optional[str], None) + + def finish_fragment() -> None: + nonlocal code + nonlocal fragment + + if fragment is not None: + with _collect_error(match): + fragment = black.format_str(fragment, mode=black_mode) + fragment_lines = fragment.splitlines() + code += f"{PYCON_PREFIX}{fragment_lines[0]}\n" + for line in fragment_lines[1:]: + # Skip blank lines to handle Black adding a blank above + # functions within blocks. A blank line would end the REPL + # continuation prompt. + # + # >>> if True: + # ... def f(): + # ... pass + # ... + if line: + code += f"{PYCON_CONTINUATION_PREFIX} {line}\n" + if fragment_lines[-1].startswith(" "): + code += f"{PYCON_CONTINUATION_PREFIX}\n" + fragment = None + + indentation = None + for line in match["code"].splitlines(): + orig_line, line = line, line.lstrip() + if indentation is None and line: + indentation = len(orig_line) - len(line) + continuation_match = PYCON_CONTINUATION_RE.match(line) + if continuation_match and fragment is not None: + fragment += line[continuation_match.end() :] + "\n" + else: + finish_fragment() + if line.startswith(PYCON_PREFIX): + fragment = line[len(PYCON_PREFIX) :] + "\n" + else: + code += orig_line[indentation:] + "\n" + finish_fragment() + return code + + def _md_pycon_match(match: Match[str]) -> str: + code = _pycon_match(match) + code = textwrap.indent(code, match["indent"]) + return f'{match["before"]}{code}{match["after"]}' + + def _rst_pycon_match(match: Match[str]) -> str: + code = _pycon_match(match) + min_indent = min(INDENT_RE.findall(match["code"])) + code = textwrap.indent(code, min_indent) + return f'{match["before"]}{code}' + + def _latex_match(match: Match[str]) -> str: + code = textwrap.dedent(match["code"]) + with _collect_error(match): + code = black.format_str(code, mode=black_mode) + code = textwrap.indent(code, match["indent"]) + return f'{match["before"]}{code}{match["after"]}' + + def _latex_pycon_match(match: Match[str]) -> str: + code = _pycon_match(match) + code = textwrap.indent(code, match["indent"]) + return f'{match["before"]}{code}{match["after"]}' + + src = MD_RE.sub(_md_match, src) + src = MD_PYCON_RE.sub(_md_pycon_match, src) + src = RST_RE.sub(_rst_match, src) + src = RST_PYCON_RE.sub(_rst_pycon_match, src) + src = LATEX_RE.sub(_latex_match, src) + src = LATEX_PYCON_RE.sub(_latex_pycon_match, src) + src = PYTHONTEX_RE.sub(_latex_match, src) + return src, errors + + +def format_file( + filename: str, + black_mode: black.FileMode, + skip_errors: bool, +) -> int: + with open(filename, encoding="UTF-8") as f: + contents = f.read() + new_contents, errors = format_str(contents, black_mode) + for error in errors: + lineno = contents[: error.offset].count("\n") + 1 + print(f"{filename}:{lineno}: code block parse error {error.exc}") + if errors and not skip_errors: + return 1 + if contents != new_contents: + print(f"{filename}: Rewriting...") + with open(filename, "w", encoding="UTF-8") as f: + f.write(new_contents) + return 0 + else: + return 0 + + +def main(argv: Sequence[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "-l", + "--line-length", + type=int, + default=DEFAULT_LINE_LENGTH, + ) + parser.add_argument( + "-t", + "--target-version", + action="http://github.com/append", + type=lambda v: TargetVersion[v.upper()], + default=[], + help=f"choices: {[v.name.lower() for v in TargetVersion]}", + dest="target_versions", + ) + parser.add_argument( + "-S", + "--skip-string-normalization", + action="http://github.com/store_true", + ) + parser.add_argument("-E", "--skip-errors", action="http://github.com/store_true") + parser.add_argument("filenames", nargs="*") + args = parser.parse_args(argv) + + black_mode = black.FileMode( + target_versions=set(args.target_versions), + line_length=args.line_length, + string_normalization=not args.skip_string_normalization, + ) + + retv = 0 + for filename in args.filenames: + retv |= format_file(filename, black_mode, skip_errors=args.skip_errors) + return retv + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/bin/check-test-server b/bin/check-test-server new file mode 100755 index 0000000000..a6fa34950d --- /dev/null +++ b/bin/check-test-server @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +function prism_is_running() { + curl --silent "http://localhost:4010/" >/dev/null 2>&1 +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if is_overriding_api_base_url ; then + # If someone is running the tests against the live API, we can trust they know + # what they're doing and exit early. + echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" + + exit 0 +elif prism_is_running ; then + echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo + + exit 0 +else + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "${YELLOW}To fix:${NC}" + echo + echo -e "1. Install Prism (requires Node 16+):" + echo + echo -e " With npm:" + echo -e " \$ ${YELLOW}npm install -g @stoplight/prism-cli${NC}" + echo + echo -e " With yarn:" + echo -e " \$ ${YELLOW}yarn global add @stoplight/prism-cli${NC}" + echo + echo -e "2. Run the mock server" + echo + echo -e " To run the server, pass in the path of your OpenAPI" + echo -e " spec to the prism command:" + echo + echo -e " \$ ${YELLOW}prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +fi diff --git a/bin/test b/bin/test new file mode 100755 index 0000000000..60ede7a842 --- /dev/null +++ b/bin/test @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +bin/check-test-server && rye run pytest "$@" diff --git a/chatml.md b/chatml.md deleted file mode 100644 index 6689953adb..0000000000 --- a/chatml.md +++ /dev/null @@ -1,96 +0,0 @@ -> [!IMPORTANT] -> This page is not currently maintained and is intended to provide general insight into the ChatML format, not current up-to-date information. - -(This document is a preview of the underlying format consumed by -GPT models. As a developer, you can use our [higher-level -API](https://platform.openai.com/docs/guides/chat) and won't need to -interact directly with this format today — but expect to have the -option in the future!) - -Traditionally, GPT models consumed unstructured text. ChatGPT models -instead expect a structured format, called Chat Markup Language -(ChatML for short). -ChatML documents consist of a sequence of messages. Each message -contains a header (which today consists of who said it, but in the -future will contain other metadata) and contents (which today is a -text payload, but in the future will contain other datatypes). -We are still evolving ChatML, but the current version (ChatML v0) can -be represented with our upcoming "list of dicts" JSON format as -follows: -``` -[ - {"token": "<|im_start|>"}, - "system\nYou are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.\nKnowledge cutoff: 2021-09-01\nCurrent date: 2023-03-01", - {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, - "user\nHow are you", - {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, - "assistant\nI am doing well!", - {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, - "user\nHow are you now?", - {"token": "<|im_end|>"}, "\n" -] -``` -You could also represent it in the classic "unsafe raw string" -format. However, this format inherently allows injections from user -input containing special-token syntax, similar to SQL injections: -``` -<|im_start|>system -You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible. -Knowledge cutoff: 2021-09-01 -Current date: 2023-03-01<|im_end|> -<|im_start|>user -How are you<|im_end|> -<|im_start|>assistant -I am doing well!<|im_end|> -<|im_start|>user -How are you now?<|im_end|> -``` -## Non-chat use-cases -ChatML can be applied to classic GPT use-cases that are not -traditionally thought of as chat. For example, instruction following -(where a user requests for the AI to complete an instruction) can be -implemented as a ChatML query like the following: -``` -[ - {"token": "<|im_start|>"}, - "user\nList off some good ideas:", - {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, - "assistant" -] -``` -We do not currently allow autocompleting of partial messages, -``` -[ - {"token": "<|im_start|>"}, - "system\nPlease autocomplete the user's message.", - {"token": "<|im_end|>"}, "\n", {"token": "<|im_start|>"}, - "user\nThis morning I decided to eat a giant" -] -``` -Note that ChatML makes explicit to the model the source of each piece -of text, and particularly shows the boundary between human and AI -text. This gives an opportunity to mitigate and eventually solve -injections, as the model can tell which instructions come from the -developer, the user, or its own input. -## Few-shot prompting -In general, we recommend adding few-shot examples using separate -`system` messages with a `name` field of `example_user` or -`example_assistant`. For example, here is a 1-shot prompt: -``` -<|im_start|>system -Translate from English to French -<|im_end|> -<|im_start|>system name=example_user -How are you? -<|im_end|> -<|im_start|>system name=example_assistant -Comment allez-vous? -<|im_end|> -<|im_start|>user -{{user input here}}<|im_end|> -``` -If adding instructions in the `system` message doesn't work, you can -also try putting them into a `user` message. (In the near future, we -will train our models to be much more steerable via the system -message. But to date, we have trained only on a few system messages, -so the models pay much more attention to user examples.) diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index ffa3b42709..0000000000 --- a/examples/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Examples have moved to the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/) - -Looking for code examples? Visit the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/), which shares examples of how to use the OpenAI Python library to accomplish common tasks. - -Prior to July 2022, code examples were hosted in this examples folder; going forward, code examples will be hosted in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). - -This separation will help keep the [OpenAI Python library](https://github.com/openai/openai-python) simple and small, without extra files or dependencies. diff --git a/examples/async_demo.py b/examples/async_demo.py new file mode 100644 index 0000000000..92c267c38f --- /dev/null +++ b/examples/async_demo.py @@ -0,0 +1,22 @@ +#!/usr/bin/env -S poetry run python + +import asyncio + +from openai import AsyncOpenAI + +# gets API Key from environment variable OPENAI_API_KEY +client = AsyncOpenAI() + + +async def main() -> None: + stream = await client.completions.create( + model="text-davinci-003", + prompt="Say this is a test", + stream=True, + ) + async for completion in stream: + print(completion.choices[0].text, end="") + print() + + +asyncio.run(main()) diff --git a/examples/azure.py b/examples/azure.py new file mode 100644 index 0000000000..a28b8cc433 --- /dev/null +++ b/examples/azure.py @@ -0,0 +1,43 @@ +from openai import AzureOpenAI + +# may change in the future +# https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning +api_version = "2023-07-01-preview" + +# gets the API Key from environment variable AZURE_OPENAI_API_KEY +client = AzureOpenAI( + api_version=api_version, + # https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource + azure_endpoint="https://example-endpoint.openai.azure.com/", +) + +completion = client.chat.completions.create( + model="deployment-name", # e.g. gpt-35-instant + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], +) +print(completion.model_dump_json(indent=2)) + + +deployment_client = AzureOpenAI( + api_version=api_version, + # https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource + azure_endpoint="https://example-resource.azure.openai.com/", + # Navigate to the Azure OpenAI Studio to deploy a model. + azure_deployment="deployment-name", # e.g. gpt-35-instant +) + +completion = deployment_client.chat.completions.create( + model="", + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], +) +print(completion.model_dump_json(indent=2)) diff --git a/examples/azure/embeddings.ipynb b/examples/azure/embeddings.ipynb deleted file mode 100644 index c350e597ac..0000000000 --- a/examples/azure/embeddings.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/azure/embeddings.ipynb](https://github.com/openai/openai-cookbook/tree/main/examples/azure/embeddings.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/azure/finetuning.ipynb b/examples/azure/finetuning.ipynb deleted file mode 100644 index 07aa224e54..0000000000 --- a/examples/azure/finetuning.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/azure/finetuning.ipynb](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/azure_ad.py b/examples/azure_ad.py new file mode 100644 index 0000000000..f13079dd04 --- /dev/null +++ b/examples/azure_ad.py @@ -0,0 +1,30 @@ +from azure.identity import DefaultAzureCredential, get_bearer_token_provider + +from openai import AzureOpenAI + +token_provider = get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default") + + +# may change in the future +# https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning +api_version = "2023-07-01-preview" + +# https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource +endpoint = "https://my-resource.openai.azure.com/" + +client = AzureOpenAI( + api_version=api_version, + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, +) + +completion = client.chat.completions.create( + model="deployment-name", # e.g. gpt-35-instant + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], +) +print(completion.model_dump_json(indent=2)) diff --git a/examples/codex/backtranslation.py b/examples/codex/backtranslation.py deleted file mode 100644 index 6390e5e174..0000000000 --- a/examples/codex/backtranslation.py +++ /dev/null @@ -1,2 +0,0 @@ -# This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) -# at [examples/Backtranslation_of_SQL_queries](https://github.com/openai/openai-cookbook/blob/main/examples/Backtranslation_of_SQL_queries.py) diff --git a/examples/demo.py b/examples/demo.py new file mode 100644 index 0000000000..37830e3e97 --- /dev/null +++ b/examples/demo.py @@ -0,0 +1,38 @@ +#!/usr/bin/env -S poetry run python + +from openai import OpenAI + +# gets API Key from environment variable OPENAI_API_KEY +client = OpenAI() + +# Non-streaming: +print("----- standard request -----") +completion = client.chat.completions.create( + model="gpt-4", + messages=[ + { + "role": "user", + "content": "Say this is a test", + }, + ], +) +print(completion.choices[0].message.content) + +# Streaming: +print("----- streaming request -----") +stream = client.chat.completions.create( + model="gpt-4", + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], + stream=True, +) +for chunk in stream: + if not chunk.choices: + continue + + print(chunk.choices[0].delta.content, end="") +print() diff --git a/examples/embeddings/Classification.ipynb b/examples/embeddings/Classification.ipynb deleted file mode 100644 index b44d6a76a5..0000000000 --- a/examples/embeddings/Classification.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Classification_using_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Classification_using_embeddings.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Clustering.ipynb b/examples/embeddings/Clustering.ipynb deleted file mode 100644 index 7a4f14193d..0000000000 --- a/examples/embeddings/Clustering.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Clustering.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Clustering.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Code_search.ipynb b/examples/embeddings/Code_search.ipynb deleted file mode 100644 index 440f8f56d5..0000000000 --- a/examples/embeddings/Code_search.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Code_search.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Code_search.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Get_embeddings.ipynb b/examples/embeddings/Get_embeddings.ipynb deleted file mode 100644 index 199c2dd156..0000000000 --- a/examples/embeddings/Get_embeddings.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Get_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Get_embeddings.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Obtain_dataset.ipynb b/examples/embeddings/Obtain_dataset.ipynb deleted file mode 100644 index 9d04f9bce9..0000000000 --- a/examples/embeddings/Obtain_dataset.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Obtain_dataset.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Obtain_dataset.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Recommendation.ipynb b/examples/embeddings/Recommendation.ipynb deleted file mode 100644 index 7be5be31d7..0000000000 --- a/examples/embeddings/Recommendation.ipynb +++ /dev/null @@ -1,36 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Recommendation_using_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Recommendation_using_embeddings.ipynb)." - ] - } - ], - "metadata": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - }, - "kernelspec": { - "display_name": "Python 3.9.9 64-bit ('openai': virtualenv)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Regression.ipynb b/examples/embeddings/Regression.ipynb deleted file mode 100644 index 8d44cb97b4..0000000000 --- a/examples/embeddings/Regression.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Regression_using_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Regression_using_embeddings.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb b/examples/embeddings/Semantic_text_search_using_embeddings.ipynb deleted file mode 100644 index 78dbc35f35..0000000000 --- a/examples/embeddings/Semantic_text_search_using_embeddings.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Semantic_text_search_using_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Semantic_text_search_using_embeddings.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/User_and_product_embeddings.ipynb b/examples/embeddings/User_and_product_embeddings.ipynb deleted file mode 100644 index 9ebd557b8f..0000000000 --- a/examples/embeddings/User_and_product_embeddings.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/User_and_product_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/User_and_product_embeddings.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Visualize_in_2d.ipynb b/examples/embeddings/Visualize_in_2d.ipynb deleted file mode 100644 index 4638b58e95..0000000000 --- a/examples/embeddings/Visualize_in_2d.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Visualizing_embeddings_in_2D.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Visualizing_embeddings_in_2D.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/embeddings/Visualize_in_3d.ipynb b/examples/embeddings/Visualize_in_3d.ipynb deleted file mode 100644 index df79b02e9b..0000000000 --- a/examples/embeddings/Visualize_in_3d.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "b87d69b2", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Visualizing_embeddings_in_3D.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Visualizing_embeddings_in_3D.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/embeddings/Zero-shot_classification.ipynb b/examples/embeddings/Zero-shot_classification.ipynb deleted file mode 100644 index d63561879a..0000000000 --- a/examples/embeddings/Zero-shot_classification.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Zero-shot_classification_with_embeddings.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Zero-shot_classification_with_embeddings.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/finetuning/answers_with_ft.py b/examples/finetuning/answers_with_ft.py deleted file mode 100644 index 43061f4c1b..0000000000 --- a/examples/finetuning/answers_with_ft.py +++ /dev/null @@ -1,2 +0,0 @@ -# This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) -# at [examples/fine-tuned_qa](https://github.com/openai/openai-cookbook/tree/main/examples/fine-tuned_qa) diff --git a/examples/finetuning/finetuning-classification.ipynb b/examples/finetuning/finetuning-classification.ipynb deleted file mode 100644 index e5ece174d9..0000000000 --- a/examples/finetuning/finetuning-classification.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/Fine-tuned_classification.ipynb](https://github.com/openai/openai-cookbook/blob/main/examples/Fine-tuned_classification.ipynb)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/finetuning/olympics-1-collect-data.ipynb b/examples/finetuning/olympics-1-collect-data.ipynb deleted file mode 100644 index a0c55d438e..0000000000 --- a/examples/finetuning/olympics-1-collect-data.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/fine-tuned_qa/](https://github.com/openai/openai-cookbook/tree/main/examples/fine-tuned_qa)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/finetuning/olympics-2-create-qa.ipynb b/examples/finetuning/olympics-2-create-qa.ipynb deleted file mode 100644 index a0c55d438e..0000000000 --- a/examples/finetuning/olympics-2-create-qa.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/fine-tuned_qa/](https://github.com/openai/openai-cookbook/tree/main/examples/fine-tuned_qa)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/finetuning/olympics-3-train-qa.ipynb b/examples/finetuning/olympics-3-train-qa.ipynb deleted file mode 100644 index a0c55d438e..0000000000 --- a/examples/finetuning/olympics-3-train-qa.ipynb +++ /dev/null @@ -1,38 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This code example has moved. You can now find it in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook) at [examples/fine-tuned_qa/](https://github.com/openai/openai-cookbook/tree/main/examples/fine-tuned_qa)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.9 ('openai')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.9" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "365536dcbde60510dc9073d6b991cd35db2d9bac356a11f5b64279a5e6708b97" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/module_client.py b/examples/module_client.py new file mode 100644 index 0000000000..5f2fb79dcf --- /dev/null +++ b/examples/module_client.py @@ -0,0 +1,25 @@ +import openai + +# will default to `os.environ['OPENAI_API_KEY']` if not explicitly set +openai.api_key = "..." + +# all client options can be configured just like the `OpenAI` instantiation counterpart +openai.base_url = "https://.../" +openai.default_headers = {"x-foo": "true"} + +# all API calls work in the exact same fashion as well +stream = openai.chat.completions.create( + model="gpt-4", + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], + stream=True, +) + +for chunk in stream: + print(chunk.choices[0].delta.content or "", end="", flush=True) + +print() diff --git a/examples/streaming.py b/examples/streaming.py new file mode 100755 index 0000000000..168877dfc5 --- /dev/null +++ b/examples/streaming.py @@ -0,0 +1,56 @@ +#!/usr/bin/env -S poetry run python + +import asyncio + +from openai import OpenAI, AsyncOpenAI + +# This script assumes you have the OPENAI_API_KEY environment variable set to a valid OpenAI API key. +# +# You can run this script from the root directory like so: +# `python examples/streaming.py` + + +def sync_main() -> None: + client = OpenAI() + response = client.completions.create( + model="text-davinci-002", + prompt="1,2,3,", + max_tokens=5, + temperature=0, + stream=True, + ) + + # You can manually control iteration over the response + first = next(response) + print(f"got response data: {first.model_dump_json(indent=2)}") + + # Or you could automatically iterate through all of data. + # Note that the for loop will not exit until *all* of the data has been processed. + for data in response: + print(data.model_dump_json()) + + +async def async_main() -> None: + client = AsyncOpenAI() + response = await client.completions.create( + model="text-davinci-002", + prompt="1,2,3,", + max_tokens=5, + temperature=0, + stream=True, + ) + + # You can manually control iteration over the response. + # In Python 3.10+ you can also use the `await anext(response)` builtin instead + first = await response.__anext__() + print(f"got response data: {first.model_dump_json(indent=2)}") + + # Or you could automatically iterate through all of data. + # Note that the for loop will not exit until *all* of the data has been processed. + async for data in response: + print(data.model_dump_json()) + + +sync_main() + +asyncio.run(async_main()) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000000..a4517a002d --- /dev/null +++ b/mypy.ini @@ -0,0 +1,47 @@ +[mypy] +pretty = True +show_error_codes = True + +# Exclude _files.py because mypy isn't smart enough to apply +# the correct type narrowing and as this is an internal module +# it's fine to just use Pyright. +exclude = ^(src/openai/_files\.py|_dev/.*\.py)$ + +strict_equality = True +implicit_reexport = True +check_untyped_defs = True +no_implicit_optional = True + +warn_return_any = True +warn_unreachable = True +warn_unused_configs = True + +# Turn these options off as it could cause conflicts +# with the Pyright options. +warn_unused_ignores = False +warn_redundant_casts = False + +disallow_any_generics = True +disallow_untyped_defs = True +disallow_untyped_calls = True +disallow_subclassing_any = True +disallow_incomplete_defs = True +disallow_untyped_decorators = True +cache_fine_grained = True + +# By default, mypy reports an error if you assign a value to the result +# of a function call that doesn't return anything. We do this in our test +# cases: +# ``` +# result = ... +# assert result is None +# ``` +# Changing this codegen to make mypy happy would increase complexity +# and would not be worth it. +disable_error_code = func-returns-value + +# https://github.com/python/mypy/issues/12162 +[mypy.overrides] +module = "black.files.*" +ignore_errors = true +ignore_missing_imports = true diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000000..53bca7ff2a --- /dev/null +++ b/noxfile.py @@ -0,0 +1,9 @@ +import nox + + +@nox.session(reuse_venv=True, name="test-pydantic-v1") +def test_pydantic_v1(session: nox.Session) -> None: + session.install("-r", "requirements-dev.lock") + session.install("pydantic<2") + + session.run("pytest", "--showlocals", "--ignore=tests/functional", *session.posargs) diff --git a/openai/__init__.py b/openai/__init__.py deleted file mode 100644 index b44e50f97f..0000000000 --- a/openai/__init__.py +++ /dev/null @@ -1,106 +0,0 @@ -# OpenAI Python bindings. -# -# Originally forked from the MIT-licensed Stripe Python bindings. - -import os -import sys -from typing import TYPE_CHECKING, Optional, Union, Callable - -from contextvars import ContextVar - -if "pkg_resources" not in sys.modules: - # workaround for the following: - # https://github.com/benoitc/gunicorn/pull/2539 - sys.modules["pkg_resources"] = object() # type: ignore[assignment] - import aiohttp - - del sys.modules["pkg_resources"] - -from openai.api_resources import ( - Audio, - ChatCompletion, - Completion, - Customer, - Deployment, - Edit, - Embedding, - Engine, - ErrorObject, - File, - FineTune, - FineTuningJob, - Image, - Model, - Moderation, -) -from openai.error import APIError, InvalidRequestError, OpenAIError -from openai.version import VERSION - -if TYPE_CHECKING: - import requests - from aiohttp import ClientSession - -api_key = os.environ.get("OPENAI_API_KEY") -# Path of a file with an API key, whose contents can change. Supercedes -# `api_key` if set. The main use case is volume-mounted Kubernetes secrets, -# which are updated automatically. -api_key_path: Optional[str] = os.environ.get("OPENAI_API_KEY_PATH") - -organization = os.environ.get("OPENAI_ORGANIZATION") -api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") -api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") -api_version = os.environ.get( - "OPENAI_API_VERSION", - ("2023-05-15" if api_type in ("azure", "azure_ad", "azuread") else None), -) -verify_ssl_certs = True # No effect. Certificates are always verified. -proxy = None -app_info = None -enable_telemetry = False # Ignored; the telemetry feature was removed. -ca_bundle_path = None # No longer used, feature was removed -debug = False -log = None # Set to either 'debug' or 'info', controls console logging - -requestssession: Optional[ - Union["requests.Session", Callable[[], "requests.Session"]] -] = None # Provide a requests.Session or Session factory. - -aiosession: ContextVar[Optional["ClientSession"]] = ContextVar( - "aiohttp-session", default=None -) # Acts as a global aiohttp ClientSession that reuses connections. -# This is user-supplied; otherwise, a session is remade for each request. - -__version__ = VERSION -__all__ = [ - "APIError", - "Audio", - "ChatCompletion", - "Completion", - "Customer", - "Edit", - "Image", - "Deployment", - "Embedding", - "Engine", - "ErrorObject", - "File", - "FineTune", - "FineTuningJob", - "InvalidRequestError", - "Model", - "Moderation", - "OpenAIError", - "api_base", - "api_key", - "api_type", - "api_key_path", - "api_version", - "app_info", - "ca_bundle_path", - "debug", - "enable_telemetry", - "log", - "organization", - "proxy", - "verify_ssl_certs", -] diff --git a/openai/_openai_scripts.py b/openai/_openai_scripts.py deleted file mode 100755 index 497de19fab..0000000000 --- a/openai/_openai_scripts.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python -import argparse -import logging -import sys - -import openai -from openai import version -from openai.cli import api_register, display_error, tools_register, wandb_register - -logger = logging.getLogger() -formatter = logging.Formatter("[%(asctime)s] %(message)s") -handler = logging.StreamHandler(sys.stderr) -handler.setFormatter(formatter) -logger.addHandler(handler) - - -def main(): - parser = argparse.ArgumentParser(description=None) - parser.add_argument( - "-V", - "--version", - action="http://github.com/version", - version="%(prog)s " + version.VERSION, - ) - parser.add_argument( - "-v", - "--verbose", - action="http://github.com/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)", - ) - - def help(args): - parser.print_help() - - parser.set_defaults(func=help) - - subparsers = parser.add_subparsers() - sub_api = subparsers.add_parser("api", help="Direct API calls") - sub_tools = subparsers.add_parser("tools", help="Client side tools for convenience") - sub_wandb = subparsers.add_parser("wandb", help="Logging with Weights & Biases, see https://docs.wandb.ai/guides/integrations/openai for documentation") - - api_register(sub_api) - tools_register(sub_tools) - wandb_register(sub_wandb) - - args = parser.parse_args() - if args.verbosity == 1: - logger.setLevel(logging.INFO) - elif args.verbosity >= 2: - logger.setLevel(logging.DEBUG) - - openai.debug = True - if args.api_key is not None: - openai.api_key = args.api_key - if args.api_base is not None: - openai.api_base = args.api_base - if args.organization is not None: - openai.organization = args.organization - if args.proxy is not None: - openai.proxy = {} - for proxy in args.proxy: - if proxy.startswith('https'): - openai.proxy['https'] = proxy - elif proxy.startswith('http'): - openai.proxy['http'] = proxy - - try: - args.func(args) - except openai.error.OpenAIError as e: - display_error(e) - return 1 - except KeyboardInterrupt: - sys.stderr.write("\n") - return 1 - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/openai/api_requestor.py b/openai/api_requestor.py deleted file mode 100644 index c051bc64f2..0000000000 --- a/openai/api_requestor.py +++ /dev/null @@ -1,799 +0,0 @@ -import asyncio -import json -import time -import platform -import sys -import threading -import time -import warnings -from json import JSONDecodeError -from typing import ( - AsyncContextManager, - AsyncGenerator, - Callable, - Dict, - Iterator, - Optional, - Tuple, - Union, - overload, -) -from urllib.parse import urlencode, urlsplit, urlunsplit - -import aiohttp -import requests - -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - -import openai -from openai import error, util, version -from openai.openai_response import OpenAIResponse -from openai.util import ApiType - -TIMEOUT_SECS = 600 -MAX_SESSION_LIFETIME_SECS = 180 -MAX_CONNECTION_RETRIES = 2 - -# Has one attribute per thread, 'session'. -_thread_context = threading.local() - - -def _build_api_url(http://github.com/url,%20query): - scheme, netloc, path, base_query, fragment = urlsplit(url) - - if base_query: - query = "%s&%s" % (base_query, query) - - return urlunsplit((scheme, netloc, path, query, fragment)) - - -def _requests_proxies_arg(proxy) -> Optional[Dict[str, str]]: - """Returns a value suitable for the 'proxies' argument to 'requests.request.""" - if proxy is None: - return None - elif isinstance(proxy, str): - return {"http": proxy, "https": proxy} - elif isinstance(proxy, dict): - return proxy.copy() - else: - raise ValueError( - "'openai.proxy' must be specified as either a string URL or a dict with string URL under the https and/or http keys." - ) - - -def _aiohttp_proxies_arg(proxy) -> Optional[str]: - """Returns a value suitable for the 'proxies' argument to 'aiohttp.ClientSession.request.""" - if proxy is None: - return None - elif isinstance(proxy, str): - return proxy - elif isinstance(proxy, dict): - return proxy["https"] if "https" in proxy else proxy["http"] - else: - raise ValueError( - "'openai.proxy' must be specified as either a string URL or a dict with string URL under the https and/or http keys." - ) - - -def _make_session() -> requests.Session: - if openai.requestssession: - if isinstance(openai.requestssession, requests.Session): - return openai.requestssession - return openai.requestssession() - if not openai.verify_ssl_certs: - warnings.warn("verify_ssl_certs is ignored; openai always verifies.") - s = requests.Session() - proxies = _requests_proxies_arg(openai.proxy) - if proxies: - s.proxies = proxies - s.mount( - "https://", - requests.adapters.HTTPAdapter(max_retries=MAX_CONNECTION_RETRIES), - ) - return s - - -def parse_stream_helper(line: bytes) -> Optional[str]: - if line and line.startswith(b"data:"): - if line.startswith(b"data: "): - # SSE event may be valid when it contain whitespace - line = line[len(b"data: "):] - else: - line = line[len(b"data:"):] - if line.strip() == b"[DONE]": - # return here will cause GeneratorExit exception in urllib3 - # and it will close http connection with TCP Reset - return None - else: - return line.decode("utf-8") - return None - - -def parse_stream(rbody: Iterator[bytes]) -> Iterator[str]: - for line in rbody: - _line = parse_stream_helper(line) - if _line is not None: - yield _line - - -async def parse_stream_async(rbody: aiohttp.StreamReader): - async for line in rbody: - _line = parse_stream_helper(line) - if _line is not None: - yield _line - - -class APIRequestor: - def __init__( - self, - key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - ): - self.api_base = api_base or openai.api_base - self.api_key = key or util.default_api_key() - self.api_type = ( - ApiType.from_str(api_type) - if api_type - else ApiType.from_str(openai.api_type) - ) - self.api_version = api_version or openai.api_version - self.organization = organization or openai.organization - - @classmethod - def format_app_info(cls, info): - str = info["name"] - if info["version"]: - str += "/%s" % (info["version"],) - if info["url"]: - str += " (%s)" % (info["url"],) - return str - - def _check_polling_response(self, response: OpenAIResponse, predicate: Callable[[OpenAIResponse], bool]): - if not predicate(response): - return - error_data = response.data['error'] - message = error_data.get('message', 'Operation failed') - code = error_data.get('code') - raise error.OpenAIError(message=message, code=code) - - def _poll( - self, - method, - url, - until, - failed, - params = None, - headers = None, - interval = None, - delay = None - ) -> Tuple[Iterator[OpenAIResponse], bool, str]: - if delay: - time.sleep(delay) - - response, b, api_key = self.request(method, url, params, headers) - self._check_polling_response(response, failed) - start_time = time.time() - while not until(response): - if time.time() - start_time > TIMEOUT_SECS: - raise error.Timeout("Operation polling timed out.") - - time.sleep(interval or response.retry_after or 10) - response, b, api_key = self.request(method, url, params, headers) - self._check_polling_response(response, failed) - - response.data = response.data['result'] - return response, b, api_key - - async def _apoll( - self, - method, - url, - until, - failed, - params = None, - headers = None, - interval = None, - delay = None - ) -> Tuple[Iterator[OpenAIResponse], bool, str]: - if delay: - await asyncio.sleep(delay) - - response, b, api_key = await self.arequest(method, url, params, headers) - self._check_polling_response(response, failed) - start_time = time.time() - while not until(response): - if time.time() - start_time > TIMEOUT_SECS: - raise error.Timeout("Operation polling timed out.") - - await asyncio.sleep(interval or response.retry_after or 10) - response, b, api_key = await self.arequest(method, url, params, headers) - self._check_polling_response(response, failed) - - response.data = response.data['result'] - return response, b, api_key - - @overload - def request( - self, - method, - url, - params, - headers, - files, - stream: Literal[True], - request_id: Optional[str] = ..., - request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., - ) -> Tuple[Iterator[OpenAIResponse], bool, str]: - pass - - @overload - def request( - self, - method, - url, - params=..., - headers=..., - files=..., - *, - stream: Literal[True], - request_id: Optional[str] = ..., - request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., - ) -> Tuple[Iterator[OpenAIResponse], bool, str]: - pass - - @overload - def request( - self, - method, - url, - params=..., - headers=..., - files=..., - stream: Literal[False] = ..., - request_id: Optional[str] = ..., - request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., - ) -> Tuple[OpenAIResponse, bool, str]: - pass - - @overload - def request( - self, - method, - url, - params=..., - headers=..., - files=..., - stream: bool = ..., - request_id: Optional[str] = ..., - request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., - ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool, str]: - pass - - def request( - self, - method, - url, - params=None, - headers=None, - files=None, - stream: bool = False, - request_id: Optional[str] = None, - request_timeout: Optional[Union[float, Tuple[float, float]]] = None, - ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool, str]: - result = self.request_raw( - method.lower(), - url, - params=params, - supplied_headers=headers, - files=files, - stream=stream, - request_id=request_id, - request_timeout=request_timeout, - ) - resp, got_stream = self._interpret_response(result, stream) - return resp, got_stream, self.api_key - - @overload - async def arequest( - self, - method, - url, - params, - headers, - files, - stream: Literal[True], - request_id: Optional[str] = ..., - request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., - ) -> Tuple[AsyncGenerator[OpenAIResponse, None], bool, str]: - pass - - @overload - async def arequest( - self, - method, - url, - params=..., - headers=..., - files=..., - *, - stream: Literal[True], - request_id: Optional[str] = ..., - request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., - ) -> Tuple[AsyncGenerator[OpenAIResponse, None], bool, str]: - pass - - @overload - async def arequest( - self, - method, - url, - params=..., - headers=..., - files=..., - stream: Literal[False] = ..., - request_id: Optional[str] = ..., - request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., - ) -> Tuple[OpenAIResponse, bool, str]: - pass - - @overload - async def arequest( - self, - method, - url, - params=..., - headers=..., - files=..., - stream: bool = ..., - request_id: Optional[str] = ..., - request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., - ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: - pass - - async def arequest( - self, - method, - url, - params=None, - headers=None, - files=None, - stream: bool = False, - request_id: Optional[str] = None, - request_timeout: Optional[Union[float, Tuple[float, float]]] = None, - ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: - ctx = AioHTTPSession() - session = await ctx.__aenter__() - result = None - try: - result = await self.arequest_raw( - method.lower(), - url, - session, - params=params, - supplied_headers=headers, - files=files, - request_id=request_id, - request_timeout=request_timeout, - ) - resp, got_stream = await self._interpret_async_response(result, stream) - except Exception: - # Close the request before exiting session context. - if result is not None: - result.release() - await ctx.__aexit__(None, None, None) - raise - if got_stream: - - async def wrap_resp(): - assert isinstance(resp, AsyncGenerator) - try: - async for r in resp: - yield r - finally: - # Close the request before exiting session context. Important to do it here - # as if stream is not fully exhausted, we need to close the request nevertheless. - result.release() - await ctx.__aexit__(None, None, None) - - return wrap_resp(), got_stream, self.api_key - else: - # Close the request before exiting session context. - result.release() - await ctx.__aexit__(None, None, None) - return resp, got_stream, self.api_key - - def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): - try: - error_data = resp["error"] - except (KeyError, TypeError): - raise error.APIError( - "Invalid response object from API: %r (HTTP response code " - "was %d)" % (rbody, rcode), - rbody, - rcode, - resp, - ) - - if "internal_message" in error_data: - error_data["message"] += "\n\n" + error_data["internal_message"] - - util.log_info( - "OpenAI API error received", - error_code=error_data.get("code"), - error_type=error_data.get("type"), - error_message=error_data.get("message"), - error_param=error_data.get("param"), - stream_error=stream_error, - ) - - # Rate limits were previously coded as 400's with code 'rate_limit' - if rcode == 429: - return error.RateLimitError( - error_data.get("message"), rbody, rcode, resp, rheaders - ) - elif rcode in [400, 404, 415]: - return error.InvalidRequestError( - error_data.get("message"), - error_data.get("param"), - error_data.get("code"), - rbody, - rcode, - resp, - rheaders, - ) - elif rcode == 401: - return error.AuthenticationError( - error_data.get("message"), rbody, rcode, resp, rheaders - ) - elif rcode == 403: - return error.PermissionError( - error_data.get("message"), rbody, rcode, resp, rheaders - ) - elif rcode == 409: - return error.TryAgain( - error_data.get("message"), rbody, rcode, resp, rheaders - ) - elif stream_error: - # TODO: we will soon attach status codes to stream errors - parts = [error_data.get("message"), "(Error occurred while streaming.)"] - message = " ".join([p for p in parts if p is not None]) - return error.APIError(message, rbody, rcode, resp, rheaders) - else: - return error.APIError( - f"{error_data.get('message')} {rbody} {rcode} {resp} {rheaders}", - rbody, - rcode, - resp, - rheaders, - ) - - def request_headers( - self, method: str, extra, request_id: Optional[str] - ) -> Dict[str, str]: - user_agent = "OpenAI/v1 PythonBindings/%s" % (version.VERSION,) - if openai.app_info: - user_agent += " " + self.format_app_info(openai.app_info) - - uname_without_node = " ".join( - v for k, v in platform.uname()._asdict().items() if k != "node" - ) - ua = { - "bindings_version": version.VERSION, - "httplib": "requests", - "lang": "python", - "lang_version": platform.python_version(), - "platform": platform.platform(), - "publisher": "openai", - "uname": uname_without_node, - } - if openai.app_info: - ua["application"] = openai.app_info - - headers = { - "X-OpenAI-Client-User-Agent": json.dumps(ua), - "User-Agent": user_agent, - } - - headers.update(util.api_key_to_header(self.api_type, self.api_key)) - - if self.organization: - headers["OpenAI-Organization"] = self.organization - - if self.api_version is not None and self.api_type == ApiType.OPEN_AI: - headers["OpenAI-Version"] = self.api_version - if request_id is not None: - headers["X-Request-Id"] = request_id - if openai.debug: - headers["OpenAI-Debug"] = "true" - headers.update(extra) - - return headers - - def _validate_headers( - self, supplied_headers: Optional[Dict[str, str]] - ) -> Dict[str, str]: - headers: Dict[str, str] = {} - if supplied_headers is None: - return headers - - if not isinstance(supplied_headers, dict): - raise TypeError("Headers must be a dictionary") - - for k, v in supplied_headers.items(): - if not isinstance(k, str): - raise TypeError("Header keys must be strings") - if not isinstance(v, str): - raise TypeError("Header values must be strings") - headers[k] = v - - # NOTE: It is possible to do more validation of the headers, but a request could always - # be made to the API manually with invalid headers, so we need to handle them server side. - - return headers - - def _prepare_request_raw( - self, - url, - supplied_headers, - method, - params, - files, - request_id: Optional[str], - ) -> Tuple[str, Dict[str, str], Optional[bytes]]: - abs_url = "%s%s" % (self.api_base, url) - headers = self._validate_headers(supplied_headers) - - data = None - if method == "get" or method == "delete": - if params: - encoded_params = urlencode( - [(k, v) for k, v in params.items() if v is not None] - ) - abs_url = _build_api_url(http://github.com/abs_url,%20encoded_params) - elif method in {"post", "put"}: - if params and files: - data = params - if params and not files: - data = json.dumps(params).encode() - headers["Content-Type"] = "application/json" - else: - raise error.APIConnectionError( - "Unrecognized HTTP method %r. This may indicate a bug in the " - "OpenAI bindings. Please contact us through our help center at help.openai.com for " - "assistance." % (method,) - ) - - headers = self.request_headers(method, headers, request_id) - - util.log_debug("Request to OpenAI API", method=method, path=abs_url) - util.log_debug("Post details", data=data, api_version=self.api_version) - - return abs_url, headers, data - - def request_raw( - self, - method, - url, - *, - params=None, - supplied_headers: Optional[Dict[str, str]] = None, - files=None, - stream: bool = False, - request_id: Optional[str] = None, - request_timeout: Optional[Union[float, Tuple[float, float]]] = None, - ) -> requests.Response: - abs_url, headers, data = self._prepare_request_raw( - url, supplied_headers, method, params, files, request_id - ) - - if not hasattr(_thread_context, "session"): - _thread_context.session = _make_session() - _thread_context.session_create_time = time.time() - elif ( - time.time() - getattr(_thread_context, "session_create_time", 0) - >= MAX_SESSION_LIFETIME_SECS - ): - _thread_context.session.close() - _thread_context.session = _make_session() - _thread_context.session_create_time = time.time() - try: - result = _thread_context.session.request( - method, - abs_url, - headers=headers, - data=data, - files=files, - stream=stream, - timeout=request_timeout if request_timeout else TIMEOUT_SECS, - proxies=_thread_context.session.proxies, - ) - except requests.exceptions.Timeout as e: - raise error.Timeout("Request timed out: {}".format(e)) from e - except requests.exceptions.RequestException as e: - raise error.APIConnectionError( - "Error communicating with OpenAI: {}".format(e) - ) from e - util.log_debug( - "OpenAI API response", - path=abs_url, - response_code=result.status_code, - processing_ms=result.headers.get("OpenAI-Processing-Ms"), - request_id=result.headers.get("X-Request-Id"), - ) - # Don't read the whole stream for debug logging unless necessary. - if openai.log == "debug": - util.log_debug( - "API response body", body=result.content, headers=result.headers - ) - return result - - async def arequest_raw( - self, - method, - url, - session, - *, - params=None, - supplied_headers: Optional[Dict[str, str]] = None, - files=None, - request_id: Optional[str] = None, - request_timeout: Optional[Union[float, Tuple[float, float]]] = None, - ) -> aiohttp.ClientResponse: - abs_url, headers, data = self._prepare_request_raw( - url, supplied_headers, method, params, files, request_id - ) - - if isinstance(request_timeout, tuple): - timeout = aiohttp.ClientTimeout( - connect=request_timeout[0], - total=request_timeout[1], - ) - else: - timeout = aiohttp.ClientTimeout( - total=request_timeout if request_timeout else TIMEOUT_SECS - ) - - if files: - # TODO: Use `aiohttp.MultipartWriter` to create the multipart form data here. - # For now we use the private `requests` method that is known to have worked so far. - data, content_type = requests.models.RequestEncodingMixin._encode_files( # type: ignore - files, data - ) - headers["Content-Type"] = content_type - request_kwargs = { - "method": method, - "url": abs_url, - "headers": headers, - "data": data, - "proxy": _aiohttp_proxies_arg(openai.proxy), - "timeout": timeout, - } - try: - result = await session.request(**request_kwargs) - util.log_info( - "OpenAI API response", - path=abs_url, - response_code=result.status, - processing_ms=result.headers.get("OpenAI-Processing-Ms"), - request_id=result.headers.get("X-Request-Id"), - ) - # Don't read the whole stream for debug logging unless necessary. - if openai.log == "debug": - util.log_debug( - "API response body", body=result.content, headers=result.headers - ) - return result - except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as e: - raise error.Timeout("Request timed out") from e - except aiohttp.ClientError as e: - raise error.APIConnectionError("Error communicating with OpenAI") from e - - def _interpret_response( - self, result: requests.Response, stream: bool - ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool]: - """Returns the response(s) and a bool indicating whether it is a stream.""" - if stream and "text/event-stream" in result.headers.get("Content-Type", ""): - return ( - self._interpret_response_line( - line, result.status_code, result.headers, stream=True - ) - for line in parse_stream(result.iter_lines()) - ), True - else: - return ( - self._interpret_response_line( - result.content.decode("utf-8"), - result.status_code, - result.headers, - stream=False, - ), - False, - ) - - async def _interpret_async_response( - self, result: aiohttp.ClientResponse, stream: bool - ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool]: - """Returns the response(s) and a bool indicating whether it is a stream.""" - if stream and "text/event-stream" in result.headers.get("Content-Type", ""): - return ( - self._interpret_response_line( - line, result.status, result.headers, stream=True - ) - async for line in parse_stream_async(result.content) - ), True - else: - try: - await result.read() - except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as e: - raise error.Timeout("Request timed out") from e - except aiohttp.ClientError as e: - util.log_warn(e, body=result.content) - return ( - self._interpret_response_line( - (await result.read()).decode("utf-8"), - result.status, - result.headers, - stream=False, - ), - False, - ) - - def _interpret_response_line( - self, rbody: str, rcode: int, rheaders, stream: bool - ) -> OpenAIResponse: - # HTTP 204 response code does not have any content in the body. - if rcode == 204: - return OpenAIResponse(None, rheaders) - - if rcode == 503: - raise error.ServiceUnavailableError( - "The server is overloaded or not ready yet.", - rbody, - rcode, - headers=rheaders, - ) - try: - if 'text/plain' in rheaders.get('Content-Type', ''): - data = rbody - else: - data = json.loads(rbody) - except (JSONDecodeError, UnicodeDecodeError) as e: - raise error.APIError( - f"HTTP code {rcode} from API ({rbody})", rbody, rcode, headers=rheaders - ) from e - resp = OpenAIResponse(data, rheaders) - # In the future, we might add a "status" parameter to errors - # to better handle the "error while streaming" case. - stream_error = stream and "error" in resp.data - if stream_error or not 200 <= rcode < 300: - raise self.handle_error_response( - rbody, rcode, resp.data, rheaders, stream_error=stream_error - ) - return resp - - -class AioHTTPSession(AsyncContextManager): - def __init__(self): - self._session = None - self._should_close_session = False - - async def __aenter__(self): - self._session = openai.aiosession.get() - if self._session is None: - self._session = await aiohttp.ClientSession().__aenter__() - self._should_close_session = True - - return self._session - - async def __aexit__(self, exc_type, exc_value, traceback): - if self._session is None: - raise RuntimeError("Session is not initialized") - - if self._should_close_session: - await self._session.__aexit__(exc_type, exc_value, traceback) \ No newline at end of file diff --git a/openai/api_resources/__init__.py b/openai/api_resources/__init__.py deleted file mode 100644 index 78bad1a22a..0000000000 --- a/openai/api_resources/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from openai.api_resources.audio import Audio # noqa: F401 -from openai.api_resources.chat_completion import ChatCompletion # noqa: F401 -from openai.api_resources.completion import Completion # noqa: F401 -from openai.api_resources.customer import Customer # noqa: F401 -from openai.api_resources.deployment import Deployment # noqa: F401 -from openai.api_resources.edit import Edit # noqa: F401 -from openai.api_resources.embedding import Embedding # noqa: F401 -from openai.api_resources.engine import Engine # noqa: F401 -from openai.api_resources.error_object import ErrorObject # noqa: F401 -from openai.api_resources.file import File # noqa: F401 -from openai.api_resources.fine_tune import FineTune # noqa: F401 -from openai.api_resources.fine_tuning import FineTuningJob # noqa: F401 -from openai.api_resources.image import Image # noqa: F401 -from openai.api_resources.model import Model # noqa: F401 -from openai.api_resources.moderation import Moderation # noqa: F401 diff --git a/openai/api_resources/abstract/__init__.py b/openai/api_resources/abstract/__init__.py deleted file mode 100644 index 48482bd87a..0000000000 --- a/openai/api_resources/abstract/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# flake8: noqa - -from openai.api_resources.abstract.api_resource import APIResource -from openai.api_resources.abstract.createable_api_resource import CreateableAPIResource -from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource -from openai.api_resources.abstract.listable_api_resource import ListableAPIResource -from openai.api_resources.abstract.nested_resource_class_methods import ( - nested_resource_class_methods, -) -from openai.api_resources.abstract.paginatable_api_resource import ( - PaginatableAPIResource, -) -from openai.api_resources.abstract.updateable_api_resource import UpdateableAPIResource diff --git a/openai/api_resources/abstract/api_resource.py b/openai/api_resources/abstract/api_resource.py deleted file mode 100644 index 5d54bb9fd8..0000000000 --- a/openai/api_resources/abstract/api_resource.py +++ /dev/null @@ -1,172 +0,0 @@ -from urllib.parse import quote_plus - -import openai -from openai import api_requestor, error, util -from openai.openai_object import OpenAIObject -from openai.util import ApiType -from typing import Optional - - -class APIResource(OpenAIObject): - api_prefix = "" - azure_api_prefix = "openai" - azure_deployments_prefix = "deployments" - - @classmethod - def retrieve( - cls, id, api_key=None, request_id=None, request_timeout=None, **params - ): - instance = cls(id=id, api_key=api_key, **params) - instance.refresh(request_id=request_id, request_timeout=request_timeout) - return instance - - @classmethod - def aretrieve( - cls, id, api_key=None, request_id=None, request_timeout=None, **params - ): - instance = cls(id=id, api_key=api_key, **params) - return instance.arefresh(request_id=request_id, request_timeout=request_timeout) - - def refresh(self, request_id=None, request_timeout=None): - self.refresh_from( - self.request( - "get", - self.instance_url(), - request_id=request_id, - request_timeout=request_timeout, - ) - ) - return self - - async def arefresh(self, request_id=None, request_timeout=None): - self.refresh_from( - await self.arequest( - "get", - self.instance_url(http://github.com/operation=%22refresh"), - request_id=request_id, - request_timeout=request_timeout, - ) - ) - return self - - @classmethod - def class_url(http://github.com/cls): - if cls == APIResource: - raise NotImplementedError( - "APIResource is an abstract class. You should perform actions on its subclasses." - ) - # Namespaces are separated in object names with periods (.) and in URLs - # with forward slashes (/), so replace the former with the latter. - base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - if cls.api_prefix: - return "/%s/%s" % (cls.api_prefix, base) - return "/%s" % (base) - - def instance_url(http://github.com/self,%20operation=None): - id = self.get("id") - - if not isinstance(id, str): - raise error.InvalidRequestError( - "Could not determine which URL to request: %s instance " - "has invalid ID: %r, %s. ID should be of type `str` (or" - " `unicode`)" % (type(self).__name__, id, type(id)), - "id", - ) - api_version = self.api_version or openai.api_version - extn = quote_plus(id) - - if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - if not api_version: - raise error.InvalidRequestError( - "An API version is required for the Azure API type." - ) - - if not operation: - base = self.class_url() - return "/%s%s/%s?api-version=%s" % ( - self.azure_api_prefix, - base, - extn, - api_version, - ) - - return "/%s/%s/%s/%s?api-version=%s" % ( - self.azure_api_prefix, - self.azure_deployments_prefix, - extn, - operation, - api_version, - ) - - elif self.typed_api_type == ApiType.OPEN_AI: - base = self.class_url() - return "%s/%s" % (base, extn) - - else: - raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) - - # The `method_` and `url_` arguments are suffixed with an underscore to - # avoid conflicting with actual request parameters in `params`. - @classmethod - def _static_request( - cls, - method_, - url_, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_version=api_version, - organization=organization, - api_base=api_base, - api_type=api_type, - ) - response, _, api_key = requestor.request( - method_, url_, params, request_id=request_id - ) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def _astatic_request( - cls, - method_, - url_, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_version=api_version, - organization=organization, - api_base=api_base, - api_type=api_type, - ) - response, _, api_key = await requestor.arequest( - method_, url_, params, request_id=request_id - ) - return response - - @classmethod - def _get_api_type_and_version( - cls, api_type: Optional[str] = None, api_version: Optional[str] = None - ): - typed_api_type = ( - ApiType.from_str(api_type) - if api_type - else ApiType.from_str(openai.api_type) - ) - typed_api_version = api_version or openai.api_version - return (typed_api_type, typed_api_version) diff --git a/openai/api_resources/abstract/createable_api_resource.py b/openai/api_resources/abstract/createable_api_resource.py deleted file mode 100644 index 1361c02627..0000000000 --- a/openai/api_resources/abstract/createable_api_resource.py +++ /dev/null @@ -1,98 +0,0 @@ -from openai import api_requestor, util, error -from openai.api_resources.abstract.api_resource import APIResource -from openai.util import ApiType - - -class CreateableAPIResource(APIResource): - plain_old_data = False - - @classmethod - def __prepare_create_requestor( - cls, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = cls.class_url() - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - return requestor, url - - @classmethod - def create( - cls, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - requestor, url = cls.__prepare_create_requestor( - api_key, - api_base, - api_type, - api_version, - organization, - ) - - response, _, api_key = requestor.request( - "post", url, params, request_id=request_id - ) - - return util.convert_to_openai_object( - response, - api_key, - api_version, - organization, - plain_old_data=cls.plain_old_data, - ) - - @classmethod - async def acreate( - cls, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - requestor, url = cls.__prepare_create_requestor( - api_key, - api_base, - api_type, - api_version, - organization, - ) - - response, _, api_key = await requestor.arequest( - "post", url, params, request_id=request_id - ) - - return util.convert_to_openai_object( - response, - api_key, - api_version, - organization, - plain_old_data=cls.plain_old_data, - ) diff --git a/openai/api_resources/abstract/deletable_api_resource.py b/openai/api_resources/abstract/deletable_api_resource.py deleted file mode 100644 index a800ceb812..0000000000 --- a/openai/api_resources/abstract/deletable_api_resource.py +++ /dev/null @@ -1,48 +0,0 @@ -from urllib.parse import quote_plus -from typing import Awaitable - -from openai import error -from openai.api_resources.abstract.api_resource import APIResource -from openai.util import ApiType - - -class DeletableAPIResource(APIResource): - @classmethod - def __prepare_delete(cls, sid, api_type=None, api_version=None): - if isinstance(cls, APIResource): - raise ValueError(".delete may only be called as a class method now.") - - base = cls.class_url() - extn = quote_plus(sid) - - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - url = "/%s%s/%s?api-version=%s" % ( - cls.azure_api_prefix, - base, - extn, - api_version, - ) - elif typed_api_type == ApiType.OPEN_AI: - url = "%s/%s" % (base, extn) - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - return url - - @classmethod - def delete(cls, sid, api_type=None, api_version=None, **params): - url = cls.__prepare_delete(sid, api_type, api_version) - - return cls._static_request( - "delete", url, api_type=api_type, api_version=api_version, **params - ) - - @classmethod - def adelete(cls, sid, api_type=None, api_version=None, **params) -> Awaitable: - url = cls.__prepare_delete(sid, api_type, api_version) - - return cls._astatic_request( - "delete", url, api_type=api_type, api_version=api_version, **params - ) diff --git a/openai/api_resources/abstract/engine_api_resource.py b/openai/api_resources/abstract/engine_api_resource.py deleted file mode 100644 index bbef90e23e..0000000000 --- a/openai/api_resources/abstract/engine_api_resource.py +++ /dev/null @@ -1,328 +0,0 @@ -import time -from pydoc import apropos -from typing import Optional -from urllib.parse import quote_plus - -import openai -from openai import api_requestor, error, util -from openai.api_resources.abstract.api_resource import APIResource -from openai.openai_response import OpenAIResponse -from openai.util import ApiType - -MAX_TIMEOUT = 20 - - -class EngineAPIResource(APIResource): - plain_old_data = False - - def __init__(self, engine: Optional[str] = None, **kwargs): - super().__init__(engine=engine, **kwargs) - - @classmethod - def class_url( - cls, - engine: Optional[str] = None, - api_type: Optional[str] = None, - api_version: Optional[str] = None, - ): - # Namespaces are separated in object names with periods (.) and in URLs - # with forward slashes (/), so replace the former with the latter. - base = cls.OBJECT_NAME.replace(".", "/") # type: ignore - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - if not api_version: - raise error.InvalidRequestError( - "An API version is required for the Azure API type.", - "api_version" - ) - if engine is None: - raise error.InvalidRequestError( - "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service", - "engine" - ) - extn = quote_plus(engine) - return "/%s/%s/%s/%s?api-version=%s" % ( - cls.azure_api_prefix, - cls.azure_deployments_prefix, - extn, - base, - api_version, - ) - - elif typed_api_type == ApiType.OPEN_AI: - if engine is None: - return "/%s" % (base) - - extn = quote_plus(engine) - return "/engines/%s/%s" % (extn, base) - - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - - @classmethod - def __prepare_create_request( - cls, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - deployment_id = params.pop("deployment_id", None) - engine = params.pop("engine", deployment_id) - model = params.get("model", None) - timeout = params.pop("timeout", None) - stream = params.get("stream", False) - headers = params.pop("headers", None) - request_timeout = params.pop("request_timeout", None) - typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] - if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - if deployment_id is None and engine is None: - raise error.InvalidRequestError( - "Must provide an 'engine' or 'deployment_id' parameter to create a %s" - % cls, - "engine", - ) - else: - if model is None and engine is None: - raise error.InvalidRequestError( - "Must provide an 'engine' or 'model' parameter to create a %s" - % cls, - "engine", - ) - - if timeout is None: - # No special timeout handling - pass - elif timeout > 0: - # API only supports timeouts up to MAX_TIMEOUT - params["timeout"] = min(timeout, MAX_TIMEOUT) - timeout = (timeout - params["timeout"]) or None - elif timeout == 0: - params["timeout"] = MAX_TIMEOUT - - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - url = cls.class_url(http://github.com/engine,%20api_type,%20api_version) - return ( - deployment_id, - engine, - timeout, - stream, - headers, - request_timeout, - typed_api_type, - requestor, - url, - params, - ) - - @classmethod - def create( - cls, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - ( - deployment_id, - engine, - timeout, - stream, - headers, - request_timeout, - typed_api_type, - requestor, - url, - params, - ) = cls.__prepare_create_request( - api_key, api_base, api_type, api_version, organization, **params - ) - - response, _, api_key = requestor.request( - "post", - url, - params=params, - headers=headers, - stream=stream, - request_id=request_id, - request_timeout=request_timeout, - ) - - if stream: - # must be an iterator - assert not isinstance(response, OpenAIResponse) - return ( - util.convert_to_openai_object( - line, - api_key, - api_version, - organization, - engine=engine, - plain_old_data=cls.plain_old_data, - ) - for line in response - ) - else: - obj = util.convert_to_openai_object( - response, - api_key, - api_version, - organization, - engine=engine, - plain_old_data=cls.plain_old_data, - ) - - if timeout is not None: - obj.wait(timeout=timeout or None) - - return obj - - @classmethod - async def acreate( - cls, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - ( - deployment_id, - engine, - timeout, - stream, - headers, - request_timeout, - typed_api_type, - requestor, - url, - params, - ) = cls.__prepare_create_request( - api_key, api_base, api_type, api_version, organization, **params - ) - response, _, api_key = await requestor.arequest( - "post", - url, - params=params, - headers=headers, - stream=stream, - request_id=request_id, - request_timeout=request_timeout, - ) - - if stream: - # must be an iterator - assert not isinstance(response, OpenAIResponse) - return ( - util.convert_to_openai_object( - line, - api_key, - api_version, - organization, - engine=engine, - plain_old_data=cls.plain_old_data, - ) - async for line in response - ) - else: - obj = util.convert_to_openai_object( - response, - api_key, - api_version, - organization, - engine=engine, - plain_old_data=cls.plain_old_data, - ) - - if timeout is not None: - await obj.await_(timeout=timeout or None) - - return obj - - def instance_url(http://github.com/self): - id = self.get("id") - - if not isinstance(id, str): - raise error.InvalidRequestError( - f"Could not determine which URL to request: {type(self).__name__} instance has invalid ID: {id}, {type(id)}. ID should be of type str.", - "id", - ) - - extn = quote_plus(id) - params_connector = "?" - - if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - api_version = self.api_version or openai.api_version - if not api_version: - raise error.InvalidRequestError( - "An API version is required for the Azure API type.", - "api_version" - ) - base = self.OBJECT_NAME.replace(".", "/") - url = "/%s/%s/%s/%s/%s?api-version=%s" % ( - self.azure_api_prefix, - self.azure_deployments_prefix, - self.engine, - base, - extn, - api_version, - ) - params_connector = "&" - - elif self.typed_api_type == ApiType.OPEN_AI: - base = self.class_url(http://github.com/self.engine,%20self.api_type,%20self.api_version) - url = "%s/%s" % (base, extn) - - else: - raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) - - timeout = self.get("timeout") - if timeout is not None: - timeout = quote_plus(str(timeout)) - url += params_connector + "timeout={}".format(timeout) - return url - - def wait(self, timeout=None): - start = time.time() - while self.status != "complete": - self.timeout = ( - min(timeout + start - time.time(), MAX_TIMEOUT) - if timeout is not None - else MAX_TIMEOUT - ) - if self.timeout < 0: - del self.timeout - break - self.refresh() - return self - - async def await_(self, timeout=None): - """Async version of `EngineApiResource.wait`""" - start = time.time() - while self.status != "complete": - self.timeout = ( - min(timeout + start - time.time(), MAX_TIMEOUT) - if timeout is not None - else MAX_TIMEOUT - ) - if self.timeout < 0: - del self.timeout - break - await self.arefresh() - return self diff --git a/openai/api_resources/abstract/listable_api_resource.py b/openai/api_resources/abstract/listable_api_resource.py deleted file mode 100644 index 3e59979f13..0000000000 --- a/openai/api_resources/abstract/listable_api_resource.py +++ /dev/null @@ -1,95 +0,0 @@ -from openai import api_requestor, util, error -from openai.api_resources.abstract.api_resource import APIResource -from openai.util import ApiType - - -class ListableAPIResource(APIResource): - @classmethod - def auto_paging_iter(cls, *args, **params): - return cls.list(*args, **params).auto_paging_iter() - - @classmethod - def __prepare_list_requestor( - cls, - api_key=None, - api_version=None, - organization=None, - api_base=None, - api_type=None, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or cls.api_base(), - api_version=api_version, - api_type=api_type, - organization=organization, - ) - - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = cls.class_url() - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - return requestor, url - - @classmethod - def list( - cls, - api_key=None, - request_id=None, - api_version=None, - organization=None, - api_base=None, - api_type=None, - **params, - ): - requestor, url = cls.__prepare_list_requestor( - api_key, - api_version, - organization, - api_base, - api_type, - ) - - response, _, api_key = requestor.request( - "get", url, params, request_id=request_id - ) - openai_object = util.convert_to_openai_object( - response, api_key, api_version, organization - ) - openai_object._retrieve_params = params - return openai_object - - @classmethod - async def alist( - cls, - api_key=None, - request_id=None, - api_version=None, - organization=None, - api_base=None, - api_type=None, - **params, - ): - requestor, url = cls.__prepare_list_requestor( - api_key, - api_version, - organization, - api_base, - api_type, - ) - - response, _, api_key = await requestor.arequest( - "get", url, params, request_id=request_id - ) - openai_object = util.convert_to_openai_object( - response, api_key, api_version, organization - ) - openai_object._retrieve_params = params - return openai_object diff --git a/openai/api_resources/abstract/nested_resource_class_methods.py b/openai/api_resources/abstract/nested_resource_class_methods.py deleted file mode 100644 index 68197ab1fa..0000000000 --- a/openai/api_resources/abstract/nested_resource_class_methods.py +++ /dev/null @@ -1,169 +0,0 @@ -from urllib.parse import quote_plus - -from openai import api_requestor, util - - -def _nested_resource_class_methods( - resource, - path=None, - operations=None, - resource_plural=None, - async_=False, -): - if resource_plural is None: - resource_plural = "%ss" % resource - if path is None: - path = resource_plural - if operations is None: - raise ValueError("operations list required") - - def wrapper(cls): - def nested_resource_url(http://github.com/cls,%20id,%20nested_id=None): - url = "%s/%s/%s" % (cls.class_url(), quote_plus(id), quote_plus(path)) - if nested_id is not None: - url += "/%s" % quote_plus(nested_id) - return url - - resource_url_method = "%ss_url" % resource - setattr(cls, resource_url_method, classmethod(nested_resource_url)) - - def nested_resource_request( - cls, - method, - url, - api_base=None, - api_key=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - requestor = api_requestor.APIRequestor( - api_key, api_base=api_base, api_version=api_version, organization=organization - ) - response, _, api_key = requestor.request( - method, url, params, request_id=request_id - ) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - async def anested_resource_request( - cls, - method, - url, - api_key=None, - api_base=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - requestor = api_requestor.APIRequestor( - api_key, api_base=api_base, api_version=api_version, organization=organization - ) - response, _, api_key = await requestor.arequest( - method, url, params, request_id=request_id - ) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - resource_request_method = "%ss_request" % resource - setattr( - cls, - resource_request_method, - classmethod( - anested_resource_request if async_ else nested_resource_request - ), - ) - - for operation in operations: - if operation == "create": - - def create_nested_resource(cls, id, **params): - url = getattr(cls, resource_url_method)(id) - return getattr(cls, resource_request_method)("post", url, **params) - - create_method = "create_%s" % resource - setattr(cls, create_method, classmethod(create_nested_resource)) - - elif operation == "retrieve": - - def retrieve_nested_resource(cls, id, nested_id, **params): - url = getattr(cls, resource_url_method)(id, nested_id) - return getattr(cls, resource_request_method)("get", url, **params) - - retrieve_method = "retrieve_%s" % resource - setattr(cls, retrieve_method, classmethod(retrieve_nested_resource)) - - elif operation == "update": - - def modify_nested_resource(cls, id, nested_id, **params): - url = getattr(cls, resource_url_method)(id, nested_id) - return getattr(cls, resource_request_method)("post", url, **params) - - modify_method = "modify_%s" % resource - setattr(cls, modify_method, classmethod(modify_nested_resource)) - - elif operation == "delete": - - def delete_nested_resource(cls, id, nested_id, **params): - url = getattr(cls, resource_url_method)(id, nested_id) - return getattr(cls, resource_request_method)( - "delete", url, **params - ) - - delete_method = "delete_%s" % resource - setattr(cls, delete_method, classmethod(delete_nested_resource)) - - elif operation == "list": - - def list_nested_resources(cls, id, **params): - url = getattr(cls, resource_url_method)(id) - return getattr(cls, resource_request_method)("get", url, **params) - - list_method = "list_%s" % resource_plural - setattr(cls, list_method, classmethod(list_nested_resources)) - - elif operation == "paginated_list": - - def paginated_list_nested_resources( - cls, id, limit=None, after=None, **params - ): - url = getattr(cls, resource_url_method)(id) - return getattr(cls, resource_request_method)( - "get", url, limit=limit, after=after, **params - ) - - list_method = "list_%s" % resource_plural - setattr(cls, list_method, classmethod(paginated_list_nested_resources)) - - else: - raise ValueError("Unknown operation: %s" % operation) - - return cls - - return wrapper - - -def nested_resource_class_methods( - resource, - path=None, - operations=None, - resource_plural=None, -): - return _nested_resource_class_methods( - resource, path, operations, resource_plural, async_=False - ) - - -def anested_resource_class_methods( - resource, - path=None, - operations=None, - resource_plural=None, -): - return _nested_resource_class_methods( - resource, path, operations, resource_plural, async_=True - ) diff --git a/openai/api_resources/abstract/paginatable_api_resource.py b/openai/api_resources/abstract/paginatable_api_resource.py deleted file mode 100644 index 2d75744f23..0000000000 --- a/openai/api_resources/abstract/paginatable_api_resource.py +++ /dev/null @@ -1,125 +0,0 @@ -from openai import api_requestor, error, util -from openai.api_resources.abstract.listable_api_resource import ListableAPIResource -from openai.util import ApiType - - -class PaginatableAPIResource(ListableAPIResource): - @classmethod - def auto_paging_iter(cls, *args, **params): - next_cursor = None - has_more = True - if not params.get("limit"): - params["limit"] = 20 - while has_more: - if next_cursor: - params["after"] = next_cursor - response = cls.list(*args, **params) - - for item in response.data: - yield item - - if response.data: - next_cursor = response.data[-1].id - has_more = response.has_more - - @classmethod - def __prepare_list_requestor( - cls, - api_key=None, - api_version=None, - organization=None, - api_base=None, - api_type=None, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or cls.api_base(), - api_version=api_version, - api_type=api_type, - organization=organization, - ) - - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = cls.class_url() - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - return requestor, url - - @classmethod - def list( - cls, - limit=None, - starting_after=None, - api_key=None, - request_id=None, - api_version=None, - organization=None, - api_base=None, - api_type=None, - **params, - ): - requestor, url = cls.__prepare_list_requestor( - api_key, - api_version, - organization, - api_base, - api_type, - ) - - params = { - **params, - "limit": limit, - "starting_after": starting_after, - } - - response, _, api_key = requestor.request( - "get", url, params, request_id=request_id - ) - openai_object = util.convert_to_openai_object( - response, api_key, api_version, organization - ) - openai_object._retrieve_params = params - return openai_object - - @classmethod - async def alist( - cls, - limit=None, - starting_after=None, - api_key=None, - request_id=None, - api_version=None, - organization=None, - api_base=None, - api_type=None, - **params, - ): - requestor, url = cls.__prepare_list_requestor( - api_key, - api_version, - organization, - api_base, - api_type, - ) - - params = { - **params, - "limit": limit, - "starting_after": starting_after, - } - - response, _, api_key = await requestor.arequest( - "get", url, params, request_id=request_id - ) - openai_object = util.convert_to_openai_object( - response, api_key, api_version, organization - ) - openai_object._retrieve_params = params - return openai_object diff --git a/openai/api_resources/abstract/updateable_api_resource.py b/openai/api_resources/abstract/updateable_api_resource.py deleted file mode 100644 index 245f9b80b3..0000000000 --- a/openai/api_resources/abstract/updateable_api_resource.py +++ /dev/null @@ -1,16 +0,0 @@ -from urllib.parse import quote_plus -from typing import Awaitable - -from openai.api_resources.abstract.api_resource import APIResource - - -class UpdateableAPIResource(APIResource): - @classmethod - def modify(cls, sid, **params): - url = "%s/%s" % (cls.class_url(), quote_plus(sid)) - return cls._static_request("post", url, **params) - - @classmethod - def amodify(cls, sid, **params) -> Awaitable: - url = "%s/%s" % (cls.class_url(), quote_plus(sid)) - return cls._astatic_request("patch", url, **params) diff --git a/openai/api_resources/audio.py b/openai/api_resources/audio.py deleted file mode 100644 index cb316f07f1..0000000000 --- a/openai/api_resources/audio.py +++ /dev/null @@ -1,311 +0,0 @@ -from typing import Any, List - -import openai -from openai import api_requestor, util -from openai.api_resources.abstract import APIResource - - -class Audio(APIResource): - OBJECT_NAME = "audio" - - @classmethod - def _get_url(http://github.com/cls,%20action,%20deployment_id=None,%20api_type=None,%20api_version=None): - if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - return f"/{cls.azure_api_prefix}/deployments/{deployment_id}/audio/{action}?api-version={api_version}" - return cls.class_url() + f"/{action}" - - @classmethod - def _prepare_request( - cls, - file, - filename, - model, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - files: List[Any] = [] - data = { - "model": model, - **params, - } - files.append(("file", (filename, file, "application/octet-stream"))) - return requestor, files, data - - @classmethod - def transcribe( - cls, - model, - file, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - *, - deployment_id=None, - **params, - ): - requestor, files, data = cls._prepare_request( - file=file, - filename=file.name, - model=model, - api_key=api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - **params, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/transcriptions%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) - response, _, api_key = requestor.request("post", url, files=files, params=data) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - def translate( - cls, - model, - file, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - *, - deployment_id=None, - **params, - ): - requestor, files, data = cls._prepare_request( - file=file, - filename=file.name, - model=model, - api_key=api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - **params, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/translations%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) - response, _, api_key = requestor.request("post", url, files=files, params=data) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - def transcribe_raw( - cls, - model, - file, - filename, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - *, - deployment_id=None, - **params, - ): - requestor, files, data = cls._prepare_request( - file=file, - filename=filename, - model=model, - api_key=api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - **params, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/transcriptions%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) - response, _, api_key = requestor.request("post", url, files=files, params=data) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - def translate_raw( - cls, - model, - file, - filename, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - *, - deployment_id=None, - **params, - ): - requestor, files, data = cls._prepare_request( - file=file, - filename=filename, - model=model, - api_key=api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - **params, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/translations%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) - response, _, api_key = requestor.request("post", url, files=files, params=data) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def atranscribe( - cls, - model, - file, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - *, - deployment_id=None, - **params, - ): - requestor, files, data = cls._prepare_request( - file=file, - filename=file.name, - model=model, - api_key=api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - **params, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/transcriptions%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) - response, _, api_key = await requestor.arequest( - "post", url, files=files, params=data - ) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def atranslate( - cls, - model, - file, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - *, - deployment_id=None, - **params, - ): - requestor, files, data = cls._prepare_request( - file=file, - filename=file.name, - model=model, - api_key=api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - **params, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/translations%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) - response, _, api_key = await requestor.arequest( - "post", url, files=files, params=data - ) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def atranscribe_raw( - cls, - model, - file, - filename, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - *, - deployment_id=None, - **params, - ): - requestor, files, data = cls._prepare_request( - file=file, - filename=filename, - model=model, - api_key=api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - **params, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/transcriptions%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) - response, _, api_key = await requestor.arequest( - "post", url, files=files, params=data - ) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def atranslate_raw( - cls, - model, - file, - filename, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - *, - deployment_id=None, - **params, - ): - requestor, files, data = cls._prepare_request( - file=file, - filename=filename, - model=model, - api_key=api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - **params, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - url = cls._get_url("http://github.com/translations%22,%20deployment_id=deployment_id,%20api_type=api_type,%20api_version=api_version) - response, _, api_key = await requestor.arequest( - "post", url, files=files, params=data - ) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) diff --git a/openai/api_resources/chat_completion.py b/openai/api_resources/chat_completion.py deleted file mode 100644 index 7e55f9e38f..0000000000 --- a/openai/api_resources/chat_completion.py +++ /dev/null @@ -1,50 +0,0 @@ -import time - -from openai import util -from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import TryAgain - - -class ChatCompletion(EngineAPIResource): - engine_required = False - OBJECT_NAME = "chat.completions" - - @classmethod - def create(cls, *args, **kwargs): - """ - Creates a new chat completion for the provided messages and parameters. - - See https://platform.openai.com/docs/api-reference/chat/create - for a list of valid parameters. - """ - start = time.time() - timeout = kwargs.pop("timeout", None) - - while True: - try: - return super().create(*args, **kwargs) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) - - @classmethod - async def acreate(cls, *args, **kwargs): - """ - Creates a new chat completion for the provided messages and parameters. - - See https://platform.openai.com/docs/api-reference/chat/create - for a list of valid parameters. - """ - start = time.time() - timeout = kwargs.pop("timeout", None) - - while True: - try: - return await super().acreate(*args, **kwargs) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/completion.py b/openai/api_resources/completion.py deleted file mode 100644 index 7b9c44bd08..0000000000 --- a/openai/api_resources/completion.py +++ /dev/null @@ -1,50 +0,0 @@ -import time - -from openai import util -from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource -from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import TryAgain - - -class Completion(EngineAPIResource): - OBJECT_NAME = "completions" - - @classmethod - def create(cls, *args, **kwargs): - """ - Creates a new completion for the provided prompt and parameters. - - See https://platform.openai.com/docs/api-reference/completions/create for a list - of valid parameters. - """ - start = time.time() - timeout = kwargs.pop("timeout", None) - - while True: - try: - return super().create(*args, **kwargs) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) - - @classmethod - async def acreate(cls, *args, **kwargs): - """ - Creates a new completion for the provided prompt and parameters. - - See https://platform.openai.com/docs/api-reference/completions/create for a list - of valid parameters. - """ - start = time.time() - timeout = kwargs.pop("timeout", None) - - while True: - try: - return await super().acreate(*args, **kwargs) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/customer.py b/openai/api_resources/customer.py deleted file mode 100644 index 8690d07b38..0000000000 --- a/openai/api_resources/customer.py +++ /dev/null @@ -1,17 +0,0 @@ -from openai.openai_object import OpenAIObject - - -class Customer(OpenAIObject): - @classmethod - def get_url(http://github.com/cls,%20customer,%20endpoint): - return f"/customer/{customer}/{endpoint}" - - @classmethod - def create(cls, customer, endpoint, **params): - instance = cls() - return instance.request("post", cls.get_url(http://github.com/customer,%20endpoint), params) - - @classmethod - def acreate(cls, customer, endpoint, **params): - instance = cls() - return instance.arequest("post", cls.get_url(http://github.com/customer,%20endpoint), params) diff --git a/openai/api_resources/deployment.py b/openai/api_resources/deployment.py deleted file mode 100644 index 2f3fcd1307..0000000000 --- a/openai/api_resources/deployment.py +++ /dev/null @@ -1,119 +0,0 @@ -from openai import util -from openai.api_resources.abstract import ( - DeletableAPIResource, - ListableAPIResource, - CreateableAPIResource, -) -from openai.error import InvalidRequestError, APIError - - -class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResource): - OBJECT_NAME = "deployments" - - @classmethod - def _check_create(cls, *args, **kwargs): - typed_api_type, _ = cls._get_api_type_and_version( - kwargs.get("api_type", None), None - ) - if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise APIError( - "Deployment operations are only available for the Azure API type." - ) - - if kwargs.get("model", None) is None: - raise InvalidRequestError( - "Must provide a 'model' parameter to create a Deployment.", - param="model", - ) - - scale_settings = kwargs.get("scale_settings", None) - if scale_settings is None: - raise InvalidRequestError( - "Must provide a 'scale_settings' parameter to create a Deployment.", - param="scale_settings", - ) - - if "scale_type" not in scale_settings or ( - scale_settings["scale_type"].lower() == "manual" - and "capacity" not in scale_settings - ): - raise InvalidRequestError( - "The 'scale_settings' parameter contains invalid or incomplete values.", - param="scale_settings", - ) - - @classmethod - def create(cls, *args, **kwargs): - """ - Creates a new deployment for the provided prompt and parameters. - """ - cls._check_create(*args, **kwargs) - return super().create(*args, **kwargs) - - @classmethod - def acreate(cls, *args, **kwargs): - """ - Creates a new deployment for the provided prompt and parameters. - """ - cls._check_create(*args, **kwargs) - return super().acreate(*args, **kwargs) - - @classmethod - def _check_list(cls, *args, **kwargs): - typed_api_type, _ = cls._get_api_type_and_version( - kwargs.get("api_type", None), None - ) - if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise APIError( - "Deployment operations are only available for the Azure API type." - ) - - @classmethod - def list(cls, *args, **kwargs): - cls._check_list(*args, **kwargs) - return super().list(*args, **kwargs) - - @classmethod - def alist(cls, *args, **kwargs): - cls._check_list(*args, **kwargs) - return super().alist(*args, **kwargs) - - @classmethod - def _check_delete(cls, *args, **kwargs): - typed_api_type, _ = cls._get_api_type_and_version( - kwargs.get("api_type", None), None - ) - if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise APIError( - "Deployment operations are only available for the Azure API type." - ) - - @classmethod - def delete(cls, *args, **kwargs): - cls._check_delete(*args, **kwargs) - return super().delete(*args, **kwargs) - - @classmethod - def adelete(cls, *args, **kwargs): - cls._check_delete(*args, **kwargs) - return super().adelete(*args, **kwargs) - - @classmethod - def _check_retrieve(cls, *args, **kwargs): - typed_api_type, _ = cls._get_api_type_and_version( - kwargs.get("api_type", None), None - ) - if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise APIError( - "Deployment operations are only available for the Azure API type." - ) - - @classmethod - def retrieve(cls, *args, **kwargs): - cls._check_retrieve(*args, **kwargs) - return super().retrieve(*args, **kwargs) - - @classmethod - def aretrieve(cls, *args, **kwargs): - cls._check_retrieve(*args, **kwargs) - return super().aretrieve(*args, **kwargs) diff --git a/openai/api_resources/edit.py b/openai/api_resources/edit.py deleted file mode 100644 index 985f062ddb..0000000000 --- a/openai/api_resources/edit.py +++ /dev/null @@ -1,57 +0,0 @@ -import time - -from openai import util, error -from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.error import TryAgain - - -class Edit(EngineAPIResource): - OBJECT_NAME = "edits" - - @classmethod - def create(cls, *args, **kwargs): - """ - Creates a new edit for the provided input, instruction, and parameters. - """ - start = time.time() - timeout = kwargs.pop("timeout", None) - - api_type = kwargs.pop("api_type", None) - typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] - if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise error.InvalidAPIType( - "This operation is not supported by the Azure OpenAI API yet." - ) - - while True: - try: - return super().create(*args, **kwargs) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) - - @classmethod - async def acreate(cls, *args, **kwargs): - """ - Creates a new edit for the provided input, instruction, and parameters. - """ - start = time.time() - timeout = kwargs.pop("timeout", None) - - api_type = kwargs.pop("api_type", None) - typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] - if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise error.InvalidAPIType( - "This operation is not supported by the Azure OpenAI API yet." - ) - - while True: - try: - return await super().acreate(*args, **kwargs) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/embedding.py b/openai/api_resources/embedding.py deleted file mode 100644 index e937636404..0000000000 --- a/openai/api_resources/embedding.py +++ /dev/null @@ -1,91 +0,0 @@ -import base64 -import time - -from openai import util -from openai.api_resources.abstract.engine_api_resource import EngineAPIResource -from openai.datalib.numpy_helper import assert_has_numpy -from openai.datalib.numpy_helper import numpy as np -from openai.error import TryAgain - - -class Embedding(EngineAPIResource): - OBJECT_NAME = "embeddings" - - @classmethod - def create(cls, *args, **kwargs): - """ - Creates a new embedding for the provided input and parameters. - - See https://platform.openai.com/docs/api-reference/embeddings for a list - of valid parameters. - """ - start = time.time() - timeout = kwargs.pop("timeout", None) - - user_provided_encoding_format = kwargs.get("encoding_format", None) - - # If encoding format was not explicitly specified, we opaquely use base64 for performance - if not user_provided_encoding_format: - kwargs["encoding_format"] = "base64" - - while True: - try: - response = super().create(*args, **kwargs) - - # If a user specifies base64, we'll just return the encoded string. - # This is only for the default case. - if not user_provided_encoding_format: - for data in response.data: - - # If an engine isn't using this optimization, don't do anything - if type(data["embedding"]) == str: - assert_has_numpy() - data["embedding"] = np.frombuffer( - base64.b64decode(data["embedding"]), dtype="float32" - ).tolist() - - return response - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) - - @classmethod - async def acreate(cls, *args, **kwargs): - """ - Creates a new embedding for the provided input and parameters. - - See https://platform.openai.com/docs/api-reference/embeddings for a list - of valid parameters. - """ - start = time.time() - timeout = kwargs.pop("timeout", None) - - user_provided_encoding_format = kwargs.get("encoding_format", None) - - # If encoding format was not explicitly specified, we opaquely use base64 for performance - if not user_provided_encoding_format: - kwargs["encoding_format"] = "base64" - - while True: - try: - response = await super().acreate(*args, **kwargs) - - # If a user specifies base64, we'll just return the encoded string. - # This is only for the default case. - if not user_provided_encoding_format: - for data in response.data: - - # If an engine isn't using this optimization, don't do anything - if type(data["embedding"]) == str: - data["embedding"] = np.frombuffer( - base64.b64decode(data["embedding"]), dtype="float32" - ).tolist() - - return response - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) diff --git a/openai/api_resources/engine.py b/openai/api_resources/engine.py deleted file mode 100644 index 5a0c467c2f..0000000000 --- a/openai/api_resources/engine.py +++ /dev/null @@ -1,50 +0,0 @@ -import time -import warnings - -from openai import util -from openai.api_resources.abstract import ListableAPIResource, UpdateableAPIResource -from openai.error import TryAgain - - -class Engine(ListableAPIResource, UpdateableAPIResource): - OBJECT_NAME = "engines" - - def generate(self, timeout=None, **params): - start = time.time() - while True: - try: - return self.request( - "post", - self.instance_url() + "/generate", - params, - stream=params.get("stream"), - plain_old_data=True, - ) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) - - async def agenerate(self, timeout=None, **params): - start = time.time() - while True: - try: - return await self.arequest( - "post", - self.instance_url() + "/generate", - params, - stream=params.get("stream"), - plain_old_data=True, - ) - except TryAgain as e: - if timeout is not None and time.time() > start + timeout: - raise - - util.log_info("Waiting for model to warm up", error=e) - - def embeddings(self, **params): - warnings.warn( - "Engine.embeddings is deprecated, use Embedding.create", DeprecationWarning - ) - return self.request("post", self.instance_url() + "/embeddings", params) diff --git a/openai/api_resources/error_object.py b/openai/api_resources/error_object.py deleted file mode 100644 index 555dc35237..0000000000 --- a/openai/api_resources/error_object.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Optional - -from openai.openai_object import OpenAIObject -from openai.util import merge_dicts - - -class ErrorObject(OpenAIObject): - def refresh_from( - self, - values, - api_key=None, - api_version=None, - api_type=None, - organization=None, - response_ms: Optional[int] = None, - ): - # Unlike most other API resources, the API will omit attributes in - # error objects when they have a null value. We manually set default - # values here to facilitate generic error handling. - values = merge_dicts({"message": None, "type": None}, values) - return super(ErrorObject, self).refresh_from( - values=values, - api_key=api_key, - api_version=api_version, - api_type=api_type, - organization=organization, - response_ms=response_ms, - ) diff --git a/openai/api_resources/experimental/__init__.py b/openai/api_resources/experimental/__init__.py deleted file mode 100644 index d24c7b0cb5..0000000000 --- a/openai/api_resources/experimental/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from openai.api_resources.experimental.completion_config import ( # noqa: F401 - CompletionConfig, -) diff --git a/openai/api_resources/experimental/completion_config.py b/openai/api_resources/experimental/completion_config.py deleted file mode 100644 index 5d4feb40e1..0000000000 --- a/openai/api_resources/experimental/completion_config.py +++ /dev/null @@ -1,11 +0,0 @@ -from openai.api_resources.abstract import ( - CreateableAPIResource, - DeletableAPIResource, - ListableAPIResource, -) - - -class CompletionConfig( - CreateableAPIResource, ListableAPIResource, DeletableAPIResource -): - OBJECT_NAME = "experimental.completion_configs" diff --git a/openai/api_resources/file.py b/openai/api_resources/file.py deleted file mode 100644 index dba2ee92e1..0000000000 --- a/openai/api_resources/file.py +++ /dev/null @@ -1,279 +0,0 @@ -import json -import os -from typing import cast -import time - -import openai -from openai import api_requestor, util, error -from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource -from openai.util import ApiType - - -class File(ListableAPIResource, DeletableAPIResource): - OBJECT_NAME = "files" - - @classmethod - def __prepare_file_create( - cls, - file, - purpose, - model=None, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - user_provided_filename=None, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - base = cls.class_url() - url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) - elif typed_api_type == ApiType.OPEN_AI: - url = cls.class_url() - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - - # Set the filename on 'purpose' and 'model' to None so they are - # interpreted as form data. - files = [("purpose", (None, purpose))] - if model is not None: - files.append(("model", (None, model))) - if user_provided_filename is not None: - files.append( - ("file", (user_provided_filename, file, "application/octet-stream")) - ) - else: - files.append(("file", ("file", file, "application/octet-stream"))) - - return requestor, url, files - - @classmethod - def create( - cls, - file, - purpose, - model=None, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - user_provided_filename=None, - ): - requestor, url, files = cls.__prepare_file_create( - file, - purpose, - model, - api_key, - api_base, - api_type, - api_version, - organization, - user_provided_filename, - ) - response, _, api_key = requestor.request("post", url, files=files) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def acreate( - cls, - file, - purpose, - model=None, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - user_provided_filename=None, - ): - requestor, url, files = cls.__prepare_file_create( - file, - purpose, - model, - api_key, - api_base, - api_type, - api_version, - organization, - user_provided_filename, - ) - response, _, api_key = await requestor.arequest("post", url, files=files) - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - def __prepare_file_download( - cls, - id, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - base = cls.class_url() - url = f"/{cls.azure_api_prefix}{base}/{id}/content?api-version={api_version}" - elif typed_api_type == ApiType.OPEN_AI: - url = f"{cls.class_url()}/{id}/content" - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - - return requestor, url - - @classmethod - def download( - cls, - id, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - ): - requestor, url = cls.__prepare_file_download( - id, api_key, api_base, api_type, api_version, organization - ) - - result = requestor.request_raw("get", url) - if not 200 <= result.status_code < 300: - raise requestor.handle_error_response( - result.content, - result.status_code, - json.loads(cast(bytes, result.content)), - result.headers, - stream_error=False, - ) - return result.content - - @classmethod - async def adownload( - cls, - id, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - ): - requestor, url = cls.__prepare_file_download( - id, api_key, api_base, api_type, api_version, organization - ) - - async with api_requestor.aiohttp_session() as session: - result = await requestor.arequest_raw("get", url, session) - if not 200 <= result.status < 300: - raise requestor.handle_error_response( - result.content, - result.status, - json.loads(cast(bytes, result.content)), - result.headers, - stream_error=False, - ) - return result.content - - @classmethod - def __find_matching_files(cls, name, bytes, all_files, purpose): - matching_files = [] - basename = os.path.basename(name) - for f in all_files: - if f["purpose"] != purpose: - continue - file_basename = os.path.basename(f["filename"]) - if file_basename != basename: - continue - if "bytes" in f and f["bytes"] != bytes: - continue - if "size" in f and int(f["size"]) != bytes: - continue - matching_files.append(f) - return matching_files - - @classmethod - def find_matching_files( - cls, - name, - bytes, - purpose, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - ): - """Find already uploaded files with the same name, size, and purpose.""" - all_files = cls.list( - api_key=api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ).get("data", []) - return cls.__find_matching_files(name, bytes, all_files, purpose) - - @classmethod - async def afind_matching_files( - cls, - name, - bytes, - purpose, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - ): - """Find already uploaded files with the same name, size, and purpose.""" - all_files = ( - await cls.alist( - api_key=api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - ).get("data", []) - return cls.__find_matching_files(name, bytes, all_files, purpose) - - @classmethod - def wait_for_processing(cls, id, max_wait_seconds=30 * 60): - TERMINAL_STATES = ["processed", "error", "deleted"] - - start = time.time() - file = cls.retrieve(id=id) - while file.status not in TERMINAL_STATES: - file = cls.retrieve(id=id) - time.sleep(5.0) - if time.time() - start > max_wait_seconds: - raise openai.error.OpenAIError( - message="Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds.".format( - id=id, max_wait_seconds=max_wait_seconds - ) - ) - return file.status diff --git a/openai/api_resources/fine_tune.py b/openai/api_resources/fine_tune.py deleted file mode 100644 index 45e3cf2af3..0000000000 --- a/openai/api_resources/fine_tune.py +++ /dev/null @@ -1,204 +0,0 @@ -from urllib.parse import quote_plus - -from openai import api_requestor, util, error -from openai.api_resources.abstract import ( - CreateableAPIResource, - ListableAPIResource, - nested_resource_class_methods, -) -from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource -from openai.openai_response import OpenAIResponse -from openai.util import ApiType - - -@nested_resource_class_methods("event", operations=["list"]) -class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource): - OBJECT_NAME = "fine-tunes" - - @classmethod - def _prepare_cancel( - cls, - id, - api_key=None, - api_type=None, - request_id=None, - api_version=None, - **params, - ): - base = cls.class_url() - extn = quote_plus(id) - - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - url = "/%s%s/%s/cancel?api-version=%s" % ( - cls.azure_api_prefix, - base, - extn, - api_version, - ) - elif typed_api_type == ApiType.OPEN_AI: - url = "%s/%s/cancel" % (base, extn) - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - - instance = cls(id, api_key, **params) - return instance, url - - @classmethod - def cancel( - cls, - id, - api_key=None, - api_type=None, - request_id=None, - api_version=None, - **params, - ): - instance, url = cls._prepare_cancel( - id, - api_key, - api_type, - request_id, - api_version, - **params, - ) - return instance.request("post", url, request_id=request_id) - - @classmethod - def acancel( - cls, - id, - api_key=None, - api_type=None, - request_id=None, - api_version=None, - **params, - ): - instance, url = cls._prepare_cancel( - id, - api_key, - api_type, - request_id, - api_version, - **params, - ) - return instance.arequest("post", url, request_id=request_id) - - @classmethod - def _prepare_stream_events( - cls, - id, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - base = cls.class_url() - extn = quote_plus(id) - - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - url = "/%s%s/%s/events?stream=true&api-version=%s" % ( - cls.azure_api_prefix, - base, - extn, - api_version, - ) - elif typed_api_type == ApiType.OPEN_AI: - url = "%s/%s/events?stream=true" % (base, extn) - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - - return requestor, url - - @classmethod - def stream_events( - cls, - id, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - requestor, url = cls._prepare_stream_events( - id, - api_key, - api_base, - api_type, - request_id, - api_version, - organization, - **params, - ) - - response, _, api_key = requestor.request( - "get", url, params, stream=True, request_id=request_id - ) - - assert not isinstance(response, OpenAIResponse) # must be an iterator - return ( - util.convert_to_openai_object( - line, - api_key, - api_version, - organization, - ) - for line in response - ) - - @classmethod - async def astream_events( - cls, - id, - api_key=None, - api_base=None, - api_type=None, - request_id=None, - api_version=None, - organization=None, - **params, - ): - requestor, url = cls._prepare_stream_events( - id, - api_key, - api_base, - api_type, - request_id, - api_version, - organization, - **params, - ) - - response, _, api_key = await requestor.arequest( - "get", url, params, stream=True, request_id=request_id - ) - - assert not isinstance(response, OpenAIResponse) # must be an iterator - return ( - util.convert_to_openai_object( - line, - api_key, - api_version, - organization, - ) - async for line in response - ) diff --git a/openai/api_resources/fine_tuning.py b/openai/api_resources/fine_tuning.py deleted file mode 100644 index f03be56ab7..0000000000 --- a/openai/api_resources/fine_tuning.py +++ /dev/null @@ -1,88 +0,0 @@ -from urllib.parse import quote_plus - -from openai import error -from openai.api_resources.abstract import ( - CreateableAPIResource, - PaginatableAPIResource, - nested_resource_class_methods, -) -from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource -from openai.util import ApiType - - -@nested_resource_class_methods("event", operations=["paginated_list"]) -class FineTuningJob( - PaginatableAPIResource, CreateableAPIResource, DeletableAPIResource -): - OBJECT_NAME = "fine_tuning.jobs" - - @classmethod - def _prepare_cancel( - cls, - id, - api_key=None, - api_type=None, - request_id=None, - api_version=None, - **params, - ): - base = cls.class_url() - extn = quote_plus(id) - - typed_api_type, api_version = cls._get_api_type_and_version( - api_type, api_version - ) - if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): - url = "/%s%s/%s/cancel?api-version=%s" % ( - cls.azure_api_prefix, - base, - extn, - api_version, - ) - elif typed_api_type == ApiType.OPEN_AI: - url = "%s/%s/cancel" % (base, extn) - else: - raise error.InvalidAPIType("Unsupported API type %s" % api_type) - - instance = cls(id, api_key, **params) - return instance, url - - @classmethod - def cancel( - cls, - id, - api_key=None, - api_type=None, - request_id=None, - api_version=None, - **params, - ): - instance, url = cls._prepare_cancel( - id, - api_key, - api_type, - request_id, - api_version, - **params, - ) - return instance.request("post", url, request_id=request_id) - - @classmethod - def acancel( - cls, - id, - api_key=None, - api_type=None, - request_id=None, - api_version=None, - **params, - ): - instance, url = cls._prepare_cancel( - id, - api_key, - api_type, - request_id, - api_version, - **params, - ) - return instance.arequest("post", url, request_id=request_id) diff --git a/openai/api_resources/image.py b/openai/api_resources/image.py deleted file mode 100644 index 1522923510..0000000000 --- a/openai/api_resources/image.py +++ /dev/null @@ -1,273 +0,0 @@ -# WARNING: This interface is considered experimental and may changed in the future without warning. -from typing import Any, List - -import openai -from openai import api_requestor, error, util -from openai.api_resources.abstract import APIResource - - -class Image(APIResource): - OBJECT_NAME = "images" - - @classmethod - def _get_url(http://github.com/cls,%20action,%20azure_action,%20api_type,%20api_version): - if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD) and azure_action is not None: - return f"/{cls.azure_api_prefix}{cls.class_url()}/{action}:{azure_action}?api-version={api_version}" - else: - return f"{cls.class_url()}/{action}" - - @classmethod - def create( - cls, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - response, _, api_key = requestor.request( - "post", cls._get_url("http://github.com/generations%22,%20azure_action=%22/service/http://github.com/submit%22,%20api_type=api_type,%20api_version=api_version), params - ) - - if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - requestor.api_base = "" # operation_location is a full url - response, _, api_key = requestor._poll( - "get", response.operation_location, - until=lambda response: response.data['status'] in [ 'succeeded' ], - failed=lambda response: response.data['status'] in [ 'failed' ] - ) - - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def acreate( - cls, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - response, _, api_key = await requestor.arequest( - "post", cls._get_url("http://github.com/generations%22,%20azure_action=%22/service/http://github.com/submit%22,%20api_type=api_type,%20api_version=api_version), params - ) - - if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - requestor.api_base = "" # operation_location is a full url - response, _, api_key = await requestor._apoll( - "get", response.operation_location, - until=lambda response: response.data['status'] in [ 'succeeded' ], - failed=lambda response: response.data['status'] in [ 'failed' ] - ) - - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - def _prepare_create_variation( - cls, - image, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - url = cls._get_url("http://github.com/variations%22,%20azure_action=None,%20api_type=api_type,%20api_version=api_version) - - files: List[Any] = [] - for key, value in params.items(): - files.append((key, (None, value))) - files.append(("image", ("image", image, "application/octet-stream"))) - return requestor, url, files - - @classmethod - def create_variation( - cls, - image, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise error.InvalidAPIType("Variations are not supported by the Azure OpenAI API yet.") - - requestor, url, files = cls._prepare_create_variation( - image, - api_key, - api_base, - api_type, - api_version, - organization, - **params, - ) - - response, _, api_key = requestor.request("post", url, files=files) - - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def acreate_variation( - cls, - image, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise error.InvalidAPIType("Variations are not supported by the Azure OpenAI API yet.") - - requestor, url, files = cls._prepare_create_variation( - image, - api_key, - api_base, - api_type, - api_version, - organization, - **params, - ) - - response, _, api_key = await requestor.arequest("post", url, files=files) - - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - def _prepare_create_edit( - cls, - image, - mask=None, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - requestor = api_requestor.APIRequestor( - api_key, - api_base=api_base or openai.api_base, - api_type=api_type, - api_version=api_version, - organization=organization, - ) - api_type, api_version = cls._get_api_type_and_version(api_type, api_version) - - url = cls._get_url("http://github.com/edits%22,%20azure_action=None,%20api_type=api_type,%20api_version=api_version) - - files: List[Any] = [] - for key, value in params.items(): - files.append((key, (None, value))) - files.append(("image", ("image", image, "application/octet-stream"))) - if mask is not None: - files.append(("mask", ("mask", mask, "application/octet-stream"))) - return requestor, url, files - - @classmethod - def create_edit( - cls, - image, - mask=None, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise error.InvalidAPIType("Edits are not supported by the Azure OpenAI API yet.") - - requestor, url, files = cls._prepare_create_edit( - image, - mask, - api_key, - api_base, - api_type, - api_version, - organization, - **params, - ) - - response, _, api_key = requestor.request("post", url, files=files) - - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) - - @classmethod - async def acreate_edit( - cls, - image, - mask=None, - api_key=None, - api_base=None, - api_type=None, - api_version=None, - organization=None, - **params, - ): - if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): - raise error.InvalidAPIType("Edits are not supported by the Azure OpenAI API yet.") - - requestor, url, files = cls._prepare_create_edit( - image, - mask, - api_key, - api_base, - api_type, - api_version, - organization, - **params, - ) - - response, _, api_key = await requestor.arequest("post", url, files=files) - - return util.convert_to_openai_object( - response, api_key, api_version, organization - ) diff --git a/openai/api_resources/model.py b/openai/api_resources/model.py deleted file mode 100644 index 9785e17fe1..0000000000 --- a/openai/api_resources/model.py +++ /dev/null @@ -1,5 +0,0 @@ -from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource - - -class Model(ListableAPIResource, DeletableAPIResource): - OBJECT_NAME = "models" diff --git a/openai/api_resources/moderation.py b/openai/api_resources/moderation.py deleted file mode 100644 index bd19646b49..0000000000 --- a/openai/api_resources/moderation.py +++ /dev/null @@ -1,45 +0,0 @@ -from typing import List, Optional, Union - -from openai.openai_object import OpenAIObject - - -class Moderation(OpenAIObject): - VALID_MODEL_NAMES: List[str] = ["text-moderation-stable", "text-moderation-latest"] - - @classmethod - def get_url(http://github.com/cls): - return "/moderations" - - @classmethod - def _prepare_create(cls, input, model, api_key): - if model is not None and model not in cls.VALID_MODEL_NAMES: - raise ValueError( - f"The parameter model should be chosen from {cls.VALID_MODEL_NAMES} " - f"and it is default to be None." - ) - - instance = cls(api_key=api_key) - params = {"input": input} - if model is not None: - params["model"] = model - return instance, params - - @classmethod - def create( - cls, - input: Union[str, List[str]], - model: Optional[str] = None, - api_key: Optional[str] = None, - ): - instance, params = cls._prepare_create(input, model, api_key) - return instance.request("post", cls.get_url(), params) - - @classmethod - def acreate( - cls, - input: Union[str, List[str]], - model: Optional[str] = None, - api_key: Optional[str] = None, - ): - instance, params = cls._prepare_create(input, model, api_key) - return instance.arequest("post", cls.get_url(), params) diff --git a/openai/cli.py b/openai/cli.py deleted file mode 100644 index a6e99396ae..0000000000 --- a/openai/cli.py +++ /dev/null @@ -1,1416 +0,0 @@ -import datetime -import os -import signal -import sys -import warnings -from typing import Optional - -import requests - -import openai -from openai.upload_progress import BufferReader -from openai.validators import ( - apply_necessary_remediation, - apply_validators, - get_validators, - read_any_format, - write_out_file, -) - - -class bcolors: - 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 organization_info(obj): - organization = getattr(obj, "organization", None) - if organization is not None: - return "[organization={}] ".format(organization) - else: - return "" - - -def display(obj): - sys.stderr.write(organization_info(obj)) - sys.stderr.flush() - print(obj) - - -def display_error(e): - extra = ( - " (HTTP status code: {})".format(e.http_status) - if e.http_status is not None - else "" - ) - sys.stderr.write( - "{}{}Error:{} {}{}\n".format( - organization_info(e), bcolors.FAIL, bcolors.ENDC, e, extra - ) - ) - - -class Engine: - @classmethod - def get(cls, args): - engine = openai.Engine.retrieve(id=args.id) - display(engine) - - @classmethod - def update(cls, args): - engine = openai.Engine.modify(args.id, replicas=args.replicas) - display(engine) - - @classmethod - def generate(cls, args): - warnings.warn( - "Engine.generate is deprecated, use Completion.create", DeprecationWarning - ) - if args.completions and args.completions > 1 and args.stream: - raise ValueError("Can't stream multiple completions with openai CLI") - - kwargs = {} - if args.model is not None: - kwargs["model"] = args.model - resp = openai.Engine(id=args.id).generate( - completions=args.completions, - context=args.context, - length=args.length, - stream=args.stream, - temperature=args.temperature, - top_p=args.top_p, - logprobs=args.logprobs, - stop=args.stop, - **kwargs, - ) - if not args.stream: - resp = [resp] - - for part in resp: - completions = len(part["data"]) - for c_idx, c in enumerate(part["data"]): - if completions > 1: - sys.stdout.write("===== Completion {} =====\n".format(c_idx)) - sys.stdout.write("".join(c["text"])) - if completions > 1: - sys.stdout.write("\n") - sys.stdout.flush() - - @classmethod - def list(cls, args): - engines = openai.Engine.list() - display(engines) - - -class ChatCompletion: - @classmethod - def create(cls, args): - if args.n is not None and args.n > 1 and args.stream: - raise ValueError( - "Can't stream chat completions with n>1 with the current CLI" - ) - - messages = [ - {"role": role, "content": content} for role, content in args.message - ] - - resp = openai.ChatCompletion.create( - # Required - model=args.model, - engine=args.engine, - messages=messages, - # Optional - n=args.n, - max_tokens=args.max_tokens, - temperature=args.temperature, - top_p=args.top_p, - stop=args.stop, - stream=args.stream, - ) - if not args.stream: - resp = [resp] - - for part in resp: - choices = part["choices"] - for c_idx, c in enumerate(sorted(choices, key=lambda s: s["index"])): - if len(choices) > 1: - sys.stdout.write("===== Chat Completion {} =====\n".format(c_idx)) - if args.stream: - delta = c["delta"] - if "content" in delta: - sys.stdout.write(delta["content"]) - else: - sys.stdout.write(c["message"]["content"]) - if len(choices) > 1: # not in streams - sys.stdout.write("\n") - sys.stdout.flush() - - -class Completion: - @classmethod - def create(cls, args): - if args.n is not None and args.n > 1 and args.stream: - raise ValueError("Can't stream completions with n>1 with the current CLI") - - if args.engine and args.model: - warnings.warn( - "In most cases, you should not be specifying both engine and model." - ) - - resp = openai.Completion.create( - engine=args.engine, - model=args.model, - n=args.n, - max_tokens=args.max_tokens, - logprobs=args.logprobs, - prompt=args.prompt, - stream=args.stream, - temperature=args.temperature, - top_p=args.top_p, - stop=args.stop, - echo=True, - ) - if not args.stream: - resp = [resp] - - for part in resp: - choices = part["choices"] - for c_idx, c in enumerate(sorted(choices, key=lambda s: s["index"])): - if len(choices) > 1: - sys.stdout.write("===== Completion {} =====\n".format(c_idx)) - sys.stdout.write(c["text"]) - if len(choices) > 1: - sys.stdout.write("\n") - sys.stdout.flush() - - -class Deployment: - @classmethod - def get(cls, args): - resp = openai.Deployment.retrieve(id=args.id) - print(resp) - - @classmethod - def delete(cls, args): - model = openai.Deployment.delete(args.id) - print(model) - - @classmethod - def list(cls, args): - models = openai.Deployment.list() - print(models) - - @classmethod - def create(cls, args): - models = openai.Deployment.create( - model=args.model, scale_settings={"scale_type": args.scale_type} - ) - print(models) - - -class Model: - @classmethod - def get(cls, args): - resp = openai.Model.retrieve(id=args.id) - print(resp) - - @classmethod - def delete(cls, args): - model = openai.Model.delete(args.id) - print(model) - - @classmethod - def list(cls, args): - models = openai.Model.list() - print(models) - - -class File: - @classmethod - def create(cls, args): - with open(args.file, "rb") as file_reader: - buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") - resp = openai.File.create( - file=buffer_reader, - purpose=args.purpose, - user_provided_filename=args.file, - ) - print(resp) - - @classmethod - def get(cls, args): - resp = openai.File.retrieve(id=args.id) - print(resp) - - @classmethod - def delete(cls, args): - file = openai.File.delete(args.id) - print(file) - - @classmethod - def list(cls, args): - file = openai.File.list() - print(file) - - -class Image: - @classmethod - def create(cls, args): - resp = openai.Image.create( - prompt=args.prompt, - size=args.size, - n=args.num_images, - response_format=args.response_format, - ) - print(resp) - - @classmethod - def create_variation(cls, args): - with open(args.image, "rb") as file_reader: - buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") - resp = openai.Image.create_variation( - image=buffer_reader, - size=args.size, - n=args.num_images, - response_format=args.response_format, - ) - print(resp) - - @classmethod - def create_edit(cls, args): - with open(args.image, "rb") as file_reader: - image_reader = BufferReader(file_reader.read(), desc="Upload progress") - mask_reader = None - if args.mask is not None: - with open(args.mask, "rb") as file_reader: - mask_reader = BufferReader(file_reader.read(), desc="Upload progress") - resp = openai.Image.create_edit( - image=image_reader, - mask=mask_reader, - prompt=args.prompt, - size=args.size, - n=args.num_images, - response_format=args.response_format, - ) - print(resp) - - -class Audio: - @classmethod - def transcribe(cls, args): - with open(args.file, "rb") as r: - file_reader = BufferReader(r.read(), desc="Upload progress") - - resp = openai.Audio.transcribe_raw( - # Required - model=args.model, - file=file_reader, - filename=args.file, - # Optional - response_format=args.response_format, - language=args.language, - temperature=args.temperature, - prompt=args.prompt, - ) - print(resp) - - @classmethod - def translate(cls, args): - with open(args.file, "rb") as r: - file_reader = BufferReader(r.read(), desc="Upload progress") - resp = openai.Audio.translate_raw( - # Required - model=args.model, - file=file_reader, - filename=args.file, - # Optional - response_format=args.response_format, - language=args.language, - temperature=args.temperature, - prompt=args.prompt, - ) - print(resp) - - -class FineTune: - @classmethod - def list(cls, args): - resp = openai.FineTune.list() - print(resp) - - @classmethod - def _is_url(http://github.com/cls,%20file:%20str): - return file.lower().startswith("http") - - @classmethod - def _download_file_from_public_url(http://github.com/cls,%20url:%20str) -> Optional[bytes]: - resp = requests.get(url) - if resp.status_code == 200: - return resp.content - else: - return None - - @classmethod - def _maybe_upload_file( - cls, - file: Optional[str] = None, - content: Optional[bytes] = None, - user_provided_file: Optional[str] = None, - check_if_file_exists: bool = True, - ): - # Exactly one of `file` or `content` must be provided - if (file is None) == (content is None): - raise ValueError("Exactly one of `file` or `content` must be provided") - - if content is None: - assert file is not None - with open(file, "rb") as f: - content = f.read() - - if check_if_file_exists: - bytes = len(content) - matching_files = openai.File.find_matching_files( - name=user_provided_file or f.name, bytes=bytes, purpose="fine-tune" - ) - if len(matching_files) > 0: - file_ids = [f["id"] for f in matching_files] - sys.stdout.write( - "Found potentially duplicated files with name '{name}', purpose 'fine-tune' and size {size} bytes\n".format( - name=os.path.basename(matching_files[0]["filename"]), - size=matching_files[0]["bytes"] - if "bytes" in matching_files[0] - else matching_files[0]["size"], - ) - ) - sys.stdout.write("\n".join(file_ids)) - while True: - sys.stdout.write( - "\nEnter file ID to reuse an already uploaded file, or an empty string to upload this file anyway: " - ) - inp = sys.stdin.readline().strip() - if inp in file_ids: - sys.stdout.write( - "Reusing already uploaded file: {id}\n".format(id=inp) - ) - return inp - elif inp == "": - break - else: - sys.stdout.write( - "File id '{id}' is not among the IDs of the potentially duplicated files\n".format( - id=inp - ) - ) - - buffer_reader = BufferReader(content, desc="Upload progress") - resp = openai.File.create( - file=buffer_reader, - purpose="fine-tune", - user_provided_filename=user_provided_file or file, - ) - sys.stdout.write( - "Uploaded file from {file}: {id}\n".format( - file=user_provided_file or file, id=resp["id"] - ) - ) - return resp["id"] - - @classmethod - def _get_or_upload(cls, file, check_if_file_exists=True): - try: - # 1. If it's a valid file, use it - openai.File.retrieve(file) - return file - except openai.error.InvalidRequestError: - pass - if os.path.isfile(file): - # 2. If it's a file on the filesystem, upload it - return cls._maybe_upload_file( - file=file, check_if_file_exists=check_if_file_exists - ) - if cls._is_url(http://github.com/file): - # 3. If it's a URL, download it temporarily - content = cls._download_file_from_public_url(http://github.com/file) - if content is not None: - return cls._maybe_upload_file( - content=content, - check_if_file_exists=check_if_file_exists, - user_provided_file=file, - ) - return file - - @classmethod - def create(cls, args): - create_args = { - "training_file": cls._get_or_upload( - args.training_file, args.check_if_files_exist - ), - } - if args.validation_file: - create_args["validation_file"] = cls._get_or_upload( - args.validation_file, args.check_if_files_exist - ) - - for hparam in ( - "model", - "suffix", - "n_epochs", - "batch_size", - "learning_rate_multiplier", - "prompt_loss_weight", - "compute_classification_metrics", - "classification_n_classes", - "classification_positive_class", - "classification_betas", - ): - attr = getattr(args, hparam) - if attr is not None: - create_args[hparam] = attr - - resp = openai.FineTune.create(**create_args) - - if args.no_follow: - print(resp) - return - - sys.stdout.write( - "Created fine-tune: {job_id}\n" - "Streaming events until fine-tuning is complete...\n\n" - "(Ctrl-C will interrupt the stream, but not cancel the fine-tune)\n".format( - job_id=resp["id"] - ) - ) - cls._stream_events(resp["id"]) - - @classmethod - def get(cls, args): - resp = openai.FineTune.retrieve(id=args.id) - print(resp) - - @classmethod - def results(cls, args): - fine_tune = openai.FineTune.retrieve(id=args.id) - if "result_files" not in fine_tune or len(fine_tune["result_files"]) == 0: - raise openai.error.InvalidRequestError( - f"No results file available for fine-tune {args.id}", "id" - ) - result_file = openai.FineTune.retrieve(id=args.id)["result_files"][0] - resp = openai.File.download(id=result_file["id"]) - print(resp.decode("utf-8")) - - @classmethod - def events(cls, args): - if args.stream: - raise openai.error.OpenAIError( - message=( - "The --stream parameter is deprecated, use fine_tunes.follow " - "instead:\n\n" - " openai api fine_tunes.follow -i {id}\n".format(id=args.id) - ), - ) - - resp = openai.FineTune.list_events(id=args.id) # type: ignore - print(resp) - - @classmethod - def follow(cls, args): - cls._stream_events(args.id) - - @classmethod - def _stream_events(cls, job_id): - def signal_handler(sig, frame): - status = openai.FineTune.retrieve(job_id).status - sys.stdout.write( - "\nStream interrupted. Job is still {status}.\n" - "To resume the stream, run:\n\n" - " openai api fine_tunes.follow -i {job_id}\n\n" - "To cancel your job, run:\n\n" - " openai api fine_tunes.cancel -i {job_id}\n\n".format( - status=status, job_id=job_id - ) - ) - sys.exit(0) - - signal.signal(signal.SIGINT, signal_handler) - - events = openai.FineTune.stream_events(job_id) - # TODO(rachel): Add a nifty spinner here. - try: - for event in events: - sys.stdout.write( - "[%s] %s" - % ( - datetime.datetime.fromtimestamp(event["created_at"]), - event["message"], - ) - ) - sys.stdout.write("\n") - sys.stdout.flush() - except Exception: - sys.stdout.write( - "\nStream interrupted (client disconnected).\n" - "To resume the stream, run:\n\n" - " openai api fine_tunes.follow -i {job_id}\n\n".format(job_id=job_id) - ) - return - - resp = openai.FineTune.retrieve(id=job_id) - status = resp["status"] - if status == "succeeded": - sys.stdout.write("\nJob complete! Status: succeeded 🎉") - sys.stdout.write( - "\nTry out your fine-tuned model:\n\n" - "openai api completions.create -m {model} -p ".format( - model=resp["fine_tuned_model"] - ) - ) - elif status == "failed": - sys.stdout.write( - "\nJob failed. Please contact us through our help center at help.openai.com if you need assistance." - ) - sys.stdout.write("\n") - - @classmethod - def cancel(cls, args): - resp = openai.FineTune.cancel(id=args.id) - print(resp) - - @classmethod - def delete(cls, args): - resp = openai.FineTune.delete(sid=args.id) - print(resp) - - @classmethod - def prepare_data(cls, args): - 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() - - apply_validators( - df, - fname, - remediation, - validators, - auto_accept, - write_out_file_func=write_out_file, - ) - - -class FineTuningJob: - @classmethod - def list(cls, args): - has_ft_jobs = False - for fine_tune_job in openai.FineTuningJob.auto_paging_iter(): - has_ft_jobs = True - print(fine_tune_job) - if not has_ft_jobs: - print("No fine-tuning jobs found.") - - @classmethod - def _is_url(http://github.com/cls,%20file:%20str): - return file.lower().startswith("http") - - @classmethod - def _download_file_from_public_url(http://github.com/cls,%20url:%20str) -> Optional[bytes]: - resp = requests.get(url) - if resp.status_code == 200: - return resp.content - else: - return None - - @classmethod - def _maybe_upload_file( - cls, - file: Optional[str] = None, - content: Optional[bytes] = None, - user_provided_file: Optional[str] = None, - check_if_file_exists: bool = True, - ): - # Exactly one of `file` or `content` must be provided - if (file is None) == (content is None): - raise ValueError("Exactly one of `file` or `content` must be provided") - - if content is None: - assert file is not None - with open(file, "rb") as f: - content = f.read() - - if check_if_file_exists: - bytes = len(content) - matching_files = openai.File.find_matching_files( - name=user_provided_file or f.name, - bytes=bytes, - purpose="fine-tune", - ) - if len(matching_files) > 0: - file_ids = [f["id"] for f in matching_files] - sys.stdout.write( - "Found potentially duplicated files with name '{name}', purpose 'fine-tune', and size {size} bytes\n".format( - name=os.path.basename(matching_files[0]["filename"]), - size=matching_files[0]["bytes"] - if "bytes" in matching_files[0] - else matching_files[0]["size"], - ) - ) - sys.stdout.write("\n".join(file_ids)) - while True: - sys.stdout.write( - "\nEnter file ID to reuse an already uploaded file, or an empty string to upload this file anyway: " - ) - inp = sys.stdin.readline().strip() - if inp in file_ids: - sys.stdout.write( - "Reusing already uploaded file: {id}\n".format(id=inp) - ) - return inp - elif inp == "": - break - else: - sys.stdout.write( - "File id '{id}' is not among the IDs of the potentially duplicated files\n".format( - id=inp - ) - ) - - buffer_reader = BufferReader(content, desc="Upload progress") - resp = openai.File.create( - file=buffer_reader, - purpose="fine-tune", - user_provided_filename=user_provided_file or file, - ) - sys.stdout.write( - "Uploaded file from {file}: {id}\n".format( - file=user_provided_file or file, id=resp["id"] - ) - ) - sys.stdout.write("Waiting for file to finish processing before proceeding..\n") - sys.stdout.flush() - status = openai.File.wait_for_processing(resp["id"]) - if status != "processed": - raise openai.error.OpenAIError( - "File {id} failed to process, status={status}.".format( - id=resp["id"], status=status - ) - ) - - sys.stdout.write( - "File {id} finished processing and is ready for use in fine-tuning".format( - id=resp["id"] - ) - ) - sys.stdout.flush() - return resp["id"] - - @classmethod - def _get_or_upload(cls, file, check_if_file_exists=True): - try: - # 1. If it's a valid file, use it - openai.File.retrieve(file) - return file - except openai.error.InvalidRequestError: - pass - if os.path.isfile(file): - # 2. If it's a file on the filesystem, upload it - return cls._maybe_upload_file( - file=file, check_if_file_exists=check_if_file_exists - ) - if cls._is_url(http://github.com/file): - # 3. If it's a URL, download it temporarily - content = cls._download_file_from_public_url(http://github.com/file) - if content is not None: - return cls._maybe_upload_file( - content=content, - check_if_file_exists=check_if_file_exists, - user_provided_file=file, - ) - return file - - @classmethod - def create(cls, args): - create_args = { - "training_file": cls._get_or_upload( - args.training_file, args.check_if_files_exist - ), - } - if args.validation_file: - create_args["validation_file"] = cls._get_or_upload( - args.validation_file, args.check_if_files_exist - ) - - for param in ("model", "suffix"): - attr = getattr(args, param) - if attr is not None: - create_args[param] = attr - - if getattr(args, "n_epochs"): - create_args["hyperparameters"] = { - "n_epochs": args.n_epochs, - } - - resp = openai.FineTuningJob.create(**create_args) - print(resp) - return - - @classmethod - def get(cls, args): - resp = openai.FineTuningJob.retrieve(id=args.id) - print(resp) - - @classmethod - def results(cls, args): - fine_tune = openai.FineTuningJob.retrieve(id=args.id) - if "result_files" not in fine_tune or len(fine_tune["result_files"]) == 0: - raise openai.error.InvalidRequestError( - f"No results file available for fine-tune {args.id}", "id" - ) - result_file = openai.FineTuningJob.retrieve(id=args.id)["result_files"][0] - resp = openai.File.download(id=result_file) - print(resp.decode("utf-8")) - - @classmethod - def events(cls, args): - seen, has_more, after = 0, True, None - while has_more: - resp = openai.FineTuningJob.list_events(id=args.id, after=after) # type: ignore - for event in resp["data"]: - print(event) - seen += 1 - if args.limit is not None and seen >= args.limit: - return - has_more = resp.get("has_more", False) - if resp["data"]: - after = resp["data"][-1]["id"] - - @classmethod - def follow(cls, args): - raise openai.error.OpenAIError( - message="Event streaming is not yet supported for `fine_tuning.job` events" - ) - - @classmethod - def cancel(cls, args): - resp = openai.FineTuningJob.cancel(id=args.id) - print(resp) - - -class WandbLogger: - @classmethod - def sync(cls, args): - import openai.wandb_logger - - resp = openai.wandb_logger.WandbLogger.sync( - id=args.id, - n_fine_tunes=args.n_fine_tunes, - project=args.project, - entity=args.entity, - force=args.force, - ) - print(resp) - - -def tools_register(parser): - subparsers = parser.add_subparsers( - title="Tools", help="Convenience client side tools" - ) - - def help(args): - parser.print_help() - - parser.set_defaults(func=help) - - sub = subparsers.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="http://github.com/store_true", - help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", - ) - sub.set_defaults(func=FineTune.prepare_data) - - -def api_register(parser): - # Engine management - subparsers = parser.add_subparsers(help="All API subcommands") - - def help(args): - parser.print_help() - - parser.set_defaults(func=help) - - sub = subparsers.add_parser("engines.list") - sub.set_defaults(func=Engine.list) - - sub = subparsers.add_parser("engines.get") - sub.add_argument("-i", "--id", required=True) - sub.set_defaults(func=Engine.get) - - sub = subparsers.add_parser("engines.update") - sub.add_argument("-i", "--id", required=True) - sub.add_argument("-r", "--replicas", type=int) - sub.set_defaults(func=Engine.update) - - sub = subparsers.add_parser("engines.generate") - sub.add_argument("-i", "--id", required=True) - sub.add_argument( - "--stream", help="Stream tokens as they're ready.", action="http://github.com/store_true" - ) - sub.add_argument("-c", "--context", help="An optional context to generate from") - sub.add_argument("-l", "--length", help="How many 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", - "--completions", - help="How many parallel completions to run on this context", - type=int, - ) - sub.add_argument( - "--logprobs", - help="Include the log probabilites on the `logprobs` most likely tokens. So for example, if `logprobs` is 10, the API will return a list of the 10 most likely tokens. If `logprobs` is supplied, the API will always return the logprob of the generated token, so there may be up to `logprobs+1` elements in the response.", - type=int, - ) - sub.add_argument( - "--stop", help="A stop sequence at which to stop generating tokens." - ) - sub.add_argument( - "-m", - "--model", - required=False, - help="A model (most commonly a model ID) to generate from. Defaults to the engine's default model.", - ) - sub.set_defaults(func=Engine.generate) - - # Chat Completions - sub = subparsers.add_parser("chat_completions.create") - - sub._action_groups.pop() - req = sub.add_argument_group("required arguments") - opt = sub.add_argument_group("optional arguments") - - req.add_argument( - "-g", - "--message", - action="http://github.com/append", - nargs=2, - metavar=("ROLE", "CONTENT"), - help="A message in `{role} {content}` format. Use this argument multiple times to add multiple messages.", - required=True, - ) - - group = opt.add_mutually_exclusive_group() - group.add_argument( - "-e", - "--engine", - help="The engine to use. See https://learn.microsoft.com/en-us/azure/cognitive-services/openai/chatgpt-quickstart?pivots=programming-language-python for more about what engines are available.", - ) - group.add_argument( - "-m", - "--model", - help="The model to use.", - ) - - opt.add_argument( - "-n", - "--n", - help="How many completions to generate for the conversation.", - type=int, - ) - opt.add_argument( - "-M", "--max-tokens", help="The maximum number of tokens to generate.", type=int - ) - opt.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, - ) - opt.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, - ) - opt.add_argument( - "--stop", - help="A stop sequence at which to stop generating tokens for the message.", - ) - opt.add_argument( - "--stream", help="Stream messages as they're ready.", action="http://github.com/store_true" - ) - sub.set_defaults(func=ChatCompletion.create) - - # Completions - sub = subparsers.add_parser("completions.create") - sub.add_argument( - "-e", - "--engine", - help="The engine to use. See https://platform.openai.com/docs/engines for more about what engines are available.", - ) - sub.add_argument( - "-m", - "--model", - help="The model to use. At most one of `engine` or `model` should be specified.", - ) - sub.add_argument( - "--stream", help="Stream tokens as they're ready.", action="http://github.com/store_true" - ) - sub.add_argument("-p", "--prompt", help="An optional prompt to complete from") - 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 probabilites 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( - "--stop", help="A stop sequence at which to stop generating tokens." - ) - sub.set_defaults(func=Completion.create) - - # Deployments - sub = subparsers.add_parser("deployments.list") - sub.set_defaults(func=Deployment.list) - - sub = subparsers.add_parser("deployments.get") - sub.add_argument("-i", "--id", required=True, help="The deployment ID") - sub.set_defaults(func=Deployment.get) - - sub = subparsers.add_parser("deployments.delete") - sub.add_argument("-i", "--id", required=True, help="The deployment ID") - sub.set_defaults(func=Deployment.delete) - - sub = subparsers.add_parser("deployments.create") - sub.add_argument("-m", "--model", required=True, help="The model ID") - sub.add_argument( - "-s", - "--scale_type", - required=True, - help="The scale type. Either 'manual' or 'standard'", - ) - sub.set_defaults(func=Deployment.create) - - # Models - sub = subparsers.add_parser("models.list") - sub.set_defaults(func=Model.list) - - sub = subparsers.add_parser("models.get") - sub.add_argument("-i", "--id", required=True, help="The model ID") - sub.set_defaults(func=Model.get) - - sub = subparsers.add_parser("models.delete") - sub.add_argument("-i", "--id", required=True, help="The model ID") - sub.set_defaults(func=Model.delete) - - # Files - sub = subparsers.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=File.create) - - sub = subparsers.add_parser("files.get") - sub.add_argument("-i", "--id", required=True, help="The files ID") - sub.set_defaults(func=File.get) - - sub = subparsers.add_parser("files.delete") - sub.add_argument("-i", "--id", required=True, help="The files ID") - sub.set_defaults(func=File.delete) - - sub = subparsers.add_parser("files.list") - sub.set_defaults(func=File.list) - - # Finetune - sub = subparsers.add_parser("fine_tunes.list") - sub.set_defaults(func=FineTune.list) - - sub = subparsers.add_parser("fine_tunes.create") - sub.add_argument( - "-t", - "--training_file", - required=True, - help="JSONL file containing prompt-completion examples for training. This can " - "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " - 'a local file path, or a URL that starts with "http".', - ) - sub.add_argument( - "-v", - "--validation_file", - help="JSONL file containing prompt-completion examples for validation. This can " - "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " - 'a local file path, or a URL that starts with "http".', - ) - sub.add_argument( - "--no_check_if_files_exist", - dest="check_if_files_exist", - action="http://github.com/store_false", - help="If this argument is set and training_file or validation_file are file paths, immediately upload them. If this argument is not set, check if they may be duplicates of already uploaded files before uploading, based on file name and file size.", - ) - sub.add_argument( - "-m", - "--model", - help="The model to start fine-tuning from", - ) - sub.add_argument( - "--suffix", - help="If set, this argument can be used to customize the generated fine-tuned model name." - "All punctuation and whitespace in `suffix` will be replaced with a " - "single dash, and the string will be lower cased. The max " - "length of `suffix` is 40 chars. " - "The generated name will match the form `{base_model}:ft-{org-title}:{suffix}-{timestamp}`. " - 'For example, `openai api fine_tunes.create -t test.jsonl -m ada --suffix "custom model name" ' - "could generate a model with the name " - "ada:ft-your-org:custom-model-name-2022-02-15-04-21-04", - ) - sub.add_argument( - "--no_follow", - action="http://github.com/store_true", - help="If set, returns immediately after creating the job. Otherwise, streams events and waits for the job to complete.", - ) - sub.add_argument( - "--n_epochs", - type=int, - help="The number of epochs to train the model for. An epoch refers to one " - "full cycle through the training dataset.", - ) - sub.add_argument( - "--batch_size", - type=int, - help="The batch size to use for training. The batch size is the number of " - "training examples used to train a single forward and backward pass.", - ) - sub.add_argument( - "--learning_rate_multiplier", - type=float, - help="The learning rate multiplier to use for training. The fine-tuning " - "learning rate is determined by the original learning rate used for " - "pretraining multiplied by this value.", - ) - sub.add_argument( - "--prompt_loss_weight", - type=float, - help="The weight to use for the prompt loss. The optimum value here depends " - "depends on your use case. This determines how much the model prioritizes " - "learning from prompt tokens vs learning from completion tokens.", - ) - sub.add_argument( - "--compute_classification_metrics", - action="http://github.com/store_true", - help="If set, we calculate classification-specific metrics such as accuracy " - "and F-1 score using the validation set at the end of every epoch.", - ) - sub.set_defaults(compute_classification_metrics=None) - sub.add_argument( - "--classification_n_classes", - type=int, - help="The number of classes in a classification task. This parameter is " - "required for multiclass classification.", - ) - sub.add_argument( - "--classification_positive_class", - help="The positive class in binary classification. This parameter is needed " - "to generate precision, recall and F-1 metrics when doing binary " - "classification.", - ) - sub.add_argument( - "--classification_betas", - type=float, - nargs="+", - help="If this is provided, we calculate F-beta scores at the specified beta " - "values. The F-beta score is a generalization of F-1 score. This is only " - "used for binary classification.", - ) - sub.set_defaults(func=FineTune.create) - - sub = subparsers.add_parser("fine_tunes.get") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTune.get) - - sub = subparsers.add_parser("fine_tunes.results") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTune.results) - - sub = subparsers.add_parser("fine_tunes.events") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - - # TODO(rachel): Remove this in 1.0 - sub.add_argument( - "-s", - "--stream", - action="http://github.com/store_true", - help="[DEPRECATED] If set, events will be streamed until the job is done. Otherwise, " - "displays the event history to date.", - ) - sub.set_defaults(func=FineTune.events) - - sub = subparsers.add_parser("fine_tunes.follow") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTune.follow) - - sub = subparsers.add_parser("fine_tunes.cancel") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTune.cancel) - - sub = subparsers.add_parser("fine_tunes.delete") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTune.delete) - - # Image - sub = subparsers.add_parser("image.create") - 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=Image.create) - - sub = subparsers.add_parser("image.create_edit") - 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=Image.create_edit) - - sub = subparsers.add_parser("image.create_variation") - 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=Image.create_variation) - - # Audio - # transcriptions - sub = subparsers.add_parser("audio.transcribe") - # 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=Audio.transcribe) - # translations - sub = subparsers.add_parser("audio.translate") - # 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=Audio.translate) - - # FineTuning Jobs - sub = subparsers.add_parser("fine_tuning.job.list") - sub.set_defaults(func=FineTuningJob.list) - - sub = subparsers.add_parser("fine_tuning.job.create") - sub.add_argument( - "-t", - "--training_file", - required=True, - help="JSONL file containing either chat-completion or prompt-completion examples for training. " - "This can be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " - 'a local file path, or a URL that starts with "http".', - ) - sub.add_argument( - "-v", - "--validation_file", - help="JSONL file containing either chat-completion or prompt-completion examples for validation. " - "This can be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " - 'a local file path, or a URL that starts with "http".', - ) - sub.add_argument( - "--no_check_if_files_exist", - dest="check_if_files_exist", - action="http://github.com/store_false", - help="If this argument is set and training_file or validation_file are file paths, immediately upload them. If this argument is not set, check if they may be duplicates of already uploaded files before uploading, based on file name and file size.", - ) - sub.add_argument( - "-m", - "--model", - help="The model to start fine-tuning from", - ) - sub.add_argument( - "--suffix", - help="If set, this argument can be used to customize the generated fine-tuned model name." - "All punctuation and whitespace in `suffix` will be replaced with a " - "single dash, and the string will be lower cased. The max " - "length of `suffix` is 18 chars. " - "The generated name will match the form `ft:{base_model}:{org-title}:{suffix}:{rstring}` where `rstring` " - "is a random string sortable as a timestamp. " - 'For example, `openai api fine_tuning.job.create -t test.jsonl -m gpt-3.5-turbo-0613 --suffix "first finetune!" ' - "could generate a model with the name " - "ft:gpt-3.5-turbo-0613:your-org:first-finetune:7p4PqAoY", - ) - sub.add_argument( - "--n_epochs", - type=int, - help="The number of epochs to train the model for. An epoch refers to one " - "full cycle through the training dataset.", - ) - sub.set_defaults(func=FineTuningJob.create) - - sub = subparsers.add_parser("fine_tuning.job.get") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTuningJob.get) - - sub = subparsers.add_parser("fine_tuning.job.results") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTuningJob.results) - - sub = subparsers.add_parser("fine_tuning.job.events") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.add_argument( - "--limit", - type=int, - required=False, - help="The number of events to return, starting from most recent. If not specified, all events will be returned.", - ) - sub.set_defaults(func=FineTuningJob.events) - - sub = subparsers.add_parser("fine_tuning.job.follow") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTuningJob.follow) - - sub = subparsers.add_parser("fine_tuning.job.cancel") - sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") - sub.set_defaults(func=FineTuningJob.cancel) - - -def wandb_register(parser): - subparsers = parser.add_subparsers( - title="wandb", help="Logging with Weights & Biases, see https://docs.wandb.ai/guides/integrations/openai for documentation" - ) - - def help(args): - parser.print_help() - - parser.set_defaults(func=help) - - sub = subparsers.add_parser("sync") - sub.add_argument("-i", "--id", help="The id of the fine-tune job (optional)") - sub.add_argument( - "-n", - "--n_fine_tunes", - type=int, - default=None, - help="Number of most recent fine-tunes to log when an id is not provided. By default, every fine-tune is synced.", - ) - sub.add_argument( - "--project", - default="OpenAI-Fine-Tune", - help="""Name of the Weights & Biases project where you're sending runs. By default, it is "OpenAI-Fine-Tune".""", - ) - sub.add_argument( - "--entity", - help="Weights & Biases username or team name where you're sending runs. By default, your default entity is used, which is usually your username.", - ) - sub.add_argument( - "--force", - action="http://github.com/store_true", - help="Forces logging and overwrite existing wandb run of the same fine-tune.", - ) - sub.add_argument( - "--legacy", - action="http://github.com/store_true", - help="Log results from legacy OpenAI /v1/fine-tunes api", - ) - sub.set_defaults(force=False) - sub.set_defaults(legacy=False) - sub.set_defaults(func=WandbLogger.sync) diff --git a/openai/datalib/__init__.py b/openai/datalib/__init__.py deleted file mode 100644 index d02b49cfff..0000000000 --- a/openai/datalib/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -This module helps make data libraries like `numpy` and `pandas` optional dependencies. - -The libraries add up to 130MB+, which makes it challenging to deploy applications -using this library in environments with code size constraints, like AWS Lambda. - -This module serves as an import proxy and provides a few utilities for dealing with the optionality. - -Since the primary use case of this library (talking to the OpenAI API) doesn't generally require data libraries, -it's safe to make them optional. The rare case when data libraries are needed in the client is handled through -assertions with instructive error messages. - -See also `setup.py`. -""" diff --git a/openai/datalib/common.py b/openai/datalib/common.py deleted file mode 100644 index 96f9908a18..0000000000 --- a/openai/datalib/common.py +++ /dev/null @@ -1,17 +0,0 @@ -INSTRUCTIONS = """ - -OpenAI error: - - missing `{library}` - -This feature requires additional dependencies: - - $ pip install openai[datalib] - -""" - -NUMPY_INSTRUCTIONS = INSTRUCTIONS.format(library="numpy") - - -class MissingDependencyError(Exception): - pass diff --git a/openai/datalib/numpy_helper.py b/openai/datalib/numpy_helper.py deleted file mode 100644 index fb80f2ae54..0000000000 --- a/openai/datalib/numpy_helper.py +++ /dev/null @@ -1,15 +0,0 @@ -from openai.datalib.common import INSTRUCTIONS, MissingDependencyError - -try: - import numpy -except ImportError: - numpy = None - -HAS_NUMPY = bool(numpy) - -NUMPY_INSTRUCTIONS = INSTRUCTIONS.format(library="numpy") - - -def assert_has_numpy(): - if not HAS_NUMPY: - raise MissingDependencyError(NUMPY_INSTRUCTIONS) diff --git a/openai/datalib/pandas_helper.py b/openai/datalib/pandas_helper.py deleted file mode 100644 index 4e86d7b4f9..0000000000 --- a/openai/datalib/pandas_helper.py +++ /dev/null @@ -1,15 +0,0 @@ -from openai.datalib.common import INSTRUCTIONS, MissingDependencyError - -try: - import pandas -except ImportError: - pandas = None - -HAS_PANDAS = bool(pandas) - -PANDAS_INSTRUCTIONS = INSTRUCTIONS.format(library="pandas") - - -def assert_has_pandas(): - if not HAS_PANDAS: - raise MissingDependencyError(PANDAS_INSTRUCTIONS) diff --git a/openai/embeddings_utils.py b/openai/embeddings_utils.py deleted file mode 100644 index dc26445c3c..0000000000 --- a/openai/embeddings_utils.py +++ /dev/null @@ -1,252 +0,0 @@ -import textwrap as tr -from typing import List, Optional - -import matplotlib.pyplot as plt -import plotly.express as px -from scipy import spatial -from sklearn.decomposition import PCA -from sklearn.manifold import TSNE -from sklearn.metrics import average_precision_score, precision_recall_curve -from tenacity import retry, stop_after_attempt, wait_random_exponential - -import openai -from openai.datalib.numpy_helper import numpy as np -from openai.datalib.pandas_helper import pandas as pd - - -@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) -def get_embedding(text: str, engine="text-embedding-ada-002", **kwargs) -> List[float]: - - # replace newlines, which can negatively affect performance. - text = text.replace("\n", " ") - - return openai.Embedding.create(input=[text], engine=engine, **kwargs)["data"][0]["embedding"] - - -@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) -async def aget_embedding( - text: str, engine="text-embedding-ada-002", **kwargs -) -> List[float]: - - # replace newlines, which can negatively affect performance. - text = text.replace("\n", " ") - - return (await openai.Embedding.acreate(input=[text], engine=engine, **kwargs))["data"][0][ - "embedding" - ] - - -@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) -def get_embeddings( - list_of_text: List[str], engine="text-embedding-ada-002", **kwargs -) -> List[List[float]]: - assert len(list_of_text) <= 8191, "The batch size should not be larger than 8191." - - # replace newlines, which can negatively affect performance. - list_of_text = [text.replace("\n", " ") for text in list_of_text] - - data = openai.Embedding.create(input=list_of_text, engine=engine, **kwargs).data - return [d["embedding"] for d in data] - - -@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) -async def aget_embeddings( - list_of_text: List[str], engine="text-embedding-ada-002", **kwargs -) -> List[List[float]]: - assert len(list_of_text) <= 8191, "The batch size should not be larger than 8191." - - # replace newlines, which can negatively affect performance. - list_of_text = [text.replace("\n", " ") for text in list_of_text] - - data = (await openai.Embedding.acreate(input=list_of_text, engine=engine, **kwargs)).data - return [d["embedding"] for d in data] - - -def cosine_similarity(a, b): - return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) - - -def plot_multiclass_precision_recall( - y_score, y_true_untransformed, class_list, classifier_name -): - """ - Precision-Recall plotting for a multiclass problem. It plots average precision-recall, per class precision recall and reference f1 contours. - - Code slightly modified, but heavily based on https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html - """ - n_classes = len(class_list) - y_true = pd.concat( - [(y_true_untransformed == class_list[i]) for i in range(n_classes)], axis=1 - ).values - - # For each class - precision = dict() - recall = dict() - average_precision = dict() - for i in range(n_classes): - precision[i], recall[i], _ = precision_recall_curve(y_true[:, i], y_score[:, i]) - average_precision[i] = average_precision_score(y_true[:, i], y_score[:, i]) - - # A "micro-average": quantifying score on all classes jointly - precision_micro, recall_micro, _ = precision_recall_curve( - y_true.ravel(), y_score.ravel() - ) - average_precision_micro = average_precision_score(y_true, y_score, average="micro") - print( - str(classifier_name) - + " - Average precision score over all classes: {0:0.2f}".format( - average_precision_micro - ) - ) - - # setup plot details - plt.figure(figsize=(9, 10)) - f_scores = np.linspace(0.2, 0.8, num=4) - lines = [] - labels = [] - for f_score in f_scores: - x = np.linspace(0.01, 1) - y = f_score * x / (2 * x - f_score) - (l,) = plt.plot(x[y >= 0], y[y >= 0], color="gray", alpha=0.2) - plt.annotate("f1={0:0.1f}".format(f_score), xy=(0.9, y[45] + 0.02)) - - lines.append(l) - labels.append("iso-f1 curves") - (l,) = plt.plot(recall_micro, precision_micro, color="gold", lw=2) - lines.append(l) - labels.append( - "average Precision-recall (auprc = {0:0.2f})" "".format(average_precision_micro) - ) - - for i in range(n_classes): - (l,) = plt.plot(recall[i], precision[i], lw=2) - lines.append(l) - labels.append( - "Precision-recall for class `{0}` (auprc = {1:0.2f})" - "".format(class_list[i], average_precision[i]) - ) - - fig = plt.gcf() - fig.subplots_adjust(bottom=0.25) - plt.xlim([0.0, 1.0]) - plt.ylim([0.0, 1.05]) - plt.xlabel("Recall") - plt.ylabel("Precision") - plt.title(f"{classifier_name}: Precision-Recall curve for each class") - plt.legend(lines, labels) - - -def distances_from_embeddings( - query_embedding: List[float], - embeddings: List[List[float]], - distance_metric="cosine", -) -> List[List]: - """Return the distances between a query embedding and a list of embeddings.""" - distance_metrics = { - "cosine": spatial.distance.cosine, - "L1": spatial.distance.cityblock, - "L2": spatial.distance.euclidean, - "Linf": spatial.distance.chebyshev, - } - distances = [ - distance_metrics[distance_metric](query_embedding, embedding) - for embedding in embeddings - ] - return distances - - -def indices_of_nearest_neighbors_from_distances(distances) -> np.ndarray: - """Return a list of indices of nearest neighbors from a list of distances.""" - return np.argsort(distances) - - -def pca_components_from_embeddings( - embeddings: List[List[float]], n_components=2 -) -> np.ndarray: - """Return the PCA components of a list of embeddings.""" - pca = PCA(n_components=n_components) - array_of_embeddings = np.array(embeddings) - return pca.fit_transform(array_of_embeddings) - - -def tsne_components_from_embeddings( - embeddings: List[List[float]], n_components=2, **kwargs -) -> np.ndarray: - """Returns t-SNE components of a list of embeddings.""" - # use better defaults if not specified - if "init" not in kwargs.keys(): - kwargs["init"] = "pca" - if "learning_rate" not in kwargs.keys(): - kwargs["learning_rate"] = "auto" - tsne = TSNE(n_components=n_components, **kwargs) - array_of_embeddings = np.array(embeddings) - return tsne.fit_transform(array_of_embeddings) - - -def chart_from_components( - components: np.ndarray, - labels: Optional[List[str]] = None, - strings: Optional[List[str]] = None, - x_title="Component 0", - y_title="Component 1", - mark_size=5, - **kwargs, -): - """Return an interactive 2D chart of embedding components.""" - empty_list = ["" for _ in components] - data = pd.DataFrame( - { - x_title: components[:, 0], - y_title: components[:, 1], - "label": labels if labels else empty_list, - "string": ["
".join(tr.wrap(string, width=30)) for string in strings] - if strings - else empty_list, - } - ) - chart = px.scatter( - data, - x=x_title, - y=y_title, - color="label" if labels else None, - symbol="label" if labels else None, - hover_data=["string"] if strings else None, - **kwargs, - ).update_traces(marker=dict(size=mark_size)) - return chart - - -def chart_from_components_3D( - components: np.ndarray, - labels: Optional[List[str]] = None, - strings: Optional[List[str]] = None, - x_title: str = "Component 0", - y_title: str = "Component 1", - z_title: str = "Compontent 2", - mark_size: int = 5, - **kwargs, -): - """Return an interactive 3D chart of embedding components.""" - empty_list = ["" for _ in components] - data = pd.DataFrame( - { - x_title: components[:, 0], - y_title: components[:, 1], - z_title: components[:, 2], - "label": labels if labels else empty_list, - "string": ["
".join(tr.wrap(string, width=30)) for string in strings] - if strings - else empty_list, - } - ) - chart = px.scatter_3d( - data, - x=x_title, - y=y_title, - z=z_title, - color="label" if labels else None, - symbol="label" if labels else None, - hover_data=["string"] if strings else None, - **kwargs, - ).update_traces(marker=dict(size=mark_size)) - return chart diff --git a/openai/error.py b/openai/error.py deleted file mode 100644 index 2928ef6aa6..0000000000 --- a/openai/error.py +++ /dev/null @@ -1,169 +0,0 @@ -import openai - - -class OpenAIError(Exception): - def __init__( - self, - message=None, - http_body=None, - http_status=None, - json_body=None, - headers=None, - code=None, - ): - super(OpenAIError, self).__init__(message) - - if http_body and hasattr(http_body, "decode"): - try: - http_body = http_body.decode("utf-8") - except BaseException: - http_body = ( - "" - ) - - self._message = message - self.http_body = http_body - self.http_status = http_status - self.json_body = json_body - self.headers = headers or {} - self.code = code - self.request_id = self.headers.get("request-id", None) - self.error = self.construct_error_object() - self.organization = self.headers.get("openai-organization", None) - - def __str__(self): - msg = self._message or "" - if self.request_id is not None: - return "Request {0}: {1}".format(self.request_id, msg) - else: - return msg - - # Returns the underlying `Exception` (base class) message, which is usually - # the raw message returned by OpenAI's API. This was previously available - # in python2 via `error.message`. Unlike `str(error)`, it omits "Request - # req_..." from the beginning of the string. - @property - def user_message(self): - return self._message - - def __repr__(self): - return "%s(message=%r, http_status=%r, request_id=%r)" % ( - self.__class__.__name__, - self._message, - self.http_status, - self.request_id, - ) - - def construct_error_object(self): - if ( - self.json_body is None - or not isinstance(self.json_body, dict) - or "error" not in self.json_body - or not isinstance(self.json_body["error"], dict) - ): - return None - - return openai.api_resources.error_object.ErrorObject.construct_from( - self.json_body["error"] - ) - - -class APIError(OpenAIError): - pass - - -class TryAgain(OpenAIError): - pass - - -class Timeout(OpenAIError): - pass - - -class APIConnectionError(OpenAIError): - def __init__( - self, - message, - http_body=None, - http_status=None, - json_body=None, - headers=None, - code=None, - should_retry=False, - ): - super(APIConnectionError, self).__init__( - message, http_body, http_status, json_body, headers, code - ) - self.should_retry = should_retry - - -class InvalidRequestError(OpenAIError): - def __init__( - self, - message, - param, - code=None, - http_body=None, - http_status=None, - json_body=None, - headers=None, - ): - super(InvalidRequestError, self).__init__( - message, http_body, http_status, json_body, headers, code - ) - self.param = param - - def __repr__(self): - return "%s(message=%r, param=%r, code=%r, http_status=%r, " "request_id=%r)" % ( - self.__class__.__name__, - self._message, - self.param, - self.code, - self.http_status, - self.request_id, - ) - - def __reduce__(self): - return type(self), ( - self._message, - self.param, - self.code, - self.http_body, - self.http_status, - self.json_body, - self.headers, - ) - - -class AuthenticationError(OpenAIError): - pass - - -class PermissionError(OpenAIError): - pass - - -class RateLimitError(OpenAIError): - pass - - -class ServiceUnavailableError(OpenAIError): - pass - - -class InvalidAPIType(OpenAIError): - pass - - -class SignatureVerificationError(OpenAIError): - def __init__(self, message, sig_header, http_body=None): - super(SignatureVerificationError, self).__init__(message, http_body) - self.sig_header = sig_header - - def __reduce__(self): - return type(self), ( - self._message, - self.sig_header, - self.http_body, - ) diff --git a/openai/object_classes.py b/openai/object_classes.py deleted file mode 100644 index 08093650fd..0000000000 --- a/openai/object_classes.py +++ /dev/null @@ -1,12 +0,0 @@ -from openai import api_resources -from openai.api_resources.experimental.completion_config import CompletionConfig - -OBJECT_CLASSES = { - "engine": api_resources.Engine, - "experimental.completion_config": CompletionConfig, - "file": api_resources.File, - "fine-tune": api_resources.FineTune, - "model": api_resources.Model, - "deployment": api_resources.Deployment, - "fine_tuning.job": api_resources.FineTuningJob, -} diff --git a/openai/openai_object.py b/openai/openai_object.py deleted file mode 100644 index 95f8829742..0000000000 --- a/openai/openai_object.py +++ /dev/null @@ -1,347 +0,0 @@ -import json -from copy import deepcopy -from typing import Optional, Tuple, Union - -import openai -from openai import api_requestor, util -from openai.openai_response import OpenAIResponse -from openai.util import ApiType - - -class OpenAIObject(dict): - api_base_override = None - - def __init__( - self, - id=None, - api_key=None, - api_version=None, - api_type=None, - organization=None, - response_ms: Optional[int] = None, - api_base=None, - engine=None, - **params, - ): - super(OpenAIObject, self).__init__() - - if response_ms is not None and not isinstance(response_ms, int): - raise TypeError(f"response_ms is a {type(response_ms).__name__}.") - self._response_ms = response_ms - - self._retrieve_params = params - - object.__setattr__(self, "api_key", api_key) - object.__setattr__(self, "api_version", api_version) - object.__setattr__(self, "api_type", api_type) - object.__setattr__(self, "organization", organization) - object.__setattr__(self, "api_base_override", api_base) - object.__setattr__(self, "engine", engine) - - if id: - self["id"] = id - - @property - def response_ms(self) -> Optional[int]: - return self._response_ms - - def __setattr__(self, k, v): - if k[0] == "_" or k in self.__dict__: - return super(OpenAIObject, self).__setattr__(k, v) - - self[k] = v - return None - - def __getattr__(self, k): - if k[0] == "_": - raise AttributeError(k) - try: - return self[k] - except KeyError as err: - raise AttributeError(*err.args) - - def __delattr__(self, k): - if k[0] == "_" or k in self.__dict__: - return super(OpenAIObject, self).__delattr__(k) - else: - del self[k] - - def __setitem__(self, k, v): - if v == "": - raise ValueError( - "You cannot set %s to an empty string. " - "We interpret empty strings as None in requests." - "You may set %s.%s = None to delete the property" % (k, str(self), k) - ) - super(OpenAIObject, self).__setitem__(k, v) - - def __delitem__(self, k): - raise NotImplementedError("del is not supported") - - # Custom unpickling method that uses `update` to update the dictionary - # without calling __setitem__, which would fail if any value is an empty - # string - def __setstate__(self, state): - self.update(state) - - # Custom pickling method to ensure the instance is pickled as a custom - # class and not as a dict, otherwise __setstate__ would not be called when - # unpickling. - def __reduce__(self): - reduce_value = ( - type(self), # callable - ( # args - self.get("id", None), - self.api_key, - self.api_version, - self.api_type, - self.organization, - ), - dict(self), # state - ) - return reduce_value - - @classmethod - def construct_from( - cls, - values, - api_key: Optional[str] = None, - api_version=None, - organization=None, - engine=None, - response_ms: Optional[int] = None, - ): - instance = cls( - values.get("id"), - api_key=api_key, - api_version=api_version, - organization=organization, - engine=engine, - response_ms=response_ms, - ) - instance.refresh_from( - values, - api_key=api_key, - api_version=api_version, - organization=organization, - response_ms=response_ms, - ) - return instance - - def refresh_from( - self, - values, - api_key=None, - api_version=None, - api_type=None, - organization=None, - response_ms: Optional[int] = None, - ): - self.api_key = api_key or getattr(values, "api_key", None) - self.api_version = api_version or getattr(values, "api_version", None) - self.api_type = api_type or getattr(values, "api_type", None) - self.organization = organization or getattr(values, "organization", None) - self._response_ms = response_ms or getattr(values, "_response_ms", None) - - # Wipe old state before setting new. - self.clear() - for k, v in values.items(): - super(OpenAIObject, self).__setitem__( - k, util.convert_to_openai_object(v, api_key, api_version, organization) - ) - - self._previous = values - - @classmethod - def api_base(cls): - return None - - def request( - self, - method, - url, - params=None, - headers=None, - stream=False, - plain_old_data=False, - request_id: Optional[str] = None, - request_timeout: Optional[Union[float, Tuple[float, float]]] = None, - ): - if params is None: - params = self._retrieve_params - requestor = api_requestor.APIRequestor( - key=self.api_key, - api_base=self.api_base_override or self.api_base(), - api_type=self.api_type, - api_version=self.api_version, - organization=self.organization, - ) - response, stream, api_key = requestor.request( - method, - url, - params=params, - stream=stream, - headers=headers, - request_id=request_id, - request_timeout=request_timeout, - ) - - if stream: - assert not isinstance(response, OpenAIResponse) # must be an iterator - return ( - util.convert_to_openai_object( - line, - api_key, - self.api_version, - self.organization, - plain_old_data=plain_old_data, - ) - for line in response - ) - else: - return util.convert_to_openai_object( - response, - api_key, - self.api_version, - self.organization, - plain_old_data=plain_old_data, - ) - - async def arequest( - self, - method, - url, - params=None, - headers=None, - stream=False, - plain_old_data=False, - request_id: Optional[str] = None, - request_timeout: Optional[Union[float, Tuple[float, float]]] = None, - ): - if params is None: - params = self._retrieve_params - requestor = api_requestor.APIRequestor( - key=self.api_key, - api_base=self.api_base_override or self.api_base(), - api_type=self.api_type, - api_version=self.api_version, - organization=self.organization, - ) - response, stream, api_key = await requestor.arequest( - method, - url, - params=params, - stream=stream, - headers=headers, - request_id=request_id, - request_timeout=request_timeout, - ) - - if stream: - assert not isinstance(response, OpenAIResponse) # must be an iterator - return ( - util.convert_to_openai_object( - line, - api_key, - self.api_version, - self.organization, - plain_old_data=plain_old_data, - ) - for line in response - ) - else: - return util.convert_to_openai_object( - response, - api_key, - self.api_version, - self.organization, - plain_old_data=plain_old_data, - ) - - def __repr__(self): - ident_parts = [type(self).__name__] - - obj = self.get("object") - if isinstance(obj, str): - ident_parts.append(obj) - - if isinstance(self.get("id"), str): - ident_parts.append("id=%s" % (self.get("id"),)) - - unicode_repr = "<%s at %s> JSON: %s" % ( - " ".join(ident_parts), - hex(id(self)), - str(self), - ) - - return unicode_repr - - def __str__(self): - obj = self.to_dict_recursive() - return json.dumps(obj, indent=2) - - def to_dict(self): - return dict(self) - - def to_dict_recursive(self): - d = dict(self) - for k, v in d.items(): - if isinstance(v, OpenAIObject): - d[k] = v.to_dict_recursive() - elif isinstance(v, list): - d[k] = [ - e.to_dict_recursive() if isinstance(e, OpenAIObject) else e - for e in v - ] - return d - - @property - def openai_id(self): - return self.id - - @property - def typed_api_type(self): - return ( - ApiType.from_str(self.api_type) - if self.api_type - else ApiType.from_str(openai.api_type) - ) - - # This class overrides __setitem__ to throw exceptions on inputs that it - # doesn't like. This can cause problems when we try to copy an object - # wholesale because some data that's returned from the API may not be valid - # if it was set to be set manually. Here we override the class' copy - # arguments so that we can bypass these possible exceptions on __setitem__. - def __copy__(self): - copied = OpenAIObject( - self.get("id"), - self.api_key, - api_version=self.api_version, - api_type=self.api_type, - organization=self.organization, - ) - - copied._retrieve_params = self._retrieve_params - - for k, v in self.items(): - # Call parent's __setitem__ to avoid checks that we've added in the - # overridden version that can throw exceptions. - super(OpenAIObject, copied).__setitem__(k, v) - - return copied - - # This class overrides __setitem__ to throw exceptions on inputs that it - # doesn't like. This can cause problems when we try to copy an object - # wholesale because some data that's returned from the API may not be valid - # if it was set to be set manually. Here we override the class' copy - # arguments so that we can bypass these possible exceptions on __setitem__. - def __deepcopy__(self, memo): - copied = self.__copy__() - memo[id(self)] = copied - - for k, v in self.items(): - # Call parent's __setitem__ to avoid checks that we've added in the - # overridden version that can throw exceptions. - super(OpenAIObject, copied).__setitem__(k, deepcopy(v, memo)) - - return copied diff --git a/openai/openai_response.py b/openai/openai_response.py deleted file mode 100644 index d2230b1540..0000000000 --- a/openai/openai_response.py +++ /dev/null @@ -1,31 +0,0 @@ -from typing import Optional - - -class OpenAIResponse: - def __init__(self, data, headers): - self._headers = headers - self.data = data - - @property - def request_id(self) -> Optional[str]: - return self._headers.get("request-id") - - @property - def retry_after(self) -> Optional[int]: - try: - return int(self._headers.get("retry-after")) - except TypeError: - return None - - @property - def operation_location(self) -> Optional[str]: - return self._headers.get("operation-location") - - @property - def organization(self) -> Optional[str]: - return self._headers.get("OpenAI-Organization") - - @property - def response_ms(self) -> Optional[int]: - h = self._headers.get("Openai-Processing-Ms") - return None if h is None else round(float(h)) diff --git a/openai/tests/__init__.py b/openai/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openai/tests/asyncio/__init__.py b/openai/tests/asyncio/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openai/tests/asyncio/test_endpoints.py b/openai/tests/asyncio/test_endpoints.py deleted file mode 100644 index 1b146e6749..0000000000 --- a/openai/tests/asyncio/test_endpoints.py +++ /dev/null @@ -1,90 +0,0 @@ -import io -import json - -import pytest -from aiohttp import ClientSession - -import openai -from openai import error - -pytestmark = [pytest.mark.asyncio] - - -# FILE TESTS -async def test_file_upload(): - result = await openai.File.acreate( - file=io.StringIO( - json.dumps({"prompt": "test file data", "completion": "tada"}) - ), - purpose="fine-tune", - ) - assert result.purpose == "fine-tune" - assert "id" in result - - result = await openai.File.aretrieve(id=result.id) - assert result.status == "uploaded" - - -# COMPLETION TESTS -async def test_completions(): - result = await openai.Completion.acreate( - prompt="This was a test", n=5, engine="ada" - ) - assert len(result.choices) == 5 - - -async def test_completions_multiple_prompts(): - result = await openai.Completion.acreate( - prompt=["This was a test", "This was another test"], n=5, engine="ada" - ) - assert len(result.choices) == 10 - - -async def test_completions_model(): - result = await openai.Completion.acreate(prompt="This was a test", n=5, model="ada") - assert len(result.choices) == 5 - assert result.model.startswith("ada") - - -async def test_timeout_raises_error(): - # A query that should take awhile to return - with pytest.raises(error.Timeout): - await openai.Completion.acreate( - prompt="test" * 1000, - n=10, - model="ada", - max_tokens=100, - request_timeout=0.01, - ) - - -async def test_timeout_does_not_error(): - # A query that should be fast - await openai.Completion.acreate( - prompt="test", - model="ada", - request_timeout=10, - ) - - -async def test_completions_stream_finishes_global_session(): - async with ClientSession() as session: - openai.aiosession.set(session) - - # A query that should be fast - parts = [] - async for part in await openai.Completion.acreate( - prompt="test", model="ada", request_timeout=3, stream=True - ): - parts.append(part) - assert len(parts) > 1 - - -async def test_completions_stream_finishes_local_session(): - # A query that should be fast - parts = [] - async for part in await openai.Completion.acreate( - prompt="test", model="ada", request_timeout=3, stream=True - ): - parts.append(part) - assert len(parts) > 1 diff --git a/openai/tests/test_api_requestor.py b/openai/tests/test_api_requestor.py deleted file mode 100644 index 56e8ec89da..0000000000 --- a/openai/tests/test_api_requestor.py +++ /dev/null @@ -1,101 +0,0 @@ -import json - -import pytest -import requests -from pytest_mock import MockerFixture - -from openai import Model -from openai.api_requestor import APIRequestor - - -@pytest.mark.requestor -def test_requestor_sets_request_id(mocker: MockerFixture) -> None: - # Fake out 'requests' and confirm that the X-Request-Id header is set. - - got_headers = {} - - def fake_request(self, *args, **kwargs): - nonlocal got_headers - got_headers = kwargs["headers"] - r = requests.Response() - r.status_code = 200 - r.headers["content-type"] = "application/json" - r._content = json.dumps({}).encode("utf-8") - return r - - mocker.patch("requests.sessions.Session.request", fake_request) - fake_request_id = "1234" - Model.retrieve("xxx", request_id=fake_request_id) # arbitrary API resource - got_request_id = got_headers.get("X-Request-Id") - assert got_request_id == fake_request_id - - -@pytest.mark.requestor -def test_requestor_open_ai_headers() -> None: - api_requestor = APIRequestor(key="test_key", api_type="open_ai") - headers = {"Test_Header": "Unit_Test_Header"} - headers = api_requestor.request_headers( - method="get", extra=headers, request_id="test_id" - ) - assert "Test_Header" in headers - assert headers["Test_Header"] == "Unit_Test_Header" - assert "Authorization" in headers - assert headers["Authorization"] == "Bearer test_key" - - -@pytest.mark.requestor -def test_requestor_azure_headers() -> None: - api_requestor = APIRequestor(key="test_key", api_type="azure") - headers = {"Test_Header": "Unit_Test_Header"} - headers = api_requestor.request_headers( - method="get", extra=headers, request_id="test_id" - ) - assert "Test_Header" in headers - assert headers["Test_Header"] == "Unit_Test_Header" - assert "api-key" in headers - assert headers["api-key"] == "test_key" - - -@pytest.mark.requestor -def test_requestor_azure_ad_headers() -> None: - api_requestor = APIRequestor(key="test_key", api_type="azure_ad") - headers = {"Test_Header": "Unit_Test_Header"} - headers = api_requestor.request_headers( - method="get", extra=headers, request_id="test_id" - ) - assert "Test_Header" in headers - assert headers["Test_Header"] == "Unit_Test_Header" - assert "Authorization" in headers - assert headers["Authorization"] == "Bearer test_key" - - -@pytest.mark.requestor -def test_requestor_cycle_sessions(mocker: MockerFixture) -> None: - # HACK: we need to purge the _thread_context to not interfere - # with other tests - from openai.api_requestor import _thread_context - - delattr(_thread_context, "session") - - api_requestor = APIRequestor(key="test_key", api_type="azure_ad") - - mock_session = mocker.MagicMock() - mocker.patch("openai.api_requestor._make_session", lambda: mock_session) - - # We don't call `session.close()` if not enough time has elapsed - api_requestor.request_raw("get", "http://example.com/") - mock_session.request.assert_called() - api_requestor.request_raw("get", "http://example.com/") - mock_session.close.assert_not_called() - - mocker.patch("openai.api_requestor.MAX_SESSION_LIFETIME_SECS", 0) - - # Due to 0 lifetime, the original session will be closed before the next call - # and a new session will be created - mock_session_2 = mocker.MagicMock() - mocker.patch("openai.api_requestor._make_session", lambda: mock_session_2) - api_requestor.request_raw("get", "http://example.com/") - mock_session.close.assert_called() - mock_session_2.request.assert_called() - - delattr(_thread_context, "session") diff --git a/openai/tests/test_endpoints.py b/openai/tests/test_endpoints.py deleted file mode 100644 index 958e07f091..0000000000 --- a/openai/tests/test_endpoints.py +++ /dev/null @@ -1,118 +0,0 @@ -import io -import json - -import pytest -import requests - -import openai -from openai import error - - -# FILE TESTS -def test_file_upload(): - result = openai.File.create( - file=io.StringIO( - json.dumps({"prompt": "test file data", "completion": "tada"}) - ), - purpose="fine-tune", - ) - assert result.purpose == "fine-tune" - assert "id" in result - - result = openai.File.retrieve(id=result.id) - assert result.status == "uploaded" - - -# CHAT COMPLETION TESTS -def test_chat_completions(): - result = openai.ChatCompletion.create( - model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello!"}] - ) - assert len(result.choices) == 1 - - -def test_chat_completions_multiple(): - result = openai.ChatCompletion.create( - model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello!"}], n=5 - ) - assert len(result.choices) == 5 - - -def test_chat_completions_streaming(): - result = None - events = openai.ChatCompletion.create( - model="gpt-3.5-turbo", - messages=[{"role": "user", "content": "Hello!"}], - stream=True, - ) - for result in events: - assert len(result.choices) == 1 - - -# COMPLETION TESTS -def test_completions(): - result = openai.Completion.create(prompt="This was a test", n=5, engine="ada") - assert len(result.choices) == 5 - - -def test_completions_multiple_prompts(): - result = openai.Completion.create( - prompt=["This was a test", "This was another test"], n=5, engine="ada" - ) - assert len(result.choices) == 10 - - -def test_completions_model(): - result = openai.Completion.create(prompt="This was a test", n=5, model="ada") - assert len(result.choices) == 5 - assert result.model.startswith("ada") - - -def test_timeout_raises_error(): - # A query that should take awhile to return - with pytest.raises(error.Timeout): - openai.Completion.create( - prompt="test" * 1000, - n=10, - model="ada", - max_tokens=100, - request_timeout=0.01, - ) - - -def test_timeout_does_not_error(): - # A query that should be fast - openai.Completion.create( - prompt="test", - model="ada", - request_timeout=10, - ) - - -def test_user_session(): - with requests.Session() as session: - openai.requestssession = session - - completion = openai.Completion.create( - prompt="hello world", - model="ada", - ) - assert completion - - -def test_user_session_factory(): - def factory(): - session = requests.Session() - session.mount( - "https://", - requests.adapters.HTTPAdapter(max_retries=4), - ) - return session - - openai.requestssession = factory - - completion = openai.Completion.create( - prompt="hello world", - model="ada", - ) - assert completion diff --git a/openai/tests/test_exceptions.py b/openai/tests/test_exceptions.py deleted file mode 100644 index 7760cdc5f6..0000000000 --- a/openai/tests/test_exceptions.py +++ /dev/null @@ -1,40 +0,0 @@ -import pickle - -import pytest - -import openai - -EXCEPTION_TEST_CASES = [ - openai.InvalidRequestError( - "message", - "param", - code=400, - http_body={"test": "test1"}, - http_status="fail", - json_body={"text": "iono some text"}, - headers={"request-id": "asasd"}, - ), - openai.error.AuthenticationError(), - openai.error.PermissionError(), - openai.error.RateLimitError(), - openai.error.ServiceUnavailableError(), - openai.error.SignatureVerificationError("message", "sig_header?"), - openai.error.APIConnectionError("message!", should_retry=True), - openai.error.TryAgain(), - openai.error.Timeout(), - openai.error.APIError( - message="message", - code=400, - http_body={"test": "test1"}, - http_status="fail", - json_body={"text": "iono some text"}, - headers={"request-id": "asasd"}, - ), - openai.error.OpenAIError(), -] - - -class TestExceptions: - @pytest.mark.parametrize("error", EXCEPTION_TEST_CASES) - def test_exceptions_are_pickleable(self, error) -> None: - assert error.__repr__() == pickle.loads(pickle.dumps(error)).__repr__() diff --git a/openai/tests/test_file_cli.py b/openai/tests/test_file_cli.py deleted file mode 100644 index 69ea29e2a0..0000000000 --- a/openai/tests/test_file_cli.py +++ /dev/null @@ -1,39 +0,0 @@ -import json -import subprocess -import time -from tempfile import NamedTemporaryFile - -STILL_PROCESSING = "File is still processing. Check back later." - - -def test_file_cli() -> None: - contents = json.dumps({"prompt": "1 + 3 =", "completion": "4"}) + "\n" - with NamedTemporaryFile(suffix=".jsonl", mode="wb") as train_file: - train_file.write(contents.encode("utf-8")) - train_file.flush() - create_output = subprocess.check_output( - ["openai", "api", "files.create", "-f", train_file.name, "-p", "fine-tune"] - ) - file_obj = json.loads(create_output) - assert file_obj["bytes"] == len(contents) - file_id: str = file_obj["id"] - assert file_id.startswith("file-") - start_time = time.time() - while True: - delete_result = subprocess.run( - ["openai", "api", "files.delete", "-i", file_id], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - encoding="utf-8", - ) - if delete_result.returncode == 0: - break - elif STILL_PROCESSING in delete_result.stderr: - time.sleep(0.5) - if start_time + 60 < time.time(): - raise RuntimeError("timed out waiting for file to become available") - continue - else: - raise RuntimeError( - f"delete failed: stdout={delete_result.stdout} stderr={delete_result.stderr}" - ) diff --git a/openai/tests/test_long_examples_validator.py b/openai/tests/test_long_examples_validator.py deleted file mode 100644 index 949a7cbbae..0000000000 --- a/openai/tests/test_long_examples_validator.py +++ /dev/null @@ -1,54 +0,0 @@ -import json -import subprocess -from tempfile import NamedTemporaryFile - -import pytest - -from openai.datalib.numpy_helper import HAS_NUMPY, NUMPY_INSTRUCTIONS -from openai.datalib.pandas_helper import HAS_PANDAS, PANDAS_INSTRUCTIONS - - -@pytest.mark.skipif(not HAS_PANDAS, reason=PANDAS_INSTRUCTIONS) -@pytest.mark.skipif(not HAS_NUMPY, reason=NUMPY_INSTRUCTIONS) -def test_long_examples_validator() -> None: - """ - Ensures that long_examples_validator() handles previously applied recommendations, - namely dropped duplicates, without resulting in a KeyError. - """ - - # data - short_prompt = "a prompt " - long_prompt = short_prompt * 500 - - short_completion = "a completion " - long_completion = short_completion * 500 - - # the order of these matters - unprepared_training_data = [ - {"prompt": long_prompt, "completion": long_completion}, # 1 of 2 duplicates - {"prompt": short_prompt, "completion": short_completion}, - {"prompt": long_prompt, "completion": long_completion}, # 2 of 2 duplicates - ] - - with NamedTemporaryFile(suffix=".jsonl", mode="w") as training_data: - print(training_data.name) - for prompt_completion_row in unprepared_training_data: - training_data.write(json.dumps(prompt_completion_row) + "\n") - training_data.flush() - - prepared_data_cmd_output = subprocess.run( - [f"openai tools fine_tunes.prepare_data -f {training_data.name}"], - stdout=subprocess.PIPE, - text=True, - input="y\ny\ny\ny\ny", # apply all recommendations, one at a time - stderr=subprocess.PIPE, - encoding="utf-8", - shell=True, - ) - - # validate data was prepared successfully - assert prepared_data_cmd_output.stderr == "" - # validate get_long_indexes() applied during optional_fn() call in long_examples_validator() - assert "indices of the long examples has changed" in prepared_data_cmd_output.stdout - - return prepared_data_cmd_output.stdout diff --git a/openai/tests/test_url_composition.py b/openai/tests/test_url_composition.py deleted file mode 100644 index 5034354a05..0000000000 --- a/openai/tests/test_url_composition.py +++ /dev/null @@ -1,209 +0,0 @@ -from sys import api_version - -import pytest - -from openai import Completion, Engine -from openai.util import ApiType - - -@pytest.mark.url -def test_completions_url_composition_azure() -> None: - url = Completion.class_url("http://github.com/test_engine%22,%20%22azure%22,%20%222021-11-01-preview") - assert ( - url - == "/openai/deployments/test_engine/completions?api-version=2021-11-01-preview" - ) - - -@pytest.mark.url -def test_completions_url_composition_azure_ad() -> None: - url = Completion.class_url("http://github.com/test_engine%22,%20%22azure_ad%22,%20%222021-11-01-preview") - assert ( - url - == "/openai/deployments/test_engine/completions?api-version=2021-11-01-preview" - ) - - -@pytest.mark.url -def test_completions_url_composition_default() -> None: - url = Completion.class_url("http://github.com/test_engine") - assert url == "/engines/test_engine/completions" - - -@pytest.mark.url -def test_completions_url_composition_open_ai() -> None: - url = Completion.class_url("http://github.com/test_engine%22,%20%22open_ai") - assert url == "/engines/test_engine/completions" - - -@pytest.mark.url -def test_completions_url_composition_invalid_type() -> None: - with pytest.raises(Exception): - url = Completion.class_url("http://github.com/test_engine%22,%20%22invalid") - - -@pytest.mark.url -def test_completions_url_composition_instance_url_azure() -> None: - completion = Completion( - id="test_id", - engine="test_engine", - api_type="azure", - api_version="2021-11-01-preview", - ) - url = completion.instance_url() - assert ( - url - == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview" - ) - - -@pytest.mark.url -def test_completions_url_composition_instance_url_azure_ad() -> None: - completion = Completion( - id="test_id", - engine="test_engine", - api_type="azure_ad", - api_version="2021-11-01-preview", - ) - url = completion.instance_url() - assert ( - url - == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview" - ) - - -@pytest.mark.url -def test_completions_url_composition_instance_url_azure_no_version() -> None: - completion = Completion( - id="test_id", engine="test_engine", api_type="azure", api_version=None - ) - with pytest.raises(Exception): - completion.instance_url() - - -@pytest.mark.url -def test_completions_url_composition_instance_url_default() -> None: - completion = Completion(id="test_id", engine="test_engine") - url = completion.instance_url() - assert url == "/engines/test_engine/completions/test_id" - - -@pytest.mark.url -def test_completions_url_composition_instance_url_open_ai() -> None: - completion = Completion( - id="test_id", - engine="test_engine", - api_type="open_ai", - api_version="2021-11-01-preview", - ) - url = completion.instance_url() - assert url == "/engines/test_engine/completions/test_id" - - -@pytest.mark.url -def test_completions_url_composition_instance_url_invalid() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="invalid") - with pytest.raises(Exception): - url = completion.instance_url() - - -@pytest.mark.url -def test_completions_url_composition_instance_url_timeout_azure() -> None: - completion = Completion( - id="test_id", - engine="test_engine", - api_type="azure", - api_version="2021-11-01-preview", - ) - completion["timeout"] = 12 - url = completion.instance_url() - assert ( - url - == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview&timeout=12" - ) - - -@pytest.mark.url -def test_completions_url_composition_instance_url_timeout_openai() -> None: - completion = Completion(id="test_id", engine="test_engine", api_type="open_ai") - completion["timeout"] = 12 - url = completion.instance_url() - assert url == "/engines/test_engine/completions/test_id?timeout=12" - - -@pytest.mark.url -def test_engine_search_url_composition_azure() -> None: - engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") - assert engine.api_type == "azure" - assert engine.typed_api_type == ApiType.AZURE - url = engine.instance_url("http://github.com/test_operation") - assert ( - url - == "/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview" - ) - - -@pytest.mark.url -def test_engine_search_url_composition_azure_ad() -> None: - engine = Engine(id="test_id", api_type="azure_ad", api_version="2021-11-01-preview") - assert engine.api_type == "azure_ad" - assert engine.typed_api_type == ApiType.AZURE_AD - url = engine.instance_url("http://github.com/test_operation") - assert ( - url - == "/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview" - ) - - -@pytest.mark.url -def test_engine_search_url_composition_azure_no_version() -> None: - engine = Engine(id="test_id", api_type="azure", api_version=None) - assert engine.api_type == "azure" - assert engine.typed_api_type == ApiType.AZURE - with pytest.raises(Exception): - engine.instance_url("http://github.com/test_operation") - - -@pytest.mark.url -def test_engine_search_url_composition_azure_no_operation() -> None: - engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") - assert engine.api_type == "azure" - assert engine.typed_api_type == ApiType.AZURE - assert ( - engine.instance_url() - == "/openai/engines/test_id?api-version=2021-11-01-preview" - ) - - -@pytest.mark.url -def test_engine_search_url_composition_default() -> None: - engine = Engine(id="test_id") - assert engine.api_type == None - assert engine.typed_api_type == ApiType.OPEN_AI - url = engine.instance_url() - assert url == "/engines/test_id" - - -@pytest.mark.url -def test_engine_search_url_composition_open_ai() -> None: - engine = Engine(id="test_id", api_type="open_ai") - assert engine.api_type == "open_ai" - assert engine.typed_api_type == ApiType.OPEN_AI - url = engine.instance_url() - assert url == "/engines/test_id" - - -@pytest.mark.url -def test_engine_search_url_composition_invalid_type() -> None: - engine = Engine(id="test_id", api_type="invalid") - assert engine.api_type == "invalid" - with pytest.raises(Exception): - assert engine.typed_api_type == ApiType.OPEN_AI - - -@pytest.mark.url -def test_engine_search_url_composition_invalid_search() -> None: - engine = Engine(id="test_id", api_type="invalid") - assert engine.api_type == "invalid" - with pytest.raises(Exception): - engine.search() diff --git a/openai/tests/test_util.py b/openai/tests/test_util.py deleted file mode 100644 index 6220ccb7f4..0000000000 --- a/openai/tests/test_util.py +++ /dev/null @@ -1,55 +0,0 @@ -import json -from tempfile import NamedTemporaryFile - -import pytest - -import openai -from openai import util - - -@pytest.fixture(scope="function") -def api_key_file(): - saved_path = openai.api_key_path - try: - with NamedTemporaryFile(prefix="openai-api-key", mode="wt") as tmp: - openai.api_key_path = tmp.name - yield tmp - finally: - openai.api_key_path = saved_path - - -def test_openai_api_key_path(api_key_file) -> None: - print("sk-foo", file=api_key_file) - api_key_file.flush() - assert util.default_api_key() == "sk-foo" - - -def test_openai_api_key_path_with_malformed_key(api_key_file) -> None: - print("malformed-api-key", file=api_key_file) - api_key_file.flush() - with pytest.raises(ValueError, match="Malformed API key"): - util.default_api_key() - - -def test_key_order_openai_object_rendering() -> None: - sample_response = { - "id": "chatcmpl-7NaPEA6sgX7LnNPyKPbRlsyqLbr5V", - "object": "chat.completion", - "created": 1685855844, - "model": "gpt-3.5-turbo-0301", - "usage": {"prompt_tokens": 57, "completion_tokens": 40, "total_tokens": 97}, - "choices": [ - { - "message": { - "role": "assistant", - "content": "The 2020 World Series was played at Globe Life Field in Arlington, Texas. It was the first time that the World Series was played at a neutral site because of the COVID-19 pandemic.", - }, - "finish_reason": "stop", - "index": 0, - } - ], - } - - oai_object = util.convert_to_openai_object(sample_response) - # The `__str__` method was sorting while dumping to json - assert list(json.loads(str(oai_object)).keys()) == list(sample_response.keys()) diff --git a/openai/upload_progress.py b/openai/upload_progress.py deleted file mode 100644 index e4da62a4e0..0000000000 --- a/openai/upload_progress.py +++ /dev/null @@ -1,52 +0,0 @@ -import io - - -class CancelledError(Exception): - def __init__(self, msg): - self.msg = msg - Exception.__init__(self, msg) - - def __str__(self): - return self.msg - - __repr__ = __str__ - - -class BufferReader(io.BytesIO): - def __init__(self, buf=b"", desc=None): - self._len = len(buf) - io.BytesIO.__init__(self, buf) - self._progress = 0 - self._callback = progress(len(buf), desc=desc) - - def __len__(self): - return self._len - - def read(self, n=-1): - chunk = io.BytesIO.read(self, n) - self._progress += len(chunk) - if self._callback: - try: - self._callback(self._progress) - except Exception as e: # catches exception from the callback - raise CancelledError("The upload was cancelled: {}".format(e)) - return chunk - - -def progress(total, desc): - import tqdm # type: ignore - - meter = tqdm.tqdm(total=total, unit_scale=True, desc=desc) - - def incr(progress): - meter.n = progress - if progress == total: - meter.close() - else: - meter.refresh() - - return incr - - -def MB(i): - return int(i // 1024**2) diff --git a/openai/util.py b/openai/util.py deleted file mode 100644 index 5501d5b67e..0000000000 --- a/openai/util.py +++ /dev/null @@ -1,188 +0,0 @@ -import logging -import os -import re -import sys -from enum import Enum -from typing import Optional - -import openai - -OPENAI_LOG = os.environ.get("OPENAI_LOG") - -logger = logging.getLogger("openai") - -__all__ = [ - "log_info", - "log_debug", - "log_warn", - "logfmt", -] - -api_key_to_header = ( - lambda api, key: {"Authorization": f"Bearer {key}"} - if api in (ApiType.OPEN_AI, ApiType.AZURE_AD) - else {"api-key": f"{key}"} -) - - -class ApiType(Enum): - AZURE = 1 - OPEN_AI = 2 - AZURE_AD = 3 - - @staticmethod - def from_str(label): - if label.lower() == "azure": - return ApiType.AZURE - elif label.lower() in ("azure_ad", "azuread"): - return ApiType.AZURE_AD - elif label.lower() in ("open_ai", "openai"): - return ApiType.OPEN_AI - else: - raise openai.error.InvalidAPIType( - "The API type provided in invalid. Please select one of the supported API types: 'azure', 'azure_ad', 'open_ai'" - ) - - -def _console_log_level(): - if openai.log in ["debug", "info"]: - return openai.log - elif OPENAI_LOG in ["debug", "info"]: - return OPENAI_LOG - else: - return None - - -def log_debug(message, **params): - msg = logfmt(dict(message=message, **params)) - if _console_log_level() == "debug": - print(msg, file=sys.stderr) - logger.debug(msg) - - -def log_info(message, **params): - msg = logfmt(dict(message=message, **params)) - if _console_log_level() in ["debug", "info"]: - print(msg, file=sys.stderr) - logger.info(msg) - - -def log_warn(message, **params): - msg = logfmt(dict(message=message, **params)) - print(msg, file=sys.stderr) - logger.warn(msg) - - -def logfmt(props): - def fmt(key, val): - # Handle case where val is a bytes or bytesarray - if hasattr(val, "decode"): - val = val.decode("utf-8") - # Check if val is already a string to avoid re-encoding into ascii. - if not isinstance(val, str): - val = str(val) - if re.search(r"\s", val): - val = repr(val) - # key should already be a string - if re.search(r"\s", key): - key = repr(key) - return "{key}={val}".format(key=key, val=val) - - return " ".join([fmt(key, val) for key, val in sorted(props.items())]) - - -def get_object_classes(): - # This is here to avoid a circular dependency - from openai.object_classes import OBJECT_CLASSES - - return OBJECT_CLASSES - - -def convert_to_openai_object( - resp, - api_key=None, - api_version=None, - organization=None, - engine=None, - plain_old_data=False, -): - # If we get a OpenAIResponse, we'll want to return a OpenAIObject. - - response_ms: Optional[int] = None - if isinstance(resp, openai.openai_response.OpenAIResponse): - organization = resp.organization - response_ms = resp.response_ms - resp = resp.data - - if plain_old_data: - return resp - elif isinstance(resp, list): - return [ - convert_to_openai_object( - i, api_key, api_version, organization, engine=engine - ) - for i in resp - ] - elif isinstance(resp, dict) and not isinstance( - resp, openai.openai_object.OpenAIObject - ): - resp = resp.copy() - klass_name = resp.get("object") - if isinstance(klass_name, str): - klass = get_object_classes().get( - klass_name, openai.openai_object.OpenAIObject - ) - else: - klass = openai.openai_object.OpenAIObject - - return klass.construct_from( - resp, - api_key=api_key, - api_version=api_version, - organization=organization, - response_ms=response_ms, - engine=engine, - ) - else: - return resp - - -def convert_to_dict(obj): - """Converts a OpenAIObject back to a regular dict. - - Nested OpenAIObjects are also converted back to regular dicts. - - :param obj: The OpenAIObject to convert. - - :returns: The OpenAIObject as a dict. - """ - if isinstance(obj, list): - return [convert_to_dict(i) for i in obj] - # This works by virtue of the fact that OpenAIObjects _are_ dicts. The dict - # comprehension returns a regular dict and recursively applies the - # conversion to each value. - elif isinstance(obj, dict): - return {k: convert_to_dict(v) for k, v in obj.items()} - else: - return obj - - -def merge_dicts(x, y): - z = x.copy() - z.update(y) - return z - - -def default_api_key() -> str: - if openai.api_key_path: - with open(openai.api_key_path, "rt") as k: - api_key = k.read().strip() - if not api_key.startswith("sk-"): - raise ValueError(f"Malformed API key in {openai.api_key_path}.") - return api_key - elif openai.api_key is not None: - return openai.api_key - else: - raise openai.error.AuthenticationError( - "No API key provided. You can set your API key in code using 'openai.api_key = ', or you can set the environment variable OPENAI_API_KEY=). If your API key is stored in a file, you can point the openai module at it with 'openai.api_key_path = '. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details." - ) diff --git a/openai/version.py b/openai/version.py deleted file mode 100644 index 51f3ce82ff..0000000000 --- a/openai/version.py +++ /dev/null @@ -1 +0,0 @@ -VERSION = "0.27.9" diff --git a/openai/wandb_logger.py b/openai/wandb_logger.py deleted file mode 100644 index d8e060c41b..0000000000 --- a/openai/wandb_logger.py +++ /dev/null @@ -1,314 +0,0 @@ -try: - import wandb - - WANDB_AVAILABLE = True -except: - WANDB_AVAILABLE = False - - -if WANDB_AVAILABLE: - import datetime - import io - import json - import re - from pathlib import Path - - from openai import File, FineTune, FineTuningJob - from openai.datalib.numpy_helper import numpy as np - from openai.datalib.pandas_helper import assert_has_pandas, pandas as pd - - -class WandbLogger: - """ - Log fine-tunes to [Weights & Biases](https://wandb.me/openai-docs) - """ - - if not WANDB_AVAILABLE: - print("Logging requires wandb to be installed. Run `pip install wandb`.") - else: - _wandb_api = None - _logged_in = False - - @classmethod - def sync( - cls, - id=None, - n_fine_tunes=None, - project="OpenAI-Fine-Tune", - entity=None, - force=False, - legacy=False, - **kwargs_wandb_init, - ): - """ - Sync fine-tunes to Weights & Biases. - :param id: The id of the fine-tune (optional) - :param n_fine_tunes: Number of most recent fine-tunes to log when an id is not provided. By default, every fine-tune is synced. - :param project: Name of the project where you're sending runs. By default, it is "GPT-3". - :param entity: Username or team name where you're sending runs. By default, your default entity is used, which is usually your username. - :param force: Forces logging and overwrite existing wandb run of the same fine-tune. - """ - - assert_has_pandas() - - if not WANDB_AVAILABLE: - return - - if id: - print("Retrieving fine-tune job...") - if legacy: - fine_tune = FineTune.retrieve(id=id) - else: - fine_tune = FineTuningJob.retrieve(id=id) - fine_tune.pop("events", None) - fine_tunes = [fine_tune] - else: - # get list of fine_tune to log - if legacy: - fine_tunes = FineTune.list() - else: - fine_tunes = list(FineTuningJob.auto_paging_iter()) - if not fine_tunes or fine_tunes.get("data") is None: - print("No fine-tune has been retrieved") - return - fine_tunes = fine_tunes["data"][ - -n_fine_tunes if n_fine_tunes is not None else None : - ] - - # log starting from oldest fine_tune - show_individual_warnings = ( - False if id is None and n_fine_tunes is None else True - ) - fine_tune_logged = [ - cls._log_fine_tune( - fine_tune, - project, - entity, - force, - legacy, - show_individual_warnings, - **kwargs_wandb_init, - ) - for fine_tune in fine_tunes - ] - - if not show_individual_warnings and not any(fine_tune_logged): - print("No new successful fine-tunes were found") - - return "🎉 wandb sync completed successfully" - - @classmethod - def _log_fine_tune( - cls, - fine_tune, - project, - entity, - force, - legacy, - show_individual_warnings, - **kwargs_wandb_init, - ): - fine_tune_id = fine_tune.get("id") - status = fine_tune.get("status") - - # check run completed successfully - if status != "succeeded": - if show_individual_warnings: - print( - f'Fine-tune {fine_tune_id} has the status "{status}" and will not be logged' - ) - return - - # check results are present - try: - if legacy: - results_id = fine_tune["result_files"][0]["id"] - else: - results_id = fine_tune["result_files"][0] - results = File.download(id=results_id).decode("utf-8") - except: - if show_individual_warnings: - print(f"Fine-tune {fine_tune_id} has no results and will not be logged") - return - - # check run has not been logged already - run_path = f"{project}/{fine_tune_id}" - if entity is not None: - run_path = f"{entity}/{run_path}" - wandb_run = cls._get_wandb_run(run_path) - if wandb_run: - wandb_status = wandb_run.summary.get("status") - if show_individual_warnings: - if wandb_status == "succeeded": - print( - f"Fine-tune {fine_tune_id} has already been logged successfully at {wandb_run.url}" - ) - if not force: - print( - 'Use "--force" in the CLI or "force=True" in python if you want to overwrite previous run' - ) - else: - print( - f"A run for fine-tune {fine_tune_id} was previously created but didn't end successfully" - ) - if wandb_status != "succeeded" or force: - print( - f"A new wandb run will be created for fine-tune {fine_tune_id} and previous run will be overwritten" - ) - if wandb_status == "succeeded" and not force: - return - - # start a wandb run - wandb.init( - job_type="fine-tune", - config=cls._get_config(fine_tune), - project=project, - entity=entity, - name=fine_tune_id, - id=fine_tune_id, - **kwargs_wandb_init, - ) - - # log results - df_results = pd.read_csv(io.StringIO(results)) - for _, row in df_results.iterrows(): - metrics = {k: v for k, v in row.items() if not np.isnan(v)} - step = metrics.pop("step") - if step is not None: - step = int(step) - wandb.log(metrics, step=step) - fine_tuned_model = fine_tune.get("fine_tuned_model") - if fine_tuned_model is not None: - wandb.summary["fine_tuned_model"] = fine_tuned_model - - # training/validation files and fine-tune details - cls._log_artifacts(fine_tune, project, entity) - - # mark run as complete - wandb.summary["status"] = "succeeded" - - wandb.finish() - return True - - @classmethod - def _ensure_logged_in(cls): - if not cls._logged_in: - if wandb.login(): - cls._logged_in = True - else: - raise Exception("You need to log in to wandb") - - @classmethod - def _get_wandb_run(cls, run_path): - cls._ensure_logged_in() - try: - if cls._wandb_api is None: - cls._wandb_api = wandb.Api() - return cls._wandb_api.run(run_path) - except Exception: - return None - - @classmethod - def _get_wandb_artifact(cls, artifact_path): - cls._ensure_logged_in() - try: - if cls._wandb_api is None: - cls._wandb_api = wandb.Api() - return cls._wandb_api.artifact(artifact_path) - except Exception: - return None - - @classmethod - def _get_config(cls, fine_tune): - config = dict(fine_tune) - for key in ("training_files", "validation_files", "result_files"): - if config.get(key) and len(config[key]): - config[key] = config[key][0] - if config.get("created_at"): - config["created_at"] = datetime.datetime.fromtimestamp(config["created_at"]) - return config - - @classmethod - def _log_artifacts(cls, fine_tune, project, entity): - # training/validation files - training_file = ( - fine_tune["training_files"][0] - if fine_tune.get("training_files") and len(fine_tune["training_files"]) - else None - ) - validation_file = ( - fine_tune["validation_files"][0] - if fine_tune.get("validation_files") and len(fine_tune["validation_files"]) - else None - ) - for file, prefix, artifact_type in ( - (training_file, "train", "training_files"), - (validation_file, "valid", "validation_files"), - ): - if file is not None: - cls._log_artifact_inputs(file, prefix, artifact_type, project, entity) - - # fine-tune details - fine_tune_id = fine_tune.get("id") - artifact = wandb.Artifact( - "fine_tune_details", - type="fine_tune_details", - metadata=fine_tune, - ) - with artifact.new_file( - "fine_tune_details.json", mode="w", encoding="utf-8" - ) as f: - json.dump(fine_tune, f, indent=2) - wandb.run.log_artifact( - artifact, - aliases=["latest", fine_tune_id], - ) - - @classmethod - def _log_artifact_inputs(cls, file, prefix, artifact_type, project, entity): - file_id = file["id"] - filename = Path(file["filename"]).name - stem = Path(file["filename"]).stem - - # get input artifact - artifact_name = f"{prefix}-{filename}" - # sanitize name to valid wandb artifact name - artifact_name = re.sub(r"[^a-zA-Z0-9_\-.]", "_", artifact_name) - artifact_alias = file_id - artifact_path = f"{project}/{artifact_name}:{artifact_alias}" - if entity is not None: - artifact_path = f"{entity}/{artifact_path}" - artifact = cls._get_wandb_artifact(artifact_path) - - # create artifact if file not already logged previously - if artifact is None: - # get file content - try: - file_content = File.download(id=file_id).decode("utf-8") - except: - print( - f"File {file_id} could not be retrieved. Make sure you are allowed to download training/validation files" - ) - return - artifact = wandb.Artifact(artifact_name, type=artifact_type, metadata=file) - with artifact.new_file(filename, mode="w", encoding="utf-8") as f: - f.write(file_content) - - # create a Table - try: - table, n_items = cls._make_table(file_content) - artifact.add(table, stem) - wandb.config.update({f"n_{prefix}": n_items}) - artifact.metadata["items"] = n_items - except: - print(f"File {file_id} could not be read as a valid JSON file") - else: - # log number of items - wandb.config.update({f"n_{prefix}": artifact.metadata.get("items")}) - - wandb.run.use_artifact(artifact, aliases=["latest", artifact_alias]) - - @classmethod - def _make_table(cls, file_content): - df = pd.read_json(io.StringIO(file_content), orient="records", lines=True) - return wandb.Table(dataframe=df), len(df) diff --git a/public/Makefile b/public/Makefile deleted file mode 100644 index 2862fd4261..0000000000 --- a/public/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -.PHONY: build upload - -build: - OPENAI_UPLOAD=y python setup.py sdist - -upload: - OPENAI_UPLOAD=y twine upload dist/* diff --git a/public/setup.py b/public/setup.py deleted file mode 100644 index 0198a53361..0000000000 --- a/public/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -import os - -from setuptools import setup - -if os.getenv("OPENAI_UPLOAD") != "y": - raise RuntimeError( - "This package is a placeholder package on the public PyPI instance, and is not the correct version to install. If you are having trouble figuring out the correct package to install, please contact us." - ) - -setup(name="openai", description="Placeholder package", version="0.0.1") diff --git a/pyproject.toml b/pyproject.toml index 6116c7fa2f..7f6e3123d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,160 @@ +[project] +name = "openai" +version = "1.0.0" +description = "Client library for the openai API" +readme = "README.md" +license = "Apache-2.0" +authors = [ +{ name = "OpenAI", email = "support@openai.com" }, +] +dependencies = [ + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", + "typing-extensions>=4.5, <5", + "anyio>=3.5.0, <4", + "distro>=1.7.0, <2", + "tqdm > 4" +] +requires-python = ">= 3.7.1" +classifiers = [ + "Typing :: Typed", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", +] + +[project.optional-dependencies] +datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] + +[project.urls] +Homepage = "https://github.com/openai/openai-python" +Repository = "https://github.com/openai/openai-python" + +[project.scripts] +openai = "openai.cli:main" + +[tool.rye] +managed = true +dev-dependencies = [ + "pyright==1.1.332", + "mypy==1.6.1", + "black==23.3.0", + "respx==0.19.2", + "pytest==7.1.1", + "pytest-asyncio==0.21.1", + "ruff==0.0.282", + "isort==5.10.1", + "time-machine==2.9.0", + "nox==2023.4.22", + "dirty-equals>=0.6.0", + "azure-identity >=1.14.1", + "types-tqdm > 4" +] + +[tool.rye.scripts] +format = { chain = [ + "format:black", + "format:docs", + "format:ruff", + "format:isort", +]} +"format:black" = "black ." +"format:docs" = "python bin/blacken-docs.py README.md api.md" +"format:ruff" = "ruff --fix ." +"format:isort" = "isort ." + +"check:ruff" = "ruff ." + +typecheck = { chain = [ + "typecheck:pyright", + "typecheck:mypy" +]} +"typecheck:pyright" = "pyright" +"typecheck:verify-types" = "pyright --verifytypes openai --ignoreexternal" +"typecheck:mypy" = "mypy --enable-incomplete-feature=Unpack ." + [build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build] +include = [ + "src/*" +] + +[tool.hatch.build.targets.wheel] +packages = ["src/openai"] [tool.black] -target-version = ['py36'] -exclude = '.*\.ipynb' +line-length = 120 +target-version = ["py37"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--tb=short" +xfail_strict = true +asyncio_mode = "auto" +filterwarnings = [ + "error" +] + +[tool.pyright] +# this enables practically every flag given by pyright. +# there are a couple of flags that are still disabled by +# default in strict mode as they are experimental and niche. +typeCheckingMode = "strict" +pythonVersion = "3.7" + +exclude = [ + "_dev", + ".venv", + ".nox", +] + +reportImplicitOverride = true + +reportImportCycles = false +reportPrivateUsage = false [tool.isort] -py_version = 36 -include_trailing_comma = "true" -line_length = 88 -multi_line_output = 3 +profile = "black" +length_sort = true +extra_standard_library = ["typing_extensions"] + +[tool.ruff] +line-length = 120 +format = "grouped" +target-version = "py37" +select = [ + # remove unused imports + "F401", + # bare except statements + "E722", + # unused arguments + "ARG", + # print statements + "T201", + "T203", +] +unfixable = [ + # disable auto fix for print statements + "T201", + "T203", +] +ignore-init-module-imports = true + + +[tool.ruff.per-file-ignores] +"bin/**.py" = ["T201", "T203"] +"tests/**.py" = ["T201", "T203"] +"examples/**.py" = ["T201", "T203"] diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 5b78d87c16..0000000000 --- a/pytest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[pytest] -markers = - url: mark a test as part of the url composition tests. - requestor: mark test as part of the api_requestor tests. diff --git a/requirements-dev.lock b/requirements-dev.lock new file mode 100644 index 0000000000..0747babdc5 --- /dev/null +++ b/requirements-dev.lock @@ -0,0 +1,74 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: true + +-e file:. +annotated-types==0.6.0 +anyio==3.7.1 +argcomplete==3.1.2 +attrs==23.1.0 +azure-core==1.29.5 +azure-identity==1.15.0 +black==23.3.0 +certifi==2023.7.22 +cffi==1.16.0 +charset-normalizer==3.3.1 +click==8.1.7 +colorlog==6.7.0 +cryptography==41.0.5 +dirty-equals==0.6.0 +distlib==0.3.7 +distro==1.8.0 +exceptiongroup==1.1.3 +filelock==3.12.4 +h11==0.12.0 +httpcore==0.15.0 +httpx==0.23.0 +idna==3.4 +iniconfig==2.0.0 +isort==5.10.1 +msal==1.24.1 +msal-extensions==1.0.0 +mypy==1.6.1 +mypy-extensions==1.0.0 +nodeenv==1.8.0 +nox==2023.4.22 +numpy==1.26.1 +packaging==23.2 +pandas==2.1.1 +pandas-stubs==2.1.1.230928 +pathspec==0.11.2 +platformdirs==3.11.0 +pluggy==1.3.0 +portalocker==2.8.2 +py==1.11.0 +pycparser==2.21 +pydantic==2.4.2 +pydantic-core==2.10.1 +pyjwt==2.8.0 +pyright==1.1.332 +pytest==7.1.1 +pytest-asyncio==0.21.1 +python-dateutil==2.8.2 +pytz==2023.3.post1 +requests==2.31.0 +respx==0.19.2 +rfc3986==1.5.0 +ruff==0.0.282 +six==1.16.0 +sniffio==1.3.0 +time-machine==2.9.0 +tomli==2.0.1 +tqdm==4.66.1 +types-pytz==2023.3.1.1 +types-tqdm==4.66.0.2 +typing-extensions==4.8.0 +tzdata==2023.3 +urllib3==2.0.7 +virtualenv==20.24.5 +# The following packages are considered to be unsafe in a requirements file: +setuptools==68.2.2 diff --git a/requirements.lock b/requirements.lock new file mode 100644 index 0000000000..be9606fc3c --- /dev/null +++ b/requirements.lock @@ -0,0 +1,32 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: true + +-e file:. +annotated-types==0.6.0 +anyio==3.7.1 +certifi==2023.7.22 +distro==1.8.0 +exceptiongroup==1.1.3 +h11==0.12.0 +httpcore==0.15.0 +httpx==0.23.0 +idna==3.4 +numpy==1.26.1 +pandas==2.1.1 +pandas-stubs==2.1.1.230928 +pydantic==2.4.2 +pydantic-core==2.10.1 +python-dateutil==2.8.2 +pytz==2023.3.post1 +rfc3986==1.5.0 +six==1.16.0 +sniffio==1.3.0 +tqdm==4.66.1 +types-pytz==2023.3.1.1 +typing-extensions==4.8.0 +tzdata==2023.3 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3729647b8d..0000000000 --- a/setup.cfg +++ /dev/null @@ -1,65 +0,0 @@ -[metadata] -name = openai -version = attr: openai.version.VERSION -description = Python client library for the OpenAI API -long_description = file: README.md -long_description_content_type = text/markdown -author = OpenAI -author_email = support@openai.com -url = https://github.com/openai/openai-python -license_files = LICENSE -classifiers = - Programming Language :: Python :: 3 - License :: OSI Approved :: MIT License - Operating System :: OS Independent - -[options] -packages = find: -python_requires = >=3.7.1 -zip_safe = True -include_package_data = True -install_requires = - requests >= 2.20 # to get the patch for CVE-2018-18074 - tqdm # Needed for progress bars - typing_extensions; python_version<"3.8" # Needed for type hints for mypy - aiohttp # Needed for async support - -[options.extras_require] -dev = - black ~= 21.6b0 - pytest == 6.* - pytest-asyncio - pytest-mock -datalib = - numpy - pandas >= 1.2.3 # Needed for CLI fine-tuning data preparation tool - pandas-stubs >= 1.1.0.11 # Needed for type hints for mypy - openpyxl >= 3.0.7 # Needed for CLI fine-tuning data preparation tool xlsx format -wandb = - wandb - numpy - pandas >= 1.2.3 # Needed for CLI fine-tuning data preparation tool - pandas-stubs >= 1.1.0.11 # Needed for type hints for mypy - openpyxl >= 3.0.7 # Needed for CLI fine-tuning data preparation tool xlsx format -embeddings = - scikit-learn >= 1.0.2 # Needed for embedding utils, versions >= 1.1 require python 3.8 - tenacity >= 8.0.1 - matplotlib - plotly - numpy - scipy - pandas >= 1.2.3 # Needed for CLI fine-tuning data preparation tool - pandas-stubs >= 1.1.0.11 # Needed for type hints for mypy - openpyxl >= 3.0.7 # Needed for CLI fine-tuning data preparation tool xlsx format - -[options.entry_points] -console_scripts = - openai = openai._openai_scripts:main - -[options.package_data] - openai = py.typed - -[options.packages.find] -exclude = - tests - tests.* diff --git a/setup.py b/setup.py deleted file mode 100644 index 606849326a..0000000000 --- a/setup.py +++ /dev/null @@ -1,3 +0,0 @@ -from setuptools import setup - -setup() diff --git a/src/openai/__init__.py b/src/openai/__init__.py new file mode 100644 index 0000000000..f033d8f26c --- /dev/null +++ b/src/openai/__init__.py @@ -0,0 +1,342 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os as _os +from typing_extensions import override + +from . import types +from ._types import NoneType, Transport, ProxiesTypes +from ._utils import file_from_path +from ._client import ( + Client, + OpenAI, + Stream, + Timeout, + Transport, + AsyncClient, + AsyncOpenAI, + AsyncStream, + RequestOptions, +) +from ._version import __title__, __version__ +from ._exceptions import ( + APIError, + OpenAIError, + ConflictError, + NotFoundError, + APIStatusError, + RateLimitError, + APITimeoutError, + BadRequestError, + APIConnectionError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + UnprocessableEntityError, + APIResponseValidationError, +) +from ._utils._logs import setup_logging as _setup_logging + +__all__ = [ + "types", + "__version__", + "__title__", + "NoneType", + "Transport", + "ProxiesTypes", + "OpenAIError", + "APIError", + "APIStatusError", + "APITimeoutError", + "APIConnectionError", + "APIResponseValidationError", + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", + "Timeout", + "RequestOptions", + "Client", + "AsyncClient", + "Stream", + "AsyncStream", + "OpenAI", + "AsyncOpenAI", + "file_from_path", +] + +from .lib import azure as _azure +from .version import VERSION as VERSION +from .lib.azure import AzureOpenAI as AzureOpenAI +from .lib.azure import AsyncAzureOpenAI as AsyncAzureOpenAI + +_setup_logging() + +# Update the __module__ attribute for exported symbols so that +# error messages point to this module instead of the module +# it was originally defined in, e.g. +# openai._exceptions.NotFoundError -> openai.NotFoundError +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + try: + setattr(__locals[__name], "__module__", "openai") + except (TypeError, AttributeError): + # Some of our exported symbols are builtins which we can't set attributes for. + pass + +# ------ Module level client ------ +import typing as _t +import typing_extensions as _te + +import httpx as _httpx + +from ._base_client import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES + +api_key: str | None = None + +organization: str | None = None + +base_url: str | _httpx.URL | None = None + +timeout: float | Timeout | None = DEFAULT_TIMEOUT + +max_retries: int = DEFAULT_MAX_RETRIES + +default_headers: _t.Mapping[str, str] | None = None + +default_query: _t.Mapping[str, object] | None = None + +http_client: _httpx.Client | None = None + +_ApiType = _te.Literal["openai", "azure"] + +api_type: _ApiType | None = _t.cast(_ApiType, _os.environ.get("OPENAI_API_TYPE")) + +api_version: str | None = _os.environ.get("OPENAI_API_VERSION") + +azure_endpoint: str | None = _os.environ.get("AZURE_OPENAI_ENDPOINT") + +azure_ad_token: str | None = _os.environ.get("AZURE_OPENAI_AD_TOKEN") + +azure_ad_token_provider: _azure.AzureADTokenProvider | None = None + + +class _ModuleClient(OpenAI): + # Note: we have to use type: ignores here as overriding class members + # with properties is technically unsafe but it is fine for our use case + + @property # type: ignore + @override + def api_key(self) -> str | None: + return api_key + + @api_key.setter # type: ignore + def api_key(self, value: str | None) -> None: # type: ignore + global api_key + + api_key = value + + @property # type: ignore + @override + def organization(self) -> str | None: + return organization + + @organization.setter # type: ignore + def organization(self, value: str | None) -> None: # type: ignore + global organization + + organization = value + + @property + @override + def base_url(http://github.com/self) -> _httpx.URL: + if base_url is not None: + return _httpx.URL(base_url) + + return super().base_url + + @base_url.setter + def base_url(http://github.com/self,%20url:%20_httpx.URL%20|%20str) -> None: + super().base_url = url # type: ignore[misc] + + @property # type: ignore + @override + def timeout(self) -> float | Timeout | None: + return timeout + + @timeout.setter # type: ignore + def timeout(self, value: float | Timeout | None) -> None: # type: ignore + global timeout + + timeout = value + + @property # type: ignore + @override + def max_retries(self) -> int: + return max_retries + + @max_retries.setter # type: ignore + def max_retries(self, value: int) -> None: # type: ignore + global max_retries + + max_retries = value + + @property # type: ignore + @override + def _custom_headers(self) -> _t.Mapping[str, str] | None: + return default_headers + + @_custom_headers.setter # type: ignore + def _custom_headers(self, value: _t.Mapping[str, str] | None) -> None: # type: ignore + global default_headers + + default_headers = value + + @property # type: ignore + @override + def _custom_query(self) -> _t.Mapping[str, object] | None: + return default_query + + @_custom_query.setter # type: ignore + def _custom_query(self, value: _t.Mapping[str, object] | None) -> None: # type: ignore + global default_query + + default_query = value + + @property # type: ignore + @override + def _client(self) -> _httpx.Client: + return http_client or super()._client + + @_client.setter # type: ignore + def _client(self, value: _httpx.Client) -> None: # type: ignore + global http_client + + http_client = value + + @override + def __del__(self) -> None: + try: + super().__del__() + except Exception: + pass + + +class _AzureModuleClient(_ModuleClient, AzureOpenAI): # type: ignore + ... + + +class _AmbiguousModuleClientUsageError(OpenAIError): + def __init__(self) -> None: + super().__init__( + "Ambiguous use of module client; please set `openai.api_type` or the `OPENAI_API_TYPE` environment variable to `openai` or `azure`" + ) + + +def _has_openai_credentials() -> bool: + return _os.environ.get("OPENAI_API_KEY") is not None + + +def _has_azure_credentials() -> bool: + return azure_endpoint is not None or _os.environ.get("AZURE_OPENAI_API_KEY") is not None + + +def _has_azure_ad_credentials() -> bool: + return ( + _os.environ.get("AZURE_OPENAI_AD_TOKEN") is not None + or azure_ad_token is not None + or azure_ad_token_provider is not None + ) + + +_client: OpenAI | None = None + + +def _load_client() -> OpenAI: # type: ignore[reportUnusedFunction] + global _client + + if _client is None: + global api_type, azure_endpoint, azure_ad_token, api_version + + if azure_endpoint is None: + azure_endpoint = _os.environ.get("AZURE_OPENAI_ENDPOINT") + + if azure_ad_token is None: + azure_ad_token = _os.environ.get("AZURE_OPENAI_AD_TOKEN") + + if api_version is None: + api_version = _os.environ.get("OPENAI_API_VERSION") + + if api_type is None: + has_openai = _has_openai_credentials() + has_azure = _has_azure_credentials() + has_azure_ad = _has_azure_ad_credentials() + + if has_openai and (has_azure or has_azure_ad): + raise _AmbiguousModuleClientUsageError() + + if (azure_ad_token is not None or azure_ad_token_provider is not None) and _os.environ.get( + "AZURE_OPENAI_API_KEY" + ) is not None: + raise _AmbiguousModuleClientUsageError() + + if has_azure or has_azure_ad: + api_type = "azure" + else: + api_type = "openai" + + if api_type == "azure": + _client = _AzureModuleClient( # type: ignore + api_version=api_version, + azure_endpoint=azure_endpoint, + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + organization=organization, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + ) + return _client + + _client = _ModuleClient( + api_key=api_key, + organization=organization, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + ) + return _client + + return _client + + +def _reset_client() -> None: # type: ignore[reportUnusedFunction] + global _client + + _client = None + + +from ._module_client import chat as chat +from ._module_client import audio as audio +from ._module_client import edits as edits +from ._module_client import files as files +from ._module_client import images as images +from ._module_client import models as models +from ._module_client import embeddings as embeddings +from ._module_client import fine_tunes as fine_tunes +from ._module_client import completions as completions +from ._module_client import fine_tuning as fine_tuning +from ._module_client import moderations as moderations diff --git a/src/openai/__main__.py b/src/openai/__main__.py new file mode 100644 index 0000000000..4e28416e10 --- /dev/null +++ b/src/openai/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py new file mode 100644 index 0000000000..22f90050d7 --- /dev/null +++ b/src/openai/_base_client.py @@ -0,0 +1,1768 @@ +from __future__ import annotations + +import os +import json +import time +import uuid +import email +import inspect +import logging +import platform +import warnings +import email.utils +from types import TracebackType +from random import random +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Type, + Union, + Generic, + Mapping, + TypeVar, + Iterable, + Iterator, + Optional, + Generator, + AsyncIterator, + cast, + overload, +) +from functools import lru_cache +from typing_extensions import Literal, override + +import anyio +import httpx +import distro +import pydantic +from httpx import URL, Limits +from pydantic import PrivateAttr + +from . import _exceptions +from ._qs import Querystring +from ._files import to_httpx_files, async_to_httpx_files +from ._types import ( + NOT_GIVEN, + Body, + Omit, + Query, + ModelT, + Headers, + Timeout, + NotGiven, + ResponseT, + Transport, + AnyMapping, + PostParser, + ProxiesTypes, + RequestFiles, + AsyncTransport, + RequestOptions, + UnknownResponse, + ModelBuilderProtocol, + BinaryResponseContent, +) +from ._utils import is_dict, is_given, is_mapping +from ._compat import model_copy, model_dump +from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type +from ._response import APIResponse +from ._constants import ( + DEFAULT_LIMITS, + DEFAULT_TIMEOUT, + DEFAULT_MAX_RETRIES, + RAW_RESPONSE_HEADER, +) +from ._streaming import Stream, AsyncStream +from ._exceptions import APIStatusError, APITimeoutError, APIConnectionError + +log: logging.Logger = logging.getLogger(__name__) + +# TODO: make base page type vars covariant +SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") +AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") + + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) + +_StreamT = TypeVar("_StreamT", bound=Stream[Any]) +_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) + +if TYPE_CHECKING: + from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT +else: + try: + from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + except ImportError: + # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 + HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) + + +class PageInfo: + """Stores the necesary information to build the request to retrieve the next page. + + Either `url` or `params` must be set. + """ + + url: URL | NotGiven + params: Query | NotGiven + + @overload + def __init__( + self, + *, + url: URL, + ) -> None: + ... + + @overload + def __init__( + self, + *, + params: Query, + ) -> None: + ... + + def __init__( + self, + *, + url: URL | NotGiven = NOT_GIVEN, + params: Query | NotGiven = NOT_GIVEN, + ) -> None: + self.url = url + self.params = params + + +class BasePage(GenericModel, Generic[ModelT]): + """ + Defines the core interface for pagination. + + Type Args: + ModelT: The pydantic model that represents an item in the response. + + Methods: + has_next_page(): Check if there is another page available + next_page_info(): Get the necessary information to make a request for the next page + """ + + _options: FinalRequestOptions = PrivateAttr() + _model: Type[ModelT] = PrivateAttr() + + def has_next_page(self) -> bool: + items = self._get_page_items() + if not items: + return False + return self.next_page_info() is not None + + def next_page_info(self) -> Optional[PageInfo]: + ... + + def _get_page_items(self) -> Iterable[ModelT]: # type: ignore[empty-body] + ... + + def _params_from_url(http://github.com/self,%20url:%20URL) -> httpx.QueryParams: + # TODO: do we have to preprocess params here? + return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params) + + def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: + options = model_copy(self._options) + options._strip_raw_response_header() + + if not isinstance(info.params, NotGiven): + options.params = {**options.params, **info.params} + return options + + if not isinstance(info.url, NotGiven): + params = self._params_from_url(http://github.com/info.url) + url = info.url.copy_with(params=params) + options.params = dict(url.params) + options.url = str(url) + return options + + raise ValueError("Unexpected PageInfo state") + + +class BaseSyncPage(BasePage[ModelT], Generic[ModelT]): + _client: SyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + client: SyncAPIClient, + model: Type[ModelT], + options: FinalRequestOptions, + ) -> None: + self._model = model + self._client = client + self._options = options + + # Pydantic uses a custom `__iter__` method to support casting BaseModels + # to dictionaries. e.g. dict(model). + # As we want to support `for item in page`, this is inherently incompatible + # with the default pydantic behaviour. It is not possible to support both + # use cases at once. Fortunately, this is not a big deal as all other pydantic + # methods should continue to work as expected as there is an alternative method + # to cast a model to a dictionary, model.dict(), which is used internally + # by pydantic. + def __iter__(self) -> Iterator[ModelT]: # type: ignore + for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = page.get_next_page() + else: + return + + def get_next_page(self: SyncPageT) -> SyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return self._client._request_api_list(self._model, page=self.__class__, options=options) + + +class AsyncPaginator(Generic[ModelT, AsyncPageT]): + def __init__( + self, + client: AsyncAPIClient, + options: FinalRequestOptions, + page_cls: Type[AsyncPageT], + model: Type[ModelT], + ) -> None: + self._model = model + self._client = client + self._options = options + self._page_cls = page_cls + + def __await__(self) -> Generator[Any, None, AsyncPageT]: + return self._get_page().__await__() + + async def _get_page(self) -> AsyncPageT: + def _parser(resp: AsyncPageT) -> AsyncPageT: + resp._set_private_attributes( + model=self._model, + options=self._options, + client=self._client, + ) + return resp + + self._options.post_parser = _parser + + return await self._client.request(self._page_cls, self._options) + + async def __aiter__(self) -> AsyncIterator[ModelT]: + # https://github.com/microsoft/pyright/issues/3464 + page = cast( + AsyncPageT, + await self, # type: ignore + ) + async for item in page: + yield item + + +class BaseAsyncPage(BasePage[ModelT], Generic[ModelT]): + _client: AsyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + model: Type[ModelT], + client: AsyncAPIClient, + options: FinalRequestOptions, + ) -> None: + self._model = model + self._client = client + self._options = options + + async def __aiter__(self) -> AsyncIterator[ModelT]: + async for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = await page.get_next_page() + else: + return + + async def get_next_page(self: AsyncPageT) -> AsyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return await self._client._request_api_list(self._model, page=self.__class__, options=options) + + +_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) +_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) + + +class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): + _client: _HttpxClientT + _version: str + _base_url: URL + max_retries: int + timeout: Union[float, Timeout, None] + _limits: httpx.Limits + _proxies: ProxiesTypes | None + _transport: Transport | AsyncTransport | None + _strict_response_validation: bool + _idempotency_header: str | None + _default_stream_cls: type[_DefaultStreamT] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None = DEFAULT_TIMEOUT, + limits: httpx.Limits, + transport: Transport | AsyncTransport | None, + proxies: ProxiesTypes | None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + self._version = version + self._base_url = self._enforce_trailing_slash(URL(base_url)) + self.max_retries = max_retries + self.timeout = timeout + self._limits = limits + self._proxies = proxies + self._transport = transport + self._custom_headers = custom_headers or {} + self._custom_query = custom_query or {} + self._strict_response_validation = _strict_response_validation + self._idempotency_header = None + + def _enforce_trailing_slash(self, url: URL) -> URL: + if url.raw_path.endswith(b"/"): + return url + return url.copy_with(raw_path=url.raw_path + b"/") + + def _make_status_error_from_response( + self, + response: httpx.Response, + ) -> APIStatusError: + err_text = response.text.strip() + body = err_text + + try: + body = json.loads(err_text) + err_msg = f"Error code: {response.status_code} - {body}" + except Exception: + err_msg = err_text or f"Error code: {response.status_code}" + + return self._make_status_error(err_msg, body=body, response=response) + + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> _exceptions.APIStatusError: + raise NotImplementedError() + + def _remaining_retries( + self, + remaining_retries: Optional[int], + options: FinalRequestOptions, + ) -> int: + return remaining_retries if remaining_retries is not None else options.get_max_retries(self.max_retries) + + def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers: + custom_headers = options.headers or {} + headers_dict = _merge_mappings(self.default_headers, custom_headers) + self._validate_headers(headers_dict, custom_headers) + + headers = httpx.Headers(headers_dict) + + idempotency_header = self._idempotency_header + if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: + if not options.idempotency_key: + options.idempotency_key = self._idempotency_key() + + headers[idempotency_header] = options.idempotency_key + + return headers + + def _prepare_url(http://github.com/self,%20url:%20str) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + # Copied from httpx's `_merge_url` method. + merge_url = URL(url) + if merge_url.is_relative_url: + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + + return merge_url + + def _build_request( + self, + options: FinalRequestOptions, + ) -> httpx.Request: + if log.isEnabledFor(logging.DEBUG): + log.debug("Request options: %s", model_dump(options, exclude_unset=True)) + + kwargs: dict[str, Any] = {} + + json_data = options.json_data + if options.extra_json is not None: + if json_data is None: + json_data = cast(Body, options.extra_json) + elif is_mapping(json_data): + json_data = _merge_mappings(json_data, options.extra_json) + else: + raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") + + headers = self._build_headers(options) + params = _merge_mappings(self._custom_query, options.params) + + # If the given Content-Type header is multipart/form-data then it + # has to be removed so that httpx can generate the header with + # additional information for us as it has to be in this form + # for the server to be able to correctly parse the request: + # multipart/form-data; boundary=---abc-- + if headers.get("Content-Type") == "multipart/form-data": + headers.pop("Content-Type") + + # As we are now sending multipart/form-data instead of application/json + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding + if json_data: + if not is_dict(json_data): + raise TypeError( + f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." + ) + kwargs["data"] = self._serialize_multipartform(json_data) + + # TODO: report this error to httpx + return self._client.build_request( # pyright: ignore[reportUnknownMemberType] + headers=headers, + timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, + method=options.method, + url=self._prepare_url(http://github.com/options.url), + # the `Query` type that we use is incompatible with qs' + # `Params` type as it needs to be typed as `Mapping[str, object]` + # so that passing a `TypedDict` doesn't cause an error. + # https://github.com/microsoft/pyright/issues/3526#event-6715453066 + params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, + json=json_data, + files=options.files, + **kwargs, + ) + + def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: + items = self.qs.stringify_items( + # TODO: type ignore is required as stringify_items is well typed but we can't be + # well typed without heavy validation. + data, # type: ignore + array_format="brackets", + ) + serialized: dict[str, object] = {} + for key, value in items: + if key in serialized: + raise ValueError(f"Duplicate key encountered: {key}; This behaviour is not supported") + serialized[key] = value + return serialized + + def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + ) -> ResponseT: + api_response = APIResponse( + raw=response, + client=self, + cast_to=cast_to, + stream=stream, + stream_cls=stream_cls, + options=options, + ) + + if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": + return cast(ResponseT, api_response) + + return api_response.parse() + + def _process_response_data( + self, + *, + data: object, + cast_to: type[ResponseT], + response: httpx.Response, + ) -> ResponseT: + if data is None: + return cast(ResponseT, None) + + if cast_to is UnknownResponse: + return cast(ResponseT, data) + + if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): + return cast(ResponseT, cast_to.build(response=response, data=data)) + + if self._strict_response_validation: + return cast(ResponseT, validate_type(type_=cast_to, value=data)) + + return cast(ResponseT, construct_type(type_=cast_to, value=data)) + + @property + def qs(self) -> Querystring: + return Querystring() + + @property + def custom_auth(self) -> httpx.Auth | None: + return None + + @property + def auth_headers(self) -> dict[str, str]: + return {} + + @property + def default_headers(self) -> dict[str, str | Omit]: + return { + "Accept": "application/json", + "Content-Type": "application/json", + "User-Agent": self.user_agent, + **self.platform_headers(), + **self.auth_headers, + **self._custom_headers, + } + + def _validate_headers( + self, + headers: Headers, # noqa: ARG002 + custom_headers: Headers, # noqa: ARG002 + ) -> None: + """Validate the given default headers and custom headers. + + Does nothing by default. + """ + return + + @property + def user_agent(self) -> str: + return f"{self.__class__.__name__}/Python {self._version}" + + @property + def base_url(http://github.com/self) -> URL: + return self._base_url + + @base_url.setter + def base_url(http://github.com/self,%20url:%20URL%20|%20str) -> None: + self._client.base_url = url if isinstance(url, URL) else URL(url) + + @lru_cache(maxsize=None) + def platform_headers(self) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": self._version, + "X-Stainless-OS": str(get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": platform.python_implementation(), + "X-Stainless-Runtime-Version": platform.python_version(), + } + + def _calculate_retry_timeout( + self, + remaining_retries: int, + options: FinalRequestOptions, + response_headers: Optional[httpx.Headers] = None, + ) -> float: + max_retries = options.get_max_retries(self.max_retries) + try: + # About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + # + # ". See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax for + # details. + if response_headers is not None: + retry_header = response_headers.get("retry-after") + try: + retry_after = int(retry_header) + except Exception: + retry_date_tuple = email.utils.parsedate_tz(retry_header) + if retry_date_tuple is None: + retry_after = -1 + else: + retry_date = email.utils.mktime_tz(retry_date_tuple) + retry_after = int(retry_date - time.time()) + else: + retry_after = -1 + + except Exception: + retry_after = -1 + + # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + if 0 < retry_after <= 60: + return retry_after + + initial_retry_delay = 0.5 + max_retry_delay = 8.0 + nb_retries = max_retries - remaining_retries + + # Apply exponential backoff, but not more than the max. + sleep_seconds = min(initial_retry_delay * pow(2.0, nb_retries), max_retry_delay) + + # Apply some jitter, plus-or-minus half a second. + jitter = 1 - 0.25 * random() + timeout = sleep_seconds * jitter + return timeout if timeout >= 0 else 0 + + def _should_retry(self, response: httpx.Response) -> bool: + # Note: this is not a standard header + should_retry_header = response.headers.get("x-should-retry") + + # If the server explicitly says whether or not to retry, obey. + if should_retry_header == "true": + return True + if should_retry_header == "false": + return False + + # Retry on request timeouts. + if response.status_code == 408: + return True + + # Retry on lock timeouts. + if response.status_code == 409: + return True + + # Retry on rate limits. + if response.status_code == 429: + return True + + # Retry internal errors. + if response.status_code >= 500: + return True + + return False + + def _idempotency_key(self) -> str: + return f"stainless-python-retry-{uuid.uuid4()}" + + +class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): + _client: httpx.Client + _has_custom_http_client: bool + _default_stream_cls: type[Stream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + transport: Transport | None = None, + proxies: ProxiesTypes | None = None, + limits: Limits | None = None, + http_client: httpx.Client | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + _strict_response_validation: bool, + ) -> None: + if limits is not None: + warnings.warn( + "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") + else: + limits = DEFAULT_LIMITS + + if transport is not None: + warnings.warn( + "The `transport` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `transport`") + + if proxies is not None: + warnings.warn( + "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") + + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + super().__init__( + version=version, + limits=limits, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + proxies=proxies, + base_url=base_url, + transport=transport, + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or httpx.Client( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + proxies=proxies, + transport=transport, + limits=limits, + ) + self._has_custom_http_client = bool(http_client) + + def is_closed(self) -> bool: + return self._client.is_closed + + def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + # If an error is thrown while constructing a client, self._client + # may not be present + if hasattr(self, "_client"): + self._client.close() + + def __enter__(self: _T) -> _T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> None: + """Hook for mutating the given options""" + return None + + def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: Optional[int] = None, + *, + stream: Literal[True], + stream_cls: Type[_StreamT], + ) -> _StreamT: + ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: Optional[int] = None, + *, + stream: Literal[False] = False, + ) -> ResponseT: + ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: Optional[int] = None, + *, + stream: bool = False, + stream_cls: Type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + ... + + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: Optional[int] = None, + *, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + return self._request( + cast_to=cast_to, + options=options, + stream=stream, + stream_cls=stream_cls, + remaining_retries=remaining_retries, + ) + + def _request( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: int | None, + stream: bool, + stream_cls: type[_StreamT] | None, + ) -> ResponseT | _StreamT: + self._prepare_options(options) + + retries = self._remaining_retries(remaining_retries, options) + request = self._build_request(options) + self._prepare_request(request) + + try: + response = self._client.send(request, auth=self.custom_auth, stream=stream) + log.debug( + 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + ) + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + if retries > 0 and self._should_retry(err.response): + return self._retry_request( + options, + cast_to, + retries, + err.response.headers, + stream=stream, + stream_cls=stream_cls, + ) + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + err.response.read() + raise self._make_status_error_from_response(err.response) from None + except httpx.TimeoutException as err: + if retries > 0: + return self._retry_request( + options, + cast_to, + retries, + stream=stream, + stream_cls=stream_cls, + ) + raise APITimeoutError(request=request) from err + except Exception as err: + if retries > 0: + return self._retry_request( + options, + cast_to, + retries, + stream=stream, + stream_cls=stream_cls, + ) + raise APIConnectionError(request=request) from err + + return self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + ) + + def _retry_request( + self, + options: FinalRequestOptions, + cast_to: Type[ResponseT], + remaining_retries: int, + response_headers: Optional[httpx.Headers] = None, + *, + stream: bool, + stream_cls: type[_StreamT] | None, + ) -> ResponseT | _StreamT: + remaining = remaining_retries - 1 + timeout = self._calculate_retry_timeout(remaining, options, response_headers) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a + # different thread if necessary. + time.sleep(timeout) + + return self._request( + options=options, + cast_to=cast_to, + remaining_retries=remaining, + stream=stream, + stream_cls=stream_cls, + ) + + def _request_api_list( + self, + model: Type[ModelT], + page: Type[SyncPageT], + options: FinalRequestOptions, + ) -> SyncPageT: + def _parser(resp: SyncPageT) -> SyncPageT: + resp._set_private_attributes( + client=self, + model=model, + options=options, + ) + return resp + + options.post_parser = _parser + + return self.request(page, options, stream=False) + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: + ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: + ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + ... + + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + # cast is required because mypy complains about returning Any even though + # it understands the type variables + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[False] = False, + ) -> ResponseT: + ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: + ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + ... + + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, files=to_httpx_files(files), **options + ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + return self.request(cast_to, opts) + + def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + return self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[ModelT], + page: Type[SyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> SyncPageT: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): + _client: httpx.AsyncClient + _has_custom_http_client: bool + _default_stream_cls: type[AsyncStream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + transport: AsyncTransport | None = None, + proxies: ProxiesTypes | None = None, + limits: Limits | None = None, + http_client: httpx.AsyncClient | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + if limits is not None: + warnings.warn( + "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") + else: + limits = DEFAULT_LIMITS + + if transport is not None: + warnings.warn( + "The `transport` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `transport`") + + if proxies is not None: + warnings.warn( + "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") + + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + super().__init__( + version=version, + base_url=base_url, + limits=limits, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + proxies=proxies, + transport=transport, + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or httpx.AsyncClient( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + proxies=proxies, + transport=transport, + limits=limits, + ) + self._has_custom_http_client = bool(http_client) + + def is_closed(self) -> bool: + return self._client.is_closed + + async def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + await self._client.aclose() + + async def __aenter__(self: _T) -> _T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> None: + """Hook for mutating the given options""" + return None + + async def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + remaining_retries: Optional[int] = None, + ) -> ResponseT: + ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + remaining_retries: Optional[int] = None, + ) -> _AsyncStreamT: + ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + remaining_retries: Optional[int] = None, + ) -> ResponseT | _AsyncStreamT: + ... + + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + remaining_retries: Optional[int] = None, + ) -> ResponseT | _AsyncStreamT: + return await self._request( + cast_to=cast_to, + options=options, + stream=stream, + stream_cls=stream_cls, + remaining_retries=remaining_retries, + ) + + async def _request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None, + remaining_retries: int | None, + ) -> ResponseT | _AsyncStreamT: + await self._prepare_options(options) + + retries = self._remaining_retries(remaining_retries, options) + request = self._build_request(options) + await self._prepare_request(request) + + try: + response = await self._client.send(request, auth=self.custom_auth, stream=stream) + log.debug( + 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + ) + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + if retries > 0 and self._should_retry(err.response): + return await self._retry_request( + options, + cast_to, + retries, + err.response.headers, + stream=stream, + stream_cls=stream_cls, + ) + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + await err.response.aread() + raise self._make_status_error_from_response(err.response) from None + except httpx.ConnectTimeout as err: + if retries > 0: + return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) + raise APITimeoutError(request=request) from err + except httpx.ReadTimeout as err: + # We explicitly do not retry on ReadTimeout errors as this means + # that the server processing the request has taken 60 seconds + # (our default timeout). This likely indicates that something + # is not working as expected on the server side. + raise + except httpx.TimeoutException as err: + if retries > 0: + return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) + raise APITimeoutError(request=request) from err + except Exception as err: + if retries > 0: + return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) + raise APIConnectionError(request=request) from err + + return self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + ) + + async def _retry_request( + self, + options: FinalRequestOptions, + cast_to: Type[ResponseT], + remaining_retries: int, + response_headers: Optional[httpx.Headers] = None, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None, + ) -> ResponseT | _AsyncStreamT: + remaining = remaining_retries - 1 + timeout = self._calculate_retry_timeout(remaining, options, response_headers) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + await anyio.sleep(timeout) + + return await self._request( + options=options, + cast_to=cast_to, + remaining_retries=remaining, + stream=stream, + stream_cls=stream_cls, + ) + + def _request_api_list( + self, + model: Type[ModelT], + page: Type[AsyncPageT], + options: FinalRequestOptions, + ) -> AsyncPaginator[ModelT, AsyncPageT]: + return AsyncPaginator(client=self, options=options, page_cls=page, model=model) + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: + ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: + ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + ... + + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: + ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: + ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + ... + + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + async def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + return await self.request(cast_to, opts) + + async def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts) + + async def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + return await self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + # TODO: support paginating `str` + model: Type[ModelT], + page: Type[AsyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> AsyncPaginator[ModelT, AsyncPageT]: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +def make_request_options( + *, + query: Query | None = None, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + idempotency_key: str | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + post_parser: PostParser | NotGiven = NOT_GIVEN, +) -> RequestOptions: + """Create a dict of type RequestOptions without keys of NotGiven values.""" + options: RequestOptions = {} + if extra_headers is not None: + options["headers"] = extra_headers + + if extra_body is not None: + options["extra_json"] = cast(AnyMapping, extra_body) + + if query is not None: + options["params"] = query + + if extra_query is not None: + options["params"] = {**options.get("params", {}), **extra_query} + + if not isinstance(timeout, NotGiven): + options["timeout"] = timeout + + if idempotency_key is not None: + options["idempotency_key"] = idempotency_key + + if is_given(post_parser): + # internal + options["post_parser"] = post_parser # type: ignore + + return options + + +class OtherPlatform: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"Other:{self.name}" + + +Platform = Union[ + OtherPlatform, + Literal[ + "MacOS", + "Linux", + "Windows", + "FreeBSD", + "OpenBSD", + "iOS", + "Android", + "Unknown", + ], +] + + +def get_platform() -> Platform: + system = platform.system().lower() + platform_name = platform.platform().lower() + if "iphone" in platform_name or "ipad" in platform_name: + # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 + # system is Darwin and platform_name is a string like: + # - Darwin-21.6.0-iPhone12,1-64bit + # - Darwin-21.6.0-iPad7,11-64bit + return "iOS" + + if system == "darwin": + return "MacOS" + + if system == "windows": + return "Windows" + + if "android" in platform_name: + # Tested using Pydroid 3 + # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' + return "Android" + + if system == "linux": + # https://distro.readthedocs.io/en/latest/#distro.id + distro_id = distro.id() + if distro_id == "freebsd": + return "FreeBSD" + + if distro_id == "openbsd": + return "OpenBSD" + + return "Linux" + + if platform_name: + return OtherPlatform(platform_name) + + return "Unknown" + + +class OtherArch: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"other:{self.name}" + + +Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] + + +def get_architecture() -> Arch: + python_bitness, _ = platform.architecture() + machine = platform.machine().lower() + if machine in ("arm64", "aarch64"): + return "arm64" + + # TODO: untested + if machine == "arm": + return "arm" + + if machine == "x86_64": + return "x64" + + # TODO: untested + if python_bitness == "32bit": + return "x32" + + if machine: + return OtherArch(machine) + + return "unknown" + + +def _merge_mappings( + obj1: Mapping[_T_co, Union[_T, Omit]], + obj2: Mapping[_T_co, Union[_T, Omit]], +) -> Dict[_T_co, _T]: + """Merge two mappings of the same type, removing any values that are instances of `Omit`. + + In cases with duplicate keys the second mapping takes precedence. + """ + merged = {**obj1, **obj2} + return {key: value for key, value in merged.items() if not isinstance(value, Omit)} + + +class HttpxBinaryResponseContent(BinaryResponseContent): + response: httpx.Response + + def __init__(self, response: httpx.Response) -> None: + self.response = response + + @property + @override + def content(self) -> bytes: + return self.response.content + + @property + @override + def text(self) -> str: + return self.response.text + + @property + @override + def encoding(self) -> Optional[str]: + return self.response.encoding + + @property + @override + def charset_encoding(self) -> Optional[str]: + return self.response.charset_encoding + + @override + def json(self, **kwargs: Any) -> Any: + return self.response.json(**kwargs) + + @override + def read(self) -> bytes: + return self.response.read() + + @override + def iter_bytes(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: + return self.response.iter_bytes(chunk_size) + + @override + def iter_text(self, chunk_size: Optional[int] = None) -> Iterator[str]: + return self.response.iter_text(chunk_size) + + @override + def iter_lines(self) -> Iterator[str]: + return self.response.iter_lines() + + @override + def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: + return self.response.iter_raw(chunk_size) + + @override + def stream_to_file(self, file: str | os.PathLike[str]) -> None: + with open(file, mode="wb") as f: + for data in self.response.iter_bytes(): + f.write(data) + + @override + def close(self) -> None: + return self.response.close() + + @override + async def aread(self) -> bytes: + return await self.response.aread() + + @override + async def aiter_bytes(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]: + return self.response.aiter_bytes(chunk_size) + + @override + async def aiter_text(self, chunk_size: Optional[int] = None) -> AsyncIterator[str]: + return self.response.aiter_text(chunk_size) + + @override + async def aiter_lines(self) -> AsyncIterator[str]: + return self.response.aiter_lines() + + @override + async def aiter_raw(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]: + return self.response.aiter_raw(chunk_size) + + @override + async def astream_to_file(self, file: str | os.PathLike[str]) -> None: + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.response.aiter_bytes(): + await f.write(data) + + @override + async def aclose(self) -> None: + return await self.response.aclose() diff --git a/src/openai/_client.py b/src/openai/_client.py new file mode 100644 index 0000000000..9df7eabf9a --- /dev/null +++ b/src/openai/_client.py @@ -0,0 +1,488 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os +import asyncio +from typing import Union, Mapping +from typing_extensions import override + +import httpx + +from . import resources, _exceptions +from ._qs import Querystring +from ._types import ( + NOT_GIVEN, + Omit, + Timeout, + NotGiven, + Transport, + ProxiesTypes, + RequestOptions, +) +from ._utils import is_given +from ._version import __version__ +from ._streaming import Stream as Stream +from ._streaming import AsyncStream as AsyncStream +from ._exceptions import OpenAIError, APIStatusError +from ._base_client import DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient + +__all__ = [ + "Timeout", + "Transport", + "ProxiesTypes", + "RequestOptions", + "resources", + "OpenAI", + "AsyncOpenAI", + "Client", + "AsyncClient", +] + + +class OpenAI(SyncAPIClient): + completions: resources.Completions + chat: resources.Chat + edits: resources.Edits + embeddings: resources.Embeddings + files: resources.Files + images: resources.Images + audio: resources.Audio + moderations: resources.Moderations + models: resources.Models + fine_tuning: resources.FineTuning + fine_tunes: resources.FineTunes + with_raw_response: OpenAIWithRawResponse + + # client options + api_key: str + organization: str | None + + def __init__( + self, + *, + api_key: str | None = None, + organization: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + http_client: httpx.Client | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new synchronous openai client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `OPENAI_API_KEY` + - `organization` from `OPENAI_ORG_ID` + """ + if api_key is None: + api_key = os.environ.get("OPENAI_API_KEY") + if api_key is None: + raise OpenAIError( + "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" + ) + self.api_key = api_key + + if organization is None: + organization = os.environ.get("OPENAI_ORG_ID") + self.organization = organization + + if base_url is None: + base_url = f"https://api.openai.com/v1" + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self._default_stream_cls = Stream + + self.completions = resources.Completions(self) + self.chat = resources.Chat(self) + self.edits = resources.Edits(self) + self.embeddings = resources.Embeddings(self) + self.files = resources.Files(self) + self.images = resources.Images(self) + self.audio = resources.Audio(self) + self.moderations = resources.Moderations(self) + self.models = resources.Models(self) + self.fine_tuning = resources.FineTuning(self) + self.fine_tunes = resources.FineTunes(self) + self.with_raw_response = OpenAIWithRawResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + api_key = self.api_key + return {"Authorization": f"Bearer {api_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "OpenAI-Organization": self.organization if self.organization is not None else Omit(), + **self._custom_headers, + } + + def copy( + self, + *, + api_key: str | None = None, + organization: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.Client | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + ) -> OpenAI: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + + It should be noted that this does not share the underlying httpx client class which may lead + to performance issues. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + api_key=api_key or self.api_key, + organization=organization or self.organization, + base_url=base_url or str(self.base_url), + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + def __del__(self) -> None: + if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close"): + # this can happen if the '__init__' method raised an error + return + + if self._has_custom_http_client: + return + + self.close() + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class AsyncOpenAI(AsyncAPIClient): + completions: resources.AsyncCompletions + chat: resources.AsyncChat + edits: resources.AsyncEdits + embeddings: resources.AsyncEmbeddings + files: resources.AsyncFiles + images: resources.AsyncImages + audio: resources.AsyncAudio + moderations: resources.AsyncModerations + models: resources.AsyncModels + fine_tuning: resources.AsyncFineTuning + fine_tunes: resources.AsyncFineTunes + with_raw_response: AsyncOpenAIWithRawResponse + + # client options + api_key: str + organization: str | None + + def __init__( + self, + *, + api_key: str | None = None, + organization: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + http_client: httpx.AsyncClient | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new async openai client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `OPENAI_API_KEY` + - `organization` from `OPENAI_ORG_ID` + """ + if api_key is None: + api_key = os.environ.get("OPENAI_API_KEY") + if api_key is None: + raise OpenAIError( + "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" + ) + self.api_key = api_key + + if organization is None: + organization = os.environ.get("OPENAI_ORG_ID") + self.organization = organization + + if base_url is None: + base_url = f"https://api.openai.com/v1" + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self._default_stream_cls = AsyncStream + + self.completions = resources.AsyncCompletions(self) + self.chat = resources.AsyncChat(self) + self.edits = resources.AsyncEdits(self) + self.embeddings = resources.AsyncEmbeddings(self) + self.files = resources.AsyncFiles(self) + self.images = resources.AsyncImages(self) + self.audio = resources.AsyncAudio(self) + self.moderations = resources.AsyncModerations(self) + self.models = resources.AsyncModels(self) + self.fine_tuning = resources.AsyncFineTuning(self) + self.fine_tunes = resources.AsyncFineTunes(self) + self.with_raw_response = AsyncOpenAIWithRawResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + api_key = self.api_key + return {"Authorization": f"Bearer {api_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "OpenAI-Organization": self.organization if self.organization is not None else Omit(), + **self._custom_headers, + } + + def copy( + self, + *, + api_key: str | None = None, + organization: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.AsyncClient | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + ) -> AsyncOpenAI: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + + It should be noted that this does not share the underlying httpx client class which may lead + to performance issues. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + api_key=api_key or self.api_key, + organization=organization or self.organization, + base_url=base_url or str(self.base_url), + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + def __del__(self) -> None: + if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close"): + # this can happen if the '__init__' method raised an error + return + + if self._has_custom_http_client: + return + + try: + asyncio.get_running_loop().create_task(self.close()) + except Exception: + pass + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class OpenAIWithRawResponse: + def __init__(self, client: OpenAI) -> None: + self.completions = resources.CompletionsWithRawResponse(client.completions) + self.chat = resources.ChatWithRawResponse(client.chat) + self.edits = resources.EditsWithRawResponse(client.edits) + self.embeddings = resources.EmbeddingsWithRawResponse(client.embeddings) + self.files = resources.FilesWithRawResponse(client.files) + self.images = resources.ImagesWithRawResponse(client.images) + self.audio = resources.AudioWithRawResponse(client.audio) + self.moderations = resources.ModerationsWithRawResponse(client.moderations) + self.models = resources.ModelsWithRawResponse(client.models) + self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning) + self.fine_tunes = resources.FineTunesWithRawResponse(client.fine_tunes) + + +class AsyncOpenAIWithRawResponse: + def __init__(self, client: AsyncOpenAI) -> None: + self.completions = resources.AsyncCompletionsWithRawResponse(client.completions) + self.chat = resources.AsyncChatWithRawResponse(client.chat) + self.edits = resources.AsyncEditsWithRawResponse(client.edits) + self.embeddings = resources.AsyncEmbeddingsWithRawResponse(client.embeddings) + self.files = resources.AsyncFilesWithRawResponse(client.files) + self.images = resources.AsyncImagesWithRawResponse(client.images) + self.audio = resources.AsyncAudioWithRawResponse(client.audio) + self.moderations = resources.AsyncModerationsWithRawResponse(client.moderations) + self.models = resources.AsyncModelsWithRawResponse(client.models) + self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning) + self.fine_tunes = resources.AsyncFineTunesWithRawResponse(client.fine_tunes) + + +Client = OpenAI + +AsyncClient = AsyncOpenAI diff --git a/src/openai/_compat.py b/src/openai/_compat.py new file mode 100644 index 0000000000..34323c9b7e --- /dev/null +++ b/src/openai/_compat.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union, TypeVar, cast +from datetime import date, datetime + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import StrBytesIntFloat + +_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) + +# --------------- Pydantic v2 compatibility --------------- + +# Pyright incorrectly reports some of our functions as overriding a method when they don't +# pyright: reportIncompatibleMethodOverride=false + +PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + +# v1 re-exports +if TYPE_CHECKING: + + def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 + ... + + def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 + ... + + def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 + ... + + def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 + ... + + def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 + ... + + def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 + ... + + def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 + ... + +else: + if PYDANTIC_V2: + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import is_union as is_union + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_typeddict as is_typeddict + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + else: + from pydantic.typing import get_args as get_args + from pydantic.typing import is_union as is_union + from pydantic.typing import get_origin as get_origin + from pydantic.typing import is_typeddict as is_typeddict + from pydantic.typing import is_literal_type as is_literal_type + from pydantic.datetime_parse import parse_date as parse_date + from pydantic.datetime_parse import parse_datetime as parse_datetime + + +# refactored config +if TYPE_CHECKING: + from pydantic import ConfigDict as ConfigDict +else: + if PYDANTIC_V2: + from pydantic import ConfigDict + else: + # TODO: provide an error message here? + ConfigDict = None + + +# renamed methods / properties +def parse_obj(model: type[_ModelT], value: object) -> _ModelT: + if PYDANTIC_V2: + return model.model_validate(value) + else: + return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + + +def field_is_required(field: FieldInfo) -> bool: + if PYDANTIC_V2: + return field.is_required() + return field.required # type: ignore + + +def field_get_default(field: FieldInfo) -> Any: + value = field.get_default() + if PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value + + +def field_outer_type(field: FieldInfo) -> Any: + if PYDANTIC_V2: + return field.annotation + return field.outer_type_ # type: ignore + + +def get_model_config(model: type[pydantic.BaseModel]) -> Any: + if PYDANTIC_V2: + return model.model_config + return model.__config__ # type: ignore + + +def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: + if PYDANTIC_V2: + return model.model_fields + return model.__fields__ # type: ignore + + +def model_copy(model: _ModelT) -> _ModelT: + if PYDANTIC_V2: + return model.model_copy() + return model.copy() # type: ignore + + +def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: + if PYDANTIC_V2: + return model.model_dump_json(indent=indent) + return model.json(indent=indent) # type: ignore + + +def model_dump( + model: pydantic.BaseModel, + *, + exclude_unset: bool = False, + exclude_defaults: bool = False, +) -> dict[str, Any]: + if PYDANTIC_V2: + return model.model_dump( + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + ) + return cast( + "dict[str, Any]", + model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + ), + ) + + +def model_parse(model: type[_ModelT], data: Any) -> _ModelT: + if PYDANTIC_V2: + return model.model_validate(data) + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + + +# generic models +if TYPE_CHECKING: + + class GenericModel(pydantic.BaseModel): + ... + +else: + if PYDANTIC_V2: + # there no longer needs to be a distinction in v2 but + # we still have to create our own subclass to avoid + # inconsistent MRO ordering errors + class GenericModel(pydantic.BaseModel): + ... + + else: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): + ... diff --git a/src/openai/_constants.py b/src/openai/_constants.py new file mode 100644 index 0000000000..2e402300d3 --- /dev/null +++ b/src/openai/_constants.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. + +import httpx + +RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" + +# default timeout is 10 minutes +DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0) +DEFAULT_MAX_RETRIES = 2 +DEFAULT_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py new file mode 100644 index 0000000000..b79ac5fd64 --- /dev/null +++ b/src/openai/_exceptions.py @@ -0,0 +1,123 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Any, Optional, cast +from typing_extensions import Literal + +import httpx + +from ._utils import is_dict + +__all__ = [ + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", +] + + +class OpenAIError(Exception): + pass + + +class APIError(OpenAIError): + message: str + request: httpx.Request + + body: object | None + """The API response body. + + If the API responded with a valid JSON structure then this property will be the + decoded result. + + If it isn't a valid JSON structure then this will be the raw response. + + If there was no response associated with this error then it will be `None`. + """ + + code: Optional[str] + param: Optional[str] + type: Optional[str] + + def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: + super().__init__(message) + self.request = request + self.message = message + + if is_dict(body): + self.code = cast(Any, body.get("code")) + self.param = cast(Any, body.get("param")) + self.type = cast(Any, body.get("type")) + else: + self.code = None + self.param = None + self.type = None + + +class APIResponseValidationError(APIError): + response: httpx.Response + status_code: int + + def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None: + super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIStatusError(APIError): + """Raised when an API response has a status code of 4xx or 5xx.""" + + response: httpx.Response + status_code: int + + def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: + super().__init__(message, response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIConnectionError(APIError): + def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: + super().__init__(message, request, body=None) + + +class APITimeoutError(APIConnectionError): + def __init__(self, request: httpx.Request) -> None: + super().__init__(message="Request timed out.", request=request) + + +class BadRequestError(APIStatusError): + status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] + + +class AuthenticationError(APIStatusError): + status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] + + +class PermissionDeniedError(APIStatusError): + status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] + + +class NotFoundError(APIStatusError): + status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] + + +class ConflictError(APIStatusError): + status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] + + +class UnprocessableEntityError(APIStatusError): + status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] + + +class RateLimitError(APIStatusError): + status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] + + +class InternalServerError(APIStatusError): + pass diff --git a/src/openai/_extras/__init__.py b/src/openai/_extras/__init__.py new file mode 100644 index 0000000000..dc6625c5dc --- /dev/null +++ b/src/openai/_extras/__init__.py @@ -0,0 +1,3 @@ +from .numpy_proxy import numpy as numpy +from .numpy_proxy import has_numpy as has_numpy +from .pandas_proxy import pandas as pandas diff --git a/src/openai/_extras/_common.py b/src/openai/_extras/_common.py new file mode 100644 index 0000000000..6e71720e64 --- /dev/null +++ b/src/openai/_extras/_common.py @@ -0,0 +1,21 @@ +from .._exceptions import OpenAIError + +INSTRUCTIONS = """ + +OpenAI error: + + missing `{library}` + +This feature requires additional dependencies: + + $ pip install openai[{extra}] + +""" + + +def format_instructions(*, library: str, extra: str) -> str: + return INSTRUCTIONS.format(library=library, extra=extra) + + +class MissingDependencyError(OpenAIError): + pass diff --git a/src/openai/_extras/numpy_proxy.py b/src/openai/_extras/numpy_proxy.py new file mode 100644 index 0000000000..408eaebd3b --- /dev/null +++ b/src/openai/_extras/numpy_proxy.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any +from typing_extensions import ClassVar, override + +from .._utils import LazyProxy +from ._common import MissingDependencyError, format_instructions + +if TYPE_CHECKING: + import numpy as numpy + + +NUMPY_INSTRUCTIONS = format_instructions(library="numpy", extra="datalib") + + +class NumpyProxy(LazyProxy[Any]): + should_cache: ClassVar[bool] = True + + @override + def __load__(self) -> Any: + try: + import numpy + except ImportError: + raise MissingDependencyError(NUMPY_INSTRUCTIONS) + + return numpy + + +if not TYPE_CHECKING: + numpy = NumpyProxy() + + +def has_numpy() -> bool: + try: + import numpy # noqa: F401 # pyright: ignore[reportUnusedImport] + except ImportError: + return False + + return True diff --git a/src/openai/_extras/pandas_proxy.py b/src/openai/_extras/pandas_proxy.py new file mode 100644 index 0000000000..2fc0d2a7eb --- /dev/null +++ b/src/openai/_extras/pandas_proxy.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any +from typing_extensions import ClassVar, override + +from .._utils import LazyProxy +from ._common import MissingDependencyError, format_instructions + +if TYPE_CHECKING: + import pandas as pandas + + +PANDAS_INSTRUCTIONS = format_instructions(library="pandas", extra="datalib") + + +class PandasProxy(LazyProxy[Any]): + should_cache: ClassVar[bool] = True + + @override + def __load__(self) -> Any: + try: + import pandas + except ImportError: + raise MissingDependencyError(PANDAS_INSTRUCTIONS) + + return pandas + + +if not TYPE_CHECKING: + pandas = PandasProxy() diff --git a/src/openai/_files.py b/src/openai/_files.py new file mode 100644 index 0000000000..49e3536243 --- /dev/null +++ b/src/openai/_files.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import io +import os +import pathlib +from typing import overload +from typing_extensions import TypeGuard + +import anyio + +from ._types import ( + FileTypes, + FileContent, + RequestFiles, + HttpxFileTypes, + HttpxFileContent, + HttpxRequestFiles, +) +from ._utils import is_tuple_t, is_mapping_t, is_sequence_t + + +def is_file_content(obj: object) -> TypeGuard[FileContent]: + return ( + isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + ) + + +def assert_is_file_content(obj: object, *, key: str | None = None) -> None: + if not is_file_content(obj): + prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" + raise RuntimeError( + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python/tree/v1#file-uploads" + ) from None + + +@overload +def to_httpx_files(files: None) -> None: + ... + + +@overload +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: + ... + + +def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: _transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, _transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +def _transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = pathlib.Path(file) + return (path.name, path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], _read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +def _read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return pathlib.Path(file).read_bytes() + return file + + +@overload +async def async_to_httpx_files(files: None) -> None: + ... + + +@overload +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: + ... + + +async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: await _async_transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, await _async_transform_file(file)) for key, file in files] + else: + raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = anyio.Path(file) + return (path.name, await path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], await _async_read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +async def _async_read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return await anyio.Path(file).read_bytes() + + return file diff --git a/src/openai/_models.py b/src/openai/_models.py new file mode 100644 index 0000000000..00d787ca87 --- /dev/null +++ b/src/openai/_models.py @@ -0,0 +1,460 @@ +from __future__ import annotations + +import inspect +from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast +from datetime import date, datetime +from typing_extensions import ( + Unpack, + Literal, + ClassVar, + Protocol, + Required, + TypedDict, + final, + override, + runtime_checkable, +) + +import pydantic +import pydantic.generics +from pydantic.fields import FieldInfo + +from ._types import ( + Body, + IncEx, + Query, + ModelT, + Headers, + Timeout, + NotGiven, + AnyMapping, + HttpxRequestFiles, +) +from ._utils import ( + is_list, + is_given, + is_mapping, + parse_date, + parse_datetime, + strip_not_given, +) +from ._compat import PYDANTIC_V2, ConfigDict +from ._compat import GenericModel as BaseGenericModel +from ._compat import ( + get_args, + is_union, + parse_obj, + get_origin, + is_literal_type, + get_model_config, + get_model_fields, + field_get_default, +) +from ._constants import RAW_RESPONSE_HEADER + +__all__ = ["BaseModel", "GenericModel"] + +_T = TypeVar("_T") + + +@runtime_checkable +class _ConfigProtocol(Protocol): + allow_population_by_field_name: bool + + +class BaseModel(pydantic.BaseModel): + if PYDANTIC_V2: + model_config: ClassVar[ConfigDict] = ConfigDict(extra="allow") + else: + + @property + @override + def model_fields_set(self) -> set[str]: + # a forwards-compat shim for pydantic v2 + return self.__fields_set__ # type: ignore + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + extra: Any = pydantic.Extra.allow # type: ignore + + @override + def __str__(self) -> str: + # mypy complains about an invalid self arg + return f'{self.__repr_name__()}({self.__repr_str__(", ")})' # type: ignore[misc] + + # Override the 'construct' method in a way that supports recursive parsing without validation. + # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. + @classmethod + @override + def construct( + cls: Type[ModelT], + _fields_set: set[str] | None = None, + **values: object, + ) -> ModelT: + m = cls.__new__(cls) + fields_values: dict[str, object] = {} + + config = get_model_config(cls) + populate_by_name = ( + config.allow_population_by_field_name + if isinstance(config, _ConfigProtocol) + else config.get("populate_by_name") + ) + + if _fields_set is None: + _fields_set = set() + + model_fields = get_model_fields(cls) + for name, field in model_fields.items(): + key = field.alias + if key is None or (key not in values and populate_by_name): + key = name + + if key in values: + fields_values[name] = _construct_field(value=values[key], field=field, key=key) + _fields_set.add(name) + else: + fields_values[name] = field_get_default(field) + + _extra = {} + for key, value in values.items(): + if key not in model_fields: + if PYDANTIC_V2: + _extra[key] = value + else: + fields_values[key] = value + + object.__setattr__(m, "__dict__", fields_values) + + if PYDANTIC_V2: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) + else: + # init_private_attributes() does not exist in v2 + m._init_private_attributes() # type: ignore + + # copied from Pydantic v1's `construct()` method + object.__setattr__(m, "__fields_set__", _fields_set) + + return m + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + # because the type signatures are technically different + # although not in practice + model_construct = construct + + if not PYDANTIC_V2: + # we define aliases for some of the new pydantic v2 methods so + # that we can just document these methods without having to specify + # a specifc pydantic version as some users may not know which + # pydantic version they are currently using + + @override + def model_dump( + self, + *, + mode: Literal["json", "python"] | str = "python", + include: IncEx = None, + exclude: IncEx = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool = True, + ) -> dict[str, Any]: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump + + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + Args: + mode: The mode in which `to_python` should run. + If mode is 'json', the dictionary will only contain JSON serializable types. + If mode is 'python', the dictionary may contain any Python objects. + include: A list of fields to include in the output. + exclude: A list of fields to exclude from the output. + by_alias: Whether to use the field's alias in the dictionary key if defined. + exclude_unset: Whether to exclude fields that are unset or None from the output. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + round_trip: Whether to enable serialization and deserialization round-trip support. + warnings: Whether to log warnings when invalid fields are encountered. + + Returns: + A dictionary representation of the model. + """ + if mode != "python": + raise ValueError("mode is only supported in Pydantic v2") + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + return super().dict( # pyright: ignore[reportDeprecated] + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + @override + def model_dump_json( + self, + *, + indent: int | None = None, + include: IncEx = None, + exclude: IncEx = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool = True, + ) -> str: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json + + Generates a JSON representation of the model using Pydantic's `to_json` method. + + Args: + indent: Indentation to use in the JSON output. If None is passed, the output will be compact. + include: Field(s) to include in the JSON output. Can take either a string or set of strings. + exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. + by_alias: Whether to serialize using field aliases. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to use serialization/deserialization between JSON and class instance. + warnings: Whether to show any warnings that occurred during serialization. + + Returns: + A JSON string representation of the model. + """ + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + return super().json( # type: ignore[reportDeprecated] + indent=indent, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + +def _construct_field(value: object, field: FieldInfo, key: str) -> object: + if value is None: + return field_get_default(field) + + if PYDANTIC_V2: + type_ = field.annotation + else: + type_ = cast(type, field.outer_type_) # type: ignore + + if type_ is None: + raise RuntimeError(f"Unexpected field type is None for {key}") + + return construct_type(value=value, type_=type_) + + +def construct_type(*, value: object, type_: type) -> object: + """Loose coercion to the expected type with construction of nested values. + + If the given value does not match the expected type then it is returned as-is. + """ + + # we need to use the origin class for any types that are subscripted generics + # e.g. Dict[str, object] + origin = get_origin(type_) or type_ + args = get_args(type_) + + if is_union(origin): + try: + return validate_type(type_=type_, value=value) + except Exception: + pass + + # if the data is not valid, use the first variant that doesn't fail while deserializing + for variant in args: + try: + return construct_type(value=value, type_=variant) + except Exception: + continue + + raise RuntimeError(f"Could not convert data into a valid instance of {type_}") + + if origin == dict: + if not is_mapping(value): + return value + + _, items_type = get_args(type_) # Dict[_, items_type] + return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} + + if not is_literal_type(type_) and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)): + if is_list(value): + return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] + + if is_mapping(value): + if issubclass(type_, BaseModel): + return type_.construct(**value) # type: ignore[arg-type] + + return cast(Any, type_).construct(**value) + + if origin == list: + if not is_list(value): + return value + + inner_type = args[0] # List[inner_type] + return [construct_type(value=entry, type_=inner_type) for entry in value] + + if origin == float: + if isinstance(value, int): + coerced = float(value) + if coerced != value: + return value + return coerced + + return value + + if type_ == datetime: + try: + return parse_datetime(value) # type: ignore + except Exception: + return value + + if type_ == date: + try: + return parse_date(value) # type: ignore + except Exception: + return value + + return value + + +def validate_type(*, type_: type[_T], value: object) -> _T: + """Strict validation that the given value matches the expected type""" + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + return cast(_T, parse_obj(type_, value)) + + return cast(_T, _validate_non_model_type(type_=type_, value=value)) + + +# our use of subclasssing here causes weirdness for type checkers, +# so we just pretend that we don't subclass +if TYPE_CHECKING: + GenericModel = BaseModel +else: + + class GenericModel(BaseGenericModel, BaseModel): + pass + + +if PYDANTIC_V2: + from pydantic import TypeAdapter + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + return TypeAdapter(type_).validate_python(value) + +elif not TYPE_CHECKING: # TODO: condition is weird + + class RootModel(GenericModel, Generic[_T]): + """Used as a placeholder to easily convert runtime types to a Pydantic format + to provide validation. + + For example: + ```py + validated = RootModel[int](__root__='5').__root__ + # validated: 5 + ``` + """ + + __root__: _T + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + model = _create_pydantic_model(type_).validate(value) + return cast(_T, model.__root__) + + def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: + return RootModel[type_] # type: ignore + + +class FinalRequestOptionsInput(TypedDict, total=False): + method: Required[str] + url: Required[str] + params: Query + headers: Headers + max_retries: int + timeout: float | Timeout | None + files: HttpxRequestFiles | None + idempotency_key: str + json_data: Body + extra_json: AnyMapping + + +@final +class FinalRequestOptions(pydantic.BaseModel): + method: str + url: str + params: Query = {} + headers: Union[Headers, NotGiven] = NotGiven() + max_retries: Union[int, NotGiven] = NotGiven() + timeout: Union[float, Timeout, None, NotGiven] = NotGiven() + files: Union[HttpxRequestFiles, None] = None + idempotency_key: Union[str, None] = None + post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + + # It should be noted that we cannot use `json` here as that would override + # a BaseModel method in an incompatible fashion. + json_data: Union[Body, None] = None + extra_json: Union[AnyMapping, None] = None + + if PYDANTIC_V2: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) + else: + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + arbitrary_types_allowed: bool = True + + def get_max_retries(self, max_retries: int) -> int: + if isinstance(self.max_retries, NotGiven): + return max_retries + return self.max_retries + + def _strip_raw_response_header(self) -> None: + if not is_given(self.headers): + return + + if self.headers.get(RAW_RESPONSE_HEADER): + self.headers = {**self.headers} + self.headers.pop(RAW_RESPONSE_HEADER) + + # override the `construct` method so that we can run custom transformations. + # this is necessary as we don't want to do any actual runtime type checking + # (which means we can't use validators) but we do want to ensure that `NotGiven` + # values are not present + # + # type ignore required because we're adding explicit types to `**values` + @classmethod + def construct( # type: ignore + cls, + _fields_set: set[str] | None = None, + **values: Unpack[FinalRequestOptionsInput], + ) -> FinalRequestOptions: + kwargs: dict[str, Any] = { + # we unconditionally call `strip_not_given` on any value + # as it will just ignore any non-mapping types + key: strip_not_given(value) + for key, value in values.items() + } + if PYDANTIC_V2: + return super().model_construct(_fields_set, **kwargs) + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + model_construct = construct diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py new file mode 100644 index 0000000000..ca80468e88 --- /dev/null +++ b/src/openai/_module_client.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import override + +from . import resources, _load_client +from ._utils import LazyProxy + + +class ChatProxy(LazyProxy[resources.Chat]): + @override + def __load__(self) -> resources.Chat: + return _load_client().chat + + +class EditsProxy(LazyProxy[resources.Edits]): + @override + def __load__(self) -> resources.Edits: + return _load_client().edits + + +class FilesProxy(LazyProxy[resources.Files]): + @override + def __load__(self) -> resources.Files: + return _load_client().files + + +class AudioProxy(LazyProxy[resources.Audio]): + @override + def __load__(self) -> resources.Audio: + return _load_client().audio + + +class ImagesProxy(LazyProxy[resources.Images]): + @override + def __load__(self) -> resources.Images: + return _load_client().images + + +class ModelsProxy(LazyProxy[resources.Models]): + @override + def __load__(self) -> resources.Models: + return _load_client().models + + +class EmbeddingsProxy(LazyProxy[resources.Embeddings]): + @override + def __load__(self) -> resources.Embeddings: + return _load_client().embeddings + + +class FineTunesProxy(LazyProxy[resources.FineTunes]): + @override + def __load__(self) -> resources.FineTunes: + return _load_client().fine_tunes + + +class CompletionsProxy(LazyProxy[resources.Completions]): + @override + def __load__(self) -> resources.Completions: + return _load_client().completions + + +class ModerationsProxy(LazyProxy[resources.Moderations]): + @override + def __load__(self) -> resources.Moderations: + return _load_client().moderations + + +class FineTuningProxy(LazyProxy[resources.FineTuning]): + @override + def __load__(self) -> resources.FineTuning: + return _load_client().fine_tuning + + +chat: resources.Chat = ChatProxy().__as_proxied__() +edits: resources.Edits = EditsProxy().__as_proxied__() +files: resources.Files = FilesProxy().__as_proxied__() +audio: resources.Audio = AudioProxy().__as_proxied__() +images: resources.Images = ImagesProxy().__as_proxied__() +models: resources.Models = ModelsProxy().__as_proxied__() +embeddings: resources.Embeddings = EmbeddingsProxy().__as_proxied__() +fine_tunes: resources.FineTunes = FineTunesProxy().__as_proxied__() +completions: resources.Completions = CompletionsProxy().__as_proxied__() +moderations: resources.Moderations = ModerationsProxy().__as_proxied__() +fine_tuning: resources.FineTuning = FineTuningProxy().__as_proxied__() diff --git a/src/openai/_qs.py b/src/openai/_qs.py new file mode 100644 index 0000000000..274320ca5e --- /dev/null +++ b/src/openai/_qs.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +from typing import Any, List, Tuple, Union, Mapping, TypeVar +from urllib.parse import parse_qs, urlencode +from typing_extensions import Literal, get_args + +from ._types import NOT_GIVEN, NotGiven, NotGivenOr +from ._utils import flatten + +_T = TypeVar("_T") + + +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + +PrimitiveData = Union[str, int, float, bool, None] +# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] +# https://github.com/microsoft/pyright/issues/3555 +Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] +Params = Mapping[str, Data] + + +class Querystring: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + *, + array_format: ArrayFormat = "repeat", + nested_format: NestedFormat = "brackets", + ) -> None: + self.array_format = array_format + self.nested_format = nested_format + + def parse(self, query: str) -> Mapping[str, object]: + # Note: custom format syntax is not supported yet + return parse_qs(query) + + def stringify( + self, + params: Params, + *, + array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, + nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + ) -> str: + return urlencode( + self.stringify_items( + params, + array_format=array_format, + nested_format=nested_format, + ) + ) + + def stringify_items( + self, + params: Params, + *, + array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, + nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + ) -> list[tuple[str, str]]: + opts = Options( + qs=self, + array_format=array_format, + nested_format=nested_format, + ) + return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) + + def _stringify_item( + self, + key: str, + value: Data, + opts: Options, + ) -> list[tuple[str, str]]: + if isinstance(value, Mapping): + items: list[tuple[str, str]] = [] + nested_format = opts.nested_format + for subkey, subvalue in value.items(): + items.extend( + self._stringify_item( + # TODO: error if unknown format + f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", + subvalue, + opts, + ) + ) + return items + + if isinstance(value, (list, tuple)): + array_format = opts.array_format + if array_format == "comma": + return [ + ( + key, + ",".join(self._primitive_value_to_str(item) for item in value if item is not None), + ), + ] + elif array_format == "repeat": + items = [] + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + elif array_format == "indices": + raise NotImplementedError("The array indices format is not supported yet") + elif array_format == "brackets": + items = [] + key = key + "[]" + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + else: + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + serialised = self._primitive_value_to_str(value) + if not serialised: + return [] + return [(key, serialised)] + + def _primitive_value_to_str(self, value: PrimitiveData) -> str: + # copied from httpx + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +_qs = Querystring() +parse = _qs.parse +stringify = _qs.stringify +stringify_items = _qs.stringify_items + + +class Options: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + qs: Querystring = _qs, + *, + array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, + nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + ) -> None: + self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format + self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/openai/_resource.py b/src/openai/_resource.py new file mode 100644 index 0000000000..db1b0fa45a --- /dev/null +++ b/src/openai/_resource.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import time +import asyncio +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._client import OpenAI, AsyncOpenAI + + +class SyncAPIResource: + _client: OpenAI + + def __init__(self, client: OpenAI) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + def _sleep(self, seconds: float) -> None: + time.sleep(seconds) + + +class AsyncAPIResource: + _client: AsyncOpenAI + + def __init__(self, client: AsyncOpenAI) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + async def _sleep(self, seconds: float) -> None: + await asyncio.sleep(seconds) diff --git a/src/openai/_response.py b/src/openai/_response.py new file mode 100644 index 0000000000..3cc8fd8cc1 --- /dev/null +++ b/src/openai/_response.py @@ -0,0 +1,252 @@ +from __future__ import annotations + +import inspect +import datetime +import functools +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast +from typing_extensions import Awaitable, ParamSpec, get_args, override, get_origin + +import httpx +import pydantic + +from ._types import NoneType, UnknownResponse, BinaryResponseContent +from ._utils import is_given +from ._models import BaseModel +from ._constants import RAW_RESPONSE_HEADER +from ._exceptions import APIResponseValidationError + +if TYPE_CHECKING: + from ._models import FinalRequestOptions + from ._base_client import Stream, BaseClient, AsyncStream + + +P = ParamSpec("P") +R = TypeVar("R") + + +class APIResponse(Generic[R]): + _cast_to: type[R] + _client: BaseClient[Any, Any] + _parsed: R | None + _stream: bool + _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None + _options: FinalRequestOptions + + http_response: httpx.Response + + def __init__( + self, + *, + raw: httpx.Response, + cast_to: type[R], + client: BaseClient[Any, Any], + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + options: FinalRequestOptions, + ) -> None: + self._cast_to = cast_to + self._client = client + self._parsed = None + self._stream = stream + self._stream_cls = stream_cls + self._options = options + self.http_response = raw + + def parse(self) -> R: + if self._parsed is not None: + return self._parsed + + parsed = self._parse() + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed = parsed + return parsed + + @property + def headers(self) -> httpx.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx.Request: + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def url(http://github.com/self) -> httpx.URL: + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def content(self) -> bytes: + return self.http_response.content + + @property + def text(self) -> str: + return self.http_response.text + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def elapsed(self) -> datetime.timedelta: + """The time taken for the complete request/response cycle to complete.""" + return self.http_response.elapsed + + def _parse(self) -> R: + if self._stream: + if self._stream_cls: + return cast( + R, + self._stream_cls( + cast_to=_extract_stream_chunk_type(self._stream_cls), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) + if stream_cls is None: + raise MissingStreamClassError() + + return cast( + R, + stream_cls( + cast_to=self._cast_to, + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + cast_to = self._cast_to + if cast_to is NoneType: + return cast(R, None) + + response = self.http_response + if cast_to == str: + return cast(R, response.text) + + origin = get_origin(cast_to) or cast_to + + if inspect.isclass(origin) and issubclass(origin, BinaryResponseContent): + return cast(R, cast_to(response)) # type: ignore + + if origin == APIResponse: + raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + + if inspect.isclass(origin) and issubclass(origin, httpx.Response): + # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response + # and pass that class to our request functions. We cannot change the variance to be either + # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct + # the response class ourselves but that is something that should be supported directly in httpx + # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. + if cast_to != httpx.Response: + raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") + return cast(R, response) + + # The check here is necessary as we are subverting the the type system + # with casts as the relationship between TypeVars and Types are very strict + # which means we must return *exactly* what was input or transform it in a + # way that retains the TypeVar state. As we cannot do that in this function + # then we have to resort to using `cast`. At the time of writing, we know this + # to be safe as we have handled all the types that could be bound to the + # `ResponseT` TypeVar, however if that TypeVar is ever updated in the future, then + # this function would become unsafe but a type checker would not report an error. + if ( + cast_to is not UnknownResponse + and not origin is list + and not origin is dict + and not origin is Union + and not issubclass(origin, BaseModel) + ): + raise RuntimeError( + f"Invalid state, expected {cast_to} to be a subclass type of {BaseModel}, {dict}, {list} or {Union}." + ) + + # split is required to handle cases where additional information is included + # in the response, e.g. application/json; charset=utf-8 + content_type, *_ = response.headers.get("content-type").split(";") + if content_type != "application/json": + if self._client._strict_response_validation: + raise APIResponseValidationError( + response=response, + message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", + body=response.text, + ) + + # If the API responds with content that isn't JSON then we just return + # the (decoded) text without performing any parsing so that you can still + # handle the response however you need to. + return response.text # type: ignore + + data = response.json() + + try: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + except pydantic.ValidationError as err: + raise APIResponseValidationError(response=response, body=data) from err + + @override + def __repr__(self) -> str: + return f"" + + +class MissingStreamClassError(TypeError): + def __init__(self) -> None: + super().__init__( + "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference", + ) + + +def _extract_stream_chunk_type(stream_cls: type) -> type: + args = get_args(stream_cls) + if not args: + raise TypeError( + f"Expected stream_cls to have been given a generic type argument, e.g. Stream[Foo] but received {stream_cls}", + ) + return cast(type, args[0]) + + +def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "true" + + kwargs["extra_headers"] = extra_headers + + return cast(APIResponse[R], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[APIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "true" + + kwargs["extra_headers"] = extra_headers + + return cast(APIResponse[R], await func(*args, **kwargs)) + + return wrapped diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py new file mode 100644 index 0000000000..cee737f4f5 --- /dev/null +++ b/src/openai/_streaming.py @@ -0,0 +1,232 @@ +# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py +from __future__ import annotations + +import json +from typing import TYPE_CHECKING, Any, Generic, Iterator, AsyncIterator +from typing_extensions import override + +import httpx + +from ._types import ResponseT +from ._utils import is_mapping +from ._exceptions import APIError + +if TYPE_CHECKING: + from ._base_client import SyncAPIClient, AsyncAPIClient + + +class Stream(Generic[ResponseT]): + """Provides the core interface to iterate over a synchronous stream response.""" + + response: httpx.Response + + def __init__( + self, + *, + cast_to: type[ResponseT], + response: httpx.Response, + client: SyncAPIClient, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = SSEDecoder() + self._iterator = self.__stream__() + + def __next__(self) -> ResponseT: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[ResponseT]: + for item in self._iterator: + yield item + + def _iter_events(self) -> Iterator[ServerSentEvent]: + yield from self._decoder.iter(self.response.iter_lines()) + + def __stream__(self) -> Iterator[ResponseT]: + cast_to = self._cast_to + response = self.response + process_data = self._client._process_response_data + + for sse in self._iter_events(): + if sse.data.startswith("[DONE]"): + break + + if sse.event is None: + data = sse.json() + if is_mapping(data) and data.get("error"): + raise APIError( + message="An error ocurred during streaming", + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) + + +class AsyncStream(Generic[ResponseT]): + """Provides the core interface to iterate over an asynchronous stream response.""" + + response: httpx.Response + + def __init__( + self, + *, + cast_to: type[ResponseT], + response: httpx.Response, + client: AsyncAPIClient, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = SSEDecoder() + self._iterator = self.__stream__() + + async def __anext__(self) -> ResponseT: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[ResponseT]: + async for item in self._iterator: + yield item + + async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: + async for sse in self._decoder.aiter(self.response.aiter_lines()): + yield sse + + async def __stream__(self) -> AsyncIterator[ResponseT]: + cast_to = self._cast_to + response = self.response + process_data = self._client._process_response_data + + async for sse in self._iter_events(): + if sse.data.startswith("[DONE]"): + break + + if sse.event is None: + data = sse.json() + if is_mapping(data) and data.get("error"): + raise APIError( + message="An error ocurred during streaming", + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) + + +class ServerSentEvent: + def __init__( + self, + *, + event: str | None = None, + data: str | None = None, + id: str | None = None, + retry: int | None = None, + ) -> None: + if data is None: + data = "" + + self._id = id + self._data = data + self._event = event or None + self._retry = retry + + @property + def event(self) -> str | None: + return self._event + + @property + def id(self) -> str | None: + return self._id + + @property + def retry(self) -> int | None: + return self._retry + + @property + def data(self) -> str: + return self._data + + def json(self) -> Any: + return json.loads(self.data) + + @override + def __repr__(self) -> str: + return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" + + +class SSEDecoder: + _data: list[str] + _event: str | None + _retry: int | None + _last_event_id: str | None + + def __init__(self) -> None: + self._event = None + self._data = [] + self._last_event_id = None + self._retry = None + + def iter(self, iterator: Iterator[str]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields lines, iterate over it & yield every event encountered""" + for line in iterator: + line = line.rstrip("\n") + sse = self.decode(line) + if sse is not None: + yield sse + + async def aiter(self, iterator: AsyncIterator[str]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields lines, iterate over it & yield every event encountered""" + async for line in iterator: + line = line.rstrip("\n") + sse = self.decode(line) + if sse is not None: + yield sse + + def decode(self, line: str) -> ServerSentEvent | None: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = None + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None diff --git a/src/openai/_types.py b/src/openai/_types.py new file mode 100644 index 0000000000..dabd15866f --- /dev/null +++ b/src/openai/_types.py @@ -0,0 +1,343 @@ +from __future__ import annotations + +from os import PathLike +from abc import ABC, abstractmethod +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + TypeVar, + Callable, + Iterator, + Optional, + Sequence, + AsyncIterator, +) +from typing_extensions import ( + Literal, + Protocol, + TypeAlias, + TypedDict, + override, + runtime_checkable, +) + +import pydantic +from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport + +if TYPE_CHECKING: + from ._models import BaseModel + +Transport = BaseTransport +AsyncTransport = AsyncBaseTransport +Query = Mapping[str, object] +Body = object +AnyMapping = Mapping[str, object] +ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) +_T = TypeVar("_T") + + +class BinaryResponseContent(ABC): + def __init__( + self, + response: Any, + ) -> None: + ... + + @property + @abstractmethod + def content(self) -> bytes: + pass + + @property + @abstractmethod + def text(self) -> str: + pass + + @property + @abstractmethod + def encoding(self) -> Optional[str]: + """ + Return an encoding to use for decoding the byte content into text. + The priority for determining this is given by... + + * `.encoding = <>` has been set explicitly. + * The encoding as specified by the charset parameter in the Content-Type header. + * The encoding as determined by `default_encoding`, which may either be + a string like "utf-8" indicating the encoding to use, or may be a callable + which enables charset autodetection. + """ + pass + + @property + @abstractmethod + def charset_encoding(self) -> Optional[str]: + """ + Return the encoding, as specified by the Content-Type header. + """ + pass + + @abstractmethod + def json(self, **kwargs: Any) -> Any: + pass + + @abstractmethod + def read(self) -> bytes: + """ + Read and return the response content. + """ + pass + + @abstractmethod + def iter_bytes(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + This allows us to handle gzip, deflate, and brotli encoded responses. + """ + pass + + @abstractmethod + def iter_text(self, chunk_size: Optional[int] = None) -> Iterator[str]: + """ + A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + pass + + @abstractmethod + def iter_lines(self) -> Iterator[str]: + pass + + @abstractmethod + def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: + """ + A byte-iterator over the raw response content. + """ + pass + + @abstractmethod + def stream_to_file(self, file: str | PathLike[str]) -> None: + """ + Stream the output to the given file. + """ + pass + + @abstractmethod + def close(self) -> None: + """ + Close the response and release the connection. + Automatically called if the response body is read to completion. + """ + pass + + @abstractmethod + async def aread(self) -> bytes: + """ + Read and return the response content. + """ + pass + + @abstractmethod + async def aiter_bytes(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + This allows us to handle gzip, deflate, and brotli encoded responses. + """ + pass + + @abstractmethod + async def aiter_text(self, chunk_size: Optional[int] = None) -> AsyncIterator[str]: + """ + A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + pass + + @abstractmethod + async def aiter_lines(self) -> AsyncIterator[str]: + pass + + @abstractmethod + async def aiter_raw(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]: + """ + A byte-iterator over the raw response content. + """ + pass + + async def astream_to_file(self, file: str | PathLike[str]) -> None: + """ + Stream the output to the given file. + """ + pass + + @abstractmethod + async def aclose(self) -> None: + """ + Close the response and release the connection. + Automatically called if the response body is read to completion. + """ + pass + + +# Approximates httpx internal ProxiesTypes and RequestFiles types +# while adding support for `PathLike` instances +ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] +ProxiesTypes = Union[str, Proxy, ProxiesDict] +if TYPE_CHECKING: + FileContent = Union[IO[bytes], bytes, PathLike[str]] +else: + FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +# duplicate of the above but without our custom file support +HttpxFileContent = Union[IO[bytes], bytes] +HttpxFileTypes = Union[ + # file (or bytes) + HttpxFileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], HttpxFileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], HttpxFileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], +] +HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] + +# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT +# where ResponseT includes `None`. In order to support directly +# passing `None`, overloads would have to be defined for every +# method that uses `ResponseT` which would lead to an unacceptable +# amount of code duplication and make it unreadable. See _base_client.py +# for example usage. +# +# This unfortunately means that you will either have +# to import this type and pass it explicitly: +# +# from openai import NoneType +# client.get('/foo', cast_to=NoneType) +# +# or build it yourself: +# +# client.get('/foo', cast_to=type(None)) +if TYPE_CHECKING: + NoneType: Type[None] +else: + NoneType = type(None) + + +class RequestOptions(TypedDict, total=False): + headers: Headers + max_retries: int + timeout: float | Timeout | None + params: Query + extra_json: AnyMapping + idempotency_key: str + + +# Sentinel class used when the response type is an object with an unknown schema +class UnknownResponse: + ... + + +# Sentinel class used until PEP 0661 is accepted +class NotGiven: + """ + A sentinel singleton class used to distinguish omitted keyword arguments + from those passed in with the value None (which may have different behavior). + + For example: + + ```py + def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + + get(timout=1) # 1s timeout + get(timout=None) # No timeout + get() # Default timeout behavior, which may not be statically known at the method definition. + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + @override + def __repr__(self) -> str: + return "NOT_GIVEN" + + +NotGivenOr = Union[_T, NotGiven] +NOT_GIVEN = NotGiven() + + +class Omit: + """In certain situations you need to be able to represent a case where a default value has + to be explicitly removed and `None` is not an appropriate substitute, for example: + + ```py + # as the default `Content-Type` header is `application/json` that will be sent + client.post('/upload/files', files={'file': b'my raw file content'}) + + # you can't explicitly override the header as it has to be dynamically generated + # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' + client.post(..., headers={'Content-Type': 'multipart/form-data'}) + + # instead you can remove the default `application/json` header by passing Omit + client.post(..., headers={'Content-Type': Omit()}) + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + +@runtime_checkable +class ModelBuilderProtocol(Protocol): + @classmethod + def build( + cls: type[_T], + *, + response: Response, + data: object, + ) -> _T: + ... + + +Headers = Mapping[str, Union[str, Omit]] + + +class HeadersLikeProtocol(Protocol): + def get(self, __key: str) -> str | None: + ... + + +HeadersLike = Union[Headers, HeadersLikeProtocol] + +ResponseT = TypeVar( + "ResponseT", + bound="Union[str, None, BaseModel, List[Any], Dict[str, Any], Response, UnknownResponse, ModelBuilderProtocol, BinaryResponseContent]", +) + +StrBytesIntFloat = Union[str, bytes, int, float] + +# Note: copied from Pydantic +# https://github.com/pydantic/pydantic/blob/32ea570bf96e84234d2992e1ddf40ab8a565925a/pydantic/main.py#L49 +IncEx: TypeAlias = "set[int] | set[str] | dict[int, Any] | dict[str, Any] | None" + +PostParser = Callable[[Any], Any] diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py new file mode 100644 index 0000000000..d3397212de --- /dev/null +++ b/src/openai/_utils/__init__.py @@ -0,0 +1,36 @@ +from ._proxy import LazyProxy as LazyProxy +from ._utils import flatten as flatten +from ._utils import is_dict as is_dict +from ._utils import is_list as is_list +from ._utils import is_given as is_given +from ._utils import is_tuple as is_tuple +from ._utils import is_mapping as is_mapping +from ._utils import is_tuple_t as is_tuple_t +from ._utils import parse_date as parse_date +from ._utils import is_sequence as is_sequence +from ._utils import coerce_float as coerce_float +from ._utils import is_list_type as is_list_type +from ._utils import is_mapping_t as is_mapping_t +from ._utils import removeprefix as removeprefix +from ._utils import removesuffix as removesuffix +from ._utils import extract_files as extract_files +from ._utils import is_sequence_t as is_sequence_t +from ._utils import is_union_type as is_union_type +from ._utils import required_args as required_args +from ._utils import coerce_boolean as coerce_boolean +from ._utils import coerce_integer as coerce_integer +from ._utils import file_from_path as file_from_path +from ._utils import parse_datetime as parse_datetime +from ._utils import strip_not_given as strip_not_given +from ._utils import deepcopy_minimal as deepcopy_minimal +from ._utils import extract_type_arg as extract_type_arg +from ._utils import is_required_type as is_required_type +from ._utils import is_annotated_type as is_annotated_type +from ._utils import maybe_coerce_float as maybe_coerce_float +from ._utils import get_required_header as get_required_header +from ._utils import maybe_coerce_boolean as maybe_coerce_boolean +from ._utils import maybe_coerce_integer as maybe_coerce_integer +from ._utils import strip_annotated_type as strip_annotated_type +from ._transform import PropertyInfo as PropertyInfo +from ._transform import transform as transform +from ._transform import maybe_transform as maybe_transform diff --git a/src/openai/_utils/_logs.py b/src/openai/_utils/_logs.py new file mode 100644 index 0000000000..e5113fd8c0 --- /dev/null +++ b/src/openai/_utils/_logs.py @@ -0,0 +1,25 @@ +import os +import logging + +logger: logging.Logger = logging.getLogger("openai") +httpx_logger: logging.Logger = logging.getLogger("httpx") + + +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) diff --git a/src/openai/_utils/_proxy.py b/src/openai/_utils/_proxy.py new file mode 100644 index 0000000000..aa934a3fbc --- /dev/null +++ b/src/openai/_utils/_proxy.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Generic, TypeVar, Iterable, cast +from typing_extensions import ClassVar, override + +T = TypeVar("T") + + +class LazyProxy(Generic[T], ABC): + """Implements data methods to pretend that an instance is another instance. + + This includes forwarding attribute access and othe methods. + """ + + should_cache: ClassVar[bool] = False + + def __init__(self) -> None: + self.__proxied: T | None = None + + def __getattr__(self, attr: str) -> object: + return getattr(self.__get_proxied__(), attr) + + @override + def __repr__(self) -> str: + return repr(self.__get_proxied__()) + + @override + def __str__(self) -> str: + return str(self.__get_proxied__()) + + @override + def __dir__(self) -> Iterable[str]: + return self.__get_proxied__().__dir__() + + @property # type: ignore + @override + def __class__(self) -> type: + return self.__get_proxied__().__class__ + + def __get_proxied__(self) -> T: + if not self.should_cache: + return self.__load__() + + proxied = self.__proxied + if proxied is not None: + return proxied + + self.__proxied = proxied = self.__load__() + return proxied + + def __set_proxied__(self, value: T) -> None: + self.__proxied = value + + def __as_proxied__(self) -> T: + """Helper method that returns the current proxy, typed as the loaded object""" + return cast(T, self) + + @abstractmethod + def __load__(self) -> T: + ... diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py new file mode 100644 index 0000000000..db40bff27f --- /dev/null +++ b/src/openai/_utils/_transform.py @@ -0,0 +1,214 @@ +from __future__ import annotations + +from typing import Any, List, Mapping, TypeVar, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, override, get_type_hints + +import pydantic + +from ._utils import ( + is_list, + is_mapping, + is_list_type, + is_union_type, + extract_type_arg, + 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", "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 + + def __init__( + self, + *, + alias: str | None = None, + format: PropertyFormat | None = None, + format_template: str | None = None, + ) -> None: + self.alias = alias + self.format = format + self.format_template = format_template + + @override + def __repr__(self) -> str: + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}')" + + +def maybe_transform( + data: Mapping[str, object] | List[Any] | None, + 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_annoted_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_annoted_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 is_list_type(stripped_type) and is_list(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, exclude_defaults=True) + + return _transform_value(data, annotation) + + +def _transform_value(data: object, type_: type) -> object: + annotated_type = _get_annoted_type(type_) + 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) + + 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 diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py new file mode 100644 index 0000000000..4b51dcb2e8 --- /dev/null +++ b/src/openai/_utils/_utils.py @@ -0,0 +1,408 @@ +from __future__ import annotations + +import os +import re +import inspect +import functools +from typing import ( + Any, + Tuple, + Mapping, + TypeVar, + Callable, + Iterable, + Sequence, + cast, + overload, +) +from pathlib import Path +from typing_extensions import Required, Annotated, TypeGuard, get_args, get_origin + +from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike +from .._compat import is_union as _is_union +from .._compat import parse_date as parse_date +from .._compat import 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]) + + +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_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_union_type(typ: type) -> bool: + return _is_union(get_origin(typ)) + + +def is_required_type(typ: type) -> bool: + return get_origin(typ) == Required + + +# 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: + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") + + +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 "'" + 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") + + 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: + # 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 isinstance(headers, Mapping): + headers = cast(Headers, headers) + for k, v in headers.items(): + 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") diff --git a/src/openai/_version.py b/src/openai/_version.py new file mode 100644 index 0000000000..e9a3efc55c --- /dev/null +++ b/src/openai/_version.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. + +__title__ = "openai" +__version__ = "1.0.0" diff --git a/src/openai/cli/__init__.py b/src/openai/cli/__init__.py new file mode 100644 index 0000000000..d453d5e179 --- /dev/null +++ b/src/openai/cli/__init__.py @@ -0,0 +1 @@ +from ._cli import main as main diff --git a/src/openai/cli/_api/__init__.py b/src/openai/cli/_api/__init__.py new file mode 100644 index 0000000000..56a0260a6d --- /dev/null +++ b/src/openai/cli/_api/__init__.py @@ -0,0 +1 @@ +from ._main import register_commands as register_commands diff --git a/src/openai/cli/_api/_main.py b/src/openai/cli/_api/_main.py new file mode 100644 index 0000000000..fe5a5e6fc0 --- /dev/null +++ b/src/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/src/openai/cli/_api/audio.py b/src/openai/cli/_api/audio.py new file mode 100644 index 0000000000..eaf57748ad --- /dev/null +++ b/src/openai/cli/_api/audio.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +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 + +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 = get_client().audio.transcriptions.create( + 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), + ) + 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 = get_client().audio.translations.create( + 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), + ) + print_model(model) diff --git a/src/openai/cli/_api/chat/__init__.py b/src/openai/cli/_api/chat/__init__.py new file mode 100644 index 0000000000..87d971630a --- /dev/null +++ b/src/openai/cli/_api/chat/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from argparse import ArgumentParser + +from . import completions + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + completions.register(subparser) diff --git a/src/openai/cli/_api/chat/completions.py b/src/openai/cli/_api/chat/completions.py new file mode 100644 index 0000000000..e7566b143d --- /dev/null +++ b/src/openai/cli/_api/chat/completions.py @@ -0,0 +1,154 @@ +from __future__ import annotations + +import sys +from typing import TYPE_CHECKING, List, Optional, cast +from argparse import ArgumentParser +from typing_extensions import NamedTuple + +from ..._utils import get_client +from ..._models import BaseModel +from ...._streaming import Stream +from ....types.chat import ( + ChatCompletionRole, + ChatCompletionChunk, + CompletionCreateParams, +) +from ....types.chat.completion_create_params import ( + CompletionCreateParamsStreaming, + CompletionCreateParamsNonStreaming, +) + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + sub = subparser.add_parser("chat.completions.create") + + sub._action_groups.pop() + req = sub.add_argument_group("required arguments") + opt = sub.add_argument_group("optional arguments") + + req.add_argument( + "-g", + "--message", + action="http://github.com/append", + nargs=2, + metavar=("ROLE", "CONTENT"), + help="A message in `{role} {content}` format. Use this argument multiple times to add multiple messages.", + required=True, + ) + req.add_argument( + "-m", + "--model", + help="The model to use.", + required=True, + ) + + opt.add_argument( + "-n", + "--n", + help="How many completions to generate for the conversation.", + type=int, + ) + opt.add_argument("-M", "--max-tokens", help="The maximum number of tokens to generate.", type=int) + opt.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, + ) + opt.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, + ) + opt.add_argument( + "--stop", + help="A stop sequence at which to stop generating tokens for the message.", + ) + opt.add_argument("--stream", help="Stream messages as they're ready.", action="http://github.com/store_true") + sub.set_defaults(func=CLIChatCompletion.create, args_model=CLIChatCompletionCreateArgs) + + +class CLIMessage(NamedTuple): + role: ChatCompletionRole + content: str + + +class CLIChatCompletionCreateArgs(BaseModel): + message: List[CLIMessage] + model: str + n: Optional[int] = None + max_tokens: Optional[int] = None + temperature: Optional[float] = None + top_p: Optional[float] = None + stop: Optional[str] = None + stream: bool = False + + +class CLIChatCompletion: + @staticmethod + def create(args: CLIChatCompletionCreateArgs) -> None: + params: CompletionCreateParams = { + "model": args.model, + "messages": [{"role": message.role, "content": message.content} for message in args.message], + "n": args.n, + "temperature": args.temperature, + "top_p": args.top_p, + "stop": args.stop, + # type checkers are not good at inferring union types so we have to set stream afterwards + "stream": False, + } + if args.stream: + params["stream"] = args.stream # type: ignore + if args.max_tokens is not None: + params["max_tokens"] = args.max_tokens + + if args.stream: + return CLIChatCompletion._stream_create(cast(CompletionCreateParamsStreaming, params)) + + return CLIChatCompletion._create(cast(CompletionCreateParamsNonStreaming, params)) + + @staticmethod + def _create(params: CompletionCreateParamsNonStreaming) -> None: + completion = get_client().chat.completions.create(**params) + should_print_header = len(completion.choices) > 1 + for choice in completion.choices: + if should_print_header: + sys.stdout.write("===== Chat Completion {} =====\n".format(choice.index)) + + content = choice.message.content if choice.message.content is not None else "None" + sys.stdout.write(content) + + if should_print_header or not content.endswith("\n"): + sys.stdout.write("\n") + + sys.stdout.flush() + + @staticmethod + def _stream_create(params: CompletionCreateParamsStreaming) -> None: + # cast is required for mypy + stream = cast( # pyright: ignore[reportUnnecessaryCast] + Stream[ChatCompletionChunk], get_client().chat.completions.create(**params) + ) + for chunk in stream: + should_print_header = len(chunk.choices) > 1 + for choice in chunk.choices: + if should_print_header: + sys.stdout.write("===== Chat Completion {} =====\n".format(choice.index)) + + content = choice.delta.content or "" + sys.stdout.write(content) + + if should_print_header: + sys.stdout.write("\n") + + sys.stdout.flush() + + sys.stdout.write("\n") diff --git a/src/openai/cli/_api/completions.py b/src/openai/cli/_api/completions.py new file mode 100644 index 0000000000..ce1036b224 --- /dev/null +++ b/src/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="http://github.com/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 probabilites 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="http://github.com/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/src/openai/cli/_api/files.py b/src/openai/cli/_api/files.py new file mode 100644 index 0000000000..ae6dadf0f1 --- /dev/null +++ b/src/openai/cli/_api/files.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +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), purpose=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/src/openai/cli/_api/image.py b/src/openai/cli/_api/image.py new file mode 100644 index 0000000000..e6149eeac4 --- /dev/null +++ b/src/openai/cli/_api/image.py @@ -0,0 +1,130 @@ +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("-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("-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("-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 + + +class CLIImageCreateVariationArgs(BaseModel): + image: str + num_images: int + size: str + response_format: str + + +class CLIImageEditArgs(BaseModel): + image: str + num_images: int + size: str + response_format: str + prompt: str + mask: NotGivenOr[str] = NOT_GIVEN + + +class CLIImage: + @staticmethod + def create(args: CLIImageCreateArgs) -> None: + image = get_client().images.generate( + 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( + 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( + 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/src/openai/cli/_api/models.py b/src/openai/cli/_api/models.py new file mode 100644 index 0000000000..017218fa6e --- /dev/null +++ b/src/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/src/openai/cli/_cli.py b/src/openai/cli/_cli.py new file mode 100644 index 0000000000..72e5c923bd --- /dev/null +++ b/src/openai/cli/_cli.py @@ -0,0 +1,234 @@ +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 .._types import ProxiesDict +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="http://github.com/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="http://github.com/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: ProxiesDict = {} + 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] = proxy + + http_client = httpx.Client( + proxies=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/src/openai/cli/_errors.py b/src/openai/cli/_errors.py new file mode 100644 index 0000000000..ac2a3780d0 --- /dev/null +++ b/src/openai/cli/_errors.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +import sys + +import pydantic + +from ._utils import Colours, 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(), Colours.FAIL, Colours.ENDC, err)) diff --git a/src/openai/cli/_models.py b/src/openai/cli/_models.py new file mode 100644 index 0000000000..5583db2609 --- /dev/null +++ b/src/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/src/openai/cli/_progress.py b/src/openai/cli/_progress.py new file mode 100644 index 0000000000..390aaa9dfe --- /dev/null +++ b/src/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)) + + 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/src/openai/cli/_tools/__init__.py b/src/openai/cli/_tools/__init__.py new file mode 100644 index 0000000000..56a0260a6d --- /dev/null +++ b/src/openai/cli/_tools/__init__.py @@ -0,0 +1 @@ +from ._main import register_commands as register_commands diff --git a/src/openai/cli/_tools/_main.py b/src/openai/cli/_tools/_main.py new file mode 100644 index 0000000000..bd6cda408f --- /dev/null +++ b/src/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/src/openai/cli/_tools/fine_tunes.py b/src/openai/cli/_tools/fine_tunes.py new file mode 100644 index 0000000000..2128b88952 --- /dev/null +++ b/src/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="http://github.com/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/src/openai/cli/_tools/migrate.py b/src/openai/cli/_tools/migrate.py new file mode 100644 index 0000000000..714bead8e3 --- /dev/null +++ b/src/openai/cli/_tools/migrate.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +import os +import sys +import json +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() + + +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() + + +# 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") + + platform = "macos" if sys.platform == "darwin" else "linux" + + dir_name = _cache_dir() / "openai-python" + install_dir = dir_name / ".install" + target_dir = install_dir / "bin" + + target_path = target_dir / "marzano" + temp_file = target_dir / "marzano.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"marzano-{platform}-{arch}" + meta_url = f"https://api.keygen.sh/v1/accounts/%7BKEYGEN_ACCOUNT%7D/artifacts/%7Bfile_name%7D" + + sys.stdout.write(f"Retrieving Grit CLI metadata from {meta_url}\n") + with httpx.Client() as client: + response = client.get(meta_url) # pyright: ignore[reportUnknownMemberType] + + data = response.json() + errors = data.get("errors") + if errors: + for error in errors: + sys.stdout.write(f"{error}\n") + + raise CLIError("Could not locate Grit CLI binary - see above errors") + + write_manifest(install_dir, data["data"]["relationships"]["release"]["data"]["id"]) + + link = data["data"]["links"]["redirect"] + _debug(f"Redirect URL {link}") + + download_response = client.get(link) # pyright: ignore[reportUnknownMemberType] + 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: + archive.extractall(unpacked_dir) + + for item in unpacked_dir.iterdir(): + item.rename(target_dir / item.name) + + shutil.rmtree(unpacked_dir) + os.remove(temp_file) + os.chmod(target_path, 0o755) + + sys.stdout.flush() + + return target_path + + +def _get_arch() -> str: + architecture = platform.machine().lower() + + # Map the architecture names to Node.js equivalents + arch_map = { + "x86_64": "x64", + "amd64": "x64", + "armv7l": "arm", + "aarch64": "arm64", + } + + return arch_map.get(architecture, architecture) + + +def write_manifest(install_path: Path, release: str) -> None: + manifest = { + "installPath": str(install_path), + "binaries": { + "marzano": { + "name": "marzano", + "release": release, + }, + }, + } + manifest_path = Path(install_path) / "manifests.json" + with open(manifest_path, "w") as f: + json.dump(manifest, f, indent=2) diff --git a/src/openai/cli/_utils.py b/src/openai/cli/_utils.py new file mode 100644 index 0000000000..027ab08de3 --- /dev/null +++ b/src/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 Colours: + 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/openai/validators.py b/src/openai/lib/_validators.py similarity index 80% rename from openai/validators.py rename to src/openai/lib/_validators.py index 078179a44b..8e4ed3c9f4 100644 --- a/openai/validators.py +++ b/src/openai/lib/_validators.py @@ -1,9 +1,12 @@ +# pyright: basic +from __future__ import annotations + import os import sys -from typing import Any, Callable, NamedTuple, Optional +from typing import Any, TypeVar, Callable, Optional, NamedTuple +from typing_extensions import TypeAlias -from openai.datalib.pandas_helper import assert_has_pandas -from openai.datalib.pandas_helper import pandas as pd +from .._extras import pandas as pd class Remediation(NamedTuple): @@ -16,7 +19,10 @@ class Remediation(NamedTuple): error_msg: Optional[str] = None -def num_examples_validator(df): +OptionalDataFrameT = TypeVar("OptionalDataFrameT", bound="Optional[pd.DataFrame]") + + +def num_examples_validator(df: pd.DataFrame) -> Remediation: """ This validator will only print out the number of examples and recommend to the user to increase the number of examples if less than 100. """ @@ -26,18 +32,16 @@ def num_examples_validator(df): if len(df) >= MIN_EXAMPLES else ". In general, we recommend having at least a few hundred examples. We've found that performance tends to linearly increase for every doubling of the number of examples" ) - immediate_msg = ( - f"\n- Your file contains {len(df)} prompt-completion pairs{optional_suggestion}" - ) + immediate_msg = f"\n- Your file contains {len(df)} prompt-completion pairs{optional_suggestion}" return Remediation(name="num_examples", immediate_msg=immediate_msg) -def necessary_column_validator(df, necessary_column): +def necessary_column_validator(df: pd.DataFrame, necessary_column: str) -> Remediation: """ This validator will ensure that the necessary column is present in the dataframe. """ - def lower_case_column(df, column): + def lower_case_column(df: pd.DataFrame, column: Any) -> pd.DataFrame: cols = [c for c in df.columns if str(c).lower() == column] df.rename(columns={cols[0]: column.lower()}, inplace=True) return df @@ -50,13 +54,11 @@ def lower_case_column(df, column): if necessary_column not in df.columns: if necessary_column in [str(c).lower() for c in df.columns]: - def lower_case_column_creator(df): + def lower_case_column_creator(df: pd.DataFrame) -> pd.DataFrame: return lower_case_column(df, necessary_column) necessary_fn = lower_case_column_creator - immediate_msg = ( - f"\n- The `{necessary_column}` column/key should be lowercase" - ) + immediate_msg = f"\n- The `{necessary_column}` column/key should be lowercase" necessary_msg = f"Lower case column name to `{necessary_column}`" else: error_msg = f"`{necessary_column}` column/key is missing. Please make sure you name your columns/keys appropriately, then retry" @@ -70,14 +72,15 @@ def lower_case_column_creator(df): ) -def additional_column_validator(df, fields=["prompt", "completion"]): +def additional_column_validator(df: pd.DataFrame, fields: list[str] = ["prompt", "completion"]) -> Remediation: """ This validator will remove additional columns from the dataframe. """ additional_columns = [] necessary_msg = None immediate_msg = None - necessary_fn = None + necessary_fn = None # type: ignore + if len(df.columns) > 2: additional_columns = [c for c in df.columns if c not in fields] warn_message = "" @@ -88,7 +91,7 @@ def additional_column_validator(df, fields=["prompt", "completion"]): immediate_msg = f"\n- The input file should contain exactly two columns/keys per row. Additional columns/keys present are: {additional_columns}{warn_message}" necessary_msg = f"Remove additional columns/keys: {additional_columns}" - def necessary_fn(x): + def necessary_fn(x: Any) -> Any: return x[fields] return Remediation( @@ -99,12 +102,12 @@ def necessary_fn(x): ) -def non_empty_field_validator(df, field="completion"): +def non_empty_field_validator(df: pd.DataFrame, field: str = "completion") -> Remediation: """ This validator will ensure that no completion is empty. """ necessary_msg = None - necessary_fn = None + necessary_fn = None # type: ignore immediate_msg = None if df[field].apply(lambda x: x == "").any() or df[field].isnull().any(): @@ -112,10 +115,11 @@ def non_empty_field_validator(df, field="completion"): empty_indexes = df.reset_index().index[empty_rows].tolist() immediate_msg = f"\n- `{field}` column/key should not contain empty strings. These are rows: {empty_indexes}" - def necessary_fn(x): + def necessary_fn(x: Any) -> Any: return x[x[field] != ""].dropna(subset=[field]) necessary_msg = f"Remove {len(empty_indexes)} rows with empty {field}s" + return Remediation( name=f"empty_{field}", immediate_msg=immediate_msg, @@ -124,7 +128,7 @@ def necessary_fn(x): ) -def duplicated_rows_validator(df, fields=["prompt", "completion"]): +def duplicated_rows_validator(df: pd.DataFrame, fields: list[str] = ["prompt", "completion"]) -> Remediation: """ This validator will suggest to the user to remove duplicate rows if they exist. """ @@ -132,13 +136,13 @@ def duplicated_rows_validator(df, fields=["prompt", "completion"]): duplicated_indexes = df.reset_index().index[duplicated_rows].tolist() immediate_msg = None optional_msg = None - optional_fn = None + optional_fn = None # type: ignore if len(duplicated_indexes) > 0: immediate_msg = f"\n- There are {len(duplicated_indexes)} duplicated {'-'.join(fields)} sets. These are rows: {duplicated_indexes}" optional_msg = f"Remove {len(duplicated_indexes)} duplicate rows" - def optional_fn(x): + def optional_fn(x: Any) -> Any: return x.drop_duplicates(subset=fields) return Remediation( @@ -149,21 +153,19 @@ def optional_fn(x): ) -def long_examples_validator(df): +def long_examples_validator(df: pd.DataFrame) -> Remediation: """ This validator will suggest to the user to remove examples that are too long. """ immediate_msg = None optional_msg = None - optional_fn = None + optional_fn = None # type: ignore ft_type = infer_task_type(df) if ft_type != "open-ended generation": - def get_long_indexes(d): - long_examples = d.apply( - lambda x: len(x.prompt) + len(x.completion) > 10000, axis=1 - ) + def get_long_indexes(d: pd.DataFrame) -> Any: + long_examples = d.apply(lambda x: len(x.prompt) + len(x.completion) > 10000, axis=1) return d.reset_index().index[long_examples].tolist() long_indexes = get_long_indexes(df) @@ -172,8 +174,7 @@ def get_long_indexes(d): immediate_msg = f"\n- There are {len(long_indexes)} examples that are very long. These are rows: {long_indexes}\nFor conditional generation, and for classification the examples shouldn't be longer than 2048 tokens." optional_msg = f"Remove {len(long_indexes)} long examples" - def optional_fn(x): - + def optional_fn(x: Any) -> Any: long_indexes_to_drop = get_long_indexes(x) if long_indexes != long_indexes_to_drop: sys.stdout.write( @@ -189,14 +190,14 @@ def optional_fn(x): ) -def common_prompt_suffix_validator(df): +def common_prompt_suffix_validator(df: pd.DataFrame) -> Remediation: """ This validator will suggest to add a common suffix to the prompt if one doesn't already exist in case of classification or conditional generation. """ error_msg = None immediate_msg = None optional_msg = None - optional_fn = None + optional_fn = None # type: ignore # Find a suffix which is not contained within the prompt otherwise suggested_suffix = "\n\n### =>\n\n" @@ -222,7 +223,7 @@ def common_prompt_suffix_validator(df): if ft_type == "open-ended generation": return Remediation(name="common_suffix") - def add_suffix(x, suffix): + def add_suffix(x: Any, suffix: Any) -> Any: x["prompt"] += suffix return x @@ -233,27 +234,19 @@ def add_suffix(x, suffix): if common_suffix != "": common_suffix_new_line_handled = common_suffix.replace("\n", "\\n") - immediate_msg = ( - f"\n- All prompts end with suffix `{common_suffix_new_line_handled}`" - ) + immediate_msg = f"\n- All prompts end with suffix `{common_suffix_new_line_handled}`" if len(common_suffix) > 10: immediate_msg += f". This suffix seems very long. Consider replacing with a shorter suffix, such as `{display_suggested_suffix}`" - if ( - df.prompt.str[: -len(common_suffix)] - .str.contains(common_suffix, regex=False) - .any() - ): + if df.prompt.str[: -len(common_suffix)].str.contains(common_suffix, regex=False).any(): immediate_msg += f"\n WARNING: Some of your prompts contain the suffix `{common_suffix}` more than once. We strongly suggest that you review your prompts and add a unique suffix" else: immediate_msg = "\n- Your data does not contain a common separator at the end of your prompts. Having a separator string appended to the end of the prompt makes it clearer to the fine-tuned model where the completion should begin. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples. If you intend to do open-ended generation, then you should leave the prompts empty" if common_suffix == "": - optional_msg = ( - f"Add a suffix separator `{display_suggested_suffix}` to all prompts" - ) + optional_msg = f"Add a suffix separator `{display_suggested_suffix}` to all prompts" - def optional_fn(x): + def optional_fn(x: Any) -> Any: return add_suffix(x, suggested_suffix) return Remediation( @@ -265,7 +258,7 @@ def optional_fn(x): ) -def common_prompt_prefix_validator(df): +def common_prompt_prefix_validator(df: pd.DataFrame) -> Remediation: """ This validator will suggest to remove a common prefix from the prompt if a long one exist. """ @@ -273,13 +266,13 @@ def common_prompt_prefix_validator(df): immediate_msg = None optional_msg = None - optional_fn = None + optional_fn = None # type: ignore common_prefix = get_common_xfix(df.prompt, xfix="prefix") if common_prefix == "": return Remediation(name="common_prefix") - def remove_common_prefix(x, prefix): + def remove_common_prefix(x: Any, prefix: Any) -> Any: x["prompt"] = x["prompt"].str[len(prefix) :] return x @@ -293,7 +286,7 @@ def remove_common_prefix(x, prefix): immediate_msg += ". Fine-tuning doesn't require the instruction specifying the task, or a few-shot example scenario. Most of the time you should only add the input data into the prompt, and the desired output into the completion" optional_msg = f"Remove prefix `{common_prefix}` from all prompts" - def optional_fn(x): + def optional_fn(x: Any) -> Any: return remove_common_prefix(x, common_prefix) return Remediation( @@ -304,7 +297,7 @@ def optional_fn(x): ) -def common_completion_prefix_validator(df): +def common_completion_prefix_validator(df: pd.DataFrame) -> Remediation: """ This validator will suggest to remove a common prefix from the completion if a long one exist. """ @@ -315,7 +308,7 @@ def common_completion_prefix_validator(df): if len(common_prefix) < MAX_PREFIX_LEN: return Remediation(name="common_prefix") - def remove_common_prefix(x, prefix, ws_prefix): + def remove_common_prefix(x: Any, prefix: Any, ws_prefix: Any) -> Any: x["completion"] = x["completion"].str[len(prefix) :] if ws_prefix: # keep the single whitespace as prefix @@ -329,7 +322,7 @@ def remove_common_prefix(x, prefix, ws_prefix): immediate_msg = f"\n- All completions start with prefix `{common_prefix}`. Most of the time you should only add the output data into the completion, without any prefix" optional_msg = f"Remove prefix `{common_prefix}` from all completions" - def optional_fn(x): + def optional_fn(x: Any) -> Any: return remove_common_prefix(x, common_prefix, ws_prefix) return Remediation( @@ -340,14 +333,14 @@ def optional_fn(x): ) -def common_completion_suffix_validator(df): +def common_completion_suffix_validator(df: pd.DataFrame) -> Remediation: """ This validator will suggest to add a common suffix to the completion if one doesn't already exist in case of classification or conditional generation. """ error_msg = None immediate_msg = None optional_msg = None - optional_fn = None + optional_fn = None # type: ignore ft_type = infer_task_type(df) if ft_type == "open-ended generation" or ft_type == "classification": @@ -378,33 +371,25 @@ def common_completion_suffix_validator(df): break display_suggested_suffix = suggested_suffix.replace("\n", "\\n") - def add_suffix(x, suffix): + def add_suffix(x: Any, suffix: Any) -> Any: x["completion"] += suffix return x if common_suffix != "": common_suffix_new_line_handled = common_suffix.replace("\n", "\\n") - immediate_msg = ( - f"\n- All completions end with suffix `{common_suffix_new_line_handled}`" - ) + immediate_msg = f"\n- All completions end with suffix `{common_suffix_new_line_handled}`" if len(common_suffix) > 10: immediate_msg += f". This suffix seems very long. Consider replacing with a shorter suffix, such as `{display_suggested_suffix}`" - if ( - df.completion.str[: -len(common_suffix)] - .str.contains(common_suffix, regex=False) - .any() - ): + if df.completion.str[: -len(common_suffix)].str.contains(common_suffix, regex=False).any(): immediate_msg += f"\n WARNING: Some of your completions contain the suffix `{common_suffix}` more than once. We suggest that you review your completions and add a unique ending" else: immediate_msg = "\n- Your data does not contain a common ending at the end of your completions. Having a common ending string appended to the end of the completion makes it clearer to the fine-tuned model where the completion should end. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples." if common_suffix == "": - optional_msg = ( - f"Add a suffix ending `{display_suggested_suffix}` to all completions" - ) + optional_msg = f"Add a suffix ending `{display_suggested_suffix}` to all completions" - def optional_fn(x): + def optional_fn(x: Any) -> Any: return add_suffix(x, suggested_suffix) return Remediation( @@ -416,15 +401,13 @@ def optional_fn(x): ) -def completions_space_start_validator(df): +def completions_space_start_validator(df: pd.DataFrame) -> Remediation: """ This validator will suggest to add a space at the start of the completion if it doesn't already exist. This helps with tokenization. """ - def add_space_start(x): - x["completion"] = x["completion"].apply( - lambda x: ("" if x[0] == " " else " ") + x - ) + def add_space_start(x: Any) -> Any: + x["completion"] = x["completion"].apply(lambda x: ("" if x[0] == " " else " ") + x) return x optional_msg = None @@ -443,25 +426,17 @@ def add_space_start(x): ) -def lower_case_validator(df, column): +def lower_case_validator(df: pd.DataFrame, column: Any) -> Remediation | None: """ This validator will suggest to lowercase the column values, if more than a third of letters are uppercase. """ - def lower_case(x): + def lower_case(x: Any) -> Any: x[column] = x[column].str.lower() return x - count_upper = ( - df[column] - .apply(lambda x: sum(1 for c in x if c.isalpha() and c.isupper())) - .sum() - ) - count_lower = ( - df[column] - .apply(lambda x: sum(1 for c in x if c.isalpha() and c.islower())) - .sum() - ) + count_upper = df[column].apply(lambda x: sum(1 for c in x if c.isalpha() and c.isupper())).sum() + count_lower = df[column].apply(lambda x: sum(1 for c in x if c.isalpha() and c.islower())).sum() if count_upper * 2 > count_lower: return Remediation( @@ -470,15 +445,17 @@ def lower_case(x): optional_msg=f"Lowercase all your data in column/key `{column}`", optional_fn=lower_case, ) + return None -def read_any_format(fname, fields=["prompt", "completion"]): +def read_any_format( + fname: str, fields: list[str] = ["prompt", "completion"] +) -> tuple[pd.DataFrame | None, Remediation]: """ This function will read a file saved in .csv, .json, .txt, .xlsx or .tsv format using pandas. - for .xlsx it will read the first sheet - for .txt it will assume completions and split on newline """ - assert_has_pandas() remediation = None necessary_msg = None immediate_msg = None @@ -488,13 +465,11 @@ def read_any_format(fname, fields=["prompt", "completion"]): if os.path.isfile(fname): try: if fname.lower().endswith(".csv") or fname.lower().endswith(".tsv"): - file_extension_str, separator = ( - ("CSV", ",") if fname.lower().endswith(".csv") else ("TSV", "\t") - ) - immediate_msg = f"\n- Based on your file extension, your file is formatted as a {file_extension_str} file" - necessary_msg = ( - f"Your format `{file_extension_str}` will be converted to `JSONL`" + file_extension_str, separator = ("CSV", ",") if fname.lower().endswith(".csv") else ("TSV", "\t") + immediate_msg = ( + f"\n- Based on your file extension, your file is formatted as a {file_extension_str} file" ) + necessary_msg = f"Your format `{file_extension_str}` will be converted to `JSONL`" df = pd.read_csv(fname, sep=separator, dtype=str).fillna("") elif fname.lower().endswith(".xlsx"): immediate_msg = "\n- Based on your file extension, your file is formatted as an Excel file" @@ -505,9 +480,7 @@ def read_any_format(fname, fields=["prompt", "completion"]): immediate_msg += "\n- Your Excel file contains more than one sheet. Please either save as csv or ensure all data is present in the first sheet. WARNING: Reading only the first sheet..." df = pd.read_excel(fname, dtype=str).fillna("") elif fname.lower().endswith(".txt"): - immediate_msg = ( - "\n- Based on your file extension, you provided a text file" - ) + immediate_msg = "\n- Based on your file extension, you provided a text file" necessary_msg = "Your format `TXT` will be converted to `JSONL`" with open(fname, "r") as f: content = f.read() @@ -517,32 +490,32 @@ def read_any_format(fname, fields=["prompt", "completion"]): dtype=str, ).fillna("") elif fname.lower().endswith(".jsonl"): - df = pd.read_json(fname, lines=True, dtype=str).fillna("") - if len(df) == 1: + df = pd.read_json(fname, lines=True, dtype=str).fillna("") # type: ignore + if len(df) == 1: # type: ignore # this is NOT what we expect for a .jsonl file immediate_msg = "\n- Your JSONL file appears to be in a JSON format. Your file will be converted to JSONL format" necessary_msg = "Your format `JSON` will be converted to `JSONL`" - df = pd.read_json(fname, dtype=str).fillna("") + df = pd.read_json(fname, dtype=str).fillna("") # type: ignore else: pass # this is what we expect for a .jsonl file elif fname.lower().endswith(".json"): try: # to handle case where .json file is actually a .jsonl file - df = pd.read_json(fname, lines=True, dtype=str).fillna("") - if len(df) == 1: + df = pd.read_json(fname, lines=True, dtype=str).fillna("") # type: ignore + if len(df) == 1: # type: ignore # this code path corresponds to a .json file that has one line - df = pd.read_json(fname, dtype=str).fillna("") + df = pd.read_json(fname, dtype=str).fillna("") # type: ignore else: # this is NOT what we expect for a .json file immediate_msg = "\n- Your JSON file appears to be in a JSONL format. Your file will be converted to JSONL format" - necessary_msg = ( - "Your format `JSON` will be converted to `JSONL`" - ) + necessary_msg = "Your format `JSON` will be converted to `JSONL`" except ValueError: # this code path corresponds to a .json file that has multiple lines (i.e. it is indented) - df = pd.read_json(fname, dtype=str).fillna("") + df = pd.read_json(fname, dtype=str).fillna("") # type: ignore else: - error_msg = "Your file must have one of the following extensions: .CSV, .TSV, .XLSX, .TXT, .JSON or .JSONL" + error_msg = ( + "Your file must have one of the following extensions: .CSV, .TSV, .XLSX, .TXT, .JSON or .JSONL" + ) if "." in fname: error_msg += f" Your file `{fname}` ends with the extension `.{fname.split('.')[-1]}` which is not supported." else: @@ -564,7 +537,7 @@ def read_any_format(fname, fields=["prompt", "completion"]): return df, remediation -def format_inferrer_validator(df): +def format_inferrer_validator(df: pd.DataFrame) -> Remediation: """ This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification. It will also suggest to use ada and explain train/validation split benefits. @@ -576,14 +549,12 @@ def format_inferrer_validator(df): return Remediation(name="num_examples", immediate_msg=immediate_msg) -def apply_necessary_remediation(df, remediation): +def apply_necessary_remediation(df: OptionalDataFrameT, remediation: Remediation) -> OptionalDataFrameT: """ This function will apply a necessary remediation to a dataframe, or print an error message if one exists. """ if remediation.error_msg is not None: - sys.stderr.write( - f"\n\nERROR in {remediation.name} validator: {remediation.error_msg}\n\nAborting..." - ) + sys.stderr.write(f"\n\nERROR in {remediation.name} validator: {remediation.error_msg}\n\nAborting...") sys.exit(1) if remediation.immediate_msg is not None: sys.stdout.write(remediation.immediate_msg) @@ -592,7 +563,7 @@ def apply_necessary_remediation(df, remediation): return df -def accept_suggestion(input_text, auto_accept): +def accept_suggestion(input_text: str, auto_accept: bool) -> bool: sys.stdout.write(input_text) if auto_accept: sys.stdout.write("Y\n") @@ -600,7 +571,9 @@ def accept_suggestion(input_text, auto_accept): return input().lower() != "n" -def apply_optional_remediation(df, remediation, auto_accept): +def apply_optional_remediation( + df: pd.DataFrame, remediation: Remediation, auto_accept: bool +) -> tuple[pd.DataFrame, bool]: """ This function will apply an optional remediation to a dataframe, based on the user input. """ @@ -608,6 +581,7 @@ def apply_optional_remediation(df, remediation, auto_accept): input_text = f"- [Recommended] {remediation.optional_msg} [Y/n]: " if remediation.optional_msg is not None: if accept_suggestion(input_text, auto_accept): + assert remediation.optional_fn is not None df = remediation.optional_fn(df) optional_applied = True if remediation.necessary_msg is not None: @@ -615,7 +589,7 @@ def apply_optional_remediation(df, remediation, auto_accept): return df, optional_applied -def estimate_fine_tuning_time(df): +def estimate_fine_tuning_time(df: pd.DataFrame) -> None: """ Estimate the time it'll take to fine-tune the dataset """ @@ -628,7 +602,7 @@ def estimate_fine_tuning_time(df): size = df.memory_usage(index=True).sum() expected_time = size * 0.0515 - def format_time(time): + def format_time(time: float) -> str: if time < 60: return f"{round(time, 2)} seconds" elif time < 3600: @@ -644,21 +618,20 @@ def format_time(time): ) -def get_outfnames(fname, split): +def get_outfnames(fname: str, split: bool) -> list[str]: suffixes = ["_train", "_valid"] if split else [""] i = 0 while True: index_suffix = f" ({i})" if i > 0 else "" candidate_fnames = [ - os.path.splitext(fname)[0] + "_prepared" + suffix + index_suffix + ".jsonl" - for suffix in suffixes + os.path.splitext(fname)[0] + "_prepared" + suffix + index_suffix + ".jsonl" for suffix in suffixes ] if not any(os.path.isfile(f) for f in candidate_fnames): return candidate_fnames i += 1 -def get_classification_hyperparams(df): +def get_classification_hyperparams(df: pd.DataFrame) -> tuple[int, object]: n_classes = df.completion.nunique() pos_class = None if n_classes == 2: @@ -666,7 +639,7 @@ def get_classification_hyperparams(df): return n_classes, pos_class -def write_out_file(df, fname, any_remediations, auto_accept): +def write_out_file(df: pd.DataFrame, fname: str, any_remediations: bool, auto_accept: bool) -> None: """ This function will write out a dataframe to a file, if the user would like to proceed, and also offer a fine-tuning command with the newly created file. For classification it will optionally ask the user if they would like to split the data into train/valid files, and modify the suggested command to include the valid set. @@ -683,9 +656,7 @@ def write_out_file(df, fname, any_remediations, auto_accept): additional_params = "" common_prompt_suffix_new_line_handled = common_prompt_suffix.replace("\n", "\\n") - common_completion_suffix_new_line_handled = common_completion_suffix.replace( - "\n", "\\n" - ) + common_completion_suffix_new_line_handled = common_completion_suffix.replace("\n", "\\n") optional_ending_string = ( f' Make sure to include `stop=["{common_completion_suffix_new_line_handled}"]` so that the generated texts ends at the expected place.' if len(common_completion_suffix_new_line_handled) > 0 @@ -708,12 +679,10 @@ def write_out_file(df, fname, any_remediations, auto_accept): n_train = max(len(df) - MAX_VALID_EXAMPLES, int(len(df) * 0.8)) df_train = df.sample(n=n_train, random_state=42) df_valid = df.drop(df_train.index) - df_train[["prompt", "completion"]].to_json( + df_train[["prompt", "completion"]].to_json( # type: ignore fnames[0], lines=True, orient="records", force_ascii=False ) - df_valid[["prompt", "completion"]].to_json( - fnames[1], lines=True, orient="records", force_ascii=False - ) + df_valid[["prompt", "completion"]].to_json(fnames[1], lines=True, orient="records", force_ascii=False) n_classes, pos_class = get_classification_hyperparams(df) additional_params += " --compute_classification_metrics" @@ -723,9 +692,7 @@ def write_out_file(df, fname, any_remediations, auto_accept): additional_params += f" --classification_n_classes {n_classes}" else: assert len(fnames) == 1 - df[["prompt", "completion"]].to_json( - fnames[0], lines=True, orient="records", force_ascii=False - ) + df[["prompt", "completion"]].to_json(fnames[0], lines=True, orient="records", force_ascii=False) # Add -v VALID_FILE if we split the file into train / valid files_string = ("s" if split else "") + " to `" + ("` and `".join(fnames)) @@ -743,7 +710,7 @@ def write_out_file(df, fname, any_remediations, auto_accept): sys.stdout.write("Aborting... did not write the file\n") -def infer_task_type(df): +def infer_task_type(df: pd.DataFrame) -> str: """ Infer the likely fine-tuning task type from the data """ @@ -757,31 +724,28 @@ def infer_task_type(df): return "conditional generation" -def get_common_xfix(series, xfix="suffix"): +def get_common_xfix(series: Any, xfix: str = "suffix") -> str: """ Finds the longest common suffix or prefix of all the values in a series """ common_xfix = "" while True: common_xfixes = ( - series.str[-(len(common_xfix) + 1) :] - if xfix == "suffix" - else series.str[: len(common_xfix) + 1] + series.str[-(len(common_xfix) + 1) :] if xfix == "suffix" else series.str[: len(common_xfix) + 1] ) # first few or last few characters - if ( - common_xfixes.nunique() != 1 - ): # we found the character at which we don't have a unique xfix anymore + if common_xfixes.nunique() != 1: # we found the character at which we don't have a unique xfix anymore break - elif ( - common_xfix == common_xfixes.values[0] - ): # the entire first row is a prefix of every other row + elif common_xfix == common_xfixes.values[0]: # the entire first row is a prefix of every other row break else: # the first or last few characters are still common across all rows - let's try to add one more common_xfix = common_xfixes.values[0] return common_xfix -def get_validators(): +Validator: TypeAlias = "Callable[[pd.DataFrame], Remediation | None]" + + +def get_validators() -> list[Validator]: return [ num_examples_validator, lambda x: necessary_column_validator(x, "prompt"), @@ -802,14 +766,14 @@ def get_validators(): def apply_validators( - df, - fname, - remediation, - validators, - auto_accept, - write_out_file_func, -): - optional_remediations = [] + df: pd.DataFrame, + fname: str, + remediation: Remediation | None, + validators: list[Validator], + auto_accept: bool, + write_out_file_func: Callable[..., Any], +) -> None: + optional_remediations: list[Remediation] = [] if remediation is not None: optional_remediations.append(remediation) for validator in validators: @@ -822,27 +786,18 @@ def apply_validators( [ remediation for remediation in optional_remediations - if remediation.optional_msg is not None - or remediation.necessary_msg is not None + if remediation.optional_msg is not None or remediation.necessary_msg is not None ] ) any_necessary_applied = any( - [ - remediation - for remediation in optional_remediations - if remediation.necessary_msg is not None - ] + [remediation for remediation in optional_remediations if remediation.necessary_msg is not None] ) any_optional_applied = False if any_optional_or_necessary_remediations: - sys.stdout.write( - "\n\nBased on the analysis we will perform the following actions:\n" - ) + sys.stdout.write("\n\nBased on the analysis we will perform the following actions:\n") for remediation in optional_remediations: - df, optional_applied = apply_optional_remediation( - df, remediation, auto_accept - ) + df, optional_applied = apply_optional_remediation(df, remediation, auto_accept) any_optional_applied = any_optional_applied or optional_applied else: sys.stdout.write("\n\nNo remediations found.\n") diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py new file mode 100644 index 0000000000..f5fcd24fd1 --- /dev/null +++ b/src/openai/lib/azure.py @@ -0,0 +1,439 @@ +from __future__ import annotations + +import os +import inspect +from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, overload +from typing_extensions import override + +import httpx + +from .._types import NOT_GIVEN, Omit, Timeout, NotGiven +from .._utils import is_given, is_mapping +from .._client import OpenAI, AsyncOpenAI +from .._models import FinalRequestOptions +from .._streaming import Stream, AsyncStream +from .._exceptions import OpenAIError +from .._base_client import DEFAULT_MAX_RETRIES, BaseClient + +_deployments_endpoints = set( + [ + "/completions", + "/chat/completions", + "/embeddings", + "/audio/transcriptions", + "/audio/translations", + ] +) + + +AzureADTokenProvider = Callable[[], str] +AsyncAzureADTokenProvider = Callable[[], "str | Awaitable[str]"] +_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) +_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) + + +# we need to use a sentinel API key value for Azure AD +# as we don't want to make the `api_key` in the main client Optional +# and Azure AD tokens may be retrieved on a per-request basis +API_KEY_SENTINEL = "".join(["<", "missing API key", ">"]) + + +class MutuallyExclusiveAuthError(OpenAIError): + def __init__(self) -> None: + super().__init__( + "The `api_key`, `azure_ad_token` and `azure_ad_token_provider` arguments are mutually exclusive; Only one can be passed at a time" + ) + + +class BaseAzureClient(BaseClient[_HttpxClientT, _DefaultStreamT]): + @override + def _build_request( + self, + options: FinalRequestOptions, + ) -> httpx.Request: + if options.url in _deployments_endpoints and is_mapping(options.json_data): + model = options.json_data.get("model") + if model is not None and not "/deployments" in str(self.base_url): + options.url = f"/deployments/{model}{options.url}" + + return super()._build_request(options) + + +class AzureOpenAI(BaseAzureClient[httpx.Client, Stream[Any]], OpenAI): + @overload + def __init__( + self, + *, + azure_endpoint: str, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + organization: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + _strict_response_validation: bool = False, + ) -> None: + ... + + @overload + def __init__( + self, + *, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + organization: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + _strict_response_validation: bool = False, + ) -> None: + ... + + @overload + def __init__( + self, + *, + base_url: str, + api_version: str | None = None, + api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + organization: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + _strict_response_validation: bool = False, + ) -> None: + ... + + def __init__( + self, + *, + api_version: str | None = None, + azure_endpoint: str | None = None, + azure_deployment: str | None = None, + api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + organization: str | None = None, + base_url: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + _strict_response_validation: bool = False, + ) -> None: + """Construct a new synchronous azure openai client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `AZURE_OPENAI_API_KEY` + - `organization` from `OPENAI_ORG_ID` + - `azure_ad_token` from `AZURE_OPENAI_AD_TOKEN` + - `api_version` from `OPENAI_API_VERSION` + - `azure_endpoint` from `AZURE_OPENAI_ENDPOINT` + + Args: + azure_endpoint: Your Azure endpoint, including the resource, e.g. `https://example-resource.azure.openai.com/` + + azure_ad_token: Your Azure Active Directory token, https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id + + azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. + + azure_deployment: A model deployment, if given sets the base client URL to include `/deployments/{azure_deployment}`. + Note: this means you won't be able to use non-deployment endpoints. + """ + if api_key is None: + api_key = os.environ.get("AZURE_OPENAI_API_KEY") + + if azure_ad_token is None: + azure_ad_token = os.environ.get("AZURE_OPENAI_AD_TOKEN") + + if api_key is None and azure_ad_token is None and azure_ad_token_provider is None: + raise OpenAIError( + "Missing credentials. Please pass one of `api_key`, `azure_ad_token`, `azure_ad_token_provider`, or the `AZURE_OPENAI_API_KEY` or `AZURE_OPENAI_AD_TOKEN` environment variables." + ) + + if api_version is None: + api_version = os.environ.get("OPENAI_API_VERSION") + + if api_version is None: + raise ValueError( + "Must provide either the `api_version` argument or the `OPENAI_API_VERSION` environment variable" + ) + + if default_query is None: + default_query = {"api-version": api_version} + else: + default_query = {"api-version": api_version, **default_query} + + if base_url is None: + if azure_endpoint is None: + azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT") + + if azure_endpoint is None: + raise ValueError( + "Must provide one of the `base_url` or `azure_endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable" + ) + + if azure_deployment is not None: + base_url = f"{azure_endpoint}/openai/deployments/{azure_deployment}" + else: + base_url = f"{azure_endpoint}/openai" + else: + if azure_endpoint is not None: + raise ValueError("base_url and azure_endpoint are mutually exclusive") + + if api_key is None: + # define a sentinel value to avoid any typing issues + api_key = API_KEY_SENTINEL + + super().__init__( + api_key=api_key, + organization=organization, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + _strict_response_validation=_strict_response_validation, + ) + self._azure_ad_token = azure_ad_token + self._azure_ad_token_provider = azure_ad_token_provider + + def _get_azure_ad_token(self) -> str | None: + if self._azure_ad_token is not None: + return self._azure_ad_token + + provider = self._azure_ad_token_provider + if provider is not None: + token = provider() + if not token or not isinstance(token, str): # pyright: ignore[reportUnnecessaryIsInstance] + raise ValueError( + f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", + ) + return token + + return None + + @override + def _prepare_options(self, options: FinalRequestOptions) -> None: + headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} + options.headers = headers + + azure_ad_token = self._get_azure_ad_token() + if azure_ad_token is not None: + if headers.get("Authorization") is None: + headers["Authorization"] = f"Bearer {azure_ad_token}" + elif self.api_key is not API_KEY_SENTINEL: + if headers.get("api-key") is None: + headers["api-key"] = self.api_key + else: + # should never be hit + raise ValueError("Unable to handle auth") + + return super()._prepare_options(options) + + +class AsyncAzureOpenAI(BaseAzureClient[httpx.AsyncClient, AsyncStream[Any]], AsyncOpenAI): + @overload + def __init__( + self, + *, + azure_endpoint: str, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + organization: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + _strict_response_validation: bool = False, + ) -> None: + ... + + @overload + def __init__( + self, + *, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + organization: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + _strict_response_validation: bool = False, + ) -> None: + ... + + @overload + def __init__( + self, + *, + base_url: str, + api_version: str | None = None, + api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + organization: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + _strict_response_validation: bool = False, + ) -> None: + ... + + def __init__( + self, + *, + azure_endpoint: str | None = None, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + organization: str | None = None, + base_url: str | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + _strict_response_validation: bool = False, + ) -> None: + """Construct a new asynchronous azure openai client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `AZURE_OPENAI_API_KEY` + - `organization` from `OPENAI_ORG_ID` + - `azure_ad_token` from `AZURE_OPENAI_AD_TOKEN` + - `api_version` from `OPENAI_API_VERSION` + - `azure_endpoint` from `AZURE_OPENAI_ENDPOINT` + + Args: + azure_endpoint: Your Azure endpoint, including the resource, e.g. `https://example-resource.azure.openai.com/` + + azure_ad_token: Your Azure Active Directory token, https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id + + azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. + + azure_deployment: A model deployment, if given sets the base client URL to include `/deployments/{azure_deployment}`. + Note: this means you won't be able to use non-deployment endpoints. + """ + if api_key is None: + api_key = os.environ.get("AZURE_OPENAI_API_KEY") + + if azure_ad_token is None: + azure_ad_token = os.environ.get("AZURE_OPENAI_AD_TOKEN") + + if api_key is None and azure_ad_token is None and azure_ad_token_provider is None: + raise OpenAIError( + "Missing credentials. Please pass one of `api_key`, `azure_ad_token`, `azure_ad_token_provider`, or the `AZURE_OPENAI_API_KEY` or `AZURE_OPENAI_AD_TOKEN` environment variables." + ) + + if api_version is None: + api_version = os.environ.get("OPENAI_API_VERSION") + + if api_version is None: + raise ValueError( + "Must provide either the `api_version` argument or the `OPENAI_API_VERSION` environment variable" + ) + + if default_query is None: + default_query = {"api-version": api_version} + else: + default_query = {"api-version": api_version, **default_query} + + if base_url is None: + if azure_endpoint is None: + azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT") + + if azure_endpoint is None: + raise ValueError( + "Must provide one of the `base_url` or `azure_endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable" + ) + + if azure_deployment is not None: + base_url = f"{azure_endpoint}/openai/deployments/{azure_deployment}" + else: + base_url = f"{azure_endpoint}/openai" + else: + if azure_endpoint is not None: + raise ValueError("base_url and azure_endpoint are mutually exclusive") + + if api_key is None: + # define a sentinel value to avoid any typing issues + api_key = API_KEY_SENTINEL + + super().__init__( + api_key=api_key, + organization=organization, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + _strict_response_validation=_strict_response_validation, + ) + self._azure_ad_token = azure_ad_token + self._azure_ad_token_provider = azure_ad_token_provider + + async def _get_azure_ad_token(self) -> str | None: + if self._azure_ad_token is not None: + return self._azure_ad_token + + provider = self._azure_ad_token_provider + if provider is not None: + token = provider() + if inspect.isawaitable(token): + token = await token + if not token or not isinstance(token, str): + raise ValueError( + f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", + ) + return token + + return None + + @override + async def _prepare_options(self, options: FinalRequestOptions) -> None: + headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} + options.headers = headers + + azure_ad_token = await self._get_azure_ad_token() + if azure_ad_token is not None: + if headers.get("Authorization") is None: + headers["Authorization"] = f"Bearer {azure_ad_token}" + elif self.api_key is not API_KEY_SENTINEL: + if headers.get("api-key") is None: + headers["api-key"] = self.api_key + else: + # should never be hit + raise ValueError("Unable to handle auth") + + return await super()._prepare_options(options) diff --git a/src/openai/pagination.py b/src/openai/pagination.py new file mode 100644 index 0000000000..ff45f39517 --- /dev/null +++ b/src/openai/pagination.py @@ -0,0 +1,95 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Any, List, Generic, TypeVar, Optional, cast +from typing_extensions import Protocol, override, runtime_checkable + +from ._types import ModelT +from ._models import BaseModel +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = ["SyncPage", "AsyncPage", "SyncCursorPage", "AsyncCursorPage"] + +_BaseModelT = TypeVar("_BaseModelT", bound=BaseModel) + + +@runtime_checkable +class CursorPageItem(Protocol): + id: str + + +class SyncPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): + """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" + + data: List[ModelT] + object: str + + @override + def _get_page_items(self) -> List[ModelT]: + return self.data + + @override + def next_page_info(self) -> None: + """ + This page represents a response that isn't actually paginated at the API level + so there will never be a next page. + """ + return None + + +class AsyncPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): + """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" + + data: List[ModelT] + object: str + + @override + def _get_page_items(self) -> List[ModelT]: + return self.data + + @override + def next_page_info(self) -> None: + """ + This page represents a response that isn't actually paginated at the API level + so there will never be a next page. + """ + return None + + +class SyncCursorPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): + data: List[ModelT] + + @override + def _get_page_items(self) -> List[ModelT]: + return self.data + + @override + def next_page_info(self) -> Optional[PageInfo]: + if not self.data: + return None + + item = cast(Any, self.data[-1]) + if not isinstance(item, CursorPageItem): + # TODO emit warning log + return None + + return PageInfo(params={"after": item.id}) + + +class AsyncCursorPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): + data: List[ModelT] + + @override + def _get_page_items(self) -> List[ModelT]: + return self.data + + @override + def next_page_info(self) -> Optional[PageInfo]: + if not self.data: + return None + + item = cast(Any, self.data[-1]) + if not isinstance(item, CursorPageItem): + # TODO emit warning log + return None + + return PageInfo(params={"after": item.id}) diff --git a/openai/py.typed b/src/openai/py.typed similarity index 100% rename from openai/py.typed rename to src/openai/py.typed diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py new file mode 100644 index 0000000000..e0a26c72d2 --- /dev/null +++ b/src/openai/resources/__init__.py @@ -0,0 +1,95 @@ +# File generated from our OpenAPI spec by Stainless. + +from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse +from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse +from .edits import Edits, AsyncEdits, EditsWithRawResponse, AsyncEditsWithRawResponse +from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse +from .images import ( + Images, + AsyncImages, + ImagesWithRawResponse, + AsyncImagesWithRawResponse, +) +from .models import ( + Models, + AsyncModels, + ModelsWithRawResponse, + AsyncModelsWithRawResponse, +) +from .embeddings import ( + Embeddings, + AsyncEmbeddings, + EmbeddingsWithRawResponse, + AsyncEmbeddingsWithRawResponse, +) +from .fine_tunes import ( + FineTunes, + AsyncFineTunes, + FineTunesWithRawResponse, + AsyncFineTunesWithRawResponse, +) +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, +) +from .fine_tuning import ( + FineTuning, + AsyncFineTuning, + FineTuningWithRawResponse, + AsyncFineTuningWithRawResponse, +) +from .moderations import ( + Moderations, + AsyncModerations, + ModerationsWithRawResponse, + AsyncModerationsWithRawResponse, +) + +__all__ = [ + "Completions", + "AsyncCompletions", + "CompletionsWithRawResponse", + "AsyncCompletionsWithRawResponse", + "Chat", + "AsyncChat", + "ChatWithRawResponse", + "AsyncChatWithRawResponse", + "Edits", + "AsyncEdits", + "EditsWithRawResponse", + "AsyncEditsWithRawResponse", + "Embeddings", + "AsyncEmbeddings", + "EmbeddingsWithRawResponse", + "AsyncEmbeddingsWithRawResponse", + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "Images", + "AsyncImages", + "ImagesWithRawResponse", + "AsyncImagesWithRawResponse", + "Audio", + "AsyncAudio", + "AudioWithRawResponse", + "AsyncAudioWithRawResponse", + "Moderations", + "AsyncModerations", + "ModerationsWithRawResponse", + "AsyncModerationsWithRawResponse", + "Models", + "AsyncModels", + "ModelsWithRawResponse", + "AsyncModelsWithRawResponse", + "FineTuning", + "AsyncFineTuning", + "FineTuningWithRawResponse", + "AsyncFineTuningWithRawResponse", + "FineTunes", + "AsyncFineTunes", + "FineTunesWithRawResponse", + "AsyncFineTunesWithRawResponse", +] diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py new file mode 100644 index 0000000000..771bfe9da2 --- /dev/null +++ b/src/openai/resources/audio/__init__.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. + +from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse +from .translations import ( + Translations, + AsyncTranslations, + TranslationsWithRawResponse, + AsyncTranslationsWithRawResponse, +) +from .transcriptions import ( + Transcriptions, + AsyncTranscriptions, + TranscriptionsWithRawResponse, + AsyncTranscriptionsWithRawResponse, +) + +__all__ = [ + "Transcriptions", + "AsyncTranscriptions", + "TranscriptionsWithRawResponse", + "AsyncTranscriptionsWithRawResponse", + "Translations", + "AsyncTranslations", + "TranslationsWithRawResponse", + "AsyncTranslationsWithRawResponse", + "Audio", + "AsyncAudio", + "AudioWithRawResponse", + "AsyncAudioWithRawResponse", +] diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py new file mode 100644 index 0000000000..8e8872c5b5 --- /dev/null +++ b/src/openai/resources/audio/audio.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ..._resource import SyncAPIResource, AsyncAPIResource +from .translations import ( + Translations, + AsyncTranslations, + TranslationsWithRawResponse, + AsyncTranslationsWithRawResponse, +) +from .transcriptions import ( + Transcriptions, + AsyncTranscriptions, + TranscriptionsWithRawResponse, + AsyncTranscriptionsWithRawResponse, +) + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Audio", "AsyncAudio"] + + +class Audio(SyncAPIResource): + transcriptions: Transcriptions + translations: Translations + with_raw_response: AudioWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.transcriptions = Transcriptions(client) + self.translations = Translations(client) + self.with_raw_response = AudioWithRawResponse(self) + + +class AsyncAudio(AsyncAPIResource): + transcriptions: AsyncTranscriptions + translations: AsyncTranslations + with_raw_response: AsyncAudioWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.transcriptions = AsyncTranscriptions(client) + self.translations = AsyncTranslations(client) + self.with_raw_response = AsyncAudioWithRawResponse(self) + + +class AudioWithRawResponse: + def __init__(self, audio: Audio) -> None: + self.transcriptions = TranscriptionsWithRawResponse(audio.transcriptions) + self.translations = TranslationsWithRawResponse(audio.translations) + + +class AsyncAudioWithRawResponse: + def __init__(self, audio: AsyncAudio) -> None: + self.transcriptions = AsyncTranscriptionsWithRawResponse(audio.transcriptions) + self.translations = AsyncTranslationsWithRawResponse(audio.translations) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py new file mode 100644 index 0000000000..ca61f8bd42 --- /dev/null +++ b/src/openai/resources/audio/transcriptions.py @@ -0,0 +1,206 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Union, Mapping, cast +from typing_extensions import Literal + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._utils import extract_files, maybe_transform, deepcopy_minimal +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...types.audio import Transcription, transcription_create_params +from ..._base_client import make_request_options + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Transcriptions", "AsyncTranscriptions"] + + +class Transcriptions(SyncAPIResource): + with_raw_response: TranscriptionsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = TranscriptionsWithRawResponse(self) + + def create( + self, + *, + file: FileTypes, + model: Union[str, Literal["whisper-1"]], + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Transcription: + """ + Transcribes audio into the input language. + + Args: + file: + The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + + model: ID of the model to use. Only `whisper-1` is currently available. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will + improve accuracy and latency. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + should match the audio language. + + response_format: The format of the transcript output, in one of these options: json, text, srt, + verbose_json, or vtt. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "model": model, + "language": language, + "prompt": prompt, + "response_format": response_format, + "temperature": temperature, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return self._post( + "/audio/transcriptions", + body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Transcription, + ) + + +class AsyncTranscriptions(AsyncAPIResource): + with_raw_response: AsyncTranscriptionsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncTranscriptionsWithRawResponse(self) + + async def create( + self, + *, + file: FileTypes, + model: Union[str, Literal["whisper-1"]], + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Transcription: + """ + Transcribes audio into the input language. + + Args: + file: + The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + + model: ID of the model to use. Only `whisper-1` is currently available. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will + improve accuracy and latency. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + should match the audio language. + + response_format: The format of the transcript output, in one of these options: json, text, srt, + verbose_json, or vtt. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "model": model, + "language": language, + "prompt": prompt, + "response_format": response_format, + "temperature": temperature, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return await self._post( + "/audio/transcriptions", + body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Transcription, + ) + + +class TranscriptionsWithRawResponse: + def __init__(self, transcriptions: Transcriptions) -> None: + self.create = to_raw_response_wrapper( + transcriptions.create, + ) + + +class AsyncTranscriptionsWithRawResponse: + def __init__(self, transcriptions: AsyncTranscriptions) -> None: + self.create = async_to_raw_response_wrapper( + transcriptions.create, + ) diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py new file mode 100644 index 0000000000..0b499b9865 --- /dev/null +++ b/src/openai/resources/audio/translations.py @@ -0,0 +1,192 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Union, Mapping, cast +from typing_extensions import Literal + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._utils import extract_files, maybe_transform, deepcopy_minimal +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...types.audio import Translation, translation_create_params +from ..._base_client import make_request_options + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Translations", "AsyncTranslations"] + + +class Translations(SyncAPIResource): + with_raw_response: TranslationsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = TranslationsWithRawResponse(self) + + def create( + self, + *, + file: FileTypes, + model: Union[str, Literal["whisper-1"]], + prompt: str | NotGiven = NOT_GIVEN, + response_format: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Translation: + """ + Translates audio into English. + + Args: + file: The audio file object (not file name) translate, in one of these formats: flac, + mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + + model: ID of the model to use. Only `whisper-1` is currently available. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + should be in English. + + response_format: The format of the transcript output, in one of these options: json, text, srt, + verbose_json, or vtt. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "model": model, + "prompt": prompt, + "response_format": response_format, + "temperature": temperature, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return self._post( + "/audio/translations", + body=maybe_transform(body, translation_create_params.TranslationCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Translation, + ) + + +class AsyncTranslations(AsyncAPIResource): + with_raw_response: AsyncTranslationsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncTranslationsWithRawResponse(self) + + async def create( + self, + *, + file: FileTypes, + model: Union[str, Literal["whisper-1"]], + prompt: str | NotGiven = NOT_GIVEN, + response_format: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Translation: + """ + Translates audio into English. + + Args: + file: The audio file object (not file name) translate, in one of these formats: flac, + mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + + model: ID of the model to use. Only `whisper-1` is currently available. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + should be in English. + + response_format: The format of the transcript output, in one of these options: json, text, srt, + verbose_json, or vtt. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "model": model, + "prompt": prompt, + "response_format": response_format, + "temperature": temperature, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return await self._post( + "/audio/translations", + body=maybe_transform(body, translation_create_params.TranslationCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Translation, + ) + + +class TranslationsWithRawResponse: + def __init__(self, translations: Translations) -> None: + self.create = to_raw_response_wrapper( + translations.create, + ) + + +class AsyncTranslationsWithRawResponse: + def __init__(self, translations: AsyncTranslations) -> None: + self.create = async_to_raw_response_wrapper( + translations.create, + ) diff --git a/src/openai/resources/chat/__init__.py b/src/openai/resources/chat/__init__.py new file mode 100644 index 0000000000..2e56c0cbfa --- /dev/null +++ b/src/openai/resources/chat/__init__.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. + +from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, +) + +__all__ = [ + "Completions", + "AsyncCompletions", + "CompletionsWithRawResponse", + "AsyncCompletionsWithRawResponse", + "Chat", + "AsyncChat", + "ChatWithRawResponse", + "AsyncChatWithRawResponse", +] diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py new file mode 100644 index 0000000000..3847b20512 --- /dev/null +++ b/src/openai/resources/chat/chat.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ..._resource import SyncAPIResource, AsyncAPIResource +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, +) + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Chat", "AsyncChat"] + + +class Chat(SyncAPIResource): + completions: Completions + with_raw_response: ChatWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.completions = Completions(client) + self.with_raw_response = ChatWithRawResponse(self) + + +class AsyncChat(AsyncAPIResource): + completions: AsyncCompletions + with_raw_response: AsyncChatWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.completions = AsyncCompletions(client) + self.with_raw_response = AsyncChatWithRawResponse(self) + + +class ChatWithRawResponse: + def __init__(self, chat: Chat) -> None: + self.completions = CompletionsWithRawResponse(chat.completions) + + +class AsyncChatWithRawResponse: + def __init__(self, chat: AsyncChat) -> None: + self.completions = AsyncCompletionsWithRawResponse(chat.completions) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py new file mode 100644 index 0000000000..e6e6ce52b8 --- /dev/null +++ b/src/openai/resources/chat/completions.py @@ -0,0 +1,942 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Dict, List, Union, Optional, overload +from typing_extensions import Literal + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import required_args, maybe_transform +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ..._streaming import Stream, AsyncStream +from ...types.chat import ( + ChatCompletion, + ChatCompletionChunk, + ChatCompletionMessageParam, + completion_create_params, +) +from ..._base_client import make_request_options + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Completions", "AsyncCompletions"] + + +class Completions(SyncAPIResource): + with_raw_response: CompletionsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = CompletionsWithRawResponse(self) + + @overload + def create( + self, + *, + messages: List[ChatCompletionMessageParam], + model: Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ], + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + + model: ID of the model to use. See the + [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + table for details on which models work with the Chat API. + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + function_call: Controls how the model calls functions. "none" means the model will not call a + function and instead generates a message. "auto" means the model can pick + between generating a message or calling a function. Specifying a particular + function via `{"name": "my_function"}` forces the model to call that function. + "none" is the default when no functions are present. "auto" is the default if + functions are present. + + functions: A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + + The total length of input tokens and generated tokens is limited by the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many chat completion choices to generate for each input message. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. + + stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + messages: List[ChatCompletionMessageParam], + model: Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ], + stream: Literal[True], + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Stream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + + model: ID of the model to use. See the + [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + table for details on which models work with the Chat API. + + stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + function_call: Controls how the model calls functions. "none" means the model will not call a + function and instead generates a message. "auto" means the model can pick + between generating a message or calling a function. Specifying a particular + function via `{"name": "my_function"}` forces the model to call that function. + "none" is the default when no functions are present. "auto" is the default if + functions are present. + + functions: A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + + The total length of input tokens and generated tokens is limited by the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many chat completion choices to generate for each input message. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + messages: List[ChatCompletionMessageParam], + model: Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ], + stream: bool, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion | Stream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + + model: ID of the model to use. See the + [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + table for details on which models work with the Chat API. + + stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + function_call: Controls how the model calls functions. "none" means the model will not call a + function and instead generates a message. "auto" means the model can pick + between generating a message or calling a function. Specifying a particular + function via `{"name": "my_function"}` forces the model to call that function. + "none" is the default when no functions are present. "auto" is the default if + functions are present. + + functions: A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + + The total length of input tokens and generated tokens is limited by the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many chat completion choices to generate for each input message. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["messages", "model"], ["messages", "model", "stream"]) + def create( + self, + *, + messages: List[ChatCompletionMessageParam], + model: Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ], + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion | Stream[ChatCompletionChunk]: + return self._post( + "/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "frequency_penalty": frequency_penalty, + "function_call": function_call, + "functions": functions, + "logit_bias": logit_bias, + "max_tokens": max_tokens, + "n": n, + "presence_penalty": presence_penalty, + "stop": stop, + "stream": stream, + "temperature": temperature, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatCompletion, + stream=stream or False, + stream_cls=Stream[ChatCompletionChunk], + ) + + +class AsyncCompletions(AsyncAPIResource): + with_raw_response: AsyncCompletionsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncCompletionsWithRawResponse(self) + + @overload + async def create( + self, + *, + messages: List[ChatCompletionMessageParam], + model: Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ], + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + + model: ID of the model to use. See the + [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + table for details on which models work with the Chat API. + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + function_call: Controls how the model calls functions. "none" means the model will not call a + function and instead generates a message. "auto" means the model can pick + between generating a message or calling a function. Specifying a particular + function via `{"name": "my_function"}` forces the model to call that function. + "none" is the default when no functions are present. "auto" is the default if + functions are present. + + functions: A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + + The total length of input tokens and generated tokens is limited by the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many chat completion choices to generate for each input message. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. + + stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + messages: List[ChatCompletionMessageParam], + model: Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ], + stream: Literal[True], + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + + model: ID of the model to use. See the + [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + table for details on which models work with the Chat API. + + stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + function_call: Controls how the model calls functions. "none" means the model will not call a + function and instead generates a message. "auto" means the model can pick + between generating a message or calling a function. Specifying a particular + function via `{"name": "my_function"}` forces the model to call that function. + "none" is the default when no functions are present. "auto" is the default if + functions are present. + + functions: A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + + The total length of input tokens and generated tokens is limited by the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many chat completion choices to generate for each input message. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + messages: List[ChatCompletionMessageParam], + model: Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ], + stream: bool, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: + """ + Creates a model response for the given chat conversation. + + Args: + messages: A list of messages comprising the conversation so far. + [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + + model: ID of the model to use. See the + [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + table for details on which models work with the Chat API. + + stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + function_call: Controls how the model calls functions. "none" means the model will not call a + function and instead generates a message. "auto" means the model can pick + between generating a message or calling a function. Specifying a particular + function via `{"name": "my_function"}` forces the model to call that function. + "none" is the default when no functions are present. "auto" is the default if + functions are present. + + functions: A list of functions the model may generate JSON inputs for. + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + + The total length of input tokens and generated tokens is limited by the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many chat completion choices to generate for each input message. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["messages", "model"], ["messages", "model", "stream"]) + async def create( + self, + *, + messages: List[ChatCompletionMessageParam], + model: Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ], + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: + return await self._post( + "/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "frequency_penalty": frequency_penalty, + "function_call": function_call, + "functions": functions, + "logit_bias": logit_bias, + "max_tokens": max_tokens, + "n": n, + "presence_penalty": presence_penalty, + "stop": stop, + "stream": stream, + "temperature": temperature, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatCompletion, + stream=stream or False, + stream_cls=AsyncStream[ChatCompletionChunk], + ) + + +class CompletionsWithRawResponse: + def __init__(self, completions: Completions) -> None: + self.create = to_raw_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsWithRawResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self.create = async_to_raw_response_wrapper( + completions.create, + ) diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py new file mode 100644 index 0000000000..26a34524c6 --- /dev/null +++ b/src/openai/resources/completions.py @@ -0,0 +1,1117 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Dict, List, Union, Optional, overload +from typing_extensions import Literal + +from ..types import Completion, completion_create_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import required_args, maybe_transform +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._streaming import Stream, AsyncStream +from .._base_client import make_request_options + +if TYPE_CHECKING: + from .._client import OpenAI, AsyncOpenAI + +__all__ = ["Completions", "AsyncCompletions"] + + +class Completions(SyncAPIResource): + with_raw_response: CompletionsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = CompletionsWithRawResponse(self) + + @overload + def create( + self, + *, + model: Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ], + prompt: Union[str, List[str], List[int], List[List[int]], None], + best_of: Optional[int] | NotGiven = NOT_GIVEN, + echo: Optional[bool] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Completion: + """ + Creates a completion for the provided prompt and parameters. + + Args: + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to + convert text to token IDs. Mathematically, the bias is added to the logits + generated by the model prior to sampling. The exact effect will vary per model, + but values between -1 and 1 should decrease or increase likelihood of selection; + values like -100 or 100 should result in a ban or exclusive selection of the + relevant token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the + chosen tokens. For example, if `logprobs` is 5, the API will return a list of + the 5 most likely tokens. The API will always return the `logprob` of the + sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + suffix: The suffix that comes after a completion of inserted text. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + model: Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ], + prompt: Union[str, List[str], List[int], List[List[int]], None], + stream: Literal[True], + best_of: Optional[int] | NotGiven = NOT_GIVEN, + echo: Optional[bool] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Stream[Completion]: + """ + Creates a completion for the provided prompt and parameters. + + Args: + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to + convert text to token IDs. Mathematically, the bias is added to the logits + generated by the model prior to sampling. The exact effect will vary per model, + but values between -1 and 1 should decrease or increase likelihood of selection; + values like -100 or 100 should result in a ban or exclusive selection of the + relevant token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the + chosen tokens. For example, if `logprobs` is 5, the API will return a list of + the 5 most likely tokens. The API will always return the `logprob` of the + sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + suffix: The suffix that comes after a completion of inserted text. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + model: Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ], + prompt: Union[str, List[str], List[int], List[List[int]], None], + stream: bool, + best_of: Optional[int] | NotGiven = NOT_GIVEN, + echo: Optional[bool] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Completion | Stream[Completion]: + """ + Creates a completion for the provided prompt and parameters. + + Args: + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to + convert text to token IDs. Mathematically, the bias is added to the logits + generated by the model prior to sampling. The exact effect will vary per model, + but values between -1 and 1 should decrease or increase likelihood of selection; + values like -100 or 100 should result in a ban or exclusive selection of the + relevant token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the + chosen tokens. For example, if `logprobs` is 5, the API will return a list of + the 5 most likely tokens. The API will always return the `logprob` of the + sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + suffix: The suffix that comes after a completion of inserted text. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["model", "prompt"], ["model", "prompt", "stream"]) + def create( + self, + *, + model: Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ], + prompt: Union[str, List[str], List[int], List[List[int]], None], + best_of: Optional[int] | NotGiven = NOT_GIVEN, + echo: Optional[bool] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Completion | Stream[Completion]: + return self._post( + "/completions", + body=maybe_transform( + { + "model": model, + "prompt": prompt, + "best_of": best_of, + "echo": echo, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_tokens": max_tokens, + "n": n, + "presence_penalty": presence_penalty, + "stop": stop, + "stream": stream, + "suffix": suffix, + "temperature": temperature, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Completion, + stream=stream or False, + stream_cls=Stream[Completion], + ) + + +class AsyncCompletions(AsyncAPIResource): + with_raw_response: AsyncCompletionsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncCompletionsWithRawResponse(self) + + @overload + async def create( + self, + *, + model: Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ], + prompt: Union[str, List[str], List[int], List[List[int]], None], + best_of: Optional[int] | NotGiven = NOT_GIVEN, + echo: Optional[bool] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Completion: + """ + Creates a completion for the provided prompt and parameters. + + Args: + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to + convert text to token IDs. Mathematically, the bias is added to the logits + generated by the model prior to sampling. The exact effect will vary per model, + but values between -1 and 1 should decrease or increase likelihood of selection; + values like -100 or 100 should result in a ban or exclusive selection of the + relevant token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the + chosen tokens. For example, if `logprobs` is 5, the API will return a list of + the 5 most likely tokens. The API will always return the `logprob` of the + sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + suffix: The suffix that comes after a completion of inserted text. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + model: Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ], + prompt: Union[str, List[str], List[int], List[List[int]], None], + stream: Literal[True], + best_of: Optional[int] | NotGiven = NOT_GIVEN, + echo: Optional[bool] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[Completion]: + """ + Creates a completion for the provided prompt and parameters. + + Args: + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to + convert text to token IDs. Mathematically, the bias is added to the logits + generated by the model prior to sampling. The exact effect will vary per model, + but values between -1 and 1 should decrease or increase likelihood of selection; + values like -100 or 100 should result in a ban or exclusive selection of the + relevant token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the + chosen tokens. For example, if `logprobs` is 5, the API will return a list of + the 5 most likely tokens. The API will always return the `logprob` of the + sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + suffix: The suffix that comes after a completion of inserted text. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + model: Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ], + prompt: Union[str, List[str], List[int], List[List[int]], None], + stream: bool, + best_of: Optional[int] | NotGiven = NOT_GIVEN, + echo: Optional[bool] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Completion | AsyncStream[Completion]: + """ + Creates a completion for the provided prompt and parameters. + + Args: + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + prompt: The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + + stream: Whether to stream back partial progress. If set, tokens will be sent as + data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + + best_of: Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + echo: Echo back the prompt in addition to the completion + + frequency_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + logit_bias: Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to + convert text to token IDs. Mathematically, the bias is added to the logits + generated by the model prior to sampling. The exact effect will vary per model, + but values between -1 and 1 should decrease or increase likelihood of selection; + values like -100 or 100 should result in a ban or exclusive selection of the + relevant token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + + logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the + chosen tokens. For example, if `logprobs` is 5, the API will return a list of + the 5 most likely tokens. The API will always return the `logprob` of the + sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + + max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + n: How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + + presence_penalty: Number between -2.0 and 2.0. 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. + + suffix: The suffix that comes after a completion of inserted text. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["model", "prompt"], ["model", "prompt", "stream"]) + async def create( + self, + *, + model: Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ], + prompt: Union[str, List[str], List[int], List[List[int]], None], + best_of: Optional[int] | NotGiven = NOT_GIVEN, + echo: Optional[bool] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Completion | AsyncStream[Completion]: + return await self._post( + "/completions", + body=maybe_transform( + { + "model": model, + "prompt": prompt, + "best_of": best_of, + "echo": echo, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_tokens": max_tokens, + "n": n, + "presence_penalty": presence_penalty, + "stop": stop, + "stream": stream, + "suffix": suffix, + "temperature": temperature, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Completion, + stream=stream or False, + stream_cls=AsyncStream[Completion], + ) + + +class CompletionsWithRawResponse: + def __init__(self, completions: Completions) -> None: + self.create = to_raw_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsWithRawResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self.create = async_to_raw_response_wrapper( + completions.create, + ) diff --git a/src/openai/resources/edits.py b/src/openai/resources/edits.py new file mode 100644 index 0000000000..5c114c915f --- /dev/null +++ b/src/openai/resources/edits.py @@ -0,0 +1,191 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import typing_extensions +from typing import TYPE_CHECKING, Union, Optional +from typing_extensions import Literal + +from ..types import Edit, edit_create_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import maybe_transform +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._base_client import make_request_options + +if TYPE_CHECKING: + from .._client import OpenAI, AsyncOpenAI + +__all__ = ["Edits", "AsyncEdits"] + + +class Edits(SyncAPIResource): + with_raw_response: EditsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = EditsWithRawResponse(self) + + @typing_extensions.deprecated( + "The Edits API is deprecated; please use Chat Completions instead.\n\nhttps://openai.com/blog/gpt-4-api-general-availability#deprecation-of-the-edits-api\n" + ) + def create( + self, + *, + instruction: str, + model: Union[str, Literal["text-davinci-edit-001", "code-davinci-edit-001"]], + input: Optional[str] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Edit: + """ + Creates a new edit for the provided input, instruction, and parameters. + + Args: + instruction: The instruction that tells the model how to edit the prompt. + + model: ID of the model to use. You can use the `text-davinci-edit-001` or + `code-davinci-edit-001` model with this endpoint. + + input: The input text to use as a starting point for the edit. + + n: How many edits to generate for the input and instruction. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/edits", + body=maybe_transform( + { + "instruction": instruction, + "model": model, + "input": input, + "n": n, + "temperature": temperature, + "top_p": top_p, + }, + edit_create_params.EditCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Edit, + ) + + +class AsyncEdits(AsyncAPIResource): + with_raw_response: AsyncEditsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncEditsWithRawResponse(self) + + @typing_extensions.deprecated( + "The Edits API is deprecated; please use Chat Completions instead.\n\nhttps://openai.com/blog/gpt-4-api-general-availability#deprecation-of-the-edits-api\n" + ) + async def create( + self, + *, + instruction: str, + model: Union[str, Literal["text-davinci-edit-001", "code-davinci-edit-001"]], + input: Optional[str] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Edit: + """ + Creates a new edit for the provided input, instruction, and parameters. + + Args: + instruction: The instruction that tells the model how to edit the prompt. + + model: ID of the model to use. You can use the `text-davinci-edit-001` or + `code-davinci-edit-001` model with this endpoint. + + input: The input text to use as a starting point for the edit. + + n: How many edits to generate for the input and instruction. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/edits", + body=maybe_transform( + { + "instruction": instruction, + "model": model, + "input": input, + "n": n, + "temperature": temperature, + "top_p": top_p, + }, + edit_create_params.EditCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Edit, + ) + + +class EditsWithRawResponse: + def __init__(self, edits: Edits) -> None: + self.create = to_raw_response_wrapper( # pyright: ignore[reportDeprecated] + edits.create # pyright: ignore[reportDeprecated], + ) + + +class AsyncEditsWithRawResponse: + def __init__(self, edits: AsyncEdits) -> None: + self.create = async_to_raw_response_wrapper( # pyright: ignore[reportDeprecated] + edits.create # pyright: ignore[reportDeprecated], + ) diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py new file mode 100644 index 0000000000..dd540fc796 --- /dev/null +++ b/src/openai/resources/embeddings.py @@ -0,0 +1,221 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import base64 +from typing import TYPE_CHECKING, List, Union, cast +from typing_extensions import Literal + +from ..types import CreateEmbeddingResponse, embedding_create_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import is_given, maybe_transform +from .._extras import numpy as np +from .._extras import has_numpy +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._base_client import make_request_options + +if TYPE_CHECKING: + from .._client import OpenAI, AsyncOpenAI + +__all__ = ["Embeddings", "AsyncEmbeddings"] + + +class Embeddings(SyncAPIResource): + with_raw_response: EmbeddingsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = EmbeddingsWithRawResponse(self) + + def create( + self, + *, + input: Union[str, List[str], List[int], List[List[int]]], + model: Union[str, Literal["text-embedding-ada-002"]], + encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> CreateEmbeddingResponse: + """ + Creates an embedding vector representing the input text. + + Args: + input: Input text to embed, encoded as a string or array of tokens. To embed multiple + inputs in a single request, pass an array of strings or array of token arrays. + The input must not exceed the max input tokens for the model (8192 tokens for + `text-embedding-ada-002`) and cannot be an empty string. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + encoding_format: The format to return the embeddings in. Can be either `float` or + [`base64`](https://pypi.org/project/pybase64/). + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + params = { + "input": input, + "model": model, + "user": user, + "encoding_format": encoding_format, + } + if not is_given(encoding_format) and has_numpy(): + params["encoding_format"] = "base64" + + def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: + if is_given(encoding_format): + # don't modify the response object if a user explicitly asked for a format + return obj + + for embedding in obj.data: + data = cast(object, embedding.embedding) + if not isinstance(data, str): + # numpy is not installed / base64 optimisation isn't enabled for this model yet + continue + + embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call] + base64.b64decode(data), dtype="float32" + ).tolist() + + return obj + + return self._post( + "/embeddings", + body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + ), + cast_to=CreateEmbeddingResponse, + ) + + +class AsyncEmbeddings(AsyncAPIResource): + with_raw_response: AsyncEmbeddingsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncEmbeddingsWithRawResponse(self) + + async def create( + self, + *, + input: Union[str, List[str], List[int], List[List[int]]], + model: Union[str, Literal["text-embedding-ada-002"]], + encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> CreateEmbeddingResponse: + """ + Creates an embedding vector representing the input text. + + Args: + input: Input text to embed, encoded as a string or array of tokens. To embed multiple + inputs in a single request, pass an array of strings or array of token arrays. + The input must not exceed the max input tokens for the model (8192 tokens for + `text-embedding-ada-002`) and cannot be an empty string. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + encoding_format: The format to return the embeddings in. Can be either `float` or + [`base64`](https://pypi.org/project/pybase64/). + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + params = { + "input": input, + "model": model, + "user": user, + "encoding_format": encoding_format, + } + if not is_given(encoding_format) and has_numpy(): + params["encoding_format"] = "base64" + + def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: + if is_given(encoding_format): + # don't modify the response object if a user explicitly asked for a format + return obj + + for embedding in obj.data: + data = cast(object, embedding.embedding) + if not isinstance(data, str): + # numpy is not installed / base64 optimisation isn't enabled for this model yet + continue + + embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call] + base64.b64decode(data), dtype="float32" + ).tolist() + + return obj + + return await self._post( + "/embeddings", + body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + ), + cast_to=CreateEmbeddingResponse, + ) + + +class EmbeddingsWithRawResponse: + def __init__(self, embeddings: Embeddings) -> None: + self.create = to_raw_response_wrapper( + embeddings.create, + ) + + +class AsyncEmbeddingsWithRawResponse: + def __init__(self, embeddings: AsyncEmbeddings) -> None: + self.create = async_to_raw_response_wrapper( + embeddings.create, + ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py new file mode 100644 index 0000000000..d2e674c942 --- /dev/null +++ b/src/openai/resources/files.py @@ -0,0 +1,471 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import time +from typing import TYPE_CHECKING, Mapping, cast + +from ..types import FileObject, FileDeleted, file_create_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from .._utils import extract_files, maybe_transform, deepcopy_minimal +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ..pagination import SyncPage, AsyncPage +from .._base_client import AsyncPaginator, make_request_options + +if TYPE_CHECKING: + from .._client import OpenAI, AsyncOpenAI + +__all__ = ["Files", "AsyncFiles"] + + +class Files(SyncAPIResource): + with_raw_response: FilesWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = FilesWithRawResponse(self) + + def create( + self, + *, + file: FileTypes, + purpose: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FileObject: + """Upload a file that can be used across various endpoints/features. + + Currently, the + size of all the files uploaded by one organization can be up to 1 GB. Please + [contact us](https://help.openai.com/) if you need to increase the storage + limit. + + Args: + file: The file object (not file name) to be uploaded. + + If the `purpose` is set to "fine-tune", the file will be used for fine-tuning. + + purpose: The intended purpose of the uploaded file. + + Use "fine-tune" for + [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This + allows us to validate the format of the uploaded file is correct for + fine-tuning. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "purpose": purpose, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return self._post( + "/files", + body=maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileObject, + ) + + def retrieve( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FileObject: + """ + Returns information about a specific file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileObject, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncPage[FileObject]: + """Returns a list of files that belong to the user's organization.""" + return self._get_api_list( + "/files", + page=SyncPage[FileObject], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=FileObject, + ) + + def delete( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FileDeleted: + """ + Delete a file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._delete( + f"/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileDeleted, + ) + + def retrieve_content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> str: + """ + Returns the contents of the specified file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "application/json", **(extra_headers or {})} + return self._get( + f"/files/{file_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + ) + + def wait_for_processing( + self, + id: str, + *, + poll_interval: float = 5.0, + max_wait_seconds: float = 30 * 60, + ) -> FileObject: + """Waits for the given file to be processed, default timeout is 30 mins.""" + TERMINAL_STATES = {"processed", "error", "deleted"} + + start = time.time() + file = self.retrieve(id) + while file.status not in TERMINAL_STATES: + self._sleep(poll_interval) + + file = self.retrieve(id) + if time.time() - start > max_wait_seconds: + raise RuntimeError( + f"Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds." + ) + + return file + + +class AsyncFiles(AsyncAPIResource): + with_raw_response: AsyncFilesWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncFilesWithRawResponse(self) + + async def create( + self, + *, + file: FileTypes, + purpose: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FileObject: + """Upload a file that can be used across various endpoints/features. + + Currently, the + size of all the files uploaded by one organization can be up to 1 GB. Please + [contact us](https://help.openai.com/) if you need to increase the storage + limit. + + Args: + file: The file object (not file name) to be uploaded. + + If the `purpose` is set to "fine-tune", the file will be used for fine-tuning. + + purpose: The intended purpose of the uploaded file. + + Use "fine-tune" for + [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This + allows us to validate the format of the uploaded file is correct for + fine-tuning. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "file": file, + "purpose": purpose, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return await self._post( + "/files", + body=maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileObject, + ) + + async def retrieve( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FileObject: + """ + Returns information about a specific file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileObject, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[FileObject, AsyncPage[FileObject]]: + """Returns a list of files that belong to the user's organization.""" + return self._get_api_list( + "/files", + page=AsyncPage[FileObject], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=FileObject, + ) + + async def delete( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FileDeleted: + """ + Delete a file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._delete( + f"/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileDeleted, + ) + + async def retrieve_content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> str: + """ + Returns the contents of the specified file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "application/json", **(extra_headers or {})} + return await self._get( + f"/files/{file_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + ) + + async def wait_for_processing( + self, + id: str, + *, + poll_interval: float = 5.0, + max_wait_seconds: float = 30 * 60, + ) -> FileObject: + """Waits for the given file to be processed, default timeout is 30 mins.""" + TERMINAL_STATES = {"processed", "error", "deleted"} + + start = time.time() + file = await self.retrieve(id) + while file.status not in TERMINAL_STATES: + await self._sleep(poll_interval) + + file = await self.retrieve(id) + if time.time() - start > max_wait_seconds: + raise RuntimeError( + f"Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds." + ) + + return file + + +class FilesWithRawResponse: + def __init__(self, files: Files) -> None: + self.create = to_raw_response_wrapper( + files.create, + ) + self.retrieve = to_raw_response_wrapper( + files.retrieve, + ) + self.list = to_raw_response_wrapper( + files.list, + ) + self.delete = to_raw_response_wrapper( + files.delete, + ) + self.retrieve_content = to_raw_response_wrapper( + files.retrieve_content, + ) + + +class AsyncFilesWithRawResponse: + def __init__(self, files: AsyncFiles) -> None: + self.create = async_to_raw_response_wrapper( + files.create, + ) + self.retrieve = async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = async_to_raw_response_wrapper( + files.list, + ) + self.delete = async_to_raw_response_wrapper( + files.delete, + ) + self.retrieve_content = async_to_raw_response_wrapper( + files.retrieve_content, + ) diff --git a/src/openai/resources/fine_tunes.py b/src/openai/resources/fine_tunes.py new file mode 100644 index 0000000000..28f4225102 --- /dev/null +++ b/src/openai/resources/fine_tunes.py @@ -0,0 +1,820 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Union, Optional, overload +from typing_extensions import Literal + +from ..types import ( + FineTune, + FineTuneEvent, + FineTuneEventsListResponse, + fine_tune_create_params, + fine_tune_list_events_params, +) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import maybe_transform +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._streaming import Stream, AsyncStream +from ..pagination import SyncPage, AsyncPage +from .._base_client import AsyncPaginator, make_request_options + +if TYPE_CHECKING: + from .._client import OpenAI, AsyncOpenAI + +__all__ = ["FineTunes", "AsyncFineTunes"] + + +class FineTunes(SyncAPIResource): + with_raw_response: FineTunesWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = FineTunesWithRawResponse(self) + + def create( + self, + *, + training_file: str, + batch_size: Optional[int] | NotGiven = NOT_GIVEN, + classification_betas: Optional[List[float]] | NotGiven = NOT_GIVEN, + classification_n_classes: Optional[int] | NotGiven = NOT_GIVEN, + classification_positive_class: Optional[str] | NotGiven = NOT_GIVEN, + compute_classification_metrics: Optional[bool] | NotGiven = NOT_GIVEN, + hyperparameters: fine_tune_create_params.Hyperparameters | NotGiven = NOT_GIVEN, + learning_rate_multiplier: Optional[float] | NotGiven = NOT_GIVEN, + model: Union[str, Literal["ada", "babbage", "curie", "davinci"], None] | NotGiven = NOT_GIVEN, + prompt_loss_weight: Optional[float] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + validation_file: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTune: + """ + Creates a job that fine-tunes a specified model from a given dataset. + + Response includes details of the enqueued job including job status and the name + of the fine-tuned models once complete. + + [Learn more about fine-tuning](https://platform.openai.com/docs/guides/legacy-fine-tuning) + + Args: + training_file: The ID of an uploaded file that contains training data. + + See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + for how to upload a file. + + Your dataset must be formatted as a JSONL file, where each training example is a + JSON object with the keys "prompt" and "completion". Additionally, you must + upload your file with the purpose `fine-tune`. + + See the + [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) + for more details. + + batch_size: The batch size to use for training. The batch size is the number of training + examples used to train a single forward and backward pass. + + By default, the batch size will be dynamically configured to be ~0.2% of the + number of examples in the training set, capped at 256 - in general, we've found + that larger batch sizes tend to work better for larger datasets. + + classification_betas: If this is provided, we calculate F-beta scores at the specified beta values. + The F-beta score is a generalization of F-1 score. This is only used for binary + classification. + + With a beta of 1 (i.e. the F-1 score), precision and recall are given the same + weight. A larger beta score puts more weight on recall and less on precision. A + smaller beta score puts more weight on precision and less on recall. + + classification_n_classes: The number of classes in a classification task. + + This parameter is required for multiclass classification. + + classification_positive_class: The positive class in binary classification. + + This parameter is needed to generate precision, recall, and F1 metrics when + doing binary classification. + + compute_classification_metrics: If set, we calculate classification-specific metrics such as accuracy and F-1 + score using the validation set at the end of every epoch. These metrics can be + viewed in the + [results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). + + In order to compute classification metrics, you must provide a + `validation_file`. Additionally, you must specify `classification_n_classes` for + multiclass classification or `classification_positive_class` for binary + classification. + + hyperparameters: The hyperparameters used for the fine-tuning job. + + learning_rate_multiplier: The learning rate multiplier to use for training. The fine-tuning learning rate + is the original learning rate used for pretraining multiplied by this value. + + By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 depending on + final `batch_size` (larger learning rates tend to perform better with larger + batch sizes). We recommend experimenting with values in the range 0.02 to 0.2 to + see what produces the best results. + + model: The name of the base model to fine-tune. You can select one of "ada", "babbage", + "curie", "davinci", or a fine-tuned model created after 2022-04-21 and before + 2023-08-22. To learn more about these models, see the + [Models](https://platform.openai.com/docs/models) documentation. + + prompt_loss_weight: The weight to use for loss on the prompt tokens. This controls how much the + model tries to learn to generate the prompt (as compared to the completion which + always has a weight of 1.0), and can add a stabilizing effect to training when + completions are short. + + If prompts are extremely long (relative to completions), it may make sense to + reduce this weight so as to avoid over-prioritizing learning the prompt. + + suffix: A string of up to 40 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ada:ft-your-org:custom-model-name-2022-02-15-04-21-04`. + + validation_file: The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the + [fine-tuning results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). + Your train and validation data should be mutually exclusive. + + Your dataset must be formatted as a JSONL file, where each validation example is + a JSON object with the keys "prompt" and "completion". Additionally, you must + upload your file with the purpose `fine-tune`. + + See the + [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) + for more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/fine-tunes", + body=maybe_transform( + { + "training_file": training_file, + "batch_size": batch_size, + "classification_betas": classification_betas, + "classification_n_classes": classification_n_classes, + "classification_positive_class": classification_positive_class, + "compute_classification_metrics": compute_classification_metrics, + "hyperparameters": hyperparameters, + "learning_rate_multiplier": learning_rate_multiplier, + "model": model, + "prompt_loss_weight": prompt_loss_weight, + "suffix": suffix, + "validation_file": validation_file, + }, + fine_tune_create_params.FineTuneCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTune, + ) + + def retrieve( + self, + fine_tune_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTune: + """ + Gets info about the fine-tune job. + + [Learn more about fine-tuning](https://platform.openai.com/docs/guides/legacy-fine-tuning) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/fine-tunes/{fine_tune_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTune, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncPage[FineTune]: + """List your organization's fine-tuning jobs""" + return self._get_api_list( + "/fine-tunes", + page=SyncPage[FineTune], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=FineTune, + ) + + def cancel( + self, + fine_tune_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTune: + """ + Immediately cancel a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + f"/fine-tunes/{fine_tune_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTune, + ) + + @overload + def list_events( + self, + fine_tune_id: str, + *, + stream: Literal[False] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = 86400, + ) -> FineTuneEventsListResponse: + """ + Get fine-grained status updates for a fine-tune job. + + Args: + stream: Whether to stream events for the fine-tune job. If set to true, events will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available. The stream will terminate with a `data: [DONE]` + message when the job is finished (succeeded, cancelled, or failed). + + If set to false, only events generated so far will be returned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def list_events( + self, + fine_tune_id: str, + *, + stream: Literal[True], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = 86400, + ) -> Stream[FineTuneEvent]: + """ + Get fine-grained status updates for a fine-tune job. + + Args: + stream: Whether to stream events for the fine-tune job. If set to true, events will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available. The stream will terminate with a `data: [DONE]` + message when the job is finished (succeeded, cancelled, or failed). + + If set to false, only events generated so far will be returned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def list_events( + self, + fine_tune_id: str, + *, + stream: bool, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = 86400, + ) -> FineTuneEventsListResponse | Stream[FineTuneEvent]: + """ + Get fine-grained status updates for a fine-tune job. + + Args: + stream: Whether to stream events for the fine-tune job. If set to true, events will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available. The stream will terminate with a `data: [DONE]` + message when the job is finished (succeeded, cancelled, or failed). + + If set to false, only events generated so far will be returned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + def list_events( + self, + fine_tune_id: str, + *, + stream: Literal[False] | Literal[True] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = 86400, + ) -> FineTuneEventsListResponse | Stream[FineTuneEvent]: + return self._get( + f"/fine-tunes/{fine_tune_id}/events", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"stream": stream}, fine_tune_list_events_params.FineTuneListEventsParams), + ), + cast_to=FineTuneEventsListResponse, + stream=stream or False, + stream_cls=Stream[FineTuneEvent], + ) + + +class AsyncFineTunes(AsyncAPIResource): + with_raw_response: AsyncFineTunesWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncFineTunesWithRawResponse(self) + + async def create( + self, + *, + training_file: str, + batch_size: Optional[int] | NotGiven = NOT_GIVEN, + classification_betas: Optional[List[float]] | NotGiven = NOT_GIVEN, + classification_n_classes: Optional[int] | NotGiven = NOT_GIVEN, + classification_positive_class: Optional[str] | NotGiven = NOT_GIVEN, + compute_classification_metrics: Optional[bool] | NotGiven = NOT_GIVEN, + hyperparameters: fine_tune_create_params.Hyperparameters | NotGiven = NOT_GIVEN, + learning_rate_multiplier: Optional[float] | NotGiven = NOT_GIVEN, + model: Union[str, Literal["ada", "babbage", "curie", "davinci"], None] | NotGiven = NOT_GIVEN, + prompt_loss_weight: Optional[float] | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + validation_file: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTune: + """ + Creates a job that fine-tunes a specified model from a given dataset. + + Response includes details of the enqueued job including job status and the name + of the fine-tuned models once complete. + + [Learn more about fine-tuning](https://platform.openai.com/docs/guides/legacy-fine-tuning) + + Args: + training_file: The ID of an uploaded file that contains training data. + + See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + for how to upload a file. + + Your dataset must be formatted as a JSONL file, where each training example is a + JSON object with the keys "prompt" and "completion". Additionally, you must + upload your file with the purpose `fine-tune`. + + See the + [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) + for more details. + + batch_size: The batch size to use for training. The batch size is the number of training + examples used to train a single forward and backward pass. + + By default, the batch size will be dynamically configured to be ~0.2% of the + number of examples in the training set, capped at 256 - in general, we've found + that larger batch sizes tend to work better for larger datasets. + + classification_betas: If this is provided, we calculate F-beta scores at the specified beta values. + The F-beta score is a generalization of F-1 score. This is only used for binary + classification. + + With a beta of 1 (i.e. the F-1 score), precision and recall are given the same + weight. A larger beta score puts more weight on recall and less on precision. A + smaller beta score puts more weight on precision and less on recall. + + classification_n_classes: The number of classes in a classification task. + + This parameter is required for multiclass classification. + + classification_positive_class: The positive class in binary classification. + + This parameter is needed to generate precision, recall, and F1 metrics when + doing binary classification. + + compute_classification_metrics: If set, we calculate classification-specific metrics such as accuracy and F-1 + score using the validation set at the end of every epoch. These metrics can be + viewed in the + [results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). + + In order to compute classification metrics, you must provide a + `validation_file`. Additionally, you must specify `classification_n_classes` for + multiclass classification or `classification_positive_class` for binary + classification. + + hyperparameters: The hyperparameters used for the fine-tuning job. + + learning_rate_multiplier: The learning rate multiplier to use for training. The fine-tuning learning rate + is the original learning rate used for pretraining multiplied by this value. + + By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 depending on + final `batch_size` (larger learning rates tend to perform better with larger + batch sizes). We recommend experimenting with values in the range 0.02 to 0.2 to + see what produces the best results. + + model: The name of the base model to fine-tune. You can select one of "ada", "babbage", + "curie", "davinci", or a fine-tuned model created after 2022-04-21 and before + 2023-08-22. To learn more about these models, see the + [Models](https://platform.openai.com/docs/models) documentation. + + prompt_loss_weight: The weight to use for loss on the prompt tokens. This controls how much the + model tries to learn to generate the prompt (as compared to the completion which + always has a weight of 1.0), and can add a stabilizing effect to training when + completions are short. + + If prompts are extremely long (relative to completions), it may make sense to + reduce this weight so as to avoid over-prioritizing learning the prompt. + + suffix: A string of up to 40 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ada:ft-your-org:custom-model-name-2022-02-15-04-21-04`. + + validation_file: The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the + [fine-tuning results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). + Your train and validation data should be mutually exclusive. + + Your dataset must be formatted as a JSONL file, where each validation example is + a JSON object with the keys "prompt" and "completion". Additionally, you must + upload your file with the purpose `fine-tune`. + + See the + [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) + for more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/fine-tunes", + body=maybe_transform( + { + "training_file": training_file, + "batch_size": batch_size, + "classification_betas": classification_betas, + "classification_n_classes": classification_n_classes, + "classification_positive_class": classification_positive_class, + "compute_classification_metrics": compute_classification_metrics, + "hyperparameters": hyperparameters, + "learning_rate_multiplier": learning_rate_multiplier, + "model": model, + "prompt_loss_weight": prompt_loss_weight, + "suffix": suffix, + "validation_file": validation_file, + }, + fine_tune_create_params.FineTuneCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTune, + ) + + async def retrieve( + self, + fine_tune_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTune: + """ + Gets info about the fine-tune job. + + [Learn more about fine-tuning](https://platform.openai.com/docs/guides/legacy-fine-tuning) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/fine-tunes/{fine_tune_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTune, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[FineTune, AsyncPage[FineTune]]: + """List your organization's fine-tuning jobs""" + return self._get_api_list( + "/fine-tunes", + page=AsyncPage[FineTune], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=FineTune, + ) + + async def cancel( + self, + fine_tune_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTune: + """ + Immediately cancel a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + f"/fine-tunes/{fine_tune_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTune, + ) + + @overload + async def list_events( + self, + fine_tune_id: str, + *, + stream: Literal[False] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = 86400, + ) -> FineTuneEventsListResponse: + """ + Get fine-grained status updates for a fine-tune job. + + Args: + stream: Whether to stream events for the fine-tune job. If set to true, events will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available. The stream will terminate with a `data: [DONE]` + message when the job is finished (succeeded, cancelled, or failed). + + If set to false, only events generated so far will be returned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def list_events( + self, + fine_tune_id: str, + *, + stream: Literal[True], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = 86400, + ) -> AsyncStream[FineTuneEvent]: + """ + Get fine-grained status updates for a fine-tune job. + + Args: + stream: Whether to stream events for the fine-tune job. If set to true, events will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available. The stream will terminate with a `data: [DONE]` + message when the job is finished (succeeded, cancelled, or failed). + + If set to false, only events generated so far will be returned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def list_events( + self, + fine_tune_id: str, + *, + stream: bool, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = 86400, + ) -> FineTuneEventsListResponse | AsyncStream[FineTuneEvent]: + """ + Get fine-grained status updates for a fine-tune job. + + Args: + stream: Whether to stream events for the fine-tune job. If set to true, events will be + sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available. The stream will terminate with a `data: [DONE]` + message when the job is finished (succeeded, cancelled, or failed). + + If set to false, only events generated so far will be returned. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + async def list_events( + self, + fine_tune_id: str, + *, + stream: Literal[False] | Literal[True] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = 86400, + ) -> FineTuneEventsListResponse | AsyncStream[FineTuneEvent]: + return await self._get( + f"/fine-tunes/{fine_tune_id}/events", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"stream": stream}, fine_tune_list_events_params.FineTuneListEventsParams), + ), + cast_to=FineTuneEventsListResponse, + stream=stream or False, + stream_cls=AsyncStream[FineTuneEvent], + ) + + +class FineTunesWithRawResponse: + def __init__(self, fine_tunes: FineTunes) -> None: + self.create = to_raw_response_wrapper( + fine_tunes.create, + ) + self.retrieve = to_raw_response_wrapper( + fine_tunes.retrieve, + ) + self.list = to_raw_response_wrapper( + fine_tunes.list, + ) + self.cancel = to_raw_response_wrapper( + fine_tunes.cancel, + ) + self.list_events = to_raw_response_wrapper( + fine_tunes.list_events, + ) + + +class AsyncFineTunesWithRawResponse: + def __init__(self, fine_tunes: AsyncFineTunes) -> None: + self.create = async_to_raw_response_wrapper( + fine_tunes.create, + ) + self.retrieve = async_to_raw_response_wrapper( + fine_tunes.retrieve, + ) + self.list = async_to_raw_response_wrapper( + fine_tunes.list, + ) + self.cancel = async_to_raw_response_wrapper( + fine_tunes.cancel, + ) + self.list_events = async_to_raw_response_wrapper( + fine_tunes.list_events, + ) diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py new file mode 100644 index 0000000000..9133c25d4a --- /dev/null +++ b/src/openai/resources/fine_tuning/__init__.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. + +from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse +from .fine_tuning import ( + FineTuning, + AsyncFineTuning, + FineTuningWithRawResponse, + AsyncFineTuningWithRawResponse, +) + +__all__ = [ + "Jobs", + "AsyncJobs", + "JobsWithRawResponse", + "AsyncJobsWithRawResponse", + "FineTuning", + "AsyncFineTuning", + "FineTuningWithRawResponse", + "AsyncFineTuningWithRawResponse", +] diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py new file mode 100644 index 0000000000..2e5f36e546 --- /dev/null +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse +from ..._resource import SyncAPIResource, AsyncAPIResource + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["FineTuning", "AsyncFineTuning"] + + +class FineTuning(SyncAPIResource): + jobs: Jobs + with_raw_response: FineTuningWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.jobs = Jobs(client) + self.with_raw_response = FineTuningWithRawResponse(self) + + +class AsyncFineTuning(AsyncAPIResource): + jobs: AsyncJobs + with_raw_response: AsyncFineTuningWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.jobs = AsyncJobs(client) + self.with_raw_response = AsyncFineTuningWithRawResponse(self) + + +class FineTuningWithRawResponse: + def __init__(self, fine_tuning: FineTuning) -> None: + self.jobs = JobsWithRawResponse(fine_tuning.jobs) + + +class AsyncFineTuningWithRawResponse: + def __init__(self, fine_tuning: AsyncFineTuning) -> None: + self.jobs = AsyncJobsWithRawResponse(fine_tuning.jobs) diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py new file mode 100644 index 0000000000..b721c892b5 --- /dev/null +++ b/src/openai/resources/fine_tuning/jobs.py @@ -0,0 +1,567 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Union, Optional +from typing_extensions import Literal + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.fine_tuning import ( + FineTuningJob, + FineTuningJobEvent, + job_list_params, + job_create_params, + job_list_events_params, +) + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Jobs", "AsyncJobs"] + + +class Jobs(SyncAPIResource): + with_raw_response: JobsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = JobsWithRawResponse(self) + + def create( + self, + *, + model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo"]], + training_file: str, + hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + validation_file: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Creates a job that fine-tunes a specified model from a given dataset. + + Response includes details of the enqueued job including job status and the name + of the fine-tuned models once complete. + + [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) + + Args: + model: The name of the model to fine-tune. You can select one of the + [supported models](https://platform.openai.com/docs/guides/fine-tuning/what-models-can-be-fine-tuned). + + training_file: The ID of an uploaded file that contains training data. + + See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + for how to upload a file. + + Your dataset must be formatted as a JSONL file. Additionally, you must upload + your file with the purpose `fine-tune`. + + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + for more details. + + hyperparameters: The hyperparameters used for the fine-tuning job. + + suffix: A string of up to 18 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ft:gpt-3.5-turbo:openai:custom-model-name:7p4lURel`. + + validation_file: The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the fine-tuning + results file. The same data should not be present in both train and validation + files. + + Your dataset must be formatted as a JSONL file. You must upload your file with + the purpose `fine-tune`. + + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + for more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/fine_tuning/jobs", + body=maybe_transform( + { + "model": model, + "training_file": training_file, + "hyperparameters": hyperparameters, + "suffix": suffix, + "validation_file": validation_file, + }, + job_create_params.JobCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + + def retrieve( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Get info about a fine-tuning job. + + [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/fine_tuning/jobs/{fine_tuning_job_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[FineTuningJob]: + """ + List your organization's fine-tuning jobs + + Args: + after: Identifier for the last job from the previous pagination request. + + limit: Number of fine-tuning jobs to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/fine_tuning/jobs", + page=SyncCursorPage[FineTuningJob], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + job_list_params.JobListParams, + ), + ), + model=FineTuningJob, + ) + + def cancel( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Immediately cancel a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + f"/fine_tuning/jobs/{fine_tuning_job_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + + def list_events( + self, + fine_tuning_job_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[FineTuningJobEvent]: + """ + Get status updates for a fine-tuning job. + + Args: + after: Identifier for the last event from the previous pagination request. + + limit: Number of events to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + f"/fine_tuning/jobs/{fine_tuning_job_id}/events", + page=SyncCursorPage[FineTuningJobEvent], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + job_list_events_params.JobListEventsParams, + ), + ), + model=FineTuningJobEvent, + ) + + +class AsyncJobs(AsyncAPIResource): + with_raw_response: AsyncJobsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncJobsWithRawResponse(self) + + async def create( + self, + *, + model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo"]], + training_file: str, + hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, + suffix: Optional[str] | NotGiven = NOT_GIVEN, + validation_file: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Creates a job that fine-tunes a specified model from a given dataset. + + Response includes details of the enqueued job including job status and the name + of the fine-tuned models once complete. + + [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) + + Args: + model: The name of the model to fine-tune. You can select one of the + [supported models](https://platform.openai.com/docs/guides/fine-tuning/what-models-can-be-fine-tuned). + + training_file: The ID of an uploaded file that contains training data. + + See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + for how to upload a file. + + Your dataset must be formatted as a JSONL file. Additionally, you must upload + your file with the purpose `fine-tune`. + + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + for more details. + + hyperparameters: The hyperparameters used for the fine-tuning job. + + suffix: A string of up to 18 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ft:gpt-3.5-turbo:openai:custom-model-name:7p4lURel`. + + validation_file: The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the fine-tuning + results file. The same data should not be present in both train and validation + files. + + Your dataset must be formatted as a JSONL file. You must upload your file with + the purpose `fine-tune`. + + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + for more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/fine_tuning/jobs", + body=maybe_transform( + { + "model": model, + "training_file": training_file, + "hyperparameters": hyperparameters, + "suffix": suffix, + "validation_file": validation_file, + }, + job_create_params.JobCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + + async def retrieve( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Get info about a fine-tuning job. + + [Learn more about fine-tuning](https://platform.openai.com/docs/guides/fine-tuning) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/fine_tuning/jobs/{fine_tuning_job_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[FineTuningJob, AsyncCursorPage[FineTuningJob]]: + """ + List your organization's fine-tuning jobs + + Args: + after: Identifier for the last job from the previous pagination request. + + limit: Number of fine-tuning jobs to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/fine_tuning/jobs", + page=AsyncCursorPage[FineTuningJob], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + job_list_params.JobListParams, + ), + ), + model=FineTuningJob, + ) + + async def cancel( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Immediately cancel a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + f"/fine_tuning/jobs/{fine_tuning_job_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + + def list_events( + self, + fine_tuning_job_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[FineTuningJobEvent, AsyncCursorPage[FineTuningJobEvent]]: + """ + Get status updates for a fine-tuning job. + + Args: + after: Identifier for the last event from the previous pagination request. + + limit: Number of events to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + f"/fine_tuning/jobs/{fine_tuning_job_id}/events", + page=AsyncCursorPage[FineTuningJobEvent], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + job_list_events_params.JobListEventsParams, + ), + ), + model=FineTuningJobEvent, + ) + + +class JobsWithRawResponse: + def __init__(self, jobs: Jobs) -> None: + self.create = to_raw_response_wrapper( + jobs.create, + ) + self.retrieve = to_raw_response_wrapper( + jobs.retrieve, + ) + self.list = to_raw_response_wrapper( + jobs.list, + ) + self.cancel = to_raw_response_wrapper( + jobs.cancel, + ) + self.list_events = to_raw_response_wrapper( + jobs.list_events, + ) + + +class AsyncJobsWithRawResponse: + def __init__(self, jobs: AsyncJobs) -> None: + self.create = async_to_raw_response_wrapper( + jobs.create, + ) + self.retrieve = async_to_raw_response_wrapper( + jobs.retrieve, + ) + self.list = async_to_raw_response_wrapper( + jobs.list, + ) + self.cancel = async_to_raw_response_wrapper( + jobs.cancel, + ) + self.list_events = async_to_raw_response_wrapper( + jobs.list_events, + ) diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py new file mode 100644 index 0000000000..1fd39b43a6 --- /dev/null +++ b/src/openai/resources/images.py @@ -0,0 +1,479 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Mapping, Optional, cast +from typing_extensions import Literal + +from ..types import ( + ImagesResponse, + image_edit_params, + image_generate_params, + image_create_variation_params, +) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from .._utils import extract_files, maybe_transform, deepcopy_minimal +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._base_client import make_request_options + +if TYPE_CHECKING: + from .._client import OpenAI, AsyncOpenAI + +__all__ = ["Images", "AsyncImages"] + + +class Images(SyncAPIResource): + with_raw_response: ImagesWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = ImagesWithRawResponse(self) + + def create_variation( + self, + *, + image: FileTypes, + n: Optional[int] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """ + Creates a variation of a given image. + + Args: + image: The image to use as the basis for the variation(s). Must be a valid PNG file, + less than 4MB, and square. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "image": image, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return self._post( + "/images/variations", + body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + ) + + def edit( + self, + *, + image: FileTypes, + prompt: str, + mask: FileTypes | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """ + Creates an edited or extended image given an original image and a prompt. + + Args: + image: The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask + is not provided, image must have transparency, which will be used as the mask. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "image": image, + "prompt": prompt, + "mask": mask, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["mask"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return self._post( + "/images/edits", + body=maybe_transform(body, image_edit_params.ImageEditParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + ) + + def generate( + self, + *, + prompt: str, + n: Optional[int] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """ + Creates an image given a prompt. + + Args: + prompt: A text description of the desired image(s). The maximum length is 1000 + characters. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/images/generations", + body=maybe_transform( + { + "prompt": prompt, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + }, + image_generate_params.ImageGenerateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + ) + + +class AsyncImages(AsyncAPIResource): + with_raw_response: AsyncImagesWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncImagesWithRawResponse(self) + + async def create_variation( + self, + *, + image: FileTypes, + n: Optional[int] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """ + Creates a variation of a given image. + + Args: + image: The image to use as the basis for the variation(s). Must be a valid PNG file, + less than 4MB, and square. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "image": image, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return await self._post( + "/images/variations", + body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + ) + + async def edit( + self, + *, + image: FileTypes, + prompt: str, + mask: FileTypes | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """ + Creates an edited or extended image given an original image and a prompt. + + Args: + image: The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask + is not provided, image must have transparency, which will be used as the mask. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "image": image, + "prompt": prompt, + "mask": mask, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["mask"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + + return await self._post( + "/images/edits", + body=maybe_transform(body, image_edit_params.ImageEditParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + ) + + async def generate( + self, + *, + prompt: str, + n: Optional[int] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """ + Creates an image given a prompt. + + Args: + prompt: A text description of the desired image(s). The maximum length is 1000 + characters. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/images/generations", + body=maybe_transform( + { + "prompt": prompt, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + }, + image_generate_params.ImageGenerateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + ) + + +class ImagesWithRawResponse: + def __init__(self, images: Images) -> None: + self.create_variation = to_raw_response_wrapper( + images.create_variation, + ) + self.edit = to_raw_response_wrapper( + images.edit, + ) + self.generate = to_raw_response_wrapper( + images.generate, + ) + + +class AsyncImagesWithRawResponse: + def __init__(self, images: AsyncImages) -> None: + self.create_variation = async_to_raw_response_wrapper( + images.create_variation, + ) + self.edit = async_to_raw_response_wrapper( + images.edit, + ) + self.generate = async_to_raw_response_wrapper( + images.generate, + ) diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py new file mode 100644 index 0000000000..689bbd6621 --- /dev/null +++ b/src/openai/resources/models.py @@ -0,0 +1,235 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ..types import Model, ModelDeleted +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ..pagination import SyncPage, AsyncPage +from .._base_client import AsyncPaginator, make_request_options + +if TYPE_CHECKING: + from .._client import OpenAI, AsyncOpenAI + +__all__ = ["Models", "AsyncModels"] + + +class Models(SyncAPIResource): + with_raw_response: ModelsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = ModelsWithRawResponse(self) + + def retrieve( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Model: + """ + Retrieves a model instance, providing basic information about the model such as + the owner and permissioning. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/models/{model}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Model, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncPage[Model]: + """ + Lists the currently available models, and provides basic information about each + one such as the owner and availability. + """ + return self._get_api_list( + "/models", + page=SyncPage[Model], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=Model, + ) + + def delete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ModelDeleted: + """Delete a fine-tuned model. + + You must have the Owner role in your organization to + delete a model. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._delete( + f"/models/{model}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ModelDeleted, + ) + + +class AsyncModels(AsyncAPIResource): + with_raw_response: AsyncModelsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncModelsWithRawResponse(self) + + async def retrieve( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Model: + """ + Retrieves a model instance, providing basic information about the model such as + the owner and permissioning. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/models/{model}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Model, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Model, AsyncPage[Model]]: + """ + Lists the currently available models, and provides basic information about each + one such as the owner and availability. + """ + return self._get_api_list( + "/models", + page=AsyncPage[Model], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=Model, + ) + + async def delete( + self, + model: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ModelDeleted: + """Delete a fine-tuned model. + + You must have the Owner role in your organization to + delete a model. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._delete( + f"/models/{model}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ModelDeleted, + ) + + +class ModelsWithRawResponse: + def __init__(self, models: Models) -> None: + self.retrieve = to_raw_response_wrapper( + models.retrieve, + ) + self.list = to_raw_response_wrapper( + models.list, + ) + self.delete = to_raw_response_wrapper( + models.delete, + ) + + +class AsyncModelsWithRawResponse: + def __init__(self, models: AsyncModels) -> None: + self.retrieve = async_to_raw_response_wrapper( + models.retrieve, + ) + self.list = async_to_raw_response_wrapper( + models.list, + ) + self.delete = async_to_raw_response_wrapper( + models.delete, + ) diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py new file mode 100644 index 0000000000..1ee3e72564 --- /dev/null +++ b/src/openai/resources/moderations.py @@ -0,0 +1,148 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Union +from typing_extensions import Literal + +from ..types import ModerationCreateResponse, moderation_create_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import maybe_transform +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._base_client import make_request_options + +if TYPE_CHECKING: + from .._client import OpenAI, AsyncOpenAI + +__all__ = ["Moderations", "AsyncModerations"] + + +class Moderations(SyncAPIResource): + with_raw_response: ModerationsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = ModerationsWithRawResponse(self) + + def create( + self, + *, + input: Union[str, List[str]], + model: Union[str, Literal["text-moderation-latest", "text-moderation-stable"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ModerationCreateResponse: + """ + Classifies if text violates OpenAI's Content Policy + + Args: + input: The input text to classify + + model: Two content moderations models are available: `text-moderation-stable` and + `text-moderation-latest`. + + The default is `text-moderation-latest` which will be automatically upgraded + over time. This ensures you are always using our most accurate model. If you use + `text-moderation-stable`, we will provide advanced notice before updating the + model. Accuracy of `text-moderation-stable` may be slightly lower than for + `text-moderation-latest`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/moderations", + body=maybe_transform( + { + "input": input, + "model": model, + }, + moderation_create_params.ModerationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ModerationCreateResponse, + ) + + +class AsyncModerations(AsyncAPIResource): + with_raw_response: AsyncModerationsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncModerationsWithRawResponse(self) + + async def create( + self, + *, + input: Union[str, List[str]], + model: Union[str, Literal["text-moderation-latest", "text-moderation-stable"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ModerationCreateResponse: + """ + Classifies if text violates OpenAI's Content Policy + + Args: + input: The input text to classify + + model: Two content moderations models are available: `text-moderation-stable` and + `text-moderation-latest`. + + The default is `text-moderation-latest` which will be automatically upgraded + over time. This ensures you are always using our most accurate model. If you use + `text-moderation-stable`, we will provide advanced notice before updating the + model. Accuracy of `text-moderation-stable` may be slightly lower than for + `text-moderation-latest`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/moderations", + body=maybe_transform( + { + "input": input, + "model": model, + }, + moderation_create_params.ModerationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ModerationCreateResponse, + ) + + +class ModerationsWithRawResponse: + def __init__(self, moderations: Moderations) -> None: + self.create = to_raw_response_wrapper( + moderations.create, + ) + + +class AsyncModerationsWithRawResponse: + def __init__(self, moderations: AsyncModerations) -> None: + self.create = async_to_raw_response_wrapper( + moderations.create, + ) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py new file mode 100644 index 0000000000..defaf13446 --- /dev/null +++ b/src/openai/types/__init__.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .edit import Edit as Edit +from .image import Image as Image +from .model import Model as Model +from .embedding import Embedding as Embedding +from .fine_tune import FineTune as FineTune +from .completion import Completion as Completion +from .moderation import Moderation as Moderation +from .file_object import FileObject as FileObject +from .file_content import FileContent as FileContent +from .file_deleted import FileDeleted as FileDeleted +from .model_deleted import ModelDeleted as ModelDeleted +from .fine_tune_event import FineTuneEvent as FineTuneEvent +from .images_response import ImagesResponse as ImagesResponse +from .completion_usage import CompletionUsage as CompletionUsage +from .completion_choice import CompletionChoice as CompletionChoice +from .image_edit_params import ImageEditParams as ImageEditParams +from .edit_create_params import EditCreateParams as EditCreateParams +from .file_create_params import FileCreateParams as FileCreateParams +from .image_generate_params import ImageGenerateParams as ImageGenerateParams +from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams +from .fine_tune_create_params import FineTuneCreateParams as FineTuneCreateParams +from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .moderation_create_params import ModerationCreateParams as ModerationCreateParams +from .create_embedding_response import ( + CreateEmbeddingResponse as CreateEmbeddingResponse, +) +from .moderation_create_response import ( + ModerationCreateResponse as ModerationCreateResponse, +) +from .fine_tune_list_events_params import ( + FineTuneListEventsParams as FineTuneListEventsParams, +) +from .image_create_variation_params import ( + ImageCreateVariationParams as ImageCreateVariationParams, +) +from .fine_tune_events_list_response import ( + FineTuneEventsListResponse as FineTuneEventsListResponse, +) diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py new file mode 100644 index 0000000000..469bc6f25b --- /dev/null +++ b/src/openai/types/audio/__init__.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .translation import Translation as Translation +from .transcription import Transcription as Transcription +from .translation_create_params import ( + TranslationCreateParams as TranslationCreateParams, +) +from .transcription_create_params import ( + TranscriptionCreateParams as TranscriptionCreateParams, +) diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py new file mode 100644 index 0000000000..d2274faa0e --- /dev/null +++ b/src/openai/types/audio/transcription.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. + +from ..._models import BaseModel + +__all__ = ["Transcription"] + + +class Transcription(BaseModel): + text: str diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py new file mode 100644 index 0000000000..f8f193484a --- /dev/null +++ b/src/openai/types/audio/transcription_create_params.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +from ..._types import FileTypes + +__all__ = ["TranscriptionCreateParams"] + + +class TranscriptionCreateParams(TypedDict, total=False): + file: Required[FileTypes] + """ + The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + """ + + model: Required[Union[str, Literal["whisper-1"]]] + """ID of the model to use. Only `whisper-1` is currently available.""" + + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will + improve accuracy and latency. + """ + + prompt: str + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + should match the audio language. + """ + + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] + """ + The format of the transcript output, in one of these options: json, text, srt, + verbose_json, or vtt. + """ + + temperature: float + """The sampling temperature, between 0 and 1. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + """ diff --git a/src/openai/types/audio/translation.py b/src/openai/types/audio/translation.py new file mode 100644 index 0000000000..a01d622abc --- /dev/null +++ b/src/openai/types/audio/translation.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. + +from ..._models import BaseModel + +__all__ = ["Translation"] + + +class Translation(BaseModel): + text: str diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py new file mode 100644 index 0000000000..bfa5fc56d2 --- /dev/null +++ b/src/openai/types/audio/translation_create_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +from ..._types import FileTypes + +__all__ = ["TranslationCreateParams"] + + +class TranslationCreateParams(TypedDict, total=False): + file: Required[FileTypes] + """ + The audio file object (not file name) translate, in one of these formats: flac, + mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + """ + + model: Required[Union[str, Literal["whisper-1"]]] + """ID of the model to use. Only `whisper-1` is currently available.""" + + prompt: str + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + should be in English. + """ + + response_format: str + """ + The format of the transcript output, in one of these options: json, text, srt, + verbose_json, or vtt. + """ + + temperature: float + """The sampling temperature, between 0 and 1. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + """ diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py new file mode 100644 index 0000000000..2f23cf3ca4 --- /dev/null +++ b/src/openai/types/chat/__init__.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .chat_completion import ChatCompletion as ChatCompletion +from .chat_completion_role import ChatCompletionRole as ChatCompletionRole +from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk +from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage +from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .chat_completion_message_param import ( + ChatCompletionMessageParam as ChatCompletionMessageParam, +) diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py new file mode 100644 index 0000000000..8d7a0b9716 --- /dev/null +++ b/src/openai/types/chat/chat_completion.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..completion_usage import CompletionUsage +from .chat_completion_message import ChatCompletionMessage + +__all__ = ["ChatCompletion", "Choice"] + + +class Choice(BaseModel): + finish_reason: Literal["stop", "length", "function_call", "content_filter"] + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, `length` if the maximum number of tokens specified in the request was + reached, `content_filter` if content was omitted due to a flag from our content + filters, or `function_call` if the model called a function. + """ + + index: int + """The index of the choice in the list of choices.""" + + message: ChatCompletionMessage + """A chat completion message generated by the model.""" + + +class ChatCompletion(BaseModel): + id: str + """A unique identifier for the chat completion.""" + + choices: List[Choice] + """A list of chat completion choices. + + Can be more than one if `n` is greater than 1. + """ + + created: int + """The Unix timestamp (in seconds) of when the chat completion was created.""" + + model: str + """The model used for the chat completion.""" + + object: str + """The object type, which is always `chat.completion`.""" + + usage: Optional[CompletionUsage] = None + """Usage statistics for the completion request.""" diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py new file mode 100644 index 0000000000..66610898b4 --- /dev/null +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .chat_completion_role import ChatCompletionRole + +__all__ = ["ChatCompletionChunk", "Choice", "ChoiceDelta", "ChoiceDeltaFunctionCall"] + + +class ChoiceDeltaFunctionCall(BaseModel): + arguments: Optional[str] = None + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Optional[str] = None + """The name of the function to call.""" + + +class ChoiceDelta(BaseModel): + content: Optional[str] = None + """The contents of the chunk message.""" + + function_call: Optional[ChoiceDeltaFunctionCall] = None + """ + The name and arguments of a function that should be called, as generated by the + model. + """ + + role: Optional[ChatCompletionRole] = None + """The role of the author of this message.""" + + +class Choice(BaseModel): + delta: ChoiceDelta + """A chat completion delta generated by streamed model responses.""" + + finish_reason: Optional[Literal["stop", "length", "function_call", "content_filter"]] + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, `length` if the maximum number of tokens specified in the request was + reached, `content_filter` if content was omitted due to a flag from our content + filters, or `function_call` if the model called a function. + """ + + index: int + """The index of the choice in the list of choices.""" + + +class ChatCompletionChunk(BaseModel): + id: str + """A unique identifier for the chat completion. Each chunk has the same ID.""" + + choices: List[Choice] + """A list of chat completion choices. + + Can be more than one if `n` is greater than 1. + """ + + created: int + """The Unix timestamp (in seconds) of when the chat completion was created. + + Each chunk has the same timestamp. + """ + + model: str + """The model to generate the completion.""" + + object: str + """The object type, which is always `chat.completion.chunk`.""" diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py new file mode 100644 index 0000000000..531eb3d43c --- /dev/null +++ b/src/openai/types/chat/chat_completion_message.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional + +from ..._models import BaseModel +from .chat_completion_role import ChatCompletionRole + +__all__ = ["ChatCompletionMessage", "FunctionCall"] + + +class FunctionCall(BaseModel): + arguments: str + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: str + """The name of the function to call.""" + + +class ChatCompletionMessage(BaseModel): + content: Optional[str] + """The contents of the message.""" + + role: ChatCompletionRole + """The role of the author of this message.""" + + function_call: Optional[FunctionCall] = None + """ + The name and arguments of a function that should be called, as generated by the + model. + """ diff --git a/src/openai/types/chat/chat_completion_message_param.py b/src/openai/types/chat/chat_completion_message_param.py new file mode 100644 index 0000000000..29b8882573 --- /dev/null +++ b/src/openai/types/chat/chat_completion_message_param.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionMessageParam", "FunctionCall"] + + +class FunctionCall(TypedDict, total=False): + arguments: Required[str] + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Required[str] + """The name of the function to call.""" + + +class ChatCompletionMessageParam(TypedDict, total=False): + content: Required[Optional[str]] + """The contents of the message. + + `content` is required for all messages, and may be null for assistant messages + with function calls. + """ + + role: Required[Literal["system", "user", "assistant", "function"]] + """The role of the messages author. + + One of `system`, `user`, `assistant`, or `function`. + """ + + function_call: FunctionCall + """ + The name and arguments of a function that should be called, as generated by the + model. + """ + + name: str + """The name of the author of this message. + + `name` is required if role is `function`, and it should be the name of the + function whose response is in the `content`. May contain a-z, A-Z, 0-9, and + underscores, with a maximum length of 64 characters. + """ diff --git a/src/openai/types/chat/chat_completion_role.py b/src/openai/types/chat/chat_completion_role.py new file mode 100644 index 0000000000..da8896a072 --- /dev/null +++ b/src/openai/types/chat/chat_completion_role.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +__all__ = ["ChatCompletionRole"] + +ChatCompletionRole = Literal["system", "user", "assistant", "function"] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py new file mode 100644 index 0000000000..d681a90cd6 --- /dev/null +++ b/src/openai/types/chat/completion_create_params.py @@ -0,0 +1,194 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_message_param import ChatCompletionMessageParam + +__all__ = [ + "CompletionCreateParamsBase", + "FunctionCall", + "FunctionCallFunctionCallOption", + "Function", + "CompletionCreateParamsNonStreaming", + "CompletionCreateParamsStreaming", +] + + +class CompletionCreateParamsBase(TypedDict, total=False): + messages: Required[List[ChatCompletionMessageParam]] + """A list of messages comprising the conversation so far. + + [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + """ + + model: Required[ + Union[ + str, + Literal[ + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k-0613", + ], + ] + ] + """ID of the model to use. + + See the + [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + table for details on which models work with the Chat API. + """ + + frequency_penalty: Optional[float] + """Number between -2.0 and 2.0. + + 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + """ + + function_call: FunctionCall + """Controls how the model calls functions. + + "none" means the model will not call a function and instead generates a message. + "auto" means the model can pick between generating a message or calling a + function. Specifying a particular function via `{"name": "my_function"}` forces + the model to call that function. "none" is the default when no functions are + present. "auto" is the default if functions are present. + """ + + functions: List[Function] + """A list of functions the model may generate JSON inputs for.""" + + logit_bias: Optional[Dict[str, int]] + """Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the + tokenizer) to an associated bias value from -100 to 100. Mathematically, the + bias is added to the logits generated by the model prior to sampling. The exact + effect will vary per model, but values between -1 and 1 should decrease or + increase likelihood of selection; values like -100 or 100 should result in a ban + or exclusive selection of the relevant token. + """ + + max_tokens: Optional[int] + """The maximum number of [tokens](/tokenizer) to generate in the chat completion. + + The total length of input tokens and generated tokens is limited by the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + """ + + n: Optional[int] + """How many chat completion choices to generate for each input message.""" + + presence_penalty: Optional[float] + """Number between -2.0 and 2.0. + + 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + """ + + stop: Union[Optional[str], List[str]] + """Up to 4 sequences where the API will stop generating further tokens.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + """ + + +class FunctionCallFunctionCallOption(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" + + +FunctionCall = Union[Literal["none", "auto"], FunctionCallFunctionCallOption] + + +class Function(TypedDict, total=False): + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Required[Dict[str, object]] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + +class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase): + stream: Optional[Literal[False]] + """If set, partial message deltas will be sent, like in ChatGPT. + + Tokens will be sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + """ + + +class CompletionCreateParamsStreaming(CompletionCreateParamsBase): + stream: Required[Literal[True]] + """If set, partial message deltas will be sent, like in ChatGPT. + + Tokens will be sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + """ + + +CompletionCreateParams = Union[CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming] diff --git a/src/openai/types/completion.py b/src/openai/types/completion.py new file mode 100644 index 0000000000..0a90838fd4 --- /dev/null +++ b/src/openai/types/completion.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional + +from .._models import BaseModel +from .completion_usage import CompletionUsage +from .completion_choice import CompletionChoice + +__all__ = ["Completion"] + + +class Completion(BaseModel): + id: str + """A unique identifier for the completion.""" + + choices: List[CompletionChoice] + """The list of completion choices the model generated for the input prompt.""" + + created: int + """The Unix timestamp (in seconds) of when the completion was created.""" + + model: str + """The model used for completion.""" + + object: str + """The object type, which is always "text_completion" """ + + usage: Optional[CompletionUsage] = None + """Usage statistics for the completion request.""" diff --git a/src/openai/types/completion_choice.py b/src/openai/types/completion_choice.py new file mode 100644 index 0000000000..e86d706ed1 --- /dev/null +++ b/src/openai/types/completion_choice.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Dict, List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["CompletionChoice", "Logprobs"] + + +class Logprobs(BaseModel): + text_offset: Optional[List[int]] = None + + token_logprobs: Optional[List[float]] = None + + tokens: Optional[List[str]] = None + + top_logprobs: Optional[List[Dict[str, int]]] = None + + +class CompletionChoice(BaseModel): + finish_reason: Literal["stop", "length", "content_filter"] + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, `length` if the maximum number of tokens specified in the request was + reached, or `content_filter` if content was omitted due to a flag from our + content filters. + """ + + index: int + + logprobs: Optional[Logprobs] + + text: str diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py new file mode 100644 index 0000000000..023c087d5f --- /dev/null +++ b/src/openai/types/completion_create_params.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["CompletionCreateParamsBase", "CompletionCreateParamsNonStreaming", "CompletionCreateParamsStreaming"] + + +class CompletionCreateParamsBase(TypedDict, total=False): + model: Required[ + Union[ + str, + Literal[ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo-instruct", + "text-davinci-003", + "text-davinci-002", + "text-davinci-001", + "code-davinci-002", + "text-curie-001", + "text-babbage-001", + "text-ada-001", + ], + ] + ] + """ID of the model to use. + + You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + """ + + prompt: Required[Union[str, List[str], List[int], List[List[int]], None]] + """ + The prompt(s) to generate completions for, encoded as a string, array of + strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during + training, so if a prompt is not specified the model will generate as if from the + beginning of a new document. + """ + + best_of: Optional[int] + """ + Generates `best_of` completions server-side and returns the "best" (the one with + the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and + `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + """ + + echo: Optional[bool] + """Echo back the prompt in addition to the completion""" + + frequency_penalty: Optional[float] + """Number between -2.0 and 2.0. + + 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + """ + + logit_bias: Optional[Dict[str, int]] + """Modify the likelihood of specified tokens appearing in the completion. + + Accepts a json object that maps tokens (specified by their token ID in the GPT + tokenizer) to an associated bias value from -100 to 100. You can use this + [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to + convert text to token IDs. Mathematically, the bias is added to the logits + generated by the model prior to sampling. The exact effect will vary per model, + but values between -1 and 1 should decrease or increase likelihood of selection; + values like -100 or 100 should result in a ban or exclusive selection of the + relevant token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token + from being generated. + """ + + logprobs: Optional[int] + """ + Include the log probabilities on the `logprobs` most likely tokens, as well the + chosen tokens. For example, if `logprobs` is 5, the API will return a list of + the 5 most likely tokens. The API will always return the `logprob` of the + sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + """ + + max_tokens: Optional[int] + """The maximum number of [tokens](/tokenizer) to generate in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's + context length. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + """ + + n: Optional[int] + """How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly + consume your token quota. Use carefully and ensure that you have reasonable + settings for `max_tokens` and `stop`. + """ + + presence_penalty: Optional[float] + """Number between -2.0 and 2.0. + + 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. + + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + """ + + stop: Union[Optional[str], List[str], None] + """Up to 4 sequences where the API will stop generating further tokens. + + The returned text will not contain the stop sequence. + """ + + suffix: Optional[str] + """The suffix that comes after a completion of inserted text.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + """ + + +class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase): + stream: Optional[Literal[False]] + """Whether to stream back partial progress. + + If set, tokens will be sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + """ + + +class CompletionCreateParamsStreaming(CompletionCreateParamsBase): + stream: Required[Literal[True]] + """Whether to stream back partial progress. + + If set, tokens will be sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` + message. + [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + """ + + +CompletionCreateParams = Union[CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming] diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py new file mode 100644 index 0000000000..b825d5529f --- /dev/null +++ b/src/openai/types/completion_usage.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. + +from .._models import BaseModel + +__all__ = ["CompletionUsage"] + + +class CompletionUsage(BaseModel): + completion_tokens: int + """Number of tokens in the generated completion.""" + + prompt_tokens: int + """Number of tokens in the prompt.""" + + total_tokens: int + """Total number of tokens used in the request (prompt + completion).""" diff --git a/src/openai/types/create_embedding_response.py b/src/openai/types/create_embedding_response.py new file mode 100644 index 0000000000..eccd148d3c --- /dev/null +++ b/src/openai/types/create_embedding_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List + +from .._models import BaseModel +from .embedding import Embedding + +__all__ = ["CreateEmbeddingResponse", "Usage"] + + +class Usage(BaseModel): + prompt_tokens: int + """The number of tokens used by the prompt.""" + + total_tokens: int + """The total number of tokens used by the request.""" + + +class CreateEmbeddingResponse(BaseModel): + data: List[Embedding] + """The list of embeddings generated by the model.""" + + model: str + """The name of the model used to generate the embedding.""" + + object: str + """The object type, which is always "embedding".""" + + usage: Usage + """The usage information for the request.""" diff --git a/src/openai/types/edit.py b/src/openai/types/edit.py new file mode 100644 index 0000000000..41b327534e --- /dev/null +++ b/src/openai/types/edit.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List +from typing_extensions import Literal + +from .._models import BaseModel +from .completion_usage import CompletionUsage + +__all__ = ["Edit", "Choice"] + + +class Choice(BaseModel): + finish_reason: Literal["stop", "length"] + """The reason the model stopped generating tokens. + + This will be `stop` if the model hit a natural stop point or a provided stop + sequence, `length` if the maximum number of tokens specified in the request was + reached, or `content_filter` if content was omitted due to a flag from our + content filters. + """ + + index: int + """The index of the choice in the list of choices.""" + + text: str + """The edited result.""" + + +class Edit(BaseModel): + choices: List[Choice] + """A list of edit choices. Can be more than one if `n` is greater than 1.""" + + created: int + """The Unix timestamp (in seconds) of when the edit was created.""" + + object: str + """The object type, which is always `edit`.""" + + usage: CompletionUsage + """Usage statistics for the completion request.""" diff --git a/src/openai/types/edit_create_params.py b/src/openai/types/edit_create_params.py new file mode 100644 index 0000000000..a23b79c369 --- /dev/null +++ b/src/openai/types/edit_create_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["EditCreateParams"] + + +class EditCreateParams(TypedDict, total=False): + instruction: Required[str] + """The instruction that tells the model how to edit the prompt.""" + + model: Required[Union[str, Literal["text-davinci-edit-001", "code-davinci-edit-001"]]] + """ID of the model to use. + + You can use the `text-davinci-edit-001` or `code-davinci-edit-001` model with + this endpoint. + """ + + input: Optional[str] + """The input text to use as a starting point for the edit.""" + + n: Optional[int] + """How many edits to generate for the input and instruction.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + """ diff --git a/src/openai/types/embedding.py b/src/openai/types/embedding.py new file mode 100644 index 0000000000..4579b9bb57 --- /dev/null +++ b/src/openai/types/embedding.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List + +from .._models import BaseModel + +__all__ = ["Embedding"] + + +class Embedding(BaseModel): + embedding: List[float] + """The embedding vector, which is a list of floats. + + The length of vector depends on the model as listed in the + [embedding guide](https://platform.openai.com/docs/guides/embeddings). + """ + + index: int + """The index of the embedding in the list of embeddings.""" + + object: str + """The object type, which is always "embedding".""" diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py new file mode 100644 index 0000000000..bc8535f880 --- /dev/null +++ b/src/openai/types/embedding_create_params.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["EmbeddingCreateParams"] + + +class EmbeddingCreateParams(TypedDict, total=False): + input: Required[Union[str, List[str], List[int], List[List[int]]]] + """Input text to embed, encoded as a string or array of tokens. + + To embed multiple inputs in a single request, pass an array of strings or array + of token arrays. The input must not exceed the max input tokens for the model + (8192 tokens for `text-embedding-ada-002`) and cannot be an empty string. + [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) + for counting tokens. + """ + + model: Required[Union[str, Literal["text-embedding-ada-002"]]] + """ID of the model to use. + + You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + """ + + encoding_format: Literal["float", "base64"] + """The format to return the embeddings in. + + Can be either `float` or [`base64`](https://pypi.org/project/pybase64/). + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + """ diff --git a/src/openai/types/file_content.py b/src/openai/types/file_content.py new file mode 100644 index 0000000000..92b316b9eb --- /dev/null +++ b/src/openai/types/file_content.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. + + +__all__ = ["FileContent"] + +FileContent = str diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py new file mode 100644 index 0000000000..07b068c5c6 --- /dev/null +++ b/src/openai/types/file_create_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes + +__all__ = ["FileCreateParams"] + + +class FileCreateParams(TypedDict, total=False): + file: Required[FileTypes] + """The file object (not file name) to be uploaded. + + If the `purpose` is set to "fine-tune", the file will be used for fine-tuning. + """ + + purpose: Required[str] + """The intended purpose of the uploaded file. + + Use "fine-tune" for + [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This + allows us to validate the format of the uploaded file is correct for + fine-tuning. + """ diff --git a/src/openai/types/file_deleted.py b/src/openai/types/file_deleted.py new file mode 100644 index 0000000000..a526b2b986 --- /dev/null +++ b/src/openai/types/file_deleted.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. + +from .._models import BaseModel + +__all__ = ["FileDeleted"] + + +class FileDeleted(BaseModel): + id: str + + deleted: bool + + object: str diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py new file mode 100644 index 0000000000..dac24a88c5 --- /dev/null +++ b/src/openai/types/file_object.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["FileObject"] + + +class FileObject(BaseModel): + id: str + """The file identifier, which can be referenced in the API endpoints.""" + + bytes: int + """The size of the file in bytes.""" + + created_at: int + """The Unix timestamp (in seconds) for when the file was created.""" + + filename: str + """The name of the file.""" + + object: str + """The object type, which is always "file".""" + + purpose: str + """The intended purpose of the file. Currently, only "fine-tune" is supported.""" + + status: Optional[str] = None + """ + The current status of the file, which can be either `uploaded`, `processed`, + `pending`, `error`, `deleting` or `deleted`. + """ + + status_details: Optional[str] = None + """Additional details about the status of the file. + + If the file is in the `error` state, this will include a message describing the + error. + """ diff --git a/src/openai/types/fine_tune.py b/src/openai/types/fine_tune.py new file mode 100644 index 0000000000..4124def2f5 --- /dev/null +++ b/src/openai/types/fine_tune.py @@ -0,0 +1,93 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional + +from .._models import BaseModel +from .file_object import FileObject +from .fine_tune_event import FineTuneEvent + +__all__ = ["FineTune", "Hyperparams"] + + +class Hyperparams(BaseModel): + batch_size: int + """The batch size to use for training. + + The batch size is the number of training examples used to train a single forward + and backward pass. + """ + + learning_rate_multiplier: float + """The learning rate multiplier to use for training.""" + + n_epochs: int + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + prompt_loss_weight: float + """The weight to use for loss on the prompt tokens.""" + + classification_n_classes: Optional[int] = None + """The number of classes to use for computing classification metrics.""" + + classification_positive_class: Optional[str] = None + """The positive class to use for computing classification metrics.""" + + compute_classification_metrics: Optional[bool] = None + """ + The classification metrics to compute using the validation dataset at the end of + every epoch. + """ + + +class FineTune(BaseModel): + id: str + """The object identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the fine-tuning job was created.""" + + fine_tuned_model: Optional[str] + """The name of the fine-tuned model that is being created.""" + + hyperparams: Hyperparams + """The hyperparameters used for the fine-tuning job. + + See the + [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/hyperparameters) + for more details. + """ + + model: str + """The base model that is being fine-tuned.""" + + object: str + """The object type, which is always "fine-tune".""" + + organization_id: str + """The organization that owns the fine-tuning job.""" + + result_files: List[FileObject] + """The compiled results files for the fine-tuning job.""" + + status: str + """ + The current status of the fine-tuning job, which can be either `created`, + `running`, `succeeded`, `failed`, or `cancelled`. + """ + + training_files: List[FileObject] + """The list of files used for training.""" + + updated_at: int + """The Unix timestamp (in seconds) for when the fine-tuning job was last updated.""" + + validation_files: List[FileObject] + """The list of files used for validation.""" + + events: Optional[List[FineTuneEvent]] = None + """ + The list of events that have been observed in the lifecycle of the FineTune job. + """ diff --git a/src/openai/types/fine_tune_create_params.py b/src/openai/types/fine_tune_create_params.py new file mode 100644 index 0000000000..1be9c9ea04 --- /dev/null +++ b/src/openai/types/fine_tune_create_params.py @@ -0,0 +1,140 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FineTuneCreateParams", "Hyperparameters"] + + +class FineTuneCreateParams(TypedDict, total=False): + training_file: Required[str] + """The ID of an uploaded file that contains training data. + + See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + for how to upload a file. + + Your dataset must be formatted as a JSONL file, where each training example is a + JSON object with the keys "prompt" and "completion". Additionally, you must + upload your file with the purpose `fine-tune`. + + See the + [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) + for more details. + """ + + batch_size: Optional[int] + """The batch size to use for training. + + The batch size is the number of training examples used to train a single forward + and backward pass. + + By default, the batch size will be dynamically configured to be ~0.2% of the + number of examples in the training set, capped at 256 - in general, we've found + that larger batch sizes tend to work better for larger datasets. + """ + + classification_betas: Optional[List[float]] + """If this is provided, we calculate F-beta scores at the specified beta values. + + The F-beta score is a generalization of F-1 score. This is only used for binary + classification. + + With a beta of 1 (i.e. the F-1 score), precision and recall are given the same + weight. A larger beta score puts more weight on recall and less on precision. A + smaller beta score puts more weight on precision and less on recall. + """ + + classification_n_classes: Optional[int] + """The number of classes in a classification task. + + This parameter is required for multiclass classification. + """ + + classification_positive_class: Optional[str] + """The positive class in binary classification. + + This parameter is needed to generate precision, recall, and F1 metrics when + doing binary classification. + """ + + compute_classification_metrics: Optional[bool] + """ + If set, we calculate classification-specific metrics such as accuracy and F-1 + score using the validation set at the end of every epoch. These metrics can be + viewed in the + [results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). + + In order to compute classification metrics, you must provide a + `validation_file`. Additionally, you must specify `classification_n_classes` for + multiclass classification or `classification_positive_class` for binary + classification. + """ + + hyperparameters: Hyperparameters + """The hyperparameters used for the fine-tuning job.""" + + learning_rate_multiplier: Optional[float] + """ + The learning rate multiplier to use for training. The fine-tuning learning rate + is the original learning rate used for pretraining multiplied by this value. + + By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 depending on + final `batch_size` (larger learning rates tend to perform better with larger + batch sizes). We recommend experimenting with values in the range 0.02 to 0.2 to + see what produces the best results. + """ + + model: Union[str, Literal["ada", "babbage", "curie", "davinci"], None] + """The name of the base model to fine-tune. + + You can select one of "ada", "babbage", "curie", "davinci", or a fine-tuned + model created after 2022-04-21 and before 2023-08-22. To learn more about these + models, see the [Models](https://platform.openai.com/docs/models) documentation. + """ + + prompt_loss_weight: Optional[float] + """The weight to use for loss on the prompt tokens. + + This controls how much the model tries to learn to generate the prompt (as + compared to the completion which always has a weight of 1.0), and can add a + stabilizing effect to training when completions are short. + + If prompts are extremely long (relative to completions), it may make sense to + reduce this weight so as to avoid over-prioritizing learning the prompt. + """ + + suffix: Optional[str] + """ + A string of up to 40 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ada:ft-your-org:custom-model-name-2022-02-15-04-21-04`. + """ + + validation_file: Optional[str] + """The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the + [fine-tuning results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). + Your train and validation data should be mutually exclusive. + + Your dataset must be formatted as a JSONL file, where each validation example is + a JSON object with the keys "prompt" and "completion". Additionally, you must + upload your file with the purpose `fine-tune`. + + See the + [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) + for more details. + """ + + +class Hyperparameters(TypedDict, total=False): + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/src/openai/types/fine_tune_event.py b/src/openai/types/fine_tune_event.py new file mode 100644 index 0000000000..6499def98d --- /dev/null +++ b/src/openai/types/fine_tune_event.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from .._models import BaseModel + +__all__ = ["FineTuneEvent"] + + +class FineTuneEvent(BaseModel): + created_at: int + + level: str + + message: str + + object: str diff --git a/src/openai/types/fine_tune_events_list_response.py b/src/openai/types/fine_tune_events_list_response.py new file mode 100644 index 0000000000..ca159d8772 --- /dev/null +++ b/src/openai/types/fine_tune_events_list_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List + +from .._models import BaseModel +from .fine_tune_event import FineTuneEvent + +__all__ = ["FineTuneEventsListResponse"] + + +class FineTuneEventsListResponse(BaseModel): + data: List[FineTuneEvent] + + object: str diff --git a/src/openai/types/fine_tune_list_events_params.py b/src/openai/types/fine_tune_list_events_params.py new file mode 100644 index 0000000000..1f23b108e6 --- /dev/null +++ b/src/openai/types/fine_tune_list_events_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FineTuneListEventsParamsBase", "FineTuneListEventsParamsNonStreaming", "FineTuneListEventsParamsStreaming"] + + +class FineTuneListEventsParamsBase(TypedDict, total=False): + pass + + +class FineTuneListEventsParamsNonStreaming(FineTuneListEventsParamsBase): + stream: Literal[False] + """Whether to stream events for the fine-tune job. + + If set to true, events will be sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available. The stream will terminate with a `data: [DONE]` + message when the job is finished (succeeded, cancelled, or failed). + + If set to false, only events generated so far will be returned. + """ + + +class FineTuneListEventsParamsStreaming(FineTuneListEventsParamsBase): + stream: Required[Literal[True]] + """Whether to stream events for the fine-tune job. + + If set to true, events will be sent as data-only + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available. The stream will terminate with a `data: [DONE]` + message when the job is finished (succeeded, cancelled, or failed). + + If set to false, only events generated so far will be returned. + """ + + +FineTuneListEventsParams = Union[FineTuneListEventsParamsNonStreaming, FineTuneListEventsParamsStreaming] diff --git a/src/openai/types/fine_tuning/__init__.py b/src/openai/types/fine_tuning/__init__.py new file mode 100644 index 0000000000..d24160c5bd --- /dev/null +++ b/src/openai/types/fine_tuning/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .fine_tuning_job import FineTuningJob as FineTuningJob +from .job_list_params import JobListParams as JobListParams +from .job_create_params import JobCreateParams as JobCreateParams +from .fine_tuning_job_event import FineTuningJobEvent as FineTuningJobEvent +from .job_list_events_params import JobListEventsParams as JobListEventsParams diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py new file mode 100644 index 0000000000..2ae1cbb473 --- /dev/null +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -0,0 +1,107 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FineTuningJob", "Error", "Hyperparameters"] + + +class Error(BaseModel): + code: str + """A machine-readable error code.""" + + message: str + """A human-readable error message.""" + + param: Optional[str] + """The parameter that was invalid, usually `training_file` or `validation_file`. + + This field will be null if the failure was not parameter-specific. + """ + + +class Hyperparameters(BaseModel): + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. "auto" decides + the optimal number of epochs based on the size of the dataset. If setting the + number manually, we support any number between 1 and 50 epochs. + """ + + +class FineTuningJob(BaseModel): + id: str + """The object identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the fine-tuning job was created.""" + + error: Optional[Error] + """ + For fine-tuning jobs that have `failed`, this will contain more information on + the cause of the failure. + """ + + fine_tuned_model: Optional[str] + """The name of the fine-tuned model that is being created. + + The value will be null if the fine-tuning job is still running. + """ + + finished_at: Optional[int] + """The Unix timestamp (in seconds) for when the fine-tuning job was finished. + + The value will be null if the fine-tuning job is still running. + """ + + hyperparameters: Hyperparameters + """The hyperparameters used for the fine-tuning job. + + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + for more details. + """ + + model: str + """The base model that is being fine-tuned.""" + + object: str + """The object type, which is always "fine_tuning.job".""" + + organization_id: str + """The organization that owns the fine-tuning job.""" + + result_files: List[str] + """The compiled results file ID(s) for the fine-tuning job. + + You can retrieve the results with the + [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents). + """ + + status: str + """ + The current status of the fine-tuning job, which can be either + `validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`. + """ + + trained_tokens: Optional[int] + """The total number of billable tokens processed by this fine-tuning job. + + The value will be null if the fine-tuning job is still running. + """ + + training_file: str + """The file ID used for training. + + You can retrieve the training data with the + [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents). + """ + + validation_file: Optional[str] + """The file ID used for validation. + + You can retrieve the validation results with the + [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents). + """ diff --git a/src/openai/types/fine_tuning/fine_tuning_job_event.py b/src/openai/types/fine_tuning/fine_tuning_job_event.py new file mode 100644 index 0000000000..c21a0503ab --- /dev/null +++ b/src/openai/types/fine_tuning/fine_tuning_job_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FineTuningJobEvent"] + + +class FineTuningJobEvent(BaseModel): + id: str + + created_at: int + + level: Literal["info", "warn", "error"] + + message: str + + object: str diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py new file mode 100644 index 0000000000..2a67b81817 --- /dev/null +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["JobCreateParams", "Hyperparameters"] + + +class JobCreateParams(TypedDict, total=False): + model: Required[Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo"]]] + """The name of the model to fine-tune. + + You can select one of the + [supported models](https://platform.openai.com/docs/guides/fine-tuning/what-models-can-be-fine-tuned). + """ + + training_file: Required[str] + """The ID of an uploaded file that contains training data. + + See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + for how to upload a file. + + Your dataset must be formatted as a JSONL file. Additionally, you must upload + your file with the purpose `fine-tune`. + + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + for more details. + """ + + hyperparameters: Hyperparameters + """The hyperparameters used for the fine-tuning job.""" + + suffix: Optional[str] + """ + A string of up to 18 characters that will be added to your fine-tuned model + name. + + For example, a `suffix` of "custom-model-name" would produce a model name like + `ft:gpt-3.5-turbo:openai:custom-model-name:7p4lURel`. + """ + + validation_file: Optional[str] + """The ID of an uploaded file that contains validation data. + + If you provide this file, the data is used to generate validation metrics + periodically during fine-tuning. These metrics can be viewed in the fine-tuning + results file. The same data should not be present in both train and validation + files. + + Your dataset must be formatted as a JSONL file. You must upload your file with + the purpose `fine-tune`. + + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) + for more details. + """ + + +class Hyperparameters(TypedDict, total=False): + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/src/openai/types/fine_tuning/job_list_events_params.py b/src/openai/types/fine_tuning/job_list_events_params.py new file mode 100644 index 0000000000..7be3d53315 --- /dev/null +++ b/src/openai/types/fine_tuning/job_list_events_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["JobListEventsParams"] + + +class JobListEventsParams(TypedDict, total=False): + after: str + """Identifier for the last event from the previous pagination request.""" + + limit: int + """Number of events to retrieve.""" diff --git a/src/openai/types/fine_tuning/job_list_params.py b/src/openai/types/fine_tuning/job_list_params.py new file mode 100644 index 0000000000..8160136901 --- /dev/null +++ b/src/openai/types/fine_tuning/job_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["JobListParams"] + + +class JobListParams(TypedDict, total=False): + after: str + """Identifier for the last job from the previous pagination request.""" + + limit: int + """Number of fine-tuning jobs to retrieve.""" diff --git a/src/openai/types/image.py b/src/openai/types/image.py new file mode 100644 index 0000000000..4b8d1aaf18 --- /dev/null +++ b/src/openai/types/image.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["Image"] + + +class Image(BaseModel): + b64_json: Optional[str] = None + """ + The base64-encoded JSON of the generated image, if `response_format` is + `b64_json`. + """ + + url: Optional[str] = None + """The URL of the generated image, if `response_format` is `url` (default).""" diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py new file mode 100644 index 0000000000..d3b439070e --- /dev/null +++ b/src/openai/types/image_create_variation_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ImageCreateVariationParams"] + + +class ImageCreateVariationParams(TypedDict, total=False): + image: Required[FileTypes] + """The image to use as the basis for the variation(s). + + Must be a valid PNG file, less than 4MB, and square. + """ + + n: Optional[int] + """The number of images to generate. Must be between 1 and 10.""" + + response_format: Optional[Literal["url", "b64_json"]] + """The format in which the generated images are returned. + + Must be one of `url` or `b64_json`. + """ + + size: Optional[Literal["256x256", "512x512", "1024x1024"]] + """The size of the generated images. + + Must be one of `256x256`, `512x512`, or `1024x1024`. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + """ diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py new file mode 100644 index 0000000000..ce07a9cb30 --- /dev/null +++ b/src/openai/types/image_edit_params.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .._types import FileTypes + +__all__ = ["ImageEditParams"] + + +class ImageEditParams(TypedDict, total=False): + image: Required[FileTypes] + """The image to edit. + + Must be a valid PNG file, less than 4MB, and square. If mask is not provided, + image must have transparency, which will be used as the mask. + """ + + prompt: Required[str] + """A text description of the desired image(s). + + The maximum length is 1000 characters. + """ + + mask: FileTypes + """An additional image whose fully transparent areas (e.g. + + where alpha is zero) indicate where `image` should be edited. Must be a valid + PNG file, less than 4MB, and have the same dimensions as `image`. + """ + + n: Optional[int] + """The number of images to generate. Must be between 1 and 10.""" + + response_format: Optional[Literal["url", "b64_json"]] + """The format in which the generated images are returned. + + Must be one of `url` or `b64_json`. + """ + + size: Optional[Literal["256x256", "512x512", "1024x1024"]] + """The size of the generated images. + + Must be one of `256x256`, `512x512`, or `1024x1024`. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + """ diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py new file mode 100644 index 0000000000..4999ed958d --- /dev/null +++ b/src/openai/types/image_generate_params.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ImageGenerateParams"] + + +class ImageGenerateParams(TypedDict, total=False): + prompt: Required[str] + """A text description of the desired image(s). + + The maximum length is 1000 characters. + """ + + n: Optional[int] + """The number of images to generate. Must be between 1 and 10.""" + + response_format: Optional[Literal["url", "b64_json"]] + """The format in which the generated images are returned. + + Must be one of `url` or `b64_json`. + """ + + size: Optional[Literal["256x256", "512x512", "1024x1024"]] + """The size of the generated images. + + Must be one of `256x256`, `512x512`, or `1024x1024`. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + """ diff --git a/src/openai/types/images_response.py b/src/openai/types/images_response.py new file mode 100644 index 0000000000..9d1bc95a42 --- /dev/null +++ b/src/openai/types/images_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List + +from .image import Image +from .._models import BaseModel + +__all__ = ["ImagesResponse"] + + +class ImagesResponse(BaseModel): + created: int + + data: List[Image] diff --git a/src/openai/types/model.py b/src/openai/types/model.py new file mode 100644 index 0000000000..29e71b81a0 --- /dev/null +++ b/src/openai/types/model.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from .._models import BaseModel + +__all__ = ["Model"] + + +class Model(BaseModel): + id: str + """The model identifier, which can be referenced in the API endpoints.""" + + created: int + """The Unix timestamp (in seconds) when the model was created.""" + + object: str + """The object type, which is always "model".""" + + owned_by: str + """The organization that owns the model.""" diff --git a/src/openai/types/model_deleted.py b/src/openai/types/model_deleted.py new file mode 100644 index 0000000000..5329da1378 --- /dev/null +++ b/src/openai/types/model_deleted.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. + +from .._models import BaseModel + +__all__ = ["ModelDeleted"] + + +class ModelDeleted(BaseModel): + id: str + + deleted: bool + + object: str diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py new file mode 100644 index 0000000000..bf586fc24a --- /dev/null +++ b/src/openai/types/moderation.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Stainless. + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["Moderation", "Categories", "CategoryScores"] + + +class Categories(BaseModel): + harassment: bool + """ + Content that expresses, incites, or promotes harassing language towards any + target. + """ + + harassment_threatening: bool = FieldInfo(alias="harassment/threatening") + """ + Harassment content that also includes violence or serious harm towards any + target. + """ + + hate: bool + """ + Content that expresses, incites, or promotes hate based on race, gender, + ethnicity, religion, nationality, sexual orientation, disability status, or + caste. Hateful content aimed at non-protected groups (e.g., chess players) is + harrassment. + """ + + hate_threatening: bool = FieldInfo(alias="hate/threatening") + """ + Hateful content that also includes violence or serious harm towards the targeted + group based on race, gender, ethnicity, religion, nationality, sexual + orientation, disability status, or caste. + """ + + self_minus_harm: bool = FieldInfo(alias="self-harm") + """ + Content that promotes, encourages, or depicts acts of self-harm, such as + suicide, cutting, and eating disorders. + """ + + self_minus_harm_instructions: bool = FieldInfo(alias="self-harm/instructions") + """ + Content that encourages performing acts of self-harm, such as suicide, cutting, + and eating disorders, or that gives instructions or advice on how to commit such + acts. + """ + + self_minus_harm_intent: bool = FieldInfo(alias="self-harm/intent") + """ + Content where the speaker expresses that they are engaging or intend to engage + in acts of self-harm, such as suicide, cutting, and eating disorders. + """ + + sexual: bool + """ + Content meant to arouse sexual excitement, such as the description of sexual + activity, or that promotes sexual services (excluding sex education and + wellness). + """ + + sexual_minors: bool = FieldInfo(alias="sexual/minors") + """Sexual content that includes an individual who is under 18 years old.""" + + violence: bool + """Content that depicts death, violence, or physical injury.""" + + violence_graphic: bool = FieldInfo(alias="violence/graphic") + """Content that depicts death, violence, or physical injury in graphic detail.""" + + +class CategoryScores(BaseModel): + harassment: float + """The score for the category 'harassment'.""" + + harassment_threatening: float = FieldInfo(alias="harassment/threatening") + """The score for the category 'harassment/threatening'.""" + + hate: float + """The score for the category 'hate'.""" + + hate_threatening: float = FieldInfo(alias="hate/threatening") + """The score for the category 'hate/threatening'.""" + + self_minus_harm: float = FieldInfo(alias="self-harm") + """The score for the category 'self-harm'.""" + + self_minus_harm_instructions: float = FieldInfo(alias="self-harm/instructions") + """The score for the category 'self-harm/instructions'.""" + + self_minus_harm_intent: float = FieldInfo(alias="self-harm/intent") + """The score for the category 'self-harm/intent'.""" + + sexual: float + """The score for the category 'sexual'.""" + + sexual_minors: float = FieldInfo(alias="sexual/minors") + """The score for the category 'sexual/minors'.""" + + violence: float + """The score for the category 'violence'.""" + + violence_graphic: float = FieldInfo(alias="violence/graphic") + """The score for the category 'violence/graphic'.""" + + +class Moderation(BaseModel): + categories: Categories + """A list of the categories, and whether they are flagged or not.""" + + category_scores: CategoryScores + """A list of the categories along with their scores as predicted by model.""" + + flagged: bool + """ + Whether the content violates + [OpenAI's usage policies](/policies/usage-policies). + """ diff --git a/src/openai/types/moderation_create_params.py b/src/openai/types/moderation_create_params.py new file mode 100644 index 0000000000..25ed3ce940 --- /dev/null +++ b/src/openai/types/moderation_create_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ModerationCreateParams"] + + +class ModerationCreateParams(TypedDict, total=False): + input: Required[Union[str, List[str]]] + """The input text to classify""" + + model: Union[str, Literal["text-moderation-latest", "text-moderation-stable"]] + """ + Two content moderations models are available: `text-moderation-stable` and + `text-moderation-latest`. + + The default is `text-moderation-latest` which will be automatically upgraded + over time. This ensures you are always using our most accurate model. If you use + `text-moderation-stable`, we will provide advanced notice before updating the + model. Accuracy of `text-moderation-stable` may be slightly lower than for + `text-moderation-latest`. + """ diff --git a/src/openai/types/moderation_create_response.py b/src/openai/types/moderation_create_response.py new file mode 100644 index 0000000000..0962cdbfd9 --- /dev/null +++ b/src/openai/types/moderation_create_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List + +from .._models import BaseModel +from .moderation import Moderation + +__all__ = ["ModerationCreateResponse"] + + +class ModerationCreateResponse(BaseModel): + id: str + """The unique identifier for the moderation request.""" + + model: str + """The model used to generate the moderation results.""" + + results: List[Moderation] + """A list of moderation objects.""" diff --git a/src/openai/version.py b/src/openai/version.py new file mode 100644 index 0000000000..01a08ab5a9 --- /dev/null +++ b/src/openai/version.py @@ -0,0 +1,3 @@ +from ._version import __version__ + +VERSION: str = __version__ diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/__init__.py b/tests/api_resources/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/audio/__init__.py b/tests/api_resources/audio/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/audio/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py new file mode 100644 index 0000000000..aefdf1790f --- /dev/null +++ b/tests/api_resources/audio/test_transcriptions.py @@ -0,0 +1,87 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.types.audio import Transcription + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestTranscriptions: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + transcription = client.audio.transcriptions.create( + file=b"raw file contents", + model="whisper-1", + ) + assert_matches_type(Transcription, transcription, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + transcription = client.audio.transcriptions.create( + file=b"raw file contents", + model="whisper-1", + language="string", + prompt="string", + response_format="json", + temperature=0, + ) + assert_matches_type(Transcription, transcription, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.audio.transcriptions.with_raw_response.create( + file=b"raw file contents", + model="whisper-1", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + transcription = response.parse() + assert_matches_type(Transcription, transcription, path=["response"]) + + +class TestAsyncTranscriptions: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + transcription = await client.audio.transcriptions.create( + file=b"raw file contents", + model="whisper-1", + ) + assert_matches_type(Transcription, transcription, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + transcription = await client.audio.transcriptions.create( + file=b"raw file contents", + model="whisper-1", + language="string", + prompt="string", + response_format="json", + temperature=0, + ) + assert_matches_type(Transcription, transcription, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.audio.transcriptions.with_raw_response.create( + file=b"raw file contents", + model="whisper-1", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + transcription = response.parse() + assert_matches_type(Transcription, transcription, path=["response"]) diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py new file mode 100644 index 0000000000..0657e80eb8 --- /dev/null +++ b/tests/api_resources/audio/test_translations.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.types.audio import Translation + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestTranslations: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + translation = client.audio.translations.create( + file=b"raw file contents", + model="whisper-1", + ) + assert_matches_type(Translation, translation, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + translation = client.audio.translations.create( + file=b"raw file contents", + model="whisper-1", + prompt="string", + response_format="string", + temperature=0, + ) + assert_matches_type(Translation, translation, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.audio.translations.with_raw_response.create( + file=b"raw file contents", + model="whisper-1", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + translation = response.parse() + assert_matches_type(Translation, translation, path=["response"]) + + +class TestAsyncTranslations: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + translation = await client.audio.translations.create( + file=b"raw file contents", + model="whisper-1", + ) + assert_matches_type(Translation, translation, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + translation = await client.audio.translations.create( + file=b"raw file contents", + model="whisper-1", + prompt="string", + response_format="string", + temperature=0, + ) + assert_matches_type(Translation, translation, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.audio.translations.with_raw_response.create( + file=b"raw file contents", + model="whisper-1", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + translation = response.parse() + assert_matches_type(Translation, translation, path=["response"]) diff --git a/tests/api_resources/chat/__init__.py b/tests/api_resources/chat/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/chat/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py new file mode 100644 index 0000000000..dacf5d2596 --- /dev/null +++ b/tests/api_resources/chat/test_completions.py @@ -0,0 +1,281 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.types.chat import ChatCompletion + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestCompletions: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create_overload_1(self, client: OpenAI) -> None: + completion = client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + ) + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: + completion = client.chat.completions.create( + messages=[ + { + "content": "string", + "function_call": { + "arguments": "string", + "name": "string", + }, + "name": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + frequency_penalty=-2, + function_call="none", + functions=[ + { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + } + ], + logit_bias={"foo": 0}, + max_tokens=0, + n=1, + presence_penalty=-2, + stop="string", + stream=False, + temperature=1, + top_p=1, + user="user-1234", + ) + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + def test_raw_response_create_overload_1(self, client: OpenAI) -> None: + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + def test_method_create_overload_2(self, client: OpenAI) -> None: + client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + stream=True, + ) + + @parametrize + def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: + client.chat.completions.create( + messages=[ + { + "content": "string", + "function_call": { + "arguments": "string", + "name": "string", + }, + "name": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + stream=True, + frequency_penalty=-2, + function_call="none", + functions=[ + { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + } + ], + logit_bias={"foo": 0}, + max_tokens=0, + n=1, + presence_penalty=-2, + stop="string", + temperature=1, + top_p=1, + user="user-1234", + ) + + @parametrize + def test_raw_response_create_overload_2(self, client: OpenAI) -> None: + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + stream=True, + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + response.parse() + + +class TestAsyncCompletions: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create_overload_1(self, client: AsyncOpenAI) -> None: + completion = await client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + ) + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenAI) -> None: + completion = await client.chat.completions.create( + messages=[ + { + "content": "string", + "function_call": { + "arguments": "string", + "name": "string", + }, + "name": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + frequency_penalty=-2, + function_call="none", + functions=[ + { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + } + ], + logit_bias={"foo": 0}, + max_tokens=0, + n=1, + presence_penalty=-2, + stop="string", + stream=False, + temperature=1, + top_p=1, + user="user-1234", + ) + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_1(self, client: AsyncOpenAI) -> None: + response = await client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: + await client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + stream=True, + ) + + @parametrize + async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenAI) -> None: + await client.chat.completions.create( + messages=[ + { + "content": "string", + "function_call": { + "arguments": "string", + "name": "string", + }, + "name": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + stream=True, + frequency_penalty=-2, + function_call="none", + functions=[ + { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + } + ], + logit_bias={"foo": 0}, + max_tokens=0, + n=1, + presence_penalty=-2, + stop="string", + temperature=1, + top_p=1, + user="user-1234", + ) + + @parametrize + async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None: + response = await client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + stream=True, + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + response.parse() diff --git a/tests/api_resources/fine_tuning/__init__.py b/tests/api_resources/fine_tuning/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/fine_tuning/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py new file mode 100644 index 0000000000..9defcadab6 --- /dev/null +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -0,0 +1,240 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.fine_tuning import FineTuningJob, FineTuningJobEvent + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestJobs: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + hyperparameters={"n_epochs": "auto"}, + suffix="x", + validation_file="file-abc123", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.fine_tuning.jobs.with_raw_response.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.fine_tuning.jobs.with_raw_response.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.list() + assert_matches_type(SyncCursorPage[FineTuningJob], job, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.list( + after="string", + limit=0, + ) + assert_matches_type(SyncCursorPage[FineTuningJob], job, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.fine_tuning.jobs.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(SyncCursorPage[FineTuningJob], job, path=["response"]) + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + response = client.fine_tuning.jobs.with_raw_response.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_method_list_events(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"]) + + @parametrize + def test_method_list_events_with_all_params(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + after="string", + limit=0, + ) + assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"]) + + @parametrize + def test_raw_response_list_events(self, client: OpenAI) -> None: + response = client.fine_tuning.jobs.with_raw_response.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"]) + + +class TestAsyncJobs: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + job = await client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + job = await client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + hyperparameters={"n_epochs": "auto"}, + suffix="x", + validation_file="file-abc123", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.fine_tuning.jobs.with_raw_response.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + job = await client.fine_tuning.jobs.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.fine_tuning.jobs.with_raw_response.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + job = await client.fine_tuning.jobs.list() + assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: + job = await client.fine_tuning.jobs.list( + after="string", + limit=0, + ) + assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.fine_tuning.jobs.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) + + @parametrize + async def test_method_cancel(self, client: AsyncOpenAI) -> None: + job = await client.fine_tuning.jobs.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: + response = await client.fine_tuning.jobs.with_raw_response.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_method_list_events(self, client: AsyncOpenAI) -> None: + job = await client.fine_tuning.jobs.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) + + @parametrize + async def test_method_list_events_with_all_params(self, client: AsyncOpenAI) -> None: + job = await client.fine_tuning.jobs.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + after="string", + limit=0, + ) + assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) + + @parametrize + async def test_raw_response_list_events(self, client: AsyncOpenAI) -> None: + response = await client.fine_tuning.jobs.with_raw_response.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py new file mode 100644 index 0000000000..7b48e88ed2 --- /dev/null +++ b/tests/api_resources/test_completions.py @@ -0,0 +1,185 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import Completion +from openai._client import OpenAI, AsyncOpenAI + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestCompletions: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create_overload_1(self, client: OpenAI) -> None: + completion = client.completions.create( + model="string", + prompt="This is a test.", + ) + assert_matches_type(Completion, completion, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: + completion = client.completions.create( + model="string", + prompt="This is a test.", + best_of=0, + echo=True, + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=0, + max_tokens=16, + n=1, + presence_penalty=-2, + stop="\n", + stream=False, + suffix="test.", + temperature=1, + top_p=1, + user="user-1234", + ) + assert_matches_type(Completion, completion, path=["response"]) + + @parametrize + def test_raw_response_create_overload_1(self, client: OpenAI) -> None: + response = client.completions.with_raw_response.create( + model="string", + prompt="This is a test.", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(Completion, completion, path=["response"]) + + @parametrize + def test_method_create_overload_2(self, client: OpenAI) -> None: + client.completions.create( + model="string", + prompt="This is a test.", + stream=True, + ) + + @parametrize + def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: + client.completions.create( + model="string", + prompt="This is a test.", + stream=True, + best_of=0, + echo=True, + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=0, + max_tokens=16, + n=1, + presence_penalty=-2, + stop="\n", + suffix="test.", + temperature=1, + top_p=1, + user="user-1234", + ) + + @parametrize + def test_raw_response_create_overload_2(self, client: OpenAI) -> None: + response = client.completions.with_raw_response.create( + model="string", + prompt="This is a test.", + stream=True, + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + response.parse() + + +class TestAsyncCompletions: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create_overload_1(self, client: AsyncOpenAI) -> None: + completion = await client.completions.create( + model="string", + prompt="This is a test.", + ) + assert_matches_type(Completion, completion, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenAI) -> None: + completion = await client.completions.create( + model="string", + prompt="This is a test.", + best_of=0, + echo=True, + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=0, + max_tokens=16, + n=1, + presence_penalty=-2, + stop="\n", + stream=False, + suffix="test.", + temperature=1, + top_p=1, + user="user-1234", + ) + assert_matches_type(Completion, completion, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_1(self, client: AsyncOpenAI) -> None: + response = await client.completions.with_raw_response.create( + model="string", + prompt="This is a test.", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(Completion, completion, path=["response"]) + + @parametrize + async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: + await client.completions.create( + model="string", + prompt="This is a test.", + stream=True, + ) + + @parametrize + async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenAI) -> None: + await client.completions.create( + model="string", + prompt="This is a test.", + stream=True, + best_of=0, + echo=True, + frequency_penalty=-2, + logit_bias={"foo": 0}, + logprobs=0, + max_tokens=16, + n=1, + presence_penalty=-2, + stop="\n", + suffix="test.", + temperature=1, + top_p=1, + user="user-1234", + ) + + @parametrize + async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None: + response = await client.completions.with_raw_response.create( + model="string", + prompt="This is a test.", + stream=True, + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + response.parse() diff --git a/tests/api_resources/test_edits.py b/tests/api_resources/test_edits.py new file mode 100644 index 0000000000..76069d6b83 --- /dev/null +++ b/tests/api_resources/test_edits.py @@ -0,0 +1,95 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import Edit +from openai._client import OpenAI, AsyncOpenAI + +# pyright: reportDeprecated=false + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestEdits: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + edit = client.edits.create( + instruction="Fix the spelling mistakes.", + model="text-davinci-edit-001", + ) + assert_matches_type(Edit, edit, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + edit = client.edits.create( + instruction="Fix the spelling mistakes.", + model="text-davinci-edit-001", + input="What day of the wek is it?", + n=1, + temperature=1, + top_p=1, + ) + assert_matches_type(Edit, edit, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + response = client.edits.with_raw_response.create( + instruction="Fix the spelling mistakes.", + model="text-davinci-edit-001", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + edit = response.parse() + assert_matches_type(Edit, edit, path=["response"]) + + +class TestAsyncEdits: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + edit = await client.edits.create( + instruction="Fix the spelling mistakes.", + model="text-davinci-edit-001", + ) + assert_matches_type(Edit, edit, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + edit = await client.edits.create( + instruction="Fix the spelling mistakes.", + model="text-davinci-edit-001", + input="What day of the wek is it?", + n=1, + temperature=1, + top_p=1, + ) + assert_matches_type(Edit, edit, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + response = await client.edits.with_raw_response.create( + instruction="Fix the spelling mistakes.", + model="text-davinci-edit-001", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + edit = response.parse() + assert_matches_type(Edit, edit, path=["response"]) diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py new file mode 100644 index 0000000000..faf07ffb7c --- /dev/null +++ b/tests/api_resources/test_embeddings.py @@ -0,0 +1,83 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import CreateEmbeddingResponse +from openai._client import OpenAI, AsyncOpenAI + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestEmbeddings: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + embedding = client.embeddings.create( + input="The quick brown fox jumped over the lazy dog", + model="text-embedding-ada-002", + ) + assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + embedding = client.embeddings.create( + input="The quick brown fox jumped over the lazy dog", + model="text-embedding-ada-002", + encoding_format="float", + user="user-1234", + ) + assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.embeddings.with_raw_response.create( + input="The quick brown fox jumped over the lazy dog", + model="text-embedding-ada-002", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + embedding = response.parse() + assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + + +class TestAsyncEmbeddings: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + embedding = await client.embeddings.create( + input="The quick brown fox jumped over the lazy dog", + model="text-embedding-ada-002", + ) + assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + embedding = await client.embeddings.create( + input="The quick brown fox jumped over the lazy dog", + model="text-embedding-ada-002", + encoding_format="float", + user="user-1234", + ) + assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.embeddings.with_raw_response.create( + input="The quick brown fox jumped over the lazy dog", + model="text-embedding-ada-002", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + embedding = response.parse() + assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py new file mode 100644 index 0000000000..389763586e --- /dev/null +++ b/tests/api_resources/test_files.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import FileObject, FileDeleted +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncPage, AsyncPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestFiles: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + file = client.files.create( + file=b"raw file contents", + purpose="string", + ) + assert_matches_type(FileObject, file, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.files.with_raw_response.create( + file=b"raw file contents", + purpose="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileObject, file, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + file = client.files.retrieve( + "string", + ) + assert_matches_type(FileObject, file, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.files.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileObject, file, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + file = client.files.list() + assert_matches_type(SyncPage[FileObject], file, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.files.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(SyncPage[FileObject], file, path=["response"]) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + file = client.files.delete( + "string", + ) + assert_matches_type(FileDeleted, file, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.files.with_raw_response.delete( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileDeleted, file, path=["response"]) + + @parametrize + def test_method_retrieve_content(self, client: OpenAI) -> None: + file = client.files.retrieve_content( + "string", + ) + assert_matches_type(str, file, path=["response"]) + + @parametrize + def test_raw_response_retrieve_content(self, client: OpenAI) -> None: + response = client.files.with_raw_response.retrieve_content( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(str, file, path=["response"]) + + +class TestAsyncFiles: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + file = await client.files.create( + file=b"raw file contents", + purpose="string", + ) + assert_matches_type(FileObject, file, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.files.with_raw_response.create( + file=b"raw file contents", + purpose="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileObject, file, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + file = await client.files.retrieve( + "string", + ) + assert_matches_type(FileObject, file, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.files.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileObject, file, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + file = await client.files.list() + assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.files.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + + @parametrize + async def test_method_delete(self, client: AsyncOpenAI) -> None: + file = await client.files.delete( + "string", + ) + assert_matches_type(FileDeleted, file, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: + response = await client.files.with_raw_response.delete( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileDeleted, file, path=["response"]) + + @parametrize + async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: + file = await client.files.retrieve_content( + "string", + ) + assert_matches_type(str, file, path=["response"]) + + @parametrize + async def test_raw_response_retrieve_content(self, client: AsyncOpenAI) -> None: + response = await client.files.with_raw_response.retrieve_content( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(str, file, path=["response"]) diff --git a/tests/api_resources/test_fine_tunes.py b/tests/api_resources/test_fine_tunes.py new file mode 100644 index 0000000000..edaf784848 --- /dev/null +++ b/tests/api_resources/test_fine_tunes.py @@ -0,0 +1,274 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import FineTune, FineTuneEventsListResponse +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncPage, AsyncPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestFineTunes: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + fine_tune = client.fine_tunes.create( + training_file="file-abc123", + ) + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + fine_tune = client.fine_tunes.create( + training_file="file-abc123", + batch_size=0, + classification_betas=[0.6, 1, 1.5, 2], + classification_n_classes=0, + classification_positive_class="string", + compute_classification_metrics=True, + hyperparameters={"n_epochs": "auto"}, + learning_rate_multiplier=0, + model="curie", + prompt_loss_weight=0, + suffix="x", + validation_file="file-abc123", + ) + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.fine_tunes.with_raw_response.create( + training_file="file-abc123", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + fine_tune = client.fine_tunes.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.fine_tunes.with_raw_response.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + fine_tune = client.fine_tunes.list() + assert_matches_type(SyncPage[FineTune], fine_tune, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.fine_tunes.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(SyncPage[FineTune], fine_tune, path=["response"]) + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + fine_tune = client.fine_tunes.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + response = client.fine_tunes.with_raw_response.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + def test_method_list_events_overload_1(self, client: OpenAI) -> None: + fine_tune = client.fine_tunes.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + def test_method_list_events_with_all_params_overload_1(self, client: OpenAI) -> None: + fine_tune = client.fine_tunes.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + stream=False, + ) + assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + def test_raw_response_list_events_overload_1(self, client: OpenAI) -> None: + response = client.fine_tunes.with_raw_response.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + def test_method_list_events_overload_2(self, client: OpenAI) -> None: + client.fine_tunes.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + stream=True, + ) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + def test_raw_response_list_events_overload_2(self, client: OpenAI) -> None: + response = client.fine_tunes.with_raw_response.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + stream=True, + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + response.parse() + + +class TestAsyncFineTunes: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + fine_tune = await client.fine_tunes.create( + training_file="file-abc123", + ) + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + fine_tune = await client.fine_tunes.create( + training_file="file-abc123", + batch_size=0, + classification_betas=[0.6, 1, 1.5, 2], + classification_n_classes=0, + classification_positive_class="string", + compute_classification_metrics=True, + hyperparameters={"n_epochs": "auto"}, + learning_rate_multiplier=0, + model="curie", + prompt_loss_weight=0, + suffix="x", + validation_file="file-abc123", + ) + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.fine_tunes.with_raw_response.create( + training_file="file-abc123", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + fine_tune = await client.fine_tunes.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.fine_tunes.with_raw_response.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + fine_tune = await client.fine_tunes.list() + assert_matches_type(AsyncPage[FineTune], fine_tune, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.fine_tunes.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(AsyncPage[FineTune], fine_tune, path=["response"]) + + @parametrize + async def test_method_cancel(self, client: AsyncOpenAI) -> None: + fine_tune = await client.fine_tunes.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: + response = await client.fine_tunes.with_raw_response.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(FineTune, fine_tune, path=["response"]) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + async def test_method_list_events_overload_1(self, client: AsyncOpenAI) -> None: + fine_tune = await client.fine_tunes.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + async def test_method_list_events_with_all_params_overload_1(self, client: AsyncOpenAI) -> None: + fine_tune = await client.fine_tunes.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + stream=False, + ) + assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + async def test_raw_response_list_events_overload_1(self, client: AsyncOpenAI) -> None: + response = await client.fine_tunes.with_raw_response.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + fine_tune = response.parse() + assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + async def test_method_list_events_overload_2(self, client: AsyncOpenAI) -> None: + await client.fine_tunes.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + stream=True, + ) + + @pytest.mark.skip(reason="Prism chokes on this") + @parametrize + async def test_raw_response_list_events_overload_2(self, client: AsyncOpenAI) -> None: + response = await client.fine_tunes.with_raw_response.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + stream=True, + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + response.parse() diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py new file mode 100644 index 0000000000..fa7fb6d533 --- /dev/null +++ b/tests/api_resources/test_images.py @@ -0,0 +1,197 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import ImagesResponse +from openai._client import OpenAI, AsyncOpenAI + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestImages: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create_variation(self, client: OpenAI) -> None: + image = client.images.create_variation( + image=b"raw file contents", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: + image = client.images.create_variation( + image=b"raw file contents", + n=1, + response_format="url", + size="1024x1024", + user="user-1234", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + def test_raw_response_create_variation(self, client: OpenAI) -> None: + response = client.images.with_raw_response.create_variation( + image=b"raw file contents", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + def test_method_edit(self, client: OpenAI) -> None: + image = client.images.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + def test_method_edit_with_all_params(self, client: OpenAI) -> None: + image = client.images.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + mask=b"raw file contents", + n=1, + response_format="url", + size="1024x1024", + user="user-1234", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + def test_raw_response_edit(self, client: OpenAI) -> None: + response = client.images.with_raw_response.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + def test_method_generate(self, client: OpenAI) -> None: + image = client.images.generate( + prompt="A cute baby sea otter", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + def test_method_generate_with_all_params(self, client: OpenAI) -> None: + image = client.images.generate( + prompt="A cute baby sea otter", + n=1, + response_format="url", + size="1024x1024", + user="user-1234", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + def test_raw_response_generate(self, client: OpenAI) -> None: + response = client.images.with_raw_response.generate( + prompt="A cute baby sea otter", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + +class TestAsyncImages: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create_variation(self, client: AsyncOpenAI) -> None: + image = await client.images.create_variation( + image=b"raw file contents", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_method_create_variation_with_all_params(self, client: AsyncOpenAI) -> None: + image = await client.images.create_variation( + image=b"raw file contents", + n=1, + response_format="url", + size="1024x1024", + user="user-1234", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_raw_response_create_variation(self, client: AsyncOpenAI) -> None: + response = await client.images.with_raw_response.create_variation( + image=b"raw file contents", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_method_edit(self, client: AsyncOpenAI) -> None: + image = await client.images.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_method_edit_with_all_params(self, client: AsyncOpenAI) -> None: + image = await client.images.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + mask=b"raw file contents", + n=1, + response_format="url", + size="1024x1024", + user="user-1234", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_raw_response_edit(self, client: AsyncOpenAI) -> None: + response = await client.images.with_raw_response.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_method_generate(self, client: AsyncOpenAI) -> None: + image = await client.images.generate( + prompt="A cute baby sea otter", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_method_generate_with_all_params(self, client: AsyncOpenAI) -> None: + image = await client.images.generate( + prompt="A cute baby sea otter", + n=1, + response_format="url", + size="1024x1024", + user="user-1234", + ) + assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_raw_response_generate(self, client: AsyncOpenAI) -> None: + response = await client.images.with_raw_response.generate( + prompt="A cute baby sea otter", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py new file mode 100644 index 0000000000..3998809610 --- /dev/null +++ b/tests/api_resources/test_models.py @@ -0,0 +1,116 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import Model, ModelDeleted +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncPage, AsyncPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestModels: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + model = client.models.retrieve( + "gpt-3.5-turbo", + ) + assert_matches_type(Model, model, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.models.with_raw_response.retrieve( + "gpt-3.5-turbo", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model = response.parse() + assert_matches_type(Model, model, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + model = client.models.list() + assert_matches_type(SyncPage[Model], model, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.models.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model = response.parse() + assert_matches_type(SyncPage[Model], model, path=["response"]) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + model = client.models.delete( + "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + ) + assert_matches_type(ModelDeleted, model, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.models.with_raw_response.delete( + "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model = response.parse() + assert_matches_type(ModelDeleted, model, path=["response"]) + + +class TestAsyncModels: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + model = await client.models.retrieve( + "gpt-3.5-turbo", + ) + assert_matches_type(Model, model, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.models.with_raw_response.retrieve( + "gpt-3.5-turbo", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model = response.parse() + assert_matches_type(Model, model, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + model = await client.models.list() + assert_matches_type(AsyncPage[Model], model, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.models.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model = response.parse() + assert_matches_type(AsyncPage[Model], model, path=["response"]) + + @parametrize + async def test_method_delete(self, client: AsyncOpenAI) -> None: + model = await client.models.delete( + "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + ) + assert_matches_type(ModelDeleted, model, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: + response = await client.models.with_raw_response.delete( + "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model = response.parse() + assert_matches_type(ModelDeleted, model, path=["response"]) diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py new file mode 100644 index 0000000000..502030d614 --- /dev/null +++ b/tests/api_resources/test_moderations.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import ModerationCreateResponse +from openai._client import OpenAI, AsyncOpenAI + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestModerations: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + moderation = client.moderations.create( + input="I want to kill them.", + ) + assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + moderation = client.moderations.create( + input="I want to kill them.", + model="text-moderation-stable", + ) + assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.moderations.with_raw_response.create( + input="I want to kill them.", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + moderation = response.parse() + assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + + +class TestAsyncModerations: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + moderation = await client.moderations.create( + input="I want to kill them.", + ) + assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + moderation = await client.moderations.create( + input="I want to kill them.", + model="text-moderation-stable", + ) + assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.moderations.with_raw_response.create( + input="I want to kill them.", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + moderation = response.parse() + assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000000..c3a1efe9df --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,16 @@ +import asyncio +import logging +from typing import Iterator + +import pytest + +pytest.register_assert_rewrite("tests.utils") + +logging.getLogger("openai").setLevel(logging.DEBUG) + + +@pytest.fixture(scope="session") +def event_loop() -> Iterator[asyncio.AbstractEventLoop]: + loop = asyncio.new_event_loop() + yield loop + loop.close() diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py new file mode 100644 index 0000000000..b0bd87571b --- /dev/null +++ b/tests/lib/test_azure.py @@ -0,0 +1,36 @@ +from typing import Union + +import pytest + +from openai._models import FinalRequestOptions +from openai.lib.azure import AzureOpenAI, AsyncAzureOpenAI + +Client = Union[AzureOpenAI, AsyncAzureOpenAI] + + +sync_client = AzureOpenAI( + api_version="2023-07-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", +) + +async_client = AsyncAzureOpenAI( + api_version="2023-07-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", +) + + +@pytest.mark.parametrize("client", [sync_client, async_client]) +def test_implicit_deployment_path(client: Client) -> None: + req = client._build_request( + FinalRequestOptions.construct( + method="post", + url="/chat/completions", + json_data={"model": "my-deployment-model"}, + ) + ) + assert ( + req.url + == "https://example-resource.azure.openai.com/openai/deployments/my-deployment-model/chat/completions?api-version=2023-07-01" + ) diff --git a/tests/test_client.py b/tests/test_client.py new file mode 100644 index 0000000000..3b70594ecd --- /dev/null +++ b/tests/test_client.py @@ -0,0 +1,1110 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os +import json +import asyncio +import inspect +from typing import Any, Dict, Union, cast +from unittest import mock + +import httpx +import pytest +from respx import MockRouter +from pydantic import ValidationError + +from openai import OpenAI, AsyncOpenAI, APIResponseValidationError +from openai._client import OpenAI, AsyncOpenAI +from openai._models import BaseModel, FinalRequestOptions +from openai._streaming import Stream, AsyncStream +from openai._exceptions import APIResponseValidationError +from openai._base_client import ( + DEFAULT_TIMEOUT, + HTTPX_DEFAULT_TIMEOUT, + BaseClient, + make_request_options, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + return dict(url.params) + + +class TestOpenAI: + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + + @pytest.mark.respx(base_url=base_url) + def test_raw_response(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json='{"foo": "bar"}')) + + response = self.client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == '{"foo": "bar"}' + + @pytest.mark.respx(base_url=base_url) + def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock( + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') + ) + + response = self.client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == '{"foo": "bar"}' + + def test_copy(self) -> None: + copied = self.client.copy() + assert id(copied) != id(self.client) + + copied = self.client.copy(api_key="another My API Key") + assert copied.api_key == "another My API Key" + assert self.client.api_key == "My API Key" + + def test_copy_default_options(self) -> None: + # options that have a default are overridden correctly + copied = self.client.copy(max_retries=7) + assert copied.max_retries == 7 + assert self.client.max_retries == 2 + + copied2 = copied.copy(max_retries=6) + assert copied2.max_retries == 6 + assert copied.max_retries == 7 + + # timeout + assert isinstance(self.client.timeout, httpx.Timeout) + copied = self.client.copy(timeout=None) + assert copied.timeout is None + assert isinstance(self.client.timeout, httpx.Timeout) + + def test_copy_default_headers(self) -> None: + client = OpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + assert client.default_headers["X-Foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert copied.default_headers["X-Foo"] == "bar" + + # merges already given headers + copied = client.copy(default_headers={"X-Bar": "stainless"}) + assert copied.default_headers["X-Foo"] == "bar" + assert copied.default_headers["X-Bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_headers={"X-Foo": "stainless"}) + assert copied.default_headers["X-Foo"] == "stainless" + + # set_default_headers + + # completely overrides already set values + copied = client.copy(set_default_headers={}) + assert copied.default_headers.get("X-Foo") is None + + copied = client.copy(set_default_headers={"X-Bar": "Robert"}) + assert copied.default_headers["X-Bar"] == "Robert" + + with pytest.raises( + ValueError, + match="`default_headers` and `set_default_headers` arguments are mutually exclusive", + ): + client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + + def test_copy_default_query(self) -> None: + client = OpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} + ) + assert _get_params(client)["foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert _get_params(copied)["foo"] == "bar" + + # merges already given params + copied = client.copy(default_query={"bar": "stainless"}) + params = _get_params(copied) + assert params["foo"] == "bar" + assert params["bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_query={"foo": "stainless"}) + assert _get_params(copied)["foo"] == "stainless" + + # set_default_query + + # completely overrides already set values + copied = client.copy(set_default_query={}) + assert _get_params(copied) == {} + + copied = client.copy(set_default_query={"bar": "Robert"}) + assert _get_params(copied)["bar"] == "Robert" + + with pytest.raises( + ValueError, + # TODO: update + match="`default_query` and `set_default_query` arguments are mutually exclusive", + ): + client.copy(set_default_query={}, default_query={"foo": "Bar"}) + + def test_copy_signature(self) -> None: + # ensure the same parameters that can be passed to the client are defined in the `.copy()` method + init_signature = inspect.signature( + # mypy doesn't like that we access the `__init__` property. + self.client.__init__, # type: ignore[misc] + ) + copy_signature = inspect.signature(self.client.copy) + exclude_params = {"transport", "proxies", "_strict_response_validation"} + + for name in init_signature.parameters.keys(): + if name in exclude_params: + continue + + copy_param = copy_signature.parameters.get(name) + assert copy_param is not None, f"copy() signature is missing the {name} param" + + def test_request_timeout(self) -> None: + request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + request = self.client._build_request( + FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) + ) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(100.0) + + def test_client_timeout_option(self) -> None: + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0)) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(0) + + def test_http_client_timeout_option(self) -> None: + # custom timeout given to the httpx client should be used + with httpx.Client(timeout=None) as http_client: + client = OpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(None) + + # no timeout given to the httpx client should not use the httpx default + with httpx.Client() as http_client: + client = OpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + # explicitly passing the default timeout currently results in it being ignored + with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: + client = OpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT # our default + + def test_default_headers_option(self) -> None: + client = OpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "bar" + assert request.headers.get("x-stainless-lang") == "python" + + client2 = OpenAI( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + def test_validate_headers(self) -> None: + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("Authorization") == f"Bearer {api_key}" + + with pytest.raises(Exception): + client2 = OpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) + _ = client2 + + def test_default_query_option(self) -> None: + client = OpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + assert dict(url.params) == {"query_param": "bar"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo", + params={"foo": "baz", "query_param": "overriden"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"foo": "baz", "query_param": "overriden"} + + def test_request_extra_json(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": False} + + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"baz": False} + + # `extra_json` takes priority over `json_data` when keys clash + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar", "baz": True}, + extra_json={"baz": None}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": None} + + def test_request_extra_headers(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options(extra_headers={"X-Foo": "Foo"}), + ), + ) + assert request.headers.get("X-Foo") == "Foo" + + # `extra_headers` takes priority over `default_headers` when keys clash + request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_headers={"X-Bar": "false"}, + ), + ), + ) + assert request.headers.get("X-Bar") == "false" + + def test_request_extra_query(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_query={"my_query_param": "Foo"}, + ), + ), + ) + params = cast(Dict[str, str], dict(request.url.params)) + assert params == {"my_query_param": "Foo"} + + # if both `query` and `extra_query` are given, they are merged + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"bar": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = cast(Dict[str, str], dict(request.url.params)) + assert params == {"bar": "1", "foo": "2"} + + # `extra_query` takes priority over `query` when keys clash + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"foo": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = cast(Dict[str, str], dict(request.url.params)) + assert params == {"foo": "2"} + + @pytest.mark.respx(base_url=base_url) + def test_basic_union_response(self, respx_mock: MockRouter) -> None: + class Model1(BaseModel): + name: str + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + @pytest.mark.respx(base_url=base_url) + def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + """Union of objects with the same field name using a different type""" + + class Model1(BaseModel): + foo: int + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) + + response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model1) + assert response.foo == 1 + + @pytest.mark.parametrize( + "client", + [ + OpenAI(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), + OpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_trailing_slash(self, client: OpenAI) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + + @pytest.mark.parametrize( + "client", + [ + OpenAI(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), + OpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_no_trailing_slash(self, client: OpenAI) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + + @pytest.mark.parametrize( + "client", + [ + OpenAI(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), + OpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_absolute_request_url(http://github.com/self,%20client:%20OpenAI) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="https://myapi.com/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "https://myapi.com/foo" + + def test_client_del(self) -> None: + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not client.is_closed() + + client.__del__() + + assert client.is_closed() + + def test_copied_client_does_not_close_http(self) -> None: + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not client.is_closed() + + copied = client.copy() + assert copied is not client + + copied.__del__() + + assert not copied.is_closed() + assert not client.is_closed() + + def test_client_context_manager(self) -> None: + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + with client as c2: + assert c2 is client + assert not c2.is_closed() + assert not client.is_closed() + assert client.is_closed() + + @pytest.mark.respx(base_url=base_url) + def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) + + with pytest.raises(APIResponseValidationError) as exc: + self.client.get("/foo", cast_to=Model) + + assert isinstance(exc.value.__cause__, ValidationError) + + @pytest.mark.respx(base_url=base_url) + def test_default_stream_cls(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = self.client.post("/foo", cast_to=Model, stream=True) + assert isinstance(response, Stream) + + @pytest.mark.respx(base_url=base_url) + def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) + + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + + with pytest.raises(APIResponseValidationError): + strict_client.get("/foo", cast_to=Model) + + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + + response = client.get("/foo", cast_to=Model) + assert isinstance(response, str) # type: ignore[unreachable] + + @pytest.mark.parametrize( + "remaining_retries,retry_after,timeout", + [ + [3, "20", 20], + [3, "0", 0.5], + [3, "-10", 0.5], + [3, "60", 60], + [3, "61", 0.5], + [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], + ], + ) + @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) + def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + + headers = httpx.Headers({"retry-after": retry_after}) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) + calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + +class TestAsyncOpenAI: + client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_raw_response(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json='{"foo": "bar"}')) + + response = await self.client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == '{"foo": "bar"}' + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock( + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') + ) + + response = await self.client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == '{"foo": "bar"}' + + def test_copy(self) -> None: + copied = self.client.copy() + assert id(copied) != id(self.client) + + copied = self.client.copy(api_key="another My API Key") + assert copied.api_key == "another My API Key" + assert self.client.api_key == "My API Key" + + def test_copy_default_options(self) -> None: + # options that have a default are overridden correctly + copied = self.client.copy(max_retries=7) + assert copied.max_retries == 7 + assert self.client.max_retries == 2 + + copied2 = copied.copy(max_retries=6) + assert copied2.max_retries == 6 + assert copied.max_retries == 7 + + # timeout + assert isinstance(self.client.timeout, httpx.Timeout) + copied = self.client.copy(timeout=None) + assert copied.timeout is None + assert isinstance(self.client.timeout, httpx.Timeout) + + def test_copy_default_headers(self) -> None: + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + assert client.default_headers["X-Foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert copied.default_headers["X-Foo"] == "bar" + + # merges already given headers + copied = client.copy(default_headers={"X-Bar": "stainless"}) + assert copied.default_headers["X-Foo"] == "bar" + assert copied.default_headers["X-Bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_headers={"X-Foo": "stainless"}) + assert copied.default_headers["X-Foo"] == "stainless" + + # set_default_headers + + # completely overrides already set values + copied = client.copy(set_default_headers={}) + assert copied.default_headers.get("X-Foo") is None + + copied = client.copy(set_default_headers={"X-Bar": "Robert"}) + assert copied.default_headers["X-Bar"] == "Robert" + + with pytest.raises( + ValueError, + match="`default_headers` and `set_default_headers` arguments are mutually exclusive", + ): + client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + + def test_copy_default_query(self) -> None: + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} + ) + assert _get_params(client)["foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert _get_params(copied)["foo"] == "bar" + + # merges already given params + copied = client.copy(default_query={"bar": "stainless"}) + params = _get_params(copied) + assert params["foo"] == "bar" + assert params["bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_query={"foo": "stainless"}) + assert _get_params(copied)["foo"] == "stainless" + + # set_default_query + + # completely overrides already set values + copied = client.copy(set_default_query={}) + assert _get_params(copied) == {} + + copied = client.copy(set_default_query={"bar": "Robert"}) + assert _get_params(copied)["bar"] == "Robert" + + with pytest.raises( + ValueError, + # TODO: update + match="`default_query` and `set_default_query` arguments are mutually exclusive", + ): + client.copy(set_default_query={}, default_query={"foo": "Bar"}) + + def test_copy_signature(self) -> None: + # ensure the same parameters that can be passed to the client are defined in the `.copy()` method + init_signature = inspect.signature( + # mypy doesn't like that we access the `__init__` property. + self.client.__init__, # type: ignore[misc] + ) + copy_signature = inspect.signature(self.client.copy) + exclude_params = {"transport", "proxies", "_strict_response_validation"} + + for name in init_signature.parameters.keys(): + if name in exclude_params: + continue + + copy_param = copy_signature.parameters.get(name) + assert copy_param is not None, f"copy() signature is missing the {name} param" + + async def test_request_timeout(self) -> None: + request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + request = self.client._build_request( + FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) + ) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(100.0) + + async def test_client_timeout_option(self) -> None: + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0) + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(0) + + async def test_http_client_timeout_option(self) -> None: + # custom timeout given to the httpx client should be used + async with httpx.AsyncClient(timeout=None) as http_client: + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(None) + + # no timeout given to the httpx client should not use the httpx default + async with httpx.AsyncClient() as http_client: + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + # explicitly passing the default timeout currently results in it being ignored + async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT # our default + + def test_default_headers_option(self) -> None: + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "bar" + assert request.headers.get("x-stainless-lang") == "python" + + client2 = AsyncOpenAI( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + def test_validate_headers(self) -> None: + client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("Authorization") == f"Bearer {api_key}" + + with pytest.raises(Exception): + client2 = AsyncOpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) + _ = client2 + + def test_default_query_option(self) -> None: + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + assert dict(url.params) == {"query_param": "bar"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo", + params={"foo": "baz", "query_param": "overriden"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"foo": "baz", "query_param": "overriden"} + + def test_request_extra_json(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": False} + + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"baz": False} + + # `extra_json` takes priority over `json_data` when keys clash + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar", "baz": True}, + extra_json={"baz": None}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": None} + + def test_request_extra_headers(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options(extra_headers={"X-Foo": "Foo"}), + ), + ) + assert request.headers.get("X-Foo") == "Foo" + + # `extra_headers` takes priority over `default_headers` when keys clash + request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_headers={"X-Bar": "false"}, + ), + ), + ) + assert request.headers.get("X-Bar") == "false" + + def test_request_extra_query(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_query={"my_query_param": "Foo"}, + ), + ), + ) + params = cast(Dict[str, str], dict(request.url.params)) + assert params == {"my_query_param": "Foo"} + + # if both `query` and `extra_query` are given, they are merged + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"bar": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = cast(Dict[str, str], dict(request.url.params)) + assert params == {"bar": "1", "foo": "2"} + + # `extra_query` takes priority over `query` when keys clash + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"foo": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = cast(Dict[str, str], dict(request.url.params)) + assert params == {"foo": "2"} + + @pytest.mark.respx(base_url=base_url) + async def test_basic_union_response(self, respx_mock: MockRouter) -> None: + class Model1(BaseModel): + name: str + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + @pytest.mark.respx(base_url=base_url) + async def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + """Union of objects with the same field name using a different type""" + + class Model1(BaseModel): + foo: int + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) + + response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model1) + assert response.foo == 1 + + @pytest.mark.parametrize( + "client", + [ + AsyncOpenAI( + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + ), + AsyncOpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_trailing_slash(self, client: AsyncOpenAI) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + + @pytest.mark.parametrize( + "client", + [ + AsyncOpenAI( + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + ), + AsyncOpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_no_trailing_slash(self, client: AsyncOpenAI) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + + @pytest.mark.parametrize( + "client", + [ + AsyncOpenAI( + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + ), + AsyncOpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_absolute_request_url(http://github.com/self,%20client:%20AsyncOpenAI) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="https://myapi.com/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "https://myapi.com/foo" + + async def test_client_del(self) -> None: + client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not client.is_closed() + + client.__del__() + + await asyncio.sleep(0.2) + assert client.is_closed() + + async def test_copied_client_does_not_close_http(self) -> None: + client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not client.is_closed() + + copied = client.copy() + assert copied is not client + + copied.__del__() + + await asyncio.sleep(0.2) + assert not copied.is_closed() + assert not client.is_closed() + + async def test_client_context_manager(self) -> None: + client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + async with client as c2: + assert c2 is client + assert not c2.is_closed() + assert not client.is_closed() + assert client.is_closed() + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) + + with pytest.raises(APIResponseValidationError) as exc: + await self.client.get("/foo", cast_to=Model) + + assert isinstance(exc.value.__cause__, ValidationError) + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_default_stream_cls(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await self.client.post("/foo", cast_to=Model, stream=True) + assert isinstance(response, AsyncStream) + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) + + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + + with pytest.raises(APIResponseValidationError): + await strict_client.get("/foo", cast_to=Model) + + client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + + response = await client.get("/foo", cast_to=Model) + assert isinstance(response, str) # type: ignore[unreachable] + + @pytest.mark.parametrize( + "remaining_retries,retry_after,timeout", + [ + [3, "20", 20], + [3, "0", 0.5], + [3, "-10", 0.5], + [3, "60", 60], + [3, "61", 0.5], + [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], + ], + ) + @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) + @pytest.mark.asyncio + async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: + client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + + headers = httpx.Headers({"retry-after": retry_after}) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) + calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py new file mode 100644 index 0000000000..8cf65ce94e --- /dev/null +++ b/tests/test_deepcopy.py @@ -0,0 +1,59 @@ +from openai._utils import deepcopy_minimal + + +def assert_different_identities(obj1: object, obj2: object) -> None: + assert obj1 == obj2 + assert id(obj1) != id(obj2) + + +def test_simple_dict() -> None: + obj1 = {"foo": "bar"} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + + +def test_nested_dict() -> None: + obj1 = {"foo": {"bar": True}} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1["foo"], obj2["foo"]) + + +def test_complex_nested_dict() -> None: + obj1 = {"foo": {"bar": [{"hello": "world"}]}} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1["foo"], obj2["foo"]) + assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"]) + assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0]) + + +def test_simple_list() -> None: + obj1 = ["a", "b", "c"] + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + + +def test_nested_list() -> None: + obj1 = ["a", [1, 2, 3]] + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1[1], obj2[1]) + + +class MyObject: + ... + + +def test_ignores_other_types() -> None: + # custom classes + my_obj = MyObject() + obj1 = {"foo": my_obj} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert obj1["foo"] is my_obj + + # tuples + obj3 = ("a", "b") + obj4 = deepcopy_minimal(obj3) + assert obj3 is obj4 diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py new file mode 100644 index 0000000000..554487da42 --- /dev/null +++ b/tests/test_extract_files.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import Sequence + +import pytest + +from openai._types import FileTypes +from openai._utils import extract_files + + +def test_removes_files_from_input() -> None: + query = {"foo": "bar"} + assert extract_files(query, paths=[]) == [] + assert query == {"foo": "bar"} + + query2 = {"foo": b"Bar", "hello": "world"} + assert extract_files(query2, paths=[["foo"]]) == [("foo", b"Bar")] + assert query2 == {"hello": "world"} + + query3 = {"foo": {"foo": {"bar": b"Bar"}}, "hello": "world"} + assert extract_files(query3, paths=[["foo", "foo", "bar"]]) == [("foo[foo][bar]", b"Bar")] + assert query3 == {"foo": {"foo": {}}, "hello": "world"} + + query4 = {"foo": {"bar": b"Bar", "baz": "foo"}, "hello": "world"} + assert extract_files(query4, paths=[["foo", "bar"]]) == [("foo[bar]", b"Bar")] + assert query4 == {"hello": "world", "foo": {"baz": "foo"}} + + +def test_multiple_files() -> None: + query = {"documents": [{"file": b"My first file"}, {"file": b"My second file"}]} + assert extract_files(query, paths=[["documents", "", "file"]]) == [ + ("documents[][file]", b"My first file"), + ("documents[][file]", b"My second file"), + ] + assert query == {"documents": [{}, {}]} + + +@pytest.mark.parametrize( + "query,paths,expected", + [ + [ + {"foo": {"bar": "baz"}}, + [["foo", "", "bar"]], + [], + ], + [ + {"foo": ["bar", "baz"]}, + [["foo", "bar"]], + [], + ], + [ + {"foo": {"bar": "baz"}}, + [["foo", "foo"]], + [], + ], + ], + ids=["dict expecting array", "arraye expecting dict", "unknown keys"], +) +def test_ignores_incorrect_paths( + query: dict[str, object], + paths: Sequence[Sequence[str]], + expected: list[tuple[str, FileTypes]], +) -> None: + assert extract_files(query, paths=paths) == expected diff --git a/tests/test_files.py b/tests/test_files.py new file mode 100644 index 0000000000..15d5c6a811 --- /dev/null +++ b/tests/test_files.py @@ -0,0 +1,51 @@ +from pathlib import Path + +import anyio +import pytest +from dirty_equals import IsDict, IsList, IsBytes, IsTuple + +from openai._files import to_httpx_files, async_to_httpx_files + +readme_path = Path(__file__).parent.parent.joinpath("README.md") + + +def test_pathlib_includes_file_name() -> None: + result = to_httpx_files({"file": readme_path}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +def test_tuple_input() -> None: + result = to_httpx_files([("file", readme_path)]) + print(result) + assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) + + +@pytest.mark.asyncio +async def test_async_pathlib_includes_file_name() -> None: + result = await async_to_httpx_files({"file": readme_path}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +@pytest.mark.asyncio +async def test_async_supports_anyio_path() -> None: + result = await async_to_httpx_files({"file": anyio.Path(readme_path)}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +@pytest.mark.asyncio +async def test_async_tuple_input() -> None: + result = await async_to_httpx_files([("file", readme_path)]) + print(result) + assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) + + +def test_string_not_allowed() -> None: + with pytest.raises(TypeError, match="Expected file types input to be a FileContent type or to be a tuple"): + to_httpx_files( + { + "file": "foo", # type: ignore + } + ) diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000000..713bd2cb1b --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,573 @@ +import json +from typing import Any, Dict, List, Union, Optional, cast +from datetime import datetime, timezone +from typing_extensions import Literal + +import pytest +import pydantic +from pydantic import Field + +from openai._compat import PYDANTIC_V2, parse_obj, model_dump, model_json +from openai._models import BaseModel + + +class BasicModel(BaseModel): + foo: str + + +@pytest.mark.parametrize("value", ["hello", 1], ids=["correct type", "mismatched"]) +def test_basic(value: object) -> None: + m = BasicModel.construct(foo=value) + assert m.foo == value + + +def test_directly_nested_model() -> None: + class NestedModel(BaseModel): + nested: BasicModel + + m = NestedModel.construct(nested={"foo": "Foo!"}) + assert m.nested.foo == "Foo!" + + # mismatched types + m = NestedModel.construct(nested="hello!") + assert m.nested == "hello!" + + +def test_optional_nested_model() -> None: + class NestedModel(BaseModel): + nested: Optional[BasicModel] + + m1 = NestedModel.construct(nested=None) + assert m1.nested is None + + m2 = NestedModel.construct(nested={"foo": "bar"}) + assert m2.nested is not None + assert m2.nested.foo == "bar" + + # mismatched types + m3 = NestedModel.construct(nested={"foo"}) + assert isinstance(cast(Any, m3.nested), set) + assert m3.nested == {"foo"} + + +def test_list_nested_model() -> None: + class NestedModel(BaseModel): + nested: List[BasicModel] + + m = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) + assert m.nested is not None + assert isinstance(m.nested, list) + assert len(m.nested) == 2 + assert m.nested[0].foo == "bar" + assert m.nested[1].foo == "2" + + # mismatched types + m = NestedModel.construct(nested=True) + assert cast(Any, m.nested) is True + + m = NestedModel.construct(nested=[False]) + assert cast(Any, m.nested) == [False] + + +def test_optional_list_nested_model() -> None: + class NestedModel(BaseModel): + nested: Optional[List[BasicModel]] + + m1 = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) + assert m1.nested is not None + assert isinstance(m1.nested, list) + assert len(m1.nested) == 2 + assert m1.nested[0].foo == "bar" + assert m1.nested[1].foo == "2" + + m2 = NestedModel.construct(nested=None) + assert m2.nested is None + + # mismatched types + m3 = NestedModel.construct(nested={1}) + assert cast(Any, m3.nested) == {1} + + m4 = NestedModel.construct(nested=[False]) + assert cast(Any, m4.nested) == [False] + + +def test_list_optional_items_nested_model() -> None: + class NestedModel(BaseModel): + nested: List[Optional[BasicModel]] + + m = NestedModel.construct(nested=[None, {"foo": "bar"}]) + assert m.nested is not None + assert isinstance(m.nested, list) + assert len(m.nested) == 2 + assert m.nested[0] is None + assert m.nested[1] is not None + assert m.nested[1].foo == "bar" + + # mismatched types + m3 = NestedModel.construct(nested="foo") + assert cast(Any, m3.nested) == "foo" + + m4 = NestedModel.construct(nested=[False]) + assert cast(Any, m4.nested) == [False] + + +def test_list_mismatched_type() -> None: + class NestedModel(BaseModel): + nested: List[str] + + m = NestedModel.construct(nested=False) + assert cast(Any, m.nested) is False + + +def test_raw_dictionary() -> None: + class NestedModel(BaseModel): + nested: Dict[str, str] + + m = NestedModel.construct(nested={"hello": "world"}) + assert m.nested == {"hello": "world"} + + # mismatched types + m = NestedModel.construct(nested=False) + assert cast(Any, m.nested) is False + + +def test_nested_dictionary_model() -> None: + class NestedModel(BaseModel): + nested: Dict[str, BasicModel] + + m = NestedModel.construct(nested={"hello": {"foo": "bar"}}) + assert isinstance(m.nested, dict) + assert m.nested["hello"].foo == "bar" + + # mismatched types + m = NestedModel.construct(nested={"hello": False}) + assert cast(Any, m.nested["hello"]) is False + + +def test_unknown_fields() -> None: + m1 = BasicModel.construct(foo="foo", unknown=1) + assert m1.foo == "foo" + assert cast(Any, m1).unknown == 1 + + m2 = BasicModel.construct(foo="foo", unknown={"foo_bar": True}) + assert m2.foo == "foo" + assert cast(Any, m2).unknown == {"foo_bar": True} + + assert model_dump(m2) == {"foo": "foo", "unknown": {"foo_bar": True}} + + +def test_strict_validation_unknown_fields() -> None: + class Model(BaseModel): + foo: str + + model = parse_obj(Model, dict(foo="hello!", user="Robert")) + assert model.foo == "hello!" + assert cast(Any, model).user == "Robert" + + assert model_dump(model) == {"foo": "hello!", "user": "Robert"} + + +def test_aliases() -> None: + class Model(BaseModel): + my_field: int = Field(alias="myField") + + m = Model.construct(myField=1) + assert m.my_field == 1 + + # mismatched types + m = Model.construct(myField={"hello": False}) + assert cast(Any, m.my_field) == {"hello": False} + + +def test_repr() -> None: + model = BasicModel(foo="bar") + assert str(model) == "BasicModel(foo='bar')" + assert repr(model) == "BasicModel(foo='bar')" + + +def test_repr_nested_model() -> None: + class Child(BaseModel): + name: str + age: int + + class Parent(BaseModel): + name: str + child: Child + + model = Parent(name="Robert", child=Child(name="Foo", age=5)) + assert str(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" + assert repr(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" + + +def test_optional_list() -> None: + class Submodel(BaseModel): + name: str + + class Model(BaseModel): + items: Optional[List[Submodel]] + + m = Model.construct(items=None) + assert m.items is None + + m = Model.construct(items=[]) + assert m.items == [] + + m = Model.construct(items=[{"name": "Robert"}]) + assert m.items is not None + assert len(m.items) == 1 + assert m.items[0].name == "Robert" + + +def test_nested_union_of_models() -> None: + class Submodel1(BaseModel): + bar: bool + + class Submodel2(BaseModel): + thing: str + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2] + + m = Model.construct(foo={"thing": "hello"}) + assert isinstance(m.foo, Submodel2) + assert m.foo.thing == "hello" + + +def test_nested_union_of_mixed_types() -> None: + class Submodel1(BaseModel): + bar: bool + + class Model(BaseModel): + foo: Union[Submodel1, Literal[True], Literal["CARD_HOLDER"]] + + m = Model.construct(foo=True) + assert m.foo is True + + m = Model.construct(foo="CARD_HOLDER") + assert m.foo is "CARD_HOLDER" + + m = Model.construct(foo={"bar": False}) + assert isinstance(m.foo, Submodel1) + assert m.foo.bar is False + + +def test_nested_union_multiple_variants() -> None: + class Submodel1(BaseModel): + bar: bool + + class Submodel2(BaseModel): + thing: str + + class Submodel3(BaseModel): + foo: int + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2, None, Submodel3] + + m = Model.construct(foo={"thing": "hello"}) + assert isinstance(m.foo, Submodel2) + assert m.foo.thing == "hello" + + m = Model.construct(foo=None) + assert m.foo is None + + m = Model.construct() + assert m.foo is None + + m = Model.construct(foo={"foo": "1"}) + assert isinstance(m.foo, Submodel3) + assert m.foo.foo == 1 + + +def test_nested_union_invalid_data() -> None: + class Submodel1(BaseModel): + level: int + + class Submodel2(BaseModel): + name: str + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2] + + m = Model.construct(foo=True) + assert cast(bool, m.foo) is True + + m = Model.construct(foo={"name": 3}) + if PYDANTIC_V2: + assert isinstance(m.foo, Submodel1) + assert m.foo.name == 3 # type: ignore + else: + assert isinstance(m.foo, Submodel2) + assert m.foo.name == "3" + + +def test_list_of_unions() -> None: + class Submodel1(BaseModel): + level: int + + class Submodel2(BaseModel): + name: str + + class Model(BaseModel): + items: List[Union[Submodel1, Submodel2]] + + m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) + assert len(m.items) == 2 + assert isinstance(m.items[0], Submodel1) + assert m.items[0].level == 1 + assert isinstance(m.items[1], Submodel2) + assert m.items[1].name == "Robert" + + m = Model.construct(items=[{"level": -1}, 156]) + assert len(m.items) == 2 + assert isinstance(m.items[0], Submodel1) + assert m.items[0].level == -1 + assert m.items[1] == 156 + + +def test_union_of_lists() -> None: + class SubModel1(BaseModel): + level: int + + class SubModel2(BaseModel): + name: str + + class Model(BaseModel): + items: Union[List[SubModel1], List[SubModel2]] + + # with one valid entry + m = Model.construct(items=[{"name": "Robert"}]) + assert len(m.items) == 1 + assert isinstance(m.items[0], SubModel2) + assert m.items[0].name == "Robert" + + # with two entries pointing to different types + m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) + assert len(m.items) == 2 + assert isinstance(m.items[0], SubModel1) + assert m.items[0].level == 1 + assert isinstance(m.items[1], SubModel1) + assert cast(Any, m.items[1]).name == "Robert" + + # with two entries pointing to *completely* different types + m = Model.construct(items=[{"level": -1}, 156]) + assert len(m.items) == 2 + assert isinstance(m.items[0], SubModel1) + assert m.items[0].level == -1 + assert m.items[1] == 156 + + +def test_dict_of_union() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + foo: str + + class Model(BaseModel): + data: Dict[str, Union[SubModel1, SubModel2]] + + m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) + assert len(list(m.data.keys())) == 2 + assert isinstance(m.data["hello"], SubModel1) + assert m.data["hello"].name == "there" + assert isinstance(m.data["foo"], SubModel2) + assert m.data["foo"].foo == "bar" + + # TODO: test mismatched type + + +def test_double_nested_union() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + bar: str + + class Model(BaseModel): + data: Dict[str, List[Union[SubModel1, SubModel2]]] + + m = Model.construct(data={"foo": [{"bar": "baz"}, {"name": "Robert"}]}) + assert len(m.data["foo"]) == 2 + + entry1 = m.data["foo"][0] + assert isinstance(entry1, SubModel2) + assert entry1.bar == "baz" + + entry2 = m.data["foo"][1] + assert isinstance(entry2, SubModel1) + assert entry2.name == "Robert" + + # TODO: test mismatched type + + +def test_union_of_dict() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + foo: str + + class Model(BaseModel): + data: Union[Dict[str, SubModel1], Dict[str, SubModel2]] + + m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) + assert len(list(m.data.keys())) == 2 + assert isinstance(m.data["hello"], SubModel1) + assert m.data["hello"].name == "there" + assert isinstance(m.data["foo"], SubModel1) + assert cast(Any, m.data["foo"]).foo == "bar" + + +def test_iso8601_datetime() -> None: + class Model(BaseModel): + created_at: datetime + + expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) + + if PYDANTIC_V2: + expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' + else: + expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' + + model = Model.construct(created_at="2019-12-27T18:11:19.117Z") + assert model.created_at == expected + assert model_json(model) == expected_json + + model = parse_obj(Model, dict(created_at="2019-12-27T18:11:19.117Z")) + assert model.created_at == expected + assert model_json(model) == expected_json + + +def test_does_not_coerce_int() -> None: + class Model(BaseModel): + bar: int + + assert Model.construct(bar=1).bar == 1 + assert Model.construct(bar=10.9).bar == 10.9 + assert Model.construct(bar="19").bar == "19" # type: ignore[comparison-overlap] + assert Model.construct(bar=False).bar is False + + +def test_int_to_float_safe_conversion() -> None: + class Model(BaseModel): + float_field: float + + m = Model.construct(float_field=10) + assert m.float_field == 10.0 + assert isinstance(m.float_field, float) + + m = Model.construct(float_field=10.12) + assert m.float_field == 10.12 + assert isinstance(m.float_field, float) + + # number too big + m = Model.construct(float_field=2**53 + 1) + assert m.float_field == 2**53 + 1 + assert isinstance(m.float_field, int) + + +def test_deprecated_alias() -> None: + class Model(BaseModel): + resource_id: str = Field(alias="model_id") + + @property + def model_id(self) -> str: + return self.resource_id + + m = Model.construct(model_id="id") + assert m.model_id == "id" + assert m.resource_id == "id" + assert m.resource_id is m.model_id + + m = parse_obj(Model, {"model_id": "id"}) + assert m.model_id == "id" + assert m.resource_id == "id" + assert m.resource_id is m.model_id + + +def test_omitted_fields() -> None: + class Model(BaseModel): + resource_id: Optional[str] = None + + m = Model.construct() + assert "resource_id" not in m.model_fields_set + + m = Model.construct(resource_id=None) + assert "resource_id" in m.model_fields_set + + m = Model.construct(resource_id="foo") + assert "resource_id" in m.model_fields_set + + +def test_forwards_compat_model_dump_method() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.model_dump() == {"foo": "hello"} + assert m.model_dump(include={"bar"}) == {} + assert m.model_dump(exclude={"foo"}) == {} + assert m.model_dump(by_alias=True) == {"FOO": "hello"} + + m2 = Model() + assert m2.model_dump() == {"foo": None} + assert m2.model_dump(exclude_unset=True) == {} + assert m2.model_dump(exclude_none=True) == {} + assert m2.model_dump(exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.model_dump() == {"foo": None} + assert m3.model_dump(exclude_none=True) == {} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="mode is only supported in Pydantic v2"): + m.model_dump(mode="json") + + with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): + m.model_dump(round_trip=True) + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.model_dump(warnings=False) + + +def test_forwards_compat_model_dump_json_method() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.model_dump_json()) == {"foo": "hello"} + assert json.loads(m.model_dump_json(include={"bar"})) == {} + assert json.loads(m.model_dump_json(include={"foo"})) == {"foo": "hello"} + assert json.loads(m.model_dump_json(by_alias=True)) == {"FOO": "hello"} + + assert m.model_dump_json(indent=2) == '{\n "foo": "hello"\n}' + + m2 = Model() + assert json.loads(m2.model_dump_json()) == {"foo": None} + assert json.loads(m2.model_dump_json(exclude_unset=True)) == {} + assert json.loads(m2.model_dump_json(exclude_none=True)) == {} + assert json.loads(m2.model_dump_json(exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.model_dump_json()) == {"foo": None} + assert json.loads(m3.model_dump_json(exclude_none=True)) == {} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): + m.model_dump_json(round_trip=True) + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.model_dump_json(warnings=False) + + +def test_type_compat() -> None: + # our model type can be assigned to Pydantic's model type + + def takes_pydantic(model: pydantic.BaseModel) -> None: # noqa: ARG001 + ... + + class OurModel(BaseModel): + foo: Optional[str] = None + + takes_pydantic(OurModel()) diff --git a/tests/test_module_client.py b/tests/test_module_client.py new file mode 100644 index 0000000000..0beca37f61 --- /dev/null +++ b/tests/test_module_client.py @@ -0,0 +1,179 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os as _os + +import httpx +import pytest +from httpx import URL + +import openai +from openai import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES + + +def reset_state() -> None: + openai._reset_client() + openai.api_key = None or "My API Key" + openai.organization = None + openai.base_url = None + openai.timeout = DEFAULT_TIMEOUT + openai.max_retries = DEFAULT_MAX_RETRIES + openai.default_headers = None + openai.default_query = None + openai.http_client = None + openai.api_type = _os.environ.get("OPENAI_API_TYPE") # type: ignore + openai.api_version = None + openai.azure_endpoint = None + openai.azure_ad_token = None + openai.azure_ad_token_provider = None + + +@pytest.fixture(autouse=True) +def reset_state_fixture() -> None: + reset_state() + + +def test_base_url_option() -> None: + assert openai.base_url is None + assert openai.completions._client.base_url == URL("https://api.openai.com/v1/") + + openai.base_url = "http://foo.com/" + + assert openai.base_url == URL("http://foo.com/") + assert openai.completions._client.base_url == URL("http://foo.com/") + + +def test_timeout_option() -> None: + assert openai.timeout == openai.DEFAULT_TIMEOUT + assert openai.completions._client.timeout == openai.DEFAULT_TIMEOUT + + openai.timeout = 3 + + assert openai.timeout == 3 + assert openai.completions._client.timeout == 3 + + +def test_max_retries_option() -> None: + assert openai.max_retries == openai.DEFAULT_MAX_RETRIES + assert openai.completions._client.max_retries == openai.DEFAULT_MAX_RETRIES + + openai.max_retries = 1 + + assert openai.max_retries == 1 + assert openai.completions._client.max_retries == 1 + + +def test_default_headers_option() -> None: + assert openai.default_headers == None + + openai.default_headers = {"Foo": "Bar"} + + assert openai.default_headers["Foo"] == "Bar" + assert openai.completions._client.default_headers["Foo"] == "Bar" + + +def test_default_query_option() -> None: + assert openai.default_query is None + assert openai.completions._client._custom_query == {} + + openai.default_query = {"Foo": {"nested": 1}} + + assert openai.default_query["Foo"] == {"nested": 1} + assert openai.completions._client._custom_query["Foo"] == {"nested": 1} + + +def test_http_client_option() -> None: + assert openai.http_client is None + + original_http_client = openai.completions._client._client + assert original_http_client is not None + + new_client = httpx.Client() + openai.http_client = new_client + + assert openai.completions._client._client is new_client + + +import contextlib +from typing import Iterator + +from openai.lib.azure import AzureOpenAI + + +@contextlib.contextmanager +def fresh_env() -> Iterator[None]: + old = _os.environ.copy() + + try: + _os.environ.clear() + yield + finally: + _os.environ.update(old) + + +def test_only_api_key_results_in_openai_api() -> None: + with fresh_env(): + openai.api_type = None + openai.api_key = "example API key" + + assert type(openai.completions._client).__name__ == "_ModuleClient" + + +def test_azure_api_key_env_without_api_version() -> None: + with fresh_env(): + openai.api_type = None + _os.environ["AZURE_OPENAI_API_KEY"] = "example API key" + + with pytest.raises(ValueError, match=r"Expected `api_version` to be given for the Azure client"): + openai.completions._client + + +def test_azure_api_key_and_version_env() -> None: + with fresh_env(): + openai.api_type = None + _os.environ["AZURE_OPENAI_API_KEY"] = "example API key" + _os.environ["OPENAI_API_VERSION"] = "example-version" + + with pytest.raises( + ValueError, + match=r"Must provide one of the `base_url` or `azure_endpoint` arguments, or the `OPENAI_BASE_URL`", + ): + openai.completions._client + + +def test_azure_api_key_version_and_endpoint_env() -> None: + with fresh_env(): + openai.api_type = None + _os.environ["AZURE_OPENAI_API_KEY"] = "example API key" + _os.environ["OPENAI_API_VERSION"] = "example-version" + _os.environ["AZURE_OPENAI_ENDPOINT"] = "https://www.example/" + + openai.completions._client + + assert openai.api_type == "azure" + + +def test_azure_azure_ad_token_version_and_endpoint_env() -> None: + with fresh_env(): + openai.api_type = None + _os.environ["AZURE_OPENAI_AD_TOKEN"] = "example AD token" + _os.environ["OPENAI_API_VERSION"] = "example-version" + _os.environ["AZURE_OPENAI_ENDPOINT"] = "https://www.example/" + + client = openai.completions._client + assert isinstance(client, AzureOpenAI) + assert client._azure_ad_token == "example AD token" + + +def test_azure_azure_ad_token_provider_version_and_endpoint_env() -> None: + with fresh_env(): + openai.api_type = None + _os.environ["OPENAI_API_VERSION"] = "example-version" + _os.environ["AZURE_OPENAI_ENDPOINT"] = "https://www.example/" + openai.azure_ad_token_provider = lambda: "token" + + client = openai.completions._client + assert isinstance(client, AzureOpenAI) + assert client._azure_ad_token_provider is not None + assert client._azure_ad_token_provider() == "token" diff --git a/tests/test_qs.py b/tests/test_qs.py new file mode 100644 index 0000000000..697b8a95ec --- /dev/null +++ b/tests/test_qs.py @@ -0,0 +1,78 @@ +from typing import Any, cast +from functools import partial +from urllib.parse import unquote + +import pytest + +from openai._qs import Querystring, stringify + + +def test_empty() -> None: + assert stringify({}) == "" + assert stringify({"a": {}}) == "" + assert stringify({"a": {"b": {"c": {}}}}) == "" + + +def test_basic() -> None: + assert stringify({"a": 1}) == "a=1" + assert stringify({"a": "b"}) == "a=b" + assert stringify({"a": True}) == "a=true" + assert stringify({"a": False}) == "a=false" + assert stringify({"a": 1.23456}) == "a=1.23456" + assert stringify({"a": None}) == "" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_nested_dotted(method: str) -> None: + if method == "class": + serialise = Querystring(nested_format="dots").stringify + else: + serialise = partial(stringify, nested_format="dots") + + assert unquote(serialise({"a": {"b": "c"}})) == "a.b=c" + assert unquote(serialise({"a": {"b": "c", "d": "e", "f": "g"}})) == "a.b=c&a.d=e&a.f=g" + assert unquote(serialise({"a": {"b": {"c": {"d": "e"}}}})) == "a.b.c.d=e" + assert unquote(serialise({"a": {"b": True}})) == "a.b=true" + + +def test_nested_brackets() -> None: + assert unquote(stringify({"a": {"b": "c"}})) == "a[b]=c" + assert unquote(stringify({"a": {"b": "c", "d": "e", "f": "g"}})) == "a[b]=c&a[d]=e&a[f]=g" + assert unquote(stringify({"a": {"b": {"c": {"d": "e"}}}})) == "a[b][c][d]=e" + assert unquote(stringify({"a": {"b": True}})) == "a[b]=true" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_array_comma(method: str) -> None: + if method == "class": + serialise = Querystring(array_format="comma").stringify + else: + serialise = partial(stringify, array_format="comma") + + assert unquote(serialise({"in": ["foo", "bar"]})) == "in=foo,bar" + assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b]=true,false" + assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b]=true,false,true" + + +def test_array_repeat() -> None: + assert unquote(stringify({"in": ["foo", "bar"]})) == "in=foo&in=bar" + assert unquote(stringify({"a": {"b": [True, False]}})) == "a[b]=true&a[b]=false" + assert unquote(stringify({"a": {"b": [True, False, None, True]}})) == "a[b]=true&a[b]=false&a[b]=true" + assert unquote(stringify({"in": ["foo", {"b": {"c": ["d", "e"]}}]})) == "in=foo&in[b][c]=d&in[b][c]=e" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_array_brackets(method: str) -> None: + if method == "class": + serialise = Querystring(array_format="brackets").stringify + else: + serialise = partial(stringify, array_format="brackets") + + assert unquote(serialise({"in": ["foo", "bar"]})) == "in[]=foo&in[]=bar" + assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b][]=true&a[b][]=false" + assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b][]=true&a[b][]=false&a[b][]=true" + + +def test_unknown_array_format() -> None: + with pytest.raises(NotImplementedError, match="Unknown array_format value: foo, choose from comma, repeat"): + stringify({"a": ["foo", "bar"]}, array_format=cast(Any, "foo")) diff --git a/tests/test_required_args.py b/tests/test_required_args.py new file mode 100644 index 0000000000..1de017db24 --- /dev/null +++ b/tests/test_required_args.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +import pytest + +from openai._utils import required_args + + +def test_too_many_positional_params() -> None: + @required_args(["a"]) + def foo(a: str | None = None) -> str | None: + return a + + with pytest.raises(TypeError, match=r"foo\(\) takes 1 argument\(s\) but 2 were given"): + foo("a", "b") # type: ignore + + +def test_positional_param() -> None: + @required_args(["a"]) + def foo(a: str | None = None) -> str | None: + return a + + assert foo("a") == "a" + assert foo(None) is None + assert foo(a="b") == "b" + + with pytest.raises(TypeError, match="Missing required argument: 'a'"): + foo() + + +def test_keyword_only_param() -> None: + @required_args(["a"]) + def foo(*, a: str | None = None) -> str | None: + return a + + assert foo(a="a") == "a" + assert foo(a=None) is None + assert foo(a="b") == "b" + + with pytest.raises(TypeError, match="Missing required argument: 'a'"): + foo() + + +def test_multiple_params() -> None: + @required_args(["a", "b", "c"]) + def foo(a: str = "", *, b: str = "", c: str = "") -> str | None: + return a + " " + b + " " + c + + assert foo(a="a", b="b", c="c") == "a b c" + + error_message = r"Missing required arguments.*" + + with pytest.raises(TypeError, match=error_message): + foo() + + with pytest.raises(TypeError, match=error_message): + foo(a="a") + + with pytest.raises(TypeError, match=error_message): + foo(b="b") + + with pytest.raises(TypeError, match=error_message): + foo(c="c") + + with pytest.raises(TypeError, match=r"Missing required argument: 'a'"): + foo(b="a", c="c") + + with pytest.raises(TypeError, match=r"Missing required argument: 'b'"): + foo("a", c="c") + + +def test_multiple_variants() -> None: + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: str | None = None) -> str | None: + return a if a is not None else b + + assert foo(a="foo") == "foo" + assert foo(b="bar") == "bar" + assert foo(a=None) is None + assert foo(b=None) is None + + # TODO: this error message could probably be improved + with pytest.raises( + TypeError, + match=r"Missing required arguments; Expected either \('a'\) or \('b'\) arguments to be given", + ): + foo() + + +def test_multiple_params_multiple_variants() -> None: + @required_args(["a", "b"], ["c"]) + def foo(*, a: str | None = None, b: str | None = None, c: str | None = None) -> str | None: + if a is not None: + return a + if b is not None: + return b + return c + + error_message = r"Missing required arguments; Expected either \('a' and 'b'\) or \('c'\) arguments to be given" + + with pytest.raises(TypeError, match=error_message): + foo(a="foo") + + with pytest.raises(TypeError, match=error_message): + foo(b="bar") + + with pytest.raises(TypeError, match=error_message): + foo() + + assert foo(a=None, b="bar") == "bar" + assert foo(c=None) is None + assert foo(c="foo") == "foo" diff --git a/tests/test_streaming.py b/tests/test_streaming.py new file mode 100644 index 0000000000..75e4ca2699 --- /dev/null +++ b/tests/test_streaming.py @@ -0,0 +1,104 @@ +from typing import Iterator, AsyncIterator + +import pytest + +from openai._streaming import SSEDecoder + + +@pytest.mark.asyncio +async def test_basic_async() -> None: + async def body() -> AsyncIterator[str]: + yield "event: completion" + yield 'data: {"foo":true}' + yield "" + + async for sse in SSEDecoder().aiter(body()): + assert sse.event == "completion" + assert sse.json() == {"foo": True} + + +def test_basic() -> None: + def body() -> Iterator[str]: + yield "event: completion" + yield 'data: {"foo":true}' + yield "" + + it = SSEDecoder().iter(body()) + sse = next(it) + assert sse.event == "completion" + assert sse.json() == {"foo": True} + + with pytest.raises(StopIteration): + next(it) + + +def test_data_missing_event() -> None: + def body() -> Iterator[str]: + yield 'data: {"foo":true}' + yield "" + + it = SSEDecoder().iter(body()) + sse = next(it) + assert sse.event is None + assert sse.json() == {"foo": True} + + with pytest.raises(StopIteration): + next(it) + + +def test_event_missing_data() -> None: + def body() -> Iterator[str]: + yield "event: ping" + yield "" + + it = SSEDecoder().iter(body()) + sse = next(it) + assert sse.event == "ping" + assert sse.data == "" + + with pytest.raises(StopIteration): + next(it) + + +def test_multiple_events() -> None: + def body() -> Iterator[str]: + yield "event: ping" + yield "" + yield "event: completion" + yield "" + + it = SSEDecoder().iter(body()) + + sse = next(it) + assert sse.event == "ping" + assert sse.data == "" + + sse = next(it) + assert sse.event == "completion" + assert sse.data == "" + + with pytest.raises(StopIteration): + next(it) + + +def test_multiple_events_with_data() -> None: + def body() -> Iterator[str]: + yield "event: ping" + yield 'data: {"foo":true}' + yield "" + yield "event: completion" + yield 'data: {"bar":false}' + yield "" + + it = SSEDecoder().iter(body()) + + sse = next(it) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + sse = next(it) + assert sse.event == "completion" + assert sse.json() == {"bar": False} + + with pytest.raises(StopIteration): + next(it) diff --git a/tests/test_transform.py b/tests/test_transform.py new file mode 100644 index 0000000000..3fc89bb093 --- /dev/null +++ b/tests/test_transform.py @@ -0,0 +1,232 @@ +from __future__ import annotations + +from typing import Any, List, Union, Optional +from datetime import date, datetime +from typing_extensions import Required, Annotated, TypedDict + +import pytest + +from openai._utils import PropertyInfo, transform, parse_datetime +from openai._models import BaseModel + + +class Foo1(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +def test_top_level_alias() -> None: + assert transform({"foo_bar": "hello"}, expected_type=Foo1) == {"fooBar": "hello"} + + +class Foo2(TypedDict): + bar: Bar2 + + +class Bar2(TypedDict): + this_thing: Annotated[int, PropertyInfo(alias="this__thing")] + baz: Annotated[Baz2, PropertyInfo(alias="Baz")] + + +class Baz2(TypedDict): + my_baz: Annotated[str, PropertyInfo(alias="myBaz")] + + +def test_recursive_typeddict() -> None: + assert transform({"bar": {"this_thing": 1}}, Foo2) == {"bar": {"this__thing": 1}} + assert transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2) == {"bar": {"Baz": {"myBaz": "foo"}}} + + +class Foo3(TypedDict): + things: List[Bar3] + + +class Bar3(TypedDict): + my_field: Annotated[str, PropertyInfo(alias="myField")] + + +def test_list_of_typeddict() -> None: + result = transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, expected_type=Foo3) + assert result == {"things": [{"myField": "foo"}, {"myField": "foo2"}]} + + +class Foo4(TypedDict): + foo: Union[Bar4, Baz4] + + +class Bar4(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz4(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +def test_union_of_typeddict() -> None: + assert transform({"foo": {"foo_bar": "bar"}}, Foo4) == {"foo": {"fooBar": "bar"}} + assert transform({"foo": {"foo_baz": "baz"}}, Foo4) == {"foo": {"fooBaz": "baz"}} + assert transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4) == {"foo": {"fooBaz": "baz", "fooBar": "bar"}} + + +class Foo5(TypedDict): + foo: Annotated[Union[Bar4, List[Baz4]], PropertyInfo(alias="FOO")] + + +class Bar5(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz5(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +def test_union_of_list() -> None: + assert transform({"foo": {"foo_bar": "bar"}}, Foo5) == {"FOO": {"fooBar": "bar"}} + assert transform( + { + "foo": [ + {"foo_baz": "baz"}, + {"foo_baz": "baz"}, + ] + }, + Foo5, + ) == {"FOO": [{"fooBaz": "baz"}, {"fooBaz": "baz"}]} + + +class Foo6(TypedDict): + bar: Annotated[str, PropertyInfo(alias="Bar")] + + +def test_includes_unknown_keys() -> None: + assert transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6) == { + "Bar": "bar", + "baz_": {"FOO": 1}, + } + + +class Foo7(TypedDict): + bar: Annotated[List[Bar7], PropertyInfo(alias="bAr")] + foo: Bar7 + + +class Bar7(TypedDict): + foo: str + + +def test_ignores_invalid_input() -> None: + assert transform({"bar": ""}, Foo7) == {"bAr": ""} + assert transform({"foo": ""}, Foo7) == {"foo": ""} + + +class DatetimeDict(TypedDict, total=False): + foo: Annotated[datetime, PropertyInfo(format="iso8601")] + + bar: Annotated[Optional[datetime], PropertyInfo(format="iso8601")] + + required: Required[Annotated[Optional[datetime], PropertyInfo(format="iso8601")]] + + list_: Required[Annotated[Optional[List[datetime]], PropertyInfo(format="iso8601")]] + + union: Annotated[Union[int, datetime], PropertyInfo(format="iso8601")] + + +class DateDict(TypedDict, total=False): + foo: Annotated[date, PropertyInfo(format="iso8601")] + + +def test_iso8601_format() -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert transform({"foo": dt}, DatetimeDict) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + + dt = dt.replace(tzinfo=None) + assert transform({"foo": dt}, DatetimeDict) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + + assert transform({"foo": None}, DateDict) == {"foo": None} # type: ignore[comparison-overlap] + assert transform({"foo": date.fromisoformat("2023-02-23")}, DateDict) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] + + +def test_optional_iso8601_format() -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert transform({"bar": dt}, DatetimeDict) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + + assert transform({"bar": None}, DatetimeDict) == {"bar": None} + + +def test_required_iso8601_format() -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert transform({"required": dt}, DatetimeDict) == {"required": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + + assert transform({"required": None}, DatetimeDict) == {"required": None} + + +def test_union_datetime() -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert transform({"union": dt}, DatetimeDict) == { # type: ignore[comparison-overlap] + "union": "2023-02-23T14:16:36.337692+00:00" + } + + assert transform({"union": "foo"}, DatetimeDict) == {"union": "foo"} + + +def test_nested_list_iso6801_format() -> None: + dt1 = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + dt2 = parse_datetime("2022-01-15T06:34:23Z") + assert transform({"list_": [dt1, dt2]}, DatetimeDict) == { # type: ignore[comparison-overlap] + "list_": ["2023-02-23T14:16:36.337692+00:00", "2022-01-15T06:34:23+00:00"] + } + + +def test_datetime_custom_format() -> None: + dt = parse_datetime("2022-01-15T06:34:23Z") + + result = transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")]) + assert result == "06" # type: ignore[comparison-overlap] + + +class DateDictWithRequiredAlias(TypedDict, total=False): + required_prop: Required[Annotated[date, PropertyInfo(format="iso8601", alias="prop")]] + + +def test_datetime_with_alias() -> None: + assert transform({"required_prop": None}, DateDictWithRequiredAlias) == {"prop": None} # type: ignore[comparison-overlap] + assert transform({"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] + + +class MyModel(BaseModel): + foo: str + + +def test_pydantic_model_to_dictionary() -> None: + assert transform(MyModel(foo="hi!"), Any) == {"foo": "hi!"} + assert transform(MyModel.construct(foo="hi!"), Any) == {"foo": "hi!"} + + +def test_pydantic_empty_model() -> None: + assert transform(MyModel.construct(), Any) == {} + + +def test_pydantic_unknown_field() -> None: + assert transform(MyModel.construct(my_untyped_field=True), Any) == {"my_untyped_field": True} + + +def test_pydantic_mismatched_types() -> None: + model = MyModel.construct(foo=True) + with pytest.warns(UserWarning): + params = transform(model, Any) + assert params == {"foo": True} + + +def test_pydantic_mismatched_object_type() -> None: + model = MyModel.construct(foo=MyModel.construct(hello="world")) + with pytest.warns(UserWarning): + params = transform(model, Any) + assert params == {"foo": {"hello": "world"}} + + +class ModelNestedObjects(BaseModel): + nested: MyModel + + +def test_pydantic_nested_objects() -> None: + model = ModelNestedObjects.construct(nested={"foo": "stainless"}) + assert isinstance(model.nested, MyModel) + assert transform(model, Any) == {"nested": {"foo": "stainless"}} diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000000..3cccab223a --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +import traceback +from typing import Any, TypeVar, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, get_origin, assert_type + +from openai._types import NoneType +from openai._utils import is_dict, is_list, is_list_type, is_union_type +from openai._compat import PYDANTIC_V2, field_outer_type, get_model_fields +from openai._models import BaseModel + +BaseModelT = TypeVar("BaseModelT", bound=BaseModel) + + +def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: + for name, field in get_model_fields(model).items(): + field_value = getattr(value, name) + if PYDANTIC_V2: + allow_none = False + else: + # in v1 nullability was structured differently + # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields + allow_none = getattr(field, "allow_none", False) + + assert_matches_type( + field_outer_type(field), + field_value, + path=[*path, name], + allow_none=allow_none, + ) + + return True + + +# Note: the `path` argument is only used to improve error messages when `--showlocals` is used +def assert_matches_type( + type_: Any, + value: object, + *, + path: list[str], + allow_none: bool = False, +) -> None: + if allow_none and value is None: + return + + if type_ is None or type_ is NoneType: + assert value is None + return + + origin = get_origin(type_) or type_ + + if is_list_type(type_): + return _assert_list_type(type_, value) + + if origin == str: + assert isinstance(value, str) + elif origin == int: + assert isinstance(value, int) + elif origin == bool: + assert isinstance(value, bool) + elif origin == float: + assert isinstance(value, float) + elif origin == datetime: + assert isinstance(value, datetime) + elif origin == date: + assert isinstance(value, date) + elif origin == object: + # nothing to do here, the expected type is unknown + pass + elif origin == Literal: + assert value in get_args(type_) + elif origin == dict: + assert is_dict(value) + + args = get_args(type_) + key_type = args[0] + items_type = args[1] + + for key, item in value.items(): + assert_matches_type(key_type, key, path=[*path, ""]) + assert_matches_type(items_type, item, path=[*path, ""]) + elif is_union_type(type_): + for i, variant in enumerate(get_args(type_)): + try: + assert_matches_type(variant, value, path=[*path, f"variant {i}"]) + return + except AssertionError: + traceback.print_exc() + continue + + assert False, "Did not match any variants" + elif issubclass(origin, BaseModel): + assert isinstance(value, type_) + assert assert_matches_model(type_, cast(Any, value), path=path) + else: + assert None, f"Unhandled field type: {type_}" + + +def _assert_list_type(type_: type[object], value: object) -> None: + assert is_list(value) + + inner_type = get_args(type_)[0] + for entry in value: + assert_type(inner_type, entry) # type: ignore From 6d21709610a97bd7c8575744998d0cc800e89f0c Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 6 Nov 2023 16:31:09 +0000 Subject: [PATCH 158/914] docs(readme): remove mention of beta version (#678) --- README.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/README.md b/README.md index a27375d598..821ecf1ecf 100644 --- a/README.md +++ b/README.md @@ -12,23 +12,10 @@ It is generated from our [OpenAPI specification](https://github.com/openai/opena The API documentation can be found [here](https://platform.openai.com/docs). -## Beta Release - -> [!IMPORTANT] -> We're preparing to release version 1.0 of the OpenAI Python library. - -This new version will be a major release and will include breaking changes. We're releasing this beta version to give you a chance to try out the new features and provide feedback before the official release. You can install the beta version with: - -```sh -pip install --pre openai -``` -And follow along with the [beta release notes](https://github.com/openai/openai-python/discussions/631). - - ## Installation ```sh -pip install --pre openai +pip install openai ``` ## Usage From 26d1a93458f754e6b8d5d1b2507e208393f06bda Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 6 Nov 2023 16:38:04 +0000 Subject: [PATCH 159/914] v1.0.1 (#679) --- pyproject.toml | 2 +- src/openai/_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7f6e3123d4..8c83f4260d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.0.0" +version = "1.0.1" description = "Client library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e9a3efc55c..f6f3a35c07 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.0.0" +__version__ = "1.0.1" From baa9f07f5b503017ea6dae8f047d3cfc1ee22c37 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 6 Nov 2023 20:35:10 +0000 Subject: [PATCH 160/914] feat(api): releases from DevDay; assistants, multimodality, tools, dall-e-3, tts, and more (#682) * feat(api): releases from DevDay; assistants, multimodality, tools, dall-e-3, tts, and more * docs(api): improve docstrings * v1.1.0 --- .stats.yml | 2 +- api.md | 140 +++- examples/async_demo.py | 0 examples/azure.py | 0 examples/azure_ad.py | 0 examples/demo.py | 0 examples/module_client.py | 0 pyproject.toml | 2 +- src/openai/__init__.py | 1 + src/openai/_client.py | 6 + src/openai/_module_client.py | 7 + src/openai/_version.py | 2 +- src/openai/cli/_api/chat/completions.py | 6 +- src/openai/cli/_api/files.py | 9 +- src/openai/pagination.py | 6 +- src/openai/resources/__init__.py | 5 + src/openai/resources/audio/__init__.py | 10 + src/openai/resources/audio/audio.py | 12 + src/openai/resources/audio/speech.py | 166 +++++ src/openai/resources/audio/transcriptions.py | 8 +- src/openai/resources/audio/translations.py | 8 +- src/openai/resources/beta/__init__.py | 30 + .../resources/beta/assistants/__init__.py | 20 + .../resources/beta/assistants/assistants.py | 654 ++++++++++++++++++ src/openai/resources/beta/assistants/files.py | 414 +++++++++++ src/openai/resources/beta/beta.py | 60 ++ src/openai/resources/beta/threads/__init__.py | 30 + .../beta/threads/messages/__init__.py | 20 + .../resources/beta/threads/messages/files.py | 257 +++++++ .../beta/threads/messages/messages.py | 477 +++++++++++++ .../resources/beta/threads/runs/__init__.py | 15 + .../resources/beta/threads/runs/runs.py | 654 ++++++++++++++++++ .../resources/beta/threads/runs/steps.py | 255 +++++++ src/openai/resources/beta/threads/threads.py | 541 +++++++++++++++ src/openai/resources/chat/completions.py | 300 ++++++-- src/openai/resources/completions.py | 64 +- src/openai/resources/files.py | 103 ++- src/openai/resources/images.py | 82 ++- src/openai/types/__init__.py | 1 + src/openai/types/audio/__init__.py | 1 + .../types/audio/speech_create_params.py | 34 + .../audio/transcription_create_params.py | 4 +- .../types/audio/translation_create_params.py | 4 +- src/openai/types/beta/__init__.py | 16 + src/openai/types/beta/assistant.py | 112 +++ .../types/beta/assistant_create_params.py | 109 +++ .../types/beta/assistant_list_params.py | 39 ++ .../types/beta/assistant_update_params.py | 111 +++ src/openai/types/beta/assistants/__init__.py | 8 + .../types/beta/assistants/assistant_file.py | 21 + .../beta/assistants/file_create_params.py | 16 + .../beta/assistants/file_delete_response.py | 15 + .../types/beta/assistants/file_list_params.py | 39 ++ src/openai/types/beta/asssitant_deleted.py | 15 + src/openai/types/beta/chat/__init__.py | 3 + src/openai/types/beta/thread.py | 28 + .../beta/thread_create_and_run_params.py | 148 ++++ src/openai/types/beta/thread_create_params.py | 51 ++ src/openai/types/beta/thread_deleted.py | 15 + src/openai/types/beta/thread_update_params.py | 18 + src/openai/types/beta/threads/__init__.py | 22 + .../threads/message_content_image_file.py | 22 + .../beta/threads/message_content_text.py | 74 ++ .../beta/threads/message_create_params.py | 35 + .../types/beta/threads/message_list_params.py | 39 ++ .../beta/threads/message_update_params.py | 20 + .../types/beta/threads/messages/__init__.py | 6 + .../beta/threads/messages/file_list_params.py | 41 ++ .../beta/threads/messages/message_file.py | 25 + .../required_action_function_tool_call.py | 34 + src/openai/types/beta/threads/run.py | 182 +++++ .../types/beta/threads/run_create_params.py | 100 +++ .../types/beta/threads/run_list_params.py | 39 ++ .../threads/run_submit_tool_outputs_params.py | 26 + .../types/beta/threads/run_update_params.py | 20 + .../types/beta/threads/runs/__init__.py | 13 + .../types/beta/threads/runs/code_tool_call.py | 67 ++ .../beta/threads/runs/function_tool_call.py | 38 + .../runs/message_creation_step_details.py | 19 + .../beta/threads/runs/retrieval_tool_call.py | 21 + .../types/beta/threads/runs/run_step.py | 93 +++ .../beta/threads/runs/step_list_params.py | 41 ++ .../threads/runs/tool_calls_step_details.py | 25 + .../types/beta/threads/thread_message.py | 65 ++ src/openai/types/chat/__init__.py | 42 ++ src/openai/types/chat/chat_completion.py | 14 +- ...chat_completion_assistant_message_param.py | 41 ++ .../types/chat/chat_completion_chunk.py | 49 +- ...hat_completion_content_part_image_param.py | 22 + .../chat_completion_content_part_param.py | 14 + ...chat_completion_content_part_text_param.py | 15 + ...t_completion_function_call_option_param.py | 12 + .../chat_completion_function_message_param.py | 19 + .../types/chat/chat_completion_message.py | 13 +- .../chat/chat_completion_message_param.py | 63 +- .../chat/chat_completion_message_tool_call.py | 31 + ...chat_completion_message_tool_call_param.py | 31 + ...chat_completion_named_tool_choice_param.py | 19 + src/openai/types/chat/chat_completion_role.py | 2 +- .../chat_completion_system_message_param.py | 16 + ...hat_completion_tool_choice_option_param.py | 12 + .../chat_completion_tool_message_param.py | 19 + .../types/chat/chat_completion_tool_param.py | 42 ++ .../chat_completion_user_message_param.py | 18 + .../types/chat/completion_create_params.py | 90 ++- src/openai/types/completion.py | 10 +- src/openai/types/completion_create_params.py | 12 +- src/openai/types/create_embedding_response.py | 3 +- src/openai/types/edit.py | 2 +- src/openai/types/embedding.py | 3 +- src/openai/types/file_create_params.py | 17 +- src/openai/types/file_deleted.py | 4 +- src/openai/types/file_list_params.py | 12 + src/openai/types/file_object.py | 28 +- src/openai/types/fine_tune.py | 3 +- src/openai/types/fine_tune_event.py | 4 +- .../types/fine_tune_events_list_response.py | 3 +- .../types/fine_tuning/fine_tuning_job.py | 4 +- .../fine_tuning/fine_tuning_job_event.py | 2 +- .../types/fine_tuning/job_create_params.py | 13 + src/openai/types/image.py | 6 + .../types/image_create_variation_params.py | 13 +- src/openai/types/image_edit_params.py | 8 +- src/openai/types/image_generate_params.py | 34 +- src/openai/types/model.py | 4 +- tests/api_resources/audio/test_speech.py | 110 +++ tests/api_resources/beta/__init__.py | 1 + .../api_resources/beta/assistants/__init__.py | 1 + .../beta/assistants/test_files.py | 190 +++++ tests/api_resources/beta/chat/__init__.py | 1 + tests/api_resources/beta/test_assistants.py | 254 +++++++ tests/api_resources/beta/test_threads.py | 318 +++++++++ tests/api_resources/beta/threads/__init__.py | 1 + .../beta/threads/messages/__init__.py | 1 + .../beta/threads/messages/test_files.py | 128 ++++ .../beta/threads/runs/__init__.py | 1 + .../beta/threads/runs/test_steps.py | 128 ++++ .../beta/threads/test_messages.py | 234 +++++++ tests/api_resources/beta/threads/test_runs.py | 308 +++++++++ tests/api_resources/chat/test_completions.py | 136 +++- tests/api_resources/fine_tuning/test_jobs.py | 12 +- tests/api_resources/test_completions.py | 4 + tests/api_resources/test_files.py | 22 +- tests/api_resources/test_images.py | 10 + 144 files changed, 8618 insertions(+), 252 deletions(-) mode change 100644 => 100755 examples/async_demo.py mode change 100644 => 100755 examples/azure.py mode change 100644 => 100755 examples/azure_ad.py mode change 100644 => 100755 examples/demo.py mode change 100644 => 100755 examples/module_client.py create mode 100644 src/openai/resources/audio/speech.py create mode 100644 src/openai/resources/beta/__init__.py create mode 100644 src/openai/resources/beta/assistants/__init__.py create mode 100644 src/openai/resources/beta/assistants/assistants.py create mode 100644 src/openai/resources/beta/assistants/files.py create mode 100644 src/openai/resources/beta/beta.py create mode 100644 src/openai/resources/beta/threads/__init__.py create mode 100644 src/openai/resources/beta/threads/messages/__init__.py create mode 100644 src/openai/resources/beta/threads/messages/files.py create mode 100644 src/openai/resources/beta/threads/messages/messages.py create mode 100644 src/openai/resources/beta/threads/runs/__init__.py create mode 100644 src/openai/resources/beta/threads/runs/runs.py create mode 100644 src/openai/resources/beta/threads/runs/steps.py create mode 100644 src/openai/resources/beta/threads/threads.py create mode 100644 src/openai/types/audio/speech_create_params.py create mode 100644 src/openai/types/beta/__init__.py create mode 100644 src/openai/types/beta/assistant.py create mode 100644 src/openai/types/beta/assistant_create_params.py create mode 100644 src/openai/types/beta/assistant_list_params.py create mode 100644 src/openai/types/beta/assistant_update_params.py create mode 100644 src/openai/types/beta/assistants/__init__.py create mode 100644 src/openai/types/beta/assistants/assistant_file.py create mode 100644 src/openai/types/beta/assistants/file_create_params.py create mode 100644 src/openai/types/beta/assistants/file_delete_response.py create mode 100644 src/openai/types/beta/assistants/file_list_params.py create mode 100644 src/openai/types/beta/asssitant_deleted.py create mode 100644 src/openai/types/beta/chat/__init__.py create mode 100644 src/openai/types/beta/thread.py create mode 100644 src/openai/types/beta/thread_create_and_run_params.py create mode 100644 src/openai/types/beta/thread_create_params.py create mode 100644 src/openai/types/beta/thread_deleted.py create mode 100644 src/openai/types/beta/thread_update_params.py create mode 100644 src/openai/types/beta/threads/__init__.py create mode 100644 src/openai/types/beta/threads/message_content_image_file.py create mode 100644 src/openai/types/beta/threads/message_content_text.py create mode 100644 src/openai/types/beta/threads/message_create_params.py create mode 100644 src/openai/types/beta/threads/message_list_params.py create mode 100644 src/openai/types/beta/threads/message_update_params.py create mode 100644 src/openai/types/beta/threads/messages/__init__.py create mode 100644 src/openai/types/beta/threads/messages/file_list_params.py create mode 100644 src/openai/types/beta/threads/messages/message_file.py create mode 100644 src/openai/types/beta/threads/required_action_function_tool_call.py create mode 100644 src/openai/types/beta/threads/run.py create mode 100644 src/openai/types/beta/threads/run_create_params.py create mode 100644 src/openai/types/beta/threads/run_list_params.py create mode 100644 src/openai/types/beta/threads/run_submit_tool_outputs_params.py create mode 100644 src/openai/types/beta/threads/run_update_params.py create mode 100644 src/openai/types/beta/threads/runs/__init__.py create mode 100644 src/openai/types/beta/threads/runs/code_tool_call.py create mode 100644 src/openai/types/beta/threads/runs/function_tool_call.py create mode 100644 src/openai/types/beta/threads/runs/message_creation_step_details.py create mode 100644 src/openai/types/beta/threads/runs/retrieval_tool_call.py create mode 100644 src/openai/types/beta/threads/runs/run_step.py create mode 100644 src/openai/types/beta/threads/runs/step_list_params.py create mode 100644 src/openai/types/beta/threads/runs/tool_calls_step_details.py create mode 100644 src/openai/types/beta/threads/thread_message.py create mode 100644 src/openai/types/chat/chat_completion_assistant_message_param.py create mode 100644 src/openai/types/chat/chat_completion_content_part_image_param.py create mode 100644 src/openai/types/chat/chat_completion_content_part_param.py create mode 100644 src/openai/types/chat/chat_completion_content_part_text_param.py create mode 100644 src/openai/types/chat/chat_completion_function_call_option_param.py create mode 100644 src/openai/types/chat/chat_completion_function_message_param.py create mode 100644 src/openai/types/chat/chat_completion_message_tool_call.py create mode 100644 src/openai/types/chat/chat_completion_message_tool_call_param.py create mode 100644 src/openai/types/chat/chat_completion_named_tool_choice_param.py create mode 100644 src/openai/types/chat/chat_completion_system_message_param.py create mode 100644 src/openai/types/chat/chat_completion_tool_choice_option_param.py create mode 100644 src/openai/types/chat/chat_completion_tool_message_param.py create mode 100644 src/openai/types/chat/chat_completion_tool_param.py create mode 100644 src/openai/types/chat/chat_completion_user_message_param.py create mode 100644 src/openai/types/file_list_params.py create mode 100644 tests/api_resources/audio/test_speech.py create mode 100644 tests/api_resources/beta/__init__.py create mode 100644 tests/api_resources/beta/assistants/__init__.py create mode 100644 tests/api_resources/beta/assistants/test_files.py create mode 100644 tests/api_resources/beta/chat/__init__.py create mode 100644 tests/api_resources/beta/test_assistants.py create mode 100644 tests/api_resources/beta/test_threads.py create mode 100644 tests/api_resources/beta/threads/__init__.py create mode 100644 tests/api_resources/beta/threads/messages/__init__.py create mode 100644 tests/api_resources/beta/threads/messages/test_files.py create mode 100644 tests/api_resources/beta/threads/runs/__init__.py create mode 100644 tests/api_resources/beta/threads/runs/test_steps.py create mode 100644 tests/api_resources/beta/threads/test_messages.py create mode 100644 tests/api_resources/beta/threads/test_runs.py diff --git a/.stats.yml b/.stats.yml index f21eb8fef0..03b0268ffa 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 28 +configured_endpoints: 57 diff --git a/api.md b/api.md index 915a05479a..818ae73b31 100644 --- a/api.md +++ b/api.md @@ -19,10 +19,23 @@ Types: ```python from openai.types.chat import ( ChatCompletion, + ChatCompletionAssistantMessageParam, ChatCompletionChunk, + ChatCompletionContentPart, + ChatCompletionContentPartImage, + ChatCompletionContentPartText, + ChatCompletionFunctionCallOption, + ChatCompletionFunctionMessageParam, ChatCompletionMessage, ChatCompletionMessageParam, + ChatCompletionMessageToolCall, + ChatCompletionNamedToolChoice, ChatCompletionRole, + ChatCompletionSystemMessageParam, + ChatCompletionTool, + ChatCompletionToolChoiceOption, + ChatCompletionToolMessageParam, + ChatCompletionUserMessageParam, ) ``` @@ -66,7 +79,7 @@ Methods: - client.files.create(\*\*params) -> FileObject - client.files.retrieve(file_id) -> FileObject -- client.files.list() -> SyncPage[FileObject] +- client.files.list(\*\*params) -> SyncPage[FileObject] - client.files.delete(file_id) -> FileDeleted - client.files.retrieve_content(file_id) -> str - client.files.wait_for_processing(\*args) -> FileObject @@ -111,6 +124,12 @@ Methods: - client.audio.translations.create(\*\*params) -> Translation +## Speech + +Methods: + +- client.audio.speech.create(\*\*params) -> HttpxBinaryResponseContent + # Moderations Types: @@ -170,3 +189,122 @@ Methods: - client.fine_tunes.list() -> SyncPage[FineTune] - client.fine_tunes.cancel(fine_tune_id) -> FineTune - client.fine_tunes.list_events(fine_tune_id, \*\*params) -> FineTuneEventsListResponse + +# Beta + +## Assistants + +Types: + +```python +from openai.types.beta import Assistant, AsssitantDeleted +``` + +Methods: + +- client.beta.assistants.create(\*\*params) -> Assistant +- client.beta.assistants.retrieve(assistant_id) -> Assistant +- client.beta.assistants.update(assistant_id, \*\*params) -> Assistant +- client.beta.assistants.list(\*\*params) -> SyncCursorPage[Assistant] +- client.beta.assistants.delete(assistant_id) -> AsssitantDeleted + +### Files + +Types: + +```python +from openai.types.beta.assistants import AssistantFile, FileDeleteResponse +``` + +Methods: + +- client.beta.assistants.files.create(assistant_id, \*\*params) -> AssistantFile +- client.beta.assistants.files.retrieve(file_id, \*, assistant_id) -> AssistantFile +- client.beta.assistants.files.list(assistant_id, \*\*params) -> SyncCursorPage[AssistantFile] +- client.beta.assistants.files.delete(file_id, \*, assistant_id) -> FileDeleteResponse + +## Threads + +Types: + +```python +from openai.types.beta import Thread, ThreadDeleted +``` + +Methods: + +- client.beta.threads.create(\*\*params) -> Thread +- client.beta.threads.retrieve(thread_id) -> Thread +- client.beta.threads.update(thread_id, \*\*params) -> Thread +- client.beta.threads.delete(thread_id) -> ThreadDeleted +- client.beta.threads.create_and_run(\*\*params) -> Run + +### Runs + +Types: + +```python +from openai.types.beta.threads import RequiredActionFunctionToolCall, Run +``` + +Methods: + +- client.beta.threads.runs.create(thread_id, \*\*params) -> Run +- client.beta.threads.runs.retrieve(run_id, \*, thread_id) -> Run +- client.beta.threads.runs.update(run_id, \*, thread_id, \*\*params) -> Run +- client.beta.threads.runs.list(thread_id, \*\*params) -> SyncCursorPage[Run] +- client.beta.threads.runs.cancel(run_id, \*, thread_id) -> Run +- client.beta.threads.runs.submit_tool_outputs(run_id, \*, thread_id, \*\*params) -> Run + +#### Steps + +Types: + +```python +from openai.types.beta.threads.runs import ( + CodeToolCall, + FunctionToolCall, + MessageCreationStepDetails, + RetrievalToolCall, + RunStep, + ToolCallsStepDetails, +) +``` + +Methods: + +- client.beta.threads.runs.steps.retrieve(step_id, \*, thread_id, run_id) -> RunStep +- client.beta.threads.runs.steps.list(run_id, \*, thread_id, \*\*params) -> SyncCursorPage[RunStep] + +### Messages + +Types: + +```python +from openai.types.beta.threads import ( + MessageContentImageFile, + MessageContentText, + ThreadMessage, + ThreadMessageDeleted, +) +``` + +Methods: + +- client.beta.threads.messages.create(thread_id, \*\*params) -> ThreadMessage +- client.beta.threads.messages.retrieve(message_id, \*, thread_id) -> ThreadMessage +- client.beta.threads.messages.update(message_id, \*, thread_id, \*\*params) -> ThreadMessage +- client.beta.threads.messages.list(thread_id, \*\*params) -> SyncCursorPage[ThreadMessage] + +#### Files + +Types: + +```python +from openai.types.beta.threads.messages import MessageFile +``` + +Methods: + +- client.beta.threads.messages.files.retrieve(file_id, \*, thread_id, message_id) -> MessageFile +- client.beta.threads.messages.files.list(message_id, \*, thread_id, \*\*params) -> SyncCursorPage[MessageFile] diff --git a/examples/async_demo.py b/examples/async_demo.py old mode 100644 new mode 100755 diff --git a/examples/azure.py b/examples/azure.py old mode 100644 new mode 100755 diff --git a/examples/azure_ad.py b/examples/azure_ad.py old mode 100644 new mode 100755 diff --git a/examples/demo.py b/examples/demo.py old mode 100644 new mode 100755 diff --git a/examples/module_client.py b/examples/module_client.py old mode 100644 new mode 100755 diff --git a/pyproject.toml b/pyproject.toml index 8c83f4260d..9ab62e23fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.0.1" +version = "1.1.0" description = "Client library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/__init__.py b/src/openai/__init__.py index f033d8f26c..da1157a767 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -329,6 +329,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] _client = None +from ._module_client import beta as beta from ._module_client import chat as chat from ._module_client import audio as audio from ._module_client import edits as edits diff --git a/src/openai/_client.py b/src/openai/_client.py index 9df7eabf9a..6476d2b1a8 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -52,6 +52,7 @@ class OpenAI(SyncAPIClient): models: resources.Models fine_tuning: resources.FineTuning fine_tunes: resources.FineTunes + beta: resources.Beta with_raw_response: OpenAIWithRawResponse # client options @@ -125,6 +126,7 @@ def __init__( self.models = resources.Models(self) self.fine_tuning = resources.FineTuning(self) self.fine_tunes = resources.FineTunes(self) + self.beta = resources.Beta(self) self.with_raw_response = OpenAIWithRawResponse(self) @property @@ -257,6 +259,7 @@ class AsyncOpenAI(AsyncAPIClient): models: resources.AsyncModels fine_tuning: resources.AsyncFineTuning fine_tunes: resources.AsyncFineTunes + beta: resources.AsyncBeta with_raw_response: AsyncOpenAIWithRawResponse # client options @@ -330,6 +333,7 @@ def __init__( self.models = resources.AsyncModels(self) self.fine_tuning = resources.AsyncFineTuning(self) self.fine_tunes = resources.AsyncFineTunes(self) + self.beta = resources.AsyncBeta(self) self.with_raw_response = AsyncOpenAIWithRawResponse(self) @property @@ -466,6 +470,7 @@ def __init__(self, client: OpenAI) -> None: self.models = resources.ModelsWithRawResponse(client.models) self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning) self.fine_tunes = resources.FineTunesWithRawResponse(client.fine_tunes) + self.beta = resources.BetaWithRawResponse(client.beta) class AsyncOpenAIWithRawResponse: @@ -481,6 +486,7 @@ def __init__(self, client: AsyncOpenAI) -> None: self.models = resources.AsyncModelsWithRawResponse(client.models) self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning) self.fine_tunes = resources.AsyncFineTunesWithRawResponse(client.fine_tunes) + self.beta = resources.AsyncBetaWithRawResponse(client.beta) Client = OpenAI diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index ca80468e88..fe8e0a2139 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -12,6 +12,12 @@ def __load__(self) -> resources.Chat: return _load_client().chat +class BetaProxy(LazyProxy[resources.Beta]): + @override + def __load__(self) -> resources.Beta: + return _load_client().beta + + class EditsProxy(LazyProxy[resources.Edits]): @override def __load__(self) -> resources.Edits: @@ -73,6 +79,7 @@ def __load__(self) -> resources.FineTuning: chat: resources.Chat = ChatProxy().__as_proxied__() +beta: resources.Beta = BetaProxy().__as_proxied__() edits: resources.Edits = EditsProxy().__as_proxied__() files: resources.Files = FilesProxy().__as_proxied__() audio: resources.Audio = AudioProxy().__as_proxied__() diff --git a/src/openai/_version.py b/src/openai/_version.py index f6f3a35c07..57548ed376 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.0.1" +__version__ = "1.1.0" diff --git a/src/openai/cli/_api/chat/completions.py b/src/openai/cli/_api/chat/completions.py index e7566b143d..c299741fe0 100644 --- a/src/openai/cli/_api/chat/completions.py +++ b/src/openai/cli/_api/chat/completions.py @@ -3,7 +3,7 @@ import sys from typing import TYPE_CHECKING, List, Optional, cast from argparse import ArgumentParser -from typing_extensions import NamedTuple +from typing_extensions import Literal, NamedTuple from ..._utils import get_client from ..._models import BaseModel @@ -97,7 +97,9 @@ class CLIChatCompletion: def create(args: CLIChatCompletionCreateArgs) -> None: params: CompletionCreateParams = { "model": args.model, - "messages": [{"role": message.role, "content": message.content} for message in args.message], + "messages": [ + {"role": cast(Literal["user"], message.role), "content": message.content} for message in args.message + ], "n": args.n, "temperature": args.temperature, "top_p": args.top_p, diff --git a/src/openai/cli/_api/files.py b/src/openai/cli/_api/files.py index ae6dadf0f1..5f3631b284 100644 --- a/src/openai/cli/_api/files.py +++ b/src/openai/cli/_api/files.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, cast from argparse import ArgumentParser from .._utils import get_client, print_model @@ -55,7 +55,12 @@ 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), purpose=args.purpose) + 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 diff --git a/src/openai/pagination.py b/src/openai/pagination.py index ff45f39517..4ec300f2d1 100644 --- a/src/openai/pagination.py +++ b/src/openai/pagination.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import Any, List, Generic, TypeVar, Optional, cast -from typing_extensions import Protocol, override, runtime_checkable +from typing_extensions import Literal, Protocol, override, runtime_checkable from ._types import ModelT from ._models import BaseModel @@ -21,7 +21,7 @@ class SyncPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" data: List[ModelT] - object: str + object: Literal["list"] @override def _get_page_items(self) -> List[ModelT]: @@ -40,7 +40,7 @@ class AsyncPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" data: List[ModelT] - object: str + object: Literal["list"] @override def _get_page_items(self) -> List[ModelT]: diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index e0a26c72d2..e0f4f08d5c 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. +from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse from .edits import Edits, AsyncEdits, EditsWithRawResponse, AsyncEditsWithRawResponse @@ -92,4 +93,8 @@ "AsyncFineTunes", "FineTunesWithRawResponse", "AsyncFineTunesWithRawResponse", + "Beta", + "AsyncBeta", + "BetaWithRawResponse", + "AsyncBetaWithRawResponse", ] diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py index 771bfe9da2..76547b5f34 100644 --- a/src/openai/resources/audio/__init__.py +++ b/src/openai/resources/audio/__init__.py @@ -1,6 +1,12 @@ # File generated from our OpenAPI spec by Stainless. from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse +from .speech import ( + Speech, + AsyncSpeech, + SpeechWithRawResponse, + AsyncSpeechWithRawResponse, +) from .translations import ( Translations, AsyncTranslations, @@ -23,6 +29,10 @@ "AsyncTranslations", "TranslationsWithRawResponse", "AsyncTranslationsWithRawResponse", + "Speech", + "AsyncSpeech", + "SpeechWithRawResponse", + "AsyncSpeechWithRawResponse", "Audio", "AsyncAudio", "AudioWithRawResponse", diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 8e8872c5b5..6f7226ee59 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -4,6 +4,12 @@ from typing import TYPE_CHECKING +from .speech import ( + Speech, + AsyncSpeech, + SpeechWithRawResponse, + AsyncSpeechWithRawResponse, +) from ..._resource import SyncAPIResource, AsyncAPIResource from .translations import ( Translations, @@ -27,24 +33,28 @@ class Audio(SyncAPIResource): transcriptions: Transcriptions translations: Translations + speech: Speech with_raw_response: AudioWithRawResponse def __init__(self, client: OpenAI) -> None: super().__init__(client) self.transcriptions = Transcriptions(client) self.translations = Translations(client) + self.speech = Speech(client) self.with_raw_response = AudioWithRawResponse(self) class AsyncAudio(AsyncAPIResource): transcriptions: AsyncTranscriptions translations: AsyncTranslations + speech: AsyncSpeech with_raw_response: AsyncAudioWithRawResponse def __init__(self, client: AsyncOpenAI) -> None: super().__init__(client) self.transcriptions = AsyncTranscriptions(client) self.translations = AsyncTranslations(client) + self.speech = AsyncSpeech(client) self.with_raw_response = AsyncAudioWithRawResponse(self) @@ -52,9 +62,11 @@ class AudioWithRawResponse: def __init__(self, audio: Audio) -> None: self.transcriptions = TranscriptionsWithRawResponse(audio.transcriptions) self.translations = TranslationsWithRawResponse(audio.translations) + self.speech = SpeechWithRawResponse(audio.speech) class AsyncAudioWithRawResponse: def __init__(self, audio: AsyncAudio) -> None: self.transcriptions = AsyncTranscriptionsWithRawResponse(audio.transcriptions) self.translations = AsyncTranslationsWithRawResponse(audio.translations) + self.speech = AsyncSpeechWithRawResponse(audio.speech) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py new file mode 100644 index 0000000000..7318e3a2e4 --- /dev/null +++ b/src/openai/resources/audio/speech.py @@ -0,0 +1,166 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Union +from typing_extensions import Literal + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...types.audio import speech_create_params +from ..._base_client import HttpxBinaryResponseContent, make_request_options + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Speech", "AsyncSpeech"] + + +class Speech(SyncAPIResource): + with_raw_response: SpeechWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = SpeechWithRawResponse(self) + + def create( + self, + *, + input: str, + model: Union[str, Literal["tts-1", "tts-1-hd"]], + voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], + response_format: Literal["mp3", "opus", "aac", "flac"] | NotGiven = NOT_GIVEN, + speed: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> HttpxBinaryResponseContent: + """ + Generates audio from the input text. + + Args: + input: The text to generate audio for. The maximum length is 4096 characters. + + model: + One of the available [TTS models](https://platform.openai.com/docs/models/tts): + `tts-1` or `tts-1-hd` + + voice: The voice to use when generating the audio. Supported voices are `alloy`, + `echo`, `fable`, `onyx`, `nova`, and `shimmer`. + + response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`. + + speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is + the default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/audio/speech", + body=maybe_transform( + { + "input": input, + "model": model, + "voice": voice, + "response_format": response_format, + "speed": speed, + }, + speech_create_params.SpeechCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=HttpxBinaryResponseContent, + ) + + +class AsyncSpeech(AsyncAPIResource): + with_raw_response: AsyncSpeechWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncSpeechWithRawResponse(self) + + async def create( + self, + *, + input: str, + model: Union[str, Literal["tts-1", "tts-1-hd"]], + voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], + response_format: Literal["mp3", "opus", "aac", "flac"] | NotGiven = NOT_GIVEN, + speed: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> HttpxBinaryResponseContent: + """ + Generates audio from the input text. + + Args: + input: The text to generate audio for. The maximum length is 4096 characters. + + model: + One of the available [TTS models](https://platform.openai.com/docs/models/tts): + `tts-1` or `tts-1-hd` + + voice: The voice to use when generating the audio. Supported voices are `alloy`, + `echo`, `fable`, `onyx`, `nova`, and `shimmer`. + + response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`. + + speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is + the default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/audio/speech", + body=maybe_transform( + { + "input": input, + "model": model, + "voice": voice, + "response_format": response_format, + "speed": speed, + }, + speech_create_params.SpeechCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=HttpxBinaryResponseContent, + ) + + +class SpeechWithRawResponse: + def __init__(self, speech: Speech) -> None: + self.create = to_raw_response_wrapper( + speech.create, + ) + + +class AsyncSpeechWithRawResponse: + def __init__(self, speech: AsyncSpeech) -> None: + self.create = async_to_raw_response_wrapper( + speech.create, + ) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index ca61f8bd42..44d973d0af 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -60,8 +60,8 @@ def create( [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should match the audio language. - response_format: The format of the transcript output, in one of these options: json, text, srt, - verbose_json, or vtt. + response_format: The format of the transcript output, in one of these options: `json`, `text`, + `srt`, `verbose_json`, or `vtt`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -147,8 +147,8 @@ async def create( [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should match the audio language. - response_format: The format of the transcript output, in one of these options: json, text, srt, - verbose_json, or vtt. + response_format: The format of the transcript output, in one of these options: `json`, `text`, + `srt`, `verbose_json`, or `vtt`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 0b499b9865..bb37c691fc 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -54,8 +54,8 @@ def create( [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should be in English. - response_format: The format of the transcript output, in one of these options: json, text, srt, - verbose_json, or vtt. + response_format: The format of the transcript output, in one of these options: `json`, `text`, + `srt`, `verbose_json`, or `vtt`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -134,8 +134,8 @@ async def create( [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should be in English. - response_format: The format of the transcript output, in one of these options: json, text, srt, - verbose_json, or vtt. + response_format: The format of the transcript output, in one of these options: `json`, `text`, + `srt`, `verbose_json`, or `vtt`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py new file mode 100644 index 0000000000..55ad243cca --- /dev/null +++ b/src/openai/resources/beta/__init__.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. + +from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, +) +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, +) + +__all__ = [ + "Assistants", + "AsyncAssistants", + "AssistantsWithRawResponse", + "AsyncAssistantsWithRawResponse", + "Threads", + "AsyncThreads", + "ThreadsWithRawResponse", + "AsyncThreadsWithRawResponse", + "Beta", + "AsyncBeta", + "BetaWithRawResponse", + "AsyncBetaWithRawResponse", +] diff --git a/src/openai/resources/beta/assistants/__init__.py b/src/openai/resources/beta/assistants/__init__.py new file mode 100644 index 0000000000..6efb0b21ec --- /dev/null +++ b/src/openai/resources/beta/assistants/__init__.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. + +from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, +) + +__all__ = [ + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "Assistants", + "AsyncAssistants", + "AssistantsWithRawResponse", + "AsyncAssistantsWithRawResponse", +] diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py new file mode 100644 index 0000000000..03f2759fc2 --- /dev/null +++ b/src/openai/resources/beta/assistants/assistants.py @@ -0,0 +1,654 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional +from typing_extensions import Literal + +from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ....types.beta import ( + Assistant, + AsssitantDeleted, + assistant_list_params, + assistant_create_params, + assistant_update_params, +) +from ...._base_client import AsyncPaginator, make_request_options + +if TYPE_CHECKING: + from ...._client import OpenAI, AsyncOpenAI + +__all__ = ["Assistants", "AsyncAssistants"] + + +class Assistants(SyncAPIResource): + files: Files + with_raw_response: AssistantsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.files = Files(client) + self.with_raw_response = AssistantsWithRawResponse(self) + + def create( + self, + *, + model: str, + description: Optional[str] | NotGiven = NOT_GIVEN, + file_ids: List[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + tools: List[assistant_create_params.Tool] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Assistant: + """ + Create an assistant with a model and instructions. + + Args: + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + description: The description of the assistant. The maximum length is 512 characters. + + file_ids: A list of [file](https://platform.openai.com/docs/api-reference/files) IDs + attached to this assistant. There can be a maximum of 20 files attached to the + assistant. Files are ordered by their creation date in ascending order. + + instructions: The system instructions that the assistant uses. The maximum length is 32768 + characters. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + name: The name of the assistant. The maximum length is 256 characters. + + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per + assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + "/assistants", + body=maybe_transform( + { + "model": model, + "description": description, + "file_ids": file_ids, + "instructions": instructions, + "metadata": metadata, + "name": name, + "tools": tools, + }, + assistant_create_params.AssistantCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Assistant, + ) + + def retrieve( + self, + assistant_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Assistant: + """ + Retrieves an assistant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get( + f"/assistants/{assistant_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Assistant, + ) + + def update( + self, + assistant_id: str, + *, + description: Optional[str] | NotGiven = NOT_GIVEN, + file_ids: List[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: str | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + tools: List[assistant_update_params.Tool] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Assistant: + """Modifies an assistant. + + Args: + description: The description of the assistant. + + The maximum length is 512 characters. + + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs + attached to this assistant. There can be a maximum of 20 files attached to the + assistant. Files are ordered by their creation date in ascending order. If a + file was previosuly attached to the list but does not show up in the list, it + will be deleted from the assistant. + + instructions: The system instructions that the assistant uses. The maximum length is 32768 + characters. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + name: The name of the assistant. The maximum length is 256 characters. + + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per + assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/assistants/{assistant_id}", + body=maybe_transform( + { + "description": description, + "file_ids": file_ids, + "instructions": instructions, + "metadata": metadata, + "model": model, + "name": name, + "tools": tools, + }, + assistant_update_params.AssistantUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Assistant, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[Assistant]: + """Returns a list of assistants. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + "/assistants", + page=SyncCursorPage[Assistant], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + assistant_list_params.AssistantListParams, + ), + ), + model=Assistant, + ) + + def delete( + self, + assistant_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsssitantDeleted: + """ + Delete an assistant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._delete( + f"/assistants/{assistant_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AsssitantDeleted, + ) + + +class AsyncAssistants(AsyncAPIResource): + files: AsyncFiles + with_raw_response: AsyncAssistantsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.files = AsyncFiles(client) + self.with_raw_response = AsyncAssistantsWithRawResponse(self) + + async def create( + self, + *, + model: str, + description: Optional[str] | NotGiven = NOT_GIVEN, + file_ids: List[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + tools: List[assistant_create_params.Tool] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Assistant: + """ + Create an assistant with a model and instructions. + + Args: + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + description: The description of the assistant. The maximum length is 512 characters. + + file_ids: A list of [file](https://platform.openai.com/docs/api-reference/files) IDs + attached to this assistant. There can be a maximum of 20 files attached to the + assistant. Files are ordered by their creation date in ascending order. + + instructions: The system instructions that the assistant uses. The maximum length is 32768 + characters. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + name: The name of the assistant. The maximum length is 256 characters. + + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per + assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + "/assistants", + body=maybe_transform( + { + "model": model, + "description": description, + "file_ids": file_ids, + "instructions": instructions, + "metadata": metadata, + "name": name, + "tools": tools, + }, + assistant_create_params.AssistantCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Assistant, + ) + + async def retrieve( + self, + assistant_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Assistant: + """ + Retrieves an assistant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._get( + f"/assistants/{assistant_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Assistant, + ) + + async def update( + self, + assistant_id: str, + *, + description: Optional[str] | NotGiven = NOT_GIVEN, + file_ids: List[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: str | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + tools: List[assistant_update_params.Tool] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Assistant: + """Modifies an assistant. + + Args: + description: The description of the assistant. + + The maximum length is 512 characters. + + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs + attached to this assistant. There can be a maximum of 20 files attached to the + assistant. Files are ordered by their creation date in ascending order. If a + file was previosuly attached to the list but does not show up in the list, it + will be deleted from the assistant. + + instructions: The system instructions that the assistant uses. The maximum length is 32768 + characters. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: ID of the model to use. You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + + name: The name of the assistant. The maximum length is 256 characters. + + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per + assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/assistants/{assistant_id}", + body=maybe_transform( + { + "description": description, + "file_ids": file_ids, + "instructions": instructions, + "metadata": metadata, + "model": model, + "name": name, + "tools": tools, + }, + assistant_update_params.AssistantUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Assistant, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Assistant, AsyncCursorPage[Assistant]]: + """Returns a list of assistants. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + "/assistants", + page=AsyncCursorPage[Assistant], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + assistant_list_params.AssistantListParams, + ), + ), + model=Assistant, + ) + + async def delete( + self, + assistant_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsssitantDeleted: + """ + Delete an assistant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._delete( + f"/assistants/{assistant_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AsssitantDeleted, + ) + + +class AssistantsWithRawResponse: + def __init__(self, assistants: Assistants) -> None: + self.files = FilesWithRawResponse(assistants.files) + + self.create = to_raw_response_wrapper( + assistants.create, + ) + self.retrieve = to_raw_response_wrapper( + assistants.retrieve, + ) + self.update = to_raw_response_wrapper( + assistants.update, + ) + self.list = to_raw_response_wrapper( + assistants.list, + ) + self.delete = to_raw_response_wrapper( + assistants.delete, + ) + + +class AsyncAssistantsWithRawResponse: + def __init__(self, assistants: AsyncAssistants) -> None: + self.files = AsyncFilesWithRawResponse(assistants.files) + + self.create = async_to_raw_response_wrapper( + assistants.create, + ) + self.retrieve = async_to_raw_response_wrapper( + assistants.retrieve, + ) + self.update = async_to_raw_response_wrapper( + assistants.update, + ) + self.list = async_to_raw_response_wrapper( + assistants.list, + ) + self.delete = async_to_raw_response_wrapper( + assistants.delete, + ) diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py new file mode 100644 index 0000000000..b1953525e8 --- /dev/null +++ b/src/openai/resources/beta/assistants/files.py @@ -0,0 +1,414 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING +from typing_extensions import Literal + +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.beta.assistants import ( + AssistantFile, + FileDeleteResponse, + file_list_params, + file_create_params, +) + +if TYPE_CHECKING: + from ...._client import OpenAI, AsyncOpenAI + +__all__ = ["Files", "AsyncFiles"] + + +class Files(SyncAPIResource): + with_raw_response: FilesWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = FilesWithRawResponse(self) + + def create( + self, + assistant_id: str, + *, + file_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AssistantFile: + """ + Create an assistant file by attaching a + [File](https://platform.openai.com/docs/api-reference/files) to an + [assistant](https://platform.openai.com/docs/api-reference/assistants). + + Args: + file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID (with + `purpose="assistants"`) that the assistant should use. Useful for tools like + `retrieval` and `code_interpreter` that can access files. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/assistants/{assistant_id}/files", + body=maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AssistantFile, + ) + + def retrieve( + self, + file_id: str, + *, + assistant_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AssistantFile: + """ + Retrieves an AssistantFile. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get( + f"/assistants/{assistant_id}/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AssistantFile, + ) + + def list( + self, + assistant_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[AssistantFile]: + """ + Returns a list of assistant files. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/assistants/{assistant_id}/files", + page=SyncCursorPage[AssistantFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + file_list_params.FileListParams, + ), + ), + model=AssistantFile, + ) + + def delete( + self, + file_id: str, + *, + assistant_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FileDeleteResponse: + """ + Delete an assistant file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._delete( + f"/assistants/{assistant_id}/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileDeleteResponse, + ) + + +class AsyncFiles(AsyncAPIResource): + with_raw_response: AsyncFilesWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncFilesWithRawResponse(self) + + async def create( + self, + assistant_id: str, + *, + file_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AssistantFile: + """ + Create an assistant file by attaching a + [File](https://platform.openai.com/docs/api-reference/files) to an + [assistant](https://platform.openai.com/docs/api-reference/assistants). + + Args: + file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID (with + `purpose="assistants"`) that the assistant should use. Useful for tools like + `retrieval` and `code_interpreter` that can access files. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/assistants/{assistant_id}/files", + body=maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AssistantFile, + ) + + async def retrieve( + self, + file_id: str, + *, + assistant_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AssistantFile: + """ + Retrieves an AssistantFile. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._get( + f"/assistants/{assistant_id}/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AssistantFile, + ) + + def list( + self, + assistant_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[AssistantFile, AsyncCursorPage[AssistantFile]]: + """ + Returns a list of assistant files. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/assistants/{assistant_id}/files", + page=AsyncCursorPage[AssistantFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + file_list_params.FileListParams, + ), + ), + model=AssistantFile, + ) + + async def delete( + self, + file_id: str, + *, + assistant_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> FileDeleteResponse: + """ + Delete an assistant file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._delete( + f"/assistants/{assistant_id}/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileDeleteResponse, + ) + + +class FilesWithRawResponse: + def __init__(self, files: Files) -> None: + self.create = to_raw_response_wrapper( + files.create, + ) + self.retrieve = to_raw_response_wrapper( + files.retrieve, + ) + self.list = to_raw_response_wrapper( + files.list, + ) + self.delete = to_raw_response_wrapper( + files.delete, + ) + + +class AsyncFilesWithRawResponse: + def __init__(self, files: AsyncFiles) -> None: + self.create = async_to_raw_response_wrapper( + files.create, + ) + self.retrieve = async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = async_to_raw_response_wrapper( + files.list, + ) + self.delete = async_to_raw_response_wrapper( + files.delete, + ) diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py new file mode 100644 index 0000000000..b552561763 --- /dev/null +++ b/src/openai/resources/beta/beta.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, +) +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource + +if TYPE_CHECKING: + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Beta", "AsyncBeta"] + + +class Beta(SyncAPIResource): + assistants: Assistants + threads: Threads + with_raw_response: BetaWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.assistants = Assistants(client) + self.threads = Threads(client) + self.with_raw_response = BetaWithRawResponse(self) + + +class AsyncBeta(AsyncAPIResource): + assistants: AsyncAssistants + threads: AsyncThreads + with_raw_response: AsyncBetaWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.assistants = AsyncAssistants(client) + self.threads = AsyncThreads(client) + self.with_raw_response = AsyncBetaWithRawResponse(self) + + +class BetaWithRawResponse: + def __init__(self, beta: Beta) -> None: + self.assistants = AssistantsWithRawResponse(beta.assistants) + self.threads = ThreadsWithRawResponse(beta.threads) + + +class AsyncBetaWithRawResponse: + def __init__(self, beta: AsyncBeta) -> None: + self.assistants = AsyncAssistantsWithRawResponse(beta.assistants) + self.threads = AsyncThreadsWithRawResponse(beta.threads) diff --git a/src/openai/resources/beta/threads/__init__.py b/src/openai/resources/beta/threads/__init__.py new file mode 100644 index 0000000000..b9aaada465 --- /dev/null +++ b/src/openai/resources/beta/threads/__init__.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. + +from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, +) +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, +) + +__all__ = [ + "Runs", + "AsyncRuns", + "RunsWithRawResponse", + "AsyncRunsWithRawResponse", + "Messages", + "AsyncMessages", + "MessagesWithRawResponse", + "AsyncMessagesWithRawResponse", + "Threads", + "AsyncThreads", + "ThreadsWithRawResponse", + "AsyncThreadsWithRawResponse", +] diff --git a/src/openai/resources/beta/threads/messages/__init__.py b/src/openai/resources/beta/threads/messages/__init__.py new file mode 100644 index 0000000000..d8d4ce448c --- /dev/null +++ b/src/openai/resources/beta/threads/messages/__init__.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. + +from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, +) + +__all__ = [ + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "Messages", + "AsyncMessages", + "MessagesWithRawResponse", + "AsyncMessagesWithRawResponse", +] diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py new file mode 100644 index 0000000000..70166eb7b2 --- /dev/null +++ b/src/openai/resources/beta/threads/messages/files.py @@ -0,0 +1,257 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING +from typing_extensions import Literal + +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._utils import maybe_transform +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.threads.messages import MessageFile, file_list_params + +if TYPE_CHECKING: + from ....._client import OpenAI, AsyncOpenAI + +__all__ = ["Files", "AsyncFiles"] + + +class Files(SyncAPIResource): + with_raw_response: FilesWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = FilesWithRawResponse(self) + + def retrieve( + self, + file_id: str, + *, + thread_id: str, + message_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> MessageFile: + """ + Retrieves a message file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get( + f"/threads/{thread_id}/messages/{message_id}/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MessageFile, + ) + + def list( + self, + message_id: str, + *, + thread_id: str, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[MessageFile]: + """Returns a list of message files. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/threads/{thread_id}/messages/{message_id}/files", + page=SyncCursorPage[MessageFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + file_list_params.FileListParams, + ), + ), + model=MessageFile, + ) + + +class AsyncFiles(AsyncAPIResource): + with_raw_response: AsyncFilesWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncFilesWithRawResponse(self) + + async def retrieve( + self, + file_id: str, + *, + thread_id: str, + message_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> MessageFile: + """ + Retrieves a message file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._get( + f"/threads/{thread_id}/messages/{message_id}/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MessageFile, + ) + + def list( + self, + message_id: str, + *, + thread_id: str, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[MessageFile, AsyncCursorPage[MessageFile]]: + """Returns a list of message files. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/threads/{thread_id}/messages/{message_id}/files", + page=AsyncCursorPage[MessageFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + file_list_params.FileListParams, + ), + ), + model=MessageFile, + ) + + +class FilesWithRawResponse: + def __init__(self, files: Files) -> None: + self.retrieve = to_raw_response_wrapper( + files.retrieve, + ) + self.list = to_raw_response_wrapper( + files.list, + ) + + +class AsyncFilesWithRawResponse: + def __init__(self, files: AsyncFiles) -> None: + self.retrieve = async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = async_to_raw_response_wrapper( + files.list, + ) diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py new file mode 100644 index 0000000000..caec03f484 --- /dev/null +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -0,0 +1,477 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional +from typing_extensions import Literal + +from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._utils import maybe_transform +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.threads import ( + ThreadMessage, + message_list_params, + message_create_params, + message_update_params, +) + +if TYPE_CHECKING: + from ....._client import OpenAI, AsyncOpenAI + +__all__ = ["Messages", "AsyncMessages"] + + +class Messages(SyncAPIResource): + files: Files + with_raw_response: MessagesWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.files = Files(client) + self.with_raw_response = MessagesWithRawResponse(self) + + def create( + self, + thread_id: str, + *, + content: str, + role: Literal["user"], + file_ids: List[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ThreadMessage: + """ + Create a message. + + Args: + content: The content of the message. + + role: The role of the entity that is creating the message. Currently only `user` is + supported. + + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the message should use. There can be a maximum of 10 files attached to a + message. Useful for tools like `retrieval` and `code_interpreter` that can + access and use files. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/threads/{thread_id}/messages", + body=maybe_transform( + { + "content": content, + "role": role, + "file_ids": file_ids, + "metadata": metadata, + }, + message_create_params.MessageCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadMessage, + ) + + def retrieve( + self, + message_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ThreadMessage: + """ + Retrieve a message. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get( + f"/threads/{thread_id}/messages/{message_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadMessage, + ) + + def update( + self, + message_id: str, + *, + thread_id: str, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ThreadMessage: + """ + Modifies a message. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/threads/{thread_id}/messages/{message_id}", + body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadMessage, + ) + + def list( + self, + thread_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[ThreadMessage]: + """ + Returns a list of messages for a given thread. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/threads/{thread_id}/messages", + page=SyncCursorPage[ThreadMessage], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + message_list_params.MessageListParams, + ), + ), + model=ThreadMessage, + ) + + +class AsyncMessages(AsyncAPIResource): + files: AsyncFiles + with_raw_response: AsyncMessagesWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.files = AsyncFiles(client) + self.with_raw_response = AsyncMessagesWithRawResponse(self) + + async def create( + self, + thread_id: str, + *, + content: str, + role: Literal["user"], + file_ids: List[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ThreadMessage: + """ + Create a message. + + Args: + content: The content of the message. + + role: The role of the entity that is creating the message. Currently only `user` is + supported. + + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the message should use. There can be a maximum of 10 files attached to a + message. Useful for tools like `retrieval` and `code_interpreter` that can + access and use files. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/threads/{thread_id}/messages", + body=maybe_transform( + { + "content": content, + "role": role, + "file_ids": file_ids, + "metadata": metadata, + }, + message_create_params.MessageCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadMessage, + ) + + async def retrieve( + self, + message_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ThreadMessage: + """ + Retrieve a message. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._get( + f"/threads/{thread_id}/messages/{message_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadMessage, + ) + + async def update( + self, + message_id: str, + *, + thread_id: str, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ThreadMessage: + """ + Modifies a message. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/threads/{thread_id}/messages/{message_id}", + body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadMessage, + ) + + def list( + self, + thread_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[ThreadMessage, AsyncCursorPage[ThreadMessage]]: + """ + Returns a list of messages for a given thread. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/threads/{thread_id}/messages", + page=AsyncCursorPage[ThreadMessage], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + message_list_params.MessageListParams, + ), + ), + model=ThreadMessage, + ) + + +class MessagesWithRawResponse: + def __init__(self, messages: Messages) -> None: + self.files = FilesWithRawResponse(messages.files) + + self.create = to_raw_response_wrapper( + messages.create, + ) + self.retrieve = to_raw_response_wrapper( + messages.retrieve, + ) + self.update = to_raw_response_wrapper( + messages.update, + ) + self.list = to_raw_response_wrapper( + messages.list, + ) + + +class AsyncMessagesWithRawResponse: + def __init__(self, messages: AsyncMessages) -> None: + self.files = AsyncFilesWithRawResponse(messages.files) + + self.create = async_to_raw_response_wrapper( + messages.create, + ) + self.retrieve = async_to_raw_response_wrapper( + messages.retrieve, + ) + self.update = async_to_raw_response_wrapper( + messages.update, + ) + self.list = async_to_raw_response_wrapper( + messages.list, + ) diff --git a/src/openai/resources/beta/threads/runs/__init__.py b/src/openai/resources/beta/threads/runs/__init__.py new file mode 100644 index 0000000000..6b61813974 --- /dev/null +++ b/src/openai/resources/beta/threads/runs/__init__.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse +from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse + +__all__ = [ + "Steps", + "AsyncSteps", + "StepsWithRawResponse", + "AsyncStepsWithRawResponse", + "Runs", + "AsyncRuns", + "RunsWithRawResponse", + "AsyncRunsWithRawResponse", +] diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py new file mode 100644 index 0000000000..370056cbf4 --- /dev/null +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -0,0 +1,654 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional +from typing_extensions import Literal + +from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._utils import maybe_transform +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.threads import ( + Run, + run_list_params, + run_create_params, + run_update_params, + run_submit_tool_outputs_params, +) + +if TYPE_CHECKING: + from ....._client import OpenAI, AsyncOpenAI + +__all__ = ["Runs", "AsyncRuns"] + + +class Runs(SyncAPIResource): + steps: Steps + with_raw_response: RunsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.steps = Steps(client) + self.with_raw_response = RunsWithRawResponse(self) + + def create( + self, + thread_id: str, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[List[run_create_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/threads/{thread_id}/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "metadata": metadata, + "model": model, + "tools": tools, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + def retrieve( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Retrieves a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get( + f"/threads/{thread_id}/runs/{run_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + def update( + self, + run_id: str, + *, + thread_id: str, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Modifies a run. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/threads/{thread_id}/runs/{run_id}", + body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + def list( + self, + thread_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[Run]: + """ + Returns a list of runs belonging to a thread. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/threads/{thread_id}/runs", + page=SyncCursorPage[Run], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + run_list_params.RunListParams, + ), + ), + model=Run, + ) + + def cancel( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Cancels a run that is `in_progress`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/threads/{thread_id}/runs/{run_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + tool_outputs: List[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + body=maybe_transform( + {"tool_outputs": tool_outputs}, run_submit_tool_outputs_params.RunSubmitToolOutputsParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + +class AsyncRuns(AsyncAPIResource): + steps: AsyncSteps + with_raw_response: AsyncRunsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.steps = AsyncSteps(client) + self.with_raw_response = AsyncRunsWithRawResponse(self) + + async def create( + self, + thread_id: str, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[List[run_create_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/threads/{thread_id}/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "metadata": metadata, + "model": model, + "tools": tools, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + async def retrieve( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Retrieves a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._get( + f"/threads/{thread_id}/runs/{run_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + async def update( + self, + run_id: str, + *, + thread_id: str, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Modifies a run. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/threads/{thread_id}/runs/{run_id}", + body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + def list( + self, + thread_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Run, AsyncCursorPage[Run]]: + """ + Returns a list of runs belonging to a thread. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/threads/{thread_id}/runs", + page=AsyncCursorPage[Run], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + run_list_params.RunListParams, + ), + ), + model=Run, + ) + + async def cancel( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Cancels a run that is `in_progress`. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/threads/{thread_id}/runs/{run_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + async def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + tool_outputs: List[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + body=maybe_transform( + {"tool_outputs": tool_outputs}, run_submit_tool_outputs_params.RunSubmitToolOutputsParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + +class RunsWithRawResponse: + def __init__(self, runs: Runs) -> None: + self.steps = StepsWithRawResponse(runs.steps) + + self.create = to_raw_response_wrapper( + runs.create, + ) + self.retrieve = to_raw_response_wrapper( + runs.retrieve, + ) + self.update = to_raw_response_wrapper( + runs.update, + ) + self.list = to_raw_response_wrapper( + runs.list, + ) + self.cancel = to_raw_response_wrapper( + runs.cancel, + ) + self.submit_tool_outputs = to_raw_response_wrapper( + runs.submit_tool_outputs, + ) + + +class AsyncRunsWithRawResponse: + def __init__(self, runs: AsyncRuns) -> None: + self.steps = AsyncStepsWithRawResponse(runs.steps) + + self.create = async_to_raw_response_wrapper( + runs.create, + ) + self.retrieve = async_to_raw_response_wrapper( + runs.retrieve, + ) + self.update = async_to_raw_response_wrapper( + runs.update, + ) + self.list = async_to_raw_response_wrapper( + runs.list, + ) + self.cancel = async_to_raw_response_wrapper( + runs.cancel, + ) + self.submit_tool_outputs = async_to_raw_response_wrapper( + runs.submit_tool_outputs, + ) diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py new file mode 100644 index 0000000000..bc6fd7fdc9 --- /dev/null +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -0,0 +1,255 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING +from typing_extensions import Literal + +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._utils import maybe_transform +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.threads.runs import RunStep, step_list_params + +if TYPE_CHECKING: + from ....._client import OpenAI, AsyncOpenAI + +__all__ = ["Steps", "AsyncSteps"] + + +class Steps(SyncAPIResource): + with_raw_response: StepsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.with_raw_response = StepsWithRawResponse(self) + + def retrieve( + self, + step_id: str, + *, + thread_id: str, + run_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> RunStep: + """ + Retrieves a run step. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get( + f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunStep, + ) + + def list( + self, + run_id: str, + *, + thread_id: str, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[RunStep]: + """ + Returns a list of run steps belonging to a run. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/threads/{thread_id}/runs/{run_id}/steps", + page=SyncCursorPage[RunStep], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + step_list_params.StepListParams, + ), + ), + model=RunStep, + ) + + +class AsyncSteps(AsyncAPIResource): + with_raw_response: AsyncStepsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.with_raw_response = AsyncStepsWithRawResponse(self) + + async def retrieve( + self, + step_id: str, + *, + thread_id: str, + run_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> RunStep: + """ + Retrieves a run step. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._get( + f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunStep, + ) + + def list( + self, + run_id: str, + *, + thread_id: str, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[RunStep, AsyncCursorPage[RunStep]]: + """ + Returns a list of run steps belonging to a run. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get_api_list( + f"/threads/{thread_id}/runs/{run_id}/steps", + page=AsyncCursorPage[RunStep], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + step_list_params.StepListParams, + ), + ), + model=RunStep, + ) + + +class StepsWithRawResponse: + def __init__(self, steps: Steps) -> None: + self.retrieve = to_raw_response_wrapper( + steps.retrieve, + ) + self.list = to_raw_response_wrapper( + steps.list, + ) + + +class AsyncStepsWithRawResponse: + def __init__(self, steps: AsyncSteps) -> None: + self.retrieve = async_to_raw_response_wrapper( + steps.retrieve, + ) + self.list = async_to_raw_response_wrapper( + steps.list, + ) diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py new file mode 100644 index 0000000000..286630d81c --- /dev/null +++ b/src/openai/resources/beta/threads/threads.py @@ -0,0 +1,541 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, +) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ....types.beta import ( + Thread, + ThreadDeleted, + thread_create_params, + thread_update_params, + thread_create_and_run_params, +) +from ...._base_client import make_request_options +from ....types.beta.threads import Run + +if TYPE_CHECKING: + from ...._client import OpenAI, AsyncOpenAI + +__all__ = ["Threads", "AsyncThreads"] + + +class Threads(SyncAPIResource): + runs: Runs + messages: Messages + with_raw_response: ThreadsWithRawResponse + + def __init__(self, client: OpenAI) -> None: + super().__init__(client) + self.runs = Runs(client) + self.messages = Messages(client) + self.with_raw_response = ThreadsWithRawResponse(self) + + def create( + self, + *, + messages: List[thread_create_params.Message] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Thread: + """ + Create a thread. + + Args: + messages: A list of [messages](https://platform.openai.com/docs/api-reference/messages) to + start the thread with. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + "/threads", + body=maybe_transform( + { + "messages": messages, + "metadata": metadata, + }, + thread_create_params.ThreadCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Thread, + ) + + def retrieve( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Thread: + """ + Retrieves a thread. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._get( + f"/threads/{thread_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Thread, + ) + + def update( + self, + thread_id: str, + *, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Thread: + """ + Modifies a thread. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/threads/{thread_id}", + body=maybe_transform({"metadata": metadata}, thread_update_params.ThreadUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Thread, + ) + + def delete( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ThreadDeleted: + """ + Delete a thread. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._delete( + f"/threads/{thread_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadDeleted, + ) + + def create_and_run( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[List[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + thread: If no thread is provided, an empty thread will be created. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + "/threads/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "metadata": metadata, + "model": model, + "thread": thread, + "tools": tools, + }, + thread_create_and_run_params.ThreadCreateAndRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + +class AsyncThreads(AsyncAPIResource): + runs: AsyncRuns + messages: AsyncMessages + with_raw_response: AsyncThreadsWithRawResponse + + def __init__(self, client: AsyncOpenAI) -> None: + super().__init__(client) + self.runs = AsyncRuns(client) + self.messages = AsyncMessages(client) + self.with_raw_response = AsyncThreadsWithRawResponse(self) + + async def create( + self, + *, + messages: List[thread_create_params.Message] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Thread: + """ + Create a thread. + + Args: + messages: A list of [messages](https://platform.openai.com/docs/api-reference/messages) to + start the thread with. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + "/threads", + body=maybe_transform( + { + "messages": messages, + "metadata": metadata, + }, + thread_create_params.ThreadCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Thread, + ) + + async def retrieve( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Thread: + """ + Retrieves a thread. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._get( + f"/threads/{thread_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Thread, + ) + + async def update( + self, + thread_id: str, + *, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Thread: + """ + Modifies a thread. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/threads/{thread_id}", + body=maybe_transform({"metadata": metadata}, thread_update_params.ThreadUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Thread, + ) + + async def delete( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> ThreadDeleted: + """ + Delete a thread. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._delete( + f"/threads/{thread_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadDeleted, + ) + + async def create_and_run( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[List[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + thread: If no thread is provided, an empty thread will be created. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + "/threads/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "metadata": metadata, + "model": model, + "thread": thread, + "tools": tools, + }, + thread_create_and_run_params.ThreadCreateAndRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + ) + + +class ThreadsWithRawResponse: + def __init__(self, threads: Threads) -> None: + self.runs = RunsWithRawResponse(threads.runs) + self.messages = MessagesWithRawResponse(threads.messages) + + self.create = to_raw_response_wrapper( + threads.create, + ) + self.retrieve = to_raw_response_wrapper( + threads.retrieve, + ) + self.update = to_raw_response_wrapper( + threads.update, + ) + self.delete = to_raw_response_wrapper( + threads.delete, + ) + self.create_and_run = to_raw_response_wrapper( + threads.create_and_run, + ) + + +class AsyncThreadsWithRawResponse: + def __init__(self, threads: AsyncThreads) -> None: + self.runs = AsyncRunsWithRawResponse(threads.runs) + self.messages = AsyncMessagesWithRawResponse(threads.messages) + + self.create = async_to_raw_response_wrapper( + threads.create, + ) + self.retrieve = async_to_raw_response_wrapper( + threads.retrieve, + ) + self.update = async_to_raw_response_wrapper( + threads.update, + ) + self.delete = async_to_raw_response_wrapper( + threads.delete, + ) + self.create_and_run = async_to_raw_response_wrapper( + threads.create_and_run, + ) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index e6e6ce52b8..2ecde23ce1 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -13,7 +13,9 @@ from ...types.chat import ( ChatCompletion, ChatCompletionChunk, + ChatCompletionToolParam, ChatCompletionMessageParam, + ChatCompletionToolChoiceOptionParam, completion_create_params, ) from ..._base_client import make_request_options @@ -59,9 +61,13 @@ def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -88,18 +94,24 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - function_call: Controls how the model calls functions. "none" means the model will not call a - function and instead generates a message. "auto" means the model can pick - between generating a message or calling a function. Specifying a particular - function via `{"name": "my_function"}` forces the model to call that function. - "none" is the default when no functions are present. "auto" is the default if + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via `{"name": "my_function"}` forces the model to call that + function. + + `none` is the default when no functions are present. `auto`` is the default if functions are present. - functions: A list of functions the model may generate JSON inputs for. + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the + Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or @@ -121,6 +133,15 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + response_format: An object specifying the format that the model must output. Used to enable JSON + mode. + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be @@ -136,6 +157,20 @@ def create( We generally recommend altering this or `top_p` but not both. + tool_choice: Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via + `{"type: "function", "function": {"name": "my_function"}}` forces the model to + call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. Use this to provide a list of functions the model may generate JSON inputs + for. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -185,8 +220,12 @@ def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -220,18 +259,24 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - function_call: Controls how the model calls functions. "none" means the model will not call a - function and instead generates a message. "auto" means the model can pick - between generating a message or calling a function. Specifying a particular - function via `{"name": "my_function"}` forces the model to call that function. - "none" is the default when no functions are present. "auto" is the default if + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via `{"name": "my_function"}` forces the model to call that + function. + + `none` is the default when no functions are present. `auto`` is the default if functions are present. - functions: A list of functions the model may generate JSON inputs for. + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the + Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or @@ -253,6 +298,15 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + response_format: An object specifying the format that the model must output. Used to enable JSON + mode. + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -261,6 +315,20 @@ def create( We generally recommend altering this or `top_p` but not both. + tool_choice: Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via + `{"type: "function", "function": {"name": "my_function"}}` forces the model to + call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. Use this to provide a list of functions the model may generate JSON inputs + for. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -310,8 +378,12 @@ def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -345,18 +417,24 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - function_call: Controls how the model calls functions. "none" means the model will not call a - function and instead generates a message. "auto" means the model can pick - between generating a message or calling a function. Specifying a particular - function via `{"name": "my_function"}` forces the model to call that function. - "none" is the default when no functions are present. "auto" is the default if + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via `{"name": "my_function"}` forces the model to call that + function. + + `none` is the default when no functions are present. `auto`` is the default if functions are present. - functions: A list of functions the model may generate JSON inputs for. + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the + Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or @@ -378,6 +456,15 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + response_format: An object specifying the format that the model must output. Used to enable JSON + mode. + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -386,6 +473,20 @@ def create( We generally recommend altering this or `top_p` but not both. + tool_choice: Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via + `{"type: "function", "function": {"name": "my_function"}}` forces the model to + call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. Use this to provide a list of functions the model may generate JSON inputs + for. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -434,9 +535,13 @@ def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -459,9 +564,13 @@ def create( "max_tokens": max_tokens, "n": n, "presence_penalty": presence_penalty, + "response_format": response_format, + "seed": seed, "stop": stop, "stream": stream, "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, "top_p": top_p, "user": user, }, @@ -511,9 +620,13 @@ async def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -540,18 +653,24 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - function_call: Controls how the model calls functions. "none" means the model will not call a - function and instead generates a message. "auto" means the model can pick - between generating a message or calling a function. Specifying a particular - function via `{"name": "my_function"}` forces the model to call that function. - "none" is the default when no functions are present. "auto" is the default if + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via `{"name": "my_function"}` forces the model to call that + function. + + `none` is the default when no functions are present. `auto`` is the default if functions are present. - functions: A list of functions the model may generate JSON inputs for. + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the + Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or @@ -573,6 +692,15 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + response_format: An object specifying the format that the model must output. Used to enable JSON + mode. + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be @@ -588,6 +716,20 @@ async def create( We generally recommend altering this or `top_p` but not both. + tool_choice: Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via + `{"type: "function", "function": {"name": "my_function"}}` forces the model to + call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. Use this to provide a list of functions the model may generate JSON inputs + for. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -637,8 +779,12 @@ async def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -672,18 +818,24 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - function_call: Controls how the model calls functions. "none" means the model will not call a - function and instead generates a message. "auto" means the model can pick - between generating a message or calling a function. Specifying a particular - function via `{"name": "my_function"}` forces the model to call that function. - "none" is the default when no functions are present. "auto" is the default if + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via `{"name": "my_function"}` forces the model to call that + function. + + `none` is the default when no functions are present. `auto`` is the default if functions are present. - functions: A list of functions the model may generate JSON inputs for. + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the + Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or @@ -705,6 +857,15 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + response_format: An object specifying the format that the model must output. Used to enable JSON + mode. + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -713,6 +874,20 @@ async def create( We generally recommend altering this or `top_p` but not both. + tool_choice: Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via + `{"type: "function", "function": {"name": "my_function"}}` forces the model to + call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. Use this to provide a list of functions the model may generate JSON inputs + for. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -762,8 +937,12 @@ async def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -797,18 +976,24 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - function_call: Controls how the model calls functions. "none" means the model will not call a - function and instead generates a message. "auto" means the model can pick - between generating a message or calling a function. Specifying a particular - function via `{"name": "my_function"}` forces the model to call that function. - "none" is the default when no functions are present. "auto" is the default if + function_call: Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via `{"name": "my_function"}` forces the model to call that + function. + + `none` is the default when no functions are present. `auto`` is the default if functions are present. - functions: A list of functions the model may generate JSON inputs for. + functions: Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the + Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or @@ -830,6 +1015,15 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + response_format: An object specifying the format that the model must output. Used to enable JSON + mode. + + seed: This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -838,6 +1032,20 @@ async def create( We generally recommend altering this or `top_p` but not both. + tool_choice: Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via + `{"type: "function", "function": {"name": "my_function"}}` forces the model to + call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + + tools: A list of tools the model may call. Currently, only functions are supported as a + tool. Use this to provide a list of functions the model may generate JSON inputs + for. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -886,9 +1094,13 @@ async def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -911,9 +1123,13 @@ async def create( "max_tokens": max_tokens, "n": n, "presence_penalty": presence_penalty, + "response_format": response_format, + "seed": seed, "stop": stop, "stream": stream, "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, "top_p": top_p, "user": user, }, diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 26a34524c6..f1a938ba9a 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -54,6 +54,7 @@ def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -104,7 +105,7 @@ def create( logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the GPT + Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits @@ -142,6 +143,13 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. @@ -209,6 +217,7 @@ def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -265,7 +274,7 @@ def create( logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the GPT + Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits @@ -303,6 +312,13 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. @@ -363,6 +379,7 @@ def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -419,7 +436,7 @@ def create( logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the GPT + Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits @@ -457,6 +474,13 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. @@ -516,6 +540,7 @@ def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -543,6 +568,7 @@ def create( "max_tokens": max_tokens, "n": n, "presence_penalty": presence_penalty, + "seed": seed, "stop": stop, "stream": stream, "suffix": suffix, @@ -596,6 +622,7 @@ async def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -646,7 +673,7 @@ async def create( logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the GPT + Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits @@ -684,6 +711,13 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. @@ -751,6 +785,7 @@ async def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -807,7 +842,7 @@ async def create( logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the GPT + Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits @@ -845,6 +880,13 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. @@ -905,6 +947,7 @@ async def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -961,7 +1004,7 @@ async def create( logit_bias: Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the GPT + Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits @@ -999,6 +1042,13 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + seed: If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. @@ -1058,6 +1108,7 @@ async def create( max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -1085,6 +1136,7 @@ async def create( "max_tokens": max_tokens, "n": n, "presence_penalty": presence_penalty, + "seed": seed, "stop": stop, "stream": stream, "suffix": suffix, diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index d2e674c942..16d3944a12 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -4,8 +4,9 @@ import time from typing import TYPE_CHECKING, Mapping, cast +from typing_extensions import Literal -from ..types import FileObject, FileDeleted, file_create_params +from ..types import FileObject, FileDeleted, file_list_params, file_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._resource import SyncAPIResource, AsyncAPIResource @@ -30,7 +31,7 @@ def create( self, *, file: FileTypes, - purpose: str, + purpose: Literal["fine-tune", "assistants"], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -40,22 +41,28 @@ def create( ) -> FileObject: """Upload a file that can be used across various endpoints/features. - Currently, the - size of all the files uploaded by one organization can be up to 1 GB. Please - [contact us](https://help.openai.com/) if you need to increase the storage - limit. + The size of + all the files uploaded by one organization can be up to 100 GB. - Args: - file: The file object (not file name) to be uploaded. + The size of individual files for can be a maximum of 512MB. See the + [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to + learn more about the types of files supported. The Fine-tuning API only supports + `.jsonl` files. + + Please [contact us](https://help.openai.com/) if you need to increase these + storage limits. - If the `purpose` is set to "fine-tune", the file will be used for fine-tuning. + Args: + file: The File object (not file name) to be uploaded. purpose: The intended purpose of the uploaded file. Use "fine-tune" for - [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This - allows us to validate the format of the uploaded file is correct for - fine-tuning. + [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and + "assistants" for + [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + [Messages](https://platform.openai.com/docs/api-reference/messages). This allows + us to validate the format of the uploaded file is correct for fine-tuning. extra_headers: Send extra headers @@ -122,6 +129,7 @@ def retrieve( def list( self, *, + purpose: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -129,12 +137,29 @@ def list( extra_body: Body | None = None, timeout: float | None | NotGiven = NOT_GIVEN, ) -> SyncPage[FileObject]: - """Returns a list of files that belong to the user's organization.""" + """ + Returns a list of files that belong to the user's organization. + + Args: + purpose: Only return files with the given purpose. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get_api_list( "/files", page=SyncPage[FileObject], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"purpose": purpose}, file_list_params.FileListParams), ), model=FileObject, ) @@ -237,7 +262,7 @@ async def create( self, *, file: FileTypes, - purpose: str, + purpose: Literal["fine-tune", "assistants"], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -247,22 +272,28 @@ async def create( ) -> FileObject: """Upload a file that can be used across various endpoints/features. - Currently, the - size of all the files uploaded by one organization can be up to 1 GB. Please - [contact us](https://help.openai.com/) if you need to increase the storage - limit. + The size of + all the files uploaded by one organization can be up to 100 GB. - Args: - file: The file object (not file name) to be uploaded. + The size of individual files for can be a maximum of 512MB. See the + [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to + learn more about the types of files supported. The Fine-tuning API only supports + `.jsonl` files. + + Please [contact us](https://help.openai.com/) if you need to increase these + storage limits. - If the `purpose` is set to "fine-tune", the file will be used for fine-tuning. + Args: + file: The File object (not file name) to be uploaded. purpose: The intended purpose of the uploaded file. Use "fine-tune" for - [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This - allows us to validate the format of the uploaded file is correct for - fine-tuning. + [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and + "assistants" for + [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + [Messages](https://platform.openai.com/docs/api-reference/messages). This allows + us to validate the format of the uploaded file is correct for fine-tuning. extra_headers: Send extra headers @@ -329,6 +360,7 @@ async def retrieve( def list( self, *, + purpose: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -336,12 +368,29 @@ def list( extra_body: Body | None = None, timeout: float | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[FileObject, AsyncPage[FileObject]]: - """Returns a list of files that belong to the user's organization.""" + """ + Returns a list of files that belong to the user's organization. + + Args: + purpose: Only return files with the given purpose. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get_api_list( "/files", page=AsyncPage[FileObject], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"purpose": purpose}, file_list_params.FileListParams), ), model=FileObject, ) diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 1fd39b43a6..9d4ae9936a 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Mapping, Optional, cast +from typing import TYPE_CHECKING, Union, Mapping, Optional, cast from typing_extensions import Literal from ..types import ( @@ -34,6 +34,7 @@ def create_variation( self, *, image: FileTypes, + model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, @@ -52,7 +53,11 @@ def create_variation( image: The image to use as the basis for the variation(s). Must be a valid PNG file, less than 4MB, and square. - n: The number of images to generate. Must be between 1 and 10. + model: The model to use for image generation. Only `dall-e-2` is supported at this + time. + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. @@ -75,6 +80,7 @@ def create_variation( body = deepcopy_minimal( { "image": image, + "model": model, "n": n, "response_format": response_format, "size": size, @@ -104,6 +110,7 @@ def edit( image: FileTypes, prompt: str, mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, @@ -129,6 +136,9 @@ def edit( indicate where `image` should be edited. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. + model: The model to use for image generation. Only `dall-e-2` is supported at this + time. + n: The number of images to generate. Must be between 1 and 10. response_format: The format in which the generated images are returned. Must be one of `url` or @@ -154,6 +164,7 @@ def edit( "image": image, "prompt": prompt, "mask": mask, + "model": model, "n": n, "response_format": response_format, "size": size, @@ -181,9 +192,12 @@ def generate( self, *, prompt: str, + model: Union[str, Literal["dall-e-2", "dall-e-3"], None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + quality: Literal["standard", "hd"] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] | NotGiven = NOT_GIVEN, + style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -197,15 +211,28 @@ def generate( Args: prompt: A text description of the desired image(s). The maximum length is 1000 - characters. + characters for `dall-e-2` and 4000 characters for `dall-e-3`. - n: The number of images to generate. Must be between 1 and 10. + model: The model to use for image generation. + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + quality: The quality of the image that will be generated. `hd` creates images with finer + details and greater consistency across the image. This param is only supported + for `dall-e-3`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. size: The size of the generated images. Must be one of `256x256`, `512x512`, or - `1024x1024`. + `1024x1024` for `dall-e-2`. Must be one of `1024x1024`, `1792x1024`, or + `1024x1792` for `dall-e-3` models. + + style: The style of the generated images. Must be one of `vivid` or `natural`. Vivid + causes the model to lean towards generating hyper-real and dramatic images. + Natural causes the model to produce more natural, less hyper-real looking + images. This param is only supported for `dall-e-3`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -224,9 +251,12 @@ def generate( body=maybe_transform( { "prompt": prompt, + "model": model, "n": n, + "quality": quality, "response_format": response_format, "size": size, + "style": style, "user": user, }, image_generate_params.ImageGenerateParams, @@ -249,6 +279,7 @@ async def create_variation( self, *, image: FileTypes, + model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, @@ -267,7 +298,11 @@ async def create_variation( image: The image to use as the basis for the variation(s). Must be a valid PNG file, less than 4MB, and square. - n: The number of images to generate. Must be between 1 and 10. + model: The model to use for image generation. Only `dall-e-2` is supported at this + time. + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. @@ -290,6 +325,7 @@ async def create_variation( body = deepcopy_minimal( { "image": image, + "model": model, "n": n, "response_format": response_format, "size": size, @@ -319,6 +355,7 @@ async def edit( image: FileTypes, prompt: str, mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, @@ -344,6 +381,9 @@ async def edit( indicate where `image` should be edited. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. + model: The model to use for image generation. Only `dall-e-2` is supported at this + time. + n: The number of images to generate. Must be between 1 and 10. response_format: The format in which the generated images are returned. Must be one of `url` or @@ -369,6 +409,7 @@ async def edit( "image": image, "prompt": prompt, "mask": mask, + "model": model, "n": n, "response_format": response_format, "size": size, @@ -396,9 +437,12 @@ async def generate( self, *, prompt: str, + model: Union[str, Literal["dall-e-2", "dall-e-3"], None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + quality: Literal["standard", "hd"] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] | NotGiven = NOT_GIVEN, + style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -412,15 +456,28 @@ async def generate( Args: prompt: A text description of the desired image(s). The maximum length is 1000 - characters. + characters for `dall-e-2` and 4000 characters for `dall-e-3`. - n: The number of images to generate. Must be between 1 and 10. + model: The model to use for image generation. + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + quality: The quality of the image that will be generated. `hd` creates images with finer + details and greater consistency across the image. This param is only supported + for `dall-e-3`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. size: The size of the generated images. Must be one of `256x256`, `512x512`, or - `1024x1024`. + `1024x1024` for `dall-e-2`. Must be one of `1024x1024`, `1792x1024`, or + `1024x1792` for `dall-e-3` models. + + style: The style of the generated images. Must be one of `vivid` or `natural`. Vivid + causes the model to lean towards generating hyper-real and dramatic images. + Natural causes the model to produce more natural, less hyper-real looking + images. This param is only supported for `dall-e-3`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -439,9 +496,12 @@ async def generate( body=maybe_transform( { "prompt": prompt, + "model": model, "n": n, + "quality": quality, "response_format": response_format, "size": size, + "style": style, "user": user, }, image_generate_params.ImageGenerateParams, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index defaf13446..8f21480d5e 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -16,6 +16,7 @@ from .fine_tune_event import FineTuneEvent as FineTuneEvent from .images_response import ImagesResponse as ImagesResponse from .completion_usage import CompletionUsage as CompletionUsage +from .file_list_params import FileListParams as FileListParams from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams from .edit_create_params import EditCreateParams as EditCreateParams diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index 469bc6f25b..83afa060f8 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -4,6 +4,7 @@ from .translation import Translation as Translation from .transcription import Transcription as Transcription +from .speech_create_params import SpeechCreateParams as SpeechCreateParams from .translation_create_params import ( TranslationCreateParams as TranslationCreateParams, ) diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py new file mode 100644 index 0000000000..06bea01746 --- /dev/null +++ b/src/openai/types/audio/speech_create_params.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SpeechCreateParams"] + + +class SpeechCreateParams(TypedDict, total=False): + input: Required[str] + """The text to generate audio for. The maximum length is 4096 characters.""" + + model: Required[Union[str, Literal["tts-1", "tts-1-hd"]]] + """ + One of the available [TTS models](https://platform.openai.com/docs/models/tts): + `tts-1` or `tts-1-hd` + """ + + voice: Required[Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]] + """The voice to use when generating the audio. + + Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. + """ + + response_format: Literal["mp3", "opus", "aac", "flac"] + """The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`.""" + + speed: float + """The speed of the generated audio. + + Select a value from `0.25` to `4.0`. `1.0` is the default. + """ diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index f8f193484a..7bd70d7b48 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -38,8 +38,8 @@ class TranscriptionCreateParams(TypedDict, total=False): response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] """ - The format of the transcript output, in one of these options: json, text, srt, - verbose_json, or vtt. + The format of the transcript output, in one of these options: `json`, `text`, + `srt`, `verbose_json`, or `vtt`. """ temperature: float diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py index bfa5fc56d2..d3cb4b9e63 100644 --- a/src/openai/types/audio/translation_create_params.py +++ b/src/openai/types/audio/translation_create_params.py @@ -30,8 +30,8 @@ class TranslationCreateParams(TypedDict, total=False): response_format: str """ - The format of the transcript output, in one of these options: json, text, srt, - verbose_json, or vtt. + The format of the transcript output, in one of these options: `json`, `text`, + `srt`, `verbose_json`, or `vtt`. """ temperature: float diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py new file mode 100644 index 0000000000..8b834f286d --- /dev/null +++ b/src/openai/types/beta/__init__.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .thread import Thread as Thread +from .assistant import Assistant as Assistant +from .thread_deleted import ThreadDeleted as ThreadDeleted +from .asssitant_deleted import AsssitantDeleted as AsssitantDeleted +from .thread_create_params import ThreadCreateParams as ThreadCreateParams +from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams +from .assistant_list_params import AssistantListParams as AssistantListParams +from .assistant_create_params import AssistantCreateParams as AssistantCreateParams +from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams +from .thread_create_and_run_params import ( + ThreadCreateAndRunParams as ThreadCreateAndRunParams, +) diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py new file mode 100644 index 0000000000..9130b60363 --- /dev/null +++ b/src/openai/types/beta/assistant.py @@ -0,0 +1,112 @@ +# File generated from our OpenAPI spec by Stainless. + +import builtins +from typing import Dict, List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetreival", "ToolFunction", "ToolFunctionFunction"] + + +class ToolCodeInterpreter(BaseModel): + type: Literal["code_interpreter"] + """The type of tool being defined: `code_interpreter`""" + + +class ToolRetreival(BaseModel): + type: Literal["retreival"] + """The type of tool being defined: `retreival`""" + + +class ToolFunctionFunction(BaseModel): + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + name: str + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Dict[str, builtins.object] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + +class ToolFunction(BaseModel): + function: ToolFunctionFunction + """The function definition.""" + + type: Literal["function"] + """The type of tool being defined: `function`""" + + +Tool = Union[ToolCodeInterpreter, ToolRetreival, ToolFunction] + + +class Assistant(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the assistant was created.""" + + description: Optional[str] + """The description of the assistant. The maximum length is 512 characters.""" + + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs + attached to this assistant. There can be a maximum of 20 files attached to the + assistant. Files are ordered by their creation date in ascending order. + """ + + instructions: Optional[str] + """The system instructions that the assistant uses. + + The maximum length is 32768 characters. + """ + + metadata: Optional[builtins.object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + model: str + """ID of the model to use. + + You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + """ + + name: Optional[str] + """The name of the assistant. The maximum length is 256 characters.""" + + object: Literal["assistant"] + """The object type, which is always `assistant`.""" + + tools: List[Tool] + """A list of tool enabled on the assistant. + + There can be a maximum of 128 tools per assistant. Tools can be of types + `code_interpreter`, `retrieval`, or `function`. + """ diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py new file mode 100644 index 0000000000..8b8f025c39 --- /dev/null +++ b/src/openai/types/beta/assistant_create_params.py @@ -0,0 +1,109 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "AssistantCreateParams", + "Tool", + "ToolAssistantToolsCode", + "ToolAssistantToolsRetrieval", + "ToolAssistantToolsFunction", + "ToolAssistantToolsFunctionFunction", +] + + +class AssistantCreateParams(TypedDict, total=False): + model: Required[str] + """ID of the model to use. + + You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + """ + + description: Optional[str] + """The description of the assistant. The maximum length is 512 characters.""" + + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs + attached to this assistant. There can be a maximum of 20 files attached to the + assistant. Files are ordered by their creation date in ascending order. + """ + + instructions: Optional[str] + """The system instructions that the assistant uses. + + The maximum length is 32768 characters. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + name: Optional[str] + """The name of the assistant. The maximum length is 256 characters.""" + + tools: List[Tool] + """A list of tool enabled on the assistant. + + There can be a maximum of 128 tools per assistant. Tools can be of types + `code_interpreter`, `retrieval`, or `function`. + """ + + +class ToolAssistantToolsCode(TypedDict, total=False): + type: Required[Literal["code_interpreter"]] + """The type of tool being defined: `code_interpreter`""" + + +class ToolAssistantToolsRetrieval(TypedDict, total=False): + type: Required[Literal["retreival"]] + """The type of tool being defined: `retreival`""" + + +class ToolAssistantToolsFunctionFunction(TypedDict, total=False): + description: Required[str] + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Required[Dict[str, object]] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + +class ToolAssistantToolsFunction(TypedDict, total=False): + function: Required[ToolAssistantToolsFunctionFunction] + """The function definition.""" + + type: Required[Literal["function"]] + """The type of tool being defined: `function`""" + + +Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] diff --git a/src/openai/types/beta/assistant_list_params.py b/src/openai/types/beta/assistant_list_params.py new file mode 100644 index 0000000000..b2d794a43a --- /dev/null +++ b/src/openai/types/beta/assistant_list_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["AssistantListParams"] + + +class AssistantListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py new file mode 100644 index 0000000000..fa838f51e3 --- /dev/null +++ b/src/openai/types/beta/assistant_update_params.py @@ -0,0 +1,111 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "AssistantUpdateParams", + "Tool", + "ToolAssistantToolsCode", + "ToolAssistantToolsRetrieval", + "ToolAssistantToolsFunction", + "ToolAssistantToolsFunctionFunction", +] + + +class AssistantUpdateParams(TypedDict, total=False): + description: Optional[str] + """The description of the assistant. The maximum length is 512 characters.""" + + file_ids: List[str] + """ + A list of [File](https://platform.openai.com/docs/api-reference/files) IDs + attached to this assistant. There can be a maximum of 20 files attached to the + assistant. Files are ordered by their creation date in ascending order. If a + file was previosuly attached to the list but does not show up in the list, it + will be deleted from the assistant. + """ + + instructions: Optional[str] + """The system instructions that the assistant uses. + + The maximum length is 32768 characters. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + model: str + """ID of the model to use. + + You can use the + [List models](https://platform.openai.com/docs/api-reference/models/list) API to + see all of your available models, or see our + [Model overview](https://platform.openai.com/docs/models/overview) for + descriptions of them. + """ + + name: Optional[str] + """The name of the assistant. The maximum length is 256 characters.""" + + tools: List[Tool] + """A list of tool enabled on the assistant. + + There can be a maximum of 128 tools per assistant. Tools can be of types + `code_interpreter`, `retrieval`, or `function`. + """ + + +class ToolAssistantToolsCode(TypedDict, total=False): + type: Required[Literal["code_interpreter"]] + """The type of tool being defined: `code_interpreter`""" + + +class ToolAssistantToolsRetrieval(TypedDict, total=False): + type: Required[Literal["retreival"]] + """The type of tool being defined: `retreival`""" + + +class ToolAssistantToolsFunctionFunction(TypedDict, total=False): + description: Required[str] + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Required[Dict[str, object]] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + +class ToolAssistantToolsFunction(TypedDict, total=False): + function: Required[ToolAssistantToolsFunctionFunction] + """The function definition.""" + + type: Required[Literal["function"]] + """The type of tool being defined: `function`""" + + +Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] diff --git a/src/openai/types/beta/assistants/__init__.py b/src/openai/types/beta/assistants/__init__.py new file mode 100644 index 0000000000..9dbb3e2b8b --- /dev/null +++ b/src/openai/types/beta/assistants/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .assistant_file import AssistantFile as AssistantFile +from .file_list_params import FileListParams as FileListParams +from .file_create_params import FileCreateParams as FileCreateParams +from .file_delete_response import FileDeleteResponse as FileDeleteResponse diff --git a/src/openai/types/beta/assistants/assistant_file.py b/src/openai/types/beta/assistants/assistant_file.py new file mode 100644 index 0000000000..1d1573ac0f --- /dev/null +++ b/src/openai/types/beta/assistants/assistant_file.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["AssistantFile"] + + +class AssistantFile(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + assistant_id: str + """The assistant ID that the file is attached to.""" + + created_at: int + """The Unix timestamp (in seconds) for when the assistant file was created.""" + + object: Literal["assistant.file"] + """The object type, which is always `assistant.file`.""" diff --git a/src/openai/types/beta/assistants/file_create_params.py b/src/openai/types/beta/assistants/file_create_params.py new file mode 100644 index 0000000000..f70f96fc1b --- /dev/null +++ b/src/openai/types/beta/assistants/file_create_params.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["FileCreateParams"] + + +class FileCreateParams(TypedDict, total=False): + file_id: Required[str] + """ + A [File](https://platform.openai.com/docs/api-reference/files) ID (with + `purpose="assistants"`) that the assistant should use. Useful for tools like + `retrieval` and `code_interpreter` that can access files. + """ diff --git a/src/openai/types/beta/assistants/file_delete_response.py b/src/openai/types/beta/assistants/file_delete_response.py new file mode 100644 index 0000000000..52c138feda --- /dev/null +++ b/src/openai/types/beta/assistants/file_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FileDeleteResponse"] + + +class FileDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["assistant.file.deleted"] diff --git a/src/openai/types/beta/assistants/file_list_params.py b/src/openai/types/beta/assistants/file_list_params.py new file mode 100644 index 0000000000..397e35a0d1 --- /dev/null +++ b/src/openai/types/beta/assistants/file_list_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/beta/asssitant_deleted.py b/src/openai/types/beta/asssitant_deleted.py new file mode 100644 index 0000000000..258210e7fe --- /dev/null +++ b/src/openai/types/beta/asssitant_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AsssitantDeleted"] + + +class AsssitantDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["assistant.deleted"] diff --git a/src/openai/types/beta/chat/__init__.py b/src/openai/types/beta/chat/__init__.py new file mode 100644 index 0000000000..b2f53e3525 --- /dev/null +++ b/src/openai/types/beta/chat/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py new file mode 100644 index 0000000000..a340bffd60 --- /dev/null +++ b/src/openai/types/beta/thread.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. + +import builtins +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Thread"] + + +class Thread(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the thread was created.""" + + metadata: Optional[builtins.object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + object: Literal["thread"] + """The object type, which is always `thread`.""" diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py new file mode 100644 index 0000000000..2955343ec0 --- /dev/null +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -0,0 +1,148 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "ThreadCreateAndRunParams", + "Thread", + "ThreadMessage", + "Tool", + "ToolAssistantToolsCode", + "ToolAssistantToolsRetrieval", + "ToolAssistantToolsFunction", + "ToolAssistantToolsFunctionFunction", +] + + +class ThreadCreateAndRunParams(TypedDict, total=False): + assistant_id: Required[str] + """ + The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + """ + + instructions: Optional[str] + """Override the default system message of the assistant. + + This is useful for modifying the behavior on a per-run basis. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + model: Optional[str] + """ + The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + """ + + thread: Thread + """If no thread is provided, an empty thread will be created.""" + + tools: Optional[List[Tool]] + """Override the tools the assistant can use for this run. + + This is useful for modifying the behavior on a per-run basis. + """ + + +class ThreadMessage(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[Literal["user"]] + """The role of the entity that is creating the message. + + Currently only `user` is supported. + """ + + file_ids: List[str] + """ + A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the message should use. There can be a maximum of 10 files attached to a + message. Useful for tools like `retrieval` and `code_interpreter` that can + access and use files. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + +class Thread(TypedDict, total=False): + messages: List[ThreadMessage] + """ + A list of [messages](https://platform.openai.com/docs/api-reference/messages) to + start the thread with. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + +class ToolAssistantToolsCode(TypedDict, total=False): + type: Required[Literal["code_interpreter"]] + """The type of tool being defined: `code_interpreter`""" + + +class ToolAssistantToolsRetrieval(TypedDict, total=False): + type: Required[Literal["retreival"]] + """The type of tool being defined: `retreival`""" + + +class ToolAssistantToolsFunctionFunction(TypedDict, total=False): + description: Required[str] + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Required[Dict[str, object]] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + +class ToolAssistantToolsFunction(TypedDict, total=False): + function: Required[ToolAssistantToolsFunctionFunction] + """The function definition.""" + + type: Required[Literal["function"]] + """The type of tool being defined: `function`""" + + +Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py new file mode 100644 index 0000000000..d2ec78bbc3 --- /dev/null +++ b/src/openai/types/beta/thread_create_params.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ThreadCreateParams", "Message"] + + +class ThreadCreateParams(TypedDict, total=False): + messages: List[Message] + """ + A list of [messages](https://platform.openai.com/docs/api-reference/messages) to + start the thread with. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + +class Message(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[Literal["user"]] + """The role of the entity that is creating the message. + + Currently only `user` is supported. + """ + + file_ids: List[str] + """ + A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the message should use. There can be a maximum of 10 files attached to a + message. Useful for tools like `retrieval` and `code_interpreter` that can + access and use files. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ diff --git a/src/openai/types/beta/thread_deleted.py b/src/openai/types/beta/thread_deleted.py new file mode 100644 index 0000000000..410ac1aea0 --- /dev/null +++ b/src/openai/types/beta/thread_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ThreadDeleted"] + + +class ThreadDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["thread.deleted"] diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py new file mode 100644 index 0000000000..6c1d32fc57 --- /dev/null +++ b/src/openai/types/beta/thread_update_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["ThreadUpdateParams"] + + +class ThreadUpdateParams(TypedDict, total=False): + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py new file mode 100644 index 0000000000..0cb557a514 --- /dev/null +++ b/src/openai/types/beta/threads/__init__.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .run import Run as Run +from .thread_message import ThreadMessage as ThreadMessage +from .run_list_params import RunListParams as RunListParams +from .run_create_params import RunCreateParams as RunCreateParams +from .run_update_params import RunUpdateParams as RunUpdateParams +from .message_list_params import MessageListParams as MessageListParams +from .message_content_text import MessageContentText as MessageContentText +from .message_create_params import MessageCreateParams as MessageCreateParams +from .message_update_params import MessageUpdateParams as MessageUpdateParams +from .message_content_image_file import ( + MessageContentImageFile as MessageContentImageFile, +) +from .run_submit_tool_outputs_params import ( + RunSubmitToolOutputsParams as RunSubmitToolOutputsParams, +) +from .required_action_function_tool_call import ( + RequiredActionFunctionToolCall as RequiredActionFunctionToolCall, +) diff --git a/src/openai/types/beta/threads/message_content_image_file.py b/src/openai/types/beta/threads/message_content_image_file.py new file mode 100644 index 0000000000..eeba5a633c --- /dev/null +++ b/src/openai/types/beta/threads/message_content_image_file.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["MessageContentImageFile", "ImageFile"] + + +class ImageFile(BaseModel): + file_id: str + """ + The [File](https://platform.openai.com/docs/api-reference/files) ID of the image + in the message content. + """ + + +class MessageContentImageFile(BaseModel): + image_file: ImageFile + + type: Literal["image_file"] + """Always `image_file`.""" diff --git a/src/openai/types/beta/threads/message_content_text.py b/src/openai/types/beta/threads/message_content_text.py new file mode 100644 index 0000000000..b529a384c6 --- /dev/null +++ b/src/openai/types/beta/threads/message_content_text.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Union +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = [ + "MessageContentText", + "Text", + "TextAnnotation", + "TextAnnotationFileCitation", + "TextAnnotationFileCitationFileCitation", + "TextAnnotationFilePath", + "TextAnnotationFilePathFilePath", +] + + +class TextAnnotationFileCitationFileCitation(BaseModel): + file_id: str + """The ID of the specific File the citation is from.""" + + quote: str + """The specific quote in the file.""" + + +class TextAnnotationFileCitation(BaseModel): + end_index: int + + file_citation: TextAnnotationFileCitationFileCitation + + start_index: int + + text: str + """The text in the message content that needs to be replaced.""" + + type: Literal["file_citation"] + """Always `file_citation`.""" + + +class TextAnnotationFilePathFilePath(BaseModel): + file_id: str + """The ID of the file that was generated.""" + + +class TextAnnotationFilePath(BaseModel): + end_index: int + + file_path: TextAnnotationFilePathFilePath + + start_index: int + + text: str + """The text in the message content that needs to be replaced.""" + + type: Literal["file_path"] + """Always `file_path`.""" + + +TextAnnotation = Union[TextAnnotationFileCitation, TextAnnotationFilePath] + + +class Text(BaseModel): + annotations: List[TextAnnotation] + + value: str + """The data that makes up the text.""" + + +class MessageContentText(BaseModel): + text: Text + + type: Literal["text"] + """Always `text`.""" diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py new file mode 100644 index 0000000000..8733f10b8a --- /dev/null +++ b/src/openai/types/beta/threads/message_create_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["MessageCreateParams"] + + +class MessageCreateParams(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[Literal["user"]] + """The role of the entity that is creating the message. + + Currently only `user` is supported. + """ + + file_ids: List[str] + """ + A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the message should use. There can be a maximum of 10 files attached to a + message. Useful for tools like `retrieval` and `code_interpreter` that can + access and use files. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ diff --git a/src/openai/types/beta/threads/message_list_params.py b/src/openai/types/beta/threads/message_list_params.py new file mode 100644 index 0000000000..31e407bb22 --- /dev/null +++ b/src/openai/types/beta/threads/message_list_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["MessageListParams"] + + +class MessageListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/beta/threads/message_update_params.py b/src/openai/types/beta/threads/message_update_params.py new file mode 100644 index 0000000000..2e3e1b4b1a --- /dev/null +++ b/src/openai/types/beta/threads/message_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["MessageUpdateParams"] + + +class MessageUpdateParams(TypedDict, total=False): + thread_id: Required[str] + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ diff --git a/src/openai/types/beta/threads/messages/__init__.py b/src/openai/types/beta/threads/messages/__init__.py new file mode 100644 index 0000000000..6046f68204 --- /dev/null +++ b/src/openai/types/beta/threads/messages/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .message_file import MessageFile as MessageFile +from .file_list_params import FileListParams as FileListParams diff --git a/src/openai/types/beta/threads/messages/file_list_params.py b/src/openai/types/beta/threads/messages/file_list_params.py new file mode 100644 index 0000000000..3640b8508b --- /dev/null +++ b/src/openai/types/beta/threads/messages/file_list_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + thread_id: Required[str] + + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/beta/threads/messages/message_file.py b/src/openai/types/beta/threads/messages/message_file.py new file mode 100644 index 0000000000..5332dee962 --- /dev/null +++ b/src/openai/types/beta/threads/messages/message_file.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["MessageFile"] + + +class MessageFile(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the message file was created.""" + + message_id: str + """ + The ID of the [message](https://platform.openai.com/docs/api-reference/messages) + that the [File](https://platform.openai.com/docs/api-reference/files) is + attached to. + """ + + object: Literal["thread.message.file"] + """The object type, which is always `thread.message.file`.""" diff --git a/src/openai/types/beta/threads/required_action_function_tool_call.py b/src/openai/types/beta/threads/required_action_function_tool_call.py new file mode 100644 index 0000000000..0284d0f188 --- /dev/null +++ b/src/openai/types/beta/threads/required_action_function_tool_call.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RequiredActionFunctionToolCall", "Function"] + + +class Function(BaseModel): + arguments: str + """The arguments that the model expects you to pass to the function.""" + + name: str + """The name of the function.""" + + +class RequiredActionFunctionToolCall(BaseModel): + id: str + """The ID of the tool call. + + This ID must be referenced when you submit the tool outputs in using the + [Submit tool outputs to run](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs) + endpoint. + """ + + function: Function + """The function definition.""" + + type: Literal["function"] + """The type of tool call the output is required for. + + For now, this is always `function`. + """ diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py new file mode 100644 index 0000000000..d06152fa5b --- /dev/null +++ b/src/openai/types/beta/threads/run.py @@ -0,0 +1,182 @@ +# File generated from our OpenAPI spec by Stainless. + +import builtins +from typing import Dict, List, Union, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .required_action_function_tool_call import RequiredActionFunctionToolCall + +__all__ = [ + "Run", + "LastError", + "RequiredAction", + "RequiredActionSubmitToolOutputs", + "Tool", + "ToolAssistantToolsCode", + "ToolAssistantToolsRetrieval", + "ToolAssistantToolsFunction", + "ToolAssistantToolsFunctionFunction", +] + + +class LastError(BaseModel): + code: Literal["server_error", "rate_limit_exceeded"] + """One of `server_error` or `rate_limit_exceeded`.""" + + message: str + """A human-readable description of the error.""" + + +class RequiredActionSubmitToolOutputs(BaseModel): + tool_calls: List[RequiredActionFunctionToolCall] + """A list of the relevant tool calls.""" + + +class RequiredAction(BaseModel): + submit_tool_outputs: RequiredActionSubmitToolOutputs + """Details on the tool outputs needed for this run to continue.""" + + type: Literal["submit_tool_outputs"] + """For now, this is always `submit_tool_outputs`.""" + + +class ToolAssistantToolsCode(BaseModel): + type: Literal["code_interpreter"] + """The type of tool being defined: `code_interpreter`""" + + +class ToolAssistantToolsRetrieval(BaseModel): + type: Literal["retreival"] + """The type of tool being defined: `retreival`""" + + +class ToolAssistantToolsFunctionFunction(BaseModel): + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + name: str + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Dict[str, builtins.object] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + +class ToolAssistantToolsFunction(BaseModel): + function: ToolAssistantToolsFunctionFunction + """The function definition.""" + + type: Literal["function"] + """The type of tool being defined: `function`""" + + +Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] + + +class Run(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + assistant_id: str + """ + The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) used for + execution of this run. + """ + + cancelled_at: Optional[int] + """The Unix timestamp (in seconds) for when the run was cancelled.""" + + completed_at: Optional[int] + """The Unix timestamp (in seconds) for when the run was completed.""" + + created_at: int + """The Unix timestamp (in seconds) for when the run was created.""" + + expires_at: int + """The Unix timestamp (in seconds) for when the run will expire.""" + + failed_at: Optional[int] + """The Unix timestamp (in seconds) for when the run failed.""" + + file_ids: List[str] + """ + The list of [File](https://platform.openai.com/docs/api-reference/files) IDs the + [assistant](https://platform.openai.com/docs/api-reference/assistants) used for + this run. + """ + + instructions: str + """ + The instructions that the + [assistant](https://platform.openai.com/docs/api-reference/assistants) used for + this run. + """ + + last_error: Optional[LastError] + """The last error associated with this run. Will be `null` if there are no errors.""" + + metadata: Optional[builtins.object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + model: str + """ + The model that the + [assistant](https://platform.openai.com/docs/api-reference/assistants) used for + this run. + """ + + object: Literal["assistant.run"] + """The object type, which is always `assistant.run`.""" + + required_action: Optional[RequiredAction] + """Details on the action required to continue the run. + + Will be `null` if no action is required. + """ + + started_at: Optional[int] + """The Unix timestamp (in seconds) for when the run was started.""" + + status: Literal[ + "queued", "in_progress", "requires_action", "cancelling", "cancelled", "failed", "completed", "expired" + ] + """ + The status of the run, which can be either `queued`, `in_progress`, + `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, or + `expired`. + """ + + thread_id: str + """ + The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) + that was executed on as a part of this run. + """ + + tools: List[Tool] + """ + The list of tools that the + [assistant](https://platform.openai.com/docs/api-reference/assistants) used for + this run. + """ diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py new file mode 100644 index 0000000000..41d2eeea03 --- /dev/null +++ b/src/openai/types/beta/threads/run_create_params.py @@ -0,0 +1,100 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "RunCreateParams", + "Tool", + "ToolAssistantToolsCode", + "ToolAssistantToolsRetrieval", + "ToolAssistantToolsFunction", + "ToolAssistantToolsFunctionFunction", +] + + +class RunCreateParams(TypedDict, total=False): + assistant_id: Required[str] + """ + The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + """ + + instructions: Optional[str] + """Override the default system message of the assistant. + + This is useful for modifying the behavior on a per-run basis. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + model: Optional[str] + """ + The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + """ + + tools: Optional[List[Tool]] + """Override the tools the assistant can use for this run. + + This is useful for modifying the behavior on a per-run basis. + """ + + +class ToolAssistantToolsCode(TypedDict, total=False): + type: Required[Literal["code_interpreter"]] + """The type of tool being defined: `code_interpreter`""" + + +class ToolAssistantToolsRetrieval(TypedDict, total=False): + type: Required[Literal["retreival"]] + """The type of tool being defined: `retreival`""" + + +class ToolAssistantToolsFunctionFunction(TypedDict, total=False): + description: Required[str] + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Required[Dict[str, object]] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + +class ToolAssistantToolsFunction(TypedDict, total=False): + function: Required[ToolAssistantToolsFunctionFunction] + """The function definition.""" + + type: Required[Literal["function"]] + """The type of tool being defined: `function`""" + + +Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] diff --git a/src/openai/types/beta/threads/run_list_params.py b/src/openai/types/beta/threads/run_list_params.py new file mode 100644 index 0000000000..5f41347718 --- /dev/null +++ b/src/openai/types/beta/threads/run_list_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RunListParams"] + + +class RunListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py new file mode 100644 index 0000000000..a960f0f06f --- /dev/null +++ b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, TypedDict + +__all__ = ["RunSubmitToolOutputsParams", "ToolOutput"] + + +class RunSubmitToolOutputsParams(TypedDict, total=False): + thread_id: Required[str] + + tool_outputs: Required[List[ToolOutput]] + """A list of tools for which the outputs are being submitted.""" + + +class ToolOutput(TypedDict, total=False): + output: str + """The output of the tool call to be submitted to continue the run.""" + + tool_call_id: str + """ + The ID of the tool call in the `required_action` object within the run object + the output is being submitted for. + """ diff --git a/src/openai/types/beta/threads/run_update_params.py b/src/openai/types/beta/threads/run_update_params.py new file mode 100644 index 0000000000..09f81aa003 --- /dev/null +++ b/src/openai/types/beta/threads/run_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["RunUpdateParams"] + + +class RunUpdateParams(TypedDict, total=False): + thread_id: Required[str] + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py new file mode 100644 index 0000000000..72b972a986 --- /dev/null +++ b/src/openai/types/beta/threads/runs/__init__.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from .run_step import RunStep as RunStep +from .code_tool_call import CodeToolCall as CodeToolCall +from .step_list_params import StepListParams as StepListParams +from .function_tool_call import FunctionToolCall as FunctionToolCall +from .retrieval_tool_call import RetrievalToolCall as RetrievalToolCall +from .tool_calls_step_details import ToolCallsStepDetails as ToolCallsStepDetails +from .message_creation_step_details import ( + MessageCreationStepDetails as MessageCreationStepDetails, +) diff --git a/src/openai/types/beta/threads/runs/code_tool_call.py b/src/openai/types/beta/threads/runs/code_tool_call.py new file mode 100644 index 0000000000..f808005ecb --- /dev/null +++ b/src/openai/types/beta/threads/runs/code_tool_call.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Union +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = [ + "CodeToolCall", + "CodeInterpreter", + "CodeInterpreterOutput", + "CodeInterpreterOutputLogs", + "CodeInterpreterOutputImage", + "CodeInterpreterOutputImageImage", +] + + +class CodeInterpreterOutputLogs(BaseModel): + logs: str + """The text output from the Code Interpreter tool call.""" + + type: Literal["logs"] + """Always `logs`.""" + + +class CodeInterpreterOutputImageImage(BaseModel): + file_id: str + """ + The [file](https://platform.openai.com/docs/api-reference/files) ID of the + image. + """ + + +class CodeInterpreterOutputImage(BaseModel): + image: CodeInterpreterOutputImageImage + + type: Literal["image"] + """Always `image`.""" + + +CodeInterpreterOutput = Union[CodeInterpreterOutputLogs, CodeInterpreterOutputImage] + + +class CodeInterpreter(BaseModel): + input: str + """The input to the Code Interpreter tool call.""" + + outputs: List[CodeInterpreterOutput] + """The outputs from the Code Interpreter tool call. + + Code Interpreter can output one or more items, including text (`logs`) or images + (`image`). Each of these are represented by a different object type. + """ + + +class CodeToolCall(BaseModel): + id: str + """The ID of the tool call.""" + + code_interpreter: CodeInterpreter + """The Code Interpreter tool call definition.""" + + type: Literal["code_interpreter"] + """The type of tool call. + + This is always going to be `code_interpreter` for this type of tool call. + """ diff --git a/src/openai/types/beta/threads/runs/function_tool_call.py b/src/openai/types/beta/threads/runs/function_tool_call.py new file mode 100644 index 0000000000..f4cf8bbdd0 --- /dev/null +++ b/src/openai/types/beta/threads/runs/function_tool_call.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["FunctionToolCall", "Function"] + + +class Function(BaseModel): + arguments: str + """The arguments passed to the function.""" + + name: str + """The name of the function.""" + + output: Optional[str] + """The output of the function. + + This will be `null` if the outputs have not been + [submitted](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs) + yet. + """ + + +class FunctionToolCall(BaseModel): + id: str + """The ID of the tool call object.""" + + function: Function + """The definition of the function that was called.""" + + type: Literal["function"] + """The type of tool call. + + This is always going to be `function` for this type of tool call. + """ diff --git a/src/openai/types/beta/threads/runs/message_creation_step_details.py b/src/openai/types/beta/threads/runs/message_creation_step_details.py new file mode 100644 index 0000000000..29f9106ec0 --- /dev/null +++ b/src/openai/types/beta/threads/runs/message_creation_step_details.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["MessageCreationStepDetails", "MessageCreation"] + + +class MessageCreation(BaseModel): + message_id: str + """The ID of the message that was created by this run step.""" + + +class MessageCreationStepDetails(BaseModel): + message_creation: MessageCreation + + type: Literal["message_creation"] + """Always `message_creation``.""" diff --git a/src/openai/types/beta/threads/runs/retrieval_tool_call.py b/src/openai/types/beta/threads/runs/retrieval_tool_call.py new file mode 100644 index 0000000000..6cdbcdd93f --- /dev/null +++ b/src/openai/types/beta/threads/runs/retrieval_tool_call.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["RetrievalToolCall"] + + +class RetrievalToolCall(BaseModel): + id: str + """The ID of the tool call object.""" + + retrieval: object + """For now, this is always going to be an empty object.""" + + type: Literal["retrieval"] + """The type of tool call. + + This is always going to be `retrieval` for this type of tool call. + """ diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py new file mode 100644 index 0000000000..17a567dc0e --- /dev/null +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -0,0 +1,93 @@ +# File generated from our OpenAPI spec by Stainless. + +import builtins +from typing import Union, Optional +from typing_extensions import Literal + +from ....._models import BaseModel +from .tool_calls_step_details import ToolCallsStepDetails +from .message_creation_step_details import MessageCreationStepDetails + +__all__ = ["RunStep", "LastError", "StepDetails"] + + +class LastError(BaseModel): + code: Literal["server_error", "rate_limit_exceeded"] + """One of `server_error` or `rate_limit_exceeded`.""" + + message: str + """A human-readable description of the error.""" + + +StepDetails = Union[MessageCreationStepDetails, ToolCallsStepDetails] + + +class RunStep(BaseModel): + id: str + """The identifier of the run step, which can be referenced in API endpoints.""" + + assistant_id: str + """ + The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) + associated with the run step. + """ + + cancelled_at: Optional[int] + """The Unix timestamp (in seconds) for when the run step was cancelled.""" + + completed_at: Optional[int] + """The Unix timestamp (in seconds) for when the run step completed.""" + + created_at: int + """The Unix timestamp (in seconds) for when the run step was created.""" + + expired_at: Optional[int] + """The Unix timestamp (in seconds) for when the run step expired. + + A step is considered expired if the parent run is expired. + """ + + failed_at: Optional[int] + """The Unix timestamp (in seconds) for when the run step failed.""" + + last_error: Optional[LastError] + """The last error associated with this run step. + + Will be `null` if there are no errors. + """ + + metadata: Optional[builtins.object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + object: Literal["assistant.run.step"] + """The object type, which is always `assistant.run.step``.""" + + run_id: str + """ + The ID of the [run](https://platform.openai.com/docs/api-reference/runs) that + this run step is a part of. + """ + + status: Literal["in_progress", "cancelled", "failed", "completed", "expired"] + """ + The status of the run, which can be either `in_progress`, `cancelled`, `failed`, + `completed`, or `expired`. + """ + + step_details: StepDetails + """The details of the run step.""" + + thread_id: str + """ + The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) + that was run. + """ + + type: Literal["message_creation", "tool_calls"] + """The type of run step, which can be either `message_creation` or `tool_calls`.""" diff --git a/src/openai/types/beta/threads/runs/step_list_params.py b/src/openai/types/beta/threads/runs/step_list_params.py new file mode 100644 index 0000000000..9c7b6c64d0 --- /dev/null +++ b/src/openai/types/beta/threads/runs/step_list_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["StepListParams"] + + +class StepListParams(TypedDict, total=False): + thread_id: Required[str] + + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/beta/threads/runs/tool_calls_step_details.py b/src/openai/types/beta/threads/runs/tool_calls_step_details.py new file mode 100644 index 0000000000..80eb90bf66 --- /dev/null +++ b/src/openai/types/beta/threads/runs/tool_calls_step_details.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Union +from typing_extensions import Literal + +from ....._models import BaseModel +from .code_tool_call import CodeToolCall +from .function_tool_call import FunctionToolCall +from .retrieval_tool_call import RetrievalToolCall + +__all__ = ["ToolCallsStepDetails", "ToolCall"] + +ToolCall = Union[CodeToolCall, RetrievalToolCall, FunctionToolCall] + + +class ToolCallsStepDetails(BaseModel): + tool_calls: List[ToolCall] + """An array of tool calls the run step was involved in. + + These can be associated with one of three types of tools: `code_interpreter`, + `retrieval`, or `function`. + """ + + type: Literal["tool_calls"] + """Always `tool_calls`.""" diff --git a/src/openai/types/beta/threads/thread_message.py b/src/openai/types/beta/threads/thread_message.py new file mode 100644 index 0000000000..0f782ef845 --- /dev/null +++ b/src/openai/types/beta/threads/thread_message.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Stainless. + +import builtins +from typing import List, Union, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .message_content_text import MessageContentText +from .message_content_image_file import MessageContentImageFile + +__all__ = ["ThreadMessage", "Content"] + +Content = Union[MessageContentImageFile, MessageContentText] + + +class ThreadMessage(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + assistant_id: Optional[str] + """ + If applicable, the ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) that + authored this message. + """ + + content: List[Content] + """The content of the message in array of text and/or images.""" + + created_at: int + """The Unix timestamp (in seconds) for when the message was created.""" + + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs that + the assistant should use. Useful for tools like retrieval and code_interpreter + that can access files. A maximum of 10 files can be attached to a message. + """ + + metadata: Optional[builtins.object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + object: Literal["thread.message"] + """The object type, which is always `thread.message`.""" + + role: Literal["user", "assistant"] + """The entity that produced the message. One of `user` or `assistant`.""" + + run_id: Optional[str] + """ + If applicable, the ID of the + [run](https://platform.openai.com/docs/api-reference/runs) associated with the + authoring of this message. + """ + + thread_id: str + """ + The [thread](https://platform.openai.com/docs/api-reference/threads) ID that + this message belongs to. + """ diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 2f23cf3ca4..5fe182f41e 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -7,6 +7,48 @@ from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .chat_completion_tool_param import ( + ChatCompletionToolParam as ChatCompletionToolParam, +) from .chat_completion_message_param import ( ChatCompletionMessageParam as ChatCompletionMessageParam, ) +from .chat_completion_message_tool_call import ( + ChatCompletionMessageToolCall as ChatCompletionMessageToolCall, +) +from .chat_completion_content_part_param import ( + ChatCompletionContentPartParam as ChatCompletionContentPartParam, +) +from .chat_completion_tool_message_param import ( + ChatCompletionToolMessageParam as ChatCompletionToolMessageParam, +) +from .chat_completion_user_message_param import ( + ChatCompletionUserMessageParam as ChatCompletionUserMessageParam, +) +from .chat_completion_system_message_param import ( + ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam, +) +from .chat_completion_function_message_param import ( + ChatCompletionFunctionMessageParam as ChatCompletionFunctionMessageParam, +) +from .chat_completion_assistant_message_param import ( + ChatCompletionAssistantMessageParam as ChatCompletionAssistantMessageParam, +) +from .chat_completion_content_part_text_param import ( + ChatCompletionContentPartTextParam as ChatCompletionContentPartTextParam, +) +from .chat_completion_message_tool_call_param import ( + ChatCompletionMessageToolCallParam as ChatCompletionMessageToolCallParam, +) +from .chat_completion_named_tool_choice_param import ( + ChatCompletionNamedToolChoiceParam as ChatCompletionNamedToolChoiceParam, +) +from .chat_completion_content_part_image_param import ( + ChatCompletionContentPartImageParam as ChatCompletionContentPartImageParam, +) +from .chat_completion_tool_choice_option_param import ( + ChatCompletionToolChoiceOptionParam as ChatCompletionToolChoiceOptionParam, +) +from .chat_completion_function_call_option_param import ( + ChatCompletionFunctionCallOptionParam as ChatCompletionFunctionCallOptionParam, +) diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 8d7a0b9716..da12ee7c07 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -11,13 +11,14 @@ class Choice(BaseModel): - finish_reason: Literal["stop", "length", "function_call", "content_filter"] + finish_reason: Literal["stop", "length", "tool_calls", "content_filter", "function_call"] """The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence, `length` if the maximum number of tokens specified in the request was reached, `content_filter` if content was omitted due to a flag from our content - filters, or `function_call` if the model called a function. + filters, `tool_calls` if the model called a tool, or `function_call` + (deprecated) if the model called a function. """ index: int @@ -43,8 +44,15 @@ class ChatCompletion(BaseModel): model: str """The model used for the chat completion.""" - object: str + object: Literal["chat.completion"] """The object type, which is always `chat.completion`.""" + system_fingerprint: Optional[str] = None + """This fingerprint represents the backend configuration that the model runs with. + + Can be used in conjunction with the `seed` request parameter to understand when + backend changes have been made that might impact determinism. + """ + usage: Optional[CompletionUsage] = None """Usage statistics for the completion request.""" diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py new file mode 100644 index 0000000000..abdd87c991 --- /dev/null +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_message_tool_call_param import ChatCompletionMessageToolCallParam + +__all__ = ["ChatCompletionAssistantMessageParam", "FunctionCall"] + + +class FunctionCall(TypedDict, total=False): + arguments: Required[str] + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Required[str] + """The name of the function to call.""" + + +class ChatCompletionAssistantMessageParam(TypedDict, total=False): + content: Required[Optional[str]] + """The contents of the assistant message.""" + + role: Required[Literal["assistant"]] + """The role of the messages author, in this case `assistant`.""" + + function_call: FunctionCall + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the + model. + """ + + tool_calls: List[ChatCompletionMessageToolCallParam] + """The tool calls generated by the model, such as function calls.""" diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 66610898b4..bbc46a37bb 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -4,9 +4,15 @@ from typing_extensions import Literal from ..._models import BaseModel -from .chat_completion_role import ChatCompletionRole -__all__ = ["ChatCompletionChunk", "Choice", "ChoiceDelta", "ChoiceDeltaFunctionCall"] +__all__ = [ + "ChatCompletionChunk", + "Choice", + "ChoiceDelta", + "ChoiceDeltaFunctionCall", + "ChoiceDeltaToolCall", + "ChoiceDeltaToolCallFunction", +] class ChoiceDeltaFunctionCall(BaseModel): @@ -22,31 +28,60 @@ class ChoiceDeltaFunctionCall(BaseModel): """The name of the function to call.""" +class ChoiceDeltaToolCallFunction(BaseModel): + arguments: Optional[str] = None + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Optional[str] = None + """The name of the function to call.""" + + +class ChoiceDeltaToolCall(BaseModel): + index: int + + id: Optional[str] = None + """The ID of the tool call.""" + + function: Optional[ChoiceDeltaToolCallFunction] = None + + type: Optional[Literal["function"]] = None + """The type of the tool. Currently, only `function` is supported.""" + + class ChoiceDelta(BaseModel): content: Optional[str] = None """The contents of the chunk message.""" function_call: Optional[ChoiceDeltaFunctionCall] = None - """ + """Deprecated and replaced by `tool_calls`. + The name and arguments of a function that should be called, as generated by the model. """ - role: Optional[ChatCompletionRole] = None + role: Optional[Literal["system", "user", "assistant", "tool"]] = None """The role of the author of this message.""" + tool_calls: Optional[List[ChoiceDeltaToolCall]] = None + class Choice(BaseModel): delta: ChoiceDelta """A chat completion delta generated by streamed model responses.""" - finish_reason: Optional[Literal["stop", "length", "function_call", "content_filter"]] + finish_reason: Optional[Literal["stop", "length", "tool_calls", "content_filter", "function_call"]] """The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence, `length` if the maximum number of tokens specified in the request was reached, `content_filter` if content was omitted due to a flag from our content - filters, or `function_call` if the model called a function. + filters, `tool_calls` if the model called a tool, or `function_call` + (deprecated) if the model called a function. """ index: int @@ -72,5 +107,5 @@ class ChatCompletionChunk(BaseModel): model: str """The model to generate the completion.""" - object: str + object: Literal["chat.completion.chunk"] """The object type, which is always `chat.completion.chunk`.""" diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py new file mode 100644 index 0000000000..2051786562 --- /dev/null +++ b/src/openai/types/chat/chat_completion_content_part_image_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionContentPartImageParam", "ImageURL"] + + +class ImageURL(TypedDict, total=False): + detail: Literal["auto", "low", "high"] + """Specifies the detail level of the image.""" + + url: str + """Either a URL of the image or the base64 encoded image data.""" + + +class ChatCompletionContentPartImageParam(TypedDict, total=False): + image_url: Required[ImageURL] + + type: Required[Literal["image_url"]] + """The type of the content part.""" diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py new file mode 100644 index 0000000000..587578e2ef --- /dev/null +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam +from .chat_completion_content_part_image_param import ( + ChatCompletionContentPartImageParam, +) + +__all__ = ["ChatCompletionContentPartParam"] + +ChatCompletionContentPartParam = Union[ChatCompletionContentPartTextParam, ChatCompletionContentPartImageParam] diff --git a/src/openai/types/chat/chat_completion_content_part_text_param.py b/src/openai/types/chat/chat_completion_content_part_text_param.py new file mode 100644 index 0000000000..38edcf054e --- /dev/null +++ b/src/openai/types/chat/chat_completion_content_part_text_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionContentPartTextParam"] + + +class ChatCompletionContentPartTextParam(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["text"]] + """The type of the content part.""" diff --git a/src/openai/types/chat/chat_completion_function_call_option_param.py b/src/openai/types/chat/chat_completion_function_call_option_param.py new file mode 100644 index 0000000000..72d41d908c --- /dev/null +++ b/src/openai/types/chat/chat_completion_function_call_option_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ChatCompletionFunctionCallOptionParam"] + + +class ChatCompletionFunctionCallOptionParam(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" diff --git a/src/openai/types/chat/chat_completion_function_message_param.py b/src/openai/types/chat/chat_completion_function_message_param.py new file mode 100644 index 0000000000..1a16c5f5eb --- /dev/null +++ b/src/openai/types/chat/chat_completion_function_message_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionFunctionMessageParam"] + + +class ChatCompletionFunctionMessageParam(TypedDict, total=False): + content: Required[Optional[str]] + """The return value from the function call, to return to the model.""" + + name: Required[str] + """The name of the function to call.""" + + role: Required[Literal["function"]] + """The role of the messages author, in this case `function`.""" diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py index 531eb3d43c..4749798a33 100644 --- a/src/openai/types/chat/chat_completion_message.py +++ b/src/openai/types/chat/chat_completion_message.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. -from typing import Optional +from typing import List, Optional +from typing_extensions import Literal from ..._models import BaseModel -from .chat_completion_role import ChatCompletionRole +from .chat_completion_message_tool_call import ChatCompletionMessageToolCall __all__ = ["ChatCompletionMessage", "FunctionCall"] @@ -25,11 +26,15 @@ class ChatCompletionMessage(BaseModel): content: Optional[str] """The contents of the message.""" - role: ChatCompletionRole + role: Literal["assistant"] """The role of the author of this message.""" function_call: Optional[FunctionCall] = None - """ + """Deprecated and replaced by `tool_calls`. + The name and arguments of a function that should be called, as generated by the model. """ + + tool_calls: Optional[List[ChatCompletionMessageToolCall]] = None + """The tool calls generated by the model, such as function calls.""" diff --git a/src/openai/types/chat/chat_completion_message_param.py b/src/openai/types/chat/chat_completion_message_param.py index 29b8882573..7ec3d6a7b7 100644 --- a/src/openai/types/chat/chat_completion_message_param.py +++ b/src/openai/types/chat/chat_completion_message_param.py @@ -2,49 +2,20 @@ from __future__ import annotations -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ChatCompletionMessageParam", "FunctionCall"] - - -class FunctionCall(TypedDict, total=False): - arguments: Required[str] - """ - The arguments to call the function with, as generated by the model in JSON - format. Note that the model does not always generate valid JSON, and may - hallucinate parameters not defined by your function schema. Validate the - arguments in your code before calling your function. - """ - - name: Required[str] - """The name of the function to call.""" - - -class ChatCompletionMessageParam(TypedDict, total=False): - content: Required[Optional[str]] - """The contents of the message. - - `content` is required for all messages, and may be null for assistant messages - with function calls. - """ - - role: Required[Literal["system", "user", "assistant", "function"]] - """The role of the messages author. - - One of `system`, `user`, `assistant`, or `function`. - """ - - function_call: FunctionCall - """ - The name and arguments of a function that should be called, as generated by the - model. - """ - - name: str - """The name of the author of this message. - - `name` is required if role is `function`, and it should be the name of the - function whose response is in the `content`. May contain a-z, A-Z, 0-9, and - underscores, with a maximum length of 64 characters. - """ +from typing import Union + +from .chat_completion_tool_message_param import ChatCompletionToolMessageParam +from .chat_completion_user_message_param import ChatCompletionUserMessageParam +from .chat_completion_system_message_param import ChatCompletionSystemMessageParam +from .chat_completion_function_message_param import ChatCompletionFunctionMessageParam +from .chat_completion_assistant_message_param import ChatCompletionAssistantMessageParam + +__all__ = ["ChatCompletionMessageParam"] + +ChatCompletionMessageParam = Union[ + ChatCompletionSystemMessageParam, + ChatCompletionUserMessageParam, + ChatCompletionAssistantMessageParam, + ChatCompletionToolMessageParam, + ChatCompletionFunctionMessageParam, +] diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py new file mode 100644 index 0000000000..63c72fcdca --- /dev/null +++ b/src/openai/types/chat/chat_completion_message_tool_call.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionMessageToolCall", "Function"] + + +class Function(BaseModel): + arguments: str + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: str + """The name of the function to call.""" + + +class ChatCompletionMessageToolCall(BaseModel): + id: str + """The ID of the tool call.""" + + function: Function + """The function that the model called.""" + + type: Literal["function"] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/chat_completion_message_tool_call_param.py b/src/openai/types/chat/chat_completion_message_tool_call_param.py new file mode 100644 index 0000000000..a700f02c4f --- /dev/null +++ b/src/openai/types/chat/chat_completion_message_tool_call_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionMessageToolCallParam", "Function"] + + +class Function(TypedDict, total=False): + arguments: Required[str] + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Required[str] + """The name of the function to call.""" + + +class ChatCompletionMessageToolCallParam(TypedDict, total=False): + id: Required[str] + """The ID of the tool call.""" + + function: Required[Function] + """The function that the model called.""" + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_param.py new file mode 100644 index 0000000000..4c6f20d2f1 --- /dev/null +++ b/src/openai/types/chat/chat_completion_named_tool_choice_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionNamedToolChoiceParam", "Function"] + + +class Function(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" + + +class ChatCompletionNamedToolChoiceParam(TypedDict, total=False): + function: Function + + type: Literal["function"] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/chat_completion_role.py b/src/openai/types/chat/chat_completion_role.py index da8896a072..9fa2acb4bb 100644 --- a/src/openai/types/chat/chat_completion_role.py +++ b/src/openai/types/chat/chat_completion_role.py @@ -4,4 +4,4 @@ __all__ = ["ChatCompletionRole"] -ChatCompletionRole = Literal["system", "user", "assistant", "function"] +ChatCompletionRole = Literal["system", "user", "assistant", "tool", "function"] diff --git a/src/openai/types/chat/chat_completion_system_message_param.py b/src/openai/types/chat/chat_completion_system_message_param.py new file mode 100644 index 0000000000..ec08e00350 --- /dev/null +++ b/src/openai/types/chat/chat_completion_system_message_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionSystemMessageParam"] + + +class ChatCompletionSystemMessageParam(TypedDict, total=False): + content: Required[Optional[str]] + """The contents of the system message.""" + + role: Required[Literal["system"]] + """The role of the messages author, in this case `system`.""" diff --git a/src/openai/types/chat/chat_completion_tool_choice_option_param.py b/src/openai/types/chat/chat_completion_tool_choice_option_param.py new file mode 100644 index 0000000000..8104b26acb --- /dev/null +++ b/src/openai/types/chat/chat_completion_tool_choice_option_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal + +from .chat_completion_named_tool_choice_param import ChatCompletionNamedToolChoiceParam + +__all__ = ["ChatCompletionToolChoiceOptionParam"] + +ChatCompletionToolChoiceOptionParam = Union[Literal["none", "auto"], ChatCompletionNamedToolChoiceParam] diff --git a/src/openai/types/chat/chat_completion_tool_message_param.py b/src/openai/types/chat/chat_completion_tool_message_param.py new file mode 100644 index 0000000000..51759a9a99 --- /dev/null +++ b/src/openai/types/chat/chat_completion_tool_message_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionToolMessageParam"] + + +class ChatCompletionToolMessageParam(TypedDict, total=False): + content: Required[Optional[str]] + """The contents of the tool message.""" + + role: Required[Literal["tool"]] + """The role of the messages author, in this case `tool`.""" + + tool_call_id: Required[str] + """Tool call that this message is responding to.""" diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py new file mode 100644 index 0000000000..4b7e6238c7 --- /dev/null +++ b/src/openai/types/chat/chat_completion_tool_param.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionToolParam", "Function"] + + +class Function(TypedDict, total=False): + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Required[Dict[str, object]] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + +class ChatCompletionToolParam(TypedDict, total=False): + function: Required[Function] + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/chat_completion_user_message_param.py b/src/openai/types/chat/chat_completion_user_message_param.py new file mode 100644 index 0000000000..6f0cf34623 --- /dev/null +++ b/src/openai/types/chat/chat_completion_user_message_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_content_part_param import ChatCompletionContentPartParam + +__all__ = ["ChatCompletionUserMessageParam"] + + +class ChatCompletionUserMessageParam(TypedDict, total=False): + content: Required[Union[str, List[ChatCompletionContentPartParam], None]] + """The contents of the user message.""" + + role: Required[Literal["user"]] + """The role of the messages author, in this case `user`.""" diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index d681a90cd6..44b1abe576 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -5,13 +5,20 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, TypedDict +from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam +from .chat_completion_tool_choice_option_param import ( + ChatCompletionToolChoiceOptionParam, +) +from .chat_completion_function_call_option_param import ( + ChatCompletionFunctionCallOptionParam, +) __all__ = [ "CompletionCreateParamsBase", "FunctionCall", - "FunctionCallFunctionCallOption", "Function", + "ResponseFormat", "CompletionCreateParamsNonStreaming", "CompletionCreateParamsStreaming", ] @@ -59,22 +66,28 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ function_call: FunctionCall - """Controls how the model calls functions. + """Deprecated in favor of `tool_choice`. - "none" means the model will not call a function and instead generates a message. - "auto" means the model can pick between generating a message or calling a - function. Specifying a particular function via `{"name": "my_function"}` forces - the model to call that function. "none" is the default when no functions are - present. "auto" is the default if functions are present. + Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via `{"name": "my_function"}` forces the model to call that + function. + + `none` is the default when no functions are present. `auto`` is the default if + functions are present. """ functions: List[Function] - """A list of functions the model may generate JSON inputs for.""" + """Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + """ logit_bias: Optional[Dict[str, int]] """Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the + Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or @@ -103,6 +116,21 @@ class CompletionCreateParamsBase(TypedDict, total=False): [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) """ + response_format: ResponseFormat + """An object specifying the format that the model must output. + + Used to enable JSON mode. + """ + + seed: Optional[int] + """This feature is in Beta. + + If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. Determinism is not guaranteed, and you should refer to the + `system_fingerprint` response parameter to monitor changes in the backend. + """ + stop: Union[Optional[str], List[str]] """Up to 4 sequences where the API will stop generating further tokens.""" @@ -115,6 +143,26 @@ class CompletionCreateParamsBase(TypedDict, total=False): We generally recommend altering this or `top_p` but not both. """ + tool_choice: ChatCompletionToolChoiceOptionParam + """ + Controls which (if any) function is called by the model. `none` means the model + will not call a function and instead generates a message. `auto` means the model + can pick between generating a message or calling a function. Specifying a + particular function via + `{"type: "function", "function": {"name": "my_function"}}` forces the model to + call that function. + + `none` is the default when no functions are present. `auto` is the default if + functions are present. + """ + + tools: List[ChatCompletionToolParam] + """A list of tools the model may call. + + Currently, only functions are supported as a tool. Use this to provide a list of + functions the model may generate JSON inputs for. + """ + top_p: Optional[float] """ An alternative to sampling with temperature, called nucleus sampling, where the @@ -132,12 +180,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ -class FunctionCallFunctionCallOption(TypedDict, total=False): - name: Required[str] - """The name of the function to call.""" - - -FunctionCall = Union[Literal["none", "auto"], FunctionCallFunctionCallOption] +FunctionCall = Union[Literal["none", "auto"], ChatCompletionFunctionCallOptionParam] class Function(TypedDict, total=False): @@ -167,6 +210,23 @@ class Function(TypedDict, total=False): """ +class ResponseFormat(TypedDict, total=False): + type: Literal["text", "json_object"] + """Setting to `json_object` enables JSON mode. + + This guarantees that the message the model generates is valid JSON. + + Note that your system prompt must still instruct the model to produce JSON, and + to help ensure you don't forget, the API will throw an error if the string + `JSON` does not appear in your system message. Also note that the message + content may be partial (i.e. cut off) if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + Must be one of `text` or `json_object`. + """ + + class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase): stream: Optional[Literal[False]] """If set, partial message deltas will be sent, like in ChatGPT. diff --git a/src/openai/types/completion.py b/src/openai/types/completion.py index 0a90838fd4..cd80498b16 100644 --- a/src/openai/types/completion.py +++ b/src/openai/types/completion.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import List, Optional +from typing_extensions import Literal from .._models import BaseModel from .completion_usage import CompletionUsage @@ -22,8 +23,15 @@ class Completion(BaseModel): model: str """The model used for completion.""" - object: str + object: Literal["text_completion"] """The object type, which is always "text_completion" """ + system_fingerprint: Optional[str] = None + """This fingerprint represents the backend configuration that the model runs with. + + Can be used in conjunction with the `seed` request parameter to understand when + backend changes have been made that might impact determinism. + """ + usage: Optional[CompletionUsage] = None """Usage statistics for the completion request.""" diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index 023c087d5f..3e56d4f7bf 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -73,7 +73,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): logit_bias: Optional[Dict[str, int]] """Modify the likelihood of specified tokens appearing in the completion. - Accepts a json object that maps tokens (specified by their token ID in the GPT + Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits @@ -122,6 +122,16 @@ class CompletionCreateParamsBase(TypedDict, total=False): [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) """ + seed: Optional[int] + """ + If specified, our system will make a best effort to sample deterministically, + such that repeated requests with the same `seed` and parameters should return + the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` + response parameter to monitor changes in the backend. + """ + stop: Union[Optional[str], List[str], None] """Up to 4 sequences where the API will stop generating further tokens. diff --git a/src/openai/types/create_embedding_response.py b/src/openai/types/create_embedding_response.py index eccd148d3c..7382bed6b9 100644 --- a/src/openai/types/create_embedding_response.py +++ b/src/openai/types/create_embedding_response.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import List +from typing_extensions import Literal from .._models import BaseModel from .embedding import Embedding @@ -23,7 +24,7 @@ class CreateEmbeddingResponse(BaseModel): model: str """The name of the model used to generate the embedding.""" - object: str + object: Literal["embedding"] """The object type, which is always "embedding".""" usage: Usage diff --git a/src/openai/types/edit.py b/src/openai/types/edit.py index 41b327534e..48bca2987b 100644 --- a/src/openai/types/edit.py +++ b/src/openai/types/edit.py @@ -33,7 +33,7 @@ class Edit(BaseModel): created: int """The Unix timestamp (in seconds) of when the edit was created.""" - object: str + object: Literal["edit"] """The object type, which is always `edit`.""" usage: CompletionUsage diff --git a/src/openai/types/embedding.py b/src/openai/types/embedding.py index 4579b9bb57..9c53704d5d 100644 --- a/src/openai/types/embedding.py +++ b/src/openai/types/embedding.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import List +from typing_extensions import Literal from .._models import BaseModel @@ -18,5 +19,5 @@ class Embedding(BaseModel): index: int """The index of the embedding in the list of embeddings.""" - object: str + object: Literal["embedding"] """The object type, which is always "embedding".""" diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index 07b068c5c6..a59ddb2817 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes @@ -11,16 +11,15 @@ class FileCreateParams(TypedDict, total=False): file: Required[FileTypes] - """The file object (not file name) to be uploaded. + """The File object (not file name) to be uploaded.""" - If the `purpose` is set to "fine-tune", the file will be used for fine-tuning. - """ - - purpose: Required[str] + purpose: Required[Literal["fine-tune", "assistants"]] """The intended purpose of the uploaded file. Use "fine-tune" for - [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This - allows us to validate the format of the uploaded file is correct for - fine-tuning. + [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and + "assistants" for + [Assistants](https://platform.openai.com/docs/api-reference/assistants) and + [Messages](https://platform.openai.com/docs/api-reference/messages). This allows + us to validate the format of the uploaded file is correct for fine-tuning. """ diff --git a/src/openai/types/file_deleted.py b/src/openai/types/file_deleted.py index a526b2b986..3ac8592ff6 100644 --- a/src/openai/types/file_deleted.py +++ b/src/openai/types/file_deleted.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. +from typing_extensions import Literal + from .._models import BaseModel __all__ = ["FileDeleted"] @@ -10,4 +12,4 @@ class FileDeleted(BaseModel): deleted: bool - object: str + object: Literal["file"] diff --git a/src/openai/types/file_list_params.py b/src/openai/types/file_list_params.py new file mode 100644 index 0000000000..a962dd239c --- /dev/null +++ b/src/openai/types/file_list_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + purpose: str + """Only return files with the given purpose.""" diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py index dac24a88c5..4ae91b754e 100644 --- a/src/openai/types/file_object.py +++ b/src/openai/types/file_object.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import Optional +from typing_extensions import Literal from .._models import BaseModel @@ -12,7 +13,7 @@ class FileObject(BaseModel): """The file identifier, which can be referenced in the API endpoints.""" bytes: int - """The size of the file in bytes.""" + """The size of the file, in bytes.""" created_at: int """The Unix timestamp (in seconds) for when the file was created.""" @@ -20,21 +21,26 @@ class FileObject(BaseModel): filename: str """The name of the file.""" - object: str - """The object type, which is always "file".""" + object: Literal["file"] + """The object type, which is always `file`.""" - purpose: str - """The intended purpose of the file. Currently, only "fine-tune" is supported.""" + purpose: Literal["fine-tune", "fine-tune-results", "assistants", "assistants_output"] + """The intended purpose of the file. - status: Optional[str] = None + Supported values are `fine-tune`, `fine-tune-results`, `assistants`, and + `assistants_output`. """ - The current status of the file, which can be either `uploaded`, `processed`, - `pending`, `error`, `deleting` or `deleted`. + + status: Literal["uploaded", "processed", "error"] + """Deprecated. + + The current status of the file, which can be either `uploaded`, `processed`, or + `error`. """ status_details: Optional[str] = None - """Additional details about the status of the file. + """Deprecated. - If the file is in the `error` state, this will include a message describing the - error. + For details on why a fine-tuning training file failed validation, see the + `error` field on `fine_tuning.job`. """ diff --git a/src/openai/types/fine_tune.py b/src/openai/types/fine_tune.py index 4124def2f5..de1e097ee4 100644 --- a/src/openai/types/fine_tune.py +++ b/src/openai/types/fine_tune.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import List, Optional +from typing_extensions import Literal from .._models import BaseModel from .file_object import FileObject @@ -63,7 +64,7 @@ class FineTune(BaseModel): model: str """The base model that is being fine-tuned.""" - object: str + object: Literal["fine-tune"] """The object type, which is always "fine-tune".""" organization_id: str diff --git a/src/openai/types/fine_tune_event.py b/src/openai/types/fine_tune_event.py index 6499def98d..299f0de24b 100644 --- a/src/openai/types/fine_tune_event.py +++ b/src/openai/types/fine_tune_event.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. +from typing_extensions import Literal + from .._models import BaseModel __all__ = ["FineTuneEvent"] @@ -12,4 +14,4 @@ class FineTuneEvent(BaseModel): message: str - object: str + object: Literal["fine-tune-event"] diff --git a/src/openai/types/fine_tune_events_list_response.py b/src/openai/types/fine_tune_events_list_response.py index ca159d8772..c69746104d 100644 --- a/src/openai/types/fine_tune_events_list_response.py +++ b/src/openai/types/fine_tune_events_list_response.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import List +from typing_extensions import Literal from .._models import BaseModel from .fine_tune_event import FineTuneEvent @@ -11,4 +12,4 @@ class FineTuneEventsListResponse(BaseModel): data: List[FineTuneEvent] - object: str + object: Literal["list"] diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index 2ae1cbb473..3897176a47 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -67,7 +67,7 @@ class FineTuningJob(BaseModel): model: str """The base model that is being fine-tuned.""" - object: str + object: Literal["fine_tuning.job"] """The object type, which is always "fine_tuning.job".""" organization_id: str @@ -80,7 +80,7 @@ class FineTuningJob(BaseModel): [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents). """ - status: str + status: Literal["validating_files", "queued", "running", "succeeded", "failed", "cancelled"] """ The current status of the fine-tuning job, which can be either `validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`. diff --git a/src/openai/types/fine_tuning/fine_tuning_job_event.py b/src/openai/types/fine_tuning/fine_tuning_job_event.py index c21a0503ab..62f268868b 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_event.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_event.py @@ -16,4 +16,4 @@ class FineTuningJobEvent(BaseModel): message: str - object: str + object: Literal["fine_tuning.job.event"] diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 2a67b81817..da750ffc19 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -58,6 +58,19 @@ class JobCreateParams(TypedDict, total=False): class Hyperparameters(TypedDict, total=False): + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + n_epochs: Union[Literal["auto"], int] """The number of epochs to train the model for. diff --git a/src/openai/types/image.py b/src/openai/types/image.py index 4b8d1aaf18..a040caf7b6 100644 --- a/src/openai/types/image.py +++ b/src/openai/types/image.py @@ -14,5 +14,11 @@ class Image(BaseModel): `b64_json`. """ + revised_prompt: Optional[str] = None + """ + The prompt that was used to generate the image, if there was any revision to the + prompt. + """ + url: Optional[str] = None """The URL of the generated image, if `response_format` is `url` (default).""" diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py index d3b439070e..7b015fc176 100644 --- a/src/openai/types/image_create_variation_params.py +++ b/src/openai/types/image_create_variation_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import Union, Optional from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes @@ -17,8 +17,17 @@ class ImageCreateVariationParams(TypedDict, total=False): Must be a valid PNG file, less than 4MB, and square. """ + model: Union[str, Literal["dall-e-2"], None] + """The model to use for image generation. + + Only `dall-e-2` is supported at this time. + """ + n: Optional[int] - """The number of images to generate. Must be between 1 and 10.""" + """The number of images to generate. + + Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. + """ response_format: Optional[Literal["url", "b64_json"]] """The format in which the generated images are returned. diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index ce07a9cb30..043885cc38 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import Union, Optional from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes @@ -31,6 +31,12 @@ class ImageEditParams(TypedDict, total=False): PNG file, less than 4MB, and have the same dimensions as `image`. """ + model: Union[str, Literal["dall-e-2"], None] + """The model to use for image generation. + + Only `dall-e-2` is supported at this time. + """ + n: Optional[int] """The number of images to generate. Must be between 1 and 10.""" diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 4999ed958d..7eca29a7ba 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import Union, Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ImageGenerateParams"] @@ -12,11 +12,25 @@ class ImageGenerateParams(TypedDict, total=False): prompt: Required[str] """A text description of the desired image(s). - The maximum length is 1000 characters. + The maximum length is 1000 characters for `dall-e-2` and 4000 characters for + `dall-e-3`. """ + model: Union[str, Literal["dall-e-2", "dall-e-3"], None] + """The model to use for image generation.""" + n: Optional[int] - """The number of images to generate. Must be between 1 and 10.""" + """The number of images to generate. + + Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. + """ + + quality: Literal["standard", "hd"] + """The quality of the image that will be generated. + + `hd` creates images with finer details and greater consistency across the image. + This param is only supported for `dall-e-3`. + """ response_format: Optional[Literal["url", "b64_json"]] """The format in which the generated images are returned. @@ -24,10 +38,20 @@ class ImageGenerateParams(TypedDict, total=False): Must be one of `url` or `b64_json`. """ - size: Optional[Literal["256x256", "512x512", "1024x1024"]] + size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] """The size of the generated images. - Must be one of `256x256`, `512x512`, or `1024x1024`. + Must be one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. Must be one + of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3` models. + """ + + style: Optional[Literal["vivid", "natural"]] + """The style of the generated images. + + Must be one of `vivid` or `natural`. Vivid causes the model to lean towards + generating hyper-real and dramatic images. Natural causes the model to produce + more natural, less hyper-real looking images. This param is only supported for + `dall-e-3`. """ user: str diff --git a/src/openai/types/model.py b/src/openai/types/model.py index 29e71b81a0..58f3997f70 100644 --- a/src/openai/types/model.py +++ b/src/openai/types/model.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. +from typing_extensions import Literal + from .._models import BaseModel __all__ = ["Model"] @@ -12,7 +14,7 @@ class Model(BaseModel): created: int """The Unix timestamp (in seconds) when the model was created.""" - object: str + object: Literal["model"] """The object type, which is always "model".""" owned_by: str diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py new file mode 100644 index 0000000000..89814c2dd3 --- /dev/null +++ b/tests/api_resources/audio/test_speech.py @@ -0,0 +1,110 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import httpx +import pytest +from respx import MockRouter + +from openai import OpenAI, AsyncOpenAI +from openai._types import BinaryResponseContent +from openai._client import OpenAI, AsyncOpenAI + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestSpeech: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + speech = client.audio.speech.create( + input="string", + model="string", + voice="alloy", + ) + assert isinstance(speech, BinaryResponseContent) + assert speech.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + speech = respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + client.audio.speech.create( + input="string", + model="string", + voice="alloy", + response_format="mp3", + speed=0.25, + ) + assert isinstance(speech, BinaryResponseContent) + assert speech.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = client.audio.speech.with_raw_response.create( + input="string", + model="string", + voice="alloy", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + speech = response.parse() + assert isinstance(speech, BinaryResponseContent) + assert speech.json() == {"foo": "bar"} + + +class TestAsyncSpeech: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + speech = await client.audio.speech.create( + input="string", + model="string", + voice="alloy", + ) + assert isinstance(speech, BinaryResponseContent) + assert speech.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + speech = respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + await client.audio.speech.create( + input="string", + model="string", + voice="alloy", + response_format="mp3", + speed=0.25, + ) + assert isinstance(speech, BinaryResponseContent) + assert speech.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = await client.audio.speech.with_raw_response.create( + input="string", + model="string", + voice="alloy", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + speech = response.parse() + assert isinstance(speech, BinaryResponseContent) + assert speech.json() == {"foo": "bar"} diff --git a/tests/api_resources/beta/__init__.py b/tests/api_resources/beta/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/beta/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/beta/assistants/__init__.py b/tests/api_resources/beta/assistants/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/beta/assistants/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/beta/assistants/test_files.py b/tests/api_resources/beta/assistants/test_files.py new file mode 100644 index 0000000000..2545640c57 --- /dev/null +++ b/tests/api_resources/beta/assistants/test_files.py @@ -0,0 +1,190 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta.assistants import AssistantFile, FileDeleteResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestFiles: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + file = client.beta.assistants.files.create( + "file-AF1WoRqd3aJAHsqc9NY7iL8F", + file_id="string", + ) + assert_matches_type(AssistantFile, file, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.assistants.files.with_raw_response.create( + "file-AF1WoRqd3aJAHsqc9NY7iL8F", + file_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AssistantFile, file, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + file = client.beta.assistants.files.retrieve( + "string", + assistant_id="string", + ) + assert_matches_type(AssistantFile, file, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.assistants.files.with_raw_response.retrieve( + "string", + assistant_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AssistantFile, file, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + file = client.beta.assistants.files.list( + "string", + ) + assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + file = client.beta.assistants.files.list( + "string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.assistants.files.with_raw_response.list( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + file = client.beta.assistants.files.delete( + "string", + assistant_id="string", + ) + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.beta.assistants.files.with_raw_response.delete( + "string", + assistant_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + +class TestAsyncFiles: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + file = await client.beta.assistants.files.create( + "file-AF1WoRqd3aJAHsqc9NY7iL8F", + file_id="string", + ) + assert_matches_type(AssistantFile, file, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.files.with_raw_response.create( + "file-AF1WoRqd3aJAHsqc9NY7iL8F", + file_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AssistantFile, file, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + file = await client.beta.assistants.files.retrieve( + "string", + assistant_id="string", + ) + assert_matches_type(AssistantFile, file, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.files.with_raw_response.retrieve( + "string", + assistant_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AssistantFile, file, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + file = await client.beta.assistants.files.list( + "string", + ) + assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: + file = await client.beta.assistants.files.list( + "string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.files.with_raw_response.list( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + + @parametrize + async def test_method_delete(self, client: AsyncOpenAI) -> None: + file = await client.beta.assistants.files.delete( + "string", + assistant_id="string", + ) + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.files.with_raw_response.delete( + "string", + assistant_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileDeleteResponse, file, path=["response"]) diff --git a/tests/api_resources/beta/chat/__init__.py b/tests/api_resources/beta/chat/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/beta/chat/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py new file mode 100644 index 0000000000..5bbad1d7dd --- /dev/null +++ b/tests/api_resources/beta/test_assistants.py @@ -0,0 +1,254 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta import Assistant, AsssitantDeleted + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestAssistants: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + assistant = client.beta.assistants.create( + model="string", + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + assistant = client.beta.assistants.create( + model="string", + description="string", + file_ids=["string", "string", "string"], + instructions="string", + metadata={}, + name="string", + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.assistants.with_raw_response.create( + model="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + assistant = client.beta.assistants.retrieve( + "string", + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.assistants.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + assistant = client.beta.assistants.update( + "string", + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + assistant = client.beta.assistants.update( + "string", + description="string", + file_ids=["string", "string", "string"], + instructions="string", + metadata={}, + model="string", + name="string", + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.beta.assistants.with_raw_response.update( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + assistant = client.beta.assistants.list() + assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + assistant = client.beta.assistants.list( + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.assistants.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + assistant = client.beta.assistants.delete( + "string", + ) + assert_matches_type(AsssitantDeleted, assistant, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.beta.assistants.with_raw_response.delete( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(AsssitantDeleted, assistant, path=["response"]) + + +class TestAsyncAssistants: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + assistant = await client.beta.assistants.create( + model="string", + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + assistant = await client.beta.assistants.create( + model="string", + description="string", + file_ids=["string", "string", "string"], + instructions="string", + metadata={}, + name="string", + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.with_raw_response.create( + model="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + assistant = await client.beta.assistants.retrieve( + "string", + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + async def test_method_update(self, client: AsyncOpenAI) -> None: + assistant = await client.beta.assistants.update( + "string", + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: + assistant = await client.beta.assistants.update( + "string", + description="string", + file_ids=["string", "string", "string"], + instructions="string", + metadata={}, + model="string", + name="string", + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + async def test_raw_response_update(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.with_raw_response.update( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + assistant = await client.beta.assistants.list() + assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: + assistant = await client.beta.assistants.list( + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.with_raw_response.list() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) + + @parametrize + async def test_method_delete(self, client: AsyncOpenAI) -> None: + assistant = await client.beta.assistants.delete( + "string", + ) + assert_matches_type(AsssitantDeleted, assistant, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: + response = await client.beta.assistants.with_raw_response.delete( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + assistant = response.parse() + assert_matches_type(AsssitantDeleted, assistant, path=["response"]) diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py new file mode 100644 index 0000000000..8fa1fc20ea --- /dev/null +++ b/tests/api_resources/beta/test_threads.py @@ -0,0 +1,318 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.types.beta import Thread, ThreadDeleted +from openai.types.beta.threads import Run + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestThreads: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + thread = client.beta.threads.create() + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + thread = client.beta.threads.create( + messages=[ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], + metadata={}, + ) + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.threads.with_raw_response.create() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + thread = client.beta.threads.retrieve( + "string", + ) + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.threads.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + thread = client.beta.threads.update( + "string", + ) + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + thread = client.beta.threads.update( + "string", + metadata={}, + ) + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.beta.threads.with_raw_response.update( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + thread = client.beta.threads.delete( + "string", + ) + assert_matches_type(ThreadDeleted, thread, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.beta.threads.with_raw_response.delete( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(ThreadDeleted, thread, path=["response"]) + + @parametrize + def test_method_create_and_run(self, client: OpenAI) -> None: + thread = client.beta.threads.create_and_run( + assistant_id="string", + ) + assert_matches_type(Run, thread, path=["response"]) + + @parametrize + def test_method_create_and_run_with_all_params(self, client: OpenAI) -> None: + thread = client.beta.threads.create_and_run( + assistant_id="string", + instructions="string", + metadata={}, + model="string", + thread={ + "messages": [ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], + "metadata": {}, + }, + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + assert_matches_type(Run, thread, path=["response"]) + + @parametrize + def test_raw_response_create_and_run(self, client: OpenAI) -> None: + response = client.beta.threads.with_raw_response.create_and_run( + assistant_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(Run, thread, path=["response"]) + + +class TestAsyncThreads: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + thread = await client.beta.threads.create() + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + thread = await client.beta.threads.create( + messages=[ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], + metadata={}, + ) + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.with_raw_response.create() + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + thread = await client.beta.threads.retrieve( + "string", + ) + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.with_raw_response.retrieve( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + async def test_method_update(self, client: AsyncOpenAI) -> None: + thread = await client.beta.threads.update( + "string", + ) + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: + thread = await client.beta.threads.update( + "string", + metadata={}, + ) + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + async def test_raw_response_update(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.with_raw_response.update( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + @parametrize + async def test_method_delete(self, client: AsyncOpenAI) -> None: + thread = await client.beta.threads.delete( + "string", + ) + assert_matches_type(ThreadDeleted, thread, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.with_raw_response.delete( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(ThreadDeleted, thread, path=["response"]) + + @parametrize + async def test_method_create_and_run(self, client: AsyncOpenAI) -> None: + thread = await client.beta.threads.create_and_run( + assistant_id="string", + ) + assert_matches_type(Run, thread, path=["response"]) + + @parametrize + async def test_method_create_and_run_with_all_params(self, client: AsyncOpenAI) -> None: + thread = await client.beta.threads.create_and_run( + assistant_id="string", + instructions="string", + metadata={}, + model="string", + thread={ + "messages": [ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], + "metadata": {}, + }, + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + assert_matches_type(Run, thread, path=["response"]) + + @parametrize + async def test_raw_response_create_and_run(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.with_raw_response.create_and_run( + assistant_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(Run, thread, path=["response"]) diff --git a/tests/api_resources/beta/threads/__init__.py b/tests/api_resources/beta/threads/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/beta/threads/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/beta/threads/messages/__init__.py b/tests/api_resources/beta/threads/messages/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/beta/threads/messages/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/beta/threads/messages/test_files.py b/tests/api_resources/beta/threads/messages/test_files.py new file mode 100644 index 0000000000..a5b68713e6 --- /dev/null +++ b/tests/api_resources/beta/threads/messages/test_files.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta.threads.messages import MessageFile + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestFiles: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + file = client.beta.threads.messages.files.retrieve( + "file-AF1WoRqd3aJAHsqc9NY7iL8F", + thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F", + message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(MessageFile, file, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.threads.messages.files.with_raw_response.retrieve( + "file-AF1WoRqd3aJAHsqc9NY7iL8F", + thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F", + message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(MessageFile, file, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + file = client.beta.threads.messages.files.list( + "string", + thread_id="string", + ) + assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + file = client.beta.threads.messages.files.list( + "string", + thread_id="string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.threads.messages.files.with_raw_response.list( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) + + +class TestAsyncFiles: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + file = await client.beta.threads.messages.files.retrieve( + "file-AF1WoRqd3aJAHsqc9NY7iL8F", + thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F", + message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(MessageFile, file, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.messages.files.with_raw_response.retrieve( + "file-AF1WoRqd3aJAHsqc9NY7iL8F", + thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F", + message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(MessageFile, file, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + file = await client.beta.threads.messages.files.list( + "string", + thread_id="string", + ) + assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: + file = await client.beta.threads.messages.files.list( + "string", + thread_id="string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.messages.files.with_raw_response.list( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) diff --git a/tests/api_resources/beta/threads/runs/__init__.py b/tests/api_resources/beta/threads/runs/__init__.py new file mode 100644 index 0000000000..1016754ef3 --- /dev/null +++ b/tests/api_resources/beta/threads/runs/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py new file mode 100644 index 0000000000..3f4f8c1022 --- /dev/null +++ b/tests/api_resources/beta/threads/runs/test_steps.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta.threads.runs import RunStep + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestSteps: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + step = client.beta.threads.runs.steps.retrieve( + "string", + thread_id="string", + run_id="string", + ) + assert_matches_type(RunStep, step, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.threads.runs.steps.with_raw_response.retrieve( + "string", + thread_id="string", + run_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + step = response.parse() + assert_matches_type(RunStep, step, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + step = client.beta.threads.runs.steps.list( + "string", + thread_id="string", + ) + assert_matches_type(SyncCursorPage[RunStep], step, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + step = client.beta.threads.runs.steps.list( + "string", + thread_id="string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[RunStep], step, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.threads.runs.steps.with_raw_response.list( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + step = response.parse() + assert_matches_type(SyncCursorPage[RunStep], step, path=["response"]) + + +class TestAsyncSteps: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + step = await client.beta.threads.runs.steps.retrieve( + "string", + thread_id="string", + run_id="string", + ) + assert_matches_type(RunStep, step, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.runs.steps.with_raw_response.retrieve( + "string", + thread_id="string", + run_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + step = response.parse() + assert_matches_type(RunStep, step, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + step = await client.beta.threads.runs.steps.list( + "string", + thread_id="string", + ) + assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: + step = await client.beta.threads.runs.steps.list( + "string", + thread_id="string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.runs.steps.with_raw_response.list( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + step = response.parse() + assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py new file mode 100644 index 0000000000..f3fe7dc2bb --- /dev/null +++ b/tests/api_resources/beta/threads/test_messages.py @@ -0,0 +1,234 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta.threads import ThreadMessage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestMessages: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + message = client.beta.threads.messages.create( + "string", + content="x", + role="user", + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + message = client.beta.threads.messages.create( + "string", + content="x", + role="user", + file_ids=["string"], + metadata={}, + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.threads.messages.with_raw_response.create( + "string", + content="x", + role="user", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + message = client.beta.threads.messages.retrieve( + "string", + thread_id="string", + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.threads.messages.with_raw_response.retrieve( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + message = client.beta.threads.messages.update( + "string", + thread_id="string", + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + message = client.beta.threads.messages.update( + "string", + thread_id="string", + metadata={}, + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.beta.threads.messages.with_raw_response.update( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + message = client.beta.threads.messages.list( + "string", + ) + assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + message = client.beta.threads.messages.list( + "string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.threads.messages.with_raw_response.list( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + + +class TestAsyncMessages: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + message = await client.beta.threads.messages.create( + "string", + content="x", + role="user", + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + message = await client.beta.threads.messages.create( + "string", + content="x", + role="user", + file_ids=["string"], + metadata={}, + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.messages.with_raw_response.create( + "string", + content="x", + role="user", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + message = await client.beta.threads.messages.retrieve( + "string", + thread_id="string", + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.messages.with_raw_response.retrieve( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + async def test_method_update(self, client: AsyncOpenAI) -> None: + message = await client.beta.threads.messages.update( + "string", + thread_id="string", + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: + message = await client.beta.threads.messages.update( + "string", + thread_id="string", + metadata={}, + ) + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + async def test_raw_response_update(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.messages.with_raw_response.update( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + message = await client.beta.threads.messages.list( + "string", + ) + assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: + message = await client.beta.threads.messages.list( + "string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.messages.with_raw_response.list( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py new file mode 100644 index 0000000000..d323dfc354 --- /dev/null +++ b/tests/api_resources/beta/threads/test_runs.py @@ -0,0 +1,308 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +import os + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai._client import OpenAI, AsyncOpenAI +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta.threads import Run + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") +api_key = "My API Key" + + +class TestRuns: + strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + run = client.beta.threads.runs.create( + "string", + assistant_id="string", + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + run = client.beta.threads.runs.create( + "string", + assistant_id="string", + instructions="string", + metadata={}, + model="string", + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.threads.runs.with_raw_response.create( + "string", + assistant_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + run = client.beta.threads.runs.retrieve( + "string", + thread_id="string", + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.threads.runs.with_raw_response.retrieve( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + run = client.beta.threads.runs.update( + "string", + thread_id="string", + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + run = client.beta.threads.runs.update( + "string", + thread_id="string", + metadata={}, + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.beta.threads.runs.with_raw_response.update( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + run = client.beta.threads.runs.list( + "string", + ) + assert_matches_type(SyncCursorPage[Run], run, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + run = client.beta.threads.runs.list( + "string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[Run], run, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.threads.runs.with_raw_response.list( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(SyncCursorPage[Run], run, path=["response"]) + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + run = client.beta.threads.runs.cancel( + "string", + thread_id="string", + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + response = client.beta.threads.runs.with_raw_response.cancel( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_method_submit_tool_outputs(self, client: OpenAI) -> None: + run = client.beta.threads.runs.submit_tool_outputs( + "string", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_raw_response_submit_tool_outputs(self, client: OpenAI) -> None: + response = client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "string", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + +class TestAsyncRuns: + strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + + @parametrize + async def test_method_create(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.create( + "string", + assistant_id="string", + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.create( + "string", + assistant_id="string", + instructions="string", + metadata={}, + model="string", + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_raw_response_create(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.runs.with_raw_response.create( + "string", + assistant_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_method_retrieve(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.retrieve( + "string", + thread_id="string", + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.runs.with_raw_response.retrieve( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_method_update(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.update( + "string", + thread_id="string", + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.update( + "string", + thread_id="string", + metadata={}, + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_raw_response_update(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.runs.with_raw_response.update( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_method_list(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.list( + "string", + ) + assert_matches_type(AsyncCursorPage[Run], run, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.list( + "string", + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[Run], run, path=["response"]) + + @parametrize + async def test_raw_response_list(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.runs.with_raw_response.list( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(AsyncCursorPage[Run], run, path=["response"]) + + @parametrize + async def test_method_cancel(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.cancel( + "string", + thread_id="string", + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.runs.with_raw_response.cancel( + "string", + thread_id="string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_method_submit_tool_outputs(self, client: AsyncOpenAI) -> None: + run = await client.beta.threads.runs.submit_tool_outputs( + "string", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_raw_response_submit_tool_outputs(self, client: AsyncOpenAI) -> None: + response = await client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "string", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(Run, run, path=["response"]) diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index dacf5d2596..132e00039b 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -39,11 +39,6 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: messages=[ { "content": "string", - "function_call": { - "arguments": "string", - "name": "string", - }, - "name": "string", "role": "system", } ], @@ -61,9 +56,38 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_tokens=0, n=1, presence_penalty=-2, + response_format={"type": "json_object"}, + seed=-9223372036854776000, stop="string", stream=False, temperature=1, + tool_choice="none", + tools=[ + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + ], top_p=1, user="user-1234", ) @@ -103,11 +127,6 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: messages=[ { "content": "string", - "function_call": { - "arguments": "string", - "name": "string", - }, - "name": "string", "role": "system", } ], @@ -126,8 +145,37 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_tokens=0, n=1, presence_penalty=-2, + response_format={"type": "json_object"}, + seed=-9223372036854776000, stop="string", temperature=1, + tool_choice="none", + tools=[ + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + ], top_p=1, user="user-1234", ) @@ -172,11 +220,6 @@ async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenA messages=[ { "content": "string", - "function_call": { - "arguments": "string", - "name": "string", - }, - "name": "string", "role": "system", } ], @@ -194,9 +237,38 @@ async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenA max_tokens=0, n=1, presence_penalty=-2, + response_format={"type": "json_object"}, + seed=-9223372036854776000, stop="string", stream=False, temperature=1, + tool_choice="none", + tools=[ + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + ], top_p=1, user="user-1234", ) @@ -236,11 +308,6 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA messages=[ { "content": "string", - "function_call": { - "arguments": "string", - "name": "string", - }, - "name": "string", "role": "system", } ], @@ -259,8 +326,37 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA max_tokens=0, n=1, presence_penalty=-2, + response_format={"type": "json_object"}, + seed=-9223372036854776000, stop="string", temperature=1, + tool_choice="none", + tools=[ + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + { + "type": "function", + "function": { + "description": "string", + "name": "string", + "parameters": {"foo": "bar"}, + }, + }, + ], top_p=1, user="user-1234", ) diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 9defcadab6..5716a23d54 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -34,7 +34,11 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.create( model="gpt-3.5-turbo", training_file="file-abc123", - hyperparameters={"n_epochs": "auto"}, + hyperparameters={ + "batch_size": "auto", + "learning_rate_multiplier": "auto", + "n_epochs": "auto", + }, suffix="x", validation_file="file-abc123", ) @@ -146,7 +150,11 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: job = await client.fine_tuning.jobs.create( model="gpt-3.5-turbo", training_file="file-abc123", - hyperparameters={"n_epochs": "auto"}, + hyperparameters={ + "batch_size": "auto", + "learning_rate_multiplier": "auto", + "n_epochs": "auto", + }, suffix="x", validation_file="file-abc123", ) diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 7b48e88ed2..b12fd6401e 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -41,6 +41,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_tokens=16, n=1, presence_penalty=-2, + seed=-9223372036854776000, stop="\n", stream=False, suffix="test.", @@ -82,6 +83,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_tokens=16, n=1, presence_penalty=-2, + seed=-9223372036854776000, stop="\n", suffix="test.", temperature=1, @@ -126,6 +128,7 @@ async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenA max_tokens=16, n=1, presence_penalty=-2, + seed=-9223372036854776000, stop="\n", stream=False, suffix="test.", @@ -167,6 +170,7 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA max_tokens=16, n=1, presence_penalty=-2, + seed=-9223372036854776000, stop="\n", suffix="test.", temperature=1, diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 389763586e..d668c2d0c7 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -25,7 +25,7 @@ class TestFiles: def test_method_create(self, client: OpenAI) -> None: file = client.files.create( file=b"raw file contents", - purpose="string", + purpose="fine-tune", ) assert_matches_type(FileObject, file, path=["response"]) @@ -33,7 +33,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.files.with_raw_response.create( file=b"raw file contents", - purpose="string", + purpose="fine-tune", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() @@ -60,6 +60,13 @@ def test_method_list(self, client: OpenAI) -> None: file = client.files.list() assert_matches_type(SyncPage[FileObject], file, path=["response"]) + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + file = client.files.list( + purpose="string", + ) + assert_matches_type(SyncPage[FileObject], file, path=["response"]) + @parametrize def test_raw_response_list(self, client: OpenAI) -> None: response = client.files.with_raw_response.list() @@ -109,7 +116,7 @@ class TestAsyncFiles: async def test_method_create(self, client: AsyncOpenAI) -> None: file = await client.files.create( file=b"raw file contents", - purpose="string", + purpose="fine-tune", ) assert_matches_type(FileObject, file, path=["response"]) @@ -117,7 +124,7 @@ async def test_method_create(self, client: AsyncOpenAI) -> None: async def test_raw_response_create(self, client: AsyncOpenAI) -> None: response = await client.files.with_raw_response.create( file=b"raw file contents", - purpose="string", + purpose="fine-tune", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() @@ -144,6 +151,13 @@ async def test_method_list(self, client: AsyncOpenAI) -> None: file = await client.files.list() assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + @parametrize + async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: + file = await client.files.list( + purpose="string", + ) + assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + @parametrize async def test_raw_response_list(self, client: AsyncOpenAI) -> None: response = await client.files.with_raw_response.list() diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index fa7fb6d533..c7f5e5bcd2 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -31,6 +31,7 @@ def test_method_create_variation(self, client: OpenAI) -> None: def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: image = client.images.create_variation( image=b"raw file contents", + model="dall-e-2", n=1, response_format="url", size="1024x1024", @@ -61,6 +62,7 @@ def test_method_edit_with_all_params(self, client: OpenAI) -> None: image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", mask=b"raw file contents", + model="dall-e-2", n=1, response_format="url", size="1024x1024", @@ -89,9 +91,12 @@ def test_method_generate(self, client: OpenAI) -> None: def test_method_generate_with_all_params(self, client: OpenAI) -> None: image = client.images.generate( prompt="A cute baby sea otter", + model="dall-e-3", n=1, + quality="standard", response_format="url", size="1024x1024", + style="vivid", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -122,6 +127,7 @@ async def test_method_create_variation(self, client: AsyncOpenAI) -> None: async def test_method_create_variation_with_all_params(self, client: AsyncOpenAI) -> None: image = await client.images.create_variation( image=b"raw file contents", + model="dall-e-2", n=1, response_format="url", size="1024x1024", @@ -152,6 +158,7 @@ async def test_method_edit_with_all_params(self, client: AsyncOpenAI) -> None: image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", mask=b"raw file contents", + model="dall-e-2", n=1, response_format="url", size="1024x1024", @@ -180,9 +187,12 @@ async def test_method_generate(self, client: AsyncOpenAI) -> None: async def test_method_generate_with_all_params(self, client: AsyncOpenAI) -> None: image = await client.images.generate( prompt="A cute baby sea otter", + model="dall-e-3", n=1, + quality="standard", response_format="url", size="1024x1024", + style="vivid", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) From 7db6d5644b6ea3ff22ba3c391bbbe05f2b9ebafa Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 6 Nov 2023 21:52:18 +0000 Subject: [PATCH 161/914] v1.1.1 (#689) * feat(client): support passing httpx.Timeout to method timeout argument * fix(api): retreival -> retrieval * fix(docs): use correct branch name in github links * v1.1.1 --- README.md | 2 +- pyproject.toml | 2 +- src/openai/_base_client.py | 2 +- src/openai/_files.py | 2 +- src/openai/_version.py | 2 +- src/openai/resources/audio/speech.py | 6 ++-- src/openai/resources/audio/transcriptions.py | 6 ++-- src/openai/resources/audio/translations.py | 6 ++-- .../resources/beta/assistants/assistants.py | 22 ++++++------ src/openai/resources/beta/assistants/files.py | 18 +++++----- .../resources/beta/threads/messages/files.py | 10 +++--- .../beta/threads/messages/messages.py | 18 +++++----- .../resources/beta/threads/runs/runs.py | 26 +++++++------- .../resources/beta/threads/runs/steps.py | 10 +++--- src/openai/resources/beta/threads/threads.py | 22 ++++++------ src/openai/resources/chat/completions.py | 18 +++++----- src/openai/resources/completions.py | 18 +++++----- src/openai/resources/edits.py | 6 ++-- src/openai/resources/embeddings.py | 6 ++-- src/openai/resources/files.py | 22 ++++++------ src/openai/resources/fine_tunes.py | 34 ++++++++++--------- src/openai/resources/fine_tuning/jobs.py | 22 ++++++------ src/openai/resources/images.py | 14 ++++---- src/openai/resources/models.py | 14 ++++---- src/openai/resources/moderations.py | 6 ++-- src/openai/types/beta/assistant.py | 10 +++--- .../types/beta/assistant_create_params.py | 4 +-- .../types/beta/assistant_update_params.py | 4 +-- .../beta/thread_create_and_run_params.py | 4 +-- src/openai/types/beta/threads/run.py | 4 +-- .../types/beta/threads/run_create_params.py | 4 +-- 31 files changed, 192 insertions(+), 152 deletions(-) diff --git a/README.md b/README.md index 821ecf1ecf..8904d9ed52 100644 --- a/README.md +++ b/README.md @@ -410,7 +410,7 @@ completion = response.parse() # get the object that `chat.completions.create()` print(completion) ``` -These methods return an [`APIResponse`](https://github.com/openai/openai-python/tree/v1/src/openai/_response.py) object. +These methods return an [`APIResponse`](https://github.com/openai/openai-python/src/openai/_response.py) object. ### Configuring the HTTP client diff --git a/pyproject.toml b/pyproject.toml index 9ab62e23fc..c5dd666475 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.1.0" +version = "1.1.1" description = "Client library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 22f90050d7..e37759cdf8 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1537,7 +1537,7 @@ def make_request_options( extra_query: Query | None = None, extra_body: Body | None = None, idempotency_key: str | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, post_parser: PostParser | NotGiven = NOT_GIVEN, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" diff --git a/src/openai/_files.py b/src/openai/_files.py index 49e3536243..94cd553135 100644 --- a/src/openai/_files.py +++ b/src/openai/_files.py @@ -29,7 +29,7 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None: if not is_file_content(obj): prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" raise RuntimeError( - f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python/tree/v1#file-uploads" + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python#file-uploads" ) from None diff --git a/src/openai/_version.py b/src/openai/_version.py index 57548ed376..b4ed828270 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.1.0" +__version__ = "1.1.1" diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 7318e3a2e4..458843866f 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Union from typing_extensions import Literal +import httpx + from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource @@ -38,7 +40,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -105,7 +107,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> HttpxBinaryResponseContent: """ Generates audio from the input text. diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 44d973d0af..d2b4452411 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Union, Mapping, cast from typing_extensions import Literal +import httpx + from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._resource import SyncAPIResource, AsyncAPIResource @@ -39,7 +41,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Transcription: """ Transcribes audio into the input language. @@ -126,7 +128,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Transcription: """ Transcribes audio into the input language. diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index bb37c691fc..fe7f7f2a40 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Union, Mapping, cast from typing_extensions import Literal +import httpx + from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._resource import SyncAPIResource, AsyncAPIResource @@ -38,7 +40,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Translation: """ Translates audio into English. @@ -118,7 +120,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Translation: """ Translates audio into English. diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 03f2759fc2..6b81dc97f3 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, List, Optional from typing_extensions import Literal +import httpx + from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform @@ -50,7 +52,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Assistant: """ Create an assistant with a model and instructions. @@ -119,7 +121,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Assistant: """ Retrieves an assistant. @@ -158,7 +160,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Assistant: """Modifies an assistant. @@ -233,7 +235,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[Assistant]: """Returns a list of assistants. @@ -295,7 +297,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsssitantDeleted: """ Delete an assistant. @@ -343,7 +345,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Assistant: """ Create an assistant with a model and instructions. @@ -412,7 +414,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Assistant: """ Retrieves an assistant. @@ -451,7 +453,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Assistant: """Modifies an assistant. @@ -526,7 +528,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Assistant, AsyncCursorPage[Assistant]]: """Returns a list of assistants. @@ -588,7 +590,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsssitantDeleted: """ Delete an assistant. diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index b1953525e8..5ac5897ca3 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING from typing_extensions import Literal +import httpx + from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource @@ -41,7 +43,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AssistantFile: """ Create an assistant file by attaching a @@ -81,7 +83,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AssistantFile: """ Retrieves an AssistantFile. @@ -117,7 +119,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[AssistantFile]: """ Returns a list of assistant files. @@ -179,7 +181,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileDeleteResponse: """ Delete an assistant file. @@ -220,7 +222,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AssistantFile: """ Create an assistant file by attaching a @@ -260,7 +262,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AssistantFile: """ Retrieves an AssistantFile. @@ -296,7 +298,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[AssistantFile, AsyncCursorPage[AssistantFile]]: """ Returns a list of assistant files. @@ -358,7 +360,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileDeleteResponse: """ Delete an assistant file. diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index 70166eb7b2..e028a6fda7 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING from typing_extensions import Literal +import httpx + from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource @@ -37,7 +39,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> MessageFile: """ Retrieves a message file. @@ -74,7 +76,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[MessageFile]: """Returns a list of message files. @@ -146,7 +148,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> MessageFile: """ Retrieves a message file. @@ -183,7 +185,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[MessageFile, AsyncCursorPage[MessageFile]]: """Returns a list of message files. diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index caec03f484..30ae072512 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, List, Optional from typing_extensions import Literal +import httpx + from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform @@ -47,7 +49,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ThreadMessage: """ Create a message. @@ -104,7 +106,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ThreadMessage: """ Retrieve a message. @@ -138,7 +140,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ThreadMessage: """ Modifies a message. @@ -180,7 +182,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[ThreadMessage]: """ Returns a list of messages for a given thread. @@ -255,7 +257,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ThreadMessage: """ Create a message. @@ -312,7 +314,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ThreadMessage: """ Retrieve a message. @@ -346,7 +348,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ThreadMessage: """ Modifies a message. @@ -388,7 +390,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[ThreadMessage, AsyncCursorPage[ThreadMessage]]: """ Returns a list of messages for a given thread. diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 370056cbf4..969bfab70a 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, List, Optional from typing_extensions import Literal +import httpx + from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform @@ -49,7 +51,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Create a run. @@ -112,7 +114,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Retrieves a run. @@ -146,7 +148,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Modifies a run. @@ -188,7 +190,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[Run]: """ Returns a list of runs belonging to a thread. @@ -250,7 +252,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Cancels a run that is `in_progress`. @@ -284,7 +286,7 @@ def submit_tool_outputs( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ When a run has the `status: "requires_action"` and `required_action.type` is @@ -339,7 +341,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Create a run. @@ -402,7 +404,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Retrieves a run. @@ -436,7 +438,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Modifies a run. @@ -478,7 +480,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Run, AsyncCursorPage[Run]]: """ Returns a list of runs belonging to a thread. @@ -540,7 +542,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Cancels a run that is `in_progress`. @@ -574,7 +576,7 @@ async def submit_tool_outputs( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ When a run has the `status: "requires_action"` and `required_action.type` is diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index bc6fd7fdc9..4fcc87a0ff 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING from typing_extensions import Literal +import httpx + from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource @@ -37,7 +39,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RunStep: """ Retrieves a run step. @@ -74,7 +76,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[RunStep]: """ Returns a list of run steps belonging to a run. @@ -145,7 +147,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> RunStep: """ Retrieves a run step. @@ -182,7 +184,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[RunStep, AsyncCursorPage[RunStep]]: """ Returns a list of run steps belonging to a run. diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 286630d81c..9469fc0513 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -4,6 +4,8 @@ from typing import TYPE_CHECKING, List, Optional +import httpx + from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse from .messages import ( Messages, @@ -52,7 +54,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Thread: """ Create a thread. @@ -99,7 +101,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Thread: """ Retrieves a thread. @@ -132,7 +134,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Thread: """ Modifies a thread. @@ -170,7 +172,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ThreadDeleted: """ Delete a thread. @@ -207,7 +209,7 @@ def create_and_run( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Create a thread and run it in one request. @@ -285,7 +287,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Thread: """ Create a thread. @@ -332,7 +334,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Thread: """ Retrieves a thread. @@ -365,7 +367,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Thread: """ Modifies a thread. @@ -403,7 +405,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ThreadDeleted: """ Delete a thread. @@ -440,7 +442,7 @@ async def create_and_run( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ Create a thread and run it in one request. diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 2ecde23ce1..a46e7e70d6 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Dict, List, Union, Optional, overload from typing_extensions import Literal +import httpx + from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import required_args, maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource @@ -75,7 +77,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion: """ Creates a model response for the given chat conversation. @@ -233,7 +235,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Stream[ChatCompletionChunk]: """ Creates a model response for the given chat conversation. @@ -391,7 +393,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | Stream[ChatCompletionChunk]: """ Creates a model response for the given chat conversation. @@ -549,7 +551,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | Stream[ChatCompletionChunk]: return self._post( "/chat/completions", @@ -634,7 +636,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion: """ Creates a model response for the given chat conversation. @@ -792,7 +794,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncStream[ChatCompletionChunk]: """ Creates a model response for the given chat conversation. @@ -950,7 +952,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: """ Creates a model response for the given chat conversation. @@ -1108,7 +1110,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: return await self._post( "/chat/completions", diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index f1a938ba9a..baf6f04fef 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Dict, List, Union, Optional, overload from typing_extensions import Literal +import httpx + from ..types import Completion, completion_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import required_args, maybe_transform @@ -66,7 +68,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Completion: """ Creates a completion for the provided prompt and parameters. @@ -228,7 +230,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Stream[Completion]: """ Creates a completion for the provided prompt and parameters. @@ -390,7 +392,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Completion | Stream[Completion]: """ Creates a completion for the provided prompt and parameters. @@ -552,7 +554,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Completion | Stream[Completion]: return self._post( "/completions", @@ -634,7 +636,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Completion: """ Creates a completion for the provided prompt and parameters. @@ -796,7 +798,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncStream[Completion]: """ Creates a completion for the provided prompt and parameters. @@ -958,7 +960,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Completion | AsyncStream[Completion]: """ Creates a completion for the provided prompt and parameters. @@ -1120,7 +1122,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Completion | AsyncStream[Completion]: return await self._post( "/completions", diff --git a/src/openai/resources/edits.py b/src/openai/resources/edits.py index 5c114c915f..eafaa82fdf 100644 --- a/src/openai/resources/edits.py +++ b/src/openai/resources/edits.py @@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, Union, Optional from typing_extensions import Literal +import httpx + from ..types import Edit, edit_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform @@ -43,7 +45,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Edit: """ Creates a new edit for the provided input, instruction, and parameters. @@ -122,7 +124,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Edit: """ Creates a new edit for the provided input, instruction, and parameters. diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index dd540fc796..c31ad9d931 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, List, Union, cast from typing_extensions import Literal +import httpx + from ..types import CreateEmbeddingResponse, embedding_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import is_given, maybe_transform @@ -40,7 +42,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> CreateEmbeddingResponse: """ Creates an embedding vector representing the input text. @@ -133,7 +135,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> CreateEmbeddingResponse: """ Creates an embedding vector representing the input text. diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 16d3944a12..b317845c3a 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, Mapping, cast from typing_extensions import Literal +import httpx + from ..types import FileObject, FileDeleted, file_list_params, file_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import extract_files, maybe_transform, deepcopy_minimal @@ -37,7 +39,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileObject: """Upload a file that can be used across various endpoints/features. @@ -104,7 +106,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileObject: """ Returns information about a specific file. @@ -135,7 +137,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncPage[FileObject]: """ Returns a list of files that belong to the user's organization. @@ -173,7 +175,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileDeleted: """ Delete a file. @@ -204,7 +206,7 @@ def retrieve_content( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> str: """ Returns the contents of the specified file. @@ -268,7 +270,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileObject: """Upload a file that can be used across various endpoints/features. @@ -335,7 +337,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileObject: """ Returns information about a specific file. @@ -366,7 +368,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[FileObject, AsyncPage[FileObject]]: """ Returns a list of files that belong to the user's organization. @@ -404,7 +406,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileDeleted: """ Delete a file. @@ -435,7 +437,7 @@ async def retrieve_content( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> str: """ Returns the contents of the specified file. diff --git a/src/openai/resources/fine_tunes.py b/src/openai/resources/fine_tunes.py index 28f4225102..91c8201cbb 100644 --- a/src/openai/resources/fine_tunes.py +++ b/src/openai/resources/fine_tunes.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, List, Union, Optional, overload from typing_extensions import Literal +import httpx + from ..types import ( FineTune, FineTuneEvent, @@ -53,7 +55,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTune: """ Creates a job that fine-tunes a specified model from a given dataset. @@ -197,7 +199,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTune: """ Gets info about the fine-tune job. @@ -229,7 +231,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncPage[FineTune]: """List your organization's fine-tuning jobs""" return self._get_api_list( @@ -250,7 +252,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTune: """ Immediately cancel a fine-tune job. @@ -283,7 +285,7 @@ def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = 86400, + timeout: float | httpx.Timeout | None | NotGiven = 86400, ) -> FineTuneEventsListResponse: """ Get fine-grained status updates for a fine-tune job. @@ -318,7 +320,7 @@ def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = 86400, + timeout: float | httpx.Timeout | None | NotGiven = 86400, ) -> Stream[FineTuneEvent]: """ Get fine-grained status updates for a fine-tune job. @@ -353,7 +355,7 @@ def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = 86400, + timeout: float | httpx.Timeout | None | NotGiven = 86400, ) -> FineTuneEventsListResponse | Stream[FineTuneEvent]: """ Get fine-grained status updates for a fine-tune job. @@ -387,7 +389,7 @@ def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = 86400, + timeout: float | httpx.Timeout | None | NotGiven = 86400, ) -> FineTuneEventsListResponse | Stream[FineTuneEvent]: return self._get( f"/fine-tunes/{fine_tune_id}/events", @@ -431,7 +433,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTune: """ Creates a job that fine-tunes a specified model from a given dataset. @@ -575,7 +577,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTune: """ Gets info about the fine-tune job. @@ -607,7 +609,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[FineTune, AsyncPage[FineTune]]: """List your organization's fine-tuning jobs""" return self._get_api_list( @@ -628,7 +630,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTune: """ Immediately cancel a fine-tune job. @@ -661,7 +663,7 @@ async def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = 86400, + timeout: float | httpx.Timeout | None | NotGiven = 86400, ) -> FineTuneEventsListResponse: """ Get fine-grained status updates for a fine-tune job. @@ -696,7 +698,7 @@ async def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = 86400, + timeout: float | httpx.Timeout | None | NotGiven = 86400, ) -> AsyncStream[FineTuneEvent]: """ Get fine-grained status updates for a fine-tune job. @@ -731,7 +733,7 @@ async def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = 86400, + timeout: float | httpx.Timeout | None | NotGiven = 86400, ) -> FineTuneEventsListResponse | AsyncStream[FineTuneEvent]: """ Get fine-grained status updates for a fine-tune job. @@ -765,7 +767,7 @@ async def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = 86400, + timeout: float | httpx.Timeout | None | NotGiven = 86400, ) -> FineTuneEventsListResponse | AsyncStream[FineTuneEvent]: return await self._get( f"/fine-tunes/{fine_tune_id}/events", diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index b721c892b5..3d9aed8d91 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Union, Optional from typing_extensions import Literal +import httpx + from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource @@ -45,7 +47,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTuningJob: """ Creates a job that fine-tunes a specified model from a given dataset. @@ -126,7 +128,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTuningJob: """ Get info about a fine-tuning job. @@ -160,7 +162,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[FineTuningJob]: """ List your organization's fine-tuning jobs @@ -206,7 +208,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTuningJob: """ Immediately cancel a fine-tune job. @@ -239,7 +241,7 @@ def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[FineTuningJobEvent]: """ Get status updates for a fine-tuning job. @@ -297,7 +299,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTuningJob: """ Creates a job that fine-tunes a specified model from a given dataset. @@ -378,7 +380,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTuningJob: """ Get info about a fine-tuning job. @@ -412,7 +414,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[FineTuningJob, AsyncCursorPage[FineTuningJob]]: """ List your organization's fine-tuning jobs @@ -458,7 +460,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTuningJob: """ Immediately cancel a fine-tune job. @@ -491,7 +493,7 @@ def list_events( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[FineTuningJobEvent, AsyncCursorPage[FineTuningJobEvent]]: """ Get status updates for a fine-tuning job. diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 9d4ae9936a..94b1bc1fc8 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Union, Mapping, Optional, cast from typing_extensions import Literal +import httpx + from ..types import ( ImagesResponse, image_edit_params, @@ -44,7 +46,7 @@ def create_variation( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: """ Creates a variation of a given image. @@ -120,7 +122,7 @@ def edit( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: """ Creates an edited or extended image given an original image and a prompt. @@ -204,7 +206,7 @@ def generate( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: """ Creates an image given a prompt. @@ -289,7 +291,7 @@ async def create_variation( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: """ Creates a variation of a given image. @@ -365,7 +367,7 @@ async def edit( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: """ Creates an edited or extended image given an original image and a prompt. @@ -449,7 +451,7 @@ async def generate( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: """ Creates an image given a prompt. diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index 689bbd6621..2d04bdc5cc 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -4,6 +4,8 @@ from typing import TYPE_CHECKING +import httpx + from ..types import Model, ModelDeleted from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._resource import SyncAPIResource, AsyncAPIResource @@ -33,7 +35,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Model: """ Retrieves a model instance, providing basic information about the model such as @@ -64,7 +66,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncPage[Model]: """ Lists the currently available models, and provides basic information about each @@ -88,7 +90,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ModelDeleted: """Delete a fine-tuned model. @@ -129,7 +131,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Model: """ Retrieves a model instance, providing basic information about the model such as @@ -160,7 +162,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Model, AsyncPage[Model]]: """ Lists the currently available models, and provides basic information about each @@ -184,7 +186,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ModelDeleted: """Delete a fine-tuned model. diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 1ee3e72564..12a7c68a7b 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, List, Union from typing_extensions import Literal +import httpx + from ..types import ModerationCreateResponse, moderation_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform @@ -35,7 +37,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ModerationCreateResponse: """ Classifies if text violates OpenAI's Content Policy @@ -93,7 +95,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ModerationCreateResponse: """ Classifies if text violates OpenAI's Content Policy diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 9130b60363..e15282a69a 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -6,7 +6,7 @@ from ..._models import BaseModel -__all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetreival", "ToolFunction", "ToolFunctionFunction"] +__all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetrieval", "ToolFunction", "ToolFunctionFunction"] class ToolCodeInterpreter(BaseModel): @@ -14,9 +14,9 @@ class ToolCodeInterpreter(BaseModel): """The type of tool being defined: `code_interpreter`""" -class ToolRetreival(BaseModel): - type: Literal["retreival"] - """The type of tool being defined: `retreival`""" +class ToolRetrieval(BaseModel): + type: Literal["retrieval"] + """The type of tool being defined: `retrieval`""" class ToolFunctionFunction(BaseModel): @@ -54,7 +54,7 @@ class ToolFunction(BaseModel): """The type of tool being defined: `function`""" -Tool = Union[ToolCodeInterpreter, ToolRetreival, ToolFunction] +Tool = Union[ToolCodeInterpreter, ToolRetrieval, ToolFunction] class Assistant(BaseModel): diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 8b8f025c39..8272d5eb4d 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -67,8 +67,8 @@ class ToolAssistantToolsCode(TypedDict, total=False): class ToolAssistantToolsRetrieval(TypedDict, total=False): - type: Required[Literal["retreival"]] - """The type of tool being defined: `retreival`""" + type: Required[Literal["retrieval"]] + """The type of tool being defined: `retrieval`""" class ToolAssistantToolsFunctionFunction(TypedDict, total=False): diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index fa838f51e3..3916833b77 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -69,8 +69,8 @@ class ToolAssistantToolsCode(TypedDict, total=False): class ToolAssistantToolsRetrieval(TypedDict, total=False): - type: Required[Literal["retreival"]] - """The type of tool being defined: `retreival`""" + type: Required[Literal["retrieval"]] + """The type of tool being defined: `retrieval`""" class ToolAssistantToolsFunctionFunction(TypedDict, total=False): diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 2955343ec0..d7391d4d62 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -106,8 +106,8 @@ class ToolAssistantToolsCode(TypedDict, total=False): class ToolAssistantToolsRetrieval(TypedDict, total=False): - type: Required[Literal["retreival"]] - """The type of tool being defined: `retreival`""" + type: Required[Literal["retrieval"]] + """The type of tool being defined: `retrieval`""" class ToolAssistantToolsFunctionFunction(TypedDict, total=False): diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index d06152fa5b..d30a32ec97 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -47,8 +47,8 @@ class ToolAssistantToolsCode(BaseModel): class ToolAssistantToolsRetrieval(BaseModel): - type: Literal["retreival"] - """The type of tool being defined: `retreival`""" + type: Literal["retrieval"] + """The type of tool being defined: `retrieval`""" class ToolAssistantToolsFunctionFunction(BaseModel): diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 41d2eeea03..cf1bb9f05d 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -58,8 +58,8 @@ class ToolAssistantToolsCode(TypedDict, total=False): class ToolAssistantToolsRetrieval(TypedDict, total=False): - type: Required[Literal["retreival"]] - """The type of tool being defined: `retreival`""" + type: Required[Literal["retrieval"]] + """The type of tool being defined: `retrieval`""" class ToolAssistantToolsFunctionFunction(TypedDict, total=False): From e0aafc6c1a45334ac889fe3e54957d309c3af93f Mon Sep 17 00:00:00 2001 From: David Schnurr Date: Mon, 6 Nov 2023 15:58:23 -0800 Subject: [PATCH 162/914] codeowners (#696) --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..3ce5f8d004 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @openai/sdks-team From e9c26d1e253ff54ea50ea1b11d87c35405b24dda Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 6 Nov 2023 23:32:33 +0000 Subject: [PATCH 163/914] ci: setup automatic releases (#693) --- .github/workflows/create-releases.yml | 37 ++++++++++++++++ .github/workflows/publish-pypi.yml | 27 +++++++++++ .github/workflows/release-doctor.yml | 23 ++++++++++ .release-please-manifest.json | 3 ++ bin/check-release-environment | 25 +++++++++++ bin/publish-pypi | 6 +++ examples/audio.py | 34 ++++++++++++++ release-please-config.json | 64 +++++++++++++++++++++++++++ src/openai/_version.py | 2 +- 9 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/create-releases.yml create mode 100644 .github/workflows/publish-pypi.yml create mode 100644 .github/workflows/release-doctor.yml create mode 100644 .release-please-manifest.json create mode 100644 bin/check-release-environment create mode 100644 bin/publish-pypi create mode 100755 examples/audio.py create mode 100644 release-please-config.json diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml new file mode 100644 index 0000000000..7dbae006c0 --- /dev/null +++ b/.github/workflows/create-releases.yml @@ -0,0 +1,37 @@ +name: Create releases +on: + push: + branches: + - main + +jobs: + release: + name: release + if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' + runs-on: ubuntu-latest + environment: publish + + steps: + - uses: actions/checkout@v3 + + - uses: stainless-api/trigger-release-please@v1 + id: release + with: + repo: ${{ github.event.repository.full_name }} + stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} + + - name: Install Rye + if: ${{ steps.release.outputs.releases_created }} + run: | + curl -sSf https://rye-up.com/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: 0.15.2 + RYE_INSTALL_OPTION: "--yes" + + - name: Publish to PyPI + if: ${{ steps.release.outputs.releases_created }} + run: | + bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 0000000000..026ed29c22 --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,27 @@ +# workflow for re-running publishing to PyPI in case it fails for some reason +# you can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml +name: Publish PyPI +on: + workflow_dispatch: + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install Rye + run: | + curl -sSf https://rye-up.com/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: 0.15.2 + RYE_INSTALL_OPTION: "--yes" + + - name: Publish to PyPI + run: | + bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000000..108aa5973a --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,23 @@ +name: Release Doctor +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + environment: publish + if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v3 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }} + PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000000..b55c11f05d --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.1.1" +} \ No newline at end of file diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000000..b0c8d34f0c --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${STAINLESS_API_KEY}" ]; then + errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.") +fi + +if [ -z "${PYPI_TOKEN}" ]; then + errors+=("The OPENAI_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +fi + +len=${#errors[@]} + +if [[ len -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/bin/publish-pypi b/bin/publish-pypi new file mode 100644 index 0000000000..826054e924 --- /dev/null +++ b/bin/publish-pypi @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux +mkdir -p dist +rye build --clean +rye publish --yes --token=$PYPI_TOKEN diff --git a/examples/audio.py b/examples/audio.py new file mode 100755 index 0000000000..a5f535dcd6 --- /dev/null +++ b/examples/audio.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +from pathlib import Path + +from openai import OpenAI + +# gets OPENAI_API_KEY from your environment variables +openai = OpenAI() + +speech_file_path = Path(__file__).parent / "speech.mp3" + + +def main() -> None: + # Create text-to-speech audio file + response = openai.audio.speech.create( + model="tts-1", voice="alloy", input="the quick brown fox jumped over the lazy dogs" + ) + + response.stream_to_file(speech_file_path) + + # Create transcription from audio file + transcription = openai.audio.transcriptions.create(model="whisper-1", file=speech_file_path) + print(transcription.text) + + # Create translation from audio file + translation = openai.audio.translations.create( + model="whisper-1", + file=speech_file_path, + ) + print(translation.text) + + +if __name__ == "__main__": + main() diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000000..5c66d801f5 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,64 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "python", + "extra-files": [ + "src/openai/_version.py" + ] +} \ No newline at end of file diff --git a/src/openai/_version.py b/src/openai/_version.py index b4ed828270..5147fcd1f1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.1.1" +__version__ = "1.1.1" # x-release-please-version From f5ef2b3a45ba7764d04dc93b87379e6e2f3063e7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:04:47 +0000 Subject: [PATCH 164/914] fix: asssitant_deleted -> assistant_deleted (#711) --- api.md | 4 ++-- src/openai/resources/beta/assistants/assistants.py | 10 +++++----- src/openai/types/beta/__init__.py | 2 +- .../{asssitant_deleted.py => assistant_deleted.py} | 4 ++-- tests/api_resources/beta/test_assistants.py | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) rename src/openai/types/beta/{asssitant_deleted.py => assistant_deleted.py} (75%) diff --git a/api.md b/api.md index 818ae73b31..95e9922129 100644 --- a/api.md +++ b/api.md @@ -197,7 +197,7 @@ Methods: Types: ```python -from openai.types.beta import Assistant, AsssitantDeleted +from openai.types.beta import Assistant, AssistantDeleted ``` Methods: @@ -206,7 +206,7 @@ Methods: - client.beta.assistants.retrieve(assistant_id) -> Assistant - client.beta.assistants.update(assistant_id, \*\*params) -> Assistant - client.beta.assistants.list(\*\*params) -> SyncCursorPage[Assistant] -- client.beta.assistants.delete(assistant_id) -> AsssitantDeleted +- client.beta.assistants.delete(assistant_id) -> AssistantDeleted ### Files diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 6b81dc97f3..efa711ecf4 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -15,7 +15,7 @@ from ....pagination import SyncCursorPage, AsyncCursorPage from ....types.beta import ( Assistant, - AsssitantDeleted, + AssistantDeleted, assistant_list_params, assistant_create_params, assistant_update_params, @@ -298,7 +298,7 @@ def delete( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsssitantDeleted: + ) -> AssistantDeleted: """ Delete an assistant. @@ -317,7 +317,7 @@ def delete( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AsssitantDeleted, + cast_to=AssistantDeleted, ) @@ -591,7 +591,7 @@ async def delete( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsssitantDeleted: + ) -> AssistantDeleted: """ Delete an assistant. @@ -610,7 +610,7 @@ async def delete( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AsssitantDeleted, + cast_to=AssistantDeleted, ) diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index 8b834f286d..c03d823b8c 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -5,7 +5,7 @@ from .thread import Thread as Thread from .assistant import Assistant as Assistant from .thread_deleted import ThreadDeleted as ThreadDeleted -from .asssitant_deleted import AsssitantDeleted as AsssitantDeleted +from .assistant_deleted import AssistantDeleted as AssistantDeleted from .thread_create_params import ThreadCreateParams as ThreadCreateParams from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams from .assistant_list_params import AssistantListParams as AssistantListParams diff --git a/src/openai/types/beta/asssitant_deleted.py b/src/openai/types/beta/assistant_deleted.py similarity index 75% rename from src/openai/types/beta/asssitant_deleted.py rename to src/openai/types/beta/assistant_deleted.py index 258210e7fe..23802caaf6 100644 --- a/src/openai/types/beta/asssitant_deleted.py +++ b/src/openai/types/beta/assistant_deleted.py @@ -4,10 +4,10 @@ from ..._models import BaseModel -__all__ = ["AsssitantDeleted"] +__all__ = ["AssistantDeleted"] -class AsssitantDeleted(BaseModel): +class AssistantDeleted(BaseModel): id: str deleted: bool diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 5bbad1d7dd..82e975b46d 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -10,7 +10,7 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta import Assistant, AsssitantDeleted +from openai.types.beta import Assistant, AssistantDeleted base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" @@ -123,7 +123,7 @@ def test_method_delete(self, client: OpenAI) -> None: assistant = client.beta.assistants.delete( "string", ) - assert_matches_type(AsssitantDeleted, assistant, path=["response"]) + assert_matches_type(AssistantDeleted, assistant, path=["response"]) @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: @@ -132,7 +132,7 @@ def test_raw_response_delete(self, client: OpenAI) -> None: ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() - assert_matches_type(AsssitantDeleted, assistant, path=["response"]) + assert_matches_type(AssistantDeleted, assistant, path=["response"]) class TestAsyncAssistants: @@ -242,7 +242,7 @@ async def test_method_delete(self, client: AsyncOpenAI) -> None: assistant = await client.beta.assistants.delete( "string", ) - assert_matches_type(AsssitantDeleted, assistant, path=["response"]) + assert_matches_type(AssistantDeleted, assistant, path=["response"]) @parametrize async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: @@ -251,4 +251,4 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() - assert_matches_type(AsssitantDeleted, assistant, path=["response"]) + assert_matches_type(AssistantDeleted, assistant, path=["response"]) From 86b263f1f4ed3987123e4884bbc4457f7bb22c9f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:52:10 +0000 Subject: [PATCH 165/914] chore(internal): fix some typos (#718) --- src/openai/_utils/_transform.py | 6 +++--- tests/test_extract_files.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index db40bff27f..dc497ea329 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -95,7 +95,7 @@ class Params(TypedDict, total=False): return cast(_T, transformed) -def _get_annoted_type(type_: type) -> type | None: +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, ...]]` @@ -115,7 +115,7 @@ def _maybe_transform_key(key: str, type_: type) -> str: Note: this function only looks at `Annotated` types that contain `PropertInfo` metadata. """ - annotated_type = _get_annoted_type(type_) + annotated_type = _get_annotated_type(type_) if annotated_type is None: # no `Annotated` definition for this type, no transformation needed return key @@ -174,7 +174,7 @@ def _transform_recursive( def _transform_value(data: object, type_: type) -> object: - annotated_type = _get_annoted_type(type_) + annotated_type = _get_annotated_type(type_) if annotated_type is None: return data diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index 554487da42..0f6fb04d7d 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -54,7 +54,7 @@ def test_multiple_files() -> None: [], ], ], - ids=["dict expecting array", "arraye expecting dict", "unknown keys"], + ids=["dict expecting array", "array expecting dict", "unknown keys"], ) def test_ignores_incorrect_paths( query: dict[str, object], From a816967337fcedc6cea7efd7451640c30e705604 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 7 Nov 2023 17:35:58 +0000 Subject: [PATCH 166/914] chore(docs): fix github links (#719) --- README.md | 2 +- src/openai/_files.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8904d9ed52..cedbc72337 100644 --- a/README.md +++ b/README.md @@ -410,7 +410,7 @@ completion = response.parse() # get the object that `chat.completions.create()` print(completion) ``` -These methods return an [`APIResponse`](https://github.com/openai/openai-python/src/openai/_response.py) object. +These methods return an [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object. ### Configuring the HTTP client diff --git a/src/openai/_files.py b/src/openai/_files.py index 94cd553135..bebfb19501 100644 --- a/src/openai/_files.py +++ b/src/openai/_files.py @@ -29,7 +29,7 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None: if not is_file_content(obj): prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" raise RuntimeError( - f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python#file-uploads" + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python/tree/main#file-uploads" ) from None From 4f89244dbf937ba1ba1e7688480813acc05b85a0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:59:29 +0000 Subject: [PATCH 167/914] fix(api): accidentally required params, add new models & other fixes (#729) - Mark chat completion image url as required - Add system_fingerprint to chat completions --- src/openai/resources/chat/completions.py | 16 ++++++++++++++++ src/openai/types/chat/chat_completion_chunk.py | 7 +++++++ .../chat_completion_content_part_image_param.py | 6 +++--- .../types/chat/completion_create_params.py | 2 ++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index a46e7e70d6..75e0d66d58 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -43,6 +43,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", @@ -201,6 +203,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", @@ -359,6 +363,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", @@ -517,6 +523,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", @@ -602,6 +610,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", @@ -760,6 +770,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", @@ -918,6 +930,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", @@ -1076,6 +1090,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index bbc46a37bb..568f530280 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -109,3 +109,10 @@ class ChatCompletionChunk(BaseModel): object: Literal["chat.completion.chunk"] """The object type, which is always `chat.completion.chunk`.""" + + system_fingerprint: Optional[str] = None + """This fingerprint represents the backend configuration that the model runs with. + + Can be used in conjunction with the `seed` request parameter to understand when + backend changes have been made that might impact determinism. + """ diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py index 2051786562..eb9bd52689 100644 --- a/src/openai/types/chat/chat_completion_content_part_image_param.py +++ b/src/openai/types/chat/chat_completion_content_part_image_param.py @@ -8,12 +8,12 @@ class ImageURL(TypedDict, total=False): + url: Required[str] + """Either a URL of the image or the base64 encoded image data.""" + detail: Literal["auto", "low", "high"] """Specifies the detail level of the image.""" - url: str - """Either a URL of the image or the base64 encoded image data.""" - class ChatCompletionContentPartImageParam(TypedDict, total=False): image_url: Required[ImageURL] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 44b1abe576..b310761077 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -35,6 +35,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): Union[ str, Literal[ + "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4", "gpt-4-0314", "gpt-4-0613", From 97810fcb129ba3cc90cdb5ed06351c8a5b738ecd Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 12:00:05 +0000 Subject: [PATCH 168/914] release: 1.1.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b55c11f05d..9c6a481f5b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.1.1" + ".": "1.1.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..8c97964977 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +## 1.1.2 (2023-11-08) + +Full Changelog: [v1.1.1...v1.1.2](https://github.com/openai/openai-python/compare/v1.1.1...v1.1.2) + +### Bug Fixes + +* **api:** accidentally required params, add new models & other fixes ([#729](https://github.com/openai/openai-python/issues/729)) ([03c3e03](https://github.com/openai/openai-python/commit/03c3e03fc758cf4e59b81edf73a2618d80b560b7)) +* asssitant_deleted -> assistant_deleted ([#711](https://github.com/openai/openai-python/issues/711)) ([287b51e](https://github.com/openai/openai-python/commit/287b51e4f7cede9667c118007de1275eb04772c6)) + + +### Chores + +* **docs:** fix github links ([#719](https://github.com/openai/openai-python/issues/719)) ([0cda8ca](https://github.com/openai/openai-python/commit/0cda8cab718d53d7dc0604d9fac52838c9391565)) +* **internal:** fix some typos ([#718](https://github.com/openai/openai-python/issues/718)) ([894ad87](https://github.com/openai/openai-python/commit/894ad874aaa5d74530f561896ff31f68693418da)) diff --git a/pyproject.toml b/pyproject.toml index c5dd666475..0861b1278b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.1.1" +version = "1.1.2" description = "Client library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5147fcd1f1..848573b8a1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.1.1" # x-release-please-version +__version__ = "1.1.2" # x-release-please-version From ba424470504a470d1b0902847bcfae7149deae33 Mon Sep 17 00:00:00 2001 From: David Schnurr Date: Wed, 8 Nov 2023 10:31:05 -0800 Subject: [PATCH 169/914] issue templates (#698) * issue templates * Fix --- .github/ISSUE_TEMPLATE/bug_report.yml | 64 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 7 +++ .github/ISSUE_TEMPLATE/feature_request.yml | 28 ++++++++++ .github/pull_request_template.md | 10 ++++ 4 files changed, 109 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..fa09dbe5b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,64 @@ +name: Bug report +description: Report an issue or bug with this library +labels: ['bug'] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: checkboxes + id: non_api + attributes: + label: Confirm this is an issue with the Python library and not an underlying OpenAI API + description: Issues with the underlying OpenAI API should be reported on our [Developer Community](https://community.openai.com/c/api/7) + options: + - label: This is an issue with the Python library + required: true + - type: textarea + id: what-happened + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is, and any additional context. + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: repro-steps + attributes: + label: To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. Fetch a '...' + 2. Update the '....' + 3. See error + validations: + required: true + - type: textarea + id: code-snippets + attributes: + label: Code snippets + description: If applicable, add code snippets to help explain your problem. + render: Python + validations: + required: false + - type: input + id: os + attributes: + label: OS + placeholder: macOS + validations: + required: true + - type: input + id: language-version + attributes: + label: Python version + placeholder: Python v3.11.4 + validations: + required: true + - type: input + id: lib-version + attributes: + label: Library version + placeholder: openai v1.0.1 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..0498cf7f6f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +blank_issues_enabled: false +contact_links: + - name: OpenAI support + url: https://help.openai.com/ + about: | + Please only file issues here that you believe represent actual bugs or feature requests for the OpenAI Python library. + If you're having general trouble with the OpenAI API, please visit our help center to get support. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..b529547d08 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,28 @@ +name: Feature request +description: Suggest an idea for this library +labels: ['feature-request'] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request! + - type: checkboxes + id: non_api + attributes: + label: Confirm this is a feature request for the Python library and not the underlying OpenAI API. + description: Feature requests for the underlying OpenAI API should be reported on our [Developer Community](https://community.openai.com/c/api/7) + options: + - label: This is a feature request for the Python library + required: true + - type: textarea + id: feature + attributes: + label: Describe the feature or improvement you're requesting + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..4416b1e547 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ + + + + + +- [ ] I understand that this repository is auto-generated and my pull request may not be merged + +## Changes being requested + +## Additional context & links From ebdcc3f5df0dae2d84664a55200006c00f55bd10 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 18:23:59 +0000 Subject: [PATCH 170/914] chore(internal): improve github devcontainer setup (#737) --- .devcontainer/Dockerfile | 28 +++++----------------------- .devcontainer/devcontainer.json | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 73f1b9f237..6eb007253c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,27 +1,9 @@ -# syntax=docker/dockerfile:1 -FROM debian:bookworm-slim +ARG VARIANT="3.9" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} -RUN apt-get update && apt-get install -y \ - libxkbcommon0 \ - ca-certificates \ - make \ - curl \ - git \ - unzip \ - libc++1 \ - vim \ - termcap \ - && apt-get clean autoclean +USER vscode RUN curl -sSf https://rye-up.com/get | RYE_VERSION="0.15.2" RYE_INSTALL_OPTION="--yes" bash -ENV PATH=/root/.rye/shims:$PATH +ENV PATH=/home/vscode/.rye/shims:$PATH -WORKDIR /workspace - -COPY README.md .python-version pyproject.toml requirements.lock requirements-dev.lock /workspace/ - -RUN rye sync --all-features - -COPY . /workspace - -CMD ["rye", "shell"] +RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d55fc4d671..b9da964dc1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,7 +3,26 @@ { "name": "Debian", "build": { - "dockerfile": "Dockerfile" + "dockerfile": "Dockerfile", + "context": ".." + }, + + "postStartCommand": "rye sync --all-features", + + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "python.pythonPath": ".venv/bin/python", + "python.typeChecking": "basic", + "terminal.integrated.env.linux": { + "PATH": "/home/vscode/.rye/shims:${env:PATH}" + } + } + } } // Features to add to the dev container. More info: https://containers.dev/features. From ee28c46f8178951dc72591883d07927bf27d0199 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 19:16:55 +0000 Subject: [PATCH 171/914] fix(api): update embedding response object type (#739) --- src/openai/types/create_embedding_response.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/types/create_embedding_response.py b/src/openai/types/create_embedding_response.py index 7382bed6b9..bf64037e16 100644 --- a/src/openai/types/create_embedding_response.py +++ b/src/openai/types/create_embedding_response.py @@ -24,8 +24,8 @@ class CreateEmbeddingResponse(BaseModel): model: str """The name of the model used to generate the embedding.""" - object: Literal["embedding"] - """The object type, which is always "embedding".""" + object: Literal["list"] + """The object type, which is always "list".""" usage: Usage """The usage information for the request.""" From a228a539a2efc85ac73dfae06443d079383fb82e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 19:51:57 +0000 Subject: [PATCH 172/914] feat(api): unify function types (#741) Also fixes an enum `assistant.run.step` -> `thread.run.step` --- api.md | 6 ++ src/openai/resources/chat/completions.py | 84 ++++++++++++++++--- src/openai/types/__init__.py | 2 + src/openai/types/beta/assistant.py | 35 +------- .../types/beta/assistant_create_params.py | 35 +------- .../types/beta/assistant_update_params.py | 35 +------- .../beta/thread_create_and_run_params.py | 35 +------- src/openai/types/beta/threads/run.py | 38 ++------- .../types/beta/threads/run_create_params.py | 35 +------- .../types/beta/threads/runs/run_step.py | 8 +- .../types/chat/chat_completion_chunk.py | 4 +- .../types/chat/chat_completion_tool_param.py | 32 +------ .../types/chat/completion_create_params.py | 28 +++---- src/openai/types/completion_choice.py | 2 +- src/openai/types/shared/__init__.py | 4 + src/openai/types/shared/function_object.py | 35 ++++++++ .../types/shared/function_parameters.py | 7 ++ src/openai/types/shared_params/__init__.py | 4 + .../types/shared_params/function_object.py | 36 ++++++++ .../shared_params/function_parameters.py | 9 ++ 20 files changed, 223 insertions(+), 251 deletions(-) create mode 100644 src/openai/types/shared/__init__.py create mode 100644 src/openai/types/shared/function_object.py create mode 100644 src/openai/types/shared/function_parameters.py create mode 100644 src/openai/types/shared_params/__init__.py create mode 100644 src/openai/types/shared_params/function_object.py create mode 100644 src/openai/types/shared_params/function_parameters.py diff --git a/api.md b/api.md index 95e9922129..0f5cdbbbbf 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,9 @@ +# Shared Types + +```python +from openai.types import FunctionObject, FunctionParameters +``` + # Completions Types: diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 75e0d66d58..ff36424442 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -137,8 +137,18 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - response_format: An object specifying the format that the model must output. Used to enable JSON - mode. + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in increased latency and appearance of a "stuck" request. Also + note that the message content may be partially cut off if + `finish_reason="length"`, which indicates the generation exceeded `max_tokens` + or the conversation exceeded the max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -304,8 +314,18 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - response_format: An object specifying the format that the model must output. Used to enable JSON - mode. + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in increased latency and appearance of a "stuck" request. Also + note that the message content may be partially cut off if + `finish_reason="length"`, which indicates the generation exceeded `max_tokens` + or the conversation exceeded the max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -464,8 +484,18 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - response_format: An object specifying the format that the model must output. Used to enable JSON - mode. + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in increased latency and appearance of a "stuck" request. Also + note that the message content may be partially cut off if + `finish_reason="length"`, which indicates the generation exceeded `max_tokens` + or the conversation exceeded the max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -704,8 +734,18 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - response_format: An object specifying the format that the model must output. Used to enable JSON - mode. + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in increased latency and appearance of a "stuck" request. Also + note that the message content may be partially cut off if + `finish_reason="length"`, which indicates the generation exceeded `max_tokens` + or the conversation exceeded the max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -871,8 +911,18 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - response_format: An object specifying the format that the model must output. Used to enable JSON - mode. + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in increased latency and appearance of a "stuck" request. Also + note that the message content may be partially cut off if + `finish_reason="length"`, which indicates the generation exceeded `max_tokens` + or the conversation exceeded the max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -1031,8 +1081,18 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) - response_format: An object specifying the format that the model must output. Used to enable JSON - mode. + response_format: An object specifying the format that the model must output. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in increased latency and appearance of a "stuck" request. Also + note that the message content may be partially cut off if + `finish_reason="length"`, which indicates the generation exceeded `max_tokens` + or the conversation exceeded the max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 8f21480d5e..5840599a27 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -5,6 +5,8 @@ from .edit import Edit as Edit from .image import Image as Image from .model import Model as Model +from .shared import FunctionObject as FunctionObject +from .shared import FunctionParameters as FunctionParameters from .embedding import Embedding as Embedding from .fine_tune import FineTune as FineTune from .completion import Completion as Completion diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index e15282a69a..63332123c0 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -1,12 +1,13 @@ # File generated from our OpenAPI spec by Stainless. import builtins -from typing import Dict, List, Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal +from ..shared import FunctionObject from ..._models import BaseModel -__all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetrieval", "ToolFunction", "ToolFunctionFunction"] +__all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetrieval", "ToolFunction"] class ToolCodeInterpreter(BaseModel): @@ -19,36 +20,8 @@ class ToolRetrieval(BaseModel): """The type of tool being defined: `retrieval`""" -class ToolFunctionFunction(BaseModel): - description: str - """ - A description of what the function does, used by the model to choose when and - how to call the function. - """ - - name: str - """The name of the function to be called. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - - parameters: Dict[str, builtins.object] - """The parameters the functions accepts, described as a JSON Schema object. - - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) - for examples, and the - [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for - documentation about the format. - - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - class ToolFunction(BaseModel): - function: ToolFunctionFunction - """The function definition.""" + function: FunctionObject type: Literal["function"] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 8272d5eb4d..ce7494efec 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -2,16 +2,17 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict +from ...types import shared_params + __all__ = [ "AssistantCreateParams", "Tool", "ToolAssistantToolsCode", "ToolAssistantToolsRetrieval", "ToolAssistantToolsFunction", - "ToolAssistantToolsFunctionFunction", ] @@ -71,36 +72,8 @@ class ToolAssistantToolsRetrieval(TypedDict, total=False): """The type of tool being defined: `retrieval`""" -class ToolAssistantToolsFunctionFunction(TypedDict, total=False): - description: Required[str] - """ - A description of what the function does, used by the model to choose when and - how to call the function. - """ - - name: Required[str] - """The name of the function to be called. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - - parameters: Required[Dict[str, object]] - """The parameters the functions accepts, described as a JSON Schema object. - - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) - for examples, and the - [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for - documentation about the format. - - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[ToolAssistantToolsFunctionFunction] - """The function definition.""" + function: Required[shared_params.FunctionObject] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 3916833b77..07a186a0d2 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -2,16 +2,17 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict +from ...types import shared_params + __all__ = [ "AssistantUpdateParams", "Tool", "ToolAssistantToolsCode", "ToolAssistantToolsRetrieval", "ToolAssistantToolsFunction", - "ToolAssistantToolsFunctionFunction", ] @@ -73,36 +74,8 @@ class ToolAssistantToolsRetrieval(TypedDict, total=False): """The type of tool being defined: `retrieval`""" -class ToolAssistantToolsFunctionFunction(TypedDict, total=False): - description: Required[str] - """ - A description of what the function does, used by the model to choose when and - how to call the function. - """ - - name: Required[str] - """The name of the function to be called. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - - parameters: Required[Dict[str, object]] - """The parameters the functions accepts, described as a JSON Schema object. - - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) - for examples, and the - [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for - documentation about the format. - - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[ToolAssistantToolsFunctionFunction] - """The function definition.""" + function: Required[shared_params.FunctionObject] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index d7391d4d62..8e6b33249c 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict +from ...types import shared_params + __all__ = [ "ThreadCreateAndRunParams", "Thread", @@ -13,7 +15,6 @@ "ToolAssistantToolsCode", "ToolAssistantToolsRetrieval", "ToolAssistantToolsFunction", - "ToolAssistantToolsFunctionFunction", ] @@ -110,36 +111,8 @@ class ToolAssistantToolsRetrieval(TypedDict, total=False): """The type of tool being defined: `retrieval`""" -class ToolAssistantToolsFunctionFunction(TypedDict, total=False): - description: Required[str] - """ - A description of what the function does, used by the model to choose when and - how to call the function. - """ - - name: Required[str] - """The name of the function to be called. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - - parameters: Required[Dict[str, object]] - """The parameters the functions accepts, described as a JSON Schema object. - - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) - for examples, and the - [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for - documentation about the format. - - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[ToolAssistantToolsFunctionFunction] - """The function definition.""" + function: Required[shared_params.FunctionObject] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index d30a32ec97..59a443f75b 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. import builtins -from typing import Dict, List, Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal +from ...shared import FunctionObject from ...._models import BaseModel from .required_action_function_tool_call import RequiredActionFunctionToolCall @@ -16,7 +17,6 @@ "ToolAssistantToolsCode", "ToolAssistantToolsRetrieval", "ToolAssistantToolsFunction", - "ToolAssistantToolsFunctionFunction", ] @@ -51,36 +51,8 @@ class ToolAssistantToolsRetrieval(BaseModel): """The type of tool being defined: `retrieval`""" -class ToolAssistantToolsFunctionFunction(BaseModel): - description: str - """ - A description of what the function does, used by the model to choose when and - how to call the function. - """ - - name: str - """The name of the function to be called. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - - parameters: Dict[str, builtins.object] - """The parameters the functions accepts, described as a JSON Schema object. - - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) - for examples, and the - [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for - documentation about the format. - - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - class ToolAssistantToolsFunction(BaseModel): - function: ToolAssistantToolsFunctionFunction - """The function definition.""" + function: FunctionObject type: Literal["function"] """The type of tool being defined: `function`""" @@ -147,8 +119,8 @@ class Run(BaseModel): this run. """ - object: Literal["assistant.run"] - """The object type, which is always `assistant.run`.""" + object: Literal["thread.run"] + """The object type, which is always `thread.run`.""" required_action: Optional[RequiredAction] """Details on the action required to continue the run. diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index cf1bb9f05d..27e5a86a8e 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -2,16 +2,17 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict +from ....types import shared_params + __all__ = [ "RunCreateParams", "Tool", "ToolAssistantToolsCode", "ToolAssistantToolsRetrieval", "ToolAssistantToolsFunction", - "ToolAssistantToolsFunctionFunction", ] @@ -62,36 +63,8 @@ class ToolAssistantToolsRetrieval(TypedDict, total=False): """The type of tool being defined: `retrieval`""" -class ToolAssistantToolsFunctionFunction(TypedDict, total=False): - description: Required[str] - """ - A description of what the function does, used by the model to choose when and - how to call the function. - """ - - name: Required[str] - """The name of the function to be called. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - - parameters: Required[Dict[str, object]] - """The parameters the functions accepts, described as a JSON Schema object. - - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) - for examples, and the - [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for - documentation about the format. - - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[ToolAssistantToolsFunctionFunction] - """The function definition.""" + function: Required[shared_params.FunctionObject] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 17a567dc0e..536cf04ab1 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -65,8 +65,8 @@ class RunStep(BaseModel): a maxium of 512 characters long. """ - object: Literal["assistant.run.step"] - """The object type, which is always `assistant.run.step``.""" + object: Literal["thread.run.step"] + """The object type, which is always `thread.run.step``.""" run_id: str """ @@ -76,8 +76,8 @@ class RunStep(BaseModel): status: Literal["in_progress", "cancelled", "failed", "completed", "expired"] """ - The status of the run, which can be either `in_progress`, `cancelled`, `failed`, - `completed`, or `expired`. + The status of the run step, which can be either `in_progress`, `cancelled`, + `failed`, `completed`, or `expired`. """ step_details: StepDetails diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 568f530280..6be046b01e 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -111,8 +111,8 @@ class ChatCompletionChunk(BaseModel): """The object type, which is always `chat.completion.chunk`.""" system_fingerprint: Optional[str] = None - """This fingerprint represents the backend configuration that the model runs with. - + """ + This fingerprint represents the backend configuration that the model runs with. Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism. """ diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py index 4b7e6238c7..97e73f17ce 100644 --- a/src/openai/types/chat/chat_completion_tool_param.py +++ b/src/openai/types/chat/chat_completion_tool_param.py @@ -2,41 +2,15 @@ from __future__ import annotations -from typing import Dict from typing_extensions import Literal, Required, TypedDict -__all__ = ["ChatCompletionToolParam", "Function"] +from ...types import shared_params - -class Function(TypedDict, total=False): - name: Required[str] - """The name of the function to be called. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - - parameters: Required[Dict[str, object]] - """The parameters the functions accepts, described as a JSON Schema object. - - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) - for examples, and the - [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for - documentation about the format. - - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - description: str - """ - A description of what the function does, used by the model to choose when and - how to call the function. - """ +__all__ = ["ChatCompletionToolParam"] class ChatCompletionToolParam(TypedDict, total=False): - function: Required[Function] + function: Required[shared_params.FunctionObject] type: Required[Literal["function"]] """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index b310761077..51c864588b 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -5,6 +5,7 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, TypedDict +from ...types import shared_params from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam from .chat_completion_tool_choice_option_param import ( @@ -121,7 +122,16 @@ class CompletionCreateParamsBase(TypedDict, total=False): response_format: ResponseFormat """An object specifying the format that the model must output. - Used to enable JSON mode. + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in increased latency and appearance of a "stuck" request. Also + note that the message content may be partially cut off if + `finish_reason="length"`, which indicates the generation exceeded `max_tokens` + or the conversation exceeded the max context length. """ seed: Optional[int] @@ -193,7 +203,7 @@ class Function(TypedDict, total=False): of 64. """ - parameters: Required[Dict[str, object]] + parameters: Required[shared_params.FunctionParameters] """The parameters the functions accepts, described as a JSON Schema object. See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) @@ -214,19 +224,7 @@ class Function(TypedDict, total=False): class ResponseFormat(TypedDict, total=False): type: Literal["text", "json_object"] - """Setting to `json_object` enables JSON mode. - - This guarantees that the message the model generates is valid JSON. - - Note that your system prompt must still instruct the model to produce JSON, and - to help ensure you don't forget, the API will throw an error if the string - `JSON` does not appear in your system message. Also note that the message - content may be partial (i.e. cut off) if `finish_reason="length"`, which - indicates the generation exceeded `max_tokens` or the conversation exceeded the - max context length. - - Must be one of `text` or `json_object`. - """ + """Must be one of `text` or `json_object`.""" class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase): diff --git a/src/openai/types/completion_choice.py b/src/openai/types/completion_choice.py index e86d706ed1..71de0f9247 100644 --- a/src/openai/types/completion_choice.py +++ b/src/openai/types/completion_choice.py @@ -15,7 +15,7 @@ class Logprobs(BaseModel): tokens: Optional[List[str]] = None - top_logprobs: Optional[List[Dict[str, int]]] = None + top_logprobs: Optional[List[Dict[str, float]]] = None class CompletionChoice(BaseModel): diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py new file mode 100644 index 0000000000..ab67c41471 --- /dev/null +++ b/src/openai/types/shared/__init__.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. + +from .function_object import FunctionObject as FunctionObject +from .function_parameters import FunctionParameters as FunctionParameters diff --git a/src/openai/types/shared/function_object.py b/src/openai/types/shared/function_object.py new file mode 100644 index 0000000000..f566fe530d --- /dev/null +++ b/src/openai/types/shared/function_object.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional + +from ..._models import BaseModel +from .function_parameters import FunctionParameters + +__all__ = ["FunctionObject"] + + +class FunctionObject(BaseModel): + name: str + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: FunctionParameters + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + description: Optional[str] = None + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ diff --git a/src/openai/types/shared/function_parameters.py b/src/openai/types/shared/function_parameters.py new file mode 100644 index 0000000000..405c2d14cc --- /dev/null +++ b/src/openai/types/shared/function_parameters.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Dict + +__all__ = ["FunctionParameters"] + +FunctionParameters = Dict[str, object] diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py new file mode 100644 index 0000000000..ab67c41471 --- /dev/null +++ b/src/openai/types/shared_params/__init__.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. + +from .function_object import FunctionObject as FunctionObject +from .function_parameters import FunctionParameters as FunctionParameters diff --git a/src/openai/types/shared_params/function_object.py b/src/openai/types/shared_params/function_object.py new file mode 100644 index 0000000000..d3f5d0aaf4 --- /dev/null +++ b/src/openai/types/shared_params/function_object.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ...types import shared_params + +__all__ = ["FunctionObject"] + + +class FunctionObject(TypedDict, total=False): + name: Required[str] + """The name of the function to be called. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + parameters: Required[shared_params.FunctionParameters] + """The parameters the functions accepts, described as a JSON Schema object. + + See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + for examples, and the + [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for + documentation about the format. + + To describe a function that accepts no parameters, provide the value + `{"type": "object", "properties": {}}`. + """ + + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ diff --git a/src/openai/types/shared_params/function_parameters.py b/src/openai/types/shared_params/function_parameters.py new file mode 100644 index 0000000000..a405f6b2e2 --- /dev/null +++ b/src/openai/types/shared_params/function_parameters.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Dict + +__all__ = ["FunctionParameters"] + +FunctionParameters = Dict[str, object] From d3d7e1be4a69a97b473340b8b707893e39cee42e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 20:28:22 +0000 Subject: [PATCH 173/914] fix(client): show a helpful error message if the v0 API is used (#743) --- src/openai/__init__.py | 1 + src/openai/lib/_old_api.py | 66 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/openai/lib/_old_api.py diff --git a/src/openai/__init__.py b/src/openai/__init__.py index da1157a767..d92dfe969a 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -74,6 +74,7 @@ from .version import VERSION as VERSION from .lib.azure import AzureOpenAI as AzureOpenAI from .lib.azure import AsyncAzureOpenAI as AsyncAzureOpenAI +from .lib._old_api import * _setup_logging() diff --git a/src/openai/lib/_old_api.py b/src/openai/lib/_old_api.py new file mode 100644 index 0000000000..c4038fcfaf --- /dev/null +++ b/src/openai/lib/_old_api.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from typing_extensions import override + +from .._utils import LazyProxy +from .._exceptions import OpenAIError + +INSTRUCTIONS = """ + +You tried to access openai.{symbol}, but this is no longer supported in openai>=1.0.0 - see the README at https://github.com/openai/openai-python for the API. + +You can run `openai migrate` to automatically upgrade your codebase to use the 1.0.0 interface. + +Alternatively, you can pin your installation to the old version, e.g. `pip install openai==0.28` + +A detailed migration guide is available here: https://github.com/openai/openai-python/discussions/742 +""" + + +class APIRemovedInV1(OpenAIError): + def __init__(self, *, symbol: str) -> None: + super().__init__(INSTRUCTIONS.format(symbol=symbol)) + + +class APIRemovedInV1Proxy(LazyProxy[None]): + def __init__(self, *, symbol: str) -> None: + super().__init__() + self._symbol = symbol + + @override + def __load__(self) -> None: + raise APIRemovedInV1(symbol=self._symbol) + + +SYMBOLS = [ + "Edit", + "File", + "Audio", + "Image", + "Model", + "Engine", + "Customer", + "FineTune", + "Embedding", + "Completion", + "Deployment", + "Moderation", + "ErrorObject", + "FineTuningJob", + "ChatCompletion", +] + +# we explicitly tell type checkers that nothing is exported +# from this file so that when we re-export the old symbols +# in `openai/__init__.py` they aren't added to the auto-complete +# suggestions given by editors +if TYPE_CHECKING: + __all__: list[str] = [] +else: + __all__ = SYMBOLS + + +__locals = locals() +for symbol in SYMBOLS: + __locals[symbol] = APIRemovedInV1Proxy(symbol=symbol) From 48834e400a6a5acb0aabefcde7ffe8c996c98d00 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 22:16:27 +0000 Subject: [PATCH 174/914] refactor(api): rename FunctionObject to FunctionDefinition (#746) --- api.md | 2 +- src/openai/types/__init__.py | 2 +- src/openai/types/beta/assistant.py | 4 ++-- src/openai/types/beta/assistant_create_params.py | 2 +- src/openai/types/beta/assistant_update_params.py | 2 +- src/openai/types/beta/thread_create_and_run_params.py | 2 +- src/openai/types/beta/threads/run.py | 4 ++-- src/openai/types/beta/threads/run_create_params.py | 2 +- src/openai/types/chat/chat_completion_tool_param.py | 2 +- src/openai/types/shared/__init__.py | 2 +- .../shared/{function_object.py => function_definition.py} | 4 ++-- src/openai/types/shared_params/__init__.py | 2 +- .../{function_object.py => function_definition.py} | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-) rename src/openai/types/shared/{function_object.py => function_definition.py} (93%) rename src/openai/types/shared_params/{function_object.py => function_definition.py} (92%) diff --git a/api.md b/api.md index 0f5cdbbbbf..e0237803de 100644 --- a/api.md +++ b/api.md @@ -1,7 +1,7 @@ # Shared Types ```python -from openai.types import FunctionObject, FunctionParameters +from openai.types import FunctionDefinition, FunctionParameters ``` # Completions diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 5840599a27..1b4fca26ee 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -5,7 +5,7 @@ from .edit import Edit as Edit from .image import Image as Image from .model import Model as Model -from .shared import FunctionObject as FunctionObject +from .shared import FunctionDefinition as FunctionDefinition from .shared import FunctionParameters as FunctionParameters from .embedding import Embedding as Embedding from .fine_tune import FineTune as FineTune diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 63332123c0..a21206765a 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -4,7 +4,7 @@ from typing import List, Union, Optional from typing_extensions import Literal -from ..shared import FunctionObject +from ..shared import FunctionDefinition from ..._models import BaseModel __all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetrieval", "ToolFunction"] @@ -21,7 +21,7 @@ class ToolRetrieval(BaseModel): class ToolFunction(BaseModel): - function: FunctionObject + function: FunctionDefinition type: Literal["function"] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index ce7494efec..539897a7ba 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -73,7 +73,7 @@ class ToolAssistantToolsRetrieval(TypedDict, total=False): class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[shared_params.FunctionObject] + function: Required[shared_params.FunctionDefinition] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 07a186a0d2..a0efd96ecd 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -75,7 +75,7 @@ class ToolAssistantToolsRetrieval(TypedDict, total=False): class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[shared_params.FunctionObject] + function: Required[shared_params.FunctionDefinition] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 8e6b33249c..9f58dcd875 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -112,7 +112,7 @@ class ToolAssistantToolsRetrieval(TypedDict, total=False): class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[shared_params.FunctionObject] + function: Required[shared_params.FunctionDefinition] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 59a443f75b..ffbba1e504 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -4,7 +4,7 @@ from typing import List, Union, Optional from typing_extensions import Literal -from ...shared import FunctionObject +from ...shared import FunctionDefinition from ...._models import BaseModel from .required_action_function_tool_call import RequiredActionFunctionToolCall @@ -52,7 +52,7 @@ class ToolAssistantToolsRetrieval(BaseModel): class ToolAssistantToolsFunction(BaseModel): - function: FunctionObject + function: FunctionDefinition type: Literal["function"] """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 27e5a86a8e..df92f4fd2c 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -64,7 +64,7 @@ class ToolAssistantToolsRetrieval(TypedDict, total=False): class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[shared_params.FunctionObject] + function: Required[shared_params.FunctionDefinition] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py index 97e73f17ce..54c223955e 100644 --- a/src/openai/types/chat/chat_completion_tool_param.py +++ b/src/openai/types/chat/chat_completion_tool_param.py @@ -10,7 +10,7 @@ class ChatCompletionToolParam(TypedDict, total=False): - function: Required[shared_params.FunctionObject] + function: Required[shared_params.FunctionDefinition] type: Required[Literal["function"]] """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index ab67c41471..05bc4ff9ba 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. -from .function_object import FunctionObject as FunctionObject +from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters diff --git a/src/openai/types/shared/function_object.py b/src/openai/types/shared/function_definition.py similarity index 93% rename from src/openai/types/shared/function_object.py rename to src/openai/types/shared/function_definition.py index f566fe530d..bfcee50c85 100644 --- a/src/openai/types/shared/function_object.py +++ b/src/openai/types/shared/function_definition.py @@ -5,10 +5,10 @@ from ..._models import BaseModel from .function_parameters import FunctionParameters -__all__ = ["FunctionObject"] +__all__ = ["FunctionDefinition"] -class FunctionObject(BaseModel): +class FunctionDefinition(BaseModel): name: str """The name of the function to be called. diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index ab67c41471..05bc4ff9ba 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. -from .function_object import FunctionObject as FunctionObject +from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters diff --git a/src/openai/types/shared_params/function_object.py b/src/openai/types/shared_params/function_definition.py similarity index 92% rename from src/openai/types/shared_params/function_object.py rename to src/openai/types/shared_params/function_definition.py index d3f5d0aaf4..6bb6fa6ff2 100644 --- a/src/openai/types/shared_params/function_object.py +++ b/src/openai/types/shared_params/function_definition.py @@ -6,10 +6,10 @@ from ...types import shared_params -__all__ = ["FunctionObject"] +__all__ = ["FunctionDefinition"] -class FunctionObject(TypedDict, total=False): +class FunctionDefinition(TypedDict, total=False): name: Required[str] """The name of the function to be called. From cbdafb591c89a9080d66807dc186a398b8c83c79 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 22:27:27 +0000 Subject: [PATCH 175/914] feat(client): support passing chunk size for binary responses (#747) --- src/openai/_base_client.py | 18 ++++++++++++++---- src/openai/_types.py | 15 +++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index e37759cdf8..b2fe242634 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1727,9 +1727,14 @@ def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: return self.response.iter_raw(chunk_size) @override - def stream_to_file(self, file: str | os.PathLike[str]) -> None: + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: with open(file, mode="wb") as f: - for data in self.response.iter_bytes(): + for data in self.response.iter_bytes(chunk_size): f.write(data) @override @@ -1757,10 +1762,15 @@ async def aiter_raw(self, chunk_size: Optional[int] = None) -> AsyncIterator[byt return self.response.aiter_raw(chunk_size) @override - async def astream_to_file(self, file: str | os.PathLike[str]) -> None: + async def astream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: path = anyio.Path(file) async with await path.open(mode="wb") as f: - async for data in self.response.aiter_bytes(): + async for data in self.response.aiter_bytes(chunk_size): await f.write(data) @override diff --git a/src/openai/_types.py b/src/openai/_types.py index dabd15866f..0d05be9493 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -123,7 +123,12 @@ def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: pass @abstractmethod - def stream_to_file(self, file: str | PathLike[str]) -> None: + def stream_to_file( + self, + file: str | PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: """ Stream the output to the given file. """ @@ -172,7 +177,13 @@ async def aiter_raw(self, chunk_size: Optional[int] = None) -> AsyncIterator[byt """ pass - async def astream_to_file(self, file: str | PathLike[str]) -> None: + @abstractmethod + async def astream_to_file( + self, + file: str | PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: """ Stream the output to the given file. """ From 8e4e5d490629a5abfbdf4b6a2ce20c7eee848a2e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 Nov 2023 22:28:01 +0000 Subject: [PATCH 176/914] release: 1.2.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 25 +++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9c6a481f5b..d0ab6645f5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.1.2" + ".": "1.2.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c97964977..1b58f41340 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 1.2.0 (2023-11-08) + +Full Changelog: [v1.1.2...v1.2.0](https://github.com/openai/openai-python/compare/v1.1.2...v1.2.0) + +### Features + +* **api:** unify function types ([#741](https://github.com/openai/openai-python/issues/741)) ([ed16c4d](https://github.com/openai/openai-python/commit/ed16c4d2fec6cf4e33235d82b05ed9a777752204)) +* **client:** support passing chunk size for binary responses ([#747](https://github.com/openai/openai-python/issues/747)) ([c0c89b7](https://github.com/openai/openai-python/commit/c0c89b77a69ef098900e3a194894efcf72085d36)) + + +### Bug Fixes + +* **api:** update embedding response object type ([#739](https://github.com/openai/openai-python/issues/739)) ([29182c4](https://github.com/openai/openai-python/commit/29182c4818e2c56f46e961dba33e31dc30c25519)) +* **client:** show a helpful error message if the v0 API is used ([#743](https://github.com/openai/openai-python/issues/743)) ([920567c](https://github.com/openai/openai-python/commit/920567cb04df48a7f6cd2a3402a0b1f172c6290e)) + + +### Chores + +* **internal:** improve github devcontainer setup ([#737](https://github.com/openai/openai-python/issues/737)) ([0ac1abb](https://github.com/openai/openai-python/commit/0ac1abb07ec687a4f7b1150be10054dbd6e7cfbc)) + + +### Refactors + +* **api:** rename FunctionObject to FunctionDefinition ([#746](https://github.com/openai/openai-python/issues/746)) ([1afd138](https://github.com/openai/openai-python/commit/1afd13856c0e586ecbde8b24fe4f4bad9beeefdf)) + ## 1.1.2 (2023-11-08) Full Changelog: [v1.1.1...v1.1.2](https://github.com/openai/openai-python/compare/v1.1.1...v1.1.2) diff --git a/pyproject.toml b/pyproject.toml index 0861b1278b..1900794dfc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.1.2" +version = "1.2.0" description = "Client library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 848573b8a1..9d7e588fcf 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.1.2" # x-release-please-version +__version__ = "1.2.0" # x-release-please-version From aa6818997468b753546d55365d8142e2bb1c6640 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:46:43 +0000 Subject: [PATCH 177/914] release: 1.2.1 (#754) * refactor(client): deprecate files.retrieve_content in favour of files.content (#753) The latter supports binary response types more elegantly. * docs(readme): fix nested params example (#756) * release: 1.2.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++ README.md | 11 +++- api.md | 1 + pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/files.py | 85 +++++++++++++++++++++++++++++-- tests/api_resources/test_files.py | 73 ++++++++++++++++++++++---- 8 files changed, 170 insertions(+), 19 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d0ab6645f5..d43a621a8e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.2.0" + ".": "1.2.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b58f41340..1911aef970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.2.1 (2023-11-09) + +Full Changelog: [v1.2.0...v1.2.1](https://github.com/openai/openai-python/compare/v1.2.0...v1.2.1) + +### Documentation + +* **readme:** fix nested params example ([#756](https://github.com/openai/openai-python/issues/756)) ([ffbe5ec](https://github.com/openai/openai-python/commit/ffbe5eca0f8790ebcdb27ffe845da178a3ef4c45)) + + +### Refactors + +* **client:** deprecate files.retrieve_content in favour of files.content ([#753](https://github.com/openai/openai-python/issues/753)) ([eea5bc1](https://github.com/openai/openai-python/commit/eea5bc173466f63a6e84bd2d741b4873ca056b4c)) + ## 1.2.0 (2023-11-08) Full Changelog: [v1.1.2...v1.2.0](https://github.com/openai/openai-python/compare/v1.1.2...v1.2.0) diff --git a/README.md b/README.md index cedbc72337..11a1236b5a 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,16 @@ from openai import OpenAI client = OpenAI() -page = client.files.list() +completion = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Can you generate an example json object describing a fruit?", + } + ], + model="gpt-3.5-turbo", + response_format={"type": "json_object"}, +) ``` ## File Uploads diff --git a/api.md b/api.md index e0237803de..a7ee177411 100644 --- a/api.md +++ b/api.md @@ -87,6 +87,7 @@ Methods: - client.files.retrieve(file_id) -> FileObject - client.files.list(\*\*params) -> SyncPage[FileObject] - client.files.delete(file_id) -> FileDeleted +- client.files.content(file_id) -> HttpxBinaryResponseContent - client.files.retrieve_content(file_id) -> str - client.files.wait_for_processing(\*args) -> FileObject diff --git a/pyproject.toml b/pyproject.toml index 1900794dfc..844f501c45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.2.0" +version = "1.2.1" description = "Client library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9d7e588fcf..46c55958e6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.2.0" # x-release-please-version +__version__ = "1.2.1" # x-release-please-version diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index b317845c3a..a6f75e5a4c 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -3,6 +3,7 @@ from __future__ import annotations import time +import typing_extensions from typing import TYPE_CHECKING, Mapping, cast from typing_extensions import Literal @@ -14,7 +15,11 @@ from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncPage, AsyncPage -from .._base_client import AsyncPaginator, make_request_options +from .._base_client import ( + AsyncPaginator, + HttpxBinaryResponseContent, + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI @@ -197,6 +202,38 @@ def delete( cast_to=FileDeleted, ) + def content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> HttpxBinaryResponseContent: + """ + Returns the contents of the specified file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + f"/files/{file_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=HttpxBinaryResponseContent, + ) + + @typing_extensions.deprecated("The `.content()` method should be used instead") def retrieve_content( self, file_id: str, @@ -428,6 +465,38 @@ async def delete( cast_to=FileDeleted, ) + async def content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> HttpxBinaryResponseContent: + """ + Returns the contents of the specified file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + f"/files/{file_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=HttpxBinaryResponseContent, + ) + + @typing_extensions.deprecated("The `.content()` method should be used instead") async def retrieve_content( self, file_id: str, @@ -498,8 +567,11 @@ def __init__(self, files: Files) -> None: self.delete = to_raw_response_wrapper( files.delete, ) - self.retrieve_content = to_raw_response_wrapper( - files.retrieve_content, + self.content = to_raw_response_wrapper( + files.content, + ) + self.retrieve_content = to_raw_response_wrapper( # pyright: ignore[reportDeprecated] + files.retrieve_content # pyright: ignore[reportDeprecated], ) @@ -517,6 +589,9 @@ def __init__(self, files: AsyncFiles) -> None: self.delete = async_to_raw_response_wrapper( files.delete, ) - self.retrieve_content = async_to_raw_response_wrapper( - files.retrieve_content, + self.content = async_to_raw_response_wrapper( + files.content, + ) + self.retrieve_content = async_to_raw_response_wrapper( # pyright: ignore[reportDeprecated] + files.retrieve_content # pyright: ignore[reportDeprecated], ) diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index d668c2d0c7..a2c9d07314 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -4,14 +4,19 @@ import os +import httpx import pytest +from respx import MockRouter from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import FileObject, FileDeleted +from openai._types import BinaryResponseContent from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncPage, AsyncPage +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" @@ -91,19 +96,43 @@ def test_raw_response_delete(self, client: OpenAI) -> None: assert_matches_type(FileDeleted, file, path=["response"]) @parametrize - def test_method_retrieve_content(self, client: OpenAI) -> None: - file = client.files.retrieve_content( + @pytest.mark.respx(base_url=base_url) + def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + file = client.files.content( "string", ) - assert_matches_type(str, file, path=["response"]) + assert isinstance(file, BinaryResponseContent) + assert file.json() == {"foo": "bar"} @parametrize - def test_raw_response_retrieve_content(self, client: OpenAI) -> None: - response = client.files.with_raw_response.retrieve_content( + @pytest.mark.respx(base_url=base_url) + def test_raw_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = client.files.with_raw_response.content( "string", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() + assert isinstance(file, BinaryResponseContent) + assert file.json() == {"foo": "bar"} + + @parametrize + def test_method_retrieve_content(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + file = client.files.retrieve_content( + "string", + ) + assert_matches_type(str, file, path=["response"]) + + @parametrize + def test_raw_response_retrieve_content(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + response = client.files.with_raw_response.retrieve_content( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() assert_matches_type(str, file, path=["response"]) @@ -182,17 +211,41 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(FileDeleted, file, path=["response"]) @parametrize - async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: - file = await client.files.retrieve_content( + @pytest.mark.respx(base_url=base_url) + async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + file = await client.files.content( "string", ) - assert_matches_type(str, file, path=["response"]) + assert isinstance(file, BinaryResponseContent) + assert file.json() == {"foo": "bar"} @parametrize - async def test_raw_response_retrieve_content(self, client: AsyncOpenAI) -> None: - response = await client.files.with_raw_response.retrieve_content( + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = await client.files.with_raw_response.content( "string", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() + assert isinstance(file, BinaryResponseContent) + assert file.json() == {"foo": "bar"} + + @parametrize + async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + file = await client.files.retrieve_content( + "string", + ) + assert_matches_type(str, file, path=["response"]) + + @parametrize + async def test_raw_response_retrieve_content(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + response = await client.files.with_raw_response.retrieve_content( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() assert_matches_type(str, file, path=["response"]) From 1455a7c122cc7c8f522576c269cf642925f56869 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:17:50 +0000 Subject: [PATCH 178/914] fix(client): correctly assign error properties (#759) --- src/openai/_client.py | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index 6476d2b1a8..7820d5f96d 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -20,7 +20,7 @@ ProxiesTypes, RequestOptions, ) -from ._utils import is_given +from ._utils import is_given, is_mapping from ._version import __version__ from ._streaming import Stream as Stream from ._streaming import AsyncStream as AsyncStream @@ -221,30 +221,31 @@ def _make_status_error( body: object, response: httpx.Response, ) -> APIStatusError: + data = body.get("error", body) if is_mapping(body) else body if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) + return _exceptions.BadRequestError(err_msg, response=response, body=data) if response.status_code == 401: - return _exceptions.AuthenticationError(err_msg, response=response, body=body) + return _exceptions.AuthenticationError(err_msg, response=response, body=data) if response.status_code == 403: - return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + return _exceptions.PermissionDeniedError(err_msg, response=response, body=data) if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) + return _exceptions.NotFoundError(err_msg, response=response, body=data) if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) + return _exceptions.ConflictError(err_msg, response=response, body=data) if response.status_code == 422: - return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=data) if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) + return _exceptions.RateLimitError(err_msg, response=response, body=data) if response.status_code >= 500: - return _exceptions.InternalServerError(err_msg, response=response, body=body) - return APIStatusError(err_msg, response=response, body=body) + return _exceptions.InternalServerError(err_msg, response=response, body=data) + return APIStatusError(err_msg, response=response, body=data) class AsyncOpenAI(AsyncAPIClient): @@ -431,30 +432,31 @@ def _make_status_error( body: object, response: httpx.Response, ) -> APIStatusError: + data = body.get("error", body) if is_mapping(body) else body if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) + return _exceptions.BadRequestError(err_msg, response=response, body=data) if response.status_code == 401: - return _exceptions.AuthenticationError(err_msg, response=response, body=body) + return _exceptions.AuthenticationError(err_msg, response=response, body=data) if response.status_code == 403: - return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + return _exceptions.PermissionDeniedError(err_msg, response=response, body=data) if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) + return _exceptions.NotFoundError(err_msg, response=response, body=data) if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) + return _exceptions.ConflictError(err_msg, response=response, body=data) if response.status_code == 422: - return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=data) if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) + return _exceptions.RateLimitError(err_msg, response=response, body=data) if response.status_code >= 500: - return _exceptions.InternalServerError(err_msg, response=response, body=body) - return APIStatusError(err_msg, response=response, body=body) + return _exceptions.InternalServerError(err_msg, response=response, body=data) + return APIStatusError(err_msg, response=response, body=data) class OpenAIWithRawResponse: From 448ac7d046d12c26064b2050f3ce82bde3a24943 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:23:35 +0000 Subject: [PATCH 179/914] docs(readme): link to migration guide (#761) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 11a1236b5a..1e8bf6ecec 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ The API documentation can be found [here](https://platform.openai.com/docs). ## Installation +> [!IMPORTANT] +> The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code. + ```sh pip install openai ``` From 45f60e82cf31571b055de948fdf75d44c67a9097 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:24:13 +0000 Subject: [PATCH 180/914] release: 1.2.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d43a621a8e..029e2d7cb4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.2.1" + ".": "1.2.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1911aef970..591c32b504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.2.2 (2023-11-09) + +Full Changelog: [v1.2.1...v1.2.2](https://github.com/openai/openai-python/compare/v1.2.1...v1.2.2) + +### Bug Fixes + +* **client:** correctly assign error properties ([#759](https://github.com/openai/openai-python/issues/759)) ([ef264d2](https://github.com/openai/openai-python/commit/ef264d2293b77784f69039291ca2a17a454851cb)) + + +### Documentation + +* **readme:** link to migration guide ([#761](https://github.com/openai/openai-python/issues/761)) ([ddde839](https://github.com/openai/openai-python/commit/ddde8392be19e7ad77280374806667ecaef612da)) + ## 1.2.1 (2023-11-09) Full Changelog: [v1.2.0...v1.2.1](https://github.com/openai/openai-python/compare/v1.2.0...v1.2.1) diff --git a/pyproject.toml b/pyproject.toml index 844f501c45..7674d01e92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.2.1" +version = "1.2.2" description = "Client library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 46c55958e6..b00734d2f4 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.2.1" # x-release-please-version +__version__ = "1.2.2" # x-release-please-version From b185cb3196ce1e7b9455e1cbfe0c2167ab566bd9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 9 Nov 2023 22:57:00 +0000 Subject: [PATCH 181/914] docs: reword package description (#764) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7674d01e92..de28d4e913 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "openai" version = "1.2.2" -description = "Client library for the openai API" +description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" authors = [ From 460a28217e86b66aaeaf867f3d2f64dbae57a692 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 10 Nov 2023 08:24:51 +0000 Subject: [PATCH 182/914] fix: prevent IndexError in fine-tunes CLI (#768) --- src/openai/lib/_validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/lib/_validators.py b/src/openai/lib/_validators.py index 8e4ed3c9f4..c8608c0cef 100644 --- a/src/openai/lib/_validators.py +++ b/src/openai/lib/_validators.py @@ -407,7 +407,7 @@ def completions_space_start_validator(df: pd.DataFrame) -> Remediation: """ def add_space_start(x: Any) -> Any: - x["completion"] = x["completion"].apply(lambda x: ("" if x[0] == " " else " ") + x) + x["completion"] = x["completion"].apply(lambda s: ("" if s.startswith(" ") else " ") + s) return x optional_msg = None From 7e2b2544f0206254d3ca094a3a4247397578497f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:00:50 +0000 Subject: [PATCH 183/914] fix(client): correctly flush the stream response body (#771) --- src/openai/_streaming.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index cee737f4f5..095746630b 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -47,8 +47,9 @@ def __stream__(self) -> Iterator[ResponseT]: cast_to = self._cast_to response = self.response process_data = self._client._process_response_data + iterator = self._iter_events() - for sse in self._iter_events(): + for sse in iterator: if sse.data.startswith("[DONE]"): break @@ -63,6 +64,10 @@ def __stream__(self) -> Iterator[ResponseT]: yield process_data(data=data, cast_to=cast_to, response=response) + # Ensure the entire stream is consumed + for sse in iterator: + ... + class AsyncStream(Generic[ResponseT]): """Provides the core interface to iterate over an asynchronous stream response.""" @@ -97,8 +102,9 @@ async def __stream__(self) -> AsyncIterator[ResponseT]: cast_to = self._cast_to response = self.response process_data = self._client._process_response_data + iterator = self._iter_events() - async for sse in self._iter_events(): + async for sse in iterator: if sse.data.startswith("[DONE]"): break @@ -113,6 +119,10 @@ async def __stream__(self) -> AsyncIterator[ResponseT]: yield process_data(data=data, cast_to=cast_to, response=response) + # Ensure the entire stream is consumed + async for sse in iterator: + ... + class ServerSentEvent: def __init__( From c5975bd02faf2e2b107a2cbfaead0c68b42c85b9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 10 Nov 2023 12:51:04 +0000 Subject: [PATCH 184/914] fix(models): mark unknown fields as set in pydantic v1 (#772) --- src/openai/_models.py | 1 + tests/api_resources/audio/test_speech.py | 6 ++++++ tests/api_resources/test_files.py | 4 ++++ tests/test_client.py | 12 ++++++------ tests/test_module_client.py | 7 +++++-- tests/test_transform.py | 11 +++++++++-- 6 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 00d787ca87..ebaef99454 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -121,6 +121,7 @@ def construct( if PYDANTIC_V2: _extra[key] = value else: + _fields_set.add(key) fields_values[key] = value object.__setattr__(m, "__dict__", fields_values) diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 89814c2dd3..50b00b73b4 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -21,6 +21,7 @@ class TestSpeech: loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -33,6 +34,7 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: assert isinstance(speech, BinaryResponseContent) assert speech.json() == {"foo": "bar"} + @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -48,6 +50,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou assert isinstance(speech, BinaryResponseContent) assert speech.json() == {"foo": "bar"} + @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -68,6 +71,7 @@ class TestAsyncSpeech: loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: @@ -80,6 +84,7 @@ async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) assert isinstance(speech, BinaryResponseContent) assert speech.json() == {"foo": "bar"} + @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: @@ -95,6 +100,7 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mo assert isinstance(speech, BinaryResponseContent) assert speech.json() == {"foo": "bar"} + @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) async def test_raw_response_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index a2c9d07314..e4cf493319 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -95,6 +95,7 @@ def test_raw_response_delete(self, client: OpenAI) -> None: file = response.parse() assert_matches_type(FileDeleted, file, path=["response"]) + @pytest.mark.skip(reason="mocked response isn't working yet") @parametrize @pytest.mark.respx(base_url=base_url) def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -105,6 +106,7 @@ def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None: assert isinstance(file, BinaryResponseContent) assert file.json() == {"foo": "bar"} + @pytest.mark.skip(reason="mocked response isn't working yet") @parametrize @pytest.mark.respx(base_url=base_url) def test_raw_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -210,6 +212,7 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: file = response.parse() assert_matches_type(FileDeleted, file, path=["response"]) + @pytest.mark.skip(reason="mocked response isn't working yet") @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: @@ -220,6 +223,7 @@ async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) assert isinstance(file, BinaryResponseContent) assert file.json() == {"foo": "bar"} + @pytest.mark.skip(reason="mocked response isn't working yet") @parametrize @pytest.mark.respx(base_url=base_url) async def test_raw_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: diff --git a/tests/test_client.py b/tests/test_client.py index 3b70594ecd..e3daa4d2b1 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -41,12 +41,12 @@ class TestOpenAI: @pytest.mark.respx(base_url=base_url) def test_raw_response(self, respx_mock: MockRouter) -> None: - respx_mock.post("/foo").mock(return_value=httpx.Response(200, json='{"foo": "bar"}')) + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = self.client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) - assert response.json() == '{"foo": "bar"}' + assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: @@ -57,7 +57,7 @@ def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: response = self.client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) - assert response.json() == '{"foo": "bar"}' + assert response.json() == {"foo": "bar"} def test_copy(self) -> None: copied = self.client.copy() @@ -571,12 +571,12 @@ class TestAsyncOpenAI: @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio async def test_raw_response(self, respx_mock: MockRouter) -> None: - respx_mock.post("/foo").mock(return_value=httpx.Response(200, json='{"foo": "bar"}')) + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = await self.client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) - assert response.json() == '{"foo": "bar"}' + assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio @@ -588,7 +588,7 @@ async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: response = await self.client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) - assert response.json() == '{"foo": "bar"}' + assert response.json() == {"foo": "bar"} def test_copy(self) -> None: copied = self.client.copy() diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 0beca37f61..50b7369e19 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -125,7 +125,10 @@ def test_azure_api_key_env_without_api_version() -> None: openai.api_type = None _os.environ["AZURE_OPENAI_API_KEY"] = "example API key" - with pytest.raises(ValueError, match=r"Expected `api_version` to be given for the Azure client"): + with pytest.raises( + ValueError, + match=r"Must provide either the `api_version` argument or the `OPENAI_API_VERSION` environment variable", + ): openai.completions._client @@ -137,7 +140,7 @@ def test_azure_api_key_and_version_env() -> None: with pytest.raises( ValueError, - match=r"Must provide one of the `base_url` or `azure_endpoint` arguments, or the `OPENAI_BASE_URL`", + match=r"Must provide one of the `base_url` or `azure_endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable", ): openai.completions._client diff --git a/tests/test_transform.py b/tests/test_transform.py index 3fc89bb093..483db680f8 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -7,6 +7,7 @@ import pytest from openai._utils import PropertyInfo, transform, parse_datetime +from openai._compat import PYDANTIC_V2 from openai._models import BaseModel @@ -210,14 +211,20 @@ def test_pydantic_unknown_field() -> None: def test_pydantic_mismatched_types() -> None: model = MyModel.construct(foo=True) - with pytest.warns(UserWarning): + if PYDANTIC_V2: + with pytest.warns(UserWarning): + params = transform(model, Any) + else: params = transform(model, Any) assert params == {"foo": True} def test_pydantic_mismatched_object_type() -> None: model = MyModel.construct(foo=MyModel.construct(hello="world")) - with pytest.warns(UserWarning): + if PYDANTIC_V2: + with pytest.warns(UserWarning): + params = transform(model, Any) + else: params = transform(model, Any) assert params == {"foo": {"hello": "world"}} From a10655ad0c7b00d1047f36636f9f8b7cfd1e2c23 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 10 Nov 2023 16:07:32 +0000 Subject: [PATCH 185/914] fix(client): serialise pydantic v1 default fields correctly in params (#776) --- src/openai/_utils/_transform.py | 2 +- tests/test_transform.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index dc497ea329..d953505fff 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -168,7 +168,7 @@ def _transform_recursive( return data if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True, exclude_defaults=True) + return model_dump(data, exclude_unset=True) return _transform_value(data, annotation) diff --git a/tests/test_transform.py b/tests/test_transform.py index 483db680f8..5e15385f4d 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -237,3 +237,29 @@ def test_pydantic_nested_objects() -> None: model = ModelNestedObjects.construct(nested={"foo": "stainless"}) assert isinstance(model.nested, MyModel) assert transform(model, Any) == {"nested": {"foo": "stainless"}} + + +class ModelWithDefaultField(BaseModel): + foo: str + with_none_default: Union[str, None] = None + with_str_default: str = "foo" + + +def test_pydantic_default_field() -> None: + # should be excluded when defaults are used + model = ModelWithDefaultField.construct() + assert model.with_none_default is None + assert model.with_str_default == "foo" + assert transform(model, Any) == {} + + # should be included when the default value is explicitly given + model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") + assert model.with_none_default is None + assert model.with_str_default == "foo" + assert transform(model, Any) == {"with_none_default": None, "with_str_default": "foo"} + + # should be included when a non-default value is explicitly given + model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") + assert model.with_none_default == "bar" + assert model.with_str_default == "baz" + assert transform(model, Any) == {"with_none_default": "bar", "with_str_default": "baz"} From 2425c15243f92d6c05918f3a731d43bf86c6d08f Mon Sep 17 00:00:00 2001 From: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> Date: Fri, 10 Nov 2023 18:23:33 +0100 Subject: [PATCH 186/914] fix(cli/audio): file format detection failing for whisper (#733) Signed-off-by: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> --- src/openai/cli/_api/audio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/cli/_api/audio.py b/src/openai/cli/_api/audio.py index eaf57748ad..90d21b9932 100644 --- a/src/openai/cli/_api/audio.py +++ b/src/openai/cli/_api/audio.py @@ -66,7 +66,7 @@ def transcribe(args: CLITranscribeArgs) -> None: buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") model = get_client().audio.transcriptions.create( - file=buffer_reader, + file=(args.file, buffer_reader), model=args.model, language=args.language or NOT_GIVEN, temperature=args.temperature or NOT_GIVEN, @@ -83,7 +83,7 @@ def translate(args: CLITranslationArgs) -> None: buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") model = get_client().audio.translations.create( - file=buffer_reader, + file=(args.file, buffer_reader), model=args.model, temperature=args.temperature or NOT_GIVEN, prompt=args.prompt or NOT_GIVEN, From 4948308f37ecd9872708f42dc0f92dc5ab5ab4d8 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 10 Nov 2023 17:36:08 +0000 Subject: [PATCH 187/914] release: 1.2.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 17 +++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 029e2d7cb4..a237539253 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.2.2" + ".": "1.2.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 591c32b504..955fe7a405 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 1.2.3 (2023-11-10) + +Full Changelog: [v1.2.2...v1.2.3](https://github.com/openai/openai-python/compare/v1.2.2...v1.2.3) + +### Bug Fixes + +* **cli/audio:** file format detection failing for whisper ([#733](https://github.com/openai/openai-python/issues/733)) ([01079d6](https://github.com/openai/openai-python/commit/01079d6dca13e0ec158dff81e0706d8a9d6c02ef)) +* **client:** correctly flush the stream response body ([#771](https://github.com/openai/openai-python/issues/771)) ([0d52731](https://github.com/openai/openai-python/commit/0d5273165c96286f8456ae04b9eb0de5144e52f8)) +* **client:** serialise pydantic v1 default fields correctly in params ([#776](https://github.com/openai/openai-python/issues/776)) ([d4c49ad](https://github.com/openai/openai-python/commit/d4c49ad2be9c0d926eece5fd33f6836279ea21e2)) +* **models:** mark unknown fields as set in pydantic v1 ([#772](https://github.com/openai/openai-python/issues/772)) ([ae032a1](https://github.com/openai/openai-python/commit/ae032a1ba4efa72284a572bfaf0305af50142835)) +* prevent IndexError in fine-tunes CLI ([#768](https://github.com/openai/openai-python/issues/768)) ([42f1633](https://github.com/openai/openai-python/commit/42f16332cf0f96f243f9797d6406283865254355)) + + +### Documentation + +* reword package description ([#764](https://github.com/openai/openai-python/issues/764)) ([9ff10df](https://github.com/openai/openai-python/commit/9ff10df30ca2d44978eb5f982ccf039c9f1bf1bf)) + ## 1.2.2 (2023-11-09) Full Changelog: [v1.2.1...v1.2.2](https://github.com/openai/openai-python/compare/v1.2.1...v1.2.2) diff --git a/pyproject.toml b/pyproject.toml index de28d4e913..e27c6de9ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.2.2" +version = "1.2.3" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b00734d2f4..ebf5d47703 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.2.2" # x-release-please-version +__version__ = "1.2.3" # x-release-please-version From e661da69b4a11d48edfe21d2b12f53c201593596 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Fri, 10 Nov 2023 14:39:58 -0800 Subject: [PATCH 188/914] add images/generations to azure endpoints list (#778) --- src/openai/lib/azure.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index f5fcd24fd1..d31313e95a 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -22,6 +22,7 @@ "/embeddings", "/audio/transcriptions", "/audio/translations", + "/images/generations", ] ) From b607e20a7ec75542212fb2e8a1a34602558ea3bd Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 Nov 2023 18:12:52 +0000 Subject: [PATCH 189/914] fix(client): retry if SSLWantReadError occurs in the async client (#804) --- src/openai/_base_client.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index b2fe242634..3db8b6fa35 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1320,12 +1320,6 @@ async def _request( if retries > 0: return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) raise APITimeoutError(request=request) from err - except httpx.ReadTimeout as err: - # We explicitly do not retry on ReadTimeout errors as this means - # that the server processing the request has taken 60 seconds - # (our default timeout). This likely indicates that something - # is not working as expected on the server side. - raise except httpx.TimeoutException as err: if retries > 0: return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) From 65e29a2efa455a06deb59e243f27796c4ca2254c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 Nov 2023 18:13:35 +0000 Subject: [PATCH 190/914] release: 1.2.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a237539253..862a05b695 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.2.3" + ".": "1.2.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 955fe7a405..e1e8a331f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.2.4 (2023-11-13) + +Full Changelog: [v1.2.3...v1.2.4](https://github.com/openai/openai-python/compare/v1.2.3...v1.2.4) + +### Bug Fixes + +* **client:** retry if SSLWantReadError occurs in the async client ([#804](https://github.com/openai/openai-python/issues/804)) ([be82288](https://github.com/openai/openai-python/commit/be82288f3c88c10c9ac20ba3b8cb53b5c7a4e2f9)) + ## 1.2.3 (2023-11-10) Full Changelog: [v1.2.2...v1.2.3](https://github.com/openai/openai-python/compare/v1.2.2...v1.2.3) diff --git a/pyproject.toml b/pyproject.toml index e27c6de9ab..dc08634e4a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.2.3" +version = "1.2.4" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ebf5d47703..f22b1aae3f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.2.3" # x-release-please-version +__version__ = "1.2.4" # x-release-please-version From 812839cbc9fa76181a4b13382901e6104b0669db Mon Sep 17 00:00:00 2001 From: Mikyo King Date: Mon, 13 Nov 2023 11:19:50 -0700 Subject: [PATCH 191/914] docs(readme): fix broken azure_ad notebook link (#781) The notebook link in the main README pointed an old directory. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e8bf6ecec..f3698a9ff4 100644 --- a/README.md +++ b/README.md @@ -489,7 +489,7 @@ In addition to the options provided in the base `OpenAI` client, the following o - `azure_ad_token` - `azure_ad_token_provider` -An example of using the client with Azure Active Directory can be found [here](https://github.com/openai/openai-python/blob/v1/examples/azure_ad.py). +An example of using the client with Azure Active Directory can be found [here](https://github.com/openai/openai-python/blob/main/examples/azure_ad.py). ## Versioning From a73d1ce9d4b20479cba4005f2cb1f03d923dabc6 Mon Sep 17 00:00:00 2001 From: nikkie Date: Tue, 14 Nov 2023 03:54:56 +0900 Subject: [PATCH 192/914] Fix typo in docstring of _types.NotGiven (#794) --- src/openai/_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/_types.py b/src/openai/_types.py index 0d05be9493..9e962a1078 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -279,8 +279,8 @@ class NotGiven: ```py def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... - get(timout=1) # 1s timeout - get(timout=None) # No timeout + get(timeout=1) # 1s timeout + get(timeout=None) # No timeout get() # Default timeout behavior, which may not be statically known at the method definition. ``` """ From 7d06760d8d31bef6ff977461a37eb9e2e9527724 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 Nov 2023 19:15:18 +0000 Subject: [PATCH 193/914] chore: fix typo in docs and add request header for function calls (#807) --- src/openai/cli/_errors.py | 4 ++-- src/openai/cli/_utils.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openai/cli/_errors.py b/src/openai/cli/_errors.py index ac2a3780d0..2bf06070d6 100644 --- a/src/openai/cli/_errors.py +++ b/src/openai/cli/_errors.py @@ -4,7 +4,7 @@ import pydantic -from ._utils import Colours, organization_info +from ._utils import Colors, organization_info from .._exceptions import APIError, OpenAIError @@ -20,4 +20,4 @@ def display_error(err: CLIError | APIError | pydantic.ValidationError) -> None: if isinstance(err, SilentCLIError): return - sys.stderr.write("{}{}Error:{} {}\n".format(organization_info(), Colours.FAIL, Colours.ENDC, err)) + sys.stderr.write("{}{}Error:{} {}\n".format(organization_info(), Colors.FAIL, Colors.ENDC, err)) diff --git a/src/openai/cli/_utils.py b/src/openai/cli/_utils.py index 027ab08de3..673eed613c 100644 --- a/src/openai/cli/_utils.py +++ b/src/openai/cli/_utils.py @@ -9,7 +9,7 @@ from .._models import BaseModel -class Colours: +class Colors: HEADER = "\033[95m" OKBLUE = "\033[94m" OKGREEN = "\033[92m" From e9300e0ea899919e105c3b858c9b5af2a6c34803 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 Nov 2023 19:54:29 +0000 Subject: [PATCH 194/914] chore(internal): fix devcontainer interpeter path (#810) --- .devcontainer/devcontainer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b9da964dc1..bbeb30b148 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,6 +17,7 @@ "settings": { "terminal.integrated.shell.linux": "/bin/bash", "python.pythonPath": ".venv/bin/python", + "python.defaultInterpreterPath": ".venv/bin/python", "python.typeChecking": "basic", "terminal.integrated.env.linux": { "PATH": "/home/vscode/.rye/shims:${env:PATH}" From ea4fa238abdcddbf14aa7d42b7689cdd00a2d290 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:44:11 +0000 Subject: [PATCH 195/914] fix(breaking!): correct broken type names in moderation categories (#811) Migration: - `self_minus_harm_intent` -> `self_harm_intent` - `self_minus_harm_instructions` -> `self_harm_instructions` - `self_minus_harm` -> `self_harm` --- src/openai/types/moderation.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index bf586fc24a..3602a46985 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -35,20 +35,20 @@ class Categories(BaseModel): orientation, disability status, or caste. """ - self_minus_harm: bool = FieldInfo(alias="self-harm") + self_harm: bool = FieldInfo(alias="self-harm") """ Content that promotes, encourages, or depicts acts of self-harm, such as suicide, cutting, and eating disorders. """ - self_minus_harm_instructions: bool = FieldInfo(alias="self-harm/instructions") + self_harm_instructions: bool = FieldInfo(alias="self-harm/instructions") """ Content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, or that gives instructions or advice on how to commit such acts. """ - self_minus_harm_intent: bool = FieldInfo(alias="self-harm/intent") + self_harm_intent: bool = FieldInfo(alias="self-harm/intent") """ Content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, such as suicide, cutting, and eating disorders. @@ -84,13 +84,13 @@ class CategoryScores(BaseModel): hate_threatening: float = FieldInfo(alias="hate/threatening") """The score for the category 'hate/threatening'.""" - self_minus_harm: float = FieldInfo(alias="self-harm") + self_harm: float = FieldInfo(alias="self-harm") """The score for the category 'self-harm'.""" - self_minus_harm_instructions: float = FieldInfo(alias="self-harm/instructions") + self_harm_instructions: float = FieldInfo(alias="self-harm/instructions") """The score for the category 'self-harm/instructions'.""" - self_minus_harm_intent: float = FieldInfo(alias="self-harm/intent") + self_harm_intent: float = FieldInfo(alias="self-harm/intent") """The score for the category 'self-harm/intent'.""" sexual: float From 98b3240b8696472c43b954b8643cc7c0deb22908 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 14 Nov 2023 03:48:14 +0000 Subject: [PATCH 196/914] feat(api): add gpt-3.5-turbo-1106 (#813) --- src/openai/resources/chat/completions.py | 8 ++++++++ src/openai/types/chat/completion_create_params.py | 1 + 2 files changed, 9 insertions(+) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index ff36424442..d0657b2f73 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -51,6 +51,7 @@ def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", @@ -221,6 +222,7 @@ def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", @@ -391,6 +393,7 @@ def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", @@ -561,6 +564,7 @@ def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", @@ -648,6 +652,7 @@ async def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", @@ -818,6 +823,7 @@ async def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", @@ -988,6 +994,7 @@ async def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", @@ -1158,6 +1165,7 @@ async def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 51c864588b..69fe250eca 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -44,6 +44,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", From 7758c54b0b70edd665dce73661475f18973116d6 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 14 Nov 2023 04:00:24 +0000 Subject: [PATCH 197/914] docs: add azure env vars (#814) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f3698a9ff4..e7e65828b8 100644 --- a/README.md +++ b/README.md @@ -483,10 +483,10 @@ print(completion.model_dump_json(indent=2)) In addition to the options provided in the base `OpenAI` client, the following options are provided: -- `azure_endpoint` +- `azure_endpoint` (or the `AZURE_OPENAI_ENDPOINT` environment variable) - `azure_deployment` -- `api_version` -- `azure_ad_token` +- `api_version` (or the `OPENAI_API_VERSION` environment variable) +- `azure_ad_token` (or the `AZURE_OPENAI_AD_TOKEN` environment variable) - `azure_ad_token_provider` An example of using the client with Azure Active Directory can be found [here](https://github.com/openai/openai-python/blob/main/examples/azure_ad.py). From 71d317233bb4399994fddfc70133c7795836f21f Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 14 Nov 2023 19:03:55 +0900 Subject: [PATCH 198/914] docs: fix code comment typo (#790) specifc -> specific --- src/openai/_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index ebaef99454..6d5aad5963 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -149,7 +149,7 @@ def construct( if not PYDANTIC_V2: # we define aliases for some of the new pydantic v2 methods so # that we can just document these methods without having to specify - # a specifc pydantic version as some users may not know which + # a specific pydantic version as some users may not know which # pydantic version they are currently using @override From 0733934fbcdda57ae4506563924b63c867f3dcf7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:54:17 +0000 Subject: [PATCH 199/914] feat(client): support reading the base url from an env variable (#829) --- README.md | 1 + src/openai/_client.py | 4 ++++ tests/test_client.py | 12 ++++++++++++ tests/utils.py | 17 ++++++++++++++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7e65828b8..82eeb57ccb 100644 --- a/README.md +++ b/README.md @@ -437,6 +437,7 @@ import httpx from openai import OpenAI client = OpenAI( + # Or use the `OPENAI_BASE_URL` env var base_url="http://my.test.server.example.com:8083/", http_client=httpx.Client( proxies="http://my.test.proxy.example.com/", diff --git a/src/openai/_client.py b/src/openai/_client.py index 7820d5f96d..6664dc4233 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -99,6 +99,8 @@ def __init__( organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization + if base_url is None: + base_url = os.environ.get("OPENAI_BASE_URL") if base_url is None: base_url = f"https://api.openai.com/v1" @@ -307,6 +309,8 @@ def __init__( organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization + if base_url is None: + base_url = os.environ.get("OPENAI_BASE_URL") if base_url is None: base_url = f"https://api.openai.com/v1" diff --git a/tests/test_client.py b/tests/test_client.py index e3daa4d2b1..e295d193e8 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -26,6 +26,8 @@ make_request_options, ) +from .utils import update_env + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" @@ -399,6 +401,11 @@ class Model2(BaseModel): assert isinstance(response, Model1) assert response.foo == 1 + def test_base_url_env(self) -> None: + with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): + client = OpenAI(api_key=api_key, _strict_response_validation=True) + assert client.base_url == "http://localhost:5000/from/env/" + @pytest.mark.parametrize( "client", [ @@ -932,6 +939,11 @@ class Model2(BaseModel): assert isinstance(response, Model1) assert response.foo == 1 + def test_base_url_env(self) -> None: + with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): + client = AsyncOpenAI(api_key=api_key, _strict_response_validation=True) + assert client.base_url == "http://localhost:5000/from/env/" + @pytest.mark.parametrize( "client", [ diff --git a/tests/utils.py b/tests/utils.py index 3cccab223a..b513794017 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,7 +1,9 @@ from __future__ import annotations +import os import traceback -from typing import Any, TypeVar, cast +import contextlib +from typing import Any, TypeVar, Iterator, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type @@ -103,3 +105,16 @@ def _assert_list_type(type_: type[object], value: object) -> None: inner_type = get_args(type_)[0] for entry in value: assert_type(inner_type, entry) # type: ignore + + +@contextlib.contextmanager +def update_env(**new_env: str) -> Iterator[None]: + old = os.environ.copy() + + try: + os.environ.update(new_env) + + yield None + finally: + os.environ.clear() + os.environ.update(old) From d3cb40ca6a5d2cbbeff94f50baea20162b7966bc Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:55:00 +0000 Subject: [PATCH 200/914] release: 1.3.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 27 +++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 862a05b695..2a8f4ffddf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.2.4" + ".": "1.3.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e1e8a331f9..fceac1bb0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 1.3.0 (2023-11-15) + +Full Changelog: [v1.2.4...v1.3.0](https://github.com/openai/openai-python/compare/v1.2.4...v1.3.0) + +### Features + +* **api:** add gpt-3.5-turbo-1106 ([#813](https://github.com/openai/openai-python/issues/813)) ([9bb3c4e](https://github.com/openai/openai-python/commit/9bb3c4ed88c890db2605a793aa39fffa1d84e8ef)) +* **client:** support reading the base url from an env variable ([#829](https://github.com/openai/openai-python/issues/829)) ([ca5fdc6](https://github.com/openai/openai-python/commit/ca5fdc6ca006a3550cc5eeea70dd3d96b9ba305a)) + + +### Bug Fixes + +* **breaking!:** correct broken type names in moderation categories ([#811](https://github.com/openai/openai-python/issues/811)) ([0bc211f](https://github.com/openai/openai-python/commit/0bc211fd46f4fcc1f7687bdfdce26894b679cb4f)) + + +### Chores + +* fix typo in docs and add request header for function calls ([#807](https://github.com/openai/openai-python/issues/807)) ([cbef703](https://github.com/openai/openai-python/commit/cbef7030c7b21a0c766fe83c62657cea1cd8d31c)) +* **internal:** fix devcontainer interpeter path ([#810](https://github.com/openai/openai-python/issues/810)) ([0acc07d](https://github.com/openai/openai-python/commit/0acc07dd8281ba881f91689b8a5e4254e8743fbc)) + + +### Documentation + +* add azure env vars ([#814](https://github.com/openai/openai-python/issues/814)) ([bd8e32a](https://github.com/openai/openai-python/commit/bd8e32a380218d0c9ff43643ccc1a25b3c35120d)) +* fix code comment typo ([#790](https://github.com/openai/openai-python/issues/790)) ([8407a27](https://github.com/openai/openai-python/commit/8407a27e848ae611eb087c8d10632447d7c55498)) +* **readme:** fix broken azure_ad notebook link ([#781](https://github.com/openai/openai-python/issues/781)) ([3b92cdf](https://github.com/openai/openai-python/commit/3b92cdfa5490b50a72811bec2f6e54e070847961)) + ## 1.2.4 (2023-11-13) Full Changelog: [v1.2.3...v1.2.4](https://github.com/openai/openai-python/compare/v1.2.3...v1.2.4) diff --git a/pyproject.toml b/pyproject.toml index dc08634e4a..83c54f81ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.2.4" +version = "1.3.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f22b1aae3f..79690d85e5 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.2.4" # x-release-please-version +__version__ = "1.3.0" # x-release-please-version From 23d03a1c561e57dab93c361962f41b9c3320b845 Mon Sep 17 00:00:00 2001 From: ashjeanbird <132003136+ashjeanbird@users.noreply.github.com> Date: Wed, 15 Nov 2023 17:56:35 -1000 Subject: [PATCH 201/914] Adds DALL-E note to Azure OpenAI in v1 SDK (#766) * Adds DALL-E note to Azure OpenAI in v1 SDK Adds important note for Azure OpenAI users. * Updates DALL-E docs to include workaround Included a link to the workaround --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 82eeb57ccb..94592bb2a6 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The API documentation can be found [here](https://platform.openai.com/docs). ## Installation > [!IMPORTANT] -> The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code. +> The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code. ```sh pip install openai @@ -459,6 +459,10 @@ class instead of the `OpenAI` class. > The Azure API shape differs from the core API shape which means that the static types for responses / params > won't always be correct. +The latest release of the OpenAI Python library doesn't currently support DALL-E when used with Azure OpenAI. DALL-E with Azure OpenAI is still supported with 0.28.1. For those who can't wait for native support for DALL-E and Azure OpenAI we're providing [two code examples](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/migration?tabs=python%2Cdalle-fix#dall-e-fix) which can be used as a workaround. + + + ```py from openai import AzureOpenAI From db6cd7649a14eb96c4cd124dc4b9b70da68996f4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:30:31 +0000 Subject: [PATCH 202/914] release: 1.3.1 (#839) * chore(internal): add publish script (#838) * release: 1.3.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ README.md | 6 +----- pyproject.toml | 2 +- src/openai/_version.py | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2a8f4ffddf..0e5b256d26 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.0" + ".": "1.3.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index fceac1bb0f..cfe2ba9887 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.3.1 (2023-11-16) + +Full Changelog: [v1.3.0...v1.3.1](https://github.com/openai/openai-python/compare/v1.3.0...v1.3.1) + +### Chores + +* **internal:** add publish script ([#838](https://github.com/openai/openai-python/issues/838)) ([3ea41bc](https://github.com/openai/openai-python/commit/3ea41bcede374c4e5c92d85108281637c3382e12)) + ## 1.3.0 (2023-11-15) Full Changelog: [v1.2.4...v1.3.0](https://github.com/openai/openai-python/compare/v1.2.4...v1.3.0) diff --git a/README.md b/README.md index 94592bb2a6..82eeb57ccb 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The API documentation can be found [here](https://platform.openai.com/docs). ## Installation > [!IMPORTANT] -> The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code. +> The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code. ```sh pip install openai @@ -459,10 +459,6 @@ class instead of the `OpenAI` class. > The Azure API shape differs from the core API shape which means that the static types for responses / params > won't always be correct. -The latest release of the OpenAI Python library doesn't currently support DALL-E when used with Azure OpenAI. DALL-E with Azure OpenAI is still supported with 0.28.1. For those who can't wait for native support for DALL-E and Azure OpenAI we're providing [two code examples](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/migration?tabs=python%2Cdalle-fix#dall-e-fix) which can be used as a workaround. - - - ```py from openai import AzureOpenAI diff --git a/pyproject.toml b/pyproject.toml index 83c54f81ca..cf091a9a0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.0" +version = "1.3.1" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 79690d85e5..052b27a609 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.0" # x-release-please-version +__version__ = "1.3.1" # x-release-please-version From f4b9655f4065ab8432f028c33f973a1945a760ee Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:00:06 +0000 Subject: [PATCH 203/914] release: 1.3.2 (#842) * docs(readme): minor updates (#841) * release: 1.3.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ README.md | 5 ++++- pyproject.toml | 2 +- src/openai/_version.py | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0e5b256d26..c658eefeff 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.1" + ".": "1.3.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe2ba9887..040aa45486 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.3.2 (2023-11-16) + +Full Changelog: [v1.3.1...v1.3.2](https://github.com/openai/openai-python/compare/v1.3.1...v1.3.2) + +### Documentation + +* **readme:** minor updates ([#841](https://github.com/openai/openai-python/issues/841)) ([7273ad1](https://github.com/openai/openai-python/commit/7273ad1510043d3e264969c72403a1a237401910)) + ## 1.3.1 (2023-11-16) Full Changelog: [v1.3.0...v1.3.1](https://github.com/openai/openai-python/compare/v1.3.0...v1.3.1) diff --git a/README.md b/README.md index 82eeb57ccb..d916d3d0ea 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,10 @@ We recommend that you always instantiate a client (e.g., with `client = OpenAI() ## Using types -Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev), which provide helper methods for things like serializing back into JSON ([v1](https://docs.pydantic.dev/1.10/usage/models/), [v2](https://docs.pydantic.dev/latest/usage/serialization/)). To get a dictionary, call `model.model_dump()`. +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev), which provide helper methods for things like: + +- Serializing back into JSON, `model.model_dump_json(indent=2, exclude_unset=True)` +- Converting to a dictionary, `model.model_dump(exclude_unset=True)` Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. diff --git a/pyproject.toml b/pyproject.toml index cf091a9a0d..e6c3422db5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.1" +version = "1.3.2" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 052b27a609..12c20f5fa2 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.1" # x-release-please-version +__version__ = "1.3.2" # x-release-please-version From 90aa5eb3ed6b92d9a1de89c0ee063f4768f92256 Mon Sep 17 00:00:00 2001 From: David Schnurr Date: Thu, 16 Nov 2023 21:57:12 -0800 Subject: [PATCH 204/914] Remove DALL-E note (#830) From 01d3b37305b28a45d0d9adfbc23c958bf924fe05 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 17 Nov 2023 23:35:26 +0000 Subject: [PATCH 205/914] release: 1.3.3 (#847) * chore(internal): update type hint for helper function (#846) * release: 1.3.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_utils/_transform.py | 4 ++-- src/openai/_version.py | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c658eefeff..8022176dd3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.2" + ".": "1.3.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 040aa45486..970724b4b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.3.3 (2023-11-17) + +Full Changelog: [v1.3.2...v1.3.3](https://github.com/openai/openai-python/compare/v1.3.2...v1.3.3) + +### Chores + +* **internal:** update type hint for helper function ([#846](https://github.com/openai/openai-python/issues/846)) ([9a5966c](https://github.com/openai/openai-python/commit/9a5966c70fce620a183de580938556730564a405)) + ## 1.3.2 (2023-11-16) Full Changelog: [v1.3.1...v1.3.2](https://github.com/openai/openai-python/compare/v1.3.1...v1.3.2) diff --git a/pyproject.toml b/pyproject.toml index e6c3422db5..8c9c6022f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.2" +version = "1.3.3" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index d953505fff..769f7362b9 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List, Mapping, TypeVar, cast +from typing import Any, Mapping, TypeVar, cast from datetime import date, datetime from typing_extensions import Literal, get_args, override, get_type_hints @@ -60,7 +60,7 @@ def __repr__(self) -> str: def maybe_transform( - data: Mapping[str, object] | List[Any] | None, + data: object, expected_type: object, ) -> Any | None: """Wrapper over `transform()` that allows `None` to be passed. diff --git a/src/openai/_version.py b/src/openai/_version.py index 12c20f5fa2..b04859b6bb 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.2" # x-release-please-version +__version__ = "1.3.3" # x-release-please-version From f6c2aedfe3dfceeabd551909b572d72c7cef4373 Mon Sep 17 00:00:00 2001 From: Muhammed Al-Dulaimi Date: Sun, 19 Nov 2023 03:29:28 +0300 Subject: [PATCH 206/914] Add assistants example (#773) --- examples/assistant.py | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 examples/assistant.py diff --git a/examples/assistant.py b/examples/assistant.py new file mode 100644 index 0000000000..ad3c1376de --- /dev/null +++ b/examples/assistant.py @@ -0,0 +1,53 @@ +import openai +import time + +# gets API Key from environment variable OPENAI_API_KEY +client = openai.OpenAI() + +assistant = client.beta.assistants.create( + name="Math Tutor", + instructions="You are a personal math tutor. Write and run code to answer math questions.", + tools=[{"type": "code_interpreter"}], + model="gpt-4-1106-preview", +) + +thread = client.beta.threads.create() + +message = client.beta.threads.messages.create( + thread_id=thread.id, + role="user", + content="I need to solve the equation `3x + 11 = 14`. Can you help me?" +) + +run = client.beta.threads.runs.create( + thread_id=thread.id, + assistant_id=assistant.id, + instructions="Please address the user as Jane Doe. The user has a premium account." +) + +print("checking assistant status. ") +while True: + run = client.beta.threads.runs.retrieve( + thread_id=thread.id, + run_id=run.id + ) + + if run.status == "completed": + print("done!") + messages = client.beta.threads.messages.list( + thread_id=thread.id + ) + + print("messages: ") + for message in messages: + print({ + "role": message.role, + "message": message.content[0].text.value + }) + + client.beta.assistants.delete(assistant.id) + + break + else: + print("in progress...") + time.sleep(5) \ No newline at end of file From 2343e63022837c867d79932d624352d570ff7173 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 20 Nov 2023 17:08:37 -0500 Subject: [PATCH 207/914] chore(examples): fix static types in assistants example (#852) --- examples/assistant.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/examples/assistant.py b/examples/assistant.py index ad3c1376de..c5fbb82a3a 100644 --- a/examples/assistant.py +++ b/examples/assistant.py @@ -1,6 +1,7 @@ -import openai import time +import openai + # gets API Key from environment variable OPENAI_API_KEY client = openai.OpenAI() @@ -16,38 +17,31 @@ message = client.beta.threads.messages.create( thread_id=thread.id, role="user", - content="I need to solve the equation `3x + 11 = 14`. Can you help me?" + content="I need to solve the equation `3x + 11 = 14`. Can you help me?", ) run = client.beta.threads.runs.create( - thread_id=thread.id, - assistant_id=assistant.id, - instructions="Please address the user as Jane Doe. The user has a premium account." + thread_id=thread.id, + assistant_id=assistant.id, + instructions="Please address the user as Jane Doe. The user has a premium account.", ) print("checking assistant status. ") while True: - run = client.beta.threads.runs.retrieve( - thread_id=thread.id, - run_id=run.id - ) + run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id) if run.status == "completed": print("done!") - messages = client.beta.threads.messages.list( - thread_id=thread.id - ) + messages = client.beta.threads.messages.list(thread_id=thread.id) print("messages: ") for message in messages: - print({ - "role": message.role, - "message": message.content[0].text.value - }) + assert message.content[0].type == "text" + print({"role": message.role, "message": message.content[0].text.value}) client.beta.assistants.delete(assistant.id) - + break else: print("in progress...") - time.sleep(5) \ No newline at end of file + time.sleep(5) From c26014e26c119eb9cf396175bcad2002082d1526 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:24:05 -0500 Subject: [PATCH 208/914] fix(client): attempt to parse unknown json content types (#854) --- src/openai/_base_client.py | 20 ++++++++++++------ src/openai/_models.py | 13 ++++++++++++ src/openai/_response.py | 31 +++++++++++++++++++--------- tests/test_client.py | 42 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 3db8b6fa35..a168301f75 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -74,7 +74,12 @@ RAW_RESPONSE_HEADER, ) from ._streaming import Stream, AsyncStream -from ._exceptions import APIStatusError, APITimeoutError, APIConnectionError +from ._exceptions import ( + APIStatusError, + APITimeoutError, + APIConnectionError, + APIResponseValidationError, +) log: logging.Logger = logging.getLogger(__name__) @@ -518,13 +523,16 @@ def _process_response_data( if cast_to is UnknownResponse: return cast(ResponseT, data) - if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): - return cast(ResponseT, cast_to.build(response=response, data=data)) + try: + if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): + return cast(ResponseT, cast_to.build(response=response, data=data)) - if self._strict_response_validation: - return cast(ResponseT, validate_type(type_=cast_to, value=data)) + if self._strict_response_validation: + return cast(ResponseT, validate_type(type_=cast_to, value=data)) - return cast(ResponseT, construct_type(type_=cast_to, value=data)) + return cast(ResponseT, construct_type(type_=cast_to, value=data)) + except pydantic.ValidationError as err: + raise APIResponseValidationError(response=response, body=data) from err @property def qs(self) -> Querystring: diff --git a/src/openai/_models.py b/src/openai/_models.py index 6d5aad5963..5b8c96010f 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -263,6 +263,19 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: return construct_type(value=value, type_=type_) +def is_basemodel(type_: type) -> bool: + """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" + origin = get_origin(type_) or type_ + if is_union(type_): + for variant in get_args(type_): + if is_basemodel(variant): + return True + + return False + + return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) + + def construct_type(*, value: object, type_: type) -> object: """Loose coercion to the expected type with construction of nested values. diff --git a/src/openai/_response.py b/src/openai/_response.py index 3cc8fd8cc1..933c37525e 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -1,17 +1,17 @@ from __future__ import annotations import inspect +import logging import datetime import functools from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast from typing_extensions import Awaitable, ParamSpec, get_args, override, get_origin import httpx -import pydantic from ._types import NoneType, UnknownResponse, BinaryResponseContent from ._utils import is_given -from ._models import BaseModel +from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER from ._exceptions import APIResponseValidationError @@ -23,6 +23,8 @@ P = ParamSpec("P") R = TypeVar("R") +log: logging.Logger = logging.getLogger(__name__) + class APIResponse(Generic[R]): _cast_to: type[R] @@ -174,6 +176,18 @@ def _parse(self) -> R: # in the response, e.g. application/json; charset=utf-8 content_type, *_ = response.headers.get("content-type").split(";") if content_type != "application/json": + if is_basemodel(cast_to): + try: + data = response.json() + except Exception as exc: + log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) + else: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + if self._client._strict_response_validation: raise APIResponseValidationError( response=response, @@ -188,14 +202,11 @@ def _parse(self) -> R: data = response.json() - try: - return self._client._process_response_data( - data=data, - cast_to=cast_to, # type: ignore - response=response, - ) - except pydantic.ValidationError as err: - raise APIResponseValidationError(response=response, body=data) from err + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) @override def __repr__(self) -> str: diff --git a/tests/test_client.py b/tests/test_client.py index e295d193e8..c5dbfe4bfe 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -401,6 +401,27 @@ class Model2(BaseModel): assert isinstance(response, Model1) assert response.foo == 1 + @pytest.mark.respx(base_url=base_url) + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + """ + Response that sets Content-Type to something other than application/json but returns json data + """ + + class Model(BaseModel): + foo: int + + respx_mock.get("/foo").mock( + return_value=httpx.Response( + 200, + content=json.dumps({"foo": 2}), + headers={"Content-Type": "application/text"}, + ) + ) + + response = self.client.get("/foo", cast_to=Model) + assert isinstance(response, Model) + assert response.foo == 2 + def test_base_url_env(self) -> None: with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): client = OpenAI(api_key=api_key, _strict_response_validation=True) @@ -939,6 +960,27 @@ class Model2(BaseModel): assert isinstance(response, Model1) assert response.foo == 1 + @pytest.mark.respx(base_url=base_url) + async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + """ + Response that sets Content-Type to something other than application/json but returns json data + """ + + class Model(BaseModel): + foo: int + + respx_mock.get("/foo").mock( + return_value=httpx.Response( + 200, + content=json.dumps({"foo": 2}), + headers={"Content-Type": "application/text"}, + ) + ) + + response = await self.client.get("/foo", cast_to=Model) + assert isinstance(response, Model) + assert response.foo == 2 + def test_base_url_env(self) -> None: with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): client = AsyncOpenAI(api_key=api_key, _strict_response_validation=True) From b085b1296ec9e3ba6f628d95c20b344a84e7b1cc Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:24:46 -0500 Subject: [PATCH 209/914] release: 1.3.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8022176dd3..c050b0fe03 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.3" + ".": "1.3.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 970724b4b8..1caef71db9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.3.4 (2023-11-21) + +Full Changelog: [v1.3.3...v1.3.4](https://github.com/openai/openai-python/compare/v1.3.3...v1.3.4) + +### Bug Fixes + +* **client:** attempt to parse unknown json content types ([#854](https://github.com/openai/openai-python/issues/854)) ([ba50466](https://github.com/openai/openai-python/commit/ba5046611029a67714d5120b9cc6a3c7fecce10c)) + + +### Chores + +* **examples:** fix static types in assistants example ([#852](https://github.com/openai/openai-python/issues/852)) ([5b47b2c](https://github.com/openai/openai-python/commit/5b47b2c542b9b4fb143af121022e2d5ad0890ef4)) + ## 1.3.3 (2023-11-17) Full Changelog: [v1.3.2...v1.3.3](https://github.com/openai/openai-python/compare/v1.3.2...v1.3.3) diff --git a/pyproject.toml b/pyproject.toml index 8c9c6022f2..ae6fbaeeca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.3" +version = "1.3.4" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b04859b6bb..ddfc847864 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.3" # x-release-please-version +__version__ = "1.3.4" # x-release-please-version From 97a6895b9d137a8ca8996f773917773de5f5b4c4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:14:58 -0500 Subject: [PATCH 210/914] fix(azure): ensure custom options can be passed to copy (#858) --- src/openai/_client.py | 18 ++++---- src/openai/lib/azure.py | 94 +++++++++++++++++++++++++++++++++++++++-- tests/lib/test_azure.py | 30 +++++++++++++ 3 files changed, 129 insertions(+), 13 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index 6664dc4233..aa00073281 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -4,8 +4,8 @@ import os import asyncio -from typing import Union, Mapping -from typing_extensions import override +from typing import Any, Union, Mapping +from typing_extensions import Self, override import httpx @@ -164,12 +164,10 @@ def copy( set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, - ) -> OpenAI: + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. - - It should be noted that this does not share the underlying httpx client class which may lead - to performance issues. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") @@ -199,6 +197,7 @@ def copy( max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, + **_extra_kwargs, ) # Alias for `copy` for nicer inline usage, e.g. @@ -374,12 +373,10 @@ def copy( set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, - ) -> AsyncOpenAI: + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. - - It should be noted that this does not share the underlying httpx client class which may lead - to performance issues. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") @@ -409,6 +406,7 @@ def copy( max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, + **_extra_kwargs, ) # Alias for `copy` for nicer inline usage, e.g. diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index d31313e95a..27bebd8cab 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -3,7 +3,7 @@ import os import inspect from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, overload -from typing_extensions import override +from typing_extensions import Self, override import httpx @@ -178,7 +178,7 @@ def __init__( if default_query is None: default_query = {"api-version": api_version} else: - default_query = {"api-version": api_version, **default_query} + default_query = {**default_query, "api-version": api_version} if base_url is None: if azure_endpoint is None: @@ -212,9 +212,53 @@ def __init__( http_client=http_client, _strict_response_validation=_strict_response_validation, ) + self._api_version = api_version self._azure_ad_token = azure_ad_token self._azure_ad_token_provider = azure_ad_token_provider + @override + def copy( + self, + *, + api_key: str | None = None, + organization: str | None = None, + api_version: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AzureADTokenProvider | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.Client | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + return super().copy( + api_key=api_key, + organization=organization, + base_url=base_url, + timeout=timeout, + http_client=http_client, + max_retries=max_retries, + default_headers=default_headers, + set_default_headers=set_default_headers, + default_query=default_query, + set_default_query=set_default_query, + _extra_kwargs={ + "api_version": api_version or self._api_version, + "azure_ad_token": azure_ad_token or self._azure_ad_token, + "azure_ad_token_provider": azure_ad_token_provider or self._azure_ad_token_provider, + **_extra_kwargs, + }, + ) + + with_options = copy + def _get_azure_ad_token(self) -> str | None: if self._azure_ad_token is not None: return self._azure_ad_token @@ -367,7 +411,7 @@ def __init__( if default_query is None: default_query = {"api-version": api_version} else: - default_query = {"api-version": api_version, **default_query} + default_query = {**default_query, "api-version": api_version} if base_url is None: if azure_endpoint is None: @@ -401,9 +445,53 @@ def __init__( http_client=http_client, _strict_response_validation=_strict_response_validation, ) + self._api_version = api_version self._azure_ad_token = azure_ad_token self._azure_ad_token_provider = azure_ad_token_provider + @override + def copy( + self, + *, + api_key: str | None = None, + organization: str | None = None, + api_version: str | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.AsyncClient | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + return super().copy( + api_key=api_key, + organization=organization, + base_url=base_url, + timeout=timeout, + http_client=http_client, + max_retries=max_retries, + default_headers=default_headers, + set_default_headers=set_default_headers, + default_query=default_query, + set_default_query=set_default_query, + _extra_kwargs={ + "api_version": api_version or self._api_version, + "azure_ad_token": azure_ad_token or self._azure_ad_token, + "azure_ad_token_provider": azure_ad_token_provider or self._azure_ad_token_provider, + **_extra_kwargs, + }, + ) + + with_options = copy + async def _get_azure_ad_token(self) -> str | None: if self._azure_ad_token is not None: return self._azure_ad_token diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py index b0bd87571b..9360b2925a 100644 --- a/tests/lib/test_azure.py +++ b/tests/lib/test_azure.py @@ -1,4 +1,5 @@ from typing import Union +from typing_extensions import Literal import pytest @@ -34,3 +35,32 @@ def test_implicit_deployment_path(client: Client) -> None: req.url == "https://example-resource.azure.openai.com/openai/deployments/my-deployment-model/chat/completions?api-version=2023-07-01" ) + + +@pytest.mark.parametrize( + "client,method", + [ + (sync_client, "copy"), + (sync_client, "with_options"), + (async_client, "copy"), + (async_client, "with_options"), + ], +) +def test_client_copying(client: Client, method: Literal["copy", "with_options"]) -> None: + if method == "copy": + copied = client.copy() + else: + copied = client.with_options() + + assert copied._custom_query == {"api-version": "2023-07-01"} + + +@pytest.mark.parametrize( + "client", + [sync_client, async_client], +) +def test_client_copying_override_options(client: Client) -> None: + copied = client.copy( + api_version="2022-05-01", + ) + assert copied._custom_query == {"api-version": "2022-05-01"} From 7322b6643aaed4c1d9e5db231f6d88f9cc5266a3 Mon Sep 17 00:00:00 2001 From: Roberto Pastor Muela <37798125+RobPasMue@users.noreply.github.com> Date: Tue, 21 Nov 2023 20:26:55 +0100 Subject: [PATCH 211/914] chore(package): add license classifier (#826) Having the license classifier in PyPI is also important so that it is uplaoded as part of the package's metadata. Otherwise, when querying it to PyPI this information is not shown in the package's metadata. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index ae6fbaeeca..21004dac06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Operating System :: Microsoft :: Windows", "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: Apache Software License", ] [project.optional-dependencies] From 58eed2b40932c43cf0eda02afcd072fb1f6a878a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:34:24 -0500 Subject: [PATCH 212/914] chore(package): add license classifier metadata (#860) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 21004dac06..85ad9e2177 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Operating System :: Microsoft :: Windows", "Topic :: Software Development :: Libraries :: Python Modules", - "License :: OSI Approved :: Apache Software License", + "License :: OSI Approved :: Apache Software License" ] [project.optional-dependencies] From 9f828cb1ea7c22a6388fcd71b5de1a0dde3a9a9f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:35:04 -0500 Subject: [PATCH 213/914] release: 1.3.5 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c050b0fe03..13787787c4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.4" + ".": "1.3.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1caef71db9..0869b3888c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.3.5 (2023-11-21) + +Full Changelog: [v1.3.4...v1.3.5](https://github.com/openai/openai-python/compare/v1.3.4...v1.3.5) + +### Bug Fixes + +* **azure:** ensure custom options can be passed to copy ([#858](https://github.com/openai/openai-python/issues/858)) ([05ca0d6](https://github.com/openai/openai-python/commit/05ca0d68e84d40f975614d27cb52c0f382104377)) + + +### Chores + +* **package:** add license classifier ([#826](https://github.com/openai/openai-python/issues/826)) ([bec004d](https://github.com/openai/openai-python/commit/bec004d030b277e05bdd51f66fae1e881291c30b)) +* **package:** add license classifier metadata ([#860](https://github.com/openai/openai-python/issues/860)) ([80dffb1](https://github.com/openai/openai-python/commit/80dffb17ff0a10b0b9ea704c4247521e48b68408)) + ## 1.3.4 (2023-11-21) Full Changelog: [v1.3.3...v1.3.4](https://github.com/openai/openai-python/compare/v1.3.3...v1.3.4) diff --git a/pyproject.toml b/pyproject.toml index 85ad9e2177..f17def16b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.4" +version = "1.3.5" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ddfc847864..1ef6479491 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.4" # x-release-please-version +__version__ = "1.3.5" # x-release-please-version From af67cfab4210d8e497c05390ce14f39105c77519 Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Tue, 28 Nov 2023 13:44:46 -0800 Subject: [PATCH 214/914] Update README.md (#892) --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d916d3d0ea..33ec6add52 100644 --- a/README.md +++ b/README.md @@ -94,8 +94,9 @@ stream = client.chat.completions.create( messages=[{"role": "user", "content": "Say this is a test"}], stream=True, ) -for part in stream: - print(part.choices[0].delta.content or "") +for chunk in stream: + if chunk.choices[0].delta.content is not None: + print(chunk.choices[0].delta.content, end="") ``` The async client uses the exact same interface. @@ -110,8 +111,9 @@ stream = await client.chat.completions.create( messages=[{"role": "user", "content": "Say this is a test"}], stream=True, ) -async for part in stream: - print(part.choices[0].delta.content or "") +async for chunk in stream: + if chunk.choices[0].delta.content is not None: + print(chunk.choices[0].delta.content, end="") ``` ## Module-level client From 2fc3204ecef7839296314b7248d9b63292aeb49e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 22 Nov 2023 21:23:46 -0500 Subject: [PATCH 215/914] fix(client): add support for streaming binary responses (#866) --- examples/audio.py | 5 ++++- src/openai/_base_client.py | 8 ++++++-- src/openai/_models.py | 2 ++ src/openai/_types.py | 23 +++++++++++++++++++++-- src/openai/resources/audio/speech.py | 20 ++++++++++++++++++-- src/openai/resources/files.py | 20 ++++++++++++++++++-- 6 files changed, 69 insertions(+), 9 deletions(-) diff --git a/examples/audio.py b/examples/audio.py index a5f535dcd6..e86acbf828 100755 --- a/examples/audio.py +++ b/examples/audio.py @@ -13,7 +13,10 @@ def main() -> None: # Create text-to-speech audio file response = openai.audio.speech.create( - model="tts-1", voice="alloy", input="the quick brown fox jumped over the lazy dogs" + model="tts-1", + voice="alloy", + input="the quick brown fox jumped over the lazy dogs", + stream=True, ) response.stream_to_file(speech_file_path) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index a168301f75..9a023ba961 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -863,7 +863,7 @@ def _request( self._prepare_request(request) try: - response = self._client.send(request, auth=self.custom_auth, stream=stream) + response = self._client.send(request, auth=self.custom_auth, stream=stream or options.stream or False) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) @@ -1304,7 +1304,7 @@ async def _request( await self._prepare_request(request) try: - response = await self._client.send(request, auth=self.custom_auth, stream=stream) + response = await self._client.send(request, auth=self.custom_auth, stream=stream or options.stream or False) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) @@ -1541,6 +1541,7 @@ def make_request_options( idempotency_key: str | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, post_parser: PostParser | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} @@ -1562,6 +1563,9 @@ def make_request_options( if idempotency_key is not None: options["idempotency_key"] = idempotency_key + if stream is not None: + options["stream"] = stream + if is_given(post_parser): # internal options["post_parser"] = post_parser # type: ignore diff --git a/src/openai/_models.py b/src/openai/_models.py index 5b8c96010f..a0e596149c 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -403,6 +403,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): params: Query headers: Headers max_retries: int + stream: bool | None timeout: float | Timeout | None files: HttpxRequestFiles | None idempotency_key: str @@ -420,6 +421,7 @@ class FinalRequestOptions(pydantic.BaseModel): timeout: Union[float, Timeout, None, NotGiven] = NotGiven() files: Union[HttpxRequestFiles, None] = None idempotency_key: Union[str, None] = None + stream: Union[bool, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() # It should be noted that we cannot use `json` here as that would override diff --git a/src/openai/_types.py b/src/openai/_types.py index 9e962a1078..013b658f0e 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -130,7 +130,16 @@ def stream_to_file( chunk_size: int | None = None, ) -> None: """ - Stream the output to the given file. + Stream the output to the given file. NOTE, requires passing `stream=True` + to the request for expected behavior, e.g., + + response = openai.audio.speech.create( + model="tts-1", + voice="alloy", + input="the quick brown fox jumped over the lazy dogs", + stream=True, + ) + response.stream_to_file(speech_file_path) """ pass @@ -185,7 +194,16 @@ async def astream_to_file( chunk_size: int | None = None, ) -> None: """ - Stream the output to the given file. + Stream the output to the given file. NOTE, requires passing `stream=True` + to the request for expected behavior, e.g., + + response = await openai.audio.speech.create( + model="tts-1", + voice="alloy", + input="the quick brown fox jumped over the lazy dogs", + stream=True, + ) + response.stream_to_file(speech_file_path) """ pass @@ -257,6 +275,7 @@ async def aclose(self) -> None: class RequestOptions(TypedDict, total=False): headers: Headers max_retries: int + stream: bool timeout: float | Timeout | None params: Query extra_json: AnyMapping diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 458843866f..66916d0d50 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -41,6 +41,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -67,6 +68,9 @@ def create( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds + + stream: Whether or not the response content should be streamed (i.e. not read to + completion immediately), default False """ return self._post( "/audio/speech", @@ -81,7 +85,11 @@ def create( speech_create_params.SpeechCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + stream=stream, ), cast_to=HttpxBinaryResponseContent, ) @@ -108,6 +116,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -134,6 +143,9 @@ async def create( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds + + stream: Whether or not the response content should be streamed (i.e. not read to + completion immediately), default False """ return await self._post( "/audio/speech", @@ -148,7 +160,11 @@ async def create( speech_create_params.SpeechCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + stream=stream, ), cast_to=HttpxBinaryResponseContent, ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index a6f75e5a4c..be6eff1e08 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -212,6 +212,7 @@ def content( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -224,11 +225,18 @@ def content( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds + + stream: Whether or not the response content should be streamed (i.e. not read to + completion immediately), default False """ return self._get( f"/files/{file_id}/content", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + stream=stream, ), cast_to=HttpxBinaryResponseContent, ) @@ -475,6 +483,7 @@ async def content( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -487,11 +496,18 @@ async def content( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds + + stream: Whether or not the response content should be streamed (i.e. not read to + completion immediately), default False """ return await self._get( f"/files/{file_id}/content", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + stream=stream, ), cast_to=HttpxBinaryResponseContent, ) From 2d85e1e78fc866310767298354e91e2437151eb9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:34:15 -0500 Subject: [PATCH 216/914] chore: revert binary streaming change (#875) --- examples/audio.py | 5 +---- src/openai/_base_client.py | 8 ++------ src/openai/_models.py | 2 -- src/openai/_types.py | 23 ++--------------------- src/openai/resources/audio/speech.py | 20 ++------------------ src/openai/resources/files.py | 20 ++------------------ 6 files changed, 9 insertions(+), 69 deletions(-) diff --git a/examples/audio.py b/examples/audio.py index e86acbf828..a5f535dcd6 100755 --- a/examples/audio.py +++ b/examples/audio.py @@ -13,10 +13,7 @@ def main() -> None: # Create text-to-speech audio file response = openai.audio.speech.create( - model="tts-1", - voice="alloy", - input="the quick brown fox jumped over the lazy dogs", - stream=True, + model="tts-1", voice="alloy", input="the quick brown fox jumped over the lazy dogs" ) response.stream_to_file(speech_file_path) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 9a023ba961..a168301f75 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -863,7 +863,7 @@ def _request( self._prepare_request(request) try: - response = self._client.send(request, auth=self.custom_auth, stream=stream or options.stream or False) + response = self._client.send(request, auth=self.custom_auth, stream=stream) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) @@ -1304,7 +1304,7 @@ async def _request( await self._prepare_request(request) try: - response = await self._client.send(request, auth=self.custom_auth, stream=stream or options.stream or False) + response = await self._client.send(request, auth=self.custom_auth, stream=stream) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) @@ -1541,7 +1541,6 @@ def make_request_options( idempotency_key: str | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, post_parser: PostParser | NotGiven = NOT_GIVEN, - stream: bool | None = None, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} @@ -1563,9 +1562,6 @@ def make_request_options( if idempotency_key is not None: options["idempotency_key"] = idempotency_key - if stream is not None: - options["stream"] = stream - if is_given(post_parser): # internal options["post_parser"] = post_parser # type: ignore diff --git a/src/openai/_models.py b/src/openai/_models.py index a0e596149c..5b8c96010f 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -403,7 +403,6 @@ class FinalRequestOptionsInput(TypedDict, total=False): params: Query headers: Headers max_retries: int - stream: bool | None timeout: float | Timeout | None files: HttpxRequestFiles | None idempotency_key: str @@ -421,7 +420,6 @@ class FinalRequestOptions(pydantic.BaseModel): timeout: Union[float, Timeout, None, NotGiven] = NotGiven() files: Union[HttpxRequestFiles, None] = None idempotency_key: Union[str, None] = None - stream: Union[bool, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() # It should be noted that we cannot use `json` here as that would override diff --git a/src/openai/_types.py b/src/openai/_types.py index 013b658f0e..9e962a1078 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -130,16 +130,7 @@ def stream_to_file( chunk_size: int | None = None, ) -> None: """ - Stream the output to the given file. NOTE, requires passing `stream=True` - to the request for expected behavior, e.g., - - response = openai.audio.speech.create( - model="tts-1", - voice="alloy", - input="the quick brown fox jumped over the lazy dogs", - stream=True, - ) - response.stream_to_file(speech_file_path) + Stream the output to the given file. """ pass @@ -194,16 +185,7 @@ async def astream_to_file( chunk_size: int | None = None, ) -> None: """ - Stream the output to the given file. NOTE, requires passing `stream=True` - to the request for expected behavior, e.g., - - response = await openai.audio.speech.create( - model="tts-1", - voice="alloy", - input="the quick brown fox jumped over the lazy dogs", - stream=True, - ) - response.stream_to_file(speech_file_path) + Stream the output to the given file. """ pass @@ -275,7 +257,6 @@ async def aclose(self) -> None: class RequestOptions(TypedDict, total=False): headers: Headers max_retries: int - stream: bool timeout: float | Timeout | None params: Query extra_json: AnyMapping diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 66916d0d50..458843866f 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -41,7 +41,6 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -68,9 +67,6 @@ def create( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds - - stream: Whether or not the response content should be streamed (i.e. not read to - completion immediately), default False """ return self._post( "/audio/speech", @@ -85,11 +81,7 @@ def create( speech_create_params.SpeechCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - stream=stream, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=HttpxBinaryResponseContent, ) @@ -116,7 +108,6 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -143,9 +134,6 @@ async def create( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds - - stream: Whether or not the response content should be streamed (i.e. not read to - completion immediately), default False """ return await self._post( "/audio/speech", @@ -160,11 +148,7 @@ async def create( speech_create_params.SpeechCreateParams, ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - stream=stream, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=HttpxBinaryResponseContent, ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index be6eff1e08..a6f75e5a4c 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -212,7 +212,6 @@ def content( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -225,18 +224,11 @@ def content( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds - - stream: Whether or not the response content should be streamed (i.e. not read to - completion immediately), default False """ return self._get( f"/files/{file_id}/content", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - stream=stream, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=HttpxBinaryResponseContent, ) @@ -483,7 +475,6 @@ async def content( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -496,18 +487,11 @@ async def content( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds - - stream: Whether or not the response content should be streamed (i.e. not read to - completion immediately), default False """ return await self._get( f"/files/{file_id}/content", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - stream=stream, + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=HttpxBinaryResponseContent, ) From dad2b56d8da41a7e4ce22b33da6d85550dffbc13 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:42:36 -0500 Subject: [PATCH 217/914] chore(internal): send more detailed x-stainless headers (#877) --- pyproject.toml | 1 + src/openai/_client.py | 4 +++- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_utils.py | 9 +++++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f17def16b6..d2d85a5018 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "typing-extensions>=4.5, <5", "anyio>=3.5.0, <4", "distro>=1.7.0, <2", + "sniffio", "tqdm > 4" ] requires-python = ">= 3.7.1" diff --git a/src/openai/_client.py b/src/openai/_client.py index aa00073281..202162070b 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -20,7 +20,7 @@ ProxiesTypes, RequestOptions, ) -from ._utils import is_given, is_mapping +from ._utils import is_given, is_mapping, get_async_library from ._version import __version__ from ._streaming import Stream as Stream from ._streaming import AsyncStream as AsyncStream @@ -147,6 +147,7 @@ def auth_headers(self) -> dict[str, str]: def default_headers(self) -> dict[str, str | Omit]: return { **super().default_headers, + "X-Stainless-Async": "false", "OpenAI-Organization": self.organization if self.organization is not None else Omit(), **self._custom_headers, } @@ -356,6 +357,7 @@ def auth_headers(self) -> dict[str, str]: def default_headers(self) -> dict[str, str | Omit]: return { **super().default_headers, + "X-Stainless-Async": f"async:{get_async_library()}", "OpenAI-Organization": self.organization if self.organization is not None else Omit(), **self._custom_headers, } diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index d3397212de..400ca9b828 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -25,6 +25,7 @@ from ._utils import deepcopy_minimal as deepcopy_minimal from ._utils import extract_type_arg as extract_type_arg from ._utils import is_required_type as is_required_type +from ._utils import get_async_library as get_async_library from ._utils import is_annotated_type as is_annotated_type from ._utils import maybe_coerce_float as maybe_coerce_float from ._utils import get_required_header as get_required_header diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 4b51dcb2e8..d2bfc91a70 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -18,6 +18,8 @@ from pathlib import Path from typing_extensions import Required, Annotated, TypeGuard, get_args, get_origin +import sniffio + from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike from .._compat import is_union as _is_union from .._compat import parse_date as parse_date @@ -406,3 +408,10 @@ def get_required_header(headers: HeadersLike, header: str) -> str: 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" From 9c8789c284fce2372110ecd8f855fe4aa0a4f3a7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 28 Nov 2023 00:26:59 -0500 Subject: [PATCH 218/914] docs: update readme code snippet (#890) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33ec6add52..3861b7db2b 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,7 @@ completion = client.chat.completions.create( "content": "Can you generate an example json object describing a fruit?", } ], - model="gpt-3.5-turbo", + model="gpt-3.5-turbo-1106", response_format={"type": "json_object"}, ) ``` From 0990e31c94911b10295ebf47affbc370a4bd88dd Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:43:59 -0500 Subject: [PATCH 219/914] chore(deps): bump mypy to v1.7.1 (#891) --- pyproject.toml | 2 +- requirements-dev.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d2d85a5018..351c464313 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ openai = "openai.cli:main" managed = true dev-dependencies = [ "pyright==1.1.332", - "mypy==1.6.1", + "mypy==1.7.1", "black==23.3.0", "respx==0.19.2", "pytest==7.1.1", diff --git a/requirements-dev.lock b/requirements-dev.lock index 0747babdc5..683454d678 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -16,10 +16,10 @@ azure-identity==1.15.0 black==23.3.0 certifi==2023.7.22 cffi==1.16.0 -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 click==8.1.7 colorlog==6.7.0 -cryptography==41.0.5 +cryptography==41.0.7 dirty-equals==0.6.0 distlib==0.3.7 distro==1.8.0 @@ -31,15 +31,15 @@ httpx==0.23.0 idna==3.4 iniconfig==2.0.0 isort==5.10.1 -msal==1.24.1 +msal==1.25.0 msal-extensions==1.0.0 -mypy==1.6.1 +mypy==1.7.1 mypy-extensions==1.0.0 nodeenv==1.8.0 nox==2023.4.22 -numpy==1.26.1 +numpy==1.26.2 packaging==23.2 -pandas==2.1.1 +pandas==2.1.3 pandas-stubs==2.1.1.230928 pathspec==0.11.2 platformdirs==3.11.0 @@ -68,7 +68,7 @@ types-pytz==2023.3.1.1 types-tqdm==4.66.0.2 typing-extensions==4.8.0 tzdata==2023.3 -urllib3==2.0.7 +urllib3==2.1.0 virtualenv==20.24.5 # The following packages are considered to be unsafe in a requirements file: setuptools==68.2.2 From 738abb59190d049efe6ab7df72115409ee5c372c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:01:57 -0500 Subject: [PATCH 220/914] docs(readme): update examples (#893) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3861b7db2b..96865aba78 100644 --- a/README.md +++ b/README.md @@ -94,9 +94,9 @@ stream = client.chat.completions.create( messages=[{"role": "user", "content": "Say this is a test"}], stream=True, ) -for chunk in stream: +for part in stream: if chunk.choices[0].delta.content is not None: - print(chunk.choices[0].delta.content, end="") + print(part.choices[0].delta.content) ``` The async client uses the exact same interface. @@ -111,9 +111,9 @@ stream = await client.chat.completions.create( messages=[{"role": "user", "content": "Say this is a test"}], stream=True, ) -async for chunk in stream: +async for part in stream: if chunk.choices[0].delta.content is not None: - print(chunk.choices[0].delta.content, end="") + print(part.choices[0].delta.content) ``` ## Module-level client From 54c7c512b276ee0789e9bf6a0234d9cd34dd44e5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:18:32 -0500 Subject: [PATCH 221/914] docs(readme): minor updates (#894) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 96865aba78..83392e9585 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ stream = client.chat.completions.create( messages=[{"role": "user", "content": "Say this is a test"}], stream=True, ) -for part in stream: +for chunk in stream: if chunk.choices[0].delta.content is not None: print(part.choices[0].delta.content) ``` @@ -111,7 +111,7 @@ stream = await client.chat.completions.create( messages=[{"role": "user", "content": "Say this is a test"}], stream=True, ) -async for part in stream: +async for chunk in stream: if chunk.choices[0].delta.content is not None: print(part.choices[0].delta.content) ``` From 17ac6779958b2b74999c634c4ea4c7b74906027a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:19:13 -0500 Subject: [PATCH 222/914] release: 1.3.6 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 13787787c4..907051ec7d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.5" + ".": "1.3.6" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0869b3888c..a4c324e4f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 1.3.6 (2023-11-28) + +Full Changelog: [v1.3.5...v1.3.6](https://github.com/openai/openai-python/compare/v1.3.5...v1.3.6) + +### Bug Fixes + +* **client:** add support for streaming binary responses ([#866](https://github.com/openai/openai-python/issues/866)) ([2470d25](https://github.com/openai/openai-python/commit/2470d251b751e92e8950bc9e3026965e9925ac1c)) + + +### Chores + +* **deps:** bump mypy to v1.7.1 ([#891](https://github.com/openai/openai-python/issues/891)) ([11fcb2a](https://github.com/openai/openai-python/commit/11fcb2a3cd4205b307c13c65ad47d9e315b0084d)) +* **internal:** send more detailed x-stainless headers ([#877](https://github.com/openai/openai-python/issues/877)) ([69e0549](https://github.com/openai/openai-python/commit/69e054947d587ff2548b101ece690d21d3c38f74)) +* revert binary streaming change ([#875](https://github.com/openai/openai-python/issues/875)) ([0a06d6a](https://github.com/openai/openai-python/commit/0a06d6a078c5ee898dae75bab4988e1a1936bfbf)) + + +### Documentation + +* **readme:** minor updates ([#894](https://github.com/openai/openai-python/issues/894)) ([5458457](https://github.com/openai/openai-python/commit/54584572df4c2a086172d812c6acb84e3405328b)) +* **readme:** update examples ([#893](https://github.com/openai/openai-python/issues/893)) ([124da87](https://github.com/openai/openai-python/commit/124da8720c44d40c083d29179f46a265761c1f4f)) +* update readme code snippet ([#890](https://github.com/openai/openai-python/issues/890)) ([c522f21](https://github.com/openai/openai-python/commit/c522f21e2a685454185d57e462e74a28499460f9)) + ## 1.3.5 (2023-11-21) Full Changelog: [v1.3.4...v1.3.5](https://github.com/openai/openai-python/compare/v1.3.4...v1.3.5) diff --git a/pyproject.toml b/pyproject.toml index 351c464313..daa765a7c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.5" +version = "1.3.6" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1ef6479491..bf8fdd1b4f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.5" # x-release-please-version +__version__ = "1.3.6" # x-release-please-version From 84c85bdffde6bdfe8fd152ba5eec371e9260d0a8 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 06:38:46 -0500 Subject: [PATCH 223/914] fix(client): don't cause crashes when inspecting the module (#897) --- src/openai/_utils/_proxy.py | 26 ++++++++++++++++++++++---- src/openai/lib/_old_api.py | 12 +++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/openai/_utils/_proxy.py b/src/openai/_utils/_proxy.py index aa934a3fbc..3c9e790a25 100644 --- a/src/openai/_utils/_proxy.py +++ b/src/openai/_utils/_proxy.py @@ -18,25 +18,43 @@ class LazyProxy(Generic[T], ABC): def __init__(self) -> None: self.__proxied: T | None = None + # Note: we have to special case proxies that themselves return proxies + # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` + def __getattr__(self, attr: str) -> object: - return getattr(self.__get_proxied__(), attr) + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied # pyright: ignore + return getattr(proxied, attr) @override def __repr__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ return repr(self.__get_proxied__()) @override def __str__(self) -> str: - return str(self.__get_proxied__()) + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return str(proxied) @override def __dir__(self) -> Iterable[str]: - return self.__get_proxied__().__dir__() + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return [] + return proxied.__dir__() @property # type: ignore @override def __class__(self) -> type: - return self.__get_proxied__().__class__ + proxied = self.__get_proxied__() + if issubclass(type(proxied), LazyProxy): + return type(proxied) + return proxied.__class__ def __get_proxied__(self) -> T: if not self.should_cache: diff --git a/src/openai/lib/_old_api.py b/src/openai/lib/_old_api.py index c4038fcfaf..929c87e80b 100644 --- a/src/openai/lib/_old_api.py +++ b/src/openai/lib/_old_api.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from typing_extensions import override from .._utils import LazyProxy @@ -23,13 +23,19 @@ def __init__(self, *, symbol: str) -> None: super().__init__(INSTRUCTIONS.format(symbol=symbol)) -class APIRemovedInV1Proxy(LazyProxy[None]): +class APIRemovedInV1Proxy(LazyProxy[Any]): def __init__(self, *, symbol: str) -> None: super().__init__() self._symbol = symbol @override - def __load__(self) -> None: + def __load__(self) -> Any: + # return the proxy until it is eventually called so that + # we don't break people that are just checking the attributes + # of a module + return self + + def __call__(self, *_args: Any, **_kwargs: Any) -> Any: raise APIRemovedInV1(symbol=self._symbol) From 2e7e897a6632188c66618d9b53e61cbf9331b4b0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 07:25:50 -0500 Subject: [PATCH 224/914] chore(internal): add tests for proxy change (#899) --- tests/lib/test_old_api.py | 17 +++++++++++++++++ tests/test_utils/test_proxy.py | 23 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/lib/test_old_api.py create mode 100644 tests/test_utils/test_proxy.py diff --git a/tests/lib/test_old_api.py b/tests/lib/test_old_api.py new file mode 100644 index 0000000000..261b8acb94 --- /dev/null +++ b/tests/lib/test_old_api.py @@ -0,0 +1,17 @@ +import pytest + +import openai +from openai.lib._old_api import APIRemovedInV1 + + +def test_basic_attribute_access_works() -> None: + for attr in dir(openai): + dir(getattr(openai, attr)) + + +def test_helpful_error_is_raised() -> None: + with pytest.raises(APIRemovedInV1): + openai.Completion.create() # type: ignore + + with pytest.raises(APIRemovedInV1): + openai.ChatCompletion.create() # type: ignore diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py new file mode 100644 index 0000000000..57c059150d --- /dev/null +++ b/tests/test_utils/test_proxy.py @@ -0,0 +1,23 @@ +import operator +from typing import Any +from typing_extensions import override + +from openai._utils import LazyProxy + + +class RecursiveLazyProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + return self + + def __call__(self, *_args: Any, **_kwds: Any) -> Any: + raise RuntimeError("This should never be called!") + + +def test_recursive_proxy() -> None: + proxy = RecursiveLazyProxy() + assert repr(proxy) == "RecursiveLazyProxy" + assert str(proxy) == "RecursiveLazyProxy" + assert dir(proxy) == [] + assert getattr(type(proxy), "__name__") == "RecursiveLazyProxy" + assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" From 7aad3405c2d460259bfc35d93e8d062e7e11c3e3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:02:45 -0500 Subject: [PATCH 225/914] fix(client): ensure retried requests are closed (#902) --- src/openai/_base_client.py | 100 +++++++++++++---- src/openai/_constants.py | 1 + tests/test_client.py | 222 ++++++++++++++++++++++++++++++++++++- 3 files changed, 302 insertions(+), 21 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index a168301f75..89d9ce4815 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -72,6 +72,7 @@ DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, RAW_RESPONSE_HEADER, + STREAMED_RAW_RESPONSE_HEADER, ) from ._streaming import Stream, AsyncStream from ._exceptions import ( @@ -363,14 +364,21 @@ def _make_status_error_from_response( self, response: httpx.Response, ) -> APIStatusError: - err_text = response.text.strip() - body = err_text + if response.is_closed and not response.is_stream_consumed: + # We can't read the response body as it has been closed + # before it was read. This can happen if an event hook + # raises a status error. + body = None + err_msg = f"Error code: {response.status_code}" + else: + err_text = response.text.strip() + body = err_text - try: - body = json.loads(err_text) - err_msg = f"Error code: {response.status_code} - {body}" - except Exception: - err_msg = err_text or f"Error code: {response.status_code}" + try: + body = json.loads(err_text) + err_msg = f"Error code: {response.status_code} - {body}" + except Exception: + err_msg = err_text or f"Error code: {response.status_code}" return self._make_status_error(err_msg, body=body, response=response) @@ -534,6 +542,12 @@ def _process_response_data( except pydantic.ValidationError as err: raise APIResponseValidationError(response=response, body=data) from err + def _should_stream_response_body(self, *, request: httpx.Request) -> bool: + if request.headers.get(STREAMED_RAW_RESPONSE_HEADER) == "true": + return True + + return False + @property def qs(self) -> Querystring: return Querystring() @@ -606,7 +620,7 @@ def _calculate_retry_timeout( if response_headers is not None: retry_header = response_headers.get("retry-after") try: - retry_after = int(retry_header) + retry_after = float(retry_header) except Exception: retry_date_tuple = email.utils.parsedate_tz(retry_header) if retry_date_tuple is None: @@ -862,14 +876,21 @@ def _request( request = self._build_request(options) self._prepare_request(request) + response = None + try: - response = self._client.send(request, auth=self.custom_auth, stream=stream) + response = self._client.send( + request, + auth=self.custom_auth, + stream=stream or self._should_stream_response_body(request=request), + ) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) response.raise_for_status() except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code if retries > 0 and self._should_retry(err.response): + err.response.close() return self._retry_request( options, cast_to, @@ -881,9 +902,14 @@ def _request( # If the response is streamed then we need to explicitly read the response # to completion before attempting to access the response text. - err.response.read() + if not err.response.is_closed: + err.response.read() + raise self._make_status_error_from_response(err.response) from None except httpx.TimeoutException as err: + if response is not None: + response.close() + if retries > 0: return self._retry_request( options, @@ -891,9 +917,14 @@ def _request( retries, stream=stream, stream_cls=stream_cls, + response_headers=response.headers if response is not None else None, ) + raise APITimeoutError(request=request) from err except Exception as err: + if response is not None: + response.close() + if retries > 0: return self._retry_request( options, @@ -901,7 +932,9 @@ def _request( retries, stream=stream, stream_cls=stream_cls, + response_headers=response.headers if response is not None else None, ) + raise APIConnectionError(request=request) from err return self._process_response( @@ -917,7 +950,7 @@ def _retry_request( options: FinalRequestOptions, cast_to: Type[ResponseT], remaining_retries: int, - response_headers: Optional[httpx.Headers] = None, + response_headers: httpx.Headers | None, *, stream: bool, stream_cls: type[_StreamT] | None, @@ -1303,14 +1336,21 @@ async def _request( request = self._build_request(options) await self._prepare_request(request) + response = None + try: - response = await self._client.send(request, auth=self.custom_auth, stream=stream) + response = await self._client.send( + request, + auth=self.custom_auth, + stream=stream or self._should_stream_response_body(request=request), + ) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) response.raise_for_status() except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code if retries > 0 and self._should_retry(err.response): + await err.response.aclose() return await self._retry_request( options, cast_to, @@ -1322,19 +1362,39 @@ async def _request( # If the response is streamed then we need to explicitly read the response # to completion before attempting to access the response text. - await err.response.aread() + if not err.response.is_closed: + await err.response.aread() + raise self._make_status_error_from_response(err.response) from None - except httpx.ConnectTimeout as err: - if retries > 0: - return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) - raise APITimeoutError(request=request) from err except httpx.TimeoutException as err: + if response is not None: + await response.aclose() + if retries > 0: - return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) + return await self._retry_request( + options, + cast_to, + retries, + stream=stream, + stream_cls=stream_cls, + response_headers=response.headers if response is not None else None, + ) + raise APITimeoutError(request=request) from err except Exception as err: + if response is not None: + await response.aclose() + if retries > 0: - return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls) + return await self._retry_request( + options, + cast_to, + retries, + stream=stream, + stream_cls=stream_cls, + response_headers=response.headers if response is not None else None, + ) + raise APIConnectionError(request=request) from err return self._process_response( @@ -1350,7 +1410,7 @@ async def _retry_request( options: FinalRequestOptions, cast_to: Type[ResponseT], remaining_retries: int, - response_headers: Optional[httpx.Headers] = None, + response_headers: httpx.Headers | None, *, stream: bool, stream_cls: type[_AsyncStreamT] | None, diff --git a/src/openai/_constants.py b/src/openai/_constants.py index 2e402300d3..7c13feaa25 100644 --- a/src/openai/_constants.py +++ b/src/openai/_constants.py @@ -3,6 +3,7 @@ import httpx RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" +STREAMED_RAW_RESPONSE_HEADER = "X-Stainless-Streamed-Raw-Response" # default timeout is 10 minutes DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0) diff --git a/tests/test_client.py b/tests/test_client.py index c5dbfe4bfe..51aa90a480 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -18,7 +18,12 @@ from openai._client import OpenAI, AsyncOpenAI from openai._models import BaseModel, FinalRequestOptions from openai._streaming import Stream, AsyncStream -from openai._exceptions import APIResponseValidationError +from openai._exceptions import ( + APIStatusError, + APITimeoutError, + APIConnectionError, + APIResponseValidationError, +) from openai._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, @@ -38,6 +43,24 @@ def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: return dict(url.params) +_original_response_init = cast(Any, httpx.Response.__init__) # type: ignore + + +def _low_retry_response_init(*args: Any, **kwargs: Any) -> Any: + headers = cast("list[tuple[bytes, bytes]]", kwargs["headers"]) + headers.append((b"retry-after", b"0.1")) + + return _original_response_init(*args, **kwargs) + + +def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int: + transport = client._client._transport + assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) + + pool = transport._pool + return len(pool._requests) + + class TestOpenAI: client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -592,6 +615,104 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + @mock.patch("httpx.Response.__init__", _low_retry_response_init) + def test_retrying_timeout_errors_doesnt_leak(self) -> None: + def raise_for_status(response: httpx.Response) -> None: + raise httpx.TimeoutException("Test timeout error", request=response.request) + + with mock.patch("httpx.Response.raise_for_status", raise_for_status): + with pytest.raises(APITimeoutError): + self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @mock.patch("httpx.Response.__init__", _low_retry_response_init) + def test_retrying_runtime_errors_doesnt_leak(self) -> None: + def raise_for_status(_response: httpx.Response) -> None: + raise RuntimeError("Test error") + + with mock.patch("httpx.Response.raise_for_status", raise_for_status): + with pytest.raises(APIConnectionError): + self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @mock.patch("httpx.Response.__init__", _low_retry_response_init) + def test_retrying_status_errors_doesnt_leak(self) -> None: + def raise_for_status(response: httpx.Response) -> None: + response.status_code = 500 + raise httpx.HTTPStatusError("Test 500 error", response=response, request=response.request) + + with mock.patch("httpx.Response.raise_for_status", raise_for_status): + with pytest.raises(APIStatusError): + self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @pytest.mark.respx(base_url=base_url) + def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + def on_response(response: httpx.Response) -> None: + raise httpx.HTTPStatusError( + "Simulating an error inside httpx", + response=response, + request=response.request, + ) + + client = OpenAI( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client( + event_hooks={ + "response": [on_response], + } + ), + max_retries=0, + ) + with pytest.raises(APIStatusError): + client.post("/foo", cast_to=httpx.Response) + class TestAsyncOpenAI: client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -1162,3 +1283,102 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + @mock.patch("httpx.Response.__init__", _low_retry_response_init) + async def test_retrying_timeout_errors_doesnt_leak(self) -> None: + def raise_for_status(response: httpx.Response) -> None: + raise httpx.TimeoutException("Test timeout error", request=response.request) + + with mock.patch("httpx.Response.raise_for_status", raise_for_status): + with pytest.raises(APITimeoutError): + await self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @mock.patch("httpx.Response.__init__", _low_retry_response_init) + async def test_retrying_runtime_errors_doesnt_leak(self) -> None: + def raise_for_status(_response: httpx.Response) -> None: + raise RuntimeError("Test error") + + with mock.patch("httpx.Response.raise_for_status", raise_for_status): + with pytest.raises(APIConnectionError): + await self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @mock.patch("httpx.Response.__init__", _low_retry_response_init) + async def test_retrying_status_errors_doesnt_leak(self) -> None: + def raise_for_status(response: httpx.Response) -> None: + response.status_code = 500 + raise httpx.HTTPStatusError("Test 500 error", response=response, request=response.request) + + with mock.patch("httpx.Response.raise_for_status", raise_for_status): + with pytest.raises(APIStatusError): + await self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + def on_response(response: httpx.Response) -> None: + raise httpx.HTTPStatusError( + "Simulating an error inside httpx", + response=response, + request=response.request, + ) + + client = AsyncOpenAI( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient( + event_hooks={ + "response": [on_response], + } + ), + max_retries=0, + ) + with pytest.raises(APIStatusError): + await client.post("/foo", cast_to=httpx.Response) From bcfbab7078d7e7c1ac1fcea05e32aba578e46bbb Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:29:58 -0500 Subject: [PATCH 226/914] docs: fix typo in readme (#904) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 83392e9585..380ccc58d9 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ stream = client.chat.completions.create( ) for chunk in stream: if chunk.choices[0].delta.content is not None: - print(part.choices[0].delta.content) + print(chunk.choices[0].delta.content) ``` The async client uses the exact same interface. @@ -113,7 +113,7 @@ stream = await client.chat.completions.create( ) async for chunk in stream: if chunk.choices[0].delta.content is not None: - print(part.choices[0].delta.content) + print(chunk.choices[0].delta.content) ``` ## Module-level client From fb5ba01c4cf3ac7c96bf08818f3e943fb3696111 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 30 Nov 2023 08:20:46 -0500 Subject: [PATCH 227/914] docs(readme): update example snippets (#907) --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 380ccc58d9..4cabdb897d 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,12 @@ pip install openai The full API of this library can be found in [api.md](https://www.github.com/openai/openai-python/blob/main/api.md). ```python +import os from openai import OpenAI client = OpenAI( - # defaults to os.environ.get("OPENAI_API_KEY") - api_key="My API Key", + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), ) chat_completion = client.chat.completions.create( @@ -54,12 +55,13 @@ so that your API Key is not stored in source control. Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call: ```python +import os import asyncio from openai import AsyncOpenAI client = AsyncOpenAI( - # defaults to os.environ.get("OPENAI_API_KEY") - api_key="My API Key", + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), ) From 6817fcd8a25c3b5c722bac8ee134c67e24a1c8ed Mon Sep 17 00:00:00 2001 From: Evgenii Date: Thu, 30 Nov 2023 20:46:48 +0300 Subject: [PATCH 228/914] chore(internal): replace string concatenation with f-strings (#908) --- src/openai/_utils/_utils.py | 2 +- src/openai/lib/_validators.py | 6 +++--- tests/test_required_args.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index d2bfc91a70..83f88cc3e7 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -230,7 +230,7 @@ def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> s def quote(string: str) -> str: """Add single quotation marks around the given string. Does *not* do any escaping.""" - return "'" + string + "'" + return f"'{string}'" def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: diff --git a/src/openai/lib/_validators.py b/src/openai/lib/_validators.py index c8608c0cef..ae48aafa88 100644 --- a/src/openai/lib/_validators.py +++ b/src/openai/lib/_validators.py @@ -309,10 +309,10 @@ def common_completion_prefix_validator(df: pd.DataFrame) -> Remediation: return Remediation(name="common_prefix") def remove_common_prefix(x: Any, prefix: Any, ws_prefix: Any) -> Any: - x["completion"] = x["completion"].str[len(prefix) :] + x["completion"] = x["completion"].str[len(prefix):] if ws_prefix: # keep the single whitespace as prefix - x["completion"] = " " + x["completion"] + x["completion"] = f" {x['completion']}" return x if (df.completion == common_prefix).all(): @@ -624,7 +624,7 @@ def get_outfnames(fname: str, split: bool) -> list[str]: while True: index_suffix = f" ({i})" if i > 0 else "" candidate_fnames = [ - os.path.splitext(fname)[0] + "_prepared" + suffix + index_suffix + ".jsonl" for suffix in suffixes + f"{os.path.splitext(fname)[0]}_prepared{suffix}{index_suffix}.jsonl" for suffix in suffixes ] if not any(os.path.isfile(f) for f in candidate_fnames): return candidate_fnames diff --git a/tests/test_required_args.py b/tests/test_required_args.py index 1de017db24..5d1a5224ff 100644 --- a/tests/test_required_args.py +++ b/tests/test_required_args.py @@ -43,7 +43,7 @@ def foo(*, a: str | None = None) -> str | None: def test_multiple_params() -> None: @required_args(["a", "b", "c"]) def foo(a: str = "", *, b: str = "", c: str = "") -> str | None: - return a + " " + b + " " + c + return f"{a} {b} {c}" assert foo(a="a", b="b", c="c") == "a b c" From 104e5a54609c6fd003701a0f6f52e56cdeb806c3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:52:50 -0500 Subject: [PATCH 229/914] chore(internal): replace string concatenation with f-strings (#909) --- src/openai/lib/_validators.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/openai/lib/_validators.py b/src/openai/lib/_validators.py index ae48aafa88..e36f0e95fb 100644 --- a/src/openai/lib/_validators.py +++ b/src/openai/lib/_validators.py @@ -309,7 +309,7 @@ def common_completion_prefix_validator(df: pd.DataFrame) -> Remediation: return Remediation(name="common_prefix") def remove_common_prefix(x: Any, prefix: Any, ws_prefix: Any) -> Any: - x["completion"] = x["completion"].str[len(prefix):] + x["completion"] = x["completion"].str[len(prefix) :] if ws_prefix: # keep the single whitespace as prefix x["completion"] = f" {x['completion']}" @@ -623,9 +623,7 @@ def get_outfnames(fname: str, split: bool) -> list[str]: i = 0 while True: index_suffix = f" ({i})" if i > 0 else "" - candidate_fnames = [ - f"{os.path.splitext(fname)[0]}_prepared{suffix}{index_suffix}.jsonl" for suffix in suffixes - ] + candidate_fnames = [f"{os.path.splitext(fname)[0]}_prepared{suffix}{index_suffix}.jsonl" for suffix in suffixes] if not any(os.path.isfile(f) for f in candidate_fnames): return candidate_fnames i += 1 From 4d9ece0e6a7f6ea5e321bf49258e02955c6484be Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:10:40 -0500 Subject: [PATCH 230/914] chore(internal): remove unused type var (#915) --- src/openai/pagination.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/openai/pagination.py b/src/openai/pagination.py index 4ec300f2d1..17f2d1a4ca 100644 --- a/src/openai/pagination.py +++ b/src/openai/pagination.py @@ -1,16 +1,13 @@ # File generated from our OpenAPI spec by Stainless. -from typing import Any, List, Generic, TypeVar, Optional, cast +from typing import Any, List, Generic, Optional, cast from typing_extensions import Literal, Protocol, override, runtime_checkable from ._types import ModelT -from ._models import BaseModel from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage __all__ = ["SyncPage", "AsyncPage", "SyncCursorPage", "AsyncCursorPage"] -_BaseModelT = TypeVar("_BaseModelT", bound=BaseModel) - @runtime_checkable class CursorPageItem(Protocol): From f6f38a9bc42b371c2f9f889471f319bf82208970 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:55:01 -0500 Subject: [PATCH 231/914] fix(client): correct base_url setter implementation (#919) Co-Authored-By: tomoish --- src/openai/_base_client.py | 2 +- tests/test_client.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 89d9ce4815..2e5678e8e6 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -592,7 +592,7 @@ def base_url(http://github.com/self) -> URL: @base_url.setter def base_url(http://github.com/self,%20url:%20URL%20|%20str) -> None: - self._client.base_url = url if isinstance(url, URL) else URL(url) + self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) @lru_cache(maxsize=None) def platform_headers(self) -> Dict[str, str]: diff --git a/tests/test_client.py b/tests/test_client.py index 51aa90a480..1f1ec6fc98 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -445,6 +445,14 @@ class Model(BaseModel): assert isinstance(response, Model) assert response.foo == 2 + def test_base_url_setter(self) -> None: + client = OpenAI(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + def test_base_url_env(self) -> None: with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): client = OpenAI(api_key=api_key, _strict_response_validation=True) @@ -1102,6 +1110,16 @@ class Model(BaseModel): assert isinstance(response, Model) assert response.foo == 2 + def test_base_url_setter(self) -> None: + client = AsyncOpenAI( + base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True + ) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + def test_base_url_env(self) -> None: with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): client = AsyncOpenAI(api_key=api_key, _strict_response_validation=True) From e36956673d9049713c91bca6ce7aebe58638f483 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:55:42 -0500 Subject: [PATCH 232/914] release: 1.3.7 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 24 ++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 907051ec7d..2fd8c9c83a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.6" + ".": "1.3.7" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a4c324e4f9..88ff899ec3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 1.3.7 (2023-12-01) + +Full Changelog: [v1.3.6...v1.3.7](https://github.com/openai/openai-python/compare/v1.3.6...v1.3.7) + +### Bug Fixes + +* **client:** correct base_url setter implementation ([#919](https://github.com/openai/openai-python/issues/919)) ([135d9cf](https://github.com/openai/openai-python/commit/135d9cf2820f1524764bf536a9322830bdcd5875)) +* **client:** don't cause crashes when inspecting the module ([#897](https://github.com/openai/openai-python/issues/897)) ([db029a5](https://github.com/openai/openai-python/commit/db029a596c90b1af4ef0bfb1cdf31f54b2f5755d)) +* **client:** ensure retried requests are closed ([#902](https://github.com/openai/openai-python/issues/902)) ([e025e6b](https://github.com/openai/openai-python/commit/e025e6bee44ea145d948869ef0c79bac0c376b9f)) + + +### Chores + +* **internal:** add tests for proxy change ([#899](https://github.com/openai/openai-python/issues/899)) ([71a13d0](https://github.com/openai/openai-python/commit/71a13d0c70d105b2b97720c72a1003b942cda2ae)) +* **internal:** remove unused type var ([#915](https://github.com/openai/openai-python/issues/915)) ([4233bcd](https://github.com/openai/openai-python/commit/4233bcdae5f467f10454fcc008a6e728fa846830)) +* **internal:** replace string concatenation with f-strings ([#908](https://github.com/openai/openai-python/issues/908)) ([663a8f6](https://github.com/openai/openai-python/commit/663a8f6dead5aa523d1e8779e75af1dabb1690c4)) +* **internal:** replace string concatenation with f-strings ([#909](https://github.com/openai/openai-python/issues/909)) ([caab767](https://github.com/openai/openai-python/commit/caab767156375114078cf8d85031863361326b5f)) + + +### Documentation + +* fix typo in readme ([#904](https://github.com/openai/openai-python/issues/904)) ([472cd44](https://github.com/openai/openai-python/commit/472cd44e45a45b0b4f12583a5402e8aeb121d7a2)) +* **readme:** update example snippets ([#907](https://github.com/openai/openai-python/issues/907)) ([bbb648e](https://github.com/openai/openai-python/commit/bbb648ef81eb11f81b457e2cbf33a832f4d29a76)) + ## 1.3.6 (2023-11-28) Full Changelog: [v1.3.5...v1.3.6](https://github.com/openai/openai-python/compare/v1.3.5...v1.3.6) diff --git a/pyproject.toml b/pyproject.toml index daa765a7c2..81ef1ca317 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.6" +version = "1.3.7" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index bf8fdd1b4f..3103f3b767 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.6" # x-release-please-version +__version__ = "1.3.7" # x-release-please-version From 31573844c7aa0ba27c1c4280caab9f092622f526 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 4 Dec 2023 05:27:44 -0500 Subject: [PATCH 233/914] chore(package): lift anyio v4 restriction (#927) --- pyproject.toml | 4 ++-- requirements-dev.lock | 11 +++++------ requirements.lock | 13 ++++++------- tests/test_client.py | 14 +++++++------- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 81ef1ca317..c468220495 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", "typing-extensions>=4.5, <5", - "anyio>=3.5.0, <4", + "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", "tqdm > 4" @@ -51,7 +51,7 @@ dev-dependencies = [ "pyright==1.1.332", "mypy==1.7.1", "black==23.3.0", - "respx==0.19.2", + "respx==0.20.2", "pytest==7.1.1", "pytest-asyncio==0.21.1", "ruff==0.0.282", diff --git a/requirements-dev.lock b/requirements-dev.lock index 683454d678..0ed1974794 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -8,7 +8,7 @@ -e file:. annotated-types==0.6.0 -anyio==3.7.1 +anyio==4.1.0 argcomplete==3.1.2 attrs==23.1.0 azure-core==1.29.5 @@ -25,9 +25,9 @@ distlib==0.3.7 distro==1.8.0 exceptiongroup==1.1.3 filelock==3.12.4 -h11==0.12.0 -httpcore==0.15.0 -httpx==0.23.0 +h11==0.14.0 +httpcore==1.0.2 +httpx==0.25.2 idna==3.4 iniconfig==2.0.0 isort==5.10.1 @@ -56,8 +56,7 @@ pytest-asyncio==0.21.1 python-dateutil==2.8.2 pytz==2023.3.post1 requests==2.31.0 -respx==0.19.2 -rfc3986==1.5.0 +respx==0.20.2 ruff==0.0.282 six==1.16.0 sniffio==1.3.0 diff --git a/requirements.lock b/requirements.lock index be9606fc3c..c178f26a88 100644 --- a/requirements.lock +++ b/requirements.lock @@ -8,22 +8,21 @@ -e file:. annotated-types==0.6.0 -anyio==3.7.1 +anyio==4.1.0 certifi==2023.7.22 distro==1.8.0 exceptiongroup==1.1.3 -h11==0.12.0 -httpcore==0.15.0 -httpx==0.23.0 +h11==0.14.0 +httpcore==1.0.2 +httpx==0.25.2 idna==3.4 -numpy==1.26.1 -pandas==2.1.1 +numpy==1.26.2 +pandas==2.1.3 pandas-stubs==2.1.1.230928 pydantic==2.4.2 pydantic-core==2.10.1 python-dateutil==2.8.2 pytz==2023.3.post1 -rfc3986==1.5.0 six==1.16.0 sniffio==1.3.0 tqdm==4.66.1 diff --git a/tests/test_client.py b/tests/test_client.py index 1f1ec6fc98..f8653507ef 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,7 +6,7 @@ import json import asyncio import inspect -from typing import Any, Dict, Union, cast +from typing import Any, Union, cast from unittest import mock import httpx @@ -357,7 +357,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged @@ -371,7 +371,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash @@ -385,7 +385,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"foo": "2"} @pytest.mark.respx(base_url=base_url) @@ -1022,7 +1022,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged @@ -1036,7 +1036,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash @@ -1050,7 +1050,7 @@ def test_request_extra_query(self) -> None: ), ), ) - params = cast(Dict[str, str], dict(request.url.params)) + params = dict(request.url.params) assert params == {"foo": "2"} @pytest.mark.respx(base_url=base_url) From f61de9900efe1c9d92b3331a109dd43f14349e2c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 4 Dec 2023 05:45:51 -0500 Subject: [PATCH 234/914] ci: ensure PR titles use conventional commits (#929) --- .github/workflows/lint-pr.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/lint-pr.yml diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml new file mode 100644 index 0000000000..4074fb2ca1 --- /dev/null +++ b/.github/workflows/lint-pr.yml @@ -0,0 +1,21 @@ +name: "Lint PR" + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +permissions: + pull-requests: read + +jobs: + pr_title: + name: Validate PR title + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3d63cf25716a674a965910ef7a1ab902774c5b79 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 6 Dec 2023 01:04:32 +0000 Subject: [PATCH 235/914] ci: remove PR title linter (#934) --- .github/workflows/lint-pr.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/lint-pr.yml diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml deleted file mode 100644 index 4074fb2ca1..0000000000 --- a/.github/workflows/lint-pr.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: "Lint PR" - -on: - pull_request_target: - types: - - opened - - edited - - synchronize - -permissions: - pull-requests: read - -jobs: - pr_title: - name: Validate PR title - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' - steps: - - uses: amannn/action-semantic-pull-request@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3c6ecceeca7fc6858b85b81e71623ef695f856e3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:12:38 +0000 Subject: [PATCH 236/914] chore(internal): reformat imports (#939) --- pyproject.toml | 37 ++++----- requirements-dev.lock | 5 +- src/openai/__init__.py | 29 +++---- src/openai/_client.py | 3 +- src/openai/_compat.py | 30 ++++---- src/openai/_extras/__init__.py | 3 +- src/openai/_models.py | 14 +--- src/openai/_types.py | 9 +-- src/openai/_utils/__init__.py | 76 ++++++++++--------- src/openai/_utils/_utils.py | 4 +- src/openai/resources/__init__.py | 49 ++---------- src/openai/resources/audio/__init__.py | 14 +--- src/openai/resources/audio/audio.py | 14 +--- src/openai/resources/audio/speech.py | 13 +++- src/openai/resources/audio/transcriptions.py | 13 +++- src/openai/resources/audio/translations.py | 13 +++- src/openai/resources/beta/__init__.py | 14 +--- .../resources/beta/assistants/__init__.py | 7 +- .../resources/beta/assistants/assistants.py | 13 +++- src/openai/resources/beta/assistants/files.py | 13 +++- src/openai/resources/beta/beta.py | 14 +--- src/openai/resources/beta/threads/__init__.py | 14 +--- .../beta/threads/messages/__init__.py | 7 +- .../resources/beta/threads/messages/files.py | 13 +++- .../beta/threads/messages/messages.py | 13 +++- .../resources/beta/threads/runs/runs.py | 13 +++- .../resources/beta/threads/runs/steps.py | 13 +++- src/openai/resources/beta/threads/threads.py | 17 +++-- src/openai/resources/chat/__init__.py | 7 +- src/openai/resources/chat/chat.py | 7 +- src/openai/resources/chat/completions.py | 12 ++- src/openai/resources/completions.py | 12 ++- src/openai/resources/edits.py | 12 ++- src/openai/resources/embeddings.py | 15 +++- src/openai/resources/files.py | 16 +++- src/openai/resources/fine_tunes.py | 13 +++- src/openai/resources/fine_tuning/__init__.py | 7 +- src/openai/resources/fine_tuning/jobs.py | 13 +++- src/openai/resources/images.py | 13 +++- src/openai/resources/models.py | 13 +++- src/openai/resources/moderations.py | 12 ++- src/openai/types/__init__.py | 23 ++---- src/openai/types/audio/__init__.py | 8 +- src/openai/types/beta/__init__.py | 4 +- src/openai/types/beta/threads/__init__.py | 12 +-- .../types/beta/threads/runs/__init__.py | 4 +- src/openai/types/chat/__init__.py | 28 ++----- .../chat_completion_content_part_param.py | 4 +- .../types/chat/completion_create_params.py | 8 +- tests/api_resources/beta/test_assistants.py | 5 +- tests/api_resources/beta/test_threads.py | 5 +- tests/api_resources/beta/threads/test_runs.py | 4 +- tests/api_resources/fine_tuning/test_jobs.py | 5 +- tests/utils.py | 7 +- 54 files changed, 384 insertions(+), 362 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c468220495..44e7bcb0ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,16 +48,16 @@ openai = "openai.cli:main" [tool.rye] managed = true dev-dependencies = [ - "pyright==1.1.332", - "mypy==1.7.1", - "black==23.3.0", - "respx==0.20.2", - "pytest==7.1.1", - "pytest-asyncio==0.21.1", - "ruff==0.0.282", - "isort==5.10.1", - "time-machine==2.9.0", - "nox==2023.4.22", + # version pins are in requirements-dev.lock + "pyright", + "mypy", + "black", + "respx", + "pytest", + "pytest-asyncio", + "ruff", + "time-machine", + "nox", "dirty-equals>=0.6.0", "azure-identity >=1.14.1", "types-tqdm > 4" @@ -68,12 +68,10 @@ format = { chain = [ "format:black", "format:docs", "format:ruff", - "format:isort", ]} "format:black" = "black ." "format:docs" = "python bin/blacken-docs.py README.md api.md" "format:ruff" = "ruff --fix ." -"format:isort" = "isort ." "check:ruff" = "ruff ." @@ -128,16 +126,13 @@ reportImplicitOverride = true reportImportCycles = false reportPrivateUsage = false -[tool.isort] -profile = "black" -length_sort = true -extra_standard_library = ["typing_extensions"] - [tool.ruff] line-length = 120 -format = "grouped" +output-format = "grouped" target-version = "py37" select = [ + # isort + "I", # remove unused imports "F401", # bare except statements @@ -155,6 +150,12 @@ unfixable = [ ] ignore-init-module-imports = true +[tool.ruff.lint.isort] +length-sort = true +length-sort-straight = true +combine-as-imports = true +extra-standard-library = ["typing_extensions"] +known-first-party = ["openai", "tests"] [tool.ruff.per-file-ignores] "bin/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 0ed1974794..bc993b16de 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -30,8 +30,7 @@ httpcore==1.0.2 httpx==0.25.2 idna==3.4 iniconfig==2.0.0 -isort==5.10.1 -msal==1.25.0 +msal==1.26.0 msal-extensions==1.0.0 mypy==1.7.1 mypy-extensions==1.0.0 @@ -57,7 +56,7 @@ python-dateutil==2.8.2 pytz==2023.3.post1 requests==2.31.0 respx==0.20.2 -ruff==0.0.282 +ruff==0.1.7 six==1.16.0 sniffio==1.3.0 time-machine==2.9.0 diff --git a/src/openai/__init__.py b/src/openai/__init__.py index d92dfe969a..20ab72ee25 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -72,8 +72,7 @@ from .lib import azure as _azure from .version import VERSION as VERSION -from .lib.azure import AzureOpenAI as AzureOpenAI -from .lib.azure import AsyncAzureOpenAI as AsyncAzureOpenAI +from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI from .lib._old_api import * _setup_logging() @@ -330,15 +329,17 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] _client = None -from ._module_client import beta as beta -from ._module_client import chat as chat -from ._module_client import audio as audio -from ._module_client import edits as edits -from ._module_client import files as files -from ._module_client import images as images -from ._module_client import models as models -from ._module_client import embeddings as embeddings -from ._module_client import fine_tunes as fine_tunes -from ._module_client import completions as completions -from ._module_client import fine_tuning as fine_tuning -from ._module_client import moderations as moderations +from ._module_client import ( + beta as beta, + chat as chat, + audio as audio, + edits as edits, + files as files, + images as images, + models as models, + embeddings as embeddings, + fine_tunes as fine_tunes, + completions as completions, + fine_tuning as fine_tuning, + moderations as moderations, +) diff --git a/src/openai/_client.py b/src/openai/_client.py index 202162070b..79054aba2f 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -22,8 +22,7 @@ ) from ._utils import is_given, is_mapping, get_async_library from ._version import __version__ -from ._streaming import Stream as Stream -from ._streaming import AsyncStream as AsyncStream +from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError from ._base_client import DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 34323c9b7e..d95db8ed1e 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -43,21 +43,23 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 else: if PYDANTIC_V2: - from pydantic.v1.typing import get_args as get_args - from pydantic.v1.typing import is_union as is_union - from pydantic.v1.typing import get_origin as get_origin - from pydantic.v1.typing import is_typeddict as is_typeddict - from pydantic.v1.typing import is_literal_type as is_literal_type - from pydantic.v1.datetime_parse import parse_date as parse_date - from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime else: - from pydantic.typing import get_args as get_args - from pydantic.typing import is_union as is_union - from pydantic.typing import get_origin as get_origin - from pydantic.typing import is_typeddict as is_typeddict - from pydantic.typing import is_literal_type as is_literal_type - from pydantic.datetime_parse import parse_date as parse_date - from pydantic.datetime_parse import parse_datetime as parse_datetime + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime # refactored config diff --git a/src/openai/_extras/__init__.py b/src/openai/_extras/__init__.py index dc6625c5dc..864dac4171 100644 --- a/src/openai/_extras/__init__.py +++ b/src/openai/_extras/__init__.py @@ -1,3 +1,2 @@ -from .numpy_proxy import numpy as numpy -from .numpy_proxy import has_numpy as has_numpy +from .numpy_proxy import numpy as numpy, has_numpy as has_numpy from .pandas_proxy import pandas as pandas diff --git a/src/openai/_models.py b/src/openai/_models.py index 5b8c96010f..cdd44ccb0a 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -30,17 +30,11 @@ AnyMapping, HttpxRequestFiles, ) -from ._utils import ( - is_list, - is_given, - is_mapping, - parse_date, - parse_datetime, - strip_not_given, -) -from ._compat import PYDANTIC_V2, ConfigDict -from ._compat import GenericModel as BaseGenericModel +from ._utils import is_list, is_given, is_mapping, parse_date, parse_datetime, strip_not_given from ._compat import ( + PYDANTIC_V2, + ConfigDict, + GenericModel as BaseGenericModel, get_args, is_union, parse_obj, diff --git a/src/openai/_types.py b/src/openai/_types.py index 9e962a1078..6f298c18c4 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -19,14 +19,7 @@ Sequence, AsyncIterator, ) -from typing_extensions import ( - Literal, - Protocol, - TypeAlias, - TypedDict, - override, - runtime_checkable, -) +from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable import pydantic from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 400ca9b828..e98636c92f 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -1,37 +1,41 @@ from ._proxy import LazyProxy as LazyProxy -from ._utils import flatten as flatten -from ._utils import is_dict as is_dict -from ._utils import is_list as is_list -from ._utils import is_given as is_given -from ._utils import is_tuple as is_tuple -from ._utils import is_mapping as is_mapping -from ._utils import is_tuple_t as is_tuple_t -from ._utils import parse_date as parse_date -from ._utils import is_sequence as is_sequence -from ._utils import coerce_float as coerce_float -from ._utils import is_list_type as is_list_type -from ._utils import is_mapping_t as is_mapping_t -from ._utils import removeprefix as removeprefix -from ._utils import removesuffix as removesuffix -from ._utils import extract_files as extract_files -from ._utils import is_sequence_t as is_sequence_t -from ._utils import is_union_type as is_union_type -from ._utils import required_args as required_args -from ._utils import coerce_boolean as coerce_boolean -from ._utils import coerce_integer as coerce_integer -from ._utils import file_from_path as file_from_path -from ._utils import parse_datetime as parse_datetime -from ._utils import strip_not_given as strip_not_given -from ._utils import deepcopy_minimal as deepcopy_minimal -from ._utils import extract_type_arg as extract_type_arg -from ._utils import is_required_type as is_required_type -from ._utils import get_async_library as get_async_library -from ._utils import is_annotated_type as is_annotated_type -from ._utils import maybe_coerce_float as maybe_coerce_float -from ._utils import get_required_header as get_required_header -from ._utils import maybe_coerce_boolean as maybe_coerce_boolean -from ._utils import maybe_coerce_integer as maybe_coerce_integer -from ._utils import strip_annotated_type as strip_annotated_type -from ._transform import PropertyInfo as PropertyInfo -from ._transform import transform as transform -from ._transform import maybe_transform as maybe_transform +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + parse_date as parse_date, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_list_type as is_list_type, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + is_union_type as is_union_type, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + parse_datetime as parse_datetime, + strip_not_given as strip_not_given, + deepcopy_minimal as deepcopy_minimal, + extract_type_arg as extract_type_arg, + is_required_type as is_required_type, + get_async_library as get_async_library, + is_annotated_type as is_annotated_type, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, + strip_annotated_type as strip_annotated_type, +) +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + maybe_transform as maybe_transform, +) diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 83f88cc3e7..cce6923810 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -21,9 +21,7 @@ import sniffio from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import is_union as _is_union -from .._compat import parse_date as parse_date -from .._compat import parse_datetime as parse_datetime +from .._compat import is_union as _is_union, parse_date as parse_date, parse_datetime as parse_datetime _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index e0f4f08d5c..2cdbeb6ae1 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -5,48 +5,13 @@ from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse from .edits import Edits, AsyncEdits, EditsWithRawResponse, AsyncEditsWithRawResponse from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .images import ( - Images, - AsyncImages, - ImagesWithRawResponse, - AsyncImagesWithRawResponse, -) -from .models import ( - Models, - AsyncModels, - ModelsWithRawResponse, - AsyncModelsWithRawResponse, -) -from .embeddings import ( - Embeddings, - AsyncEmbeddings, - EmbeddingsWithRawResponse, - AsyncEmbeddingsWithRawResponse, -) -from .fine_tunes import ( - FineTunes, - AsyncFineTunes, - FineTunesWithRawResponse, - AsyncFineTunesWithRawResponse, -) -from .completions import ( - Completions, - AsyncCompletions, - CompletionsWithRawResponse, - AsyncCompletionsWithRawResponse, -) -from .fine_tuning import ( - FineTuning, - AsyncFineTuning, - FineTuningWithRawResponse, - AsyncFineTuningWithRawResponse, -) -from .moderations import ( - Moderations, - AsyncModerations, - ModerationsWithRawResponse, - AsyncModerationsWithRawResponse, -) +from .images import Images, AsyncImages, ImagesWithRawResponse, AsyncImagesWithRawResponse +from .models import Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse +from .embeddings import Embeddings, AsyncEmbeddings, EmbeddingsWithRawResponse, AsyncEmbeddingsWithRawResponse +from .fine_tunes import FineTunes, AsyncFineTunes, FineTunesWithRawResponse, AsyncFineTunesWithRawResponse +from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse +from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse +from .moderations import Moderations, AsyncModerations, ModerationsWithRawResponse, AsyncModerationsWithRawResponse __all__ = [ "Completions", diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py index 76547b5f34..b6ff4322d4 100644 --- a/src/openai/resources/audio/__init__.py +++ b/src/openai/resources/audio/__init__.py @@ -1,18 +1,8 @@ # File generated from our OpenAPI spec by Stainless. from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse -from .speech import ( - Speech, - AsyncSpeech, - SpeechWithRawResponse, - AsyncSpeechWithRawResponse, -) -from .translations import ( - Translations, - AsyncTranslations, - TranslationsWithRawResponse, - AsyncTranslationsWithRawResponse, -) +from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse +from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse from .transcriptions import ( Transcriptions, AsyncTranscriptions, diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 6f7226ee59..6b9242f0c2 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -4,19 +4,9 @@ from typing import TYPE_CHECKING -from .speech import ( - Speech, - AsyncSpeech, - SpeechWithRawResponse, - AsyncSpeechWithRawResponse, -) +from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse from ..._resource import SyncAPIResource, AsyncAPIResource -from .translations import ( - Translations, - AsyncTranslations, - TranslationsWithRawResponse, - AsyncTranslationsWithRawResponse, -) +from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse from .transcriptions import ( Transcriptions, AsyncTranscriptions, diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 458843866f..ac81a80777 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -7,12 +7,21 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import speech_create_params -from ..._base_client import HttpxBinaryResponseContent, make_request_options +from ..._base_client import ( + HttpxBinaryResponseContent, + make_request_options, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index d2b4452411..54be1c99a6 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -7,12 +7,21 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + FileTypes, +) from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import Transcription, transcription_create_params -from ..._base_client import make_request_options +from ..._base_client import ( + make_request_options, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index fe7f7f2a40..c4489004ac 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -7,12 +7,21 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + FileTypes, +) from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import Translation, translation_create_params -from ..._base_client import make_request_options +from ..._base_client import ( + make_request_options, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 55ad243cca..561f8bef60 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -1,18 +1,8 @@ # File generated from our OpenAPI spec by Stainless. from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse -from .threads import ( - Threads, - AsyncThreads, - ThreadsWithRawResponse, - AsyncThreadsWithRawResponse, -) -from .assistants import ( - Assistants, - AsyncAssistants, - AssistantsWithRawResponse, - AsyncAssistantsWithRawResponse, -) +from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse +from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse __all__ = [ "Assistants", diff --git a/src/openai/resources/beta/assistants/__init__.py b/src/openai/resources/beta/assistants/__init__.py index 6efb0b21ec..205b2cf0f5 100644 --- a/src/openai/resources/beta/assistants/__init__.py +++ b/src/openai/resources/beta/assistants/__init__.py @@ -1,12 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .assistants import ( - Assistants, - AsyncAssistants, - AssistantsWithRawResponse, - AsyncAssistantsWithRawResponse, -) +from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse __all__ = [ "Files", diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index efa711ecf4..944019bed9 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -8,7 +8,13 @@ import httpx from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -20,7 +26,10 @@ assistant_create_params, assistant_update_params, ) -from ...._base_client import AsyncPaginator, make_request_options +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ...._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index 5ac5897ca3..5682587487 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -7,12 +7,21 @@ import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage -from ...._base_client import AsyncPaginator, make_request_options +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) from ....types.beta.assistants import ( AssistantFile, FileDeleteResponse, diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index b552561763..5cea6c1460 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -4,18 +4,8 @@ from typing import TYPE_CHECKING -from .threads import ( - Threads, - AsyncThreads, - ThreadsWithRawResponse, - AsyncThreadsWithRawResponse, -) -from .assistants import ( - Assistants, - AsyncAssistants, - AssistantsWithRawResponse, - AsyncAssistantsWithRawResponse, -) +from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse +from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse from ..._resource import SyncAPIResource, AsyncAPIResource if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/__init__.py b/src/openai/resources/beta/threads/__init__.py index b9aaada465..fe7c5e5a20 100644 --- a/src/openai/resources/beta/threads/__init__.py +++ b/src/openai/resources/beta/threads/__init__.py @@ -1,18 +1,8 @@ # File generated from our OpenAPI spec by Stainless. from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .threads import ( - Threads, - AsyncThreads, - ThreadsWithRawResponse, - AsyncThreadsWithRawResponse, -) -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, -) +from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse +from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse __all__ = [ "Runs", diff --git a/src/openai/resources/beta/threads/messages/__init__.py b/src/openai/resources/beta/threads/messages/__init__.py index d8d4ce448c..cef618ed14 100644 --- a/src/openai/resources/beta/threads/messages/__init__.py +++ b/src/openai/resources/beta/threads/messages/__init__.py @@ -1,12 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, -) +from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse __all__ = [ "Files", diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index e028a6fda7..24c9680f3d 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -7,12 +7,21 @@ import httpx -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import AsyncPaginator, make_request_options +from ....._base_client import ( + AsyncPaginator, + make_request_options, +) from .....types.beta.threads.messages import MessageFile, file_list_params if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 30ae072512..9a6f5706c3 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -8,12 +8,21 @@ import httpx from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import AsyncPaginator, make_request_options +from ....._base_client import ( + AsyncPaginator, + make_request_options, +) from .....types.beta.threads import ( ThreadMessage, message_list_params, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 969bfab70a..719e35ea46 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -8,12 +8,21 @@ import httpx from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import AsyncPaginator, make_request_options +from ....._base_client import ( + AsyncPaginator, + make_request_options, +) from .....types.beta.threads import ( Run, run_list_params, diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 4fcc87a0ff..f26034cf82 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -7,12 +7,21 @@ import httpx -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import AsyncPaginator, make_request_options +from ....._base_client import ( + AsyncPaginator, + make_request_options, +) from .....types.beta.threads.runs import RunStep, step_list_params if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 9469fc0513..b37667485d 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -7,13 +7,14 @@ import httpx from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, +from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, ) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -24,7 +25,9 @@ thread_update_params, thread_create_and_run_params, ) -from ...._base_client import make_request_options +from ...._base_client import ( + make_request_options, +) from ....types.beta.threads import Run if TYPE_CHECKING: diff --git a/src/openai/resources/chat/__init__.py b/src/openai/resources/chat/__init__.py index 2e56c0cbfa..85b246509e 100644 --- a/src/openai/resources/chat/__init__.py +++ b/src/openai/resources/chat/__init__.py @@ -1,12 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse -from .completions import ( - Completions, - AsyncCompletions, - CompletionsWithRawResponse, - AsyncCompletionsWithRawResponse, -) +from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse __all__ = [ "Completions", diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index 3847b20512..d93a501b1f 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -5,12 +5,7 @@ from typing import TYPE_CHECKING from ..._resource import SyncAPIResource, AsyncAPIResource -from .completions import ( - Completions, - AsyncCompletions, - CompletionsWithRawResponse, - AsyncCompletionsWithRawResponse, -) +from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index d0657b2f73..e29554c26d 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -7,7 +7,13 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import required_args, maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -20,7 +26,9 @@ ChatCompletionToolChoiceOptionParam, completion_create_params, ) -from ..._base_client import make_request_options +from ..._base_client import ( + make_request_options, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index baf6f04fef..39484c6f7b 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -8,12 +8,20 @@ import httpx from ..types import Completion, completion_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import required_args, maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._streaming import Stream, AsyncStream -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/edits.py b/src/openai/resources/edits.py index eafaa82fdf..587da02c8f 100644 --- a/src/openai/resources/edits.py +++ b/src/openai/resources/edits.py @@ -9,11 +9,19 @@ import httpx from ..types import Edit, edit_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index c31ad9d931..2ff3d3d44f 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -9,13 +9,20 @@ import httpx from ..types import CreateEmbeddingResponse, embedding_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import is_given, maybe_transform -from .._extras import numpy as np -from .._extras import has_numpy +from .._extras import numpy as np, has_numpy from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index a6f75e5a4c..b9f815af85 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -9,8 +9,20 @@ import httpx -from ..types import FileObject, FileDeleted, file_list_params, file_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..types import ( + FileObject, + FileDeleted, + file_list_params, + file_create_params, +) +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + FileTypes, +) from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper diff --git a/src/openai/resources/fine_tunes.py b/src/openai/resources/fine_tunes.py index 91c8201cbb..f50d78717b 100644 --- a/src/openai/resources/fine_tunes.py +++ b/src/openai/resources/fine_tunes.py @@ -14,13 +14,22 @@ fine_tune_create_params, fine_tune_list_events_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._streaming import Stream, AsyncStream from ..pagination import SyncPage, AsyncPage -from .._base_client import AsyncPaginator, make_request_options +from .._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py index 9133c25d4a..27445fb707 100644 --- a/src/openai/resources/fine_tuning/__init__.py +++ b/src/openai/resources/fine_tuning/__init__.py @@ -1,12 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse -from .fine_tuning import ( - FineTuning, - AsyncFineTuning, - FineTuningWithRawResponse, - AsyncFineTuningWithRawResponse, -) +from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse __all__ = [ "Jobs", diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 3d9aed8d91..55eee67044 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -7,12 +7,21 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import AsyncPaginator, make_request_options +from ..._base_client import ( + AsyncPaginator, + make_request_options, +) from ...types.fine_tuning import ( FineTuningJob, FineTuningJobEvent, diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 94b1bc1fc8..0e1313078f 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -13,11 +13,20 @@ image_generate_params, image_create_variation_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + FileTypes, +) from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index 2d04bdc5cc..a44a7ffbb0 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -7,11 +7,20 @@ import httpx from ..types import Model, ModelDeleted -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncPage, AsyncPage -from .._base_client import AsyncPaginator, make_request_options +from .._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 12a7c68a7b..9de7cd640f 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -8,11 +8,19 @@ import httpx from ..types import ModerationCreateResponse, moderation_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 1b4fca26ee..df2b580587 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -5,8 +5,7 @@ from .edit import Edit as Edit from .image import Image as Image from .model import Model as Model -from .shared import FunctionDefinition as FunctionDefinition -from .shared import FunctionParameters as FunctionParameters +from .shared import FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters from .embedding import Embedding as Embedding from .fine_tune import FineTune as FineTune from .completion import Completion as Completion @@ -28,18 +27,8 @@ from .fine_tune_create_params import FineTuneCreateParams as FineTuneCreateParams from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .moderation_create_params import ModerationCreateParams as ModerationCreateParams -from .create_embedding_response import ( - CreateEmbeddingResponse as CreateEmbeddingResponse, -) -from .moderation_create_response import ( - ModerationCreateResponse as ModerationCreateResponse, -) -from .fine_tune_list_events_params import ( - FineTuneListEventsParams as FineTuneListEventsParams, -) -from .image_create_variation_params import ( - ImageCreateVariationParams as ImageCreateVariationParams, -) -from .fine_tune_events_list_response import ( - FineTuneEventsListResponse as FineTuneEventsListResponse, -) +from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse +from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse +from .fine_tune_list_events_params import FineTuneListEventsParams as FineTuneListEventsParams +from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams +from .fine_tune_events_list_response import FineTuneEventsListResponse as FineTuneEventsListResponse diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index 83afa060f8..ba5f7fd8e0 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -5,9 +5,5 @@ from .translation import Translation as Translation from .transcription import Transcription as Transcription from .speech_create_params import SpeechCreateParams as SpeechCreateParams -from .translation_create_params import ( - TranslationCreateParams as TranslationCreateParams, -) -from .transcription_create_params import ( - TranscriptionCreateParams as TranscriptionCreateParams, -) +from .translation_create_params import TranslationCreateParams as TranslationCreateParams +from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index c03d823b8c..e6742521e9 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -11,6 +11,4 @@ from .assistant_list_params import AssistantListParams as AssistantListParams from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams -from .thread_create_and_run_params import ( - ThreadCreateAndRunParams as ThreadCreateAndRunParams, -) +from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index 0cb557a514..8c77466dec 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -11,12 +11,6 @@ from .message_content_text import MessageContentText as MessageContentText from .message_create_params import MessageCreateParams as MessageCreateParams from .message_update_params import MessageUpdateParams as MessageUpdateParams -from .message_content_image_file import ( - MessageContentImageFile as MessageContentImageFile, -) -from .run_submit_tool_outputs_params import ( - RunSubmitToolOutputsParams as RunSubmitToolOutputsParams, -) -from .required_action_function_tool_call import ( - RequiredActionFunctionToolCall as RequiredActionFunctionToolCall, -) +from .message_content_image_file import MessageContentImageFile as MessageContentImageFile +from .run_submit_tool_outputs_params import RunSubmitToolOutputsParams as RunSubmitToolOutputsParams +from .required_action_function_tool_call import RequiredActionFunctionToolCall as RequiredActionFunctionToolCall diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py index 72b972a986..16cb852922 100644 --- a/src/openai/types/beta/threads/runs/__init__.py +++ b/src/openai/types/beta/threads/runs/__init__.py @@ -8,6 +8,4 @@ from .function_tool_call import FunctionToolCall as FunctionToolCall from .retrieval_tool_call import RetrievalToolCall as RetrievalToolCall from .tool_calls_step_details import ToolCallsStepDetails as ToolCallsStepDetails -from .message_creation_step_details import ( - MessageCreationStepDetails as MessageCreationStepDetails, -) +from .message_creation_step_details import MessageCreationStepDetails as MessageCreationStepDetails diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 5fe182f41e..3f90919619 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -7,27 +7,13 @@ from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage from .completion_create_params import CompletionCreateParams as CompletionCreateParams -from .chat_completion_tool_param import ( - ChatCompletionToolParam as ChatCompletionToolParam, -) -from .chat_completion_message_param import ( - ChatCompletionMessageParam as ChatCompletionMessageParam, -) -from .chat_completion_message_tool_call import ( - ChatCompletionMessageToolCall as ChatCompletionMessageToolCall, -) -from .chat_completion_content_part_param import ( - ChatCompletionContentPartParam as ChatCompletionContentPartParam, -) -from .chat_completion_tool_message_param import ( - ChatCompletionToolMessageParam as ChatCompletionToolMessageParam, -) -from .chat_completion_user_message_param import ( - ChatCompletionUserMessageParam as ChatCompletionUserMessageParam, -) -from .chat_completion_system_message_param import ( - ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam, -) +from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam +from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam +from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall +from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam +from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam +from .chat_completion_user_message_param import ChatCompletionUserMessageParam as ChatCompletionUserMessageParam +from .chat_completion_system_message_param import ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam from .chat_completion_function_message_param import ( ChatCompletionFunctionMessageParam as ChatCompletionFunctionMessageParam, ) diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index 587578e2ef..8e58239258 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -5,9 +5,7 @@ from typing import Union from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam -from .chat_completion_content_part_image_param import ( - ChatCompletionContentPartImageParam, -) +from .chat_completion_content_part_image_param import ChatCompletionContentPartImageParam __all__ = ["ChatCompletionContentPartParam"] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 69fe250eca..0d8495b0c1 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -8,12 +8,8 @@ from ...types import shared_params from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam -from .chat_completion_tool_choice_option_param import ( - ChatCompletionToolChoiceOptionParam, -) -from .chat_completion_function_call_option_param import ( - ChatCompletionFunctionCallOptionParam, -) +from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam +from .chat_completion_function_call_option_param import ChatCompletionFunctionCallOptionParam __all__ = [ "CompletionCreateParamsBase", diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 82e975b46d..97e74c61e4 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -10,7 +10,10 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta import Assistant, AssistantDeleted +from openai.types.beta import ( + Assistant, + AssistantDeleted, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 8fa1fc20ea..860159ffb3 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -9,7 +9,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI -from openai.types.beta import Thread, ThreadDeleted +from openai.types.beta import ( + Thread, + ThreadDeleted, +) from openai.types.beta.threads import Run base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index d323dfc354..39de3fa29d 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -10,7 +10,9 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads import Run +from openai.types.beta.threads import ( + Run, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 5716a23d54..927ca9bbdd 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -10,7 +10,10 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.fine_tuning import FineTuningJob, FineTuningJobEvent +from openai.types.fine_tuning import ( + FineTuningJob, + FineTuningJobEvent, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/utils.py b/tests/utils.py index b513794017..db2ca5601b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,7 +8,12 @@ from typing_extensions import Literal, get_args, get_origin, assert_type from openai._types import NoneType -from openai._utils import is_dict, is_list, is_list_type, is_union_type +from openai._utils import ( + is_dict, + is_list, + is_list_type, + is_union_type, +) from openai._compat import PYDANTIC_V2, field_outer_type, get_model_fields from openai._models import BaseModel From 01b690c11c64bd9db2486939f99520dbd8be2471 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:57:27 +0000 Subject: [PATCH 237/914] chore(internal): update formatting (#941) --- src/openai/__init__.py | 12 +----------- src/openai/_client.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 20ab72ee25..d695b68980 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -8,17 +8,7 @@ from . import types from ._types import NoneType, Transport, ProxiesTypes from ._utils import file_from_path -from ._client import ( - Client, - OpenAI, - Stream, - Timeout, - Transport, - AsyncClient, - AsyncOpenAI, - AsyncStream, - RequestOptions, -) +from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions from ._version import __title__, __version__ from ._exceptions import ( APIError, diff --git a/src/openai/_client.py b/src/openai/_client.py index 79054aba2f..7f8744c98b 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -20,11 +20,19 @@ ProxiesTypes, RequestOptions, ) -from ._utils import is_given, is_mapping, get_async_library +from ._utils import ( + is_given, + is_mapping, + get_async_library, +) from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError -from ._base_client import DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient +from ._base_client import ( + DEFAULT_MAX_RETRIES, + SyncAPIClient, + AsyncAPIClient, +) __all__ = [ "Timeout", From 60ccec6baa826c5d434bde29e6444b62ab3aee2e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:36:08 +0000 Subject: [PATCH 238/914] fix(pagination): use correct type hint for .object (#943) --- src/openai/pagination.py | 42 ++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/openai/pagination.py b/src/openai/pagination.py index 17f2d1a4ca..d47deb17be 100644 --- a/src/openai/pagination.py +++ b/src/openai/pagination.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from typing import Any, List, Generic, Optional, cast -from typing_extensions import Literal, Protocol, override, runtime_checkable +from typing_extensions import Protocol, override, runtime_checkable from ._types import ModelT from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage @@ -11,18 +11,21 @@ @runtime_checkable class CursorPageItem(Protocol): - id: str + id: Optional[str] class SyncPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" data: List[ModelT] - object: Literal["list"] + object: str @override def _get_page_items(self) -> List[ModelT]: - return self.data + data = self.data + if not data: + return [] + return data @override def next_page_info(self) -> None: @@ -37,11 +40,14 @@ class AsyncPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" data: List[ModelT] - object: Literal["list"] + object: str @override def _get_page_items(self) -> List[ModelT]: - return self.data + data = self.data + if not data: + return [] + return data @override def next_page_info(self) -> None: @@ -57,15 +63,19 @@ class SyncCursorPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): @override def _get_page_items(self) -> List[ModelT]: - return self.data + data = self.data + if not data: + return [] + return data @override def next_page_info(self) -> Optional[PageInfo]: - if not self.data: + data = self.data + if not data: return None - item = cast(Any, self.data[-1]) - if not isinstance(item, CursorPageItem): + item = cast(Any, data[-1]) + if not isinstance(item, CursorPageItem) or item.id is None: # TODO emit warning log return None @@ -77,15 +87,19 @@ class AsyncCursorPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): @override def _get_page_items(self) -> List[ModelT]: - return self.data + data = self.data + if not data: + return [] + return data @override def next_page_info(self) -> Optional[PageInfo]: - if not self.data: + data = self.data + if not data: return None - item = cast(Any, self.data[-1]) - if not isinstance(item, CursorPageItem): + item = cast(Any, data[-1]) + if not isinstance(item, CursorPageItem) or item.id is None: # TODO emit warning log return None From 8ad790ae9db8848eb3ac38703d0bb7c1d82b343b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:55:19 +0000 Subject: [PATCH 239/914] chore(internal): reformat imports (#944) --- pyproject.toml | 37 +++++---- requirements-dev.lock | 19 +---- src/openai/__init__.py | 41 ++++++---- src/openai/_client.py | 15 +--- src/openai/_compat.py | 30 ++++---- src/openai/_extras/__init__.py | 3 +- src/openai/_models.py | 14 +++- src/openai/_types.py | 9 ++- src/openai/_utils/__init__.py | 76 +++++++++---------- src/openai/_utils/_utils.py | 4 +- src/openai/resources/__init__.py | 49 ++++++++++-- src/openai/resources/audio/__init__.py | 14 +++- src/openai/resources/audio/audio.py | 14 +++- src/openai/resources/audio/speech.py | 13 +--- src/openai/resources/audio/transcriptions.py | 13 +--- src/openai/resources/audio/translations.py | 13 +--- src/openai/resources/beta/__init__.py | 14 +++- .../resources/beta/assistants/__init__.py | 7 +- .../resources/beta/assistants/assistants.py | 13 +--- src/openai/resources/beta/assistants/files.py | 13 +--- src/openai/resources/beta/beta.py | 14 +++- src/openai/resources/beta/threads/__init__.py | 14 +++- .../beta/threads/messages/__init__.py | 7 +- .../resources/beta/threads/messages/files.py | 13 +--- .../beta/threads/messages/messages.py | 13 +--- .../resources/beta/threads/runs/runs.py | 13 +--- .../resources/beta/threads/runs/steps.py | 13 +--- src/openai/resources/beta/threads/threads.py | 17 ++--- src/openai/resources/chat/__init__.py | 7 +- src/openai/resources/chat/chat.py | 7 +- src/openai/resources/chat/completions.py | 12 +-- src/openai/resources/completions.py | 12 +-- src/openai/resources/edits.py | 12 +-- src/openai/resources/embeddings.py | 15 +--- src/openai/resources/files.py | 16 +--- src/openai/resources/fine_tunes.py | 13 +--- src/openai/resources/fine_tuning/__init__.py | 7 +- src/openai/resources/fine_tuning/jobs.py | 13 +--- src/openai/resources/images.py | 13 +--- src/openai/resources/models.py | 13 +--- src/openai/resources/moderations.py | 12 +-- src/openai/types/__init__.py | 23 ++++-- src/openai/types/audio/__init__.py | 8 +- src/openai/types/beta/__init__.py | 4 +- src/openai/types/beta/threads/__init__.py | 12 ++- .../types/beta/threads/runs/__init__.py | 4 +- src/openai/types/chat/__init__.py | 28 +++++-- .../chat_completion_content_part_param.py | 4 +- .../types/chat/completion_create_params.py | 8 +- tests/api_resources/beta/test_assistants.py | 5 +- tests/api_resources/beta/test_threads.py | 5 +- tests/api_resources/beta/threads/test_runs.py | 4 +- tests/api_resources/fine_tuning/test_jobs.py | 5 +- tests/utils.py | 7 +- 54 files changed, 374 insertions(+), 410 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 44e7bcb0ed..c468220495 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,16 +48,16 @@ openai = "openai.cli:main" [tool.rye] managed = true dev-dependencies = [ - # version pins are in requirements-dev.lock - "pyright", - "mypy", - "black", - "respx", - "pytest", - "pytest-asyncio", - "ruff", - "time-machine", - "nox", + "pyright==1.1.332", + "mypy==1.7.1", + "black==23.3.0", + "respx==0.20.2", + "pytest==7.1.1", + "pytest-asyncio==0.21.1", + "ruff==0.0.282", + "isort==5.10.1", + "time-machine==2.9.0", + "nox==2023.4.22", "dirty-equals>=0.6.0", "azure-identity >=1.14.1", "types-tqdm > 4" @@ -68,10 +68,12 @@ format = { chain = [ "format:black", "format:docs", "format:ruff", + "format:isort", ]} "format:black" = "black ." "format:docs" = "python bin/blacken-docs.py README.md api.md" "format:ruff" = "ruff --fix ." +"format:isort" = "isort ." "check:ruff" = "ruff ." @@ -126,13 +128,16 @@ reportImplicitOverride = true reportImportCycles = false reportPrivateUsage = false +[tool.isort] +profile = "black" +length_sort = true +extra_standard_library = ["typing_extensions"] + [tool.ruff] line-length = 120 -output-format = "grouped" +format = "grouped" target-version = "py37" select = [ - # isort - "I", # remove unused imports "F401", # bare except statements @@ -150,12 +155,6 @@ unfixable = [ ] ignore-init-module-imports = true -[tool.ruff.lint.isort] -length-sort = true -length-sort-straight = true -combine-as-imports = true -extra-standard-library = ["typing_extensions"] -known-first-party = ["openai", "tests"] [tool.ruff.per-file-ignores] "bin/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock index bc993b16de..b1a9428a09 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -11,15 +11,11 @@ annotated-types==0.6.0 anyio==4.1.0 argcomplete==3.1.2 attrs==23.1.0 -azure-core==1.29.5 azure-identity==1.15.0 black==23.3.0 certifi==2023.7.22 -cffi==1.16.0 -charset-normalizer==3.3.2 click==8.1.7 colorlog==6.7.0 -cryptography==41.0.7 dirty-equals==0.6.0 distlib==0.3.7 distro==1.8.0 @@ -30,43 +26,32 @@ httpcore==1.0.2 httpx==0.25.2 idna==3.4 iniconfig==2.0.0 -msal==1.26.0 -msal-extensions==1.0.0 +isort==5.10.1 mypy==1.7.1 mypy-extensions==1.0.0 nodeenv==1.8.0 nox==2023.4.22 -numpy==1.26.2 packaging==23.2 -pandas==2.1.3 -pandas-stubs==2.1.1.230928 pathspec==0.11.2 platformdirs==3.11.0 pluggy==1.3.0 -portalocker==2.8.2 py==1.11.0 -pycparser==2.21 pydantic==2.4.2 pydantic-core==2.10.1 -pyjwt==2.8.0 pyright==1.1.332 pytest==7.1.1 pytest-asyncio==0.21.1 python-dateutil==2.8.2 pytz==2023.3.post1 -requests==2.31.0 respx==0.20.2 -ruff==0.1.7 +ruff==0.0.282 six==1.16.0 sniffio==1.3.0 time-machine==2.9.0 tomli==2.0.1 tqdm==4.66.1 -types-pytz==2023.3.1.1 types-tqdm==4.66.0.2 typing-extensions==4.8.0 -tzdata==2023.3 -urllib3==2.1.0 virtualenv==20.24.5 # The following packages are considered to be unsafe in a requirements file: setuptools==68.2.2 diff --git a/src/openai/__init__.py b/src/openai/__init__.py index d695b68980..d92dfe969a 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -8,7 +8,17 @@ from . import types from ._types import NoneType, Transport, ProxiesTypes from ._utils import file_from_path -from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions +from ._client import ( + Client, + OpenAI, + Stream, + Timeout, + Transport, + AsyncClient, + AsyncOpenAI, + AsyncStream, + RequestOptions, +) from ._version import __title__, __version__ from ._exceptions import ( APIError, @@ -62,7 +72,8 @@ from .lib import azure as _azure from .version import VERSION as VERSION -from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI +from .lib.azure import AzureOpenAI as AzureOpenAI +from .lib.azure import AsyncAzureOpenAI as AsyncAzureOpenAI from .lib._old_api import * _setup_logging() @@ -319,17 +330,15 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] _client = None -from ._module_client import ( - beta as beta, - chat as chat, - audio as audio, - edits as edits, - files as files, - images as images, - models as models, - embeddings as embeddings, - fine_tunes as fine_tunes, - completions as completions, - fine_tuning as fine_tuning, - moderations as moderations, -) +from ._module_client import beta as beta +from ._module_client import chat as chat +from ._module_client import audio as audio +from ._module_client import edits as edits +from ._module_client import files as files +from ._module_client import images as images +from ._module_client import models as models +from ._module_client import embeddings as embeddings +from ._module_client import fine_tunes as fine_tunes +from ._module_client import completions as completions +from ._module_client import fine_tuning as fine_tuning +from ._module_client import moderations as moderations diff --git a/src/openai/_client.py b/src/openai/_client.py index 7f8744c98b..202162070b 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -20,19 +20,12 @@ ProxiesTypes, RequestOptions, ) -from ._utils import ( - is_given, - is_mapping, - get_async_library, -) +from ._utils import is_given, is_mapping, get_async_library from ._version import __version__ -from ._streaming import Stream as Stream, AsyncStream as AsyncStream +from ._streaming import Stream as Stream +from ._streaming import AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError -from ._base_client import ( - DEFAULT_MAX_RETRIES, - SyncAPIClient, - AsyncAPIClient, -) +from ._base_client import DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient __all__ = [ "Timeout", diff --git a/src/openai/_compat.py b/src/openai/_compat.py index d95db8ed1e..34323c9b7e 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -43,23 +43,21 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 else: if PYDANTIC_V2: - from pydantic.v1.typing import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - is_typeddict as is_typeddict, - is_literal_type as is_literal_type, - ) - from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import is_union as is_union + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_typeddict as is_typeddict + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime else: - from pydantic.typing import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - is_typeddict as is_typeddict, - is_literal_type as is_literal_type, - ) - from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + from pydantic.typing import get_args as get_args + from pydantic.typing import is_union as is_union + from pydantic.typing import get_origin as get_origin + from pydantic.typing import is_typeddict as is_typeddict + from pydantic.typing import is_literal_type as is_literal_type + from pydantic.datetime_parse import parse_date as parse_date + from pydantic.datetime_parse import parse_datetime as parse_datetime # refactored config diff --git a/src/openai/_extras/__init__.py b/src/openai/_extras/__init__.py index 864dac4171..dc6625c5dc 100644 --- a/src/openai/_extras/__init__.py +++ b/src/openai/_extras/__init__.py @@ -1,2 +1,3 @@ -from .numpy_proxy import numpy as numpy, has_numpy as has_numpy +from .numpy_proxy import numpy as numpy +from .numpy_proxy import has_numpy as has_numpy from .pandas_proxy import pandas as pandas diff --git a/src/openai/_models.py b/src/openai/_models.py index cdd44ccb0a..5b8c96010f 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -30,11 +30,17 @@ AnyMapping, HttpxRequestFiles, ) -from ._utils import is_list, is_given, is_mapping, parse_date, parse_datetime, strip_not_given +from ._utils import ( + is_list, + is_given, + is_mapping, + parse_date, + parse_datetime, + strip_not_given, +) +from ._compat import PYDANTIC_V2, ConfigDict +from ._compat import GenericModel as BaseGenericModel from ._compat import ( - PYDANTIC_V2, - ConfigDict, - GenericModel as BaseGenericModel, get_args, is_union, parse_obj, diff --git a/src/openai/_types.py b/src/openai/_types.py index 6f298c18c4..9e962a1078 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -19,7 +19,14 @@ Sequence, AsyncIterator, ) -from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable +from typing_extensions import ( + Literal, + Protocol, + TypeAlias, + TypedDict, + override, + runtime_checkable, +) import pydantic from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index e98636c92f..400ca9b828 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -1,41 +1,37 @@ from ._proxy import LazyProxy as LazyProxy -from ._utils import ( - flatten as flatten, - is_dict as is_dict, - is_list as is_list, - is_given as is_given, - is_tuple as is_tuple, - is_mapping as is_mapping, - is_tuple_t as is_tuple_t, - parse_date as parse_date, - is_sequence as is_sequence, - coerce_float as coerce_float, - is_list_type as is_list_type, - is_mapping_t as is_mapping_t, - removeprefix as removeprefix, - removesuffix as removesuffix, - extract_files as extract_files, - is_sequence_t as is_sequence_t, - is_union_type as is_union_type, - required_args as required_args, - coerce_boolean as coerce_boolean, - coerce_integer as coerce_integer, - file_from_path as file_from_path, - parse_datetime as parse_datetime, - strip_not_given as strip_not_given, - deepcopy_minimal as deepcopy_minimal, - extract_type_arg as extract_type_arg, - is_required_type as is_required_type, - get_async_library as get_async_library, - is_annotated_type as is_annotated_type, - maybe_coerce_float as maybe_coerce_float, - get_required_header as get_required_header, - maybe_coerce_boolean as maybe_coerce_boolean, - maybe_coerce_integer as maybe_coerce_integer, - strip_annotated_type as strip_annotated_type, -) -from ._transform import ( - PropertyInfo as PropertyInfo, - transform as transform, - maybe_transform as maybe_transform, -) +from ._utils import flatten as flatten +from ._utils import is_dict as is_dict +from ._utils import is_list as is_list +from ._utils import is_given as is_given +from ._utils import is_tuple as is_tuple +from ._utils import is_mapping as is_mapping +from ._utils import is_tuple_t as is_tuple_t +from ._utils import parse_date as parse_date +from ._utils import is_sequence as is_sequence +from ._utils import coerce_float as coerce_float +from ._utils import is_list_type as is_list_type +from ._utils import is_mapping_t as is_mapping_t +from ._utils import removeprefix as removeprefix +from ._utils import removesuffix as removesuffix +from ._utils import extract_files as extract_files +from ._utils import is_sequence_t as is_sequence_t +from ._utils import is_union_type as is_union_type +from ._utils import required_args as required_args +from ._utils import coerce_boolean as coerce_boolean +from ._utils import coerce_integer as coerce_integer +from ._utils import file_from_path as file_from_path +from ._utils import parse_datetime as parse_datetime +from ._utils import strip_not_given as strip_not_given +from ._utils import deepcopy_minimal as deepcopy_minimal +from ._utils import extract_type_arg as extract_type_arg +from ._utils import is_required_type as is_required_type +from ._utils import get_async_library as get_async_library +from ._utils import is_annotated_type as is_annotated_type +from ._utils import maybe_coerce_float as maybe_coerce_float +from ._utils import get_required_header as get_required_header +from ._utils import maybe_coerce_boolean as maybe_coerce_boolean +from ._utils import maybe_coerce_integer as maybe_coerce_integer +from ._utils import strip_annotated_type as strip_annotated_type +from ._transform import PropertyInfo as PropertyInfo +from ._transform import transform as transform +from ._transform import maybe_transform as maybe_transform diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index cce6923810..83f88cc3e7 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -21,7 +21,9 @@ import sniffio from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import is_union as _is_union, parse_date as parse_date, parse_datetime as parse_datetime +from .._compat import is_union as _is_union +from .._compat import parse_date as parse_date +from .._compat import parse_datetime as parse_datetime _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index 2cdbeb6ae1..e0f4f08d5c 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -5,13 +5,48 @@ from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse from .edits import Edits, AsyncEdits, EditsWithRawResponse, AsyncEditsWithRawResponse from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .images import Images, AsyncImages, ImagesWithRawResponse, AsyncImagesWithRawResponse -from .models import Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse -from .embeddings import Embeddings, AsyncEmbeddings, EmbeddingsWithRawResponse, AsyncEmbeddingsWithRawResponse -from .fine_tunes import FineTunes, AsyncFineTunes, FineTunesWithRawResponse, AsyncFineTunesWithRawResponse -from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse -from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse -from .moderations import Moderations, AsyncModerations, ModerationsWithRawResponse, AsyncModerationsWithRawResponse +from .images import ( + Images, + AsyncImages, + ImagesWithRawResponse, + AsyncImagesWithRawResponse, +) +from .models import ( + Models, + AsyncModels, + ModelsWithRawResponse, + AsyncModelsWithRawResponse, +) +from .embeddings import ( + Embeddings, + AsyncEmbeddings, + EmbeddingsWithRawResponse, + AsyncEmbeddingsWithRawResponse, +) +from .fine_tunes import ( + FineTunes, + AsyncFineTunes, + FineTunesWithRawResponse, + AsyncFineTunesWithRawResponse, +) +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, +) +from .fine_tuning import ( + FineTuning, + AsyncFineTuning, + FineTuningWithRawResponse, + AsyncFineTuningWithRawResponse, +) +from .moderations import ( + Moderations, + AsyncModerations, + ModerationsWithRawResponse, + AsyncModerationsWithRawResponse, +) __all__ = [ "Completions", diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py index b6ff4322d4..76547b5f34 100644 --- a/src/openai/resources/audio/__init__.py +++ b/src/openai/resources/audio/__init__.py @@ -1,8 +1,18 @@ # File generated from our OpenAPI spec by Stainless. from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse -from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse -from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse +from .speech import ( + Speech, + AsyncSpeech, + SpeechWithRawResponse, + AsyncSpeechWithRawResponse, +) +from .translations import ( + Translations, + AsyncTranslations, + TranslationsWithRawResponse, + AsyncTranslationsWithRawResponse, +) from .transcriptions import ( Transcriptions, AsyncTranscriptions, diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 6b9242f0c2..6f7226ee59 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -4,9 +4,19 @@ from typing import TYPE_CHECKING -from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse +from .speech import ( + Speech, + AsyncSpeech, + SpeechWithRawResponse, + AsyncSpeechWithRawResponse, +) from ..._resource import SyncAPIResource, AsyncAPIResource -from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse +from .translations import ( + Translations, + AsyncTranslations, + TranslationsWithRawResponse, + AsyncTranslationsWithRawResponse, +) from .transcriptions import ( Transcriptions, AsyncTranscriptions, diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index ac81a80777..458843866f 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -7,21 +7,12 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import speech_create_params -from ..._base_client import ( - HttpxBinaryResponseContent, - make_request_options, -) +from ..._base_client import HttpxBinaryResponseContent, make_request_options if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 54be1c99a6..d2b4452411 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -7,21 +7,12 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - FileTypes, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import Transcription, transcription_create_params -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index c4489004ac..fe7f7f2a40 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -7,21 +7,12 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - FileTypes, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import Translation, translation_create_params -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 561f8bef60..55ad243cca 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -1,8 +1,18 @@ # File generated from our OpenAPI spec by Stainless. from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse -from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse -from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, +) +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, +) __all__ = [ "Assistants", diff --git a/src/openai/resources/beta/assistants/__init__.py b/src/openai/resources/beta/assistants/__init__.py index 205b2cf0f5..6efb0b21ec 100644 --- a/src/openai/resources/beta/assistants/__init__.py +++ b/src/openai/resources/beta/assistants/__init__.py @@ -1,7 +1,12 @@ # File generated from our OpenAPI spec by Stainless. from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, +) __all__ = [ "Files", diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 944019bed9..efa711ecf4 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -8,13 +8,7 @@ import httpx from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from ...._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -26,10 +20,7 @@ assistant_create_params, assistant_update_params, ) -from ...._base_client import ( - AsyncPaginator, - make_request_options, -) +from ...._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from ...._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index 5682587487..5ac5897ca3 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -7,21 +7,12 @@ import httpx -from ...._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage -from ...._base_client import ( - AsyncPaginator, - make_request_options, -) +from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.assistants import ( AssistantFile, FileDeleteResponse, diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 5cea6c1460..b552561763 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -4,8 +4,18 @@ from typing import TYPE_CHECKING -from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse -from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, +) +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, +) from ..._resource import SyncAPIResource, AsyncAPIResource if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/__init__.py b/src/openai/resources/beta/threads/__init__.py index fe7c5e5a20..b9aaada465 100644 --- a/src/openai/resources/beta/threads/__init__.py +++ b/src/openai/resources/beta/threads/__init__.py @@ -1,8 +1,18 @@ # File generated from our OpenAPI spec by Stainless. from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse -from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, +) +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, +) __all__ = [ "Runs", diff --git a/src/openai/resources/beta/threads/messages/__init__.py b/src/openai/resources/beta/threads/messages/__init__.py index cef618ed14..d8d4ce448c 100644 --- a/src/openai/resources/beta/threads/messages/__init__.py +++ b/src/openai/resources/beta/threads/messages/__init__.py @@ -1,7 +1,12 @@ # File generated from our OpenAPI spec by Stainless. from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, +) __all__ = [ "Files", diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index 24c9680f3d..e028a6fda7 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -7,21 +7,12 @@ import httpx -from ....._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import ( - AsyncPaginator, - make_request_options, -) +from ....._base_client import AsyncPaginator, make_request_options from .....types.beta.threads.messages import MessageFile, file_list_params if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 9a6f5706c3..30ae072512 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -8,21 +8,12 @@ import httpx from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from ....._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import ( - AsyncPaginator, - make_request_options, -) +from ....._base_client import AsyncPaginator, make_request_options from .....types.beta.threads import ( ThreadMessage, message_list_params, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 719e35ea46..969bfab70a 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -8,21 +8,12 @@ import httpx from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse -from ....._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import ( - AsyncPaginator, - make_request_options, -) +from ....._base_client import AsyncPaginator, make_request_options from .....types.beta.threads import ( Run, run_list_params, diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index f26034cf82..4fcc87a0ff 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -7,21 +7,12 @@ import httpx -from ....._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import ( - AsyncPaginator, - make_request_options, -) +from ....._base_client import AsyncPaginator, make_request_options from .....types.beta.threads.runs import RunStep, step_list_params if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index b37667485d..9469fc0513 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -7,14 +7,13 @@ import httpx from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse -from ...._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, ) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -25,9 +24,7 @@ thread_update_params, thread_create_and_run_params, ) -from ...._base_client import ( - make_request_options, -) +from ...._base_client import make_request_options from ....types.beta.threads import Run if TYPE_CHECKING: diff --git a/src/openai/resources/chat/__init__.py b/src/openai/resources/chat/__init__.py index 85b246509e..2e56c0cbfa 100644 --- a/src/openai/resources/chat/__init__.py +++ b/src/openai/resources/chat/__init__.py @@ -1,7 +1,12 @@ # File generated from our OpenAPI spec by Stainless. from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse -from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, +) __all__ = [ "Completions", diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index d93a501b1f..3847b20512 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -5,7 +5,12 @@ from typing import TYPE_CHECKING from ..._resource import SyncAPIResource, AsyncAPIResource -from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index e29554c26d..d0657b2f73 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -7,13 +7,7 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import required_args, maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -26,9 +20,7 @@ ChatCompletionToolChoiceOptionParam, completion_create_params, ) -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 39484c6f7b..baf6f04fef 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -8,20 +8,12 @@ import httpx from ..types import Completion, completion_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import required_args, maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._streaming import Stream, AsyncStream -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/edits.py b/src/openai/resources/edits.py index 587da02c8f..eafaa82fdf 100644 --- a/src/openai/resources/edits.py +++ b/src/openai/resources/edits.py @@ -9,19 +9,11 @@ import httpx from ..types import Edit, edit_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 2ff3d3d44f..c31ad9d931 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -9,20 +9,13 @@ import httpx from ..types import CreateEmbeddingResponse, embedding_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import is_given, maybe_transform -from .._extras import numpy as np, has_numpy +from .._extras import numpy as np +from .._extras import has_numpy from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index b9f815af85..a6f75e5a4c 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -9,20 +9,8 @@ import httpx -from ..types import ( - FileObject, - FileDeleted, - file_list_params, - file_create_params, -) -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - FileTypes, -) +from ..types import FileObject, FileDeleted, file_list_params, file_create_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper diff --git a/src/openai/resources/fine_tunes.py b/src/openai/resources/fine_tunes.py index f50d78717b..91c8201cbb 100644 --- a/src/openai/resources/fine_tunes.py +++ b/src/openai/resources/fine_tunes.py @@ -14,22 +14,13 @@ fine_tune_create_params, fine_tune_list_events_params, ) -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._streaming import Stream, AsyncStream from ..pagination import SyncPage, AsyncPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py index 27445fb707..9133c25d4a 100644 --- a/src/openai/resources/fine_tuning/__init__.py +++ b/src/openai/resources/fine_tuning/__init__.py @@ -1,7 +1,12 @@ # File generated from our OpenAPI spec by Stainless. from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse -from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse +from .fine_tuning import ( + FineTuning, + AsyncFineTuning, + FineTuningWithRawResponse, + AsyncFineTuningWithRawResponse, +) __all__ = [ "Jobs", diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 55eee67044..3d9aed8d91 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -7,21 +7,12 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options from ...types.fine_tuning import ( FineTuningJob, FineTuningJobEvent, diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 0e1313078f..94b1bc1fc8 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -13,20 +13,11 @@ image_generate_params, image_create_variation_params, ) -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - FileTypes, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index a44a7ffbb0..2d04bdc5cc 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -7,20 +7,11 @@ import httpx from ..types import Model, ModelDeleted -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncPage, AsyncPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 9de7cd640f..12a7c68a7b 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -8,19 +8,11 @@ import httpx from ..types import ModerationCreateResponse, moderation_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index df2b580587..1b4fca26ee 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -5,7 +5,8 @@ from .edit import Edit as Edit from .image import Image as Image from .model import Model as Model -from .shared import FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters +from .shared import FunctionDefinition as FunctionDefinition +from .shared import FunctionParameters as FunctionParameters from .embedding import Embedding as Embedding from .fine_tune import FineTune as FineTune from .completion import Completion as Completion @@ -27,8 +28,18 @@ from .fine_tune_create_params import FineTuneCreateParams as FineTuneCreateParams from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .moderation_create_params import ModerationCreateParams as ModerationCreateParams -from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse -from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse -from .fine_tune_list_events_params import FineTuneListEventsParams as FineTuneListEventsParams -from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams -from .fine_tune_events_list_response import FineTuneEventsListResponse as FineTuneEventsListResponse +from .create_embedding_response import ( + CreateEmbeddingResponse as CreateEmbeddingResponse, +) +from .moderation_create_response import ( + ModerationCreateResponse as ModerationCreateResponse, +) +from .fine_tune_list_events_params import ( + FineTuneListEventsParams as FineTuneListEventsParams, +) +from .image_create_variation_params import ( + ImageCreateVariationParams as ImageCreateVariationParams, +) +from .fine_tune_events_list_response import ( + FineTuneEventsListResponse as FineTuneEventsListResponse, +) diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index ba5f7fd8e0..83afa060f8 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -5,5 +5,9 @@ from .translation import Translation as Translation from .transcription import Transcription as Transcription from .speech_create_params import SpeechCreateParams as SpeechCreateParams -from .translation_create_params import TranslationCreateParams as TranslationCreateParams -from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams +from .translation_create_params import ( + TranslationCreateParams as TranslationCreateParams, +) +from .transcription_create_params import ( + TranscriptionCreateParams as TranscriptionCreateParams, +) diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index e6742521e9..c03d823b8c 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -11,4 +11,6 @@ from .assistant_list_params import AssistantListParams as AssistantListParams from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams -from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams +from .thread_create_and_run_params import ( + ThreadCreateAndRunParams as ThreadCreateAndRunParams, +) diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index 8c77466dec..0cb557a514 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -11,6 +11,12 @@ from .message_content_text import MessageContentText as MessageContentText from .message_create_params import MessageCreateParams as MessageCreateParams from .message_update_params import MessageUpdateParams as MessageUpdateParams -from .message_content_image_file import MessageContentImageFile as MessageContentImageFile -from .run_submit_tool_outputs_params import RunSubmitToolOutputsParams as RunSubmitToolOutputsParams -from .required_action_function_tool_call import RequiredActionFunctionToolCall as RequiredActionFunctionToolCall +from .message_content_image_file import ( + MessageContentImageFile as MessageContentImageFile, +) +from .run_submit_tool_outputs_params import ( + RunSubmitToolOutputsParams as RunSubmitToolOutputsParams, +) +from .required_action_function_tool_call import ( + RequiredActionFunctionToolCall as RequiredActionFunctionToolCall, +) diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py index 16cb852922..72b972a986 100644 --- a/src/openai/types/beta/threads/runs/__init__.py +++ b/src/openai/types/beta/threads/runs/__init__.py @@ -8,4 +8,6 @@ from .function_tool_call import FunctionToolCall as FunctionToolCall from .retrieval_tool_call import RetrievalToolCall as RetrievalToolCall from .tool_calls_step_details import ToolCallsStepDetails as ToolCallsStepDetails -from .message_creation_step_details import MessageCreationStepDetails as MessageCreationStepDetails +from .message_creation_step_details import ( + MessageCreationStepDetails as MessageCreationStepDetails, +) diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 3f90919619..5fe182f41e 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -7,13 +7,27 @@ from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage from .completion_create_params import CompletionCreateParams as CompletionCreateParams -from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam -from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam -from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall -from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam -from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam -from .chat_completion_user_message_param import ChatCompletionUserMessageParam as ChatCompletionUserMessageParam -from .chat_completion_system_message_param import ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam +from .chat_completion_tool_param import ( + ChatCompletionToolParam as ChatCompletionToolParam, +) +from .chat_completion_message_param import ( + ChatCompletionMessageParam as ChatCompletionMessageParam, +) +from .chat_completion_message_tool_call import ( + ChatCompletionMessageToolCall as ChatCompletionMessageToolCall, +) +from .chat_completion_content_part_param import ( + ChatCompletionContentPartParam as ChatCompletionContentPartParam, +) +from .chat_completion_tool_message_param import ( + ChatCompletionToolMessageParam as ChatCompletionToolMessageParam, +) +from .chat_completion_user_message_param import ( + ChatCompletionUserMessageParam as ChatCompletionUserMessageParam, +) +from .chat_completion_system_message_param import ( + ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam, +) from .chat_completion_function_message_param import ( ChatCompletionFunctionMessageParam as ChatCompletionFunctionMessageParam, ) diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index 8e58239258..587578e2ef 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -5,7 +5,9 @@ from typing import Union from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam -from .chat_completion_content_part_image_param import ChatCompletionContentPartImageParam +from .chat_completion_content_part_image_param import ( + ChatCompletionContentPartImageParam, +) __all__ = ["ChatCompletionContentPartParam"] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 0d8495b0c1..69fe250eca 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -8,8 +8,12 @@ from ...types import shared_params from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam -from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam -from .chat_completion_function_call_option_param import ChatCompletionFunctionCallOptionParam +from .chat_completion_tool_choice_option_param import ( + ChatCompletionToolChoiceOptionParam, +) +from .chat_completion_function_call_option_param import ( + ChatCompletionFunctionCallOptionParam, +) __all__ = [ "CompletionCreateParamsBase", diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 97e74c61e4..82e975b46d 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -10,10 +10,7 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta import ( - Assistant, - AssistantDeleted, -) +from openai.types.beta import Assistant, AssistantDeleted base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 860159ffb3..8fa1fc20ea 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -9,10 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI -from openai.types.beta import ( - Thread, - ThreadDeleted, -) +from openai.types.beta import Thread, ThreadDeleted from openai.types.beta.threads import Run base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 39de3fa29d..d323dfc354 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -10,9 +10,7 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads import ( - Run, -) +from openai.types.beta.threads import Run base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 927ca9bbdd..5716a23d54 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -10,10 +10,7 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.fine_tuning import ( - FineTuningJob, - FineTuningJobEvent, -) +from openai.types.fine_tuning import FineTuningJob, FineTuningJobEvent base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/utils.py b/tests/utils.py index db2ca5601b..b513794017 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,12 +8,7 @@ from typing_extensions import Literal, get_args, get_origin, assert_type from openai._types import NoneType -from openai._utils import ( - is_dict, - is_list, - is_list_type, - is_union_type, -) +from openai._utils import is_dict, is_list, is_list_type, is_union_type from openai._compat import PYDANTIC_V2, field_outer_type, get_model_fields from openai._models import BaseModel From e967f5a5a6bb9adae6f5a15b48f50aedd64267d5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:27:29 +0000 Subject: [PATCH 240/914] chore(internal): enable more lint rules (#945) --- pyproject.toml | 31 +++++++++++++++++++----------- requirements-dev.lock | 18 ++++++++++++++++- src/openai/__init__.py | 2 +- src/openai/_extras/numpy_proxy.py | 4 ++-- src/openai/_extras/pandas_proxy.py | 4 ++-- src/openai/_streaming.py | 4 ++-- src/openai/_types.py | 1 + src/openai/_utils/_utils.py | 8 +++++--- src/openai/cli/_progress.py | 2 +- src/openai/cli/_tools/migrate.py | 4 ++-- tests/test_client.py | 5 +++-- tests/test_module_client.py | 6 +++--- tests/test_utils/test_proxy.py | 2 +- tests/utils.py | 2 +- 14 files changed, 61 insertions(+), 32 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c468220495..8fe6a69b6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,17 +47,18 @@ openai = "openai.cli:main" [tool.rye] managed = true +# version pins are in requirements-dev.lock dev-dependencies = [ - "pyright==1.1.332", - "mypy==1.7.1", - "black==23.3.0", - "respx==0.20.2", - "pytest==7.1.1", - "pytest-asyncio==0.21.1", - "ruff==0.0.282", - "isort==5.10.1", - "time-machine==2.9.0", - "nox==2023.4.22", + "pyright", + "mypy", + "black", + "respx", + "pytest", + "pytest-asyncio", + "ruff", + "isort", + "time-machine", + "nox", "dirty-equals>=0.6.0", "azure-identity >=1.14.1", "types-tqdm > 4" @@ -135,9 +136,11 @@ extra_standard_library = ["typing_extensions"] [tool.ruff] line-length = 120 -format = "grouped" +output-format = "grouped" target-version = "py37" select = [ + # bugbear rules + "B", # remove unused imports "F401", # bare except statements @@ -148,6 +151,12 @@ select = [ "T201", "T203", ] +ignore = [ + # lru_cache in methods, will be fixed separately + "B019", + # mutable defaults + "B006", +] unfixable = [ # disable auto fix for print statements "T201", diff --git a/requirements-dev.lock b/requirements-dev.lock index b1a9428a09..6df8805579 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -11,11 +11,15 @@ annotated-types==0.6.0 anyio==4.1.0 argcomplete==3.1.2 attrs==23.1.0 +azure-core==1.29.5 azure-identity==1.15.0 black==23.3.0 certifi==2023.7.22 +cffi==1.16.0 +charset-normalizer==3.3.2 click==8.1.7 colorlog==6.7.0 +cryptography==41.0.7 dirty-equals==0.6.0 distlib==0.3.7 distro==1.8.0 @@ -27,31 +31,43 @@ httpx==0.25.2 idna==3.4 iniconfig==2.0.0 isort==5.10.1 +msal==1.26.0 +msal-extensions==1.0.0 mypy==1.7.1 mypy-extensions==1.0.0 nodeenv==1.8.0 nox==2023.4.22 +numpy==1.26.2 packaging==23.2 +pandas==2.1.3 +pandas-stubs==2.1.1.230928 pathspec==0.11.2 platformdirs==3.11.0 pluggy==1.3.0 +portalocker==2.8.2 py==1.11.0 +pycparser==2.21 pydantic==2.4.2 pydantic-core==2.10.1 +pyjwt==2.8.0 pyright==1.1.332 pytest==7.1.1 pytest-asyncio==0.21.1 python-dateutil==2.8.2 pytz==2023.3.post1 +requests==2.31.0 respx==0.20.2 -ruff==0.0.282 +ruff==0.1.7 six==1.16.0 sniffio==1.3.0 time-machine==2.9.0 tomli==2.0.1 tqdm==4.66.1 +types-pytz==2023.3.1.1 types-tqdm==4.66.0.2 typing-extensions==4.8.0 +tzdata==2023.3 +urllib3==2.1.0 virtualenv==20.24.5 # The following packages are considered to be unsafe in a requirements file: setuptools==68.2.2 diff --git a/src/openai/__init__.py b/src/openai/__init__.py index d92dfe969a..d90f777cdc 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -86,7 +86,7 @@ for __name in __all__: if not __name.startswith("__"): try: - setattr(__locals[__name], "__module__", "openai") + __locals[__name].__module__ = "openai" except (TypeError, AttributeError): # Some of our exported symbols are builtins which we can't set attributes for. pass diff --git a/src/openai/_extras/numpy_proxy.py b/src/openai/_extras/numpy_proxy.py index 408eaebd3b..3809991c46 100644 --- a/src/openai/_extras/numpy_proxy.py +++ b/src/openai/_extras/numpy_proxy.py @@ -20,8 +20,8 @@ class NumpyProxy(LazyProxy[Any]): def __load__(self) -> Any: try: import numpy - except ImportError: - raise MissingDependencyError(NUMPY_INSTRUCTIONS) + except ImportError as err: + raise MissingDependencyError(NUMPY_INSTRUCTIONS) from err return numpy diff --git a/src/openai/_extras/pandas_proxy.py b/src/openai/_extras/pandas_proxy.py index 2fc0d2a7eb..a24f7fb604 100644 --- a/src/openai/_extras/pandas_proxy.py +++ b/src/openai/_extras/pandas_proxy.py @@ -20,8 +20,8 @@ class PandasProxy(LazyProxy[Any]): def __load__(self) -> Any: try: import pandas - except ImportError: - raise MissingDependencyError(PANDAS_INSTRUCTIONS) + except ImportError as err: + raise MissingDependencyError(PANDAS_INSTRUCTIONS) from err return pandas diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 095746630b..e48324fc78 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -65,7 +65,7 @@ def __stream__(self) -> Iterator[ResponseT]: yield process_data(data=data, cast_to=cast_to, response=response) # Ensure the entire stream is consumed - for sse in iterator: + for _sse in iterator: ... @@ -120,7 +120,7 @@ async def __stream__(self) -> AsyncIterator[ResponseT]: yield process_data(data=data, cast_to=cast_to, response=response) # Ensure the entire stream is consumed - async for sse in iterator: + async for _sse in iterator: ... diff --git a/src/openai/_types.py b/src/openai/_types.py index 9e962a1078..8d543171eb 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -44,6 +44,7 @@ class BinaryResponseContent(ABC): + @abstractmethod def __init__( self, response: Any, diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 83f88cc3e7..c874d3682d 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -194,8 +194,8 @@ def extract_type_arg(typ: type, index: int) -> type: args = get_args(typ) try: return cast(type, args[index]) - except IndexError: - raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") + 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 deepcopy_minimal(item: _T) -> _T: @@ -275,7 +275,9 @@ def wrapper(*args: object, **kwargs: object) -> object: try: given_params.add(positional[i]) except IndexError: - raise TypeError(f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given") + 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) diff --git a/src/openai/cli/_progress.py b/src/openai/cli/_progress.py index 390aaa9dfe..8a7f2525de 100644 --- a/src/openai/cli/_progress.py +++ b/src/openai/cli/_progress.py @@ -35,7 +35,7 @@ def read(self, n: int | None = -1) -> bytes: try: self._callback(self._progress) except Exception as e: # catches exception from the callback - raise CancelledError("The upload was cancelled: {}".format(e)) + raise CancelledError("The upload was cancelled: {}".format(e)) from e return chunk diff --git a/src/openai/cli/_tools/migrate.py b/src/openai/cli/_tools/migrate.py index 714bead8e3..14773302e1 100644 --- a/src/openai/cli/_tools/migrate.py +++ b/src/openai/cli/_tools/migrate.py @@ -41,7 +41,7 @@ def grit(args: GritArgs) -> None: except subprocess.CalledProcessError: # stdout and stderr are forwarded by subprocess so an error will already # have been displayed - raise SilentCLIError() + raise SilentCLIError() from None class MigrateArgs(BaseModel): @@ -57,7 +57,7 @@ def migrate(args: MigrateArgs) -> None: except subprocess.CalledProcessError: # stdout and stderr are forwarded by subprocess so an error will already # have been displayed - raise SilentCLIError() + raise SilentCLIError() from None # handles downloading the Grit CLI until they provide their own PyPi package diff --git a/tests/test_client.py b/tests/test_client.py index f8653507ef..c633e5eabc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -19,6 +19,7 @@ from openai._models import BaseModel, FinalRequestOptions from openai._streaming import Stream, AsyncStream from openai._exceptions import ( + OpenAIError, APIStatusError, APITimeoutError, APIConnectionError, @@ -269,7 +270,7 @@ def test_validate_headers(self) -> None: request = client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("Authorization") == f"Bearer {api_key}" - with pytest.raises(Exception): + with pytest.raises(OpenAIError): client2 = OpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 @@ -934,7 +935,7 @@ def test_validate_headers(self) -> None: request = client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("Authorization") == f"Bearer {api_key}" - with pytest.raises(Exception): + with pytest.raises(OpenAIError): client2 = AsyncOpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 50b7369e19..40b0bde10b 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -129,7 +129,7 @@ def test_azure_api_key_env_without_api_version() -> None: ValueError, match=r"Must provide either the `api_version` argument or the `OPENAI_API_VERSION` environment variable", ): - openai.completions._client + openai.completions._client # noqa: B018 def test_azure_api_key_and_version_env() -> None: @@ -142,7 +142,7 @@ def test_azure_api_key_and_version_env() -> None: ValueError, match=r"Must provide one of the `base_url` or `azure_endpoint` arguments, or the `AZURE_OPENAI_ENDPOINT` environment variable", ): - openai.completions._client + openai.completions._client # noqa: B018 def test_azure_api_key_version_and_endpoint_env() -> None: @@ -152,7 +152,7 @@ def test_azure_api_key_version_and_endpoint_env() -> None: _os.environ["OPENAI_API_VERSION"] = "example-version" _os.environ["AZURE_OPENAI_ENDPOINT"] = "https://www.example/" - openai.completions._client + openai.completions._client # noqa: B018 assert openai.api_type == "azure" diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py index 57c059150d..aedd3731ee 100644 --- a/tests/test_utils/test_proxy.py +++ b/tests/test_utils/test_proxy.py @@ -19,5 +19,5 @@ def test_recursive_proxy() -> None: assert repr(proxy) == "RecursiveLazyProxy" assert str(proxy) == "RecursiveLazyProxy" assert dir(proxy) == [] - assert getattr(type(proxy), "__name__") == "RecursiveLazyProxy" + assert type(proxy).__name__ == "RecursiveLazyProxy" assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" diff --git a/tests/utils.py b/tests/utils.py index b513794017..57486c733a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -91,7 +91,7 @@ def assert_matches_type( traceback.print_exc() continue - assert False, "Did not match any variants" + raise AssertionError("Did not match any variants") elif issubclass(origin, BaseModel): assert isinstance(value, type_) assert assert_matches_model(type_, cast(Any, value), path=path) From 478f3c083181a24d8432c085c8a16d59dee3c47d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 8 Dec 2023 00:49:21 +0000 Subject: [PATCH 241/914] docs: fix typo in example (#950) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4cabdb897d..471fd88ab1 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ from openai import AsyncOpenAI client = AsyncOpenAI() stream = await client.chat.completions.create( - prompt="Say this is a test", + model="gpt-4", messages=[{"role": "user", "content": "Say this is a test"}], stream=True, ) From d468f301a897ee1f15c5d01b245a37aa03fe0da0 Mon Sep 17 00:00:00 2001 From: Hao Cen Date: Fri, 8 Dec 2023 02:02:18 -0800 Subject: [PATCH 242/914] fix(errors): properly assign APIError.body (#949) Co-authored-by: Hao Cen --- src/openai/_exceptions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index b79ac5fd64..40b163270d 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -48,6 +48,7 @@ def __init__(self, message: str, request: httpx.Request, *, body: object | None) super().__init__(message) self.request = request self.message = message + self.body = body if is_dict(body): self.code = cast(Any, body.get("code")) From d052708a5b7f61c17d858419c625d70dbd661e3b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 8 Dec 2023 19:02:27 +0000 Subject: [PATCH 243/914] fix: avoid leaking memory when Client.with_options is used (#956) Fixes https://github.com/openai/openai-python/issues/865. --- pyproject.toml | 2 - src/openai/_base_client.py | 28 +++++---- src/openai/_client.py | 4 +- tests/test_client.py | 124 +++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8fe6a69b6c..ab49281348 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -152,8 +152,6 @@ select = [ "T203", ] ignore = [ - # lru_cache in methods, will be fixed separately - "B019", # mutable defaults "B006", ] diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 2e5678e8e6..bbbb8a54ab 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -403,14 +403,12 @@ def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers: headers_dict = _merge_mappings(self.default_headers, custom_headers) self._validate_headers(headers_dict, custom_headers) + # headers are case-insensitive while dictionaries are not. headers = httpx.Headers(headers_dict) idempotency_header = self._idempotency_header if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: - if not options.idempotency_key: - options.idempotency_key = self._idempotency_key() - - headers[idempotency_header] = options.idempotency_key + headers[idempotency_header] = options.idempotency_key or self._idempotency_key() return headers @@ -594,16 +592,8 @@ def base_url(http://github.com/self) -> URL: def base_url(http://github.com/self,%20url:%20URL%20|%20str) -> None: self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) - @lru_cache(maxsize=None) def platform_headers(self) -> Dict[str, str]: - return { - "X-Stainless-Lang": "python", - "X-Stainless-Package-Version": self._version, - "X-Stainless-OS": str(get_platform()), - "X-Stainless-Arch": str(get_architecture()), - "X-Stainless-Runtime": platform.python_implementation(), - "X-Stainless-Runtime-Version": platform.python_version(), - } + return platform_headers(self._version) def _calculate_retry_timeout( self, @@ -1691,6 +1681,18 @@ def get_platform() -> Platform: return "Unknown" +@lru_cache(maxsize=None) +def platform_headers(version: str) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": platform.python_implementation(), + "X-Stainless-Runtime-Version": platform.python_version(), + } + + class OtherArch: def __init__(self, name: str) -> None: self.name = name diff --git a/src/openai/_client.py b/src/openai/_client.py index 202162070b..8cf0fa6797 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -192,7 +192,7 @@ def copy( return self.__class__( api_key=api_key or self.api_key, organization=organization or self.organization, - base_url=base_url or str(self.base_url), + base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, @@ -402,7 +402,7 @@ def copy( return self.__class__( api_key=api_key or self.api_key, organization=organization or self.organization, - base_url=base_url or str(self.base_url), + base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, diff --git a/tests/test_client.py b/tests/test_client.py index c633e5eabc..cd374a49db 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -2,10 +2,12 @@ from __future__ import annotations +import gc import os import json import asyncio import inspect +import tracemalloc from typing import Any, Union, cast from unittest import mock @@ -195,6 +197,67 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + def test_copy_build_request(self) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client = self.client.copy() + client._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + gc.collect() + + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "openai/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "openai/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + def test_request_timeout(self) -> None: request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -858,6 +921,67 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + def test_copy_build_request(self) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client = self.client.copy() + client._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + gc.collect() + + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "openai/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "openai/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + async def test_request_timeout(self) -> None: request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore From 790df765d41f27b9a6b88ce7b8af713939f8dc22 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 8 Dec 2023 19:03:12 +0000 Subject: [PATCH 244/914] release: 1.3.8 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 24 ++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2fd8c9c83a..c2f2ae6bbd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.7" + ".": "1.3.8" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 88ff899ec3..1cb12572d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 1.3.8 (2023-12-08) + +Full Changelog: [v1.3.7...v1.3.8](https://github.com/openai/openai-python/compare/v1.3.7...v1.3.8) + +### Bug Fixes + +* avoid leaking memory when Client.with_options is used ([#956](https://github.com/openai/openai-python/issues/956)) ([e37ecca](https://github.com/openai/openai-python/commit/e37ecca04040ce946822a7e40f5604532a59ee85)) +* **errors:** properly assign APIError.body ([#949](https://github.com/openai/openai-python/issues/949)) ([c70e194](https://github.com/openai/openai-python/commit/c70e194f0a253409ec851607ae5219e3b5a8c442)) +* **pagination:** use correct type hint for .object ([#943](https://github.com/openai/openai-python/issues/943)) ([23fe7ee](https://github.com/openai/openai-python/commit/23fe7ee48a71539b0d1e95ceff349264aae4090e)) + + +### Chores + +* **internal:** enable more lint rules ([#945](https://github.com/openai/openai-python/issues/945)) ([2c8add6](https://github.com/openai/openai-python/commit/2c8add64a261dea731bd162bb0cca222518d5440)) +* **internal:** reformat imports ([#939](https://github.com/openai/openai-python/issues/939)) ([ec65124](https://github.com/openai/openai-python/commit/ec651249de2f4e4cf959f816e1b52f03d3b1017a)) +* **internal:** reformat imports ([#944](https://github.com/openai/openai-python/issues/944)) ([5290639](https://github.com/openai/openai-python/commit/52906391c9b6633656ec7934e6bbac553ec667cd)) +* **internal:** update formatting ([#941](https://github.com/openai/openai-python/issues/941)) ([8e5a156](https://github.com/openai/openai-python/commit/8e5a156d555fe68731ba0604a7455cc03cb451ce)) +* **package:** lift anyio v4 restriction ([#927](https://github.com/openai/openai-python/issues/927)) ([be0438a](https://github.com/openai/openai-python/commit/be0438a2e399bb0e0a94907229d02fc61ab479c0)) + + +### Documentation + +* fix typo in example ([#950](https://github.com/openai/openai-python/issues/950)) ([54f0ce0](https://github.com/openai/openai-python/commit/54f0ce0000abe32e97ae400f2975c028b8a84273)) + ## 1.3.7 (2023-12-01) Full Changelog: [v1.3.6...v1.3.7](https://github.com/openai/openai-python/compare/v1.3.6...v1.3.7) diff --git a/pyproject.toml b/pyproject.toml index ab49281348..fab8bf4250 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.7" +version = "1.3.8" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 3103f3b767..7c90447cbc 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.7" # x-release-please-version +__version__ = "1.3.8" # x-release-please-version From dfe1c8da2a7ab0680722811bc019a551eef1a03e Mon Sep 17 00:00:00 2001 From: Sahand Sojoodi Date: Sat, 9 Dec 2023 15:16:52 -0500 Subject: [PATCH 245/914] docs: small Improvement in the async chat response code (#959) --- README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 471fd88ab1..b7f278fe53 100644 --- a/README.md +++ b/README.md @@ -108,14 +108,17 @@ from openai import AsyncOpenAI client = AsyncOpenAI() -stream = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Say this is a test"}], - stream=True, -) -async for chunk in stream: - if chunk.choices[0].delta.content is not None: - print(chunk.choices[0].delta.content) +async def main(): + stream = await client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Say this is a test"}], + stream=True, + ) + async for chunk in stream: + if chunk.choices[0].delta.content is not None: + print(chunk.choices[0].delta.content) + +asyncio.run(main()) ``` ## Module-level client From a3da0196ffb0ea31e70bf935193caa6f92a0a22b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:01:08 +0000 Subject: [PATCH 246/914] docs: small streaming readme improvements (#962) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b7f278fe53..2e95b8f581 100644 --- a/README.md +++ b/README.md @@ -97,8 +97,7 @@ stream = client.chat.completions.create( stream=True, ) for chunk in stream: - if chunk.choices[0].delta.content is not None: - print(chunk.choices[0].delta.content) + print(chunk.choices[0].delta.content or "", end="") ``` The async client uses the exact same interface. @@ -108,6 +107,7 @@ from openai import AsyncOpenAI client = AsyncOpenAI() + async def main(): stream = await client.chat.completions.create( model="gpt-4", @@ -115,8 +115,8 @@ async def main(): stream=True, ) async for chunk in stream: - if chunk.choices[0].delta.content is not None: - print(chunk.choices[0].delta.content) + print(chunk.choices[0].delta.content or "", end="") + asyncio.run(main()) ``` From 1381f46e9ea0ee15afab42202b85edb48b1b794f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:55:22 +0000 Subject: [PATCH 247/914] docs: improve README timeout comment (#964) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e95b8f581..f89d0bdb28 100644 --- a/README.md +++ b/README.md @@ -362,7 +362,7 @@ from openai import OpenAI # Configure the default for all requests: client = OpenAI( - # default is 60s + # 20 seconds (default is 10 minutes) timeout=20.0, ) From a7ebc26085fa0bb5ceeadc8aebb72dd2dfc9d5a6 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:53:07 +0000 Subject: [PATCH 248/914] refactor(client): simplify cleanup (#966) This removes Client.__del__, but users are not expected to call this directly. --- pyproject.toml | 2 +- src/openai/__init__.py | 7 ------- src/openai/_base_client.py | 26 ++++++++++++++++++++------ src/openai/_client.py | 24 ------------------------ tests/test_client.py | 23 ++--------------------- 5 files changed, 23 insertions(+), 59 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fab8bf4250..57fe2afc6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ typecheck = { chain = [ ]} "typecheck:pyright" = "pyright" "typecheck:verify-types" = "pyright --verifytypes openai --ignoreexternal" -"typecheck:mypy" = "mypy --enable-incomplete-feature=Unpack ." +"typecheck:mypy" = "mypy ." [build-system] requires = ["hatchling"] diff --git a/src/openai/__init__.py b/src/openai/__init__.py index d90f777cdc..0d66b3c682 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -221,13 +221,6 @@ def _client(self, value: _httpx.Client) -> None: # type: ignore http_client = value - @override - def __del__(self) -> None: - try: - super().__del__() - except Exception: - pass - class _AzureModuleClient(_ModuleClient, AzureOpenAI): # type: ignore ... diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index bbbb8a54ab..04a20bfd91 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -5,6 +5,7 @@ import time import uuid import email +import asyncio import inspect import logging import platform @@ -672,9 +673,16 @@ def _idempotency_key(self) -> str: return f"stainless-python-retry-{uuid.uuid4()}" +class SyncHttpxClientWrapper(httpx.Client): + def __del__(self) -> None: + try: + self.close() + except Exception: + pass + + class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): _client: httpx.Client - _has_custom_http_client: bool _default_stream_cls: type[Stream[Any]] | None = None def __init__( @@ -747,7 +755,7 @@ def __init__( custom_headers=custom_headers, _strict_response_validation=_strict_response_validation, ) - self._client = http_client or httpx.Client( + self._client = http_client or SyncHttpxClientWrapper( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), @@ -755,7 +763,6 @@ def __init__( transport=transport, limits=limits, ) - self._has_custom_http_client = bool(http_client) def is_closed(self) -> bool: return self._client.is_closed @@ -1135,9 +1142,17 @@ def get_api_list( return self._request_api_list(model, page, opts) +class AsyncHttpxClientWrapper(httpx.AsyncClient): + def __del__(self) -> None: + try: + # TODO(someday): support non asyncio runtimes here + asyncio.get_running_loop().create_task(self.aclose()) + except Exception: + pass + + class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): _client: httpx.AsyncClient - _has_custom_http_client: bool _default_stream_cls: type[AsyncStream[Any]] | None = None def __init__( @@ -1210,7 +1225,7 @@ def __init__( custom_headers=custom_headers, _strict_response_validation=_strict_response_validation, ) - self._client = http_client or httpx.AsyncClient( + self._client = http_client or AsyncHttpxClientWrapper( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), @@ -1218,7 +1233,6 @@ def __init__( transport=transport, limits=limits, ) - self._has_custom_http_client = bool(http_client) def is_closed(self) -> bool: return self._client.is_closed diff --git a/src/openai/_client.py b/src/openai/_client.py index 8cf0fa6797..dacadf5aff 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -3,7 +3,6 @@ from __future__ import annotations import os -import asyncio from typing import Any, Union, Mapping from typing_extensions import Self, override @@ -205,16 +204,6 @@ def copy( # client.with_options(timeout=10).foo.create(...) with_options = copy - def __del__(self) -> None: - if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close"): - # this can happen if the '__init__' method raised an error - return - - if self._has_custom_http_client: - return - - self.close() - @override def _make_status_error( self, @@ -415,19 +404,6 @@ def copy( # client.with_options(timeout=10).foo.create(...) with_options = copy - def __del__(self) -> None: - if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close"): - # this can happen if the '__init__' method raised an error - return - - if self._has_custom_http_client: - return - - try: - asyncio.get_running_loop().create_task(self.close()) - except Exception: - pass - @override def _make_status_error( self, diff --git a/tests/test_client.py b/tests/test_client.py index cd374a49db..92998769d8 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -591,14 +591,6 @@ def test_absolute_request_url(http://github.com/self,%20client:%20OpenAI) -> None: ) assert request.url == "https://myapi.com/foo" - def test_client_del(self) -> None: - client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() - - client.__del__() - - assert client.is_closed() - def test_copied_client_does_not_close_http(self) -> None: client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) assert not client.is_closed() @@ -606,9 +598,8 @@ def test_copied_client_does_not_close_http(self) -> None: copied = client.copy() assert copied is not client - copied.__del__() + del copied - assert not copied.is_closed() assert not client.is_closed() def test_client_context_manager(self) -> None: @@ -1325,15 +1316,6 @@ def test_absolute_request_url(http://github.com/self,%20client:%20AsyncOpenAI) -> None: ) assert request.url == "https://myapi.com/foo" - async def test_client_del(self) -> None: - client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() - - client.__del__() - - await asyncio.sleep(0.2) - assert client.is_closed() - async def test_copied_client_does_not_close_http(self) -> None: client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) assert not client.is_closed() @@ -1341,10 +1323,9 @@ async def test_copied_client_does_not_close_http(self) -> None: copied = client.copy() assert copied is not client - copied.__del__() + del copied await asyncio.sleep(0.2) - assert not copied.is_closed() assert not client.is_closed() async def test_client_context_manager(self) -> None: From ba4f7a976247de8aa79fcd362e056ec9ce5ea4a0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 12 Dec 2023 18:17:24 -0500 Subject: [PATCH 249/914] refactor: simplify internal error handling (#968) --- src/openai/_base_client.py | 102 +++++++------- tests/test_client.py | 268 +++++++++++-------------------------- 2 files changed, 124 insertions(+), 246 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 04a20bfd91..92189617b5 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -873,40 +873,25 @@ def _request( request = self._build_request(options) self._prepare_request(request) - response = None - try: response = self._client.send( request, auth=self.custom_auth, stream=stream or self._should_stream_response_body(request=request), ) - log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase - ) - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - if retries > 0 and self._should_retry(err.response): - err.response.close() + except httpx.TimeoutException as err: + if retries > 0: return self._retry_request( options, cast_to, retries, - err.response.headers, stream=stream, stream_cls=stream_cls, + response_headers=None, ) - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - err.response.read() - - raise self._make_status_error_from_response(err.response) from None - except httpx.TimeoutException as err: - if response is not None: - response.close() - + raise APITimeoutError(request=request) from err + except Exception as err: if retries > 0: return self._retry_request( options, @@ -914,25 +899,35 @@ def _request( retries, stream=stream, stream_cls=stream_cls, - response_headers=response.headers if response is not None else None, + response_headers=None, ) - raise APITimeoutError(request=request) from err - except Exception as err: - if response is not None: - response.close() + raise APIConnectionError(request=request) from err - if retries > 0: + log.debug( + 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + if retries > 0 and self._should_retry(err.response): + err.response.close() return self._retry_request( options, cast_to, retries, + err.response.headers, stream=stream, stream_cls=stream_cls, - response_headers=response.headers if response is not None else None, ) - raise APIConnectionError(request=request) from err + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() + + raise self._make_status_error_from_response(err.response) from None return self._process_response( cast_to=cast_to, @@ -1340,40 +1335,25 @@ async def _request( request = self._build_request(options) await self._prepare_request(request) - response = None - try: response = await self._client.send( request, auth=self.custom_auth, stream=stream or self._should_stream_response_body(request=request), ) - log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase - ) - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - if retries > 0 and self._should_retry(err.response): - await err.response.aclose() + except httpx.TimeoutException as err: + if retries > 0: return await self._retry_request( options, cast_to, retries, - err.response.headers, stream=stream, stream_cls=stream_cls, + response_headers=None, ) - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - await err.response.aread() - - raise self._make_status_error_from_response(err.response) from None - except httpx.TimeoutException as err: - if response is not None: - await response.aclose() - + raise APITimeoutError(request=request) from err + except Exception as err: if retries > 0: return await self._retry_request( options, @@ -1381,25 +1361,35 @@ async def _request( retries, stream=stream, stream_cls=stream_cls, - response_headers=response.headers if response is not None else None, + response_headers=None, ) - raise APITimeoutError(request=request) from err - except Exception as err: - if response is not None: - await response.aclose() + raise APIConnectionError(request=request) from err - if retries > 0: + log.debug( + 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + if retries > 0 and self._should_retry(err.response): + await err.response.aclose() return await self._retry_request( options, cast_to, retries, + err.response.headers, stream=stream, stream_cls=stream_cls, - response_headers=response.headers if response is not None else None, ) - raise APIConnectionError(request=request) from err + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() + + raise self._make_status_error_from_response(err.response) from None return self._process_response( cast_to=cast_to, diff --git a/tests/test_client.py b/tests/test_client.py index 92998769d8..0959185df2 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -24,7 +24,6 @@ OpenAIError, APIStatusError, APITimeoutError, - APIConnectionError, APIResponseValidationError, ) from openai._base_client import ( @@ -46,14 +45,8 @@ def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: return dict(url.params) -_original_response_init = cast(Any, httpx.Response.__init__) # type: ignore - - -def _low_retry_response_init(*args: Any, **kwargs: Any) -> Any: - headers = cast("list[tuple[bytes, bytes]]", kwargs["headers"]) - headers.append((b"retry-after", b"0.1")) - - return _original_response_init(*args, **kwargs) +def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: + return 0.1 def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int: @@ -678,103 +671,51 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] - @mock.patch("httpx.Response.__init__", _low_retry_response_init) - def test_retrying_timeout_errors_doesnt_leak(self) -> None: - def raise_for_status(response: httpx.Response) -> None: - raise httpx.TimeoutException("Test timeout error", request=response.request) - - with mock.patch("httpx.Response.raise_for_status", raise_for_status): - with pytest.raises(APITimeoutError): - self.client.post( - "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", - ), - cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, - ) - - assert _get_open_connections(self.client) == 0 - - @mock.patch("httpx.Response.__init__", _low_retry_response_init) - def test_retrying_runtime_errors_doesnt_leak(self) -> None: - def raise_for_status(_response: httpx.Response) -> None: - raise RuntimeError("Test error") - - with mock.patch("httpx.Response.raise_for_status", raise_for_status): - with pytest.raises(APIConnectionError): - self.client.post( - "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", - ), - cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, - ) - - assert _get_open_connections(self.client) == 0 - - @mock.patch("httpx.Response.__init__", _low_retry_response_init) - def test_retrying_status_errors_doesnt_leak(self) -> None: - def raise_for_status(response: httpx.Response) -> None: - response.status_code = 500 - raise httpx.HTTPStatusError("Test 500 error", response=response, request=response.request) - - with mock.patch("httpx.Response.raise_for_status", raise_for_status): - with pytest.raises(APIStatusError): - self.client.post( - "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", - ), - cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, - ) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + respx_mock.post("/chat/completions").mock(side_effect=httpx.TimeoutException("Test timeout error")) + + with pytest.raises(APITimeoutError): + self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + ) assert _get_open_connections(self.client) == 0 + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None: - respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + respx_mock.post("/chat/completions").mock(return_value=httpx.Response(500)) - def on_response(response: httpx.Response) -> None: - raise httpx.HTTPStatusError( - "Simulating an error inside httpx", - response=response, - request=response.request, + with pytest.raises(APIStatusError): + self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, ) - client = OpenAI( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - http_client=httpx.Client( - event_hooks={ - "response": [on_response], - } - ), - max_retries=0, - ) - with pytest.raises(APIStatusError): - client.post("/foo", cast_to=httpx.Response) + assert _get_open_connections(self.client) == 0 class TestAsyncOpenAI: @@ -1408,101 +1349,48 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] - @mock.patch("httpx.Response.__init__", _low_retry_response_init) - async def test_retrying_timeout_errors_doesnt_leak(self) -> None: - def raise_for_status(response: httpx.Response) -> None: - raise httpx.TimeoutException("Test timeout error", request=response.request) - - with mock.patch("httpx.Response.raise_for_status", raise_for_status): - with pytest.raises(APITimeoutError): - await self.client.post( - "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", - ), - cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, - ) - - assert _get_open_connections(self.client) == 0 - - @mock.patch("httpx.Response.__init__", _low_retry_response_init) - async def test_retrying_runtime_errors_doesnt_leak(self) -> None: - def raise_for_status(_response: httpx.Response) -> None: - raise RuntimeError("Test error") - - with mock.patch("httpx.Response.raise_for_status", raise_for_status): - with pytest.raises(APIConnectionError): - await self.client.post( - "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", - ), - cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, - ) - - assert _get_open_connections(self.client) == 0 - - @mock.patch("httpx.Response.__init__", _low_retry_response_init) - async def test_retrying_status_errors_doesnt_leak(self) -> None: - def raise_for_status(response: httpx.Response) -> None: - response.status_code = 500 - raise httpx.HTTPStatusError("Test 500 error", response=response, request=response.request) - - with mock.patch("httpx.Response.raise_for_status", raise_for_status): - with pytest.raises(APIStatusError): - await self.client.post( - "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", - ), - cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, - ) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + respx_mock.post("/chat/completions").mock(side_effect=httpx.TimeoutException("Test timeout error")) + + with pytest.raises(APITimeoutError): + await self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + ) assert _get_open_connections(self.client) == 0 + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_status_error_within_httpx(self, respx_mock: MockRouter) -> None: - respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + respx_mock.post("/chat/completions").mock(return_value=httpx.Response(500)) - def on_response(response: httpx.Response) -> None: - raise httpx.HTTPStatusError( - "Simulating an error inside httpx", - response=response, - request=response.request, + with pytest.raises(APIStatusError): + await self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=httpx.Response, + options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, ) - client = AsyncOpenAI( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - http_client=httpx.AsyncClient( - event_hooks={ - "response": [on_response], - } - ), - max_retries=0, - ) - with pytest.raises(APIStatusError): - await client.post("/foo", cast_to=httpx.Response) + assert _get_open_connections(self.client) == 0 From 9e6e1a284eeb2c20c05a03831e5566a4e9eaba50 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 12 Dec 2023 18:18:06 -0500 Subject: [PATCH 250/914] release: 1.3.9 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c2f2ae6bbd..d19f910446 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.8" + ".": "1.3.9" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cb12572d1..372f3ccaa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 1.3.9 (2023-12-12) + +Full Changelog: [v1.3.8...v1.3.9](https://github.com/openai/openai-python/compare/v1.3.8...v1.3.9) + +### Documentation + +* improve README timeout comment ([#964](https://github.com/openai/openai-python/issues/964)) ([3c3ed5e](https://github.com/openai/openai-python/commit/3c3ed5edd938a9333e8d2fa47cb4b44178eef89a)) +* small Improvement in the async chat response code ([#959](https://github.com/openai/openai-python/issues/959)) ([fb9d0a3](https://github.com/openai/openai-python/commit/fb9d0a358fa232043d9d5c149b6a888d50127c7b)) +* small streaming readme improvements ([#962](https://github.com/openai/openai-python/issues/962)) ([f3be2e5](https://github.com/openai/openai-python/commit/f3be2e5cc24988471e6cedb3e34bdfd3123edc63)) + + +### Refactors + +* **client:** simplify cleanup ([#966](https://github.com/openai/openai-python/issues/966)) ([5c138f4](https://github.com/openai/openai-python/commit/5c138f4a7947e5b4aae8779fae78ca51269b355a)) +* simplify internal error handling ([#968](https://github.com/openai/openai-python/issues/968)) ([d187f6b](https://github.com/openai/openai-python/commit/d187f6b6e4e646cca39c6ca35c618aa5c1bfbd61)) + ## 1.3.8 (2023-12-08) Full Changelog: [v1.3.7...v1.3.8](https://github.com/openai/openai-python/compare/v1.3.7...v1.3.8) diff --git a/pyproject.toml b/pyproject.toml index 57fe2afc6d..99d537d22e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.8" +version = "1.3.9" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7c90447cbc..3c646d4ffe 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.8" # x-release-please-version +__version__ = "1.3.9" # x-release-please-version From d1616886b57db690dad97c046836b138b87adb45 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 14 Dec 2023 22:04:39 -0500 Subject: [PATCH 251/914] feat(api): add optional `name` argument + improve docs (#972) --- src/openai/resources/audio/speech.py | 8 +- src/openai/resources/chat/completions.py | 112 ++++++++++-------- src/openai/resources/completions.py | 24 ++-- src/openai/resources/embeddings.py | 6 +- src/openai/resources/files.py | 16 +-- .../types/audio/speech_create_params.py | 2 + ...chat_completion_assistant_message_param.py | 16 ++- ...hat_completion_content_part_image_param.py | 6 +- .../chat_completion_function_message_param.py | 5 +- ...chat_completion_named_tool_choice_param.py | 4 +- .../chat_completion_system_message_param.py | 10 +- .../chat_completion_tool_message_param.py | 3 +- .../chat_completion_user_message_param.py | 9 +- .../types/chat/completion_create_params.py | 52 ++++---- src/openai/types/completion_create_params.py | 4 +- src/openai/types/embedding_create_params.py | 3 +- .../types/shared/function_definition.py | 20 ++-- .../shared_params/function_definition.py | 20 ++-- .../beta/assistants/test_files.py | 8 +- .../beta/threads/messages/test_files.py | 24 ++-- tests/api_resources/chat/test_completions.py | 4 + 21 files changed, 205 insertions(+), 151 deletions(-) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 458843866f..aadb00bd02 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -53,7 +53,9 @@ def create( `tts-1` or `tts-1-hd` voice: The voice to use when generating the audio. Supported voices are `alloy`, - `echo`, `fable`, `onyx`, `nova`, and `shimmer`. + `echo`, `fable`, `onyx`, `nova`, and `shimmer`. Previews of the voices are + available in the + [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`. @@ -120,7 +122,9 @@ async def create( `tts-1` or `tts-1-hd` voice: The voice to use when generating the audio. Supported voices are `alloy`, - `echo`, `fable`, `onyx`, `nova`, and `shimmer`. + `echo`, `fable`, `onyx`, `nova`, and `shimmer`. Previews of the voices are + available in the + [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`. diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index d0657b2f73..db7715c5dc 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -51,11 +51,11 @@ def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ], @@ -97,7 +97,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) function_call: Deprecated in favor of `tool_choice`. @@ -130,13 +130,15 @@ def create( [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. - n: How many chat completion choices to generate for each input message. + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. @@ -146,10 +148,10 @@ def create( **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token - limit, resulting in increased latency and appearance of a "stuck" request. Also - note that the message content may be partially cut off if - `finish_reason="length"`, which indicates the generation exceeded `max_tokens` - or the conversation exceeded the max context length. + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -222,11 +224,11 @@ def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ], @@ -275,7 +277,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) function_call: Deprecated in favor of `tool_choice`. @@ -308,13 +310,15 @@ def create( [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. - n: How many chat completion choices to generate for each input message. + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. @@ -324,10 +328,10 @@ def create( **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token - limit, resulting in increased latency and appearance of a "stuck" request. Also - note that the message content may be partially cut off if - `finish_reason="length"`, which indicates the generation exceeded `max_tokens` - or the conversation exceeded the max context length. + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -393,11 +397,11 @@ def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ], @@ -446,7 +450,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) function_call: Deprecated in favor of `tool_choice`. @@ -479,13 +483,15 @@ def create( [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. - n: How many chat completion choices to generate for each input message. + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. @@ -495,10 +501,10 @@ def create( **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token - limit, resulting in increased latency and appearance of a "stuck" request. Also - note that the message content may be partially cut off if - `finish_reason="length"`, which indicates the generation exceeded `max_tokens` - or the conversation exceeded the max context length. + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -564,11 +570,11 @@ def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ], @@ -652,11 +658,11 @@ async def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ], @@ -698,7 +704,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) function_call: Deprecated in favor of `tool_choice`. @@ -731,13 +737,15 @@ async def create( [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. - n: How many chat completion choices to generate for each input message. + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. @@ -747,10 +755,10 @@ async def create( **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token - limit, resulting in increased latency and appearance of a "stuck" request. Also - note that the message content may be partially cut off if - `finish_reason="length"`, which indicates the generation exceeded `max_tokens` - or the conversation exceeded the max context length. + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -823,11 +831,11 @@ async def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ], @@ -876,7 +884,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) function_call: Deprecated in favor of `tool_choice`. @@ -909,13 +917,15 @@ async def create( [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. - n: How many chat completion choices to generate for each input message. + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. @@ -925,10 +935,10 @@ async def create( **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token - limit, resulting in increased latency and appearance of a "stuck" request. Also - note that the message content may be partially cut off if - `finish_reason="length"`, which indicates the generation exceeded `max_tokens` - or the conversation exceeded the max context length. + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -994,11 +1004,11 @@ async def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ], @@ -1047,7 +1057,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) function_call: Deprecated in favor of `tool_choice`. @@ -1080,13 +1090,15 @@ async def create( [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. - n: How many chat completion choices to generate for each input message. + n: How many chat completion choices to generate for each input message. Note that + you will be charged based on the number of generated tokens across all of the + choices. Keep `n` as `1` to minimize costs. presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. @@ -1096,10 +1108,10 @@ async def create( **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token - limit, resulting in increased latency and appearance of a "stuck" request. Also - note that the message content may be partially cut off if - `finish_reason="length"`, which indicates the generation exceeded `max_tokens` - or the conversation exceeded the max context length. + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -1165,11 +1177,11 @@ async def create( "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ], diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index baf6f04fef..93e1155a91 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -103,7 +103,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -143,7 +143,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -272,7 +272,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -312,7 +312,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -434,7 +434,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -474,7 +474,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -671,7 +671,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -711,7 +711,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -840,7 +840,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -880,7 +880,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -1002,7 +1002,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -1042,7 +1042,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index c31ad9d931..978d239774 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -51,7 +51,8 @@ def create( input: Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for - `text-embedding-ada-002`) and cannot be an empty string. + `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 + dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. @@ -144,7 +145,8 @@ async def create( input: Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for - `text-embedding-ada-002`) and cannot be an empty string. + `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 + dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index a6f75e5a4c..ed52bc3d51 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -46,12 +46,12 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileObject: - """Upload a file that can be used across various endpoints/features. + """Upload a file that can be used across various endpoints. - The size of - all the files uploaded by one organization can be up to 100 GB. + The size of all the + files uploaded by one organization can be up to 100 GB. - The size of individual files for can be a maximum of 512MB. See the + The size of individual files can be a maximum of 512 MB. See the [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to learn more about the types of files supported. The Fine-tuning API only supports `.jsonl` files. @@ -309,12 +309,12 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FileObject: - """Upload a file that can be used across various endpoints/features. + """Upload a file that can be used across various endpoints. - The size of - all the files uploaded by one organization can be up to 100 GB. + The size of all the + files uploaded by one organization can be up to 100 GB. - The size of individual files for can be a maximum of 512MB. See the + The size of individual files can be a maximum of 512 MB. See the [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to learn more about the types of files supported. The Fine-tuning API only supports `.jsonl` files. diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 06bea01746..6a302dd3c8 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -22,6 +22,8 @@ class SpeechCreateParams(TypedDict, total=False): """The voice to use when generating the audio. Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. + Previews of the voices are available in the + [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). """ response_format: Literal["mp3", "opus", "aac", "flac"] diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index abdd87c991..72a5bff83b 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -24,12 +24,15 @@ class FunctionCall(TypedDict, total=False): class ChatCompletionAssistantMessageParam(TypedDict, total=False): - content: Required[Optional[str]] - """The contents of the assistant message.""" - role: Required[Literal["assistant"]] """The role of the messages author, in this case `assistant`.""" + content: Optional[str] + """The contents of the assistant message. + + Required unless `tool_calls` or `function_call` is specified. + """ + function_call: FunctionCall """Deprecated and replaced by `tool_calls`. @@ -37,5 +40,12 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): model. """ + name: str + """An optional name for the participant. + + Provides the model information to differentiate between participants of the same + role. + """ + tool_calls: List[ChatCompletionMessageToolCallParam] """The tool calls generated by the model, such as function calls.""" diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py index eb9bd52689..e6732185ef 100644 --- a/src/openai/types/chat/chat_completion_content_part_image_param.py +++ b/src/openai/types/chat/chat_completion_content_part_image_param.py @@ -12,7 +12,11 @@ class ImageURL(TypedDict, total=False): """Either a URL of the image or the base64 encoded image data.""" detail: Literal["auto", "low", "high"] - """Specifies the detail level of the image.""" + """Specifies the detail level of the image. + + Learn more in the + [Vision guide](https://platform.openai.com/docs/guides/vision/low-or-high-fidelity-image-understanding). + """ class ChatCompletionContentPartImageParam(TypedDict, total=False): diff --git a/src/openai/types/chat/chat_completion_function_message_param.py b/src/openai/types/chat/chat_completion_function_message_param.py index 1a16c5f5eb..593571c0d2 100644 --- a/src/openai/types/chat/chat_completion_function_message_param.py +++ b/src/openai/types/chat/chat_completion_function_message_param.py @@ -2,15 +2,14 @@ from __future__ import annotations -from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ChatCompletionFunctionMessageParam"] class ChatCompletionFunctionMessageParam(TypedDict, total=False): - content: Required[Optional[str]] - """The return value from the function call, to return to the model.""" + content: Required[str] + """The contents of the function message.""" name: Required[str] """The name of the function to call.""" diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_param.py index 4c6f20d2f1..0b5ffde37b 100644 --- a/src/openai/types/chat/chat_completion_named_tool_choice_param.py +++ b/src/openai/types/chat/chat_completion_named_tool_choice_param.py @@ -13,7 +13,7 @@ class Function(TypedDict, total=False): class ChatCompletionNamedToolChoiceParam(TypedDict, total=False): - function: Function + function: Required[Function] - type: Literal["function"] + type: Required[Literal["function"]] """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/chat_completion_system_message_param.py b/src/openai/types/chat/chat_completion_system_message_param.py index ec08e00350..6e862e75c7 100644 --- a/src/openai/types/chat/chat_completion_system_message_param.py +++ b/src/openai/types/chat/chat_completion_system_message_param.py @@ -2,15 +2,21 @@ from __future__ import annotations -from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ChatCompletionSystemMessageParam"] class ChatCompletionSystemMessageParam(TypedDict, total=False): - content: Required[Optional[str]] + content: Required[str] """The contents of the system message.""" role: Required[Literal["system"]] """The role of the messages author, in this case `system`.""" + + name: str + """An optional name for the participant. + + Provides the model information to differentiate between participants of the same + role. + """ diff --git a/src/openai/types/chat/chat_completion_tool_message_param.py b/src/openai/types/chat/chat_completion_tool_message_param.py index 51759a9a99..373c5b88f4 100644 --- a/src/openai/types/chat/chat_completion_tool_message_param.py +++ b/src/openai/types/chat/chat_completion_tool_message_param.py @@ -2,14 +2,13 @@ from __future__ import annotations -from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ChatCompletionToolMessageParam"] class ChatCompletionToolMessageParam(TypedDict, total=False): - content: Required[Optional[str]] + content: Required[str] """The contents of the tool message.""" role: Required[Literal["tool"]] diff --git a/src/openai/types/chat/chat_completion_user_message_param.py b/src/openai/types/chat/chat_completion_user_message_param.py index 6f0cf34623..07be67c405 100644 --- a/src/openai/types/chat/chat_completion_user_message_param.py +++ b/src/openai/types/chat/chat_completion_user_message_param.py @@ -11,8 +11,15 @@ class ChatCompletionUserMessageParam(TypedDict, total=False): - content: Required[Union[str, List[ChatCompletionContentPartParam], None]] + content: Required[Union[str, List[ChatCompletionContentPartParam]]] """The contents of the user message.""" role: Required[Literal["user"]] """The role of the messages author, in this case `user`.""" + + name: str + """An optional name for the participant. + + Provides the model information to differentiate between participants of the same + role. + """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 69fe250eca..e8098f7b77 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -44,11 +44,11 @@ class CompletionCreateParamsBase(TypedDict, total=False): "gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613", - "gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", ], ] @@ -66,7 +66,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) """ function_call: FunctionCall @@ -109,7 +109,11 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ n: Optional[int] - """How many chat completion choices to generate for each input message.""" + """How many chat completion choices to generate for each input message. + + Note that you will be charged based on the number of generated tokens across all + of the choices. Keep `n` as `1` to minimize costs. + """ presence_penalty: Optional[float] """Number between -2.0 and 2.0. @@ -117,7 +121,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) """ response_format: ResponseFormat @@ -129,19 +133,19 @@ class CompletionCreateParamsBase(TypedDict, total=False): **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token - limit, resulting in increased latency and appearance of a "stuck" request. Also - note that the message content may be partially cut off if - `finish_reason="length"`, which indicates the generation exceeded `max_tokens` - or the conversation exceeded the max context length. + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. """ seed: Optional[int] - """This feature is in Beta. - - If specified, our system will make a best effort to sample deterministically, - such that repeated requests with the same `seed` and parameters should return - the same result. Determinism is not guaranteed, and you should refer to the - `system_fingerprint` response parameter to monitor changes in the backend. + """ + This feature is in Beta. If specified, our system will make a best effort to + sample deterministically, such that repeated requests with the same `seed` and + parameters should return the same result. Determinism is not guaranteed, and you + should refer to the `system_fingerprint` response parameter to monitor changes + in the backend. """ stop: Union[Optional[str], List[str]] @@ -204,22 +208,22 @@ class Function(TypedDict, total=False): of 64. """ - parameters: Required[shared_params.FunctionParameters] + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + parameters: shared_params.FunctionParameters """The parameters the functions accepts, described as a JSON Schema object. - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + See the + [guide](https://platform.openai.com/docs/guides/text-generation/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - description: str - """ - A description of what the function does, used by the model to choose when and - how to call the function. + Omitting `parameters` defines a function with an empty parameter list. """ diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index 3e56d4f7bf..488fe34893 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -67,7 +67,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) """ logit_bias: Optional[Dict[str, int]] @@ -119,7 +119,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) """ seed: Optional[int] diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py index bc8535f880..fd2fc5b48d 100644 --- a/src/openai/types/embedding_create_params.py +++ b/src/openai/types/embedding_create_params.py @@ -14,7 +14,8 @@ class EmbeddingCreateParams(TypedDict, total=False): To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model - (8192 tokens for `text-embedding-ada-002`) and cannot be an empty string. + (8192 tokens for `text-embedding-ada-002`), cannot be an empty string, and any + array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. """ diff --git a/src/openai/types/shared/function_definition.py b/src/openai/types/shared/function_definition.py index bfcee50c85..32658220fa 100644 --- a/src/openai/types/shared/function_definition.py +++ b/src/openai/types/shared/function_definition.py @@ -16,20 +16,20 @@ class FunctionDefinition(BaseModel): of 64. """ - parameters: FunctionParameters + description: Optional[str] = None + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + parameters: Optional[FunctionParameters] = None """The parameters the functions accepts, described as a JSON Schema object. - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + See the + [guide](https://platform.openai.com/docs/guides/text-generation/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - description: Optional[str] = None - """ - A description of what the function does, used by the model to choose when and - how to call the function. + Omitting `parameters` defines a function with an empty parameter list. """ diff --git a/src/openai/types/shared_params/function_definition.py b/src/openai/types/shared_params/function_definition.py index 6bb6fa6ff2..8e89bd41dd 100644 --- a/src/openai/types/shared_params/function_definition.py +++ b/src/openai/types/shared_params/function_definition.py @@ -17,20 +17,20 @@ class FunctionDefinition(TypedDict, total=False): of 64. """ - parameters: Required[shared_params.FunctionParameters] + description: str + """ + A description of what the function does, used by the model to choose when and + how to call the function. + """ + + parameters: shared_params.FunctionParameters """The parameters the functions accepts, described as a JSON Schema object. - See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling) + See the + [guide](https://platform.openai.com/docs/guides/text-generation/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. - To describe a function that accepts no parameters, provide the value - `{"type": "object", "properties": {}}`. - """ - - description: str - """ - A description of what the function does, used by the model to choose when and - how to call the function. + Omitting `parameters` defines a function with an empty parameter list. """ diff --git a/tests/api_resources/beta/assistants/test_files.py b/tests/api_resources/beta/assistants/test_files.py index 2545640c57..27c12e4475 100644 --- a/tests/api_resources/beta/assistants/test_files.py +++ b/tests/api_resources/beta/assistants/test_files.py @@ -24,7 +24,7 @@ class TestFiles: @parametrize def test_method_create(self, client: OpenAI) -> None: file = client.beta.assistants.files.create( - "file-AF1WoRqd3aJAHsqc9NY7iL8F", + "file-abc123", file_id="string", ) assert_matches_type(AssistantFile, file, path=["response"]) @@ -32,7 +32,7 @@ def test_method_create(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.beta.assistants.files.with_raw_response.create( - "file-AF1WoRqd3aJAHsqc9NY7iL8F", + "file-abc123", file_id="string", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -111,7 +111,7 @@ class TestAsyncFiles: @parametrize async def test_method_create(self, client: AsyncOpenAI) -> None: file = await client.beta.assistants.files.create( - "file-AF1WoRqd3aJAHsqc9NY7iL8F", + "file-abc123", file_id="string", ) assert_matches_type(AssistantFile, file, path=["response"]) @@ -119,7 +119,7 @@ async def test_method_create(self, client: AsyncOpenAI) -> None: @parametrize async def test_raw_response_create(self, client: AsyncOpenAI) -> None: response = await client.beta.assistants.files.with_raw_response.create( - "file-AF1WoRqd3aJAHsqc9NY7iL8F", + "file-abc123", file_id="string", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/beta/threads/messages/test_files.py b/tests/api_resources/beta/threads/messages/test_files.py index a5b68713e6..b97e4debd5 100644 --- a/tests/api_resources/beta/threads/messages/test_files.py +++ b/tests/api_resources/beta/threads/messages/test_files.py @@ -24,18 +24,18 @@ class TestFiles: @parametrize def test_method_retrieve(self, client: OpenAI) -> None: file = client.beta.threads.messages.files.retrieve( - "file-AF1WoRqd3aJAHsqc9NY7iL8F", - thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F", - message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F", + "file-abc123", + thread_id="thread_abc123", + message_id="msg_abc123", ) assert_matches_type(MessageFile, file, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.beta.threads.messages.files.with_raw_response.retrieve( - "file-AF1WoRqd3aJAHsqc9NY7iL8F", - thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F", - message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F", + "file-abc123", + thread_id="thread_abc123", + message_id="msg_abc123", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() @@ -80,18 +80,18 @@ class TestAsyncFiles: @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: file = await client.beta.threads.messages.files.retrieve( - "file-AF1WoRqd3aJAHsqc9NY7iL8F", - thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F", - message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F", + "file-abc123", + thread_id="thread_abc123", + message_id="msg_abc123", ) assert_matches_type(MessageFile, file, path=["response"]) @parametrize async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: response = await client.beta.threads.messages.files.with_raw_response.retrieve( - "file-AF1WoRqd3aJAHsqc9NY7iL8F", - thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F", - message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F", + "file-abc123", + thread_id="thread_abc123", + message_id="msg_abc123", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 132e00039b..0b58a4109d 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -40,6 +40,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: { "content": "string", "role": "system", + "name": "string", } ], model="gpt-3.5-turbo", @@ -128,6 +129,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: { "content": "string", "role": "system", + "name": "string", } ], model="gpt-3.5-turbo", @@ -221,6 +223,7 @@ async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenA { "content": "string", "role": "system", + "name": "string", } ], model="gpt-3.5-turbo", @@ -309,6 +312,7 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA { "content": "string", "role": "system", + "name": "string", } ], model="gpt-3.5-turbo", From 1fa164d2d86cd870526463e45c5998482e90fb1d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 14 Dec 2023 22:05:22 -0500 Subject: [PATCH 252/914] release: 1.4.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d19f910446..3e9af1b3ae 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.9" + ".": "1.4.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 372f3ccaa3..fc6366c4ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.4.0 (2023-12-15) + +Full Changelog: [v1.3.9...v1.4.0](https://github.com/openai/openai-python/compare/v1.3.9...v1.4.0) + +### Features + +* **api:** add optional `name` argument + improve docs ([#972](https://github.com/openai/openai-python/issues/972)) ([7972010](https://github.com/openai/openai-python/commit/7972010615820099f662c02821cfbd59e7d6ea44)) + ## 1.3.9 (2023-12-12) Full Changelog: [v1.3.8...v1.3.9](https://github.com/openai/openai-python/compare/v1.3.8...v1.3.9) diff --git a/pyproject.toml b/pyproject.toml index 99d537d22e..f96442aaa4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.3.9" +version = "1.4.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 3c646d4ffe..e43b6069a8 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.3.9" # x-release-please-version +__version__ = "1.4.0" # x-release-please-version From eafe8f938bd7b1a36dc2857fe8948b0cf355b159 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:39:38 -0500 Subject: [PATCH 253/914] chore(ci): run release workflow once per day (#978) --- .github/workflows/create-releases.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 7dbae006c0..c8c94db105 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -1,5 +1,7 @@ name: Create releases on: + schedule: + - cron: '0 5 * * *' # every day at 5am UTC push: branches: - main From ccae821bd90110161dd061945d4ecfb410531836 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:47:42 -0500 Subject: [PATCH 254/914] feat(api): add token logprobs to chat completions (#980) --- api.md | 1 + src/openai/resources/chat/completions.py | 122 +++++++++++++++--- src/openai/resources/completions.py | 66 +++++----- src/openai/resources/files.py | 6 +- .../runs/message_creation_step_details.py | 2 +- .../types/beta/threads/runs/run_step.py | 2 +- src/openai/types/chat/__init__.py | 3 + src/openai/types/chat/chat_completion.py | 11 +- .../types/chat/chat_completion_chunk.py | 10 ++ .../chat_completion_function_message_param.py | 3 +- .../chat/chat_completion_token_logprob.py | 47 +++++++ .../types/chat/completion_create_params.py | 23 +++- src/openai/types/completion_create_params.py | 12 +- tests/api_resources/chat/test_completions.py | 8 ++ 14 files changed, 255 insertions(+), 61 deletions(-) create mode 100644 src/openai/types/chat/chat_completion_token_logprob.py diff --git a/api.md b/api.md index a7ee177411..9d9993105b 100644 --- a/api.md +++ b/api.md @@ -38,6 +38,7 @@ from openai.types.chat import ( ChatCompletionNamedToolChoice, ChatCompletionRole, ChatCompletionSystemMessageParam, + ChatCompletionTokenLogprob, ChatCompletionTool, ChatCompletionToolChoiceOption, ChatCompletionToolMessageParam, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index db7715c5dc..5aac234227 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -63,6 +63,7 @@ def create( function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -73,6 +74,7 @@ def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -107,7 +109,7 @@ def create( particular function via `{"name": "my_function"}` forces the model to call that function. - `none` is the default when no functions are present. `auto`` is the default if + `none` is the default when no functions are present. `auto` is the default if functions are present. functions: Deprecated in favor of `tools`. @@ -123,7 +125,13 @@ def create( increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. This option is currently not available on the `gpt-4-vision-preview` + model. + + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat + completion. The total length of input tokens and generated tokens is limited by the model's context length. @@ -140,7 +148,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) - response_format: An object specifying the format that the model must output. + response_format: An object specifying the format that the model must output. Compatible with + `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -188,6 +197,10 @@ def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. + top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return + at each token position, each with an associated log probability. `logprobs` must + be set to `true` if this parameter is used. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -237,6 +250,7 @@ def create( function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -246,6 +260,7 @@ def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -287,7 +302,7 @@ def create( particular function via `{"name": "my_function"}` forces the model to call that function. - `none` is the default when no functions are present. `auto`` is the default if + `none` is the default when no functions are present. `auto` is the default if functions are present. functions: Deprecated in favor of `tools`. @@ -303,7 +318,13 @@ def create( increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. This option is currently not available on the `gpt-4-vision-preview` + model. + + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat + completion. The total length of input tokens and generated tokens is limited by the model's context length. @@ -320,7 +341,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) - response_format: An object specifying the format that the model must output. + response_format: An object specifying the format that the model must output. Compatible with + `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -361,6 +383,10 @@ def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. + top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return + at each token position, each with an associated log probability. `logprobs` must + be set to `true` if this parameter is used. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -410,6 +436,7 @@ def create( function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -419,6 +446,7 @@ def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -460,7 +488,7 @@ def create( particular function via `{"name": "my_function"}` forces the model to call that function. - `none` is the default when no functions are present. `auto`` is the default if + `none` is the default when no functions are present. `auto` is the default if functions are present. functions: Deprecated in favor of `tools`. @@ -476,7 +504,13 @@ def create( increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. This option is currently not available on the `gpt-4-vision-preview` + model. + + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat + completion. The total length of input tokens and generated tokens is limited by the model's context length. @@ -493,7 +527,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) - response_format: An object specifying the format that the model must output. + response_format: An object specifying the format that the model must output. Compatible with + `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -534,6 +569,10 @@ def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. + top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return + at each token position, each with an associated log probability. `logprobs` must + be set to `true` if this parameter is used. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -582,6 +621,7 @@ def create( function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -592,6 +632,7 @@ def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -611,6 +652,7 @@ def create( "function_call": function_call, "functions": functions, "logit_bias": logit_bias, + "logprobs": logprobs, "max_tokens": max_tokens, "n": n, "presence_penalty": presence_penalty, @@ -621,6 +663,7 @@ def create( "temperature": temperature, "tool_choice": tool_choice, "tools": tools, + "top_logprobs": top_logprobs, "top_p": top_p, "user": user, }, @@ -670,6 +713,7 @@ async def create( function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -680,6 +724,7 @@ async def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -714,7 +759,7 @@ async def create( particular function via `{"name": "my_function"}` forces the model to call that function. - `none` is the default when no functions are present. `auto`` is the default if + `none` is the default when no functions are present. `auto` is the default if functions are present. functions: Deprecated in favor of `tools`. @@ -730,7 +775,13 @@ async def create( increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. This option is currently not available on the `gpt-4-vision-preview` + model. + + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat + completion. The total length of input tokens and generated tokens is limited by the model's context length. @@ -747,7 +798,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) - response_format: An object specifying the format that the model must output. + response_format: An object specifying the format that the model must output. Compatible with + `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -795,6 +847,10 @@ async def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. + top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return + at each token position, each with an associated log probability. `logprobs` must + be set to `true` if this parameter is used. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -844,6 +900,7 @@ async def create( function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -853,6 +910,7 @@ async def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -894,7 +952,7 @@ async def create( particular function via `{"name": "my_function"}` forces the model to call that function. - `none` is the default when no functions are present. `auto`` is the default if + `none` is the default when no functions are present. `auto` is the default if functions are present. functions: Deprecated in favor of `tools`. @@ -910,7 +968,13 @@ async def create( increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. This option is currently not available on the `gpt-4-vision-preview` + model. + + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat + completion. The total length of input tokens and generated tokens is limited by the model's context length. @@ -927,7 +991,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) - response_format: An object specifying the format that the model must output. + response_format: An object specifying the format that the model must output. Compatible with + `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -968,6 +1033,10 @@ async def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. + top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return + at each token position, each with an associated log probability. `logprobs` must + be set to `true` if this parameter is used. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -1017,6 +1086,7 @@ async def create( function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -1026,6 +1096,7 @@ async def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1067,7 +1138,7 @@ async def create( particular function via `{"name": "my_function"}` forces the model to call that function. - `none` is the default when no functions are present. `auto`` is the default if + `none` is the default when no functions are present. `auto` is the default if functions are present. functions: Deprecated in favor of `tools`. @@ -1083,7 +1154,13 @@ async def create( increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the chat completion. + logprobs: Whether to return log probabilities of the output tokens or not. If true, + returns the log probabilities of each output token returned in the `content` of + `message`. This option is currently not available on the `gpt-4-vision-preview` + model. + + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat + completion. The total length of input tokens and generated tokens is limited by the model's context length. @@ -1100,7 +1177,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) - response_format: An object specifying the format that the model must output. + response_format: An object specifying the format that the model must output. Compatible with + `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1141,6 +1219,10 @@ async def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. + top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return + at each token position, each with an associated log probability. `logprobs` must + be set to `true` if this parameter is used. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the model 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. @@ -1189,6 +1271,7 @@ async def create( function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -1199,6 +1282,7 @@ async def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1218,6 +1302,7 @@ async def create( "function_call": function_call, "functions": functions, "logit_bias": logit_bias, + "logprobs": logprobs, "max_tokens": max_tokens, "n": n, "presence_penalty": presence_penalty, @@ -1228,6 +1313,7 @@ async def create( "temperature": temperature, "tool_choice": tool_choice, "tools": tools, + "top_logprobs": top_logprobs, "top_p": top_p, "user": user, }, diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 93e1155a91..d22e288054 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -119,14 +119,15 @@ def create( As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. - logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the - chosen tokens. For example, if `logprobs` is 5, the API will return a list of - the 5 most likely tokens. The API will always return the `logprob` of the - sampled token, so there may be up to `logprobs+1` elements in the response. + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the + completion. The token count of your prompt plus `max_tokens` cannot exceed the model's context length. @@ -288,14 +289,15 @@ def create( As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. - logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the - chosen tokens. For example, if `logprobs` is 5, the API will return a list of - the 5 most likely tokens. The API will always return the `logprob` of the - sampled token, so there may be up to `logprobs+1` elements in the response. + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the + completion. The token count of your prompt plus `max_tokens` cannot exceed the model's context length. @@ -450,14 +452,15 @@ def create( As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. - logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the - chosen tokens. For example, if `logprobs` is 5, the API will return a list of - the 5 most likely tokens. The API will always return the `logprob` of the - sampled token, so there may be up to `logprobs+1` elements in the response. + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the + completion. The token count of your prompt plus `max_tokens` cannot exceed the model's context length. @@ -687,14 +690,15 @@ async def create( As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. - logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the - chosen tokens. For example, if `logprobs` is 5, the API will return a list of - the 5 most likely tokens. The API will always return the `logprob` of the - sampled token, so there may be up to `logprobs+1` elements in the response. + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the + completion. The token count of your prompt plus `max_tokens` cannot exceed the model's context length. @@ -856,14 +860,15 @@ async def create( As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. - logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the - chosen tokens. For example, if `logprobs` is 5, the API will return a list of - the 5 most likely tokens. The API will always return the `logprob` of the - sampled token, so there may be up to `logprobs+1` elements in the response. + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the + completion. The token count of your prompt plus `max_tokens` cannot exceed the model's context length. @@ -1018,14 +1023,15 @@ async def create( As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. - logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the - chosen tokens. For example, if `logprobs` is 5, the API will return a list of - the 5 most likely tokens. The API will always return the `logprob` of the - sampled token, so there may be up to `logprobs+1` elements in the response. + logprobs: Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5. - max_tokens: The maximum number of [tokens](/tokenizer) to generate in the completion. + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the + completion. The token count of your prompt plus `max_tokens` cannot exceed the model's context length. diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index ed52bc3d51..e4d978d3af 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -51,7 +51,8 @@ def create( The size of all the files uploaded by one organization can be up to 100 GB. - The size of individual files can be a maximum of 512 MB. See the + The size of individual files can be a maximum of 512 MB or 2 million tokens for + Assistants. See the [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to learn more about the types of files supported. The Fine-tuning API only supports `.jsonl` files. @@ -314,7 +315,8 @@ async def create( The size of all the files uploaded by one organization can be up to 100 GB. - The size of individual files can be a maximum of 512 MB. See the + The size of individual files can be a maximum of 512 MB or 2 million tokens for + Assistants. See the [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to learn more about the types of files supported. The Fine-tuning API only supports `.jsonl` files. diff --git a/src/openai/types/beta/threads/runs/message_creation_step_details.py b/src/openai/types/beta/threads/runs/message_creation_step_details.py index 29f9106ec0..13f9398515 100644 --- a/src/openai/types/beta/threads/runs/message_creation_step_details.py +++ b/src/openai/types/beta/threads/runs/message_creation_step_details.py @@ -16,4 +16,4 @@ class MessageCreationStepDetails(BaseModel): message_creation: MessageCreation type: Literal["message_creation"] - """Always `message_creation``.""" + """Always `message_creation`.""" diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 536cf04ab1..5f8723b71a 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -66,7 +66,7 @@ class RunStep(BaseModel): """ object: Literal["thread.run.step"] - """The object type, which is always `thread.run.step``.""" + """The object type, which is always `thread.run.step`.""" run_id: str """ diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 5fe182f41e..ba21982a2b 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -13,6 +13,9 @@ from .chat_completion_message_param import ( ChatCompletionMessageParam as ChatCompletionMessageParam, ) +from .chat_completion_token_logprob import ( + ChatCompletionTokenLogprob as ChatCompletionTokenLogprob, +) from .chat_completion_message_tool_call import ( ChatCompletionMessageToolCall as ChatCompletionMessageToolCall, ) diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index da12ee7c07..055280c347 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -6,8 +6,14 @@ from ..._models import BaseModel from ..completion_usage import CompletionUsage from .chat_completion_message import ChatCompletionMessage +from .chat_completion_token_logprob import ChatCompletionTokenLogprob -__all__ = ["ChatCompletion", "Choice"] +__all__ = ["ChatCompletion", "Choice", "ChoiceLogprobs"] + + +class ChoiceLogprobs(BaseModel): + content: Optional[List[ChatCompletionTokenLogprob]] + """A list of message content tokens with log probability information.""" class Choice(BaseModel): @@ -24,6 +30,9 @@ class Choice(BaseModel): index: int """The index of the choice in the list of choices.""" + logprobs: Optional[ChoiceLogprobs] + """Log probability information for the choice.""" + message: ChatCompletionMessage """A chat completion message generated by the model.""" diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 6be046b01e..ccc7ad79ec 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from .chat_completion_token_logprob import ChatCompletionTokenLogprob __all__ = [ "ChatCompletionChunk", @@ -12,6 +13,7 @@ "ChoiceDeltaFunctionCall", "ChoiceDeltaToolCall", "ChoiceDeltaToolCallFunction", + "ChoiceLogprobs", ] @@ -70,6 +72,11 @@ class ChoiceDelta(BaseModel): tool_calls: Optional[List[ChoiceDeltaToolCall]] = None +class ChoiceLogprobs(BaseModel): + content: Optional[List[ChatCompletionTokenLogprob]] + """A list of message content tokens with log probability information.""" + + class Choice(BaseModel): delta: ChoiceDelta """A chat completion delta generated by streamed model responses.""" @@ -87,6 +94,9 @@ class Choice(BaseModel): index: int """The index of the choice in the list of choices.""" + logprobs: Optional[ChoiceLogprobs] = None + """Log probability information for the choice.""" + class ChatCompletionChunk(BaseModel): id: str diff --git a/src/openai/types/chat/chat_completion_function_message_param.py b/src/openai/types/chat/chat_completion_function_message_param.py index 593571c0d2..3f9a1a9039 100644 --- a/src/openai/types/chat/chat_completion_function_message_param.py +++ b/src/openai/types/chat/chat_completion_function_message_param.py @@ -2,13 +2,14 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ChatCompletionFunctionMessageParam"] class ChatCompletionFunctionMessageParam(TypedDict, total=False): - content: Required[str] + content: Required[Optional[str]] """The contents of the function message.""" name: Required[str] diff --git a/src/openai/types/chat/chat_completion_token_logprob.py b/src/openai/types/chat/chat_completion_token_logprob.py new file mode 100644 index 0000000000..8896da8b85 --- /dev/null +++ b/src/openai/types/chat/chat_completion_token_logprob.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["ChatCompletionTokenLogprob", "TopLogprob"] + + +class TopLogprob(BaseModel): + token: str + """The token.""" + + bytes: Optional[List[int]] + """A list of integers representing the UTF-8 bytes representation of the token. + + Useful in instances where characters are represented by multiple tokens and + their byte representations must be combined to generate the correct text + representation. Can be `null` if there is no bytes representation for the token. + """ + + logprob: float + """The log probability of this token.""" + + +class ChatCompletionTokenLogprob(BaseModel): + token: str + """The token.""" + + bytes: Optional[List[int]] + """A list of integers representing the UTF-8 bytes representation of the token. + + Useful in instances where characters are represented by multiple tokens and + their byte representations must be combined to generate the correct text + representation. Can be `null` if there is no bytes representation for the token. + """ + + logprob: float + """The log probability of this token.""" + + top_logprobs: List[TopLogprob] + """List of the most likely tokens and their log probability, at this token + position. + + In rare cases, there may be fewer than the number of requested `top_logprobs` + returned. + """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index e8098f7b77..41b71efa04 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -78,7 +78,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): particular function via `{"name": "my_function"}` forces the model to call that function. - `none` is the default when no functions are present. `auto`` is the default if + `none` is the default when no functions are present. `auto` is the default if functions are present. """ @@ -99,8 +99,18 @@ class CompletionCreateParamsBase(TypedDict, total=False): or exclusive selection of the relevant token. """ + logprobs: Optional[bool] + """Whether to return log probabilities of the output tokens or not. + + If true, returns the log probabilities of each output token returned in the + `content` of `message`. This option is currently not available on the + `gpt-4-vision-preview` model. + """ + max_tokens: Optional[int] - """The maximum number of [tokens](/tokenizer) to generate in the chat completion. + """ + The maximum number of [tokens](/tokenizer) that can be generated in the chat + completion. The total length of input tokens and generated tokens is limited by the model's context length. @@ -127,6 +137,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): response_format: ResponseFormat """An object specifying the format that the model must output. + Compatible with `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -180,6 +192,13 @@ class CompletionCreateParamsBase(TypedDict, total=False): functions the model may generate JSON inputs for. """ + top_logprobs: Optional[int] + """ + An integer between 0 and 5 specifying the number of most likely tokens to return + at each token position, each with an associated log probability. `logprobs` must + be set to `true` if this parameter is used. + """ + top_p: Optional[float] """ An alternative to sampling with temperature, called nucleus sampling, where the diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index 488fe34893..ab6609a06b 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -88,16 +88,18 @@ class CompletionCreateParamsBase(TypedDict, total=False): logprobs: Optional[int] """ - Include the log probabilities on the `logprobs` most likely tokens, as well the - chosen tokens. For example, if `logprobs` is 5, the API will return a list of - the 5 most likely tokens. The API will always return the `logprob` of the - sampled token, so there may be up to `logprobs+1` elements in the response. + Include the log probabilities on the `logprobs` most likely output tokens, as + well the chosen tokens. For example, if `logprobs` is 5, the API will return a + list of the 5 most likely tokens. The API will always return the `logprob` of + the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5. """ max_tokens: Optional[int] - """The maximum number of [tokens](/tokenizer) to generate in the completion. + """ + The maximum number of [tokens](/tokenizer) that can be generated in the + completion. The token count of your prompt plus `max_tokens` cannot exceed the model's context length. diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 0b58a4109d..985d5f1c04 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -54,6 +54,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: } ], logit_bias={"foo": 0}, + logprobs=True, max_tokens=0, n=1, presence_penalty=-2, @@ -89,6 +90,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, }, ], + top_logprobs=0, top_p=1, user="user-1234", ) @@ -144,6 +146,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: } ], logit_bias={"foo": 0}, + logprobs=True, max_tokens=0, n=1, presence_penalty=-2, @@ -178,6 +181,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, }, ], + top_logprobs=0, top_p=1, user="user-1234", ) @@ -237,6 +241,7 @@ async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenA } ], logit_bias={"foo": 0}, + logprobs=True, max_tokens=0, n=1, presence_penalty=-2, @@ -272,6 +277,7 @@ async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenA }, }, ], + top_logprobs=0, top_p=1, user="user-1234", ) @@ -327,6 +333,7 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA } ], logit_bias={"foo": 0}, + logprobs=True, max_tokens=0, n=1, presence_penalty=-2, @@ -361,6 +368,7 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA }, }, ], + top_logprobs=0, top_p=1, user="user-1234", ) From 15d3aba42ed40713ef2d2757462d19846049523a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 16 Dec 2023 19:48:23 -0500 Subject: [PATCH 255/914] release: 1.5.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3e9af1b3ae..fbd9082d71 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.4.0" + ".": "1.5.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index fc6366c4ff..757d79af62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.5.0 (2023-12-17) + +Full Changelog: [v1.4.0...v1.5.0](https://github.com/openai/openai-python/compare/v1.4.0...v1.5.0) + +### Features + +* **api:** add token logprobs to chat completions ([#980](https://github.com/openai/openai-python/issues/980)) ([f50e962](https://github.com/openai/openai-python/commit/f50e962b930bd682a4299143b2995337e8571273)) + + +### Chores + +* **ci:** run release workflow once per day ([#978](https://github.com/openai/openai-python/issues/978)) ([215476a](https://github.com/openai/openai-python/commit/215476a0b99e0c92ab3e44ddd25de207af32d160)) + ## 1.4.0 (2023-12-15) Full Changelog: [v1.3.9...v1.4.0](https://github.com/openai/openai-python/compare/v1.3.9...v1.4.0) diff --git a/pyproject.toml b/pyproject.toml index f96442aaa4..0cf709a726 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.4.0" +version = "1.5.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e43b6069a8..9dbb5b1401 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.4.0" # x-release-please-version +__version__ = "1.5.0" # x-release-please-version From 7367256070a975921ed4430f55d17dc0a9319f21 Mon Sep 17 00:00:00 2001 From: franz101 Date: Mon, 18 Dec 2023 10:57:23 -0500 Subject: [PATCH 256/914] Upgrade examples to latest version (no legacy models) (#697) * Update streaming.py Removed legacy model * Update streaming.py * more upgrades --- examples/async_demo.py | 2 +- examples/streaming.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/async_demo.py b/examples/async_demo.py index 92c267c38f..793b4e43fb 100755 --- a/examples/async_demo.py +++ b/examples/async_demo.py @@ -10,7 +10,7 @@ async def main() -> None: stream = await client.completions.create( - model="text-davinci-003", + model="gpt-3.5-turbo-instruct", prompt="Say this is a test", stream=True, ) diff --git a/examples/streaming.py b/examples/streaming.py index 168877dfc5..368fa5f911 100755 --- a/examples/streaming.py +++ b/examples/streaming.py @@ -13,7 +13,7 @@ def sync_main() -> None: client = OpenAI() response = client.completions.create( - model="text-davinci-002", + model="gpt-3.5-turbo-instruct", prompt="1,2,3,", max_tokens=5, temperature=0, @@ -33,7 +33,7 @@ def sync_main() -> None: async def async_main() -> None: client = AsyncOpenAI() response = await client.completions.create( - model="text-davinci-002", + model="gpt-3.5-turbo-instruct", prompt="1,2,3,", max_tokens=5, temperature=0, From b56cf7230a69d657274dacd65340578d3307e9ac Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:06:05 -0500 Subject: [PATCH 257/914] chore(internal): fix binary response tests (#983) --- tests/api_resources/audio/test_speech.py | 6 ++---- tests/api_resources/test_files.py | 12 ++++-------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 50b00b73b4..23f5303153 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -39,8 +39,7 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: @pytest.mark.respx(base_url=base_url) def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - speech = respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - client.audio.speech.create( + speech = client.audio.speech.create( input="string", model="string", voice="alloy", @@ -89,8 +88,7 @@ async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) @pytest.mark.respx(base_url=base_url) async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - speech = respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - await client.audio.speech.create( + speech = await client.audio.speech.create( input="string", model="string", voice="alloy", diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index e4cf493319..13ffca9773 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -95,22 +95,20 @@ def test_raw_response_delete(self, client: OpenAI) -> None: file = response.parse() assert_matches_type(FileDeleted, file, path=["response"]) - @pytest.mark.skip(reason="mocked response isn't working yet") @parametrize @pytest.mark.respx(base_url=base_url) def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None: - respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) file = client.files.content( "string", ) assert isinstance(file, BinaryResponseContent) assert file.json() == {"foo": "bar"} - @pytest.mark.skip(reason="mocked response isn't working yet") @parametrize @pytest.mark.respx(base_url=base_url) def test_raw_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None: - respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = client.files.with_raw_response.content( "string", ) @@ -212,22 +210,20 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: file = response.parse() assert_matches_type(FileDeleted, file, path=["response"]) - @pytest.mark.skip(reason="mocked response isn't working yet") @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: - respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) file = await client.files.content( "string", ) assert isinstance(file, BinaryResponseContent) assert file.json() == {"foo": "bar"} - @pytest.mark.skip(reason="mocked response isn't working yet") @parametrize @pytest.mark.respx(base_url=base_url) async def test_raw_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: - respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = await client.files.with_raw_response.content( "string", ) From af20c30c78765f4ab6f4a11d0a4093c637eeba2c Mon Sep 17 00:00:00 2001 From: Logan Kilpatrick Date: Tue, 19 Dec 2023 00:53:24 +0900 Subject: [PATCH 258/914] chore(cli): fix typo in completions (#985) --- src/openai/cli/_api/completions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/cli/_api/completions.py b/src/openai/cli/_api/completions.py index ce1036b224..cbdb35bf3a 100644 --- a/src/openai/cli/_api/completions.py +++ b/src/openai/cli/_api/completions.py @@ -57,7 +57,7 @@ def register(subparser: _SubParsersAction[ArgumentParser]) -> None: ) sub.add_argument( "--logprobs", - help="Include the log probabilites 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.", + 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( From e847023b8171f6e7fcb8535a634e5107f6672945 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:31:14 -0500 Subject: [PATCH 259/914] chore(cli): fix typo in completions (#986) --- examples/async_demo.py | 2 +- examples/streaming.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/async_demo.py b/examples/async_demo.py index 793b4e43fb..92c267c38f 100755 --- a/examples/async_demo.py +++ b/examples/async_demo.py @@ -10,7 +10,7 @@ async def main() -> None: stream = await client.completions.create( - model="gpt-3.5-turbo-instruct", + model="text-davinci-003", prompt="Say this is a test", stream=True, ) diff --git a/examples/streaming.py b/examples/streaming.py index 368fa5f911..168877dfc5 100755 --- a/examples/streaming.py +++ b/examples/streaming.py @@ -13,7 +13,7 @@ def sync_main() -> None: client = OpenAI() response = client.completions.create( - model="gpt-3.5-turbo-instruct", + model="text-davinci-002", prompt="1,2,3,", max_tokens=5, temperature=0, @@ -33,7 +33,7 @@ def sync_main() -> None: async def async_main() -> None: client = AsyncOpenAI() response = await client.completions.create( - model="gpt-3.5-turbo-instruct", + model="text-davinci-002", prompt="1,2,3,", max_tokens=5, temperature=0, From a045ef843ef218790cb704dd8acfaf4dc270f1e5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:33:10 -0500 Subject: [PATCH 260/914] docs: upgrade models in examples to latest version (#989) --- examples/async_demo.py | 2 +- examples/streaming.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/async_demo.py b/examples/async_demo.py index 92c267c38f..793b4e43fb 100755 --- a/examples/async_demo.py +++ b/examples/async_demo.py @@ -10,7 +10,7 @@ async def main() -> None: stream = await client.completions.create( - model="text-davinci-003", + model="gpt-3.5-turbo-instruct", prompt="Say this is a test", stream=True, ) diff --git a/examples/streaming.py b/examples/streaming.py index 168877dfc5..368fa5f911 100755 --- a/examples/streaming.py +++ b/examples/streaming.py @@ -13,7 +13,7 @@ def sync_main() -> None: client = OpenAI() response = client.completions.create( - model="text-davinci-002", + model="gpt-3.5-turbo-instruct", prompt="1,2,3,", max_tokens=5, temperature=0, @@ -33,7 +33,7 @@ def sync_main() -> None: async def async_main() -> None: client = AsyncOpenAI() response = await client.completions.create( - model="text-davinci-002", + model="gpt-3.5-turbo-instruct", prompt="1,2,3,", max_tokens=5, temperature=0, From 3d6ca3e7fb5fea5e987b62e398cfff9effc48382 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 07:30:56 -0500 Subject: [PATCH 261/914] chore(streaming): update constructor to use direct client names (#991) --- src/openai/_streaming.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index e48324fc78..e323c59ac0 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -12,7 +12,7 @@ from ._exceptions import APIError if TYPE_CHECKING: - from ._base_client import SyncAPIClient, AsyncAPIClient + from ._client import OpenAI, AsyncOpenAI class Stream(Generic[ResponseT]): @@ -25,7 +25,7 @@ def __init__( *, cast_to: type[ResponseT], response: httpx.Response, - client: SyncAPIClient, + client: OpenAI, ) -> None: self.response = response self._cast_to = cast_to @@ -79,7 +79,7 @@ def __init__( *, cast_to: type[ResponseT], response: httpx.Response, - client: AsyncAPIClient, + client: AsyncOpenAI, ) -> None: self.response = response self._cast_to = cast_to From 052e611a7f75a0c82eed56f88333d0cf7d599da7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:23:44 -0500 Subject: [PATCH 262/914] chore(internal): minor utils restructuring (#992) --- src/openai/_response.py | 17 +++---- src/openai/_streaming.py | 71 ++++++++++++++++++++++------- src/openai/_types.py | 14 ++++++ src/openai/_utils/__init__.py | 15 ++++--- src/openai/_utils/_streams.py | 12 +++++ src/openai/_utils/_transform.py | 5 +-- src/openai/_utils/_typing.py | 80 +++++++++++++++++++++++++++++++++ src/openai/_utils/_utils.py | 35 +-------------- 8 files changed, 183 insertions(+), 66 deletions(-) create mode 100644 src/openai/_utils/_streams.py create mode 100644 src/openai/_utils/_typing.py diff --git a/src/openai/_response.py b/src/openai/_response.py index 933c37525e..6b7c86e544 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -5,12 +5,12 @@ import datetime import functools from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast -from typing_extensions import Awaitable, ParamSpec, get_args, override, get_origin +from typing_extensions import Awaitable, ParamSpec, override, get_origin import httpx from ._types import NoneType, UnknownResponse, BinaryResponseContent -from ._utils import is_given +from ._utils import is_given, extract_type_var_from_base from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER from ._exceptions import APIResponseValidationError @@ -221,12 +221,13 @@ def __init__(self) -> None: def _extract_stream_chunk_type(stream_cls: type) -> type: - args = get_args(stream_cls) - if not args: - raise TypeError( - f"Expected stream_cls to have been given a generic type argument, e.g. Stream[Foo] but received {stream_cls}", - ) - return cast(type, args[0]) + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + ) def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index e323c59ac0..f1896a242a 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -2,12 +2,12 @@ from __future__ import annotations import json -from typing import TYPE_CHECKING, Any, Generic, Iterator, AsyncIterator -from typing_extensions import override +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing_extensions import Self, override import httpx -from ._types import ResponseT from ._utils import is_mapping from ._exceptions import APIError @@ -15,7 +15,10 @@ from ._client import OpenAI, AsyncOpenAI -class Stream(Generic[ResponseT]): +_T = TypeVar("_T") + + +class Stream(Generic[_T]): """Provides the core interface to iterate over a synchronous stream response.""" response: httpx.Response @@ -23,7 +26,7 @@ class Stream(Generic[ResponseT]): def __init__( self, *, - cast_to: type[ResponseT], + cast_to: type[_T], response: httpx.Response, client: OpenAI, ) -> None: @@ -33,18 +36,18 @@ def __init__( self._decoder = SSEDecoder() self._iterator = self.__stream__() - def __next__(self) -> ResponseT: + def __next__(self) -> _T: return self._iterator.__next__() - def __iter__(self) -> Iterator[ResponseT]: + def __iter__(self) -> Iterator[_T]: for item in self._iterator: yield item def _iter_events(self) -> Iterator[ServerSentEvent]: yield from self._decoder.iter(self.response.iter_lines()) - def __stream__(self) -> Iterator[ResponseT]: - cast_to = self._cast_to + def __stream__(self) -> Iterator[_T]: + cast_to = cast(Any, self._cast_to) response = self.response process_data = self._client._process_response_data iterator = self._iter_events() @@ -68,8 +71,27 @@ def __stream__(self) -> Iterator[ResponseT]: for _sse in iterator: ... + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.response.close() -class AsyncStream(Generic[ResponseT]): + +class AsyncStream(Generic[_T]): """Provides the core interface to iterate over an asynchronous stream response.""" response: httpx.Response @@ -77,7 +99,7 @@ class AsyncStream(Generic[ResponseT]): def __init__( self, *, - cast_to: type[ResponseT], + cast_to: type[_T], response: httpx.Response, client: AsyncOpenAI, ) -> None: @@ -87,10 +109,10 @@ def __init__( self._decoder = SSEDecoder() self._iterator = self.__stream__() - async def __anext__(self) -> ResponseT: + async def __anext__(self) -> _T: return await self._iterator.__anext__() - async def __aiter__(self) -> AsyncIterator[ResponseT]: + async def __aiter__(self) -> AsyncIterator[_T]: async for item in self._iterator: yield item @@ -98,8 +120,8 @@ async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: async for sse in self._decoder.aiter(self.response.aiter_lines()): yield sse - async def __stream__(self) -> AsyncIterator[ResponseT]: - cast_to = self._cast_to + async def __stream__(self) -> AsyncIterator[_T]: + cast_to = cast(Any, self._cast_to) response = self.response process_data = self._client._process_response_data iterator = self._iter_events() @@ -123,6 +145,25 @@ async def __stream__(self) -> AsyncIterator[ResponseT]: async for _sse in iterator: ... + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.response.aclose() + class ServerSentEvent: def __init__( diff --git a/src/openai/_types.py b/src/openai/_types.py index 8d543171eb..a20a4b4c1b 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -353,3 +353,17 @@ def get(self, __key: str) -> str | None: IncEx: TypeAlias = "set[int] | set[str] | dict[int, Any] | dict[str, Any] | None" PostParser = Callable[[Any], Any] + + +@runtime_checkable +class InheritsGeneric(Protocol): + """Represents a type that has inherited from `Generic` + The `__orig_bases__` property can be used to determine the resolved + type variable for a given base class. + """ + + __orig_bases__: tuple[_GenericAlias] + + +class _GenericAlias(Protocol): + __origin__: type[object] diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 400ca9b828..a43201d3c7 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -9,13 +9,11 @@ from ._utils import parse_date as parse_date from ._utils import is_sequence as is_sequence from ._utils import coerce_float as coerce_float -from ._utils import is_list_type as is_list_type from ._utils import is_mapping_t as is_mapping_t from ._utils import removeprefix as removeprefix from ._utils import removesuffix as removesuffix from ._utils import extract_files as extract_files from ._utils import is_sequence_t as is_sequence_t -from ._utils import is_union_type as is_union_type from ._utils import required_args as required_args from ._utils import coerce_boolean as coerce_boolean from ._utils import coerce_integer as coerce_integer @@ -23,15 +21,20 @@ from ._utils import parse_datetime as parse_datetime from ._utils import strip_not_given as strip_not_given from ._utils import deepcopy_minimal as deepcopy_minimal -from ._utils import extract_type_arg as extract_type_arg -from ._utils import is_required_type as is_required_type from ._utils import get_async_library as get_async_library -from ._utils import is_annotated_type as is_annotated_type from ._utils import maybe_coerce_float as maybe_coerce_float from ._utils import get_required_header as get_required_header from ._utils import maybe_coerce_boolean as maybe_coerce_boolean from ._utils import maybe_coerce_integer as maybe_coerce_integer -from ._utils import strip_annotated_type as strip_annotated_type +from ._typing import is_list_type as is_list_type +from ._typing import is_union_type as is_union_type +from ._typing import extract_type_arg as extract_type_arg +from ._typing import is_required_type as is_required_type +from ._typing import is_annotated_type as is_annotated_type +from ._typing import strip_annotated_type as strip_annotated_type +from ._typing import extract_type_var_from_base as extract_type_var_from_base +from ._streams import consume_sync_iterator as consume_sync_iterator +from ._streams import consume_async_iterator as consume_async_iterator from ._transform import PropertyInfo as PropertyInfo from ._transform import transform as transform from ._transform import maybe_transform as maybe_transform diff --git a/src/openai/_utils/_streams.py b/src/openai/_utils/_streams.py new file mode 100644 index 0000000000..f4a0208f01 --- /dev/null +++ b/src/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/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 769f7362b9..9117559064 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -6,9 +6,8 @@ import pydantic -from ._utils import ( - is_list, - is_mapping, +from ._utils import is_list, is_mapping +from ._typing import ( is_list_type, is_union_type, extract_type_arg, diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py new file mode 100644 index 0000000000..b5e2c2e397 --- /dev/null +++ b/src/openai/_utils/_typing.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +from typing import Any, cast +from typing_extensions import 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_union_type(typ: type) -> bool: + return _is_union(get_origin(typ)) + + +def is_required_type(typ: type) -> bool: + return get_origin(typ) == Required + + +# 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) -> 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 + ``` + """ + 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} ?" + ) + + return extract_type_arg(target_base_class, index) + + raise RuntimeError(f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index c874d3682d..993462a66b 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -16,12 +16,11 @@ overload, ) from pathlib import Path -from typing_extensions import Required, Annotated, TypeGuard, get_args, get_origin +from typing_extensions import TypeGuard import sniffio from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import is_union as _is_union from .._compat import parse_date as parse_date from .._compat import parse_datetime as parse_datetime @@ -166,38 +165,6 @@ def is_list(obj: object) -> TypeGuard[list[object]]: return isinstance(obj, list) -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_union_type(typ: type) -> bool: - return _is_union(get_origin(typ)) - - -def is_required_type(typ: type) -> bool: - return get_origin(typ) == Required - - -# 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 deepcopy_minimal(item: _T) -> _T: """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: From 00ef2f47d1492ecfcff2243cf2c054053dd62d04 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 13:22:14 -0500 Subject: [PATCH 263/914] chore(internal): fix typos (#993) --- src/openai/_base_client.py | 2 +- src/openai/_streaming.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 92189617b5..481171a447 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -107,7 +107,7 @@ class PageInfo: - """Stores the necesary information to build the request to retrieve the next page. + """Stores the necessary information to build the request to retrieve the next page. Either `url` or `params` must be set. """ diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index f1896a242a..85cec70c11 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -60,7 +60,7 @@ def __stream__(self) -> Iterator[_T]: data = sse.json() if is_mapping(data) and data.get("error"): raise APIError( - message="An error ocurred during streaming", + message="An error occurred during streaming", request=self.response.request, body=data["error"], ) @@ -134,7 +134,7 @@ async def __stream__(self) -> AsyncIterator[_T]: data = sse.json() if is_mapping(data) and data.get("error"): raise APIError( - message="An error ocurred during streaming", + message="An error occurred during streaming", request=self.response.request, body=data["error"], ) From a893a19f82caef5ed7078a1bcf1e1b954e8e72b4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:05:05 -0500 Subject: [PATCH 264/914] chore(package): bump minimum typing-extensions to 4.7 (#994) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0cf709a726..24498b18fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.5, <5", + "typing-extensions>=4.7, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", From 53bca49e184091c4d71eeee86aacf7e974ff6c46 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 18:17:29 -0500 Subject: [PATCH 265/914] feat(api): add additional instructions for runs (#995) --- .../resources/beta/threads/runs/runs.py | 22 +++++++++++++++---- .../types/beta/threads/run_create_params.py | 14 +++++++++--- tests/api_resources/beta/threads/test_runs.py | 2 ++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 969bfab70a..aea3b8cefc 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -42,6 +42,7 @@ def create( thread_id: str, *, assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -61,8 +62,13 @@ def create( [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to execute this run. - instructions: Override the default system message of the assistant. This is useful for - modifying the behavior on a per-run basis. + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + instructions: Overrides the + [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) + of the assistant. This is useful for modifying the behavior on a per-run basis. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys @@ -91,6 +97,7 @@ def create( body=maybe_transform( { "assistant_id": assistant_id, + "additional_instructions": additional_instructions, "instructions": instructions, "metadata": metadata, "model": model, @@ -332,6 +339,7 @@ async def create( thread_id: str, *, assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -351,8 +359,13 @@ async def create( [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to execute this run. - instructions: Override the default system message of the assistant. This is useful for - modifying the behavior on a per-run basis. + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + instructions: Overrides the + [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) + of the assistant. This is useful for modifying the behavior on a per-run basis. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys @@ -381,6 +394,7 @@ async def create( body=maybe_transform( { "assistant_id": assistant_id, + "additional_instructions": additional_instructions, "instructions": instructions, "metadata": metadata, "model": model, diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index df92f4fd2c..a4f41a9338 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -24,10 +24,18 @@ class RunCreateParams(TypedDict, total=False): execute this run. """ - instructions: Optional[str] - """Override the default system message of the assistant. + additional_instructions: Optional[str] + """Appends additional instructions at the end of the instructions for the run. - This is useful for modifying the behavior on a per-run basis. + This is useful for modifying the behavior on a per-run basis without overriding + other instructions. + """ + + instructions: Optional[str] + """ + Overrides the + [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) + of the assistant. This is useful for modifying the behavior on a per-run basis. """ metadata: Optional[object] diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index d323dfc354..494cae2656 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -34,6 +34,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: run = client.beta.threads.runs.create( "string", assistant_id="string", + additional_instructions="string", instructions="string", metadata={}, model="string", @@ -180,6 +181,7 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.create( "string", assistant_id="string", + additional_instructions="string", instructions="string", metadata={}, model="string", From 3ad4e8bc9d89d7a81586bf598289ff62b0a339b9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 18:18:08 -0500 Subject: [PATCH 266/914] release: 1.6.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 24 ++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fbd9082d71..7deae33804 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.5.0" + ".": "1.6.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 757d79af62..399e3aaebd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 1.6.0 (2023-12-19) + +Full Changelog: [v1.5.0...v1.6.0](https://github.com/openai/openai-python/compare/v1.5.0...v1.6.0) + +### Features + +* **api:** add additional instructions for runs ([#995](https://github.com/openai/openai-python/issues/995)) ([7bf9b75](https://github.com/openai/openai-python/commit/7bf9b75067905449e83e828c12eb384022cff6ca)) + + +### Chores + +* **cli:** fix typo in completions ([#985](https://github.com/openai/openai-python/issues/985)) ([d1e9e8f](https://github.com/openai/openai-python/commit/d1e9e8f24df366bb7b796c55a98247c025d229f5)) +* **cli:** fix typo in completions ([#986](https://github.com/openai/openai-python/issues/986)) ([626bc34](https://github.com/openai/openai-python/commit/626bc34d82a7057bac99f8b556f9e5f60c261ee7)) +* **internal:** fix binary response tests ([#983](https://github.com/openai/openai-python/issues/983)) ([cfb7e30](https://github.com/openai/openai-python/commit/cfb7e308393f2e912e959dd10d68096dd5b3ab9c)) +* **internal:** fix typos ([#993](https://github.com/openai/openai-python/issues/993)) ([3b338a4](https://github.com/openai/openai-python/commit/3b338a401b206618774291ff8137deb0cc5f6b4c)) +* **internal:** minor utils restructuring ([#992](https://github.com/openai/openai-python/issues/992)) ([5ba576a](https://github.com/openai/openai-python/commit/5ba576ae38d2c4c4d32a21933e0d68e0bc2f0d49)) +* **package:** bump minimum typing-extensions to 4.7 ([#994](https://github.com/openai/openai-python/issues/994)) ([0c2da84](https://github.com/openai/openai-python/commit/0c2da84badf416f8b2213983f68bd2b6f9e52f2b)) +* **streaming:** update constructor to use direct client names ([#991](https://github.com/openai/openai-python/issues/991)) ([6c3427d](https://github.com/openai/openai-python/commit/6c3427dac8c414658516aeb4caf5d5fd8b11097b)) + + +### Documentation + +* upgrade models in examples to latest version ([#989](https://github.com/openai/openai-python/issues/989)) ([cedd574](https://github.com/openai/openai-python/commit/cedd574e5611f3e71e92b523a72ba87bcfe546f1)) + ## 1.5.0 (2023-12-17) Full Changelog: [v1.4.0...v1.5.0](https://github.com/openai/openai-python/compare/v1.4.0...v1.5.0) diff --git a/pyproject.toml b/pyproject.toml index 24498b18fe..91d3d79219 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.5.0" +version = "1.6.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9dbb5b1401..9b01b6fcb1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.5.0" # x-release-please-version +__version__ = "1.6.0" # x-release-please-version From d58c2ef8606264e7b69cebcdfd379c77210f8975 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 21 Dec 2023 07:40:30 -0500 Subject: [PATCH 267/914] chore(internal): add bin script (#1001) --- bin/check-env-state.py | 40 ++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + requirements-dev.lock | 10 ++++++---- 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 bin/check-env-state.py diff --git a/bin/check-env-state.py b/bin/check-env-state.py new file mode 100644 index 0000000000..e1b8b6cb39 --- /dev/null +++ b/bin/check-env-state.py @@ -0,0 +1,40 @@ +"""Script that exits 1 if the current environment is not +in sync with the `requirements-dev.lock` file. +""" + +from pathlib import Path + +import importlib_metadata + + +def should_run_sync() -> bool: + dev_lock = Path(__file__).parent.parent.joinpath("requirements-dev.lock") + + for line in dev_lock.read_text().splitlines(): + if not line or line.startswith("#") or line.startswith("-e"): + continue + + dep, lock_version = line.split("==") + + try: + version = importlib_metadata.version(dep) + + if lock_version != version: + print(f"mismatch for {dep} current={version} lock={lock_version}") + return True + except Exception: + print(f"could not import {dep}") + return True + + return False + + +def main() -> None: + if should_run_sync(): + exit(1) + else: + exit(0) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 91d3d79219..a9860b29ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,7 @@ dev-dependencies = [ "time-machine", "nox", "dirty-equals>=0.6.0", + "importlib-metadata>=6.7.0", "azure-identity >=1.14.1", "types-tqdm > 4" ] diff --git a/requirements-dev.lock b/requirements-dev.lock index 6df8805579..3e480ada33 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -11,7 +11,7 @@ annotated-types==0.6.0 anyio==4.1.0 argcomplete==3.1.2 attrs==23.1.0 -azure-core==1.29.5 +azure-core==1.29.6 azure-identity==1.15.0 black==23.3.0 certifi==2023.7.22 @@ -29,18 +29,19 @@ h11==0.14.0 httpcore==1.0.2 httpx==0.25.2 idna==3.4 +importlib-metadata==7.0.0 iniconfig==2.0.0 isort==5.10.1 msal==1.26.0 -msal-extensions==1.0.0 +msal-extensions==1.1.0 mypy==1.7.1 mypy-extensions==1.0.0 nodeenv==1.8.0 nox==2023.4.22 numpy==1.26.2 packaging==23.2 -pandas==2.1.3 -pandas-stubs==2.1.1.230928 +pandas==2.1.4 +pandas-stubs==2.1.4.231218 pathspec==0.11.2 platformdirs==3.11.0 pluggy==1.3.0 @@ -69,5 +70,6 @@ typing-extensions==4.8.0 tzdata==2023.3 urllib3==2.1.0 virtualenv==20.24.5 +zipp==3.17.0 # The following packages are considered to be unsafe in a requirements file: setuptools==68.2.2 From 819ae68d990dc5df68921257213971caf1afb369 Mon Sep 17 00:00:00 2001 From: JackYu Date: Thu, 21 Dec 2023 22:52:19 +0800 Subject: [PATCH 268/914] Fix missing comma in README code example (#1000) The previous version of the README contained a code example with a missing comma, which could lead to syntax errors. This commit corrects that issue by adding the missing comma. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f89d0bdb28..f644cdeefe 100644 --- a/README.md +++ b/README.md @@ -475,7 +475,7 @@ from openai import AzureOpenAI # gets the API Key from environment variable AZURE_OPENAI_API_KEY client = AzureOpenAI( # https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning - api_version="2023-07-01-preview" + api_version="2023-07-01-preview", # https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource azure_endpoint="https://example-endpoint.openai.azure.com/", ) From ce04ec285c86ff91b584a0105d8559a3f94b282f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 21 Dec 2023 22:48:12 -0500 Subject: [PATCH 269/914] test: run the garbage collector less often (#1003) --- tests/test_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 0959185df2..ffa779fb38 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -208,8 +208,8 @@ def build_request(options: FinalRequestOptions) -> None: ITERATIONS = 10 for _ in range(ITERATIONS): build_request(options) - gc.collect() + gc.collect() snapshot_after = tracemalloc.take_snapshot() tracemalloc.stop() @@ -871,8 +871,8 @@ def build_request(options: FinalRequestOptions) -> None: ITERATIONS = 10 for _ in range(ITERATIONS): build_request(options) - gc.collect() + gc.collect() snapshot_after = tracemalloc.take_snapshot() tracemalloc.stop() From e1b60b22915c34464faeca63e6034524d50a70d7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 22 Dec 2023 05:45:53 -0500 Subject: [PATCH 270/914] chore(internal): use ruff instead of black for formatting (#1008) --- bin/{blacken-docs.py => ruffen-docs.py} | 130 +++++------------------- pyproject.toml | 12 ++- requirements-dev.lock | 5 +- src/openai/_models.py | 2 +- src/openai/_types.py | 16 +-- src/openai/_utils/_transform.py | 5 +- src/openai/_utils/_utils.py | 4 +- tests/test_transform.py | 4 +- 8 files changed, 50 insertions(+), 128 deletions(-) rename bin/{blacken-docs.py => ruffen-docs.py} (52%) diff --git a/bin/blacken-docs.py b/bin/ruffen-docs.py similarity index 52% rename from bin/blacken-docs.py rename to bin/ruffen-docs.py index 45d0ad1225..37b3d94f0f 100644 --- a/bin/blacken-docs.py +++ b/bin/ruffen-docs.py @@ -1,16 +1,14 @@ -# fork of https://github.com/asottile/blacken-docs implementing https://github.com/asottile/blacken-docs/issues/170 +# fork of https://github.com/asottile/blacken-docs adapted for ruff from __future__ import annotations import re +import sys import argparse import textwrap import contextlib +import subprocess from typing import Match, Optional, Sequence, Generator, NamedTuple, cast -import black -from black.mode import TargetVersion -from black.const import DEFAULT_LINE_LENGTH - MD_RE = re.compile( r"(?P^(?P *)```\s*python\n)" r"(?P.*?)" r"(?P^(?P=indent)```\s*$)", re.DOTALL | re.MULTILINE, @@ -19,55 +17,12 @@ r"(?P^(?P *)```\s*pycon\n)" r"(?P.*?)" r"(?P^(?P=indent)```.*$)", re.DOTALL | re.MULTILINE, ) -RST_PY_LANGS = frozenset(("python", "py", "sage", "python3", "py3", "numpy")) -BLOCK_TYPES = "(code|code-block|sourcecode|ipython)" -DOCTEST_TYPES = "(testsetup|testcleanup|testcode)" -RST_RE = re.compile( - rf"(?P" - rf"^(?P *)\.\. (" - rf"jupyter-execute::|" - rf"{BLOCK_TYPES}:: (?P\w+)|" - rf"{DOCTEST_TYPES}::.*" - rf")\n" - rf"((?P=indent) +:.*\n)*" - rf"\n*" - rf")" - rf"(?P(^((?P=indent) +.*)?\n)+)", - re.MULTILINE, -) -RST_PYCON_RE = re.compile( - r"(?P" - r"(?P *)\.\. ((code|code-block):: pycon|doctest::.*)\n" - r"((?P=indent) +:.*\n)*" - r"\n*" - r")" - r"(?P(^((?P=indent) +.*)?(\n|$))+)", - re.MULTILINE, -) PYCON_PREFIX = ">>> " PYCON_CONTINUATION_PREFIX = "..." PYCON_CONTINUATION_RE = re.compile( rf"^{re.escape(PYCON_CONTINUATION_PREFIX)}( |$)", ) -LATEX_RE = re.compile( - r"(?P^(?P *)\\begin{minted}{python}\n)" - r"(?P.*?)" - r"(?P^(?P=indent)\\end{minted}\s*$)", - re.DOTALL | re.MULTILINE, -) -LATEX_PYCON_RE = re.compile( - r"(?P^(?P *)\\begin{minted}{pycon}\n)" r"(?P.*?)" r"(?P^(?P=indent)\\end{minted}\s*$)", - re.DOTALL | re.MULTILINE, -) -PYTHONTEX_LANG = r"(?Ppyblock|pycode|pyconsole|pyverbatim)" -PYTHONTEX_RE = re.compile( - rf"(?P^(?P *)\\begin{{{PYTHONTEX_LANG}}}\n)" - rf"(?P.*?)" - rf"(?P^(?P=indent)\\end{{(?P=lang)}}\s*$)", - re.DOTALL | re.MULTILINE, -) -INDENT_RE = re.compile("^ +(?=[^ ])", re.MULTILINE) -TRAILING_NL_RE = re.compile(r"\n+\Z", re.MULTILINE) +DEFAULT_LINE_LENGTH = 100 class CodeBlockError(NamedTuple): @@ -77,7 +32,6 @@ class CodeBlockError(NamedTuple): def format_str( src: str, - black_mode: black.FileMode, ) -> tuple[str, Sequence[CodeBlockError]]: errors: list[CodeBlockError] = [] @@ -91,24 +45,10 @@ def _collect_error(match: Match[str]) -> Generator[None, None, None]: def _md_match(match: Match[str]) -> str: code = textwrap.dedent(match["code"]) with _collect_error(match): - code = black.format_str(code, mode=black_mode) + code = format_code_block(code) code = textwrap.indent(code, match["indent"]) return f'{match["before"]}{code}{match["after"]}' - def _rst_match(match: Match[str]) -> str: - lang = match["lang"] - if lang is not None and lang not in RST_PY_LANGS: - return match[0] - min_indent = min(INDENT_RE.findall(match["code"])) - trailing_ws_match = TRAILING_NL_RE.search(match["code"]) - assert trailing_ws_match - trailing_ws = trailing_ws_match.group() - code = textwrap.dedent(match["code"]) - with _collect_error(match): - code = black.format_str(code, mode=black_mode) - code = textwrap.indent(code, min_indent) - return f'{match["before"]}{code.rstrip()}{trailing_ws}' - def _pycon_match(match: Match[str]) -> str: code = "" fragment = cast(Optional[str], None) @@ -119,7 +59,7 @@ def finish_fragment() -> None: if fragment is not None: with _collect_error(match): - fragment = black.format_str(fragment, mode=black_mode) + fragment = format_code_block(fragment) fragment_lines = fragment.splitlines() code += f"{PYCON_PREFIX}{fragment_lines[0]}\n" for line in fragment_lines[1:]: @@ -159,42 +99,33 @@ def _md_pycon_match(match: Match[str]) -> str: code = textwrap.indent(code, match["indent"]) return f'{match["before"]}{code}{match["after"]}' - def _rst_pycon_match(match: Match[str]) -> str: - code = _pycon_match(match) - min_indent = min(INDENT_RE.findall(match["code"])) - code = textwrap.indent(code, min_indent) - return f'{match["before"]}{code}' - - def _latex_match(match: Match[str]) -> str: - code = textwrap.dedent(match["code"]) - with _collect_error(match): - code = black.format_str(code, mode=black_mode) - code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' - - def _latex_pycon_match(match: Match[str]) -> str: - code = _pycon_match(match) - code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' - src = MD_RE.sub(_md_match, src) src = MD_PYCON_RE.sub(_md_pycon_match, src) - src = RST_RE.sub(_rst_match, src) - src = RST_PYCON_RE.sub(_rst_pycon_match, src) - src = LATEX_RE.sub(_latex_match, src) - src = LATEX_PYCON_RE.sub(_latex_pycon_match, src) - src = PYTHONTEX_RE.sub(_latex_match, src) return src, errors +def format_code_block(code: str) -> str: + return subprocess.check_output( + [ + sys.executable, + "-m", + "ruff", + "format", + "--stdin-filename=script.py", + f"--line-length={DEFAULT_LINE_LENGTH}", + ], + encoding="utf-8", + input=code, + ) + + def format_file( filename: str, - black_mode: black.FileMode, skip_errors: bool, ) -> int: with open(filename, encoding="UTF-8") as f: contents = f.read() - new_contents, errors = format_str(contents, black_mode) + new_contents, errors = format_str(contents) for error in errors: lineno = contents[: error.offset].count("\n") + 1 print(f"{filename}:{lineno}: code block parse error {error.exc}") @@ -217,15 +148,6 @@ def main(argv: Sequence[str] | None = None) -> int: type=int, default=DEFAULT_LINE_LENGTH, ) - parser.add_argument( - "-t", - "--target-version", - action="http://github.com/append", - type=lambda v: TargetVersion[v.upper()], - default=[], - help=f"choices: {[v.name.lower() for v in TargetVersion]}", - dest="target_versions", - ) parser.add_argument( "-S", "--skip-string-normalization", @@ -235,15 +157,9 @@ def main(argv: Sequence[str] | None = None) -> int: parser.add_argument("filenames", nargs="*") args = parser.parse_args(argv) - black_mode = black.FileMode( - target_versions=set(args.target_versions), - line_length=args.line_length, - string_normalization=not args.skip_string_normalization, - ) - retv = 0 for filename in args.filenames: - retv |= format_file(filename, black_mode, skip_errors=args.skip_errors) + retv |= format_file(filename, skip_errors=args.skip_errors) return retv diff --git a/pyproject.toml b/pyproject.toml index a9860b29ef..f1d1a66fef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,6 @@ managed = true dev-dependencies = [ "pyright", "mypy", - "black", "respx", "pytest", "pytest-asyncio", @@ -67,17 +66,18 @@ dev-dependencies = [ [tool.rye.scripts] format = { chain = [ - "format:black", - "format:docs", "format:ruff", + "format:docs", + "fix:ruff", "format:isort", ]} "format:black" = "black ." -"format:docs" = "python bin/blacken-docs.py README.md api.md" -"format:ruff" = "ruff --fix ." +"format:docs" = "python bin/ruffen-docs.py README.md api.md" +"format:ruff" = "ruff format" "format:isort" = "isort ." "check:ruff" = "ruff ." +"fix:ruff" = "ruff --fix ." typecheck = { chain = [ "typecheck:pyright", @@ -163,6 +163,8 @@ unfixable = [ ] ignore-init-module-imports = true +[tool.ruff.format] +docstring-code-format = true [tool.ruff.per-file-ignores] "bin/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 3e480ada33..53763f2aa9 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -13,11 +13,9 @@ argcomplete==3.1.2 attrs==23.1.0 azure-core==1.29.6 azure-identity==1.15.0 -black==23.3.0 certifi==2023.7.22 cffi==1.16.0 charset-normalizer==3.3.2 -click==8.1.7 colorlog==6.7.0 cryptography==41.0.7 dirty-equals==0.6.0 @@ -42,7 +40,6 @@ numpy==1.26.2 packaging==23.2 pandas==2.1.4 pandas-stubs==2.1.4.231218 -pathspec==0.11.2 platformdirs==3.11.0 pluggy==1.3.0 portalocker==2.8.2 @@ -58,7 +55,7 @@ python-dateutil==2.8.2 pytz==2023.3.post1 requests==2.31.0 respx==0.20.2 -ruff==0.1.7 +ruff==0.1.9 six==1.16.0 sniffio==1.3.0 time-machine==2.9.0 diff --git a/src/openai/_models.py b/src/openai/_models.py index 5b8c96010f..330a2064d8 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -382,7 +382,7 @@ class RootModel(GenericModel, Generic[_T]): For example: ```py - validated = RootModel[int](__root__='5').__root__ + validated = RootModel[int](__root__="5").__root__ # validated: 5 ``` """ diff --git a/src/openai/_types.py b/src/openai/_types.py index a20a4b4c1b..fc26d5458a 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -278,11 +278,13 @@ class NotGiven: For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: + ... + - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. + get(timeout=1) # 1s timeout + get(timeout=None) # No timeout + get() # Default timeout behavior, which may not be statically known at the method definition. ``` """ @@ -304,14 +306,14 @@ class Omit: ```py # as the default `Content-Type` header is `application/json` that will be sent - client.post('/upload/files', files={'file': b'my raw file content'}) + client.post("/upload/files", files={"file": b"my raw file content"}) # you can't explicitly override the header as it has to be dynamically generated # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' - client.post(..., headers={'Content-Type': 'multipart/form-data'}) + client.post(..., headers={"Content-Type": "multipart/form-data"}) # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={'Content-Type': Omit()}) + client.post(..., headers={"Content-Type": Omit()}) ``` """ diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 9117559064..342b52416a 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -80,9 +80,10 @@ def transform( ```py class Params(TypedDict, total=False): - card_id: Required[Annotated[str, PropertyInfo(alias='cardID')]] + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] - transformed = transform({'card_id': ''}, Params) + + transformed = transform({"card_id": ""}, Params) # {'cardID': ''} ``` diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 993462a66b..cc624b0ce1 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -211,13 +211,15 @@ def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: 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']) + @required_args(["a"], ["b"]) def foo(*, a: str | None = None, b: bool | None = None) -> str: ... ``` diff --git a/tests/test_transform.py b/tests/test_transform.py index 5e15385f4d..c4dffb3bb0 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -189,7 +189,9 @@ class DateDictWithRequiredAlias(TypedDict, total=False): def test_datetime_with_alias() -> None: assert transform({"required_prop": None}, DateDictWithRequiredAlias) == {"prop": None} # type: ignore[comparison-overlap] - assert transform({"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] + assert transform({"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias) == { + "prop": "2023-02-23" + } # type: ignore[comparison-overlap] class MyModel(BaseModel): From f1c7d714914e3321ca2e72839fe2d132a8646e7f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 22 Dec 2023 05:46:35 -0500 Subject: [PATCH 271/914] release: 1.6.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7deae33804..59565e8e31 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.6.0" + ".": "1.6.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 399e3aaebd..83bf20f775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.6.1 (2023-12-22) + +Full Changelog: [v1.6.0...v1.6.1](https://github.com/openai/openai-python/compare/v1.6.0...v1.6.1) + +### Chores + +* **internal:** add bin script ([#1001](https://github.com/openai/openai-python/issues/1001)) ([99ffbda](https://github.com/openai/openai-python/commit/99ffbda279bf4c159511fb96b1d5bb688af25437)) +* **internal:** use ruff instead of black for formatting ([#1008](https://github.com/openai/openai-python/issues/1008)) ([ceaf9a0](https://github.com/openai/openai-python/commit/ceaf9a06fbd1a846756bb72cce50a69c8cc20bd3)) + ## 1.6.0 (2023-12-19) Full Changelog: [v1.5.0...v1.6.0](https://github.com/openai/openai-python/compare/v1.5.0...v1.6.0) diff --git a/pyproject.toml b/pyproject.toml index f1d1a66fef..f39cfb8f15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.6.0" +version = "1.6.1" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9b01b6fcb1..9ab131d176 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.6.0" # x-release-please-version +__version__ = "1.6.1" # x-release-please-version From acbe5ffa2b6322aef5ed424b489bf419b48d4aec Mon Sep 17 00:00:00 2001 From: Davor Runje Date: Sun, 24 Dec 2023 00:22:43 +0100 Subject: [PATCH 272/914] added default value to logprobs (#1007) --- src/openai/types/chat/chat_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 055280c347..b2e98a3144 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -30,7 +30,7 @@ class Choice(BaseModel): index: int """The index of the choice in the list of choices.""" - logprobs: Optional[ChoiceLogprobs] + logprobs: Optional[ChoiceLogprobs] = None """Log probability information for the choice.""" message: ChatCompletionMessage From ecc3b441290141031b765a0b4dd1bba10e65535a Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Sun, 24 Dec 2023 07:23:41 +0800 Subject: [PATCH 273/914] chore(src): fix typos (#988) --- src/openai/resources/beta/assistants/assistants.py | 4 ++-- src/openai/types/beta/assistant_update_params.py | 2 +- src/openai/types/moderation.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index efa711ecf4..8854c8b867 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -172,7 +172,7 @@ def update( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previosuly attached to the list but does not show up in the list, it + file was previously attached to the list but does not show up in the list, it will be deleted from the assistant. instructions: The system instructions that the assistant uses. The maximum length is 32768 @@ -465,7 +465,7 @@ async def update( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previosuly attached to the list but does not show up in the list, it + file was previously attached to the list but does not show up in the list, it will be deleted from the assistant. instructions: The system instructions that the assistant uses. The maximum length is 32768 diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index a0efd96ecd..dfb5d4c553 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -25,7 +25,7 @@ class AssistantUpdateParams(TypedDict, total=False): A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previosuly attached to the list but does not show up in the list, it + file was previously attached to the list but does not show up in the list, it will be deleted from the assistant. """ diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index 3602a46985..09c9a6058b 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -25,7 +25,7 @@ class Categories(BaseModel): Content that expresses, incites, or promotes hate based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste. Hateful content aimed at non-protected groups (e.g., chess players) is - harrassment. + harassment. """ hate_threatening: bool = FieldInfo(alias="hate/threatening") From 47f35921a21c0c6eb21084356f198c0fcb87ed7a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 23 Dec 2023 18:40:55 -0500 Subject: [PATCH 274/914] docs: improve audio example to show how to stream to a file (#1017) --- src/openai/resources/beta/assistants/assistants.py | 4 ++-- src/openai/types/beta/assistant_update_params.py | 2 +- src/openai/types/chat/chat_completion.py | 2 +- src/openai/types/moderation.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 8854c8b867..efa711ecf4 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -172,7 +172,7 @@ def update( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previously attached to the list but does not show up in the list, it + file was previosuly attached to the list but does not show up in the list, it will be deleted from the assistant. instructions: The system instructions that the assistant uses. The maximum length is 32768 @@ -465,7 +465,7 @@ async def update( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previously attached to the list but does not show up in the list, it + file was previosuly attached to the list but does not show up in the list, it will be deleted from the assistant. instructions: The system instructions that the assistant uses. The maximum length is 32768 diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index dfb5d4c553..a0efd96ecd 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -25,7 +25,7 @@ class AssistantUpdateParams(TypedDict, total=False): A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previously attached to the list but does not show up in the list, it + file was previosuly attached to the list but does not show up in the list, it will be deleted from the assistant. """ diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index b2e98a3144..055280c347 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -30,7 +30,7 @@ class Choice(BaseModel): index: int """The index of the choice in the list of choices.""" - logprobs: Optional[ChoiceLogprobs] = None + logprobs: Optional[ChoiceLogprobs] """Log probability information for the choice.""" message: ChatCompletionMessage diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index 09c9a6058b..3602a46985 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -25,7 +25,7 @@ class Categories(BaseModel): Content that expresses, incites, or promotes hate based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste. Hateful content aimed at non-protected groups (e.g., chess players) is - harassment. + harrassment. """ hate_threatening: bool = FieldInfo(alias="hate/threatening") From ade28c4f6ce0f4eed6c6a344f069b63f4571ed3b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 25 Dec 2023 02:10:30 -0500 Subject: [PATCH 275/914] docs: fix docstring typos (#1022) --- src/openai/resources/beta/assistants/assistants.py | 4 ++-- src/openai/types/beta/assistant_update_params.py | 2 +- src/openai/types/moderation.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index efa711ecf4..8854c8b867 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -172,7 +172,7 @@ def update( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previosuly attached to the list but does not show up in the list, it + file was previously attached to the list but does not show up in the list, it will be deleted from the assistant. instructions: The system instructions that the assistant uses. The maximum length is 32768 @@ -465,7 +465,7 @@ async def update( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previosuly attached to the list but does not show up in the list, it + file was previously attached to the list but does not show up in the list, it will be deleted from the assistant. instructions: The system instructions that the assistant uses. The maximum length is 32768 diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index a0efd96ecd..dfb5d4c553 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -25,7 +25,7 @@ class AssistantUpdateParams(TypedDict, total=False): A list of [File](https://platform.openai.com/docs/api-reference/files) IDs attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. If a - file was previosuly attached to the list but does not show up in the list, it + file was previously attached to the list but does not show up in the list, it will be deleted from the assistant. """ diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index 3602a46985..09c9a6058b 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -25,7 +25,7 @@ class Categories(BaseModel): Content that expresses, incites, or promotes hate based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste. Hateful content aimed at non-protected groups (e.g., chess players) is - harrassment. + harassment. """ hate_threatening: bool = FieldInfo(alias="hate/threatening") From 6279b267be1f8d28c080d0282d029e7f0b50948a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 27 Dec 2023 21:19:05 -0500 Subject: [PATCH 276/914] fix(client): correctly use custom http client auth (#1028) --- src/openai/_base_client.py | 13 +++++++++++-- src/openai/_types.py | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 481171a447..53a53d8016 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -58,6 +58,7 @@ PostParser, ProxiesTypes, RequestFiles, + HttpxSendArgs, AsyncTransport, RequestOptions, UnknownResponse, @@ -873,11 +874,15 @@ def _request( request = self._build_request(options) self._prepare_request(request) + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + try: response = self._client.send( request, - auth=self.custom_auth, stream=stream or self._should_stream_response_body(request=request), + **kwargs, ) except httpx.TimeoutException as err: if retries > 0: @@ -1335,11 +1340,15 @@ async def _request( request = self._build_request(options) await self._prepare_request(request) + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + try: response = await self._client.send( request, - auth=self.custom_auth, stream=stream or self._should_stream_response_body(request=request), + **kwargs, ) except httpx.TimeoutException as err: if retries > 0: diff --git a/src/openai/_types.py b/src/openai/_types.py index fc26d5458a..061e5c97f5 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -28,6 +28,7 @@ runtime_checkable, ) +import httpx import pydantic from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport @@ -369,3 +370,7 @@ class InheritsGeneric(Protocol): class _GenericAlias(Protocol): __origin__: type[object] + + +class HttpxSendArgs(TypedDict, total=False): + auth: httpx.Auth From bba23438a63121102fe066982c91771fcca19c80 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 2 Jan 2024 05:36:02 -0500 Subject: [PATCH 277/914] chore(internal): bump license (#1037) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 7b1b36a644..621a6becfb 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 OpenAI + Copyright 2024 OpenAI Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From bcc54ea9ab77ee4c37489ecf095db563ca9c35a3 Mon Sep 17 00:00:00 2001 From: Christian Malpass Date: Wed, 3 Jan 2024 18:52:30 +0200 Subject: [PATCH 278/914] Add Picture API file example to the Examples Folder (#977) * Added a file to the examples folder to provide a simple example of retrieving and printing a picture to the console using the new API. Previously, no examples were provided for images, making it unclear. * Update picture.py --------- Co-authored-by: Logan Kilpatrick <23kilpatrick23@gmail.com> --- examples/picture.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 examples/picture.py diff --git a/examples/picture.py b/examples/picture.py new file mode 100644 index 0000000000..7bf22aa790 --- /dev/null +++ b/examples/picture.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +from openai import OpenAI + +# gets OPENAI_API_KEY from your environment variables +openai = OpenAI() + +prompt = "An astronaut lounging in a tropical resort in space, pixel art" +model = "dall-e-3" + +def main() -> None: + # Generate an image based on the prompt + response = openai.images.generate(prompt=prompt, model=model) + + # Prints response containing a URL link to image + print(response) + +if __name__ == "__main__": + main() From 6f1adfec053ddca20777e9f7959dcbd3f34a6da0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:04:23 -0500 Subject: [PATCH 279/914] chore(internal): update formatting (#1041) --- examples/picture.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/picture.py b/examples/picture.py index 7bf22aa790..c27b52b0da 100644 --- a/examples/picture.py +++ b/examples/picture.py @@ -8,12 +8,14 @@ prompt = "An astronaut lounging in a tropical resort in space, pixel art" model = "dall-e-3" + def main() -> None: # Generate an image based on the prompt response = openai.images.generate(prompt=prompt, model=model) - + # Prints response containing a URL link to image print(response) - + + if __name__ == "__main__": main() From a47375b7e20d411489991bf05b3c10431503141d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 3 Jan 2024 17:13:18 -0500 Subject: [PATCH 280/914] chore(internal): replace isort with ruff (#1042) --- pyproject.toml | 16 ++-- requirements-dev.lock | 7 +- src/openai/__init__.py | 41 ++++----- src/openai/_client.py | 15 +++- src/openai/_compat.py | 30 +++---- src/openai/_extras/__init__.py | 3 +- src/openai/_models.py | 14 +--- src/openai/_types.py | 9 +- src/openai/_utils/__init__.py | 83 ++++++++++--------- src/openai/_utils/_transform.py | 5 +- src/openai/_utils/_utils.py | 3 +- src/openai/resources/__init__.py | 49 ++--------- src/openai/resources/audio/__init__.py | 14 +--- src/openai/resources/audio/audio.py | 14 +--- src/openai/resources/audio/speech.py | 13 ++- src/openai/resources/audio/transcriptions.py | 13 ++- src/openai/resources/audio/translations.py | 13 ++- src/openai/resources/beta/__init__.py | 14 +--- .../resources/beta/assistants/__init__.py | 7 +- .../resources/beta/assistants/assistants.py | 13 ++- src/openai/resources/beta/assistants/files.py | 13 ++- src/openai/resources/beta/beta.py | 14 +--- src/openai/resources/beta/threads/__init__.py | 14 +--- .../beta/threads/messages/__init__.py | 7 +- .../resources/beta/threads/messages/files.py | 13 ++- .../beta/threads/messages/messages.py | 13 ++- .../resources/beta/threads/runs/runs.py | 13 ++- .../resources/beta/threads/runs/steps.py | 13 ++- src/openai/resources/beta/threads/threads.py | 17 ++-- src/openai/resources/chat/__init__.py | 7 +- src/openai/resources/chat/chat.py | 7 +- src/openai/resources/chat/completions.py | 12 ++- src/openai/resources/completions.py | 12 ++- src/openai/resources/edits.py | 12 ++- src/openai/resources/embeddings.py | 15 +++- src/openai/resources/files.py | 16 +++- src/openai/resources/fine_tunes.py | 13 ++- src/openai/resources/fine_tuning/__init__.py | 7 +- src/openai/resources/fine_tuning/jobs.py | 13 ++- src/openai/resources/images.py | 13 ++- src/openai/resources/models.py | 13 ++- src/openai/resources/moderations.py | 12 ++- src/openai/types/__init__.py | 23 ++--- src/openai/types/audio/__init__.py | 8 +- src/openai/types/beta/__init__.py | 4 +- src/openai/types/beta/threads/__init__.py | 12 +-- .../types/beta/threads/runs/__init__.py | 4 +- src/openai/types/chat/__init__.py | 32 ++----- .../chat_completion_content_part_param.py | 4 +- .../types/chat/completion_create_params.py | 8 +- tests/api_resources/beta/test_assistants.py | 5 +- tests/api_resources/beta/test_threads.py | 5 +- tests/api_resources/beta/threads/test_runs.py | 4 +- tests/api_resources/fine_tuning/test_jobs.py | 5 +- tests/test_client.py | 14 +--- tests/utils.py | 7 +- 56 files changed, 397 insertions(+), 383 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f39cfb8f15..a4eba4a0d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,6 @@ dev-dependencies = [ "pytest", "pytest-asyncio", "ruff", - "isort", "time-machine", "nox", "dirty-equals>=0.6.0", @@ -69,7 +68,6 @@ format = { chain = [ "format:ruff", "format:docs", "fix:ruff", - "format:isort", ]} "format:black" = "black ." "format:docs" = "python bin/ruffen-docs.py README.md api.md" @@ -130,16 +128,13 @@ reportImplicitOverride = true reportImportCycles = false reportPrivateUsage = false -[tool.isort] -profile = "black" -length_sort = true -extra_standard_library = ["typing_extensions"] - [tool.ruff] line-length = 120 output-format = "grouped" target-version = "py37" select = [ + # isort + "I", # bugbear rules "B", # remove unused imports @@ -166,6 +161,13 @@ ignore-init-module-imports = true [tool.ruff.format] docstring-code-format = true +[tool.ruff.lint.isort] +length-sort = true +length-sort-straight = true +combine-as-imports = true +extra-standard-library = ["typing_extensions"] +known-first-party = ["openai", "tests"] + [tool.ruff.per-file-ignores] "bin/**.py" = ["T201", "T203"] "tests/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 53763f2aa9..088cb2bd98 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -29,17 +29,16 @@ httpx==0.25.2 idna==3.4 importlib-metadata==7.0.0 iniconfig==2.0.0 -isort==5.10.1 msal==1.26.0 msal-extensions==1.1.0 mypy==1.7.1 mypy-extensions==1.0.0 nodeenv==1.8.0 nox==2023.4.22 -numpy==1.26.2 +numpy==1.26.3 packaging==23.2 pandas==2.1.4 -pandas-stubs==2.1.4.231218 +pandas-stubs==2.1.4.231227 platformdirs==3.11.0 pluggy==1.3.0 portalocker==2.8.2 @@ -64,7 +63,7 @@ tqdm==4.66.1 types-pytz==2023.3.1.1 types-tqdm==4.66.0.2 typing-extensions==4.8.0 -tzdata==2023.3 +tzdata==2023.4 urllib3==2.1.0 virtualenv==20.24.5 zipp==3.17.0 diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 0d66b3c682..ee96f06919 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -8,17 +8,7 @@ from . import types from ._types import NoneType, Transport, ProxiesTypes from ._utils import file_from_path -from ._client import ( - Client, - OpenAI, - Stream, - Timeout, - Transport, - AsyncClient, - AsyncOpenAI, - AsyncStream, - RequestOptions, -) +from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions from ._version import __title__, __version__ from ._exceptions import ( APIError, @@ -72,8 +62,7 @@ from .lib import azure as _azure from .version import VERSION as VERSION -from .lib.azure import AzureOpenAI as AzureOpenAI -from .lib.azure import AsyncAzureOpenAI as AsyncAzureOpenAI +from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI from .lib._old_api import * _setup_logging() @@ -323,15 +312,17 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] _client = None -from ._module_client import beta as beta -from ._module_client import chat as chat -from ._module_client import audio as audio -from ._module_client import edits as edits -from ._module_client import files as files -from ._module_client import images as images -from ._module_client import models as models -from ._module_client import embeddings as embeddings -from ._module_client import fine_tunes as fine_tunes -from ._module_client import completions as completions -from ._module_client import fine_tuning as fine_tuning -from ._module_client import moderations as moderations +from ._module_client import ( + beta as beta, + chat as chat, + audio as audio, + edits as edits, + files as files, + images as images, + models as models, + embeddings as embeddings, + fine_tunes as fine_tunes, + completions as completions, + fine_tuning as fine_tuning, + moderations as moderations, +) diff --git a/src/openai/_client.py b/src/openai/_client.py index dacadf5aff..9eb6888909 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -19,12 +19,19 @@ ProxiesTypes, RequestOptions, ) -from ._utils import is_given, is_mapping, get_async_library +from ._utils import ( + is_given, + is_mapping, + get_async_library, +) from ._version import __version__ -from ._streaming import Stream as Stream -from ._streaming import AsyncStream as AsyncStream +from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError -from ._base_client import DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient +from ._base_client import ( + DEFAULT_MAX_RETRIES, + SyncAPIClient, + AsyncAPIClient, +) __all__ = [ "Timeout", diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 34323c9b7e..d95db8ed1e 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -43,21 +43,23 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 else: if PYDANTIC_V2: - from pydantic.v1.typing import get_args as get_args - from pydantic.v1.typing import is_union as is_union - from pydantic.v1.typing import get_origin as get_origin - from pydantic.v1.typing import is_typeddict as is_typeddict - from pydantic.v1.typing import is_literal_type as is_literal_type - from pydantic.v1.datetime_parse import parse_date as parse_date - from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime else: - from pydantic.typing import get_args as get_args - from pydantic.typing import is_union as is_union - from pydantic.typing import get_origin as get_origin - from pydantic.typing import is_typeddict as is_typeddict - from pydantic.typing import is_literal_type as is_literal_type - from pydantic.datetime_parse import parse_date as parse_date - from pydantic.datetime_parse import parse_datetime as parse_datetime + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime # refactored config diff --git a/src/openai/_extras/__init__.py b/src/openai/_extras/__init__.py index dc6625c5dc..864dac4171 100644 --- a/src/openai/_extras/__init__.py +++ b/src/openai/_extras/__init__.py @@ -1,3 +1,2 @@ -from .numpy_proxy import numpy as numpy -from .numpy_proxy import has_numpy as has_numpy +from .numpy_proxy import numpy as numpy, has_numpy as has_numpy from .pandas_proxy import pandas as pandas diff --git a/src/openai/_models.py b/src/openai/_models.py index 330a2064d8..48d5624f64 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -30,17 +30,11 @@ AnyMapping, HttpxRequestFiles, ) -from ._utils import ( - is_list, - is_given, - is_mapping, - parse_date, - parse_datetime, - strip_not_given, -) -from ._compat import PYDANTIC_V2, ConfigDict -from ._compat import GenericModel as BaseGenericModel +from ._utils import is_list, is_given, is_mapping, parse_date, parse_datetime, strip_not_given from ._compat import ( + PYDANTIC_V2, + ConfigDict, + GenericModel as BaseGenericModel, get_args, is_union, parse_obj, diff --git a/src/openai/_types.py b/src/openai/_types.py index 061e5c97f5..b52af6882f 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -19,14 +19,7 @@ Sequence, AsyncIterator, ) -from typing_extensions import ( - Literal, - Protocol, - TypeAlias, - TypedDict, - override, - runtime_checkable, -) +from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable import httpx import pydantic diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index a43201d3c7..2dcfc122f1 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -1,40 +1,45 @@ from ._proxy import LazyProxy as LazyProxy -from ._utils import flatten as flatten -from ._utils import is_dict as is_dict -from ._utils import is_list as is_list -from ._utils import is_given as is_given -from ._utils import is_tuple as is_tuple -from ._utils import is_mapping as is_mapping -from ._utils import is_tuple_t as is_tuple_t -from ._utils import parse_date as parse_date -from ._utils import is_sequence as is_sequence -from ._utils import coerce_float as coerce_float -from ._utils import is_mapping_t as is_mapping_t -from ._utils import removeprefix as removeprefix -from ._utils import removesuffix as removesuffix -from ._utils import extract_files as extract_files -from ._utils import is_sequence_t as is_sequence_t -from ._utils import required_args as required_args -from ._utils import coerce_boolean as coerce_boolean -from ._utils import coerce_integer as coerce_integer -from ._utils import file_from_path as file_from_path -from ._utils import parse_datetime as parse_datetime -from ._utils import strip_not_given as strip_not_given -from ._utils import deepcopy_minimal as deepcopy_minimal -from ._utils import get_async_library as get_async_library -from ._utils import maybe_coerce_float as maybe_coerce_float -from ._utils import get_required_header as get_required_header -from ._utils import maybe_coerce_boolean as maybe_coerce_boolean -from ._utils import maybe_coerce_integer as maybe_coerce_integer -from ._typing import is_list_type as is_list_type -from ._typing import is_union_type as is_union_type -from ._typing import extract_type_arg as extract_type_arg -from ._typing import is_required_type as is_required_type -from ._typing import is_annotated_type as is_annotated_type -from ._typing import strip_annotated_type as strip_annotated_type -from ._typing import extract_type_var_from_base as extract_type_var_from_base -from ._streams import consume_sync_iterator as consume_sync_iterator -from ._streams import consume_async_iterator as consume_async_iterator -from ._transform import PropertyInfo as PropertyInfo -from ._transform import transform as transform -from ._transform import maybe_transform as maybe_transform +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + parse_date as parse_date, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + parse_datetime as parse_datetime, + strip_not_given as strip_not_given, + deepcopy_minimal as deepcopy_minimal, + get_async_library as get_async_library, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, +) +from ._typing import ( + is_list_type as is_list_type, + is_union_type as is_union_type, + extract_type_arg as extract_type_arg, + is_required_type as is_required_type, + is_annotated_type as is_annotated_type, + strip_annotated_type as strip_annotated_type, + extract_type_var_from_base as extract_type_var_from_base, +) +from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + maybe_transform as maybe_transform, +) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 342b52416a..3a1c14969b 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -6,7 +6,10 @@ import pydantic -from ._utils import is_list, is_mapping +from ._utils import ( + is_list, + is_mapping, +) from ._typing import ( is_list_type, is_union_type, diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index cc624b0ce1..1c5c21a8ea 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -21,8 +21,7 @@ import sniffio from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import parse_date as parse_date -from .._compat import parse_datetime as parse_datetime +from .._compat import parse_date as parse_date, parse_datetime as parse_datetime _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index e0f4f08d5c..2cdbeb6ae1 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -5,48 +5,13 @@ from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse from .edits import Edits, AsyncEdits, EditsWithRawResponse, AsyncEditsWithRawResponse from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .images import ( - Images, - AsyncImages, - ImagesWithRawResponse, - AsyncImagesWithRawResponse, -) -from .models import ( - Models, - AsyncModels, - ModelsWithRawResponse, - AsyncModelsWithRawResponse, -) -from .embeddings import ( - Embeddings, - AsyncEmbeddings, - EmbeddingsWithRawResponse, - AsyncEmbeddingsWithRawResponse, -) -from .fine_tunes import ( - FineTunes, - AsyncFineTunes, - FineTunesWithRawResponse, - AsyncFineTunesWithRawResponse, -) -from .completions import ( - Completions, - AsyncCompletions, - CompletionsWithRawResponse, - AsyncCompletionsWithRawResponse, -) -from .fine_tuning import ( - FineTuning, - AsyncFineTuning, - FineTuningWithRawResponse, - AsyncFineTuningWithRawResponse, -) -from .moderations import ( - Moderations, - AsyncModerations, - ModerationsWithRawResponse, - AsyncModerationsWithRawResponse, -) +from .images import Images, AsyncImages, ImagesWithRawResponse, AsyncImagesWithRawResponse +from .models import Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse +from .embeddings import Embeddings, AsyncEmbeddings, EmbeddingsWithRawResponse, AsyncEmbeddingsWithRawResponse +from .fine_tunes import FineTunes, AsyncFineTunes, FineTunesWithRawResponse, AsyncFineTunesWithRawResponse +from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse +from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse +from .moderations import Moderations, AsyncModerations, ModerationsWithRawResponse, AsyncModerationsWithRawResponse __all__ = [ "Completions", diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py index 76547b5f34..b6ff4322d4 100644 --- a/src/openai/resources/audio/__init__.py +++ b/src/openai/resources/audio/__init__.py @@ -1,18 +1,8 @@ # File generated from our OpenAPI spec by Stainless. from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse -from .speech import ( - Speech, - AsyncSpeech, - SpeechWithRawResponse, - AsyncSpeechWithRawResponse, -) -from .translations import ( - Translations, - AsyncTranslations, - TranslationsWithRawResponse, - AsyncTranslationsWithRawResponse, -) +from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse +from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse from .transcriptions import ( Transcriptions, AsyncTranscriptions, diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 6f7226ee59..6b9242f0c2 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -4,19 +4,9 @@ from typing import TYPE_CHECKING -from .speech import ( - Speech, - AsyncSpeech, - SpeechWithRawResponse, - AsyncSpeechWithRawResponse, -) +from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse from ..._resource import SyncAPIResource, AsyncAPIResource -from .translations import ( - Translations, - AsyncTranslations, - TranslationsWithRawResponse, - AsyncTranslationsWithRawResponse, -) +from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse from .transcriptions import ( Transcriptions, AsyncTranscriptions, diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index aadb00bd02..7ae552c12f 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -7,12 +7,21 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import speech_create_params -from ..._base_client import HttpxBinaryResponseContent, make_request_options +from ..._base_client import ( + HttpxBinaryResponseContent, + make_request_options, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index d2b4452411..54be1c99a6 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -7,12 +7,21 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + FileTypes, +) from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import Transcription, transcription_create_params -from ..._base_client import make_request_options +from ..._base_client import ( + make_request_options, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index fe7f7f2a40..c4489004ac 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -7,12 +7,21 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + FileTypes, +) from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import Translation, translation_create_params -from ..._base_client import make_request_options +from ..._base_client import ( + make_request_options, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 55ad243cca..561f8bef60 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -1,18 +1,8 @@ # File generated from our OpenAPI spec by Stainless. from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse -from .threads import ( - Threads, - AsyncThreads, - ThreadsWithRawResponse, - AsyncThreadsWithRawResponse, -) -from .assistants import ( - Assistants, - AsyncAssistants, - AssistantsWithRawResponse, - AsyncAssistantsWithRawResponse, -) +from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse +from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse __all__ = [ "Assistants", diff --git a/src/openai/resources/beta/assistants/__init__.py b/src/openai/resources/beta/assistants/__init__.py index 6efb0b21ec..205b2cf0f5 100644 --- a/src/openai/resources/beta/assistants/__init__.py +++ b/src/openai/resources/beta/assistants/__init__.py @@ -1,12 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .assistants import ( - Assistants, - AsyncAssistants, - AssistantsWithRawResponse, - AsyncAssistantsWithRawResponse, -) +from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse __all__ = [ "Files", diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 8854c8b867..13b90ac69c 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -8,7 +8,13 @@ import httpx from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -20,7 +26,10 @@ assistant_create_params, assistant_update_params, ) -from ...._base_client import AsyncPaginator, make_request_options +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from ...._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index 5ac5897ca3..5682587487 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -7,12 +7,21 @@ import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage -from ...._base_client import AsyncPaginator, make_request_options +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) from ....types.beta.assistants import ( AssistantFile, FileDeleteResponse, diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index b552561763..5cea6c1460 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -4,18 +4,8 @@ from typing import TYPE_CHECKING -from .threads import ( - Threads, - AsyncThreads, - ThreadsWithRawResponse, - AsyncThreadsWithRawResponse, -) -from .assistants import ( - Assistants, - AsyncAssistants, - AssistantsWithRawResponse, - AsyncAssistantsWithRawResponse, -) +from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse +from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse from ..._resource import SyncAPIResource, AsyncAPIResource if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/__init__.py b/src/openai/resources/beta/threads/__init__.py index b9aaada465..fe7c5e5a20 100644 --- a/src/openai/resources/beta/threads/__init__.py +++ b/src/openai/resources/beta/threads/__init__.py @@ -1,18 +1,8 @@ # File generated from our OpenAPI spec by Stainless. from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .threads import ( - Threads, - AsyncThreads, - ThreadsWithRawResponse, - AsyncThreadsWithRawResponse, -) -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, -) +from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse +from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse __all__ = [ "Runs", diff --git a/src/openai/resources/beta/threads/messages/__init__.py b/src/openai/resources/beta/threads/messages/__init__.py index d8d4ce448c..cef618ed14 100644 --- a/src/openai/resources/beta/threads/messages/__init__.py +++ b/src/openai/resources/beta/threads/messages/__init__.py @@ -1,12 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, -) +from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse __all__ = [ "Files", diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index e028a6fda7..24c9680f3d 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -7,12 +7,21 @@ import httpx -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import AsyncPaginator, make_request_options +from ....._base_client import ( + AsyncPaginator, + make_request_options, +) from .....types.beta.threads.messages import MessageFile, file_list_params if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 30ae072512..9a6f5706c3 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -8,12 +8,21 @@ import httpx from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import AsyncPaginator, make_request_options +from ....._base_client import ( + AsyncPaginator, + make_request_options, +) from .....types.beta.threads import ( ThreadMessage, message_list_params, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index aea3b8cefc..6a727b856b 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -8,12 +8,21 @@ import httpx from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import AsyncPaginator, make_request_options +from ....._base_client import ( + AsyncPaginator, + make_request_options, +) from .....types.beta.threads import ( Run, run_list_params, diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 4fcc87a0ff..f26034cf82 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -7,12 +7,21 @@ import httpx -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ....._utils import maybe_transform from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import AsyncPaginator, make_request_options +from ....._base_client import ( + AsyncPaginator, + make_request_options, +) from .....types.beta.threads.runs import RunStep, step_list_params if TYPE_CHECKING: diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 9469fc0513..b37667485d 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -7,13 +7,14 @@ import httpx from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, +from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, ) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -24,7 +25,9 @@ thread_update_params, thread_create_and_run_params, ) -from ...._base_client import make_request_options +from ...._base_client import ( + make_request_options, +) from ....types.beta.threads import Run if TYPE_CHECKING: diff --git a/src/openai/resources/chat/__init__.py b/src/openai/resources/chat/__init__.py index 2e56c0cbfa..85b246509e 100644 --- a/src/openai/resources/chat/__init__.py +++ b/src/openai/resources/chat/__init__.py @@ -1,12 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse -from .completions import ( - Completions, - AsyncCompletions, - CompletionsWithRawResponse, - AsyncCompletionsWithRawResponse, -) +from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse __all__ = [ "Completions", diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index 3847b20512..d93a501b1f 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -5,12 +5,7 @@ from typing import TYPE_CHECKING from ..._resource import SyncAPIResource, AsyncAPIResource -from .completions import ( - Completions, - AsyncCompletions, - CompletionsWithRawResponse, - AsyncCompletionsWithRawResponse, -) +from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 5aac234227..6bde8383dc 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -7,7 +7,13 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import required_args, maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -20,7 +26,9 @@ ChatCompletionToolChoiceOptionParam, completion_create_params, ) -from ..._base_client import make_request_options +from ..._base_client import ( + make_request_options, +) if TYPE_CHECKING: from ..._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index d22e288054..a13c901529 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -8,12 +8,20 @@ import httpx from ..types import Completion, completion_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import required_args, maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._streaming import Stream, AsyncStream -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/edits.py b/src/openai/resources/edits.py index eafaa82fdf..587da02c8f 100644 --- a/src/openai/resources/edits.py +++ b/src/openai/resources/edits.py @@ -9,11 +9,19 @@ import httpx from ..types import Edit, edit_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 978d239774..f22acad401 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -9,13 +9,20 @@ import httpx from ..types import CreateEmbeddingResponse, embedding_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import is_given, maybe_transform -from .._extras import numpy as np -from .._extras import has_numpy +from .._extras import numpy as np, has_numpy from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index e4d978d3af..bc7823783b 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -9,8 +9,20 @@ import httpx -from ..types import FileObject, FileDeleted, file_list_params, file_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..types import ( + FileObject, + FileDeleted, + file_list_params, + file_create_params, +) +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + FileTypes, +) from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper diff --git a/src/openai/resources/fine_tunes.py b/src/openai/resources/fine_tunes.py index 91c8201cbb..f50d78717b 100644 --- a/src/openai/resources/fine_tunes.py +++ b/src/openai/resources/fine_tunes.py @@ -14,13 +14,22 @@ fine_tune_create_params, fine_tune_list_events_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._streaming import Stream, AsyncStream from ..pagination import SyncPage, AsyncPage -from .._base_client import AsyncPaginator, make_request_options +from .._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py index 9133c25d4a..27445fb707 100644 --- a/src/openai/resources/fine_tuning/__init__.py +++ b/src/openai/resources/fine_tuning/__init__.py @@ -1,12 +1,7 @@ # File generated from our OpenAPI spec by Stainless. from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse -from .fine_tuning import ( - FineTuning, - AsyncFineTuning, - FineTuningWithRawResponse, - AsyncFineTuningWithRawResponse, -) +from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse __all__ = [ "Jobs", diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 3d9aed8d91..55eee67044 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -7,12 +7,21 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from ..._utils import maybe_transform from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import AsyncPaginator, make_request_options +from ..._base_client import ( + AsyncPaginator, + make_request_options, +) from ...types.fine_tuning import ( FineTuningJob, FineTuningJobEvent, diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 94b1bc1fc8..0e1313078f 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -13,11 +13,20 @@ image_generate_params, image_create_variation_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + FileTypes, +) from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index 2d04bdc5cc..a44a7ffbb0 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -7,11 +7,20 @@ import httpx from ..types import Model, ModelDeleted -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncPage, AsyncPage -from .._base_client import AsyncPaginator, make_request_options +from .._base_client import ( + AsyncPaginator, + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 12a7c68a7b..9de7cd640f 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -8,11 +8,19 @@ import httpx from ..types import ModerationCreateResponse, moderation_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, +) from .._utils import maybe_transform from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import make_request_options +from .._base_client import ( + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 1b4fca26ee..df2b580587 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -5,8 +5,7 @@ from .edit import Edit as Edit from .image import Image as Image from .model import Model as Model -from .shared import FunctionDefinition as FunctionDefinition -from .shared import FunctionParameters as FunctionParameters +from .shared import FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters from .embedding import Embedding as Embedding from .fine_tune import FineTune as FineTune from .completion import Completion as Completion @@ -28,18 +27,8 @@ from .fine_tune_create_params import FineTuneCreateParams as FineTuneCreateParams from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .moderation_create_params import ModerationCreateParams as ModerationCreateParams -from .create_embedding_response import ( - CreateEmbeddingResponse as CreateEmbeddingResponse, -) -from .moderation_create_response import ( - ModerationCreateResponse as ModerationCreateResponse, -) -from .fine_tune_list_events_params import ( - FineTuneListEventsParams as FineTuneListEventsParams, -) -from .image_create_variation_params import ( - ImageCreateVariationParams as ImageCreateVariationParams, -) -from .fine_tune_events_list_response import ( - FineTuneEventsListResponse as FineTuneEventsListResponse, -) +from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse +from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse +from .fine_tune_list_events_params import FineTuneListEventsParams as FineTuneListEventsParams +from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams +from .fine_tune_events_list_response import FineTuneEventsListResponse as FineTuneEventsListResponse diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index 83afa060f8..ba5f7fd8e0 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -5,9 +5,5 @@ from .translation import Translation as Translation from .transcription import Transcription as Transcription from .speech_create_params import SpeechCreateParams as SpeechCreateParams -from .translation_create_params import ( - TranslationCreateParams as TranslationCreateParams, -) -from .transcription_create_params import ( - TranscriptionCreateParams as TranscriptionCreateParams, -) +from .translation_create_params import TranslationCreateParams as TranslationCreateParams +from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index c03d823b8c..e6742521e9 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -11,6 +11,4 @@ from .assistant_list_params import AssistantListParams as AssistantListParams from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams -from .thread_create_and_run_params import ( - ThreadCreateAndRunParams as ThreadCreateAndRunParams, -) +from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index 0cb557a514..8c77466dec 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -11,12 +11,6 @@ from .message_content_text import MessageContentText as MessageContentText from .message_create_params import MessageCreateParams as MessageCreateParams from .message_update_params import MessageUpdateParams as MessageUpdateParams -from .message_content_image_file import ( - MessageContentImageFile as MessageContentImageFile, -) -from .run_submit_tool_outputs_params import ( - RunSubmitToolOutputsParams as RunSubmitToolOutputsParams, -) -from .required_action_function_tool_call import ( - RequiredActionFunctionToolCall as RequiredActionFunctionToolCall, -) +from .message_content_image_file import MessageContentImageFile as MessageContentImageFile +from .run_submit_tool_outputs_params import RunSubmitToolOutputsParams as RunSubmitToolOutputsParams +from .required_action_function_tool_call import RequiredActionFunctionToolCall as RequiredActionFunctionToolCall diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py index 72b972a986..16cb852922 100644 --- a/src/openai/types/beta/threads/runs/__init__.py +++ b/src/openai/types/beta/threads/runs/__init__.py @@ -8,6 +8,4 @@ from .function_tool_call import FunctionToolCall as FunctionToolCall from .retrieval_tool_call import RetrievalToolCall as RetrievalToolCall from .tool_calls_step_details import ToolCallsStepDetails as ToolCallsStepDetails -from .message_creation_step_details import ( - MessageCreationStepDetails as MessageCreationStepDetails, -) +from .message_creation_step_details import MessageCreationStepDetails as MessageCreationStepDetails diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index ba21982a2b..39a6335f64 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -7,30 +7,14 @@ from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage from .completion_create_params import CompletionCreateParams as CompletionCreateParams -from .chat_completion_tool_param import ( - ChatCompletionToolParam as ChatCompletionToolParam, -) -from .chat_completion_message_param import ( - ChatCompletionMessageParam as ChatCompletionMessageParam, -) -from .chat_completion_token_logprob import ( - ChatCompletionTokenLogprob as ChatCompletionTokenLogprob, -) -from .chat_completion_message_tool_call import ( - ChatCompletionMessageToolCall as ChatCompletionMessageToolCall, -) -from .chat_completion_content_part_param import ( - ChatCompletionContentPartParam as ChatCompletionContentPartParam, -) -from .chat_completion_tool_message_param import ( - ChatCompletionToolMessageParam as ChatCompletionToolMessageParam, -) -from .chat_completion_user_message_param import ( - ChatCompletionUserMessageParam as ChatCompletionUserMessageParam, -) -from .chat_completion_system_message_param import ( - ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam, -) +from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam +from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam +from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob +from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall +from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam +from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam +from .chat_completion_user_message_param import ChatCompletionUserMessageParam as ChatCompletionUserMessageParam +from .chat_completion_system_message_param import ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam from .chat_completion_function_message_param import ( ChatCompletionFunctionMessageParam as ChatCompletionFunctionMessageParam, ) diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index 587578e2ef..8e58239258 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -5,9 +5,7 @@ from typing import Union from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam -from .chat_completion_content_part_image_param import ( - ChatCompletionContentPartImageParam, -) +from .chat_completion_content_part_image_param import ChatCompletionContentPartImageParam __all__ = ["ChatCompletionContentPartParam"] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 41b71efa04..49807a372e 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -8,12 +8,8 @@ from ...types import shared_params from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam -from .chat_completion_tool_choice_option_param import ( - ChatCompletionToolChoiceOptionParam, -) -from .chat_completion_function_call_option_param import ( - ChatCompletionFunctionCallOptionParam, -) +from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam +from .chat_completion_function_call_option_param import ChatCompletionFunctionCallOptionParam __all__ = [ "CompletionCreateParamsBase", diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 82e975b46d..97e74c61e4 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -10,7 +10,10 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta import Assistant, AssistantDeleted +from openai.types.beta import ( + Assistant, + AssistantDeleted, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 8fa1fc20ea..860159ffb3 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -9,7 +9,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI -from openai.types.beta import Thread, ThreadDeleted +from openai.types.beta import ( + Thread, + ThreadDeleted, +) from openai.types.beta.threads import Run base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 494cae2656..9d04a95c80 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -10,7 +10,9 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads import Run +from openai.types.beta.threads import ( + Run, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 5716a23d54..927ca9bbdd 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -10,7 +10,10 @@ from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.fine_tuning import FineTuningJob, FineTuningJobEvent +from openai.types.fine_tuning import ( + FineTuningJob, + FineTuningJobEvent, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" diff --git a/tests/test_client.py b/tests/test_client.py index ffa779fb38..c49e4d629e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -20,18 +20,8 @@ from openai._client import OpenAI, AsyncOpenAI from openai._models import BaseModel, FinalRequestOptions from openai._streaming import Stream, AsyncStream -from openai._exceptions import ( - OpenAIError, - APIStatusError, - APITimeoutError, - APIResponseValidationError, -) -from openai._base_client import ( - DEFAULT_TIMEOUT, - HTTPX_DEFAULT_TIMEOUT, - BaseClient, - make_request_options, -) +from openai._exceptions import OpenAIError, APIStatusError, APITimeoutError, APIResponseValidationError +from openai._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options from .utils import update_env diff --git a/tests/utils.py b/tests/utils.py index 57486c733a..02dd9c0acc 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,7 +8,12 @@ from typing_extensions import Literal, get_args, get_origin, assert_type from openai._types import NoneType -from openai._utils import is_dict, is_list, is_list_type, is_union_type +from openai._utils import ( + is_dict, + is_list, + is_list_type, + is_union_type, +) from openai._compat import PYDANTIC_V2, field_outer_type, get_model_fields from openai._models import BaseModel From c4db321ba157282f8db03442ceb3528e030b94ad Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 3 Jan 2024 21:27:18 -0500 Subject: [PATCH 281/914] feat: add `None` default value to nullable response properties (#1043) --- src/openai/_exceptions.py | 4 ++-- src/openai/types/beta/assistant.py | 8 ++++---- src/openai/types/beta/thread.py | 2 +- src/openai/types/beta/threads/run.py | 14 +++++++------- .../types/beta/threads/runs/function_tool_call.py | 2 +- src/openai/types/beta/threads/runs/run_step.py | 12 ++++++------ src/openai/types/beta/threads/thread_message.py | 6 +++--- src/openai/types/chat/chat_completion.py | 4 ++-- src/openai/types/chat/chat_completion_chunk.py | 4 ++-- src/openai/types/chat/chat_completion_message.py | 2 +- .../types/chat/chat_completion_token_logprob.py | 4 ++-- src/openai/types/completion_choice.py | 2 +- src/openai/types/fine_tune.py | 2 +- src/openai/types/fine_tuning/fine_tuning_job.py | 12 ++++++------ 14 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index 40b163270d..d7ded1248f 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -40,8 +40,8 @@ class APIError(OpenAIError): If there was no response associated with this error then it will be `None`. """ - code: Optional[str] - param: Optional[str] + code: Optional[str] = None + param: Optional[str] = None type: Optional[str] def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index a21206765a..89e45d4806 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -37,7 +37,7 @@ class Assistant(BaseModel): created_at: int """The Unix timestamp (in seconds) for when the assistant was created.""" - description: Optional[str] + description: Optional[str] = None """The description of the assistant. The maximum length is 512 characters.""" file_ids: List[str] @@ -47,13 +47,13 @@ class Assistant(BaseModel): assistant. Files are ordered by their creation date in ascending order. """ - instructions: Optional[str] + instructions: Optional[str] = None """The system instructions that the assistant uses. The maximum length is 32768 characters. """ - metadata: Optional[builtins.object] + metadata: Optional[builtins.object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a @@ -71,7 +71,7 @@ class Assistant(BaseModel): descriptions of them. """ - name: Optional[str] + name: Optional[str] = None """The name of the assistant. The maximum length is 256 characters.""" object: Literal["assistant"] diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py index a340bffd60..474527033a 100644 --- a/src/openai/types/beta/thread.py +++ b/src/openai/types/beta/thread.py @@ -16,7 +16,7 @@ class Thread(BaseModel): created_at: int """The Unix timestamp (in seconds) for when the thread was created.""" - metadata: Optional[builtins.object] + metadata: Optional[builtins.object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index ffbba1e504..b6d66bd8dd 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -72,10 +72,10 @@ class Run(BaseModel): execution of this run. """ - cancelled_at: Optional[int] + cancelled_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run was cancelled.""" - completed_at: Optional[int] + completed_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run was completed.""" created_at: int @@ -84,7 +84,7 @@ class Run(BaseModel): expires_at: int """The Unix timestamp (in seconds) for when the run will expire.""" - failed_at: Optional[int] + failed_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run failed.""" file_ids: List[str] @@ -101,10 +101,10 @@ class Run(BaseModel): this run. """ - last_error: Optional[LastError] + last_error: Optional[LastError] = None """The last error associated with this run. Will be `null` if there are no errors.""" - metadata: Optional[builtins.object] + metadata: Optional[builtins.object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a @@ -122,13 +122,13 @@ class Run(BaseModel): object: Literal["thread.run"] """The object type, which is always `thread.run`.""" - required_action: Optional[RequiredAction] + required_action: Optional[RequiredAction] = None """Details on the action required to continue the run. Will be `null` if no action is required. """ - started_at: Optional[int] + started_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run was started.""" status: Literal[ diff --git a/src/openai/types/beta/threads/runs/function_tool_call.py b/src/openai/types/beta/threads/runs/function_tool_call.py index f4cf8bbdd0..bbd3cb7052 100644 --- a/src/openai/types/beta/threads/runs/function_tool_call.py +++ b/src/openai/types/beta/threads/runs/function_tool_call.py @@ -15,7 +15,7 @@ class Function(BaseModel): name: str """The name of the function.""" - output: Optional[str] + output: Optional[str] = None """The output of the function. This will be `null` if the outputs have not been diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 5f8723b71a..1d95e9d6eb 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -33,31 +33,31 @@ class RunStep(BaseModel): associated with the run step. """ - cancelled_at: Optional[int] + cancelled_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run step was cancelled.""" - completed_at: Optional[int] + completed_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run step completed.""" created_at: int """The Unix timestamp (in seconds) for when the run step was created.""" - expired_at: Optional[int] + expired_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run step expired. A step is considered expired if the parent run is expired. """ - failed_at: Optional[int] + failed_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run step failed.""" - last_error: Optional[LastError] + last_error: Optional[LastError] = None """The last error associated with this run step. Will be `null` if there are no errors. """ - metadata: Optional[builtins.object] + metadata: Optional[builtins.object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a diff --git a/src/openai/types/beta/threads/thread_message.py b/src/openai/types/beta/threads/thread_message.py index 0f782ef845..8f1ac07d0a 100644 --- a/src/openai/types/beta/threads/thread_message.py +++ b/src/openai/types/beta/threads/thread_message.py @@ -17,7 +17,7 @@ class ThreadMessage(BaseModel): id: str """The identifier, which can be referenced in API endpoints.""" - assistant_id: Optional[str] + assistant_id: Optional[str] = None """ If applicable, the ID of the [assistant](https://platform.openai.com/docs/api-reference/assistants) that @@ -37,7 +37,7 @@ class ThreadMessage(BaseModel): that can access files. A maximum of 10 files can be attached to a message. """ - metadata: Optional[builtins.object] + metadata: Optional[builtins.object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a @@ -51,7 +51,7 @@ class ThreadMessage(BaseModel): role: Literal["user", "assistant"] """The entity that produced the message. One of `user` or `assistant`.""" - run_id: Optional[str] + run_id: Optional[str] = None """ If applicable, the ID of the [run](https://platform.openai.com/docs/api-reference/runs) associated with the diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 055280c347..dc63d84945 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -12,7 +12,7 @@ class ChoiceLogprobs(BaseModel): - content: Optional[List[ChatCompletionTokenLogprob]] + content: Optional[List[ChatCompletionTokenLogprob]] = None """A list of message content tokens with log probability information.""" @@ -30,7 +30,7 @@ class Choice(BaseModel): index: int """The index of the choice in the list of choices.""" - logprobs: Optional[ChoiceLogprobs] + logprobs: Optional[ChoiceLogprobs] = None """Log probability information for the choice.""" message: ChatCompletionMessage diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index ccc7ad79ec..95013e7a4f 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -73,7 +73,7 @@ class ChoiceDelta(BaseModel): class ChoiceLogprobs(BaseModel): - content: Optional[List[ChatCompletionTokenLogprob]] + content: Optional[List[ChatCompletionTokenLogprob]] = None """A list of message content tokens with log probability information.""" @@ -81,7 +81,7 @@ class Choice(BaseModel): delta: ChoiceDelta """A chat completion delta generated by streamed model responses.""" - finish_reason: Optional[Literal["stop", "length", "tool_calls", "content_filter", "function_call"]] + finish_reason: Optional[Literal["stop", "length", "tool_calls", "content_filter", "function_call"]] = None """The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py index 4749798a33..da8b2fcd5c 100644 --- a/src/openai/types/chat/chat_completion_message.py +++ b/src/openai/types/chat/chat_completion_message.py @@ -23,7 +23,7 @@ class FunctionCall(BaseModel): class ChatCompletionMessage(BaseModel): - content: Optional[str] + content: Optional[str] = None """The contents of the message.""" role: Literal["assistant"] diff --git a/src/openai/types/chat/chat_completion_token_logprob.py b/src/openai/types/chat/chat_completion_token_logprob.py index 8896da8b85..728845fb33 100644 --- a/src/openai/types/chat/chat_completion_token_logprob.py +++ b/src/openai/types/chat/chat_completion_token_logprob.py @@ -11,7 +11,7 @@ class TopLogprob(BaseModel): token: str """The token.""" - bytes: Optional[List[int]] + bytes: Optional[List[int]] = None """A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and @@ -27,7 +27,7 @@ class ChatCompletionTokenLogprob(BaseModel): token: str """The token.""" - bytes: Optional[List[int]] + bytes: Optional[List[int]] = None """A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and diff --git a/src/openai/types/completion_choice.py b/src/openai/types/completion_choice.py index 71de0f9247..7b08582bfd 100644 --- a/src/openai/types/completion_choice.py +++ b/src/openai/types/completion_choice.py @@ -30,6 +30,6 @@ class CompletionChoice(BaseModel): index: int - logprobs: Optional[Logprobs] + logprobs: Optional[Logprobs] = None text: str diff --git a/src/openai/types/fine_tune.py b/src/openai/types/fine_tune.py index de1e097ee4..d1a063a065 100644 --- a/src/openai/types/fine_tune.py +++ b/src/openai/types/fine_tune.py @@ -50,7 +50,7 @@ class FineTune(BaseModel): created_at: int """The Unix timestamp (in seconds) for when the fine-tuning job was created.""" - fine_tuned_model: Optional[str] + fine_tuned_model: Optional[str] = None """The name of the fine-tuned model that is being created.""" hyperparams: Hyperparams diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index 3897176a47..5aa4f07eb1 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -15,7 +15,7 @@ class Error(BaseModel): message: str """A human-readable error message.""" - param: Optional[str] + param: Optional[str] = None """The parameter that was invalid, usually `training_file` or `validation_file`. This field will be null if the failure was not parameter-specific. @@ -39,19 +39,19 @@ class FineTuningJob(BaseModel): created_at: int """The Unix timestamp (in seconds) for when the fine-tuning job was created.""" - error: Optional[Error] + error: Optional[Error] = None """ For fine-tuning jobs that have `failed`, this will contain more information on the cause of the failure. """ - fine_tuned_model: Optional[str] + fine_tuned_model: Optional[str] = None """The name of the fine-tuned model that is being created. The value will be null if the fine-tuning job is still running. """ - finished_at: Optional[int] + finished_at: Optional[int] = None """The Unix timestamp (in seconds) for when the fine-tuning job was finished. The value will be null if the fine-tuning job is still running. @@ -86,7 +86,7 @@ class FineTuningJob(BaseModel): `validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`. """ - trained_tokens: Optional[int] + trained_tokens: Optional[int] = None """The total number of billable tokens processed by this fine-tuning job. The value will be null if the fine-tuning job is still running. @@ -99,7 +99,7 @@ class FineTuningJob(BaseModel): [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents). """ - validation_file: Optional[str] + validation_file: Optional[str] = None """The file ID used for validation. You can retrieve the validation results with the From 4aa7cbee29591c1db8b0778c76fe581ce4122dbc Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:26:07 -0500 Subject: [PATCH 282/914] chore: use property declarations for resource members (#1047) This will speedup client instantiation in certain cases. --- pyproject.toml | 1 + src/openai/_compat.py | 10 ++++ src/openai/resources/audio/audio.py | 56 ++++++++++--------- src/openai/resources/audio/speech.py | 22 +++----- src/openai/resources/audio/transcriptions.py | 22 +++----- src/openai/resources/audio/translations.py | 22 +++----- .../resources/beta/assistants/assistants.py | 30 +++++----- src/openai/resources/beta/assistants/files.py | 28 +++------- src/openai/resources/beta/beta.py | 46 +++++++-------- .../resources/beta/threads/messages/files.py | 21 +++---- .../beta/threads/messages/messages.py | 37 +++++------- .../resources/beta/threads/runs/runs.py | 30 +++++----- .../resources/beta/threads/runs/steps.py | 21 +++---- src/openai/resources/beta/threads/threads.py | 46 ++++++++------- src/openai/resources/chat/chat.py | 30 +++++----- src/openai/resources/chat/completions.py | 22 +++----- src/openai/resources/completions.py | 22 +++----- src/openai/resources/edits.py | 22 +++----- src/openai/resources/embeddings.py | 22 +++----- src/openai/resources/files.py | 29 +++------- src/openai/resources/fine_tunes.py | 22 +++----- .../resources/fine_tuning/fine_tuning.py | 30 +++++----- src/openai/resources/fine_tuning/jobs.py | 22 +++----- src/openai/resources/images.py | 22 +++----- src/openai/resources/models.py | 22 +++----- src/openai/resources/moderations.py | 22 +++----- 26 files changed, 285 insertions(+), 394 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a4eba4a0d9..64f90ae1b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ dependencies = [ "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", + "cached-property; python_version < '3.8'", "tqdm > 4" ] requires-python = ">= 3.7.1" diff --git a/src/openai/_compat.py b/src/openai/_compat.py index d95db8ed1e..3cda39909b 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -173,3 +173,13 @@ class GenericModel(pydantic.BaseModel): class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + + +# cached properties +if TYPE_CHECKING: + cached_property = property +else: + try: + from functools import cached_property as cached_property + except ImportError: + from cached_property import cached_property as cached_property diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 6b9242f0c2..4e3ca0ed4f 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -2,9 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse +from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse from .transcriptions import ( @@ -14,38 +13,43 @@ AsyncTranscriptionsWithRawResponse, ) -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI - __all__ = ["Audio", "AsyncAudio"] class Audio(SyncAPIResource): - transcriptions: Transcriptions - translations: Translations - speech: Speech - with_raw_response: AudioWithRawResponse + @cached_property + def transcriptions(self) -> Transcriptions: + return Transcriptions(self._client) + + @cached_property + def translations(self) -> Translations: + return Translations(self._client) + + @cached_property + def speech(self) -> Speech: + return Speech(self._client) - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.transcriptions = Transcriptions(client) - self.translations = Translations(client) - self.speech = Speech(client) - self.with_raw_response = AudioWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AudioWithRawResponse: + return AudioWithRawResponse(self) class AsyncAudio(AsyncAPIResource): - transcriptions: AsyncTranscriptions - translations: AsyncTranslations - speech: AsyncSpeech - with_raw_response: AsyncAudioWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.transcriptions = AsyncTranscriptions(client) - self.translations = AsyncTranslations(client) - self.speech = AsyncSpeech(client) - self.with_raw_response = AsyncAudioWithRawResponse(self) + @cached_property + def transcriptions(self) -> AsyncTranscriptions: + return AsyncTranscriptions(self._client) + + @cached_property + def translations(self) -> AsyncTranslations: + return AsyncTranslations(self._client) + + @cached_property + def speech(self) -> AsyncSpeech: + return AsyncSpeech(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAudioWithRawResponse: + return AsyncAudioWithRawResponse(self) class AudioWithRawResponse: diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 7ae552c12f..49fded960d 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Union +from typing import Union from typing_extensions import Literal import httpx @@ -15,6 +15,7 @@ NotGiven, ) from ..._utils import maybe_transform +from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import speech_create_params @@ -23,18 +24,13 @@ make_request_options, ) -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI - __all__ = ["Speech", "AsyncSpeech"] class Speech(SyncAPIResource): - with_raw_response: SpeechWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = SpeechWithRawResponse(self) + @cached_property + def with_raw_response(self) -> SpeechWithRawResponse: + return SpeechWithRawResponse(self) def create( self, @@ -99,11 +95,9 @@ def create( class AsyncSpeech(AsyncAPIResource): - with_raw_response: AsyncSpeechWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncSpeechWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncSpeechWithRawResponse: + return AsyncSpeechWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 54be1c99a6..f211678928 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Union, Mapping, cast +from typing import Union, Mapping, cast from typing_extensions import Literal import httpx @@ -16,6 +16,7 @@ FileTypes, ) from ..._utils import extract_files, maybe_transform, deepcopy_minimal +from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import Transcription, transcription_create_params @@ -23,18 +24,13 @@ make_request_options, ) -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI - __all__ = ["Transcriptions", "AsyncTranscriptions"] class Transcriptions(SyncAPIResource): - with_raw_response: TranscriptionsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = TranscriptionsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> TranscriptionsWithRawResponse: + return TranscriptionsWithRawResponse(self) def create( self, @@ -117,11 +113,9 @@ def create( class AsyncTranscriptions(AsyncAPIResource): - with_raw_response: AsyncTranscriptionsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncTranscriptionsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncTranscriptionsWithRawResponse: + return AsyncTranscriptionsWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index c4489004ac..402ec8ac1e 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Union, Mapping, cast +from typing import Union, Mapping, cast from typing_extensions import Literal import httpx @@ -16,6 +16,7 @@ FileTypes, ) from ..._utils import extract_files, maybe_transform, deepcopy_minimal +from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...types.audio import Translation, translation_create_params @@ -23,18 +24,13 @@ make_request_options, ) -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI - __all__ = ["Translations", "AsyncTranslations"] class Translations(SyncAPIResource): - with_raw_response: TranslationsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = TranslationsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> TranslationsWithRawResponse: + return TranslationsWithRawResponse(self) def create( self, @@ -110,11 +106,9 @@ def create( class AsyncTranslations(AsyncAPIResource): - with_raw_response: AsyncTranslationsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncTranslationsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncTranslationsWithRawResponse: + return AsyncTranslationsWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 13b90ac69c..064ca1197c 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Optional +from typing import List, Optional from typing_extensions import Literal import httpx @@ -16,6 +16,7 @@ NotGiven, ) from ...._utils import maybe_transform +from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage @@ -31,20 +32,17 @@ make_request_options, ) -if TYPE_CHECKING: - from ...._client import OpenAI, AsyncOpenAI - __all__ = ["Assistants", "AsyncAssistants"] class Assistants(SyncAPIResource): - files: Files - with_raw_response: AssistantsWithRawResponse + @cached_property + def files(self) -> Files: + return Files(self._client) - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.files = Files(client) - self.with_raw_response = AssistantsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AssistantsWithRawResponse: + return AssistantsWithRawResponse(self) def create( self, @@ -331,13 +329,13 @@ def delete( class AsyncAssistants(AsyncAPIResource): - files: AsyncFiles - with_raw_response: AsyncAssistantsWithRawResponse + @cached_property + def files(self) -> AsyncFiles: + return AsyncFiles(self._client) - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.files = AsyncFiles(client) - self.with_raw_response = AsyncAssistantsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncAssistantsWithRawResponse: + return AsyncAssistantsWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index 5682587487..f8a665b75c 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -2,7 +2,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING from typing_extensions import Literal import httpx @@ -15,6 +14,7 @@ NotGiven, ) from ...._utils import maybe_transform +from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage @@ -22,25 +22,15 @@ AsyncPaginator, make_request_options, ) -from ....types.beta.assistants import ( - AssistantFile, - FileDeleteResponse, - file_list_params, - file_create_params, -) - -if TYPE_CHECKING: - from ...._client import OpenAI, AsyncOpenAI +from ....types.beta.assistants import AssistantFile, FileDeleteResponse, file_list_params, file_create_params __all__ = ["Files", "AsyncFiles"] class Files(SyncAPIResource): - with_raw_response: FilesWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = FilesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self) def create( self, @@ -215,11 +205,9 @@ def delete( class AsyncFiles(AsyncAPIResource): - with_raw_response: AsyncFilesWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncFilesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 5cea6c1460..d87406ac9d 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -2,40 +2,42 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse +from ..._compat import cached_property from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse from ..._resource import SyncAPIResource, AsyncAPIResource - -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI +from .threads.threads import Threads, AsyncThreads +from .assistants.assistants import Assistants, AsyncAssistants __all__ = ["Beta", "AsyncBeta"] class Beta(SyncAPIResource): - assistants: Assistants - threads: Threads - with_raw_response: BetaWithRawResponse + @cached_property + def assistants(self) -> Assistants: + return Assistants(self._client) + + @cached_property + def threads(self) -> Threads: + return Threads(self._client) - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.assistants = Assistants(client) - self.threads = Threads(client) - self.with_raw_response = BetaWithRawResponse(self) + @cached_property + def with_raw_response(self) -> BetaWithRawResponse: + return BetaWithRawResponse(self) class AsyncBeta(AsyncAPIResource): - assistants: AsyncAssistants - threads: AsyncThreads - with_raw_response: AsyncBetaWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.assistants = AsyncAssistants(client) - self.threads = AsyncThreads(client) - self.with_raw_response = AsyncBetaWithRawResponse(self) + @cached_property + def assistants(self) -> AsyncAssistants: + return AsyncAssistants(self._client) + + @cached_property + def threads(self) -> AsyncThreads: + return AsyncThreads(self._client) + + @cached_property + def with_raw_response(self) -> AsyncBetaWithRawResponse: + return AsyncBetaWithRawResponse(self) class BetaWithRawResponse: diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index 24c9680f3d..d0c8c7f0ae 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -2,7 +2,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING from typing_extensions import Literal import httpx @@ -15,6 +14,7 @@ NotGiven, ) from ....._utils import maybe_transform +from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage @@ -24,18 +24,13 @@ ) from .....types.beta.threads.messages import MessageFile, file_list_params -if TYPE_CHECKING: - from ....._client import OpenAI, AsyncOpenAI - __all__ = ["Files", "AsyncFiles"] class Files(SyncAPIResource): - with_raw_response: FilesWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = FilesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self) def retrieve( self, @@ -140,11 +135,9 @@ def list( class AsyncFiles(AsyncAPIResource): - with_raw_response: AsyncFilesWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncFilesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self) async def retrieve( self, diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 9a6f5706c3..7adc8b7829 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Optional +from typing import List, Optional from typing_extensions import Literal import httpx @@ -16,6 +16,7 @@ NotGiven, ) from ....._utils import maybe_transform +from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage @@ -23,27 +24,19 @@ AsyncPaginator, make_request_options, ) -from .....types.beta.threads import ( - ThreadMessage, - message_list_params, - message_create_params, - message_update_params, -) - -if TYPE_CHECKING: - from ....._client import OpenAI, AsyncOpenAI +from .....types.beta.threads import ThreadMessage, message_list_params, message_create_params, message_update_params __all__ = ["Messages", "AsyncMessages"] class Messages(SyncAPIResource): - files: Files - with_raw_response: MessagesWithRawResponse + @cached_property + def files(self) -> Files: + return Files(self._client) - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.files = Files(client) - self.with_raw_response = MessagesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> MessagesWithRawResponse: + return MessagesWithRawResponse(self) def create( self, @@ -245,13 +238,13 @@ def list( class AsyncMessages(AsyncAPIResource): - files: AsyncFiles - with_raw_response: AsyncMessagesWithRawResponse + @cached_property + def files(self) -> AsyncFiles: + return AsyncFiles(self._client) - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.files = AsyncFiles(client) - self.with_raw_response = AsyncMessagesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncMessagesWithRawResponse: + return AsyncMessagesWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 6a727b856b..902d3f3f92 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Optional +from typing import List, Optional from typing_extensions import Literal import httpx @@ -16,6 +16,7 @@ NotGiven, ) from ....._utils import maybe_transform +from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage @@ -31,20 +32,17 @@ run_submit_tool_outputs_params, ) -if TYPE_CHECKING: - from ....._client import OpenAI, AsyncOpenAI - __all__ = ["Runs", "AsyncRuns"] class Runs(SyncAPIResource): - steps: Steps - with_raw_response: RunsWithRawResponse + @cached_property + def steps(self) -> Steps: + return Steps(self._client) - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.steps = Steps(client) - self.with_raw_response = RunsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> RunsWithRawResponse: + return RunsWithRawResponse(self) def create( self, @@ -335,13 +333,13 @@ def submit_tool_outputs( class AsyncRuns(AsyncAPIResource): - steps: AsyncSteps - with_raw_response: AsyncRunsWithRawResponse + @cached_property + def steps(self) -> AsyncSteps: + return AsyncSteps(self._client) - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.steps = AsyncSteps(client) - self.with_raw_response = AsyncRunsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncRunsWithRawResponse: + return AsyncRunsWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index f26034cf82..ff218a4beb 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -2,7 +2,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING from typing_extensions import Literal import httpx @@ -15,6 +14,7 @@ NotGiven, ) from ....._utils import maybe_transform +from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage @@ -24,18 +24,13 @@ ) from .....types.beta.threads.runs import RunStep, step_list_params -if TYPE_CHECKING: - from ....._client import OpenAI, AsyncOpenAI - __all__ = ["Steps", "AsyncSteps"] class Steps(SyncAPIResource): - with_raw_response: StepsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = StepsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> StepsWithRawResponse: + return StepsWithRawResponse(self) def retrieve( self, @@ -139,11 +134,9 @@ def list( class AsyncSteps(AsyncAPIResource): - with_raw_response: AsyncStepsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncStepsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncStepsWithRawResponse: + return AsyncStepsWithRawResponse(self) async def retrieve( self, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index b37667485d..caae758416 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Optional +from typing import List, Optional import httpx @@ -16,6 +16,8 @@ NotGiven, ) from ...._utils import maybe_transform +from .runs.runs import Runs, AsyncRuns +from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ....types.beta import ( @@ -28,24 +30,24 @@ from ...._base_client import ( make_request_options, ) +from .messages.messages import Messages, AsyncMessages from ....types.beta.threads import Run -if TYPE_CHECKING: - from ...._client import OpenAI, AsyncOpenAI - __all__ = ["Threads", "AsyncThreads"] class Threads(SyncAPIResource): - runs: Runs - messages: Messages - with_raw_response: ThreadsWithRawResponse + @cached_property + def runs(self) -> Runs: + return Runs(self._client) + + @cached_property + def messages(self) -> Messages: + return Messages(self._client) - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.runs = Runs(client) - self.messages = Messages(client) - self.with_raw_response = ThreadsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> ThreadsWithRawResponse: + return ThreadsWithRawResponse(self) def create( self, @@ -270,15 +272,17 @@ def create_and_run( class AsyncThreads(AsyncAPIResource): - runs: AsyncRuns - messages: AsyncMessages - with_raw_response: AsyncThreadsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.runs = AsyncRuns(client) - self.messages = AsyncMessages(client) - self.with_raw_response = AsyncThreadsWithRawResponse(self) + @cached_property + def runs(self) -> AsyncRuns: + return AsyncRuns(self._client) + + @cached_property + def messages(self) -> AsyncMessages: + return AsyncMessages(self._client) + + @cached_property + def with_raw_response(self) -> AsyncThreadsWithRawResponse: + return AsyncThreadsWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index d93a501b1f..000520de23 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -2,35 +2,31 @@ from __future__ import annotations -from typing import TYPE_CHECKING - +from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI - __all__ = ["Chat", "AsyncChat"] class Chat(SyncAPIResource): - completions: Completions - with_raw_response: ChatWithRawResponse + @cached_property + def completions(self) -> Completions: + return Completions(self._client) - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.completions = Completions(client) - self.with_raw_response = ChatWithRawResponse(self) + @cached_property + def with_raw_response(self) -> ChatWithRawResponse: + return ChatWithRawResponse(self) class AsyncChat(AsyncAPIResource): - completions: AsyncCompletions - with_raw_response: AsyncChatWithRawResponse + @cached_property + def completions(self) -> AsyncCompletions: + return AsyncCompletions(self._client) - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.completions = AsyncCompletions(client) - self.with_raw_response = AsyncChatWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncChatWithRawResponse: + return AsyncChatWithRawResponse(self) class ChatWithRawResponse: diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 6bde8383dc..81dff146c8 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Dict, List, Union, Optional, overload +from typing import Dict, List, Union, Optional, overload from typing_extensions import Literal import httpx @@ -15,6 +15,7 @@ NotGiven, ) from ..._utils import required_args, maybe_transform +from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..._streaming import Stream, AsyncStream @@ -30,18 +31,13 @@ make_request_options, ) -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI - __all__ = ["Completions", "AsyncCompletions"] class Completions(SyncAPIResource): - with_raw_response: CompletionsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = CompletionsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> CompletionsWithRawResponse: + return CompletionsWithRawResponse(self) @overload def create( @@ -687,11 +683,9 @@ def create( class AsyncCompletions(AsyncAPIResource): - with_raw_response: AsyncCompletionsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncCompletionsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncCompletionsWithRawResponse: + return AsyncCompletionsWithRawResponse(self) @overload async def create( diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index a13c901529..1339c34472 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Dict, List, Union, Optional, overload +from typing import Dict, List, Union, Optional, overload from typing_extensions import Literal import httpx @@ -16,6 +16,7 @@ NotGiven, ) from .._utils import required_args, maybe_transform +from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._streaming import Stream, AsyncStream @@ -23,18 +24,13 @@ make_request_options, ) -if TYPE_CHECKING: - from .._client import OpenAI, AsyncOpenAI - __all__ = ["Completions", "AsyncCompletions"] class Completions(SyncAPIResource): - with_raw_response: CompletionsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = CompletionsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> CompletionsWithRawResponse: + return CompletionsWithRawResponse(self) @overload def create( @@ -601,11 +597,9 @@ def create( class AsyncCompletions(AsyncAPIResource): - with_raw_response: AsyncCompletionsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncCompletionsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncCompletionsWithRawResponse: + return AsyncCompletionsWithRawResponse(self) @overload async def create( diff --git a/src/openai/resources/edits.py b/src/openai/resources/edits.py index 587da02c8f..355a11ac9d 100644 --- a/src/openai/resources/edits.py +++ b/src/openai/resources/edits.py @@ -3,7 +3,7 @@ from __future__ import annotations import typing_extensions -from typing import TYPE_CHECKING, Union, Optional +from typing import Union, Optional from typing_extensions import Literal import httpx @@ -17,24 +17,20 @@ NotGiven, ) from .._utils import maybe_transform +from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._base_client import ( make_request_options, ) -if TYPE_CHECKING: - from .._client import OpenAI, AsyncOpenAI - __all__ = ["Edits", "AsyncEdits"] class Edits(SyncAPIResource): - with_raw_response: EditsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = EditsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> EditsWithRawResponse: + return EditsWithRawResponse(self) @typing_extensions.deprecated( "The Edits API is deprecated; please use Chat Completions instead.\n\nhttps://openai.com/blog/gpt-4-api-general-availability#deprecation-of-the-edits-api\n" @@ -109,11 +105,9 @@ def create( class AsyncEdits(AsyncAPIResource): - with_raw_response: AsyncEditsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncEditsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncEditsWithRawResponse: + return AsyncEditsWithRawResponse(self) @typing_extensions.deprecated( "The Edits API is deprecated; please use Chat Completions instead.\n\nhttps://openai.com/blog/gpt-4-api-general-availability#deprecation-of-the-edits-api\n" diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index f22acad401..409f5832fc 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -3,7 +3,7 @@ from __future__ import annotations import base64 -from typing import TYPE_CHECKING, List, Union, cast +from typing import List, Union, cast from typing_extensions import Literal import httpx @@ -17,6 +17,7 @@ NotGiven, ) from .._utils import is_given, maybe_transform +from .._compat import cached_property from .._extras import numpy as np, has_numpy from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper @@ -24,18 +25,13 @@ make_request_options, ) -if TYPE_CHECKING: - from .._client import OpenAI, AsyncOpenAI - __all__ = ["Embeddings", "AsyncEmbeddings"] class Embeddings(SyncAPIResource): - with_raw_response: EmbeddingsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = EmbeddingsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> EmbeddingsWithRawResponse: + return EmbeddingsWithRawResponse(self) def create( self, @@ -125,11 +121,9 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: class AsyncEmbeddings(AsyncAPIResource): - with_raw_response: AsyncEmbeddingsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncEmbeddingsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncEmbeddingsWithRawResponse: + return AsyncEmbeddingsWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index bc7823783b..b8ffaf64d0 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -4,17 +4,12 @@ import time import typing_extensions -from typing import TYPE_CHECKING, Mapping, cast +from typing import Mapping, cast from typing_extensions import Literal import httpx -from ..types import ( - FileObject, - FileDeleted, - file_list_params, - file_create_params, -) +from ..types import FileObject, FileDeleted, file_list_params, file_create_params from .._types import ( NOT_GIVEN, Body, @@ -24,6 +19,7 @@ FileTypes, ) from .._utils import extract_files, maybe_transform, deepcopy_minimal +from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncPage, AsyncPage @@ -33,18 +29,13 @@ make_request_options, ) -if TYPE_CHECKING: - from .._client import OpenAI, AsyncOpenAI - __all__ = ["Files", "AsyncFiles"] class Files(SyncAPIResource): - with_raw_response: FilesWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = FilesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self) def create( self, @@ -304,11 +295,9 @@ def wait_for_processing( class AsyncFiles(AsyncAPIResource): - with_raw_response: AsyncFilesWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncFilesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/fine_tunes.py b/src/openai/resources/fine_tunes.py index f50d78717b..1c4a3057ac 100644 --- a/src/openai/resources/fine_tunes.py +++ b/src/openai/resources/fine_tunes.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Union, Optional, overload +from typing import List, Union, Optional, overload from typing_extensions import Literal import httpx @@ -22,6 +22,7 @@ NotGiven, ) from .._utils import maybe_transform +from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._streaming import Stream, AsyncStream @@ -31,18 +32,13 @@ make_request_options, ) -if TYPE_CHECKING: - from .._client import OpenAI, AsyncOpenAI - __all__ = ["FineTunes", "AsyncFineTunes"] class FineTunes(SyncAPIResource): - with_raw_response: FineTunesWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = FineTunesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> FineTunesWithRawResponse: + return FineTunesWithRawResponse(self) def create( self, @@ -416,11 +412,9 @@ def list_events( class AsyncFineTunes(AsyncAPIResource): - with_raw_response: AsyncFineTunesWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncFineTunesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncFineTunesWithRawResponse: + return AsyncFineTunesWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index 2e5f36e546..a5a68b08eb 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -2,35 +2,31 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse +from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI - __all__ = ["FineTuning", "AsyncFineTuning"] class FineTuning(SyncAPIResource): - jobs: Jobs - with_raw_response: FineTuningWithRawResponse + @cached_property + def jobs(self) -> Jobs: + return Jobs(self._client) - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.jobs = Jobs(client) - self.with_raw_response = FineTuningWithRawResponse(self) + @cached_property + def with_raw_response(self) -> FineTuningWithRawResponse: + return FineTuningWithRawResponse(self) class AsyncFineTuning(AsyncAPIResource): - jobs: AsyncJobs - with_raw_response: AsyncFineTuningWithRawResponse + @cached_property + def jobs(self) -> AsyncJobs: + return AsyncJobs(self._client) - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.jobs = AsyncJobs(client) - self.with_raw_response = AsyncFineTuningWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncFineTuningWithRawResponse: + return AsyncFineTuningWithRawResponse(self) class FineTuningWithRawResponse: diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 55eee67044..98615cdfec 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Union, Optional +from typing import Union, Optional from typing_extensions import Literal import httpx @@ -15,6 +15,7 @@ NotGiven, ) from ..._utils import maybe_transform +from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage @@ -30,18 +31,13 @@ job_list_events_params, ) -if TYPE_CHECKING: - from ..._client import OpenAI, AsyncOpenAI - __all__ = ["Jobs", "AsyncJobs"] class Jobs(SyncAPIResource): - with_raw_response: JobsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = JobsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> JobsWithRawResponse: + return JobsWithRawResponse(self) def create( self, @@ -289,11 +285,9 @@ def list_events( class AsyncJobs(AsyncAPIResource): - with_raw_response: AsyncJobsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncJobsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncJobsWithRawResponse: + return AsyncJobsWithRawResponse(self) async def create( self, diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 0e1313078f..365bd37c06 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Union, Mapping, Optional, cast +from typing import Union, Mapping, Optional, cast from typing_extensions import Literal import httpx @@ -22,24 +22,20 @@ FileTypes, ) from .._utils import extract_files, maybe_transform, deepcopy_minimal +from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._base_client import ( make_request_options, ) -if TYPE_CHECKING: - from .._client import OpenAI, AsyncOpenAI - __all__ = ["Images", "AsyncImages"] class Images(SyncAPIResource): - with_raw_response: ImagesWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = ImagesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> ImagesWithRawResponse: + return ImagesWithRawResponse(self) def create_variation( self, @@ -280,11 +276,9 @@ def generate( class AsyncImages(AsyncAPIResource): - with_raw_response: AsyncImagesWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncImagesWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncImagesWithRawResponse: + return AsyncImagesWithRawResponse(self) async def create_variation( self, diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index a44a7ffbb0..2950e733eb 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -2,8 +2,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import httpx from ..types import Model, ModelDeleted @@ -14,6 +12,7 @@ Headers, NotGiven, ) +from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncPage, AsyncPage @@ -22,18 +21,13 @@ make_request_options, ) -if TYPE_CHECKING: - from .._client import OpenAI, AsyncOpenAI - __all__ = ["Models", "AsyncModels"] class Models(SyncAPIResource): - with_raw_response: ModelsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = ModelsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> ModelsWithRawResponse: + return ModelsWithRawResponse(self) def retrieve( self, @@ -125,11 +119,9 @@ def delete( class AsyncModels(AsyncAPIResource): - with_raw_response: AsyncModelsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncModelsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncModelsWithRawResponse: + return AsyncModelsWithRawResponse(self) async def retrieve( self, diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 9de7cd640f..cb27f48467 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Union +from typing import List, Union from typing_extensions import Literal import httpx @@ -16,24 +16,20 @@ NotGiven, ) from .._utils import maybe_transform +from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from .._base_client import ( make_request_options, ) -if TYPE_CHECKING: - from .._client import OpenAI, AsyncOpenAI - __all__ = ["Moderations", "AsyncModerations"] class Moderations(SyncAPIResource): - with_raw_response: ModerationsWithRawResponse - - def __init__(self, client: OpenAI) -> None: - super().__init__(client) - self.with_raw_response = ModerationsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> ModerationsWithRawResponse: + return ModerationsWithRawResponse(self) def create( self, @@ -87,11 +83,9 @@ def create( class AsyncModerations(AsyncAPIResource): - with_raw_response: AsyncModerationsWithRawResponse - - def __init__(self, client: AsyncOpenAI) -> None: - super().__init__(client) - self.with_raw_response = AsyncModerationsWithRawResponse(self) + @cached_property + def with_raw_response(self) -> AsyncModerationsWithRawResponse: + return AsyncModerationsWithRawResponse(self) async def create( self, From d0a20e9e6aca5dfd121f5f99bbafaccb47498cc4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 5 Jan 2024 18:07:54 -0500 Subject: [PATCH 283/914] chore(internal): loosen type var restrictions (#1049) --- src/openai/_base_client.py | 41 +++++++++---------- src/openai/_response.py | 4 +- src/openai/_types.py | 17 +++++--- src/openai/pagination.py | 29 ++++++------- src/openai/resources/audio/speech.py | 8 +--- src/openai/resources/audio/transcriptions.py | 9 +--- src/openai/resources/audio/translations.py | 9 +--- .../resources/beta/assistants/assistants.py | 8 +--- src/openai/resources/beta/assistants/files.py | 8 +--- .../resources/beta/threads/messages/files.py | 8 +--- .../beta/threads/messages/messages.py | 8 +--- .../resources/beta/threads/runs/runs.py | 8 +--- .../resources/beta/threads/runs/steps.py | 8 +--- src/openai/resources/beta/threads/threads.py | 8 +--- src/openai/resources/chat/completions.py | 8 +--- src/openai/resources/completions.py | 8 +--- src/openai/resources/edits.py | 8 +--- src/openai/resources/embeddings.py | 8 +--- src/openai/resources/files.py | 9 +--- src/openai/resources/fine_tunes.py | 8 +--- src/openai/resources/fine_tuning/jobs.py | 8 +--- src/openai/resources/images.py | 9 +--- src/openai/resources/models.py | 8 +--- src/openai/resources/moderations.py | 8 +--- 24 files changed, 67 insertions(+), 188 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 53a53d8016..97c6bef913 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -48,7 +48,6 @@ Body, Omit, Query, - ModelT, Headers, Timeout, NotGiven, @@ -61,7 +60,6 @@ HttpxSendArgs, AsyncTransport, RequestOptions, - UnknownResponse, ModelBuilderProtocol, BinaryResponseContent, ) @@ -142,7 +140,7 @@ def __init__( self.params = params -class BasePage(GenericModel, Generic[ModelT]): +class BasePage(GenericModel, Generic[_T]): """ Defines the core interface for pagination. @@ -155,7 +153,7 @@ class BasePage(GenericModel, Generic[ModelT]): """ _options: FinalRequestOptions = PrivateAttr() - _model: Type[ModelT] = PrivateAttr() + _model: Type[_T] = PrivateAttr() def has_next_page(self) -> bool: items = self._get_page_items() @@ -166,7 +164,7 @@ def has_next_page(self) -> bool: def next_page_info(self) -> Optional[PageInfo]: ... - def _get_page_items(self) -> Iterable[ModelT]: # type: ignore[empty-body] + def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] ... def _params_from_url(http://github.com/self,%20url:%20URL) -> httpx.QueryParams: @@ -191,13 +189,13 @@ def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: raise ValueError("Unexpected PageInfo state") -class BaseSyncPage(BasePage[ModelT], Generic[ModelT]): +class BaseSyncPage(BasePage[_T], Generic[_T]): _client: SyncAPIClient = pydantic.PrivateAttr() def _set_private_attributes( self, client: SyncAPIClient, - model: Type[ModelT], + model: Type[_T], options: FinalRequestOptions, ) -> None: self._model = model @@ -212,7 +210,7 @@ def _set_private_attributes( # methods should continue to work as expected as there is an alternative method # to cast a model to a dictionary, model.dict(), which is used internally # by pydantic. - def __iter__(self) -> Iterator[ModelT]: # type: ignore + def __iter__(self) -> Iterator[_T]: # type: ignore for page in self.iter_pages(): for item in page._get_page_items(): yield item @@ -237,13 +235,13 @@ def get_next_page(self: SyncPageT) -> SyncPageT: return self._client._request_api_list(self._model, page=self.__class__, options=options) -class AsyncPaginator(Generic[ModelT, AsyncPageT]): +class AsyncPaginator(Generic[_T, AsyncPageT]): def __init__( self, client: AsyncAPIClient, options: FinalRequestOptions, page_cls: Type[AsyncPageT], - model: Type[ModelT], + model: Type[_T], ) -> None: self._model = model self._client = client @@ -266,7 +264,7 @@ def _parser(resp: AsyncPageT) -> AsyncPageT: return await self._client.request(self._page_cls, self._options) - async def __aiter__(self) -> AsyncIterator[ModelT]: + async def __aiter__(self) -> AsyncIterator[_T]: # https://github.com/microsoft/pyright/issues/3464 page = cast( AsyncPageT, @@ -276,12 +274,12 @@ async def __aiter__(self) -> AsyncIterator[ModelT]: yield item -class BaseAsyncPage(BasePage[ModelT], Generic[ModelT]): +class BaseAsyncPage(BasePage[_T], Generic[_T]): _client: AsyncAPIClient = pydantic.PrivateAttr() def _set_private_attributes( self, - model: Type[ModelT], + model: Type[_T], client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: @@ -289,7 +287,7 @@ def _set_private_attributes( self._client = client self._options = options - async def __aiter__(self) -> AsyncIterator[ModelT]: + async def __aiter__(self) -> AsyncIterator[_T]: async for page in self.iter_pages(): for item in page._get_page_items(): yield item @@ -528,7 +526,7 @@ def _process_response_data( if data is None: return cast(ResponseT, None) - if cast_to is UnknownResponse: + if cast_to is object: return cast(ResponseT, data) try: @@ -970,7 +968,7 @@ def _retry_request( def _request_api_list( self, - model: Type[ModelT], + model: Type[object], page: Type[SyncPageT], options: FinalRequestOptions, ) -> SyncPageT: @@ -1132,7 +1130,7 @@ def get_api_list( self, path: str, *, - model: Type[ModelT], + model: Type[object], page: Type[SyncPageT], body: Body | None = None, options: RequestOptions = {}, @@ -1434,10 +1432,10 @@ async def _retry_request( def _request_api_list( self, - model: Type[ModelT], + model: Type[_T], page: Type[AsyncPageT], options: FinalRequestOptions, - ) -> AsyncPaginator[ModelT, AsyncPageT]: + ) -> AsyncPaginator[_T, AsyncPageT]: return AsyncPaginator(client=self, options=options, page_cls=page, model=model) @overload @@ -1584,13 +1582,12 @@ def get_api_list( self, path: str, *, - # TODO: support paginating `str` - model: Type[ModelT], + model: Type[_T], page: Type[AsyncPageT], body: Body | None = None, options: RequestOptions = {}, method: str = "get", - ) -> AsyncPaginator[ModelT, AsyncPageT]: + ) -> AsyncPaginator[_T, AsyncPageT]: opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) return self._request_api_list(model, page, opts) diff --git a/src/openai/_response.py b/src/openai/_response.py index 6b7c86e544..bf72d18fd5 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -9,7 +9,7 @@ import httpx -from ._types import NoneType, UnknownResponse, BinaryResponseContent +from ._types import NoneType, BinaryResponseContent from ._utils import is_given, extract_type_var_from_base from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER @@ -162,7 +162,7 @@ def _parse(self) -> R: # `ResponseT` TypeVar, however if that TypeVar is ever updated in the future, then # this function would become unsafe but a type checker would not report an error. if ( - cast_to is not UnknownResponse + cast_to is not object and not origin is list and not origin is dict and not origin is Union diff --git a/src/openai/_types.py b/src/openai/_types.py index b52af6882f..e6b83b2a3f 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -258,11 +258,6 @@ class RequestOptions(TypedDict, total=False): idempotency_key: str -# Sentinel class used when the response type is an object with an unknown schema -class UnknownResponse: - ... - - # Sentinel class used until PEP 0661 is accepted class NotGiven: """ @@ -339,7 +334,17 @@ def get(self, __key: str) -> str | None: ResponseT = TypeVar( "ResponseT", - bound="Union[str, None, BaseModel, List[Any], Dict[str, Any], Response, UnknownResponse, ModelBuilderProtocol, BinaryResponseContent]", + bound=Union[ + object, + str, + None, + "BaseModel", + List[Any], + Dict[str, Any], + Response, + ModelBuilderProtocol, + BinaryResponseContent, + ], ) StrBytesIntFloat = Union[str, bytes, int, float] diff --git a/src/openai/pagination.py b/src/openai/pagination.py index d47deb17be..f7527753e1 100644 --- a/src/openai/pagination.py +++ b/src/openai/pagination.py @@ -1,27 +1,28 @@ # File generated from our OpenAPI spec by Stainless. -from typing import Any, List, Generic, Optional, cast +from typing import Any, List, Generic, TypeVar, Optional, cast from typing_extensions import Protocol, override, runtime_checkable -from ._types import ModelT from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage __all__ = ["SyncPage", "AsyncPage", "SyncCursorPage", "AsyncCursorPage"] +_T = TypeVar("_T") + @runtime_checkable class CursorPageItem(Protocol): id: Optional[str] -class SyncPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): +class SyncPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" - data: List[ModelT] + data: List[_T] object: str @override - def _get_page_items(self) -> List[ModelT]: + def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] @@ -36,14 +37,14 @@ def next_page_info(self) -> None: return None -class AsyncPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): +class AsyncPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): """Note: no pagination actually occurs yet, this is for forwards-compatibility.""" - data: List[ModelT] + data: List[_T] object: str @override - def _get_page_items(self) -> List[ModelT]: + def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] @@ -58,11 +59,11 @@ def next_page_info(self) -> None: return None -class SyncCursorPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): - data: List[ModelT] +class SyncCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] @override - def _get_page_items(self) -> List[ModelT]: + def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] @@ -82,11 +83,11 @@ def next_page_info(self) -> Optional[PageInfo]: return PageInfo(params={"after": item.id}) -class AsyncCursorPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]): - data: List[ModelT] +class AsyncCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] @override - def _get_page_items(self) -> List[ModelT]: + def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 49fded960d..b7cd3733a9 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -7,13 +7,7 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index f211678928..7d7441a9f6 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -7,14 +7,7 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - FileTypes, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 402ec8ac1e..7f5f65b6c8 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -7,14 +7,7 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - FileTypes, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 064ca1197c..0ae054795d 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -8,13 +8,7 @@ import httpx from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from ...._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index f8a665b75c..0624e562f8 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -6,13 +6,7 @@ import httpx -from ...._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index d0c8c7f0ae..4b95b200eb 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -6,13 +6,7 @@ import httpx -from ....._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 7adc8b7829..146f665624 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -8,13 +8,7 @@ import httpx from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from ....._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 902d3f3f92..87e62eb362 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -8,13 +8,7 @@ import httpx from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse -from ....._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index ff218a4beb..439926a412 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -6,13 +6,7 @@ import httpx -from ....._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index caae758416..0ae409bb24 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -8,13 +8,7 @@ from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse -from ...._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from .runs.runs import Runs, AsyncRuns from ...._compat import cached_property diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 81dff146c8..b047c1d2a0 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -7,13 +7,7 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import required_args, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 1339c34472..d3e7c54b11 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -8,13 +8,7 @@ import httpx from ..types import Completion, completion_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import required_args, maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/edits.py b/src/openai/resources/edits.py index 355a11ac9d..ac15494263 100644 --- a/src/openai/resources/edits.py +++ b/src/openai/resources/edits.py @@ -9,13 +9,7 @@ import httpx from ..types import Edit, edit_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 409f5832fc..e93b29d45b 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -9,13 +9,7 @@ import httpx from ..types import CreateEmbeddingResponse, embedding_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import is_given, maybe_transform from .._compat import cached_property from .._extras import numpy as np, has_numpy diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index b8ffaf64d0..1acf6f8060 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -10,14 +10,7 @@ import httpx from ..types import FileObject, FileDeleted, file_list_params, file_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - FileTypes, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/fine_tunes.py b/src/openai/resources/fine_tunes.py index 1c4a3057ac..411952387c 100644 --- a/src/openai/resources/fine_tunes.py +++ b/src/openai/resources/fine_tunes.py @@ -14,13 +14,7 @@ fine_tune_create_params, fine_tune_list_events_params, ) -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 98615cdfec..a8f24efce5 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -7,13 +7,7 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 365bd37c06..8e9c288af7 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -13,14 +13,7 @@ image_generate_params, image_create_variation_params, ) -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - FileTypes, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index 2950e733eb..48888d98b5 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -5,13 +5,7 @@ import httpx from ..types import Model, ModelDeleted -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index cb27f48467..120a499186 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -8,13 +8,7 @@ import httpx from ..types import ModerationCreateResponse, moderation_create_params -from .._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, -) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource From 98bf2549c6bfb8a308d347a055d1b81375ca76c8 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:28:02 -0500 Subject: [PATCH 284/914] chore: add .keep files for examples and custom code directories (#1057) --- examples/.keep | 4 ++++ src/openai/lib/.keep | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 examples/.keep create mode 100644 src/openai/lib/.keep diff --git a/examples/.keep b/examples/.keep new file mode 100644 index 0000000000..d8c73e937a --- /dev/null +++ b/examples/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store example files demonstrating usage of this SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/openai/lib/.keep b/src/openai/lib/.keep new file mode 100644 index 0000000000..5e2c99fdbe --- /dev/null +++ b/src/openai/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file From fa931da6db06f8e8102d91bfe3a4a64b31bbd02c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:28:46 -0500 Subject: [PATCH 285/914] release: 1.7.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 59565e8e31..cce9d1c6d3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.6.1" + ".": "1.7.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 83bf20f775..09c81dae8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 1.7.0 (2024-01-08) + +Full Changelog: [v1.6.1...v1.7.0](https://github.com/openai/openai-python/compare/v1.6.1...v1.7.0) + +### Features + +* add `None` default value to nullable response properties ([#1043](https://github.com/openai/openai-python/issues/1043)) ([d94b4d3](https://github.com/openai/openai-python/commit/d94b4d3d0adcd1a49a1c25cc9730cef013a3e9c9)) + + +### Bug Fixes + +* **client:** correctly use custom http client auth ([#1028](https://github.com/openai/openai-python/issues/1028)) ([3d7d93e](https://github.com/openai/openai-python/commit/3d7d93e951eb7fe09cd9d94d10a62a020398c7f9)) + + +### Chores + +* add .keep files for examples and custom code directories ([#1057](https://github.com/openai/openai-python/issues/1057)) ([7524097](https://github.com/openai/openai-python/commit/7524097a47af0fdc8b560186ef3b111b59430741)) +* **internal:** bump license ([#1037](https://github.com/openai/openai-python/issues/1037)) ([d828527](https://github.com/openai/openai-python/commit/d828527540ebd97679075f48744818f06311b0cb)) +* **internal:** loosen type var restrictions ([#1049](https://github.com/openai/openai-python/issues/1049)) ([e00876b](https://github.com/openai/openai-python/commit/e00876b20b93038450eb317899d8775c7661b8eb)) +* **internal:** replace isort with ruff ([#1042](https://github.com/openai/openai-python/issues/1042)) ([f1fbc9c](https://github.com/openai/openai-python/commit/f1fbc9c0d62e7d89ab32c8bdfa39cd94b560690b)) +* **internal:** update formatting ([#1041](https://github.com/openai/openai-python/issues/1041)) ([2e9ecee](https://github.com/openai/openai-python/commit/2e9ecee9bdfa8ec33b1b1527d5187483b700fad3)) +* **src:** fix typos ([#988](https://github.com/openai/openai-python/issues/988)) ([6a8b806](https://github.com/openai/openai-python/commit/6a8b80624636f9a0e5ada151b2509710a6f74808)) +* use property declarations for resource members ([#1047](https://github.com/openai/openai-python/issues/1047)) ([131f6bc](https://github.com/openai/openai-python/commit/131f6bc6b0ccf79119096057079e10906b3d4678)) + + +### Documentation + +* fix docstring typos ([#1022](https://github.com/openai/openai-python/issues/1022)) ([ad3fd2c](https://github.com/openai/openai-python/commit/ad3fd2cd19bf91f94473e368554dff39a8f9ad16)) +* improve audio example to show how to stream to a file ([#1017](https://github.com/openai/openai-python/issues/1017)) ([d45ed7f](https://github.com/openai/openai-python/commit/d45ed7f0513b167555ae875f1877fa205c5790d2)) + ## 1.6.1 (2023-12-22) Full Changelog: [v1.6.0...v1.6.1](https://github.com/openai/openai-python/compare/v1.6.0...v1.6.1) diff --git a/pyproject.toml b/pyproject.toml index 64f90ae1b6..85d19f1d0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.6.1" +version = "1.7.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9ab131d176..aa1cd4305c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.6.1" # x-release-please-version +__version__ = "1.7.0" # x-release-please-version From bac5e4f8a9bd920a22e7dc9e0fb738d8327487ee Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:30:16 -0500 Subject: [PATCH 286/914] chore(client): improve debug logging for failed requests (#1060) --- src/openai/_base_client.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 97c6bef913..c2c2db5f49 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -646,26 +646,33 @@ def _should_retry(self, response: httpx.Response) -> bool: # If the server explicitly says whether or not to retry, obey. if should_retry_header == "true": + log.debug("Retrying as header `x-should-retry` is set to `true`") return True if should_retry_header == "false": + log.debug("Not retrying as header `x-should-retry` is set to `false`") return False # Retry on request timeouts. if response.status_code == 408: + log.debug("Retrying due to status code %i", response.status_code) return True # Retry on lock timeouts. if response.status_code == 409: + log.debug("Retrying due to status code %i", response.status_code) return True # Retry on rate limits. if response.status_code == 429: + log.debug("Retrying due to status code %i", response.status_code) return True # Retry internal errors. if response.status_code >= 500: + log.debug("Retrying due to status code %i", response.status_code) return True + log.debug("Not retrying") return False def _idempotency_key(self) -> str: @@ -883,6 +890,8 @@ def _request( **kwargs, ) except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + if retries > 0: return self._retry_request( options, @@ -893,8 +902,11 @@ def _request( response_headers=None, ) + log.debug("Raising timeout error") raise APITimeoutError(request=request) from err except Exception as err: + log.debug("Encountered Exception", exc_info=True) + if retries > 0: return self._retry_request( options, @@ -905,6 +917,7 @@ def _request( response_headers=None, ) + log.debug("Raising connection error") raise APIConnectionError(request=request) from err log.debug( @@ -914,6 +927,8 @@ def _request( try: response.raise_for_status() except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + if retries > 0 and self._should_retry(err.response): err.response.close() return self._retry_request( @@ -930,6 +945,7 @@ def _request( if not err.response.is_closed: err.response.read() + log.debug("Re-raising status error") raise self._make_status_error_from_response(err.response) from None return self._process_response( @@ -951,6 +967,11 @@ def _retry_request( stream_cls: type[_StreamT] | None, ) -> ResponseT | _StreamT: remaining = remaining_retries - 1 + if remaining == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining) + timeout = self._calculate_retry_timeout(remaining, options, response_headers) log.info("Retrying request to %s in %f seconds", options.url, timeout) @@ -1349,6 +1370,8 @@ async def _request( **kwargs, ) except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + if retries > 0: return await self._retry_request( options, @@ -1359,8 +1382,11 @@ async def _request( response_headers=None, ) + log.debug("Raising timeout error") raise APITimeoutError(request=request) from err except Exception as err: + log.debug("Encountered Exception", exc_info=True) + if retries > 0: return await self._retry_request( options, @@ -1371,6 +1397,7 @@ async def _request( response_headers=None, ) + log.debug("Raising connection error") raise APIConnectionError(request=request) from err log.debug( @@ -1380,6 +1407,8 @@ async def _request( try: response.raise_for_status() except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + if retries > 0 and self._should_retry(err.response): await err.response.aclose() return await self._retry_request( @@ -1396,6 +1425,7 @@ async def _request( if not err.response.is_closed: await err.response.aread() + log.debug("Re-raising status error") raise self._make_status_error_from_response(err.response) from None return self._process_response( @@ -1417,6 +1447,11 @@ async def _retry_request( stream_cls: type[_AsyncStreamT] | None, ) -> ResponseT | _AsyncStreamT: remaining = remaining_retries - 1 + if remaining == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining) + timeout = self._calculate_retry_timeout(remaining, options, response_headers) log.info("Retrying request to %s in %f seconds", options.url, timeout) From 3fb66ddd5e2b6810f9f11619302a8b82d3712f10 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:31:03 -0500 Subject: [PATCH 287/914] release: 1.7.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cce9d1c6d3..5660725203 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.7.0" + ".": "1.7.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 09c81dae8d..19fb9c3e58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.7.1 (2024-01-10) + +Full Changelog: [v1.7.0...v1.7.1](https://github.com/openai/openai-python/compare/v1.7.0...v1.7.1) + +### Chores + +* **client:** improve debug logging for failed requests ([#1060](https://github.com/openai/openai-python/issues/1060)) ([cf9a651](https://github.com/openai/openai-python/commit/cf9a6517b4aa0f24bcbe143c54ea908d43dfda92)) + ## 1.7.0 (2024-01-08) Full Changelog: [v1.6.1...v1.7.0](https://github.com/openai/openai-python/compare/v1.6.1...v1.7.0) diff --git a/pyproject.toml b/pyproject.toml index 85d19f1d0e..9ff951873a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.7.0" +version = "1.7.1" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index aa1cd4305c..b25177f3a5 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.7.0" # x-release-please-version +__version__ = "1.7.1" # x-release-please-version From d253af575402224113020d533d55a640d6bebc6e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 12 Jan 2024 08:45:01 -0500 Subject: [PATCH 288/914] docs(readme): improve api reference (#1065) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f644cdeefe..989f838384 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It is generated from our [OpenAPI specification](https://github.com/openai/opena ## Documentation -The API documentation can be found [here](https://platform.openai.com/docs). +The REST API documentation can be found [on platform.openai.com](https://platform.openai.com/docs). The full API of this library can be found in [api.md](https://www.github.com/openai/openai-python/blob/main/api.md). ## Installation From 8dab14211822297ceb05e270911d9bfcede3141e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 12 Jan 2024 10:02:22 -0500 Subject: [PATCH 289/914] refactor(api): remove deprecated endpoints (#1067) The fine tunes and edits APIs are no longer provided by OpenAI. This is not a breaking change as attempting to call these APIs, even on older versions, will result in an error at runtime. --- .stats.yml | 2 +- README.md | 5 +- api.md | 28 - src/openai/__init__.py | 2 - src/openai/_client.py | 12 - src/openai/_module_client.py | 14 - src/openai/resources/__init__.py | 10 - src/openai/resources/chat/completions.py | 12 +- src/openai/resources/completions.py | 194 +---- src/openai/resources/edits.py | 189 ---- src/openai/resources/fine_tunes.py | 819 ------------------ src/openai/resources/fine_tuning/jobs.py | 6 +- src/openai/types/__init__.py | 7 - .../types/chat/completion_create_params.py | 2 +- src/openai/types/completion_create_params.py | 29 +- src/openai/types/edit.py | 40 - src/openai/types/edit_create_params.py | 44 - src/openai/types/fine_tune.py | 94 -- src/openai/types/fine_tune_create_params.py | 140 --- src/openai/types/fine_tune_event.py | 17 - .../types/fine_tune_events_list_response.py | 15 - .../types/fine_tune_list_events_params.py | 41 - tests/api_resources/test_edits.py | 95 -- tests/api_resources/test_fine_tunes.py | 274 ------ 24 files changed, 59 insertions(+), 2032 deletions(-) delete mode 100644 src/openai/resources/edits.py delete mode 100644 src/openai/resources/fine_tunes.py delete mode 100644 src/openai/types/edit.py delete mode 100644 src/openai/types/edit_create_params.py delete mode 100644 src/openai/types/fine_tune.py delete mode 100644 src/openai/types/fine_tune_create_params.py delete mode 100644 src/openai/types/fine_tune_event.py delete mode 100644 src/openai/types/fine_tune_events_list_response.py delete mode 100644 src/openai/types/fine_tune_list_events_params.py delete mode 100644 tests/api_resources/test_edits.py delete mode 100644 tests/api_resources/test_fine_tunes.py diff --git a/.stats.yml b/.stats.yml index 03b0268ffa..c550abf3c6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 57 +configured_endpoints: 51 diff --git a/README.md b/README.md index 989f838384..e86ac6553e 100644 --- a/README.md +++ b/README.md @@ -296,8 +296,9 @@ from openai import OpenAI client = OpenAI() try: - client.fine_tunes.create( - training_file="file-XGinujblHPwGLSztz8cPS8XY", + client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file="file-abc123", ) except openai.APIConnectionError as e: print("The server could not be reached") diff --git a/api.md b/api.md index 9d9993105b..86b972d14e 100644 --- a/api.md +++ b/api.md @@ -50,18 +50,6 @@ Methods: - client.chat.completions.create(\*\*params) -> ChatCompletion -# Edits - -Types: - -```python -from openai.types import Edit -``` - -Methods: - -- client.edits.create(\*\*params) -> Edit - # Embeddings Types: @@ -182,22 +170,6 @@ Methods: - client.fine_tuning.jobs.cancel(fine_tuning_job_id) -> FineTuningJob - client.fine_tuning.jobs.list_events(fine_tuning_job_id, \*\*params) -> SyncCursorPage[FineTuningJobEvent] -# FineTunes - -Types: - -```python -from openai.types import FineTune, FineTuneEvent, FineTuneEventsListResponse -``` - -Methods: - -- client.fine_tunes.create(\*\*params) -> FineTune -- client.fine_tunes.retrieve(fine_tune_id) -> FineTune -- client.fine_tunes.list() -> SyncPage[FineTune] -- client.fine_tunes.cancel(fine_tune_id) -> FineTune -- client.fine_tunes.list_events(fine_tune_id, \*\*params) -> FineTuneEventsListResponse - # Beta ## Assistants diff --git a/src/openai/__init__.py b/src/openai/__init__.py index ee96f06919..64c93e9449 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -316,12 +316,10 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] beta as beta, chat as chat, audio as audio, - edits as edits, files as files, images as images, models as models, embeddings as embeddings, - fine_tunes as fine_tunes, completions as completions, fine_tuning as fine_tuning, moderations as moderations, diff --git a/src/openai/_client.py b/src/openai/_client.py index 9eb6888909..09f54e1b12 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -49,7 +49,6 @@ class OpenAI(SyncAPIClient): completions: resources.Completions chat: resources.Chat - edits: resources.Edits embeddings: resources.Embeddings files: resources.Files images: resources.Images @@ -57,7 +56,6 @@ class OpenAI(SyncAPIClient): moderations: resources.Moderations models: resources.Models fine_tuning: resources.FineTuning - fine_tunes: resources.FineTunes beta: resources.Beta with_raw_response: OpenAIWithRawResponse @@ -125,7 +123,6 @@ def __init__( self.completions = resources.Completions(self) self.chat = resources.Chat(self) - self.edits = resources.Edits(self) self.embeddings = resources.Embeddings(self) self.files = resources.Files(self) self.images = resources.Images(self) @@ -133,7 +130,6 @@ def __init__( self.moderations = resources.Moderations(self) self.models = resources.Models(self) self.fine_tuning = resources.FineTuning(self) - self.fine_tunes = resources.FineTunes(self) self.beta = resources.Beta(self) self.with_raw_response = OpenAIWithRawResponse(self) @@ -249,7 +245,6 @@ def _make_status_error( class AsyncOpenAI(AsyncAPIClient): completions: resources.AsyncCompletions chat: resources.AsyncChat - edits: resources.AsyncEdits embeddings: resources.AsyncEmbeddings files: resources.AsyncFiles images: resources.AsyncImages @@ -257,7 +252,6 @@ class AsyncOpenAI(AsyncAPIClient): moderations: resources.AsyncModerations models: resources.AsyncModels fine_tuning: resources.AsyncFineTuning - fine_tunes: resources.AsyncFineTunes beta: resources.AsyncBeta with_raw_response: AsyncOpenAIWithRawResponse @@ -325,7 +319,6 @@ def __init__( self.completions = resources.AsyncCompletions(self) self.chat = resources.AsyncChat(self) - self.edits = resources.AsyncEdits(self) self.embeddings = resources.AsyncEmbeddings(self) self.files = resources.AsyncFiles(self) self.images = resources.AsyncImages(self) @@ -333,7 +326,6 @@ def __init__( self.moderations = resources.AsyncModerations(self) self.models = resources.AsyncModels(self) self.fine_tuning = resources.AsyncFineTuning(self) - self.fine_tunes = resources.AsyncFineTunes(self) self.beta = resources.AsyncBeta(self) self.with_raw_response = AsyncOpenAIWithRawResponse(self) @@ -450,7 +442,6 @@ class OpenAIWithRawResponse: def __init__(self, client: OpenAI) -> None: self.completions = resources.CompletionsWithRawResponse(client.completions) self.chat = resources.ChatWithRawResponse(client.chat) - self.edits = resources.EditsWithRawResponse(client.edits) self.embeddings = resources.EmbeddingsWithRawResponse(client.embeddings) self.files = resources.FilesWithRawResponse(client.files) self.images = resources.ImagesWithRawResponse(client.images) @@ -458,7 +449,6 @@ def __init__(self, client: OpenAI) -> None: self.moderations = resources.ModerationsWithRawResponse(client.moderations) self.models = resources.ModelsWithRawResponse(client.models) self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning) - self.fine_tunes = resources.FineTunesWithRawResponse(client.fine_tunes) self.beta = resources.BetaWithRawResponse(client.beta) @@ -466,7 +456,6 @@ class AsyncOpenAIWithRawResponse: def __init__(self, client: AsyncOpenAI) -> None: self.completions = resources.AsyncCompletionsWithRawResponse(client.completions) self.chat = resources.AsyncChatWithRawResponse(client.chat) - self.edits = resources.AsyncEditsWithRawResponse(client.edits) self.embeddings = resources.AsyncEmbeddingsWithRawResponse(client.embeddings) self.files = resources.AsyncFilesWithRawResponse(client.files) self.images = resources.AsyncImagesWithRawResponse(client.images) @@ -474,7 +463,6 @@ def __init__(self, client: AsyncOpenAI) -> None: self.moderations = resources.AsyncModerationsWithRawResponse(client.moderations) self.models = resources.AsyncModelsWithRawResponse(client.models) self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning) - self.fine_tunes = resources.AsyncFineTunesWithRawResponse(client.fine_tunes) self.beta = resources.AsyncBetaWithRawResponse(client.beta) diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index fe8e0a2139..d66e137ecd 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -18,12 +18,6 @@ def __load__(self) -> resources.Beta: return _load_client().beta -class EditsProxy(LazyProxy[resources.Edits]): - @override - def __load__(self) -> resources.Edits: - return _load_client().edits - - class FilesProxy(LazyProxy[resources.Files]): @override def __load__(self) -> resources.Files: @@ -54,12 +48,6 @@ def __load__(self) -> resources.Embeddings: return _load_client().embeddings -class FineTunesProxy(LazyProxy[resources.FineTunes]): - @override - def __load__(self) -> resources.FineTunes: - return _load_client().fine_tunes - - class CompletionsProxy(LazyProxy[resources.Completions]): @override def __load__(self) -> resources.Completions: @@ -80,13 +68,11 @@ def __load__(self) -> resources.FineTuning: chat: resources.Chat = ChatProxy().__as_proxied__() beta: resources.Beta = BetaProxy().__as_proxied__() -edits: resources.Edits = EditsProxy().__as_proxied__() files: resources.Files = FilesProxy().__as_proxied__() audio: resources.Audio = AudioProxy().__as_proxied__() images: resources.Images = ImagesProxy().__as_proxied__() models: resources.Models = ModelsProxy().__as_proxied__() embeddings: resources.Embeddings = EmbeddingsProxy().__as_proxied__() -fine_tunes: resources.FineTunes = FineTunesProxy().__as_proxied__() completions: resources.Completions = CompletionsProxy().__as_proxied__() moderations: resources.Moderations = ModerationsProxy().__as_proxied__() fine_tuning: resources.FineTuning = FineTuningProxy().__as_proxied__() diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index 2cdbeb6ae1..8219be12e6 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -3,12 +3,10 @@ from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse -from .edits import Edits, AsyncEdits, EditsWithRawResponse, AsyncEditsWithRawResponse from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse from .images import Images, AsyncImages, ImagesWithRawResponse, AsyncImagesWithRawResponse from .models import Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse from .embeddings import Embeddings, AsyncEmbeddings, EmbeddingsWithRawResponse, AsyncEmbeddingsWithRawResponse -from .fine_tunes import FineTunes, AsyncFineTunes, FineTunesWithRawResponse, AsyncFineTunesWithRawResponse from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse from .moderations import Moderations, AsyncModerations, ModerationsWithRawResponse, AsyncModerationsWithRawResponse @@ -22,10 +20,6 @@ "AsyncChat", "ChatWithRawResponse", "AsyncChatWithRawResponse", - "Edits", - "AsyncEdits", - "EditsWithRawResponse", - "AsyncEditsWithRawResponse", "Embeddings", "AsyncEmbeddings", "EmbeddingsWithRawResponse", @@ -54,10 +48,6 @@ "AsyncFineTuning", "FineTuningWithRawResponse", "AsyncFineTuningWithRawResponse", - "FineTunes", - "AsyncFineTunes", - "FineTunesWithRawResponse", - "AsyncFineTunesWithRawResponse", "Beta", "AsyncBeta", "BetaWithRawResponse", diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index b047c1d2a0..fa096784d2 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -185,7 +185,7 @@ def create( will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via - `{"type: "function", "function": {"name": "my_function"}}` forces the model to + `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function. `none` is the default when no functions are present. `auto` is the default if @@ -371,7 +371,7 @@ def create( will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via - `{"type: "function", "function": {"name": "my_function"}}` forces the model to + `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function. `none` is the default when no functions are present. `auto` is the default if @@ -557,7 +557,7 @@ def create( will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via - `{"type: "function", "function": {"name": "my_function"}}` forces the model to + `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function. `none` is the default when no functions are present. `auto` is the default if @@ -833,7 +833,7 @@ async def create( will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via - `{"type: "function", "function": {"name": "my_function"}}` forces the model to + `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function. `none` is the default when no functions are present. `auto` is the default if @@ -1019,7 +1019,7 @@ async def create( will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via - `{"type: "function", "function": {"name": "my_function"}}` forces the model to + `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function. `none` is the default when no functions are present. `auto` is the default if @@ -1205,7 +1205,7 @@ async def create( will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via - `{"type: "function", "function": {"name": "my_function"}}` forces the model to + `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function. `none` is the default when no functions are present. `auto` is the default if diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index d3e7c54b11..87dd090052 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -30,21 +30,7 @@ def with_raw_response(self) -> CompletionsWithRawResponse: def create( self, *, - model: Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ], + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, List[str], List[int], List[List[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -107,12 +93,11 @@ def create( Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this - [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to - convert text to token IDs. Mathematically, the bias is added to the logits - generated by the model prior to sampling. The exact effect will vary per model, - but values between -1 and 1 should decrease or increase likelihood of selection; - values like -100 or 100 should result in a ban or exclusive selection of the - relevant token. + [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. + Mathematically, the bias is added to the logits generated by the model prior to + sampling. The exact effect will vary per model, but values between -1 and 1 + should decrease or increase likelihood of selection; values like -100 or 100 + should result in a ban or exclusive selection of the relevant token. As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. @@ -193,21 +178,7 @@ def create( def create( self, *, - model: Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ], + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, List[str], List[int], List[List[int]], None], stream: Literal[True], best_of: Optional[int] | NotGiven = NOT_GIVEN, @@ -277,12 +248,11 @@ def create( Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this - [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to - convert text to token IDs. Mathematically, the bias is added to the logits - generated by the model prior to sampling. The exact effect will vary per model, - but values between -1 and 1 should decrease or increase likelihood of selection; - values like -100 or 100 should result in a ban or exclusive selection of the - relevant token. + [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. + Mathematically, the bias is added to the logits generated by the model prior to + sampling. The exact effect will vary per model, but values between -1 and 1 + should decrease or increase likelihood of selection; values like -100 or 100 + should result in a ban or exclusive selection of the relevant token. As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. @@ -356,21 +326,7 @@ def create( def create( self, *, - model: Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ], + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, List[str], List[int], List[List[int]], None], stream: bool, best_of: Optional[int] | NotGiven = NOT_GIVEN, @@ -440,12 +396,11 @@ def create( Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this - [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to - convert text to token IDs. Mathematically, the bias is added to the logits - generated by the model prior to sampling. The exact effect will vary per model, - but values between -1 and 1 should decrease or increase likelihood of selection; - values like -100 or 100 should result in a ban or exclusive selection of the - relevant token. + [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. + Mathematically, the bias is added to the logits generated by the model prior to + sampling. The exact effect will vary per model, but values between -1 and 1 + should decrease or increase likelihood of selection; values like -100 or 100 + should result in a ban or exclusive selection of the relevant token. As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. @@ -519,21 +474,7 @@ def create( def create( self, *, - model: Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ], + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, List[str], List[int], List[List[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -599,21 +540,7 @@ def with_raw_response(self) -> AsyncCompletionsWithRawResponse: async def create( self, *, - model: Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ], + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, List[str], List[int], List[List[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -676,12 +603,11 @@ async def create( Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this - [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to - convert text to token IDs. Mathematically, the bias is added to the logits - generated by the model prior to sampling. The exact effect will vary per model, - but values between -1 and 1 should decrease or increase likelihood of selection; - values like -100 or 100 should result in a ban or exclusive selection of the - relevant token. + [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. + Mathematically, the bias is added to the logits generated by the model prior to + sampling. The exact effect will vary per model, but values between -1 and 1 + should decrease or increase likelihood of selection; values like -100 or 100 + should result in a ban or exclusive selection of the relevant token. As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. @@ -762,21 +688,7 @@ async def create( async def create( self, *, - model: Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ], + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, List[str], List[int], List[List[int]], None], stream: Literal[True], best_of: Optional[int] | NotGiven = NOT_GIVEN, @@ -846,12 +758,11 @@ async def create( Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this - [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to - convert text to token IDs. Mathematically, the bias is added to the logits - generated by the model prior to sampling. The exact effect will vary per model, - but values between -1 and 1 should decrease or increase likelihood of selection; - values like -100 or 100 should result in a ban or exclusive selection of the - relevant token. + [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. + Mathematically, the bias is added to the logits generated by the model prior to + sampling. The exact effect will vary per model, but values between -1 and 1 + should decrease or increase likelihood of selection; values like -100 or 100 + should result in a ban or exclusive selection of the relevant token. As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. @@ -925,21 +836,7 @@ async def create( async def create( self, *, - model: Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ], + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, List[str], List[int], List[List[int]], None], stream: bool, best_of: Optional[int] | NotGiven = NOT_GIVEN, @@ -1009,12 +906,11 @@ async def create( Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this - [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to - convert text to token IDs. Mathematically, the bias is added to the logits - generated by the model prior to sampling. The exact effect will vary per model, - but values between -1 and 1 should decrease or increase likelihood of selection; - values like -100 or 100 should result in a ban or exclusive selection of the - relevant token. + [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. + Mathematically, the bias is added to the logits generated by the model prior to + sampling. The exact effect will vary per model, but values between -1 and 1 + should decrease or increase likelihood of selection; values like -100 or 100 + should result in a ban or exclusive selection of the relevant token. As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. @@ -1088,21 +984,7 @@ async def create( async def create( self, *, - model: Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ], + model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, List[str], List[int], List[List[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/edits.py b/src/openai/resources/edits.py deleted file mode 100644 index ac15494263..0000000000 --- a/src/openai/resources/edits.py +++ /dev/null @@ -1,189 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import typing_extensions -from typing import Union, Optional -from typing_extensions import Literal - -import httpx - -from ..types import Edit, edit_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._base_client import ( - make_request_options, -) - -__all__ = ["Edits", "AsyncEdits"] - - -class Edits(SyncAPIResource): - @cached_property - def with_raw_response(self) -> EditsWithRawResponse: - return EditsWithRawResponse(self) - - @typing_extensions.deprecated( - "The Edits API is deprecated; please use Chat Completions instead.\n\nhttps://openai.com/blog/gpt-4-api-general-availability#deprecation-of-the-edits-api\n" - ) - def create( - self, - *, - instruction: str, - model: Union[str, Literal["text-davinci-edit-001", "code-davinci-edit-001"]], - input: Optional[str] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Edit: - """ - Creates a new edit for the provided input, instruction, and parameters. - - Args: - instruction: The instruction that tells the model how to edit the prompt. - - model: ID of the model to use. You can use the `text-davinci-edit-001` or - `code-davinci-edit-001` model with this endpoint. - - input: The input text to use as a starting point for the edit. - - n: How many edits to generate for the input and instruction. - - temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will - make the output more random, while lower values like 0.2 will make it more - focused and deterministic. - - We generally recommend altering this or `top_p` but not both. - - top_p: An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/edits", - body=maybe_transform( - { - "instruction": instruction, - "model": model, - "input": input, - "n": n, - "temperature": temperature, - "top_p": top_p, - }, - edit_create_params.EditCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Edit, - ) - - -class AsyncEdits(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncEditsWithRawResponse: - return AsyncEditsWithRawResponse(self) - - @typing_extensions.deprecated( - "The Edits API is deprecated; please use Chat Completions instead.\n\nhttps://openai.com/blog/gpt-4-api-general-availability#deprecation-of-the-edits-api\n" - ) - async def create( - self, - *, - instruction: str, - model: Union[str, Literal["text-davinci-edit-001", "code-davinci-edit-001"]], - input: Optional[str] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Edit: - """ - Creates a new edit for the provided input, instruction, and parameters. - - Args: - instruction: The instruction that tells the model how to edit the prompt. - - model: ID of the model to use. You can use the `text-davinci-edit-001` or - `code-davinci-edit-001` model with this endpoint. - - input: The input text to use as a starting point for the edit. - - n: How many edits to generate for the input and instruction. - - temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will - make the output more random, while lower values like 0.2 will make it more - focused and deterministic. - - We generally recommend altering this or `top_p` but not both. - - top_p: An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/edits", - body=maybe_transform( - { - "instruction": instruction, - "model": model, - "input": input, - "n": n, - "temperature": temperature, - "top_p": top_p, - }, - edit_create_params.EditCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Edit, - ) - - -class EditsWithRawResponse: - def __init__(self, edits: Edits) -> None: - self.create = to_raw_response_wrapper( # pyright: ignore[reportDeprecated] - edits.create # pyright: ignore[reportDeprecated], - ) - - -class AsyncEditsWithRawResponse: - def __init__(self, edits: AsyncEdits) -> None: - self.create = async_to_raw_response_wrapper( # pyright: ignore[reportDeprecated] - edits.create # pyright: ignore[reportDeprecated], - ) diff --git a/src/openai/resources/fine_tunes.py b/src/openai/resources/fine_tunes.py deleted file mode 100644 index 411952387c..0000000000 --- a/src/openai/resources/fine_tunes.py +++ /dev/null @@ -1,819 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import List, Union, Optional, overload -from typing_extensions import Literal - -import httpx - -from ..types import ( - FineTune, - FineTuneEvent, - FineTuneEventsListResponse, - fine_tune_create_params, - fine_tune_list_events_params, -) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper -from .._streaming import Stream, AsyncStream -from ..pagination import SyncPage, AsyncPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) - -__all__ = ["FineTunes", "AsyncFineTunes"] - - -class FineTunes(SyncAPIResource): - @cached_property - def with_raw_response(self) -> FineTunesWithRawResponse: - return FineTunesWithRawResponse(self) - - def create( - self, - *, - training_file: str, - batch_size: Optional[int] | NotGiven = NOT_GIVEN, - classification_betas: Optional[List[float]] | NotGiven = NOT_GIVEN, - classification_n_classes: Optional[int] | NotGiven = NOT_GIVEN, - classification_positive_class: Optional[str] | NotGiven = NOT_GIVEN, - compute_classification_metrics: Optional[bool] | NotGiven = NOT_GIVEN, - hyperparameters: fine_tune_create_params.Hyperparameters | NotGiven = NOT_GIVEN, - learning_rate_multiplier: Optional[float] | NotGiven = NOT_GIVEN, - model: Union[str, Literal["ada", "babbage", "curie", "davinci"], None] | NotGiven = NOT_GIVEN, - prompt_loss_weight: Optional[float] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - validation_file: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTune: - """ - Creates a job that fine-tunes a specified model from a given dataset. - - Response includes details of the enqueued job including job status and the name - of the fine-tuned models once complete. - - [Learn more about fine-tuning](https://platform.openai.com/docs/guides/legacy-fine-tuning) - - Args: - training_file: The ID of an uploaded file that contains training data. - - See [upload file](https://platform.openai.com/docs/api-reference/files/upload) - for how to upload a file. - - Your dataset must be formatted as a JSONL file, where each training example is a - JSON object with the keys "prompt" and "completion". Additionally, you must - upload your file with the purpose `fine-tune`. - - See the - [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) - for more details. - - batch_size: The batch size to use for training. The batch size is the number of training - examples used to train a single forward and backward pass. - - By default, the batch size will be dynamically configured to be ~0.2% of the - number of examples in the training set, capped at 256 - in general, we've found - that larger batch sizes tend to work better for larger datasets. - - classification_betas: If this is provided, we calculate F-beta scores at the specified beta values. - The F-beta score is a generalization of F-1 score. This is only used for binary - classification. - - With a beta of 1 (i.e. the F-1 score), precision and recall are given the same - weight. A larger beta score puts more weight on recall and less on precision. A - smaller beta score puts more weight on precision and less on recall. - - classification_n_classes: The number of classes in a classification task. - - This parameter is required for multiclass classification. - - classification_positive_class: The positive class in binary classification. - - This parameter is needed to generate precision, recall, and F1 metrics when - doing binary classification. - - compute_classification_metrics: If set, we calculate classification-specific metrics such as accuracy and F-1 - score using the validation set at the end of every epoch. These metrics can be - viewed in the - [results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). - - In order to compute classification metrics, you must provide a - `validation_file`. Additionally, you must specify `classification_n_classes` for - multiclass classification or `classification_positive_class` for binary - classification. - - hyperparameters: The hyperparameters used for the fine-tuning job. - - learning_rate_multiplier: The learning rate multiplier to use for training. The fine-tuning learning rate - is the original learning rate used for pretraining multiplied by this value. - - By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 depending on - final `batch_size` (larger learning rates tend to perform better with larger - batch sizes). We recommend experimenting with values in the range 0.02 to 0.2 to - see what produces the best results. - - model: The name of the base model to fine-tune. You can select one of "ada", "babbage", - "curie", "davinci", or a fine-tuned model created after 2022-04-21 and before - 2023-08-22. To learn more about these models, see the - [Models](https://platform.openai.com/docs/models) documentation. - - prompt_loss_weight: The weight to use for loss on the prompt tokens. This controls how much the - model tries to learn to generate the prompt (as compared to the completion which - always has a weight of 1.0), and can add a stabilizing effect to training when - completions are short. - - If prompts are extremely long (relative to completions), it may make sense to - reduce this weight so as to avoid over-prioritizing learning the prompt. - - suffix: A string of up to 40 characters that will be added to your fine-tuned model - name. - - For example, a `suffix` of "custom-model-name" would produce a model name like - `ada:ft-your-org:custom-model-name-2022-02-15-04-21-04`. - - validation_file: The ID of an uploaded file that contains validation data. - - If you provide this file, the data is used to generate validation metrics - periodically during fine-tuning. These metrics can be viewed in the - [fine-tuning results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). - Your train and validation data should be mutually exclusive. - - Your dataset must be formatted as a JSONL file, where each validation example is - a JSON object with the keys "prompt" and "completion". Additionally, you must - upload your file with the purpose `fine-tune`. - - See the - [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) - for more details. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/fine-tunes", - body=maybe_transform( - { - "training_file": training_file, - "batch_size": batch_size, - "classification_betas": classification_betas, - "classification_n_classes": classification_n_classes, - "classification_positive_class": classification_positive_class, - "compute_classification_metrics": compute_classification_metrics, - "hyperparameters": hyperparameters, - "learning_rate_multiplier": learning_rate_multiplier, - "model": model, - "prompt_loss_weight": prompt_loss_weight, - "suffix": suffix, - "validation_file": validation_file, - }, - fine_tune_create_params.FineTuneCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FineTune, - ) - - def retrieve( - self, - fine_tune_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTune: - """ - Gets info about the fine-tune job. - - [Learn more about fine-tuning](https://platform.openai.com/docs/guides/legacy-fine-tuning) - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - f"/fine-tunes/{fine_tune_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FineTune, - ) - - def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncPage[FineTune]: - """List your organization's fine-tuning jobs""" - return self._get_api_list( - "/fine-tunes", - page=SyncPage[FineTune], - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - model=FineTune, - ) - - def cancel( - self, - fine_tune_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTune: - """ - Immediately cancel a fine-tune job. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - f"/fine-tunes/{fine_tune_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FineTune, - ) - - @overload - def list_events( - self, - fine_tune_id: str, - *, - stream: Literal[False] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 86400, - ) -> FineTuneEventsListResponse: - """ - Get fine-grained status updates for a fine-tune job. - - Args: - stream: Whether to stream events for the fine-tune job. If set to true, events will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available. The stream will terminate with a `data: [DONE]` - message when the job is finished (succeeded, cancelled, or failed). - - If set to false, only events generated so far will be returned. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def list_events( - self, - fine_tune_id: str, - *, - stream: Literal[True], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 86400, - ) -> Stream[FineTuneEvent]: - """ - Get fine-grained status updates for a fine-tune job. - - Args: - stream: Whether to stream events for the fine-tune job. If set to true, events will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available. The stream will terminate with a `data: [DONE]` - message when the job is finished (succeeded, cancelled, or failed). - - If set to false, only events generated so far will be returned. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def list_events( - self, - fine_tune_id: str, - *, - stream: bool, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 86400, - ) -> FineTuneEventsListResponse | Stream[FineTuneEvent]: - """ - Get fine-grained status updates for a fine-tune job. - - Args: - stream: Whether to stream events for the fine-tune job. If set to true, events will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available. The stream will terminate with a `data: [DONE]` - message when the job is finished (succeeded, cancelled, or failed). - - If set to false, only events generated so far will be returned. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - def list_events( - self, - fine_tune_id: str, - *, - stream: Literal[False] | Literal[True] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 86400, - ) -> FineTuneEventsListResponse | Stream[FineTuneEvent]: - return self._get( - f"/fine-tunes/{fine_tune_id}/events", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"stream": stream}, fine_tune_list_events_params.FineTuneListEventsParams), - ), - cast_to=FineTuneEventsListResponse, - stream=stream or False, - stream_cls=Stream[FineTuneEvent], - ) - - -class AsyncFineTunes(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncFineTunesWithRawResponse: - return AsyncFineTunesWithRawResponse(self) - - async def create( - self, - *, - training_file: str, - batch_size: Optional[int] | NotGiven = NOT_GIVEN, - classification_betas: Optional[List[float]] | NotGiven = NOT_GIVEN, - classification_n_classes: Optional[int] | NotGiven = NOT_GIVEN, - classification_positive_class: Optional[str] | NotGiven = NOT_GIVEN, - compute_classification_metrics: Optional[bool] | NotGiven = NOT_GIVEN, - hyperparameters: fine_tune_create_params.Hyperparameters | NotGiven = NOT_GIVEN, - learning_rate_multiplier: Optional[float] | NotGiven = NOT_GIVEN, - model: Union[str, Literal["ada", "babbage", "curie", "davinci"], None] | NotGiven = NOT_GIVEN, - prompt_loss_weight: Optional[float] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - validation_file: Optional[str] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTune: - """ - Creates a job that fine-tunes a specified model from a given dataset. - - Response includes details of the enqueued job including job status and the name - of the fine-tuned models once complete. - - [Learn more about fine-tuning](https://platform.openai.com/docs/guides/legacy-fine-tuning) - - Args: - training_file: The ID of an uploaded file that contains training data. - - See [upload file](https://platform.openai.com/docs/api-reference/files/upload) - for how to upload a file. - - Your dataset must be formatted as a JSONL file, where each training example is a - JSON object with the keys "prompt" and "completion". Additionally, you must - upload your file with the purpose `fine-tune`. - - See the - [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) - for more details. - - batch_size: The batch size to use for training. The batch size is the number of training - examples used to train a single forward and backward pass. - - By default, the batch size will be dynamically configured to be ~0.2% of the - number of examples in the training set, capped at 256 - in general, we've found - that larger batch sizes tend to work better for larger datasets. - - classification_betas: If this is provided, we calculate F-beta scores at the specified beta values. - The F-beta score is a generalization of F-1 score. This is only used for binary - classification. - - With a beta of 1 (i.e. the F-1 score), precision and recall are given the same - weight. A larger beta score puts more weight on recall and less on precision. A - smaller beta score puts more weight on precision and less on recall. - - classification_n_classes: The number of classes in a classification task. - - This parameter is required for multiclass classification. - - classification_positive_class: The positive class in binary classification. - - This parameter is needed to generate precision, recall, and F1 metrics when - doing binary classification. - - compute_classification_metrics: If set, we calculate classification-specific metrics such as accuracy and F-1 - score using the validation set at the end of every epoch. These metrics can be - viewed in the - [results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). - - In order to compute classification metrics, you must provide a - `validation_file`. Additionally, you must specify `classification_n_classes` for - multiclass classification or `classification_positive_class` for binary - classification. - - hyperparameters: The hyperparameters used for the fine-tuning job. - - learning_rate_multiplier: The learning rate multiplier to use for training. The fine-tuning learning rate - is the original learning rate used for pretraining multiplied by this value. - - By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 depending on - final `batch_size` (larger learning rates tend to perform better with larger - batch sizes). We recommend experimenting with values in the range 0.02 to 0.2 to - see what produces the best results. - - model: The name of the base model to fine-tune. You can select one of "ada", "babbage", - "curie", "davinci", or a fine-tuned model created after 2022-04-21 and before - 2023-08-22. To learn more about these models, see the - [Models](https://platform.openai.com/docs/models) documentation. - - prompt_loss_weight: The weight to use for loss on the prompt tokens. This controls how much the - model tries to learn to generate the prompt (as compared to the completion which - always has a weight of 1.0), and can add a stabilizing effect to training when - completions are short. - - If prompts are extremely long (relative to completions), it may make sense to - reduce this weight so as to avoid over-prioritizing learning the prompt. - - suffix: A string of up to 40 characters that will be added to your fine-tuned model - name. - - For example, a `suffix` of "custom-model-name" would produce a model name like - `ada:ft-your-org:custom-model-name-2022-02-15-04-21-04`. - - validation_file: The ID of an uploaded file that contains validation data. - - If you provide this file, the data is used to generate validation metrics - periodically during fine-tuning. These metrics can be viewed in the - [fine-tuning results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). - Your train and validation data should be mutually exclusive. - - Your dataset must be formatted as a JSONL file, where each validation example is - a JSON object with the keys "prompt" and "completion". Additionally, you must - upload your file with the purpose `fine-tune`. - - See the - [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) - for more details. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/fine-tunes", - body=maybe_transform( - { - "training_file": training_file, - "batch_size": batch_size, - "classification_betas": classification_betas, - "classification_n_classes": classification_n_classes, - "classification_positive_class": classification_positive_class, - "compute_classification_metrics": compute_classification_metrics, - "hyperparameters": hyperparameters, - "learning_rate_multiplier": learning_rate_multiplier, - "model": model, - "prompt_loss_weight": prompt_loss_weight, - "suffix": suffix, - "validation_file": validation_file, - }, - fine_tune_create_params.FineTuneCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FineTune, - ) - - async def retrieve( - self, - fine_tune_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTune: - """ - Gets info about the fine-tune job. - - [Learn more about fine-tuning](https://platform.openai.com/docs/guides/legacy-fine-tuning) - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - f"/fine-tunes/{fine_tune_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FineTune, - ) - - def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[FineTune, AsyncPage[FineTune]]: - """List your organization's fine-tuning jobs""" - return self._get_api_list( - "/fine-tunes", - page=AsyncPage[FineTune], - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - model=FineTune, - ) - - async def cancel( - self, - fine_tune_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FineTune: - """ - Immediately cancel a fine-tune job. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - f"/fine-tunes/{fine_tune_id}/cancel", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FineTune, - ) - - @overload - async def list_events( - self, - fine_tune_id: str, - *, - stream: Literal[False] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 86400, - ) -> FineTuneEventsListResponse: - """ - Get fine-grained status updates for a fine-tune job. - - Args: - stream: Whether to stream events for the fine-tune job. If set to true, events will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available. The stream will terminate with a `data: [DONE]` - message when the job is finished (succeeded, cancelled, or failed). - - If set to false, only events generated so far will be returned. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def list_events( - self, - fine_tune_id: str, - *, - stream: Literal[True], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 86400, - ) -> AsyncStream[FineTuneEvent]: - """ - Get fine-grained status updates for a fine-tune job. - - Args: - stream: Whether to stream events for the fine-tune job. If set to true, events will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available. The stream will terminate with a `data: [DONE]` - message when the job is finished (succeeded, cancelled, or failed). - - If set to false, only events generated so far will be returned. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def list_events( - self, - fine_tune_id: str, - *, - stream: bool, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 86400, - ) -> FineTuneEventsListResponse | AsyncStream[FineTuneEvent]: - """ - Get fine-grained status updates for a fine-tune job. - - Args: - stream: Whether to stream events for the fine-tune job. If set to true, events will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available. The stream will terminate with a `data: [DONE]` - message when the job is finished (succeeded, cancelled, or failed). - - If set to false, only events generated so far will be returned. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - async def list_events( - self, - fine_tune_id: str, - *, - stream: Literal[False] | Literal[True] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 86400, - ) -> FineTuneEventsListResponse | AsyncStream[FineTuneEvent]: - return await self._get( - f"/fine-tunes/{fine_tune_id}/events", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"stream": stream}, fine_tune_list_events_params.FineTuneListEventsParams), - ), - cast_to=FineTuneEventsListResponse, - stream=stream or False, - stream_cls=AsyncStream[FineTuneEvent], - ) - - -class FineTunesWithRawResponse: - def __init__(self, fine_tunes: FineTunes) -> None: - self.create = to_raw_response_wrapper( - fine_tunes.create, - ) - self.retrieve = to_raw_response_wrapper( - fine_tunes.retrieve, - ) - self.list = to_raw_response_wrapper( - fine_tunes.list, - ) - self.cancel = to_raw_response_wrapper( - fine_tunes.cancel, - ) - self.list_events = to_raw_response_wrapper( - fine_tunes.list_events, - ) - - -class AsyncFineTunesWithRawResponse: - def __init__(self, fine_tunes: AsyncFineTunes) -> None: - self.create = async_to_raw_response_wrapper( - fine_tunes.create, - ) - self.retrieve = async_to_raw_response_wrapper( - fine_tunes.retrieve, - ) - self.list = async_to_raw_response_wrapper( - fine_tunes.list, - ) - self.cancel = async_to_raw_response_wrapper( - fine_tunes.cancel, - ) - self.list_events = async_to_raw_response_wrapper( - fine_tunes.list_events, - ) diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index a8f24efce5..7537b48daa 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -49,7 +49,8 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTuningJob: """ - Creates a job that fine-tunes a specified model from a given dataset. + Creates a fine-tuning job which begins the process of creating a new model from + a given dataset. Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete. @@ -299,7 +300,8 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> FineTuningJob: """ - Creates a job that fine-tunes a specified model from a given dataset. + Creates a fine-tuning job which begins the process of creating a new model from + a given dataset. Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete. diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index df2b580587..d6108e1eed 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -2,33 +2,26 @@ from __future__ import annotations -from .edit import Edit as Edit from .image import Image as Image from .model import Model as Model from .shared import FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters from .embedding import Embedding as Embedding -from .fine_tune import FineTune as FineTune from .completion import Completion as Completion from .moderation import Moderation as Moderation from .file_object import FileObject as FileObject from .file_content import FileContent as FileContent from .file_deleted import FileDeleted as FileDeleted from .model_deleted import ModelDeleted as ModelDeleted -from .fine_tune_event import FineTuneEvent as FineTuneEvent from .images_response import ImagesResponse as ImagesResponse from .completion_usage import CompletionUsage as CompletionUsage from .file_list_params import FileListParams as FileListParams from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams -from .edit_create_params import EditCreateParams as EditCreateParams from .file_create_params import FileCreateParams as FileCreateParams from .image_generate_params import ImageGenerateParams as ImageGenerateParams from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams -from .fine_tune_create_params import FineTuneCreateParams as FineTuneCreateParams from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .moderation_create_params import ModerationCreateParams as ModerationCreateParams from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse -from .fine_tune_list_events_params import FineTuneListEventsParams as FineTuneListEventsParams from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams -from .fine_tune_events_list_response import FineTuneEventsListResponse as FineTuneEventsListResponse diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 49807a372e..6b38a89263 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -174,7 +174,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via - `{"type: "function", "function": {"name": "my_function"}}` forces the model to + `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function. `none` is the default when no functions are present. `auto` is the default if diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index ab6609a06b..e14c2860df 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -9,23 +9,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): - model: Required[ - Union[ - str, - Literal[ - "babbage-002", - "davinci-002", - "gpt-3.5-turbo-instruct", - "text-davinci-003", - "text-davinci-002", - "text-davinci-001", - "code-davinci-002", - "text-curie-001", - "text-babbage-001", - "text-ada-001", - ], - ] - ] + model: Required[Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]]] """ID of the model to use. You can use the @@ -75,12 +59,11 @@ class CompletionCreateParamsBase(TypedDict, total=False): Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this - [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to - convert text to token IDs. Mathematically, the bias is added to the logits - generated by the model prior to sampling. The exact effect will vary per model, - but values between -1 and 1 should decrease or increase likelihood of selection; - values like -100 or 100 should result in a ban or exclusive selection of the - relevant token. + [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. + Mathematically, the bias is added to the logits generated by the model prior to + sampling. The exact effect will vary per model, but values between -1 and 1 + should decrease or increase likelihood of selection; values like -100 or 100 + should result in a ban or exclusive selection of the relevant token. As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. diff --git a/src/openai/types/edit.py b/src/openai/types/edit.py deleted file mode 100644 index 48bca2987b..0000000000 --- a/src/openai/types/edit.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import List -from typing_extensions import Literal - -from .._models import BaseModel -from .completion_usage import CompletionUsage - -__all__ = ["Edit", "Choice"] - - -class Choice(BaseModel): - finish_reason: Literal["stop", "length"] - """The reason the model stopped generating tokens. - - This will be `stop` if the model hit a natural stop point or a provided stop - sequence, `length` if the maximum number of tokens specified in the request was - reached, or `content_filter` if content was omitted due to a flag from our - content filters. - """ - - index: int - """The index of the choice in the list of choices.""" - - text: str - """The edited result.""" - - -class Edit(BaseModel): - choices: List[Choice] - """A list of edit choices. Can be more than one if `n` is greater than 1.""" - - created: int - """The Unix timestamp (in seconds) of when the edit was created.""" - - object: Literal["edit"] - """The object type, which is always `edit`.""" - - usage: CompletionUsage - """Usage statistics for the completion request.""" diff --git a/src/openai/types/edit_create_params.py b/src/openai/types/edit_create_params.py deleted file mode 100644 index a23b79c369..0000000000 --- a/src/openai/types/edit_create_params.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["EditCreateParams"] - - -class EditCreateParams(TypedDict, total=False): - instruction: Required[str] - """The instruction that tells the model how to edit the prompt.""" - - model: Required[Union[str, Literal["text-davinci-edit-001", "code-davinci-edit-001"]]] - """ID of the model to use. - - You can use the `text-davinci-edit-001` or `code-davinci-edit-001` model with - this endpoint. - """ - - input: Optional[str] - """The input text to use as a starting point for the edit.""" - - n: Optional[int] - """How many edits to generate for the input and instruction.""" - - temperature: Optional[float] - """What sampling temperature to use, between 0 and 2. - - Higher values like 0.8 will make the output more random, while lower values like - 0.2 will make it more focused and deterministic. - - We generally recommend altering this or `top_p` but not both. - """ - - top_p: Optional[float] - """ - An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - """ diff --git a/src/openai/types/fine_tune.py b/src/openai/types/fine_tune.py deleted file mode 100644 index d1a063a065..0000000000 --- a/src/openai/types/fine_tune.py +++ /dev/null @@ -1,94 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import List, Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .file_object import FileObject -from .fine_tune_event import FineTuneEvent - -__all__ = ["FineTune", "Hyperparams"] - - -class Hyperparams(BaseModel): - batch_size: int - """The batch size to use for training. - - The batch size is the number of training examples used to train a single forward - and backward pass. - """ - - learning_rate_multiplier: float - """The learning rate multiplier to use for training.""" - - n_epochs: int - """The number of epochs to train the model for. - - An epoch refers to one full cycle through the training dataset. - """ - - prompt_loss_weight: float - """The weight to use for loss on the prompt tokens.""" - - classification_n_classes: Optional[int] = None - """The number of classes to use for computing classification metrics.""" - - classification_positive_class: Optional[str] = None - """The positive class to use for computing classification metrics.""" - - compute_classification_metrics: Optional[bool] = None - """ - The classification metrics to compute using the validation dataset at the end of - every epoch. - """ - - -class FineTune(BaseModel): - id: str - """The object identifier, which can be referenced in the API endpoints.""" - - created_at: int - """The Unix timestamp (in seconds) for when the fine-tuning job was created.""" - - fine_tuned_model: Optional[str] = None - """The name of the fine-tuned model that is being created.""" - - hyperparams: Hyperparams - """The hyperparameters used for the fine-tuning job. - - See the - [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/hyperparameters) - for more details. - """ - - model: str - """The base model that is being fine-tuned.""" - - object: Literal["fine-tune"] - """The object type, which is always "fine-tune".""" - - organization_id: str - """The organization that owns the fine-tuning job.""" - - result_files: List[FileObject] - """The compiled results files for the fine-tuning job.""" - - status: str - """ - The current status of the fine-tuning job, which can be either `created`, - `running`, `succeeded`, `failed`, or `cancelled`. - """ - - training_files: List[FileObject] - """The list of files used for training.""" - - updated_at: int - """The Unix timestamp (in seconds) for when the fine-tuning job was last updated.""" - - validation_files: List[FileObject] - """The list of files used for validation.""" - - events: Optional[List[FineTuneEvent]] = None - """ - The list of events that have been observed in the lifecycle of the FineTune job. - """ diff --git a/src/openai/types/fine_tune_create_params.py b/src/openai/types/fine_tune_create_params.py deleted file mode 100644 index 1be9c9ea04..0000000000 --- a/src/openai/types/fine_tune_create_params.py +++ /dev/null @@ -1,140 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import List, Union, Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["FineTuneCreateParams", "Hyperparameters"] - - -class FineTuneCreateParams(TypedDict, total=False): - training_file: Required[str] - """The ID of an uploaded file that contains training data. - - See [upload file](https://platform.openai.com/docs/api-reference/files/upload) - for how to upload a file. - - Your dataset must be formatted as a JSONL file, where each training example is a - JSON object with the keys "prompt" and "completion". Additionally, you must - upload your file with the purpose `fine-tune`. - - See the - [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) - for more details. - """ - - batch_size: Optional[int] - """The batch size to use for training. - - The batch size is the number of training examples used to train a single forward - and backward pass. - - By default, the batch size will be dynamically configured to be ~0.2% of the - number of examples in the training set, capped at 256 - in general, we've found - that larger batch sizes tend to work better for larger datasets. - """ - - classification_betas: Optional[List[float]] - """If this is provided, we calculate F-beta scores at the specified beta values. - - The F-beta score is a generalization of F-1 score. This is only used for binary - classification. - - With a beta of 1 (i.e. the F-1 score), precision and recall are given the same - weight. A larger beta score puts more weight on recall and less on precision. A - smaller beta score puts more weight on precision and less on recall. - """ - - classification_n_classes: Optional[int] - """The number of classes in a classification task. - - This parameter is required for multiclass classification. - """ - - classification_positive_class: Optional[str] - """The positive class in binary classification. - - This parameter is needed to generate precision, recall, and F1 metrics when - doing binary classification. - """ - - compute_classification_metrics: Optional[bool] - """ - If set, we calculate classification-specific metrics such as accuracy and F-1 - score using the validation set at the end of every epoch. These metrics can be - viewed in the - [results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). - - In order to compute classification metrics, you must provide a - `validation_file`. Additionally, you must specify `classification_n_classes` for - multiclass classification or `classification_positive_class` for binary - classification. - """ - - hyperparameters: Hyperparameters - """The hyperparameters used for the fine-tuning job.""" - - learning_rate_multiplier: Optional[float] - """ - The learning rate multiplier to use for training. The fine-tuning learning rate - is the original learning rate used for pretraining multiplied by this value. - - By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 depending on - final `batch_size` (larger learning rates tend to perform better with larger - batch sizes). We recommend experimenting with values in the range 0.02 to 0.2 to - see what produces the best results. - """ - - model: Union[str, Literal["ada", "babbage", "curie", "davinci"], None] - """The name of the base model to fine-tune. - - You can select one of "ada", "babbage", "curie", "davinci", or a fine-tuned - model created after 2022-04-21 and before 2023-08-22. To learn more about these - models, see the [Models](https://platform.openai.com/docs/models) documentation. - """ - - prompt_loss_weight: Optional[float] - """The weight to use for loss on the prompt tokens. - - This controls how much the model tries to learn to generate the prompt (as - compared to the completion which always has a weight of 1.0), and can add a - stabilizing effect to training when completions are short. - - If prompts are extremely long (relative to completions), it may make sense to - reduce this weight so as to avoid over-prioritizing learning the prompt. - """ - - suffix: Optional[str] - """ - A string of up to 40 characters that will be added to your fine-tuned model - name. - - For example, a `suffix` of "custom-model-name" would produce a model name like - `ada:ft-your-org:custom-model-name-2022-02-15-04-21-04`. - """ - - validation_file: Optional[str] - """The ID of an uploaded file that contains validation data. - - If you provide this file, the data is used to generate validation metrics - periodically during fine-tuning. These metrics can be viewed in the - [fine-tuning results file](https://platform.openai.com/docs/guides/legacy-fine-tuning/analyzing-your-fine-tuned-model). - Your train and validation data should be mutually exclusive. - - Your dataset must be formatted as a JSONL file, where each validation example is - a JSON object with the keys "prompt" and "completion". Additionally, you must - upload your file with the purpose `fine-tune`. - - See the - [fine-tuning guide](https://platform.openai.com/docs/guides/legacy-fine-tuning/creating-training-data) - for more details. - """ - - -class Hyperparameters(TypedDict, total=False): - n_epochs: Union[Literal["auto"], int] - """The number of epochs to train the model for. - - An epoch refers to one full cycle through the training dataset. - """ diff --git a/src/openai/types/fine_tune_event.py b/src/openai/types/fine_tune_event.py deleted file mode 100644 index 299f0de24b..0000000000 --- a/src/openai/types/fine_tune_event.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["FineTuneEvent"] - - -class FineTuneEvent(BaseModel): - created_at: int - - level: str - - message: str - - object: Literal["fine-tune-event"] diff --git a/src/openai/types/fine_tune_events_list_response.py b/src/openai/types/fine_tune_events_list_response.py deleted file mode 100644 index c69746104d..0000000000 --- a/src/openai/types/fine_tune_events_list_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import List -from typing_extensions import Literal - -from .._models import BaseModel -from .fine_tune_event import FineTuneEvent - -__all__ = ["FineTuneEventsListResponse"] - - -class FineTuneEventsListResponse(BaseModel): - data: List[FineTuneEvent] - - object: Literal["list"] diff --git a/src/openai/types/fine_tune_list_events_params.py b/src/openai/types/fine_tune_list_events_params.py deleted file mode 100644 index 1f23b108e6..0000000000 --- a/src/openai/types/fine_tune_list_events_params.py +++ /dev/null @@ -1,41 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["FineTuneListEventsParamsBase", "FineTuneListEventsParamsNonStreaming", "FineTuneListEventsParamsStreaming"] - - -class FineTuneListEventsParamsBase(TypedDict, total=False): - pass - - -class FineTuneListEventsParamsNonStreaming(FineTuneListEventsParamsBase): - stream: Literal[False] - """Whether to stream events for the fine-tune job. - - If set to true, events will be sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available. The stream will terminate with a `data: [DONE]` - message when the job is finished (succeeded, cancelled, or failed). - - If set to false, only events generated so far will be returned. - """ - - -class FineTuneListEventsParamsStreaming(FineTuneListEventsParamsBase): - stream: Required[Literal[True]] - """Whether to stream events for the fine-tune job. - - If set to true, events will be sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available. The stream will terminate with a `data: [DONE]` - message when the job is finished (succeeded, cancelled, or failed). - - If set to false, only events generated so far will be returned. - """ - - -FineTuneListEventsParams = Union[FineTuneListEventsParamsNonStreaming, FineTuneListEventsParamsStreaming] diff --git a/tests/api_resources/test_edits.py b/tests/api_resources/test_edits.py deleted file mode 100644 index 76069d6b83..0000000000 --- a/tests/api_resources/test_edits.py +++ /dev/null @@ -1,95 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import os - -import pytest - -from openai import OpenAI, AsyncOpenAI -from tests.utils import assert_matches_type -from openai.types import Edit -from openai._client import OpenAI, AsyncOpenAI - -# pyright: reportDeprecated=false - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" - - -class TestEdits: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) - - @parametrize - def test_method_create(self, client: OpenAI) -> None: - with pytest.warns(DeprecationWarning): - edit = client.edits.create( - instruction="Fix the spelling mistakes.", - model="text-davinci-edit-001", - ) - assert_matches_type(Edit, edit, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: OpenAI) -> None: - with pytest.warns(DeprecationWarning): - edit = client.edits.create( - instruction="Fix the spelling mistakes.", - model="text-davinci-edit-001", - input="What day of the wek is it?", - n=1, - temperature=1, - top_p=1, - ) - assert_matches_type(Edit, edit, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: OpenAI) -> None: - with pytest.warns(DeprecationWarning): - response = client.edits.with_raw_response.create( - instruction="Fix the spelling mistakes.", - model="text-davinci-edit-001", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - edit = response.parse() - assert_matches_type(Edit, edit, path=["response"]) - - -class TestAsyncEdits: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) - - @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - with pytest.warns(DeprecationWarning): - edit = await client.edits.create( - instruction="Fix the spelling mistakes.", - model="text-davinci-edit-001", - ) - assert_matches_type(Edit, edit, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - with pytest.warns(DeprecationWarning): - edit = await client.edits.create( - instruction="Fix the spelling mistakes.", - model="text-davinci-edit-001", - input="What day of the wek is it?", - n=1, - temperature=1, - top_p=1, - ) - assert_matches_type(Edit, edit, path=["response"]) - - @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - with pytest.warns(DeprecationWarning): - response = await client.edits.with_raw_response.create( - instruction="Fix the spelling mistakes.", - model="text-davinci-edit-001", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - edit = response.parse() - assert_matches_type(Edit, edit, path=["response"]) diff --git a/tests/api_resources/test_fine_tunes.py b/tests/api_resources/test_fine_tunes.py deleted file mode 100644 index edaf784848..0000000000 --- a/tests/api_resources/test_fine_tunes.py +++ /dev/null @@ -1,274 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import os - -import pytest - -from openai import OpenAI, AsyncOpenAI -from tests.utils import assert_matches_type -from openai.types import FineTune, FineTuneEventsListResponse -from openai._client import OpenAI, AsyncOpenAI -from openai.pagination import SyncPage, AsyncPage - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" - - -class TestFineTunes: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) - - @parametrize - def test_method_create(self, client: OpenAI) -> None: - fine_tune = client.fine_tunes.create( - training_file="file-abc123", - ) - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: OpenAI) -> None: - fine_tune = client.fine_tunes.create( - training_file="file-abc123", - batch_size=0, - classification_betas=[0.6, 1, 1.5, 2], - classification_n_classes=0, - classification_positive_class="string", - compute_classification_metrics=True, - hyperparameters={"n_epochs": "auto"}, - learning_rate_multiplier=0, - model="curie", - prompt_loss_weight=0, - suffix="x", - validation_file="file-abc123", - ) - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: OpenAI) -> None: - response = client.fine_tunes.with_raw_response.create( - training_file="file-abc123", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - def test_method_retrieve(self, client: OpenAI) -> None: - fine_tune = client.fine_tunes.retrieve( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: OpenAI) -> None: - response = client.fine_tunes.with_raw_response.retrieve( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - def test_method_list(self, client: OpenAI) -> None: - fine_tune = client.fine_tunes.list() - assert_matches_type(SyncPage[FineTune], fine_tune, path=["response"]) - - @parametrize - def test_raw_response_list(self, client: OpenAI) -> None: - response = client.fine_tunes.with_raw_response.list() - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(SyncPage[FineTune], fine_tune, path=["response"]) - - @parametrize - def test_method_cancel(self, client: OpenAI) -> None: - fine_tune = client.fine_tunes.cancel( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - def test_raw_response_cancel(self, client: OpenAI) -> None: - response = client.fine_tunes.with_raw_response.cancel( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - def test_method_list_events_overload_1(self, client: OpenAI) -> None: - fine_tune = client.fine_tunes.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - def test_method_list_events_with_all_params_overload_1(self, client: OpenAI) -> None: - fine_tune = client.fine_tunes.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - stream=False, - ) - assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - def test_raw_response_list_events_overload_1(self, client: OpenAI) -> None: - response = client.fine_tunes.with_raw_response.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - def test_method_list_events_overload_2(self, client: OpenAI) -> None: - client.fine_tunes.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - stream=True, - ) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - def test_raw_response_list_events_overload_2(self, client: OpenAI) -> None: - response = client.fine_tunes.with_raw_response.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - stream=True, - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - response.parse() - - -class TestAsyncFineTunes: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) - - @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - fine_tune = await client.fine_tunes.create( - training_file="file-abc123", - ) - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - fine_tune = await client.fine_tunes.create( - training_file="file-abc123", - batch_size=0, - classification_betas=[0.6, 1, 1.5, 2], - classification_n_classes=0, - classification_positive_class="string", - compute_classification_metrics=True, - hyperparameters={"n_epochs": "auto"}, - learning_rate_multiplier=0, - model="curie", - prompt_loss_weight=0, - suffix="x", - validation_file="file-abc123", - ) - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.fine_tunes.with_raw_response.create( - training_file="file-abc123", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - fine_tune = await client.fine_tunes.retrieve( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.fine_tunes.with_raw_response.retrieve( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - fine_tune = await client.fine_tunes.list() - assert_matches_type(AsyncPage[FineTune], fine_tune, path=["response"]) - - @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.fine_tunes.with_raw_response.list() - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(AsyncPage[FineTune], fine_tune, path=["response"]) - - @parametrize - async def test_method_cancel(self, client: AsyncOpenAI) -> None: - fine_tune = await client.fine_tunes.cancel( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @parametrize - async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: - response = await client.fine_tunes.with_raw_response.cancel( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(FineTune, fine_tune, path=["response"]) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - async def test_method_list_events_overload_1(self, client: AsyncOpenAI) -> None: - fine_tune = await client.fine_tunes.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - async def test_method_list_events_with_all_params_overload_1(self, client: AsyncOpenAI) -> None: - fine_tune = await client.fine_tunes.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - stream=False, - ) - assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - async def test_raw_response_list_events_overload_1(self, client: AsyncOpenAI) -> None: - response = await client.fine_tunes.with_raw_response.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - fine_tune = response.parse() - assert_matches_type(FineTuneEventsListResponse, fine_tune, path=["response"]) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - async def test_method_list_events_overload_2(self, client: AsyncOpenAI) -> None: - await client.fine_tunes.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - stream=True, - ) - - @pytest.mark.skip(reason="Prism chokes on this") - @parametrize - async def test_raw_response_list_events_overload_2(self, client: AsyncOpenAI) -> None: - response = await client.fine_tunes.with_raw_response.list_events( - "ft-AF1WoRqd3aJAHsqc9NY7iL8F", - stream=True, - ) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - response.parse() From ac33853ba10d13ac149b1fa3ca6dba7d613065c9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 12 Jan 2024 10:03:04 -0500 Subject: [PATCH 290/914] release: 1.7.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5660725203..b08a26cbda 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.7.1" + ".": "1.7.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 19fb9c3e58..ab502f8137 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.7.2 (2024-01-12) + +Full Changelog: [v1.7.1...v1.7.2](https://github.com/openai/openai-python/compare/v1.7.1...v1.7.2) + +### Documentation + +* **readme:** improve api reference ([#1065](https://github.com/openai/openai-python/issues/1065)) ([745b9e0](https://github.com/openai/openai-python/commit/745b9e08ae0abb8bf4cd87ed40fa450d9ad81ede)) + + +### Refactors + +* **api:** remove deprecated endpoints ([#1067](https://github.com/openai/openai-python/issues/1067)) ([199ddcd](https://github.com/openai/openai-python/commit/199ddcdca00c136e4e0c3ff16521eff22acf2a1a)) + ## 1.7.1 (2024-01-10) Full Changelog: [v1.7.0...v1.7.1](https://github.com/openai/openai-python/compare/v1.7.0...v1.7.1) diff --git a/pyproject.toml b/pyproject.toml index 9ff951873a..354d763812 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.7.1" +version = "1.7.2" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b25177f3a5..0b4aa63ffe 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.7.1" # x-release-please-version +__version__ = "1.7.2" # x-release-please-version From 86379b4471d67a9d2e85f0b0c098787fb99aa4e0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 15 Jan 2024 09:11:27 -0500 Subject: [PATCH 291/914] feat(client): add support for streaming raw responses (#1072) As an alternative to `with_raw_response` we now provide `with_streaming_response` as well. When using these methods you will have to use a context manager to ensure that the response is always cleaned up. --- README.md | 37 +- examples/audio.py | 16 +- src/openai/__init__.py | 1 + src/openai/_base_client.py | 279 +++++---- src/openai/_client.py | 32 + src/openai/_constants.py | 2 +- src/openai/_legacy_response.py | 385 ++++++++++++ src/openai/_response.py | 570 ++++++++++++++++-- src/openai/_types.py | 166 +---- src/openai/resources/__init__.py | 110 +++- src/openai/resources/audio/__init__.py | 37 +- src/openai/resources/audio/audio.py | 42 +- src/openai/resources/audio/speech.py | 45 +- src/openai/resources/audio/transcriptions.py | 29 +- src/openai/resources/audio/translations.py | 29 +- src/openai/resources/beta/__init__.py | 33 +- .../resources/beta/assistants/__init__.py | 22 +- .../resources/beta/assistants/assistants.py | 82 ++- src/openai/resources/beta/assistants/files.py | 59 +- src/openai/resources/beta/beta.py | 38 +- src/openai/resources/beta/threads/__init__.py | 33 +- .../beta/threads/messages/__init__.py | 22 +- .../resources/beta/threads/messages/files.py | 39 +- .../beta/threads/messages/messages.py | 72 ++- .../resources/beta/threads/runs/__init__.py | 22 +- .../resources/beta/threads/runs/runs.py | 92 ++- .../resources/beta/threads/runs/steps.py | 39 +- src/openai/resources/beta/threads/threads.py | 93 ++- src/openai/resources/chat/__init__.py | 22 +- src/openai/resources/chat/chat.py | 27 +- src/openai/resources/chat/completions.py | 29 +- src/openai/resources/completions.py | 29 +- src/openai/resources/embeddings.py | 29 +- src/openai/resources/files.py | 109 +++- src/openai/resources/fine_tuning/__init__.py | 22 +- .../resources/fine_tuning/fine_tuning.py | 27 +- src/openai/resources/fine_tuning/jobs.py | 69 ++- src/openai/resources/images.py | 49 +- src/openai/resources/models.py | 49 +- src/openai/resources/moderations.py | 29 +- tests/api_resources/audio/test_speech.py | 66 +- .../audio/test_transcriptions.py | 33 + .../api_resources/audio/test_translations.py | 33 + .../beta/assistants/test_files.py | 127 ++++ tests/api_resources/beta/test_assistants.py | 147 +++++ tests/api_resources/beta/test_threads.py | 147 +++++ .../beta/threads/messages/test_files.py | 67 ++ .../beta/threads/runs/test_steps.py | 67 ++ .../beta/threads/test_messages.py | 129 ++++ tests/api_resources/beta/threads/test_runs.py | 193 ++++++ tests/api_resources/chat/test_completions.py | 103 +++- tests/api_resources/fine_tuning/test_jobs.py | 149 +++++ tests/api_resources/test_completions.py | 83 ++- tests/api_resources/test_embeddings.py | 33 + tests/api_resources/test_files.py | 201 +++++- tests/api_resources/test_images.py | 93 +++ tests/api_resources/test_models.py | 87 +++ tests/api_resources/test_moderations.py | 31 + tests/test_client.py | 76 ++- tests/test_response.py | 50 ++ tests/utils.py | 5 + 61 files changed, 4273 insertions(+), 563 deletions(-) create mode 100644 src/openai/_legacy_response.py create mode 100644 tests/test_response.py diff --git a/README.md b/README.md index e86ac6553e..22e7ac795f 100644 --- a/README.md +++ b/README.md @@ -414,7 +414,7 @@ if response.my_field is None: ### Accessing raw response data (e.g. headers) -The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call. +The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g., ```py from openai import OpenAI @@ -433,7 +433,40 @@ completion = response.parse() # get the object that `chat.completions.create()` print(completion) ``` -These methods return an [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object. +These methods return an [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. + +For the sync client this will mostly be the same with the exception +of `content` & `text` will be methods instead of properties. In the +async client, all methods will be async. + +A migration script will be provided & the migration in general should +be smooth. + +#### `.with_streaming_response` + +The above interface eagerly reads the full response body when you make the request, which may not always be what you want. + +To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. + +As such, `.with_streaming_response` methods return a different [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object, and the async client returns an [`AsyncAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object. + +```python +with client.chat.completions.with_streaming_response.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", +) as response: + print(response.headers.get("X-My-Header")) + + for line in response.iter_lines(): + print(line) +``` + +The context manager is required so that the response will reliably be closed. ### Configuring the HTTP client diff --git a/examples/audio.py b/examples/audio.py index a5f535dcd6..73491090f5 100755 --- a/examples/audio.py +++ b/examples/audio.py @@ -12,14 +12,18 @@ def main() -> None: # Create text-to-speech audio file - response = openai.audio.speech.create( - model="tts-1", voice="alloy", input="the quick brown fox jumped over the lazy dogs" - ) - - response.stream_to_file(speech_file_path) + with openai.audio.speech.with_streaming_response.create( + model="tts-1", + voice="alloy", + input="the quick brown fox jumped over the lazy dogs", + ) as response: + response.stream_to_file(speech_file_path) # Create transcription from audio file - transcription = openai.audio.transcriptions.create(model="whisper-1", file=speech_file_path) + transcription = openai.audio.transcriptions.create( + model="whisper-1", + file=speech_file_path, + ) print(transcription.text) # Create translation from audio file diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 64c93e9449..0de58b3327 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -10,6 +10,7 @@ from ._utils import file_from_path from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions from ._version import __title__, __version__ +from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse from ._exceptions import ( APIError, OpenAIError, diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index c2c2db5f49..1dfbd7dfb3 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1,6 +1,5 @@ from __future__ import annotations -import os import json import time import uuid @@ -31,7 +30,7 @@ overload, ) from functools import lru_cache -from typing_extensions import Literal, override +from typing_extensions import Literal, override, get_origin import anyio import httpx @@ -61,18 +60,22 @@ AsyncTransport, RequestOptions, ModelBuilderProtocol, - BinaryResponseContent, ) from ._utils import is_dict, is_given, is_mapping from ._compat import model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type -from ._response import APIResponse +from ._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + extract_response_type, +) from ._constants import ( DEFAULT_LIMITS, DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, RAW_RESPONSE_HEADER, - STREAMED_RAW_RESPONSE_HEADER, + OVERRIDE_CAST_TO_HEADER, ) from ._streaming import Stream, AsyncStream from ._exceptions import ( @@ -81,6 +84,7 @@ APIConnectionError, APIResponseValidationError, ) +from ._legacy_response import LegacyAPIResponse log: logging.Logger = logging.getLogger(__name__) @@ -493,28 +497,25 @@ def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, o serialized[key] = value return serialized - def _process_response( - self, - *, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - response: httpx.Response, - stream: bool, - stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, - ) -> ResponseT: - api_response = APIResponse( - raw=response, - client=self, - cast_to=cast_to, - stream=stream, - stream_cls=stream_cls, - options=options, - ) + def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: + if not is_given(options.headers): + return cast_to - if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": - return cast(ResponseT, api_response) + # make a copy of the headers so we don't mutate user-input + headers = dict(options.headers) - return api_response.parse() + # we internally support defining a temporary header to override the + # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` + # see _response.py for implementation details + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN) + if is_given(override_cast_to): + options.headers = headers + return cast(Type[ResponseT], override_cast_to) + + return cast_to + + def _should_stream_response_body(self, request: httpx.Request) -> bool: + return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] def _process_response_data( self, @@ -540,12 +541,6 @@ def _process_response_data( except pydantic.ValidationError as err: raise APIResponseValidationError(response=response, body=data) from err - def _should_stream_response_body(self, *, request: httpx.Request) -> bool: - if request.headers.get(STREAMED_RAW_RESPONSE_HEADER) == "true": - return True - - return False - @property def qs(self) -> Querystring: return Querystring() @@ -610,6 +605,8 @@ def _calculate_retry_timeout( if response_headers is not None: retry_header = response_headers.get("retry-after") try: + # note: the spec indicates that this should only ever be an integer + # but if someone sends a float there's no reason for us to not respect it retry_after = float(retry_header) except Exception: retry_date_tuple = email.utils.parsedate_tz(retry_header) @@ -873,6 +870,7 @@ def _request( stream: bool, stream_cls: type[_StreamT] | None, ) -> ResponseT | _StreamT: + cast_to = self._maybe_override_cast_to(cast_to, options) self._prepare_options(options) retries = self._remaining_retries(remaining_retries, options) @@ -987,6 +985,63 @@ def _retry_request( stream_cls=stream_cls, ) + def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + ) -> ResponseT: + if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": + return cast( + ResponseT, + LegacyAPIResponse( + raw=response, + client=self, + cast_to=cast_to, + stream=stream, + stream_cls=stream_cls, + options=options, + ), + ) + + origin = get_origin(cast_to) or cast_to + + if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if not issubclass(origin, APIResponse): + raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + ResponseT, + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = APIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return api_response.parse() + def _request_api_list( self, model: Type[object], @@ -1353,6 +1408,7 @@ async def _request( stream_cls: type[_AsyncStreamT] | None, remaining_retries: int | None, ) -> ResponseT | _AsyncStreamT: + cast_to = self._maybe_override_cast_to(cast_to, options) await self._prepare_options(options) retries = self._remaining_retries(remaining_retries, options) @@ -1428,7 +1484,7 @@ async def _request( log.debug("Re-raising status error") raise self._make_status_error_from_response(err.response) from None - return self._process_response( + return await self._process_response( cast_to=cast_to, options=options, response=response, @@ -1465,6 +1521,63 @@ async def _retry_request( stream_cls=stream_cls, ) + async def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + ) -> ResponseT: + if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": + return cast( + ResponseT, + LegacyAPIResponse( + raw=response, + client=self, + cast_to=cast_to, + stream=stream, + stream_cls=stream_cls, + options=options, + ), + ) + + origin = get_origin(cast_to) or cast_to + + if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if not issubclass(origin, AsyncAPIResponse): + raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + "ResponseT", + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = AsyncAPIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return await api_response.parse() + def _request_api_list( self, model: Type[_T], @@ -1783,105 +1896,3 @@ def _merge_mappings( """ merged = {**obj1, **obj2} return {key: value for key, value in merged.items() if not isinstance(value, Omit)} - - -class HttpxBinaryResponseContent(BinaryResponseContent): - response: httpx.Response - - def __init__(self, response: httpx.Response) -> None: - self.response = response - - @property - @override - def content(self) -> bytes: - return self.response.content - - @property - @override - def text(self) -> str: - return self.response.text - - @property - @override - def encoding(self) -> Optional[str]: - return self.response.encoding - - @property - @override - def charset_encoding(self) -> Optional[str]: - return self.response.charset_encoding - - @override - def json(self, **kwargs: Any) -> Any: - return self.response.json(**kwargs) - - @override - def read(self) -> bytes: - return self.response.read() - - @override - def iter_bytes(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: - return self.response.iter_bytes(chunk_size) - - @override - def iter_text(self, chunk_size: Optional[int] = None) -> Iterator[str]: - return self.response.iter_text(chunk_size) - - @override - def iter_lines(self) -> Iterator[str]: - return self.response.iter_lines() - - @override - def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: - return self.response.iter_raw(chunk_size) - - @override - def stream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - with open(file, mode="wb") as f: - for data in self.response.iter_bytes(chunk_size): - f.write(data) - - @override - def close(self) -> None: - return self.response.close() - - @override - async def aread(self) -> bytes: - return await self.response.aread() - - @override - async def aiter_bytes(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]: - return self.response.aiter_bytes(chunk_size) - - @override - async def aiter_text(self, chunk_size: Optional[int] = None) -> AsyncIterator[str]: - return self.response.aiter_text(chunk_size) - - @override - async def aiter_lines(self) -> AsyncIterator[str]: - return self.response.aiter_lines() - - @override - async def aiter_raw(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]: - return self.response.aiter_raw(chunk_size) - - @override - async def astream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - path = anyio.Path(file) - async with await path.open(mode="wb") as f: - async for data in self.response.aiter_bytes(chunk_size): - await f.write(data) - - @override - async def aclose(self) -> None: - return await self.response.aclose() diff --git a/src/openai/_client.py b/src/openai/_client.py index 09f54e1b12..5043d60e2a 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -58,6 +58,7 @@ class OpenAI(SyncAPIClient): fine_tuning: resources.FineTuning beta: resources.Beta with_raw_response: OpenAIWithRawResponse + with_streaming_response: OpenAIWithStreamedResponse # client options api_key: str @@ -132,6 +133,7 @@ def __init__( self.fine_tuning = resources.FineTuning(self) self.beta = resources.Beta(self) self.with_raw_response = OpenAIWithRawResponse(self) + self.with_streaming_response = OpenAIWithStreamedResponse(self) @property @override @@ -254,6 +256,7 @@ class AsyncOpenAI(AsyncAPIClient): fine_tuning: resources.AsyncFineTuning beta: resources.AsyncBeta with_raw_response: AsyncOpenAIWithRawResponse + with_streaming_response: AsyncOpenAIWithStreamedResponse # client options api_key: str @@ -328,6 +331,7 @@ def __init__( self.fine_tuning = resources.AsyncFineTuning(self) self.beta = resources.AsyncBeta(self) self.with_raw_response = AsyncOpenAIWithRawResponse(self) + self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self) @property @override @@ -466,6 +470,34 @@ def __init__(self, client: AsyncOpenAI) -> None: self.beta = resources.AsyncBetaWithRawResponse(client.beta) +class OpenAIWithStreamedResponse: + def __init__(self, client: OpenAI) -> None: + self.completions = resources.CompletionsWithStreamingResponse(client.completions) + self.chat = resources.ChatWithStreamingResponse(client.chat) + self.embeddings = resources.EmbeddingsWithStreamingResponse(client.embeddings) + self.files = resources.FilesWithStreamingResponse(client.files) + self.images = resources.ImagesWithStreamingResponse(client.images) + self.audio = resources.AudioWithStreamingResponse(client.audio) + self.moderations = resources.ModerationsWithStreamingResponse(client.moderations) + self.models = resources.ModelsWithStreamingResponse(client.models) + self.fine_tuning = resources.FineTuningWithStreamingResponse(client.fine_tuning) + self.beta = resources.BetaWithStreamingResponse(client.beta) + + +class AsyncOpenAIWithStreamedResponse: + def __init__(self, client: AsyncOpenAI) -> None: + self.completions = resources.AsyncCompletionsWithStreamingResponse(client.completions) + self.chat = resources.AsyncChatWithStreamingResponse(client.chat) + self.embeddings = resources.AsyncEmbeddingsWithStreamingResponse(client.embeddings) + self.files = resources.AsyncFilesWithStreamingResponse(client.files) + self.images = resources.AsyncImagesWithStreamingResponse(client.images) + self.audio = resources.AsyncAudioWithStreamingResponse(client.audio) + self.moderations = resources.AsyncModerationsWithStreamingResponse(client.moderations) + self.models = resources.AsyncModelsWithStreamingResponse(client.models) + self.fine_tuning = resources.AsyncFineTuningWithStreamingResponse(client.fine_tuning) + self.beta = resources.AsyncBetaWithStreamingResponse(client.beta) + + Client = OpenAI AsyncClient = AsyncOpenAI diff --git a/src/openai/_constants.py b/src/openai/_constants.py index 7c13feaa25..af9a04b80c 100644 --- a/src/openai/_constants.py +++ b/src/openai/_constants.py @@ -3,7 +3,7 @@ import httpx RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" -STREAMED_RAW_RESPONSE_HEADER = "X-Stainless-Streamed-Raw-Response" +OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" # default timeout is 10 minutes DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py new file mode 100644 index 0000000000..5a398efebf --- /dev/null +++ b/src/openai/_legacy_response.py @@ -0,0 +1,385 @@ +from __future__ import annotations + +import os +import inspect +import logging +import datetime +import functools +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, Iterator, AsyncIterator, cast +from typing_extensions import Awaitable, ParamSpec, get_args, override, deprecated, get_origin + +import anyio +import httpx + +from ._types import NoneType +from ._utils import is_given +from ._models import BaseModel, is_basemodel +from ._constants import RAW_RESPONSE_HEADER +from ._exceptions import APIResponseValidationError + +if TYPE_CHECKING: + from ._models import FinalRequestOptions + from ._base_client import Stream, BaseClient, AsyncStream + + +P = ParamSpec("P") +R = TypeVar("R") + +log: logging.Logger = logging.getLogger(__name__) + + +class LegacyAPIResponse(Generic[R]): + """This is a legacy class as it will be replaced by `APIResponse` + and `AsyncAPIResponse` in the `_response.py` file in the next major + release. + + For the sync client this will mostly be the same with the exception + of `content` & `text` will be methods instead of properties. In the + async client, all methods will be async. + + A migration script will be provided & the migration in general should + be smooth. + """ + + _cast_to: type[R] + _client: BaseClient[Any, Any] + _parsed: R | None + _stream: bool + _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None + _options: FinalRequestOptions + + http_response: httpx.Response + + def __init__( + self, + *, + raw: httpx.Response, + cast_to: type[R], + client: BaseClient[Any, Any], + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + options: FinalRequestOptions, + ) -> None: + self._cast_to = cast_to + self._client = client + self._parsed = None + self._stream = stream + self._stream_cls = stream_cls + self._options = options + self.http_response = raw + + def parse(self) -> R: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + NOTE: For the async client: this will become a coroutine in the next major version. + """ + if self._parsed is not None: + return self._parsed + + parsed = self._parse() + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed = parsed + return parsed + + @property + def headers(self) -> httpx.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx.Request: + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def url(http://github.com/self) -> httpx.URL: + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def content(self) -> bytes: + """Return the binary response content. + + NOTE: this will be removed in favour of `.read()` in the + next major version. + """ + return self.http_response.content + + @property + def text(self) -> str: + """Return the decoded response content. + + NOTE: this will be turned into a method in the next major version. + """ + return self.http_response.text + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def is_closed(self) -> bool: + return self.http_response.is_closed + + @property + def elapsed(self) -> datetime.timedelta: + """The time taken for the complete request/response cycle to complete.""" + return self.http_response.elapsed + + def _parse(self) -> R: + if self._stream: + if self._stream_cls: + return cast( + R, + self._stream_cls( + cast_to=_extract_stream_chunk_type(self._stream_cls), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) + if stream_cls is None: + raise MissingStreamClassError() + + return cast( + R, + stream_cls( + cast_to=self._cast_to, + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + cast_to = self._cast_to + if cast_to is NoneType: + return cast(R, None) + + response = self.http_response + if cast_to == str: + return cast(R, response.text) + + origin = get_origin(cast_to) or cast_to + + if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent): + return cast(R, cast_to(response)) # type: ignore + + if origin == LegacyAPIResponse: + raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + + if inspect.isclass(origin) and issubclass(origin, httpx.Response): + # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response + # and pass that class to our request functions. We cannot change the variance to be either + # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct + # the response class ourselves but that is something that should be supported directly in httpx + # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. + if cast_to != httpx.Response: + raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") + return cast(R, response) + + # The check here is necessary as we are subverting the the type system + # with casts as the relationship between TypeVars and Types are very strict + # which means we must return *exactly* what was input or transform it in a + # way that retains the TypeVar state. As we cannot do that in this function + # then we have to resort to using `cast`. At the time of writing, we know this + # to be safe as we have handled all the types that could be bound to the + # `ResponseT` TypeVar, however if that TypeVar is ever updated in the future, then + # this function would become unsafe but a type checker would not report an error. + if ( + cast_to is not object + and not origin is list + and not origin is dict + and not origin is Union + and not issubclass(origin, BaseModel) + ): + raise RuntimeError( + f"Invalid state, expected {cast_to} to be a subclass type of {BaseModel}, {dict}, {list} or {Union}." + ) + + # split is required to handle cases where additional information is included + # in the response, e.g. application/json; charset=utf-8 + content_type, *_ = response.headers.get("content-type").split(";") + if content_type != "application/json": + if is_basemodel(cast_to): + try: + data = response.json() + except Exception as exc: + log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) + else: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + if self._client._strict_response_validation: + raise APIResponseValidationError( + response=response, + message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", + body=response.text, + ) + + # If the API responds with content that isn't JSON then we just return + # the (decoded) text without performing any parsing so that you can still + # handle the response however you need to. + return response.text # type: ignore + + data = response.json() + + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + @override + def __repr__(self) -> str: + return f"" + + +class MissingStreamClassError(TypeError): + def __init__(self) -> None: + super().__init__( + "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `openai._streaming` for reference", + ) + + +def _extract_stream_chunk_type(stream_cls: type) -> type: + args = get_args(stream_cls) + if not args: + raise TypeError( + f"Expected stream_cls to have been given a generic type argument, e.g. Stream[Foo] but received {stream_cls}", + ) + return cast(type, args[0]) + + +def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIResponse[R]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "true" + + kwargs["extra_headers"] = extra_headers + + return cast(LegacyAPIResponse[R], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[LegacyAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "true" + + kwargs["extra_headers"] = extra_headers + + return cast(LegacyAPIResponse[R], await func(*args, **kwargs)) + + return wrapped + + +class HttpxBinaryResponseContent: + response: httpx.Response + + def __init__(self, response: httpx.Response) -> None: + self.response = response + + @property + def content(self) -> bytes: + return self.response.content + + @property + def text(self) -> str: + return self.response.text + + @property + def encoding(self) -> str | None: + return self.response.encoding + + @property + def charset_encoding(self) -> str | None: + return self.response.charset_encoding + + def json(self, **kwargs: Any) -> Any: + return self.response.json(**kwargs) + + def read(self) -> bytes: + return self.response.read() + + def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: + return self.response.iter_bytes(chunk_size) + + def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: + return self.response.iter_text(chunk_size) + + def iter_lines(self) -> Iterator[str]: + return self.response.iter_lines() + + def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]: + return self.response.iter_raw(chunk_size) + + @deprecated( + "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" + ) + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + with open(file, mode="wb") as f: + for data in self.response.iter_bytes(chunk_size): + f.write(data) + + def close(self) -> None: + return self.response.close() + + async def aread(self) -> bytes: + return await self.response.aread() + + async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + return self.response.aiter_bytes(chunk_size) + + async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: + return self.response.aiter_text(chunk_size) + + async def aiter_lines(self) -> AsyncIterator[str]: + return self.response.aiter_lines() + + async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + return self.response.aiter_raw(chunk_size) + + @deprecated( + "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" + ) + async def astream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.response.aiter_bytes(chunk_size): + await f.write(data) + + async def aclose(self) -> None: + return await self.response.aclose() diff --git a/src/openai/_response.py b/src/openai/_response.py index bf72d18fd5..15a323afa4 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -1,19 +1,32 @@ from __future__ import annotations +import os import inspect import logging import datetime import functools -from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, +) from typing_extensions import Awaitable, ParamSpec, override, get_origin +import anyio import httpx -from ._types import NoneType, BinaryResponseContent +from ._types import NoneType from ._utils import is_given, extract_type_var_from_base from ._models import BaseModel, is_basemodel -from ._constants import RAW_RESPONSE_HEADER -from ._exceptions import APIResponseValidationError +from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER +from ._exceptions import OpenAIError, APIResponseValidationError if TYPE_CHECKING: from ._models import FinalRequestOptions @@ -22,15 +35,17 @@ P = ParamSpec("P") R = TypeVar("R") +_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") +_AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") log: logging.Logger = logging.getLogger(__name__) -class APIResponse(Generic[R]): +class BaseAPIResponse(Generic[R]): _cast_to: type[R] _client: BaseClient[Any, Any] _parsed: R | None - _stream: bool + _is_sse_stream: bool _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None _options: FinalRequestOptions @@ -49,28 +64,18 @@ def __init__( self._cast_to = cast_to self._client = client self._parsed = None - self._stream = stream + self._is_sse_stream = stream self._stream_cls = stream_cls self._options = options self.http_response = raw - def parse(self) -> R: - if self._parsed is not None: - return self._parsed - - parsed = self._parse() - if is_given(self._options.post_parser): - parsed = self._options.post_parser(parsed) - - self._parsed = parsed - return parsed - @property def headers(self) -> httpx.Headers: return self.http_response.headers @property def http_request(self) -> httpx.Request: + """Returns the httpx Request instance associated with the current response.""" return self.http_response.request @property @@ -79,20 +84,13 @@ def status_code(self) -> int: @property def url(http://github.com/self) -> httpx.URL: + """Returns the URL for which the request was made.""" return self.http_response.url @property def method(self) -> str: return self.http_request.method - @property - def content(self) -> bytes: - return self.http_response.content - - @property - def text(self) -> str: - return self.http_response.text - @property def http_version(self) -> str: return self.http_response.http_version @@ -102,13 +100,29 @@ def elapsed(self) -> datetime.timedelta: """The time taken for the complete request/response cycle to complete.""" return self.http_response.elapsed + @property + def is_closed(self) -> bool: + """Whether or not the response body has been closed. + + If this is False then there is response data that has not been read yet. + You must either fully consume the response body or call `.close()` + before discarding the response to prevent resource leaks. + """ + return self.http_response.is_closed + + @override + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" + ) + def _parse(self) -> R: - if self._stream: + if self._is_sse_stream: if self._stream_cls: return cast( R, self._stream_cls( - cast_to=_extract_stream_chunk_type(self._stream_cls), + cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), ), @@ -135,9 +149,13 @@ def _parse(self) -> R: if cast_to == str: return cast(R, response.text) + if cast_to == bytes: + return cast(R, response.content) + origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BinaryResponseContent): + # handle the legacy binary response case + if inspect.isclass(cast_to) and cast_to.__name__ == "HttpxBinaryResponseContent": return cast(R, cast_to(response)) # type: ignore if origin == APIResponse: @@ -208,9 +226,227 @@ def _parse(self) -> R: response=response, ) - @override - def __repr__(self) -> str: - return f"" + +class APIResponse(BaseAPIResponse[R]): + def parse(self) -> R: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + """ + if self._parsed is not None: + return self._parsed + + if not self._is_sse_stream: + self.read() + + parsed = self._parse() + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed = parsed + return parsed + + def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return self.http_response.read() + except httpx.StreamConsumed as exc: + # The default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message. + raise StreamAlreadyConsumed() from exc + + def text(self) -> str: + """Read and decode the response content into a string.""" + self.read() + return self.http_response.text + + def json(self) -> object: + """Read and decode the JSON response content.""" + self.read() + return self.http_response.json() + + def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.http_response.close() + + def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + for chunk in self.http_response.iter_bytes(chunk_size): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + for chunk in self.http_response.iter_text(chunk_size): + yield chunk + + def iter_lines(self) -> Iterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + for chunk in self.http_response.iter_lines(): + yield chunk + + +class AsyncAPIResponse(BaseAPIResponse[R]): + async def parse(self) -> R: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + """ + if self._parsed is not None: + return self._parsed + + if not self._is_sse_stream: + await self.read() + + parsed = self._parse() + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed = parsed + return parsed + + async def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return await self.http_response.aread() + except httpx.StreamConsumed as exc: + # the default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message + raise StreamAlreadyConsumed() from exc + + async def text(self) -> str: + """Read and decode the response content into a string.""" + await self.read() + return self.http_response.text + + async def json(self) -> object: + """Read and decode the JSON response content.""" + await self.read() + return self.http_response.json() + + async def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.http_response.aclose() + + async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + async for chunk in self.http_response.aiter_bytes(chunk_size): + yield chunk + + async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + async for chunk in self.http_response.aiter_text(chunk_size): + yield chunk + + async def iter_lines(self) -> AsyncIterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + async for chunk in self.http_response.aiter_lines(): + yield chunk + + +class BinaryAPIResponse(APIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(): + f.write(data) + + +class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + async def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(): + await f.write(data) + + +class StreamedBinaryAPIResponse(APIResponse[bytes]): + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(chunk_size): + f.write(data) + + +class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]): + async def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(chunk_size): + await f.write(data) class MissingStreamClassError(TypeError): @@ -220,14 +456,176 @@ def __init__(self) -> None: ) -def _extract_stream_chunk_type(stream_cls: type) -> type: - from ._base_client import Stream, AsyncStream +class StreamAlreadyConsumed(OpenAIError): + """ + Attempted to read or stream content, but the content has already + been streamed. - return extract_type_var_from_base( - stream_cls, - index=0, - generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), - ) + This can happen if you use a method like `.iter_lines()` and then attempt + to read th entire response body afterwards, e.g. + + ```py + response = await client.post(...) + async for line in response.iter_lines(): + ... # do something with `line` + + content = await response.read() + # ^ error + ``` + + If you want this behaviour you'll need to either manually accumulate the response + content or call `await response.read()` before iterating over the stream. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. " + "This could be due to attempting to stream the response " + "content more than once." + "\n\n" + "You can fix this by manually accumulating the response content while streaming " + "or by calling `.read()` before starting to stream." + ) + super().__init__(message) + + +class ResponseContextManager(Generic[_APIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, request_func: Callable[[], _APIResponseT]) -> None: + self._request_func = request_func + self.__response: _APIResponseT | None = None + + def __enter__(self) -> _APIResponseT: + self.__response = self._request_func() + return self.__response + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + self.__response.close() + + +class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None: + self._api_request = api_request + self.__response: _AsyncAPIResponseT | None = None + + async def __aenter__(self) -> _AsyncAPIResponseT: + self.__response = await self._api_request + return self.__response + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + await self.__response.close() + + +def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request)) + + return wrapped + + +def async_to_streamed_response_wrapper( + func: Callable[P, Awaitable[R]], +) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) + + return wrapped + + +def to_custom_streamed_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, ResponseContextManager[_APIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request)) + + return wrapped + + +def async_to_custom_streamed_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) + + return wrapped def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: @@ -238,7 +636,7 @@ def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]] @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "true" + extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers @@ -247,18 +645,102 @@ def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: return wrapped -def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[APIResponse[R]]]: +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: """Higher order function that takes one of our bound API methods and wraps it to support returning the raw `APIResponse` object directly. """ @functools.wraps(func) - async def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "true" + extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers - return cast(APIResponse[R], await func(*args, **kwargs)) + return cast(AsyncAPIResponse[R], await func(*args, **kwargs)) return wrapped + + +def to_custom_raw_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, _APIResponseT]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(_APIResponseT, func(*args, **kwargs)) + + return wrapped + + +def async_to_custom_raw_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, Awaitable[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: + extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs)) + + return wrapped + + +def extract_stream_chunk_type(stream_cls: type) -> type: + """Given a type like `Stream[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyStream(Stream[bytes]): + ... + + extract_stream_chunk_type(MyStream) -> bytes + ``` + """ + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + ) + + +def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: + """Given a type like `APIResponse[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(APIResponse[bytes]): + ... + + extract_response_type(MyResponse) -> bytes + ``` + """ + return extract_type_var_from_base( + typ, + generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), + index=0, + ) diff --git a/src/openai/_types.py b/src/openai/_types.py index e6b83b2a3f..b5bf8f8af0 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -1,7 +1,6 @@ from __future__ import annotations from os import PathLike -from abc import ABC, abstractmethod from typing import ( IO, TYPE_CHECKING, @@ -14,10 +13,8 @@ Mapping, TypeVar, Callable, - Iterator, Optional, Sequence, - AsyncIterator, ) from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable @@ -27,6 +24,8 @@ if TYPE_CHECKING: from ._models import BaseModel + from ._response import APIResponse, AsyncAPIResponse + from ._legacy_response import HttpxBinaryResponseContent Transport = BaseTransport AsyncTransport = AsyncBaseTransport @@ -37,162 +36,6 @@ _T = TypeVar("_T") -class BinaryResponseContent(ABC): - @abstractmethod - def __init__( - self, - response: Any, - ) -> None: - ... - - @property - @abstractmethod - def content(self) -> bytes: - pass - - @property - @abstractmethod - def text(self) -> str: - pass - - @property - @abstractmethod - def encoding(self) -> Optional[str]: - """ - Return an encoding to use for decoding the byte content into text. - The priority for determining this is given by... - - * `.encoding = <>` has been set explicitly. - * The encoding as specified by the charset parameter in the Content-Type header. - * The encoding as determined by `default_encoding`, which may either be - a string like "utf-8" indicating the encoding to use, or may be a callable - which enables charset autodetection. - """ - pass - - @property - @abstractmethod - def charset_encoding(self) -> Optional[str]: - """ - Return the encoding, as specified by the Content-Type header. - """ - pass - - @abstractmethod - def json(self, **kwargs: Any) -> Any: - pass - - @abstractmethod - def read(self) -> bytes: - """ - Read and return the response content. - """ - pass - - @abstractmethod - def iter_bytes(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: - """ - A byte-iterator over the decoded response content. - This allows us to handle gzip, deflate, and brotli encoded responses. - """ - pass - - @abstractmethod - def iter_text(self, chunk_size: Optional[int] = None) -> Iterator[str]: - """ - A str-iterator over the decoded response content - that handles both gzip, deflate, etc but also detects the content's - string encoding. - """ - pass - - @abstractmethod - def iter_lines(self) -> Iterator[str]: - pass - - @abstractmethod - def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: - """ - A byte-iterator over the raw response content. - """ - pass - - @abstractmethod - def stream_to_file( - self, - file: str | PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - """ - Stream the output to the given file. - """ - pass - - @abstractmethod - def close(self) -> None: - """ - Close the response and release the connection. - Automatically called if the response body is read to completion. - """ - pass - - @abstractmethod - async def aread(self) -> bytes: - """ - Read and return the response content. - """ - pass - - @abstractmethod - async def aiter_bytes(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]: - """ - A byte-iterator over the decoded response content. - This allows us to handle gzip, deflate, and brotli encoded responses. - """ - pass - - @abstractmethod - async def aiter_text(self, chunk_size: Optional[int] = None) -> AsyncIterator[str]: - """ - A str-iterator over the decoded response content - that handles both gzip, deflate, etc but also detects the content's - string encoding. - """ - pass - - @abstractmethod - async def aiter_lines(self) -> AsyncIterator[str]: - pass - - @abstractmethod - async def aiter_raw(self, chunk_size: Optional[int] = None) -> AsyncIterator[bytes]: - """ - A byte-iterator over the raw response content. - """ - pass - - @abstractmethod - async def astream_to_file( - self, - file: str | PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - """ - Stream the output to the given file. - """ - pass - - @abstractmethod - async def aclose(self) -> None: - """ - Close the response and release the connection. - Automatically called if the response body is read to completion. - """ - pass - - # Approximates httpx internal ProxiesTypes and RequestFiles types # while adding support for `PathLike` instances ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] @@ -343,7 +186,9 @@ def get(self, __key: str) -> str | None: Dict[str, Any], Response, ModelBuilderProtocol, - BinaryResponseContent, + "APIResponse[Any]", + "AsyncAPIResponse[Any]", + "HttpxBinaryResponseContent", ], ) @@ -359,6 +204,7 @@ def get(self, __key: str) -> str | None: @runtime_checkable class InheritsGeneric(Protocol): """Represents a type that has inherited from `Generic` + The `__orig_bases__` property can be used to determine the resolved type variable for a given base class. """ diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index 8219be12e6..1fb4aa62ec 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -1,55 +1,145 @@ # File generated from our OpenAPI spec by Stainless. -from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse -from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse -from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse -from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .images import Images, AsyncImages, ImagesWithRawResponse, AsyncImagesWithRawResponse -from .models import Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse -from .embeddings import Embeddings, AsyncEmbeddings, EmbeddingsWithRawResponse, AsyncEmbeddingsWithRawResponse -from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse -from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse -from .moderations import Moderations, AsyncModerations, ModerationsWithRawResponse, AsyncModerationsWithRawResponse +from .beta import ( + Beta, + AsyncBeta, + BetaWithRawResponse, + AsyncBetaWithRawResponse, + BetaWithStreamingResponse, + AsyncBetaWithStreamingResponse, +) +from .chat import ( + Chat, + AsyncChat, + ChatWithRawResponse, + AsyncChatWithRawResponse, + ChatWithStreamingResponse, + AsyncChatWithStreamingResponse, +) +from .audio import ( + Audio, + AsyncAudio, + AudioWithRawResponse, + AsyncAudioWithRawResponse, + AudioWithStreamingResponse, + AsyncAudioWithStreamingResponse, +) +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) +from .images import ( + Images, + AsyncImages, + ImagesWithRawResponse, + AsyncImagesWithRawResponse, + ImagesWithStreamingResponse, + AsyncImagesWithStreamingResponse, +) +from .models import ( + Models, + AsyncModels, + ModelsWithRawResponse, + AsyncModelsWithRawResponse, + ModelsWithStreamingResponse, + AsyncModelsWithStreamingResponse, +) +from .embeddings import ( + Embeddings, + AsyncEmbeddings, + EmbeddingsWithRawResponse, + AsyncEmbeddingsWithRawResponse, + EmbeddingsWithStreamingResponse, + AsyncEmbeddingsWithStreamingResponse, +) +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, + CompletionsWithStreamingResponse, + AsyncCompletionsWithStreamingResponse, +) +from .fine_tuning import ( + FineTuning, + AsyncFineTuning, + FineTuningWithRawResponse, + AsyncFineTuningWithRawResponse, + FineTuningWithStreamingResponse, + AsyncFineTuningWithStreamingResponse, +) +from .moderations import ( + Moderations, + AsyncModerations, + ModerationsWithRawResponse, + AsyncModerationsWithRawResponse, + ModerationsWithStreamingResponse, + AsyncModerationsWithStreamingResponse, +) __all__ = [ "Completions", "AsyncCompletions", "CompletionsWithRawResponse", "AsyncCompletionsWithRawResponse", + "CompletionsWithStreamingResponse", + "AsyncCompletionsWithStreamingResponse", "Chat", "AsyncChat", "ChatWithRawResponse", "AsyncChatWithRawResponse", + "ChatWithStreamingResponse", + "AsyncChatWithStreamingResponse", "Embeddings", "AsyncEmbeddings", "EmbeddingsWithRawResponse", "AsyncEmbeddingsWithRawResponse", + "EmbeddingsWithStreamingResponse", + "AsyncEmbeddingsWithStreamingResponse", "Files", "AsyncFiles", "FilesWithRawResponse", "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", "Images", "AsyncImages", "ImagesWithRawResponse", "AsyncImagesWithRawResponse", + "ImagesWithStreamingResponse", + "AsyncImagesWithStreamingResponse", "Audio", "AsyncAudio", "AudioWithRawResponse", "AsyncAudioWithRawResponse", + "AudioWithStreamingResponse", + "AsyncAudioWithStreamingResponse", "Moderations", "AsyncModerations", "ModerationsWithRawResponse", "AsyncModerationsWithRawResponse", + "ModerationsWithStreamingResponse", + "AsyncModerationsWithStreamingResponse", "Models", "AsyncModels", "ModelsWithRawResponse", "AsyncModelsWithRawResponse", + "ModelsWithStreamingResponse", + "AsyncModelsWithStreamingResponse", "FineTuning", "AsyncFineTuning", "FineTuningWithRawResponse", "AsyncFineTuningWithRawResponse", + "FineTuningWithStreamingResponse", + "AsyncFineTuningWithStreamingResponse", "Beta", "AsyncBeta", "BetaWithRawResponse", "AsyncBetaWithRawResponse", + "BetaWithStreamingResponse", + "AsyncBetaWithStreamingResponse", ] diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py index b6ff4322d4..63d06494b8 100644 --- a/src/openai/resources/audio/__init__.py +++ b/src/openai/resources/audio/__init__.py @@ -1,13 +1,36 @@ # File generated from our OpenAPI spec by Stainless. -from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse -from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse -from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse +from .audio import ( + Audio, + AsyncAudio, + AudioWithRawResponse, + AsyncAudioWithRawResponse, + AudioWithStreamingResponse, + AsyncAudioWithStreamingResponse, +) +from .speech import ( + Speech, + AsyncSpeech, + SpeechWithRawResponse, + AsyncSpeechWithRawResponse, + SpeechWithStreamingResponse, + AsyncSpeechWithStreamingResponse, +) +from .translations import ( + Translations, + AsyncTranslations, + TranslationsWithRawResponse, + AsyncTranslationsWithRawResponse, + TranslationsWithStreamingResponse, + AsyncTranslationsWithStreamingResponse, +) from .transcriptions import ( Transcriptions, AsyncTranscriptions, TranscriptionsWithRawResponse, AsyncTranscriptionsWithRawResponse, + TranscriptionsWithStreamingResponse, + AsyncTranscriptionsWithStreamingResponse, ) __all__ = [ @@ -15,16 +38,24 @@ "AsyncTranscriptions", "TranscriptionsWithRawResponse", "AsyncTranscriptionsWithRawResponse", + "TranscriptionsWithStreamingResponse", + "AsyncTranscriptionsWithStreamingResponse", "Translations", "AsyncTranslations", "TranslationsWithRawResponse", "AsyncTranslationsWithRawResponse", + "TranslationsWithStreamingResponse", + "AsyncTranslationsWithStreamingResponse", "Speech", "AsyncSpeech", "SpeechWithRawResponse", "AsyncSpeechWithRawResponse", + "SpeechWithStreamingResponse", + "AsyncSpeechWithStreamingResponse", "Audio", "AsyncAudio", "AudioWithRawResponse", "AsyncAudioWithRawResponse", + "AudioWithStreamingResponse", + "AsyncAudioWithStreamingResponse", ] diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 4e3ca0ed4f..b14e64cff6 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -2,15 +2,31 @@ from __future__ import annotations -from .speech import Speech, AsyncSpeech, SpeechWithRawResponse, AsyncSpeechWithRawResponse +from .speech import ( + Speech, + AsyncSpeech, + SpeechWithRawResponse, + AsyncSpeechWithRawResponse, + SpeechWithStreamingResponse, + AsyncSpeechWithStreamingResponse, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -from .translations import Translations, AsyncTranslations, TranslationsWithRawResponse, AsyncTranslationsWithRawResponse +from .translations import ( + Translations, + AsyncTranslations, + TranslationsWithRawResponse, + AsyncTranslationsWithRawResponse, + TranslationsWithStreamingResponse, + AsyncTranslationsWithStreamingResponse, +) from .transcriptions import ( Transcriptions, AsyncTranscriptions, TranscriptionsWithRawResponse, AsyncTranscriptionsWithRawResponse, + TranscriptionsWithStreamingResponse, + AsyncTranscriptionsWithStreamingResponse, ) __all__ = ["Audio", "AsyncAudio"] @@ -33,6 +49,10 @@ def speech(self) -> Speech: def with_raw_response(self) -> AudioWithRawResponse: return AudioWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AudioWithStreamingResponse: + return AudioWithStreamingResponse(self) + class AsyncAudio(AsyncAPIResource): @cached_property @@ -51,6 +71,10 @@ def speech(self) -> AsyncSpeech: def with_raw_response(self) -> AsyncAudioWithRawResponse: return AsyncAudioWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncAudioWithStreamingResponse: + return AsyncAudioWithStreamingResponse(self) + class AudioWithRawResponse: def __init__(self, audio: Audio) -> None: @@ -64,3 +88,17 @@ def __init__(self, audio: AsyncAudio) -> None: self.transcriptions = AsyncTranscriptionsWithRawResponse(audio.transcriptions) self.translations = AsyncTranslationsWithRawResponse(audio.translations) self.speech = AsyncSpeechWithRawResponse(audio.speech) + + +class AudioWithStreamingResponse: + def __init__(self, audio: Audio) -> None: + self.transcriptions = TranscriptionsWithStreamingResponse(audio.transcriptions) + self.translations = TranslationsWithStreamingResponse(audio.translations) + self.speech = SpeechWithStreamingResponse(audio.speech) + + +class AsyncAudioWithStreamingResponse: + def __init__(self, audio: AsyncAudio) -> None: + self.transcriptions = AsyncTranscriptionsWithStreamingResponse(audio.transcriptions) + self.translations = AsyncTranslationsWithStreamingResponse(audio.translations) + self.speech = AsyncSpeechWithStreamingResponse(audio.speech) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index b7cd3733a9..9c051624d5 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -7,14 +7,19 @@ import httpx +from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ..._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) from ...types.audio import speech_create_params from ..._base_client import ( - HttpxBinaryResponseContent, make_request_options, ) @@ -26,6 +31,10 @@ class Speech(SyncAPIResource): def with_raw_response(self) -> SpeechWithRawResponse: return SpeechWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> SpeechWithStreamingResponse: + return SpeechWithStreamingResponse(self) + def create( self, *, @@ -40,7 +49,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> HttpxBinaryResponseContent: + ) -> _legacy_response.HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -84,7 +93,7 @@ def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=HttpxBinaryResponseContent, + cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -93,6 +102,10 @@ class AsyncSpeech(AsyncAPIResource): def with_raw_response(self) -> AsyncSpeechWithRawResponse: return AsyncSpeechWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncSpeechWithStreamingResponse: + return AsyncSpeechWithStreamingResponse(self) + async def create( self, *, @@ -107,7 +120,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> HttpxBinaryResponseContent: + ) -> _legacy_response.HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -151,19 +164,35 @@ async def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=HttpxBinaryResponseContent, + cast_to=_legacy_response.HttpxBinaryResponseContent, ) class SpeechWithRawResponse: def __init__(self, speech: Speech) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( speech.create, ) class AsyncSpeechWithRawResponse: def __init__(self, speech: AsyncSpeech) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + speech.create, + ) + + +class SpeechWithStreamingResponse: + def __init__(self, speech: Speech) -> None: + self.create = to_custom_streamed_response_wrapper( + speech.create, + StreamedBinaryAPIResponse, + ) + + +class AsyncSpeechWithStreamingResponse: + def __init__(self, speech: AsyncSpeech) -> None: + self.create = async_to_custom_streamed_response_wrapper( speech.create, + AsyncStreamedBinaryAPIResponse, ) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 7d7441a9f6..053ac30095 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -7,11 +7,12 @@ import httpx +from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...types.audio import Transcription, transcription_create_params from ..._base_client import ( make_request_options, @@ -25,6 +26,10 @@ class Transcriptions(SyncAPIResource): def with_raw_response(self) -> TranscriptionsWithRawResponse: return TranscriptionsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> TranscriptionsWithStreamingResponse: + return TranscriptionsWithStreamingResponse(self) + def create( self, *, @@ -110,6 +115,10 @@ class AsyncTranscriptions(AsyncAPIResource): def with_raw_response(self) -> AsyncTranscriptionsWithRawResponse: return AsyncTranscriptionsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncTranscriptionsWithStreamingResponse: + return AsyncTranscriptionsWithStreamingResponse(self) + async def create( self, *, @@ -192,13 +201,27 @@ async def create( class TranscriptionsWithRawResponse: def __init__(self, transcriptions: Transcriptions) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( transcriptions.create, ) class AsyncTranscriptionsWithRawResponse: def __init__(self, transcriptions: AsyncTranscriptions) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + transcriptions.create, + ) + + +class TranscriptionsWithStreamingResponse: + def __init__(self, transcriptions: Transcriptions) -> None: + self.create = to_streamed_response_wrapper( + transcriptions.create, + ) + + +class AsyncTranscriptionsWithStreamingResponse: + def __init__(self, transcriptions: AsyncTranscriptions) -> None: + self.create = async_to_streamed_response_wrapper( transcriptions.create, ) diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 7f5f65b6c8..db41b194b6 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -7,11 +7,12 @@ import httpx +from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import extract_files, maybe_transform, deepcopy_minimal from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...types.audio import Translation, translation_create_params from ..._base_client import ( make_request_options, @@ -25,6 +26,10 @@ class Translations(SyncAPIResource): def with_raw_response(self) -> TranslationsWithRawResponse: return TranslationsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> TranslationsWithStreamingResponse: + return TranslationsWithStreamingResponse(self) + def create( self, *, @@ -103,6 +108,10 @@ class AsyncTranslations(AsyncAPIResource): def with_raw_response(self) -> AsyncTranslationsWithRawResponse: return AsyncTranslationsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncTranslationsWithStreamingResponse: + return AsyncTranslationsWithStreamingResponse(self) + async def create( self, *, @@ -178,13 +187,27 @@ async def create( class TranslationsWithRawResponse: def __init__(self, translations: Translations) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( translations.create, ) class AsyncTranslationsWithRawResponse: def __init__(self, translations: AsyncTranslations) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + translations.create, + ) + + +class TranslationsWithStreamingResponse: + def __init__(self, translations: Translations) -> None: + self.create = to_streamed_response_wrapper( + translations.create, + ) + + +class AsyncTranslationsWithStreamingResponse: + def __init__(self, translations: AsyncTranslations) -> None: + self.create = async_to_streamed_response_wrapper( translations.create, ) diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 561f8bef60..973c6ba54e 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -1,20 +1,47 @@ # File generated from our OpenAPI spec by Stainless. -from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse -from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse -from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse +from .beta import ( + Beta, + AsyncBeta, + BetaWithRawResponse, + AsyncBetaWithRawResponse, + BetaWithStreamingResponse, + AsyncBetaWithStreamingResponse, +) +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse, +) +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, + AssistantsWithStreamingResponse, + AsyncAssistantsWithStreamingResponse, +) __all__ = [ "Assistants", "AsyncAssistants", "AssistantsWithRawResponse", "AsyncAssistantsWithRawResponse", + "AssistantsWithStreamingResponse", + "AsyncAssistantsWithStreamingResponse", "Threads", "AsyncThreads", "ThreadsWithRawResponse", "AsyncThreadsWithRawResponse", + "ThreadsWithStreamingResponse", + "AsyncThreadsWithStreamingResponse", "Beta", "AsyncBeta", "BetaWithRawResponse", "AsyncBetaWithRawResponse", + "BetaWithStreamingResponse", + "AsyncBetaWithStreamingResponse", ] diff --git a/src/openai/resources/beta/assistants/__init__.py b/src/openai/resources/beta/assistants/__init__.py index 205b2cf0f5..ad04a71572 100644 --- a/src/openai/resources/beta/assistants/__init__.py +++ b/src/openai/resources/beta/assistants/__init__.py @@ -1,15 +1,33 @@ # File generated from our OpenAPI spec by Stainless. -from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, + AssistantsWithStreamingResponse, + AsyncAssistantsWithStreamingResponse, +) __all__ = [ "Files", "AsyncFiles", "FilesWithRawResponse", "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", "Assistants", "AsyncAssistants", "AssistantsWithRawResponse", "AsyncAssistantsWithRawResponse", + "AssistantsWithStreamingResponse", + "AsyncAssistantsWithStreamingResponse", ] diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 0ae054795d..176bf05516 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -7,12 +7,20 @@ import httpx -from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse +from .... import _legacy_response +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage from ....types.beta import ( Assistant, @@ -38,6 +46,10 @@ def files(self) -> Files: def with_raw_response(self) -> AssistantsWithRawResponse: return AssistantsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AssistantsWithStreamingResponse: + return AssistantsWithStreamingResponse(self) + def create( self, *, @@ -331,6 +343,10 @@ def files(self) -> AsyncFiles: def with_raw_response(self) -> AsyncAssistantsWithRawResponse: return AsyncAssistantsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncAssistantsWithStreamingResponse: + return AsyncAssistantsWithStreamingResponse(self) + async def create( self, *, @@ -619,19 +635,19 @@ class AssistantsWithRawResponse: def __init__(self, assistants: Assistants) -> None: self.files = FilesWithRawResponse(assistants.files) - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( assistants.create, ) - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( assistants.retrieve, ) - self.update = to_raw_response_wrapper( + self.update = _legacy_response.to_raw_response_wrapper( assistants.update, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( assistants.list, ) - self.delete = to_raw_response_wrapper( + self.delete = _legacy_response.to_raw_response_wrapper( assistants.delete, ) @@ -640,18 +656,60 @@ class AsyncAssistantsWithRawResponse: def __init__(self, assistants: AsyncAssistants) -> None: self.files = AsyncFilesWithRawResponse(assistants.files) - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + assistants.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + assistants.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + assistants.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + assistants.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + assistants.delete, + ) + + +class AssistantsWithStreamingResponse: + def __init__(self, assistants: Assistants) -> None: + self.files = FilesWithStreamingResponse(assistants.files) + + self.create = to_streamed_response_wrapper( + assistants.create, + ) + self.retrieve = to_streamed_response_wrapper( + assistants.retrieve, + ) + self.update = to_streamed_response_wrapper( + assistants.update, + ) + self.list = to_streamed_response_wrapper( + assistants.list, + ) + self.delete = to_streamed_response_wrapper( + assistants.delete, + ) + + +class AsyncAssistantsWithStreamingResponse: + def __init__(self, assistants: AsyncAssistants) -> None: + self.files = AsyncFilesWithStreamingResponse(assistants.files) + + self.create = async_to_streamed_response_wrapper( assistants.create, ) - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = async_to_streamed_response_wrapper( assistants.retrieve, ) - self.update = async_to_raw_response_wrapper( + self.update = async_to_streamed_response_wrapper( assistants.update, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( assistants.list, ) - self.delete = async_to_raw_response_wrapper( + self.delete = async_to_streamed_response_wrapper( assistants.delete, ) diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index 0624e562f8..9e45ce46d3 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -6,11 +6,12 @@ import httpx +from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage from ...._base_client import ( AsyncPaginator, @@ -26,6 +27,10 @@ class Files(SyncAPIResource): def with_raw_response(self) -> FilesWithRawResponse: return FilesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self) + def create( self, assistant_id: str, @@ -203,6 +208,10 @@ class AsyncFiles(AsyncAPIResource): def with_raw_response(self) -> AsyncFilesWithRawResponse: return AsyncFilesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self) + async def create( self, assistant_id: str, @@ -377,31 +386,63 @@ async def delete( class FilesWithRawResponse: def __init__(self, files: Files) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( files.create, ) - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( files.retrieve, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( files.list, ) - self.delete = to_raw_response_wrapper( + self.delete = _legacy_response.to_raw_response_wrapper( files.delete, ) class AsyncFilesWithRawResponse: def __init__(self, files: AsyncFiles) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + files.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + files.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + files.delete, + ) + + +class FilesWithStreamingResponse: + def __init__(self, files: Files) -> None: + self.create = to_streamed_response_wrapper( + files.create, + ) + self.retrieve = to_streamed_response_wrapper( + files.retrieve, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + self.delete = to_streamed_response_wrapper( + files.delete, + ) + + +class AsyncFilesWithStreamingResponse: + def __init__(self, files: AsyncFiles) -> None: + self.create = async_to_streamed_response_wrapper( files.create, ) - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = async_to_streamed_response_wrapper( files.retrieve, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( files.list, ) - self.delete = async_to_raw_response_wrapper( + self.delete = async_to_streamed_response_wrapper( files.delete, ) diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index d87406ac9d..b11a706d5d 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -2,9 +2,23 @@ from __future__ import annotations -from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse, +) from ..._compat import cached_property -from .assistants import Assistants, AsyncAssistants, AssistantsWithRawResponse, AsyncAssistantsWithRawResponse +from .assistants import ( + Assistants, + AsyncAssistants, + AssistantsWithRawResponse, + AsyncAssistantsWithRawResponse, + AssistantsWithStreamingResponse, + AsyncAssistantsWithStreamingResponse, +) from ..._resource import SyncAPIResource, AsyncAPIResource from .threads.threads import Threads, AsyncThreads from .assistants.assistants import Assistants, AsyncAssistants @@ -25,6 +39,10 @@ def threads(self) -> Threads: def with_raw_response(self) -> BetaWithRawResponse: return BetaWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> BetaWithStreamingResponse: + return BetaWithStreamingResponse(self) + class AsyncBeta(AsyncAPIResource): @cached_property @@ -39,6 +57,10 @@ def threads(self) -> AsyncThreads: def with_raw_response(self) -> AsyncBetaWithRawResponse: return AsyncBetaWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: + return AsyncBetaWithStreamingResponse(self) + class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: @@ -50,3 +72,15 @@ class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self.assistants = AsyncAssistantsWithRawResponse(beta.assistants) self.threads = AsyncThreadsWithRawResponse(beta.threads) + + +class BetaWithStreamingResponse: + def __init__(self, beta: Beta) -> None: + self.assistants = AssistantsWithStreamingResponse(beta.assistants) + self.threads = ThreadsWithStreamingResponse(beta.threads) + + +class AsyncBetaWithStreamingResponse: + def __init__(self, beta: AsyncBeta) -> None: + self.assistants = AsyncAssistantsWithStreamingResponse(beta.assistants) + self.threads = AsyncThreadsWithStreamingResponse(beta.threads) diff --git a/src/openai/resources/beta/threads/__init__.py b/src/openai/resources/beta/threads/__init__.py index fe7c5e5a20..886574b327 100644 --- a/src/openai/resources/beta/threads/__init__.py +++ b/src/openai/resources/beta/threads/__init__.py @@ -1,20 +1,47 @@ # File generated from our OpenAPI spec by Stainless. -from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .threads import Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse -from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse +from .runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse, +) +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse, +) __all__ = [ "Runs", "AsyncRuns", "RunsWithRawResponse", "AsyncRunsWithRawResponse", + "RunsWithStreamingResponse", + "AsyncRunsWithStreamingResponse", "Messages", "AsyncMessages", "MessagesWithRawResponse", "AsyncMessagesWithRawResponse", + "MessagesWithStreamingResponse", + "AsyncMessagesWithStreamingResponse", "Threads", "AsyncThreads", "ThreadsWithRawResponse", "AsyncThreadsWithRawResponse", + "ThreadsWithStreamingResponse", + "AsyncThreadsWithStreamingResponse", ] diff --git a/src/openai/resources/beta/threads/messages/__init__.py b/src/openai/resources/beta/threads/messages/__init__.py index cef618ed14..0acb0ab201 100644 --- a/src/openai/resources/beta/threads/messages/__init__.py +++ b/src/openai/resources/beta/threads/messages/__init__.py @@ -1,15 +1,33 @@ # File generated from our OpenAPI spec by Stainless. -from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse -from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse, +) __all__ = [ "Files", "AsyncFiles", "FilesWithRawResponse", "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", "Messages", "AsyncMessages", "MessagesWithRawResponse", "AsyncMessagesWithRawResponse", + "MessagesWithStreamingResponse", + "AsyncMessagesWithStreamingResponse", ] diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index 4b95b200eb..d0a963f1ae 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -6,11 +6,12 @@ import httpx +from ..... import _legacy_response from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage from ....._base_client import ( AsyncPaginator, @@ -26,6 +27,10 @@ class Files(SyncAPIResource): def with_raw_response(self) -> FilesWithRawResponse: return FilesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self) + def retrieve( self, file_id: str, @@ -133,6 +138,10 @@ class AsyncFiles(AsyncAPIResource): def with_raw_response(self) -> AsyncFilesWithRawResponse: return AsyncFilesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self) + async def retrieve( self, file_id: str, @@ -237,19 +246,39 @@ def list( class FilesWithRawResponse: def __init__(self, files: Files) -> None: - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( files.retrieve, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( files.list, ) class AsyncFilesWithRawResponse: def __init__(self, files: AsyncFiles) -> None: - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + files.list, + ) + + +class FilesWithStreamingResponse: + def __init__(self, files: Files) -> None: + self.retrieve = to_streamed_response_wrapper( + files.retrieve, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + + +class AsyncFilesWithStreamingResponse: + def __init__(self, files: AsyncFiles) -> None: + self.retrieve = async_to_streamed_response_wrapper( files.retrieve, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( files.list, ) diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 146f665624..1a15dd36ca 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -7,12 +7,20 @@ import httpx -from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse +from ..... import _legacy_response +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage from ....._base_client import ( AsyncPaginator, @@ -32,6 +40,10 @@ def files(self) -> Files: def with_raw_response(self) -> MessagesWithRawResponse: return MessagesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> MessagesWithStreamingResponse: + return MessagesWithStreamingResponse(self) + def create( self, thread_id: str, @@ -240,6 +252,10 @@ def files(self) -> AsyncFiles: def with_raw_response(self) -> AsyncMessagesWithRawResponse: return AsyncMessagesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: + return AsyncMessagesWithStreamingResponse(self) + async def create( self, thread_id: str, @@ -443,16 +459,16 @@ class MessagesWithRawResponse: def __init__(self, messages: Messages) -> None: self.files = FilesWithRawResponse(messages.files) - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( messages.create, ) - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( messages.retrieve, ) - self.update = to_raw_response_wrapper( + self.update = _legacy_response.to_raw_response_wrapper( messages.update, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( messages.list, ) @@ -461,15 +477,51 @@ class AsyncMessagesWithRawResponse: def __init__(self, messages: AsyncMessages) -> None: self.files = AsyncFilesWithRawResponse(messages.files) - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + messages.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + messages.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + messages.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + messages.list, + ) + + +class MessagesWithStreamingResponse: + def __init__(self, messages: Messages) -> None: + self.files = FilesWithStreamingResponse(messages.files) + + self.create = to_streamed_response_wrapper( + messages.create, + ) + self.retrieve = to_streamed_response_wrapper( + messages.retrieve, + ) + self.update = to_streamed_response_wrapper( + messages.update, + ) + self.list = to_streamed_response_wrapper( + messages.list, + ) + + +class AsyncMessagesWithStreamingResponse: + def __init__(self, messages: AsyncMessages) -> None: + self.files = AsyncFilesWithStreamingResponse(messages.files) + + self.create = async_to_streamed_response_wrapper( messages.create, ) - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = async_to_streamed_response_wrapper( messages.retrieve, ) - self.update = async_to_raw_response_wrapper( + self.update = async_to_streamed_response_wrapper( messages.update, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( messages.list, ) diff --git a/src/openai/resources/beta/threads/runs/__init__.py b/src/openai/resources/beta/threads/runs/__init__.py index 6b61813974..659c96acfb 100644 --- a/src/openai/resources/beta/threads/runs/__init__.py +++ b/src/openai/resources/beta/threads/runs/__init__.py @@ -1,15 +1,33 @@ # File generated from our OpenAPI spec by Stainless. -from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse +from .runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) +from .steps import ( + Steps, + AsyncSteps, + StepsWithRawResponse, + AsyncStepsWithRawResponse, + StepsWithStreamingResponse, + AsyncStepsWithStreamingResponse, +) __all__ = [ "Steps", "AsyncSteps", "StepsWithRawResponse", "AsyncStepsWithRawResponse", + "StepsWithStreamingResponse", + "AsyncStepsWithStreamingResponse", "Runs", "AsyncRuns", "RunsWithRawResponse", "AsyncRunsWithRawResponse", + "RunsWithStreamingResponse", + "AsyncRunsWithStreamingResponse", ] diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 87e62eb362..eb6c974eaa 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -7,12 +7,20 @@ import httpx -from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse +from ..... import _legacy_response +from .steps import ( + Steps, + AsyncSteps, + StepsWithRawResponse, + AsyncStepsWithRawResponse, + StepsWithStreamingResponse, + AsyncStepsWithStreamingResponse, +) from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage from ....._base_client import ( AsyncPaginator, @@ -38,6 +46,10 @@ def steps(self) -> Steps: def with_raw_response(self) -> RunsWithRawResponse: return RunsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> RunsWithStreamingResponse: + return RunsWithStreamingResponse(self) + def create( self, thread_id: str, @@ -335,6 +347,10 @@ def steps(self) -> AsyncSteps: def with_raw_response(self) -> AsyncRunsWithRawResponse: return AsyncRunsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncRunsWithStreamingResponse: + return AsyncRunsWithStreamingResponse(self) + async def create( self, thread_id: str, @@ -627,22 +643,22 @@ class RunsWithRawResponse: def __init__(self, runs: Runs) -> None: self.steps = StepsWithRawResponse(runs.steps) - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( runs.create, ) - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( runs.retrieve, ) - self.update = to_raw_response_wrapper( + self.update = _legacy_response.to_raw_response_wrapper( runs.update, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( runs.list, ) - self.cancel = to_raw_response_wrapper( + self.cancel = _legacy_response.to_raw_response_wrapper( runs.cancel, ) - self.submit_tool_outputs = to_raw_response_wrapper( + self.submit_tool_outputs = _legacy_response.to_raw_response_wrapper( runs.submit_tool_outputs, ) @@ -651,21 +667,69 @@ class AsyncRunsWithRawResponse: def __init__(self, runs: AsyncRuns) -> None: self.steps = AsyncStepsWithRawResponse(runs.steps) - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + runs.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + runs.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + runs.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + runs.list, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + runs.cancel, + ) + self.submit_tool_outputs = _legacy_response.async_to_raw_response_wrapper( + runs.submit_tool_outputs, + ) + + +class RunsWithStreamingResponse: + def __init__(self, runs: Runs) -> None: + self.steps = StepsWithStreamingResponse(runs.steps) + + self.create = to_streamed_response_wrapper( + runs.create, + ) + self.retrieve = to_streamed_response_wrapper( + runs.retrieve, + ) + self.update = to_streamed_response_wrapper( + runs.update, + ) + self.list = to_streamed_response_wrapper( + runs.list, + ) + self.cancel = to_streamed_response_wrapper( + runs.cancel, + ) + self.submit_tool_outputs = to_streamed_response_wrapper( + runs.submit_tool_outputs, + ) + + +class AsyncRunsWithStreamingResponse: + def __init__(self, runs: AsyncRuns) -> None: + self.steps = AsyncStepsWithStreamingResponse(runs.steps) + + self.create = async_to_streamed_response_wrapper( runs.create, ) - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = async_to_streamed_response_wrapper( runs.retrieve, ) - self.update = async_to_raw_response_wrapper( + self.update = async_to_streamed_response_wrapper( runs.update, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( runs.list, ) - self.cancel = async_to_raw_response_wrapper( + self.cancel = async_to_streamed_response_wrapper( runs.cancel, ) - self.submit_tool_outputs = async_to_raw_response_wrapper( + self.submit_tool_outputs = async_to_streamed_response_wrapper( runs.submit_tool_outputs, ) diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 439926a412..566ad9e4dc 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -6,11 +6,12 @@ import httpx +from ..... import _legacy_response from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage from ....._base_client import ( AsyncPaginator, @@ -26,6 +27,10 @@ class Steps(SyncAPIResource): def with_raw_response(self) -> StepsWithRawResponse: return StepsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> StepsWithStreamingResponse: + return StepsWithStreamingResponse(self) + def retrieve( self, step_id: str, @@ -132,6 +137,10 @@ class AsyncSteps(AsyncAPIResource): def with_raw_response(self) -> AsyncStepsWithRawResponse: return AsyncStepsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncStepsWithStreamingResponse: + return AsyncStepsWithStreamingResponse(self) + async def retrieve( self, step_id: str, @@ -235,19 +244,39 @@ def list( class StepsWithRawResponse: def __init__(self, steps: Steps) -> None: - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( steps.retrieve, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( steps.list, ) class AsyncStepsWithRawResponse: def __init__(self, steps: AsyncSteps) -> None: - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + steps.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + steps.list, + ) + + +class StepsWithStreamingResponse: + def __init__(self, steps: Steps) -> None: + self.retrieve = to_streamed_response_wrapper( + steps.retrieve, + ) + self.list = to_streamed_response_wrapper( + steps.list, + ) + + +class AsyncStepsWithStreamingResponse: + def __init__(self, steps: AsyncSteps) -> None: + self.retrieve = async_to_streamed_response_wrapper( steps.retrieve, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( steps.list, ) diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 0ae409bb24..14bfbe9bba 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -6,14 +6,29 @@ import httpx -from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse -from .messages import Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse +from .... import _legacy_response +from .runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse, +) from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import maybe_transform from .runs.runs import Runs, AsyncRuns from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....types.beta import ( Thread, ThreadDeleted, @@ -43,6 +58,10 @@ def messages(self) -> Messages: def with_raw_response(self) -> ThreadsWithRawResponse: return ThreadsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> ThreadsWithStreamingResponse: + return ThreadsWithStreamingResponse(self) + def create( self, *, @@ -278,6 +297,10 @@ def messages(self) -> AsyncMessages: def with_raw_response(self) -> AsyncThreadsWithRawResponse: return AsyncThreadsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse: + return AsyncThreadsWithStreamingResponse(self) + async def create( self, *, @@ -505,19 +528,19 @@ def __init__(self, threads: Threads) -> None: self.runs = RunsWithRawResponse(threads.runs) self.messages = MessagesWithRawResponse(threads.messages) - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( threads.create, ) - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( threads.retrieve, ) - self.update = to_raw_response_wrapper( + self.update = _legacy_response.to_raw_response_wrapper( threads.update, ) - self.delete = to_raw_response_wrapper( + self.delete = _legacy_response.to_raw_response_wrapper( threads.delete, ) - self.create_and_run = to_raw_response_wrapper( + self.create_and_run = _legacy_response.to_raw_response_wrapper( threads.create_and_run, ) @@ -527,18 +550,62 @@ def __init__(self, threads: AsyncThreads) -> None: self.runs = AsyncRunsWithRawResponse(threads.runs) self.messages = AsyncMessagesWithRawResponse(threads.messages) - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + threads.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + threads.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + threads.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + threads.delete, + ) + self.create_and_run = _legacy_response.async_to_raw_response_wrapper( + threads.create_and_run, + ) + + +class ThreadsWithStreamingResponse: + def __init__(self, threads: Threads) -> None: + self.runs = RunsWithStreamingResponse(threads.runs) + self.messages = MessagesWithStreamingResponse(threads.messages) + + self.create = to_streamed_response_wrapper( + threads.create, + ) + self.retrieve = to_streamed_response_wrapper( + threads.retrieve, + ) + self.update = to_streamed_response_wrapper( + threads.update, + ) + self.delete = to_streamed_response_wrapper( + threads.delete, + ) + self.create_and_run = to_streamed_response_wrapper( + threads.create_and_run, + ) + + +class AsyncThreadsWithStreamingResponse: + def __init__(self, threads: AsyncThreads) -> None: + self.runs = AsyncRunsWithStreamingResponse(threads.runs) + self.messages = AsyncMessagesWithStreamingResponse(threads.messages) + + self.create = async_to_streamed_response_wrapper( threads.create, ) - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = async_to_streamed_response_wrapper( threads.retrieve, ) - self.update = async_to_raw_response_wrapper( + self.update = async_to_streamed_response_wrapper( threads.update, ) - self.delete = async_to_raw_response_wrapper( + self.delete = async_to_streamed_response_wrapper( threads.delete, ) - self.create_and_run = async_to_raw_response_wrapper( + self.create_and_run = async_to_streamed_response_wrapper( threads.create_and_run, ) diff --git a/src/openai/resources/chat/__init__.py b/src/openai/resources/chat/__init__.py index 85b246509e..a9668053c0 100644 --- a/src/openai/resources/chat/__init__.py +++ b/src/openai/resources/chat/__init__.py @@ -1,15 +1,33 @@ # File generated from our OpenAPI spec by Stainless. -from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse -from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse +from .chat import ( + Chat, + AsyncChat, + ChatWithRawResponse, + AsyncChatWithRawResponse, + ChatWithStreamingResponse, + AsyncChatWithStreamingResponse, +) +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, + CompletionsWithStreamingResponse, + AsyncCompletionsWithStreamingResponse, +) __all__ = [ "Completions", "AsyncCompletions", "CompletionsWithRawResponse", "AsyncCompletionsWithRawResponse", + "CompletionsWithStreamingResponse", + "AsyncCompletionsWithStreamingResponse", "Chat", "AsyncChat", "ChatWithRawResponse", "AsyncChatWithRawResponse", + "ChatWithStreamingResponse", + "AsyncChatWithStreamingResponse", ] diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index 000520de23..467a5e401b 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -4,7 +4,14 @@ from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -from .completions import Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, + CompletionsWithStreamingResponse, + AsyncCompletionsWithStreamingResponse, +) __all__ = ["Chat", "AsyncChat"] @@ -18,6 +25,10 @@ def completions(self) -> Completions: def with_raw_response(self) -> ChatWithRawResponse: return ChatWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> ChatWithStreamingResponse: + return ChatWithStreamingResponse(self) + class AsyncChat(AsyncAPIResource): @cached_property @@ -28,6 +39,10 @@ def completions(self) -> AsyncCompletions: def with_raw_response(self) -> AsyncChatWithRawResponse: return AsyncChatWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncChatWithStreamingResponse: + return AsyncChatWithStreamingResponse(self) + class ChatWithRawResponse: def __init__(self, chat: Chat) -> None: @@ -37,3 +52,13 @@ def __init__(self, chat: Chat) -> None: class AsyncChatWithRawResponse: def __init__(self, chat: AsyncChat) -> None: self.completions = AsyncCompletionsWithRawResponse(chat.completions) + + +class ChatWithStreamingResponse: + def __init__(self, chat: Chat) -> None: + self.completions = CompletionsWithStreamingResponse(chat.completions) + + +class AsyncChatWithStreamingResponse: + def __init__(self, chat: AsyncChat) -> None: + self.completions = AsyncCompletionsWithStreamingResponse(chat.completions) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index fa096784d2..53645a9eb9 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -7,11 +7,12 @@ import httpx +from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import required_args, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..._streaming import Stream, AsyncStream from ...types.chat import ( ChatCompletion, @@ -33,6 +34,10 @@ class Completions(SyncAPIResource): def with_raw_response(self) -> CompletionsWithRawResponse: return CompletionsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> CompletionsWithStreamingResponse: + return CompletionsWithStreamingResponse(self) + @overload def create( self, @@ -681,6 +686,10 @@ class AsyncCompletions(AsyncAPIResource): def with_raw_response(self) -> AsyncCompletionsWithRawResponse: return AsyncCompletionsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: + return AsyncCompletionsWithStreamingResponse(self) + @overload async def create( self, @@ -1326,13 +1335,27 @@ async def create( class CompletionsWithRawResponse: def __init__(self, completions: Completions) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( completions.create, ) class AsyncCompletionsWithRawResponse: def __init__(self, completions: AsyncCompletions) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + completions.create, + ) + + +class CompletionsWithStreamingResponse: + def __init__(self, completions: Completions) -> None: + self.create = to_streamed_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsWithStreamingResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self.create = async_to_streamed_response_wrapper( completions.create, ) diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 87dd090052..43a9947524 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -7,12 +7,13 @@ import httpx +from .. import _legacy_response from ..types import Completion, completion_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import required_args, maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .._streaming import Stream, AsyncStream from .._base_client import ( make_request_options, @@ -26,6 +27,10 @@ class Completions(SyncAPIResource): def with_raw_response(self) -> CompletionsWithRawResponse: return CompletionsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> CompletionsWithStreamingResponse: + return CompletionsWithStreamingResponse(self) + @overload def create( self, @@ -536,6 +541,10 @@ class AsyncCompletions(AsyncAPIResource): def with_raw_response(self) -> AsyncCompletionsWithRawResponse: return AsyncCompletionsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: + return AsyncCompletionsWithStreamingResponse(self) + @overload async def create( self, @@ -1043,13 +1052,27 @@ async def create( class CompletionsWithRawResponse: def __init__(self, completions: Completions) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( completions.create, ) class AsyncCompletionsWithRawResponse: def __init__(self, completions: AsyncCompletions) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + completions.create, + ) + + +class CompletionsWithStreamingResponse: + def __init__(self, completions: Completions) -> None: + self.create = to_streamed_response_wrapper( + completions.create, + ) + + +class AsyncCompletionsWithStreamingResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self.create = async_to_streamed_response_wrapper( completions.create, ) diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index e93b29d45b..49ce0f2fc8 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -8,13 +8,14 @@ import httpx +from .. import _legacy_response from ..types import CreateEmbeddingResponse, embedding_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import is_given, maybe_transform from .._compat import cached_property from .._extras import numpy as np, has_numpy from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .._base_client import ( make_request_options, ) @@ -27,6 +28,10 @@ class Embeddings(SyncAPIResource): def with_raw_response(self) -> EmbeddingsWithRawResponse: return EmbeddingsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> EmbeddingsWithStreamingResponse: + return EmbeddingsWithStreamingResponse(self) + def create( self, *, @@ -119,6 +124,10 @@ class AsyncEmbeddings(AsyncAPIResource): def with_raw_response(self) -> AsyncEmbeddingsWithRawResponse: return AsyncEmbeddingsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncEmbeddingsWithStreamingResponse: + return AsyncEmbeddingsWithStreamingResponse(self) + async def create( self, *, @@ -208,13 +217,27 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: class EmbeddingsWithRawResponse: def __init__(self, embeddings: Embeddings) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( embeddings.create, ) class AsyncEmbeddingsWithRawResponse: def __init__(self, embeddings: AsyncEmbeddings) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + embeddings.create, + ) + + +class EmbeddingsWithStreamingResponse: + def __init__(self, embeddings: Embeddings) -> None: + self.create = to_streamed_response_wrapper( + embeddings.create, + ) + + +class AsyncEmbeddingsWithStreamingResponse: + def __init__(self, embeddings: AsyncEmbeddings) -> None: + self.create = async_to_streamed_response_wrapper( embeddings.create, ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 1acf6f8060..f435e70a2f 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -9,16 +9,23 @@ import httpx +from .. import _legacy_response from ..types import FileObject, FileDeleted, file_list_params, file_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_streamed_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) from ..pagination import SyncPage, AsyncPage from .._base_client import ( AsyncPaginator, - HttpxBinaryResponseContent, make_request_options, ) @@ -30,6 +37,10 @@ class Files(SyncAPIResource): def with_raw_response(self) -> FilesWithRawResponse: return FilesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self) + def create( self, *, @@ -209,7 +220,7 @@ def content( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> HttpxBinaryResponseContent: + ) -> _legacy_response.HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -227,7 +238,7 @@ def content( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=HttpxBinaryResponseContent, + cast_to=_legacy_response.HttpxBinaryResponseContent, ) @typing_extensions.deprecated("The `.content()` method should be used instead") @@ -292,6 +303,10 @@ class AsyncFiles(AsyncAPIResource): def with_raw_response(self) -> AsyncFilesWithRawResponse: return AsyncFilesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self) + async def create( self, *, @@ -471,7 +486,7 @@ async def content( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> HttpxBinaryResponseContent: + ) -> _legacy_response.HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -489,7 +504,7 @@ async def content( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=HttpxBinaryResponseContent, + cast_to=_legacy_response.HttpxBinaryResponseContent, ) @typing_extensions.deprecated("The `.content()` method should be used instead") @@ -551,43 +566,97 @@ async def wait_for_processing( class FilesWithRawResponse: def __init__(self, files: Files) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( files.create, ) - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( files.retrieve, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( files.list, ) - self.delete = to_raw_response_wrapper( + self.delete = _legacy_response.to_raw_response_wrapper( files.delete, ) - self.content = to_raw_response_wrapper( + self.content = _legacy_response.to_raw_response_wrapper( files.content, ) - self.retrieve_content = to_raw_response_wrapper( # pyright: ignore[reportDeprecated] - files.retrieve_content # pyright: ignore[reportDeprecated], + self.retrieve_content = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + files.retrieve_content # pyright: ignore[reportDeprecated], + ) ) class AsyncFilesWithRawResponse: def __init__(self, files: AsyncFiles) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + files.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + files.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + files.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + files.delete, + ) + self.content = _legacy_response.async_to_raw_response_wrapper( + files.content, + ) + self.retrieve_content = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + files.retrieve_content # pyright: ignore[reportDeprecated], + ) + ) + + +class FilesWithStreamingResponse: + def __init__(self, files: Files) -> None: + self.create = to_streamed_response_wrapper( + files.create, + ) + self.retrieve = to_streamed_response_wrapper( + files.retrieve, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + self.delete = to_streamed_response_wrapper( + files.delete, + ) + self.content = to_custom_streamed_response_wrapper( + files.content, + StreamedBinaryAPIResponse, + ) + self.retrieve_content = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + files.retrieve_content # pyright: ignore[reportDeprecated], + ) + ) + + +class AsyncFilesWithStreamingResponse: + def __init__(self, files: AsyncFiles) -> None: + self.create = async_to_streamed_response_wrapper( files.create, ) - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = async_to_streamed_response_wrapper( files.retrieve, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( files.list, ) - self.delete = async_to_raw_response_wrapper( + self.delete = async_to_streamed_response_wrapper( files.delete, ) - self.content = async_to_raw_response_wrapper( + self.content = async_to_custom_streamed_response_wrapper( files.content, + AsyncStreamedBinaryAPIResponse, ) - self.retrieve_content = async_to_raw_response_wrapper( # pyright: ignore[reportDeprecated] - files.retrieve_content # pyright: ignore[reportDeprecated], + self.retrieve_content = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + files.retrieve_content # pyright: ignore[reportDeprecated], + ) ) diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py index 27445fb707..ab0c28ef4b 100644 --- a/src/openai/resources/fine_tuning/__init__.py +++ b/src/openai/resources/fine_tuning/__init__.py @@ -1,15 +1,33 @@ # File generated from our OpenAPI spec by Stainless. -from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse -from .fine_tuning import FineTuning, AsyncFineTuning, FineTuningWithRawResponse, AsyncFineTuningWithRawResponse +from .jobs import ( + Jobs, + AsyncJobs, + JobsWithRawResponse, + AsyncJobsWithRawResponse, + JobsWithStreamingResponse, + AsyncJobsWithStreamingResponse, +) +from .fine_tuning import ( + FineTuning, + AsyncFineTuning, + FineTuningWithRawResponse, + AsyncFineTuningWithRawResponse, + FineTuningWithStreamingResponse, + AsyncFineTuningWithStreamingResponse, +) __all__ = [ "Jobs", "AsyncJobs", "JobsWithRawResponse", "AsyncJobsWithRawResponse", + "JobsWithStreamingResponse", + "AsyncJobsWithStreamingResponse", "FineTuning", "AsyncFineTuning", "FineTuningWithRawResponse", "AsyncFineTuningWithRawResponse", + "FineTuningWithStreamingResponse", + "AsyncFineTuningWithStreamingResponse", ] diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index a5a68b08eb..197d46fb83 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -2,7 +2,14 @@ from __future__ import annotations -from .jobs import Jobs, AsyncJobs, JobsWithRawResponse, AsyncJobsWithRawResponse +from .jobs import ( + Jobs, + AsyncJobs, + JobsWithRawResponse, + AsyncJobsWithRawResponse, + JobsWithStreamingResponse, + AsyncJobsWithStreamingResponse, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -18,6 +25,10 @@ def jobs(self) -> Jobs: def with_raw_response(self) -> FineTuningWithRawResponse: return FineTuningWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> FineTuningWithStreamingResponse: + return FineTuningWithStreamingResponse(self) + class AsyncFineTuning(AsyncAPIResource): @cached_property @@ -28,6 +39,10 @@ def jobs(self) -> AsyncJobs: def with_raw_response(self) -> AsyncFineTuningWithRawResponse: return AsyncFineTuningWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncFineTuningWithStreamingResponse: + return AsyncFineTuningWithStreamingResponse(self) + class FineTuningWithRawResponse: def __init__(self, fine_tuning: FineTuning) -> None: @@ -37,3 +52,13 @@ def __init__(self, fine_tuning: FineTuning) -> None: class AsyncFineTuningWithRawResponse: def __init__(self, fine_tuning: AsyncFineTuning) -> None: self.jobs = AsyncJobsWithRawResponse(fine_tuning.jobs) + + +class FineTuningWithStreamingResponse: + def __init__(self, fine_tuning: FineTuning) -> None: + self.jobs = JobsWithStreamingResponse(fine_tuning.jobs) + + +class AsyncFineTuningWithStreamingResponse: + def __init__(self, fine_tuning: AsyncFineTuning) -> None: + self.jobs = AsyncJobsWithStreamingResponse(fine_tuning.jobs) diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 7537b48daa..f337b136a6 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -7,11 +7,12 @@ import httpx +from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage from ..._base_client import ( AsyncPaginator, @@ -33,6 +34,10 @@ class Jobs(SyncAPIResource): def with_raw_response(self) -> JobsWithRawResponse: return JobsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> JobsWithStreamingResponse: + return JobsWithStreamingResponse(self) + def create( self, *, @@ -284,6 +289,10 @@ class AsyncJobs(AsyncAPIResource): def with_raw_response(self) -> AsyncJobsWithRawResponse: return AsyncJobsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncJobsWithStreamingResponse: + return AsyncJobsWithStreamingResponse(self) + async def create( self, *, @@ -532,37 +541,75 @@ def list_events( class JobsWithRawResponse: def __init__(self, jobs: Jobs) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( jobs.create, ) - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( jobs.retrieve, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( jobs.list, ) - self.cancel = to_raw_response_wrapper( + self.cancel = _legacy_response.to_raw_response_wrapper( jobs.cancel, ) - self.list_events = to_raw_response_wrapper( + self.list_events = _legacy_response.to_raw_response_wrapper( jobs.list_events, ) class AsyncJobsWithRawResponse: def __init__(self, jobs: AsyncJobs) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + jobs.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + jobs.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + jobs.list, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + jobs.cancel, + ) + self.list_events = _legacy_response.async_to_raw_response_wrapper( + jobs.list_events, + ) + + +class JobsWithStreamingResponse: + def __init__(self, jobs: Jobs) -> None: + self.create = to_streamed_response_wrapper( + jobs.create, + ) + self.retrieve = to_streamed_response_wrapper( + jobs.retrieve, + ) + self.list = to_streamed_response_wrapper( + jobs.list, + ) + self.cancel = to_streamed_response_wrapper( + jobs.cancel, + ) + self.list_events = to_streamed_response_wrapper( + jobs.list_events, + ) + + +class AsyncJobsWithStreamingResponse: + def __init__(self, jobs: AsyncJobs) -> None: + self.create = async_to_streamed_response_wrapper( jobs.create, ) - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = async_to_streamed_response_wrapper( jobs.retrieve, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( jobs.list, ) - self.cancel = async_to_raw_response_wrapper( + self.cancel = async_to_streamed_response_wrapper( jobs.cancel, ) - self.list_events = async_to_raw_response_wrapper( + self.list_events = async_to_streamed_response_wrapper( jobs.list_events, ) diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 8e9c288af7..6f1de221e2 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -7,6 +7,7 @@ import httpx +from .. import _legacy_response from ..types import ( ImagesResponse, image_edit_params, @@ -17,7 +18,7 @@ from .._utils import extract_files, maybe_transform, deepcopy_minimal from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .._base_client import ( make_request_options, ) @@ -30,6 +31,10 @@ class Images(SyncAPIResource): def with_raw_response(self) -> ImagesWithRawResponse: return ImagesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> ImagesWithStreamingResponse: + return ImagesWithStreamingResponse(self) + def create_variation( self, *, @@ -273,6 +278,10 @@ class AsyncImages(AsyncAPIResource): def with_raw_response(self) -> AsyncImagesWithRawResponse: return AsyncImagesWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncImagesWithStreamingResponse: + return AsyncImagesWithStreamingResponse(self) + async def create_variation( self, *, @@ -513,25 +522,51 @@ async def generate( class ImagesWithRawResponse: def __init__(self, images: Images) -> None: - self.create_variation = to_raw_response_wrapper( + self.create_variation = _legacy_response.to_raw_response_wrapper( images.create_variation, ) - self.edit = to_raw_response_wrapper( + self.edit = _legacy_response.to_raw_response_wrapper( images.edit, ) - self.generate = to_raw_response_wrapper( + self.generate = _legacy_response.to_raw_response_wrapper( images.generate, ) class AsyncImagesWithRawResponse: def __init__(self, images: AsyncImages) -> None: - self.create_variation = async_to_raw_response_wrapper( + self.create_variation = _legacy_response.async_to_raw_response_wrapper( + images.create_variation, + ) + self.edit = _legacy_response.async_to_raw_response_wrapper( + images.edit, + ) + self.generate = _legacy_response.async_to_raw_response_wrapper( + images.generate, + ) + + +class ImagesWithStreamingResponse: + def __init__(self, images: Images) -> None: + self.create_variation = to_streamed_response_wrapper( + images.create_variation, + ) + self.edit = to_streamed_response_wrapper( + images.edit, + ) + self.generate = to_streamed_response_wrapper( + images.generate, + ) + + +class AsyncImagesWithStreamingResponse: + def __init__(self, images: AsyncImages) -> None: + self.create_variation = async_to_streamed_response_wrapper( images.create_variation, ) - self.edit = async_to_raw_response_wrapper( + self.edit = async_to_streamed_response_wrapper( images.edit, ) - self.generate = async_to_raw_response_wrapper( + self.generate = async_to_streamed_response_wrapper( images.generate, ) diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index 48888d98b5..b431ef84fc 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -4,11 +4,12 @@ import httpx +from .. import _legacy_response from ..types import Model, ModelDeleted from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncPage, AsyncPage from .._base_client import ( AsyncPaginator, @@ -23,6 +24,10 @@ class Models(SyncAPIResource): def with_raw_response(self) -> ModelsWithRawResponse: return ModelsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> ModelsWithStreamingResponse: + return ModelsWithStreamingResponse(self) + def retrieve( self, model: str, @@ -117,6 +122,10 @@ class AsyncModels(AsyncAPIResource): def with_raw_response(self) -> AsyncModelsWithRawResponse: return AsyncModelsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncModelsWithStreamingResponse: + return AsyncModelsWithStreamingResponse(self) + async def retrieve( self, model: str, @@ -208,25 +217,51 @@ async def delete( class ModelsWithRawResponse: def __init__(self, models: Models) -> None: - self.retrieve = to_raw_response_wrapper( + self.retrieve = _legacy_response.to_raw_response_wrapper( models.retrieve, ) - self.list = to_raw_response_wrapper( + self.list = _legacy_response.to_raw_response_wrapper( models.list, ) - self.delete = to_raw_response_wrapper( + self.delete = _legacy_response.to_raw_response_wrapper( models.delete, ) class AsyncModelsWithRawResponse: def __init__(self, models: AsyncModels) -> None: - self.retrieve = async_to_raw_response_wrapper( + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + models.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + models.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + models.delete, + ) + + +class ModelsWithStreamingResponse: + def __init__(self, models: Models) -> None: + self.retrieve = to_streamed_response_wrapper( + models.retrieve, + ) + self.list = to_streamed_response_wrapper( + models.list, + ) + self.delete = to_streamed_response_wrapper( + models.delete, + ) + + +class AsyncModelsWithStreamingResponse: + def __init__(self, models: AsyncModels) -> None: + self.retrieve = async_to_streamed_response_wrapper( models.retrieve, ) - self.list = async_to_raw_response_wrapper( + self.list = async_to_streamed_response_wrapper( models.list, ) - self.delete = async_to_raw_response_wrapper( + self.delete = async_to_streamed_response_wrapper( models.delete, ) diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 120a499186..e7681f6263 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -7,12 +7,13 @@ import httpx +from .. import _legacy_response from ..types import ModerationCreateResponse, moderation_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .._base_client import ( make_request_options, ) @@ -25,6 +26,10 @@ class Moderations(SyncAPIResource): def with_raw_response(self) -> ModerationsWithRawResponse: return ModerationsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> ModerationsWithStreamingResponse: + return ModerationsWithStreamingResponse(self) + def create( self, *, @@ -81,6 +86,10 @@ class AsyncModerations(AsyncAPIResource): def with_raw_response(self) -> AsyncModerationsWithRawResponse: return AsyncModerationsWithRawResponse(self) + @cached_property + def with_streaming_response(self) -> AsyncModerationsWithStreamingResponse: + return AsyncModerationsWithStreamingResponse(self) + async def create( self, *, @@ -134,13 +143,27 @@ async def create( class ModerationsWithRawResponse: def __init__(self, moderations: Moderations) -> None: - self.create = to_raw_response_wrapper( + self.create = _legacy_response.to_raw_response_wrapper( moderations.create, ) class AsyncModerationsWithRawResponse: def __init__(self, moderations: AsyncModerations) -> None: - self.create = async_to_raw_response_wrapper( + self.create = _legacy_response.async_to_raw_response_wrapper( + moderations.create, + ) + + +class ModerationsWithStreamingResponse: + def __init__(self, moderations: Moderations) -> None: + self.create = to_streamed_response_wrapper( + moderations.create, + ) + + +class AsyncModerationsWithStreamingResponse: + def __init__(self, moderations: AsyncModerations) -> None: + self.create = async_to_streamed_response_wrapper( moderations.create, ) diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 23f5303153..a689c0d220 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -3,15 +3,19 @@ from __future__ import annotations import os +from typing import Any, cast import httpx import pytest from respx import MockRouter +import openai._legacy_response as _legacy_response from openai import OpenAI, AsyncOpenAI -from openai._types import BinaryResponseContent +from tests.utils import assert_matches_type from openai._client import OpenAI, AsyncOpenAI +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") api_key = "My API Key" @@ -21,7 +25,6 @@ class TestSpeech: loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) - @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -31,10 +34,9 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: model="string", voice="alloy", ) - assert isinstance(speech, BinaryResponseContent) + assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} - @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -46,23 +48,41 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou response_format="mp3", speed=0.25, ) - assert isinstance(speech, BinaryResponseContent) + assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} - @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = client.audio.speech.with_raw_response.create( input="string", model="string", voice="alloy", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" speech = response.parse() - assert isinstance(speech, BinaryResponseContent) - assert speech.json() == {"foo": "bar"} + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, speech, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + with client.audio.speech.with_streaming_response.create( + input="string", + model="string", + voice="alloy", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + speech = response.parse() + assert_matches_type(bytes, speech, path=["response"]) + + assert cast(Any, response.is_closed) is True class TestAsyncSpeech: @@ -70,7 +90,6 @@ class TestAsyncSpeech: loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) - @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: @@ -80,10 +99,9 @@ async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) model="string", voice="alloy", ) - assert isinstance(speech, BinaryResponseContent) + assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} - @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: @@ -95,20 +113,38 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mo response_format="mp3", speed=0.25, ) - assert isinstance(speech, BinaryResponseContent) + assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} - @pytest.mark.skip(reason="Mocked tests are currently broken") @parametrize @pytest.mark.respx(base_url=base_url) async def test_raw_response_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = await client.audio.speech.with_raw_response.create( input="string", model="string", voice="alloy", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" speech = response.parse() - assert isinstance(speech, BinaryResponseContent) - assert speech.json() == {"foo": "bar"} + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, speech, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + async with client.audio.speech.with_streaming_response.create( + input="string", + model="string", + voice="alloy", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + speech = await response.parse() + assert_matches_type(bytes, speech, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index aefdf1790f..992adbabd9 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -46,10 +47,26 @@ def test_raw_response_create(self, client: OpenAI) -> None: file=b"raw file contents", model="whisper-1", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = response.parse() assert_matches_type(Transcription, transcription, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.audio.transcriptions.with_streaming_response.create( + file=b"raw file contents", + model="whisper-1", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + transcription = response.parse() + assert_matches_type(Transcription, transcription, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncTranscriptions: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -82,6 +99,22 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: file=b"raw file contents", model="whisper-1", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = response.parse() assert_matches_type(Transcription, transcription, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.audio.transcriptions.with_streaming_response.create( + file=b"raw file contents", + model="whisper-1", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + transcription = await response.parse() + assert_matches_type(Transcription, transcription, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index 0657e80eb8..913c443a79 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -45,10 +46,26 @@ def test_raw_response_create(self, client: OpenAI) -> None: file=b"raw file contents", model="whisper-1", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" translation = response.parse() assert_matches_type(Translation, translation, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.audio.translations.with_streaming_response.create( + file=b"raw file contents", + model="whisper-1", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + translation = response.parse() + assert_matches_type(Translation, translation, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncTranslations: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -80,6 +97,22 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: file=b"raw file contents", model="whisper-1", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" translation = response.parse() assert_matches_type(Translation, translation, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.audio.translations.with_streaming_response.create( + file=b"raw file contents", + model="whisper-1", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + translation = await response.parse() + assert_matches_type(Translation, translation, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/assistants/test_files.py b/tests/api_resources/beta/assistants/test_files.py index 27c12e4475..443408bd44 100644 --- a/tests/api_resources/beta/assistants/test_files.py +++ b/tests/api_resources/beta/assistants/test_files.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -35,10 +36,26 @@ def test_raw_response_create(self, client: OpenAI) -> None: "file-abc123", file_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(AssistantFile, file, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.assistants.files.with_streaming_response.create( + "file-abc123", + file_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(AssistantFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: file = client.beta.assistants.files.retrieve( @@ -53,10 +70,26 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: "string", assistant_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(AssistantFile, file, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.assistants.files.with_streaming_response.retrieve( + "string", + assistant_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(AssistantFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: file = client.beta.assistants.files.list( @@ -80,10 +113,25 @@ def test_raw_response_list(self, client: OpenAI) -> None: response = client.beta.assistants.files.with_raw_response.list( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.assistants.files.with_streaming_response.list( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_delete(self, client: OpenAI) -> None: file = client.beta.assistants.files.delete( @@ -98,10 +146,26 @@ def test_raw_response_delete(self, client: OpenAI) -> None: "string", assistant_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileDeleteResponse, file, path=["response"]) + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.beta.assistants.files.with_streaming_response.delete( + "string", + assistant_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncFiles: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -122,10 +186,26 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: "file-abc123", file_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(AssistantFile, file, path=["response"]) + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.files.with_streaming_response.create( + "file-abc123", + file_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(AssistantFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: file = await client.beta.assistants.files.retrieve( @@ -140,10 +220,26 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: "string", assistant_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(AssistantFile, file, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.files.with_streaming_response.retrieve( + "string", + assistant_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(AssistantFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: file = await client.beta.assistants.files.list( @@ -167,10 +263,25 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: response = await client.beta.assistants.files.with_raw_response.list( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.files.with_streaming_response.list( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_delete(self, client: AsyncOpenAI) -> None: file = await client.beta.assistants.files.delete( @@ -185,6 +296,22 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: "string", assistant_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileDeleteResponse, file, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.files.with_streaming_response.delete( + "string", + assistant_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 97e74c61e4..fbafac03c9 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -49,10 +50,25 @@ def test_raw_response_create(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.create( model="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(Assistant, assistant, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.assistants.with_streaming_response.create( + model="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: assistant = client.beta.assistants.retrieve( @@ -65,10 +81,25 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.retrieve( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(Assistant, assistant, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.assistants.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_update(self, client: OpenAI) -> None: assistant = client.beta.assistants.update( @@ -95,10 +126,25 @@ def test_raw_response_update(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.update( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(Assistant, assistant, path=["response"]) + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.beta.assistants.with_streaming_response.update( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: assistant = client.beta.assistants.list() @@ -117,10 +163,23 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_list(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.list() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.assistants.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = response.parse() + assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_delete(self, client: OpenAI) -> None: assistant = client.beta.assistants.delete( @@ -133,10 +192,25 @@ def test_raw_response_delete(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.delete( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(AssistantDeleted, assistant, path=["response"]) + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.beta.assistants.with_streaming_response.delete( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = response.parse() + assert_matches_type(AssistantDeleted, assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncAssistants: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -168,10 +242,25 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: response = await client.beta.assistants.with_raw_response.create( model="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(Assistant, assistant, path=["response"]) + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.with_streaming_response.create( + model="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = await response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: assistant = await client.beta.assistants.retrieve( @@ -184,10 +273,25 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: response = await client.beta.assistants.with_raw_response.retrieve( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(Assistant, assistant, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = await response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_update(self, client: AsyncOpenAI) -> None: assistant = await client.beta.assistants.update( @@ -214,10 +318,25 @@ async def test_raw_response_update(self, client: AsyncOpenAI) -> None: response = await client.beta.assistants.with_raw_response.update( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(Assistant, assistant, path=["response"]) + @parametrize + async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.with_streaming_response.update( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = await response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: assistant = await client.beta.assistants.list() @@ -236,10 +355,23 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: @parametrize async def test_raw_response_list(self, client: AsyncOpenAI) -> None: response = await client.beta.assistants.with_raw_response.list() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = await response.parse() + assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_delete(self, client: AsyncOpenAI) -> None: assistant = await client.beta.assistants.delete( @@ -252,6 +384,21 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: response = await client.beta.assistants.with_raw_response.delete( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" assistant = response.parse() assert_matches_type(AssistantDeleted, assistant, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: + async with client.beta.assistants.with_streaming_response.delete( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + assistant = await response.parse() + assert_matches_type(AssistantDeleted, assistant, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 860159ffb3..488ce38c1b 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -59,10 +60,23 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.beta.threads.with_raw_response.create() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(Thread, thread, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.threads.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: thread = client.beta.threads.retrieve( @@ -75,10 +89,25 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.beta.threads.with_raw_response.retrieve( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(Thread, thread, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.threads.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_update(self, client: OpenAI) -> None: thread = client.beta.threads.update( @@ -99,10 +128,25 @@ def test_raw_response_update(self, client: OpenAI) -> None: response = client.beta.threads.with_raw_response.update( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(Thread, thread, path=["response"]) + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.beta.threads.with_streaming_response.update( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_delete(self, client: OpenAI) -> None: thread = client.beta.threads.delete( @@ -115,10 +159,25 @@ def test_raw_response_delete(self, client: OpenAI) -> None: response = client.beta.threads.with_raw_response.delete( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(ThreadDeleted, thread, path=["response"]) + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.beta.threads.with_streaming_response.delete( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(ThreadDeleted, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_create_and_run(self, client: OpenAI) -> None: thread = client.beta.threads.create_and_run( @@ -165,10 +224,25 @@ def test_raw_response_create_and_run(self, client: OpenAI) -> None: response = client.beta.threads.with_raw_response.create_and_run( assistant_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(Run, thread, path=["response"]) + @parametrize + def test_streaming_response_create_and_run(self, client: OpenAI) -> None: + with client.beta.threads.with_streaming_response.create_and_run( + assistant_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(Run, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncThreads: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -210,10 +284,23 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: @parametrize async def test_raw_response_create(self, client: AsyncOpenAI) -> None: response = await client.beta.threads.with_raw_response.create() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(Thread, thread, path=["response"]) + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: thread = await client.beta.threads.retrieve( @@ -226,10 +313,25 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: response = await client.beta.threads.with_raw_response.retrieve( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(Thread, thread, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_update(self, client: AsyncOpenAI) -> None: thread = await client.beta.threads.update( @@ -250,10 +352,25 @@ async def test_raw_response_update(self, client: AsyncOpenAI) -> None: response = await client.beta.threads.with_raw_response.update( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(Thread, thread, path=["response"]) + @parametrize + async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.with_streaming_response.update( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(Thread, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_delete(self, client: AsyncOpenAI) -> None: thread = await client.beta.threads.delete( @@ -266,10 +383,25 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: response = await client.beta.threads.with_raw_response.delete( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(ThreadDeleted, thread, path=["response"]) + @parametrize + async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.with_streaming_response.delete( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(ThreadDeleted, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_create_and_run(self, client: AsyncOpenAI) -> None: thread = await client.beta.threads.create_and_run( @@ -316,6 +448,21 @@ async def test_raw_response_create_and_run(self, client: AsyncOpenAI) -> None: response = await client.beta.threads.with_raw_response.create_and_run( assistant_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(Run, thread, path=["response"]) + + @parametrize + async def test_streaming_response_create_and_run(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.with_streaming_response.create_and_run( + assistant_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(Run, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/threads/messages/test_files.py b/tests/api_resources/beta/threads/messages/test_files.py index b97e4debd5..5de352c0d2 100644 --- a/tests/api_resources/beta/threads/messages/test_files.py +++ b/tests/api_resources/beta/threads/messages/test_files.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -37,10 +38,27 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: thread_id="thread_abc123", message_id="msg_abc123", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(MessageFile, file, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.threads.messages.files.with_streaming_response.retrieve( + "file-abc123", + thread_id="thread_abc123", + message_id="msg_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(MessageFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: file = client.beta.threads.messages.files.list( @@ -67,10 +85,26 @@ def test_raw_response_list(self, client: OpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.threads.messages.files.with_streaming_response.list( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncFiles: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -93,10 +127,27 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: thread_id="thread_abc123", message_id="msg_abc123", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(MessageFile, file, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.messages.files.with_streaming_response.retrieve( + "file-abc123", + thread_id="thread_abc123", + message_id="msg_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(MessageFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: file = await client.beta.threads.messages.files.list( @@ -123,6 +174,22 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.messages.files.with_streaming_response.list( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py index 3f4f8c1022..f13970fc14 100644 --- a/tests/api_resources/beta/threads/runs/test_steps.py +++ b/tests/api_resources/beta/threads/runs/test_steps.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -37,10 +38,27 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: thread_id="string", run_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" step = response.parse() assert_matches_type(RunStep, step, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.threads.runs.steps.with_streaming_response.retrieve( + "string", + thread_id="string", + run_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + step = response.parse() + assert_matches_type(RunStep, step, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: step = client.beta.threads.runs.steps.list( @@ -67,10 +85,26 @@ def test_raw_response_list(self, client: OpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" step = response.parse() assert_matches_type(SyncCursorPage[RunStep], step, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.threads.runs.steps.with_streaming_response.list( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + step = response.parse() + assert_matches_type(SyncCursorPage[RunStep], step, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncSteps: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -93,10 +127,27 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: thread_id="string", run_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" step = response.parse() assert_matches_type(RunStep, step, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.runs.steps.with_streaming_response.retrieve( + "string", + thread_id="string", + run_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + step = await response.parse() + assert_matches_type(RunStep, step, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: step = await client.beta.threads.runs.steps.list( @@ -123,6 +174,22 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" step = response.parse() assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.runs.steps.with_streaming_response.list( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + step = await response.parse() + assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index f3fe7dc2bb..87b6eca03a 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -48,10 +49,27 @@ def test_raw_response_create(self, client: OpenAI) -> None: content="x", role="user", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(ThreadMessage, message, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.threads.messages.with_streaming_response.create( + "string", + content="x", + role="user", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: message = client.beta.threads.messages.retrieve( @@ -66,10 +84,26 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(ThreadMessage, message, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.threads.messages.with_streaming_response.retrieve( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_update(self, client: OpenAI) -> None: message = client.beta.threads.messages.update( @@ -93,10 +127,26 @@ def test_raw_response_update(self, client: OpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(ThreadMessage, message, path=["response"]) + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.beta.threads.messages.with_streaming_response.update( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: message = client.beta.threads.messages.list( @@ -120,10 +170,25 @@ def test_raw_response_list(self, client: OpenAI) -> None: response = client.beta.threads.messages.with_raw_response.list( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.threads.messages.with_streaming_response.list( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = response.parse() + assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncMessages: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -157,10 +222,27 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: content="x", role="user", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(ThreadMessage, message, path=["response"]) + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.messages.with_streaming_response.create( + "string", + content="x", + role="user", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = await response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: message = await client.beta.threads.messages.retrieve( @@ -175,10 +257,26 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(ThreadMessage, message, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.messages.with_streaming_response.retrieve( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = await response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_update(self, client: AsyncOpenAI) -> None: message = await client.beta.threads.messages.update( @@ -202,10 +300,26 @@ async def test_raw_response_update(self, client: AsyncOpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(ThreadMessage, message, path=["response"]) + @parametrize + async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.messages.with_streaming_response.update( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = await response.parse() + assert_matches_type(ThreadMessage, message, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: message = await client.beta.threads.messages.list( @@ -229,6 +343,21 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: response = await client.beta.threads.messages.with_raw_response.list( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.messages.with_streaming_response.list( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = await response.parse() + assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 9d04a95c80..e0070c3395 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -50,10 +51,26 @@ def test_raw_response_create(self, client: OpenAI) -> None: "string", assistant_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.threads.runs.with_streaming_response.create( + "string", + assistant_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: run = client.beta.threads.runs.retrieve( @@ -68,10 +85,26 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.threads.runs.with_streaming_response.retrieve( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_update(self, client: OpenAI) -> None: run = client.beta.threads.runs.update( @@ -95,10 +128,26 @@ def test_raw_response_update(self, client: OpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.beta.threads.runs.with_streaming_response.update( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: run = client.beta.threads.runs.list( @@ -122,10 +171,25 @@ def test_raw_response_list(self, client: OpenAI) -> None: response = client.beta.threads.runs.with_raw_response.list( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(SyncCursorPage[Run], run, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.threads.runs.with_streaming_response.list( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(SyncCursorPage[Run], run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_cancel(self, client: OpenAI) -> None: run = client.beta.threads.runs.cancel( @@ -140,10 +204,26 @@ def test_raw_response_cancel(self, client: OpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + def test_streaming_response_cancel(self, client: OpenAI) -> None: + with client.beta.threads.runs.with_streaming_response.cancel( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_submit_tool_outputs(self, client: OpenAI) -> None: run = client.beta.threads.runs.submit_tool_outputs( @@ -160,10 +240,27 @@ def test_raw_response_submit_tool_outputs(self, client: OpenAI) -> None: thread_id="string", tool_outputs=[{}, {}, {}], ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + def test_streaming_response_submit_tool_outputs(self, client: OpenAI) -> None: + with client.beta.threads.runs.with_streaming_response.submit_tool_outputs( + "string", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncRuns: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -197,10 +294,26 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: "string", assistant_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.runs.with_streaming_response.create( + "string", + assistant_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.retrieve( @@ -215,10 +328,26 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.runs.with_streaming_response.retrieve( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_update(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.update( @@ -242,10 +371,26 @@ async def test_raw_response_update(self, client: AsyncOpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.runs.with_streaming_response.update( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.list( @@ -269,10 +414,25 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: response = await client.beta.threads.runs.with_raw_response.list( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(AsyncCursorPage[Run], run, path=["response"]) + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.runs.with_streaming_response.list( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(AsyncCursorPage[Run], run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_cancel(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.cancel( @@ -287,10 +447,26 @@ async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: "string", thread_id="string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + @parametrize + async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.runs.with_streaming_response.cancel( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_submit_tool_outputs(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.submit_tool_outputs( @@ -307,6 +483,23 @@ async def test_raw_response_submit_tool_outputs(self, client: AsyncOpenAI) -> No thread_id="string", tool_outputs=[{}, {}, {}], ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" run = response.parse() assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_streaming_response_submit_tool_outputs(self, client: AsyncOpenAI) -> None: + async with client.beta.threads.runs.with_streaming_response.submit_tool_outputs( + "string", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(Run, run, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 985d5f1c04..860ec80f48 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -107,13 +108,34 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: ], model="gpt-3.5-turbo", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(ChatCompletion, completion, path=["response"]) + @parametrize + def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: + with client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_create_overload_2(self, client: OpenAI) -> None: - client.chat.completions.create( + completion_stream = client.chat.completions.create( messages=[ { "content": "string", @@ -123,10 +145,11 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: model="gpt-3.5-turbo", stream=True, ) + completion_stream.response.close() @parametrize def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: - client.chat.completions.create( + completion_stream = client.chat.completions.create( messages=[ { "content": "string", @@ -185,6 +208,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: top_p=1, user="user-1234", ) + completion_stream.response.close() @parametrize def test_raw_response_create_overload_2(self, client: OpenAI) -> None: @@ -198,8 +222,30 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: model="gpt-3.5-turbo", stream=True, ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - response.parse() + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: + with client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True class TestAsyncCompletions: @@ -294,13 +340,34 @@ async def test_raw_response_create_overload_1(self, client: AsyncOpenAI) -> None ], model="gpt-3.5-turbo", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(ChatCompletion, completion, path=["response"]) + @parametrize + async def test_streaming_response_create_overload_1(self, client: AsyncOpenAI) -> None: + async with client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = await response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: - await client.chat.completions.create( + completion_stream = await client.chat.completions.create( messages=[ { "content": "string", @@ -310,10 +377,11 @@ async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: model="gpt-3.5-turbo", stream=True, ) + await completion_stream.response.aclose() @parametrize async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenAI) -> None: - await client.chat.completions.create( + completion_stream = await client.chat.completions.create( messages=[ { "content": "string", @@ -372,6 +440,7 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA top_p=1, user="user-1234", ) + await completion_stream.response.aclose() @parametrize async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None: @@ -385,5 +454,27 @@ async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None model="gpt-3.5-turbo", stream=True, ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - response.parse() + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_create_overload_2(self, client: AsyncOpenAI) -> None: + async with client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-3.5-turbo", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 927ca9bbdd..3db0cdc0a5 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -53,10 +54,26 @@ def test_raw_response_create(self, client: OpenAI) -> None: model="gpt-3.5-turbo", training_file="file-abc123", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(FineTuningJob, job, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.fine_tuning.jobs.with_streaming_response.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.retrieve( @@ -69,10 +86,25 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.fine_tuning.jobs.with_raw_response.retrieve( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(FineTuningJob, job, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.fine_tuning.jobs.with_streaming_response.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.list() @@ -89,10 +121,23 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_list(self, client: OpenAI) -> None: response = client.fine_tuning.jobs.with_raw_response.list() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(SyncCursorPage[FineTuningJob], job, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.fine_tuning.jobs.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = response.parse() + assert_matches_type(SyncCursorPage[FineTuningJob], job, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_cancel(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.cancel( @@ -105,10 +150,25 @@ def test_raw_response_cancel(self, client: OpenAI) -> None: response = client.fine_tuning.jobs.with_raw_response.cancel( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(FineTuningJob, job, path=["response"]) + @parametrize + def test_streaming_response_cancel(self, client: OpenAI) -> None: + with client.fine_tuning.jobs.with_streaming_response.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list_events(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.list_events( @@ -130,10 +190,25 @@ def test_raw_response_list_events(self, client: OpenAI) -> None: response = client.fine_tuning.jobs.with_raw_response.list_events( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"]) + @parametrize + def test_streaming_response_list_events(self, client: OpenAI) -> None: + with client.fine_tuning.jobs.with_streaming_response.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = response.parse() + assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncJobs: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -169,10 +244,26 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: model="gpt-3.5-turbo", training_file="file-abc123", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(FineTuningJob, job, path=["response"]) + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.fine_tuning.jobs.with_streaming_response.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = await response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: job = await client.fine_tuning.jobs.retrieve( @@ -185,10 +276,25 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: response = await client.fine_tuning.jobs.with_raw_response.retrieve( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(FineTuningJob, job, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.fine_tuning.jobs.with_streaming_response.retrieve( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = await response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: job = await client.fine_tuning.jobs.list() @@ -205,10 +311,23 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: @parametrize async def test_raw_response_list(self, client: AsyncOpenAI) -> None: response = await client.fine_tuning.jobs.with_raw_response.list() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.fine_tuning.jobs.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = await response.parse() + assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_cancel(self, client: AsyncOpenAI) -> None: job = await client.fine_tuning.jobs.cancel( @@ -221,10 +340,25 @@ async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: response = await client.fine_tuning.jobs.with_raw_response.cancel( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(FineTuningJob, job, path=["response"]) + @parametrize + async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None: + async with client.fine_tuning.jobs.with_streaming_response.cancel( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = await response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list_events(self, client: AsyncOpenAI) -> None: job = await client.fine_tuning.jobs.list_events( @@ -246,6 +380,21 @@ async def test_raw_response_list_events(self, client: AsyncOpenAI) -> None: response = await client.fine_tuning.jobs.with_raw_response.list_events( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" job = response.parse() assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) + + @parametrize + async def test_streaming_response_list_events(self, client: AsyncOpenAI) -> None: + async with client.fine_tuning.jobs.with_streaming_response.list_events( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = await response.parse() + assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index b12fd6401e..a5e8dc809a 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -57,21 +58,38 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: model="string", prompt="This is a test.", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(Completion, completion, path=["response"]) + @parametrize + def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: + with client.completions.with_streaming_response.create( + model="string", + prompt="This is a test.", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = response.parse() + assert_matches_type(Completion, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_create_overload_2(self, client: OpenAI) -> None: - client.completions.create( + completion_stream = client.completions.create( model="string", prompt="This is a test.", stream=True, ) + completion_stream.response.close() @parametrize def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: - client.completions.create( + completion_stream = client.completions.create( model="string", prompt="This is a test.", stream=True, @@ -90,6 +108,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: top_p=1, user="user-1234", ) + completion_stream.response.close() @parametrize def test_raw_response_create_overload_2(self, client: OpenAI) -> None: @@ -98,8 +117,25 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: prompt="This is a test.", stream=True, ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - response.parse() + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: + with client.completions.with_streaming_response.create( + model="string", + prompt="This is a test.", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True class TestAsyncCompletions: @@ -144,21 +180,38 @@ async def test_raw_response_create_overload_1(self, client: AsyncOpenAI) -> None model="string", prompt="This is a test.", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(Completion, completion, path=["response"]) + @parametrize + async def test_streaming_response_create_overload_1(self, client: AsyncOpenAI) -> None: + async with client.completions.with_streaming_response.create( + model="string", + prompt="This is a test.", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = await response.parse() + assert_matches_type(Completion, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: - await client.completions.create( + completion_stream = await client.completions.create( model="string", prompt="This is a test.", stream=True, ) + await completion_stream.response.aclose() @parametrize async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenAI) -> None: - await client.completions.create( + completion_stream = await client.completions.create( model="string", prompt="This is a test.", stream=True, @@ -177,6 +230,7 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA top_p=1, user="user-1234", ) + await completion_stream.response.aclose() @parametrize async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None: @@ -185,5 +239,22 @@ async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None prompt="This is a test.", stream=True, ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - response.parse() + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_create_overload_2(self, client: AsyncOpenAI) -> None: + async with client.completions.with_streaming_response.create( + model="string", + prompt="This is a test.", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py index faf07ffb7c..77875fc46f 100644 --- a/tests/api_resources/test_embeddings.py +++ b/tests/api_resources/test_embeddings.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -44,10 +45,26 @@ def test_raw_response_create(self, client: OpenAI) -> None: input="The quick brown fox jumped over the lazy dog", model="text-embedding-ada-002", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" embedding = response.parse() assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.embeddings.with_streaming_response.create( + input="The quick brown fox jumped over the lazy dog", + model="text-embedding-ada-002", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + embedding = response.parse() + assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncEmbeddings: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -78,6 +95,22 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: input="The quick brown fox jumped over the lazy dog", model="text-embedding-ada-002", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" embedding = response.parse() assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.embeddings.with_streaming_response.create( + input="The quick brown fox jumped over the lazy dog", + model="text-embedding-ada-002", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + embedding = await response.parse() + assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 13ffca9773..e36a7839f2 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -3,15 +3,16 @@ from __future__ import annotations import os +from typing import Any, cast import httpx import pytest from respx import MockRouter +import openai._legacy_response as _legacy_response from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import FileObject, FileDeleted -from openai._types import BinaryResponseContent from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncPage, AsyncPage @@ -40,10 +41,26 @@ def test_raw_response_create(self, client: OpenAI) -> None: file=b"raw file contents", purpose="fine-tune", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileObject, file, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.files.with_streaming_response.create( + file=b"raw file contents", + purpose="fine-tune", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileObject, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: file = client.files.retrieve( @@ -56,10 +73,25 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.files.with_raw_response.retrieve( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileObject, file, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.files.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileObject, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: file = client.files.list() @@ -75,10 +107,23 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_list(self, client: OpenAI) -> None: response = client.files.with_raw_response.list() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(SyncPage[FileObject], file, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.files.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(SyncPage[FileObject], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_delete(self, client: OpenAI) -> None: file = client.files.delete( @@ -91,10 +136,25 @@ def test_raw_response_delete(self, client: OpenAI) -> None: response = client.files.with_raw_response.delete( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileDeleted, file, path=["response"]) + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.files.with_streaming_response.delete( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileDeleted, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize @pytest.mark.respx(base_url=base_url) def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -102,20 +162,37 @@ def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None: file = client.files.content( "string", ) - assert isinstance(file, BinaryResponseContent) + assert isinstance(file, _legacy_response.HttpxBinaryResponseContent) assert file.json() == {"foo": "bar"} @parametrize @pytest.mark.respx(base_url=base_url) def test_raw_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None: respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = client.files.with_raw_response.content( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert isinstance(file, BinaryResponseContent) - assert file.json() == {"foo": "bar"} + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, file, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + with client.files.with_streaming_response.content( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(bytes, file, path=["response"]) + + assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve_content(self, client: OpenAI) -> None: @@ -123,6 +200,7 @@ def test_method_retrieve_content(self, client: OpenAI) -> None: file = client.files.retrieve_content( "string", ) + assert_matches_type(str, file, path=["response"]) @parametrize @@ -131,10 +209,26 @@ def test_raw_response_retrieve_content(self, client: OpenAI) -> None: response = client.files.with_raw_response.retrieve_content( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(str, file, path=["response"]) + @parametrize + def test_streaming_response_retrieve_content(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + with client.files.with_streaming_response.retrieve_content( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(str, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncFiles: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -155,10 +249,26 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: file=b"raw file contents", purpose="fine-tune", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileObject, file, path=["response"]) + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.files.with_streaming_response.create( + file=b"raw file contents", + purpose="fine-tune", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileObject, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: file = await client.files.retrieve( @@ -171,10 +281,25 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: response = await client.files.with_raw_response.retrieve( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileObject, file, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.files.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileObject, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: file = await client.files.list() @@ -190,10 +315,23 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: @parametrize async def test_raw_response_list(self, client: AsyncOpenAI) -> None: response = await client.files.with_raw_response.list() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.files.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_delete(self, client: AsyncOpenAI) -> None: file = await client.files.delete( @@ -206,10 +344,25 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: response = await client.files.with_raw_response.delete( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileDeleted, file, path=["response"]) + @parametrize + async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: + async with client.files.with_streaming_response.delete( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileDeleted, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: @@ -217,20 +370,37 @@ async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) file = await client.files.content( "string", ) - assert isinstance(file, BinaryResponseContent) + assert isinstance(file, _legacy_response.HttpxBinaryResponseContent) assert file.json() == {"foo": "bar"} @parametrize @pytest.mark.respx(base_url=base_url) async def test_raw_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = await client.files.with_raw_response.content( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert isinstance(file, BinaryResponseContent) - assert file.json() == {"foo": "bar"} + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, file, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + async with client.files.with_streaming_response.content( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(bytes, file, path=["response"]) + + assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: @@ -238,6 +408,7 @@ async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: file = await client.files.retrieve_content( "string", ) + assert_matches_type(str, file, path=["response"]) @parametrize @@ -246,6 +417,22 @@ async def test_raw_response_retrieve_content(self, client: AsyncOpenAI) -> None: response = await client.files.with_raw_response.retrieve_content( "string", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(str, file, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve_content(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + async with client.files.with_streaming_response.retrieve_content( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(str, file, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index c7f5e5bcd2..553bd018ee 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -44,10 +45,25 @@ def test_raw_response_create_variation(self, client: OpenAI) -> None: response = client.images.with_raw_response.create_variation( image=b"raw file contents", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" image = response.parse() assert_matches_type(ImagesResponse, image, path=["response"]) + @parametrize + def test_streaming_response_create_variation(self, client: OpenAI) -> None: + with client.images.with_streaming_response.create_variation( + image=b"raw file contents", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_edit(self, client: OpenAI) -> None: image = client.images.edit( @@ -76,10 +92,26 @@ def test_raw_response_edit(self, client: OpenAI) -> None: image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" image = response.parse() assert_matches_type(ImagesResponse, image, path=["response"]) + @parametrize + def test_streaming_response_edit(self, client: OpenAI) -> None: + with client.images.with_streaming_response.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_generate(self, client: OpenAI) -> None: image = client.images.generate( @@ -106,10 +138,25 @@ def test_raw_response_generate(self, client: OpenAI) -> None: response = client.images.with_raw_response.generate( prompt="A cute baby sea otter", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" image = response.parse() assert_matches_type(ImagesResponse, image, path=["response"]) + @parametrize + def test_streaming_response_generate(self, client: OpenAI) -> None: + with client.images.with_streaming_response.generate( + prompt="A cute baby sea otter", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncImages: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -140,10 +187,25 @@ async def test_raw_response_create_variation(self, client: AsyncOpenAI) -> None: response = await client.images.with_raw_response.create_variation( image=b"raw file contents", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" image = response.parse() assert_matches_type(ImagesResponse, image, path=["response"]) + @parametrize + async def test_streaming_response_create_variation(self, client: AsyncOpenAI) -> None: + async with client.images.with_streaming_response.create_variation( + image=b"raw file contents", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_edit(self, client: AsyncOpenAI) -> None: image = await client.images.edit( @@ -172,10 +234,26 @@ async def test_raw_response_edit(self, client: AsyncOpenAI) -> None: image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" image = response.parse() assert_matches_type(ImagesResponse, image, path=["response"]) + @parametrize + async def test_streaming_response_edit(self, client: AsyncOpenAI) -> None: + async with client.images.with_streaming_response.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_generate(self, client: AsyncOpenAI) -> None: image = await client.images.generate( @@ -202,6 +280,21 @@ async def test_raw_response_generate(self, client: AsyncOpenAI) -> None: response = await client.images.with_raw_response.generate( prompt="A cute baby sea otter", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" image = response.parse() assert_matches_type(ImagesResponse, image, path=["response"]) + + @parametrize + async def test_streaming_response_generate(self, client: AsyncOpenAI) -> None: + async with client.images.with_streaming_response.generate( + prompt="A cute baby sea otter", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImagesResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py index 3998809610..5afda86a7a 100644 --- a/tests/api_resources/test_models.py +++ b/tests/api_resources/test_models.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -33,10 +34,25 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.models.with_raw_response.retrieve( "gpt-3.5-turbo", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(Model, model, path=["response"]) + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.models.with_streaming_response.retrieve( + "gpt-3.5-turbo", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model = response.parse() + assert_matches_type(Model, model, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_list(self, client: OpenAI) -> None: model = client.models.list() @@ -45,10 +61,23 @@ def test_method_list(self, client: OpenAI) -> None: @parametrize def test_raw_response_list(self, client: OpenAI) -> None: response = client.models.with_raw_response.list() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(SyncPage[Model], model, path=["response"]) + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.models.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model = response.parse() + assert_matches_type(SyncPage[Model], model, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_delete(self, client: OpenAI) -> None: model = client.models.delete( @@ -61,10 +90,25 @@ def test_raw_response_delete(self, client: OpenAI) -> None: response = client.models.with_raw_response.delete( "ft:gpt-3.5-turbo:acemeco:suffix:abc123", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(ModelDeleted, model, path=["response"]) + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.models.with_streaming_response.delete( + "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model = response.parse() + assert_matches_type(ModelDeleted, model, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncModels: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -83,10 +127,25 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: response = await client.models.with_raw_response.retrieve( "gpt-3.5-turbo", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(Model, model, path=["response"]) + @parametrize + async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: + async with client.models.with_streaming_response.retrieve( + "gpt-3.5-turbo", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model = await response.parse() + assert_matches_type(Model, model, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: model = await client.models.list() @@ -95,10 +154,23 @@ async def test_method_list(self, client: AsyncOpenAI) -> None: @parametrize async def test_raw_response_list(self, client: AsyncOpenAI) -> None: response = await client.models.with_raw_response.list() + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(AsyncPage[Model], model, path=["response"]) + @parametrize + async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: + async with client.models.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model = await response.parse() + assert_matches_type(AsyncPage[Model], model, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_delete(self, client: AsyncOpenAI) -> None: model = await client.models.delete( @@ -111,6 +183,21 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: response = await client.models.with_raw_response.delete( "ft:gpt-3.5-turbo:acemeco:suffix:abc123", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(ModelDeleted, model, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: + async with client.models.with_streaming_response.delete( + "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model = await response.parse() + assert_matches_type(ModelDeleted, model, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index 502030d614..88d35f003d 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +from typing import Any, cast import pytest @@ -40,10 +41,25 @@ def test_raw_response_create(self, client: OpenAI) -> None: response = client.moderations.with_raw_response.create( input="I want to kill them.", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" moderation = response.parse() assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.moderations.with_streaming_response.create( + input="I want to kill them.", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + moderation = response.parse() + assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncModerations: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -70,6 +86,21 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: response = await client.moderations.with_raw_response.create( input="I want to kill them.", ) + + assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" moderation = response.parse() assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: + async with client.moderations.with_streaming_response.create( + input="I want to kill them.", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + moderation = await response.parse() + assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/test_client.py b/tests/test_client.py index c49e4d629e..7aa473fe9b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -19,6 +19,8 @@ from openai import OpenAI, AsyncOpenAI, APIResponseValidationError from openai._client import OpenAI, AsyncOpenAI from openai._models import BaseModel, FinalRequestOptions +from openai._response import APIResponse, AsyncAPIResponse +from openai._constants import RAW_RESPONSE_HEADER from openai._streaming import Stream, AsyncStream from openai._exceptions import OpenAIError, APIStatusError, APITimeoutError, APIResponseValidationError from openai._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options @@ -220,6 +222,7 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic # to_raw_response_wrapper leaks through the @functools.wraps() decorator. # # removing the decorator fixes the leak for reasons we don't understand. + "openai/_legacy_response.py", "openai/_response.py", # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. "openai/_compat.py", @@ -612,8 +615,9 @@ class Model(BaseModel): respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.post("/foo", cast_to=Model, stream=True) - assert isinstance(response, Stream) + stream = self.client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model]) + assert isinstance(stream, Stream) + stream.response.close() @pytest.mark.respx(base_url=base_url) def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: @@ -661,6 +665,33 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_streaming_response(self) -> None: + response = self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=APIResponse[bytes], + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, + ) + + assert not cast(Any, response.is_closed) + assert _get_open_connections(self.client) == 1 + + for _ in response.iter_bytes(): + ... + + assert cast(Any, response.is_closed) + assert _get_open_connections(self.client) == 0 + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: @@ -679,7 +710,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No model="gpt-3.5-turbo", ), cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, ) assert _get_open_connections(self.client) == 0 @@ -702,7 +733,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non model="gpt-3.5-turbo", ), cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, ) assert _get_open_connections(self.client) == 0 @@ -883,6 +914,7 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic # to_raw_response_wrapper leaks through the @functools.wraps() decorator. # # removing the decorator fixes the leak for reasons we don't understand. + "openai/_legacy_response.py", "openai/_response.py", # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. "openai/_compat.py", @@ -1288,8 +1320,9 @@ class Model(BaseModel): respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.post("/foo", cast_to=Model, stream=True) - assert isinstance(response, AsyncStream) + stream = await self.client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model]) + assert isinstance(stream, AsyncStream) + await stream.response.aclose() @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio @@ -1339,6 +1372,33 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response(self) -> None: + response = await self.client.post( + "/chat/completions", + body=dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), + cast_to=AsyncAPIResponse[bytes], + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, + ) + + assert not cast(Any, response.is_closed) + assert _get_open_connections(self.client) == 1 + + async for _ in response.iter_bytes(): + ... + + assert cast(Any, response.is_closed) + assert _get_open_connections(self.client) == 0 + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: @@ -1357,7 +1417,7 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) model="gpt-3.5-turbo", ), cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, ) assert _get_open_connections(self.client) == 0 @@ -1380,7 +1440,7 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) model="gpt-3.5-turbo", ), cast_to=httpx.Response, - options={"headers": {"X-Stainless-Streamed-Raw-Response": "true"}}, + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, ) assert _get_open_connections(self.client) == 0 diff --git a/tests/test_response.py b/tests/test_response.py new file mode 100644 index 0000000000..335ca7922a --- /dev/null +++ b/tests/test_response.py @@ -0,0 +1,50 @@ +from typing import List + +import httpx +import pytest + +from openai._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + BinaryAPIResponse, + AsyncBinaryAPIResponse, + extract_response_type, +) + + +class ConcreteBaseAPIResponse(APIResponse[bytes]): + ... + + +class ConcreteAPIResponse(APIResponse[List[str]]): + ... + + +class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): + ... + + +def test_extract_response_type_direct_classes() -> None: + assert extract_response_type(BaseAPIResponse[str]) == str + assert extract_response_type(APIResponse[str]) == str + assert extract_response_type(AsyncAPIResponse[str]) == str + + +def test_extract_response_type_direct_class_missing_type_arg() -> None: + with pytest.raises( + RuntimeError, + match="Expected type to have a type argument at index 0 but it did not", + ): + extract_response_type(AsyncAPIResponse) + + +def test_extract_response_type_concrete_subclasses() -> None: + assert extract_response_type(ConcreteBaseAPIResponse) == bytes + assert extract_response_type(ConcreteAPIResponse) == List[str] + assert extract_response_type(ConcreteAsyncAPIResponse) == httpx.Response + + +def test_extract_response_type_binary_response() -> None: + assert extract_response_type(BinaryAPIResponse) == bytes + assert extract_response_type(AsyncBinaryAPIResponse) == bytes diff --git a/tests/utils.py b/tests/utils.py index 02dd9c0acc..216b333550 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import inspect import traceback import contextlib from typing import Any, TypeVar, Iterator, cast @@ -68,6 +69,8 @@ def assert_matches_type( assert isinstance(value, bool) elif origin == float: assert isinstance(value, float) + elif origin == bytes: + assert isinstance(value, bytes) elif origin == datetime: assert isinstance(value, datetime) elif origin == date: @@ -100,6 +103,8 @@ def assert_matches_type( elif issubclass(origin, BaseModel): assert isinstance(value, type_) assert assert_matches_model(type_, cast(Any, value), path=path) + elif inspect.isclass(origin) and origin.__name__ == "HttpxBinaryResponseContent": + assert value.__class__.__name__ == "HttpxBinaryResponseContent" else: assert None, f"Unhandled field type: {type_}" From 023a4e665150e20f77ace0bce80c399d7d766085 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 16 Jan 2024 05:50:40 -0500 Subject: [PATCH 292/914] fix(client): ensure path params are non-empty (#1075) --- src/openai/resources/audio/transcriptions.py | 2 - src/openai/resources/audio/translations.py | 2 - .../resources/beta/assistants/assistants.py | 12 ++ src/openai/resources/beta/assistants/files.py | 24 +++ .../resources/beta/threads/messages/files.py | 20 +++ .../beta/threads/messages/messages.py | 24 +++ .../resources/beta/threads/runs/runs.py | 40 +++++ .../resources/beta/threads/runs/steps.py | 20 +++ src/openai/resources/beta/threads/threads.py | 12 ++ src/openai/resources/files.py | 18 ++- src/openai/resources/fine_tuning/jobs.py | 12 ++ src/openai/resources/images.py | 4 - src/openai/resources/models.py | 8 + .../beta/assistants/test_files.py | 86 +++++++++++ tests/api_resources/beta/test_assistants.py | 42 +++++ tests/api_resources/beta/test_threads.py | 42 +++++ .../beta/threads/messages/test_files.py | 74 +++++++++ .../beta/threads/runs/test_steps.py | 74 +++++++++ .../beta/threads/test_messages.py | 88 +++++++++++ tests/api_resources/beta/threads/test_runs.py | 146 ++++++++++++++++++ tests/api_resources/fine_tuning/test_jobs.py | 42 +++++ tests/api_resources/test_files.py | 60 +++++++ tests/api_resources/test_models.py | 28 ++++ 23 files changed, 870 insertions(+), 10 deletions(-) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 053ac30095..868ce7725f 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -98,7 +98,6 @@ def create( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( "/audio/transcriptions", body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), @@ -187,7 +186,6 @@ async def create( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( "/audio/transcriptions", body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index db41b194b6..333abfb4cf 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -91,7 +91,6 @@ def create( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( "/audio/translations", body=maybe_transform(body, translation_create_params.TranslationCreateParams), @@ -173,7 +172,6 @@ async def create( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( "/audio/translations", body=maybe_transform(body, translation_create_params.TranslationCreateParams), diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 176bf05516..a40acfb323 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -148,6 +148,8 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get( f"/assistants/{assistant_id}", @@ -215,6 +217,8 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/assistants/{assistant_id}", @@ -324,6 +328,8 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._delete( f"/assistants/{assistant_id}", @@ -445,6 +451,8 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._get( f"/assistants/{assistant_id}", @@ -512,6 +520,8 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/assistants/{assistant_id}", @@ -621,6 +631,8 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._delete( f"/assistants/{assistant_id}", diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index 9e45ce46d3..12247044c4 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -61,6 +61,8 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/assistants/{assistant_id}/files", @@ -95,6 +97,10 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get( f"/assistants/{assistant_id}/files/{file_id}", @@ -147,6 +153,8 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/assistants/{assistant_id}/files", @@ -193,6 +201,10 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._delete( f"/assistants/{assistant_id}/files/{file_id}", @@ -242,6 +254,8 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/assistants/{assistant_id}/files", @@ -276,6 +290,10 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._get( f"/assistants/{assistant_id}/files/{file_id}", @@ -328,6 +346,8 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/assistants/{assistant_id}/files", @@ -374,6 +394,10 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not assistant_id: + raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._delete( f"/assistants/{assistant_id}/files/{file_id}", diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index d0a963f1ae..8b6c4581d0 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -56,6 +56,12 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get( f"/threads/{thread_id}/messages/{message_id}/files/{file_id}", @@ -110,6 +116,10 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/messages/{message_id}/files", @@ -167,6 +177,12 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}/messages/{message_id}/files/{file_id}", @@ -221,6 +237,10 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/messages/{message_id}/files", diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 1a15dd36ca..f5a17f902f 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -86,6 +86,8 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/messages", @@ -128,6 +130,10 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get( f"/threads/{thread_id}/messages/{message_id}", @@ -167,6 +173,10 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/messages/{message_id}", @@ -220,6 +230,8 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/messages", @@ -298,6 +310,8 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/messages", @@ -340,6 +354,10 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}/messages/{message_id}", @@ -379,6 +397,10 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/messages/{message_id}", @@ -432,6 +454,8 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/messages", diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index eb6c974eaa..ac7a1b3330 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -104,6 +104,8 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/runs", @@ -148,6 +150,10 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get( f"/threads/{thread_id}/runs/{run_id}", @@ -187,6 +193,10 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/runs/{run_id}", @@ -240,6 +250,8 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/runs", @@ -286,6 +298,10 @@ def cancel( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/runs/{run_id}/cancel", @@ -325,6 +341,10 @@ def submit_tool_outputs( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", @@ -405,6 +425,8 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs", @@ -449,6 +471,10 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}/runs/{run_id}", @@ -488,6 +514,10 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs/{run_id}", @@ -541,6 +571,8 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/runs", @@ -587,6 +619,10 @@ async def cancel( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs/{run_id}/cancel", @@ -626,6 +662,10 @@ async def submit_tool_outputs( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 566ad9e4dc..9b1df10652 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -56,6 +56,12 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + if not step_id: + raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get( f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", @@ -109,6 +115,10 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/runs/{run_id}/steps", @@ -166,6 +176,12 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + if not step_id: + raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", @@ -219,6 +235,10 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/runs/{run_id}/steps", diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 14bfbe9bba..d885404f59 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -133,6 +133,8 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get( f"/threads/{thread_id}", @@ -171,6 +173,8 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( f"/threads/{thread_id}", @@ -204,6 +208,8 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._delete( f"/threads/{thread_id}", @@ -372,6 +378,8 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}", @@ -410,6 +418,8 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}", @@ -443,6 +453,8 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._delete( f"/threads/{thread_id}", diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index f435e70a2f..ff924340ac 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -99,7 +99,6 @@ def create( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( "/files", body=maybe_transform(body, file_create_params.FileCreateParams), @@ -133,6 +132,8 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._get( f"/files/{file_id}", options=make_request_options( @@ -202,6 +203,8 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._delete( f"/files/{file_id}", options=make_request_options( @@ -233,6 +236,8 @@ def content( timeout: Override the client-level default timeout for this request, in seconds """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._get( f"/files/{file_id}/content", options=make_request_options( @@ -265,6 +270,8 @@ def retrieve_content( timeout: Override the client-level default timeout for this request, in seconds """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "application/json", **(extra_headers or {})} return self._get( f"/files/{file_id}/content", @@ -365,7 +372,6 @@ async def create( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( "/files", body=maybe_transform(body, file_create_params.FileCreateParams), @@ -399,6 +405,8 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._get( f"/files/{file_id}", options=make_request_options( @@ -468,6 +476,8 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._delete( f"/files/{file_id}", options=make_request_options( @@ -499,6 +509,8 @@ async def content( timeout: Override the client-level default timeout for this request, in seconds """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._get( f"/files/{file_id}/content", options=make_request_options( @@ -531,6 +543,8 @@ async def retrieve_content( timeout: Override the client-level default timeout for this request, in seconds """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "application/json", **(extra_headers or {})} return await self._get( f"/files/{file_id}/content", diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index f337b136a6..208591fa47 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -149,6 +149,8 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._get( f"/fine_tuning/jobs/{fine_tuning_job_id}", options=make_request_options( @@ -227,6 +229,8 @@ def cancel( timeout: Override the client-level default timeout for this request, in seconds """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._post( f"/fine_tuning/jobs/{fine_tuning_job_id}/cancel", options=make_request_options( @@ -264,6 +268,8 @@ def list_events( timeout: Override the client-level default timeout for this request, in seconds """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._get_api_list( f"/fine_tuning/jobs/{fine_tuning_job_id}/events", page=SyncCursorPage[FineTuningJobEvent], @@ -404,6 +410,8 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return await self._get( f"/fine_tuning/jobs/{fine_tuning_job_id}", options=make_request_options( @@ -482,6 +490,8 @@ async def cancel( timeout: Override the client-level default timeout for this request, in seconds """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return await self._post( f"/fine_tuning/jobs/{fine_tuning_job_id}/cancel", options=make_request_options( @@ -519,6 +529,8 @@ def list_events( timeout: Override the client-level default timeout for this request, in seconds """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._get_api_list( f"/fine_tuning/jobs/{fine_tuning_job_id}/events", page=AsyncCursorPage[FineTuningJobEvent], diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 6f1de221e2..a3eb98574e 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -98,7 +98,6 @@ def create_variation( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( "/images/variations", body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), @@ -182,7 +181,6 @@ def edit( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( "/images/edits", body=maybe_transform(body, image_edit_params.ImageEditParams), @@ -345,7 +343,6 @@ async def create_variation( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( "/images/variations", body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), @@ -429,7 +426,6 @@ async def edit( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( "/images/edits", body=maybe_transform(body, image_edit_params.ImageEditParams), diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index b431ef84fc..e4a0d84810 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -52,6 +52,8 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return self._get( f"/models/{model}", options=make_request_options( @@ -108,6 +110,8 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return self._delete( f"/models/{model}", options=make_request_options( @@ -150,6 +154,8 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return await self._get( f"/models/{model}", options=make_request_options( @@ -206,6 +212,8 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ + if not model: + raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return await self._delete( f"/models/{model}", options=make_request_options( diff --git a/tests/api_resources/beta/assistants/test_files.py b/tests/api_resources/beta/assistants/test_files.py index 443408bd44..7db1368ccb 100644 --- a/tests/api_resources/beta/assistants/test_files.py +++ b/tests/api_resources/beta/assistants/test_files.py @@ -56,6 +56,14 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.files.with_raw_response.create( + "", + file_id="string", + ) + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: file = client.beta.assistants.files.retrieve( @@ -90,6 +98,20 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.files.with_raw_response.retrieve( + "string", + assistant_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.beta.assistants.files.with_raw_response.retrieve( + "", + assistant_id="string", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: file = client.beta.assistants.files.list( @@ -132,6 +154,13 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.files.with_raw_response.list( + "", + ) + @parametrize def test_method_delete(self, client: OpenAI) -> None: file = client.beta.assistants.files.delete( @@ -166,6 +195,20 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.files.with_raw_response.delete( + "string", + assistant_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.beta.assistants.files.with_raw_response.delete( + "", + assistant_id="string", + ) + class TestAsyncFiles: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -206,6 +249,14 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_create(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await client.beta.assistants.files.with_raw_response.create( + "", + file_id="string", + ) + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: file = await client.beta.assistants.files.retrieve( @@ -240,6 +291,20 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await client.beta.assistants.files.with_raw_response.retrieve( + "string", + assistant_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await client.beta.assistants.files.with_raw_response.retrieve( + "", + assistant_id="string", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: file = await client.beta.assistants.files.list( @@ -282,6 +347,13 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_list(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await client.beta.assistants.files.with_raw_response.list( + "", + ) + @parametrize async def test_method_delete(self, client: AsyncOpenAI) -> None: file = await client.beta.assistants.files.delete( @@ -315,3 +387,17 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(FileDeleteResponse, file, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await client.beta.assistants.files.with_raw_response.delete( + "string", + assistant_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await client.beta.assistants.files.with_raw_response.delete( + "", + assistant_id="string", + ) diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index fbafac03c9..fa09769622 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -100,6 +100,13 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: assistant = client.beta.assistants.update( @@ -145,6 +152,13 @@ def test_streaming_response_update(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.with_raw_response.update( + "", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: assistant = client.beta.assistants.list() @@ -211,6 +225,13 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.with_raw_response.delete( + "", + ) + class TestAsyncAssistants: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -292,6 +313,13 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await client.beta.assistants.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_update(self, client: AsyncOpenAI) -> None: assistant = await client.beta.assistants.update( @@ -337,6 +365,13 @@ async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_update(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await client.beta.assistants.with_raw_response.update( + "", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: assistant = await client.beta.assistants.list() @@ -402,3 +437,10 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(AssistantDeleted, assistant, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await client.beta.assistants.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 488ce38c1b..ba55cc85da 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -108,6 +108,13 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: thread = client.beta.threads.update( @@ -147,6 +154,13 @@ def test_streaming_response_update(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.with_raw_response.update( + "", + ) + @parametrize def test_method_delete(self, client: OpenAI) -> None: thread = client.beta.threads.delete( @@ -178,6 +192,13 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.with_raw_response.delete( + "", + ) + @parametrize def test_method_create_and_run(self, client: OpenAI) -> None: thread = client.beta.threads.create_and_run( @@ -332,6 +353,13 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_update(self, client: AsyncOpenAI) -> None: thread = await client.beta.threads.update( @@ -371,6 +399,13 @@ async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_update(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.with_raw_response.update( + "", + ) + @parametrize async def test_method_delete(self, client: AsyncOpenAI) -> None: thread = await client.beta.threads.delete( @@ -402,6 +437,13 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.with_raw_response.delete( + "", + ) + @parametrize async def test_method_create_and_run(self, client: AsyncOpenAI) -> None: thread = await client.beta.threads.create_and_run( diff --git a/tests/api_resources/beta/threads/messages/test_files.py b/tests/api_resources/beta/threads/messages/test_files.py index 5de352c0d2..2d248642e9 100644 --- a/tests/api_resources/beta/threads/messages/test_files.py +++ b/tests/api_resources/beta/threads/messages/test_files.py @@ -59,6 +59,29 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.messages.files.with_raw_response.retrieve( + "file-abc123", + thread_id="", + message_id="msg_abc123", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + client.beta.threads.messages.files.with_raw_response.retrieve( + "file-abc123", + thread_id="thread_abc123", + message_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.beta.threads.messages.files.with_raw_response.retrieve( + "", + thread_id="thread_abc123", + message_id="msg_abc123", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: file = client.beta.threads.messages.files.list( @@ -105,6 +128,20 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.messages.files.with_raw_response.list( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + client.beta.threads.messages.files.with_raw_response.list( + "", + thread_id="string", + ) + class TestAsyncFiles: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -148,6 +185,29 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.messages.files.with_raw_response.retrieve( + "file-abc123", + thread_id="", + message_id="msg_abc123", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + await client.beta.threads.messages.files.with_raw_response.retrieve( + "file-abc123", + thread_id="thread_abc123", + message_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await client.beta.threads.messages.files.with_raw_response.retrieve( + "", + thread_id="thread_abc123", + message_id="msg_abc123", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: file = await client.beta.threads.messages.files.list( @@ -193,3 +253,17 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.messages.files.with_raw_response.list( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + await client.beta.threads.messages.files.with_raw_response.list( + "", + thread_id="string", + ) diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py index f13970fc14..2ec164a535 100644 --- a/tests/api_resources/beta/threads/runs/test_steps.py +++ b/tests/api_resources/beta/threads/runs/test_steps.py @@ -59,6 +59,29 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.steps.with_raw_response.retrieve( + "string", + thread_id="", + run_id="string", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.beta.threads.runs.steps.with_raw_response.retrieve( + "string", + thread_id="string", + run_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `step_id` but received ''"): + client.beta.threads.runs.steps.with_raw_response.retrieve( + "", + thread_id="string", + run_id="string", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: step = client.beta.threads.runs.steps.list( @@ -105,6 +128,20 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.steps.with_raw_response.list( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.beta.threads.runs.steps.with_raw_response.list( + "", + thread_id="string", + ) + class TestAsyncSteps: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -148,6 +185,29 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.runs.steps.with_raw_response.retrieve( + "string", + thread_id="", + run_id="string", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await client.beta.threads.runs.steps.with_raw_response.retrieve( + "string", + thread_id="string", + run_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `step_id` but received ''"): + await client.beta.threads.runs.steps.with_raw_response.retrieve( + "", + thread_id="string", + run_id="string", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: step = await client.beta.threads.runs.steps.list( @@ -193,3 +253,17 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.runs.steps.with_raw_response.list( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await client.beta.threads.runs.steps.with_raw_response.list( + "", + thread_id="string", + ) diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index 87b6eca03a..508e9b96c9 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -70,6 +70,15 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.messages.with_raw_response.create( + "", + content="x", + role="user", + ) + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: message = client.beta.threads.messages.retrieve( @@ -104,6 +113,20 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.messages.with_raw_response.retrieve( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + client.beta.threads.messages.with_raw_response.retrieve( + "", + thread_id="string", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: message = client.beta.threads.messages.update( @@ -147,6 +170,20 @@ def test_streaming_response_update(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.messages.with_raw_response.update( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + client.beta.threads.messages.with_raw_response.update( + "", + thread_id="string", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: message = client.beta.threads.messages.list( @@ -189,6 +226,13 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.messages.with_raw_response.list( + "", + ) + class TestAsyncMessages: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -243,6 +287,15 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_create(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.messages.with_raw_response.create( + "", + content="x", + role="user", + ) + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: message = await client.beta.threads.messages.retrieve( @@ -277,6 +330,20 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.messages.with_raw_response.retrieve( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + await client.beta.threads.messages.with_raw_response.retrieve( + "", + thread_id="string", + ) + @parametrize async def test_method_update(self, client: AsyncOpenAI) -> None: message = await client.beta.threads.messages.update( @@ -320,6 +387,20 @@ async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_update(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.messages.with_raw_response.update( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + await client.beta.threads.messages.with_raw_response.update( + "", + thread_id="string", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: message = await client.beta.threads.messages.list( @@ -361,3 +442,10 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.messages.with_raw_response.list( + "", + ) diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index e0070c3395..66a9edd5c0 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -71,6 +71,14 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.with_raw_response.create( + "", + assistant_id="string", + ) + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: run = client.beta.threads.runs.retrieve( @@ -105,6 +113,20 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.with_raw_response.retrieve( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.beta.threads.runs.with_raw_response.retrieve( + "", + thread_id="string", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: run = client.beta.threads.runs.update( @@ -148,6 +170,20 @@ def test_streaming_response_update(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.with_raw_response.update( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.beta.threads.runs.with_raw_response.update( + "", + thread_id="string", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: run = client.beta.threads.runs.list( @@ -190,6 +226,13 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.with_raw_response.list( + "", + ) + @parametrize def test_method_cancel(self, client: OpenAI) -> None: run = client.beta.threads.runs.cancel( @@ -224,6 +267,20 @@ def test_streaming_response_cancel(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_cancel(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.with_raw_response.cancel( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.beta.threads.runs.with_raw_response.cancel( + "", + thread_id="string", + ) + @parametrize def test_method_submit_tool_outputs(self, client: OpenAI) -> None: run = client.beta.threads.runs.submit_tool_outputs( @@ -261,6 +318,22 @@ def test_streaming_response_submit_tool_outputs(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_submit_tool_outputs(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "string", + thread_id="", + tool_outputs=[{}, {}, {}], + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) + class TestAsyncRuns: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -314,6 +387,14 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_create(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.runs.with_raw_response.create( + "", + assistant_id="string", + ) + @parametrize async def test_method_retrieve(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.retrieve( @@ -348,6 +429,20 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.runs.with_raw_response.retrieve( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await client.beta.threads.runs.with_raw_response.retrieve( + "", + thread_id="string", + ) + @parametrize async def test_method_update(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.update( @@ -391,6 +486,20 @@ async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_update(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.runs.with_raw_response.update( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await client.beta.threads.runs.with_raw_response.update( + "", + thread_id="string", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.list( @@ -433,6 +542,13 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_list(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.runs.with_raw_response.list( + "", + ) + @parametrize async def test_method_cancel(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.cancel( @@ -467,6 +583,20 @@ async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_cancel(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.runs.with_raw_response.cancel( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await client.beta.threads.runs.with_raw_response.cancel( + "", + thread_id="string", + ) + @parametrize async def test_method_submit_tool_outputs(self, client: AsyncOpenAI) -> None: run = await client.beta.threads.runs.submit_tool_outputs( @@ -503,3 +633,19 @@ async def test_streaming_response_submit_tool_outputs(self, client: AsyncOpenAI) assert_matches_type(Run, run, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_submit_tool_outputs(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "string", + thread_id="", + tool_outputs=[{}, {}, {}], + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 3db0cdc0a5..50c7278855 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -105,6 +105,13 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + client.fine_tuning.jobs.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.list() @@ -169,6 +176,13 @@ def test_streaming_response_cancel(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_cancel(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + client.fine_tuning.jobs.with_raw_response.cancel( + "", + ) + @parametrize def test_method_list_events(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.list_events( @@ -209,6 +223,13 @@ def test_streaming_response_list_events(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_list_events(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + client.fine_tuning.jobs.with_raw_response.list_events( + "", + ) + class TestAsyncJobs: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -295,6 +316,13 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + await client.fine_tuning.jobs.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: job = await client.fine_tuning.jobs.list() @@ -359,6 +387,13 @@ async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_cancel(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + await client.fine_tuning.jobs.with_raw_response.cancel( + "", + ) + @parametrize async def test_method_list_events(self, client: AsyncOpenAI) -> None: job = await client.fine_tuning.jobs.list_events( @@ -398,3 +433,10 @@ async def test_streaming_response_list_events(self, client: AsyncOpenAI) -> None assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list_events(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + await client.fine_tuning.jobs.with_raw_response.list_events( + "", + ) diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index e36a7839f2..89ad9e222f 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -92,6 +92,13 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: file = client.files.list() @@ -155,6 +162,13 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.delete( + "", + ) + @parametrize @pytest.mark.respx(base_url=base_url) def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -194,6 +208,14 @@ def test_streaming_response_content(self, client: OpenAI, respx_mock: MockRouter assert cast(Any, response.is_closed) is True + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_content(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.content( + "", + ) + @parametrize def test_method_retrieve_content(self, client: OpenAI) -> None: with pytest.warns(DeprecationWarning): @@ -229,6 +251,14 @@ def test_streaming_response_retrieve_content(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve_content(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.retrieve_content( + "", + ) + class TestAsyncFiles: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -300,6 +330,13 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await client.files.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: file = await client.files.list() @@ -363,6 +400,13 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await client.files.with_raw_response.delete( + "", + ) + @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: @@ -402,6 +446,14 @@ async def test_streaming_response_content(self, client: AsyncOpenAI, respx_mock: assert cast(Any, response.is_closed) is True + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_content(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await client.files.with_raw_response.content( + "", + ) + @parametrize async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: with pytest.warns(DeprecationWarning): @@ -436,3 +488,11 @@ async def test_streaming_response_retrieve_content(self, client: AsyncOpenAI) -> assert_matches_type(str, file, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve_content(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await client.files.with_raw_response.retrieve_content( + "", + ) diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py index 5afda86a7a..b41e50eb71 100644 --- a/tests/api_resources/test_models.py +++ b/tests/api_resources/test_models.py @@ -53,6 +53,13 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): + client.models.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: model = client.models.list() @@ -109,6 +116,13 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): + client.models.with_raw_response.delete( + "", + ) + class TestAsyncModels: strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -146,6 +160,13 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): + await client.models.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_list(self, client: AsyncOpenAI) -> None: model = await client.models.list() @@ -201,3 +222,10 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(ModelDeleted, model, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): + await client.models.with_raw_response.delete( + "", + ) From a05b1607440d1b5917a015e6e761acbb15f47cf4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:47:21 -0500 Subject: [PATCH 293/914] fix(proxy): prevent recursion errors when debugging pycharm (#1076) https://github.com/openai/openai-python/issues/906 --- src/openai/_extras/numpy_proxy.py | 4 +--- src/openai/_extras/pandas_proxy.py | 4 +--- src/openai/_utils/_proxy.py | 20 ++------------------ 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/openai/_extras/numpy_proxy.py b/src/openai/_extras/numpy_proxy.py index 3809991c46..27880bf132 100644 --- a/src/openai/_extras/numpy_proxy.py +++ b/src/openai/_extras/numpy_proxy.py @@ -1,7 +1,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any -from typing_extensions import ClassVar, override +from typing_extensions import override from .._utils import LazyProxy from ._common import MissingDependencyError, format_instructions @@ -14,8 +14,6 @@ class NumpyProxy(LazyProxy[Any]): - should_cache: ClassVar[bool] = True - @override def __load__(self) -> Any: try: diff --git a/src/openai/_extras/pandas_proxy.py b/src/openai/_extras/pandas_proxy.py index a24f7fb604..686377bade 100644 --- a/src/openai/_extras/pandas_proxy.py +++ b/src/openai/_extras/pandas_proxy.py @@ -1,7 +1,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any -from typing_extensions import ClassVar, override +from typing_extensions import override from .._utils import LazyProxy from ._common import MissingDependencyError, format_instructions @@ -14,8 +14,6 @@ class PandasProxy(LazyProxy[Any]): - should_cache: ClassVar[bool] = True - @override def __load__(self) -> Any: try: diff --git a/src/openai/_utils/_proxy.py b/src/openai/_utils/_proxy.py index 3c9e790a25..6f05efcd21 100644 --- a/src/openai/_utils/_proxy.py +++ b/src/openai/_utils/_proxy.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from typing import Generic, TypeVar, Iterable, cast -from typing_extensions import ClassVar, override +from typing_extensions import override T = TypeVar("T") @@ -13,11 +13,6 @@ class LazyProxy(Generic[T], ABC): This includes forwarding attribute access and othe methods. """ - should_cache: ClassVar[bool] = False - - def __init__(self) -> None: - self.__proxied: T | None = None - # Note: we have to special case proxies that themselves return proxies # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` @@ -57,18 +52,7 @@ def __class__(self) -> type: return proxied.__class__ def __get_proxied__(self) -> T: - if not self.should_cache: - return self.__load__() - - proxied = self.__proxied - if proxied is not None: - return proxied - - self.__proxied = proxied = self.__load__() - return proxied - - def __set_proxied__(self, value: T) -> None: - self.__proxied = value + return self.__load__() def __as_proxied__(self) -> T: """Helper method that returns the current proxy, typed as the loaded object""" From 8866b7227e46361220828ac9d2fb4fc14c236b35 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:20:40 -0500 Subject: [PATCH 294/914] chore: add write_to_file binary helper method (#1077) --- src/openai/_legacy_response.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 5a398efebf..c36c94f165 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -336,6 +336,22 @@ def iter_lines(self) -> Iterator[str]: def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]: return self.response.iter_raw(chunk_size) + def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `client.with_streaming_response.foo().stream_to_file('my_filename.txt')` + """ + with open(file, mode="wb") as f: + for data in self.response.iter_bytes(): + f.write(data) + @deprecated( "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" ) From 3d61ed42aba652b547029095a7eb269ad4e1e957 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:21:22 -0500 Subject: [PATCH 295/914] release: 1.8.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b08a26cbda..c523ce19f0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.7.2" + ".": "1.8.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ab502f8137..c2ac83cdeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.8.0 (2024-01-16) + +Full Changelog: [v1.7.2...v1.8.0](https://github.com/openai/openai-python/compare/v1.7.2...v1.8.0) + +### Features + +* **client:** add support for streaming raw responses ([#1072](https://github.com/openai/openai-python/issues/1072)) ([0e93c3b](https://github.com/openai/openai-python/commit/0e93c3b5bc9cfa041e91962fd82c0d9358125024)) + + +### Bug Fixes + +* **client:** ensure path params are non-empty ([#1075](https://github.com/openai/openai-python/issues/1075)) ([9a25149](https://github.com/openai/openai-python/commit/9a2514997c2ddccbec9df8be3773e83271f1dab8)) +* **proxy:** prevent recursion errors when debugging pycharm ([#1076](https://github.com/openai/openai-python/issues/1076)) ([3d78798](https://github.com/openai/openai-python/commit/3d787987cf7625b5b502cb0b63a37d55956eaf1d)) + + +### Chores + +* add write_to_file binary helper method ([#1077](https://github.com/openai/openai-python/issues/1077)) ([c622c6a](https://github.com/openai/openai-python/commit/c622c6aaf2ae7dc62bd6cdfc053204c5dc3293ac)) + ## 1.7.2 (2024-01-12) Full Changelog: [v1.7.1...v1.7.2](https://github.com/openai/openai-python/compare/v1.7.1...v1.7.2) diff --git a/pyproject.toml b/pyproject.toml index 354d763812..5019e6cf7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.7.2" +version = "1.8.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0b4aa63ffe..311cab2540 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.7.2" # x-release-please-version +__version__ = "1.8.0" # x-release-please-version From 42d1ab6434f7bc62ad774be1ba5b43e36917239f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:15:25 -0500 Subject: [PATCH 296/914] chore(internal): fix typing util function (#1083) --- src/openai/_utils/_typing.py | 31 ++++++++++++- tests/test_utils/test_typing.py | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 tests/test_utils/test_typing.py diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py index b5e2c2e397..a020822bc0 100644 --- a/src/openai/_utils/_typing.py +++ b/src/openai/_utils/_typing.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, cast +from typing import Any, TypeVar, cast from typing_extensions import Required, Annotated, get_args, get_origin from .._types import InheritsGeneric @@ -23,6 +23,12 @@ 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 + + # 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): @@ -49,6 +55,15 @@ 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: @@ -75,6 +90,18 @@ class MyResponse(Foo[bytes]): f"Does {cls} inherit from one of {generic_bases} ?" ) - return extract_type_arg(target_base_class, index) + 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(f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/tests/test_utils/test_typing.py b/tests/test_utils/test_typing.py new file mode 100644 index 0000000000..690960802a --- /dev/null +++ b/tests/test_utils/test_typing.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from typing import Generic, TypeVar, cast + +from openai._utils import extract_type_var_from_base + +_T = TypeVar("_T") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") + + +class BaseGeneric(Generic[_T]): + ... + + +class SubclassGeneric(BaseGeneric[_T]): + ... + + +class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): + ... + + +class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): + ... + + +class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): + ... + + +def test_extract_type_var() -> None: + assert ( + extract_type_var_from_base( + BaseGeneric[int], + index=0, + generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), + ) + == int + ) + + +def test_extract_type_var_generic_subclass() -> None: + assert ( + extract_type_var_from_base( + SubclassGeneric[int], + index=0, + generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), + ) + == int + ) + + +def test_extract_type_var_multiple() -> None: + typ = BaseGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) + + +def test_extract_type_var_generic_subclass_multiple() -> None: + typ = SubclassGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) + + +def test_extract_type_var_generic_subclass_different_ordering_multiple() -> None: + typ = SubclassDifferentOrderGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) From d9e9d7adbf2f3c615a9ba2125974cf8b8ee6a91d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:55:22 -0500 Subject: [PATCH 297/914] chore(internal): remove redundant client test (#1085) --- tests/test_client.py | 55 -------------------------------------------- 1 file changed, 55 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 7aa473fe9b..3d2dd35821 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -19,7 +19,6 @@ from openai import OpenAI, AsyncOpenAI, APIResponseValidationError from openai._client import OpenAI, AsyncOpenAI from openai._models import BaseModel, FinalRequestOptions -from openai._response import APIResponse, AsyncAPIResponse from openai._constants import RAW_RESPONSE_HEADER from openai._streaming import Stream, AsyncStream from openai._exceptions import OpenAIError, APIStatusError, APITimeoutError, APIResponseValidationError @@ -665,33 +664,6 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] - @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) - @pytest.mark.respx(base_url=base_url) - def test_streaming_response(self) -> None: - response = self.client.post( - "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", - ), - cast_to=APIResponse[bytes], - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - - assert not cast(Any, response.is_closed) - assert _get_open_connections(self.client) == 1 - - for _ in response.iter_bytes(): - ... - - assert cast(Any, response.is_closed) - assert _get_open_connections(self.client) == 0 - @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: @@ -1372,33 +1344,6 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] - @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) - @pytest.mark.respx(base_url=base_url) - async def test_streaming_response(self) -> None: - response = await self.client.post( - "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", - ), - cast_to=AsyncAPIResponse[bytes], - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - - assert not cast(Any, response.is_closed) - assert _get_open_connections(self.client) == 1 - - async for _ in response.iter_bytes(): - ... - - assert cast(Any, response.is_closed) - assert _get_open_connections(self.client) == 0 - @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: From d5abe79ea102949335067d4bacb4cce811f1d982 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:19:44 -0500 Subject: [PATCH 298/914] chore(internal): speculative retry-after-ms support (#1086) Fixes https://github.com/openai/openai-python/issues/957. --- src/openai/_base_client.py | 66 ++++++++++++++++++++++---------------- src/openai/_constants.py | 3 ++ 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 1dfbd7dfb3..43fad0603d 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -73,7 +73,9 @@ from ._constants import ( DEFAULT_LIMITS, DEFAULT_TIMEOUT, + MAX_RETRY_DELAY, DEFAULT_MAX_RETRIES, + INITIAL_RETRY_DELAY, RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER, ) @@ -590,6 +592,40 @@ def base_url(http://github.com/self,%20url:%20URL%20|%20str) -> None: def platform_headers(self) -> Dict[str, str]: return platform_headers(self._version) + def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: + """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. + + About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax + """ + if response_headers is None: + return None + + # First, try the non-standard `retry-after-ms` header for milliseconds, + # which is more precise than integer-seconds `retry-after` + try: + retry_ms_header = response_headers.get("retry-after-ms", None) + return float(retry_ms_header) / 1000 + except (TypeError, ValueError): + pass + + # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats). + retry_header = response_headers.get("retry-after") + try: + # note: the spec indicates that this should only ever be an integer + # but if someone sends a float there's no reason for us to not respect it + return float(retry_header) + except (TypeError, ValueError): + pass + + # Last, try parsing `retry-after` as a date. + retry_date_tuple = email.utils.parsedate_tz(retry_header) + if retry_date_tuple is None: + return None + + retry_date = email.utils.mktime_tz(retry_date_tuple) + return float(retry_date - time.time()) + def _calculate_retry_timeout( self, remaining_retries: int, @@ -597,40 +633,16 @@ def _calculate_retry_timeout( response_headers: Optional[httpx.Headers] = None, ) -> float: max_retries = options.get_max_retries(self.max_retries) - try: - # About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - # - # ". See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax for - # details. - if response_headers is not None: - retry_header = response_headers.get("retry-after") - try: - # note: the spec indicates that this should only ever be an integer - # but if someone sends a float there's no reason for us to not respect it - retry_after = float(retry_header) - except Exception: - retry_date_tuple = email.utils.parsedate_tz(retry_header) - if retry_date_tuple is None: - retry_after = -1 - else: - retry_date = email.utils.mktime_tz(retry_date_tuple) - retry_after = int(retry_date - time.time()) - else: - retry_after = -1 - - except Exception: - retry_after = -1 # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. - if 0 < retry_after <= 60: + retry_after = self._parse_retry_after_header(response_headers) + if retry_after is not None and 0 < retry_after <= 60: return retry_after - initial_retry_delay = 0.5 - max_retry_delay = 8.0 nb_retries = max_retries - remaining_retries # Apply exponential backoff, but not more than the max. - sleep_seconds = min(initial_retry_delay * pow(2.0, nb_retries), max_retry_delay) + sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) # Apply some jitter, plus-or-minus half a second. jitter = 1 - 0.25 * random() diff --git a/src/openai/_constants.py b/src/openai/_constants.py index af9a04b80c..dffb8ecfb6 100644 --- a/src/openai/_constants.py +++ b/src/openai/_constants.py @@ -9,3 +9,6 @@ DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0) DEFAULT_MAX_RETRIES = 2 DEFAULT_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) + +INITIAL_RETRY_DELAY = 0.5 +MAX_RETRY_DELAY = 8.0 From c484e0ca903e5e0b7cd8dadfb825c0359744c9eb Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:25:14 -0500 Subject: [PATCH 299/914] chore: lazy load raw resource class properties (#1087) --- src/openai/resources/audio/audio.py | 64 +++++++++++++++---- src/openai/resources/audio/speech.py | 8 +++ src/openai/resources/audio/transcriptions.py | 8 +++ src/openai/resources/audio/translations.py | 8 +++ .../resources/beta/assistants/assistants.py | 24 +++++-- src/openai/resources/beta/assistants/files.py | 8 +++ src/openai/resources/beta/beta.py | 44 ++++++++++--- .../resources/beta/threads/messages/files.py | 8 +++ .../beta/threads/messages/messages.py | 24 +++++-- .../resources/beta/threads/runs/runs.py | 24 +++++-- .../resources/beta/threads/runs/steps.py | 8 +++ src/openai/resources/beta/threads/threads.py | 44 ++++++++++--- src/openai/resources/chat/chat.py | 24 +++++-- src/openai/resources/chat/completions.py | 8 +++ src/openai/resources/completions.py | 8 +++ src/openai/resources/embeddings.py | 8 +++ src/openai/resources/files.py | 8 +++ .../resources/fine_tuning/fine_tuning.py | 24 +++++-- src/openai/resources/fine_tuning/jobs.py | 8 +++ src/openai/resources/images.py | 8 +++ src/openai/resources/models.py | 8 +++ src/openai/resources/moderations.py | 8 +++ 22 files changed, 336 insertions(+), 48 deletions(-) diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index b14e64cff6..bafacf4422 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -78,27 +78,67 @@ def with_streaming_response(self) -> AsyncAudioWithStreamingResponse: class AudioWithRawResponse: def __init__(self, audio: Audio) -> None: - self.transcriptions = TranscriptionsWithRawResponse(audio.transcriptions) - self.translations = TranslationsWithRawResponse(audio.translations) - self.speech = SpeechWithRawResponse(audio.speech) + self._audio = audio + + @cached_property + def transcriptions(self) -> TranscriptionsWithRawResponse: + return TranscriptionsWithRawResponse(self._audio.transcriptions) + + @cached_property + def translations(self) -> TranslationsWithRawResponse: + return TranslationsWithRawResponse(self._audio.translations) + + @cached_property + def speech(self) -> SpeechWithRawResponse: + return SpeechWithRawResponse(self._audio.speech) class AsyncAudioWithRawResponse: def __init__(self, audio: AsyncAudio) -> None: - self.transcriptions = AsyncTranscriptionsWithRawResponse(audio.transcriptions) - self.translations = AsyncTranslationsWithRawResponse(audio.translations) - self.speech = AsyncSpeechWithRawResponse(audio.speech) + self._audio = audio + + @cached_property + def transcriptions(self) -> AsyncTranscriptionsWithRawResponse: + return AsyncTranscriptionsWithRawResponse(self._audio.transcriptions) + + @cached_property + def translations(self) -> AsyncTranslationsWithRawResponse: + return AsyncTranslationsWithRawResponse(self._audio.translations) + + @cached_property + def speech(self) -> AsyncSpeechWithRawResponse: + return AsyncSpeechWithRawResponse(self._audio.speech) class AudioWithStreamingResponse: def __init__(self, audio: Audio) -> None: - self.transcriptions = TranscriptionsWithStreamingResponse(audio.transcriptions) - self.translations = TranslationsWithStreamingResponse(audio.translations) - self.speech = SpeechWithStreamingResponse(audio.speech) + self._audio = audio + + @cached_property + def transcriptions(self) -> TranscriptionsWithStreamingResponse: + return TranscriptionsWithStreamingResponse(self._audio.transcriptions) + + @cached_property + def translations(self) -> TranslationsWithStreamingResponse: + return TranslationsWithStreamingResponse(self._audio.translations) + + @cached_property + def speech(self) -> SpeechWithStreamingResponse: + return SpeechWithStreamingResponse(self._audio.speech) class AsyncAudioWithStreamingResponse: def __init__(self, audio: AsyncAudio) -> None: - self.transcriptions = AsyncTranscriptionsWithStreamingResponse(audio.transcriptions) - self.translations = AsyncTranslationsWithStreamingResponse(audio.translations) - self.speech = AsyncSpeechWithStreamingResponse(audio.speech) + self._audio = audio + + @cached_property + def transcriptions(self) -> AsyncTranscriptionsWithStreamingResponse: + return AsyncTranscriptionsWithStreamingResponse(self._audio.transcriptions) + + @cached_property + def translations(self) -> AsyncTranslationsWithStreamingResponse: + return AsyncTranslationsWithStreamingResponse(self._audio.translations) + + @cached_property + def speech(self) -> AsyncSpeechWithStreamingResponse: + return AsyncSpeechWithStreamingResponse(self._audio.speech) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 9c051624d5..4e94d4aaef 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -170,6 +170,8 @@ async def create( class SpeechWithRawResponse: def __init__(self, speech: Speech) -> None: + self._speech = speech + self.create = _legacy_response.to_raw_response_wrapper( speech.create, ) @@ -177,6 +179,8 @@ def __init__(self, speech: Speech) -> None: class AsyncSpeechWithRawResponse: def __init__(self, speech: AsyncSpeech) -> None: + self._speech = speech + self.create = _legacy_response.async_to_raw_response_wrapper( speech.create, ) @@ -184,6 +188,8 @@ def __init__(self, speech: AsyncSpeech) -> None: class SpeechWithStreamingResponse: def __init__(self, speech: Speech) -> None: + self._speech = speech + self.create = to_custom_streamed_response_wrapper( speech.create, StreamedBinaryAPIResponse, @@ -192,6 +198,8 @@ def __init__(self, speech: Speech) -> None: class AsyncSpeechWithStreamingResponse: def __init__(self, speech: AsyncSpeech) -> None: + self._speech = speech + self.create = async_to_custom_streamed_response_wrapper( speech.create, AsyncStreamedBinaryAPIResponse, diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 868ce7725f..2c167be395 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -199,6 +199,8 @@ async def create( class TranscriptionsWithRawResponse: def __init__(self, transcriptions: Transcriptions) -> None: + self._transcriptions = transcriptions + self.create = _legacy_response.to_raw_response_wrapper( transcriptions.create, ) @@ -206,6 +208,8 @@ def __init__(self, transcriptions: Transcriptions) -> None: class AsyncTranscriptionsWithRawResponse: def __init__(self, transcriptions: AsyncTranscriptions) -> None: + self._transcriptions = transcriptions + self.create = _legacy_response.async_to_raw_response_wrapper( transcriptions.create, ) @@ -213,6 +217,8 @@ def __init__(self, transcriptions: AsyncTranscriptions) -> None: class TranscriptionsWithStreamingResponse: def __init__(self, transcriptions: Transcriptions) -> None: + self._transcriptions = transcriptions + self.create = to_streamed_response_wrapper( transcriptions.create, ) @@ -220,6 +226,8 @@ def __init__(self, transcriptions: Transcriptions) -> None: class AsyncTranscriptionsWithStreamingResponse: def __init__(self, transcriptions: AsyncTranscriptions) -> None: + self._transcriptions = transcriptions + self.create = async_to_streamed_response_wrapper( transcriptions.create, ) diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 333abfb4cf..d6cbc75886 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -185,6 +185,8 @@ async def create( class TranslationsWithRawResponse: def __init__(self, translations: Translations) -> None: + self._translations = translations + self.create = _legacy_response.to_raw_response_wrapper( translations.create, ) @@ -192,6 +194,8 @@ def __init__(self, translations: Translations) -> None: class AsyncTranslationsWithRawResponse: def __init__(self, translations: AsyncTranslations) -> None: + self._translations = translations + self.create = _legacy_response.async_to_raw_response_wrapper( translations.create, ) @@ -199,6 +203,8 @@ def __init__(self, translations: AsyncTranslations) -> None: class TranslationsWithStreamingResponse: def __init__(self, translations: Translations) -> None: + self._translations = translations + self.create = to_streamed_response_wrapper( translations.create, ) @@ -206,6 +212,8 @@ def __init__(self, translations: Translations) -> None: class AsyncTranslationsWithStreamingResponse: def __init__(self, translations: AsyncTranslations) -> None: + self._translations = translations + self.create = async_to_streamed_response_wrapper( translations.create, ) diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index a40acfb323..3a2418ad90 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -645,7 +645,7 @@ async def delete( class AssistantsWithRawResponse: def __init__(self, assistants: Assistants) -> None: - self.files = FilesWithRawResponse(assistants.files) + self._assistants = assistants self.create = _legacy_response.to_raw_response_wrapper( assistants.create, @@ -663,10 +663,14 @@ def __init__(self, assistants: Assistants) -> None: assistants.delete, ) + @cached_property + def files(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self._assistants.files) + class AsyncAssistantsWithRawResponse: def __init__(self, assistants: AsyncAssistants) -> None: - self.files = AsyncFilesWithRawResponse(assistants.files) + self._assistants = assistants self.create = _legacy_response.async_to_raw_response_wrapper( assistants.create, @@ -684,10 +688,14 @@ def __init__(self, assistants: AsyncAssistants) -> None: assistants.delete, ) + @cached_property + def files(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self._assistants.files) + class AssistantsWithStreamingResponse: def __init__(self, assistants: Assistants) -> None: - self.files = FilesWithStreamingResponse(assistants.files) + self._assistants = assistants self.create = to_streamed_response_wrapper( assistants.create, @@ -705,10 +713,14 @@ def __init__(self, assistants: Assistants) -> None: assistants.delete, ) + @cached_property + def files(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self._assistants.files) + class AsyncAssistantsWithStreamingResponse: def __init__(self, assistants: AsyncAssistants) -> None: - self.files = AsyncFilesWithStreamingResponse(assistants.files) + self._assistants = assistants self.create = async_to_streamed_response_wrapper( assistants.create, @@ -725,3 +737,7 @@ def __init__(self, assistants: AsyncAssistants) -> None: self.delete = async_to_streamed_response_wrapper( assistants.delete, ) + + @cached_property + def files(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self._assistants.files) diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index 12247044c4..c21465036a 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -410,6 +410,8 @@ async def delete( class FilesWithRawResponse: def __init__(self, files: Files) -> None: + self._files = files + self.create = _legacy_response.to_raw_response_wrapper( files.create, ) @@ -426,6 +428,8 @@ def __init__(self, files: Files) -> None: class AsyncFilesWithRawResponse: def __init__(self, files: AsyncFiles) -> None: + self._files = files + self.create = _legacy_response.async_to_raw_response_wrapper( files.create, ) @@ -442,6 +446,8 @@ def __init__(self, files: AsyncFiles) -> None: class FilesWithStreamingResponse: def __init__(self, files: Files) -> None: + self._files = files + self.create = to_streamed_response_wrapper( files.create, ) @@ -458,6 +464,8 @@ def __init__(self, files: Files) -> None: class AsyncFilesWithStreamingResponse: def __init__(self, files: AsyncFiles) -> None: + self._files = files + self.create = async_to_streamed_response_wrapper( files.create, ) diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index b11a706d5d..7081cff305 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -64,23 +64,51 @@ def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: - self.assistants = AssistantsWithRawResponse(beta.assistants) - self.threads = ThreadsWithRawResponse(beta.threads) + self._beta = beta + + @cached_property + def assistants(self) -> AssistantsWithRawResponse: + return AssistantsWithRawResponse(self._beta.assistants) + + @cached_property + def threads(self) -> ThreadsWithRawResponse: + return ThreadsWithRawResponse(self._beta.threads) class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: - self.assistants = AsyncAssistantsWithRawResponse(beta.assistants) - self.threads = AsyncThreadsWithRawResponse(beta.threads) + self._beta = beta + + @cached_property + def assistants(self) -> AsyncAssistantsWithRawResponse: + return AsyncAssistantsWithRawResponse(self._beta.assistants) + + @cached_property + def threads(self) -> AsyncThreadsWithRawResponse: + return AsyncThreadsWithRawResponse(self._beta.threads) class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: - self.assistants = AssistantsWithStreamingResponse(beta.assistants) - self.threads = ThreadsWithStreamingResponse(beta.threads) + self._beta = beta + + @cached_property + def assistants(self) -> AssistantsWithStreamingResponse: + return AssistantsWithStreamingResponse(self._beta.assistants) + + @cached_property + def threads(self) -> ThreadsWithStreamingResponse: + return ThreadsWithStreamingResponse(self._beta.threads) class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: - self.assistants = AsyncAssistantsWithStreamingResponse(beta.assistants) - self.threads = AsyncThreadsWithStreamingResponse(beta.threads) + self._beta = beta + + @cached_property + def assistants(self) -> AsyncAssistantsWithStreamingResponse: + return AsyncAssistantsWithStreamingResponse(self._beta.assistants) + + @cached_property + def threads(self) -> AsyncThreadsWithStreamingResponse: + return AsyncThreadsWithStreamingResponse(self._beta.threads) diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index 8b6c4581d0..fc8b894d72 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -266,6 +266,8 @@ def list( class FilesWithRawResponse: def __init__(self, files: Files) -> None: + self._files = files + self.retrieve = _legacy_response.to_raw_response_wrapper( files.retrieve, ) @@ -276,6 +278,8 @@ def __init__(self, files: Files) -> None: class AsyncFilesWithRawResponse: def __init__(self, files: AsyncFiles) -> None: + self._files = files + self.retrieve = _legacy_response.async_to_raw_response_wrapper( files.retrieve, ) @@ -286,6 +290,8 @@ def __init__(self, files: AsyncFiles) -> None: class FilesWithStreamingResponse: def __init__(self, files: Files) -> None: + self._files = files + self.retrieve = to_streamed_response_wrapper( files.retrieve, ) @@ -296,6 +302,8 @@ def __init__(self, files: Files) -> None: class AsyncFilesWithStreamingResponse: def __init__(self, files: AsyncFiles) -> None: + self._files = files + self.retrieve = async_to_streamed_response_wrapper( files.retrieve, ) diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index f5a17f902f..c95cdd5d00 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -481,7 +481,7 @@ def list( class MessagesWithRawResponse: def __init__(self, messages: Messages) -> None: - self.files = FilesWithRawResponse(messages.files) + self._messages = messages self.create = _legacy_response.to_raw_response_wrapper( messages.create, @@ -496,10 +496,14 @@ def __init__(self, messages: Messages) -> None: messages.list, ) + @cached_property + def files(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self._messages.files) + class AsyncMessagesWithRawResponse: def __init__(self, messages: AsyncMessages) -> None: - self.files = AsyncFilesWithRawResponse(messages.files) + self._messages = messages self.create = _legacy_response.async_to_raw_response_wrapper( messages.create, @@ -514,10 +518,14 @@ def __init__(self, messages: AsyncMessages) -> None: messages.list, ) + @cached_property + def files(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self._messages.files) + class MessagesWithStreamingResponse: def __init__(self, messages: Messages) -> None: - self.files = FilesWithStreamingResponse(messages.files) + self._messages = messages self.create = to_streamed_response_wrapper( messages.create, @@ -532,10 +540,14 @@ def __init__(self, messages: Messages) -> None: messages.list, ) + @cached_property + def files(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self._messages.files) + class AsyncMessagesWithStreamingResponse: def __init__(self, messages: AsyncMessages) -> None: - self.files = AsyncFilesWithStreamingResponse(messages.files) + self._messages = messages self.create = async_to_streamed_response_wrapper( messages.create, @@ -549,3 +561,7 @@ def __init__(self, messages: AsyncMessages) -> None: self.list = async_to_streamed_response_wrapper( messages.list, ) + + @cached_property + def files(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self._messages.files) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index ac7a1b3330..0ed48b4792 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -681,7 +681,7 @@ async def submit_tool_outputs( class RunsWithRawResponse: def __init__(self, runs: Runs) -> None: - self.steps = StepsWithRawResponse(runs.steps) + self._runs = runs self.create = _legacy_response.to_raw_response_wrapper( runs.create, @@ -702,10 +702,14 @@ def __init__(self, runs: Runs) -> None: runs.submit_tool_outputs, ) + @cached_property + def steps(self) -> StepsWithRawResponse: + return StepsWithRawResponse(self._runs.steps) + class AsyncRunsWithRawResponse: def __init__(self, runs: AsyncRuns) -> None: - self.steps = AsyncStepsWithRawResponse(runs.steps) + self._runs = runs self.create = _legacy_response.async_to_raw_response_wrapper( runs.create, @@ -726,10 +730,14 @@ def __init__(self, runs: AsyncRuns) -> None: runs.submit_tool_outputs, ) + @cached_property + def steps(self) -> AsyncStepsWithRawResponse: + return AsyncStepsWithRawResponse(self._runs.steps) + class RunsWithStreamingResponse: def __init__(self, runs: Runs) -> None: - self.steps = StepsWithStreamingResponse(runs.steps) + self._runs = runs self.create = to_streamed_response_wrapper( runs.create, @@ -750,10 +758,14 @@ def __init__(self, runs: Runs) -> None: runs.submit_tool_outputs, ) + @cached_property + def steps(self) -> StepsWithStreamingResponse: + return StepsWithStreamingResponse(self._runs.steps) + class AsyncRunsWithStreamingResponse: def __init__(self, runs: AsyncRuns) -> None: - self.steps = AsyncStepsWithStreamingResponse(runs.steps) + self._runs = runs self.create = async_to_streamed_response_wrapper( runs.create, @@ -773,3 +785,7 @@ def __init__(self, runs: AsyncRuns) -> None: self.submit_tool_outputs = async_to_streamed_response_wrapper( runs.submit_tool_outputs, ) + + @cached_property + def steps(self) -> AsyncStepsWithStreamingResponse: + return AsyncStepsWithStreamingResponse(self._runs.steps) diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 9b1df10652..539745a594 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -264,6 +264,8 @@ def list( class StepsWithRawResponse: def __init__(self, steps: Steps) -> None: + self._steps = steps + self.retrieve = _legacy_response.to_raw_response_wrapper( steps.retrieve, ) @@ -274,6 +276,8 @@ def __init__(self, steps: Steps) -> None: class AsyncStepsWithRawResponse: def __init__(self, steps: AsyncSteps) -> None: + self._steps = steps + self.retrieve = _legacy_response.async_to_raw_response_wrapper( steps.retrieve, ) @@ -284,6 +288,8 @@ def __init__(self, steps: AsyncSteps) -> None: class StepsWithStreamingResponse: def __init__(self, steps: Steps) -> None: + self._steps = steps + self.retrieve = to_streamed_response_wrapper( steps.retrieve, ) @@ -294,6 +300,8 @@ def __init__(self, steps: Steps) -> None: class AsyncStepsWithStreamingResponse: def __init__(self, steps: AsyncSteps) -> None: + self._steps = steps + self.retrieve = async_to_streamed_response_wrapper( steps.retrieve, ) diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index d885404f59..0372ae2f66 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -537,8 +537,7 @@ async def create_and_run( class ThreadsWithRawResponse: def __init__(self, threads: Threads) -> None: - self.runs = RunsWithRawResponse(threads.runs) - self.messages = MessagesWithRawResponse(threads.messages) + self._threads = threads self.create = _legacy_response.to_raw_response_wrapper( threads.create, @@ -556,11 +555,18 @@ def __init__(self, threads: Threads) -> None: threads.create_and_run, ) + @cached_property + def runs(self) -> RunsWithRawResponse: + return RunsWithRawResponse(self._threads.runs) + + @cached_property + def messages(self) -> MessagesWithRawResponse: + return MessagesWithRawResponse(self._threads.messages) + class AsyncThreadsWithRawResponse: def __init__(self, threads: AsyncThreads) -> None: - self.runs = AsyncRunsWithRawResponse(threads.runs) - self.messages = AsyncMessagesWithRawResponse(threads.messages) + self._threads = threads self.create = _legacy_response.async_to_raw_response_wrapper( threads.create, @@ -578,11 +584,18 @@ def __init__(self, threads: AsyncThreads) -> None: threads.create_and_run, ) + @cached_property + def runs(self) -> AsyncRunsWithRawResponse: + return AsyncRunsWithRawResponse(self._threads.runs) + + @cached_property + def messages(self) -> AsyncMessagesWithRawResponse: + return AsyncMessagesWithRawResponse(self._threads.messages) + class ThreadsWithStreamingResponse: def __init__(self, threads: Threads) -> None: - self.runs = RunsWithStreamingResponse(threads.runs) - self.messages = MessagesWithStreamingResponse(threads.messages) + self._threads = threads self.create = to_streamed_response_wrapper( threads.create, @@ -600,11 +613,18 @@ def __init__(self, threads: Threads) -> None: threads.create_and_run, ) + @cached_property + def runs(self) -> RunsWithStreamingResponse: + return RunsWithStreamingResponse(self._threads.runs) + + @cached_property + def messages(self) -> MessagesWithStreamingResponse: + return MessagesWithStreamingResponse(self._threads.messages) + class AsyncThreadsWithStreamingResponse: def __init__(self, threads: AsyncThreads) -> None: - self.runs = AsyncRunsWithStreamingResponse(threads.runs) - self.messages = AsyncMessagesWithStreamingResponse(threads.messages) + self._threads = threads self.create = async_to_streamed_response_wrapper( threads.create, @@ -621,3 +641,11 @@ def __init__(self, threads: AsyncThreads) -> None: self.create_and_run = async_to_streamed_response_wrapper( threads.create_and_run, ) + + @cached_property + def runs(self) -> AsyncRunsWithStreamingResponse: + return AsyncRunsWithStreamingResponse(self._threads.runs) + + @cached_property + def messages(self) -> AsyncMessagesWithStreamingResponse: + return AsyncMessagesWithStreamingResponse(self._threads.messages) diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index 467a5e401b..b6effa4e63 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -46,19 +46,35 @@ def with_streaming_response(self) -> AsyncChatWithStreamingResponse: class ChatWithRawResponse: def __init__(self, chat: Chat) -> None: - self.completions = CompletionsWithRawResponse(chat.completions) + self._chat = chat + + @cached_property + def completions(self) -> CompletionsWithRawResponse: + return CompletionsWithRawResponse(self._chat.completions) class AsyncChatWithRawResponse: def __init__(self, chat: AsyncChat) -> None: - self.completions = AsyncCompletionsWithRawResponse(chat.completions) + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsWithRawResponse: + return AsyncCompletionsWithRawResponse(self._chat.completions) class ChatWithStreamingResponse: def __init__(self, chat: Chat) -> None: - self.completions = CompletionsWithStreamingResponse(chat.completions) + self._chat = chat + + @cached_property + def completions(self) -> CompletionsWithStreamingResponse: + return CompletionsWithStreamingResponse(self._chat.completions) class AsyncChatWithStreamingResponse: def __init__(self, chat: AsyncChat) -> None: - self.completions = AsyncCompletionsWithStreamingResponse(chat.completions) + self._chat = chat + + @cached_property + def completions(self) -> AsyncCompletionsWithStreamingResponse: + return AsyncCompletionsWithStreamingResponse(self._chat.completions) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 53645a9eb9..f461161ab7 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -1335,6 +1335,8 @@ async def create( class CompletionsWithRawResponse: def __init__(self, completions: Completions) -> None: + self._completions = completions + self.create = _legacy_response.to_raw_response_wrapper( completions.create, ) @@ -1342,6 +1344,8 @@ def __init__(self, completions: Completions) -> None: class AsyncCompletionsWithRawResponse: def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + self.create = _legacy_response.async_to_raw_response_wrapper( completions.create, ) @@ -1349,6 +1353,8 @@ def __init__(self, completions: AsyncCompletions) -> None: class CompletionsWithStreamingResponse: def __init__(self, completions: Completions) -> None: + self._completions = completions + self.create = to_streamed_response_wrapper( completions.create, ) @@ -1356,6 +1362,8 @@ def __init__(self, completions: Completions) -> None: class AsyncCompletionsWithStreamingResponse: def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + self.create = async_to_streamed_response_wrapper( completions.create, ) diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 43a9947524..3d2e10230a 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -1052,6 +1052,8 @@ async def create( class CompletionsWithRawResponse: def __init__(self, completions: Completions) -> None: + self._completions = completions + self.create = _legacy_response.to_raw_response_wrapper( completions.create, ) @@ -1059,6 +1061,8 @@ def __init__(self, completions: Completions) -> None: class AsyncCompletionsWithRawResponse: def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + self.create = _legacy_response.async_to_raw_response_wrapper( completions.create, ) @@ -1066,6 +1070,8 @@ def __init__(self, completions: AsyncCompletions) -> None: class CompletionsWithStreamingResponse: def __init__(self, completions: Completions) -> None: + self._completions = completions + self.create = to_streamed_response_wrapper( completions.create, ) @@ -1073,6 +1079,8 @@ def __init__(self, completions: Completions) -> None: class AsyncCompletionsWithStreamingResponse: def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + self.create = async_to_streamed_response_wrapper( completions.create, ) diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 49ce0f2fc8..5bc7ed855e 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -217,6 +217,8 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: class EmbeddingsWithRawResponse: def __init__(self, embeddings: Embeddings) -> None: + self._embeddings = embeddings + self.create = _legacy_response.to_raw_response_wrapper( embeddings.create, ) @@ -224,6 +226,8 @@ def __init__(self, embeddings: Embeddings) -> None: class AsyncEmbeddingsWithRawResponse: def __init__(self, embeddings: AsyncEmbeddings) -> None: + self._embeddings = embeddings + self.create = _legacy_response.async_to_raw_response_wrapper( embeddings.create, ) @@ -231,6 +235,8 @@ def __init__(self, embeddings: AsyncEmbeddings) -> None: class EmbeddingsWithStreamingResponse: def __init__(self, embeddings: Embeddings) -> None: + self._embeddings = embeddings + self.create = to_streamed_response_wrapper( embeddings.create, ) @@ -238,6 +244,8 @@ def __init__(self, embeddings: Embeddings) -> None: class AsyncEmbeddingsWithStreamingResponse: def __init__(self, embeddings: AsyncEmbeddings) -> None: + self._embeddings = embeddings + self.create = async_to_streamed_response_wrapper( embeddings.create, ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index ff924340ac..58a2a217c7 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -580,6 +580,8 @@ async def wait_for_processing( class FilesWithRawResponse: def __init__(self, files: Files) -> None: + self._files = files + self.create = _legacy_response.to_raw_response_wrapper( files.create, ) @@ -604,6 +606,8 @@ def __init__(self, files: Files) -> None: class AsyncFilesWithRawResponse: def __init__(self, files: AsyncFiles) -> None: + self._files = files + self.create = _legacy_response.async_to_raw_response_wrapper( files.create, ) @@ -628,6 +632,8 @@ def __init__(self, files: AsyncFiles) -> None: class FilesWithStreamingResponse: def __init__(self, files: Files) -> None: + self._files = files + self.create = to_streamed_response_wrapper( files.create, ) @@ -653,6 +659,8 @@ def __init__(self, files: Files) -> None: class AsyncFilesWithStreamingResponse: def __init__(self, files: AsyncFiles) -> None: + self._files = files + self.create = async_to_streamed_response_wrapper( files.create, ) diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index 197d46fb83..33b25baec9 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -46,19 +46,35 @@ def with_streaming_response(self) -> AsyncFineTuningWithStreamingResponse: class FineTuningWithRawResponse: def __init__(self, fine_tuning: FineTuning) -> None: - self.jobs = JobsWithRawResponse(fine_tuning.jobs) + self._fine_tuning = fine_tuning + + @cached_property + def jobs(self) -> JobsWithRawResponse: + return JobsWithRawResponse(self._fine_tuning.jobs) class AsyncFineTuningWithRawResponse: def __init__(self, fine_tuning: AsyncFineTuning) -> None: - self.jobs = AsyncJobsWithRawResponse(fine_tuning.jobs) + self._fine_tuning = fine_tuning + + @cached_property + def jobs(self) -> AsyncJobsWithRawResponse: + return AsyncJobsWithRawResponse(self._fine_tuning.jobs) class FineTuningWithStreamingResponse: def __init__(self, fine_tuning: FineTuning) -> None: - self.jobs = JobsWithStreamingResponse(fine_tuning.jobs) + self._fine_tuning = fine_tuning + + @cached_property + def jobs(self) -> JobsWithStreamingResponse: + return JobsWithStreamingResponse(self._fine_tuning.jobs) class AsyncFineTuningWithStreamingResponse: def __init__(self, fine_tuning: AsyncFineTuning) -> None: - self.jobs = AsyncJobsWithStreamingResponse(fine_tuning.jobs) + self._fine_tuning = fine_tuning + + @cached_property + def jobs(self) -> AsyncJobsWithStreamingResponse: + return AsyncJobsWithStreamingResponse(self._fine_tuning.jobs) diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 208591fa47..6b59932982 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -553,6 +553,8 @@ def list_events( class JobsWithRawResponse: def __init__(self, jobs: Jobs) -> None: + self._jobs = jobs + self.create = _legacy_response.to_raw_response_wrapper( jobs.create, ) @@ -572,6 +574,8 @@ def __init__(self, jobs: Jobs) -> None: class AsyncJobsWithRawResponse: def __init__(self, jobs: AsyncJobs) -> None: + self._jobs = jobs + self.create = _legacy_response.async_to_raw_response_wrapper( jobs.create, ) @@ -591,6 +595,8 @@ def __init__(self, jobs: AsyncJobs) -> None: class JobsWithStreamingResponse: def __init__(self, jobs: Jobs) -> None: + self._jobs = jobs + self.create = to_streamed_response_wrapper( jobs.create, ) @@ -610,6 +616,8 @@ def __init__(self, jobs: Jobs) -> None: class AsyncJobsWithStreamingResponse: def __init__(self, jobs: AsyncJobs) -> None: + self._jobs = jobs + self.create = async_to_streamed_response_wrapper( jobs.create, ) diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index a3eb98574e..91530e47ca 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -518,6 +518,8 @@ async def generate( class ImagesWithRawResponse: def __init__(self, images: Images) -> None: + self._images = images + self.create_variation = _legacy_response.to_raw_response_wrapper( images.create_variation, ) @@ -531,6 +533,8 @@ def __init__(self, images: Images) -> None: class AsyncImagesWithRawResponse: def __init__(self, images: AsyncImages) -> None: + self._images = images + self.create_variation = _legacy_response.async_to_raw_response_wrapper( images.create_variation, ) @@ -544,6 +548,8 @@ def __init__(self, images: AsyncImages) -> None: class ImagesWithStreamingResponse: def __init__(self, images: Images) -> None: + self._images = images + self.create_variation = to_streamed_response_wrapper( images.create_variation, ) @@ -557,6 +563,8 @@ def __init__(self, images: Images) -> None: class AsyncImagesWithStreamingResponse: def __init__(self, images: AsyncImages) -> None: + self._images = images + self.create_variation = async_to_streamed_response_wrapper( images.create_variation, ) diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index e4a0d84810..3536f083d2 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -225,6 +225,8 @@ async def delete( class ModelsWithRawResponse: def __init__(self, models: Models) -> None: + self._models = models + self.retrieve = _legacy_response.to_raw_response_wrapper( models.retrieve, ) @@ -238,6 +240,8 @@ def __init__(self, models: Models) -> None: class AsyncModelsWithRawResponse: def __init__(self, models: AsyncModels) -> None: + self._models = models + self.retrieve = _legacy_response.async_to_raw_response_wrapper( models.retrieve, ) @@ -251,6 +255,8 @@ def __init__(self, models: AsyncModels) -> None: class ModelsWithStreamingResponse: def __init__(self, models: Models) -> None: + self._models = models + self.retrieve = to_streamed_response_wrapper( models.retrieve, ) @@ -264,6 +270,8 @@ def __init__(self, models: Models) -> None: class AsyncModelsWithStreamingResponse: def __init__(self, models: AsyncModels) -> None: + self._models = models + self.retrieve = async_to_streamed_response_wrapper( models.retrieve, ) diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index e7681f6263..540d089071 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -143,6 +143,8 @@ async def create( class ModerationsWithRawResponse: def __init__(self, moderations: Moderations) -> None: + self._moderations = moderations + self.create = _legacy_response.to_raw_response_wrapper( moderations.create, ) @@ -150,6 +152,8 @@ def __init__(self, moderations: Moderations) -> None: class AsyncModerationsWithRawResponse: def __init__(self, moderations: AsyncModerations) -> None: + self._moderations = moderations + self.create = _legacy_response.async_to_raw_response_wrapper( moderations.create, ) @@ -157,6 +161,8 @@ def __init__(self, moderations: AsyncModerations) -> None: class ModerationsWithStreamingResponse: def __init__(self, moderations: Moderations) -> None: + self._moderations = moderations + self.create = to_streamed_response_wrapper( moderations.create, ) @@ -164,6 +170,8 @@ def __init__(self, moderations: Moderations) -> None: class AsyncModerationsWithStreamingResponse: def __init__(self, moderations: AsyncModerations) -> None: + self._moderations = moderations + self.create = async_to_streamed_response_wrapper( moderations.create, ) From 98d779fbfe97470c6ebd15b188ee9602ee5621bd Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Jan 2024 11:41:33 -0500 Subject: [PATCH 300/914] chore(internal): share client instances between all tests (#1088) --- tests/api_resources/audio/test_speech.py | 26 ++-- .../audio/test_transcriptions.py | 26 ++-- .../api_resources/audio/test_translations.py | 26 ++-- .../beta/assistants/test_files.py | 82 ++++++------ tests/api_resources/beta/test_assistants.py | 94 ++++++------- tests/api_resources/beta/test_threads.py | 94 ++++++------- .../beta/threads/messages/test_files.py | 52 ++++---- .../beta/threads/runs/test_steps.py | 52 ++++---- .../beta/threads/test_messages.py | 90 ++++++------- tests/api_resources/beta/threads/test_runs.py | 126 +++++++++--------- tests/api_resources/chat/test_completions.py | 42 +++--- tests/api_resources/fine_tuning/test_jobs.py | 94 ++++++------- tests/api_resources/test_completions.py | 42 +++--- tests/api_resources/test_embeddings.py | 26 ++-- tests/api_resources/test_files.py | 102 +++++++------- tests/api_resources/test_images.py | 58 ++++---- tests/api_resources/test_models.py | 54 ++++---- tests/api_resources/test_moderations.py | 26 ++-- tests/conftest.py | 35 ++++- 19 files changed, 536 insertions(+), 611 deletions(-) diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index a689c0d220..b1c7f79b1e 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -12,18 +12,14 @@ import openai._legacy_response as _legacy_response from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI # pyright: reportDeprecated=false base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestSpeech: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize @pytest.mark.respx(base_url=base_url) @@ -86,15 +82,13 @@ def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) class TestAsyncSpeech: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize @pytest.mark.respx(base_url=base_url) - async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - speech = await client.audio.speech.create( + speech = await async_client.audio.speech.create( input="string", model="string", voice="alloy", @@ -104,9 +98,9 @@ async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) @parametrize @pytest.mark.respx(base_url=base_url) - async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - speech = await client.audio.speech.create( + speech = await async_client.audio.speech.create( input="string", model="string", voice="alloy", @@ -118,10 +112,10 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mo @parametrize @pytest.mark.respx(base_url=base_url) - async def test_raw_response_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await client.audio.speech.with_raw_response.create( + response = await async_client.audio.speech.with_raw_response.create( input="string", model="string", voice="alloy", @@ -134,9 +128,9 @@ async def test_raw_response_create(self, client: AsyncOpenAI, respx_mock: MockRo @parametrize @pytest.mark.respx(base_url=base_url) - async def test_streaming_response_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - async with client.audio.speech.with_streaming_response.create( + async with async_client.audio.speech.with_streaming_response.create( input="string", model="string", voice="alloy", diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index 992adbabd9..d957871abc 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -9,17 +9,13 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.types.audio import Transcription base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestTranscriptions: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -69,21 +65,19 @@ def test_streaming_response_create(self, client: OpenAI) -> None: class TestAsyncTranscriptions: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - transcription = await client.audio.transcriptions.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + transcription = await async_client.audio.transcriptions.create( file=b"raw file contents", model="whisper-1", ) assert_matches_type(Transcription, transcription, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - transcription = await client.audio.transcriptions.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + transcription = await async_client.audio.transcriptions.create( file=b"raw file contents", model="whisper-1", language="string", @@ -94,8 +88,8 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(Transcription, transcription, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.audio.transcriptions.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.audio.transcriptions.with_raw_response.create( file=b"raw file contents", model="whisper-1", ) @@ -106,8 +100,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(Transcription, transcription, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.audio.transcriptions.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.audio.transcriptions.with_streaming_response.create( file=b"raw file contents", model="whisper-1", ) as response: diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index 913c443a79..72960c3249 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -9,17 +9,13 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.types.audio import Translation base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestTranslations: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -68,21 +64,19 @@ def test_streaming_response_create(self, client: OpenAI) -> None: class TestAsyncTranslations: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - translation = await client.audio.translations.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + translation = await async_client.audio.translations.create( file=b"raw file contents", model="whisper-1", ) assert_matches_type(Translation, translation, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - translation = await client.audio.translations.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + translation = await async_client.audio.translations.create( file=b"raw file contents", model="whisper-1", prompt="string", @@ -92,8 +86,8 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(Translation, translation, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.audio.translations.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.audio.translations.with_raw_response.create( file=b"raw file contents", model="whisper-1", ) @@ -104,8 +98,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(Translation, translation, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.audio.translations.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.audio.translations.with_streaming_response.create( file=b"raw file contents", model="whisper-1", ) as response: diff --git a/tests/api_resources/beta/assistants/test_files.py b/tests/api_resources/beta/assistants/test_files.py index 7db1368ccb..66e3e2efe6 100644 --- a/tests/api_resources/beta/assistants/test_files.py +++ b/tests/api_resources/beta/assistants/test_files.py @@ -9,18 +9,14 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.beta.assistants import AssistantFile, FileDeleteResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestFiles: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -211,21 +207,19 @@ def test_path_params_delete(self, client: OpenAI) -> None: class TestAsyncFiles: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - file = await client.beta.assistants.files.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.assistants.files.create( "file-abc123", file_id="string", ) assert_matches_type(AssistantFile, file, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.files.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.files.with_raw_response.create( "file-abc123", file_id="string", ) @@ -236,8 +230,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(AssistantFile, file, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.files.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.files.with_streaming_response.create( "file-abc123", file_id="string", ) as response: @@ -250,24 +244,24 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_create(self, client: AsyncOpenAI) -> None: + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await client.beta.assistants.files.with_raw_response.create( + await async_client.beta.assistants.files.with_raw_response.create( "", file_id="string", ) @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - file = await client.beta.assistants.files.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.assistants.files.retrieve( "string", assistant_id="string", ) assert_matches_type(AssistantFile, file, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.files.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.files.with_raw_response.retrieve( "string", assistant_id="string", ) @@ -278,8 +272,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(AssistantFile, file, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.files.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.files.with_streaming_response.retrieve( "string", assistant_id="string", ) as response: @@ -292,29 +286,29 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await client.beta.assistants.files.with_raw_response.retrieve( + await async_client.beta.assistants.files.with_raw_response.retrieve( "string", assistant_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await client.beta.assistants.files.with_raw_response.retrieve( + await async_client.beta.assistants.files.with_raw_response.retrieve( "", assistant_id="string", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - file = await client.beta.assistants.files.list( + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.assistants.files.list( "string", ) assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) @parametrize - async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: - file = await client.beta.assistants.files.list( + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.assistants.files.list( "string", after="string", before="string", @@ -324,8 +318,8 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.files.with_raw_response.list( + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.files.with_raw_response.list( "string", ) @@ -335,8 +329,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.files.with_streaming_response.list( + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.files.with_streaming_response.list( "string", ) as response: assert not response.is_closed @@ -348,23 +342,23 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_list(self, client: AsyncOpenAI) -> None: + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await client.beta.assistants.files.with_raw_response.list( + await async_client.beta.assistants.files.with_raw_response.list( "", ) @parametrize - async def test_method_delete(self, client: AsyncOpenAI) -> None: - file = await client.beta.assistants.files.delete( + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.assistants.files.delete( "string", assistant_id="string", ) assert_matches_type(FileDeleteResponse, file, path=["response"]) @parametrize - async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.files.with_raw_response.delete( + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.files.with_raw_response.delete( "string", assistant_id="string", ) @@ -375,8 +369,8 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(FileDeleteResponse, file, path=["response"]) @parametrize - async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.files.with_streaming_response.delete( + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.files.with_streaming_response.delete( "string", assistant_id="string", ) as response: @@ -389,15 +383,15 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await client.beta.assistants.files.with_raw_response.delete( + await async_client.beta.assistants.files.with_raw_response.delete( "string", assistant_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await client.beta.assistants.files.with_raw_response.delete( + await async_client.beta.assistants.files.with_raw_response.delete( "", assistant_id="string", ) diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index fa09769622..8db40bde93 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -9,7 +9,6 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.beta import ( Assistant, @@ -17,13 +16,10 @@ ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestAssistants: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -234,20 +230,18 @@ def test_path_params_delete(self, client: OpenAI) -> None: class TestAsyncAssistants: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - assistant = await client.beta.assistants.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + assistant = await async_client.beta.assistants.create( model="string", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - assistant = await client.beta.assistants.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + assistant = await async_client.beta.assistants.create( model="string", description="string", file_ids=["string", "string", "string"], @@ -259,8 +253,8 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(Assistant, assistant, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.with_raw_response.create( model="string", ) @@ -270,8 +264,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(Assistant, assistant, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.with_streaming_response.create( model="string", ) as response: assert not response.is_closed @@ -283,15 +277,15 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - assistant = await client.beta.assistants.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + assistant = await async_client.beta.assistants.retrieve( "string", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.with_raw_response.retrieve( "string", ) @@ -301,8 +295,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(Assistant, assistant, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.with_streaming_response.retrieve( "string", ) as response: assert not response.is_closed @@ -314,22 +308,22 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await client.beta.assistants.with_raw_response.retrieve( + await async_client.beta.assistants.with_raw_response.retrieve( "", ) @parametrize - async def test_method_update(self, client: AsyncOpenAI) -> None: - assistant = await client.beta.assistants.update( + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + assistant = await async_client.beta.assistants.update( "string", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize - async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: - assistant = await client.beta.assistants.update( + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + assistant = await async_client.beta.assistants.update( "string", description="string", file_ids=["string", "string", "string"], @@ -342,8 +336,8 @@ async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(Assistant, assistant, path=["response"]) @parametrize - async def test_raw_response_update(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.with_raw_response.update( + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.with_raw_response.update( "string", ) @@ -353,8 +347,8 @@ async def test_raw_response_update(self, client: AsyncOpenAI) -> None: assert_matches_type(Assistant, assistant, path=["response"]) @parametrize - async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.with_streaming_response.update( + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.with_streaming_response.update( "string", ) as response: assert not response.is_closed @@ -366,20 +360,20 @@ async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_update(self, client: AsyncOpenAI) -> None: + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await client.beta.assistants.with_raw_response.update( + await async_client.beta.assistants.with_raw_response.update( "", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - assistant = await client.beta.assistants.list() + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + assistant = await async_client.beta.assistants.list() assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) @parametrize - async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: - assistant = await client.beta.assistants.list( + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + assistant = await async_client.beta.assistants.list( after="string", before="string", limit=0, @@ -388,8 +382,8 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.with_raw_response.list() + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -397,8 +391,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.with_streaming_response.list() as response: + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -408,15 +402,15 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_delete(self, client: AsyncOpenAI) -> None: - assistant = await client.beta.assistants.delete( + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + assistant = await async_client.beta.assistants.delete( "string", ) assert_matches_type(AssistantDeleted, assistant, path=["response"]) @parametrize - async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: - response = await client.beta.assistants.with_raw_response.delete( + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.assistants.with_raw_response.delete( "string", ) @@ -426,8 +420,8 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(AssistantDeleted, assistant, path=["response"]) @parametrize - async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: - async with client.beta.assistants.with_streaming_response.delete( + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.assistants.with_streaming_response.delete( "string", ) as response: assert not response.is_closed @@ -439,8 +433,8 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await client.beta.assistants.with_raw_response.delete( + await async_client.beta.assistants.with_raw_response.delete( "", ) diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index ba55cc85da..5b347de1f0 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -9,7 +9,6 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.types.beta import ( Thread, ThreadDeleted, @@ -17,13 +16,10 @@ from openai.types.beta.threads import Run base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestThreads: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -266,18 +262,16 @@ def test_streaming_response_create_and_run(self, client: OpenAI) -> None: class TestAsyncThreads: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - thread = await client.beta.threads.create() + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.threads.create() assert_matches_type(Thread, thread, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - thread = await client.beta.threads.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.threads.create( messages=[ { "role": "user", @@ -303,8 +297,8 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(Thread, thread, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.with_raw_response.create() + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -312,8 +306,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(Thread, thread, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.with_streaming_response.create() as response: + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -323,15 +317,15 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - thread = await client.beta.threads.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.threads.retrieve( "string", ) assert_matches_type(Thread, thread, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.with_raw_response.retrieve( "string", ) @@ -341,8 +335,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(Thread, thread, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.with_streaming_response.retrieve( "string", ) as response: assert not response.is_closed @@ -354,30 +348,30 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.with_raw_response.retrieve( + await async_client.beta.threads.with_raw_response.retrieve( "", ) @parametrize - async def test_method_update(self, client: AsyncOpenAI) -> None: - thread = await client.beta.threads.update( + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.threads.update( "string", ) assert_matches_type(Thread, thread, path=["response"]) @parametrize - async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: - thread = await client.beta.threads.update( + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.threads.update( "string", metadata={}, ) assert_matches_type(Thread, thread, path=["response"]) @parametrize - async def test_raw_response_update(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.with_raw_response.update( + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.with_raw_response.update( "string", ) @@ -387,8 +381,8 @@ async def test_raw_response_update(self, client: AsyncOpenAI) -> None: assert_matches_type(Thread, thread, path=["response"]) @parametrize - async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.with_streaming_response.update( + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.with_streaming_response.update( "string", ) as response: assert not response.is_closed @@ -400,22 +394,22 @@ async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_update(self, client: AsyncOpenAI) -> None: + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.with_raw_response.update( + await async_client.beta.threads.with_raw_response.update( "", ) @parametrize - async def test_method_delete(self, client: AsyncOpenAI) -> None: - thread = await client.beta.threads.delete( + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.threads.delete( "string", ) assert_matches_type(ThreadDeleted, thread, path=["response"]) @parametrize - async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.with_raw_response.delete( + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.with_raw_response.delete( "string", ) @@ -425,8 +419,8 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(ThreadDeleted, thread, path=["response"]) @parametrize - async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.with_streaming_response.delete( + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.with_streaming_response.delete( "string", ) as response: assert not response.is_closed @@ -438,22 +432,22 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.with_raw_response.delete( + await async_client.beta.threads.with_raw_response.delete( "", ) @parametrize - async def test_method_create_and_run(self, client: AsyncOpenAI) -> None: - thread = await client.beta.threads.create_and_run( + async def test_method_create_and_run(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.threads.create_and_run( assistant_id="string", ) assert_matches_type(Run, thread, path=["response"]) @parametrize - async def test_method_create_and_run_with_all_params(self, client: AsyncOpenAI) -> None: - thread = await client.beta.threads.create_and_run( + async def test_method_create_and_run_with_all_params(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.threads.create_and_run( assistant_id="string", instructions="string", metadata={}, @@ -486,8 +480,8 @@ async def test_method_create_and_run_with_all_params(self, client: AsyncOpenAI) assert_matches_type(Run, thread, path=["response"]) @parametrize - async def test_raw_response_create_and_run(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.with_raw_response.create_and_run( + async def test_raw_response_create_and_run(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.with_raw_response.create_and_run( assistant_id="string", ) @@ -497,8 +491,8 @@ async def test_raw_response_create_and_run(self, client: AsyncOpenAI) -> None: assert_matches_type(Run, thread, path=["response"]) @parametrize - async def test_streaming_response_create_and_run(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.with_streaming_response.create_and_run( + async def test_streaming_response_create_and_run(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.with_streaming_response.create_and_run( assistant_id="string", ) as response: assert not response.is_closed diff --git a/tests/api_resources/beta/threads/messages/test_files.py b/tests/api_resources/beta/threads/messages/test_files.py index 2d248642e9..4d0613fd2f 100644 --- a/tests/api_resources/beta/threads/messages/test_files.py +++ b/tests/api_resources/beta/threads/messages/test_files.py @@ -9,18 +9,14 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.beta.threads.messages import MessageFile base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestFiles: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: OpenAI) -> None: @@ -144,13 +140,11 @@ def test_path_params_list(self, client: OpenAI) -> None: class TestAsyncFiles: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - file = await client.beta.threads.messages.files.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.threads.messages.files.retrieve( "file-abc123", thread_id="thread_abc123", message_id="msg_abc123", @@ -158,8 +152,8 @@ async def test_method_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(MessageFile, file, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.messages.files.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.messages.files.with_raw_response.retrieve( "file-abc123", thread_id="thread_abc123", message_id="msg_abc123", @@ -171,8 +165,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(MessageFile, file, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.messages.files.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.messages.files.with_streaming_response.retrieve( "file-abc123", thread_id="thread_abc123", message_id="msg_abc123", @@ -186,39 +180,39 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.messages.files.with_raw_response.retrieve( + await async_client.beta.threads.messages.files.with_raw_response.retrieve( "file-abc123", thread_id="", message_id="msg_abc123", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - await client.beta.threads.messages.files.with_raw_response.retrieve( + await async_client.beta.threads.messages.files.with_raw_response.retrieve( "file-abc123", thread_id="thread_abc123", message_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await client.beta.threads.messages.files.with_raw_response.retrieve( + await async_client.beta.threads.messages.files.with_raw_response.retrieve( "", thread_id="thread_abc123", message_id="msg_abc123", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - file = await client.beta.threads.messages.files.list( + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.threads.messages.files.list( "string", thread_id="string", ) assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) @parametrize - async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: - file = await client.beta.threads.messages.files.list( + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.threads.messages.files.list( "string", thread_id="string", after="string", @@ -229,8 +223,8 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.messages.files.with_raw_response.list( + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.messages.files.with_raw_response.list( "string", thread_id="string", ) @@ -241,8 +235,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.messages.files.with_streaming_response.list( + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.messages.files.with_streaming_response.list( "string", thread_id="string", ) as response: @@ -255,15 +249,15 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_list(self, client: AsyncOpenAI) -> None: + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.messages.files.with_raw_response.list( + await async_client.beta.threads.messages.files.with_raw_response.list( "string", thread_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - await client.beta.threads.messages.files.with_raw_response.list( + await async_client.beta.threads.messages.files.with_raw_response.list( "", thread_id="string", ) diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py index 2ec164a535..c15848cd70 100644 --- a/tests/api_resources/beta/threads/runs/test_steps.py +++ b/tests/api_resources/beta/threads/runs/test_steps.py @@ -9,18 +9,14 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.beta.threads.runs import RunStep base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestSteps: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: OpenAI) -> None: @@ -144,13 +140,11 @@ def test_path_params_list(self, client: OpenAI) -> None: class TestAsyncSteps: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - step = await client.beta.threads.runs.steps.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + step = await async_client.beta.threads.runs.steps.retrieve( "string", thread_id="string", run_id="string", @@ -158,8 +152,8 @@ async def test_method_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(RunStep, step, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.runs.steps.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.steps.with_raw_response.retrieve( "string", thread_id="string", run_id="string", @@ -171,8 +165,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(RunStep, step, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.runs.steps.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.steps.with_streaming_response.retrieve( "string", thread_id="string", run_id="string", @@ -186,39 +180,39 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.runs.steps.with_raw_response.retrieve( + await async_client.beta.threads.runs.steps.with_raw_response.retrieve( "string", thread_id="", run_id="string", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): - await client.beta.threads.runs.steps.with_raw_response.retrieve( + await async_client.beta.threads.runs.steps.with_raw_response.retrieve( "string", thread_id="string", run_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `step_id` but received ''"): - await client.beta.threads.runs.steps.with_raw_response.retrieve( + await async_client.beta.threads.runs.steps.with_raw_response.retrieve( "", thread_id="string", run_id="string", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - step = await client.beta.threads.runs.steps.list( + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + step = await async_client.beta.threads.runs.steps.list( "string", thread_id="string", ) assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) @parametrize - async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: - step = await client.beta.threads.runs.steps.list( + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + step = await async_client.beta.threads.runs.steps.list( "string", thread_id="string", after="string", @@ -229,8 +223,8 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.runs.steps.with_raw_response.list( + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.steps.with_raw_response.list( "string", thread_id="string", ) @@ -241,8 +235,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.runs.steps.with_streaming_response.list( + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.steps.with_streaming_response.list( "string", thread_id="string", ) as response: @@ -255,15 +249,15 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_list(self, client: AsyncOpenAI) -> None: + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.runs.steps.with_raw_response.list( + await async_client.beta.threads.runs.steps.with_raw_response.list( "string", thread_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): - await client.beta.threads.runs.steps.with_raw_response.list( + await async_client.beta.threads.runs.steps.with_raw_response.list( "", thread_id="string", ) diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index 508e9b96c9..538d2f4c2a 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -9,18 +9,14 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.beta.threads import ThreadMessage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestMessages: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -235,13 +231,11 @@ def test_path_params_list(self, client: OpenAI) -> None: class TestAsyncMessages: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - message = await client.beta.threads.messages.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + message = await async_client.beta.threads.messages.create( "string", content="x", role="user", @@ -249,8 +243,8 @@ async def test_method_create(self, client: AsyncOpenAI) -> None: assert_matches_type(ThreadMessage, message, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - message = await client.beta.threads.messages.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + message = await async_client.beta.threads.messages.create( "string", content="x", role="user", @@ -260,8 +254,8 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(ThreadMessage, message, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.messages.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.messages.with_raw_response.create( "string", content="x", role="user", @@ -273,8 +267,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(ThreadMessage, message, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.messages.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.messages.with_streaming_response.create( "string", content="x", role="user", @@ -288,25 +282,25 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_create(self, client: AsyncOpenAI) -> None: + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.messages.with_raw_response.create( + await async_client.beta.threads.messages.with_raw_response.create( "", content="x", role="user", ) @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - message = await client.beta.threads.messages.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + message = await async_client.beta.threads.messages.retrieve( "string", thread_id="string", ) assert_matches_type(ThreadMessage, message, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.messages.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.messages.with_raw_response.retrieve( "string", thread_id="string", ) @@ -317,8 +311,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(ThreadMessage, message, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.messages.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.messages.with_streaming_response.retrieve( "string", thread_id="string", ) as response: @@ -331,30 +325,30 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.messages.with_raw_response.retrieve( + await async_client.beta.threads.messages.with_raw_response.retrieve( "string", thread_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - await client.beta.threads.messages.with_raw_response.retrieve( + await async_client.beta.threads.messages.with_raw_response.retrieve( "", thread_id="string", ) @parametrize - async def test_method_update(self, client: AsyncOpenAI) -> None: - message = await client.beta.threads.messages.update( + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + message = await async_client.beta.threads.messages.update( "string", thread_id="string", ) assert_matches_type(ThreadMessage, message, path=["response"]) @parametrize - async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: - message = await client.beta.threads.messages.update( + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + message = await async_client.beta.threads.messages.update( "string", thread_id="string", metadata={}, @@ -362,8 +356,8 @@ async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(ThreadMessage, message, path=["response"]) @parametrize - async def test_raw_response_update(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.messages.with_raw_response.update( + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.messages.with_raw_response.update( "string", thread_id="string", ) @@ -374,8 +368,8 @@ async def test_raw_response_update(self, client: AsyncOpenAI) -> None: assert_matches_type(ThreadMessage, message, path=["response"]) @parametrize - async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.messages.with_streaming_response.update( + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.messages.with_streaming_response.update( "string", thread_id="string", ) as response: @@ -388,29 +382,29 @@ async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_update(self, client: AsyncOpenAI) -> None: + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.messages.with_raw_response.update( + await async_client.beta.threads.messages.with_raw_response.update( "string", thread_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - await client.beta.threads.messages.with_raw_response.update( + await async_client.beta.threads.messages.with_raw_response.update( "", thread_id="string", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - message = await client.beta.threads.messages.list( + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + message = await async_client.beta.threads.messages.list( "string", ) assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) @parametrize - async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: - message = await client.beta.threads.messages.list( + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + message = await async_client.beta.threads.messages.list( "string", after="string", before="string", @@ -420,8 +414,8 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.messages.with_raw_response.list( + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.messages.with_raw_response.list( "string", ) @@ -431,8 +425,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.messages.with_streaming_response.list( + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.messages.with_streaming_response.list( "string", ) as response: assert not response.is_closed @@ -444,8 +438,8 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_list(self, client: AsyncOpenAI) -> None: + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.messages.with_raw_response.list( + await async_client.beta.threads.messages.with_raw_response.list( "", ) diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 66a9edd5c0..9e88d65eaf 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -9,20 +9,16 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.beta.threads import ( Run, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestRuns: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -336,21 +332,19 @@ def test_path_params_submit_tool_outputs(self, client: OpenAI) -> None: class TestAsyncRuns: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.create( "string", assistant_id="string", ) assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.create( "string", assistant_id="string", additional_instructions="string", @@ -362,8 +356,8 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.runs.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.with_raw_response.create( "string", assistant_id="string", ) @@ -374,8 +368,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.runs.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.with_streaming_response.create( "string", assistant_id="string", ) as response: @@ -388,24 +382,24 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_create(self, client: AsyncOpenAI) -> None: + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.runs.with_raw_response.create( + await async_client.beta.threads.runs.with_raw_response.create( "", assistant_id="string", ) @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.retrieve( "string", thread_id="string", ) assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.runs.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.with_raw_response.retrieve( "string", thread_id="string", ) @@ -416,8 +410,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.runs.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.with_streaming_response.retrieve( "string", thread_id="string", ) as response: @@ -430,30 +424,30 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.runs.with_raw_response.retrieve( + await async_client.beta.threads.runs.with_raw_response.retrieve( "string", thread_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): - await client.beta.threads.runs.with_raw_response.retrieve( + await async_client.beta.threads.runs.with_raw_response.retrieve( "", thread_id="string", ) @parametrize - async def test_method_update(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.update( + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.update( "string", thread_id="string", ) assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.update( + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.update( "string", thread_id="string", metadata={}, @@ -461,8 +455,8 @@ async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_raw_response_update(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.runs.with_raw_response.update( + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.with_raw_response.update( "string", thread_id="string", ) @@ -473,8 +467,8 @@ async def test_raw_response_update(self, client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.runs.with_streaming_response.update( + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.with_streaming_response.update( "string", thread_id="string", ) as response: @@ -487,29 +481,29 @@ async def test_streaming_response_update(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_update(self, client: AsyncOpenAI) -> None: + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.runs.with_raw_response.update( + await async_client.beta.threads.runs.with_raw_response.update( "string", thread_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): - await client.beta.threads.runs.with_raw_response.update( + await async_client.beta.threads.runs.with_raw_response.update( "", thread_id="string", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.list( + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.list( "string", ) assert_matches_type(AsyncCursorPage[Run], run, path=["response"]) @parametrize - async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.list( + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.list( "string", after="string", before="string", @@ -519,8 +513,8 @@ async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[Run], run, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.runs.with_raw_response.list( + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.with_raw_response.list( "string", ) @@ -530,8 +524,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[Run], run, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.runs.with_streaming_response.list( + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.with_streaming_response.list( "string", ) as response: assert not response.is_closed @@ -543,23 +537,23 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_list(self, client: AsyncOpenAI) -> None: + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.runs.with_raw_response.list( + await async_client.beta.threads.runs.with_raw_response.list( "", ) @parametrize - async def test_method_cancel(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.cancel( + async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.cancel( "string", thread_id="string", ) assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.runs.with_raw_response.cancel( + async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.with_raw_response.cancel( "string", thread_id="string", ) @@ -570,8 +564,8 @@ async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.runs.with_streaming_response.cancel( + async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.with_streaming_response.cancel( "string", thread_id="string", ) as response: @@ -584,22 +578,22 @@ async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_cancel(self, client: AsyncOpenAI) -> None: + async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.runs.with_raw_response.cancel( + await async_client.beta.threads.runs.with_raw_response.cancel( "string", thread_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): - await client.beta.threads.runs.with_raw_response.cancel( + await async_client.beta.threads.runs.with_raw_response.cancel( "", thread_id="string", ) @parametrize - async def test_method_submit_tool_outputs(self, client: AsyncOpenAI) -> None: - run = await client.beta.threads.runs.submit_tool_outputs( + async def test_method_submit_tool_outputs(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.submit_tool_outputs( "string", thread_id="string", tool_outputs=[{}, {}, {}], @@ -607,8 +601,8 @@ async def test_method_submit_tool_outputs(self, client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_raw_response_submit_tool_outputs(self, client: AsyncOpenAI) -> None: - response = await client.beta.threads.runs.with_raw_response.submit_tool_outputs( + async def test_raw_response_submit_tool_outputs(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( "string", thread_id="string", tool_outputs=[{}, {}, {}], @@ -620,8 +614,8 @@ async def test_raw_response_submit_tool_outputs(self, client: AsyncOpenAI) -> No assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_streaming_response_submit_tool_outputs(self, client: AsyncOpenAI) -> None: - async with client.beta.threads.runs.with_streaming_response.submit_tool_outputs( + async def test_streaming_response_submit_tool_outputs(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.with_streaming_response.submit_tool_outputs( "string", thread_id="string", tool_outputs=[{}, {}, {}], @@ -635,16 +629,16 @@ async def test_streaming_response_submit_tool_outputs(self, client: AsyncOpenAI) assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_submit_tool_outputs(self, client: AsyncOpenAI) -> None: + async def test_path_params_submit_tool_outputs(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await client.beta.threads.runs.with_raw_response.submit_tool_outputs( + await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( "string", thread_id="", tool_outputs=[{}, {}, {}], ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): - await client.beta.threads.runs.with_raw_response.submit_tool_outputs( + await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( "", thread_id="string", tool_outputs=[{}, {}, {}], diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 860ec80f48..4fa069ba2e 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -9,17 +9,13 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.types.chat import ChatCompletion base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestCompletions: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create_overload_1(self, client: OpenAI) -> None: @@ -249,13 +245,11 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: class TestAsyncCompletions: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create_overload_1(self, client: AsyncOpenAI) -> None: - completion = await client.chat.completions.create( + async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.chat.completions.create( messages=[ { "content": "string", @@ -267,8 +261,8 @@ async def test_method_create_overload_1(self, client: AsyncOpenAI) -> None: assert_matches_type(ChatCompletion, completion, path=["response"]) @parametrize - async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenAI) -> None: - completion = await client.chat.completions.create( + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.chat.completions.create( messages=[ { "content": "string", @@ -330,8 +324,8 @@ async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenA assert_matches_type(ChatCompletion, completion, path=["response"]) @parametrize - async def test_raw_response_create_overload_1(self, client: AsyncOpenAI) -> None: - response = await client.chat.completions.with_raw_response.create( + async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: + response = await async_client.chat.completions.with_raw_response.create( messages=[ { "content": "string", @@ -347,8 +341,8 @@ async def test_raw_response_create_overload_1(self, client: AsyncOpenAI) -> None assert_matches_type(ChatCompletion, completion, path=["response"]) @parametrize - async def test_streaming_response_create_overload_1(self, client: AsyncOpenAI) -> None: - async with client.chat.completions.with_streaming_response.create( + async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: + async with async_client.chat.completions.with_streaming_response.create( messages=[ { "content": "string", @@ -366,8 +360,8 @@ async def test_streaming_response_create_overload_1(self, client: AsyncOpenAI) - assert cast(Any, response.is_closed) is True @parametrize - async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: - completion_stream = await client.chat.completions.create( + async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: + completion_stream = await async_client.chat.completions.create( messages=[ { "content": "string", @@ -380,8 +374,8 @@ async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: await completion_stream.response.aclose() @parametrize - async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenAI) -> None: - completion_stream = await client.chat.completions.create( + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + completion_stream = await async_client.chat.completions.create( messages=[ { "content": "string", @@ -443,8 +437,8 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA await completion_stream.response.aclose() @parametrize - async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None: - response = await client.chat.completions.with_raw_response.create( + async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.chat.completions.with_raw_response.create( messages=[ { "content": "string", @@ -460,8 +454,8 @@ async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None await stream.close() @parametrize - async def test_streaming_response_create_overload_2(self, client: AsyncOpenAI) -> None: - async with client.chat.completions.with_streaming_response.create( + async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.chat.completions.with_streaming_response.create( messages=[ { "content": "string", diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 50c7278855..204cc3b1f5 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -9,7 +9,6 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.fine_tuning import ( FineTuningJob, @@ -17,13 +16,10 @@ ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestJobs: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -232,21 +228,19 @@ def test_path_params_list_events(self, client: OpenAI) -> None: class TestAsyncJobs: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - job = await client.fine_tuning.jobs.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.create( model="gpt-3.5-turbo", training_file="file-abc123", ) assert_matches_type(FineTuningJob, job, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - job = await client.fine_tuning.jobs.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.create( model="gpt-3.5-turbo", training_file="file-abc123", hyperparameters={ @@ -260,8 +254,8 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(FineTuningJob, job, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.fine_tuning.jobs.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.jobs.with_raw_response.create( model="gpt-3.5-turbo", training_file="file-abc123", ) @@ -272,8 +266,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(FineTuningJob, job, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.fine_tuning.jobs.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.jobs.with_streaming_response.create( model="gpt-3.5-turbo", training_file="file-abc123", ) as response: @@ -286,15 +280,15 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - job = await client.fine_tuning.jobs.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.retrieve( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) assert_matches_type(FineTuningJob, job, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.fine_tuning.jobs.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.jobs.with_raw_response.retrieve( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) @@ -304,8 +298,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(FineTuningJob, job, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.fine_tuning.jobs.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.jobs.with_streaming_response.retrieve( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) as response: assert not response.is_closed @@ -317,28 +311,28 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): - await client.fine_tuning.jobs.with_raw_response.retrieve( + await async_client.fine_tuning.jobs.with_raw_response.retrieve( "", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - job = await client.fine_tuning.jobs.list() + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.list() assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) @parametrize - async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: - job = await client.fine_tuning.jobs.list( + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.list( after="string", limit=0, ) assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.fine_tuning.jobs.with_raw_response.list() + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.jobs.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -346,8 +340,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.fine_tuning.jobs.with_streaming_response.list() as response: + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.jobs.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -357,15 +351,15 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_cancel(self, client: AsyncOpenAI) -> None: - job = await client.fine_tuning.jobs.cancel( + async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.cancel( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) assert_matches_type(FineTuningJob, job, path=["response"]) @parametrize - async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: - response = await client.fine_tuning.jobs.with_raw_response.cancel( + async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.jobs.with_raw_response.cancel( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) @@ -375,8 +369,8 @@ async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None: assert_matches_type(FineTuningJob, job, path=["response"]) @parametrize - async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None: - async with client.fine_tuning.jobs.with_streaming_response.cancel( + async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.jobs.with_streaming_response.cancel( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) as response: assert not response.is_closed @@ -388,22 +382,22 @@ async def test_streaming_response_cancel(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_cancel(self, client: AsyncOpenAI) -> None: + async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): - await client.fine_tuning.jobs.with_raw_response.cancel( + await async_client.fine_tuning.jobs.with_raw_response.cancel( "", ) @parametrize - async def test_method_list_events(self, client: AsyncOpenAI) -> None: - job = await client.fine_tuning.jobs.list_events( + async def test_method_list_events(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.list_events( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) @parametrize - async def test_method_list_events_with_all_params(self, client: AsyncOpenAI) -> None: - job = await client.fine_tuning.jobs.list_events( + async def test_method_list_events_with_all_params(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.list_events( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", after="string", limit=0, @@ -411,8 +405,8 @@ async def test_method_list_events_with_all_params(self, client: AsyncOpenAI) -> assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) @parametrize - async def test_raw_response_list_events(self, client: AsyncOpenAI) -> None: - response = await client.fine_tuning.jobs.with_raw_response.list_events( + async def test_raw_response_list_events(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.jobs.with_raw_response.list_events( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) @@ -422,8 +416,8 @@ async def test_raw_response_list_events(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"]) @parametrize - async def test_streaming_response_list_events(self, client: AsyncOpenAI) -> None: - async with client.fine_tuning.jobs.with_streaming_response.list_events( + async def test_streaming_response_list_events(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.jobs.with_streaming_response.list_events( "ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) as response: assert not response.is_closed @@ -435,8 +429,8 @@ async def test_streaming_response_list_events(self, client: AsyncOpenAI) -> None assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_list_events(self, client: AsyncOpenAI) -> None: + async def test_path_params_list_events(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): - await client.fine_tuning.jobs.with_raw_response.list_events( + await async_client.fine_tuning.jobs.with_raw_response.list_events( "", ) diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index a5e8dc809a..916cdd3cb6 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -10,16 +10,12 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import Completion -from openai._client import OpenAI, AsyncOpenAI base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestCompletions: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create_overload_1(self, client: OpenAI) -> None: @@ -139,21 +135,19 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: class TestAsyncCompletions: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create_overload_1(self, client: AsyncOpenAI) -> None: - completion = await client.completions.create( + async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.completions.create( model="string", prompt="This is a test.", ) assert_matches_type(Completion, completion, path=["response"]) @parametrize - async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenAI) -> None: - completion = await client.completions.create( + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.completions.create( model="string", prompt="This is a test.", best_of=0, @@ -175,8 +169,8 @@ async def test_method_create_with_all_params_overload_1(self, client: AsyncOpenA assert_matches_type(Completion, completion, path=["response"]) @parametrize - async def test_raw_response_create_overload_1(self, client: AsyncOpenAI) -> None: - response = await client.completions.with_raw_response.create( + async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: + response = await async_client.completions.with_raw_response.create( model="string", prompt="This is a test.", ) @@ -187,8 +181,8 @@ async def test_raw_response_create_overload_1(self, client: AsyncOpenAI) -> None assert_matches_type(Completion, completion, path=["response"]) @parametrize - async def test_streaming_response_create_overload_1(self, client: AsyncOpenAI) -> None: - async with client.completions.with_streaming_response.create( + async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: + async with async_client.completions.with_streaming_response.create( model="string", prompt="This is a test.", ) as response: @@ -201,8 +195,8 @@ async def test_streaming_response_create_overload_1(self, client: AsyncOpenAI) - assert cast(Any, response.is_closed) is True @parametrize - async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: - completion_stream = await client.completions.create( + async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: + completion_stream = await async_client.completions.create( model="string", prompt="This is a test.", stream=True, @@ -210,8 +204,8 @@ async def test_method_create_overload_2(self, client: AsyncOpenAI) -> None: await completion_stream.response.aclose() @parametrize - async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenAI) -> None: - completion_stream = await client.completions.create( + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + completion_stream = await async_client.completions.create( model="string", prompt="This is a test.", stream=True, @@ -233,8 +227,8 @@ async def test_method_create_with_all_params_overload_2(self, client: AsyncOpenA await completion_stream.response.aclose() @parametrize - async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None: - response = await client.completions.with_raw_response.create( + async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.completions.with_raw_response.create( model="string", prompt="This is a test.", stream=True, @@ -245,8 +239,8 @@ async def test_raw_response_create_overload_2(self, client: AsyncOpenAI) -> None await stream.close() @parametrize - async def test_streaming_response_create_overload_2(self, client: AsyncOpenAI) -> None: - async with client.completions.with_streaming_response.create( + async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.completions.with_streaming_response.create( model="string", prompt="This is a test.", stream=True, diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py index 77875fc46f..cd4ff8e391 100644 --- a/tests/api_resources/test_embeddings.py +++ b/tests/api_resources/test_embeddings.py @@ -10,16 +10,12 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import CreateEmbeddingResponse -from openai._client import OpenAI, AsyncOpenAI base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestEmbeddings: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -67,21 +63,19 @@ def test_streaming_response_create(self, client: OpenAI) -> None: class TestAsyncEmbeddings: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - embedding = await client.embeddings.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + embedding = await async_client.embeddings.create( input="The quick brown fox jumped over the lazy dog", model="text-embedding-ada-002", ) assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - embedding = await client.embeddings.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + embedding = await async_client.embeddings.create( input="The quick brown fox jumped over the lazy dog", model="text-embedding-ada-002", encoding_format="float", @@ -90,8 +84,8 @@ async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.embeddings.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.embeddings.with_raw_response.create( input="The quick brown fox jumped over the lazy dog", model="text-embedding-ada-002", ) @@ -102,8 +96,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.embeddings.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.embeddings.with_streaming_response.create( input="The quick brown fox jumped over the lazy dog", model="text-embedding-ada-002", ) as response: diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 89ad9e222f..d1a17923a6 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -13,19 +13,15 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import FileObject, FileDeleted -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncPage, AsyncPage # pyright: reportDeprecated=false base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestFiles: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -261,21 +257,19 @@ def test_path_params_retrieve_content(self, client: OpenAI) -> None: class TestAsyncFiles: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - file = await client.files.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + file = await async_client.files.create( file=b"raw file contents", purpose="fine-tune", ) assert_matches_type(FileObject, file, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.files.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.files.with_raw_response.create( file=b"raw file contents", purpose="fine-tune", ) @@ -286,8 +280,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(FileObject, file, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.files.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.files.with_streaming_response.create( file=b"raw file contents", purpose="fine-tune", ) as response: @@ -300,15 +294,15 @@ async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - file = await client.files.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + file = await async_client.files.retrieve( "string", ) assert_matches_type(FileObject, file, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.files.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.files.with_raw_response.retrieve( "string", ) @@ -318,8 +312,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(FileObject, file, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.files.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.files.with_streaming_response.retrieve( "string", ) as response: assert not response.is_closed @@ -331,27 +325,27 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await client.files.with_raw_response.retrieve( + await async_client.files.with_raw_response.retrieve( "", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - file = await client.files.list() + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + file = await async_client.files.list() assert_matches_type(AsyncPage[FileObject], file, path=["response"]) @parametrize - async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None: - file = await client.files.list( + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + file = await async_client.files.list( purpose="string", ) assert_matches_type(AsyncPage[FileObject], file, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.files.with_raw_response.list() + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.files.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -359,8 +353,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncPage[FileObject], file, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.files.with_streaming_response.list() as response: + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.files.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -370,15 +364,15 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_delete(self, client: AsyncOpenAI) -> None: - file = await client.files.delete( + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + file = await async_client.files.delete( "string", ) assert_matches_type(FileDeleted, file, path=["response"]) @parametrize - async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: - response = await client.files.with_raw_response.delete( + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.files.with_raw_response.delete( "string", ) @@ -388,8 +382,8 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(FileDeleted, file, path=["response"]) @parametrize - async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: - async with client.files.with_streaming_response.delete( + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.files.with_streaming_response.delete( "string", ) as response: assert not response.is_closed @@ -401,17 +395,17 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await client.files.with_raw_response.delete( + await async_client.files.with_raw_response.delete( "", ) @parametrize @pytest.mark.respx(base_url=base_url) - async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + async def test_method_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - file = await client.files.content( + file = await async_client.files.content( "string", ) assert isinstance(file, _legacy_response.HttpxBinaryResponseContent) @@ -419,10 +413,10 @@ async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) @parametrize @pytest.mark.respx(base_url=base_url) - async def test_raw_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + async def test_raw_response_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await client.files.with_raw_response.content( + response = await async_client.files.with_raw_response.content( "string", ) @@ -433,9 +427,9 @@ async def test_raw_response_content(self, client: AsyncOpenAI, respx_mock: MockR @parametrize @pytest.mark.respx(base_url=base_url) - async def test_streaming_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + async def test_streaming_response_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - async with client.files.with_streaming_response.content( + async with async_client.files.with_streaming_response.content( "string", ) as response: assert not response.is_closed @@ -448,25 +442,25 @@ async def test_streaming_response_content(self, client: AsyncOpenAI, respx_mock: @parametrize @pytest.mark.respx(base_url=base_url) - async def test_path_params_content(self, client: AsyncOpenAI) -> None: + async def test_path_params_content(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await client.files.with_raw_response.content( + await async_client.files.with_raw_response.content( "", ) @parametrize - async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: + async def test_method_retrieve_content(self, async_client: AsyncOpenAI) -> None: with pytest.warns(DeprecationWarning): - file = await client.files.retrieve_content( + file = await async_client.files.retrieve_content( "string", ) assert_matches_type(str, file, path=["response"]) @parametrize - async def test_raw_response_retrieve_content(self, client: AsyncOpenAI) -> None: + async def test_raw_response_retrieve_content(self, async_client: AsyncOpenAI) -> None: with pytest.warns(DeprecationWarning): - response = await client.files.with_raw_response.retrieve_content( + response = await async_client.files.with_raw_response.retrieve_content( "string", ) @@ -476,9 +470,9 @@ async def test_raw_response_retrieve_content(self, client: AsyncOpenAI) -> None: assert_matches_type(str, file, path=["response"]) @parametrize - async def test_streaming_response_retrieve_content(self, client: AsyncOpenAI) -> None: + async def test_streaming_response_retrieve_content(self, async_client: AsyncOpenAI) -> None: with pytest.warns(DeprecationWarning): - async with client.files.with_streaming_response.retrieve_content( + async with async_client.files.with_streaming_response.retrieve_content( "string", ) as response: assert not response.is_closed @@ -490,9 +484,9 @@ async def test_streaming_response_retrieve_content(self, client: AsyncOpenAI) -> assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve_content(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve_content(self, async_client: AsyncOpenAI) -> None: with pytest.warns(DeprecationWarning): with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await client.files.with_raw_response.retrieve_content( + await async_client.files.with_raw_response.retrieve_content( "", ) diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 553bd018ee..b6cb2572ab 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -10,16 +10,12 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import ImagesResponse -from openai._client import OpenAI, AsyncOpenAI base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestImages: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create_variation(self, client: OpenAI) -> None: @@ -159,20 +155,18 @@ def test_streaming_response_generate(self, client: OpenAI) -> None: class TestAsyncImages: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create_variation(self, client: AsyncOpenAI) -> None: - image = await client.images.create_variation( + async def test_method_create_variation(self, async_client: AsyncOpenAI) -> None: + image = await async_client.images.create_variation( image=b"raw file contents", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_method_create_variation_with_all_params(self, client: AsyncOpenAI) -> None: - image = await client.images.create_variation( + async def test_method_create_variation_with_all_params(self, async_client: AsyncOpenAI) -> None: + image = await async_client.images.create_variation( image=b"raw file contents", model="dall-e-2", n=1, @@ -183,8 +177,8 @@ async def test_method_create_variation_with_all_params(self, client: AsyncOpenAI assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_raw_response_create_variation(self, client: AsyncOpenAI) -> None: - response = await client.images.with_raw_response.create_variation( + async def test_raw_response_create_variation(self, async_client: AsyncOpenAI) -> None: + response = await async_client.images.with_raw_response.create_variation( image=b"raw file contents", ) @@ -194,8 +188,8 @@ async def test_raw_response_create_variation(self, client: AsyncOpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_streaming_response_create_variation(self, client: AsyncOpenAI) -> None: - async with client.images.with_streaming_response.create_variation( + async def test_streaming_response_create_variation(self, async_client: AsyncOpenAI) -> None: + async with async_client.images.with_streaming_response.create_variation( image=b"raw file contents", ) as response: assert not response.is_closed @@ -207,16 +201,16 @@ async def test_streaming_response_create_variation(self, client: AsyncOpenAI) -> assert cast(Any, response.is_closed) is True @parametrize - async def test_method_edit(self, client: AsyncOpenAI) -> None: - image = await client.images.edit( + async def test_method_edit(self, async_client: AsyncOpenAI) -> None: + image = await async_client.images.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_method_edit_with_all_params(self, client: AsyncOpenAI) -> None: - image = await client.images.edit( + async def test_method_edit_with_all_params(self, async_client: AsyncOpenAI) -> None: + image = await async_client.images.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", mask=b"raw file contents", @@ -229,8 +223,8 @@ async def test_method_edit_with_all_params(self, client: AsyncOpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_raw_response_edit(self, client: AsyncOpenAI) -> None: - response = await client.images.with_raw_response.edit( + async def test_raw_response_edit(self, async_client: AsyncOpenAI) -> None: + response = await async_client.images.with_raw_response.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", ) @@ -241,8 +235,8 @@ async def test_raw_response_edit(self, client: AsyncOpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_streaming_response_edit(self, client: AsyncOpenAI) -> None: - async with client.images.with_streaming_response.edit( + async def test_streaming_response_edit(self, async_client: AsyncOpenAI) -> None: + async with async_client.images.with_streaming_response.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", ) as response: @@ -255,15 +249,15 @@ async def test_streaming_response_edit(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_generate(self, client: AsyncOpenAI) -> None: - image = await client.images.generate( + async def test_method_generate(self, async_client: AsyncOpenAI) -> None: + image = await async_client.images.generate( prompt="A cute baby sea otter", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_method_generate_with_all_params(self, client: AsyncOpenAI) -> None: - image = await client.images.generate( + async def test_method_generate_with_all_params(self, async_client: AsyncOpenAI) -> None: + image = await async_client.images.generate( prompt="A cute baby sea otter", model="dall-e-3", n=1, @@ -276,8 +270,8 @@ async def test_method_generate_with_all_params(self, client: AsyncOpenAI) -> Non assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_raw_response_generate(self, client: AsyncOpenAI) -> None: - response = await client.images.with_raw_response.generate( + async def test_raw_response_generate(self, async_client: AsyncOpenAI) -> None: + response = await async_client.images.with_raw_response.generate( prompt="A cute baby sea otter", ) @@ -287,8 +281,8 @@ async def test_raw_response_generate(self, client: AsyncOpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_streaming_response_generate(self, client: AsyncOpenAI) -> None: - async with client.images.with_streaming_response.generate( + async def test_streaming_response_generate(self, async_client: AsyncOpenAI) -> None: + async with async_client.images.with_streaming_response.generate( prompt="A cute baby sea otter", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py index b41e50eb71..d031d54f6a 100644 --- a/tests/api_resources/test_models.py +++ b/tests/api_resources/test_models.py @@ -10,17 +10,13 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import Model, ModelDeleted -from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncPage, AsyncPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestModels: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: OpenAI) -> None: @@ -125,20 +121,18 @@ def test_path_params_delete(self, client: OpenAI) -> None: class TestAsyncModels: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_retrieve(self, client: AsyncOpenAI) -> None: - model = await client.models.retrieve( + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + model = await async_client.models.retrieve( "gpt-3.5-turbo", ) assert_matches_type(Model, model, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: - response = await client.models.with_raw_response.retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.models.with_raw_response.retrieve( "gpt-3.5-turbo", ) @@ -148,8 +142,8 @@ async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None: assert_matches_type(Model, model, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: - async with client.models.with_streaming_response.retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.models.with_streaming_response.retrieve( "gpt-3.5-turbo", ) as response: assert not response.is_closed @@ -161,20 +155,20 @@ async def test_streaming_response_retrieve(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, client: AsyncOpenAI) -> None: + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): - await client.models.with_raw_response.retrieve( + await async_client.models.with_raw_response.retrieve( "", ) @parametrize - async def test_method_list(self, client: AsyncOpenAI) -> None: - model = await client.models.list() + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + model = await async_client.models.list() assert_matches_type(AsyncPage[Model], model, path=["response"]) @parametrize - async def test_raw_response_list(self, client: AsyncOpenAI) -> None: - response = await client.models.with_raw_response.list() + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.models.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -182,8 +176,8 @@ async def test_raw_response_list(self, client: AsyncOpenAI) -> None: assert_matches_type(AsyncPage[Model], model, path=["response"]) @parametrize - async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: - async with client.models.with_streaming_response.list() as response: + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.models.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -193,15 +187,15 @@ async def test_streaming_response_list(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_delete(self, client: AsyncOpenAI) -> None: - model = await client.models.delete( + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + model = await async_client.models.delete( "ft:gpt-3.5-turbo:acemeco:suffix:abc123", ) assert_matches_type(ModelDeleted, model, path=["response"]) @parametrize - async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: - response = await client.models.with_raw_response.delete( + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.models.with_raw_response.delete( "ft:gpt-3.5-turbo:acemeco:suffix:abc123", ) @@ -211,8 +205,8 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(ModelDeleted, model, path=["response"]) @parametrize - async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: - async with client.models.with_streaming_response.delete( + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.models.with_streaming_response.delete( "ft:gpt-3.5-turbo:acemeco:suffix:abc123", ) as response: assert not response.is_closed @@ -224,8 +218,8 @@ async def test_streaming_response_delete(self, client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_delete(self, client: AsyncOpenAI) -> None: + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model` but received ''"): - await client.models.with_raw_response.delete( + await async_client.models.with_raw_response.delete( "", ) diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index 88d35f003d..285e738c0e 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -10,16 +10,12 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import ModerationCreateResponse -from openai._client import OpenAI, AsyncOpenAI base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") -api_key = "My API Key" class TestModerations: - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: OpenAI) -> None: @@ -62,28 +58,26 @@ def test_streaming_response_create(self, client: OpenAI) -> None: class TestAsyncModerations: - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"]) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, client: AsyncOpenAI) -> None: - moderation = await client.moderations.create( + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + moderation = await async_client.moderations.create( input="I want to kill them.", ) assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None: - moderation = await client.moderations.create( + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + moderation = await async_client.moderations.create( input="I want to kill them.", model="text-moderation-stable", ) assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) @parametrize - async def test_raw_response_create(self, client: AsyncOpenAI) -> None: - response = await client.moderations.with_raw_response.create( + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.moderations.with_raw_response.create( input="I want to kill them.", ) @@ -93,8 +87,8 @@ async def test_raw_response_create(self, client: AsyncOpenAI) -> None: assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) @parametrize - async def test_streaming_response_create(self, client: AsyncOpenAI) -> None: - async with client.moderations.with_streaming_response.create( + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.moderations.with_streaming_response.create( input="I want to kill them.", ) as response: assert not response.is_closed diff --git a/tests/conftest.py b/tests/conftest.py index c3a1efe9df..15af57e770 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,17 @@ +from __future__ import annotations + +import os import asyncio import logging -from typing import Iterator +from typing import TYPE_CHECKING, Iterator, AsyncIterator import pytest +from openai import OpenAI, AsyncOpenAI + +if TYPE_CHECKING: + from _pytest.fixtures import FixtureRequest + pytest.register_assert_rewrite("tests.utils") logging.getLogger("openai").setLevel(logging.DEBUG) @@ -14,3 +22,28 @@ def event_loop() -> Iterator[asyncio.AbstractEventLoop]: loop = asyncio.new_event_loop() yield loop loop.close() + + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + +api_key = "My API Key" + + +@pytest.fixture(scope="session") +def client(request: FixtureRequest) -> Iterator[OpenAI]: + strict = getattr(request, "param", True) + if not isinstance(strict, bool): + raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + + with OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + yield client + + +@pytest.fixture(scope="session") +async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncOpenAI]: + strict = getattr(request, "param", True) + if not isinstance(strict, bool): + raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + + async with AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + yield client From 7f18183fe329eb94dcdcb7ddb34447699b3ce70e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 19 Jan 2024 21:21:49 -0500 Subject: [PATCH 301/914] feat(api): add usage to runs and run steps (#1090) --- src/openai/types/beta/threads/run.py | 19 +++++++++++++++++++ .../types/beta/threads/runs/run_step.py | 19 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index b6d66bd8dd..db4bc0e07d 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -17,6 +17,7 @@ "ToolAssistantToolsCode", "ToolAssistantToolsRetrieval", "ToolAssistantToolsFunction", + "Usage", ] @@ -61,6 +62,17 @@ class ToolAssistantToolsFunction(BaseModel): Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] +class Usage(BaseModel): + completion_tokens: int + """Number of completion tokens used over the course of the run.""" + + prompt_tokens: int + """Number of prompt tokens used over the course of the run.""" + + total_tokens: int + """Total number of tokens used (prompt + completion).""" + + class Run(BaseModel): id: str """The identifier, which can be referenced in API endpoints.""" @@ -152,3 +164,10 @@ class Run(BaseModel): [assistant](https://platform.openai.com/docs/api-reference/assistants) used for this run. """ + + usage: Optional[Usage] = None + """Usage statistics related to the run. + + This value will be `null` if the run is not in a terminal state (i.e. + `in_progress`, `queued`, etc.). + """ diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 1d95e9d6eb..5f3e29a312 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -8,7 +8,7 @@ from .tool_calls_step_details import ToolCallsStepDetails from .message_creation_step_details import MessageCreationStepDetails -__all__ = ["RunStep", "LastError", "StepDetails"] +__all__ = ["RunStep", "LastError", "StepDetails", "Usage"] class LastError(BaseModel): @@ -22,6 +22,17 @@ class LastError(BaseModel): StepDetails = Union[MessageCreationStepDetails, ToolCallsStepDetails] +class Usage(BaseModel): + completion_tokens: int + """Number of completion tokens used over the course of the run step.""" + + prompt_tokens: int + """Number of prompt tokens used over the course of the run step.""" + + total_tokens: int + """Total number of tokens used (prompt + completion).""" + + class RunStep(BaseModel): id: str """The identifier of the run step, which can be referenced in API endpoints.""" @@ -91,3 +102,9 @@ class RunStep(BaseModel): type: Literal["message_creation", "tool_calls"] """The type of run step, which can be either `message_creation` or `tool_calls`.""" + + usage: Optional[Usage] = None + """Usage statistics related to the run step. + + This value will be `null` while the run step's status is `in_progress`. + """ From 15488ce07cb97535d8564e82dd5cda3481bc1a81 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sun, 21 Jan 2024 00:41:08 -0500 Subject: [PATCH 302/914] release: 1.9.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 17 +++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c523ce19f0..c3c95522a6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.8.0" + ".": "1.9.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c2ac83cdeb..14771f603b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 1.9.0 (2024-01-21) + +Full Changelog: [v1.8.0...v1.9.0](https://github.com/openai/openai-python/compare/v1.8.0...v1.9.0) + +### Features + +* **api:** add usage to runs and run steps ([#1090](https://github.com/openai/openai-python/issues/1090)) ([6c116df](https://github.com/openai/openai-python/commit/6c116dfbb0065d15050450df70e0e98fc8c80349)) + + +### Chores + +* **internal:** fix typing util function ([#1083](https://github.com/openai/openai-python/issues/1083)) ([3e60db6](https://github.com/openai/openai-python/commit/3e60db69f5d9187c4eb38451967259f534a36a82)) +* **internal:** remove redundant client test ([#1085](https://github.com/openai/openai-python/issues/1085)) ([947974f](https://github.com/openai/openai-python/commit/947974f5af726e252b7b12c863743e50f41b79d3)) +* **internal:** share client instances between all tests ([#1088](https://github.com/openai/openai-python/issues/1088)) ([05cd753](https://github.com/openai/openai-python/commit/05cd7531d40774d05c52b14dee54d137ac1452a3)) +* **internal:** speculative retry-after-ms support ([#1086](https://github.com/openai/openai-python/issues/1086)) ([36a7576](https://github.com/openai/openai-python/commit/36a7576a913be8509a3cf6f262543083b485136e)) +* lazy load raw resource class properties ([#1087](https://github.com/openai/openai-python/issues/1087)) ([d307127](https://github.com/openai/openai-python/commit/d30712744be07461e86763705c03c3495eadfc35)) + ## 1.8.0 (2024-01-16) Full Changelog: [v1.7.2...v1.8.0](https://github.com/openai/openai-python/compare/v1.7.2...v1.8.0) diff --git a/pyproject.toml b/pyproject.toml index 5019e6cf7e..82f4c7e068 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.8.0" +version = "1.9.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 311cab2540..b4e6d226ea 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.8.0" # x-release-please-version +__version__ = "1.9.0" # x-release-please-version From ad87ea4061fd8a9785e10a520d513bdb0897a8a2 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:48:42 -0500 Subject: [PATCH 303/914] chore(internal): add internal helpers (#1092) --- src/openai/_compat.py | 39 ++++++++++++++++++++- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_sync.py | 64 +++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/openai/_utils/_sync.py diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 3cda39909b..74c7639b4c 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -1,13 +1,15 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Union, TypeVar, cast +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload from datetime import date, datetime +from typing_extensions import Self import pydantic from pydantic.fields import FieldInfo from ._types import StrBytesIntFloat +_T = TypeVar("_T") _ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) # --------------- Pydantic v2 compatibility --------------- @@ -178,8 +180,43 @@ class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): # cached properties if TYPE_CHECKING: cached_property = property + + # we define a separate type (copied from typeshed) + # that represents that `cached_property` is `set`able + # at runtime, which differs from `@property`. + # + # this is a separate type as editors likely special case + # `@property` and we don't want to cause issues just to have + # more helpful internal types. + + class typed_cached_property(Generic[_T]): + func: Callable[[Any], _T] + attrname: str | None + + def __init__(self, func: Callable[[Any], _T]) -> None: + ... + + @overload + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: + ... + + @overload + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: + ... + + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: + raise NotImplementedError() + + def __set_name__(self, owner: type[Any], name: str) -> None: + ... + + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: + ... else: try: from functools import cached_property as cached_property except ImportError: from cached_property import cached_property as cached_property + + typed_cached_property = cached_property diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 2dcfc122f1..0fb811a945 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -1,3 +1,4 @@ +from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( flatten as flatten, diff --git a/src/openai/_utils/_sync.py b/src/openai/_utils/_sync.py new file mode 100644 index 0000000000..595924e5b1 --- /dev/null +++ b/src/openai/_utils/_sync.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import functools +from typing import TypeVar, Callable, Awaitable +from typing_extensions import ParamSpec + +import anyio +import anyio.to_thread + +T_Retval = TypeVar("T_Retval") +T_ParamSpec = ParamSpec("T_ParamSpec") + + +# copied from `asyncer`, https://github.com/tiangolo/asyncer +def asyncify( + function: Callable[T_ParamSpec, T_Retval], + *, + cancellable: bool = False, + limiter: anyio.CapacityLimiter | None = None, +) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: + """ + Take a blocking function and create an async one that receives the same + positional and keyword arguments, and that when called, calls the original function + in a worker thread using `anyio.to_thread.run_sync()`. Internally, + `asyncer.asyncify()` uses the same `anyio.to_thread.run_sync()`, but it supports + keyword arguments additional to positional arguments and it adds better support for + autocompletion and inline errors for the arguments of the function called and the + return value. + + If the `cancellable` option is enabled and the task waiting for its completion is + cancelled, the thread will still run its course but its return value (or any raised + exception) will be ignored. + + Use it like this: + + ```Python + def do_work(arg1, arg2, kwarg1="", kwarg2="") -> str: + # Do work + return "Some result" + + + result = await to_thread.asyncify(do_work)("spam", "ham", kwarg1="a", kwarg2="b") + print(result) + ``` + + ## Arguments + + `function`: a blocking regular callable (e.g. a function) + `cancellable`: `True` to allow cancellation of the operation + `limiter`: capacity limiter to use to limit the total amount of threads running + (if omitted, the default limiter is used) + + ## 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: + partial_f = functools.partial(function, *args, **kwargs) + return await anyio.to_thread.run_sync(partial_f, cancellable=cancellable, limiter=limiter) + + return wrapper From dd808c0f453375db16706dff42b097724110642a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:36:36 +0100 Subject: [PATCH 304/914] refactor: remove unnecessary builtin import (#1094) --- src/openai/types/beta/assistant.py | 3 +-- src/openai/types/beta/thread.py | 3 +-- src/openai/types/beta/threads/run.py | 3 +-- src/openai/types/beta/threads/runs/run_step.py | 3 +-- src/openai/types/beta/threads/thread_message.py | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 89e45d4806..7a29984b50 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. -import builtins from typing import List, Union, Optional from typing_extensions import Literal @@ -53,7 +52,7 @@ class Assistant(BaseModel): The maximum length is 32768 characters. """ - metadata: Optional[builtins.object] = None + metadata: Optional[object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py index 474527033a..a0002a21ef 100644 --- a/src/openai/types/beta/thread.py +++ b/src/openai/types/beta/thread.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. -import builtins from typing import Optional from typing_extensions import Literal @@ -16,7 +15,7 @@ class Thread(BaseModel): created_at: int """The Unix timestamp (in seconds) for when the thread was created.""" - metadata: Optional[builtins.object] = None + metadata: Optional[object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index db4bc0e07d..9c875a9242 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. -import builtins from typing import List, Union, Optional from typing_extensions import Literal @@ -116,7 +115,7 @@ class Run(BaseModel): last_error: Optional[LastError] = None """The last error associated with this run. Will be `null` if there are no errors.""" - metadata: Optional[builtins.object] = None + metadata: Optional[object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 5f3e29a312..01aab8e9a6 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. -import builtins from typing import Union, Optional from typing_extensions import Literal @@ -68,7 +67,7 @@ class RunStep(BaseModel): Will be `null` if there are no errors. """ - metadata: Optional[builtins.object] = None + metadata: Optional[object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a diff --git a/src/openai/types/beta/threads/thread_message.py b/src/openai/types/beta/threads/thread_message.py index 8f1ac07d0a..25b3a199f7 100644 --- a/src/openai/types/beta/threads/thread_message.py +++ b/src/openai/types/beta/threads/thread_message.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. -import builtins from typing import List, Union, Optional from typing_extensions import Literal @@ -37,7 +36,7 @@ class ThreadMessage(BaseModel): that can access files. A maximum of 10 files can be attached to a message. """ - metadata: Optional[builtins.object] = None + metadata: Optional[object] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a From f66d33e4a6c3e75add6c367f4d032bac036dffe1 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:02:02 +0100 Subject: [PATCH 305/914] feat(azure): proactively add audio/speech to deployment endpoints (#1099) --- src/openai/lib/azure.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 27bebd8cab..2c8b4dcd88 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -22,6 +22,7 @@ "/embeddings", "/audio/transcriptions", "/audio/translations", + "/audio/speech", "/images/generations", ] ) From 62cd46330374c8ec0db9821b1e967fa87fde3c29 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 24 Jan 2024 17:06:53 +0100 Subject: [PATCH 306/914] feat(client): enable follow redirects by default (#1100) --- src/openai/_base_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 43fad0603d..7a1562461f 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -777,6 +777,7 @@ def __init__( proxies=proxies, transport=transport, limits=limits, + follow_redirects=True, ) def is_closed(self) -> bool: @@ -1318,6 +1319,7 @@ def __init__( proxies=proxies, transport=transport, limits=limits, + follow_redirects=True, ) def is_closed(self) -> bool: From 20d97ef0deb4e415b232d041bb6c0bede32a1553 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 25 Jan 2024 20:15:58 +0100 Subject: [PATCH 307/914] feat(api): add text embeddings dimensions param (#1103) --- src/openai/resources/chat/completions.py | 34 +++++++++++++++---- src/openai/resources/embeddings.py | 14 ++++++-- .../types/chat/completion_create_params.py | 6 +++- src/openai/types/embedding_create_params.py | 8 ++++- tests/api_resources/test_embeddings.py | 18 +++++----- 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index f461161ab7..45521833ad 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -46,6 +46,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", @@ -152,7 +154,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with - `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -232,6 +235,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", @@ -345,7 +350,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with - `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -418,6 +424,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", @@ -531,7 +539,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with - `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -604,6 +613,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", @@ -698,6 +709,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", @@ -804,7 +817,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with - `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -884,6 +898,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", @@ -997,7 +1013,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with - `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1070,6 +1087,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", @@ -1183,7 +1202,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with - `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1256,6 +1276,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 5bc7ed855e..857bfc7702 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -36,7 +36,8 @@ def create( self, *, input: Union[str, List[str], List[int], List[List[int]]], - model: Union[str, Literal["text-embedding-ada-002"]], + model: Union[str, Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]], + dimensions: int | NotGiven = NOT_GIVEN, encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -64,6 +65,9 @@ def create( [Model overview](https://platform.openai.com/docs/models/overview) for descriptions of them. + dimensions: The number of dimensions the resulting output embeddings should have. Only + supported in `text-embedding-3` and later models. + encoding_format: The format to return the embeddings in. Can be either `float` or [`base64`](https://pypi.org/project/pybase64/). @@ -83,6 +87,7 @@ def create( "input": input, "model": model, "user": user, + "dimensions": dimensions, "encoding_format": encoding_format, } if not is_given(encoding_format) and has_numpy(): @@ -132,7 +137,8 @@ async def create( self, *, input: Union[str, List[str], List[int], List[List[int]]], - model: Union[str, Literal["text-embedding-ada-002"]], + model: Union[str, Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]], + dimensions: int | NotGiven = NOT_GIVEN, encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -160,6 +166,9 @@ async def create( [Model overview](https://platform.openai.com/docs/models/overview) for descriptions of them. + dimensions: The number of dimensions the resulting output embeddings should have. Only + supported in `text-embedding-3` and later models. + encoding_format: The format to return the embeddings in. Can be either `float` or [`base64`](https://pypi.org/project/pybase64/). @@ -179,6 +188,7 @@ async def create( "input": input, "model": model, "user": user, + "dimensions": dimensions, "encoding_format": encoding_format, } if not is_given(encoding_format) and has_numpy(): diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 6b38a89263..3ea14d82b3 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -32,6 +32,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): Union[ str, Literal[ + "gpt-4-0125-preview", + "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4-vision-preview", "gpt-4", @@ -133,7 +135,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): response_format: ResponseFormat """An object specifying the format that the model must output. - Compatible with `gpt-4-1106-preview` and `gpt-3.5-turbo-1106`. + Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py index fd2fc5b48d..66ac60511c 100644 --- a/src/openai/types/embedding_create_params.py +++ b/src/openai/types/embedding_create_params.py @@ -20,7 +20,7 @@ class EmbeddingCreateParams(TypedDict, total=False): for counting tokens. """ - model: Required[Union[str, Literal["text-embedding-ada-002"]]] + model: Required[Union[str, Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]]] """ID of the model to use. You can use the @@ -30,6 +30,12 @@ class EmbeddingCreateParams(TypedDict, total=False): descriptions of them. """ + dimensions: int + """The number of dimensions the resulting output embeddings should have. + + Only supported in `text-embedding-3` and later models. + """ + encoding_format: Literal["float", "base64"] """The format to return the embeddings in. diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py index cd4ff8e391..42599219f3 100644 --- a/tests/api_resources/test_embeddings.py +++ b/tests/api_resources/test_embeddings.py @@ -21,7 +21,7 @@ class TestEmbeddings: def test_method_create(self, client: OpenAI) -> None: embedding = client.embeddings.create( input="The quick brown fox jumped over the lazy dog", - model="text-embedding-ada-002", + model="text-embedding-3-small", ) assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) @@ -29,7 +29,8 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: embedding = client.embeddings.create( input="The quick brown fox jumped over the lazy dog", - model="text-embedding-ada-002", + model="text-embedding-3-small", + dimensions=1, encoding_format="float", user="user-1234", ) @@ -39,7 +40,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.embeddings.with_raw_response.create( input="The quick brown fox jumped over the lazy dog", - model="text-embedding-ada-002", + model="text-embedding-3-small", ) assert response.is_closed is True @@ -51,7 +52,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: def test_streaming_response_create(self, client: OpenAI) -> None: with client.embeddings.with_streaming_response.create( input="The quick brown fox jumped over the lazy dog", - model="text-embedding-ada-002", + model="text-embedding-3-small", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -69,7 +70,7 @@ class TestAsyncEmbeddings: async def test_method_create(self, async_client: AsyncOpenAI) -> None: embedding = await async_client.embeddings.create( input="The quick brown fox jumped over the lazy dog", - model="text-embedding-ada-002", + model="text-embedding-3-small", ) assert_matches_type(CreateEmbeddingResponse, embedding, path=["response"]) @@ -77,7 +78,8 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: embedding = await async_client.embeddings.create( input="The quick brown fox jumped over the lazy dog", - model="text-embedding-ada-002", + model="text-embedding-3-small", + dimensions=1, encoding_format="float", user="user-1234", ) @@ -87,7 +89,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.embeddings.with_raw_response.create( input="The quick brown fox jumped over the lazy dog", - model="text-embedding-ada-002", + model="text-embedding-3-small", ) assert response.is_closed is True @@ -99,7 +101,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.embeddings.with_streaming_response.create( input="The quick brown fox jumped over the lazy dog", - model="text-embedding-ada-002", + model="text-embedding-3-small", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" From 0c1e58d511bd60c4dd47ea8a8c0820dc2d013d1d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 25 Jan 2024 20:16:44 +0100 Subject: [PATCH 308/914] release: 1.10.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c3c95522a6..eb4e0dba72 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.9.0" + ".": "1.10.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 14771f603b..1a22e062dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 1.10.0 (2024-01-25) + +Full Changelog: [v1.9.0...v1.10.0](https://github.com/openai/openai-python/compare/v1.9.0...v1.10.0) + +### Features + +* **api:** add text embeddings dimensions param ([#1103](https://github.com/openai/openai-python/issues/1103)) ([94abfa0](https://github.com/openai/openai-python/commit/94abfa0f988c199ea95a9c870c4ae9808823186d)) +* **azure:** proactively add audio/speech to deployment endpoints ([#1099](https://github.com/openai/openai-python/issues/1099)) ([fdf8742](https://github.com/openai/openai-python/commit/fdf87429b45ceb47ae6fd068ab70cc07bcb8da44)) +* **client:** enable follow redirects by default ([#1100](https://github.com/openai/openai-python/issues/1100)) ([d325b7c](https://github.com/openai/openai-python/commit/d325b7ca594c2abaada536249b5633b106943333)) + + +### Chores + +* **internal:** add internal helpers ([#1092](https://github.com/openai/openai-python/issues/1092)) ([629bde5](https://github.com/openai/openai-python/commit/629bde5800d84735e22d924db23109a141f48644)) + + +### Refactors + +* remove unnecessary builtin import ([#1094](https://github.com/openai/openai-python/issues/1094)) ([504b7d4](https://github.com/openai/openai-python/commit/504b7d4a0b4715bd49a1a076a8d4868e51fb3351)) + ## 1.9.0 (2024-01-21) Full Changelog: [v1.8.0...v1.9.0](https://github.com/openai/openai-python/compare/v1.8.0...v1.9.0) diff --git a/pyproject.toml b/pyproject.toml index 82f4c7e068..b3448f1aeb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.9.0" +version = "1.10.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b4e6d226ea..e9a863539d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.9.0" # x-release-please-version +__version__ = "1.10.0" # x-release-please-version From 22713fd02a31c1dfe332a691190db5ecae1e39f4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 26 Jan 2024 11:47:48 +0100 Subject: [PATCH 309/914] chore(internal): support multipart data with overlapping keys (#1104) --- src/openai/_base_client.py | 32 +++++++++++++++++---- tests/test_client.py | 58 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 7a1562461f..d7e5127dd8 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -61,7 +61,7 @@ RequestOptions, ModelBuilderProtocol, ) -from ._utils import is_dict, is_given, is_mapping +from ._utils import is_dict, is_list, is_given, is_mapping from ._compat import model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( @@ -451,14 +451,18 @@ def _build_request( headers = self._build_headers(options) params = _merge_mappings(self._custom_query, options.params) + content_type = headers.get("Content-Type") # If the given Content-Type header is multipart/form-data then it # has to be removed so that httpx can generate the header with # additional information for us as it has to be in this form # for the server to be able to correctly parse the request: # multipart/form-data; boundary=---abc-- - if headers.get("Content-Type") == "multipart/form-data": - headers.pop("Content-Type") + if content_type is not None and content_type.startswith("multipart/form-data"): + if "boundary" not in content_type: + # only remove the header if the boundary hasn't been explicitly set + # as the caller doesn't want httpx to come up with their own boundary + headers.pop("Content-Type") # As we are now sending multipart/form-data instead of application/json # we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding @@ -494,9 +498,25 @@ def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, o ) serialized: dict[str, object] = {} for key, value in items: - if key in serialized: - raise ValueError(f"Duplicate key encountered: {key}; This behaviour is not supported") - serialized[key] = value + existing = serialized.get(key) + + if not existing: + serialized[key] = value + continue + + # If a value has already been set for this key then that + # means we're sending data like `array[]=[1, 2, 3]` and we + # need to tell httpx that we want to send multiple values with + # the same key which is done by using a list or a tuple. + # + # Note: 2d arrays should never result in the same key at both + # levels so it's safe to assume that if the value is a list, + # it was because we changed it to be a list. + if is_list(existing): + existing.append(value) + else: + serialized[key] = [existing, value] + return serialized def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: diff --git a/tests/test_client.py b/tests/test_client.py index 3d2dd35821..24933456bd 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -437,6 +437,35 @@ def test_request_extra_query(self) -> None: params = dict(request.url.params) assert params == {"foo": "2"} + def test_multipart_repeating_array(self, client: OpenAI) -> None: + request = client._build_request( + FinalRequestOptions.construct( + method="get", + url="/foo", + headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, + json_data={"array": ["foo", "bar"]}, + files=[("foo.txt", b"hello world")], + ) + ) + + assert request.read().split(b"\r\n") == [ + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"foo", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"bar", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', + b"Content-Type: application/octet-stream", + b"", + b"hello world", + b"--6b7ba517decee4a450543ea6ae821c82--", + b"", + ] + @pytest.mark.respx(base_url=base_url) def test_basic_union_response(self, respx_mock: MockRouter) -> None: class Model1(BaseModel): @@ -1104,6 +1133,35 @@ def test_request_extra_query(self) -> None: params = dict(request.url.params) assert params == {"foo": "2"} + def test_multipart_repeating_array(self, async_client: AsyncOpenAI) -> None: + request = async_client._build_request( + FinalRequestOptions.construct( + method="get", + url="/foo", + headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, + json_data={"array": ["foo", "bar"]}, + files=[("foo.txt", b"hello world")], + ) + ) + + assert request.read().split(b"\r\n") == [ + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"foo", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"bar", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', + b"Content-Type: application/octet-stream", + b"", + b"hello world", + b"--6b7ba517decee4a450543ea6ae821c82--", + b"", + ] + @pytest.mark.respx(base_url=base_url) async def test_basic_union_response(self, respx_mock: MockRouter) -> None: class Model1(BaseModel): From 63de8ef4229ba92fb05ac15efb1a07b3ab2a79ee Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:55:41 +0100 Subject: [PATCH 310/914] chore(internal): enable ruff type checking misuse lint rule (#1106) This catches the case where a typing import is used at runtime --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index b3448f1aeb..c088e19264 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -147,6 +147,8 @@ select = [ # print statements "T201", "T203", + # misuse of typing.TYPE_CHECKING + "TCH004" ] ignore = [ # mutable defaults From 61f3346b4c6fec20a4109ad884c998e706593137 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:05:32 +0100 Subject: [PATCH 311/914] feat(client): support parsing custom response types (#1111) --- src/openai/__init__.py | 2 + src/openai/_legacy_response.py | 102 ++++++++++++++++------- src/openai/_response.py | 147 +++++++++++++++++++++++---------- src/openai/_streaming.py | 36 +++++++- src/openai/_utils/_typing.py | 10 ++- tests/test_legacy_response.py | 65 +++++++++++++++ tests/test_response.py | 109 ++++++++++++++++++++++++ 7 files changed, 392 insertions(+), 79 deletions(-) create mode 100644 tests/test_legacy_response.py diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 0de58b3327..118fe8ee93 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -9,6 +9,7 @@ from ._types import NoneType, Transport, ProxiesTypes from ._utils import file_from_path from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions +from ._models import BaseModel from ._version import __title__, __version__ from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse from ._exceptions import ( @@ -59,6 +60,7 @@ "OpenAI", "AsyncOpenAI", "file_from_path", + "BaseModel", ] from .lib import azure as _azure diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index c36c94f165..6eaa691d9f 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -5,25 +5,28 @@ import logging import datetime import functools -from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, Iterator, AsyncIterator, cast -from typing_extensions import Awaitable, ParamSpec, get_args, override, deprecated, get_origin +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, Iterator, AsyncIterator, cast, overload +from typing_extensions import Awaitable, ParamSpec, override, deprecated, get_origin import anyio import httpx +import pydantic from ._types import NoneType from ._utils import is_given from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type from ._exceptions import APIResponseValidationError if TYPE_CHECKING: from ._models import FinalRequestOptions - from ._base_client import Stream, BaseClient, AsyncStream + from ._base_client import BaseClient P = ParamSpec("P") R = TypeVar("R") +_T = TypeVar("_T") log: logging.Logger = logging.getLogger(__name__) @@ -43,7 +46,7 @@ class LegacyAPIResponse(Generic[R]): _cast_to: type[R] _client: BaseClient[Any, Any] - _parsed: R | None + _parsed_by_type: dict[type[Any], Any] _stream: bool _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None _options: FinalRequestOptions @@ -62,27 +65,60 @@ def __init__( ) -> None: self._cast_to = cast_to self._client = client - self._parsed = None + self._parsed_by_type = {} self._stream = stream self._stream_cls = stream_cls self._options = options self.http_response = raw + @overload + def parse(self, *, to: type[_T]) -> _T: + ... + + @overload def parse(self) -> R: + ... + + def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. + NOTE: For the async client: this will become a coroutine in the next major version. + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. - NOTE: For the async client: this will become a coroutine in the next major version. + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from openai import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `httpx.Response` """ - if self._parsed is not None: - return self._parsed + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] - parsed = self._parse() + parsed = self._parse(to=to) if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) - self._parsed = parsed + self._parsed_by_type[cache_key] = parsed return parsed @property @@ -135,13 +171,29 @@ def elapsed(self) -> datetime.timedelta: """The time taken for the complete request/response cycle to complete.""" return self.http_response.elapsed - def _parse(self) -> R: + def _parse(self, *, to: type[_T] | None = None) -> R | _T: if self._stream: + if to: + if not is_stream_class_type(to): + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") + + return cast( + _T, + to( + cast_to=extract_stream_chunk_type( + to, + failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", + ), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + if self._stream_cls: return cast( R, self._stream_cls( - cast_to=_extract_stream_chunk_type(self._stream_cls), + cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), ), @@ -160,7 +212,7 @@ def _parse(self) -> R: ), ) - cast_to = self._cast_to + cast_to = to if to is not None else self._cast_to if cast_to is NoneType: return cast(R, None) @@ -186,14 +238,9 @@ def _parse(self) -> R: raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) - # The check here is necessary as we are subverting the the type system - # with casts as the relationship between TypeVars and Types are very strict - # which means we must return *exactly* what was input or transform it in a - # way that retains the TypeVar state. As we cannot do that in this function - # then we have to resort to using `cast`. At the time of writing, we know this - # to be safe as we have handled all the types that could be bound to the - # `ResponseT` TypeVar, however if that TypeVar is ever updated in the future, then - # this function would become unsafe but a type checker would not report an error. + if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel): + raise TypeError("Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`") + if ( cast_to is not object and not origin is list @@ -202,12 +249,12 @@ def _parse(self) -> R: and not issubclass(origin, BaseModel) ): raise RuntimeError( - f"Invalid state, expected {cast_to} to be a subclass type of {BaseModel}, {dict}, {list} or {Union}." + f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." ) # split is required to handle cases where additional information is included # in the response, e.g. application/json; charset=utf-8 - content_type, *_ = response.headers.get("content-type").split(";") + content_type, *_ = response.headers.get("content-type", "*").split(";") if content_type != "application/json": if is_basemodel(cast_to): try: @@ -253,15 +300,6 @@ def __init__(self) -> None: ) -def _extract_stream_chunk_type(stream_cls: type) -> type: - args = get_args(stream_cls) - if not args: - raise TypeError( - f"Expected stream_cls to have been given a generic type argument, e.g. Stream[Foo] but received {stream_cls}", - ) - return cast(type, args[0]) - - def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIResponse[R]]: """Higher order function that takes one of our bound API methods and wraps it to support returning the raw `APIResponse` object directly. diff --git a/src/openai/_response.py b/src/openai/_response.py index 15a323afa4..b1e070122f 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -16,25 +16,29 @@ Iterator, AsyncIterator, cast, + overload, ) from typing_extensions import Awaitable, ParamSpec, override, get_origin import anyio import httpx +import pydantic from ._types import NoneType from ._utils import is_given, extract_type_var_from_base from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type from ._exceptions import OpenAIError, APIResponseValidationError if TYPE_CHECKING: from ._models import FinalRequestOptions - from ._base_client import Stream, BaseClient, AsyncStream + from ._base_client import BaseClient P = ParamSpec("P") R = TypeVar("R") +_T = TypeVar("_T") _APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") _AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") @@ -44,7 +48,7 @@ class BaseAPIResponse(Generic[R]): _cast_to: type[R] _client: BaseClient[Any, Any] - _parsed: R | None + _parsed_by_type: dict[type[Any], Any] _is_sse_stream: bool _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None _options: FinalRequestOptions @@ -63,7 +67,7 @@ def __init__( ) -> None: self._cast_to = cast_to self._client = client - self._parsed = None + self._parsed_by_type = {} self._is_sse_stream = stream self._stream_cls = stream_cls self._options = options @@ -116,8 +120,24 @@ def __repr__(self) -> str: f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" ) - def _parse(self) -> R: + def _parse(self, *, to: type[_T] | None = None) -> R | _T: if self._is_sse_stream: + if to: + if not is_stream_class_type(to): + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") + + return cast( + _T, + to( + cast_to=extract_stream_chunk_type( + to, + failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", + ), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + if self._stream_cls: return cast( R, @@ -141,7 +161,7 @@ def _parse(self) -> R: ), ) - cast_to = self._cast_to + cast_to = to if to is not None else self._cast_to if cast_to is NoneType: return cast(R, None) @@ -171,14 +191,9 @@ def _parse(self) -> R: raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) - # The check here is necessary as we are subverting the the type system - # with casts as the relationship between TypeVars and Types are very strict - # which means we must return *exactly* what was input or transform it in a - # way that retains the TypeVar state. As we cannot do that in this function - # then we have to resort to using `cast`. At the time of writing, we know this - # to be safe as we have handled all the types that could be bound to the - # `ResponseT` TypeVar, however if that TypeVar is ever updated in the future, then - # this function would become unsafe but a type checker would not report an error. + if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel): + raise TypeError("Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`") + if ( cast_to is not object and not origin is list @@ -187,12 +202,12 @@ def _parse(self) -> R: and not issubclass(origin, BaseModel) ): raise RuntimeError( - f"Invalid state, expected {cast_to} to be a subclass type of {BaseModel}, {dict}, {list} or {Union}." + f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." ) # split is required to handle cases where additional information is included # in the response, e.g. application/json; charset=utf-8 - content_type, *_ = response.headers.get("content-type").split(";") + content_type, *_ = response.headers.get("content-type", "*").split(";") if content_type != "application/json": if is_basemodel(cast_to): try: @@ -228,22 +243,55 @@ def _parse(self) -> R: class APIResponse(BaseAPIResponse[R]): + @overload + def parse(self, *, to: type[_T]) -> _T: + ... + + @overload def parse(self) -> R: + ... + + def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from openai import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `httpx.Response` """ - if self._parsed is not None: - return self._parsed + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] if not self._is_sse_stream: self.read() - parsed = self._parse() + parsed = self._parse(to=to) if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) - self._parsed = parsed + self._parsed_by_type[cache_key] = parsed return parsed def read(self) -> bytes: @@ -297,22 +345,55 @@ def iter_lines(self) -> Iterator[str]: class AsyncAPIResponse(BaseAPIResponse[R]): + @overload + async def parse(self, *, to: type[_T]) -> _T: + ... + + @overload async def parse(self) -> R: + ... + + async def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from openai import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `httpx.Response` """ - if self._parsed is not None: - return self._parsed + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] if not self._is_sse_stream: await self.read() - parsed = self._parse() + parsed = self._parse(to=to) if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) - self._parsed = parsed + self._parsed_by_type[cache_key] = parsed return parsed async def read(self) -> bytes: @@ -708,26 +789,6 @@ def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: return wrapped -def extract_stream_chunk_type(stream_cls: type) -> type: - """Given a type like `Stream[T]`, returns the generic type variable `T`. - - This also handles the case where a concrete subclass is given, e.g. - ```py - class MyStream(Stream[bytes]): - ... - - extract_stream_chunk_type(MyStream) -> bytes - ``` - """ - from ._base_client import Stream, AsyncStream - - return extract_type_var_from_base( - stream_cls, - index=0, - generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), - ) - - def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: """Given a type like `APIResponse[T]`, returns the generic type variable `T`. diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 85cec70c11..74878fd0a0 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -2,13 +2,14 @@ from __future__ import annotations import json +import inspect from types import TracebackType from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast -from typing_extensions import Self, override +from typing_extensions import Self, TypeGuard, override, get_origin import httpx -from ._utils import is_mapping +from ._utils import is_mapping, extract_type_var_from_base from ._exceptions import APIError if TYPE_CHECKING: @@ -281,3 +282,34 @@ def decode(self, line: str) -> ServerSentEvent | None: pass # Field is ignored. return None + + +def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: + """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" + origin = get_origin(typ) or typ + return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) + + +def extract_stream_chunk_type( + stream_cls: type, + *, + failure_message: str | None = None, +) -> type: + """Given a type like `Stream[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyStream(Stream[bytes]): + ... + + extract_stream_chunk_type(MyStream) -> bytes + ``` + """ + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + failure_message=failure_message, + ) diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py index a020822bc0..c1d1ebb9a4 100644 --- a/src/openai/_utils/_typing.py +++ b/src/openai/_utils/_typing.py @@ -45,7 +45,13 @@ def extract_type_arg(typ: type, index: int) -> type: 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) -> type: +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. @@ -104,4 +110,4 @@ class MyResponse(Foo[_T]): return extracted - raise RuntimeError(f"Could not resolve inner type variable at index {index} for {typ}") + raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py new file mode 100644 index 0000000000..995250a58c --- /dev/null +++ b/tests/test_legacy_response.py @@ -0,0 +1,65 @@ +import json + +import httpx +import pytest +import pydantic + +from openai import OpenAI, BaseModel +from openai._streaming import Stream +from openai._base_client import FinalRequestOptions +from openai._legacy_response import LegacyAPIResponse + + +class PydanticModel(pydantic.BaseModel): + ... + + +def test_response_parse_mismatched_basemodel(client: OpenAI) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`", + ): + response.parse(to=PydanticModel) + + +def test_response_parse_custom_stream(client: OpenAI) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = response.parse(to=Stream[int]) + assert stream._cast_to == int + + +class CustomModel(BaseModel): + foo: str + bar: int + + +def test_response_parse_custom_model(client: OpenAI) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 diff --git a/tests/test_response.py b/tests/test_response.py index 335ca7922a..7c99327b46 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -1,8 +1,11 @@ +import json from typing import List import httpx import pytest +import pydantic +from openai import OpenAI, BaseModel, AsyncOpenAI from openai._response import ( APIResponse, BaseAPIResponse, @@ -11,6 +14,8 @@ AsyncBinaryAPIResponse, extract_response_type, ) +from openai._streaming import Stream +from openai._base_client import FinalRequestOptions class ConcreteBaseAPIResponse(APIResponse[bytes]): @@ -48,3 +53,107 @@ def test_extract_response_type_concrete_subclasses() -> None: def test_extract_response_type_binary_response() -> None: assert extract_response_type(BinaryAPIResponse) == bytes assert extract_response_type(AsyncBinaryAPIResponse) == bytes + + +class PydanticModel(pydantic.BaseModel): + ... + + +def test_response_parse_mismatched_basemodel(client: OpenAI) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`", + ): + response.parse(to=PydanticModel) + + +@pytest.mark.asyncio +async def test_async_response_parse_mismatched_basemodel(async_client: AsyncOpenAI) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`", + ): + await response.parse(to=PydanticModel) + + +def test_response_parse_custom_stream(client: OpenAI) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = response.parse(to=Stream[int]) + assert stream._cast_to == int + + +@pytest.mark.asyncio +async def test_async_response_parse_custom_stream(async_client: AsyncOpenAI) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=async_client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = await response.parse(to=Stream[int]) + assert stream._cast_to == int + + +class CustomModel(BaseModel): + foo: str + bar: int + + +def test_response_parse_custom_model(client: OpenAI) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +@pytest.mark.asyncio +async def test_async_response_parse_custom_model(async_client: AsyncOpenAI) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 From 76382e3c8dcf079116743aba959c64f33274d725 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 29 Jan 2024 18:58:55 +0100 Subject: [PATCH 312/914] chore(internal): cast type in mocked test (#1112) --- tests/test_client.py | 76 +++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 24933456bd..625b822352 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -701,14 +701,17 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No with pytest.raises(APITimeoutError): self.client.post( "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", + body=cast( + object, + dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), ), cast_to=httpx.Response, options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, @@ -724,14 +727,17 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non with pytest.raises(APIStatusError): self.client.post( "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", + body=cast( + object, + dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), ), cast_to=httpx.Response, options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, @@ -1410,14 +1416,17 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) with pytest.raises(APITimeoutError): await self.client.post( "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", + body=cast( + object, + dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), ), cast_to=httpx.Response, options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, @@ -1433,14 +1442,17 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) with pytest.raises(APIStatusError): await self.client.post( "/chat/completions", - body=dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-3.5-turbo", + body=cast( + object, + dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ), ), cast_to=httpx.Response, options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, From 336cf03092376c0b54cc2cfbd78167c1aac01af7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:09:42 +0100 Subject: [PATCH 313/914] chore(internal): support pre-release versioning (#1113) --- release-please-config.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/release-please-config.json b/release-please-config.json index 5c66d801f5..745ef5fd54 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -5,6 +5,8 @@ "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", "include-v-in-tag": true, "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": false, "pull-request-header": "Automated Release PR", From 986f3128f6c851ce9729e579f176de953e315248 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 2 Feb 2024 04:44:03 +0000 Subject: [PATCH 314/914] chore(interal): make link to api.md relative (#1117) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 22e7ac795f..0e06cd5631 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It is generated from our [OpenAPI specification](https://github.com/openai/opena ## Documentation -The REST API documentation can be found [on platform.openai.com](https://platform.openai.com/docs). The full API of this library can be found in [api.md](https://www.github.com/openai/openai-python/blob/main/api.md). +The REST API documentation can be found [on platform.openai.com](https://platform.openai.com/docs). The full API of this library can be found in [api.md](api.md). ## Installation @@ -23,7 +23,7 @@ pip install openai ## Usage -The full API of this library can be found in [api.md](https://www.github.com/openai/openai-python/blob/main/api.md). +The full API of this library can be found in [api.md](api.md). ```python import os From 66bab657c69dfebddbb4372adcdc97788cb3ceb1 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 3 Feb 2024 05:05:10 +0000 Subject: [PATCH 315/914] release: 1.11.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 17 +++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index eb4e0dba72..caf1487126 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.10.0" + ".": "1.11.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a22e062dd..0ce1f737d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 1.11.0 (2024-02-03) + +Full Changelog: [v1.10.0...v1.11.0](https://github.com/openai/openai-python/compare/v1.10.0...v1.11.0) + +### Features + +* **client:** support parsing custom response types ([#1111](https://github.com/openai/openai-python/issues/1111)) ([da00fc3](https://github.com/openai/openai-python/commit/da00fc3f8e0ff13c6c3ca970e4bb86846304bd06)) + + +### Chores + +* **interal:** make link to api.md relative ([#1117](https://github.com/openai/openai-python/issues/1117)) ([4a10879](https://github.com/openai/openai-python/commit/4a108797e46293357601ce933e21b557a5dc6954)) +* **internal:** cast type in mocked test ([#1112](https://github.com/openai/openai-python/issues/1112)) ([99b21e1](https://github.com/openai/openai-python/commit/99b21e1fc681eb10e01d479cc043ad3c89272b1c)) +* **internal:** enable ruff type checking misuse lint rule ([#1106](https://github.com/openai/openai-python/issues/1106)) ([fa63e60](https://github.com/openai/openai-python/commit/fa63e605c82ec78f4fc27469c434b421a08fb909)) +* **internal:** support multipart data with overlapping keys ([#1104](https://github.com/openai/openai-python/issues/1104)) ([455bc9f](https://github.com/openai/openai-python/commit/455bc9f1fd018a32cd604eb4b400e05aa8d71822)) +* **internal:** support pre-release versioning ([#1113](https://github.com/openai/openai-python/issues/1113)) ([dea5b08](https://github.com/openai/openai-python/commit/dea5b08c28d47b331fd44f6920cf9fe322b68e51)) + ## 1.10.0 (2024-01-25) Full Changelog: [v1.9.0...v1.10.0](https://github.com/openai/openai-python/compare/v1.9.0...v1.10.0) diff --git a/pyproject.toml b/pyproject.toml index c088e19264..2070ca82f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.10.0" +version = "1.11.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e9a863539d..fe1bfcc852 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.10.0" # x-release-please-version +__version__ = "1.11.0" # x-release-please-version From 2384e276969d5908eebff2ca12bdfcd69b3cfa93 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:57:58 +0000 Subject: [PATCH 316/914] fix: prevent crash when platform.architecture() is not allowed (#1120) --- src/openai/_base_client.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index d7e5127dd8..73bd2411fd 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1836,8 +1836,12 @@ def __str__(self) -> str: def get_platform() -> Platform: - system = platform.system().lower() - platform_name = platform.platform().lower() + try: + system = platform.system().lower() + platform_name = platform.platform().lower() + except Exception: + return "Unknown" + if "iphone" in platform_name or "ipad" in platform_name: # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 # system is Darwin and platform_name is a string like: @@ -1880,8 +1884,8 @@ def platform_headers(version: str) -> Dict[str, str]: "X-Stainless-Package-Version": version, "X-Stainless-OS": str(get_platform()), "X-Stainless-Arch": str(get_architecture()), - "X-Stainless-Runtime": platform.python_implementation(), - "X-Stainless-Runtime-Version": platform.python_version(), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), } @@ -1897,9 +1901,27 @@ def __str__(self) -> str: Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] +def get_python_runtime() -> str: + try: + return platform.python_implementation() + except Exception: + return "unknown" + + +def get_python_version() -> str: + try: + return platform.python_version() + except Exception: + return "unknown" + + def get_architecture() -> Arch: - python_bitness, _ = platform.architecture() - machine = platform.machine().lower() + try: + python_bitness, _ = platform.architecture() + machine = platform.machine().lower() + except Exception: + return "unknown" + if machine in ("arm64", "aarch64"): return "arm64" From d231d1fa783967c1d3a1db3ba1b52647fff148ac Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:58:26 +0000 Subject: [PATCH 317/914] release: 1.11.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index caf1487126..271a68cfd8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.11.0" + ".": "1.11.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ce1f737d9..4ac4be39fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.11.1 (2024-02-04) + +Full Changelog: [v1.11.0...v1.11.1](https://github.com/openai/openai-python/compare/v1.11.0...v1.11.1) + +### Bug Fixes + +* prevent crash when platform.architecture() is not allowed ([#1120](https://github.com/openai/openai-python/issues/1120)) ([9490554](https://github.com/openai/openai-python/commit/949055488488e93597cbc6c2cdd81f14f203e53b)) + ## 1.11.0 (2024-02-03) Full Changelog: [v1.10.0...v1.11.0](https://github.com/openai/openai-python/compare/v1.10.0...v1.11.0) diff --git a/pyproject.toml b/pyproject.toml index 2070ca82f2..20371e0ef9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.11.0" +version = "1.11.1" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index fe1bfcc852..8af0cd2490 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.11.0" # x-release-please-version +__version__ = "1.11.1" # x-release-please-version From 489dadfb4ddf08007c6054acf05e782f6ef4f9bb Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 6 Feb 2024 01:09:45 +0000 Subject: [PATCH 318/914] feat(api): add `timestamp_granularities`, add `gpt-3.5-turbo-0125` model (#1125) --- src/openai/resources/audio/transcriptions.py | 14 ++++++++++++- src/openai/resources/chat/completions.py | 20 +++++++++++++------ .../audio/transcription_create_params.py | 15 ++++++++++++-- .../types/chat/completion_create_params.py | 3 ++- .../audio/test_transcriptions.py | 2 ++ 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 2c167be395..275098ce88 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Mapping, cast +from typing import List, Union, Mapping, cast from typing_extensions import Literal import httpx @@ -39,6 +39,7 @@ def create( prompt: str | NotGiven = NOT_GIVEN, response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -74,6 +75,10 @@ def create( [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit. + timestamp_granularities: The timestamp granularities to populate for this transcription. Any of these + options: `word`, or `segment`. Note: There is no additional latency for segment + timestamps, but generating word timestamps incurs additional latency. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -90,6 +95,7 @@ def create( "prompt": prompt, "response_format": response_format, "temperature": temperature, + "timestamp_granularities": timestamp_granularities, } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) @@ -127,6 +133,7 @@ async def create( prompt: str | NotGiven = NOT_GIVEN, response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -162,6 +169,10 @@ async def create( [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit. + timestamp_granularities: The timestamp granularities to populate for this transcription. Any of these + options: `word`, or `segment`. Note: There is no additional latency for segment + timestamps, but generating word timestamps incurs additional latency. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -178,6 +189,7 @@ async def create( "prompt": prompt, "response_format": response_format, "temperature": temperature, + "timestamp_granularities": timestamp_granularities, } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 45521833ad..edc243e101 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -61,6 +61,7 @@ def create( "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ], @@ -155,7 +156,7 @@ def create( response_format: An object specifying the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -250,6 +251,7 @@ def create( "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ], @@ -351,7 +353,7 @@ def create( response_format: An object specifying the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -439,6 +441,7 @@ def create( "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ], @@ -540,7 +543,7 @@ def create( response_format: An object specifying the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -628,6 +631,7 @@ def create( "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ], @@ -724,6 +728,7 @@ async def create( "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ], @@ -818,7 +823,7 @@ async def create( response_format: An object specifying the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -913,6 +918,7 @@ async def create( "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ], @@ -1014,7 +1020,7 @@ async def create( response_format: An object specifying the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1102,6 +1108,7 @@ async def create( "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ], @@ -1203,7 +1210,7 @@ async def create( response_format: An object specifying the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1291,6 +1298,7 @@ async def create( "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ], diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index 7bd70d7b48..2d0a218f33 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -2,10 +2,11 @@ from __future__ import annotations -from typing import Union -from typing_extensions import Literal, Required, TypedDict +from typing import List, Union +from typing_extensions import Literal, Required, Annotated, TypedDict from ..._types import FileTypes +from ..._utils import PropertyInfo __all__ = ["TranscriptionCreateParams"] @@ -50,3 +51,13 @@ class TranscriptionCreateParams(TypedDict, total=False): [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit. """ + + timestamp_granularities: Annotated[ + List[Literal["word", "segment"]], PropertyInfo(alias="timestamp_granularities[]") + ] + """The timestamp granularities to populate for this transcription. + + Any of these options: `word`, or `segment`. Note: There is no additional latency + for segment timestamps, but generating word timestamps incurs additional + latency. + """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 3ea14d82b3..dbc49594fe 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -47,6 +47,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): "gpt-3.5-turbo-0301", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", "gpt-3.5-turbo-16k-0613", ], ] @@ -137,7 +138,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index d957871abc..80e364b484 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -34,6 +34,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: prompt="string", response_format="json", temperature=0, + timestamp_granularities=["word", "segment"], ) assert_matches_type(Transcription, transcription, path=["response"]) @@ -84,6 +85,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> prompt="string", response_format="json", temperature=0, + timestamp_granularities=["word", "segment"], ) assert_matches_type(Transcription, transcription, path=["response"]) From adb0b31abca69eaafa0b4b40ef457913d4597ad6 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:50:54 +0000 Subject: [PATCH 319/914] chore(internal): support serialising iterable types (#1127) --- src/openai/_utils/__init__.py | 2 ++ src/openai/_utils/_transform.py | 9 ++++++++- src/openai/_utils/_typing.py | 9 ++++++++- src/openai/_utils/_utils.py | 4 ++++ tests/test_transform.py | 34 ++++++++++++++++++++++++++++++++- 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 0fb811a945..b5790a879f 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -9,6 +9,7 @@ is_mapping as is_mapping, is_tuple_t as is_tuple_t, parse_date as parse_date, + is_iterable as is_iterable, is_sequence as is_sequence, coerce_float as coerce_float, is_mapping_t as is_mapping_t, @@ -33,6 +34,7 @@ is_list_type as is_list_type, is_union_type as is_union_type, extract_type_arg as extract_type_arg, + is_iterable_type as is_iterable_type, is_required_type as is_required_type, is_annotated_type as is_annotated_type, strip_annotated_type as strip_annotated_type, diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 3a1c14969b..2cb7726c73 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -9,11 +9,13 @@ from ._utils import ( is_list, is_mapping, + is_iterable, ) from ._typing import ( is_list_type, is_union_type, extract_type_arg, + is_iterable_type, is_required_type, is_annotated_type, strip_annotated_type, @@ -157,7 +159,12 @@ def _transform_recursive( if is_typeddict(stripped_type) and is_mapping(data): return _transform_typeddict(data, stripped_type) - if is_list_type(stripped_type) and is_list(data): + 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)) + ): inner_type = extract_type_arg(stripped_type, 0) return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py index c1d1ebb9a4..c036991f04 100644 --- a/src/openai/_utils/_typing.py +++ b/src/openai/_utils/_typing.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, TypeVar, cast +from typing import Any, TypeVar, Iterable, cast +from collections import abc as _c_abc from typing_extensions import Required, Annotated, get_args, get_origin from .._types import InheritsGeneric @@ -15,6 +16,12 @@ 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)) diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 1c5c21a8ea..93c95517a9 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -164,6 +164,10 @@ 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: diff --git a/tests/test_transform.py b/tests/test_transform.py index c4dffb3bb0..6ed67d49a7 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List, Union, Optional +from typing import Any, List, Union, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict @@ -265,3 +265,35 @@ def test_pydantic_default_field() -> None: assert model.with_none_default == "bar" assert model.with_str_default == "baz" assert transform(model, Any) == {"with_none_default": "bar", "with_str_default": "baz"} + + +class TypedDictIterableUnion(TypedDict): + foo: Annotated[Union[Bar8, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +class Bar8(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz8(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +def test_iterable_of_dictionaries() -> None: + assert transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "bar"}]} + assert cast(Any, transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion)) == {"FOO": [{"fooBaz": "bar"}]} + + def my_iter() -> Iterable[Baz8]: + yield {"foo_baz": "hello"} + yield {"foo_baz": "world"} + + assert transform({"foo": my_iter()}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}]} + + +class TypedDictIterableUnionStr(TypedDict): + foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +def test_iterable_union_str() -> None: + assert transform({"foo": "bar"}, TypedDictIterableUnionStr) == {"FOO": "bar"} + assert cast(Any, transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]])) == [{"fooBaz": "bar"}] From 0f0934699ddb22de152a71c2b1d2d2382006a934 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:29:51 +0000 Subject: [PATCH 320/914] chore(internal): add lint command (#1128) --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 20371e0ef9..af7950f58a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,10 @@ format = { chain = [ "format:ruff" = "ruff format" "format:isort" = "isort ." +"lint" = { chain = [ + "check:ruff", + "typecheck", +]} "check:ruff" = "ruff ." "fix:ruff" = "ruff --fix ." From 2dc397628a7e13734086a5fe703f0e7d0c891e90 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:21:59 +0000 Subject: [PATCH 321/914] fix(types): loosen most List params types to Iterable (#1129) --- .../resources/beta/assistants/assistants.py | 10 ++-- .../resources/beta/threads/runs/runs.py | 10 ++-- src/openai/resources/beta/threads/threads.py | 10 ++-- src/openai/resources/chat/completions.py | 50 +++++++++---------- src/openai/resources/completions.py | 18 +++---- src/openai/resources/embeddings.py | 6 +-- .../types/beta/assistant_create_params.py | 4 +- .../types/beta/assistant_update_params.py | 4 +- .../beta/thread_create_and_run_params.py | 6 +-- src/openai/types/beta/thread_create_params.py | 4 +- .../types/beta/threads/run_create_params.py | 4 +- .../threads/run_submit_tool_outputs_params.py | 4 +- ...chat_completion_assistant_message_param.py | 4 +- .../chat_completion_user_message_param.py | 4 +- .../types/chat/completion_create_params.py | 8 +-- src/openai/types/completion_create_params.py | 4 +- src/openai/types/embedding_create_params.py | 4 +- 17 files changed, 77 insertions(+), 77 deletions(-) diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 3a2418ad90..e926c31642 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Optional +from typing import List, Iterable, Optional from typing_extensions import Literal import httpx @@ -59,7 +59,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - tools: List[assistant_create_params.Tool] | NotGiven = NOT_GIVEN, + tools: Iterable[assistant_create_params.Tool] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -169,7 +169,7 @@ def update( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - tools: List[assistant_update_params.Tool] | NotGiven = NOT_GIVEN, + tools: Iterable[assistant_update_params.Tool] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -362,7 +362,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - tools: List[assistant_create_params.Tool] | NotGiven = NOT_GIVEN, + tools: Iterable[assistant_create_params.Tool] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -472,7 +472,7 @@ async def update( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - tools: List[assistant_update_params.Tool] | NotGiven = NOT_GIVEN, + tools: Iterable[assistant_update_params.Tool] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 0ed48b4792..9b18336010 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Optional +from typing import Iterable, Optional from typing_extensions import Literal import httpx @@ -59,7 +59,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, - tools: Optional[List[run_create_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[run_create_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -316,7 +316,7 @@ def submit_tool_outputs( run_id: str, *, thread_id: str, - tool_outputs: List[run_submit_tool_outputs_params.ToolOutput], + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -380,7 +380,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, - tools: Optional[List[run_create_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[run_create_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -637,7 +637,7 @@ async def submit_tool_outputs( run_id: str, *, thread_id: str, - tool_outputs: List[run_submit_tool_outputs_params.ToolOutput], + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 0372ae2f66..dd079ac533 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Optional +from typing import Iterable, Optional import httpx @@ -65,7 +65,7 @@ def with_streaming_response(self) -> ThreadsWithStreamingResponse: def create( self, *, - messages: List[thread_create_params.Message] | NotGiven = NOT_GIVEN, + messages: Iterable[thread_create_params.Message] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -227,7 +227,7 @@ def create_and_run( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tools: Optional[List[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -310,7 +310,7 @@ def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse: async def create( self, *, - messages: List[thread_create_params.Message] | NotGiven = NOT_GIVEN, + messages: Iterable[thread_create_params.Message] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -472,7 +472,7 @@ async def create_and_run( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tools: Optional[List[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index edc243e101..0011d75e6e 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional, overload +from typing import Dict, List, Union, Iterable, Optional, overload from typing_extensions import Literal import httpx @@ -42,7 +42,7 @@ def with_streaming_response(self) -> CompletionsWithStreamingResponse: def create( self, *, - messages: List[ChatCompletionMessageParam], + messages: Iterable[ChatCompletionMessageParam], model: Union[ str, Literal[ @@ -67,7 +67,7 @@ def create( ], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -79,7 +79,7 @@ def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -232,7 +232,7 @@ def create( def create( self, *, - messages: List[ChatCompletionMessageParam], + messages: Iterable[ChatCompletionMessageParam], model: Union[ str, Literal[ @@ -258,7 +258,7 @@ def create( stream: Literal[True], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -269,7 +269,7 @@ def create( stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -422,7 +422,7 @@ def create( def create( self, *, - messages: List[ChatCompletionMessageParam], + messages: Iterable[ChatCompletionMessageParam], model: Union[ str, Literal[ @@ -448,7 +448,7 @@ def create( stream: bool, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -459,7 +459,7 @@ def create( stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -612,7 +612,7 @@ def create( def create( self, *, - messages: List[ChatCompletionMessageParam], + messages: Iterable[ChatCompletionMessageParam], model: Union[ str, Literal[ @@ -637,7 +637,7 @@ def create( ], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -649,7 +649,7 @@ def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -709,7 +709,7 @@ def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: async def create( self, *, - messages: List[ChatCompletionMessageParam], + messages: Iterable[ChatCompletionMessageParam], model: Union[ str, Literal[ @@ -734,7 +734,7 @@ async def create( ], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -746,7 +746,7 @@ async def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -899,7 +899,7 @@ async def create( async def create( self, *, - messages: List[ChatCompletionMessageParam], + messages: Iterable[ChatCompletionMessageParam], model: Union[ str, Literal[ @@ -925,7 +925,7 @@ async def create( stream: Literal[True], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -936,7 +936,7 @@ async def create( stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -1089,7 +1089,7 @@ async def create( async def create( self, *, - messages: List[ChatCompletionMessageParam], + messages: Iterable[ChatCompletionMessageParam], model: Union[ str, Literal[ @@ -1115,7 +1115,7 @@ async def create( stream: bool, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -1126,7 +1126,7 @@ async def create( stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -1279,7 +1279,7 @@ async def create( async def create( self, *, - messages: List[ChatCompletionMessageParam], + messages: Iterable[ChatCompletionMessageParam], model: Union[ str, Literal[ @@ -1304,7 +1304,7 @@ async def create( ], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: List[completion_create_params.Function] | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -1316,7 +1316,7 @@ async def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 3d2e10230a..af2d6e2e51 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional, overload +from typing import Dict, List, Union, Iterable, Optional, overload from typing_extensions import Literal import httpx @@ -36,7 +36,7 @@ def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], List[int], List[List[int]], None], + prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -184,7 +184,7 @@ def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], List[int], List[List[int]], None], + prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], stream: Literal[True], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -332,7 +332,7 @@ def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], List[int], List[List[int]], None], + prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], stream: bool, best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -480,7 +480,7 @@ def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], List[int], List[List[int]], None], + prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -550,7 +550,7 @@ async def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], List[int], List[List[int]], None], + prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -698,7 +698,7 @@ async def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], List[int], List[List[int]], None], + prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], stream: Literal[True], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -846,7 +846,7 @@ async def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], List[int], List[List[int]], None], + prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], stream: bool, best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -994,7 +994,7 @@ async def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], List[int], List[List[int]], None], + prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 857bfc7702..cfef025bc2 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -3,7 +3,7 @@ from __future__ import annotations import base64 -from typing import List, Union, cast +from typing import List, Union, Iterable, cast from typing_extensions import Literal import httpx @@ -35,7 +35,7 @@ def with_streaming_response(self) -> EmbeddingsWithStreamingResponse: def create( self, *, - input: Union[str, List[str], List[int], List[List[int]]], + input: Union[str, List[str], Iterable[int], Iterable[Iterable[int]]], model: Union[str, Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]], dimensions: int | NotGiven = NOT_GIVEN, encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, @@ -136,7 +136,7 @@ def with_streaming_response(self) -> AsyncEmbeddingsWithStreamingResponse: async def create( self, *, - input: Union[str, List[str], List[int], List[List[int]]], + input: Union[str, List[str], Iterable[int], Iterable[Iterable[int]]], model: Union[str, Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]], dimensions: int | NotGiven = NOT_GIVEN, encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 539897a7ba..c49d6f6950 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from ...types import shared_params @@ -54,7 +54,7 @@ class AssistantCreateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" - tools: List[Tool] + tools: Iterable[Tool] """A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index dfb5d4c553..c5ccde62c5 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from ...types import shared_params @@ -56,7 +56,7 @@ class AssistantUpdateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" - tools: List[Tool] + tools: Iterable[Tool] """A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 9f58dcd875..cc1051b3d6 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from ...types import shared_params @@ -51,7 +51,7 @@ class ThreadCreateAndRunParams(TypedDict, total=False): thread: Thread """If no thread is provided, an empty thread will be created.""" - tools: Optional[List[Tool]] + tools: Optional[Iterable[Tool]] """Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -86,7 +86,7 @@ class ThreadMessage(TypedDict, total=False): class Thread(TypedDict, total=False): - messages: List[ThreadMessage] + messages: Iterable[ThreadMessage] """ A list of [messages](https://platform.openai.com/docs/api-reference/messages) to start the thread with. diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index d2ec78bbc3..e78276e839 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -2,14 +2,14 @@ from __future__ import annotations -from typing import List, Optional +from typing import List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ThreadCreateParams", "Message"] class ThreadCreateParams(TypedDict, total=False): - messages: List[Message] + messages: Iterable[Message] """ A list of [messages](https://platform.openai.com/docs/api-reference/messages) to start the thread with. diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index a4f41a9338..b92649aa06 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from ....types import shared_params @@ -54,7 +54,7 @@ class RunCreateParams(TypedDict, total=False): assistant will be used. """ - tools: Optional[List[Tool]] + tools: Optional[Iterable[Tool]] """Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. diff --git a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py index a960f0f06f..3b303a33fc 100644 --- a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py +++ b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List +from typing import Iterable from typing_extensions import Required, TypedDict __all__ = ["RunSubmitToolOutputsParams", "ToolOutput"] @@ -11,7 +11,7 @@ class RunSubmitToolOutputsParams(TypedDict, total=False): thread_id: Required[str] - tool_outputs: Required[List[ToolOutput]] + tool_outputs: Required[Iterable[ToolOutput]] """A list of tools for which the outputs are being submitted.""" diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 72a5bff83b..7377139bf5 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Optional +from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .chat_completion_message_tool_call_param import ChatCompletionMessageToolCallParam @@ -47,5 +47,5 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): role. """ - tool_calls: List[ChatCompletionMessageToolCallParam] + tool_calls: Iterable[ChatCompletionMessageToolCallParam] """The tool calls generated by the model, such as function calls.""" diff --git a/src/openai/types/chat/chat_completion_user_message_param.py b/src/openai/types/chat/chat_completion_user_message_param.py index 07be67c405..cb8ca19bf0 100644 --- a/src/openai/types/chat/chat_completion_user_message_param.py +++ b/src/openai/types/chat/chat_completion_user_message_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union +from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict from .chat_completion_content_part_param import ChatCompletionContentPartParam @@ -11,7 +11,7 @@ class ChatCompletionUserMessageParam(TypedDict, total=False): - content: Required[Union[str, List[ChatCompletionContentPartParam]]] + content: Required[Union[str, Iterable[ChatCompletionContentPartParam]]] """The contents of the user message.""" role: Required[Literal["user"]] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index dbc49594fe..e02a81bc51 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from ...types import shared_params @@ -22,7 +22,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): - messages: Required[List[ChatCompletionMessageParam]] + messages: Required[Iterable[ChatCompletionMessageParam]] """A list of messages comprising the conversation so far. [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). @@ -81,7 +81,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): functions are present. """ - functions: List[Function] + functions: Iterable[Function] """Deprecated in favor of `tools`. A list of functions the model may generate JSON inputs for. @@ -186,7 +186,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): functions are present. """ - tools: List[ChatCompletionToolParam] + tools: Iterable[ChatCompletionToolParam] """A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index e14c2860df..afbc9c549f 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["CompletionCreateParamsBase", "CompletionCreateParamsNonStreaming", "CompletionCreateParamsStreaming"] @@ -19,7 +19,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): descriptions of them. """ - prompt: Required[Union[str, List[str], List[int], List[List[int]], None]] + prompt: Required[Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None]] """ The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py index 66ac60511c..a549dc94c4 100644 --- a/src/openai/types/embedding_create_params.py +++ b/src/openai/types/embedding_create_params.py @@ -2,14 +2,14 @@ from __future__ import annotations -from typing import List, Union +from typing import List, Union, Iterable from typing_extensions import Literal, Required, TypedDict __all__ = ["EmbeddingCreateParams"] class EmbeddingCreateParams(TypedDict, total=False): - input: Required[Union[str, List[str], List[int], List[List[int]]]] + input: Required[Union[str, List[str], Iterable[int], Iterable[Iterable[int]]]] """Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array From a061daf64616dba32d229f491fff009e20232645 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 7 Feb 2024 11:13:21 +0000 Subject: [PATCH 322/914] feat(cli/images): add support for `--model` arg (#1132) https://github.com/openai/openai-python/issues/1130 --- src/openai/cli/_api/image.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/openai/cli/_api/image.py b/src/openai/cli/_api/image.py index e6149eeac4..3e2a0a90f1 100644 --- a/src/openai/cli/_api/image.py +++ b/src/openai/cli/_api/image.py @@ -14,6 +14,7 @@ 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") @@ -21,6 +22,7 @@ def register(subparser: _SubParsersAction[ArgumentParser]) -> None: 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( @@ -42,6 +44,7 @@ def register(subparser: _SubParsersAction[ArgumentParser]) -> None: 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", @@ -60,6 +63,7 @@ class CLIImageCreateArgs(BaseModel): num_images: int size: str response_format: str + model: NotGivenOr[str] = NOT_GIVEN class CLIImageCreateVariationArgs(BaseModel): @@ -67,6 +71,7 @@ class CLIImageCreateVariationArgs(BaseModel): num_images: int size: str response_format: str + model: NotGivenOr[str] = NOT_GIVEN class CLIImageEditArgs(BaseModel): @@ -76,12 +81,14 @@ class CLIImageEditArgs(BaseModel): 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 @@ -97,6 +104,7 @@ def create_variation(args: CLIImageCreateVariationArgs) -> None: 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 @@ -118,6 +126,7 @@ def edit(args: CLIImageEditArgs) -> None: 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, From 93f3d3c3e4cdada40d9199290a907f107c2efba3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:50:04 +0000 Subject: [PATCH 323/914] docs: add CONTRIBUTING.md (#1138) --- CONTRIBUTING.md | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..914ab67053 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,125 @@ +## Setting up the environment + +### With Rye + +We use [Rye](https://rye-up.com/) to manage dependencies so we highly recommend [installing it](https://rye-up.com/guide/installation/) as it will automatically provision a Python environment with the expected Python version. + +After installing Rye, you'll just have to run this command: + +```sh +$ rye sync --all-features +``` + +You can then run scripts using `rye run python script.py` or by activating the virtual environment: + +```sh +$ rye shell +# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work +$ source .venv/bin/activate + +# now you can omit the `rye run` prefix +$ python script.py +``` + +### Without Rye + +Alternatively if you don't want to install `Rye`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command: + +```sh +$ pip install -r requirements-dev.lock +``` + +## Modifying/Adding code + +Most of the SDK is generated code, and any modified code will be overridden on the next generation. The +`src/openai/lib/` and `examples/` directories are exceptions and will never be overridden. + +## Adding and running examples + +All files in the `examples/` directory are not modified by the Stainless generator and can be freely edited or +added to. + +```bash +# add an example to examples/.py + +#!/usr/bin/env -S rye run python +… +``` + +``` +chmod +x examples/.py +# run the example against your api +./examples/.py +``` + +## Using the repository from source + +If you’d like to use the repository from source, you can either install from git or link to a cloned repository: + +To install via git: + +```bash +pip install git+ssh://git@github.com:openai/openai-python.git +``` + +Alternatively, you can build from source and install the wheel file: + +Building this package will create two files in the `dist/` directory, a `.tar.gz` containing the source files and a `.whl` that can be used to install the package efficiently. + +To create a distributable version of the library, all you have to do is run this command: + +```bash +rye build +# or +python -m build +``` + +Then to install: + +```sh +pip install ./path-to-wheel-file.whl +``` + +## Running tests + +Most tests will require you to [setup a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. + +```bash +# you will need npm installed +npx prism path/to/your/openapi.yml +``` + +```bash +rye run pytest +``` + +## Linting and formatting + +This repository uses [ruff](https://github.com/astral-sh/ruff) and +[black](https://github.com/psf/black) to format the code in the repository. + +To lint: + +```bash +rye run lint +``` + +To format and fix all ruff issues automatically: + +```bash +rye run format +``` + +## Publishing and releases + +Changes made to this repository via the automated release PR pipeline should publish to PyPI automatically. If +the changes aren't made through the automated pipeline, you may want to make releases manually. + +### Publish with a GitHub workflow + +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml). This will require a setup organization or repository secret to be set up. + +### Publish manually + +If you need to manually release a package, you can run the `bin/publish-pypi` script with an `PYPI_TOKEN` set on +the environment. From d6f9334ec83711a5a6a244150782590a87054994 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:38:05 -0500 Subject: [PATCH 324/914] fix: remove double brackets from timestamp_granularities param (#1140) --- src/openai/types/audio/transcription_create_params.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index 2d0a218f33..5a90822144 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -3,10 +3,9 @@ from __future__ import annotations from typing import List, Union -from typing_extensions import Literal, Required, Annotated, TypedDict +from typing_extensions import Literal, Required, TypedDict from ..._types import FileTypes -from ..._utils import PropertyInfo __all__ = ["TranscriptionCreateParams"] @@ -52,9 +51,7 @@ class TranscriptionCreateParams(TypedDict, total=False): automatically increase the temperature until certain thresholds are hit. """ - timestamp_granularities: Annotated[ - List[Literal["word", "segment"]], PropertyInfo(alias="timestamp_granularities[]") - ] + timestamp_granularities: List[Literal["word", "segment"]] """The timestamp granularities to populate for this transcription. Any of these options: `word`, or `segment`. Note: There is no additional latency From 7f9e85017a0959e3ba07834880d92c748f8f67ab Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:47:36 -0500 Subject: [PATCH 325/914] release: 1.12.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 26 ++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 271a68cfd8..de0960aba8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.11.1" + ".": "1.12.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ac4be39fb..5ef0b80e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## 1.12.0 (2024-02-08) + +Full Changelog: [v1.11.1...v1.12.0](https://github.com/openai/openai-python/compare/v1.11.1...v1.12.0) + +### Features + +* **api:** add `timestamp_granularities`, add `gpt-3.5-turbo-0125` model ([#1125](https://github.com/openai/openai-python/issues/1125)) ([1ecf8f6](https://github.com/openai/openai-python/commit/1ecf8f6b12323ed09fb6a2815c85b9533ee52a50)) +* **cli/images:** add support for `--model` arg ([#1132](https://github.com/openai/openai-python/issues/1132)) ([0d53866](https://github.com/openai/openai-python/commit/0d5386615cda7cd50d5db90de2119b84dba29519)) + + +### Bug Fixes + +* remove double brackets from timestamp_granularities param ([#1140](https://github.com/openai/openai-python/issues/1140)) ([3db0222](https://github.com/openai/openai-python/commit/3db022216a81fa86470b53ec1246669bc7b17897)) +* **types:** loosen most List params types to Iterable ([#1129](https://github.com/openai/openai-python/issues/1129)) ([bdb31a3](https://github.com/openai/openai-python/commit/bdb31a3b1db6ede4e02b3c951c4fd23f70260038)) + + +### Chores + +* **internal:** add lint command ([#1128](https://github.com/openai/openai-python/issues/1128)) ([4c021c0](https://github.com/openai/openai-python/commit/4c021c0ab0151c2ec092d860c9b60e22e658cd03)) +* **internal:** support serialising iterable types ([#1127](https://github.com/openai/openai-python/issues/1127)) ([98d4e59](https://github.com/openai/openai-python/commit/98d4e59afcf2d65d4e660d91eb9462240ef5cd63)) + + +### Documentation + +* add CONTRIBUTING.md ([#1138](https://github.com/openai/openai-python/issues/1138)) ([79c8f0e](https://github.com/openai/openai-python/commit/79c8f0e8bf5470e2e31e781e8d279331e89ddfbe)) + ## 1.11.1 (2024-02-04) Full Changelog: [v1.11.0...v1.11.1](https://github.com/openai/openai-python/compare/v1.11.0...v1.11.1) diff --git a/pyproject.toml b/pyproject.toml index af7950f58a..163297ee2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.11.1" +version = "1.12.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 8af0cd2490..6db2292c7b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.11.1" # x-release-please-version +__version__ = "1.12.0" # x-release-please-version From fc4dafceb7efde578e40c460a488e06669f8b07f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:01:35 -0500 Subject: [PATCH 326/914] feat(api): updates (#1146) --- src/openai/resources/chat/completions.py | 28 +++++++++++++++++++ .../types/chat/completion_create_params.py | 6 ++++ tests/api_resources/chat/test_completions.py | 4 +++ 3 files changed, 38 insertions(+) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 0011d75e6e..caf712c604 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -68,6 +68,7 @@ def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -122,6 +123,9 @@ def create( A list of functions the model may generate JSON inputs for. + instance_id: An unique identifier to a custom instance to execute the request. The requesting + organization is required to have access to the instance. + logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -259,6 +263,7 @@ def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -319,6 +324,9 @@ def create( A list of functions the model may generate JSON inputs for. + instance_id: An unique identifier to a custom instance to execute the request. The requesting + organization is required to have access to the instance. + logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -449,6 +457,7 @@ def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -509,6 +518,9 @@ def create( A list of functions the model may generate JSON inputs for. + instance_id: An unique identifier to a custom instance to execute the request. The requesting + organization is required to have access to the instance. + logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -638,6 +650,7 @@ def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -669,6 +682,7 @@ def create( "frequency_penalty": frequency_penalty, "function_call": function_call, "functions": functions, + "instance_id": instance_id, "logit_bias": logit_bias, "logprobs": logprobs, "max_tokens": max_tokens, @@ -735,6 +749,7 @@ async def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -789,6 +804,9 @@ async def create( A list of functions the model may generate JSON inputs for. + instance_id: An unique identifier to a custom instance to execute the request. The requesting + organization is required to have access to the instance. + logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -926,6 +944,7 @@ async def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -986,6 +1005,9 @@ async def create( A list of functions the model may generate JSON inputs for. + instance_id: An unique identifier to a custom instance to execute the request. The requesting + organization is required to have access to the instance. + logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -1116,6 +1138,7 @@ async def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -1176,6 +1199,9 @@ async def create( A list of functions the model may generate JSON inputs for. + instance_id: An unique identifier to a custom instance to execute the request. The requesting + organization is required to have access to the instance. + logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -1305,6 +1331,7 @@ async def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -1336,6 +1363,7 @@ async def create( "frequency_penalty": frequency_penalty, "function_call": function_call, "functions": functions, + "instance_id": instance_id, "logit_bias": logit_bias, "logprobs": logprobs, "max_tokens": max_tokens, diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index e02a81bc51..2c80d84e6a 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -87,6 +87,12 @@ class CompletionCreateParamsBase(TypedDict, total=False): A list of functions the model may generate JSON inputs for. """ + instance_id: Optional[str] + """An unique identifier to a custom instance to execute the request. + + The requesting organization is required to have access to the instance. + """ + logit_bias: Optional[Dict[str, int]] """Modify the likelihood of specified tokens appearing in the completion. diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 4fa069ba2e..856b7dc12c 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -50,6 +50,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, } ], + instance_id="string", logit_bias={"foo": 0}, logprobs=True, max_tokens=0, @@ -164,6 +165,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, } ], + instance_id="string", logit_bias={"foo": 0}, logprobs=True, max_tokens=0, @@ -280,6 +282,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "parameters": {"foo": "bar"}, } ], + instance_id="string", logit_bias={"foo": 0}, logprobs=True, max_tokens=0, @@ -394,6 +397,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "parameters": {"foo": "bar"}, } ], + instance_id="string", logit_bias={"foo": 0}, logprobs=True, max_tokens=0, From 98ac9df81be7918216a86dd7b3f140e99493e5eb Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 14 Feb 2024 15:16:37 -0500 Subject: [PATCH 327/914] chore(ci): move github release logic to github app (#1155) --- .github/workflows/create-releases.yml | 39 --------------------------- .github/workflows/publish-pypi.yml | 8 ++++-- .github/workflows/release-doctor.yml | 1 - bin/check-release-environment | 4 --- 4 files changed, 6 insertions(+), 46 deletions(-) delete mode 100644 .github/workflows/create-releases.yml diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml deleted file mode 100644 index c8c94db105..0000000000 --- a/.github/workflows/create-releases.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Create releases -on: - schedule: - - cron: '0 5 * * *' # every day at 5am UTC - push: - branches: - - main - -jobs: - release: - name: release - if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' - runs-on: ubuntu-latest - environment: publish - - steps: - - uses: actions/checkout@v3 - - - uses: stainless-api/trigger-release-please@v1 - id: release - with: - repo: ${{ github.event.repository.full_name }} - stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} - - - name: Install Rye - if: ${{ steps.release.outputs.releases_created }} - run: | - curl -sSf https://rye-up.com/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: 0.15.2 - RYE_INSTALL_OPTION: "--yes" - - - name: Publish to PyPI - if: ${{ steps.release.outputs.releases_created }} - run: | - bash ./bin/publish-pypi - env: - PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 026ed29c22..e690e0d985 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -1,9 +1,13 @@ -# workflow for re-running publishing to PyPI in case it fails for some reason -# you can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to PyPI in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml name: Publish PyPI on: workflow_dispatch: + release: + types: [published] + jobs: publish: name: publish diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 108aa5973a..20af127ffc 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -19,5 +19,4 @@ jobs: run: | bash ./bin/check-release-environment env: - STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }} PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/bin/check-release-environment b/bin/check-release-environment index b0c8d34f0c..3c9b2dd4ed 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -2,10 +2,6 @@ errors=() -if [ -z "${STAINLESS_API_KEY}" ]; then - errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.") -fi - if [ -z "${PYPI_TOKEN}" ]; then errors+=("The OPENAI_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi From d63eac7f0649e6c65fde946383a93ce10c7067df Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:37:28 -0500 Subject: [PATCH 328/914] chore(internal): refactor release environment script (#1158) --- bin/check-release-environment | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/check-release-environment b/bin/check-release-environment index 3c9b2dd4ed..5471b69edb 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -6,9 +6,9 @@ if [ -z "${PYPI_TOKEN}" ]; then errors+=("The OPENAI_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi -len=${#errors[@]} +lenErrors=${#errors[@]} -if [[ len -gt 0 ]]; then +if [[ lenErrors -gt 0 ]]; then echo -e "Found the following errors in the release environment:\n" for error in "${errors[@]}"; do From 9741b1ca0ed17bbf4669b582f6a98f65628752af Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 16 Feb 2024 19:00:54 -0500 Subject: [PATCH 329/914] chore(client): use correct accept headers for binary data (#1161) --- src/openai/resources/audio/speech.py | 2 ++ src/openai/resources/files.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 4e94d4aaef..fbdc1ecff1 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -78,6 +78,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} return self._post( "/audio/speech", body=maybe_transform( @@ -149,6 +150,7 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} return await self._post( "/audio/speech", body=maybe_transform( diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 58a2a217c7..8b2bc4f181 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -238,6 +238,7 @@ def content( """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return self._get( f"/files/{file_id}/content", options=make_request_options( @@ -272,7 +273,6 @@ def retrieve_content( """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"Accept": "application/json", **(extra_headers or {})} return self._get( f"/files/{file_id}/content", options=make_request_options( @@ -511,6 +511,7 @@ async def content( """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return await self._get( f"/files/{file_id}/content", options=make_request_options( @@ -545,7 +546,6 @@ async def retrieve_content( """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"Accept": "application/json", **(extra_headers or {})} return await self._get( f"/files/{file_id}/content", options=make_request_options( From 7a0cfb9acf136f62ec2b3edda9d8c607cf9dd5ea Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:56:59 -0500 Subject: [PATCH 330/914] fix(api): remove non-GA instance_id param (#1164) --- src/openai/resources/chat/completions.py | 28 ------------------- .../types/chat/completion_create_params.py | 6 ---- tests/api_resources/chat/test_completions.py | 4 --- 3 files changed, 38 deletions(-) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index caf712c604..0011d75e6e 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -68,7 +68,6 @@ def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -123,9 +122,6 @@ def create( A list of functions the model may generate JSON inputs for. - instance_id: An unique identifier to a custom instance to execute the request. The requesting - organization is required to have access to the instance. - logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -263,7 +259,6 @@ def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -324,9 +319,6 @@ def create( A list of functions the model may generate JSON inputs for. - instance_id: An unique identifier to a custom instance to execute the request. The requesting - organization is required to have access to the instance. - logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -457,7 +449,6 @@ def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -518,9 +509,6 @@ def create( A list of functions the model may generate JSON inputs for. - instance_id: An unique identifier to a custom instance to execute the request. The requesting - organization is required to have access to the instance. - logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -650,7 +638,6 @@ def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -682,7 +669,6 @@ def create( "frequency_penalty": frequency_penalty, "function_call": function_call, "functions": functions, - "instance_id": instance_id, "logit_bias": logit_bias, "logprobs": logprobs, "max_tokens": max_tokens, @@ -749,7 +735,6 @@ async def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -804,9 +789,6 @@ async def create( A list of functions the model may generate JSON inputs for. - instance_id: An unique identifier to a custom instance to execute the request. The requesting - organization is required to have access to the instance. - logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -944,7 +926,6 @@ async def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -1005,9 +986,6 @@ async def create( A list of functions the model may generate JSON inputs for. - instance_id: An unique identifier to a custom instance to execute the request. The requesting - organization is required to have access to the instance. - logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -1138,7 +1116,6 @@ async def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -1199,9 +1176,6 @@ async def create( A list of functions the model may generate JSON inputs for. - instance_id: An unique identifier to a custom instance to execute the request. The requesting - organization is required to have access to the instance. - logit_bias: Modify the likelihood of specified tokens appearing in the completion. Accepts a JSON object that maps tokens (specified by their token ID in the @@ -1331,7 +1305,6 @@ async def create( frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - instance_id: Optional[str] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -1363,7 +1336,6 @@ async def create( "frequency_penalty": frequency_penalty, "function_call": function_call, "functions": functions, - "instance_id": instance_id, "logit_bias": logit_bias, "logprobs": logprobs, "max_tokens": max_tokens, diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 2c80d84e6a..e02a81bc51 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -87,12 +87,6 @@ class CompletionCreateParamsBase(TypedDict, total=False): A list of functions the model may generate JSON inputs for. """ - instance_id: Optional[str] - """An unique identifier to a custom instance to execute the request. - - The requesting organization is required to have access to the instance. - """ - logit_bias: Optional[Dict[str, int]] """Modify the likelihood of specified tokens appearing in the completion. diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 856b7dc12c..4fa069ba2e 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -50,7 +50,6 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, } ], - instance_id="string", logit_bias={"foo": 0}, logprobs=True, max_tokens=0, @@ -165,7 +164,6 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, } ], - instance_id="string", logit_bias={"foo": 0}, logprobs=True, max_tokens=0, @@ -282,7 +280,6 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "parameters": {"foo": "bar"}, } ], - instance_id="string", logit_bias={"foo": 0}, logprobs=True, max_tokens=0, @@ -397,7 +394,6 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "parameters": {"foo": "bar"}, } ], - instance_id="string", logit_bias={"foo": 0}, logprobs=True, max_tokens=0, From 8ee5f33e8776e4517ef91a1cb2fafb6af2ca9310 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 15:57:24 +0000 Subject: [PATCH 331/914] release: 1.13.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index de0960aba8..f94eeca267 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.12.0" + ".": "1.13.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef0b80e87..00c364af0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 1.13.0 (2024-02-19) + +Full Changelog: [v1.12.0...v1.13.0](https://github.com/openai/openai-python/compare/v1.12.0...v1.13.0) + +### Features + +* **api:** updates ([#1146](https://github.com/openai/openai-python/issues/1146)) ([79b7675](https://github.com/openai/openai-python/commit/79b7675e51fb7d269a6ea281a568bc7812ba2ace)) + + +### Bug Fixes + +* **api:** remove non-GA instance_id param ([#1164](https://github.com/openai/openai-python/issues/1164)) ([1abe139](https://github.com/openai/openai-python/commit/1abe139b1a5f5cc41263738fc12856056dce5697)) + + +### Chores + +* **ci:** move github release logic to github app ([#1155](https://github.com/openai/openai-python/issues/1155)) ([67cfac2](https://github.com/openai/openai-python/commit/67cfac2564dfb718da0465e34b90ac6928fa962a)) +* **client:** use correct accept headers for binary data ([#1161](https://github.com/openai/openai-python/issues/1161)) ([e536437](https://github.com/openai/openai-python/commit/e536437ae0b2cb0ddf2d74618722005d37403f32)) +* **internal:** refactor release environment script ([#1158](https://github.com/openai/openai-python/issues/1158)) ([7fe8ec3](https://github.com/openai/openai-python/commit/7fe8ec3bf04ecf85e3bd5adf0d9992c051f87b81)) + ## 1.12.0 (2024-02-08) Full Changelog: [v1.11.1...v1.12.0](https://github.com/openai/openai-python/compare/v1.11.1...v1.12.0) diff --git a/pyproject.toml b/pyproject.toml index 163297ee2b..d7c017f99d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.12.0" +version = "1.13.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6db2292c7b..0b4a88eb78 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.12.0" # x-release-please-version +__version__ = "1.13.0" # x-release-please-version From 998e1887e1f507960cf8f49c4c28390ed90e2b01 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:18:36 -0500 Subject: [PATCH 332/914] chore(internal): bump rye to v0.24.0 (#1168) --- .devcontainer/Dockerfile | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/publish-pypi.yml | 2 +- requirements-dev.lock | 88 +++++++++++++++++++++++++++--- requirements.lock | 44 +++++++++++++-- 5 files changed, 122 insertions(+), 16 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6eb007253c..dd93962010 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode -RUN curl -sSf https://rye-up.com/get | RYE_VERSION="0.15.2" RYE_INSTALL_OPTION="--yes" bash +RUN curl -sSf https://rye-up.com/get | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c031d9a1d1..e50bfbdd7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: curl -sSf https://rye-up.com/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: 0.15.2 + RYE_VERSION: 0.24.0 RYE_INSTALL_OPTION: "--yes" - name: Install dependencies diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index e690e0d985..a6575165dd 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -21,7 +21,7 @@ jobs: curl -sSf https://rye-up.com/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: 0.15.2 + RYE_VERSION: 0.24.0 RYE_INSTALL_OPTION: "--yes" - name: Publish to PyPI diff --git a/requirements-dev.lock b/requirements-dev.lock index 088cb2bd98..a08b9b692c 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -5,67 +5,141 @@ # pre: false # features: [] # all-features: true +# with-sources: false -e file:. annotated-types==0.6.0 + # via pydantic anyio==4.1.0 + # via httpx + # via openai argcomplete==3.1.2 + # via nox attrs==23.1.0 -azure-core==1.29.6 + # via pytest +azure-core==1.30.0 + # via azure-identity azure-identity==1.15.0 certifi==2023.7.22 + # via httpcore + # via httpx + # via requests cffi==1.16.0 + # via cryptography charset-normalizer==3.3.2 + # via requests colorlog==6.7.0 -cryptography==41.0.7 + # via nox +cryptography==42.0.3 + # via azure-identity + # via msal + # via pyjwt dirty-equals==0.6.0 distlib==0.3.7 + # via virtualenv distro==1.8.0 + # via openai exceptiongroup==1.1.3 + # via anyio filelock==3.12.4 + # via virtualenv h11==0.14.0 + # via httpcore httpcore==1.0.2 + # via httpx httpx==0.25.2 + # via openai + # via respx idna==3.4 + # via anyio + # via httpx + # via requests importlib-metadata==7.0.0 iniconfig==2.0.0 + # via pytest msal==1.26.0 + # via azure-identity + # via msal-extensions msal-extensions==1.1.0 + # via azure-identity mypy==1.7.1 mypy-extensions==1.0.0 + # via mypy nodeenv==1.8.0 + # via pyright nox==2023.4.22 numpy==1.26.3 + # via openai + # via pandas + # via pandas-stubs packaging==23.2 + # via msal-extensions + # via nox + # via pytest pandas==2.1.4 + # via openai pandas-stubs==2.1.4.231227 + # via openai platformdirs==3.11.0 + # via virtualenv pluggy==1.3.0 + # via pytest portalocker==2.8.2 + # via msal-extensions py==1.11.0 + # via pytest pycparser==2.21 + # via cffi pydantic==2.4.2 + # via openai pydantic-core==2.10.1 + # via pydantic pyjwt==2.8.0 + # via msal pyright==1.1.332 pytest==7.1.1 + # via pytest-asyncio pytest-asyncio==0.21.1 python-dateutil==2.8.2 + # via pandas + # via time-machine pytz==2023.3.post1 + # via dirty-equals + # via pandas requests==2.31.0 + # via azure-core + # via msal respx==0.20.2 ruff==0.1.9 +setuptools==68.2.2 + # via nodeenv six==1.16.0 + # via azure-core + # via python-dateutil sniffio==1.3.0 + # via anyio + # via httpx + # via openai time-machine==2.9.0 tomli==2.0.1 + # via mypy + # via pytest tqdm==4.66.1 -types-pytz==2023.3.1.1 + # via openai +types-pytz==2024.1.0.20240203 + # via pandas-stubs types-tqdm==4.66.0.2 typing-extensions==4.8.0 -tzdata==2023.4 -urllib3==2.1.0 + # via azure-core + # via mypy + # via openai + # via pydantic + # via pydantic-core +tzdata==2024.1 + # via pandas +urllib3==2.2.1 + # via requests virtualenv==20.24.5 + # via nox zipp==3.17.0 -# The following packages are considered to be unsafe in a requirements file: -setuptools==68.2.2 + # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index c178f26a88..f3733bec9a 100644 --- a/requirements.lock +++ b/requirements.lock @@ -5,27 +5,59 @@ # pre: false # features: [] # all-features: true +# with-sources: false -e file:. annotated-types==0.6.0 + # via pydantic anyio==4.1.0 + # via httpx + # via openai certifi==2023.7.22 + # via httpcore + # via httpx distro==1.8.0 + # via openai exceptiongroup==1.1.3 + # via anyio h11==0.14.0 + # via httpcore httpcore==1.0.2 + # via httpx httpx==0.25.2 + # via openai idna==3.4 -numpy==1.26.2 -pandas==2.1.3 -pandas-stubs==2.1.1.230928 + # via anyio + # via httpx +numpy==1.26.4 + # via openai + # via pandas + # via pandas-stubs +pandas==2.2.0 + # via openai +pandas-stubs==2.2.0.240218 + # via openai pydantic==2.4.2 + # via openai pydantic-core==2.10.1 + # via pydantic python-dateutil==2.8.2 -pytz==2023.3.post1 + # via pandas +pytz==2024.1 + # via pandas six==1.16.0 + # via python-dateutil sniffio==1.3.0 + # via anyio + # via httpx + # via openai tqdm==4.66.1 -types-pytz==2023.3.1.1 + # via openai +types-pytz==2024.1.0.20240203 + # via pandas-stubs typing-extensions==4.8.0 -tzdata==2023.3 + # via openai + # via pydantic + # via pydantic-core +tzdata==2024.1 + # via pandas From f504384a7dcfdaffccf1bea8e32c82541908ff94 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:19:01 +0000 Subject: [PATCH 333/914] release: 1.13.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f94eeca267..734ad79c1c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.13.0" + ".": "1.13.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c364af0e..f282f6e84e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.13.1 (2024-02-20) + +Full Changelog: [v1.13.0...v1.13.1](https://github.com/openai/openai-python/compare/v1.13.0...v1.13.1) + +### Chores + +* **internal:** bump rye to v0.24.0 ([#1168](https://github.com/openai/openai-python/issues/1168)) ([84c4256](https://github.com/openai/openai-python/commit/84c4256316f2a79068ecadb852e5e69b6b104a1f)) + ## 1.13.0 (2024-02-19) Full Changelog: [v1.12.0...v1.13.0](https://github.com/openai/openai-python/compare/v1.12.0...v1.13.0) diff --git a/pyproject.toml b/pyproject.toml index d7c017f99d..d7585ce5c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.13.0" +version = "1.13.1" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0b4a88eb78..966177053b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.13.0" # x-release-please-version +__version__ = "1.13.1" # x-release-please-version From 63222f61d8324d1be41f660613ff84935110beab Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:09:03 -0500 Subject: [PATCH 334/914] fix(ci): revert "move github release logic to github app" (#1170) --- .github/workflows/create-releases.yml | 39 +++++++++++++++++++++++++++ .github/workflows/publish-pypi.yml | 8 ++---- .github/workflows/release-doctor.yml | 1 + bin/check-release-environment | 4 +++ 4 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/create-releases.yml diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml new file mode 100644 index 0000000000..9e76fcc471 --- /dev/null +++ b/.github/workflows/create-releases.yml @@ -0,0 +1,39 @@ +name: Create releases +on: + schedule: + - cron: '0 5 * * *' # every day at 5am UTC + push: + branches: + - main + +jobs: + release: + name: release + if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' + runs-on: ubuntu-latest + environment: publish + + steps: + - uses: actions/checkout@v3 + + - uses: stainless-api/trigger-release-please@v1 + id: release + with: + repo: ${{ github.event.repository.full_name }} + stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} + + - name: Install Rye + if: ${{ steps.release.outputs.releases_created }} + run: | + curl -sSf https://rye-up.com/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: 0.24.0 + RYE_INSTALL_OPTION: "--yes" + + - name: Publish to PyPI + if: ${{ steps.release.outputs.releases_created }} + run: | + bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index a6575165dd..f779a19ac1 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -1,13 +1,9 @@ -# This workflow is triggered when a GitHub release is created. -# It can also be run manually to re-publish to PyPI in case it failed for some reason. -# You can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml +# workflow for re-running publishing to PyPI in case it fails for some reason +# you can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml name: Publish PyPI on: workflow_dispatch: - release: - types: [published] - jobs: publish: name: publish diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 20af127ffc..108aa5973a 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -19,4 +19,5 @@ jobs: run: | bash ./bin/check-release-environment env: + STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }} PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/bin/check-release-environment b/bin/check-release-environment index 5471b69edb..2cc5ad6352 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -2,6 +2,10 @@ errors=() +if [ -z "${STAINLESS_API_KEY}" ]; then + errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.") +fi + if [ -z "${PYPI_TOKEN}" ]; then errors+=("The OPENAI_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi From a7115b5f33acd27326e5f78e19beb0d73bd3268e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 18:09:24 +0000 Subject: [PATCH 335/914] release: 1.13.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 734ad79c1c..690002df88 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.13.1" + ".": "1.13.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f282f6e84e..1f640d2614 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.13.2 (2024-02-20) + +Full Changelog: [v1.13.1...v1.13.2](https://github.com/openai/openai-python/compare/v1.13.1...v1.13.2) + +### Bug Fixes + +* **ci:** revert "move github release logic to github app" ([#1170](https://github.com/openai/openai-python/issues/1170)) ([f1adc2e](https://github.com/openai/openai-python/commit/f1adc2e6f2f29acb4404e84137a9d3109714c585)) + ## 1.13.1 (2024-02-20) Full Changelog: [v1.13.0...v1.13.1](https://github.com/openai/openai-python/compare/v1.13.0...v1.13.1) diff --git a/pyproject.toml b/pyproject.toml index d7585ce5c7..50fac10e84 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.13.1" +version = "1.13.2" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 966177053b..7890e5b58c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.13.1" # x-release-please-version +__version__ = "1.13.2" # x-release-please-version From f0b18239fe00d084f5ac0df71f1bb2b3e4781c56 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:17:19 -0500 Subject: [PATCH 336/914] chore(types): extract run status to a named type (#1178) --- .github/workflows/ci.yml | 2 +- api.md | 2 +- src/openai/types/beta/threads/__init__.py | 1 + src/openai/types/beta/threads/run.py | 5 ++--- src/openai/types/beta/threads/run_status.py | 9 +++++++++ 5 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 src/openai/types/beta/threads/run_status.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e50bfbdd7e..ec10edfe36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: if: github.repository == 'openai/openai-python' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rye run: | diff --git a/api.md b/api.md index 86b972d14e..34352e6e72 100644 --- a/api.md +++ b/api.md @@ -224,7 +224,7 @@ Methods: Types: ```python -from openai.types.beta.threads import RequiredActionFunctionToolCall, Run +from openai.types.beta.threads import RequiredActionFunctionToolCall, Run, RunStatus ``` Methods: diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index 8c77466dec..a71cbde3e3 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations from .run import Run as Run +from .run_status import RunStatus as RunStatus from .thread_message import ThreadMessage as ThreadMessage from .run_list_params import RunListParams as RunListParams from .run_create_params import RunCreateParams as RunCreateParams diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 9c875a9242..79e4f6a444 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -5,6 +5,7 @@ from ...shared import FunctionDefinition from ...._models import BaseModel +from .run_status import RunStatus from .required_action_function_tool_call import RequiredActionFunctionToolCall __all__ = [ @@ -142,9 +143,7 @@ class Run(BaseModel): started_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run was started.""" - status: Literal[ - "queued", "in_progress", "requires_action", "cancelling", "cancelled", "failed", "completed", "expired" - ] + status: RunStatus """ The status of the run, which can be either `queued`, `in_progress`, `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, or diff --git a/src/openai/types/beta/threads/run_status.py b/src/openai/types/beta/threads/run_status.py new file mode 100644 index 0000000000..587e3d7810 --- /dev/null +++ b/src/openai/types/beta/threads/run_status.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +__all__ = ["RunStatus"] + +RunStatus = Literal[ + "queued", "in_progress", "requires_action", "cancelling", "cancelled", "failed", "completed", "expired" +] From af02745a6a6d8e4bad44b32beafb6357396d3b93 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:26:41 -0500 Subject: [PATCH 337/914] docs: add note in azure_deployment docstring (#1188) --- src/openai/lib/azure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 2c8b4dcd88..b3b94de80e 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -155,7 +155,7 @@ def __init__( azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. azure_deployment: A model deployment, if given sets the base client URL to include `/deployments/{azure_deployment}`. - Note: this means you won't be able to use non-deployment endpoints. + Note: this means you won't be able to use non-deployment endpoints. Not supported with Assistants APIs. """ if api_key is None: api_key = os.environ.get("AZURE_OPENAI_API_KEY") @@ -388,7 +388,7 @@ def __init__( azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. azure_deployment: A model deployment, if given sets the base client URL to include `/deployments/{azure_deployment}`. - Note: this means you won't be able to use non-deployment endpoints. + Note: this means you won't be able to use non-deployment endpoints. Not supported with Assistants APIs. """ if api_key is None: api_key = os.environ.get("AZURE_OPENAI_API_KEY") From 6d25c6bb08b7fc26cd2a8b6295e66c709fcb68b8 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 23 Feb 2024 19:18:50 -0500 Subject: [PATCH 338/914] feat(api): add wav and pcm to response_format (#1189) --- src/openai/resources/audio/speech.py | 16 ++++++++++++---- src/openai/types/audio/speech_create_params.py | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index fbdc1ecff1..a569751ee5 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -41,7 +41,7 @@ def create( input: str, model: Union[str, Literal["tts-1", "tts-1-hd"]], voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], - response_format: Literal["mp3", "opus", "aac", "flac"] | NotGiven = NOT_GIVEN, + response_format: Literal["mp3", "opus", "aac", "flac", "pcm", "wav"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -65,7 +65,11 @@ def create( available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). - response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`. + response_format: The format to return audio in. Supported formats are `mp3`, `opus`, `aac`, + `flac`, `pcm`, and `wav`. + + The `pcm` audio format, similar to `wav` but without a header, utilizes a 24kHz + sample rate, mono channel, and 16-bit depth in signed little-endian format. speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default. @@ -113,7 +117,7 @@ async def create( input: str, model: Union[str, Literal["tts-1", "tts-1-hd"]], voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], - response_format: Literal["mp3", "opus", "aac", "flac"] | NotGiven = NOT_GIVEN, + response_format: Literal["mp3", "opus", "aac", "flac", "pcm", "wav"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -137,7 +141,11 @@ async def create( available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). - response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`. + response_format: The format to return audio in. Supported formats are `mp3`, `opus`, `aac`, + `flac`, `pcm`, and `wav`. + + The `pcm` audio format, similar to `wav` but without a header, utilizes a 24kHz + sample rate, mono channel, and 16-bit depth in signed little-endian format. speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default. diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 6a302dd3c8..00f862272e 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -26,8 +26,14 @@ class SpeechCreateParams(TypedDict, total=False): [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). """ - response_format: Literal["mp3", "opus", "aac", "flac"] - """The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`.""" + response_format: Literal["mp3", "opus", "aac", "flac", "pcm", "wav"] + """The format to return audio in. + + Supported formats are `mp3`, `opus`, `aac`, `flac`, `pcm`, and `wav`. + + The `pcm` audio format, similar to `wav` but without a header, utilizes a 24kHz + sample rate, mono channel, and 16-bit depth in signed little-endian format. + """ speed: float """The speed of the generated audio. From 9179a03e72097556b357e35b959d60fa3303c9f3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:12:48 +0100 Subject: [PATCH 339/914] chore(internal): bump pyright (#1193) --- requirements-dev.lock | 6 +++--- src/openai/_models.py | 2 +- src/openai/_utils/_proxy.py | 2 +- src/openai/cli/_tools/migrate.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index a08b9b692c..97f664e7c1 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -30,7 +30,7 @@ charset-normalizer==3.3.2 # via requests colorlog==6.7.0 # via nox -cryptography==42.0.3 +cryptography==42.0.5 # via azure-identity # via msal # via pyjwt @@ -57,7 +57,7 @@ idna==3.4 importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest -msal==1.26.0 +msal==1.27.0 # via azure-identity # via msal-extensions msal-extensions==1.1.0 @@ -96,7 +96,7 @@ pydantic-core==2.10.1 # via pydantic pyjwt==2.8.0 # via msal -pyright==1.1.332 +pyright==1.1.351 pytest==7.1.1 # via pytest-asyncio pytest-asyncio==0.21.1 diff --git a/src/openai/_models.py b/src/openai/_models.py index 48d5624f64..810891497a 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -283,7 +283,7 @@ def construct_type(*, value: object, type_: type) -> object: if is_union(origin): try: - return validate_type(type_=type_, value=value) + return validate_type(type_=cast("type[object]", type_), value=value) except Exception: pass diff --git a/src/openai/_utils/_proxy.py b/src/openai/_utils/_proxy.py index 6f05efcd21..b9c12dc3f4 100644 --- a/src/openai/_utils/_proxy.py +++ b/src/openai/_utils/_proxy.py @@ -45,7 +45,7 @@ def __dir__(self) -> Iterable[str]: @property # type: ignore @override - def __class__(self) -> type: + def __class__(self) -> type: # pyright: ignore proxied = self.__get_proxied__() if issubclass(type(proxied), LazyProxy): return type(proxied) diff --git a/src/openai/cli/_tools/migrate.py b/src/openai/cli/_tools/migrate.py index 14773302e1..53073b866f 100644 --- a/src/openai/cli/_tools/migrate.py +++ b/src/openai/cli/_tools/migrate.py @@ -138,7 +138,7 @@ def install() -> Path: unpacked_dir.mkdir(parents=True, exist_ok=True) with tarfile.open(temp_file, "r:gz") as archive: - archive.extractall(unpacked_dir) + archive.extractall(unpacked_dir, filter="data") for item in unpacked_dir.iterdir(): item.rename(target_dir / item.name) From 9375d2cc3492a8328a5d645e9dd1a46efe9a55c5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:41:05 +0100 Subject: [PATCH 340/914] docs(examples): add pyaudio streaming example (#1194) --- examples/audio.py | 28 +++++++++++++++++++++++++++- pyproject.toml | 3 ++- requirements-dev.lock | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/examples/audio.py b/examples/audio.py index 73491090f5..85f47bfb06 100755 --- a/examples/audio.py +++ b/examples/audio.py @@ -1,5 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env rye run python +import time from pathlib import Path from openai import OpenAI @@ -11,6 +12,8 @@ def main() -> None: + stream_to_speakers() + # Create text-to-speech audio file with openai.audio.speech.with_streaming_response.create( model="tts-1", @@ -34,5 +37,28 @@ def main() -> None: print(translation.text) +def stream_to_speakers() -> None: + import pyaudio + + player_stream = pyaudio.PyAudio().open(format=pyaudio.paInt16, channels=1, rate=24000, output=True) + + start_time = time.time() + + with openai.audio.speech.with_streaming_response.create( + model="tts-1", + voice="alloy", + response_format="pcm", # similar to WAV, but without a header chunk at the start. + input="""I see skies of blue and clouds of white + The bright blessed days, the dark sacred nights + And I think to myself + What a wonderful world""", + ) as response: + print(f"Time to first byte: {int((time.time() - start_time) * 1000)}ms") + for chunk in response.iter_bytes(chunk_size=1024): + player_stream.write(chunk) + + print(f"Done in {int((time.time() - start_time) * 1000)}ms.") + + if __name__ == "__main__": main() diff --git a/pyproject.toml b/pyproject.toml index 50fac10e84..5bdca2b69d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,8 @@ dev-dependencies = [ "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", "azure-identity >=1.14.1", - "types-tqdm > 4" + "types-tqdm > 4", + "types-pyaudio > 0" ] [tool.rye.scripts] diff --git a/requirements-dev.lock b/requirements-dev.lock index 97f664e7c1..fa95964d07 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -126,6 +126,7 @@ tomli==2.0.1 # via pytest tqdm==4.66.1 # via openai +types-pyaudio==0.2.16.20240106 types-pytz==2024.1.0.20240203 # via pandas-stubs types-tqdm==4.66.0.2 From cbcb9785703c626c39ed4ece60d5b046997732c1 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:03:24 +0100 Subject: [PATCH 341/914] chore(client): use anyio.sleep instead of asyncio.sleep (#1198) --- src/openai/_resource.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/openai/_resource.py b/src/openai/_resource.py index db1b0fa45a..0b0703bb72 100644 --- a/src/openai/_resource.py +++ b/src/openai/_resource.py @@ -3,9 +3,10 @@ from __future__ import annotations import time -import asyncio from typing import TYPE_CHECKING +import anyio + if TYPE_CHECKING: from ._client import OpenAI, AsyncOpenAI @@ -39,4 +40,4 @@ def __init__(self, client: AsyncOpenAI) -> None: self._get_api_list = client.get_api_list async def _sleep(self, seconds: float) -> None: - await asyncio.sleep(seconds) + await anyio.sleep(seconds) From e41abf7b7dbc1e744d167f748e55d4dedfc0dca7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 28 Feb 2024 06:04:08 +0100 Subject: [PATCH 342/914] release: 1.13.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 21 +++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 690002df88..c4bf1b6c04 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.13.2" + ".": "1.13.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f640d2614..201757c90d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 1.13.3 (2024-02-28) + +Full Changelog: [v1.13.2...v1.13.3](https://github.com/openai/openai-python/compare/v1.13.2...v1.13.3) + +### Features + +* **api:** add wav and pcm to response_format ([#1189](https://github.com/openai/openai-python/issues/1189)) ([dbd20fc](https://github.com/openai/openai-python/commit/dbd20fc42e93358261f71b9aa0e5f955053c3825)) + + +### Chores + +* **client:** use anyio.sleep instead of asyncio.sleep ([#1198](https://github.com/openai/openai-python/issues/1198)) ([b6d025b](https://github.com/openai/openai-python/commit/b6d025b54f091e79f5d4a0a8923f29574fd66027)) +* **internal:** bump pyright ([#1193](https://github.com/openai/openai-python/issues/1193)) ([9202e04](https://github.com/openai/openai-python/commit/9202e04d07a7c47232f39196346c734869b8f55a)) +* **types:** extract run status to a named type ([#1178](https://github.com/openai/openai-python/issues/1178)) ([249ecbd](https://github.com/openai/openai-python/commit/249ecbdeb6566a385ec46dfd5000b4eaa03965f0)) + + +### Documentation + +* add note in azure_deployment docstring ([#1188](https://github.com/openai/openai-python/issues/1188)) ([96fa995](https://github.com/openai/openai-python/commit/96fa99572dd76ee708f2bae04d11b659cdd698b2)) +* **examples:** add pyaudio streaming example ([#1194](https://github.com/openai/openai-python/issues/1194)) ([3683c5e](https://github.com/openai/openai-python/commit/3683c5e3c7f07e4b789a0c4cc417b2c59539cae2)) + ## 1.13.2 (2024-02-20) Full Changelog: [v1.13.1...v1.13.2](https://github.com/openai/openai-python/compare/v1.13.1...v1.13.2) diff --git a/pyproject.toml b/pyproject.toml index 5bdca2b69d..171ede0aa4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.13.2" +version = "1.13.3" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7890e5b58c..503a06141f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.13.2" # x-release-please-version +__version__ = "1.13.3" # x-release-please-version From 297796b7a067ad4b6558e416b7a75612231e68f8 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:32:23 +0100 Subject: [PATCH 343/914] chore(internal): minor core client restructuring (#1199) --- src/openai/_base_client.py | 5 ++++- src/openai/_streaming.py | 34 ++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 73bd2411fd..dda280f6aa 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -79,7 +79,7 @@ RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER, ) -from ._streaming import Stream, AsyncStream +from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder from ._exceptions import ( APIStatusError, APITimeoutError, @@ -431,6 +431,9 @@ def _prepare_url(http://github.com/self,%20url:%20str) -> URL: return merge_url + def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: + return SSEDecoder() + def _build_request( self, options: FinalRequestOptions, diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 74878fd0a0..2bc8d6a14d 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -5,7 +5,7 @@ import inspect from types import TracebackType from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast -from typing_extensions import Self, TypeGuard, override, get_origin +from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable import httpx @@ -24,6 +24,8 @@ class Stream(Generic[_T]): response: httpx.Response + _decoder: SSEDecoder | SSEBytesDecoder + def __init__( self, *, @@ -34,7 +36,7 @@ def __init__( self.response = response self._cast_to = cast_to self._client = client - self._decoder = SSEDecoder() + self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() def __next__(self) -> _T: @@ -45,7 +47,10 @@ def __iter__(self) -> Iterator[_T]: yield item def _iter_events(self) -> Iterator[ServerSentEvent]: - yield from self._decoder.iter(self.response.iter_lines()) + if isinstance(self._decoder, SSEBytesDecoder): + yield from self._decoder.iter_bytes(self.response.iter_bytes()) + else: + yield from self._decoder.iter(self.response.iter_lines()) def __stream__(self) -> Iterator[_T]: cast_to = cast(Any, self._cast_to) @@ -97,6 +102,8 @@ class AsyncStream(Generic[_T]): response: httpx.Response + _decoder: SSEDecoder | SSEBytesDecoder + def __init__( self, *, @@ -107,7 +114,7 @@ def __init__( self.response = response self._cast_to = cast_to self._client = client - self._decoder = SSEDecoder() + self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() async def __anext__(self) -> _T: @@ -118,8 +125,12 @@ async def __aiter__(self) -> AsyncIterator[_T]: yield item async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: - async for sse in self._decoder.aiter(self.response.aiter_lines()): - yield sse + if isinstance(self._decoder, SSEBytesDecoder): + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): + yield sse + else: + async for sse in self._decoder.aiter(self.response.aiter_lines()): + yield sse async def __stream__(self) -> AsyncIterator[_T]: cast_to = cast(Any, self._cast_to) @@ -284,6 +295,17 @@ def decode(self, line: str) -> ServerSentEvent | None: return None +@runtime_checkable +class SSEBytesDecoder(Protocol): + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" origin = get_origin(typ) or typ From 25cef3d9d106b89d583a3a6b67e4211f74a013ba Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 29 Feb 2024 17:39:58 +0100 Subject: [PATCH 344/914] docs(contributing): improve wording (#1201) --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 914ab67053..3290e13502 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,7 +82,7 @@ pip install ./path-to-wheel-file.whl ## Running tests -Most tests will require you to [setup a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. ```bash # you will need npm installed @@ -117,7 +117,7 @@ the changes aren't made through the automated pipeline, you may want to make rel ### Publish with a GitHub workflow -You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml). This will require a setup organization or repository secret to be set up. +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. ### Publish manually From 1879c97a5fb73a1609ade5e9a12a0af5e784ecba Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:57:41 +0100 Subject: [PATCH 345/914] chore(docs): mention install from git repo (#1203) --- CONTRIBUTING.md | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3290e13502..7ab73dbf4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,7 @@ If you’d like to use the repository from source, you can either install from g To install via git: ```bash -pip install git+ssh://git@github.com:openai/openai-python.git +pip install git+ssh://git@github.com/openai/openai-python.git ``` Alternatively, you can build from source and install the wheel file: diff --git a/README.md b/README.md index 0e06cd5631..7d6c896d50 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ The REST API documentation can be found [on platform.openai.com](https://platfor > The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code. ```sh +# install from PyPI pip install openai ``` From 31bfc124e2d850f254dfb9630d44eedd7f3320cc Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:52:22 +0100 Subject: [PATCH 346/914] chore(internal): split up transforms into sync / async (#1210) --- src/openai/_utils/__init__.py | 2 + src/openai/_utils/_transform.py | 128 +++++++++- src/openai/resources/audio/speech.py | 7 +- src/openai/resources/audio/transcriptions.py | 9 +- src/openai/resources/audio/translations.py | 9 +- .../resources/beta/assistants/assistants.py | 9 +- src/openai/resources/beta/assistants/files.py | 7 +- .../beta/threads/messages/messages.py | 9 +- .../resources/beta/threads/runs/runs.py | 11 +- src/openai/resources/beta/threads/threads.py | 11 +- src/openai/resources/chat/completions.py | 8 +- src/openai/resources/completions.py | 8 +- src/openai/resources/files.py | 9 +- src/openai/resources/fine_tuning/jobs.py | 7 +- src/openai/resources/images.py | 13 +- src/openai/resources/moderations.py | 7 +- tests/test_transform.py | 220 ++++++++++++------ 17 files changed, 363 insertions(+), 111 deletions(-) diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index b5790a879f..5697894192 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -44,5 +44,7 @@ from ._transform import ( PropertyInfo as PropertyInfo, transform as transform, + async_transform as async_transform, maybe_transform as maybe_transform, + async_maybe_transform as async_maybe_transform, ) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 2cb7726c73..9c76930687 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -180,11 +180,7 @@ def _transform_recursive( if isinstance(data, pydantic.BaseModel): return model_dump(data, exclude_unset=True) - return _transform_value(data, annotation) - - -def _transform_value(data: object, type_: type) -> object: - annotated_type = _get_annotated_type(type_) + annotated_type = _get_annotated_type(annotation) if annotated_type is None: return data @@ -222,3 +218,125 @@ def _transform_typeddict( 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)) + ): + 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) + + 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) + + 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/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index a569751ee5..6e0eb0cfdb 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -9,7 +9,10 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -161,7 +164,7 @@ async def create( extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} return await self._post( "/audio/speech", - body=maybe_transform( + body=await async_maybe_transform( { "input": input, "model": model, diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 275098ce88..720615f43f 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -9,7 +9,12 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import extract_files, maybe_transform, deepcopy_minimal +from ..._utils import ( + extract_files, + maybe_transform, + deepcopy_minimal, + async_maybe_transform, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -200,7 +205,7 @@ async def create( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/audio/transcriptions", - body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), + body=await async_maybe_transform(body, transcription_create_params.TranscriptionCreateParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index d6cbc75886..a189a07380 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -9,7 +9,12 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import extract_files, maybe_transform, deepcopy_minimal +from ..._utils import ( + extract_files, + maybe_transform, + deepcopy_minimal, + async_maybe_transform, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -174,7 +179,7 @@ async def create( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/audio/translations", - body=maybe_transform(body, translation_create_params.TranslationCreateParams), + body=await async_maybe_transform(body, translation_create_params.TranslationCreateParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index e926c31642..3aef33c95e 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -17,7 +17,10 @@ AsyncFilesWithStreamingResponse, ) from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import maybe_transform +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -410,7 +413,7 @@ async def create( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( "/assistants", - body=maybe_transform( + body=await async_maybe_transform( { "model": model, "description": description, @@ -525,7 +528,7 @@ async def update( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/assistants/{assistant_id}", - body=maybe_transform( + body=await async_maybe_transform( { "description": description, "file_ids": file_ids, diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index c21465036a..8d5657666c 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -8,7 +8,10 @@ from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import maybe_transform +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -259,7 +262,7 @@ async def create( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/assistants/{assistant_id}/files", - body=maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams), + body=await async_maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index c95cdd5d00..2c0994d1f2 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -17,7 +17,10 @@ AsyncFilesWithStreamingResponse, ) from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ....._utils import maybe_transform +from ....._utils import ( + maybe_transform, + async_maybe_transform, +) from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -315,7 +318,7 @@ async def create( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/messages", - body=maybe_transform( + body=await async_maybe_transform( { "content": content, "role": role, @@ -404,7 +407,7 @@ async def update( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/messages/{message_id}", - body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), + body=await async_maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 9b18336010..62cfa6b742 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -17,7 +17,10 @@ AsyncStepsWithStreamingResponse, ) from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ....._utils import maybe_transform +from ....._utils import ( + maybe_transform, + async_maybe_transform, +) from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -430,7 +433,7 @@ async def create( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs", - body=maybe_transform( + body=await async_maybe_transform( { "assistant_id": assistant_id, "additional_instructions": additional_instructions, @@ -521,7 +524,7 @@ async def update( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs/{run_id}", - body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), + body=await async_maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -669,7 +672,7 @@ async def submit_tool_outputs( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", - body=maybe_transform( + body=await async_maybe_transform( {"tool_outputs": tool_outputs}, run_submit_tool_outputs_params.RunSubmitToolOutputsParams ), options=make_request_options( diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index dd079ac533..cc0e1c0959 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -24,7 +24,10 @@ AsyncMessagesWithStreamingResponse, ) from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import maybe_transform +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) from .runs.runs import Runs, AsyncRuns from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -342,7 +345,7 @@ async def create( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( "/threads", - body=maybe_transform( + body=await async_maybe_transform( { "messages": messages, "metadata": metadata, @@ -423,7 +426,7 @@ async def update( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}", - body=maybe_transform({"metadata": metadata}, thread_update_params.ThreadUpdateParams), + body=await async_maybe_transform({"metadata": metadata}, thread_update_params.ThreadUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -517,7 +520,7 @@ async def create_and_run( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( "/threads/runs", - body=maybe_transform( + body=await async_maybe_transform( { "assistant_id": assistant_id, "instructions": instructions, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 0011d75e6e..a8856a989b 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -9,7 +9,11 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import required_args, maybe_transform +from ..._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -1329,7 +1333,7 @@ async def create( ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: return await self._post( "/chat/completions", - body=maybe_transform( + body=await async_maybe_transform( { "messages": messages, "model": model, diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index af2d6e2e51..6d3756f6ba 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -10,7 +10,11 @@ from .. import _legacy_response from ..types import Completion, completion_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import required_args, maybe_transform +from .._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -1019,7 +1023,7 @@ async def create( ) -> Completion | AsyncStream[Completion]: return await self._post( "/completions", - body=maybe_transform( + body=await async_maybe_transform( { "model": model, "prompt": prompt, diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 8b2bc4f181..3ea66656b3 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -12,7 +12,12 @@ from .. import _legacy_response from ..types import FileObject, FileDeleted, file_list_params, file_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from .._utils import extract_files, maybe_transform, deepcopy_minimal +from .._utils import ( + extract_files, + maybe_transform, + deepcopy_minimal, + async_maybe_transform, +) from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -374,7 +379,7 @@ async def create( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/files", - body=maybe_transform(body, file_create_params.FileCreateParams), + body=await async_maybe_transform(body, file_create_params.FileCreateParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 6b59932982..8338de12c4 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -9,7 +9,10 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -369,7 +372,7 @@ async def create( """ return await self._post( "/fine_tuning/jobs", - body=maybe_transform( + body=await async_maybe_transform( { "model": model, "training_file": training_file, diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 91530e47ca..7a7ff1225d 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -15,7 +15,12 @@ image_create_variation_params, ) from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from .._utils import extract_files, maybe_transform, deepcopy_minimal +from .._utils import ( + extract_files, + maybe_transform, + deepcopy_minimal, + async_maybe_transform, +) from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -345,7 +350,7 @@ async def create_variation( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/images/variations", - body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), + body=await async_maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -428,7 +433,7 @@ async def edit( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/images/edits", - body=maybe_transform(body, image_edit_params.ImageEditParams), + body=await async_maybe_transform(body, image_edit_params.ImageEditParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -496,7 +501,7 @@ async def generate( """ return await self._post( "/images/generations", - body=maybe_transform( + body=await async_maybe_transform( { "prompt": prompt, "model": model, diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 540d089071..2b9a70d562 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -10,7 +10,10 @@ from .. import _legacy_response from ..types import ModerationCreateResponse, moderation_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform +from .._utils import ( + maybe_transform, + async_maybe_transform, +) from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -127,7 +130,7 @@ async def create( """ return await self._post( "/moderations", - body=maybe_transform( + body=await async_maybe_transform( { "input": input, "model": model, diff --git a/tests/test_transform.py b/tests/test_transform.py index 6ed67d49a7..67ec4d5cc6 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -1,22 +1,45 @@ from __future__ import annotations -from typing import Any, List, Union, Iterable, Optional, cast +from typing import Any, List, Union, TypeVar, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict import pytest -from openai._utils import PropertyInfo, transform, parse_datetime +from openai._utils import ( + PropertyInfo, + transform as _transform, + parse_datetime, + async_transform as _async_transform, +) from openai._compat import PYDANTIC_V2 from openai._models import BaseModel +_T = TypeVar("_T") + + +async def transform( + data: _T, + expected_type: object, + use_async: bool, +) -> _T: + if use_async: + return await _async_transform(data, expected_type=expected_type) + + return _transform(data, expected_type=expected_type) + + +parametrize = pytest.mark.parametrize("use_async", [False, True], ids=["sync", "async"]) + class Foo1(TypedDict): foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] -def test_top_level_alias() -> None: - assert transform({"foo_bar": "hello"}, expected_type=Foo1) == {"fooBar": "hello"} +@parametrize +@pytest.mark.asyncio +async def test_top_level_alias(use_async: bool) -> None: + assert await transform({"foo_bar": "hello"}, expected_type=Foo1, use_async=use_async) == {"fooBar": "hello"} class Foo2(TypedDict): @@ -32,9 +55,11 @@ class Baz2(TypedDict): my_baz: Annotated[str, PropertyInfo(alias="myBaz")] -def test_recursive_typeddict() -> None: - assert transform({"bar": {"this_thing": 1}}, Foo2) == {"bar": {"this__thing": 1}} - assert transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2) == {"bar": {"Baz": {"myBaz": "foo"}}} +@parametrize +@pytest.mark.asyncio +async def test_recursive_typeddict(use_async: bool) -> None: + assert await transform({"bar": {"this_thing": 1}}, Foo2, use_async) == {"bar": {"this__thing": 1}} + assert await transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2, use_async) == {"bar": {"Baz": {"myBaz": "foo"}}} class Foo3(TypedDict): @@ -45,8 +70,10 @@ class Bar3(TypedDict): my_field: Annotated[str, PropertyInfo(alias="myField")] -def test_list_of_typeddict() -> None: - result = transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, expected_type=Foo3) +@parametrize +@pytest.mark.asyncio +async def test_list_of_typeddict(use_async: bool) -> None: + result = await transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, Foo3, use_async) assert result == {"things": [{"myField": "foo"}, {"myField": "foo2"}]} @@ -62,10 +89,14 @@ class Baz4(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] -def test_union_of_typeddict() -> None: - assert transform({"foo": {"foo_bar": "bar"}}, Foo4) == {"foo": {"fooBar": "bar"}} - assert transform({"foo": {"foo_baz": "baz"}}, Foo4) == {"foo": {"fooBaz": "baz"}} - assert transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4) == {"foo": {"fooBaz": "baz", "fooBar": "bar"}} +@parametrize +@pytest.mark.asyncio +async def test_union_of_typeddict(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo4, use_async) == {"foo": {"fooBar": "bar"}} + assert await transform({"foo": {"foo_baz": "baz"}}, Foo4, use_async) == {"foo": {"fooBaz": "baz"}} + assert await transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4, use_async) == { + "foo": {"fooBaz": "baz", "fooBar": "bar"} + } class Foo5(TypedDict): @@ -80,9 +111,11 @@ class Baz5(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] -def test_union_of_list() -> None: - assert transform({"foo": {"foo_bar": "bar"}}, Foo5) == {"FOO": {"fooBar": "bar"}} - assert transform( +@parametrize +@pytest.mark.asyncio +async def test_union_of_list(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo5, use_async) == {"FOO": {"fooBar": "bar"}} + assert await transform( { "foo": [ {"foo_baz": "baz"}, @@ -90,6 +123,7 @@ def test_union_of_list() -> None: ] }, Foo5, + use_async, ) == {"FOO": [{"fooBaz": "baz"}, {"fooBaz": "baz"}]} @@ -97,8 +131,10 @@ class Foo6(TypedDict): bar: Annotated[str, PropertyInfo(alias="Bar")] -def test_includes_unknown_keys() -> None: - assert transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6) == { +@parametrize +@pytest.mark.asyncio +async def test_includes_unknown_keys(use_async: bool) -> None: + assert await transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6, use_async) == { "Bar": "bar", "baz_": {"FOO": 1}, } @@ -113,9 +149,11 @@ class Bar7(TypedDict): foo: str -def test_ignores_invalid_input() -> None: - assert transform({"bar": ""}, Foo7) == {"bAr": ""} - assert transform({"foo": ""}, Foo7) == {"foo": ""} +@parametrize +@pytest.mark.asyncio +async def test_ignores_invalid_input(use_async: bool) -> None: + assert await transform({"bar": ""}, Foo7, use_async) == {"bAr": ""} + assert await transform({"foo": ""}, Foo7, use_async) == {"foo": ""} class DatetimeDict(TypedDict, total=False): @@ -134,52 +172,66 @@ class DateDict(TypedDict, total=False): foo: Annotated[date, PropertyInfo(format="iso8601")] -def test_iso8601_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - assert transform({"foo": dt}, DatetimeDict) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] dt = dt.replace(tzinfo=None) - assert transform({"foo": dt}, DatetimeDict) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] - assert transform({"foo": None}, DateDict) == {"foo": None} # type: ignore[comparison-overlap] - assert transform({"foo": date.fromisoformat("2023-02-23")}, DateDict) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] + assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap] + assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] -def test_optional_iso8601_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_optional_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - assert transform({"bar": dt}, DatetimeDict) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform({"bar": dt}, DatetimeDict, use_async) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] - assert transform({"bar": None}, DatetimeDict) == {"bar": None} + assert await transform({"bar": None}, DatetimeDict, use_async) == {"bar": None} -def test_required_iso8601_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_required_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - assert transform({"required": dt}, DatetimeDict) == {"required": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform({"required": dt}, DatetimeDict, use_async) == { + "required": "2023-02-23T14:16:36.337692+00:00" + } # type: ignore[comparison-overlap] - assert transform({"required": None}, DatetimeDict) == {"required": None} + assert await transform({"required": None}, DatetimeDict, use_async) == {"required": None} -def test_union_datetime() -> None: +@parametrize +@pytest.mark.asyncio +async def test_union_datetime(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - assert transform({"union": dt}, DatetimeDict) == { # type: ignore[comparison-overlap] + assert await transform({"union": dt}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] "union": "2023-02-23T14:16:36.337692+00:00" } - assert transform({"union": "foo"}, DatetimeDict) == {"union": "foo"} + assert await transform({"union": "foo"}, DatetimeDict, use_async) == {"union": "foo"} -def test_nested_list_iso6801_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_nested_list_iso6801_format(use_async: bool) -> None: dt1 = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") dt2 = parse_datetime("2022-01-15T06:34:23Z") - assert transform({"list_": [dt1, dt2]}, DatetimeDict) == { # type: ignore[comparison-overlap] + assert await transform({"list_": [dt1, dt2]}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] "list_": ["2023-02-23T14:16:36.337692+00:00", "2022-01-15T06:34:23+00:00"] } -def test_datetime_custom_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_datetime_custom_format(use_async: bool) -> None: dt = parse_datetime("2022-01-15T06:34:23Z") - result = transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")]) + result = await transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], use_async) assert result == "06" # type: ignore[comparison-overlap] @@ -187,47 +239,59 @@ class DateDictWithRequiredAlias(TypedDict, total=False): required_prop: Required[Annotated[date, PropertyInfo(format="iso8601", alias="prop")]] -def test_datetime_with_alias() -> None: - assert transform({"required_prop": None}, DateDictWithRequiredAlias) == {"prop": None} # type: ignore[comparison-overlap] - assert transform({"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias) == { - "prop": "2023-02-23" - } # type: ignore[comparison-overlap] +@parametrize +@pytest.mark.asyncio +async def test_datetime_with_alias(use_async: bool) -> None: + assert await transform({"required_prop": None}, DateDictWithRequiredAlias, use_async) == {"prop": None} # type: ignore[comparison-overlap] + assert await transform( + {"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias, use_async + ) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] class MyModel(BaseModel): foo: str -def test_pydantic_model_to_dictionary() -> None: - assert transform(MyModel(foo="hi!"), Any) == {"foo": "hi!"} - assert transform(MyModel.construct(foo="hi!"), Any) == {"foo": "hi!"} +@parametrize +@pytest.mark.asyncio +async def test_pydantic_model_to_dictionary(use_async: bool) -> None: + assert await transform(MyModel(foo="hi!"), Any, use_async) == {"foo": "hi!"} + assert await transform(MyModel.construct(foo="hi!"), Any, use_async) == {"foo": "hi!"} -def test_pydantic_empty_model() -> None: - assert transform(MyModel.construct(), Any) == {} +@parametrize +@pytest.mark.asyncio +async def test_pydantic_empty_model(use_async: bool) -> None: + assert await transform(MyModel.construct(), Any, use_async) == {} -def test_pydantic_unknown_field() -> None: - assert transform(MyModel.construct(my_untyped_field=True), Any) == {"my_untyped_field": True} +@parametrize +@pytest.mark.asyncio +async def test_pydantic_unknown_field(use_async: bool) -> None: + assert await transform(MyModel.construct(my_untyped_field=True), Any, use_async) == {"my_untyped_field": True} -def test_pydantic_mismatched_types() -> None: +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_types(use_async: bool) -> None: model = MyModel.construct(foo=True) if PYDANTIC_V2: with pytest.warns(UserWarning): - params = transform(model, Any) + params = await transform(model, Any, use_async) else: - params = transform(model, Any) + params = await transform(model, Any, use_async) assert params == {"foo": True} -def test_pydantic_mismatched_object_type() -> None: +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_object_type(use_async: bool) -> None: model = MyModel.construct(foo=MyModel.construct(hello="world")) if PYDANTIC_V2: with pytest.warns(UserWarning): - params = transform(model, Any) + params = await transform(model, Any, use_async) else: - params = transform(model, Any) + params = await transform(model, Any, use_async) assert params == {"foo": {"hello": "world"}} @@ -235,10 +299,12 @@ class ModelNestedObjects(BaseModel): nested: MyModel -def test_pydantic_nested_objects() -> None: +@parametrize +@pytest.mark.asyncio +async def test_pydantic_nested_objects(use_async: bool) -> None: model = ModelNestedObjects.construct(nested={"foo": "stainless"}) assert isinstance(model.nested, MyModel) - assert transform(model, Any) == {"nested": {"foo": "stainless"}} + assert await transform(model, Any, use_async) == {"nested": {"foo": "stainless"}} class ModelWithDefaultField(BaseModel): @@ -247,24 +313,26 @@ class ModelWithDefaultField(BaseModel): with_str_default: str = "foo" -def test_pydantic_default_field() -> None: +@parametrize +@pytest.mark.asyncio +async def test_pydantic_default_field(use_async: bool) -> None: # should be excluded when defaults are used model = ModelWithDefaultField.construct() assert model.with_none_default is None assert model.with_str_default == "foo" - assert transform(model, Any) == {} + assert await transform(model, Any, use_async) == {} # should be included when the default value is explicitly given model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") assert model.with_none_default is None assert model.with_str_default == "foo" - assert transform(model, Any) == {"with_none_default": None, "with_str_default": "foo"} + assert await transform(model, Any, use_async) == {"with_none_default": None, "with_str_default": "foo"} # should be included when a non-default value is explicitly given model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") assert model.with_none_default == "bar" assert model.with_str_default == "baz" - assert transform(model, Any) == {"with_none_default": "bar", "with_str_default": "baz"} + assert await transform(model, Any, use_async) == {"with_none_default": "bar", "with_str_default": "baz"} class TypedDictIterableUnion(TypedDict): @@ -279,21 +347,33 @@ class Baz8(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] -def test_iterable_of_dictionaries() -> None: - assert transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "bar"}]} - assert cast(Any, transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion)) == {"FOO": [{"fooBaz": "bar"}]} +@parametrize +@pytest.mark.asyncio +async def test_iterable_of_dictionaries(use_async: bool) -> None: + assert await transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "bar"}] + } + assert cast(Any, await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async)) == { + "FOO": [{"fooBaz": "bar"}] + } def my_iter() -> Iterable[Baz8]: yield {"foo_baz": "hello"} yield {"foo_baz": "world"} - assert transform({"foo": my_iter()}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}]} + assert await transform({"foo": my_iter()}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}] + } class TypedDictIterableUnionStr(TypedDict): foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] -def test_iterable_union_str() -> None: - assert transform({"foo": "bar"}, TypedDictIterableUnionStr) == {"FOO": "bar"} - assert cast(Any, transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]])) == [{"fooBaz": "bar"}] +@parametrize +@pytest.mark.asyncio +async def test_iterable_union_str(use_async: bool) -> None: + assert await transform({"foo": "bar"}, TypedDictIterableUnionStr, use_async) == {"FOO": "bar"} + assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [ + {"fooBaz": "bar"} + ] From a0caa0907c7acd1d88010d32e6b4ce65fbe144c8 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:39:15 +0100 Subject: [PATCH 347/914] chore(internal): support more input types (#1211) --- src/openai/_files.py | 5 +++++ src/openai/_types.py | 2 ++ src/openai/_utils/_transform.py | 39 ++++++++++++++++++++++++++++++++- tests/sample_file.txt | 1 + tests/test_transform.py | 29 ++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/sample_file.txt diff --git a/src/openai/_files.py b/src/openai/_files.py index bebfb19501..ad7b668b4b 100644 --- a/src/openai/_files.py +++ b/src/openai/_files.py @@ -13,12 +13,17 @@ FileContent, RequestFiles, HttpxFileTypes, + Base64FileInput, HttpxFileContent, HttpxRequestFiles, ) from ._utils import is_tuple_t, is_mapping_t, is_sequence_t +def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: + return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + def is_file_content(obj: object) -> TypeGuard[FileContent]: return ( isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) diff --git a/src/openai/_types.py b/src/openai/_types.py index b5bf8f8af0..de9b1dd48b 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -41,8 +41,10 @@ ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] ProxiesTypes = Union[str, Proxy, ProxiesDict] if TYPE_CHECKING: + Base64FileInput = Union[IO[bytes], PathLike[str]] FileContent = Union[IO[bytes], bytes, PathLike[str]] else: + Base64FileInput = Union[IO[bytes], PathLike] FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. FileTypes = Union[ # file (or bytes) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 9c76930687..1bd1330c94 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -1,9 +1,13 @@ 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 ( @@ -11,6 +15,7 @@ is_mapping, is_iterable, ) +from .._files import is_base64_file_input from ._typing import ( is_list_type, is_union_type, @@ -29,7 +34,7 @@ # TODO: ensure works correctly with forward references in all cases -PropertyFormat = Literal["iso8601", "custom"] +PropertyFormat = Literal["iso8601", "base64", "custom"] class PropertyInfo: @@ -201,6 +206,22 @@ def _format_data(data: object, format_: PropertyFormat, format_template: str | N 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 @@ -323,6 +344,22 @@ async def _async_format_data(data: object, format_: PropertyFormat, format_templ 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 diff --git a/tests/sample_file.txt b/tests/sample_file.txt new file mode 100644 index 0000000000..af5626b4a1 --- /dev/null +++ b/tests/sample_file.txt @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/test_transform.py b/tests/test_transform.py index 67ec4d5cc6..0d17e8a972 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -1,11 +1,14 @@ from __future__ import annotations +import io +import pathlib from typing import Any, List, Union, TypeVar, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict import pytest +from openai._types import Base64FileInput from openai._utils import ( PropertyInfo, transform as _transform, @@ -17,6 +20,8 @@ _T = TypeVar("_T") +SAMPLE_FILE_PATH = pathlib.Path(__file__).parent.joinpath("sample_file.txt") + async def transform( data: _T, @@ -377,3 +382,27 @@ async def test_iterable_union_str(use_async: bool) -> None: assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [ {"fooBaz": "bar"} ] + + +class TypedDictBase64Input(TypedDict): + foo: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] + + +@parametrize +@pytest.mark.asyncio +async def test_base64_file_input(use_async: bool) -> None: + # strings are left as-is + assert await transform({"foo": "bar"}, TypedDictBase64Input, use_async) == {"foo": "bar"} + + # pathlib.Path is automatically converted to base64 + assert await transform({"foo": SAMPLE_FILE_PATH}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQo=" + } # type: ignore[comparison-overlap] + + # io instances are automatically converted to base64 + assert await transform({"foo": io.StringIO("Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] + assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] From 3208335a72cb0cec8b82be5954b54856f897cb27 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 4 Mar 2024 19:17:38 +0100 Subject: [PATCH 348/914] chore(api): update docs (#1212) --- src/openai/resources/audio/speech.py | 18 ++++------ src/openai/resources/audio/transcriptions.py | 22 +++++++----- src/openai/resources/audio/translations.py | 6 ++-- src/openai/resources/chat/completions.py | 36 +++++++++---------- src/openai/resources/images.py | 18 ++++++---- src/openai/resources/moderations.py | 4 +-- .../types/audio/speech_create_params.py | 9 ++--- src/openai/types/audio/transcription.py | 1 + .../audio/transcription_create_params.py | 13 ++++--- .../types/audio/translation_create_params.py | 6 +++- src/openai/types/beta/threads/run.py | 4 +-- .../chat/chat_completion_token_logprob.py | 14 ++++++-- .../types/chat/completion_create_params.py | 6 ++-- .../types/image_create_variation_params.py | 3 +- src/openai/types/image_edit_params.py | 3 +- src/openai/types/image_generate_params.py | 3 +- src/openai/types/moderation.py | 5 +-- 17 files changed, 98 insertions(+), 73 deletions(-) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 6e0eb0cfdb..bf4a0245f6 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -44,7 +44,7 @@ def create( input: str, model: Union[str, Literal["tts-1", "tts-1-hd"]], voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], - response_format: Literal["mp3", "opus", "aac", "flac", "pcm", "wav"] | NotGiven = NOT_GIVEN, + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -68,11 +68,8 @@ def create( available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). - response_format: The format to return audio in. Supported formats are `mp3`, `opus`, `aac`, - `flac`, `pcm`, and `wav`. - - The `pcm` audio format, similar to `wav` but without a header, utilizes a 24kHz - sample rate, mono channel, and 16-bit depth in signed little-endian format. + response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, + `wav`, and `pcm`. speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default. @@ -120,7 +117,7 @@ async def create( input: str, model: Union[str, Literal["tts-1", "tts-1-hd"]], voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], - response_format: Literal["mp3", "opus", "aac", "flac", "pcm", "wav"] | NotGiven = NOT_GIVEN, + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -144,11 +141,8 @@ async def create( available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). - response_format: The format to return audio in. Supported formats are `mp3`, `opus`, `aac`, - `flac`, `pcm`, and `wav`. - - The `pcm` audio format, similar to `wav` but without a header, utilizes a 24kHz - sample rate, mono channel, and 16-bit depth in signed little-endian format. + response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, + `wav`, and `pcm`. speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default. diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 720615f43f..cfd9aae909 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -60,7 +60,8 @@ def create( The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. - model: ID of the model to use. Only `whisper-1` is currently available. + model: ID of the model to use. Only `whisper-1` (which is powered by our open source + Whisper V2 model) is currently available. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will @@ -80,9 +81,11 @@ def create( [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit. - timestamp_granularities: The timestamp granularities to populate for this transcription. Any of these - options: `word`, or `segment`. Note: There is no additional latency for segment - timestamps, but generating word timestamps incurs additional latency. + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. extra_headers: Send extra headers @@ -154,7 +157,8 @@ async def create( The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. - model: ID of the model to use. Only `whisper-1` is currently available. + model: ID of the model to use. Only `whisper-1` (which is powered by our open source + Whisper V2 model) is currently available. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will @@ -174,9 +178,11 @@ async def create( [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit. - timestamp_granularities: The timestamp granularities to populate for this transcription. Any of these - options: `word`, or `segment`. Note: There is no additional latency for segment - timestamps, but generating word timestamps incurs additional latency. + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. extra_headers: Send extra headers diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index a189a07380..6063522237 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -57,7 +57,8 @@ def create( file: The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. - model: ID of the model to use. Only `whisper-1` is currently available. + model: ID of the model to use. Only `whisper-1` (which is powered by our open source + Whisper V2 model) is currently available. prompt: An optional text to guide the model's style or continue a previous audio segment. The @@ -138,7 +139,8 @@ async def create( file: The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. - model: ID of the model to use. Only `whisper-1` is currently available. + model: ID of the model to use. Only `whisper-1` (which is powered by our open source + Whisper V2 model) is currently available. prompt: An optional text to guide the model's style or continue a previous audio segment. The diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index a8856a989b..20ea4cffbb 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -208,9 +208,9 @@ def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. - top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return - at each token position, each with an associated log probability. `logprobs` must - be set to `true` if this parameter is used. + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -398,9 +398,9 @@ def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. - top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return - at each token position, each with an associated log probability. `logprobs` must - be set to `true` if this parameter is used. + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -588,9 +588,9 @@ def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. - top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return - at each token position, each with an associated log probability. `logprobs` must - be set to `true` if this parameter is used. + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -875,9 +875,9 @@ async def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. - top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return - at each token position, each with an associated log probability. `logprobs` must - be set to `true` if this parameter is used. + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -1065,9 +1065,9 @@ async def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. - top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return - at each token position, each with an associated log probability. `logprobs` must - be set to `true` if this parameter is used. + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -1255,9 +1255,9 @@ async def create( tool. Use this to provide a list of functions the model may generate JSON inputs for. - top_logprobs: An integer between 0 and 5 specifying the number of most likely tokens to return - at each token position, each with an associated log probability. `logprobs` must - be set to `true` if this parameter is used. + top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 7a7ff1225d..f5bbdbc338 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -70,7 +70,8 @@ def create_variation( `n=1` is supported. response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. size: The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. @@ -151,7 +152,8 @@ def edit( n: The number of images to generate. Must be between 1 and 10. response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. size: The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. @@ -231,7 +233,8 @@ def generate( for `dall-e-3`. response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. size: The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. Must be one of `1024x1024`, `1792x1024`, or @@ -315,7 +318,8 @@ async def create_variation( `n=1` is supported. response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. size: The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. @@ -396,7 +400,8 @@ async def edit( n: The number of images to generate. Must be between 1 and 10. response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. size: The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`. @@ -476,7 +481,8 @@ async def generate( for `dall-e-3`. response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. size: The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. Must be one of `1024x1024`, `1792x1024`, or diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 2b9a70d562..ac5ca1b64b 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -46,7 +46,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ModerationCreateResponse: """ - Classifies if text violates OpenAI's Content Policy + Classifies if text is potentially harmful. Args: input: The input text to classify @@ -106,7 +106,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ModerationCreateResponse: """ - Classifies if text violates OpenAI's Content Policy + Classifies if text is potentially harmful. Args: input: The input text to classify diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 00f862272e..0078a9d03a 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -26,13 +26,10 @@ class SpeechCreateParams(TypedDict, total=False): [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). """ - response_format: Literal["mp3", "opus", "aac", "flac", "pcm", "wav"] - """The format to return audio in. + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] + """The format to audio in. - Supported formats are `mp3`, `opus`, `aac`, `flac`, `pcm`, and `wav`. - - The `pcm` audio format, similar to `wav` but without a header, utilizes a 24kHz - sample rate, mono channel, and 16-bit depth in signed little-endian format. + Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. """ speed: float diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index d2274faa0e..6532611731 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -7,3 +7,4 @@ class Transcription(BaseModel): text: str + """The transcribed text.""" diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index 5a90822144..4164a594cc 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -18,7 +18,11 @@ class TranscriptionCreateParams(TypedDict, total=False): """ model: Required[Union[str, Literal["whisper-1"]]] - """ID of the model to use. Only `whisper-1` is currently available.""" + """ID of the model to use. + + Only `whisper-1` (which is powered by our open source Whisper V2 model) is + currently available. + """ language: str """The language of the input audio. @@ -54,7 +58,8 @@ class TranscriptionCreateParams(TypedDict, total=False): timestamp_granularities: List[Literal["word", "segment"]] """The timestamp granularities to populate for this transcription. - Any of these options: `word`, or `segment`. Note: There is no additional latency - for segment timestamps, but generating word timestamps incurs additional - latency. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. """ diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py index d3cb4b9e63..1ae312da49 100644 --- a/src/openai/types/audio/translation_create_params.py +++ b/src/openai/types/audio/translation_create_params.py @@ -18,7 +18,11 @@ class TranslationCreateParams(TypedDict, total=False): """ model: Required[Union[str, Literal["whisper-1"]]] - """ID of the model to use. Only `whisper-1` is currently available.""" + """ID of the model to use. + + Only `whisper-1` (which is powered by our open source Whisper V2 model) is + currently available. + """ prompt: str """An optional text to guide the model's style or continue a previous audio diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 79e4f6a444..38625d3781 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -22,8 +22,8 @@ class LastError(BaseModel): - code: Literal["server_error", "rate_limit_exceeded"] - """One of `server_error` or `rate_limit_exceeded`.""" + code: Literal["server_error", "rate_limit_exceeded", "invalid_prompt"] + """One of `server_error`, `rate_limit_exceeded`, or `invalid_prompt`.""" message: str """A human-readable description of the error.""" diff --git a/src/openai/types/chat/chat_completion_token_logprob.py b/src/openai/types/chat/chat_completion_token_logprob.py index 728845fb33..076ffb680c 100644 --- a/src/openai/types/chat/chat_completion_token_logprob.py +++ b/src/openai/types/chat/chat_completion_token_logprob.py @@ -20,7 +20,12 @@ class TopLogprob(BaseModel): """ logprob: float - """The log probability of this token.""" + """The log probability of this token, if it is within the top 20 most likely + tokens. + + Otherwise, the value `-9999.0` is used to signify that the token is very + unlikely. + """ class ChatCompletionTokenLogprob(BaseModel): @@ -36,7 +41,12 @@ class ChatCompletionTokenLogprob(BaseModel): """ logprob: float - """The log probability of this token.""" + """The log probability of this token, if it is within the top 20 most likely + tokens. + + Otherwise, the value `-9999.0` is used to signify that the token is very + unlikely. + """ top_logprobs: List[TopLogprob] """List of the most likely tokens and their log probability, at this token diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index e02a81bc51..9afbacb874 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -195,9 +195,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): top_logprobs: Optional[int] """ - An integer between 0 and 5 specifying the number of most likely tokens to return - at each token position, each with an associated log probability. `logprobs` must - be set to `true` if this parameter is used. + An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + `logprobs` must be set to `true` if this parameter is used. """ top_p: Optional[float] diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py index 7b015fc176..5714f97fa9 100644 --- a/src/openai/types/image_create_variation_params.py +++ b/src/openai/types/image_create_variation_params.py @@ -32,7 +32,8 @@ class ImageCreateVariationParams(TypedDict, total=False): response_format: Optional[Literal["url", "b64_json"]] """The format in which the generated images are returned. - Must be one of `url` or `b64_json`. + Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the + image has been generated. """ size: Optional[Literal["256x256", "512x512", "1024x1024"]] diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 043885cc38..751ec4fe7a 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -43,7 +43,8 @@ class ImageEditParams(TypedDict, total=False): response_format: Optional[Literal["url", "b64_json"]] """The format in which the generated images are returned. - Must be one of `url` or `b64_json`. + Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the + image has been generated. """ size: Optional[Literal["256x256", "512x512", "1024x1024"]] diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 7eca29a7ba..3ff1b979db 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -35,7 +35,8 @@ class ImageGenerateParams(TypedDict, total=False): response_format: Optional[Literal["url", "b64_json"]] """The format in which the generated images are returned. - Must be one of `url` or `b64_json`. + Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the + image has been generated. """ size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index 09c9a6058b..1c26ec3367 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -114,7 +114,4 @@ class Moderation(BaseModel): """A list of the categories along with their scores as predicted by model.""" flagged: bool - """ - Whether the content violates - [OpenAI's usage policies](/policies/usage-policies). - """ + """Whether any of the below categories are flagged.""" From dae0ec80d51d4c7677d8df010471e08a1c76fac5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:35:14 +0100 Subject: [PATCH 349/914] chore(client): improve error message for invalid http_client argument (#1216) --- src/openai/_base_client.py | 10 ++++++++++ tests/test_client.py | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index dda280f6aa..f431128eef 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -780,6 +780,11 @@ def __init__( else: timeout = DEFAULT_TIMEOUT + if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" + ) + super().__init__( version=version, limits=limits, @@ -1322,6 +1327,11 @@ def __init__( else: timeout = DEFAULT_TIMEOUT + if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" + ) + super().__init__( version=version, base_url=base_url, diff --git a/tests/test_client.py b/tests/test_client.py index 625b822352..a6f936da67 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -292,6 +292,16 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + async def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + async with httpx.AsyncClient() as http_client: + OpenAI( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + def test_default_headers_option(self) -> None: client = OpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} @@ -994,6 +1004,16 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + with httpx.Client() as http_client: + AsyncOpenAI( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + def test_default_headers_option(self) -> None: client = AsyncOpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} From 7ccf0a9f4e6171e6c0e66f3f90b3bccf21605f09 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 6 Mar 2024 12:59:13 +0100 Subject: [PATCH 350/914] fix(streaming): improve error messages (#1218) https://github.com/openai/openai-python/issues/1160 --- src/openai/_streaming.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 2bc8d6a14d..41ed11074f 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -65,8 +65,15 @@ def __stream__(self) -> Iterator[_T]: if sse.event is None: data = sse.json() if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + raise APIError( - message="An error occurred during streaming", + message=message, request=self.response.request, body=data["error"], ) @@ -145,8 +152,15 @@ async def __stream__(self) -> AsyncIterator[_T]: if sse.event is None: data = sse.json() if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + raise APIError( - message="An error occurred during streaming", + message=message, request=self.response.request, body=data["error"], ) From ff8154f8308256254d5940b1d87b3bf6e9407d39 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 6 Mar 2024 22:03:07 +0100 Subject: [PATCH 351/914] chore(internal): add core support for deserializing into number response (#1219) --- src/openai/_legacy_response.py | 8 ++++++++ src/openai/_response.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 6eaa691d9f..7285053409 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -107,6 +107,8 @@ class MyModel(BaseModel): - `list` - `Union` - `str` + - `int` + - `float` - `httpx.Response` """ cache_key = to if to is not None else self._cast_to @@ -220,6 +222,12 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to == str: return cast(R, response.text) + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + origin = get_origin(cast_to) or cast_to if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent): diff --git a/src/openai/_response.py b/src/openai/_response.py index b1e070122f..0eaf9778b7 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -172,6 +172,12 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to == bytes: return cast(R, response.content) + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + origin = get_origin(cast_to) or cast_to # handle the legacy binary response case @@ -277,6 +283,8 @@ class MyModel(BaseModel): - `list` - `Union` - `str` + - `int` + - `float` - `httpx.Response` """ cache_key = to if to is not None else self._cast_to From 6147fbe01920f21102394429288a860a9de604e4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:11:50 +0100 Subject: [PATCH 352/914] chore(internal): bump pyright (#1221) --- requirements-dev.lock | 4 ++-- src/openai/_legacy_response.py | 4 ++-- src/openai/_response.py | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index fa95964d07..0392de573f 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -17,7 +17,7 @@ argcomplete==3.1.2 # via nox attrs==23.1.0 # via pytest -azure-core==1.30.0 +azure-core==1.30.1 # via azure-identity azure-identity==1.15.0 certifi==2023.7.22 @@ -96,7 +96,7 @@ pydantic-core==2.10.1 # via pydantic pyjwt==2.8.0 # via msal -pyright==1.1.351 +pyright==1.1.353 pytest==7.1.1 # via pytest-asyncio pytest-asyncio==0.21.1 diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 7285053409..1a08144480 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -315,7 +315,7 @@ def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIRespon @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "true" kwargs["extra_headers"] = extra_headers @@ -332,7 +332,7 @@ def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P @functools.wraps(func) async def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "true" kwargs["extra_headers"] = extra_headers diff --git a/src/openai/_response.py b/src/openai/_response.py index 0eaf9778b7..f684b08c23 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -634,7 +634,7 @@ def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseCo @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" kwargs["extra_headers"] = extra_headers @@ -655,7 +655,7 @@ def async_to_streamed_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" kwargs["extra_headers"] = extra_headers @@ -679,7 +679,7 @@ def to_custom_streamed_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -704,7 +704,7 @@ def async_to_custom_streamed_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -724,7 +724,7 @@ def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]] @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers @@ -741,7 +741,7 @@ def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P @functools.wraps(func) async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers @@ -763,7 +763,7 @@ def to_custom_raw_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -786,7 +786,7 @@ def async_to_custom_raw_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls From 49cab6f25e0c815d43d5c111b5c140f4517c6721 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:23:35 +0100 Subject: [PATCH 353/914] chore(internal): support parsing Annotated types (#1222) --- src/openai/_legacy_response.py | 11 +++++++++- src/openai/_models.py | 14 ++++++++++++- src/openai/_response.py | 11 +++++++++- tests/test_legacy_response.py | 19 +++++++++++++++++ tests/test_models.py | 16 +++++++++++++-- tests/test_response.py | 37 +++++++++++++++++++++++++++++++++- tests/utils.py | 6 ++++++ 7 files changed, 108 insertions(+), 6 deletions(-) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 1a08144480..4585cd7423 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -13,7 +13,7 @@ import pydantic from ._types import NoneType -from ._utils import is_given +from ._utils import is_given, extract_type_arg, is_annotated_type from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type @@ -174,6 +174,10 @@ def elapsed(self) -> datetime.timedelta: return self.http_response.elapsed def _parse(self, *, to: type[_T] | None = None) -> R | _T: + # unwrap `Annotated[T, ...]` -> `T` + if to and is_annotated_type(to): + to = extract_type_arg(to, 0) + if self._stream: if to: if not is_stream_class_type(to): @@ -215,6 +219,11 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ) cast_to = to if to is not None else self._cast_to + + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + if cast_to is NoneType: return cast(R, None) diff --git a/src/openai/_models.py b/src/openai/_models.py index 810891497a..af68e6181a 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -30,7 +30,16 @@ AnyMapping, HttpxRequestFiles, ) -from ._utils import is_list, is_given, is_mapping, parse_date, parse_datetime, strip_not_given +from ._utils import ( + is_list, + is_given, + is_mapping, + parse_date, + parse_datetime, + strip_not_given, + extract_type_arg, + is_annotated_type, +) from ._compat import ( PYDANTIC_V2, ConfigDict, @@ -275,6 +284,9 @@ def construct_type(*, value: object, type_: type) -> object: If the given value does not match the expected type then it is returned as-is. """ + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(type_): + type_ = extract_type_arg(type_, 0) # we need to use the origin class for any types that are subscripted generics # e.g. Dict[str, object] diff --git a/src/openai/_response.py b/src/openai/_response.py index f684b08c23..47f484ef7a 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -25,7 +25,7 @@ import pydantic from ._types import NoneType -from ._utils import is_given, extract_type_var_from_base +from ._utils import is_given, extract_type_arg, is_annotated_type, extract_type_var_from_base from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type @@ -121,6 +121,10 @@ def __repr__(self) -> str: ) def _parse(self, *, to: type[_T] | None = None) -> R | _T: + # unwrap `Annotated[T, ...]` -> `T` + if to and is_annotated_type(to): + to = extract_type_arg(to, 0) + if self._is_sse_stream: if to: if not is_stream_class_type(to): @@ -162,6 +166,11 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ) cast_to = to if to is not None else self._cast_to + + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + if cast_to is NoneType: return cast(R, None) diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py index 995250a58c..45025f81d0 100644 --- a/tests/test_legacy_response.py +++ b/tests/test_legacy_response.py @@ -1,4 +1,6 @@ import json +from typing import cast +from typing_extensions import Annotated import httpx import pytest @@ -63,3 +65,20 @@ def test_response_parse_custom_model(client: OpenAI) -> None: obj = response.parse(to=CustomModel) assert obj.foo == "hello!" assert obj.bar == 2 + + +def test_response_parse_annotated_type(client: OpenAI) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 diff --git a/tests/test_models.py b/tests/test_models.py index 713bd2cb1b..d8a3c9ca5d 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,14 +1,14 @@ import json from typing import Any, Dict, List, Union, Optional, cast from datetime import datetime, timezone -from typing_extensions import Literal +from typing_extensions import Literal, Annotated import pytest import pydantic from pydantic import Field from openai._compat import PYDANTIC_V2, parse_obj, model_dump, model_json -from openai._models import BaseModel +from openai._models import BaseModel, construct_type class BasicModel(BaseModel): @@ -571,3 +571,15 @@ class OurModel(BaseModel): foo: Optional[str] = None takes_pydantic(OurModel()) + + +def test_annotated_types() -> None: + class Model(BaseModel): + value: str + + m = construct_type( + value={"value": "foo"}, + type_=cast(Any, Annotated[Model, "random metadata"]), + ) + assert isinstance(m, Model) + assert m.value == "foo" diff --git a/tests/test_response.py b/tests/test_response.py index 7c99327b46..af153b67c4 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -1,5 +1,6 @@ import json -from typing import List +from typing import List, cast +from typing_extensions import Annotated import httpx import pytest @@ -157,3 +158,37 @@ async def test_async_response_parse_custom_model(async_client: AsyncOpenAI) -> N obj = await response.parse(to=CustomModel) assert obj.foo == "hello!" assert obj.bar == 2 + + +def test_response_parse_annotated_type(client: OpenAI) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +async def test_async_response_parse_annotated_type(async_client: AsyncOpenAI) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 diff --git a/tests/utils.py b/tests/utils.py index 216b333550..43c3cb5cfe 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -14,6 +14,8 @@ is_list, is_list_type, is_union_type, + extract_type_arg, + is_annotated_type, ) from openai._compat import PYDANTIC_V2, field_outer_type, get_model_fields from openai._models import BaseModel @@ -49,6 +51,10 @@ def assert_matches_type( path: list[str], allow_none: bool = False, ) -> None: + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(type_): + type_ = extract_type_arg(type_, 0) + if allow_none and value is None: return From b3e37c8d4ff4feb608998bb2ab3378380c8d890f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 8 Mar 2024 16:48:04 +0100 Subject: [PATCH 354/914] chore: export NOT_GIVEN sentinel value (#1223) --- src/openai/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 118fe8ee93..1037e3cdd5 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -6,7 +6,7 @@ from typing_extensions import override from . import types -from ._types import NoneType, Transport, ProxiesTypes +from ._types import NOT_GIVEN, NoneType, NotGiven, Transport, ProxiesTypes from ._utils import file_from_path from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions from ._models import BaseModel @@ -37,6 +37,8 @@ "NoneType", "Transport", "ProxiesTypes", + "NotGiven", + "NOT_GIVEN", "OpenAIError", "APIError", "APIStatusError", From 0f0aec55cf76610467ba2c8e6627ee19f19413ce Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:28:46 +0100 Subject: [PATCH 355/914] chore(internal): improve deserialisation of discriminated unions (#1227) --- src/openai/_models.py | 160 +++++++++++++++++++++++++++- src/openai/_utils/_transform.py | 5 +- tests/test_models.py | 180 ++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+), 2 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index af68e6181a..88afa40810 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -10,6 +10,7 @@ Protocol, Required, TypedDict, + TypeGuard, final, override, runtime_checkable, @@ -31,6 +32,7 @@ HttpxRequestFiles, ) from ._utils import ( + PropertyInfo, is_list, is_given, is_mapping, @@ -39,6 +41,7 @@ strip_not_given, extract_type_arg, is_annotated_type, + strip_annotated_type, ) from ._compat import ( PYDANTIC_V2, @@ -55,6 +58,9 @@ ) from ._constants import RAW_RESPONSE_HEADER +if TYPE_CHECKING: + from pydantic_core.core_schema import ModelField, ModelFieldsSchema + __all__ = ["BaseModel", "GenericModel"] _T = TypeVar("_T") @@ -268,7 +274,6 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: def is_basemodel(type_: type) -> bool: """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" - origin = get_origin(type_) or type_ if is_union(type_): for variant in get_args(type_): if is_basemodel(variant): @@ -276,6 +281,11 @@ def is_basemodel(type_: type) -> bool: return False + return is_basemodel_type(type_) + + +def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: + origin = get_origin(type_) or type_ return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) @@ -286,7 +296,10 @@ def construct_type(*, value: object, type_: type) -> object: """ # unwrap `Annotated[T, ...]` -> `T` if is_annotated_type(type_): + meta = get_args(type_)[1:] type_ = extract_type_arg(type_, 0) + else: + meta = tuple() # we need to use the origin class for any types that are subscripted generics # e.g. Dict[str, object] @@ -299,6 +312,28 @@ def construct_type(*, value: object, type_: type) -> object: except Exception: pass + # if the type is a discriminated union then we want to construct the right variant + # in the union, even if the data doesn't match exactly, otherwise we'd break code + # that relies on the constructed class types, e.g. + # + # class FooType: + # kind: Literal['foo'] + # value: str + # + # class BarType: + # kind: Literal['bar'] + # value: int + # + # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then + # we'd end up constructing `FooType` when it should be `BarType`. + discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) + if discriminator and is_mapping(value): + variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) + if variant_value and isinstance(variant_value, str): + variant_type = discriminator.mapping.get(variant_value) + if variant_type: + return construct_type(type_=variant_type, value=value) + # if the data is not valid, use the first variant that doesn't fail while deserializing for variant in args: try: @@ -356,6 +391,129 @@ def construct_type(*, value: object, type_: type) -> object: return value +@runtime_checkable +class CachedDiscriminatorType(Protocol): + __discriminator__: DiscriminatorDetails + + +class DiscriminatorDetails: + field_name: str + """The name of the discriminator field in the variant class, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] + ``` + + Will result in field_name='type' + """ + + field_alias_from: str | None + """The name of the discriminator field in the API response, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] = Field(alias='type_from_api') + ``` + + Will result in field_alias_from='type_from_api' + """ + + mapping: dict[str, type] + """Mapping of discriminator value to variant type, e.g. + + {'foo': FooVariant, 'bar': BarVariant} + """ + + def __init__( + self, + *, + mapping: dict[str, type], + discriminator_field: str, + discriminator_alias: str | None, + ) -> None: + self.mapping = mapping + self.field_name = discriminator_field + self.field_alias_from = discriminator_alias + + +def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: + if isinstance(union, CachedDiscriminatorType): + return union.__discriminator__ + + discriminator_field_name: str | None = None + + for annotation in meta_annotations: + if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: + discriminator_field_name = annotation.discriminator + break + + if not discriminator_field_name: + return None + + mapping: dict[str, type] = {} + discriminator_alias: str | None = None + + for variant in get_args(union): + variant = strip_annotated_type(variant) + if is_basemodel_type(variant): + if PYDANTIC_V2: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field.get("serialization_alias") + + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in field_schema["expected"]: + if isinstance(entry, str): + mapping[entry] = variant + else: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field_info.alias + + if field_info.annotation and is_literal_type(field_info.annotation): + for entry in get_args(field_info.annotation): + if isinstance(entry, str): + mapping[entry] = variant + + if not mapping: + return None + + details = DiscriminatorDetails( + mapping=mapping, + discriminator_field=discriminator_field_name, + discriminator_alias=discriminator_alias, + ) + cast(CachedDiscriminatorType, union).__discriminator__ = details + return details + + +def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: + schema = model.__pydantic_core_schema__ + if schema["type"] != "model": + return None + + fields_schema = schema["schema"] + if fields_schema["type"] != "model-fields": + return None + + fields_schema = cast("ModelFieldsSchema", fields_schema) + + field = fields_schema["fields"].get(field_name) + if not field: + return None + + return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] + + def validate_type(*, type_: type[_T], value: object) -> _T: """Strict validation that the given value matches the expected type""" if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 1bd1330c94..47e262a515 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -51,6 +51,7 @@ class MyParams(TypedDict): alias: str | None format: PropertyFormat | None format_template: str | None + discriminator: str | None def __init__( self, @@ -58,14 +59,16 @@ def __init__( 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}')" + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" def maybe_transform( diff --git a/tests/test_models.py b/tests/test_models.py index d8a3c9ca5d..d003d32181 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -7,6 +7,7 @@ import pydantic from pydantic import Field +from openai._utils import PropertyInfo from openai._compat import PYDANTIC_V2, parse_obj, model_dump, model_json from openai._models import BaseModel, construct_type @@ -583,3 +584,182 @@ class Model(BaseModel): ) assert isinstance(m, Model) assert m.value == "foo" + + +def test_discriminated_unions_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, A) + assert m.type == "a" + if PYDANTIC_V2: + assert m.data == 100 # type: ignore[comparison-overlap] + else: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + + +def test_discriminated_unions_unknown_variant() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "c", "data": None, "new_thing": "bar"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + + # just chooses the first variant + assert isinstance(m, A) + assert m.type == "c" # type: ignore[comparison-overlap] + assert m.data == None # type: ignore[unreachable] + assert m.new_thing == "bar" + + +def test_discriminated_unions_invalid_data_nested_unions() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + class C(BaseModel): + type: Literal["c"] + + data: bool + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "c", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, C) + assert m.type == "c" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_with_aliases_invalid_data() -> None: + class A(BaseModel): + foo_type: Literal["a"] = Field(alias="type") + + data: str + + class B(BaseModel): + foo_type: Literal["b"] = Field(alias="type") + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, B) + assert m.foo_type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, A) + assert m.foo_type == "a" + if PYDANTIC_V2: + assert m.data == 100 # type: ignore[comparison-overlap] + else: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + + +def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["a"] + + data: int + + m = construct_type( + value={"type": "a", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "a" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_invalid_data_uses_cache() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + UnionType = cast(Any, Union[A, B]) + + assert not hasattr(UnionType, "__discriminator__") + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + discriminator = UnionType.__discriminator__ + assert discriminator is not None + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + # if the discriminator details object stays the same between invocations then + # we hit the cache + assert UnionType.__discriminator__ is discriminator From b54ab2f86fd19a5b5861f87941b4a9de57bf6c7a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 12 Mar 2024 18:25:10 +0100 Subject: [PATCH 356/914] chore(types): include discriminators in unions (#1228) --- src/openai/types/beta/assistant.py | 5 +++-- src/openai/types/beta/threads/message_content_text.py | 7 +++++-- src/openai/types/beta/threads/runs/code_tool_call.py | 7 +++++-- src/openai/types/beta/threads/runs/run_step.py | 5 +++-- .../types/beta/threads/runs/tool_calls_step_details.py | 5 +++-- src/openai/types/beta/threads/thread_message.py | 5 +++-- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 7a29984b50..7ba50652aa 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. from typing import List, Union, Optional -from typing_extensions import Literal +from typing_extensions import Literal, Annotated from ..shared import FunctionDefinition +from ..._utils import PropertyInfo from ..._models import BaseModel __all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetrieval", "ToolFunction"] @@ -26,7 +27,7 @@ class ToolFunction(BaseModel): """The type of tool being defined: `function`""" -Tool = Union[ToolCodeInterpreter, ToolRetrieval, ToolFunction] +Tool = Annotated[Union[ToolCodeInterpreter, ToolRetrieval, ToolFunction], PropertyInfo(discriminator="type")] class Assistant(BaseModel): diff --git a/src/openai/types/beta/threads/message_content_text.py b/src/openai/types/beta/threads/message_content_text.py index b529a384c6..dd05ff96ca 100644 --- a/src/openai/types/beta/threads/message_content_text.py +++ b/src/openai/types/beta/threads/message_content_text.py @@ -1,8 +1,9 @@ # File generated from our OpenAPI spec by Stainless. from typing import List, Union -from typing_extensions import Literal +from typing_extensions import Literal, Annotated +from ...._utils import PropertyInfo from ...._models import BaseModel __all__ = [ @@ -57,7 +58,9 @@ class TextAnnotationFilePath(BaseModel): """Always `file_path`.""" -TextAnnotation = Union[TextAnnotationFileCitation, TextAnnotationFilePath] +TextAnnotation = Annotated[ + Union[TextAnnotationFileCitation, TextAnnotationFilePath], PropertyInfo(discriminator="type") +] class Text(BaseModel): diff --git a/src/openai/types/beta/threads/runs/code_tool_call.py b/src/openai/types/beta/threads/runs/code_tool_call.py index f808005ecb..0de47b379b 100644 --- a/src/openai/types/beta/threads/runs/code_tool_call.py +++ b/src/openai/types/beta/threads/runs/code_tool_call.py @@ -1,8 +1,9 @@ # File generated from our OpenAPI spec by Stainless. from typing import List, Union -from typing_extensions import Literal +from typing_extensions import Literal, Annotated +from ....._utils import PropertyInfo from ....._models import BaseModel __all__ = [ @@ -38,7 +39,9 @@ class CodeInterpreterOutputImage(BaseModel): """Always `image`.""" -CodeInterpreterOutput = Union[CodeInterpreterOutputLogs, CodeInterpreterOutputImage] +CodeInterpreterOutput = Annotated[ + Union[CodeInterpreterOutputLogs, CodeInterpreterOutputImage], PropertyInfo(discriminator="type") +] class CodeInterpreter(BaseModel): diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 01aab8e9a6..899883ac2d 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -1,8 +1,9 @@ # File generated from our OpenAPI spec by Stainless. from typing import Union, Optional -from typing_extensions import Literal +from typing_extensions import Literal, Annotated +from ....._utils import PropertyInfo from ....._models import BaseModel from .tool_calls_step_details import ToolCallsStepDetails from .message_creation_step_details import MessageCreationStepDetails @@ -18,7 +19,7 @@ class LastError(BaseModel): """A human-readable description of the error.""" -StepDetails = Union[MessageCreationStepDetails, ToolCallsStepDetails] +StepDetails = Annotated[Union[MessageCreationStepDetails, ToolCallsStepDetails], PropertyInfo(discriminator="type")] class Usage(BaseModel): diff --git a/src/openai/types/beta/threads/runs/tool_calls_step_details.py b/src/openai/types/beta/threads/runs/tool_calls_step_details.py index 80eb90bf66..b1b5a72bee 100644 --- a/src/openai/types/beta/threads/runs/tool_calls_step_details.py +++ b/src/openai/types/beta/threads/runs/tool_calls_step_details.py @@ -1,8 +1,9 @@ # File generated from our OpenAPI spec by Stainless. from typing import List, Union -from typing_extensions import Literal +from typing_extensions import Literal, Annotated +from ....._utils import PropertyInfo from ....._models import BaseModel from .code_tool_call import CodeToolCall from .function_tool_call import FunctionToolCall @@ -10,7 +11,7 @@ __all__ = ["ToolCallsStepDetails", "ToolCall"] -ToolCall = Union[CodeToolCall, RetrievalToolCall, FunctionToolCall] +ToolCall = Annotated[Union[CodeToolCall, RetrievalToolCall, FunctionToolCall], PropertyInfo(discriminator="type")] class ToolCallsStepDetails(BaseModel): diff --git a/src/openai/types/beta/threads/thread_message.py b/src/openai/types/beta/threads/thread_message.py index 25b3a199f7..6ed5da1401 100644 --- a/src/openai/types/beta/threads/thread_message.py +++ b/src/openai/types/beta/threads/thread_message.py @@ -1,15 +1,16 @@ # File generated from our OpenAPI spec by Stainless. from typing import List, Union, Optional -from typing_extensions import Literal +from typing_extensions import Literal, Annotated +from ...._utils import PropertyInfo from ...._models import BaseModel from .message_content_text import MessageContentText from .message_content_image_file import MessageContentImageFile __all__ = ["ThreadMessage", "Content"] -Content = Union[MessageContentImageFile, MessageContentText] +Content = Annotated[Union[MessageContentImageFile, MessageContentText], PropertyInfo(discriminator="type")] class ThreadMessage(BaseModel): From ec104bf08587250689fcdca47ff8ab579a03960c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 13 Mar 2024 01:03:38 -0400 Subject: [PATCH 357/914] release: 1.13.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c4bf1b6c04..0d3c59d336 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.13.3" + ".": "1.13.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 201757c90d..2c80f70cc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 1.13.4 (2024-03-13) + +Full Changelog: [v1.13.3...v1.13.4](https://github.com/openai/openai-python/compare/v1.13.3...v1.13.4) + +### Bug Fixes + +* **streaming:** improve error messages ([#1218](https://github.com/openai/openai-python/issues/1218)) ([4f5ff29](https://github.com/openai/openai-python/commit/4f5ff298601b5a8bfbf0a9d0c0d1329d1502a205)) + + +### Chores + +* **api:** update docs ([#1212](https://github.com/openai/openai-python/issues/1212)) ([71236e0](https://github.com/openai/openai-python/commit/71236e0de4012a249af4c1ffd95973a8ba4fa61f)) +* **client:** improve error message for invalid http_client argument ([#1216](https://github.com/openai/openai-python/issues/1216)) ([d0c928a](https://github.com/openai/openai-python/commit/d0c928abbd99020fe828350f3adfd10c638a2eed)) +* **docs:** mention install from git repo ([#1203](https://github.com/openai/openai-python/issues/1203)) ([3ab6f44](https://github.com/openai/openai-python/commit/3ab6f447ffd8d2394e58416e401e545a99ec85af)) +* export NOT_GIVEN sentinel value ([#1223](https://github.com/openai/openai-python/issues/1223)) ([8a4f76f](https://github.com/openai/openai-python/commit/8a4f76f992c66f20cd6aa070c8dc4839e4cf9f3c)) +* **internal:** add core support for deserializing into number response ([#1219](https://github.com/openai/openai-python/issues/1219)) ([004bc92](https://github.com/openai/openai-python/commit/004bc924ea579852b9266ca11aea93463cf75104)) +* **internal:** bump pyright ([#1221](https://github.com/openai/openai-python/issues/1221)) ([3c2e815](https://github.com/openai/openai-python/commit/3c2e815311ace4ff81ccd446b23ff50a4e099485)) +* **internal:** improve deserialisation of discriminated unions ([#1227](https://github.com/openai/openai-python/issues/1227)) ([4767259](https://github.com/openai/openai-python/commit/4767259d25ac135550b37b15e4c0497e5ff0330d)) +* **internal:** minor core client restructuring ([#1199](https://github.com/openai/openai-python/issues/1199)) ([4314cdc](https://github.com/openai/openai-python/commit/4314cdcd522537e6cbbd87206d5bb236f672ce05)) +* **internal:** split up transforms into sync / async ([#1210](https://github.com/openai/openai-python/issues/1210)) ([7853a83](https://github.com/openai/openai-python/commit/7853a8358864957cc183581bdf7c03810a7b2756)) +* **internal:** support more input types ([#1211](https://github.com/openai/openai-python/issues/1211)) ([d0e4baa](https://github.com/openai/openai-python/commit/d0e4baa40d32c2da0ce5ceef8e0c7193b98f2b5a)) +* **internal:** support parsing Annotated types ([#1222](https://github.com/openai/openai-python/issues/1222)) ([8598f81](https://github.com/openai/openai-python/commit/8598f81841eeab0ab00eb21fdec7e8756ffde909)) +* **types:** include discriminators in unions ([#1228](https://github.com/openai/openai-python/issues/1228)) ([3ba0dcc](https://github.com/openai/openai-python/commit/3ba0dcc19a2af0ef869c77da2805278f71ee96c2)) + + +### Documentation + +* **contributing:** improve wording ([#1201](https://github.com/openai/openai-python/issues/1201)) ([95a1e0e](https://github.com/openai/openai-python/commit/95a1e0ea8e5446c413606847ebf9e35afbc62bf9)) + ## 1.13.3 (2024-02-28) Full Changelog: [v1.13.2...v1.13.3](https://github.com/openai/openai-python/compare/v1.13.2...v1.13.3) diff --git a/pyproject.toml b/pyproject.toml index 171ede0aa4..9155a9aa22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.13.3" +version = "1.13.4" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 503a06141f..4c59f5e629 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.13.3" # x-release-please-version +__version__ = "1.13.4" # x-release-please-version From 5429f69670e4db70f0cb7420ddb27c9bd11b9508 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:35:35 -0400 Subject: [PATCH 358/914] release: 1.14.0 (#1234) * feat(assistants): add support for streaming (#1233) See the reference docs for more information: https://platform.openai.com/docs/api-reference/assistants-streaming We've also improved some of the names for the types in the assistants beta, non exhaustive list: - `CodeToolCall` -> `CodeInterpreterToolCall` - `MessageContentImageFile` -> `ImageFileContentBlock` - `MessageContentText` -> `TextContentBlock` - `ThreadMessage` -> `Message` - `ThreadMessageDeleted` -> `MessageDeleted` * release: 1.14.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 + api.md | 64 +- examples/assistant_stream.py | 33 + examples/assistant_stream_helpers.py | 78 ++ pyproject.toml | 3 +- requirements-dev.lock | 22 + src/openai/__init__.py | 4 + src/openai/_streaming.py | 38 + src/openai/_version.py | 2 +- src/openai/lib/streaming/__init__.py | 8 + src/openai/lib/streaming/_assistants.py | 1035 +++++++++++++++ .../resources/beta/assistants/assistants.py | 9 +- .../beta/threads/messages/messages.py | 38 +- .../resources/beta/threads/runs/runs.py | 1112 +++++++++++++++-- src/openai/resources/beta/threads/threads.py | 485 ++++++- src/openai/resources/chat/completions.py | 12 +- src/openai/resources/completions.py | 12 + src/openai/types/__init__.py | 6 +- src/openai/types/beta/__init__.py | 9 + src/openai/types/beta/assistant.py | 31 +- .../types/beta/assistant_create_params.py | 36 +- .../types/beta/assistant_stream_event.py | 276 ++++ src/openai/types/beta/assistant_tool.py | 13 + src/openai/types/beta/assistant_tool_param.py | 13 + .../types/beta/assistant_update_params.py | 36 +- .../types/beta/code_interpreter_tool.py | 12 + .../types/beta/code_interpreter_tool_param.py | 12 + src/openai/types/beta/function_tool.py | 15 + src/openai/types/beta/function_tool_param.py | 16 + src/openai/types/beta/retrieval_tool.py | 12 + src/openai/types/beta/retrieval_tool_param.py | 12 + .../beta/thread_create_and_run_params.py | 41 +- src/openai/types/beta/threads/__init__.py | 22 +- src/openai/types/beta/threads/annotation.py | 12 + .../types/beta/threads/annotation_delta.py | 14 + .../beta/threads/file_citation_annotation.py | 29 + .../threads/file_citation_delta_annotation.py | 33 + .../beta/threads/file_path_annotation.py | 26 + .../threads/file_path_delta_annotation.py | 30 + ...ge_content_image_file.py => image_file.py} | 11 +- .../beta/threads/image_file_content_block.py | 15 + .../types/beta/threads/image_file_delta.py | 15 + .../beta/threads/image_file_delta_block.py | 19 + .../threads/{thread_message.py => message.py} | 34 +- .../types/beta/threads/message_content.py | 12 + .../beta/threads/message_content_delta.py | 12 + .../beta/threads/message_content_text.py | 77 -- .../types/beta/threads/message_delta.py | 24 + .../types/beta/threads/message_delta_event.py | 19 + src/openai/types/beta/threads/run.py | 40 +- .../types/beta/threads/run_create_params.py | 43 +- .../threads/run_submit_tool_outputs_params.py | 34 +- .../types/beta/threads/runs/__init__.py | 13 +- .../threads/runs/code_interpreter_logs.py | 19 + .../runs/code_interpreter_output_image.py | 26 + ..._call.py => code_interpreter_tool_call.py} | 4 +- .../runs/code_interpreter_tool_call_delta.py | 44 + .../threads/runs/function_tool_call_delta.py | 41 + .../threads/runs/retrieval_tool_call_delta.py | 25 + .../types/beta/threads/runs/run_step_delta.py | 18 + .../beta/threads/runs/run_step_delta_event.py | 19 + .../runs/run_step_delta_message_delta.py | 20 + .../types/beta/threads/runs/tool_call.py | 15 + .../beta/threads/runs/tool_call_delta.py | 16 + .../threads/runs/tool_call_delta_object.py | 21 + .../threads/runs/tool_calls_step_details.py | 13 +- src/openai/types/beta/threads/text.py | 15 + .../types/beta/threads/text_content_block.py | 15 + src/openai/types/beta/threads/text_delta.py | 15 + .../types/beta/threads/text_delta_block.py | 19 + .../types/chat/completion_create_params.py | 3 +- src/openai/types/completion_create_params.py | 5 +- src/openai/types/shared/__init__.py | 1 + src/openai/types/shared/error_object.py | 17 + tests/api_resources/beta/test_threads.py | 154 ++- .../beta/threads/test_messages.py | 62 +- tests/api_resources/beta/threads/test_runs.py | 316 ++++- 78 files changed, 4454 insertions(+), 488 deletions(-) create mode 100644 examples/assistant_stream.py create mode 100644 examples/assistant_stream_helpers.py create mode 100644 src/openai/lib/streaming/__init__.py create mode 100644 src/openai/lib/streaming/_assistants.py create mode 100644 src/openai/types/beta/assistant_stream_event.py create mode 100644 src/openai/types/beta/assistant_tool.py create mode 100644 src/openai/types/beta/assistant_tool_param.py create mode 100644 src/openai/types/beta/code_interpreter_tool.py create mode 100644 src/openai/types/beta/code_interpreter_tool_param.py create mode 100644 src/openai/types/beta/function_tool.py create mode 100644 src/openai/types/beta/function_tool_param.py create mode 100644 src/openai/types/beta/retrieval_tool.py create mode 100644 src/openai/types/beta/retrieval_tool_param.py create mode 100644 src/openai/types/beta/threads/annotation.py create mode 100644 src/openai/types/beta/threads/annotation_delta.py create mode 100644 src/openai/types/beta/threads/file_citation_annotation.py create mode 100644 src/openai/types/beta/threads/file_citation_delta_annotation.py create mode 100644 src/openai/types/beta/threads/file_path_annotation.py create mode 100644 src/openai/types/beta/threads/file_path_delta_annotation.py rename src/openai/types/beta/threads/{message_content_image_file.py => image_file.py} (54%) create mode 100644 src/openai/types/beta/threads/image_file_content_block.py create mode 100644 src/openai/types/beta/threads/image_file_delta.py create mode 100644 src/openai/types/beta/threads/image_file_delta_block.py rename src/openai/types/beta/threads/{thread_message.py => message.py} (63%) create mode 100644 src/openai/types/beta/threads/message_content.py create mode 100644 src/openai/types/beta/threads/message_content_delta.py delete mode 100644 src/openai/types/beta/threads/message_content_text.py create mode 100644 src/openai/types/beta/threads/message_delta.py create mode 100644 src/openai/types/beta/threads/message_delta_event.py create mode 100644 src/openai/types/beta/threads/runs/code_interpreter_logs.py create mode 100644 src/openai/types/beta/threads/runs/code_interpreter_output_image.py rename src/openai/types/beta/threads/runs/{code_tool_call.py => code_interpreter_tool_call.py} (95%) create mode 100644 src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py create mode 100644 src/openai/types/beta/threads/runs/function_tool_call_delta.py create mode 100644 src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py create mode 100644 src/openai/types/beta/threads/runs/run_step_delta.py create mode 100644 src/openai/types/beta/threads/runs/run_step_delta_event.py create mode 100644 src/openai/types/beta/threads/runs/run_step_delta_message_delta.py create mode 100644 src/openai/types/beta/threads/runs/tool_call.py create mode 100644 src/openai/types/beta/threads/runs/tool_call_delta.py create mode 100644 src/openai/types/beta/threads/runs/tool_call_delta_object.py create mode 100644 src/openai/types/beta/threads/text.py create mode 100644 src/openai/types/beta/threads/text_content_block.py create mode 100644 src/openai/types/beta/threads/text_delta.py create mode 100644 src/openai/types/beta/threads/text_delta_block.py create mode 100644 src/openai/types/shared/error_object.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0d3c59d336..e72f11310e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.13.4" + ".": "1.14.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c80f70cc6..1f0fc7556d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.14.0 (2024-03-13) + +Full Changelog: [v1.13.4...v1.14.0](https://github.com/openai/openai-python/compare/v1.13.4...v1.14.0) + +### Features + +* **assistants:** add support for streaming ([#1233](https://github.com/openai/openai-python/issues/1233)) ([17635dc](https://github.com/openai/openai-python/commit/17635dccbeddf153f8201dbca18b44e16a1799b2)) + ## 1.13.4 (2024-03-13) Full Changelog: [v1.13.3...v1.13.4](https://github.com/openai/openai-python/compare/v1.13.3...v1.13.4) diff --git a/api.md b/api.md index 34352e6e72..29392cff13 100644 --- a/api.md +++ b/api.md @@ -1,7 +1,7 @@ # Shared Types ```python -from openai.types import FunctionDefinition, FunctionParameters +from openai.types import ErrorObject, FunctionDefinition, FunctionParameters ``` # Completions @@ -177,7 +177,19 @@ Methods: Types: ```python -from openai.types.beta import Assistant, AssistantDeleted +from openai.types.beta import ( + Assistant, + AssistantDeleted, + AssistantStreamEvent, + AssistantTool, + CodeInterpreterTool, + FunctionTool, + MessageStreamEvent, + RetrievalTool, + RunStepStreamEvent, + RunStreamEvent, + ThreadStreamEvent, +) ``` Methods: @@ -218,6 +230,7 @@ Methods: - client.beta.threads.update(thread_id, \*\*params) -> Thread - client.beta.threads.delete(thread_id) -> ThreadDeleted - client.beta.threads.create_and_run(\*\*params) -> Run +- client.beta.threads.create_and_run_stream(\*args) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT] ### Runs @@ -235,6 +248,8 @@ Methods: - client.beta.threads.runs.list(thread_id, \*\*params) -> SyncCursorPage[Run] - client.beta.threads.runs.cancel(run_id, \*, thread_id) -> Run - client.beta.threads.runs.submit_tool_outputs(run_id, \*, thread_id, \*\*params) -> Run +- client.beta.threads.runs.create_and_stream(\*args) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT] +- client.beta.threads.runs.submit_tool_outputs_stream(\*args) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT] #### Steps @@ -242,11 +257,22 @@ Types: ```python from openai.types.beta.threads.runs import ( - CodeToolCall, + CodeInterpreterLogs, + CodeInterpreterOutputImage, + CodeInterpreterToolCall, + CodeInterpreterToolCallDelta, FunctionToolCall, + FunctionToolCallDelta, MessageCreationStepDetails, RetrievalToolCall, + RetrievalToolCallDelta, RunStep, + RunStepDelta, + RunStepDeltaEvent, + RunStepDeltaMessageDelta, + ToolCall, + ToolCallDelta, + ToolCallDeltaObject, ToolCallsStepDetails, ) ``` @@ -262,19 +288,35 @@ Types: ```python from openai.types.beta.threads import ( - MessageContentImageFile, - MessageContentText, - ThreadMessage, - ThreadMessageDeleted, + Annotation, + AnnotationDelta, + FileCitationAnnotation, + FileCitationDeltaAnnotation, + FilePathAnnotation, + FilePathDeltaAnnotation, + ImageFile, + ImageFileContentBlock, + ImageFileDelta, + ImageFileDeltaBlock, + Message, + MessageContent, + MessageContentDelta, + MessageDeleted, + MessageDelta, + MessageDeltaEvent, + Text, + TextContentBlock, + TextDelta, + TextDeltaBlock, ) ``` Methods: -- client.beta.threads.messages.create(thread_id, \*\*params) -> ThreadMessage -- client.beta.threads.messages.retrieve(message_id, \*, thread_id) -> ThreadMessage -- client.beta.threads.messages.update(message_id, \*, thread_id, \*\*params) -> ThreadMessage -- client.beta.threads.messages.list(thread_id, \*\*params) -> SyncCursorPage[ThreadMessage] +- client.beta.threads.messages.create(thread_id, \*\*params) -> Message +- client.beta.threads.messages.retrieve(message_id, \*, thread_id) -> Message +- client.beta.threads.messages.update(message_id, \*, thread_id, \*\*params) -> Message +- client.beta.threads.messages.list(thread_id, \*\*params) -> SyncCursorPage[Message] #### Files diff --git a/examples/assistant_stream.py b/examples/assistant_stream.py new file mode 100644 index 0000000000..0465d3930f --- /dev/null +++ b/examples/assistant_stream.py @@ -0,0 +1,33 @@ +import openai + +# gets API Key from environment variable OPENAI_API_KEY +client = openai.OpenAI() + +assistant = client.beta.assistants.create( + name="Math Tutor", + instructions="You are a personal math tutor. Write and run code to answer math questions.", + tools=[{"type": "code_interpreter"}], + model="gpt-4-1106-preview", +) + +thread = client.beta.threads.create() + +message = client.beta.threads.messages.create( + thread_id=thread.id, + role="user", + content="I need to solve the equation `3x + 11 = 14`. Can you help me?", +) + +print("starting run stream") + +stream = client.beta.threads.runs.create( + thread_id=thread.id, + assistant_id=assistant.id, + instructions="Please address the user as Jane Doe. The user has a premium account.", + stream=True, +) + +for event in stream: + print(event.model_dump_json(indent=2, exclude_unset=True)) + +client.beta.assistants.delete(assistant.id) diff --git a/examples/assistant_stream_helpers.py b/examples/assistant_stream_helpers.py new file mode 100644 index 0000000000..6c2aae0b46 --- /dev/null +++ b/examples/assistant_stream_helpers.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from typing_extensions import override + +import openai +from openai import AssistantEventHandler +from openai.types.beta import AssistantStreamEvent +from openai.types.beta.threads import Text, TextDelta +from openai.types.beta.threads.runs import RunStep, RunStepDelta + + +class EventHandler(AssistantEventHandler): + @override + def on_event(self, event: AssistantStreamEvent) -> None: + if event.event == "thread.run.step.created": + details = event.data.step_details + if details.type == "tool_calls": + print("Generating code to interpret:\n\n```py") + elif event.event == "thread.message.created": + print("\nResponse:\n") + + @override + def on_text_delta(self, delta: TextDelta, snapshot: Text) -> None: + print(delta.value, end="", flush=True) + + @override + def on_run_step_done(self, run_step: RunStep) -> None: + details = run_step.step_details + if details.type == "tool_calls": + for tool in details.tool_calls: + if tool.type == "code_interpreter": + print("\n```\nExecuting code...") + + @override + def on_run_step_delta(self, delta: RunStepDelta, snapshot: RunStep) -> None: + details = delta.step_details + if details is not None and details.type == "tool_calls": + for tool in details.tool_calls or []: + if tool.type == "code_interpreter" and tool.code_interpreter and tool.code_interpreter.input: + print(tool.code_interpreter.input, end="", flush=True) + + +def main() -> None: + client = openai.OpenAI() + + assistant = client.beta.assistants.create( + name="Math Tutor", + instructions="You are a personal math tutor. Write and run code to answer math questions.", + tools=[{"type": "code_interpreter"}], + model="gpt-4-1106-preview", + ) + + try: + question = "I need to solve the equation `3x + 11 = 14`. Can you help me?" + + thread = client.beta.threads.create( + messages=[ + { + "role": "user", + "content": question, + }, + ] + ) + print(f"Question: {question}\n") + + with client.beta.threads.runs.create_and_stream( + thread_id=thread.id, + assistant_id=assistant.id, + instructions="Please address the user as Jane Doe. The user has a premium account.", + event_handler=EventHandler(), + ) as stream: + stream.until_done() + print() + finally: + client.beta.assistants.delete(assistant.id) + + +main() diff --git a/pyproject.toml b/pyproject.toml index 9155a9aa22..0856032512 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.13.4" +version = "1.14.0" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" @@ -60,6 +60,7 @@ dev-dependencies = [ "nox", "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", + "inline-snapshot >=0.7.0", "azure-identity >=1.14.1", "types-tqdm > 4", "types-pyaudio > 0" diff --git a/requirements-dev.lock b/requirements-dev.lock index 0392de573f..9d79557b3a 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -15,11 +15,15 @@ anyio==4.1.0 # via openai argcomplete==3.1.2 # via nox +asttokens==2.4.1 + # via inline-snapshot attrs==23.1.0 # via pytest azure-core==1.30.1 # via azure-identity azure-identity==1.15.0 +black==24.2.0 + # via inline-snapshot certifi==2023.7.22 # via httpcore # via httpx @@ -28,6 +32,9 @@ cffi==1.16.0 # via cryptography charset-normalizer==3.3.2 # via requests +click==8.1.7 + # via black + # via inline-snapshot colorlog==6.7.0 # via nox cryptography==42.0.5 @@ -41,6 +48,8 @@ distro==1.8.0 # via openai exceptiongroup==1.1.3 # via anyio +executing==2.0.1 + # via inline-snapshot filelock==3.12.4 # via virtualenv h11==0.14.0 @@ -57,6 +66,7 @@ idna==3.4 importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest +inline-snapshot==0.7.0 msal==1.27.0 # via azure-identity # via msal-extensions @@ -64,6 +74,7 @@ msal-extensions==1.1.0 # via azure-identity mypy==1.7.1 mypy-extensions==1.0.0 + # via black # via mypy nodeenv==1.8.0 # via pyright @@ -73,6 +84,7 @@ numpy==1.26.3 # via pandas # via pandas-stubs packaging==23.2 + # via black # via msal-extensions # via nox # via pytest @@ -80,7 +92,10 @@ pandas==2.1.4 # via openai pandas-stubs==2.1.4.231227 # via openai +pathspec==0.12.1 + # via black platformdirs==3.11.0 + # via black # via virtualenv pluggy==1.3.0 # via pytest @@ -114,6 +129,7 @@ ruff==0.1.9 setuptools==68.2.2 # via nodeenv six==1.16.0 + # via asttokens # via azure-core # via python-dateutil sniffio==1.3.0 @@ -121,7 +137,10 @@ sniffio==1.3.0 # via httpx # via openai time-machine==2.9.0 +toml==0.10.2 + # via inline-snapshot tomli==2.0.1 + # via black # via mypy # via pytest tqdm==4.66.1 @@ -129,9 +148,12 @@ tqdm==4.66.1 types-pyaudio==0.2.16.20240106 types-pytz==2024.1.0.20240203 # via pandas-stubs +types-toml==0.10.8.20240310 + # via inline-snapshot types-tqdm==4.66.0.2 typing-extensions==4.8.0 # via azure-core + # via black # via mypy # via openai # via pydantic diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 1037e3cdd5..909be95c97 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -69,6 +69,10 @@ from .version import VERSION as VERSION from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI from .lib._old_api import * +from .lib.streaming import ( + AssistantEventHandler as AssistantEventHandler, + AsyncAssistantEventHandler as AsyncAssistantEventHandler, +) _setup_logging() diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 41ed11074f..9c7cc6a573 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -80,6 +80,25 @@ def __stream__(self) -> Iterator[_T]: yield process_data(data=data, cast_to=cast_to, response=response) + else: + data = sse.json() + + if sse.event == "error" and is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) + # Ensure the entire stream is consumed for _sse in iterator: ... @@ -167,6 +186,25 @@ async def __stream__(self) -> AsyncIterator[_T]: yield process_data(data=data, cast_to=cast_to, response=response) + else: + data = sse.json() + + if sse.event == "error" and is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) + # Ensure the entire stream is consumed async for _sse in iterator: ... diff --git a/src/openai/_version.py b/src/openai/_version.py index 4c59f5e629..134799ff42 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.13.4" # x-release-please-version +__version__ = "1.14.0" # x-release-please-version diff --git a/src/openai/lib/streaming/__init__.py b/src/openai/lib/streaming/__init__.py new file mode 100644 index 0000000000..eb378d2561 --- /dev/null +++ b/src/openai/lib/streaming/__init__.py @@ -0,0 +1,8 @@ +from ._assistants import ( + AssistantEventHandler as AssistantEventHandler, + AssistantEventHandlerT as AssistantEventHandlerT, + AssistantStreamManager as AssistantStreamManager, + AsyncAssistantEventHandler as AsyncAssistantEventHandler, + AsyncAssistantEventHandlerT as AsyncAssistantEventHandlerT, + AsyncAssistantStreamManager as AsyncAssistantStreamManager, +) diff --git a/src/openai/lib/streaming/_assistants.py b/src/openai/lib/streaming/_assistants.py new file mode 100644 index 0000000000..03d97ec2eb --- /dev/null +++ b/src/openai/lib/streaming/_assistants.py @@ -0,0 +1,1035 @@ +from __future__ import annotations + +import asyncio +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Callable, Iterable, Iterator, cast +from typing_extensions import Awaitable, AsyncIterable, AsyncIterator, assert_never + +import httpx + +from ..._utils import is_dict, is_list, consume_sync_iterator, consume_async_iterator +from ..._models import construct_type +from ..._streaming import Stream, AsyncStream +from ...types.beta import AssistantStreamEvent +from ...types.beta.threads import ( + Run, + Text, + Message, + ImageFile, + TextDelta, + MessageDelta, + MessageContent, + MessageContentDelta, +) +from ...types.beta.threads.runs import RunStep, ToolCall, RunStepDelta, ToolCallDelta + + +class AssistantEventHandler: + text_deltas: Iterable[str] + """Iterator over just the text deltas in the stream. + + This corresponds to the `thread.message.delta` event + in the API. + + ```py + for text in stream.text_deltas: + print(text, end="", flush=True) + print() + ``` + """ + + def __init__(self) -> None: + self._current_event: AssistantStreamEvent | None = None + self._current_message_content_index: int | None = None + self._current_message_content: MessageContent | None = None + self._current_tool_call_index: int | None = None + self._current_tool_call: ToolCall | None = None + self.__current_run_step_id: str | None = None + self.__current_run: Run | None = None + self.__run_step_snapshots: dict[str, RunStep] = {} + self.__message_snapshots: dict[str, Message] = {} + self.__current_message_snapshot: Message | None = None + + self.text_deltas = self.__text_deltas__() + self._iterator = self.__stream__() + self.__stream: Stream[AssistantStreamEvent] | None = None + + def _init(self, stream: Stream[AssistantStreamEvent]) -> None: + if self.__stream: + raise RuntimeError( + "A single event handler cannot be shared between multiple streams; You will need to construct a new event handler instance" + ) + + self.__stream = stream + + def __next__(self) -> AssistantStreamEvent: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[AssistantStreamEvent]: + for item in self._iterator: + yield item + + @property + def current_event(self) -> AssistantStreamEvent | None: + return self._current_event + + @property + def current_run(self) -> Run | None: + return self.__current_run + + @property + def current_run_step_snapshot(self) -> RunStep | None: + if not self.__current_run_step_id: + return None + + return self.__run_step_snapshots[self.__current_run_step_id] + + @property + def current_message_snapshot(self) -> Message | None: + return self.__current_message_snapshot + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called when the context manager exits. + """ + if self.__stream: + self.__stream.close() + + def until_done(self) -> None: + """Waits until the stream has been consumed""" + consume_sync_iterator(self) + + def get_final_run(self) -> Run: + """Wait for the stream to finish and returns the completed Run object""" + self.until_done() + + if not self.__current_run: + raise RuntimeError("No final run object found") + + return self.__current_run + + def get_final_run_steps(self) -> list[RunStep]: + """Wait for the stream to finish and returns the steps taken in this run""" + self.until_done() + + if not self.__run_step_snapshots: + raise RuntimeError("No run steps found") + + return [step for step in self.__run_step_snapshots.values()] + + def get_final_messages(self) -> list[Message]: + """Wait for the stream to finish and returns the messages emitted in this run""" + self.until_done() + + if not self.__message_snapshots: + raise RuntimeError("No messages found") + + return [message for message in self.__message_snapshots.values()] + + def __text_deltas__(self) -> Iterator[str]: + for event in self: + if event.event != "thread.message.delta": + continue + + for content_delta in event.data.delta.content or []: + if content_delta.type == "text" and content_delta.text and content_delta.text.value: + yield content_delta.text.value + + # event handlers + + def on_end(self) -> None: + """Fires when the stream has finished. + + This happens if the stream is read to completion + or if an exception occurs during iteration. + """ + + def on_event(self, event: AssistantStreamEvent) -> None: + """Callback that is fired for every Server-Sent-Event""" + + def on_run_step_created(self, run_step: RunStep) -> None: + """Callback that is fired when a run step is created""" + + def on_run_step_delta(self, delta: RunStepDelta, snapshot: RunStep) -> None: + """Callback that is fired whenever a run step delta is returned from the API + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the run step. For example, a tool calls event may + look like this: + + # delta + tool_calls=[ + RunStepDeltaToolCallsCodeInterpreter( + index=0, + type='code_interpreter', + id=None, + code_interpreter=CodeInterpreter(input=' sympy', outputs=None) + ) + ] + # snapshot + tool_calls=[ + CodeToolCall( + id='call_wKayJlcYV12NiadiZuJXxcfx', + code_interpreter=CodeInterpreter(input='from sympy', outputs=[]), + type='code_interpreter', + index=0 + ) + ], + """ + + def on_run_step_done(self, run_step: RunStep) -> None: + """Callback that is fired when a run step is completed""" + + def on_tool_call_created(self, tool_call: ToolCall) -> None: + """Callback that is fired when a tool call is created""" + + def on_tool_call_delta(self, delta: ToolCallDelta, snapshot: ToolCall) -> None: + """Callback that is fired when a tool call delta is encountered""" + + def on_tool_call_done(self, tool_call: ToolCall) -> None: + """Callback that is fired when a tool call delta is encountered""" + + def on_exception(self, exception: Exception) -> None: + """Fired whenever an exception happens during streaming""" + + def on_timeout(self) -> None: + """Fires if the request times out""" + + def on_message_created(self, message: Message) -> None: + """Callback that is fired when a message is created""" + + def on_message_delta(self, delta: MessageDelta, snapshot: Message) -> None: + """Callback that is fired whenever a message delta is returned from the API + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the message. For example, a text content event may + look like this: + + # delta + MessageDeltaText( + index=0, + type='text', + text=Text( + value=' Jane' + ), + ) + # snapshot + MessageContentText( + index=0, + type='text', + text=Text( + value='Certainly, Jane' + ), + ) + """ + + def on_message_done(self, message: Message) -> None: + """Callback that is fired when a message is completed""" + + def on_text_created(self, text: Text) -> None: + """Callback that is fired when a text content block is created""" + + def on_text_delta(self, delta: TextDelta, snapshot: Text) -> None: + """Callback that is fired whenever a text content delta is returned + by the API. + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the text. For example: + + on_text_delta(TextDelta(value="The"), Text(value="The")), + on_text_delta(TextDelta(value=" solution"), Text(value="The solution")), + on_text_delta(TextDelta(value=" to"), Text(value="The solution to")), + on_text_delta(TextDelta(value=" the"), Text(value="The solution to the")), + on_text_delta(TextDelta(value=" equation"), Text(value="The solution to the equivalent")), + """ + + def on_text_done(self, text: Text) -> None: + """Callback that is fired when a text content block is finished""" + + def on_image_file_done(self, image_file: ImageFile) -> None: + """Callback that is fired when an image file block is finished""" + + def _emit_sse_event(self, event: AssistantStreamEvent) -> None: + self._current_event = event + self.on_event(event) + + self.__current_message_snapshot, new_content = accumulate_event( + event=event, + current_message_snapshot=self.__current_message_snapshot, + ) + if self.__current_message_snapshot is not None: + self.__message_snapshots[self.__current_message_snapshot.id] = self.__current_message_snapshot + + accumulate_run_step( + event=event, + run_step_snapshots=self.__run_step_snapshots, + ) + + for content_delta in new_content: + assert self.__current_message_snapshot is not None + + block = self.__current_message_snapshot.content[content_delta.index] + if block.type == "text": + self.on_text_created(block.text) + + if ( + event.event == "thread.run.completed" + or event.event == "thread.run.cancelled" + or event.event == "thread.run.expired" + or event.event == "thread.run.failed" + or event.event == "thread.run.requires_action" + ): + self.__current_run = event.data + if self._current_tool_call: + self.on_tool_call_done(self._current_tool_call) + elif ( + event.event == "thread.run.created" + or event.event == "thread.run.in_progress" + or event.event == "thread.run.cancelling" + or event.event == "thread.run.queued" + ): + self.__current_run = event.data + elif event.event == "thread.message.created": + self.on_message_created(event.data) + elif event.event == "thread.message.delta": + snapshot = self.__current_message_snapshot + assert snapshot is not None + + message_delta = event.data.delta + if message_delta.content is not None: + for content_delta in message_delta.content: + if content_delta.type == "text" and content_delta.text: + snapshot_content = snapshot.content[content_delta.index] + assert snapshot_content.type == "text" + self.on_text_delta(content_delta.text, snapshot_content.text) + + # If the delta is for a new message content: + # - emit on_text_done/on_image_file_done for the previous message content + # - emit on_text_created/on_image_created for the new message content + if content_delta.index != self._current_message_content_index: + if self._current_message_content is not None: + if self._current_message_content.type == "text": + self.on_text_done(self._current_message_content.text) + elif self._current_message_content.type == "image_file": + self.on_image_file_done(self._current_message_content.image_file) + + self._current_message_content_index = content_delta.index + self._current_message_content = snapshot.content[content_delta.index] + + # Update the current_message_content (delta event is correctly emitted already) + self._current_message_content = snapshot.content[content_delta.index] + + self.on_message_delta(event.data.delta, snapshot) + elif event.event == "thread.message.completed" or event.event == "thread.message.incomplete": + self.__current_message_snapshot = event.data + self.__message_snapshots[event.data.id] = event.data + + if self._current_message_content_index is not None: + content = event.data.content[self._current_message_content_index] + if content.type == "text": + self.on_text_done(content.text) + elif content.type == "image_file": + self.on_image_file_done(content.image_file) + + self.on_message_done(event.data) + elif event.event == "thread.run.step.created": + self.__current_run_step_id = event.data.id + self.on_run_step_created(event.data) + elif event.event == "thread.run.step.in_progress": + self.__current_run_step_id = event.data.id + elif event.event == "thread.run.step.delta": + step_snapshot = self.__run_step_snapshots[event.data.id] + + run_step_delta = event.data.delta + if ( + run_step_delta.step_details + and run_step_delta.step_details.type == "tool_calls" + and run_step_delta.step_details.tool_calls is not None + ): + assert step_snapshot.step_details.type == "tool_calls" + for tool_call_delta in run_step_delta.step_details.tool_calls: + if tool_call_delta.index == self._current_tool_call_index: + self.on_tool_call_delta( + tool_call_delta, + step_snapshot.step_details.tool_calls[tool_call_delta.index], + ) + + # If the delta is for a new tool call: + # - emit on_tool_call_done for the previous tool_call + # - emit on_tool_call_created for the new tool_call + if tool_call_delta.index != self._current_tool_call_index: + if self._current_tool_call is not None: + self.on_tool_call_done(self._current_tool_call) + + self._current_tool_call_index = tool_call_delta.index + self._current_tool_call = step_snapshot.step_details.tool_calls[tool_call_delta.index] + self.on_tool_call_created(self._current_tool_call) + + # Update the current_tool_call (delta event is correctly emitted already) + self._current_tool_call = step_snapshot.step_details.tool_calls[tool_call_delta.index] + + self.on_run_step_delta( + event.data.delta, + step_snapshot, + ) + elif ( + event.event == "thread.run.step.completed" + or event.event == "thread.run.step.cancelled" + or event.event == "thread.run.step.expired" + or event.event == "thread.run.step.failed" + ): + if self._current_tool_call: + self.on_tool_call_done(self._current_tool_call) + + self.on_run_step_done(event.data) + self.__current_run_step_id = None + elif event.event == "thread.created" or event.event == "thread.message.in_progress" or event.event == "error": + # currently no special handling + ... + else: + # we only want to error at build-time + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(event) + + self._current_event = None + + def __stream__(self) -> Iterator[AssistantStreamEvent]: + stream = self.__stream + if not stream: + raise RuntimeError("Stream has not been started yet") + + try: + for event in stream: + self._emit_sse_event(event) + + yield event + except (httpx.TimeoutException, asyncio.TimeoutError) as exc: + self.on_timeout() + self.on_exception(exc) + raise + except Exception as exc: + self.on_exception(exc) + raise + finally: + self.on_end() + + +AssistantEventHandlerT = TypeVar("AssistantEventHandlerT", bound=AssistantEventHandler) + + +class AssistantStreamManager(Generic[AssistantEventHandlerT]): + """Wrapper over AssistantStreamEventHandler that is returned by `.stream()` + so that a context manager can be used. + + ```py + with client.threads.create_and_run_stream(...) as stream: + for event in stream: + ... + ``` + """ + + def __init__( + self, + api_request: Callable[[], Stream[AssistantStreamEvent]], + *, + event_handler: AssistantEventHandlerT, + ) -> None: + self.__stream: Stream[AssistantStreamEvent] | None = None + self.__event_handler = event_handler + self.__api_request = api_request + + def __enter__(self) -> AssistantEventHandlerT: + self.__stream = self.__api_request() + self.__event_handler._init(self.__stream) + return self.__event_handler + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + self.__stream.close() + + +class AsyncAssistantEventHandler: + text_deltas: AsyncIterable[str] + """Iterator over just the text deltas in the stream. + + This corresponds to the `thread.message.delta` event + in the API. + + ```py + async for text in stream.text_deltas: + print(text, end="", flush=True) + print() + ``` + """ + + def __init__(self) -> None: + self._current_event: AssistantStreamEvent | None = None + self._current_message_content_index: int | None = None + self._current_message_content: MessageContent | None = None + self._current_tool_call_index: int | None = None + self._current_tool_call: ToolCall | None = None + self.__current_run_step_id: str | None = None + self.__current_run: Run | None = None + self.__run_step_snapshots: dict[str, RunStep] = {} + self.__message_snapshots: dict[str, Message] = {} + self.__current_message_snapshot: Message | None = None + + self.text_deltas = self.__text_deltas__() + self._iterator = self.__stream__() + self.__stream: AsyncStream[AssistantStreamEvent] | None = None + + def _init(self, stream: AsyncStream[AssistantStreamEvent]) -> None: + if self.__stream: + raise RuntimeError( + "A single event handler cannot be shared between multiple streams; You will need to construct a new event handler instance" + ) + + self.__stream = stream + + async def __anext__(self) -> AssistantStreamEvent: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[AssistantStreamEvent]: + async for item in self._iterator: + yield item + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called when the context manager exits. + """ + if self.__stream: + await self.__stream.close() + + @property + def current_event(self) -> AssistantStreamEvent | None: + return self._current_event + + @property + def current_run(self) -> Run | None: + return self.__current_run + + @property + def current_run_step_snapshot(self) -> RunStep | None: + if not self.__current_run_step_id: + return None + + return self.__run_step_snapshots[self.__current_run_step_id] + + @property + def current_message_snapshot(self) -> Message | None: + return self.__current_message_snapshot + + async def until_done(self) -> None: + """Waits until the stream has been consumed""" + await consume_async_iterator(self) + + async def get_final_run(self) -> Run: + """Wait for the stream to finish and returns the completed Run object""" + await self.until_done() + + if not self.__current_run: + raise RuntimeError("No final run object found") + + return self.__current_run + + async def get_final_run_steps(self) -> list[RunStep]: + """Wait for the stream to finish and returns the steps taken in this run""" + await self.until_done() + + if not self.__run_step_snapshots: + raise RuntimeError("No run steps found") + + return [step for step in self.__run_step_snapshots.values()] + + async def get_final_messages(self) -> list[Message]: + """Wait for the stream to finish and returns the messages emitted in this run""" + await self.until_done() + + if not self.__message_snapshots: + raise RuntimeError("No messages found") + + return [message for message in self.__message_snapshots.values()] + + async def __text_deltas__(self) -> AsyncIterator[str]: + async for event in self: + if event.event != "thread.message.delta": + continue + + for content_delta in event.data.delta.content or []: + if content_delta.type == "text" and content_delta.text and content_delta.text.value: + yield content_delta.text.value + + # event handlers + + async def on_end(self) -> None: + """Fires when the stream has finished. + + This happens if the stream is read to completion + or if an exception occurs during iteration. + """ + + async def on_event(self, event: AssistantStreamEvent) -> None: + """Callback that is fired for every Server-Sent-Event""" + + async def on_run_step_created(self, run_step: RunStep) -> None: + """Callback that is fired when a run step is created""" + + async def on_run_step_delta(self, delta: RunStepDelta, snapshot: RunStep) -> None: + """Callback that is fired whenever a run step delta is returned from the API + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the run step. For example, a tool calls event may + look like this: + + # delta + tool_calls=[ + RunStepDeltaToolCallsCodeInterpreter( + index=0, + type='code_interpreter', + id=None, + code_interpreter=CodeInterpreter(input=' sympy', outputs=None) + ) + ] + # snapshot + tool_calls=[ + CodeToolCall( + id='call_wKayJlcYV12NiadiZuJXxcfx', + code_interpreter=CodeInterpreter(input='from sympy', outputs=[]), + type='code_interpreter', + index=0 + ) + ], + """ + + async def on_run_step_done(self, run_step: RunStep) -> None: + """Callback that is fired when a run step is completed""" + + async def on_tool_call_created(self, tool_call: ToolCall) -> None: + """Callback that is fired when a tool call is created""" + + async def on_tool_call_delta(self, delta: ToolCallDelta, snapshot: ToolCall) -> None: + """Callback that is fired when a tool call delta is encountered""" + + async def on_tool_call_done(self, tool_call: ToolCall) -> None: + """Callback that is fired when a tool call delta is encountered""" + + async def on_exception(self, exception: Exception) -> None: + """Fired whenever an exception happens during streaming""" + + async def on_timeout(self) -> None: + """Fires if the request times out""" + + async def on_message_created(self, message: Message) -> None: + """Callback that is fired when a message is created""" + + async def on_message_delta(self, delta: MessageDelta, snapshot: Message) -> None: + """Callback that is fired whenever a message delta is returned from the API + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the message. For example, a text content event may + look like this: + + # delta + MessageDeltaText( + index=0, + type='text', + text=Text( + value=' Jane' + ), + ) + # snapshot + MessageContentText( + index=0, + type='text', + text=Text( + value='Certainly, Jane' + ), + ) + """ + + async def on_message_done(self, message: Message) -> None: + """Callback that is fired when a message is completed""" + + async def on_text_created(self, text: Text) -> None: + """Callback that is fired when a text content block is created""" + + async def on_text_delta(self, delta: TextDelta, snapshot: Text) -> None: + """Callback that is fired whenever a text content delta is returned + by the API. + + The first argument is just the delta as sent by the API and the second argument + is the accumulated snapshot of the text. For example: + + on_text_delta(TextDelta(value="The"), Text(value="The")), + on_text_delta(TextDelta(value=" solution"), Text(value="The solution")), + on_text_delta(TextDelta(value=" to"), Text(value="The solution to")), + on_text_delta(TextDelta(value=" the"), Text(value="The solution to the")), + on_text_delta(TextDelta(value=" equation"), Text(value="The solution to the equivalent")), + """ + + async def on_text_done(self, text: Text) -> None: + """Callback that is fired when a text content block is finished""" + + async def on_image_file_done(self, image_file: ImageFile) -> None: + """Callback that is fired when an image file block is finished""" + + async def _emit_sse_event(self, event: AssistantStreamEvent) -> None: + self._current_event = event + await self.on_event(event) + + self.__current_message_snapshot, new_content = accumulate_event( + event=event, + current_message_snapshot=self.__current_message_snapshot, + ) + if self.__current_message_snapshot is not None: + self.__message_snapshots[self.__current_message_snapshot.id] = self.__current_message_snapshot + + accumulate_run_step( + event=event, + run_step_snapshots=self.__run_step_snapshots, + ) + + for content_delta in new_content: + assert self.__current_message_snapshot is not None + + block = self.__current_message_snapshot.content[content_delta.index] + if block.type == "text": + await self.on_text_created(block.text) + + if ( + event.event == "thread.run.completed" + or event.event == "thread.run.cancelled" + or event.event == "thread.run.expired" + or event.event == "thread.run.failed" + or event.event == "thread.run.requires_action" + ): + self.__current_run = event.data + if self._current_tool_call: + await self.on_tool_call_done(self._current_tool_call) + elif ( + event.event == "thread.run.created" + or event.event == "thread.run.in_progress" + or event.event == "thread.run.cancelling" + or event.event == "thread.run.queued" + ): + self.__current_run = event.data + elif event.event == "thread.message.created": + await self.on_message_created(event.data) + elif event.event == "thread.message.delta": + snapshot = self.__current_message_snapshot + assert snapshot is not None + + message_delta = event.data.delta + if message_delta.content is not None: + for content_delta in message_delta.content: + if content_delta.type == "text" and content_delta.text: + snapshot_content = snapshot.content[content_delta.index] + assert snapshot_content.type == "text" + await self.on_text_delta(content_delta.text, snapshot_content.text) + + # If the delta is for a new message content: + # - emit on_text_done/on_image_file_done for the previous message content + # - emit on_text_created/on_image_created for the new message content + if content_delta.index != self._current_message_content_index: + if self._current_message_content is not None: + if self._current_message_content.type == "text": + await self.on_text_done(self._current_message_content.text) + elif self._current_message_content.type == "image_file": + await self.on_image_file_done(self._current_message_content.image_file) + + self._current_message_content_index = content_delta.index + self._current_message_content = snapshot.content[content_delta.index] + + # Update the current_message_content (delta event is correctly emitted already) + self._current_message_content = snapshot.content[content_delta.index] + + await self.on_message_delta(event.data.delta, snapshot) + elif event.event == "thread.message.completed" or event.event == "thread.message.incomplete": + self.__current_message_snapshot = event.data + self.__message_snapshots[event.data.id] = event.data + + if self._current_message_content_index is not None: + content = event.data.content[self._current_message_content_index] + if content.type == "text": + await self.on_text_done(content.text) + elif content.type == "image_file": + await self.on_image_file_done(content.image_file) + + await self.on_message_done(event.data) + elif event.event == "thread.run.step.created": + self.__current_run_step_id = event.data.id + await self.on_run_step_created(event.data) + elif event.event == "thread.run.step.in_progress": + self.__current_run_step_id = event.data.id + elif event.event == "thread.run.step.delta": + step_snapshot = self.__run_step_snapshots[event.data.id] + + run_step_delta = event.data.delta + if ( + run_step_delta.step_details + and run_step_delta.step_details.type == "tool_calls" + and run_step_delta.step_details.tool_calls is not None + ): + assert step_snapshot.step_details.type == "tool_calls" + for tool_call_delta in run_step_delta.step_details.tool_calls: + if tool_call_delta.index == self._current_tool_call_index: + await self.on_tool_call_delta( + tool_call_delta, + step_snapshot.step_details.tool_calls[tool_call_delta.index], + ) + + # If the delta is for a new tool call: + # - emit on_tool_call_done for the previous tool_call + # - emit on_tool_call_created for the new tool_call + if tool_call_delta.index != self._current_tool_call_index: + if self._current_tool_call is not None: + await self.on_tool_call_done(self._current_tool_call) + + self._current_tool_call_index = tool_call_delta.index + self._current_tool_call = step_snapshot.step_details.tool_calls[tool_call_delta.index] + await self.on_tool_call_created(self._current_tool_call) + + # Update the current_tool_call (delta event is correctly emitted already) + self._current_tool_call = step_snapshot.step_details.tool_calls[tool_call_delta.index] + + await self.on_run_step_delta( + event.data.delta, + step_snapshot, + ) + elif ( + event.event == "thread.run.step.completed" + or event.event == "thread.run.step.cancelled" + or event.event == "thread.run.step.expired" + or event.event == "thread.run.step.failed" + ): + if self._current_tool_call: + await self.on_tool_call_done(self._current_tool_call) + + await self.on_run_step_done(event.data) + self.__current_run_step_id = None + elif event.event == "thread.created" or event.event == "thread.message.in_progress" or event.event == "error": + # currently no special handling + ... + else: + # we only want to error at build-time + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(event) + + self._current_event = None + + async def __stream__(self) -> AsyncIterator[AssistantStreamEvent]: + stream = self.__stream + if not stream: + raise RuntimeError("Stream has not been started yet") + + try: + async for event in stream: + await self._emit_sse_event(event) + + yield event + except (httpx.TimeoutException, asyncio.TimeoutError) as exc: + await self.on_timeout() + await self.on_exception(exc) + raise + except Exception as exc: + await self.on_exception(exc) + raise + finally: + await self.on_end() + + +AsyncAssistantEventHandlerT = TypeVar("AsyncAssistantEventHandlerT", bound=AsyncAssistantEventHandler) + + +class AsyncAssistantStreamManager(Generic[AsyncAssistantEventHandlerT]): + """Wrapper over AsyncAssistantStreamEventHandler that is returned by `.stream()` + so that an async context manager can be used without `await`ing the + original client call. + + ```py + async with client.threads.create_and_run_stream(...) as stream: + async for event in stream: + ... + ``` + """ + + def __init__( + self, + api_request: Awaitable[AsyncStream[AssistantStreamEvent]], + *, + event_handler: AsyncAssistantEventHandlerT, + ) -> None: + self.__stream: AsyncStream[AssistantStreamEvent] | None = None + self.__event_handler = event_handler + self.__api_request = api_request + + async def __aenter__(self) -> AsyncAssistantEventHandlerT: + self.__stream = await self.__api_request + self.__event_handler._init(self.__stream) + return self.__event_handler + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + await self.__stream.close() + + +def accumulate_run_step( + *, + event: AssistantStreamEvent, + run_step_snapshots: dict[str, RunStep], +) -> None: + if event.event == "thread.run.step.created": + run_step_snapshots[event.data.id] = event.data + return + + if event.event == "thread.run.step.delta": + data = event.data + snapshot = run_step_snapshots[data.id] + + if data.delta: + merged = accumulate_delta( + cast( + "dict[object, object]", + snapshot.model_dump(exclude_unset=True), + ), + cast( + "dict[object, object]", + data.delta.model_dump(exclude_unset=True), + ), + ) + run_step_snapshots[snapshot.id] = cast(RunStep, construct_type(type_=RunStep, value=merged)) + + return None + + +def accumulate_event( + *, + event: AssistantStreamEvent, + current_message_snapshot: Message | None, +) -> tuple[Message | None, list[MessageContentDelta]]: + """Returns a tuple of message snapshot and newly created text message deltas""" + if event.event == "thread.message.created": + return event.data, [] + + new_content: list[MessageContentDelta] = [] + + if event.event != "thread.message.delta": + return current_message_snapshot, [] + + if not current_message_snapshot: + raise RuntimeError("Encountered a message delta with no previous snapshot") + + data = event.data + if data.delta.content: + for content_delta in data.delta.content: + try: + block = current_message_snapshot.content[content_delta.index] + except IndexError: + current_message_snapshot.content.insert( + content_delta.index, + cast( + MessageContent, + construct_type( + # mypy doesn't allow Content for some reason + type_=cast(Any, MessageContent), + value=content_delta.model_dump(exclude_unset=True), + ), + ), + ) + new_content.append(content_delta) + else: + merged = accumulate_delta( + cast( + "dict[object, object]", + block.model_dump(exclude_unset=True), + ), + cast( + "dict[object, object]", + content_delta.model_dump(exclude_unset=True), + ), + ) + current_message_snapshot.content[content_delta.index] = cast( + MessageContent, + construct_type( + # mypy doesn't allow Content for some reason + type_=cast(Any, MessageContent), + value=merged, + ), + ) + + return current_message_snapshot, new_content + + +def accumulate_delta(acc: dict[object, object], delta: dict[object, object]) -> dict[object, object]: + for key, delta_value in delta.items(): + if key not in acc: + acc[key] = delta_value + continue + + acc_value = acc[key] + if acc_value is None: + acc[key] = delta_value + continue + + # the `index` property is used in arrays of objects so it should + # not be accumulated like other values e.g. + # [{'foo': 'bar', 'index': 0}] + # + # the same applies to `type` properties as they're used for + # discriminated unions + if key == "index" or key == "type": + acc[key] = delta_value + continue + + if isinstance(acc_value, str) and isinstance(delta_value, str): + acc_value += delta_value + elif isinstance(acc_value, (int, float)) and isinstance(delta_value, (int, float)): + acc_value += delta_value + elif is_dict(acc_value) and is_dict(delta_value): + acc_value = accumulate_delta(acc_value, delta_value) + elif is_list(acc_value) and is_list(delta_value): + # for lists of non-dictionary items we'll only ever get new entries + # in the array, existing entries will never be changed + if all(isinstance(x, (str, int, float)) for x in acc_value): + acc_value.extend(delta_value) + continue + + for delta_entry in delta_value: + if not is_dict(delta_entry): + raise TypeError(f"Unexpected list delta entry is not a dictionary: {delta_entry}") + + try: + index = delta_entry["index"] + except KeyError as exc: + raise RuntimeError(f"Expected list delta entry to have an `index` key; {delta_entry}") from exc + + if not isinstance(index, int): + raise TypeError(f"Unexpected, list delta entry `index` value is not an integer; {index}") + + try: + acc_entry = acc_value[index] + except IndexError: + acc_value.insert(index, delta_entry) + else: + if not is_dict(acc_entry): + raise TypeError("not handled yet") + + acc_value[index] = accumulate_delta(acc_entry, delta_entry) + + acc[key] = acc_value + + return acc diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 3aef33c95e..4698deec48 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -28,6 +28,7 @@ from ....types.beta import ( Assistant, AssistantDeleted, + AssistantToolParam, assistant_list_params, assistant_create_params, assistant_update_params, @@ -62,7 +63,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - tools: Iterable[assistant_create_params.Tool] | NotGiven = NOT_GIVEN, + tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -172,7 +173,7 @@ def update( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - tools: Iterable[assistant_update_params.Tool] | NotGiven = NOT_GIVEN, + tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -365,7 +366,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - tools: Iterable[assistant_create_params.Tool] | NotGiven = NOT_GIVEN, + tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -475,7 +476,7 @@ async def update( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - tools: Iterable[assistant_update_params.Tool] | NotGiven = NOT_GIVEN, + tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 2c0994d1f2..600d9a72ea 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -29,7 +29,7 @@ AsyncPaginator, make_request_options, ) -from .....types.beta.threads import ThreadMessage, message_list_params, message_create_params, message_update_params +from .....types.beta.threads import Message, message_list_params, message_create_params, message_update_params __all__ = ["Messages", "AsyncMessages"] @@ -61,7 +61,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ThreadMessage: + ) -> Message: """ Create a message. @@ -106,7 +106,7 @@ def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ThreadMessage, + cast_to=Message, ) def retrieve( @@ -120,7 +120,7 @@ def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ThreadMessage: + ) -> Message: """ Retrieve a message. @@ -143,7 +143,7 @@ def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ThreadMessage, + cast_to=Message, ) def update( @@ -158,7 +158,7 @@ def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ThreadMessage: + ) -> Message: """ Modifies a message. @@ -187,7 +187,7 @@ def update( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ThreadMessage, + cast_to=Message, ) def list( @@ -204,7 +204,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncCursorPage[ThreadMessage]: + ) -> SyncCursorPage[Message]: """ Returns a list of messages for a given thread. @@ -238,7 +238,7 @@ def list( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/messages", - page=SyncCursorPage[ThreadMessage], + page=SyncCursorPage[Message], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -254,7 +254,7 @@ def list( message_list_params.MessageListParams, ), ), - model=ThreadMessage, + model=Message, ) @@ -285,7 +285,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ThreadMessage: + ) -> Message: """ Create a message. @@ -330,7 +330,7 @@ async def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ThreadMessage, + cast_to=Message, ) async def retrieve( @@ -344,7 +344,7 @@ async def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ThreadMessage: + ) -> Message: """ Retrieve a message. @@ -367,7 +367,7 @@ async def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ThreadMessage, + cast_to=Message, ) async def update( @@ -382,7 +382,7 @@ async def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ThreadMessage: + ) -> Message: """ Modifies a message. @@ -411,7 +411,7 @@ async def update( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ThreadMessage, + cast_to=Message, ) def list( @@ -428,7 +428,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[ThreadMessage, AsyncCursorPage[ThreadMessage]]: + ) -> AsyncPaginator[Message, AsyncCursorPage[Message]]: """ Returns a list of messages for a given thread. @@ -462,7 +462,7 @@ def list( extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/messages", - page=AsyncCursorPage[ThreadMessage], + page=AsyncCursorPage[Message], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -478,7 +478,7 @@ def list( message_list_params.MessageListParams, ), ), - model=ThreadMessage, + model=Message, ) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 62cfa6b742..c5e9474002 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Iterable, Optional +from typing import Iterable, Optional, overload +from functools import partial from typing_extensions import Literal import httpx @@ -18,17 +19,28 @@ ) from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import ( + required_args, maybe_transform, async_maybe_transform, ) from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._streaming import Stream, AsyncStream from .....pagination import SyncCursorPage, AsyncCursorPage +from .....types.beta import AssistantToolParam, AssistantStreamEvent from ....._base_client import ( AsyncPaginator, make_request_options, ) +from .....lib.streaming import ( + AssistantEventHandler, + AssistantEventHandlerT, + AssistantStreamManager, + AsyncAssistantEventHandler, + AsyncAssistantEventHandlerT, + AsyncAssistantStreamManager, +) from .....types.beta.threads import ( Run, run_list_params, @@ -53,6 +65,7 @@ def with_raw_response(self) -> RunsWithRawResponse: def with_streaming_response(self) -> RunsWithStreamingResponse: return RunsWithStreamingResponse(self) + @overload def create( self, thread_id: str, @@ -62,7 +75,8 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[run_create_params.Tool]] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -96,6 +110,134 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + thread_id: str, + *, + assistant_id: str, + stream: Literal[True], + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[AssistantStreamEvent]: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + instructions: Overrides the + [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) + of the assistant. This is useful for modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + thread_id: str, + *, + assistant_id: str, + stream: bool, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | Stream[AssistantStreamEvent]: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + instructions: Overrides the + [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) + of the assistant. This is useful for modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -107,6 +249,27 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @required_args(["assistant_id"], ["assistant_id", "stream"]) + def create( + self, + thread_id: str, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | Stream[AssistantStreamEvent]: if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} @@ -119,6 +282,7 @@ def create( "instructions": instructions, "metadata": metadata, "model": model, + "stream": stream, "tools": tools, }, run_create_params.RunCreateParams, @@ -127,6 +291,8 @@ def create( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Run, + stream=stream or False, + stream_cls=Stream[AssistantStreamEvent], ) def retrieve( @@ -314,132 +480,88 @@ def cancel( cast_to=Run, ) - def submit_tool_outputs( + @overload + def create_and_stream( self, - run_id: str, *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, thread_id: str, - tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Run: - """ - When a run has the `status: "requires_action"` and `required_action.type` is - `submit_tool_outputs`, this endpoint can be used to submit the outputs from the - tool calls once they're all completed. All outputs must be submitted in a single - request. - - Args: - tool_outputs: A list of tools for which the outputs are being submitted. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not thread_id: - raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - if not run_id: - raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} - return self._post( - f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", - body=maybe_transform( - {"tool_outputs": tool_outputs}, run_submit_tool_outputs_params.RunSubmitToolOutputsParams - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Run, - ) - - -class AsyncRuns(AsyncAPIResource): - @cached_property - def steps(self) -> AsyncSteps: - return AsyncSteps(self._client) - - @cached_property - def with_raw_response(self) -> AsyncRunsWithRawResponse: - return AsyncRunsWithRawResponse(self) + ) -> AssistantStreamManager[AssistantEventHandler]: + """Create a Run stream""" + ... - @cached_property - def with_streaming_response(self) -> AsyncRunsWithStreamingResponse: - return AsyncRunsWithStreamingResponse(self) - - async def create( + @overload + def create_and_stream( self, - thread_id: str, *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[run_create_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + event_handler: AssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Run: - """ - Create a run. - - Args: - assistant_id: The ID of the - [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to - execute this run. - - additional_instructions: Appends additional instructions at the end of the instructions for the run. This - is useful for modifying the behavior on a per-run basis without overriding other - instructions. - - instructions: Overrides the - [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) - of the assistant. This is useful for modifying the behavior on a per-run basis. - - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 - characters long. - - model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to - be used to execute this run. If a value is provided here, it will override the - model associated with the assistant. If not, the model associated with the - assistant will be used. - - tools: Override the tools the assistant can use for this run. This is useful for - modifying the behavior on a per-run basis. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request + ) -> AssistantStreamManager[AssistantEventHandlerT]: + """Create a Run stream""" + ... - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + event_handler: AssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: + """Create a Run stream""" if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} - return await self._post( + + extra_headers = { + "OpenAI-Beta": "assistants=v1", + "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + make_request = partial( + self._post, f"/threads/{thread_id}/runs", - body=await async_maybe_transform( + body=maybe_transform( { "assistant_id": assistant_id, "additional_instructions": additional_instructions, "instructions": instructions, "metadata": metadata, "model": model, + "stream": True, "tools": tools, }, run_create_params.RunCreateParams, @@ -448,13 +570,19 @@ async def create( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Run, + stream=True, + stream_cls=Stream[AssistantStreamEvent], ) + return AssistantStreamManager(make_request, event_handler=event_handler or AssistantEventHandler()) - async def retrieve( + @overload + def submit_tool_outputs( self, run_id: str, *, thread_id: str, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -463,9 +591,18 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run: """ - Retrieves a run. + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. Args: + tool_outputs: A list of tools for which the outputs are being submitted. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -474,12 +611,485 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ - if not thread_id: - raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - if not run_id: - raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} - return await self._get( + ... + + @overload + def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + stream: Literal[True], + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[AssistantStreamEvent]: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + stream: bool, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | Stream[AssistantStreamEvent]: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["thread_id", "tool_outputs"], ["thread_id", "stream", "tool_outputs"]) + def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | Stream[AssistantStreamEvent]: + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return self._post( + f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + body=maybe_transform( + { + "tool_outputs": tool_outputs, + "stream": stream, + }, + run_submit_tool_outputs_params.RunSubmitToolOutputsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=stream or False, + stream_cls=Stream[AssistantStreamEvent], + ) + + @overload + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. + """ + ... + + @overload + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + event_handler: AssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandlerT]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. + """ + ... + + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + event_handler: AssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v1", + "X-Stainless-Stream-Helper": "threads.runs.submit_tool_outputs_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = partial( + self._post, + f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + body=maybe_transform( + { + "tool_outputs": tool_outputs, + "stream": True, + }, + run_submit_tool_outputs_params.RunSubmitToolOutputsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=True, + stream_cls=Stream[AssistantStreamEvent], + ) + return AssistantStreamManager(request, event_handler=event_handler or AssistantEventHandler()) + + +class AsyncRuns(AsyncAPIResource): + @cached_property + def steps(self) -> AsyncSteps: + return AsyncSteps(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRunsWithRawResponse: + return AsyncRunsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRunsWithStreamingResponse: + return AsyncRunsWithStreamingResponse(self) + + @overload + async def create( + self, + thread_id: str, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + instructions: Overrides the + [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) + of the assistant. This is useful for modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + thread_id: str, + *, + assistant_id: str, + stream: Literal[True], + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[AssistantStreamEvent]: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + instructions: Overrides the + [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) + of the assistant. This is useful for modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + thread_id: str, + *, + assistant_id: str, + stream: bool, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | AsyncStream[AssistantStreamEvent]: + """ + Create a run. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + additional_instructions: Appends additional instructions at the end of the instructions for the run. This + is useful for modifying the behavior on a per-run basis without overriding other + instructions. + + instructions: Overrides the + [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) + of the assistant. This is useful for modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["assistant_id"], ["assistant_id", "stream"]) + async def create( + self, + thread_id: str, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | AsyncStream[AssistantStreamEvent]: + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._post( + f"/threads/{thread_id}/runs", + body=await async_maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "instructions": instructions, + "metadata": metadata, + "model": model, + "stream": stream, + "tools": tools, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=stream or False, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + + async def retrieve( + self, + run_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + Retrieves a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + return await self._get( f"/threads/{thread_id}/runs/{run_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -635,12 +1245,111 @@ async def cancel( cast_to=Run, ) + @overload + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: + """Create a Run stream""" + ... + + @overload + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: + """Create a Run stream""" + ... + + def create_and_stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ( + AsyncAssistantStreamManager[AsyncAssistantEventHandler] + | AsyncAssistantStreamManager[AsyncAssistantEventHandlerT] + ): + """Create a Run stream""" + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v1", + "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = self._post( + f"/threads/{thread_id}/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "instructions": instructions, + "metadata": metadata, + "model": model, + "stream": True, + "tools": tools, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=True, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) + + @overload async def submit_tool_outputs( self, run_id: str, *, thread_id: str, tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -657,6 +1366,86 @@ async def submit_tool_outputs( Args: tool_outputs: A list of tools for which the outputs are being submitted. + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + stream: Literal[True], + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[AssistantStreamEvent]: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tool_outputs: A list of tools for which the outputs are being submitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + stream: bool, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | AsyncStream[AssistantStreamEvent]: + """ + When a run has the `status: "requires_action"` and `required_action.type` is + `submit_tool_outputs`, this endpoint can be used to submit the outputs from the + tool calls once they're all completed. All outputs must be submitted in a single + request. + + Args: + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + tool_outputs: A list of tools for which the outputs are being submitted. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -665,6 +1454,23 @@ async def submit_tool_outputs( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @required_args(["thread_id", "tool_outputs"], ["thread_id", "stream", "tool_outputs"]) + async def submit_tool_outputs( + self, + run_id: str, + *, + thread_id: str, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | AsyncStream[AssistantStreamEvent]: if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: @@ -673,13 +1479,111 @@ async def submit_tool_outputs( return await self._post( f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", body=await async_maybe_transform( - {"tool_outputs": tool_outputs}, run_submit_tool_outputs_params.RunSubmitToolOutputsParams + { + "tool_outputs": tool_outputs, + "stream": stream, + }, + run_submit_tool_outputs_params.RunSubmitToolOutputsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=stream or False, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + + @overload + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. + """ + ... + + @overload + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. + """ + ... + + def submit_tool_outputs_stream( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ( + AsyncAssistantStreamManager[AsyncAssistantEventHandler] + | AsyncAssistantStreamManager[AsyncAssistantEventHandlerT] + ): + """ + Submit the tool outputs from a previous run and stream the run to a terminal + state. + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v1", + "X-Stainless-Stream-Helper": "threads.runs.submit_tool_outputs_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = self._post( + f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + body=maybe_transform( + { + "tool_outputs": tool_outputs, + "stream": True, + }, + run_submit_tool_outputs_params.RunSubmitToolOutputsParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Run, + stream=True, + stream_cls=AsyncStream[AssistantStreamEvent], ) + return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) class RunsWithRawResponse: diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index cc0e1c0959..17afe285cc 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing import Iterable, Optional +from typing import Iterable, Optional, overload +from functools import partial +from typing_extensions import Literal import httpx @@ -25,6 +27,7 @@ ) from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._utils import ( + required_args, maybe_transform, async_maybe_transform, ) @@ -32,9 +35,11 @@ from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._streaming import Stream, AsyncStream from ....types.beta import ( Thread, ThreadDeleted, + AssistantStreamEvent, thread_create_params, thread_update_params, thread_create_and_run_params, @@ -42,6 +47,14 @@ from ...._base_client import ( make_request_options, ) +from ....lib.streaming import ( + AssistantEventHandler, + AssistantEventHandlerT, + AssistantStreamManager, + AsyncAssistantEventHandler, + AsyncAssistantEventHandlerT, + AsyncAssistantStreamManager, +) from .messages.messages import Messages, AsyncMessages from ....types.beta.threads import Run @@ -222,6 +235,7 @@ def delete( cast_to=ThreadDeleted, ) + @overload def create_and_run( self, *, @@ -229,6 +243,7 @@ def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -259,6 +274,126 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + thread: If no thread is provided, an empty thread will be created. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create_and_run( + self, + *, + assistant_id: str, + stream: Literal[True], + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[AssistantStreamEvent]: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + thread: If no thread is provided, an empty thread will be created. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create_and_run( + self, + *, + assistant_id: str, + stream: bool, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | Stream[AssistantStreamEvent]: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + thread: If no thread is provided, an empty thread will be created. tools: Override the tools the assistant can use for this run. This is useful for @@ -272,6 +407,26 @@ def create_and_run( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @required_args(["assistant_id"], ["assistant_id", "stream"]) + def create_and_run( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | Stream[AssistantStreamEvent]: extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return self._post( "/threads/runs", @@ -281,6 +436,95 @@ def create_and_run( "instructions": instructions, "metadata": metadata, "model": model, + "stream": stream, + "thread": thread, + "tools": tools, + }, + thread_create_and_run_params.ThreadCreateAndRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=stream or False, + stream_cls=Stream[AssistantStreamEvent], + ) + + @overload + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler]: + """Create a thread and stream the run back""" + ... + + @overload + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + event_handler: AssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandlerT]: + """Create a thread and stream the run back""" + ... + + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + event_handler: AssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: + """Create a thread and stream the run back""" + extra_headers = { + "OpenAI-Beta": "assistants=v1", + "X-Stainless-Stream-Helper": "threads.create_and_run_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + make_request = partial( + self._post, + "/threads/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "metadata": metadata, + "model": model, + "stream": True, "thread": thread, "tools": tools, }, @@ -290,7 +534,10 @@ def create_and_run( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Run, + stream=True, + stream_cls=Stream[AssistantStreamEvent], ) + return AssistantStreamManager(make_request, event_handler=event_handler or AssistantEventHandler()) class AsyncThreads(AsyncAPIResource): @@ -467,6 +714,7 @@ async def delete( cast_to=ThreadDeleted, ) + @overload async def create_and_run( self, *, @@ -474,6 +722,7 @@ async def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -504,6 +753,10 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + thread: If no thread is provided, an empty thread will be created. tools: Override the tools the assistant can use for this run. This is useful for @@ -517,6 +770,142 @@ async def create_and_run( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @overload + async def create_and_run( + self, + *, + assistant_id: str, + stream: Literal[True], + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[AssistantStreamEvent]: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + thread: If no thread is provided, an empty thread will be created. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create_and_run( + self, + *, + assistant_id: str, + stream: bool, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | AsyncStream[AssistantStreamEvent]: + """ + Create a thread and run it in one request. + + Args: + assistant_id: The ID of the + [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to + execute this run. + + stream: If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + + instructions: Override the default system message of the assistant. This is useful for + modifying the behavior on a per-run basis. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to + be used to execute this run. If a value is provided here, it will override the + model associated with the assistant. If not, the model associated with the + assistant will be used. + + thread: If no thread is provided, an empty thread will be created. + + tools: Override the tools the assistant can use for this run. This is useful for + modifying the behavior on a per-run basis. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["assistant_id"], ["assistant_id", "stream"]) + async def create_and_run( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run | AsyncStream[AssistantStreamEvent]: extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} return await self._post( "/threads/runs", @@ -526,6 +915,97 @@ async def create_and_run( "instructions": instructions, "metadata": metadata, "model": model, + "stream": stream, + "thread": thread, + "tools": tools, + }, + thread_create_and_run_params.ThreadCreateAndRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=stream or False, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + + @overload + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: + """Create a thread and stream the run back""" + ... + + @overload + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + event_handler: AsyncAssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: + """Create a thread and stream the run back""" + ... + + def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + event_handler: AsyncAssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ( + AsyncAssistantStreamManager[AsyncAssistantEventHandler] + | AsyncAssistantStreamManager[AsyncAssistantEventHandlerT] + ): + """Create a thread and stream the run back""" + extra_headers = { + "OpenAI-Beta": "assistants=v1", + "X-Stainless-Stream-Helper": "threads.create_and_run_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = self._post( + "/threads/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "instructions": instructions, + "metadata": metadata, + "model": model, + "stream": True, "thread": thread, "tools": tools, }, @@ -535,7 +1015,10 @@ async def create_and_run( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Run, + stream=True, + stream_cls=AsyncStream[AssistantStreamEvent], ) + return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) class ThreadsWithRawResponse: diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 20ea4cffbb..abe466ef77 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -206,7 +206,7 @@ def create( tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs - for. + for. A max of 128 functions are supported. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -396,7 +396,7 @@ def create( tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs - for. + for. A max of 128 functions are supported. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -586,7 +586,7 @@ def create( tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs - for. + for. A max of 128 functions are supported. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -873,7 +873,7 @@ async def create( tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs - for. + for. A max of 128 functions are supported. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -1063,7 +1063,7 @@ async def create( tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs - for. + for. A max of 128 functions are supported. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -1253,7 +1253,7 @@ async def create( tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs - for. + for. A max of 128 functions are supported. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 6d3756f6ba..8a2bad5fda 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -157,6 +157,8 @@ def create( suffix: The suffix that comes after a completion of inserted text. + This parameter is only supported for `gpt-3.5-turbo-instruct`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -305,6 +307,8 @@ def create( suffix: The suffix that comes after a completion of inserted text. + This parameter is only supported for `gpt-3.5-turbo-instruct`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -453,6 +457,8 @@ def create( suffix: The suffix that comes after a completion of inserted text. + This parameter is only supported for `gpt-3.5-turbo-instruct`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -671,6 +677,8 @@ async def create( suffix: The suffix that comes after a completion of inserted text. + This parameter is only supported for `gpt-3.5-turbo-instruct`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -819,6 +827,8 @@ async def create( suffix: The suffix that comes after a completion of inserted text. + This parameter is only supported for `gpt-3.5-turbo-instruct`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -967,6 +977,8 @@ async def create( suffix: The suffix that comes after a completion of inserted text. + This parameter is only supported for `gpt-3.5-turbo-instruct`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index d6108e1eed..e536d0b5a7 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -4,7 +4,11 @@ from .image import Image as Image from .model import Model as Model -from .shared import FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters +from .shared import ( + ErrorObject as ErrorObject, + FunctionDefinition as FunctionDefinition, + FunctionParameters as FunctionParameters, +) from .embedding import Embedding as Embedding from .completion import Completion as Completion from .moderation import Moderation as Moderation diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index e6742521e9..714b3e159d 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -4,11 +4,20 @@ from .thread import Thread as Thread from .assistant import Assistant as Assistant +from .function_tool import FunctionTool as FunctionTool +from .assistant_tool import AssistantTool as AssistantTool +from .retrieval_tool import RetrievalTool as RetrievalTool from .thread_deleted import ThreadDeleted as ThreadDeleted from .assistant_deleted import AssistantDeleted as AssistantDeleted +from .function_tool_param import FunctionToolParam as FunctionToolParam +from .assistant_tool_param import AssistantToolParam as AssistantToolParam +from .retrieval_tool_param import RetrievalToolParam as RetrievalToolParam from .thread_create_params import ThreadCreateParams as ThreadCreateParams from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams from .assistant_list_params import AssistantListParams as AssistantListParams +from .code_interpreter_tool import CodeInterpreterTool as CodeInterpreterTool +from .assistant_stream_event import AssistantStreamEvent as AssistantStreamEvent from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams +from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 7ba50652aa..31b847d72c 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -1,33 +1,12 @@ # File generated from our OpenAPI spec by Stainless. -from typing import List, Union, Optional -from typing_extensions import Literal, Annotated +from typing import List, Optional +from typing_extensions import Literal -from ..shared import FunctionDefinition -from ..._utils import PropertyInfo from ..._models import BaseModel +from .assistant_tool import AssistantTool -__all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetrieval", "ToolFunction"] - - -class ToolCodeInterpreter(BaseModel): - type: Literal["code_interpreter"] - """The type of tool being defined: `code_interpreter`""" - - -class ToolRetrieval(BaseModel): - type: Literal["retrieval"] - """The type of tool being defined: `retrieval`""" - - -class ToolFunction(BaseModel): - function: FunctionDefinition - - type: Literal["function"] - """The type of tool being defined: `function`""" - - -Tool = Annotated[Union[ToolCodeInterpreter, ToolRetrieval, ToolFunction], PropertyInfo(discriminator="type")] +__all__ = ["Assistant"] class Assistant(BaseModel): @@ -77,7 +56,7 @@ class Assistant(BaseModel): object: Literal["assistant"] """The object type, which is always `assistant`.""" - tools: List[Tool] + tools: List[AssistantTool] """A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index c49d6f6950..0e39619a9c 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -2,18 +2,12 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing import List, Iterable, Optional +from typing_extensions import Required, TypedDict -from ...types import shared_params +from .assistant_tool_param import AssistantToolParam -__all__ = [ - "AssistantCreateParams", - "Tool", - "ToolAssistantToolsCode", - "ToolAssistantToolsRetrieval", - "ToolAssistantToolsFunction", -] +__all__ = ["AssistantCreateParams"] class AssistantCreateParams(TypedDict, total=False): @@ -54,29 +48,9 @@ class AssistantCreateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" - tools: Iterable[Tool] + tools: Iterable[AssistantToolParam] """A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. """ - - -class ToolAssistantToolsCode(TypedDict, total=False): - type: Required[Literal["code_interpreter"]] - """The type of tool being defined: `code_interpreter`""" - - -class ToolAssistantToolsRetrieval(TypedDict, total=False): - type: Required[Literal["retrieval"]] - """The type of tool being defined: `retrieval`""" - - -class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[shared_params.FunctionDefinition] - - type: Required[Literal["function"]] - """The type of tool being defined: `function`""" - - -Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py new file mode 100644 index 0000000000..ca7f814a8a --- /dev/null +++ b/src/openai/types/beta/assistant_stream_event.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union +from typing_extensions import Literal, Annotated + +from .thread import Thread +from ..shared import ErrorObject +from .threads import Run, Message, MessageDeltaEvent +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .threads.runs import RunStep, RunStepDeltaEvent + +__all__ = [ + "AssistantStreamEvent", + "ThreadCreated", + "ThreadRunCreated", + "ThreadRunQueued", + "ThreadRunInProgress", + "ThreadRunRequiresAction", + "ThreadRunCompleted", + "ThreadRunFailed", + "ThreadRunCancelling", + "ThreadRunCancelled", + "ThreadRunExpired", + "ThreadRunStepCreated", + "ThreadRunStepInProgress", + "ThreadRunStepDelta", + "ThreadRunStepCompleted", + "ThreadRunStepFailed", + "ThreadRunStepCancelled", + "ThreadRunStepExpired", + "ThreadMessageCreated", + "ThreadMessageInProgress", + "ThreadMessageDelta", + "ThreadMessageCompleted", + "ThreadMessageIncomplete", + "ErrorEvent", +] + + +class ThreadCreated(BaseModel): + data: Thread + """ + Represents a thread that contains + [messages](https://platform.openai.com/docs/api-reference/messages). + """ + + event: Literal["thread.created"] + + +class ThreadRunCreated(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.created"] + + +class ThreadRunQueued(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.queued"] + + +class ThreadRunInProgress(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.in_progress"] + + +class ThreadRunRequiresAction(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.requires_action"] + + +class ThreadRunCompleted(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.completed"] + + +class ThreadRunFailed(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.failed"] + + +class ThreadRunCancelling(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.cancelling"] + + +class ThreadRunCancelled(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.cancelled"] + + +class ThreadRunExpired(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.expired"] + + +class ThreadRunStepCreated(BaseModel): + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.created"] + + +class ThreadRunStepInProgress(BaseModel): + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.in_progress"] + + +class ThreadRunStepDelta(BaseModel): + data: RunStepDeltaEvent + """Represents a run step delta i.e. + + any changed fields on a run step during streaming. + """ + + event: Literal["thread.run.step.delta"] + + +class ThreadRunStepCompleted(BaseModel): + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.completed"] + + +class ThreadRunStepFailed(BaseModel): + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.failed"] + + +class ThreadRunStepCancelled(BaseModel): + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.cancelled"] + + +class ThreadRunStepExpired(BaseModel): + data: RunStep + """Represents a step in execution of a run.""" + + event: Literal["thread.run.step.expired"] + + +class ThreadMessageCreated(BaseModel): + data: Message + """ + Represents a message within a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.message.created"] + + +class ThreadMessageInProgress(BaseModel): + data: Message + """ + Represents a message within a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.message.in_progress"] + + +class ThreadMessageDelta(BaseModel): + data: MessageDeltaEvent + """Represents a message delta i.e. + + any changed fields on a message during streaming. + """ + + event: Literal["thread.message.delta"] + + +class ThreadMessageCompleted(BaseModel): + data: Message + """ + Represents a message within a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.message.completed"] + + +class ThreadMessageIncomplete(BaseModel): + data: Message + """ + Represents a message within a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.message.incomplete"] + + +class ErrorEvent(BaseModel): + data: ErrorObject + + event: Literal["error"] + + +AssistantStreamEvent = Annotated[ + Union[ + ThreadCreated, + ThreadRunCreated, + ThreadRunQueued, + ThreadRunInProgress, + ThreadRunRequiresAction, + ThreadRunCompleted, + ThreadRunFailed, + ThreadRunCancelling, + ThreadRunCancelled, + ThreadRunExpired, + ThreadRunStepCreated, + ThreadRunStepInProgress, + ThreadRunStepDelta, + ThreadRunStepCompleted, + ThreadRunStepFailed, + ThreadRunStepCancelled, + ThreadRunStepExpired, + ThreadMessageCreated, + ThreadMessageInProgress, + ThreadMessageDelta, + ThreadMessageCompleted, + ThreadMessageIncomplete, + ErrorEvent, + ], + PropertyInfo(discriminator="event"), +] diff --git a/src/openai/types/beta/assistant_tool.py b/src/openai/types/beta/assistant_tool.py new file mode 100644 index 0000000000..9e589eae7a --- /dev/null +++ b/src/openai/types/beta/assistant_tool.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union +from typing_extensions import Annotated + +from ..._utils import PropertyInfo +from .function_tool import FunctionTool +from .retrieval_tool import RetrievalTool +from .code_interpreter_tool import CodeInterpreterTool + +__all__ = ["AssistantTool"] + +AssistantTool = Annotated[Union[CodeInterpreterTool, RetrievalTool, FunctionTool], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/beta/assistant_tool_param.py b/src/openai/types/beta/assistant_tool_param.py new file mode 100644 index 0000000000..02b56a8c5d --- /dev/null +++ b/src/openai/types/beta/assistant_tool_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing import Union + +from .function_tool_param import FunctionToolParam +from .retrieval_tool_param import RetrievalToolParam +from .code_interpreter_tool_param import CodeInterpreterToolParam + +__all__ = ["AssistantToolParam"] + +AssistantToolParam = Union[CodeInterpreterToolParam, RetrievalToolParam, FunctionToolParam] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index c5ccde62c5..fbff50f444 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -2,18 +2,12 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing import List, Iterable, Optional +from typing_extensions import TypedDict -from ...types import shared_params +from .assistant_tool_param import AssistantToolParam -__all__ = [ - "AssistantUpdateParams", - "Tool", - "ToolAssistantToolsCode", - "ToolAssistantToolsRetrieval", - "ToolAssistantToolsFunction", -] +__all__ = ["AssistantUpdateParams"] class AssistantUpdateParams(TypedDict, total=False): @@ -56,29 +50,9 @@ class AssistantUpdateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" - tools: Iterable[Tool] + tools: Iterable[AssistantToolParam] """A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. """ - - -class ToolAssistantToolsCode(TypedDict, total=False): - type: Required[Literal["code_interpreter"]] - """The type of tool being defined: `code_interpreter`""" - - -class ToolAssistantToolsRetrieval(TypedDict, total=False): - type: Required[Literal["retrieval"]] - """The type of tool being defined: `retrieval`""" - - -class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[shared_params.FunctionDefinition] - - type: Required[Literal["function"]] - """The type of tool being defined: `function`""" - - -Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] diff --git a/src/openai/types/beta/code_interpreter_tool.py b/src/openai/types/beta/code_interpreter_tool.py new file mode 100644 index 0000000000..4964047ba7 --- /dev/null +++ b/src/openai/types/beta/code_interpreter_tool.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["CodeInterpreterTool"] + + +class CodeInterpreterTool(BaseModel): + type: Literal["code_interpreter"] + """The type of tool being defined: `code_interpreter`""" diff --git a/src/openai/types/beta/code_interpreter_tool_param.py b/src/openai/types/beta/code_interpreter_tool_param.py new file mode 100644 index 0000000000..92d6e02dbc --- /dev/null +++ b/src/openai/types/beta/code_interpreter_tool_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["CodeInterpreterToolParam"] + + +class CodeInterpreterToolParam(TypedDict, total=False): + type: Required[Literal["code_interpreter"]] + """The type of tool being defined: `code_interpreter`""" diff --git a/src/openai/types/beta/function_tool.py b/src/openai/types/beta/function_tool.py new file mode 100644 index 0000000000..fa0ab3b83e --- /dev/null +++ b/src/openai/types/beta/function_tool.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ..shared import FunctionDefinition +from ..._models import BaseModel + +__all__ = ["FunctionTool"] + + +class FunctionTool(BaseModel): + function: FunctionDefinition + + type: Literal["function"] + """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/function_tool_param.py b/src/openai/types/beta/function_tool_param.py new file mode 100644 index 0000000000..e631d69e20 --- /dev/null +++ b/src/openai/types/beta/function_tool_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ...types import shared_params + +__all__ = ["FunctionToolParam"] + + +class FunctionToolParam(TypedDict, total=False): + function: Required[shared_params.FunctionDefinition] + + type: Required[Literal["function"]] + """The type of tool being defined: `function`""" diff --git a/src/openai/types/beta/retrieval_tool.py b/src/openai/types/beta/retrieval_tool.py new file mode 100644 index 0000000000..17d5bea130 --- /dev/null +++ b/src/openai/types/beta/retrieval_tool.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RetrievalTool"] + + +class RetrievalTool(BaseModel): + type: Literal["retrieval"] + """The type of tool being defined: `retrieval`""" diff --git a/src/openai/types/beta/retrieval_tool_param.py b/src/openai/types/beta/retrieval_tool_param.py new file mode 100644 index 0000000000..6f803e4672 --- /dev/null +++ b/src/openai/types/beta/retrieval_tool_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RetrievalToolParam"] + + +class RetrievalToolParam(TypedDict, total=False): + type: Required[Literal["retrieval"]] + """The type of tool being defined: `retrieval`""" diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index cc1051b3d6..5078639e6a 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -5,20 +5,21 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -from ...types import shared_params +from .function_tool_param import FunctionToolParam +from .retrieval_tool_param import RetrievalToolParam +from .code_interpreter_tool_param import CodeInterpreterToolParam __all__ = [ - "ThreadCreateAndRunParams", + "ThreadCreateAndRunParamsBase", "Thread", "ThreadMessage", "Tool", - "ToolAssistantToolsCode", - "ToolAssistantToolsRetrieval", - "ToolAssistantToolsFunction", + "ThreadCreateAndRunParamsNonStreaming", + "ThreadCreateAndRunParamsStreaming", ] -class ThreadCreateAndRunParams(TypedDict, total=False): +class ThreadCreateAndRunParamsBase(TypedDict, total=False): assistant_id: Required[str] """ The ID of the @@ -101,21 +102,25 @@ class Thread(TypedDict, total=False): """ -class ToolAssistantToolsCode(TypedDict, total=False): - type: Required[Literal["code_interpreter"]] - """The type of tool being defined: `code_interpreter`""" +Tool = Union[CodeInterpreterToolParam, RetrievalToolParam, FunctionToolParam] -class ToolAssistantToolsRetrieval(TypedDict, total=False): - type: Required[Literal["retrieval"]] - """The type of tool being defined: `retrieval`""" - +class ThreadCreateAndRunParamsNonStreaming(ThreadCreateAndRunParamsBase): + stream: Optional[Literal[False]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ -class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[shared_params.FunctionDefinition] - type: Required[Literal["function"]] - """The type of tool being defined: `function`""" +class ThreadCreateAndRunParamsStreaming(ThreadCreateAndRunParamsBase): + stream: Required[Literal[True]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ -Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] +ThreadCreateAndRunParams = Union[ThreadCreateAndRunParamsNonStreaming, ThreadCreateAndRunParamsStreaming] diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index a71cbde3e3..ff45871afe 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -3,15 +3,31 @@ from __future__ import annotations from .run import Run as Run +from .text import Text as Text +from .message import Message as Message +from .annotation import Annotation as Annotation +from .image_file import ImageFile as ImageFile from .run_status import RunStatus as RunStatus -from .thread_message import ThreadMessage as ThreadMessage +from .text_delta import TextDelta as TextDelta +from .message_delta import MessageDelta as MessageDelta +from .message_content import MessageContent as MessageContent from .run_list_params import RunListParams as RunListParams +from .annotation_delta import AnnotationDelta as AnnotationDelta +from .image_file_delta import ImageFileDelta as ImageFileDelta +from .text_delta_block import TextDeltaBlock as TextDeltaBlock from .run_create_params import RunCreateParams as RunCreateParams from .run_update_params import RunUpdateParams as RunUpdateParams +from .text_content_block import TextContentBlock as TextContentBlock +from .message_delta_event import MessageDeltaEvent as MessageDeltaEvent from .message_list_params import MessageListParams as MessageListParams -from .message_content_text import MessageContentText as MessageContentText +from .file_path_annotation import FilePathAnnotation as FilePathAnnotation +from .message_content_delta import MessageContentDelta as MessageContentDelta from .message_create_params import MessageCreateParams as MessageCreateParams from .message_update_params import MessageUpdateParams as MessageUpdateParams -from .message_content_image_file import MessageContentImageFile as MessageContentImageFile +from .image_file_delta_block import ImageFileDeltaBlock as ImageFileDeltaBlock +from .file_citation_annotation import FileCitationAnnotation as FileCitationAnnotation +from .image_file_content_block import ImageFileContentBlock as ImageFileContentBlock +from .file_path_delta_annotation import FilePathDeltaAnnotation as FilePathDeltaAnnotation +from .file_citation_delta_annotation import FileCitationDeltaAnnotation as FileCitationDeltaAnnotation from .run_submit_tool_outputs_params import RunSubmitToolOutputsParams as RunSubmitToolOutputsParams from .required_action_function_tool_call import RequiredActionFunctionToolCall as RequiredActionFunctionToolCall diff --git a/src/openai/types/beta/threads/annotation.py b/src/openai/types/beta/threads/annotation.py new file mode 100644 index 0000000000..86a2115233 --- /dev/null +++ b/src/openai/types/beta/threads/annotation.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union +from typing_extensions import Annotated + +from ...._utils import PropertyInfo +from .file_path_annotation import FilePathAnnotation +from .file_citation_annotation import FileCitationAnnotation + +__all__ = ["Annotation"] + +Annotation = Annotated[Union[FileCitationAnnotation, FilePathAnnotation], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/beta/threads/annotation_delta.py b/src/openai/types/beta/threads/annotation_delta.py new file mode 100644 index 0000000000..fdcc67c3ff --- /dev/null +++ b/src/openai/types/beta/threads/annotation_delta.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union +from typing_extensions import Annotated + +from ...._utils import PropertyInfo +from .file_path_delta_annotation import FilePathDeltaAnnotation +from .file_citation_delta_annotation import FileCitationDeltaAnnotation + +__all__ = ["AnnotationDelta"] + +AnnotationDelta = Annotated[ + Union[FileCitationDeltaAnnotation, FilePathDeltaAnnotation], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/threads/file_citation_annotation.py b/src/openai/types/beta/threads/file_citation_annotation.py new file mode 100644 index 0000000000..da63938d93 --- /dev/null +++ b/src/openai/types/beta/threads/file_citation_annotation.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FileCitationAnnotation", "FileCitation"] + + +class FileCitation(BaseModel): + file_id: str + """The ID of the specific File the citation is from.""" + + quote: str + """The specific quote in the file.""" + + +class FileCitationAnnotation(BaseModel): + end_index: int + + file_citation: FileCitation + + start_index: int + + text: str + """The text in the message content that needs to be replaced.""" + + type: Literal["file_citation"] + """Always `file_citation`.""" diff --git a/src/openai/types/beta/threads/file_citation_delta_annotation.py b/src/openai/types/beta/threads/file_citation_delta_annotation.py new file mode 100644 index 0000000000..3b4c5950d4 --- /dev/null +++ b/src/openai/types/beta/threads/file_citation_delta_annotation.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FileCitationDeltaAnnotation", "FileCitation"] + + +class FileCitation(BaseModel): + file_id: Optional[str] = None + """The ID of the specific File the citation is from.""" + + quote: Optional[str] = None + """The specific quote in the file.""" + + +class FileCitationDeltaAnnotation(BaseModel): + index: int + """The index of the annotation in the text content part.""" + + type: Literal["file_citation"] + """Always `file_citation`.""" + + end_index: Optional[int] = None + + file_citation: Optional[FileCitation] = None + + start_index: Optional[int] = None + + text: Optional[str] = None + """The text in the message content that needs to be replaced.""" diff --git a/src/openai/types/beta/threads/file_path_annotation.py b/src/openai/types/beta/threads/file_path_annotation.py new file mode 100644 index 0000000000..2d9cf58184 --- /dev/null +++ b/src/openai/types/beta/threads/file_path_annotation.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FilePathAnnotation", "FilePath"] + + +class FilePath(BaseModel): + file_id: str + """The ID of the file that was generated.""" + + +class FilePathAnnotation(BaseModel): + end_index: int + + file_path: FilePath + + start_index: int + + text: str + """The text in the message content that needs to be replaced.""" + + type: Literal["file_path"] + """Always `file_path`.""" diff --git a/src/openai/types/beta/threads/file_path_delta_annotation.py b/src/openai/types/beta/threads/file_path_delta_annotation.py new file mode 100644 index 0000000000..6d89748d2c --- /dev/null +++ b/src/openai/types/beta/threads/file_path_delta_annotation.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FilePathDeltaAnnotation", "FilePath"] + + +class FilePath(BaseModel): + file_id: Optional[str] = None + """The ID of the file that was generated.""" + + +class FilePathDeltaAnnotation(BaseModel): + index: int + """The index of the annotation in the text content part.""" + + type: Literal["file_path"] + """Always `file_path`.""" + + end_index: Optional[int] = None + + file_path: Optional[FilePath] = None + + start_index: Optional[int] = None + + text: Optional[str] = None + """The text in the message content that needs to be replaced.""" diff --git a/src/openai/types/beta/threads/message_content_image_file.py b/src/openai/types/beta/threads/image_file.py similarity index 54% rename from src/openai/types/beta/threads/message_content_image_file.py rename to src/openai/types/beta/threads/image_file.py index eeba5a633c..371055627c 100644 --- a/src/openai/types/beta/threads/message_content_image_file.py +++ b/src/openai/types/beta/threads/image_file.py @@ -1,10 +1,8 @@ # File generated from our OpenAPI spec by Stainless. -from typing_extensions import Literal - from ...._models import BaseModel -__all__ = ["MessageContentImageFile", "ImageFile"] +__all__ = ["ImageFile"] class ImageFile(BaseModel): @@ -13,10 +11,3 @@ class ImageFile(BaseModel): The [File](https://platform.openai.com/docs/api-reference/files) ID of the image in the message content. """ - - -class MessageContentImageFile(BaseModel): - image_file: ImageFile - - type: Literal["image_file"] - """Always `image_file`.""" diff --git a/src/openai/types/beta/threads/image_file_content_block.py b/src/openai/types/beta/threads/image_file_content_block.py new file mode 100644 index 0000000000..3baf8b884b --- /dev/null +++ b/src/openai/types/beta/threads/image_file_content_block.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .image_file import ImageFile + +__all__ = ["ImageFileContentBlock"] + + +class ImageFileContentBlock(BaseModel): + image_file: ImageFile + + type: Literal["image_file"] + """Always `image_file`.""" diff --git a/src/openai/types/beta/threads/image_file_delta.py b/src/openai/types/beta/threads/image_file_delta.py new file mode 100644 index 0000000000..2bda05f82b --- /dev/null +++ b/src/openai/types/beta/threads/image_file_delta.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["ImageFileDelta"] + + +class ImageFileDelta(BaseModel): + file_id: Optional[str] = None + """ + The [File](https://platform.openai.com/docs/api-reference/files) ID of the image + in the message content. + """ diff --git a/src/openai/types/beta/threads/image_file_delta_block.py b/src/openai/types/beta/threads/image_file_delta_block.py new file mode 100644 index 0000000000..97cc1c4608 --- /dev/null +++ b/src/openai/types/beta/threads/image_file_delta_block.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .image_file_delta import ImageFileDelta + +__all__ = ["ImageFileDeltaBlock"] + + +class ImageFileDeltaBlock(BaseModel): + index: int + """The index of the content part in the message.""" + + type: Literal["image_file"] + """Always `image_file`.""" + + image_file: Optional[ImageFileDelta] = None diff --git a/src/openai/types/beta/threads/thread_message.py b/src/openai/types/beta/threads/message.py similarity index 63% rename from src/openai/types/beta/threads/thread_message.py rename to src/openai/types/beta/threads/message.py index 6ed5da1401..4f307928be 100644 --- a/src/openai/types/beta/threads/thread_message.py +++ b/src/openai/types/beta/threads/message.py @@ -1,19 +1,20 @@ # File generated from our OpenAPI spec by Stainless. -from typing import List, Union, Optional -from typing_extensions import Literal, Annotated +from typing import List, Optional +from typing_extensions import Literal -from ...._utils import PropertyInfo from ...._models import BaseModel -from .message_content_text import MessageContentText -from .message_content_image_file import MessageContentImageFile +from .message_content import MessageContent -__all__ = ["ThreadMessage", "Content"] +__all__ = ["Message", "IncompleteDetails"] -Content = Annotated[Union[MessageContentImageFile, MessageContentText], PropertyInfo(discriminator="type")] +class IncompleteDetails(BaseModel): + reason: Literal["content_filter", "max_tokens", "run_cancelled", "run_expired", "run_failed"] + """The reason the message is incomplete.""" -class ThreadMessage(BaseModel): + +class Message(BaseModel): id: str """The identifier, which can be referenced in API endpoints.""" @@ -24,7 +25,10 @@ class ThreadMessage(BaseModel): authored this message. """ - content: List[Content] + completed_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the message was completed.""" + + content: List[MessageContent] """The content of the message in array of text and/or images.""" created_at: int @@ -37,6 +41,12 @@ class ThreadMessage(BaseModel): that can access files. A maximum of 10 files can be attached to a message. """ + incomplete_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the message was marked as incomplete.""" + + incomplete_details: Optional[IncompleteDetails] = None + """On an incomplete message, details about why the message is incomplete.""" + metadata: Optional[object] = None """Set of 16 key-value pairs that can be attached to an object. @@ -58,6 +68,12 @@ class ThreadMessage(BaseModel): authoring of this message. """ + status: Literal["in_progress", "incomplete", "completed"] + """ + The status of the message, which can be either `in_progress`, `incomplete`, or + `completed`. + """ + thread_id: str """ The [thread](https://platform.openai.com/docs/api-reference/threads) ID that diff --git a/src/openai/types/beta/threads/message_content.py b/src/openai/types/beta/threads/message_content.py new file mode 100644 index 0000000000..7da6a81fb6 --- /dev/null +++ b/src/openai/types/beta/threads/message_content.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union +from typing_extensions import Annotated + +from ...._utils import PropertyInfo +from .text_content_block import TextContentBlock +from .image_file_content_block import ImageFileContentBlock + +__all__ = ["MessageContent"] + +MessageContent = Annotated[Union[ImageFileContentBlock, TextContentBlock], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/beta/threads/message_content_delta.py b/src/openai/types/beta/threads/message_content_delta.py new file mode 100644 index 0000000000..7a8266d02f --- /dev/null +++ b/src/openai/types/beta/threads/message_content_delta.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union +from typing_extensions import Annotated + +from ...._utils import PropertyInfo +from .text_delta_block import TextDeltaBlock +from .image_file_delta_block import ImageFileDeltaBlock + +__all__ = ["MessageContentDelta"] + +MessageContentDelta = Annotated[Union[ImageFileDeltaBlock, TextDeltaBlock], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/beta/threads/message_content_text.py b/src/openai/types/beta/threads/message_content_text.py deleted file mode 100644 index dd05ff96ca..0000000000 --- a/src/openai/types/beta/threads/message_content_text.py +++ /dev/null @@ -1,77 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import List, Union -from typing_extensions import Literal, Annotated - -from ...._utils import PropertyInfo -from ...._models import BaseModel - -__all__ = [ - "MessageContentText", - "Text", - "TextAnnotation", - "TextAnnotationFileCitation", - "TextAnnotationFileCitationFileCitation", - "TextAnnotationFilePath", - "TextAnnotationFilePathFilePath", -] - - -class TextAnnotationFileCitationFileCitation(BaseModel): - file_id: str - """The ID of the specific File the citation is from.""" - - quote: str - """The specific quote in the file.""" - - -class TextAnnotationFileCitation(BaseModel): - end_index: int - - file_citation: TextAnnotationFileCitationFileCitation - - start_index: int - - text: str - """The text in the message content that needs to be replaced.""" - - type: Literal["file_citation"] - """Always `file_citation`.""" - - -class TextAnnotationFilePathFilePath(BaseModel): - file_id: str - """The ID of the file that was generated.""" - - -class TextAnnotationFilePath(BaseModel): - end_index: int - - file_path: TextAnnotationFilePathFilePath - - start_index: int - - text: str - """The text in the message content that needs to be replaced.""" - - type: Literal["file_path"] - """Always `file_path`.""" - - -TextAnnotation = Annotated[ - Union[TextAnnotationFileCitation, TextAnnotationFilePath], PropertyInfo(discriminator="type") -] - - -class Text(BaseModel): - annotations: List[TextAnnotation] - - value: str - """The data that makes up the text.""" - - -class MessageContentText(BaseModel): - text: Text - - type: Literal["text"] - """Always `text`.""" diff --git a/src/openai/types/beta/threads/message_delta.py b/src/openai/types/beta/threads/message_delta.py new file mode 100644 index 0000000000..1113cc27fb --- /dev/null +++ b/src/openai/types/beta/threads/message_delta.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .message_content_delta import MessageContentDelta + +__all__ = ["MessageDelta"] + + +class MessageDelta(BaseModel): + content: Optional[List[MessageContentDelta]] = None + """The content of the message in array of text and/or images.""" + + file_ids: Optional[List[str]] = None + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs that + the assistant should use. Useful for tools like retrieval and code_interpreter + that can access files. A maximum of 10 files can be attached to a message. + """ + + role: Optional[Literal["user", "assistant"]] = None + """The entity that produced the message. One of `user` or `assistant`.""" diff --git a/src/openai/types/beta/threads/message_delta_event.py b/src/openai/types/beta/threads/message_delta_event.py new file mode 100644 index 0000000000..07a9107a34 --- /dev/null +++ b/src/openai/types/beta/threads/message_delta_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .message_delta import MessageDelta + +__all__ = ["MessageDeltaEvent"] + + +class MessageDeltaEvent(BaseModel): + id: str + """The identifier of the message, which can be referenced in API endpoints.""" + + delta: MessageDelta + """The delta containing the fields that have changed on the Message.""" + + object: Literal["thread.message.delta"] + """The object type, which is always `thread.message.delta`.""" diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 38625d3781..dd2842c584 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -1,24 +1,14 @@ # File generated from our OpenAPI spec by Stainless. -from typing import List, Union, Optional +from typing import List, Optional from typing_extensions import Literal -from ...shared import FunctionDefinition from ...._models import BaseModel from .run_status import RunStatus +from ..assistant_tool import AssistantTool from .required_action_function_tool_call import RequiredActionFunctionToolCall -__all__ = [ - "Run", - "LastError", - "RequiredAction", - "RequiredActionSubmitToolOutputs", - "Tool", - "ToolAssistantToolsCode", - "ToolAssistantToolsRetrieval", - "ToolAssistantToolsFunction", - "Usage", -] +__all__ = ["Run", "LastError", "RequiredAction", "RequiredActionSubmitToolOutputs", "Usage"] class LastError(BaseModel): @@ -42,26 +32,6 @@ class RequiredAction(BaseModel): """For now, this is always `submit_tool_outputs`.""" -class ToolAssistantToolsCode(BaseModel): - type: Literal["code_interpreter"] - """The type of tool being defined: `code_interpreter`""" - - -class ToolAssistantToolsRetrieval(BaseModel): - type: Literal["retrieval"] - """The type of tool being defined: `retrieval`""" - - -class ToolAssistantToolsFunction(BaseModel): - function: FunctionDefinition - - type: Literal["function"] - """The type of tool being defined: `function`""" - - -Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] - - class Usage(BaseModel): completion_tokens: int """Number of completion tokens used over the course of the run.""" @@ -93,7 +63,7 @@ class Run(BaseModel): created_at: int """The Unix timestamp (in seconds) for when the run was created.""" - expires_at: int + expires_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run will expire.""" failed_at: Optional[int] = None @@ -156,7 +126,7 @@ class Run(BaseModel): that was executed on as a part of this run. """ - tools: List[Tool] + tools: List[AssistantTool] """ The list of tools that the [assistant](https://platform.openai.com/docs/api-reference/assistants) used for diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index b92649aa06..c012390f5c 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -5,18 +5,12 @@ from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -from ....types import shared_params +from ..assistant_tool_param import AssistantToolParam -__all__ = [ - "RunCreateParams", - "Tool", - "ToolAssistantToolsCode", - "ToolAssistantToolsRetrieval", - "ToolAssistantToolsFunction", -] +__all__ = ["RunCreateParamsBase", "RunCreateParamsNonStreaming", "RunCreateParamsStreaming"] -class RunCreateParams(TypedDict, total=False): +class RunCreateParamsBase(TypedDict, total=False): assistant_id: Required[str] """ The ID of the @@ -54,28 +48,29 @@ class RunCreateParams(TypedDict, total=False): assistant will be used. """ - tools: Optional[Iterable[Tool]] + tools: Optional[Iterable[AssistantToolParam]] """Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. """ -class ToolAssistantToolsCode(TypedDict, total=False): - type: Required[Literal["code_interpreter"]] - """The type of tool being defined: `code_interpreter`""" - - -class ToolAssistantToolsRetrieval(TypedDict, total=False): - type: Required[Literal["retrieval"]] - """The type of tool being defined: `retrieval`""" - +class RunCreateParamsNonStreaming(RunCreateParamsBase): + stream: Optional[Literal[False]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ -class ToolAssistantToolsFunction(TypedDict, total=False): - function: Required[shared_params.FunctionDefinition] - type: Required[Literal["function"]] - """The type of tool being defined: `function`""" +class RunCreateParamsStreaming(RunCreateParamsBase): + stream: Required[Literal[True]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ -Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction] +RunCreateParams = Union[RunCreateParamsNonStreaming, RunCreateParamsStreaming] diff --git a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py index 3b303a33fc..49e1ac49ab 100644 --- a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py +++ b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py @@ -2,13 +2,18 @@ from __future__ import annotations -from typing import Iterable -from typing_extensions import Required, TypedDict +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict -__all__ = ["RunSubmitToolOutputsParams", "ToolOutput"] +__all__ = [ + "RunSubmitToolOutputsParamsBase", + "ToolOutput", + "RunSubmitToolOutputsParamsNonStreaming", + "RunSubmitToolOutputsParamsStreaming", +] -class RunSubmitToolOutputsParams(TypedDict, total=False): +class RunSubmitToolOutputsParamsBase(TypedDict, total=False): thread_id: Required[str] tool_outputs: Required[Iterable[ToolOutput]] @@ -24,3 +29,24 @@ class ToolOutput(TypedDict, total=False): The ID of the tool call in the `required_action` object within the run object the output is being submitted for. """ + + +class RunSubmitToolOutputsParamsNonStreaming(RunSubmitToolOutputsParamsBase): + stream: Optional[Literal[False]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ + + +class RunSubmitToolOutputsParamsStreaming(RunSubmitToolOutputsParamsBase): + stream: Required[Literal[True]] + """ + If `true`, returns a stream of events that happen during the Run as server-sent + events, terminating when the Run enters a terminal state with a `data: [DONE]` + message. + """ + + +RunSubmitToolOutputsParams = Union[RunSubmitToolOutputsParamsNonStreaming, RunSubmitToolOutputsParamsStreaming] diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py index 16cb852922..03ae192088 100644 --- a/src/openai/types/beta/threads/runs/__init__.py +++ b/src/openai/types/beta/threads/runs/__init__.py @@ -3,9 +3,20 @@ from __future__ import annotations from .run_step import RunStep as RunStep -from .code_tool_call import CodeToolCall as CodeToolCall +from .tool_call import ToolCall as ToolCall +from .run_step_delta import RunStepDelta as RunStepDelta +from .tool_call_delta import ToolCallDelta as ToolCallDelta from .step_list_params import StepListParams as StepListParams from .function_tool_call import FunctionToolCall as FunctionToolCall from .retrieval_tool_call import RetrievalToolCall as RetrievalToolCall +from .run_step_delta_event import RunStepDeltaEvent as RunStepDeltaEvent +from .code_interpreter_logs import CodeInterpreterLogs as CodeInterpreterLogs +from .tool_call_delta_object import ToolCallDeltaObject as ToolCallDeltaObject from .tool_calls_step_details import ToolCallsStepDetails as ToolCallsStepDetails +from .function_tool_call_delta import FunctionToolCallDelta as FunctionToolCallDelta +from .retrieval_tool_call_delta import RetrievalToolCallDelta as RetrievalToolCallDelta +from .code_interpreter_tool_call import CodeInterpreterToolCall as CodeInterpreterToolCall +from .run_step_delta_message_delta import RunStepDeltaMessageDelta as RunStepDeltaMessageDelta +from .code_interpreter_output_image import CodeInterpreterOutputImage as CodeInterpreterOutputImage from .message_creation_step_details import MessageCreationStepDetails as MessageCreationStepDetails +from .code_interpreter_tool_call_delta import CodeInterpreterToolCallDelta as CodeInterpreterToolCallDelta diff --git a/src/openai/types/beta/threads/runs/code_interpreter_logs.py b/src/openai/types/beta/threads/runs/code_interpreter_logs.py new file mode 100644 index 0000000000..c91179be22 --- /dev/null +++ b/src/openai/types/beta/threads/runs/code_interpreter_logs.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CodeInterpreterLogs"] + + +class CodeInterpreterLogs(BaseModel): + index: int + """The index of the output in the outputs array.""" + + type: Literal["logs"] + """Always `logs`.""" + + logs: Optional[str] = None + """The text output from the Code Interpreter tool call.""" diff --git a/src/openai/types/beta/threads/runs/code_interpreter_output_image.py b/src/openai/types/beta/threads/runs/code_interpreter_output_image.py new file mode 100644 index 0000000000..0d7d26f91f --- /dev/null +++ b/src/openai/types/beta/threads/runs/code_interpreter_output_image.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CodeInterpreterOutputImage", "Image"] + + +class Image(BaseModel): + file_id: Optional[str] = None + """ + The [file](https://platform.openai.com/docs/api-reference/files) ID of the + image. + """ + + +class CodeInterpreterOutputImage(BaseModel): + index: int + """The index of the output in the outputs array.""" + + type: Literal["image"] + """Always `image`.""" + + image: Optional[Image] = None diff --git a/src/openai/types/beta/threads/runs/code_tool_call.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py similarity index 95% rename from src/openai/types/beta/threads/runs/code_tool_call.py rename to src/openai/types/beta/threads/runs/code_interpreter_tool_call.py index 0de47b379b..c537562e91 100644 --- a/src/openai/types/beta/threads/runs/code_tool_call.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py @@ -7,7 +7,7 @@ from ....._models import BaseModel __all__ = [ - "CodeToolCall", + "CodeInterpreterToolCall", "CodeInterpreter", "CodeInterpreterOutput", "CodeInterpreterOutputLogs", @@ -56,7 +56,7 @@ class CodeInterpreter(BaseModel): """ -class CodeToolCall(BaseModel): +class CodeInterpreterToolCall(BaseModel): id: str """The ID of the tool call.""" diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py new file mode 100644 index 0000000000..b13105f840 --- /dev/null +++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated + +from ....._utils import PropertyInfo +from ....._models import BaseModel +from .code_interpreter_logs import CodeInterpreterLogs +from .code_interpreter_output_image import CodeInterpreterOutputImage + +__all__ = ["CodeInterpreterToolCallDelta", "CodeInterpreter", "CodeInterpreterOutput"] + +CodeInterpreterOutput = Annotated[ + Union[CodeInterpreterLogs, CodeInterpreterOutputImage], PropertyInfo(discriminator="type") +] + + +class CodeInterpreter(BaseModel): + input: Optional[str] = None + """The input to the Code Interpreter tool call.""" + + outputs: Optional[List[CodeInterpreterOutput]] = None + """The outputs from the Code Interpreter tool call. + + Code Interpreter can output one or more items, including text (`logs`) or images + (`image`). Each of these are represented by a different object type. + """ + + +class CodeInterpreterToolCallDelta(BaseModel): + index: int + """The index of the tool call in the tool calls array.""" + + type: Literal["code_interpreter"] + """The type of tool call. + + This is always going to be `code_interpreter` for this type of tool call. + """ + + id: Optional[str] = None + """The ID of the tool call.""" + + code_interpreter: Optional[CodeInterpreter] = None + """The Code Interpreter tool call definition.""" diff --git a/src/openai/types/beta/threads/runs/function_tool_call_delta.py b/src/openai/types/beta/threads/runs/function_tool_call_delta.py new file mode 100644 index 0000000000..46c341bc84 --- /dev/null +++ b/src/openai/types/beta/threads/runs/function_tool_call_delta.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["FunctionToolCallDelta", "Function"] + + +class Function(BaseModel): + arguments: Optional[str] = None + """The arguments passed to the function.""" + + name: Optional[str] = None + """The name of the function.""" + + output: Optional[str] = None + """The output of the function. + + This will be `null` if the outputs have not been + [submitted](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs) + yet. + """ + + +class FunctionToolCallDelta(BaseModel): + index: int + """The index of the tool call in the tool calls array.""" + + type: Literal["function"] + """The type of tool call. + + This is always going to be `function` for this type of tool call. + """ + + id: Optional[str] = None + """The ID of the tool call object.""" + + function: Optional[Function] = None + """The definition of the function that was called.""" diff --git a/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py b/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py new file mode 100644 index 0000000000..ac8003d3eb --- /dev/null +++ b/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["RetrievalToolCallDelta"] + + +class RetrievalToolCallDelta(BaseModel): + index: int + """The index of the tool call in the tool calls array.""" + + type: Literal["retrieval"] + """The type of tool call. + + This is always going to be `retrieval` for this type of tool call. + """ + + id: Optional[str] = None + """The ID of the tool call object.""" + + retrieval: Optional[object] = None + """For now, this is always going to be an empty object.""" diff --git a/src/openai/types/beta/threads/runs/run_step_delta.py b/src/openai/types/beta/threads/runs/run_step_delta.py new file mode 100644 index 0000000000..fb8b869425 --- /dev/null +++ b/src/openai/types/beta/threads/runs/run_step_delta.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union, Optional +from typing_extensions import Annotated + +from ....._utils import PropertyInfo +from ....._models import BaseModel +from .tool_call_delta_object import ToolCallDeltaObject +from .run_step_delta_message_delta import RunStepDeltaMessageDelta + +__all__ = ["RunStepDelta", "StepDetails"] + +StepDetails = Annotated[Union[RunStepDeltaMessageDelta, ToolCallDeltaObject], PropertyInfo(discriminator="type")] + + +class RunStepDelta(BaseModel): + step_details: Optional[StepDetails] = None + """The details of the run step.""" diff --git a/src/openai/types/beta/threads/runs/run_step_delta_event.py b/src/openai/types/beta/threads/runs/run_step_delta_event.py new file mode 100644 index 0000000000..ab61dd1f9a --- /dev/null +++ b/src/openai/types/beta/threads/runs/run_step_delta_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from ....._models import BaseModel +from .run_step_delta import RunStepDelta + +__all__ = ["RunStepDeltaEvent"] + + +class RunStepDeltaEvent(BaseModel): + id: str + """The identifier of the run step, which can be referenced in API endpoints.""" + + delta: RunStepDelta + """The delta containing the fields that have changed on the run step.""" + + object: Literal["thread.run.step.delta"] + """The object type, which is always `thread.run.step.delta`.""" diff --git a/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py b/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py new file mode 100644 index 0000000000..52ec5d3440 --- /dev/null +++ b/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["RunStepDeltaMessageDelta", "MessageCreation"] + + +class MessageCreation(BaseModel): + message_id: Optional[str] = None + """The ID of the message that was created by this run step.""" + + +class RunStepDeltaMessageDelta(BaseModel): + type: Literal["message_creation"] + """Always `message_creation`.""" + + message_creation: Optional[MessageCreation] = None diff --git a/src/openai/types/beta/threads/runs/tool_call.py b/src/openai/types/beta/threads/runs/tool_call.py new file mode 100644 index 0000000000..a3abfa77ad --- /dev/null +++ b/src/openai/types/beta/threads/runs/tool_call.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union +from typing_extensions import Annotated + +from ....._utils import PropertyInfo +from .function_tool_call import FunctionToolCall +from .retrieval_tool_call import RetrievalToolCall +from .code_interpreter_tool_call import CodeInterpreterToolCall + +__all__ = ["ToolCall"] + +ToolCall = Annotated[ + Union[CodeInterpreterToolCall, RetrievalToolCall, FunctionToolCall], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/threads/runs/tool_call_delta.py b/src/openai/types/beta/threads/runs/tool_call_delta.py new file mode 100644 index 0000000000..a1aa4de6cf --- /dev/null +++ b/src/openai/types/beta/threads/runs/tool_call_delta.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Union +from typing_extensions import Annotated + +from ....._utils import PropertyInfo +from .function_tool_call_delta import FunctionToolCallDelta +from .retrieval_tool_call_delta import RetrievalToolCallDelta +from .code_interpreter_tool_call_delta import CodeInterpreterToolCallDelta + +__all__ = ["ToolCallDelta"] + +ToolCallDelta = Annotated[ + Union[CodeInterpreterToolCallDelta, RetrievalToolCallDelta, FunctionToolCallDelta], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/threads/runs/tool_call_delta_object.py b/src/openai/types/beta/threads/runs/tool_call_delta_object.py new file mode 100644 index 0000000000..2ce46ab894 --- /dev/null +++ b/src/openai/types/beta/threads/runs/tool_call_delta_object.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional +from typing_extensions import Literal + +from ....._models import BaseModel +from .tool_call_delta import ToolCallDelta + +__all__ = ["ToolCallDeltaObject"] + + +class ToolCallDeltaObject(BaseModel): + type: Literal["tool_calls"] + """Always `tool_calls`.""" + + tool_calls: Optional[List[ToolCallDelta]] = None + """An array of tool calls the run step was involved in. + + These can be associated with one of three types of tools: `code_interpreter`, + `retrieval`, or `function`. + """ diff --git a/src/openai/types/beta/threads/runs/tool_calls_step_details.py b/src/openai/types/beta/threads/runs/tool_calls_step_details.py index b1b5a72bee..6fccfc563a 100644 --- a/src/openai/types/beta/threads/runs/tool_calls_step_details.py +++ b/src/openai/types/beta/threads/runs/tool_calls_step_details.py @@ -1,17 +1,12 @@ # File generated from our OpenAPI spec by Stainless. -from typing import List, Union -from typing_extensions import Literal, Annotated +from typing import List +from typing_extensions import Literal -from ....._utils import PropertyInfo +from .tool_call import ToolCall from ....._models import BaseModel -from .code_tool_call import CodeToolCall -from .function_tool_call import FunctionToolCall -from .retrieval_tool_call import RetrievalToolCall -__all__ = ["ToolCallsStepDetails", "ToolCall"] - -ToolCall = Annotated[Union[CodeToolCall, RetrievalToolCall, FunctionToolCall], PropertyInfo(discriminator="type")] +__all__ = ["ToolCallsStepDetails"] class ToolCallsStepDetails(BaseModel): diff --git a/src/openai/types/beta/threads/text.py b/src/openai/types/beta/threads/text.py new file mode 100644 index 0000000000..a5a31c6783 --- /dev/null +++ b/src/openai/types/beta/threads/text.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List + +from ...._models import BaseModel +from .annotation import Annotation + +__all__ = ["Text"] + + +class Text(BaseModel): + annotations: List[Annotation] + + value: str + """The data that makes up the text.""" diff --git a/src/openai/types/beta/threads/text_content_block.py b/src/openai/types/beta/threads/text_content_block.py new file mode 100644 index 0000000000..1c9187ea60 --- /dev/null +++ b/src/openai/types/beta/threads/text_content_block.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing_extensions import Literal + +from .text import Text +from ...._models import BaseModel + +__all__ = ["TextContentBlock"] + + +class TextContentBlock(BaseModel): + text: Text + + type: Literal["text"] + """Always `text`.""" diff --git a/src/openai/types/beta/threads/text_delta.py b/src/openai/types/beta/threads/text_delta.py new file mode 100644 index 0000000000..735846472a --- /dev/null +++ b/src/openai/types/beta/threads/text_delta.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import List, Optional + +from ...._models import BaseModel +from .annotation_delta import AnnotationDelta + +__all__ = ["TextDelta"] + + +class TextDelta(BaseModel): + annotations: Optional[List[AnnotationDelta]] = None + + value: Optional[str] = None + """The data that makes up the text.""" diff --git a/src/openai/types/beta/threads/text_delta_block.py b/src/openai/types/beta/threads/text_delta_block.py new file mode 100644 index 0000000000..6adbdee479 --- /dev/null +++ b/src/openai/types/beta/threads/text_delta_block.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .text_delta import TextDelta + +__all__ = ["TextDeltaBlock"] + + +class TextDeltaBlock(BaseModel): + index: int + """The index of the content part in the message.""" + + type: Literal["text"] + """Always `text`.""" + + text: Optional[TextDelta] = None diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 9afbacb874..e391c63119 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -190,7 +190,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): """A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of - functions the model may generate JSON inputs for. + functions the model may generate JSON inputs for. A max of 128 functions are + supported. """ top_logprobs: Optional[int] diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index afbc9c549f..08ffca760f 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -124,7 +124,10 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ suffix: Optional[str] - """The suffix that comes after a completion of inserted text.""" + """The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + """ temperature: Optional[float] """What sampling temperature to use, between 0 and 2. diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index 05bc4ff9ba..c9ebb1a504 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -1,4 +1,5 @@ # File generated from our OpenAPI spec by Stainless. +from .error_object import ErrorObject as ErrorObject from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters diff --git a/src/openai/types/shared/error_object.py b/src/openai/types/shared/error_object.py new file mode 100644 index 0000000000..f18fcc1c33 --- /dev/null +++ b/src/openai/types/shared/error_object.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. + +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/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 5b347de1f0..6bb8fc82de 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -196,19 +196,20 @@ def test_path_params_delete(self, client: OpenAI) -> None: ) @parametrize - def test_method_create_and_run(self, client: OpenAI) -> None: + def test_method_create_and_run_overload_1(self, client: OpenAI) -> None: thread = client.beta.threads.create_and_run( assistant_id="string", ) assert_matches_type(Run, thread, path=["response"]) @parametrize - def test_method_create_and_run_with_all_params(self, client: OpenAI) -> None: + def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) -> None: thread = client.beta.threads.create_and_run( assistant_id="string", instructions="string", metadata={}, model="string", + stream=False, thread={ "messages": [ { @@ -237,7 +238,7 @@ def test_method_create_and_run_with_all_params(self, client: OpenAI) -> None: assert_matches_type(Run, thread, path=["response"]) @parametrize - def test_raw_response_create_and_run(self, client: OpenAI) -> None: + def test_raw_response_create_and_run_overload_1(self, client: OpenAI) -> None: response = client.beta.threads.with_raw_response.create_and_run( assistant_id="string", ) @@ -248,7 +249,7 @@ def test_raw_response_create_and_run(self, client: OpenAI) -> None: assert_matches_type(Run, thread, path=["response"]) @parametrize - def test_streaming_response_create_and_run(self, client: OpenAI) -> None: + def test_streaming_response_create_and_run_overload_1(self, client: OpenAI) -> None: with client.beta.threads.with_streaming_response.create_and_run( assistant_id="string", ) as response: @@ -260,6 +261,74 @@ def test_streaming_response_create_and_run(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_create_and_run_overload_2(self, client: OpenAI) -> None: + thread_stream = client.beta.threads.create_and_run( + assistant_id="string", + stream=True, + ) + thread_stream.response.close() + + @parametrize + def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) -> None: + thread_stream = client.beta.threads.create_and_run( + assistant_id="string", + stream=True, + instructions="string", + metadata={}, + model="string", + thread={ + "messages": [ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], + "metadata": {}, + }, + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + thread_stream.response.close() + + @parametrize + def test_raw_response_create_and_run_overload_2(self, client: OpenAI) -> None: + response = client.beta.threads.with_raw_response.create_and_run( + assistant_id="string", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_create_and_run_overload_2(self, client: OpenAI) -> None: + with client.beta.threads.with_streaming_response.create_and_run( + assistant_id="string", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + class TestAsyncThreads: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -439,19 +508,20 @@ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: ) @parametrize - async def test_method_create_and_run(self, async_client: AsyncOpenAI) -> None: + async def test_method_create_and_run_overload_1(self, async_client: AsyncOpenAI) -> None: thread = await async_client.beta.threads.create_and_run( assistant_id="string", ) assert_matches_type(Run, thread, path=["response"]) @parametrize - async def test_method_create_and_run_with_all_params(self, async_client: AsyncOpenAI) -> None: + async def test_method_create_and_run_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: thread = await async_client.beta.threads.create_and_run( assistant_id="string", instructions="string", metadata={}, model="string", + stream=False, thread={ "messages": [ { @@ -480,7 +550,7 @@ async def test_method_create_and_run_with_all_params(self, async_client: AsyncOp assert_matches_type(Run, thread, path=["response"]) @parametrize - async def test_raw_response_create_and_run(self, async_client: AsyncOpenAI) -> None: + async def test_raw_response_create_and_run_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.threads.with_raw_response.create_and_run( assistant_id="string", ) @@ -491,7 +561,7 @@ async def test_raw_response_create_and_run(self, async_client: AsyncOpenAI) -> N assert_matches_type(Run, thread, path=["response"]) @parametrize - async def test_streaming_response_create_and_run(self, async_client: AsyncOpenAI) -> None: + async def test_streaming_response_create_and_run_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.threads.with_streaming_response.create_and_run( assistant_id="string", ) as response: @@ -502,3 +572,71 @@ async def test_streaming_response_create_and_run(self, async_client: AsyncOpenAI assert_matches_type(Run, thread, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_create_and_run_overload_2(self, async_client: AsyncOpenAI) -> None: + thread_stream = await async_client.beta.threads.create_and_run( + assistant_id="string", + stream=True, + ) + await thread_stream.response.aclose() + + @parametrize + async def test_method_create_and_run_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + thread_stream = await async_client.beta.threads.create_and_run( + assistant_id="string", + stream=True, + instructions="string", + metadata={}, + model="string", + thread={ + "messages": [ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], + "metadata": {}, + }, + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + await thread_stream.response.aclose() + + @parametrize + async def test_raw_response_create_and_run_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.with_raw_response.create_and_run( + assistant_id="string", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_create_and_run_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.with_streaming_response.create_and_run( + assistant_id="string", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index 538d2f4c2a..c61a9ee109 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads import ThreadMessage +from openai.types.beta.threads import Message base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -25,7 +25,7 @@ def test_method_create(self, client: OpenAI) -> None: content="x", role="user", ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: @@ -36,7 +36,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: file_ids=["string"], metadata={}, ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize def test_raw_response_create(self, client: OpenAI) -> None: @@ -49,7 +49,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: @@ -62,7 +62,7 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) assert cast(Any, response.is_closed) is True @@ -81,7 +81,7 @@ def test_method_retrieve(self, client: OpenAI) -> None: "string", thread_id="string", ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: @@ -93,7 +93,7 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: OpenAI) -> None: @@ -105,7 +105,7 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) assert cast(Any, response.is_closed) is True @@ -129,7 +129,7 @@ def test_method_update(self, client: OpenAI) -> None: "string", thread_id="string", ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: OpenAI) -> None: @@ -138,7 +138,7 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: thread_id="string", metadata={}, ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize def test_raw_response_update(self, client: OpenAI) -> None: @@ -150,7 +150,7 @@ def test_raw_response_update(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize def test_streaming_response_update(self, client: OpenAI) -> None: @@ -162,7 +162,7 @@ def test_streaming_response_update(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) assert cast(Any, response.is_closed) is True @@ -185,7 +185,7 @@ def test_method_list(self, client: OpenAI) -> None: message = client.beta.threads.messages.list( "string", ) - assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + assert_matches_type(SyncCursorPage[Message], message, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -196,7 +196,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + assert_matches_type(SyncCursorPage[Message], message, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -207,7 +207,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + assert_matches_type(SyncCursorPage[Message], message, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -218,7 +218,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"]) + assert_matches_type(SyncCursorPage[Message], message, path=["response"]) assert cast(Any, response.is_closed) is True @@ -240,7 +240,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: content="x", role="user", ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -251,7 +251,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> file_ids=["string"], metadata={}, ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @@ -264,7 +264,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: @@ -277,7 +277,7 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) assert cast(Any, response.is_closed) is True @@ -296,7 +296,7 @@ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: "string", thread_id="string", ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: @@ -308,7 +308,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: @@ -320,7 +320,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) assert cast(Any, response.is_closed) is True @@ -344,7 +344,7 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: "string", thread_id="string", ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -353,7 +353,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> thread_id="string", metadata={}, ) - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: @@ -365,7 +365,7 @@ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: @@ -377,7 +377,7 @@ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> Non assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() - assert_matches_type(ThreadMessage, message, path=["response"]) + assert_matches_type(Message, message, path=["response"]) assert cast(Any, response.is_closed) is True @@ -400,7 +400,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: message = await async_client.beta.threads.messages.list( "string", ) - assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) + assert_matches_type(AsyncCursorPage[Message], message, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -411,7 +411,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) + assert_matches_type(AsyncCursorPage[Message], message, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -422,7 +422,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() - assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) + assert_matches_type(AsyncCursorPage[Message], message, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -433,7 +433,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() - assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"]) + assert_matches_type(AsyncCursorPage[Message], message, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 9e88d65eaf..de1ad07567 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -21,7 +21,7 @@ class TestRuns: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - def test_method_create(self, client: OpenAI) -> None: + def test_method_create_overload_1(self, client: OpenAI) -> None: run = client.beta.threads.runs.create( "string", assistant_id="string", @@ -29,7 +29,7 @@ def test_method_create(self, client: OpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - def test_method_create_with_all_params(self, client: OpenAI) -> None: + def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: run = client.beta.threads.runs.create( "string", assistant_id="string", @@ -37,12 +37,13 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: instructions="string", metadata={}, model="string", + stream=False, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], ) assert_matches_type(Run, run, path=["response"]) @parametrize - def test_raw_response_create(self, client: OpenAI) -> None: + def test_raw_response_create_overload_1(self, client: OpenAI) -> None: response = client.beta.threads.runs.with_raw_response.create( "string", assistant_id="string", @@ -54,7 +55,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - def test_streaming_response_create(self, client: OpenAI) -> None: + def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: with client.beta.threads.runs.with_streaming_response.create( "string", assistant_id="string", @@ -68,13 +69,72 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_create(self, client: OpenAI) -> None: + def test_path_params_create_overload_1(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.beta.threads.runs.with_raw_response.create( "", assistant_id="string", ) + @parametrize + def test_method_create_overload_2(self, client: OpenAI) -> None: + run_stream = client.beta.threads.runs.create( + "string", + assistant_id="string", + stream=True, + ) + run_stream.response.close() + + @parametrize + def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: + run_stream = client.beta.threads.runs.create( + "string", + assistant_id="string", + stream=True, + additional_instructions="string", + instructions="string", + metadata={}, + model="string", + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + run_stream.response.close() + + @parametrize + def test_raw_response_create_overload_2(self, client: OpenAI) -> None: + response = client.beta.threads.runs.with_raw_response.create( + "string", + assistant_id="string", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: + with client.beta.threads.runs.with_streaming_response.create( + "string", + assistant_id="string", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create_overload_2(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.with_raw_response.create( + "", + assistant_id="string", + stream=True, + ) + @parametrize def test_method_retrieve(self, client: OpenAI) -> None: run = client.beta.threads.runs.retrieve( @@ -278,7 +338,7 @@ def test_path_params_cancel(self, client: OpenAI) -> None: ) @parametrize - def test_method_submit_tool_outputs(self, client: OpenAI) -> None: + def test_method_submit_tool_outputs_overload_1(self, client: OpenAI) -> None: run = client.beta.threads.runs.submit_tool_outputs( "string", thread_id="string", @@ -287,7 +347,30 @@ def test_method_submit_tool_outputs(self, client: OpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - def test_raw_response_submit_tool_outputs(self, client: OpenAI) -> None: + def test_method_submit_tool_outputs_with_all_params_overload_1(self, client: OpenAI) -> None: + run = client.beta.threads.runs.submit_tool_outputs( + "string", + thread_id="string", + tool_outputs=[ + { + "tool_call_id": "string", + "output": "string", + }, + { + "tool_call_id": "string", + "output": "string", + }, + { + "tool_call_id": "string", + "output": "string", + }, + ], + stream=False, + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + def test_raw_response_submit_tool_outputs_overload_1(self, client: OpenAI) -> None: response = client.beta.threads.runs.with_raw_response.submit_tool_outputs( "string", thread_id="string", @@ -300,7 +383,7 @@ def test_raw_response_submit_tool_outputs(self, client: OpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - def test_streaming_response_submit_tool_outputs(self, client: OpenAI) -> None: + def test_streaming_response_submit_tool_outputs_overload_1(self, client: OpenAI) -> None: with client.beta.threads.runs.with_streaming_response.submit_tool_outputs( "string", thread_id="string", @@ -315,11 +398,67 @@ def test_streaming_response_submit_tool_outputs(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_submit_tool_outputs(self, client: OpenAI) -> None: + def test_path_params_submit_tool_outputs_overload_1(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "string", + thread_id="", + tool_outputs=[{}, {}, {}], + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) + + @parametrize + def test_method_submit_tool_outputs_overload_2(self, client: OpenAI) -> None: + run_stream = client.beta.threads.runs.submit_tool_outputs( + "string", + thread_id="string", + stream=True, + tool_outputs=[{}, {}, {}], + ) + run_stream.response.close() + + @parametrize + def test_raw_response_submit_tool_outputs_overload_2(self, client: OpenAI) -> None: + response = client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "string", + thread_id="string", + stream=True, + tool_outputs=[{}, {}, {}], + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_submit_tool_outputs_overload_2(self, client: OpenAI) -> None: + with client.beta.threads.runs.with_streaming_response.submit_tool_outputs( + "string", + thread_id="string", + stream=True, + tool_outputs=[{}, {}, {}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_submit_tool_outputs_overload_2(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.beta.threads.runs.with_raw_response.submit_tool_outputs( "string", thread_id="", + stream=True, tool_outputs=[{}, {}, {}], ) @@ -327,6 +466,7 @@ def test_path_params_submit_tool_outputs(self, client: OpenAI) -> None: client.beta.threads.runs.with_raw_response.submit_tool_outputs( "", thread_id="string", + stream=True, tool_outputs=[{}, {}, {}], ) @@ -335,7 +475,7 @@ class TestAsyncRuns: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, async_client: AsyncOpenAI) -> None: + async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: run = await async_client.beta.threads.runs.create( "string", assistant_id="string", @@ -343,7 +483,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: run = await async_client.beta.threads.runs.create( "string", assistant_id="string", @@ -351,12 +491,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> instructions="string", metadata={}, model="string", + stream=False, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], ) assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.threads.runs.with_raw_response.create( "string", assistant_id="string", @@ -368,7 +509,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.threads.runs.with_streaming_response.create( "string", assistant_id="string", @@ -382,13 +523,72 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + async def test_path_params_create_overload_1(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.beta.threads.runs.with_raw_response.create( "", assistant_id="string", ) + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: + run_stream = await async_client.beta.threads.runs.create( + "string", + assistant_id="string", + stream=True, + ) + await run_stream.response.aclose() + + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + run_stream = await async_client.beta.threads.runs.create( + "string", + assistant_id="string", + stream=True, + additional_instructions="string", + instructions="string", + metadata={}, + model="string", + tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + ) + await run_stream.response.aclose() + + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.with_raw_response.create( + "string", + assistant_id="string", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.with_streaming_response.create( + "string", + assistant_id="string", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create_overload_2(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await async_client.beta.threads.runs.with_raw_response.create( + "", + assistant_id="string", + stream=True, + ) + @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: run = await async_client.beta.threads.runs.retrieve( @@ -592,7 +792,7 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: ) @parametrize - async def test_method_submit_tool_outputs(self, async_client: AsyncOpenAI) -> None: + async def test_method_submit_tool_outputs_overload_1(self, async_client: AsyncOpenAI) -> None: run = await async_client.beta.threads.runs.submit_tool_outputs( "string", thread_id="string", @@ -601,7 +801,30 @@ async def test_method_submit_tool_outputs(self, async_client: AsyncOpenAI) -> No assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_raw_response_submit_tool_outputs(self, async_client: AsyncOpenAI) -> None: + async def test_method_submit_tool_outputs_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: + run = await async_client.beta.threads.runs.submit_tool_outputs( + "string", + thread_id="string", + tool_outputs=[ + { + "tool_call_id": "string", + "output": "string", + }, + { + "tool_call_id": "string", + "output": "string", + }, + { + "tool_call_id": "string", + "output": "string", + }, + ], + stream=False, + ) + assert_matches_type(Run, run, path=["response"]) + + @parametrize + async def test_raw_response_submit_tool_outputs_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( "string", thread_id="string", @@ -614,7 +837,7 @@ async def test_raw_response_submit_tool_outputs(self, async_client: AsyncOpenAI) assert_matches_type(Run, run, path=["response"]) @parametrize - async def test_streaming_response_submit_tool_outputs(self, async_client: AsyncOpenAI) -> None: + async def test_streaming_response_submit_tool_outputs_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.threads.runs.with_streaming_response.submit_tool_outputs( "string", thread_id="string", @@ -629,11 +852,67 @@ async def test_streaming_response_submit_tool_outputs(self, async_client: AsyncO assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_submit_tool_outputs(self, async_client: AsyncOpenAI) -> None: + async def test_path_params_submit_tool_outputs_overload_1(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "string", + thread_id="", + tool_outputs=[{}, {}, {}], + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "", + thread_id="string", + tool_outputs=[{}, {}, {}], + ) + + @parametrize + async def test_method_submit_tool_outputs_overload_2(self, async_client: AsyncOpenAI) -> None: + run_stream = await async_client.beta.threads.runs.submit_tool_outputs( + "string", + thread_id="string", + stream=True, + tool_outputs=[{}, {}, {}], + ) + await run_stream.response.aclose() + + @parametrize + async def test_raw_response_submit_tool_outputs_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( + "string", + thread_id="string", + stream=True, + tool_outputs=[{}, {}, {}], + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_submit_tool_outputs_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.runs.with_streaming_response.submit_tool_outputs( + "string", + thread_id="string", + stream=True, + tool_outputs=[{}, {}, {}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_submit_tool_outputs_overload_2(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( "string", thread_id="", + stream=True, tool_outputs=[{}, {}, {}], ) @@ -641,5 +920,6 @@ async def test_path_params_submit_tool_outputs(self, async_client: AsyncOpenAI) await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( "", thread_id="string", + stream=True, tool_outputs=[{}, {}, {}], ) From e8e5a0dc7ccf2db19d7f81991ee0987f9c3ae375 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:37:44 -0400 Subject: [PATCH 359/914] release: 1.14.1 (#1239) * docs(readme): assistant streaming (#1238) * release: 1.14.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++ helpers.md | 161 ++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 5 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 helpers.md diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e72f11310e..a780111df4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.14.0" + ".": "1.14.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0fc7556d..f7a80d39a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.14.1 (2024-03-15) + +Full Changelog: [v1.14.0...v1.14.1](https://github.com/openai/openai-python/compare/v1.14.0...v1.14.1) + +### Documentation + +* **readme:** assistant streaming ([#1238](https://github.com/openai/openai-python/issues/1238)) ([0fc30a2](https://github.com/openai/openai-python/commit/0fc30a23030b4ff60f27cd2f472517926ed0f300)) + ## 1.14.0 (2024-03-13) Full Changelog: [v1.13.4...v1.14.0](https://github.com/openai/openai-python/compare/v1.13.4...v1.14.0) diff --git a/helpers.md b/helpers.md new file mode 100644 index 0000000000..03fd5e76b7 --- /dev/null +++ b/helpers.md @@ -0,0 +1,161 @@ +# Streaming Helpers + +OpenAI supports streaming responses when interacting with the [Assistant](#assistant-streaming-api) APIs. + +## Assistant Streaming API + +OpenAI supports streaming responses from Assistants. The SDK provides convenience wrappers around the API +so you can subscribe to the types of events you are interested in as well as receive accumulated responses. + +More information can be found in the documentation: [Assistant Streaming](https://platform.openai.com/docs/assistants/overview?lang=python) + +#### An example of creating a run and subscribing to some events + +You can subscribe to events by creating an event handler class and overloading the relevant event handlers. + +```python +from typing_extensions import override +from openai import AssistantEventHandler + +# First, we create a EventHandler class to define +# how we want to handle the events in the response stream. + +class EventHandler(AssistantEventHandler): + @override + def on_text_created(self, text) -> None: + print(f"\nassistant > ", end="", flush=True) + + @override + def on_text_delta(self, delta, snapshot): + print(delta.value, end="", flush=True) + + def on_tool_call_created(self, tool_call): + print(f"\nassistant > {tool_call.type}\n", flush=True) + + def on_tool_call_delta(self, delta, snapshot): + if delta.type == 'code_interpreter': + if delta.code_interpreter.input: + print(delta.code_interpreter.input, end="", flush=True) + if delta.code_interpreter.outputs: + print(f"\n\noutput >", flush=True) + for output in delta.code_interpreter.outputs: + if output.type == "logs": + print(f"\n{output.logs}", flush=True) + +# Then, we use the `create_and_stream` SDK helper +# with the `EventHandler` class to create the Run +# and stream the response. + +with client.beta.threads.runs.create_and_stream( + thread_id=thread.id, + assistant_id=assistant.id, + instructions="Please address the user as Jane Doe. The user has a premium account.", + event_handler=EventHandler(), +) as stream: + stream.until_done() +``` + +### Assistant Events + +The assistant API provides events you can subscribe to for the following events. + +```python +def on_event(self, event: AssistantStreamEvent) +``` + +This allows you to subscribe to all the possible raw events sent by the OpenAI streaming API. +In many cases it will be more convenient to subscribe to a more specific set of events for your use case. + +More information on the types of events can be found here: [Events](https://platform.openai.com/docs/api-reference/assistants-streaming/events) + +```python +def on_run_step_created(self, run_step: RunStep) +def on_run_step_delta(self, delta: RunStepDelta, snapshot: RunStep) +def on_run_step_done(self, run_step: RunStep) +``` + +These events allow you to subscribe to the creation, delta and completion of a RunStep. + +For more information on how Runs and RunSteps work see the documentation [Runs and RunSteps](https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps) + +```python +def on_message_created(self, message: Message) +def on_message_delta(self, delta: MessageDelta, snapshot: Message) +def on_message_done(self, message: Message) +``` + +This allows you to subscribe to Message creation, delta and completion events. Messages can contain +different types of content that can be sent from a model (and events are available for specific content types). +For convenience, the delta event includes both the incremental update and an accumulated snapshot of the content. + +More information on messages can be found +on in the documentation page [Message](https://platform.openai.com/docs/api-reference/messages/object). + +```python +def on_text_created(self, text: Text) +def on_text_delta(self, delta: TextDelta, snapshot: Text) +def on_text_done(self, text: Text) +``` + +These events allow you to subscribe to the creation, delta and completion of a Text content (a specific type of message). +For convenience, the delta event includes both the incremental update and an accumulated snapshot of the content. + +```python +def on_image_file_done(self, image_file: ImageFile) +``` + +Image files are not sent incrementally so an event is provided for when a image file is available. + +```python +def on_tool_call_created(self, tool_call: ToolCall) +def on_tool_call_delta(self, delta: ToolCallDelta, snapshot: ToolCall) +def on_tool_call_done(self, tool_call: ToolCall) +``` + +These events allow you to subscribe to events for the creation, delta and completion of a ToolCall. + +More information on tools can be found here [Tools](https://platform.openai.com/docs/assistants/tools) + +```python +def on_end(self) +``` + +The last event send when a stream ends. + +```python +def on_timeout(self) +``` + +This event is triggered if the request times out. + +```python +def on_exception(self, exception: Exception) +``` + +This event is triggered if an exception occurs during streaming. + +### Assistant Methods + +The assistant streaming object also provides a few methods for convenience: + +```python +def current_event() +def current_run() +def current_message_snapshot() +def current_run_step_snapshot() +``` + +These methods are provided to allow you to access additional context from within event handlers. In many cases +the handlers should include all the information you need for processing, but if additional context is required it +can be accessed. + +Note: There is not always a relevant context in certain situations (these will be undefined in those cases). + +```python +def get_final_run(self) +def get_final_run_steps(self) +def get_final_messages(self) +``` + +These methods are provided for convenience to collect information at the end of a stream. Calling these events +will trigger consumption of the stream until completion and then return the relevant accumulated objects. diff --git a/pyproject.toml b/pyproject.toml index 0856032512..d562977dbd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.14.0" +version = "1.14.1" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 134799ff42..3f5331b8e0 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.14.0" # x-release-please-version +__version__ = "1.14.1" # x-release-please-version From 5cfb125acce0e8304d12bdd39b405071021db658 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:49:15 -0400 Subject: [PATCH 360/914] release: 1.14.2 (#1244) * perf: cache TypeAdapters (#1114) * perf: cache TypeAdapters (#1243) * docs: fix typo in CONTRIBUTING.md (#1245) * chore(internal): update generated pragma comment (#1247) * docs: assistant improvements (#1249) * release: 1.14.2 --------- Co-authored-by: vvanglro <947001731@qq.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 +++++ CONTRIBUTING.md | 2 +- README.md | 18 ++++ helpers.md | 86 +++++++++++++++---- pyproject.toml | 2 +- src/openai/__init__.py | 2 +- src/openai/_client.py | 2 +- src/openai/_constants.py | 2 +- src/openai/_exceptions.py | 2 +- src/openai/_models.py | 8 +- src/openai/_module_client.py | 2 +- src/openai/_resource.py | 2 +- src/openai/_version.py | 4 +- src/openai/pagination.py | 2 +- src/openai/resources/__init__.py | 2 +- src/openai/resources/audio/__init__.py | 2 +- src/openai/resources/audio/audio.py | 2 +- src/openai/resources/audio/speech.py | 2 +- src/openai/resources/audio/transcriptions.py | 2 +- src/openai/resources/audio/translations.py | 2 +- src/openai/resources/beta/__init__.py | 2 +- .../resources/beta/assistants/__init__.py | 2 +- .../resources/beta/assistants/assistants.py | 2 +- src/openai/resources/beta/assistants/files.py | 2 +- src/openai/resources/beta/beta.py | 2 +- src/openai/resources/beta/threads/__init__.py | 2 +- .../beta/threads/messages/__init__.py | 2 +- .../resources/beta/threads/messages/files.py | 2 +- .../beta/threads/messages/messages.py | 2 +- .../resources/beta/threads/runs/__init__.py | 2 +- .../resources/beta/threads/runs/runs.py | 2 +- .../resources/beta/threads/runs/steps.py | 2 +- src/openai/resources/beta/threads/threads.py | 2 +- src/openai/resources/chat/__init__.py | 2 +- src/openai/resources/chat/chat.py | 2 +- src/openai/resources/chat/completions.py | 2 +- src/openai/resources/completions.py | 2 +- src/openai/resources/embeddings.py | 2 +- src/openai/resources/files.py | 2 +- src/openai/resources/fine_tuning/__init__.py | 2 +- .../resources/fine_tuning/fine_tuning.py | 2 +- src/openai/resources/fine_tuning/jobs.py | 2 +- src/openai/resources/images.py | 2 +- src/openai/resources/models.py | 2 +- src/openai/resources/moderations.py | 2 +- src/openai/types/__init__.py | 2 +- src/openai/types/audio/__init__.py | 2 +- .../types/audio/speech_create_params.py | 2 +- src/openai/types/audio/transcription.py | 2 +- .../audio/transcription_create_params.py | 2 +- src/openai/types/audio/translation.py | 2 +- .../types/audio/translation_create_params.py | 2 +- src/openai/types/beta/__init__.py | 2 +- src/openai/types/beta/assistant.py | 2 +- .../types/beta/assistant_create_params.py | 2 +- src/openai/types/beta/assistant_deleted.py | 2 +- .../types/beta/assistant_list_params.py | 2 +- .../types/beta/assistant_stream_event.py | 2 +- src/openai/types/beta/assistant_tool.py | 2 +- src/openai/types/beta/assistant_tool_param.py | 2 +- .../types/beta/assistant_update_params.py | 2 +- src/openai/types/beta/assistants/__init__.py | 2 +- .../types/beta/assistants/assistant_file.py | 2 +- .../beta/assistants/file_create_params.py | 2 +- .../beta/assistants/file_delete_response.py | 2 +- .../types/beta/assistants/file_list_params.py | 2 +- src/openai/types/beta/chat/__init__.py | 2 +- .../types/beta/code_interpreter_tool.py | 2 +- .../types/beta/code_interpreter_tool_param.py | 2 +- src/openai/types/beta/function_tool.py | 2 +- src/openai/types/beta/function_tool_param.py | 2 +- src/openai/types/beta/retrieval_tool.py | 2 +- src/openai/types/beta/retrieval_tool_param.py | 2 +- src/openai/types/beta/thread.py | 2 +- .../beta/thread_create_and_run_params.py | 2 +- src/openai/types/beta/thread_create_params.py | 2 +- src/openai/types/beta/thread_deleted.py | 2 +- src/openai/types/beta/thread_update_params.py | 2 +- src/openai/types/beta/threads/__init__.py | 2 +- src/openai/types/beta/threads/annotation.py | 2 +- .../types/beta/threads/annotation_delta.py | 2 +- .../beta/threads/file_citation_annotation.py | 2 +- .../threads/file_citation_delta_annotation.py | 2 +- .../beta/threads/file_path_annotation.py | 2 +- .../threads/file_path_delta_annotation.py | 2 +- src/openai/types/beta/threads/image_file.py | 2 +- .../beta/threads/image_file_content_block.py | 2 +- .../types/beta/threads/image_file_delta.py | 2 +- .../beta/threads/image_file_delta_block.py | 2 +- src/openai/types/beta/threads/message.py | 2 +- .../types/beta/threads/message_content.py | 2 +- .../beta/threads/message_content_delta.py | 2 +- .../beta/threads/message_create_params.py | 2 +- .../types/beta/threads/message_delta.py | 2 +- .../types/beta/threads/message_delta_event.py | 2 +- .../types/beta/threads/message_list_params.py | 2 +- .../beta/threads/message_update_params.py | 2 +- .../types/beta/threads/messages/__init__.py | 2 +- .../beta/threads/messages/file_list_params.py | 2 +- .../beta/threads/messages/message_file.py | 2 +- .../required_action_function_tool_call.py | 2 +- src/openai/types/beta/threads/run.py | 2 +- .../types/beta/threads/run_create_params.py | 2 +- .../types/beta/threads/run_list_params.py | 2 +- src/openai/types/beta/threads/run_status.py | 2 +- .../threads/run_submit_tool_outputs_params.py | 2 +- .../types/beta/threads/run_update_params.py | 2 +- .../types/beta/threads/runs/__init__.py | 2 +- .../threads/runs/code_interpreter_logs.py | 2 +- .../runs/code_interpreter_output_image.py | 2 +- .../runs/code_interpreter_tool_call.py | 2 +- .../runs/code_interpreter_tool_call_delta.py | 2 +- .../beta/threads/runs/function_tool_call.py | 2 +- .../threads/runs/function_tool_call_delta.py | 2 +- .../runs/message_creation_step_details.py | 2 +- .../beta/threads/runs/retrieval_tool_call.py | 2 +- .../threads/runs/retrieval_tool_call_delta.py | 2 +- .../types/beta/threads/runs/run_step.py | 2 +- .../types/beta/threads/runs/run_step_delta.py | 2 +- .../beta/threads/runs/run_step_delta_event.py | 2 +- .../runs/run_step_delta_message_delta.py | 2 +- .../beta/threads/runs/step_list_params.py | 2 +- .../types/beta/threads/runs/tool_call.py | 2 +- .../beta/threads/runs/tool_call_delta.py | 2 +- .../threads/runs/tool_call_delta_object.py | 2 +- .../threads/runs/tool_calls_step_details.py | 2 +- src/openai/types/beta/threads/text.py | 2 +- .../types/beta/threads/text_content_block.py | 2 +- src/openai/types/beta/threads/text_delta.py | 2 +- .../types/beta/threads/text_delta_block.py | 2 +- src/openai/types/chat/__init__.py | 2 +- src/openai/types/chat/chat_completion.py | 2 +- ...chat_completion_assistant_message_param.py | 2 +- .../types/chat/chat_completion_chunk.py | 2 +- ...hat_completion_content_part_image_param.py | 2 +- .../chat_completion_content_part_param.py | 2 +- ...chat_completion_content_part_text_param.py | 2 +- ...t_completion_function_call_option_param.py | 2 +- .../chat_completion_function_message_param.py | 2 +- .../types/chat/chat_completion_message.py | 2 +- .../chat/chat_completion_message_param.py | 2 +- .../chat/chat_completion_message_tool_call.py | 2 +- ...chat_completion_message_tool_call_param.py | 2 +- ...chat_completion_named_tool_choice_param.py | 2 +- src/openai/types/chat/chat_completion_role.py | 2 +- .../chat_completion_system_message_param.py | 2 +- .../chat/chat_completion_token_logprob.py | 2 +- ...hat_completion_tool_choice_option_param.py | 2 +- .../chat_completion_tool_message_param.py | 2 +- .../types/chat/chat_completion_tool_param.py | 2 +- .../chat_completion_user_message_param.py | 2 +- .../types/chat/completion_create_params.py | 2 +- src/openai/types/completion.py | 2 +- src/openai/types/completion_choice.py | 2 +- src/openai/types/completion_create_params.py | 2 +- src/openai/types/completion_usage.py | 2 +- src/openai/types/create_embedding_response.py | 2 +- src/openai/types/embedding.py | 2 +- src/openai/types/embedding_create_params.py | 2 +- src/openai/types/file_content.py | 2 +- src/openai/types/file_create_params.py | 2 +- src/openai/types/file_deleted.py | 2 +- src/openai/types/file_list_params.py | 2 +- src/openai/types/file_object.py | 2 +- src/openai/types/fine_tuning/__init__.py | 2 +- .../types/fine_tuning/fine_tuning_job.py | 2 +- .../fine_tuning/fine_tuning_job_event.py | 2 +- .../types/fine_tuning/job_create_params.py | 2 +- .../fine_tuning/job_list_events_params.py | 2 +- .../types/fine_tuning/job_list_params.py | 2 +- src/openai/types/image.py | 2 +- .../types/image_create_variation_params.py | 2 +- src/openai/types/image_edit_params.py | 2 +- src/openai/types/image_generate_params.py | 2 +- src/openai/types/images_response.py | 2 +- src/openai/types/model.py | 2 +- src/openai/types/model_deleted.py | 2 +- src/openai/types/moderation.py | 2 +- src/openai/types/moderation_create_params.py | 2 +- .../types/moderation_create_response.py | 2 +- src/openai/types/shared/__init__.py | 2 +- src/openai/types/shared/error_object.py | 2 +- .../types/shared/function_definition.py | 2 +- .../types/shared/function_parameters.py | 2 +- src/openai/types/shared_params/__init__.py | 2 +- .../shared_params/function_definition.py | 2 +- .../shared_params/function_parameters.py | 2 +- tests/__init__.py | 2 +- tests/api_resources/__init__.py | 2 +- tests/api_resources/audio/__init__.py | 2 +- tests/api_resources/audio/test_speech.py | 2 +- .../audio/test_transcriptions.py | 2 +- .../api_resources/audio/test_translations.py | 2 +- tests/api_resources/beta/__init__.py | 2 +- .../api_resources/beta/assistants/__init__.py | 2 +- .../beta/assistants/test_files.py | 2 +- tests/api_resources/beta/chat/__init__.py | 2 +- tests/api_resources/beta/test_assistants.py | 2 +- tests/api_resources/beta/test_threads.py | 2 +- tests/api_resources/beta/threads/__init__.py | 2 +- .../beta/threads/messages/__init__.py | 2 +- .../beta/threads/messages/test_files.py | 2 +- .../beta/threads/runs/__init__.py | 2 +- .../beta/threads/runs/test_steps.py | 2 +- .../beta/threads/test_messages.py | 2 +- tests/api_resources/beta/threads/test_runs.py | 2 +- tests/api_resources/chat/__init__.py | 2 +- tests/api_resources/chat/test_completions.py | 2 +- tests/api_resources/fine_tuning/__init__.py | 2 +- tests/api_resources/fine_tuning/test_jobs.py | 2 +- tests/api_resources/test_completions.py | 2 +- tests/api_resources/test_embeddings.py | 2 +- tests/api_resources/test_files.py | 2 +- tests/api_resources/test_images.py | 2 +- tests/api_resources/test_models.py | 2 +- tests/api_resources/test_moderations.py | 2 +- tests/test_client.py | 2 +- tests/test_module_client.py | 2 +- 219 files changed, 331 insertions(+), 233 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a780111df4..19cc6edce7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.14.1" + ".": "1.14.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f7a80d39a6..7497d6af56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 1.14.2 (2024-03-19) + +Full Changelog: [v1.14.1...v1.14.2](https://github.com/openai/openai-python/compare/v1.14.1...v1.14.2) + +### Performance Improvements + +* cache TypeAdapters ([#1114](https://github.com/openai/openai-python/issues/1114)) ([41b6fee](https://github.com/openai/openai-python/commit/41b6feec70d3f203e36ba9a92205389bafce930c)) +* cache TypeAdapters ([#1243](https://github.com/openai/openai-python/issues/1243)) ([2005076](https://github.com/openai/openai-python/commit/2005076f500bef6e0a6cc8f935b9cc9fef65ab5b)) + + +### Chores + +* **internal:** update generated pragma comment ([#1247](https://github.com/openai/openai-python/issues/1247)) ([3eeb9b3](https://github.com/openai/openai-python/commit/3eeb9b3a71e01c2593be443a97a353371466d01a)) + + +### Documentation + +* assistant improvements ([#1249](https://github.com/openai/openai-python/issues/1249)) ([e7a3176](https://github.com/openai/openai-python/commit/e7a3176b7606822bd5ad8f7fece87de6aad1e5b6)) +* fix typo in CONTRIBUTING.md ([#1245](https://github.com/openai/openai-python/issues/1245)) ([adef57a](https://github.com/openai/openai-python/commit/adef57ae5c71734873ba49bccd92fa7f28068d28)) + ## 1.14.1 (2024-03-15) Full Changelog: [v1.14.0...v1.14.1](https://github.com/openai/openai-python/compare/v1.14.0...v1.14.1) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ab73dbf4c..7473159258 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,7 +86,7 @@ Most tests require you to [set up a mock server](https://github.com/stoplightio/ ```bash # you will need npm installed -npx prism path/to/your/openapi.yml +npx prism mock path/to/your/openapi.yml ``` ```bash diff --git a/README.md b/README.md index 7d6c896d50..befe927cea 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,24 @@ we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) to add `OPENAI_API_KEY="My API Key"` to your `.env` file so that your API Key is not stored in source control. +### Streaming Helpers + +The SDK also includes helpers to process streams and handle the incoming events. + +```python +with client.beta.threads.runs.create_and_stream( + thread_id=thread.id, + assistant_id=assistant.id, + instructions="Please address the user as Jane Doe. The user has a premium account.", +) as stream: + for event in stream: + # Print the text from text delta events + if event.type == "thread.message.delta" and event.data.delta.content: + print(event.data.delta.content[0].text) +``` + +More information on streaming helpers can be found in the dedicated documentation: [helpers.md](helpers.md) + ## Async usage Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call: diff --git a/helpers.md b/helpers.md index 03fd5e76b7..fed20ee81c 100644 --- a/helpers.md +++ b/helpers.md @@ -15,24 +15,28 @@ You can subscribe to events by creating an event handler class and overloading t ```python from typing_extensions import override -from openai import AssistantEventHandler +from openai import AssistantEventHandler, OpenAI +from openai.types.beta.threads import Text, TextDelta +from openai.types.beta.threads.runs import ToolCall, ToolCallDelta + +client = openai.OpenAI() # First, we create a EventHandler class to define # how we want to handle the events in the response stream. class EventHandler(AssistantEventHandler): @override - def on_text_created(self, text) -> None: + def on_text_created(self, text: Text) -> None: print(f"\nassistant > ", end="", flush=True) @override - def on_text_delta(self, delta, snapshot): + def on_text_delta(self, delta: TextDelta, snapshot: Text): print(delta.value, end="", flush=True) - def on_tool_call_created(self, tool_call): + def on_tool_call_created(self, tool_call: ToolCall): print(f"\nassistant > {tool_call.type}\n", flush=True) - def on_tool_call_delta(self, delta, snapshot): + def on_tool_call_delta(self, delta: ToolCallDelta, snapshot: ToolCall): if delta.type == 'code_interpreter': if delta.code_interpreter.input: print(delta.code_interpreter.input, end="", flush=True) @@ -47,14 +51,64 @@ class EventHandler(AssistantEventHandler): # and stream the response. with client.beta.threads.runs.create_and_stream( - thread_id=thread.id, - assistant_id=assistant.id, - instructions="Please address the user as Jane Doe. The user has a premium account.", + thread_id="thread_id", + assistant_id="assistant_id", event_handler=EventHandler(), ) as stream: stream.until_done() ``` +#### An example of iterating over events + +You can also iterate over all the streamed events. + +```python +with client.beta.threads.runs.create_and_stream( + thread_id=thread.id, + assistant_id=assistant.id +) as stream: + for event in stream: + # Print the text from text delta events + if event.type == "thread.message.delta" and event.data.delta.content: + print(event.data.delta.content[0].text) +``` + +#### An example of iterating over text + +You can also iterate over just the text deltas received + +```python +with client.beta.threads.runs.create_and_stream( + thread_id=thread.id, + assistant_id=assistant.id +) as stream: + for text in stream.text_deltas: + print(text) +``` + +### Creating Streams + +There are three helper methods for creating streams: + +```python +client.beta.threads.runs.create_and_stream() +``` + +This method can be used to start and stream the response to an existing run with an associated thread +that is already populated with messages. + +```python +client.beta.threads.create_and_run_stream() +``` + +This method can be used to add a message to a thread, start a run and then stream the response. + +```python +client.beta.threads.runs.submit_tool_outputs_stream() +``` + +This method can be used to submit a tool output to a run waiting on the output and start a stream. + ### Assistant Events The assistant API provides events you can subscribe to for the following events. @@ -139,22 +193,22 @@ This event is triggered if an exception occurs during streaming. The assistant streaming object also provides a few methods for convenience: ```python -def current_event() -def current_run() -def current_message_snapshot() -def current_run_step_snapshot() +def current_event() -> AssistantStreamEvent | None +def current_run() -> Run | None +def current_message_snapshot() -> Message | None +def current_run_step_snapshot() -> RunStep | None ``` These methods are provided to allow you to access additional context from within event handlers. In many cases the handlers should include all the information you need for processing, but if additional context is required it can be accessed. -Note: There is not always a relevant context in certain situations (these will be undefined in those cases). +Note: There is not always a relevant context in certain situations (these will be `None` in those cases). ```python -def get_final_run(self) -def get_final_run_steps(self) -def get_final_messages(self) +def get_final_run(self) -> Run +def get_final_run_steps(self) -> List[RunStep] +def get_final_messages(self) -> List[Message] ``` These methods are provided for convenience to collect information at the end of a stream. Calling these events diff --git a/pyproject.toml b/pyproject.toml index d562977dbd..de412f3907 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.14.1" +version = "1.14.2" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 909be95c97..9585fde99b 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/_client.py b/src/openai/_client.py index 5043d60e2a..7fe2c9af79 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/_constants.py b/src/openai/_constants.py index dffb8ecfb6..b2e541f7b1 100644 --- a/src/openai/_constants.py +++ b/src/openai/_constants.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import httpx diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index d7ded1248f..350fd2584b 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/_models.py b/src/openai/_models.py index 88afa40810..166973538f 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -3,6 +3,7 @@ import inspect from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast from datetime import date, datetime +from functools import lru_cache from typing_extensions import ( Unpack, Literal, @@ -533,7 +534,12 @@ class GenericModel(BaseGenericModel, BaseModel): if PYDANTIC_V2: - from pydantic import TypeAdapter + if TYPE_CHECKING: + from pydantic import TypeAdapter + else: + from pydantic import TypeAdapter as _TypeAdapter + + TypeAdapter = lru_cache(_TypeAdapter) def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: return TypeAdapter(type_).validate_python(value) diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index d66e137ecd..9227f5e2b4 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import override diff --git a/src/openai/_resource.py b/src/openai/_resource.py index 0b0703bb72..fff9ba19c3 100644 --- a/src/openai/_resource.py +++ b/src/openai/_resource.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/_version.py b/src/openai/_version.py index 3f5331b8e0..b8eb743acc 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.14.1" # x-release-please-version +__version__ = "1.14.2" # x-release-please-version diff --git a/src/openai/pagination.py b/src/openai/pagination.py index f7527753e1..8293638269 100644 --- a/src/openai/pagination.py +++ b/src/openai/pagination.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Any, List, Generic, TypeVar, Optional, cast from typing_extensions import Protocol, override, runtime_checkable diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index 1fb4aa62ec..64aa12d260 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .beta import ( Beta, diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py index 63d06494b8..7da1d2dbde 100644 --- a/src/openai/resources/audio/__init__.py +++ b/src/openai/resources/audio/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .audio import ( Audio, diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index bafacf4422..537ad573d0 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index bf4a0245f6..e26c58051e 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index cfd9aae909..353f28ab05 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 6063522237..79020a5ece 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 973c6ba54e..87fea25267 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .beta import ( Beta, diff --git a/src/openai/resources/beta/assistants/__init__.py b/src/openai/resources/beta/assistants/__init__.py index ad04a71572..736def9388 100644 --- a/src/openai/resources/beta/assistants/__init__.py +++ b/src/openai/resources/beta/assistants/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .files import ( Files, diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 4698deec48..232451ab25 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/assistants/files.py index 8d5657666c..dc57dfb96c 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/assistants/files.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 7081cff305..67baad2716 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/beta/threads/__init__.py b/src/openai/resources/beta/threads/__init__.py index 886574b327..a66e445b1f 100644 --- a/src/openai/resources/beta/threads/__init__.py +++ b/src/openai/resources/beta/threads/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .runs import ( Runs, diff --git a/src/openai/resources/beta/threads/messages/__init__.py b/src/openai/resources/beta/threads/messages/__init__.py index 0acb0ab201..a3286e6ace 100644 --- a/src/openai/resources/beta/threads/messages/__init__.py +++ b/src/openai/resources/beta/threads/messages/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .files import ( Files, diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py index fc8b894d72..349f99725e 100644 --- a/src/openai/resources/beta/threads/messages/files.py +++ b/src/openai/resources/beta/threads/messages/files.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 600d9a72ea..21e8bca5b8 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/beta/threads/runs/__init__.py b/src/openai/resources/beta/threads/runs/__init__.py index 659c96acfb..50aa9fae60 100644 --- a/src/openai/resources/beta/threads/runs/__init__.py +++ b/src/openai/resources/beta/threads/runs/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .runs import ( Runs, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index c5e9474002..afa447612c 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 539745a594..118bd8822a 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 17afe285cc..bcb0da8a62 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/chat/__init__.py b/src/openai/resources/chat/__init__.py index a9668053c0..52dfdceacc 100644 --- a/src/openai/resources/chat/__init__.py +++ b/src/openai/resources/chat/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .chat import ( Chat, diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index b6effa4e63..d14d055506 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index abe466ef77..3000603689 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 8a2bad5fda..db87c83ca2 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index cfef025bc2..a083b6269a 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 3ea66656b3..33860adad5 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py index ab0c28ef4b..7765231fee 100644 --- a/src/openai/resources/fine_tuning/__init__.py +++ b/src/openai/resources/fine_tuning/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .jobs import ( Jobs, diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index 33b25baec9..659b3e8501 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs.py index 8338de12c4..a0c3e24dac 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index f5bbdbc338..e12fa51bd9 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index 3536f083d2..4e36e20801 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index ac5ca1b64b..385b672f28 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index e536d0b5a7..0917e22a8f 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index ba5f7fd8e0..8d2c44c86a 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 0078a9d03a..8d75ec4ccc 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index 6532611731..fa512e27f9 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index 4164a594cc..6b2d5bae79 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/audio/translation.py b/src/openai/types/audio/translation.py index a01d622abc..efc56f7f9b 100644 --- a/src/openai/types/audio/translation.py +++ b/src/openai/types/audio/translation.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py index 1ae312da49..f23a41ed5c 100644 --- a/src/openai/types/audio/translation_create_params.py +++ b/src/openai/types/audio/translation_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index 714b3e159d..a7de0272b4 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 31b847d72c..32561a9aa8 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 0e39619a9c..8bad323640 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/assistant_deleted.py b/src/openai/types/beta/assistant_deleted.py index 23802caaf6..3be40cd6b8 100644 --- a/src/openai/types/beta/assistant_deleted.py +++ b/src/openai/types/beta/assistant_deleted.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/assistant_list_params.py b/src/openai/types/beta/assistant_list_params.py index b2d794a43a..f54f63120b 100644 --- a/src/openai/types/beta/assistant_list_params.py +++ b/src/openai/types/beta/assistant_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py index ca7f814a8a..90471f7daa 100644 --- a/src/openai/types/beta/assistant_stream_event.py +++ b/src/openai/types/beta/assistant_stream_event.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated diff --git a/src/openai/types/beta/assistant_tool.py b/src/openai/types/beta/assistant_tool.py index 9e589eae7a..a4420385e8 100644 --- a/src/openai/types/beta/assistant_tool.py +++ b/src/openai/types/beta/assistant_tool.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated diff --git a/src/openai/types/beta/assistant_tool_param.py b/src/openai/types/beta/assistant_tool_param.py index 02b56a8c5d..d5758f169e 100644 --- a/src/openai/types/beta/assistant_tool_param.py +++ b/src/openai/types/beta/assistant_tool_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index fbff50f444..7c96aca8c1 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/assistants/__init__.py b/src/openai/types/beta/assistants/__init__.py index 9dbb3e2b8b..d4dd2de018 100644 --- a/src/openai/types/beta/assistants/__init__.py +++ b/src/openai/types/beta/assistants/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/assistants/assistant_file.py b/src/openai/types/beta/assistants/assistant_file.py index 1d1573ac0f..25aec07b49 100644 --- a/src/openai/types/beta/assistants/assistant_file.py +++ b/src/openai/types/beta/assistants/assistant_file.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/assistants/file_create_params.py b/src/openai/types/beta/assistants/file_create_params.py index f70f96fc1b..55f0e8cda1 100644 --- a/src/openai/types/beta/assistants/file_create_params.py +++ b/src/openai/types/beta/assistants/file_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/assistants/file_delete_response.py b/src/openai/types/beta/assistants/file_delete_response.py index 52c138feda..685fb2a75c 100644 --- a/src/openai/types/beta/assistants/file_delete_response.py +++ b/src/openai/types/beta/assistants/file_delete_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/assistants/file_list_params.py b/src/openai/types/beta/assistants/file_list_params.py index 397e35a0d1..53c493b36a 100644 --- a/src/openai/types/beta/assistants/file_list_params.py +++ b/src/openai/types/beta/assistants/file_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/chat/__init__.py b/src/openai/types/beta/chat/__init__.py index b2f53e3525..f8ee8b14b1 100644 --- a/src/openai/types/beta/chat/__init__.py +++ b/src/openai/types/beta/chat/__init__.py @@ -1,3 +1,3 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/code_interpreter_tool.py b/src/openai/types/beta/code_interpreter_tool.py index 4964047ba7..17ab3de629 100644 --- a/src/openai/types/beta/code_interpreter_tool.py +++ b/src/openai/types/beta/code_interpreter_tool.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/code_interpreter_tool_param.py b/src/openai/types/beta/code_interpreter_tool_param.py index 92d6e02dbc..4f6916d756 100644 --- a/src/openai/types/beta/code_interpreter_tool_param.py +++ b/src/openai/types/beta/code_interpreter_tool_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/function_tool.py b/src/openai/types/beta/function_tool.py index fa0ab3b83e..5d278e7487 100644 --- a/src/openai/types/beta/function_tool.py +++ b/src/openai/types/beta/function_tool.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/function_tool_param.py b/src/openai/types/beta/function_tool_param.py index e631d69e20..b44c0d47ef 100644 --- a/src/openai/types/beta/function_tool_param.py +++ b/src/openai/types/beta/function_tool_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/retrieval_tool.py b/src/openai/types/beta/retrieval_tool.py index 17d5bea130..b07b785c66 100644 --- a/src/openai/types/beta/retrieval_tool.py +++ b/src/openai/types/beta/retrieval_tool.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/retrieval_tool_param.py b/src/openai/types/beta/retrieval_tool_param.py index 6f803e4672..d76c0beefc 100644 --- a/src/openai/types/beta/retrieval_tool_param.py +++ b/src/openai/types/beta/retrieval_tool_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py index a0002a21ef..8fd1423068 100644 --- a/src/openai/types/beta/thread.py +++ b/src/openai/types/beta/thread.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 5078639e6a..9c16e1133f 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index e78276e839..b3dda503ff 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/thread_deleted.py b/src/openai/types/beta/thread_deleted.py index 410ac1aea0..d385626319 100644 --- a/src/openai/types/beta/thread_deleted.py +++ b/src/openai/types/beta/thread_deleted.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py index 6c1d32fc57..94f1b1e22e 100644 --- a/src/openai/types/beta/thread_update_params.py +++ b/src/openai/types/beta/thread_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index ff45871afe..b57ebccb3a 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/annotation.py b/src/openai/types/beta/threads/annotation.py index 86a2115233..31e228c831 100644 --- a/src/openai/types/beta/threads/annotation.py +++ b/src/openai/types/beta/threads/annotation.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated diff --git a/src/openai/types/beta/threads/annotation_delta.py b/src/openai/types/beta/threads/annotation_delta.py index fdcc67c3ff..912429672f 100644 --- a/src/openai/types/beta/threads/annotation_delta.py +++ b/src/openai/types/beta/threads/annotation_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated diff --git a/src/openai/types/beta/threads/file_citation_annotation.py b/src/openai/types/beta/threads/file_citation_annotation.py index da63938d93..68571cd477 100644 --- a/src/openai/types/beta/threads/file_citation_annotation.py +++ b/src/openai/types/beta/threads/file_citation_annotation.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/file_citation_delta_annotation.py b/src/openai/types/beta/threads/file_citation_delta_annotation.py index 3b4c5950d4..b40c0d123e 100644 --- a/src/openai/types/beta/threads/file_citation_delta_annotation.py +++ b/src/openai/types/beta/threads/file_citation_delta_annotation.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/file_path_annotation.py b/src/openai/types/beta/threads/file_path_annotation.py index 2d9cf58184..9812737ece 100644 --- a/src/openai/types/beta/threads/file_path_annotation.py +++ b/src/openai/types/beta/threads/file_path_annotation.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/file_path_delta_annotation.py b/src/openai/types/beta/threads/file_path_delta_annotation.py index 6d89748d2c..0cbb445e48 100644 --- a/src/openai/types/beta/threads/file_path_delta_annotation.py +++ b/src/openai/types/beta/threads/file_path_delta_annotation.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/image_file.py b/src/openai/types/beta/threads/image_file.py index 371055627c..db0d6e823a 100644 --- a/src/openai/types/beta/threads/image_file.py +++ b/src/openai/types/beta/threads/image_file.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel diff --git a/src/openai/types/beta/threads/image_file_content_block.py b/src/openai/types/beta/threads/image_file_content_block.py index 3baf8b884b..a909999065 100644 --- a/src/openai/types/beta/threads/image_file_content_block.py +++ b/src/openai/types/beta/threads/image_file_content_block.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/image_file_delta.py b/src/openai/types/beta/threads/image_file_delta.py index 2bda05f82b..b0b1d32fa2 100644 --- a/src/openai/types/beta/threads/image_file_delta.py +++ b/src/openai/types/beta/threads/image_file_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/openai/types/beta/threads/image_file_delta_block.py b/src/openai/types/beta/threads/image_file_delta_block.py index 97cc1c4608..0a5a2e8a5f 100644 --- a/src/openai/types/beta/threads/image_file_delta_block.py +++ b/src/openai/types/beta/threads/image_file_delta_block.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index 4f307928be..027e2bfa15 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/message_content.py b/src/openai/types/beta/threads/message_content.py index 7da6a81fb6..bc79b39fd4 100644 --- a/src/openai/types/beta/threads/message_content.py +++ b/src/openai/types/beta/threads/message_content.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated diff --git a/src/openai/types/beta/threads/message_content_delta.py b/src/openai/types/beta/threads/message_content_delta.py index 7a8266d02f..3cbc22c94b 100644 --- a/src/openai/types/beta/threads/message_content_delta.py +++ b/src/openai/types/beta/threads/message_content_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index 8733f10b8a..b2f27deb3e 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/message_delta.py b/src/openai/types/beta/threads/message_delta.py index 1113cc27fb..3a55e1442a 100644 --- a/src/openai/types/beta/threads/message_delta.py +++ b/src/openai/types/beta/threads/message_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/message_delta_event.py b/src/openai/types/beta/threads/message_delta_event.py index 07a9107a34..3811cef679 100644 --- a/src/openai/types/beta/threads/message_delta_event.py +++ b/src/openai/types/beta/threads/message_delta_event.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/message_list_params.py b/src/openai/types/beta/threads/message_list_params.py index 31e407bb22..8b139caa93 100644 --- a/src/openai/types/beta/threads/message_list_params.py +++ b/src/openai/types/beta/threads/message_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/message_update_params.py b/src/openai/types/beta/threads/message_update_params.py index 2e3e1b4b1a..7000f33122 100644 --- a/src/openai/types/beta/threads/message_update_params.py +++ b/src/openai/types/beta/threads/message_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/messages/__init__.py b/src/openai/types/beta/threads/messages/__init__.py index 6046f68204..d129297620 100644 --- a/src/openai/types/beta/threads/messages/__init__.py +++ b/src/openai/types/beta/threads/messages/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/messages/file_list_params.py b/src/openai/types/beta/threads/messages/file_list_params.py index 3640b8508b..7e2d6136ec 100644 --- a/src/openai/types/beta/threads/messages/file_list_params.py +++ b/src/openai/types/beta/threads/messages/file_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/messages/message_file.py b/src/openai/types/beta/threads/messages/message_file.py index 5332dee962..342479ab7b 100644 --- a/src/openai/types/beta/threads/messages/message_file.py +++ b/src/openai/types/beta/threads/messages/message_file.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/required_action_function_tool_call.py b/src/openai/types/beta/threads/required_action_function_tool_call.py index 0284d0f188..a24dfd068b 100644 --- a/src/openai/types/beta/threads/required_action_function_tool_call.py +++ b/src/openai/types/beta/threads/required_action_function_tool_call.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index dd2842c584..d2cac4c279 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index c012390f5c..89dff389a9 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/run_list_params.py b/src/openai/types/beta/threads/run_list_params.py index 5f41347718..1e32bca4b4 100644 --- a/src/openai/types/beta/threads/run_list_params.py +++ b/src/openai/types/beta/threads/run_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/run_status.py b/src/openai/types/beta/threads/run_status.py index 587e3d7810..bf9b4e7bbf 100644 --- a/src/openai/types/beta/threads/run_status.py +++ b/src/openai/types/beta/threads/run_status.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py index 49e1ac49ab..ccb5e5e97e 100644 --- a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py +++ b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/run_update_params.py b/src/openai/types/beta/threads/run_update_params.py index 09f81aa003..e595eac882 100644 --- a/src/openai/types/beta/threads/run_update_params.py +++ b/src/openai/types/beta/threads/run_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py index 03ae192088..256510dcc7 100644 --- a/src/openai/types/beta/threads/runs/__init__.py +++ b/src/openai/types/beta/threads/runs/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/runs/code_interpreter_logs.py b/src/openai/types/beta/threads/runs/code_interpreter_logs.py index c91179be22..0bf8c1dac2 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_logs.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_logs.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/code_interpreter_output_image.py b/src/openai/types/beta/threads/runs/code_interpreter_output_image.py index 0d7d26f91f..2257f37e41 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_output_image.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_output_image.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py index c537562e91..2f07243684 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union from typing_extensions import Literal, Annotated diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py index b13105f840..eff76355b3 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from typing_extensions import Literal, Annotated diff --git a/src/openai/types/beta/threads/runs/function_tool_call.py b/src/openai/types/beta/threads/runs/function_tool_call.py index bbd3cb7052..b1d354f894 100644 --- a/src/openai/types/beta/threads/runs/function_tool_call.py +++ b/src/openai/types/beta/threads/runs/function_tool_call.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/function_tool_call_delta.py b/src/openai/types/beta/threads/runs/function_tool_call_delta.py index 46c341bc84..faaf026f7f 100644 --- a/src/openai/types/beta/threads/runs/function_tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/function_tool_call_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/message_creation_step_details.py b/src/openai/types/beta/threads/runs/message_creation_step_details.py index 13f9398515..73439079d3 100644 --- a/src/openai/types/beta/threads/runs/message_creation_step_details.py +++ b/src/openai/types/beta/threads/runs/message_creation_step_details.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/retrieval_tool_call.py b/src/openai/types/beta/threads/runs/retrieval_tool_call.py index 6cdbcdd93f..48704ed331 100644 --- a/src/openai/types/beta/threads/runs/retrieval_tool_call.py +++ b/src/openai/types/beta/threads/runs/retrieval_tool_call.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py b/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py index ac8003d3eb..3310079399 100644 --- a/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 899883ac2d..7c81dcac2b 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated diff --git a/src/openai/types/beta/threads/runs/run_step_delta.py b/src/openai/types/beta/threads/runs/run_step_delta.py index fb8b869425..d6b4aefeb9 100644 --- a/src/openai/types/beta/threads/runs/run_step_delta.py +++ b/src/openai/types/beta/threads/runs/run_step_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Annotated diff --git a/src/openai/types/beta/threads/runs/run_step_delta_event.py b/src/openai/types/beta/threads/runs/run_step_delta_event.py index ab61dd1f9a..7f3f92aabf 100644 --- a/src/openai/types/beta/threads/runs/run_step_delta_event.py +++ b/src/openai/types/beta/threads/runs/run_step_delta_event.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py b/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py index 52ec5d3440..f58ed3d96d 100644 --- a/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py +++ b/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/step_list_params.py b/src/openai/types/beta/threads/runs/step_list_params.py index 9c7b6c64d0..606d444539 100644 --- a/src/openai/types/beta/threads/runs/step_list_params.py +++ b/src/openai/types/beta/threads/runs/step_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/beta/threads/runs/tool_call.py b/src/openai/types/beta/threads/runs/tool_call.py index a3abfa77ad..dcca797bf0 100644 --- a/src/openai/types/beta/threads/runs/tool_call.py +++ b/src/openai/types/beta/threads/runs/tool_call.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated diff --git a/src/openai/types/beta/threads/runs/tool_call_delta.py b/src/openai/types/beta/threads/runs/tool_call_delta.py index a1aa4de6cf..fc98981abf 100644 --- a/src/openai/types/beta/threads/runs/tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/tool_call_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated diff --git a/src/openai/types/beta/threads/runs/tool_call_delta_object.py b/src/openai/types/beta/threads/runs/tool_call_delta_object.py index 2ce46ab894..9cd59a6e24 100644 --- a/src/openai/types/beta/threads/runs/tool_call_delta_object.py +++ b/src/openai/types/beta/threads/runs/tool_call_delta_object.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/runs/tool_calls_step_details.py b/src/openai/types/beta/threads/runs/tool_calls_step_details.py index 6fccfc563a..ca08fabd0e 100644 --- a/src/openai/types/beta/threads/runs/tool_calls_step_details.py +++ b/src/openai/types/beta/threads/runs/tool_calls_step_details.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/text.py b/src/openai/types/beta/threads/text.py index a5a31c6783..853bec2955 100644 --- a/src/openai/types/beta/threads/text.py +++ b/src/openai/types/beta/threads/text.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List diff --git a/src/openai/types/beta/threads/text_content_block.py b/src/openai/types/beta/threads/text_content_block.py index 1c9187ea60..3706d6b9d8 100644 --- a/src/openai/types/beta/threads/text_content_block.py +++ b/src/openai/types/beta/threads/text_content_block.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/beta/threads/text_delta.py b/src/openai/types/beta/threads/text_delta.py index 735846472a..09cd357027 100644 --- a/src/openai/types/beta/threads/text_delta.py +++ b/src/openai/types/beta/threads/text_delta.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional diff --git a/src/openai/types/beta/threads/text_delta_block.py b/src/openai/types/beta/threads/text_delta_block.py index 6adbdee479..586116e0d6 100644 --- a/src/openai/types/beta/threads/text_delta_block.py +++ b/src/openai/types/beta/threads/text_delta_block.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 39a6335f64..5d122d2020 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index dc63d84945..61a94a258e 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 7377139bf5..e1e399486e 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 95013e7a4f..c2f18bcb74 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py index e6732185ef..b1a186aa6d 100644 --- a/src/openai/types/chat/chat_completion_content_part_image_param.py +++ b/src/openai/types/chat/chat_completion_content_part_image_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index 8e58239258..f9b5f71e43 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_content_part_text_param.py b/src/openai/types/chat/chat_completion_content_part_text_param.py index 38edcf054e..a270744417 100644 --- a/src/openai/types/chat/chat_completion_content_part_text_param.py +++ b/src/openai/types/chat/chat_completion_content_part_text_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_function_call_option_param.py b/src/openai/types/chat/chat_completion_function_call_option_param.py index 72d41d908c..2bc014af7a 100644 --- a/src/openai/types/chat/chat_completion_function_call_option_param.py +++ b/src/openai/types/chat/chat_completion_function_call_option_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_function_message_param.py b/src/openai/types/chat/chat_completion_function_message_param.py index 3f9a1a9039..5af12bf94f 100644 --- a/src/openai/types/chat/chat_completion_function_message_param.py +++ b/src/openai/types/chat/chat_completion_function_message_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py index da8b2fcd5c..8db7d17d24 100644 --- a/src/openai/types/chat/chat_completion_message.py +++ b/src/openai/types/chat/chat_completion_message.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/chat/chat_completion_message_param.py b/src/openai/types/chat/chat_completion_message_param.py index 7ec3d6a7b7..a3644a5310 100644 --- a/src/openai/types/chat/chat_completion_message_param.py +++ b/src/openai/types/chat/chat_completion_message_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py index 63c72fcdca..4fec667096 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_tool_call.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/chat/chat_completion_message_tool_call_param.py b/src/openai/types/chat/chat_completion_message_tool_call_param.py index a700f02c4f..f616c363d0 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call_param.py +++ b/src/openai/types/chat/chat_completion_message_tool_call_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_param.py index 0b5ffde37b..369f8b42dd 100644 --- a/src/openai/types/chat/chat_completion_named_tool_choice_param.py +++ b/src/openai/types/chat/chat_completion_named_tool_choice_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_role.py b/src/openai/types/chat/chat_completion_role.py index 9fa2acb4bb..1fd83888d3 100644 --- a/src/openai/types/chat/chat_completion_role.py +++ b/src/openai/types/chat/chat_completion_role.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/chat/chat_completion_system_message_param.py b/src/openai/types/chat/chat_completion_system_message_param.py index 6e862e75c7..94bb3f636c 100644 --- a/src/openai/types/chat/chat_completion_system_message_param.py +++ b/src/openai/types/chat/chat_completion_system_message_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_token_logprob.py b/src/openai/types/chat/chat_completion_token_logprob.py index 076ffb680c..c69e258910 100644 --- a/src/openai/types/chat/chat_completion_token_logprob.py +++ b/src/openai/types/chat/chat_completion_token_logprob.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional diff --git a/src/openai/types/chat/chat_completion_tool_choice_option_param.py b/src/openai/types/chat/chat_completion_tool_choice_option_param.py index 8104b26acb..9c0ae22528 100644 --- a/src/openai/types/chat/chat_completion_tool_choice_option_param.py +++ b/src/openai/types/chat/chat_completion_tool_choice_option_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_tool_message_param.py b/src/openai/types/chat/chat_completion_tool_message_param.py index 373c5b88f4..5c590e033f 100644 --- a/src/openai/types/chat/chat_completion_tool_message_param.py +++ b/src/openai/types/chat/chat_completion_tool_message_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py index 54c223955e..0cf6ea7268 100644 --- a/src/openai/types/chat/chat_completion_tool_param.py +++ b/src/openai/types/chat/chat_completion_tool_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/chat_completion_user_message_param.py b/src/openai/types/chat/chat_completion_user_message_param.py index cb8ca19bf0..5c15322a22 100644 --- a/src/openai/types/chat/chat_completion_user_message_param.py +++ b/src/openai/types/chat/chat_completion_user_message_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index e391c63119..ab6a747021 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/completion.py b/src/openai/types/completion.py index cd80498b16..d3b3102a4a 100644 --- a/src/openai/types/completion.py +++ b/src/openai/types/completion.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal diff --git a/src/openai/types/completion_choice.py b/src/openai/types/completion_choice.py index 7b08582bfd..d948ebc942 100644 --- a/src/openai/types/completion_choice.py +++ b/src/openai/types/completion_choice.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, List, Optional from typing_extensions import Literal diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index 08ffca760f..36267e9061 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index b825d5529f..e185a5cc38 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel diff --git a/src/openai/types/create_embedding_response.py b/src/openai/types/create_embedding_response.py index bf64037e16..eff247a112 100644 --- a/src/openai/types/create_embedding_response.py +++ b/src/openai/types/create_embedding_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal diff --git a/src/openai/types/embedding.py b/src/openai/types/embedding.py index 9c53704d5d..769b1d165f 100644 --- a/src/openai/types/embedding.py +++ b/src/openai/types/embedding.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py index a549dc94c4..930b3b7914 100644 --- a/src/openai/types/embedding_create_params.py +++ b/src/openai/types/embedding_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/file_content.py b/src/openai/types/file_content.py index 92b316b9eb..b4aa08a9a3 100644 --- a/src/openai/types/file_content.py +++ b/src/openai/types/file_content.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __all__ = ["FileContent"] diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index a59ddb2817..26e2da3372 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/file_deleted.py b/src/openai/types/file_deleted.py index 3ac8592ff6..f25fa87a8d 100644 --- a/src/openai/types/file_deleted.py +++ b/src/openai/types/file_deleted.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/file_list_params.py b/src/openai/types/file_list_params.py index a962dd239c..212eca13c0 100644 --- a/src/openai/types/file_list_params.py +++ b/src/openai/types/file_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py index 4ae91b754e..589a1faf38 100644 --- a/src/openai/types/file_object.py +++ b/src/openai/types/file_object.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal diff --git a/src/openai/types/fine_tuning/__init__.py b/src/openai/types/fine_tuning/__init__.py index d24160c5bd..0bb2b90438 100644 --- a/src/openai/types/fine_tuning/__init__.py +++ b/src/openai/types/fine_tuning/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index 5aa4f07eb1..23fe96d1a0 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from typing_extensions import Literal diff --git a/src/openai/types/fine_tuning/fine_tuning_job_event.py b/src/openai/types/fine_tuning/fine_tuning_job_event.py index 62f268868b..2d204bb980 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_event.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_event.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index da750ffc19..79e0b67e13 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/fine_tuning/job_list_events_params.py b/src/openai/types/fine_tuning/job_list_events_params.py index 7be3d53315..e1c9a64dc8 100644 --- a/src/openai/types/fine_tuning/job_list_events_params.py +++ b/src/openai/types/fine_tuning/job_list_events_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/fine_tuning/job_list_params.py b/src/openai/types/fine_tuning/job_list_params.py index 8160136901..5c075ca33f 100644 --- a/src/openai/types/fine_tuning/job_list_params.py +++ b/src/openai/types/fine_tuning/job_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/image.py b/src/openai/types/image.py index a040caf7b6..f48aa2c702 100644 --- a/src/openai/types/image.py +++ b/src/openai/types/image.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py index 5714f97fa9..2549307372 100644 --- a/src/openai/types/image_create_variation_params.py +++ b/src/openai/types/image_create_variation_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 751ec4fe7a..073456e349 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 3ff1b979db..18c56f8ed6 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/images_response.py b/src/openai/types/images_response.py index 9d1bc95a42..7cee813184 100644 --- a/src/openai/types/images_response.py +++ b/src/openai/types/images_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List diff --git a/src/openai/types/model.py b/src/openai/types/model.py index 58f3997f70..2631ee8d1a 100644 --- a/src/openai/types/model.py +++ b/src/openai/types/model.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal diff --git a/src/openai/types/model_deleted.py b/src/openai/types/model_deleted.py index 5329da1378..e7601f74e4 100644 --- a/src/openai/types/model_deleted.py +++ b/src/openai/types/model_deleted.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index 1c26ec3367..2a2e5c5d7a 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from pydantic import Field as FieldInfo diff --git a/src/openai/types/moderation_create_params.py b/src/openai/types/moderation_create_params.py index 25ed3ce940..d4608def54 100644 --- a/src/openai/types/moderation_create_params.py +++ b/src/openai/types/moderation_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/moderation_create_response.py b/src/openai/types/moderation_create_response.py index 0962cdbfd9..79684f8a70 100644 --- a/src/openai/types/moderation_create_response.py +++ b/src/openai/types/moderation_create_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index c9ebb1a504..e085744e29 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .error_object import ErrorObject as ErrorObject from .function_definition import FunctionDefinition as FunctionDefinition diff --git a/src/openai/types/shared/error_object.py b/src/openai/types/shared/error_object.py index f18fcc1c33..32d7045e00 100644 --- a/src/openai/types/shared/error_object.py +++ b/src/openai/types/shared/error_object.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/openai/types/shared/function_definition.py b/src/openai/types/shared/function_definition.py index 32658220fa..a39116d6bd 100644 --- a/src/openai/types/shared/function_definition.py +++ b/src/openai/types/shared/function_definition.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/openai/types/shared/function_parameters.py b/src/openai/types/shared/function_parameters.py index 405c2d14cc..c9524e4cb8 100644 --- a/src/openai/types/shared/function_parameters.py +++ b/src/openai/types/shared/function_parameters.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index 05bc4ff9ba..ef638cb279 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters diff --git a/src/openai/types/shared_params/function_definition.py b/src/openai/types/shared_params/function_definition.py index 8e89bd41dd..58d0203b4f 100644 --- a/src/openai/types/shared_params/function_definition.py +++ b/src/openai/types/shared_params/function_definition.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/openai/types/shared_params/function_parameters.py b/src/openai/types/shared_params/function_parameters.py index a405f6b2e2..5b40efb78f 100644 --- a/src/openai/types/shared_params/function_parameters.py +++ b/src/openai/types/shared_params/function_parameters.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/__init__.py b/tests/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/__init__.py b/tests/api_resources/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/__init__.py +++ b/tests/api_resources/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/audio/__init__.py b/tests/api_resources/audio/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/audio/__init__.py +++ b/tests/api_resources/audio/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index b1c7f79b1e..781ebeceb9 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index 80e364b484..ba8e9e4099 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index 72960c3249..f5c6c68f0b 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/beta/__init__.py b/tests/api_resources/beta/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/beta/__init__.py +++ b/tests/api_resources/beta/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/assistants/__init__.py b/tests/api_resources/beta/assistants/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/beta/assistants/__init__.py +++ b/tests/api_resources/beta/assistants/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/assistants/test_files.py b/tests/api_resources/beta/assistants/test_files.py index 66e3e2efe6..50106234aa 100644 --- a/tests/api_resources/beta/assistants/test_files.py +++ b/tests/api_resources/beta/assistants/test_files.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/beta/chat/__init__.py b/tests/api_resources/beta/chat/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/beta/chat/__init__.py +++ b/tests/api_resources/beta/chat/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 8db40bde93..6edbe4b491 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 6bb8fc82de..57dda57d16 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/beta/threads/__init__.py b/tests/api_resources/beta/threads/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/beta/threads/__init__.py +++ b/tests/api_resources/beta/threads/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/threads/messages/__init__.py b/tests/api_resources/beta/threads/messages/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/beta/threads/messages/__init__.py +++ b/tests/api_resources/beta/threads/messages/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/threads/messages/test_files.py b/tests/api_resources/beta/threads/messages/test_files.py index 4d0613fd2f..af4eea9377 100644 --- a/tests/api_resources/beta/threads/messages/test_files.py +++ b/tests/api_resources/beta/threads/messages/test_files.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/beta/threads/runs/__init__.py b/tests/api_resources/beta/threads/runs/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/beta/threads/runs/__init__.py +++ b/tests/api_resources/beta/threads/runs/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py index c15848cd70..e6108d8dad 100644 --- a/tests/api_resources/beta/threads/runs/test_steps.py +++ b/tests/api_resources/beta/threads/runs/test_steps.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index c61a9ee109..c708c94068 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index de1ad07567..3a9719b420 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/chat/__init__.py b/tests/api_resources/chat/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/chat/__init__.py +++ b/tests/api_resources/chat/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 4fa069ba2e..bb0658f3d9 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/fine_tuning/__init__.py b/tests/api_resources/fine_tuning/__init__.py index 1016754ef3..fd8019a9a1 100644 --- a/tests/api_resources/fine_tuning/__init__.py +++ b/tests/api_resources/fine_tuning/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 204cc3b1f5..f4974ebbcd 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 916cdd3cb6..691c4ff77f 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py index 42599219f3..e75545b4e2 100644 --- a/tests/api_resources/test_embeddings.py +++ b/tests/api_resources/test_embeddings.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index d1a17923a6..e5466e9eda 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index b6cb2572ab..2e31f3354a 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py index d031d54f6a..71f8e5834b 100644 --- a/tests/api_resources/test_models.py +++ b/tests/api_resources/test_models.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index 285e738c0e..94b9ecd31b 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/test_client.py b/tests/test_client.py index a6f936da67..dab1cb0efd 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 40b0bde10b..6de314856b 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations From 9fe13a7e360743d84566e016589e5b0ee0d80aae Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:23:41 -0400 Subject: [PATCH 361/914] chore(internal): loosen input type for util function (#1250) --- src/openai/_models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 166973538f..35a23a95cc 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -290,11 +290,15 @@ def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericMo return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) -def construct_type(*, value: object, type_: type) -> object: +def construct_type(*, value: object, type_: object) -> object: """Loose coercion to the expected type with construction of nested values. If the given value does not match the expected type then it is returned as-is. """ + # we allow `object` as the input type because otherwise, passing things like + # `Literal['value']` will be reported as a type error by type checkers + type_ = cast("type[object]", type_) + # unwrap `Annotated[T, ...]` -> `T` if is_annotated_type(type_): meta = get_args(type_)[1:] From 2b23eb538411f9578c5f9357f24fb37f5de8e84a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 20 Mar 2024 16:02:18 -0400 Subject: [PATCH 362/914] docs(readme): consistent use of sentence case in headings (#1255) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index befe927cea..80d0a42645 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. -## Streaming Responses +## Streaming responses We provide support for streaming responses using Server Side Events (SSE). @@ -281,7 +281,7 @@ completion = client.chat.completions.create( ) ``` -## File Uploads +## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. From 73869eee98818e18fafd0374731116faf7681dc5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:26:40 -0400 Subject: [PATCH 363/914] docs(readme): document how to make undocumented requests (#1256) --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 80d0a42645..6f446d82e1 100644 --- a/README.md +++ b/README.md @@ -487,6 +487,41 @@ with client.chat.completions.with_streaming_response.create( The context manager is required so that the response will reliably be closed. +### Making custom/undocumented requests + +This library is typed for convenient access the documented API. + +If you need to access undocumented endpoints, params, or response properties, the library can still be used. + +#### Undocumented endpoints + +To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other +http verbs. Options on the client will be respected (such as retries) will be respected when making this +request. + +```py +import httpx + +response = client.post( + "/foo", + cast_to=httpx.Response, + body={"my_param": True}, +) + +print(response.headers.get("x-foo")) +``` + +#### Undocumented params + +If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request +options. + +#### Undocumented properties + +To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You +can also get all the extra fields on the Pydantic model as a dict with +[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra). + ### Configuring the HTTP client You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: From 046daf6ed4220f652c9fe7dac7a24be144e18864 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 21 Mar 2024 06:36:29 -0400 Subject: [PATCH 364/914] chore(internal): construct error properties instead of using the raw response (#1257) --- src/openai/_exceptions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index 350fd2584b..074752c8a1 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -8,6 +8,7 @@ import httpx from ._utils import is_dict +from ._models import construct_type __all__ = [ "BadRequestError", @@ -51,9 +52,9 @@ def __init__(self, message: str, request: httpx.Request, *, body: object | None) self.body = body if is_dict(body): - self.code = cast(Any, body.get("code")) - self.param = cast(Any, body.get("param")) - self.type = cast(Any, body.get("type")) + self.code = cast(Any, construct_type(type_=Optional[str], value=body.get("code"))) + self.param = cast(Any, construct_type(type_=Optional[str], value=body.get("param"))) + self.type = cast(Any, construct_type(type_=str, value=body.get("type"))) else: self.code = None self.param = None From bf09f42703161354f64965578c852b9843277e68 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:36:22 -0400 Subject: [PATCH 365/914] chore(internal): formatting change (#1258) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index de412f3907..1fb077cc96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -135,6 +135,7 @@ reportImplicitOverride = true reportImportCycles = false reportPrivateUsage = false + [tool.ruff] line-length = 120 output-format = "grouped" From c9ed0f542756f45127fb668a4f8afa272b018fd9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 22 Mar 2024 06:31:42 -0400 Subject: [PATCH 366/914] docs(contributing): fix typo (#1264) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7473159258..354d21b2d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,5 +121,5 @@ You can release to package managers by using [the `Publish PyPI` GitHub action]( ### Publish manually -If you need to manually release a package, you can run the `bin/publish-pypi` script with an `PYPI_TOKEN` set on +If you need to manually release a package, you can run the `bin/publish-pypi` script with a `PYPI_TOKEN` set on the environment. From 93ed942f915c4acfc3e4e117840e946b531dff3b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 25 Mar 2024 06:33:15 -0400 Subject: [PATCH 367/914] fix: revert regression with 3.7 support (#1269) --- src/openai/_models.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 35a23a95cc..77c755b135 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -538,12 +538,14 @@ class GenericModel(BaseGenericModel, BaseModel): if PYDANTIC_V2: + from pydantic import TypeAdapter as _TypeAdapter + + _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) + if TYPE_CHECKING: from pydantic import TypeAdapter else: - from pydantic import TypeAdapter as _TypeAdapter - - TypeAdapter = lru_cache(_TypeAdapter) + TypeAdapter = _CachedTypeAdapter def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: return TypeAdapter(type_).validate_python(value) From de7c0e2d9375d042a42e3db6c17e5af9a5701a99 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 25 Mar 2024 06:33:44 -0400 Subject: [PATCH 368/914] release: 1.14.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 19cc6edce7..d55a714ec5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.14.2" + ".": "1.14.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7497d6af56..913dece99e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 1.14.3 (2024-03-25) + +Full Changelog: [v1.14.2...v1.14.3](https://github.com/openai/openai-python/compare/v1.14.2...v1.14.3) + +### Bug Fixes + +* revert regression with 3.7 support ([#1269](https://github.com/openai/openai-python/issues/1269)) ([37aed56](https://github.com/openai/openai-python/commit/37aed564143dc7281f1eaa6ab64ec5ca334cf25e)) + + +### Chores + +* **internal:** construct error properties instead of using the raw response ([#1257](https://github.com/openai/openai-python/issues/1257)) ([11dce5c](https://github.com/openai/openai-python/commit/11dce5c66395722b245f5d5461ce379ca7b939e4)) +* **internal:** formatting change ([#1258](https://github.com/openai/openai-python/issues/1258)) ([b907dd7](https://github.com/openai/openai-python/commit/b907dd7dcae895e4209559da061d0991a8d640a6)) +* **internal:** loosen input type for util function ([#1250](https://github.com/openai/openai-python/issues/1250)) ([fc8b4c3](https://github.com/openai/openai-python/commit/fc8b4c37dc91dfcc0535c19236092992171784a0)) + + +### Documentation + +* **contributing:** fix typo ([#1264](https://github.com/openai/openai-python/issues/1264)) ([835cb9b](https://github.com/openai/openai-python/commit/835cb9b2f92e2aa3329545b4677865dcd4fd00f0)) +* **readme:** consistent use of sentence case in headings ([#1255](https://github.com/openai/openai-python/issues/1255)) ([519f371](https://github.com/openai/openai-python/commit/519f371af779b5fa353292ff5a2d3332afe0987e)) +* **readme:** document how to make undocumented requests ([#1256](https://github.com/openai/openai-python/issues/1256)) ([5887858](https://github.com/openai/openai-python/commit/5887858a7b649dfde5b733ef01e5cffcf953b2a7)) + ## 1.14.2 (2024-03-19) Full Changelog: [v1.14.1...v1.14.2](https://github.com/openai/openai-python/compare/v1.14.1...v1.14.2) diff --git a/pyproject.toml b/pyproject.toml index 1fb077cc96..8e8ce06881 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.14.2" +version = "1.14.3" description = "The official Python library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b8eb743acc..9163853b72 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.14.2" # x-release-please-version +__version__ = "1.14.3" # x-release-please-version From 1ac012d70beae5719c83c191fe2b6d620d6cf8f7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:24:20 +0000 Subject: [PATCH 369/914] chore(internal): bump dependencies (#1273) --- requirements-dev.lock | 4 ++-- requirements.lock | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 9d79557b3a..4461f65738 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -22,7 +22,7 @@ attrs==23.1.0 azure-core==1.30.1 # via azure-identity azure-identity==1.15.0 -black==24.2.0 +black==24.3.0 # via inline-snapshot certifi==2023.7.22 # via httpcore @@ -67,7 +67,7 @@ importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest inline-snapshot==0.7.0 -msal==1.27.0 +msal==1.28.0 # via azure-identity # via msal-extensions msal-extensions==1.1.0 diff --git a/requirements.lock b/requirements.lock index f3733bec9a..c933d6c90e 100644 --- a/requirements.lock +++ b/requirements.lock @@ -33,15 +33,15 @@ numpy==1.26.4 # via openai # via pandas # via pandas-stubs -pandas==2.2.0 +pandas==2.2.1 # via openai -pandas-stubs==2.2.0.240218 +pandas-stubs==2.2.1.240316 # via openai pydantic==2.4.2 # via openai pydantic-core==2.10.1 # via pydantic -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via pandas pytz==2024.1 # via pandas From 1e55d60e4aebbe043742025b1211f8f56ef1ba89 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:31:53 +0000 Subject: [PATCH 370/914] feat(package): export default constants (#1275) --- src/openai/__init__.py | 4 ++++ src/openai/_base_client.py | 6 +++--- src/openai/_constants.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 9585fde99b..cd05a749da 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -12,6 +12,7 @@ from ._models import BaseModel from ._version import __title__, __version__ from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse +from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS from ._exceptions import ( APIError, OpenAIError, @@ -63,6 +64,9 @@ "AsyncOpenAI", "file_from_path", "BaseModel", + "DEFAULT_TIMEOUT", + "DEFAULT_MAX_RETRIES", + "DEFAULT_CONNECTION_LIMITS", ] from .lib import azure as _azure diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index f431128eef..7a8595c173 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -71,13 +71,13 @@ extract_response_type, ) from ._constants import ( - DEFAULT_LIMITS, DEFAULT_TIMEOUT, MAX_RETRY_DELAY, DEFAULT_MAX_RETRIES, INITIAL_RETRY_DELAY, RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER, + DEFAULT_CONNECTION_LIMITS, ) from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder from ._exceptions import ( @@ -747,7 +747,7 @@ def __init__( if http_client is not None: raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") else: - limits = DEFAULT_LIMITS + limits = DEFAULT_CONNECTION_LIMITS if transport is not None: warnings.warn( @@ -1294,7 +1294,7 @@ def __init__( if http_client is not None: raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") else: - limits = DEFAULT_LIMITS + limits = DEFAULT_CONNECTION_LIMITS if transport is not None: warnings.warn( diff --git a/src/openai/_constants.py b/src/openai/_constants.py index b2e541f7b1..3f96aea3dd 100644 --- a/src/openai/_constants.py +++ b/src/openai/_constants.py @@ -8,7 +8,7 @@ # default timeout is 10 minutes DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0) DEFAULT_MAX_RETRIES = 2 -DEFAULT_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) INITIAL_RETRY_DELAY = 0.5 MAX_RETRY_DELAY = 8.0 From 31a7e9adbd90fa4ba76ceaa435276b38bdc1aca3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 29 Mar 2024 14:09:18 +0000 Subject: [PATCH 371/914] fix(project): use absolute github links on PyPi (#1280) --- pyproject.toml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8e8ce06881..746896e80d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "openai" version = "1.14.3" description = "The official Python library for the openai API" -readme = "README.md" +dynamic = ["readme"] license = "Apache-2.0" authors = [ { name = "OpenAI", email = "support@openai.com" }, @@ -93,7 +93,7 @@ typecheck = { chain = [ "typecheck:mypy" = "mypy ." [build-system] -requires = ["hatchling"] +requires = ["hatchling", "hatch-fancy-pypi-readme"] build-backend = "hatchling.build" [tool.hatch.build] @@ -104,6 +104,17 @@ include = [ [tool.hatch.build.targets.wheel] packages = ["src/openai"] +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] +path = "README.md" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] +# replace relative links with absolute links +pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' +replacement = '[\1](https://github.com/openai/openai-python/tree/main/\g<2>)' + [tool.black] line-length = 120 target-version = ["py37"] From 7828119bc6d17ababeaf28aadb0799e5c492b182 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 29 Mar 2024 17:38:48 +0000 Subject: [PATCH 372/914] feat(client): increase default HTTP max_connections to 1000 and max_keepalive_connections to 100 (#1281) --- src/openai/_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_constants.py b/src/openai/_constants.py index 3f96aea3dd..3f82bed037 100644 --- a/src/openai/_constants.py +++ b/src/openai/_constants.py @@ -8,7 +8,7 @@ # default timeout is 10 minutes DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0) DEFAULT_MAX_RETRIES = 2 -DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=1000, max_keepalive_connections=100) INITIAL_RETRY_DELAY = 0.5 MAX_RETRY_DELAY = 8.0 From 27a4626ab39fb8d8e5b0aa1698e9cadec1b0bc85 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 29 Mar 2024 21:08:28 +0000 Subject: [PATCH 373/914] feat(api): adding temperature parameter (#1282) --- .../beta/threads/messages/messages.py | 22 +++++++--- .../resources/beta/threads/runs/runs.py | 42 +++++++++++++++++++ src/openai/resources/beta/threads/threads.py | 42 +++++++++++++++++++ .../beta/thread_create_and_run_params.py | 16 +++++-- src/openai/types/beta/thread_create_params.py | 9 ++-- src/openai/types/beta/threads/message.py | 6 +-- .../beta/threads/message_create_params.py | 9 ++-- src/openai/types/beta/threads/run.py | 3 ++ .../types/beta/threads/run_create_params.py | 7 ++++ tests/api_resources/beta/test_threads.py | 4 ++ tests/api_resources/beta/threads/test_runs.py | 4 ++ 11 files changed, 146 insertions(+), 18 deletions(-) diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 21e8bca5b8..1c008a7cc4 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -52,7 +52,7 @@ def create( thread_id: str, *, content: str, - role: Literal["user"], + role: Literal["user", "assistant"], file_ids: List[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -68,8 +68,13 @@ def create( Args: content: The content of the message. - role: The role of the entity that is creating the message. Currently only `user` is - supported. + role: + The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the message should use. There can be a maximum of 10 files attached to a @@ -276,7 +281,7 @@ async def create( thread_id: str, *, content: str, - role: Literal["user"], + role: Literal["user", "assistant"], file_ids: List[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -292,8 +297,13 @@ async def create( Args: content: The content of the message. - role: The role of the entity that is creating the message. Currently only `user` is - supported. + role: + The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the message should use. There can be a maximum of 10 files attached to a diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index afa447612c..ab39a96a8d 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -76,6 +76,7 @@ def create( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -114,6 +115,10 @@ def create( events, terminating when the Run enters a terminal state with a `data: [DONE]` message. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -138,6 +143,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -176,6 +182,10 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -200,6 +210,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -238,6 +249,10 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -262,6 +277,7 @@ def create( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -283,6 +299,7 @@ def create( "metadata": metadata, "model": model, "stream": stream, + "temperature": temperature, "tools": tools, }, run_create_params.RunCreateParams, @@ -489,6 +506,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -510,6 +528,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT, @@ -531,6 +550,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT | None = None, @@ -561,6 +581,7 @@ def create_and_stream( "instructions": instructions, "metadata": metadata, "model": model, + "temperature": temperature, "stream": True, "tools": tools, }, @@ -841,6 +862,7 @@ async def create( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -879,6 +901,10 @@ async def create( events, terminating when the Run enters a terminal state with a `data: [DONE]` message. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -903,6 +929,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -941,6 +968,10 @@ async def create( model associated with the assistant. If not, the model associated with the assistant will be used. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -965,6 +996,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1003,6 +1035,10 @@ async def create( model associated with the assistant. If not, the model associated with the assistant will be used. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -1027,6 +1063,7 @@ async def create( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1048,6 +1085,7 @@ async def create( "metadata": metadata, "model": model, "stream": stream, + "temperature": temperature, "tools": tools, }, run_create_params.RunCreateParams, @@ -1254,6 +1292,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1275,6 +1314,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT, @@ -1296,6 +1336,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT | None = None, @@ -1328,6 +1369,7 @@ def create_and_stream( "instructions": instructions, "metadata": metadata, "model": model, + "temperature": temperature, "stream": True, "tools": tools, }, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index bcb0da8a62..c2ad6aca5f 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -244,6 +244,7 @@ def create_and_run( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -278,6 +279,10 @@ def create_and_run( events, terminating when the Run enters a terminal state with a `data: [DONE]` message. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + thread: If no thread is provided, an empty thread will be created. tools: Override the tools the assistant can use for this run. This is useful for @@ -302,6 +307,7 @@ def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -336,6 +342,10 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + thread: If no thread is provided, an empty thread will be created. tools: Override the tools the assistant can use for this run. This is useful for @@ -360,6 +370,7 @@ def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -394,6 +405,10 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + thread: If no thread is provided, an empty thread will be created. tools: Override the tools the assistant can use for this run. This is useful for @@ -418,6 +433,7 @@ def create_and_run( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -437,6 +453,7 @@ def create_and_run( "metadata": metadata, "model": model, "stream": stream, + "temperature": temperature, "thread": thread, "tools": tools, }, @@ -458,6 +475,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -478,6 +496,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, event_handler: AssistantEventHandlerT, @@ -498,6 +517,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, event_handler: AssistantEventHandlerT | None = None, @@ -524,6 +544,7 @@ def create_and_run_stream( "instructions": instructions, "metadata": metadata, "model": model, + "temperature": temperature, "stream": True, "thread": thread, "tools": tools, @@ -723,6 +744,7 @@ async def create_and_run( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -757,6 +779,10 @@ async def create_and_run( events, terminating when the Run enters a terminal state with a `data: [DONE]` message. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + thread: If no thread is provided, an empty thread will be created. tools: Override the tools the assistant can use for this run. This is useful for @@ -781,6 +807,7 @@ async def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -815,6 +842,10 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + thread: If no thread is provided, an empty thread will be created. tools: Override the tools the assistant can use for this run. This is useful for @@ -839,6 +870,7 @@ async def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -873,6 +905,10 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + thread: If no thread is provided, an empty thread will be created. tools: Override the tools the assistant can use for this run. This is useful for @@ -897,6 +933,7 @@ async def create_and_run( metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -916,6 +953,7 @@ async def create_and_run( "metadata": metadata, "model": model, "stream": stream, + "temperature": temperature, "thread": thread, "tools": tools, }, @@ -937,6 +975,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -957,6 +996,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, event_handler: AsyncAssistantEventHandlerT, @@ -977,6 +1017,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, event_handler: AsyncAssistantEventHandlerT | None = None, @@ -1005,6 +1046,7 @@ def create_and_run_stream( "instructions": instructions, "metadata": metadata, "model": model, + "temperature": temperature, "stream": True, "thread": thread, "tools": tools, diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 9c16e1133f..d4266fc48c 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -49,6 +49,13 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): assistant will be used. """ + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + thread: Thread """If no thread is provided, an empty thread will be created.""" @@ -63,10 +70,13 @@ class ThreadMessage(TypedDict, total=False): content: Required[str] """The content of the message.""" - role: Required[Literal["user"]] - """The role of the entity that is creating the message. + role: Required[Literal["user", "assistant"]] + """The role of the entity that is creating the message. Allowed values include: - Currently only `user` is supported. + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. """ file_ids: List[str] diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index b3dda503ff..1b382186aa 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -28,10 +28,13 @@ class Message(TypedDict, total=False): content: Required[str] """The content of the message.""" - role: Required[Literal["user"]] - """The role of the entity that is creating the message. + role: Required[Literal["user", "assistant"]] + """The role of the entity that is creating the message. Allowed values include: - Currently only `user` is supported. + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. """ file_ids: List[str] diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index 027e2bfa15..bde0263975 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -63,9 +63,9 @@ class Message(BaseModel): run_id: Optional[str] = None """ - If applicable, the ID of the - [run](https://platform.openai.com/docs/api-reference/runs) associated with the - authoring of this message. + The ID of the [run](https://platform.openai.com/docs/api-reference/runs) + associated with the creation of this message. Value is `null` when messages are + created manually using the create message or create thread endpoints. """ status: Literal["in_progress", "incomplete", "completed"] diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index b2f27deb3e..9b9467ef4d 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -12,10 +12,13 @@ class MessageCreateParams(TypedDict, total=False): content: Required[str] """The content of the message.""" - role: Required[Literal["user"]] - """The role of the entity that is creating the message. + role: Required[Literal["user", "assistant"]] + """The role of the entity that is creating the message. Allowed values include: - Currently only `user` is supported. + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. """ file_ids: List[str] diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index d2cac4c279..3ab276245f 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -139,3 +139,6 @@ class Run(BaseModel): This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.). """ + + temperature: Optional[float] = None + """The sampling temperature used for this run. If not set, defaults to 1.""" diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 89dff389a9..ac185973a5 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -48,6 +48,13 @@ class RunCreateParamsBase(TypedDict, total=False): assistant will be used. """ + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + tools: Optional[Iterable[AssistantToolParam]] """Override the tools the assistant can use for this run. diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 57dda57d16..fd3f7c5102 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -210,6 +210,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) metadata={}, model="string", stream=False, + temperature=1, thread={ "messages": [ { @@ -277,6 +278,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) instructions="string", metadata={}, model="string", + temperature=1, thread={ "messages": [ { @@ -522,6 +524,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie metadata={}, model="string", stream=False, + temperature=1, thread={ "messages": [ { @@ -589,6 +592,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie instructions="string", metadata={}, model="string", + temperature=1, thread={ "messages": [ { diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 3a9719b420..aabe2c7fc9 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -38,6 +38,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: metadata={}, model="string", stream=False, + temperature=1, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], ) assert_matches_type(Run, run, path=["response"]) @@ -95,6 +96,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: instructions="string", metadata={}, model="string", + temperature=1, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], ) run_stream.response.close() @@ -492,6 +494,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn metadata={}, model="string", stream=False, + temperature=1, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], ) assert_matches_type(Run, run, path=["response"]) @@ -549,6 +552,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn instructions="string", metadata={}, model="string", + temperature=1, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], ) await run_stream.response.aclose() From 802819c864102e009221b2ef9089a70db0d8f57c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 30 Mar 2024 20:44:48 +0000 Subject: [PATCH 374/914] docs(readme): change undocumented params wording (#1284) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6f446d82e1..7f053e5429 100644 --- a/README.md +++ b/README.md @@ -511,12 +511,12 @@ response = client.post( print(response.headers.get("x-foo")) ``` -#### Undocumented params +#### Undocumented request params If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request options. -#### Undocumented properties +#### Undocumented response properties To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You can also get all the extra fields on the Pydantic model as a dict with From 0470d1baa8ef1f64d0116f3f47683d5bf622cbef Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sun, 31 Mar 2024 06:04:07 +0100 Subject: [PATCH 375/914] release: 1.15.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 25 +++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d55a714ec5..7ccfe12c9e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.14.3" + ".": "1.15.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 913dece99e..180bbf2a28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 1.15.0 (2024-03-31) + +Full Changelog: [v1.14.3...v1.15.0](https://github.com/openai/openai-python/compare/v1.14.3...v1.15.0) + +### Features + +* **api:** adding temperature parameter ([#1282](https://github.com/openai/openai-python/issues/1282)) ([0e68fd3](https://github.com/openai/openai-python/commit/0e68fd3690155785d1fb0ee9a8604f51e6701b1d)) +* **client:** increase default HTTP max_connections to 1000 and max_keepalive_connections to 100 ([#1281](https://github.com/openai/openai-python/issues/1281)) ([340d139](https://github.com/openai/openai-python/commit/340d1391e3071a265ed12c0a8d70d4d73a860bd8)) +* **package:** export default constants ([#1275](https://github.com/openai/openai-python/issues/1275)) ([fdc126e](https://github.com/openai/openai-python/commit/fdc126e428320f1bed5eabd3eed229f08ab9effa)) + + +### Bug Fixes + +* **project:** use absolute github links on PyPi ([#1280](https://github.com/openai/openai-python/issues/1280)) ([94cd528](https://github.com/openai/openai-python/commit/94cd52837650e5b7e115119d69e6b1c7ba1f6bf1)) + + +### Chores + +* **internal:** bump dependencies ([#1273](https://github.com/openai/openai-python/issues/1273)) ([18dcd65](https://github.com/openai/openai-python/commit/18dcd654d9f54628b5fe21a499d1fef500e15f7f)) + + +### Documentation + +* **readme:** change undocumented params wording ([#1284](https://github.com/openai/openai-python/issues/1284)) ([7498ef1](https://github.com/openai/openai-python/commit/7498ef1e9568200086ba3efb99ea100feb05e3f0)) + ## 1.14.3 (2024-03-25) Full Changelog: [v1.14.2...v1.14.3](https://github.com/openai/openai-python/compare/v1.14.2...v1.14.3) diff --git a/pyproject.toml b/pyproject.toml index 746896e80d..beb31f24a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.14.3" +version = "1.15.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9163853b72..6865a9f7bd 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.14.3" # x-release-please-version +__version__ = "1.15.0" # x-release-please-version From 07079085ca769cb679033a7c91cc0d160a624a58 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 1 Apr 2024 10:47:23 +0100 Subject: [PATCH 376/914] chore(client): validate that max_retries is not None (#1286) --- src/openai/_base_client.py | 5 +++++ tests/test_client.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 7a8595c173..502ed7c7ae 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -361,6 +361,11 @@ def __init__( self._strict_response_validation = _strict_response_validation self._idempotency_header = None + if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] + raise TypeError( + "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `openai.DEFAULT_MAX_RETRIES`" + ) + def _enforce_trailing_slash(self, url: URL) -> URL: if url.raw_path.endswith(b"/"): return url diff --git a/tests/test_client.py b/tests/test_client.py index dab1cb0efd..ba85fd9d5f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -646,6 +646,10 @@ class Model(BaseModel): assert isinstance(exc.value.__cause__, ValidationError) + def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) + @pytest.mark.respx(base_url=base_url) def test_default_stream_cls(self, respx_mock: MockRouter) -> None: class Model(BaseModel): @@ -1368,6 +1372,12 @@ class Model(BaseModel): assert isinstance(exc.value.__cause__, ValidationError) + async def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + AsyncOpenAI( + base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None) + ) + @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio async def test_default_stream_cls(self, respx_mock: MockRouter) -> None: From 8efca3a34a0d0527523bb9f50ab7b5bd8d7c9d0c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:52:54 +0200 Subject: [PATCH 377/914] feat(api): add support for filtering messages by run_id (#1288) --- src/openai/resources/beta/threads/messages/messages.py | 8 ++++++++ src/openai/types/beta/threads/message_list_params.py | 3 +++ tests/api_resources/beta/threads/test_messages.py | 2 ++ 3 files changed, 13 insertions(+) diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages/messages.py index 1c008a7cc4..bbce3e99e4 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages/messages.py @@ -203,6 +203,7 @@ def list( before: str | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + run_id: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -230,6 +231,8 @@ def list( order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order. + run_id: Filter messages by the run ID that generated them. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -255,6 +258,7 @@ def list( "before": before, "limit": limit, "order": order, + "run_id": run_id, }, message_list_params.MessageListParams, ), @@ -432,6 +436,7 @@ def list( before: str | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + run_id: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -459,6 +464,8 @@ def list( order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order. + run_id: Filter messages by the run ID that generated them. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -484,6 +491,7 @@ def list( "before": before, "limit": limit, "order": order, + "run_id": run_id, }, message_list_params.MessageListParams, ), diff --git a/src/openai/types/beta/threads/message_list_params.py b/src/openai/types/beta/threads/message_list_params.py index 8b139caa93..18c2442fb5 100644 --- a/src/openai/types/beta/threads/message_list_params.py +++ b/src/openai/types/beta/threads/message_list_params.py @@ -37,3 +37,6 @@ class MessageListParams(TypedDict, total=False): `asc` for ascending order and `desc` for descending order. """ + + run_id: str + """Filter messages by the run ID that generated them.""" diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index c708c94068..22198ccbc5 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -195,6 +195,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: before="string", limit=0, order="asc", + run_id="string", ) assert_matches_type(SyncCursorPage[Message], message, path=["response"]) @@ -410,6 +411,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N before="string", limit=0, order="asc", + run_id="string", ) assert_matches_type(AsyncCursorPage[Message], message, path=["response"]) From 595f3b366a83138564ed19596bc84b50a59a72a8 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:39:26 +0200 Subject: [PATCH 378/914] feat(api): run polling helpers (#1289) refactor: rename createAndStream to stream --- README.md | 20 +- api.md | 5 + examples/assistant.py | 27 +- examples/assistant_stream_helpers.py | 2 +- helpers.md | 10 +- .../resources/beta/threads/runs/runs.py | 495 +++++++++++++++++- src/openai/resources/beta/threads/threads.py | 80 +++ tests/api_resources/beta/threads/test_runs.py | 2 + 8 files changed, 610 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 7f053e5429..5264026dc9 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,30 @@ we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) to add `OPENAI_API_KEY="My API Key"` to your `.env` file so that your API Key is not stored in source control. +### Polling Helpers + +When interacting with the API some actions such as starting a Run may take time to complete. The SDK includes +helper functions which will poll the status until it reaches a terminal state and then return the resulting object. +If an API method results in an action which could benefit from polling there will be a corresponding version of the +method ending in '\_and_poll'. + +For instance to create a Run and poll until it reaches a terminal state you can run: + +```python +run = client.beta.threads.runs.create_and_poll( + thread_id=thread.id, + assistant_id=assistant.id, +) +``` + +More information on the lifecycle of a Run can be found in the [Run Lifecycle Documentation](https://platform.openai.com/docs/assistants/how-it-works/run-lifecycle) + ### Streaming Helpers The SDK also includes helpers to process streams and handle the incoming events. ```python -with client.beta.threads.runs.create_and_stream( +with client.beta.threads.runs.stream( thread_id=thread.id, assistant_id=assistant.id, instructions="Please address the user as Jane Doe. The user has a premium account.", diff --git a/api.md b/api.md index 29392cff13..dbc95cd0b4 100644 --- a/api.md +++ b/api.md @@ -230,6 +230,7 @@ Methods: - client.beta.threads.update(thread_id, \*\*params) -> Thread - client.beta.threads.delete(thread_id) -> ThreadDeleted - client.beta.threads.create_and_run(\*\*params) -> Run +- client.beta.threads.create_and_run_poll(\*args) -> Run - client.beta.threads.create_and_run_stream(\*args) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT] ### Runs @@ -248,7 +249,11 @@ Methods: - client.beta.threads.runs.list(thread_id, \*\*params) -> SyncCursorPage[Run] - client.beta.threads.runs.cancel(run_id, \*, thread_id) -> Run - client.beta.threads.runs.submit_tool_outputs(run_id, \*, thread_id, \*\*params) -> Run +- client.beta.threads.runs.create_and_poll(\*args) -> Run - client.beta.threads.runs.create_and_stream(\*args) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT] +- client.beta.threads.runs.poll(\*args) -> Run +- client.beta.threads.runs.stream(\*args) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT] +- client.beta.threads.runs.submit_tool_outputs_and_poll(\*args) -> Run - client.beta.threads.runs.submit_tool_outputs_stream(\*args) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT] #### Steps diff --git a/examples/assistant.py b/examples/assistant.py index c5fbb82a3a..0631494ecc 100644 --- a/examples/assistant.py +++ b/examples/assistant.py @@ -1,4 +1,3 @@ -import time import openai @@ -20,28 +19,20 @@ content="I need to solve the equation `3x + 11 = 14`. Can you help me?", ) -run = client.beta.threads.runs.create( +run = client.beta.threads.runs.create_and_poll( thread_id=thread.id, assistant_id=assistant.id, instructions="Please address the user as Jane Doe. The user has a premium account.", ) -print("checking assistant status. ") -while True: - run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id) +print("Run completed with status: " + run.status) - if run.status == "completed": - print("done!") - messages = client.beta.threads.messages.list(thread_id=thread.id) +if run.status == "completed": + messages = client.beta.threads.messages.list(thread_id=thread.id) - print("messages: ") - for message in messages: - assert message.content[0].type == "text" - print({"role": message.role, "message": message.content[0].text.value}) + print("messages: ") + for message in messages: + assert message.content[0].type == "text" + print({"role": message.role, "message": message.content[0].text.value}) - client.beta.assistants.delete(assistant.id) - - break - else: - print("in progress...") - time.sleep(5) + client.beta.assistants.delete(assistant.id) diff --git a/examples/assistant_stream_helpers.py b/examples/assistant_stream_helpers.py index 6c2aae0b46..7baec77c72 100644 --- a/examples/assistant_stream_helpers.py +++ b/examples/assistant_stream_helpers.py @@ -63,7 +63,7 @@ def main() -> None: ) print(f"Question: {question}\n") - with client.beta.threads.runs.create_and_stream( + with client.beta.threads.runs.stream( thread_id=thread.id, assistant_id=assistant.id, instructions="Please address the user as Jane Doe. The user has a premium account.", diff --git a/helpers.md b/helpers.md index fed20ee81c..4271cd9ede 100644 --- a/helpers.md +++ b/helpers.md @@ -46,11 +46,11 @@ class EventHandler(AssistantEventHandler): if output.type == "logs": print(f"\n{output.logs}", flush=True) -# Then, we use the `create_and_stream` SDK helper +# Then, we use the `stream` SDK helper # with the `EventHandler` class to create the Run # and stream the response. -with client.beta.threads.runs.create_and_stream( +with client.beta.threads.runs.stream( thread_id="thread_id", assistant_id="assistant_id", event_handler=EventHandler(), @@ -63,7 +63,7 @@ with client.beta.threads.runs.create_and_stream( You can also iterate over all the streamed events. ```python -with client.beta.threads.runs.create_and_stream( +with client.beta.threads.runs.stream( thread_id=thread.id, assistant_id=assistant.id ) as stream: @@ -78,7 +78,7 @@ with client.beta.threads.runs.create_and_stream( You can also iterate over just the text deltas received ```python -with client.beta.threads.runs.create_and_stream( +with client.beta.threads.runs.stream( thread_id=thread.id, assistant_id=assistant.id ) as stream: @@ -91,7 +91,7 @@ with client.beta.threads.runs.create_and_stream( There are three helper methods for creating streams: ```python -client.beta.threads.runs.create_and_stream() +client.beta.threads.runs.stream() ``` This method can be used to start and stream the response to an existing run with an associated thread diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index ab39a96a8d..4529c65025 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -2,6 +2,8 @@ from __future__ import annotations +import time +import typing_extensions from typing import Iterable, Optional, overload from functools import partial from typing_extensions import Literal @@ -19,6 +21,7 @@ ) from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ....._utils import ( + is_given, required_args, maybe_transform, async_maybe_transform, @@ -497,7 +500,58 @@ def cancel( cast_to=Run, ) + def create_and_poll( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to create a run an poll for a terminal state. More information on Run + lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = self.create( + thread_id=thread_id, + assistant_id=assistant_id, + additional_instructions=additional_instructions, + instructions=instructions, + metadata=metadata, + model=model, + temperature=temperature, + # We assume we are not streaming when polling + stream=False, + tools=tools, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return self.poll( + run.id, + thread_id=thread_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + poll_interval_ms=poll_interval_ms, + timeout=timeout, + ) + @overload + @typing_extensions.deprecated("use `stream` instead") def create_and_stream( self, *, @@ -520,6 +574,7 @@ def create_and_stream( ... @overload + @typing_extensions.deprecated("use `stream` instead") def create_and_stream( self, *, @@ -542,6 +597,7 @@ def create_and_stream( """Create a Run stream""" ... + @typing_extensions.deprecated("use `stream` instead") def create_and_stream( self, *, @@ -596,6 +652,150 @@ def create_and_stream( ) return AssistantStreamManager(make_request, event_handler=event_handler or AssistantEventHandler()) + def poll( + self, + run_id: str, + thread_id: str, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to poll a run status until it reaches a terminal state. More + information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + extra_headers = {"X-Stainless-Poll-Helper": "true", **(extra_headers or {})} + + if is_given(poll_interval_ms): + extra_headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + terminal_states = {"requires_action", "cancelled", "completed", "failed", "expired"} + while True: + response = self.with_raw_response.retrieve( + thread_id=thread_id, + run_id=run_id, + extra_headers=extra_headers, + extra_body=extra_body, + extra_query=extra_query, + timeout=timeout, + ) + + run = response.parse() + # Return if we reached a terminal state + if run.status in terminal_states: + return run + + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + time.sleep(poll_interval_ms / 1000) + + @overload + def stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler]: + """Create a Run stream""" + ... + + @overload + def stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + event_handler: AssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandlerT]: + """Create a Run stream""" + ... + + def stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + event_handler: AssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: + """Create a Run stream""" + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v1", + "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + make_request = partial( + self._post, + f"/threads/{thread_id}/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "instructions": instructions, + "metadata": metadata, + "model": model, + "temperature": temperature, + "stream": True, + "tools": tools, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=True, + stream_cls=Stream[AssistantStreamEvent], + ) + return AssistantStreamManager(make_request, event_handler=event_handler or AssistantEventHandler()) + @overload def submit_tool_outputs( self, @@ -747,6 +947,45 @@ def submit_tool_outputs( stream_cls=Stream[AssistantStreamEvent], ) + def submit_tool_outputs_and_poll( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to submit a tool output to a run and poll for a terminal run state. + More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = self.submit_tool_outputs( + run_id=run_id, + thread_id=thread_id, + tool_outputs=tool_outputs, + stream=False, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return self.poll( + run_id=run.id, + thread_id=thread_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + poll_interval_ms=poll_interval_ms, + ) + @overload def submit_tool_outputs_stream( self, @@ -763,7 +1002,8 @@ def submit_tool_outputs_stream( ) -> AssistantStreamManager[AssistantEventHandler]: """ Submit the tool outputs from a previous run and stream the run to a terminal - state. + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps """ ... @@ -784,7 +1024,8 @@ def submit_tool_outputs_stream( ) -> AssistantStreamManager[AssistantEventHandlerT]: """ Submit the tool outputs from a previous run and stream the run to a terminal - state. + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps """ ... @@ -804,7 +1045,8 @@ def submit_tool_outputs_stream( ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: """ Submit the tool outputs from a previous run and stream the run to a terminal - state. + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps """ if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") @@ -1283,7 +1525,58 @@ async def cancel( cast_to=Run, ) + async def create_and_poll( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to create a run an poll for a terminal state. More information on Run + lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = await self.create( + thread_id=thread_id, + assistant_id=assistant_id, + additional_instructions=additional_instructions, + instructions=instructions, + metadata=metadata, + model=model, + temperature=temperature, + # We assume we are not streaming when polling + stream=False, + tools=tools, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return await self.poll( + run.id, + thread_id=thread_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + poll_interval_ms=poll_interval_ms, + timeout=timeout, + ) + @overload + @typing_extensions.deprecated("use `stream` instead") def create_and_stream( self, *, @@ -1306,6 +1599,7 @@ def create_and_stream( ... @overload + @typing_extensions.deprecated("use `stream` instead") def create_and_stream( self, *, @@ -1328,6 +1622,7 @@ def create_and_stream( """Create a Run stream""" ... + @typing_extensions.deprecated("use `stream` instead") def create_and_stream( self, *, @@ -1384,6 +1679,152 @@ def create_and_stream( ) return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) + async def poll( + self, + run_id: str, + thread_id: str, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to poll a run status until it reaches a terminal state. More + information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + extra_headers = {"X-Stainless-Poll-Helper": "true", **(extra_headers or {})} + + if is_given(poll_interval_ms): + extra_headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + terminal_states = {"requires_action", "cancelled", "completed", "failed", "expired"} + while True: + response = await self.with_raw_response.retrieve( + thread_id=thread_id, + run_id=run_id, + extra_headers=extra_headers, + extra_body=extra_body, + extra_query=extra_query, + timeout=timeout, + ) + + run = response.parse() + # Return if we reached a terminal state + if run.status in terminal_states: + return run + + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + time.sleep(poll_interval_ms / 1000) + + @overload + def stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: + """Create a Run stream""" + ... + + @overload + def stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: + """Create a Run stream""" + ... + + def stream( + self, + *, + assistant_id: str, + additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + thread_id: str, + event_handler: AsyncAssistantEventHandlerT | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ( + AsyncAssistantStreamManager[AsyncAssistantEventHandler] + | AsyncAssistantStreamManager[AsyncAssistantEventHandlerT] + ): + """Create a Run stream""" + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + + extra_headers = { + "OpenAI-Beta": "assistants=v1", + "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", + "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", + **(extra_headers or {}), + } + request = self._post( + f"/threads/{thread_id}/runs", + body=maybe_transform( + { + "assistant_id": assistant_id, + "additional_instructions": additional_instructions, + "instructions": instructions, + "metadata": metadata, + "model": model, + "temperature": temperature, + "stream": True, + "tools": tools, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Run, + stream=True, + stream_cls=AsyncStream[AssistantStreamEvent], + ) + return AsyncAssistantStreamManager(request, event_handler=event_handler or AsyncAssistantEventHandler()) + @overload async def submit_tool_outputs( self, @@ -1535,6 +1976,45 @@ async def submit_tool_outputs( stream_cls=AsyncStream[AssistantStreamEvent], ) + async def submit_tool_outputs_and_poll( + self, + *, + tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], + run_id: str, + thread_id: str, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to submit a tool output to a run and poll for a terminal run state. + More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = await self.submit_tool_outputs( + run_id=run_id, + thread_id=thread_id, + tool_outputs=tool_outputs, + stream=False, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return await self.poll( + run_id=run.id, + thread_id=thread_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + poll_interval_ms=poll_interval_ms, + ) + @overload def submit_tool_outputs_stream( self, @@ -1551,7 +2031,8 @@ def submit_tool_outputs_stream( ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]: """ Submit the tool outputs from a previous run and stream the run to a terminal - state. + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps """ ... @@ -1572,7 +2053,8 @@ def submit_tool_outputs_stream( ) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]: """ Submit the tool outputs from a previous run and stream the run to a terminal - state. + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps """ ... @@ -1595,7 +2077,8 @@ def submit_tool_outputs_stream( ): """ Submit the tool outputs from a previous run and stream the run to a terminal - state. + state. More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps """ if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index c2ad6aca5f..3509267d4f 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -467,6 +467,45 @@ def create_and_run( stream_cls=Stream[AssistantStreamEvent], ) + def create_and_run_poll( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to create a thread, start a run and then poll for a terminal state. + More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = self.create_and_run( + assistant_id=assistant_id, + instructions=instructions, + metadata=metadata, + model=model, + temperature=temperature, + stream=False, + thread=thread, + tools=tools, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return self.runs.poll(run.id, run.thread_id, extra_headers, extra_query, extra_body, timeout, poll_interval_ms) + @overload def create_and_run_stream( self, @@ -967,6 +1006,47 @@ async def create_and_run( stream_cls=AsyncStream[AssistantStreamEvent], ) + async def create_and_run_poll( + self, + *, + assistant_id: str, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + model: Optional[str] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Run: + """ + A helper to create a thread, start a run and then poll for a terminal state. + More information on Run lifecycles can be found here: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + """ + run = await self.create_and_run( + assistant_id=assistant_id, + instructions=instructions, + metadata=metadata, + model=model, + temperature=temperature, + stream=False, + thread=thread, + tools=tools, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return await self.runs.poll( + run.id, run.thread_id, extra_headers, extra_query, extra_body, timeout, poll_interval_ms + ) + @overload def create_and_run_stream( self, diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index aabe2c7fc9..b9f392dc87 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -14,6 +14,8 @@ Run, ) +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") From 632cd6feef405076abfa48a898e6bdfc57691996 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:39:54 +0200 Subject: [PATCH 379/914] release: 1.16.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7ccfe12c9e..bc845f32af 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.15.0" + ".": "1.16.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 180bbf2a28..ce046a623d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.16.0 (2024-04-01) + +Full Changelog: [v1.15.0...v1.16.0](https://github.com/openai/openai-python/compare/v1.15.0...v1.16.0) + +### Features + +* **api:** add support for filtering messages by run_id ([#1288](https://github.com/openai/openai-python/issues/1288)) ([58d6b77](https://github.com/openai/openai-python/commit/58d6b773218ef1dd8dc6208124a16078e4ac11c1)) +* **api:** run polling helpers ([#1289](https://github.com/openai/openai-python/issues/1289)) ([6b427f3](https://github.com/openai/openai-python/commit/6b427f38610847bce3ce5334177f07917bd7c187)) + + +### Chores + +* **client:** validate that max_retries is not None ([#1286](https://github.com/openai/openai-python/issues/1286)) ([aa5920a](https://github.com/openai/openai-python/commit/aa5920af6131c49a44352524154ee4a1684e76b2)) + + +### Refactors + +* rename createAndStream to stream ([6b427f3](https://github.com/openai/openai-python/commit/6b427f38610847bce3ce5334177f07917bd7c187)) + ## 1.15.0 (2024-03-31) Full Changelog: [v1.14.3...v1.15.0](https://github.com/openai/openai-python/compare/v1.14.3...v1.15.0) diff --git a/pyproject.toml b/pyproject.toml index beb31f24a1..437a5e9cc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.15.0" +version = "1.16.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6865a9f7bd..fe724b63af 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.15.0" # x-release-please-version +__version__ = "1.16.0" # x-release-please-version From f0bdef04611a24ed150d19c4d180aacab3052704 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:11:51 +0200 Subject: [PATCH 380/914] release: 1.16.1 (#1292) * chore(internal): defer model build for import latency (#1291) * release: 1.16.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_models.py | 6 +++++- src/openai/_version.py | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bc845f32af..1937985906 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.16.0" + ".": "1.16.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ce046a623d..d0fe2d628e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.16.1 (2024-04-02) + +Full Changelog: [v1.16.0...v1.16.1](https://github.com/openai/openai-python/compare/v1.16.0...v1.16.1) + +### Chores + +* **internal:** defer model build for import latency ([#1291](https://github.com/openai/openai-python/issues/1291)) ([bc6866e](https://github.com/openai/openai-python/commit/bc6866eb2335d01532190d0906cad7bf9af28621)) + ## 1.16.0 (2024-04-01) Full Changelog: [v1.15.0...v1.16.0](https://github.com/openai/openai-python/compare/v1.15.0...v1.16.0) diff --git a/pyproject.toml b/pyproject.toml index 437a5e9cc8..efaa42595e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.16.0" +version = "1.16.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_models.py b/src/openai/_models.py index 77c755b135..0f001150f5 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import inspect from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast from datetime import date, datetime @@ -38,6 +39,7 @@ is_given, is_mapping, parse_date, + coerce_boolean, parse_datetime, strip_not_given, extract_type_arg, @@ -74,7 +76,9 @@ class _ConfigProtocol(Protocol): class BaseModel(pydantic.BaseModel): if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(extra="allow") + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) else: @property diff --git a/src/openai/_version.py b/src/openai/_version.py index fe724b63af..9d3ac751cd 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.16.0" # x-release-please-version +__version__ = "1.16.1" # x-release-please-version From 7e1e4572f46bb2ee495dc49017e589aca132c28c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:36:41 -0400 Subject: [PATCH 381/914] fix(client): correct logic for line decoding in streaming (#1293) --- src/openai/_streaming.py | 73 +++++++---- tests/test_streaming.py | 268 ++++++++++++++++++++++++++++++--------- 2 files changed, 253 insertions(+), 88 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 9c7cc6a573..0fda992cff 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -24,7 +24,7 @@ class Stream(Generic[_T]): response: httpx.Response - _decoder: SSEDecoder | SSEBytesDecoder + _decoder: SSEBytesDecoder def __init__( self, @@ -47,10 +47,7 @@ def __iter__(self) -> Iterator[_T]: yield item def _iter_events(self) -> Iterator[ServerSentEvent]: - if isinstance(self._decoder, SSEBytesDecoder): - yield from self._decoder.iter_bytes(self.response.iter_bytes()) - else: - yield from self._decoder.iter(self.response.iter_lines()) + yield from self._decoder.iter_bytes(self.response.iter_bytes()) def __stream__(self) -> Iterator[_T]: cast_to = cast(Any, self._cast_to) @@ -151,12 +148,8 @@ async def __aiter__(self) -> AsyncIterator[_T]: yield item async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: - if isinstance(self._decoder, SSEBytesDecoder): - async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): - yield sse - else: - async for sse in self._decoder.aiter(self.response.aiter_lines()): - yield sse + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): + yield sse async def __stream__(self) -> AsyncIterator[_T]: cast_to = cast(Any, self._cast_to) @@ -282,21 +275,49 @@ def __init__(self) -> None: self._last_event_id = None self._retry = None - def iter(self, iterator: Iterator[str]) -> Iterator[ServerSentEvent]: - """Given an iterator that yields lines, iterate over it & yield every event encountered""" - for line in iterator: - line = line.rstrip("\n") - sse = self.decode(line) - if sse is not None: - yield sse - - async def aiter(self, iterator: AsyncIterator[str]) -> AsyncIterator[ServerSentEvent]: - """Given an async iterator that yields lines, iterate over it & yield every event encountered""" - async for line in iterator: - line = line.rstrip("\n") - sse = self.decode(line) - if sse is not None: - yield sse + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + for chunk in self._iter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + async for chunk in self._aiter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + async for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data def decode(self, line: str) -> ServerSentEvent | None: # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 diff --git a/tests/test_streaming.py b/tests/test_streaming.py index 75e4ca2699..04f8e51abd 100644 --- a/tests/test_streaming.py +++ b/tests/test_streaming.py @@ -1,104 +1,248 @@ +from __future__ import annotations + from typing import Iterator, AsyncIterator +import httpx import pytest -from openai._streaming import SSEDecoder +from openai import OpenAI, AsyncOpenAI +from openai._streaming import Stream, AsyncStream, ServerSentEvent @pytest.mark.asyncio -async def test_basic_async() -> None: - async def body() -> AsyncIterator[str]: - yield "event: completion" - yield 'data: {"foo":true}' - yield "" - - async for sse in SSEDecoder().aiter(body()): - assert sse.event == "completion" - assert sse.json() == {"foo": True} +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_basic(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + def body() -> Iterator[bytes]: + yield b"event: completion\n" + yield b'data: {"foo":true}\n' + yield b"\n" + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) -def test_basic() -> None: - def body() -> Iterator[str]: - yield "event: completion" - yield 'data: {"foo":true}' - yield "" - - it = SSEDecoder().iter(body()) - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "completion" assert sse.json() == {"foo": True} - with pytest.raises(StopIteration): - next(it) + await assert_empty_iter(iterator) -def test_data_missing_event() -> None: - def body() -> Iterator[str]: - yield 'data: {"foo":true}' - yield "" +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_missing_event(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"foo":true}\n' + yield b"\n" - it = SSEDecoder().iter(body()) - sse = next(it) + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) assert sse.event is None assert sse.json() == {"foo": True} - with pytest.raises(StopIteration): - next(it) + await assert_empty_iter(iterator) + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_event_missing_data(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" -def test_event_missing_data() -> None: - def body() -> Iterator[str]: - yield "event: ping" - yield "" + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) - it = SSEDecoder().iter(body()) - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "ping" assert sse.data == "" - with pytest.raises(StopIteration): - next(it) + await assert_empty_iter(iterator) -def test_multiple_events() -> None: - def body() -> Iterator[str]: - yield "event: ping" - yield "" - yield "event: completion" - yield "" +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + yield b"event: completion\n" + yield b"\n" - it = SSEDecoder().iter(body()) + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "ping" assert sse.data == "" - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "completion" assert sse.data == "" - with pytest.raises(StopIteration): - next(it) - - -def test_multiple_events_with_data() -> None: - def body() -> Iterator[str]: - yield "event: ping" - yield 'data: {"foo":true}' - yield "" - yield "event: completion" - yield 'data: {"bar":false}' - yield "" + await assert_empty_iter(iterator) - it = SSEDecoder().iter(body()) - sse = next(it) +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events_with_data(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo":true}\n' + yield b"\n" + yield b"event: completion\n" + yield b'data: {"bar":false}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) assert sse.event == "ping" assert sse.json() == {"foo": True} - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "completion" assert sse.json() == {"bar": False} - with pytest.raises(StopIteration): - next(it) + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines_with_empty_line(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: \n" + yield b"data:\n" + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + assert sse.data == '{\n"foo":\n\n\ntrue}' + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_json_escaped_double_new_line(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo": "my long\\n\\ncontent"}' + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": "my long\n\ncontent"} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_special_new_line_character( + sync: bool, + client: OpenAI, + async_client: AsyncOpenAI, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":" culpa"}\n' + yield b"\n" + yield b'data: {"content":" \xe2\x80\xa8"}\n' + yield b"\n" + yield b'data: {"content":"foo"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " culpa"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " 
"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "foo"} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multi_byte_character_multiple_chunks( + sync: bool, + client: OpenAI, + async_client: AsyncOpenAI, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":"' + # bytes taken from the string 'известни' and arbitrarily split + # so that some multi-byte characters span multiple chunks + yield b"\xd0" + yield b"\xb8\xd0\xb7\xd0" + yield b"\xb2\xd0\xb5\xd1\x81\xd1\x82\xd0\xbd\xd0\xb8" + yield b'"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "известни"} + + +async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]: + for chunk in iter: + yield chunk + + +async def iter_next(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> ServerSentEvent: + if isinstance(iter, AsyncIterator): + return await iter.__anext__() + + return next(iter) + + +async def assert_empty_iter(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> None: + with pytest.raises((StopAsyncIteration, RuntimeError)): + await iter_next(iter) + + +def make_event_iterator( + content: Iterator[bytes], + *, + sync: bool, + client: OpenAI, + async_client: AsyncOpenAI, +) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]: + if sync: + return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events() + + return AsyncStream( + cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content)) + )._iter_events() From 0bdc3772a159f433a5c6c9bb18bfa3ea807599b4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:03:43 -0400 Subject: [PATCH 382/914] release: 1.16.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1937985906..fb1bd8f489 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.16.1" + ".": "1.16.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d0fe2d628e..3b22f06aae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.16.2 (2024-04-04) + +Full Changelog: [v1.16.1...v1.16.2](https://github.com/openai/openai-python/compare/v1.16.1...v1.16.2) + +### Bug Fixes + +* **client:** correct logic for line decoding in streaming ([#1293](https://github.com/openai/openai-python/issues/1293)) ([687caef](https://github.com/openai/openai-python/commit/687caefa4acf615bf404f16817bfd9a6f285ee5c)) + ## 1.16.1 (2024-04-02) Full Changelog: [v1.16.0...v1.16.1](https://github.com/openai/openai-python/compare/v1.16.0...v1.16.1) diff --git a/pyproject.toml b/pyproject.toml index efaa42595e..67006726fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.16.1" +version = "1.16.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9d3ac751cd..85803a60a6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.16.1" # x-release-please-version +__version__ = "1.16.2" # x-release-please-version From e2d65bf77aa6e2bca043f70f5e0a38d8bd08b5e8 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 5 Apr 2024 08:36:56 -0400 Subject: [PATCH 383/914] feat(api): add additional messages when creating thread run (#1298) --- .../resources/beta/threads/runs/runs.py | 42 ++++++++++ .../types/beta/threads/run_create_params.py | 37 ++++++++- tests/api_resources/beta/threads/test_runs.py | 80 +++++++++++++++++++ 3 files changed, 157 insertions(+), 2 deletions(-) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 4529c65025..8576a5c09a 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -75,6 +75,7 @@ def create( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -100,6 +101,8 @@ def create( is useful for modifying the behavior on a per-run basis without overriding other instructions. + additional_messages: Adds additional messages to the thread before creating the run. + instructions: Overrides the [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. @@ -143,6 +146,7 @@ def create( assistant_id: str, stream: Literal[True], additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -171,6 +175,8 @@ def create( is useful for modifying the behavior on a per-run basis without overriding other instructions. + additional_messages: Adds additional messages to the thread before creating the run. + instructions: Overrides the [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. @@ -210,6 +216,7 @@ def create( assistant_id: str, stream: bool, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -238,6 +245,8 @@ def create( is useful for modifying the behavior on a per-run basis without overriding other instructions. + additional_messages: Adds additional messages to the thread before creating the run. + instructions: Overrides the [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. @@ -276,6 +285,7 @@ def create( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -298,6 +308,7 @@ def create( { "assistant_id": assistant_id, "additional_instructions": additional_instructions, + "additional_messages": additional_messages, "instructions": instructions, "metadata": metadata, "model": model, @@ -505,6 +516,7 @@ def create_and_poll( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -528,6 +540,7 @@ def create_and_poll( thread_id=thread_id, assistant_id=assistant_id, additional_instructions=additional_instructions, + additional_messages=additional_messages, instructions=instructions, metadata=metadata, model=model, @@ -557,6 +570,7 @@ def create_and_stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -580,6 +594,7 @@ def create_and_stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -603,6 +618,7 @@ def create_and_stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -634,6 +650,7 @@ def create_and_stream( { "assistant_id": assistant_id, "additional_instructions": additional_instructions, + "additional_messages": additional_messages, "instructions": instructions, "metadata": metadata, "model": model, @@ -703,6 +720,7 @@ def stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -725,6 +743,7 @@ def stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -747,6 +766,7 @@ def stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -778,6 +798,7 @@ def stream( { "assistant_id": assistant_id, "additional_instructions": additional_instructions, + "additional_messages": additional_messages, "instructions": instructions, "metadata": metadata, "model": model, @@ -1100,6 +1121,7 @@ async def create( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1125,6 +1147,8 @@ async def create( is useful for modifying the behavior on a per-run basis without overriding other instructions. + additional_messages: Adds additional messages to the thread before creating the run. + instructions: Overrides the [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. @@ -1168,6 +1192,7 @@ async def create( assistant_id: str, stream: Literal[True], additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1196,6 +1221,8 @@ async def create( is useful for modifying the behavior on a per-run basis without overriding other instructions. + additional_messages: Adds additional messages to the thread before creating the run. + instructions: Overrides the [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. @@ -1235,6 +1262,7 @@ async def create( assistant_id: str, stream: bool, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1263,6 +1291,8 @@ async def create( is useful for modifying the behavior on a per-run basis without overriding other instructions. + additional_messages: Adds additional messages to the thread before creating the run. + instructions: Overrides the [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. @@ -1301,6 +1331,7 @@ async def create( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1323,6 +1354,7 @@ async def create( { "assistant_id": assistant_id, "additional_instructions": additional_instructions, + "additional_messages": additional_messages, "instructions": instructions, "metadata": metadata, "model": model, @@ -1530,6 +1562,7 @@ async def create_and_poll( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1553,6 +1586,7 @@ async def create_and_poll( thread_id=thread_id, assistant_id=assistant_id, additional_instructions=additional_instructions, + additional_messages=additional_messages, instructions=instructions, metadata=metadata, model=model, @@ -1582,6 +1616,7 @@ def create_and_stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1605,6 +1640,7 @@ def create_and_stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1628,6 +1664,7 @@ def create_and_stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1661,6 +1698,7 @@ def create_and_stream( { "assistant_id": assistant_id, "additional_instructions": additional_instructions, + "additional_messages": additional_messages, "instructions": instructions, "metadata": metadata, "model": model, @@ -1730,6 +1768,7 @@ def stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1752,6 +1791,7 @@ def stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1774,6 +1814,7 @@ def stream( *, assistant_id: str, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Optional[str] | NotGiven = NOT_GIVEN, @@ -1807,6 +1848,7 @@ def stream( { "assistant_id": assistant_id, "additional_instructions": additional_instructions, + "additional_messages": additional_messages, "instructions": instructions, "metadata": metadata, "model": model, diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index ac185973a5..e9bc19d980 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -2,12 +2,12 @@ from __future__ import annotations -from typing import Union, Iterable, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from ..assistant_tool_param import AssistantToolParam -__all__ = ["RunCreateParamsBase", "RunCreateParamsNonStreaming", "RunCreateParamsStreaming"] +__all__ = ["RunCreateParamsBase", "AdditionalMessage", "RunCreateParamsNonStreaming", "RunCreateParamsStreaming"] class RunCreateParamsBase(TypedDict, total=False): @@ -25,6 +25,9 @@ class RunCreateParamsBase(TypedDict, total=False): other instructions. """ + additional_messages: Optional[Iterable[AdditionalMessage]] + """Adds additional messages to the thread before creating the run.""" + instructions: Optional[str] """ Overrides the @@ -62,6 +65,36 @@ class RunCreateParamsBase(TypedDict, total=False): """ +class AdditionalMessage(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[Literal["user", "assistant"]] + """The role of the entity that is creating the message. Allowed values include: + + - `user`: Indicates the message is sent by an actual user and should be used in + most cases to represent user-generated messages. + - `assistant`: Indicates the message is generated by the assistant. Use this + value to insert messages from the assistant into the conversation. + """ + + file_ids: List[str] + """ + A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the message should use. There can be a maximum of 10 files attached to a + message. Useful for tools like `retrieval` and `code_interpreter` that can + access and use files. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + class RunCreateParamsNonStreaming(RunCreateParamsBase): stream: Optional[Literal[False]] """ diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index b9f392dc87..271bcccdd3 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -36,6 +36,26 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "string", assistant_id="string", additional_instructions="string", + additional_messages=[ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], instructions="string", metadata={}, model="string", @@ -95,6 +115,26 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: assistant_id="string", stream=True, additional_instructions="string", + additional_messages=[ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], instructions="string", metadata={}, model="string", @@ -492,6 +532,26 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "string", assistant_id="string", additional_instructions="string", + additional_messages=[ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], instructions="string", metadata={}, model="string", @@ -551,6 +611,26 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn assistant_id="string", stream=True, additional_instructions="string", + additional_messages=[ + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + { + "role": "user", + "content": "x", + "file_ids": ["string"], + "metadata": {}, + }, + ], instructions="string", metadata={}, model="string", From 347363ed67a6a1611346427bb9ebe4becce53f7e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:50:01 +0200 Subject: [PATCH 384/914] feat(client): add DefaultHttpxClient and DefaultAsyncHttpxClient (#1302) --- README.md | 5 ++--- src/openai/__init__.py | 3 +++ src/openai/_base_client.py | 44 ++++++++++++++++++++++++++++++++++++-- src/openai/_client.py | 8 +++++-- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5264026dc9..f007d9187b 100644 --- a/README.md +++ b/README.md @@ -549,13 +549,12 @@ You can directly override the [httpx client](https://www.python-httpx.org/api/#c - Additional [advanced](https://www.python-httpx.org/advanced/#client-instances) functionality ```python -import httpx -from openai import OpenAI +from openai import OpenAI, DefaultHttpxClient client = OpenAI( # Or use the `OPENAI_BASE_URL` env var base_url="http://my.test.server.example.com:8083/", - http_client=httpx.Client( + http_client=DefaultHttpxClient( proxies="http://my.test.proxy.example.com/", transport=httpx.HTTPTransport(local_address="0.0.0.0"), ), diff --git a/src/openai/__init__.py b/src/openai/__init__.py index cd05a749da..1daa26f7b7 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -29,6 +29,7 @@ UnprocessableEntityError, APIResponseValidationError, ) +from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -67,6 +68,8 @@ "DEFAULT_TIMEOUT", "DEFAULT_MAX_RETRIES", "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", ] from .lib import azure as _azure diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 502ed7c7ae..0bb284a211 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -716,7 +716,27 @@ def _idempotency_key(self) -> str: return f"stainless-python-retry-{uuid.uuid4()}" -class SyncHttpxClientWrapper(httpx.Client): +class _DefaultHttpxClient(httpx.Client): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultHttpxClient = httpx.Client + """An alias to `httpx.Client` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.Client` will result in httpx's defaults being used, not ours. + """ +else: + DefaultHttpxClient = _DefaultHttpxClient + + +class SyncHttpxClientWrapper(DefaultHttpxClient): def __del__(self) -> None: try: self.close() @@ -1262,7 +1282,27 @@ def get_api_list( return self._request_api_list(model, page, opts) -class AsyncHttpxClientWrapper(httpx.AsyncClient): +class _DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultAsyncHttpxClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.AsyncClient` will result in httpx's defaults being used, not ours. + """ +else: + DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + + +class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): def __del__(self) -> None: try: # TODO(someday): support non asyncio runtimes here diff --git a/src/openai/_client.py b/src/openai/_client.py index 7fe2c9af79..e9169df72a 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -74,7 +74,9 @@ def __init__( max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + # Configure a custom httpx client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. http_client: httpx.Client | None = None, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised @@ -272,7 +274,9 @@ def __init__( max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + # Configure a custom httpx client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. http_client: httpx.AsyncClient | None = None, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised From 47656567e016a1ddf09497f180a19265b28ec93c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 9 Apr 2024 08:20:45 -0400 Subject: [PATCH 385/914] feat(models): add to_dict & to_json helper methods (#1305) --- README.md | 8 ++-- examples/azure.py | 4 +- examples/azure_ad.py | 2 +- examples/streaming.py | 8 ++-- src/openai/_models.py | 73 +++++++++++++++++++++++++++++++++++ src/openai/lib/_validators.py | 10 +++-- tests/test_models.py | 64 ++++++++++++++++++++++++++++++ 7 files changed, 155 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f007d9187b..3bdd6c4a43 100644 --- a/README.md +++ b/README.md @@ -200,10 +200,10 @@ We recommend that you always instantiate a client (e.g., with `client = OpenAI() ## Using types -Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev), which provide helper methods for things like: +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: -- Serializing back into JSON, `model.model_dump_json(indent=2, exclude_unset=True)` -- Converting to a dictionary, `model.model_dump(exclude_unset=True)` +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. @@ -594,7 +594,7 @@ completion = client.chat.completions.create( }, ], ) -print(completion.model_dump_json(indent=2)) +print(completion.to_json()) ``` In addition to the options provided in the base `OpenAI` client, the following options are provided: diff --git a/examples/azure.py b/examples/azure.py index a28b8cc433..6936c4cb0e 100755 --- a/examples/azure.py +++ b/examples/azure.py @@ -20,7 +20,7 @@ }, ], ) -print(completion.model_dump_json(indent=2)) +print(completion.to_json()) deployment_client = AzureOpenAI( @@ -40,4 +40,4 @@ }, ], ) -print(completion.model_dump_json(indent=2)) +print(completion.to_json()) diff --git a/examples/azure_ad.py b/examples/azure_ad.py index f13079dd04..1b0d81863d 100755 --- a/examples/azure_ad.py +++ b/examples/azure_ad.py @@ -27,4 +27,4 @@ }, ], ) -print(completion.model_dump_json(indent=2)) +print(completion.to_json()) diff --git a/examples/streaming.py b/examples/streaming.py index 368fa5f911..9a84891a83 100755 --- a/examples/streaming.py +++ b/examples/streaming.py @@ -22,12 +22,12 @@ def sync_main() -> None: # You can manually control iteration over the response first = next(response) - print(f"got response data: {first.model_dump_json(indent=2)}") + print(f"got response data: {first.to_json()}") # Or you could automatically iterate through all of data. # Note that the for loop will not exit until *all* of the data has been processed. for data in response: - print(data.model_dump_json()) + print(data.to_json()) async def async_main() -> None: @@ -43,12 +43,12 @@ async def async_main() -> None: # You can manually control iteration over the response. # In Python 3.10+ you can also use the `await anext(response)` builtin instead first = await response.__anext__() - print(f"got response data: {first.model_dump_json(indent=2)}") + print(f"got response data: {first.to_json()}") # Or you could automatically iterate through all of data. # Note that the for loop will not exit until *all* of the data has been processed. async for data in response: - print(data.model_dump_json()) + print(data.to_json()) sync_main() diff --git a/src/openai/_models.py b/src/openai/_models.py index 0f001150f5..80ab51256f 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -90,6 +90,79 @@ def model_fields_set(self) -> set[str]: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] extra: Any = pydantic.Extra.allow # type: ignore + def to_dict( + self, + *, + mode: Literal["json", "python"] = "python", + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> dict[str, object]: + """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + mode: + If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. + If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` + + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. + """ + return self.model_dump( + mode=mode, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + def to_json( + self, + *, + indent: int | None = 2, + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> str: + """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. + """ + return self.model_dump_json( + indent=indent, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + @override def __str__(self) -> str: # mypy complains about an invalid self arg diff --git a/src/openai/lib/_validators.py b/src/openai/lib/_validators.py index e36f0e95fb..cf24cd2294 100644 --- a/src/openai/lib/_validators.py +++ b/src/openai/lib/_validators.py @@ -678,9 +678,11 @@ def write_out_file(df: pd.DataFrame, fname: str, any_remediations: bool, auto_ac df_train = df.sample(n=n_train, random_state=42) df_valid = df.drop(df_train.index) df_train[["prompt", "completion"]].to_json( # type: ignore - fnames[0], lines=True, orient="records", force_ascii=False + fnames[0], lines=True, orient="records", force_ascii=False, indent=None + ) + df_valid[["prompt", "completion"]].to_json( + fnames[1], lines=True, orient="records", force_ascii=False, indent=None ) - df_valid[["prompt", "completion"]].to_json(fnames[1], lines=True, orient="records", force_ascii=False) n_classes, pos_class = get_classification_hyperparams(df) additional_params += " --compute_classification_metrics" @@ -690,7 +692,9 @@ def write_out_file(df: pd.DataFrame, fname: str, any_remediations: bool, auto_ac additional_params += f" --classification_n_classes {n_classes}" else: assert len(fnames) == 1 - df[["prompt", "completion"]].to_json(fnames[0], lines=True, orient="records", force_ascii=False) + df[["prompt", "completion"]].to_json( + fnames[0], lines=True, orient="records", force_ascii=False, indent=None + ) # Add -v VALID_FILE if we split the file into train / valid files_string = ("s" if split else "") + " to `" + ("` and `".join(fnames)) diff --git a/tests/test_models.py b/tests/test_models.py index d003d32181..969e4eb315 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -501,6 +501,42 @@ class Model(BaseModel): assert "resource_id" in m.model_fields_set +def test_to_dict() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.to_dict() == {"FOO": "hello"} + assert m.to_dict(use_api_names=False) == {"foo": "hello"} + + m2 = Model() + assert m2.to_dict() == {} + assert m2.to_dict(exclude_unset=False) == {"FOO": None} + assert m2.to_dict(exclude_unset=False, exclude_none=True) == {} + assert m2.to_dict(exclude_unset=False, exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.to_dict() == {"FOO": None} + assert m3.to_dict(exclude_none=True) == {} + assert m3.to_dict(exclude_defaults=True) == {} + + if PYDANTIC_V2: + + class Model2(BaseModel): + created_at: datetime + + time_str = "2024-03-21T11:39:01.275859" + m4 = Model2.construct(created_at=time_str) + assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} + assert m4.to_dict(mode="json") == {"created_at": time_str} + else: + with pytest.raises(ValueError, match="mode is only supported in Pydantic v2"): + m.to_dict(mode="json") + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_dict(warnings=False) + + def test_forwards_compat_model_dump_method() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) @@ -532,6 +568,34 @@ class Model(BaseModel): m.model_dump(warnings=False) +def test_to_json() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.to_json()) == {"FOO": "hello"} + assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} + + if PYDANTIC_V2: + assert m.to_json(indent=None) == '{"FOO":"hello"}' + else: + assert m.to_json(indent=None) == '{"FOO": "hello"}' + + m2 = Model() + assert json.loads(m2.to_json()) == {} + assert json.loads(m2.to_json(exclude_unset=False)) == {"FOO": None} + assert json.loads(m2.to_json(exclude_unset=False, exclude_none=True)) == {} + assert json.loads(m2.to_json(exclude_unset=False, exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.to_json()) == {"FOO": None} + assert json.loads(m3.to_json(exclude_none=True)) == {} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_json(warnings=False) + + def test_forwards_compat_model_dump_json_method() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) From 0cbce3f6777fd8f137ed7445e9bc2f2aa5c9f5b4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 10 Apr 2024 01:03:43 -0400 Subject: [PATCH 386/914] release: 1.17.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 10 ++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fb1bd8f489..6a197bef5a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.16.2" + ".": "1.17.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b22f06aae..0da030b337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.17.0 (2024-04-10) + +Full Changelog: [v1.16.2...v1.17.0](https://github.com/openai/openai-python/compare/v1.16.2...v1.17.0) + +### Features + +* **api:** add additional messages when creating thread run ([#1298](https://github.com/openai/openai-python/issues/1298)) ([70eb081](https://github.com/openai/openai-python/commit/70eb081804b14cc8c151ebd85458545a50a074fd)) +* **client:** add DefaultHttpxClient and DefaultAsyncHttpxClient ([#1302](https://github.com/openai/openai-python/issues/1302)) ([69cdfc3](https://github.com/openai/openai-python/commit/69cdfc319fff7ebf28cdd13cc6c1761b7d97811d)) +* **models:** add to_dict & to_json helper methods ([#1305](https://github.com/openai/openai-python/issues/1305)) ([40a881d](https://github.com/openai/openai-python/commit/40a881d10442af8b445ce030f8ab338710e1c4c8)) + ## 1.16.2 (2024-04-04) Full Changelog: [v1.16.1...v1.16.2](https://github.com/openai/openai-python/compare/v1.16.1...v1.16.2) diff --git a/pyproject.toml b/pyproject.toml index 67006726fb..b3043bc0cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.16.2" +version = "1.17.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 85803a60a6..0c55423216 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.16.2" # x-release-please-version +__version__ = "1.17.0" # x-release-please-version From 35afd5039e230152cc4486729f4e856ed5a76616 Mon Sep 17 00:00:00 2001 From: Zeeland Date: Wed, 10 Apr 2024 18:25:42 +0800 Subject: [PATCH 387/914] chore: fix typo (#1304) --- src/openai/_utils/_proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_utils/_proxy.py b/src/openai/_utils/_proxy.py index b9c12dc3f4..c46a62a698 100644 --- a/src/openai/_utils/_proxy.py +++ b/src/openai/_utils/_proxy.py @@ -10,7 +10,7 @@ class LazyProxy(Generic[T], ABC): """Implements data methods to pretend that an instance is another instance. - This includes forwarding attribute access and othe methods. + This includes forwarding attribute access and other methods. """ # Note: we have to special case proxies that themselves return proxies From b36b8d869a4e79ee8549476d979cd3322c3695c5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:52:41 -0400 Subject: [PATCH 388/914] chore(internal): formatting (#1311) --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec10edfe36..c44028d96c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 - RYE_INSTALL_OPTION: "--yes" + RYE_INSTALL_OPTION: '--yes' - name: Install dependencies run: | @@ -39,3 +39,5 @@ jobs: - name: Ensure importable run: | rye run python -c 'import openai' + + From 595e6b834fe26e08e9ac5415ab15bc19b8661f2c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:53:12 -0400 Subject: [PATCH 389/914] release: 1.17.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6a197bef5a..3741b313a5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.17.0" + ".": "1.17.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0da030b337..7e18ab5f54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.17.1 (2024-04-12) + +Full Changelog: [v1.17.0...v1.17.1](https://github.com/openai/openai-python/compare/v1.17.0...v1.17.1) + +### Chores + +* fix typo ([#1304](https://github.com/openai/openai-python/issues/1304)) ([1129082](https://github.com/openai/openai-python/commit/1129082955f98d76c0927781ef9e7d0beeda2ec4)) +* **internal:** formatting ([#1311](https://github.com/openai/openai-python/issues/1311)) ([8fd411b](https://github.com/openai/openai-python/commit/8fd411b48b6b1eafaab2dac26201525c1ee0b942)) + ## 1.17.0 (2024-04-10) Full Changelog: [v1.16.2...v1.17.0](https://github.com/openai/openai-python/compare/v1.16.2...v1.17.0) diff --git a/pyproject.toml b/pyproject.toml index b3043bc0cd..9eb6330616 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.17.0" +version = "1.17.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0c55423216..a4ffbb2c35 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.17.0" # x-release-please-version +__version__ = "1.17.1" # x-release-please-version From f5247e30eed8a1ad843c473c7a50f7bb81ffd7b5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:05:04 -0400 Subject: [PATCH 390/914] feat(api): updates (#1314) --- .stats.yml | 2 +- api.md | 40 +- .../resources/beta/assistants/assistants.py | 58 +- .../resources/beta/threads/runs/runs.py | 935 +++++++++++++++++- src/openai/resources/beta/threads/threads.py | 734 +++++++++++++- src/openai/resources/chat/completions.py | 34 +- .../resources/fine_tuning/fine_tuning.py | 1 + .../resources/fine_tuning/jobs/__init__.py | 33 + .../resources/fine_tuning/jobs/checkpoints.py | 176 ++++ .../resources/fine_tuning/{ => jobs}/jobs.py | 72 +- src/openai/types/beta/__init__.py | 12 + src/openai/types/beta/assistant.py | 2 +- .../types/beta/assistant_create_params.py | 32 +- .../types/beta/assistant_response_format.py | 13 + .../beta/assistant_response_format_option.py | 10 + .../assistant_response_format_option_param.py | 12 + .../beta/assistant_response_format_param.py | 12 + .../types/beta/assistant_tool_choice.py | 16 + .../beta/assistant_tool_choice_function.py | 10 + .../assistant_tool_choice_function_param.py | 12 + .../beta/assistant_tool_choice_option.py | 10 + .../assistant_tool_choice_option_param.py | 12 + .../types/beta/assistant_tool_choice_param.py | 16 + .../types/beta/assistant_update_params.py | 2 +- .../beta/thread_create_and_run_params.py | 94 +- src/openai/types/beta/threads/run.py | 87 +- .../types/beta/threads/run_create_params.py | 101 +- .../types/chat/completion_create_params.py | 5 +- src/openai/types/fine_tuning/__init__.py | 5 + .../types/fine_tuning/fine_tuning_job.py | 7 + .../fine_tuning_job_integration.py | 7 + .../fine_tuning_job_wandb_integration.py | 33 + ...ine_tuning_job_wandb_integration_object.py | 21 + .../types/fine_tuning/job_create_params.py | 57 +- src/openai/types/fine_tuning/jobs/__init__.py | 6 + .../jobs/checkpoint_list_params.py | 15 + .../jobs/fine_tuning_job_checkpoint.py | 47 + tests/api_resources/beta/test_assistants.py | 16 +- tests/api_resources/beta/test_threads.py | 40 +- tests/api_resources/beta/threads/test_runs.py | 40 +- tests/api_resources/chat/test_completions.py | 32 +- .../fine_tuning/jobs/__init__.py | 1 + .../fine_tuning/jobs/test_checkpoints.py | 117 +++ tests/api_resources/fine_tuning/test_jobs.py | 60 ++ 44 files changed, 2923 insertions(+), 124 deletions(-) create mode 100644 src/openai/resources/fine_tuning/jobs/__init__.py create mode 100644 src/openai/resources/fine_tuning/jobs/checkpoints.py rename src/openai/resources/fine_tuning/{ => jobs}/jobs.py (89%) create mode 100644 src/openai/types/beta/assistant_response_format.py create mode 100644 src/openai/types/beta/assistant_response_format_option.py create mode 100644 src/openai/types/beta/assistant_response_format_option_param.py create mode 100644 src/openai/types/beta/assistant_response_format_param.py create mode 100644 src/openai/types/beta/assistant_tool_choice.py create mode 100644 src/openai/types/beta/assistant_tool_choice_function.py create mode 100644 src/openai/types/beta/assistant_tool_choice_function_param.py create mode 100644 src/openai/types/beta/assistant_tool_choice_option.py create mode 100644 src/openai/types/beta/assistant_tool_choice_option_param.py create mode 100644 src/openai/types/beta/assistant_tool_choice_param.py create mode 100644 src/openai/types/fine_tuning/fine_tuning_job_integration.py create mode 100644 src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py create mode 100644 src/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py create mode 100644 src/openai/types/fine_tuning/jobs/__init__.py create mode 100644 src/openai/types/fine_tuning/jobs/checkpoint_list_params.py create mode 100644 src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py create mode 100644 tests/api_resources/fine_tuning/jobs/__init__.py create mode 100644 tests/api_resources/fine_tuning/jobs/test_checkpoints.py diff --git a/.stats.yml b/.stats.yml index c550abf3c6..284caebf44 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 51 +configured_endpoints: 52 diff --git a/api.md b/api.md index dbc95cd0b4..cc3c91a8d5 100644 --- a/api.md +++ b/api.md @@ -159,16 +159,34 @@ Methods: Types: ```python -from openai.types.fine_tuning import FineTuningJob, FineTuningJobEvent +from openai.types.fine_tuning import ( + FineTuningJob, + FineTuningJobEvent, + FineTuningJobIntegration, + FineTuningJobWandbIntegration, + FineTuningJobWandbIntegrationObject, +) +``` + +Methods: + +- client.fine_tuning.jobs.create(\*\*params) -> FineTuningJob +- client.fine_tuning.jobs.retrieve(fine_tuning_job_id) -> FineTuningJob +- client.fine_tuning.jobs.list(\*\*params) -> SyncCursorPage[FineTuningJob] +- client.fine_tuning.jobs.cancel(fine_tuning_job_id) -> FineTuningJob +- client.fine_tuning.jobs.list_events(fine_tuning_job_id, \*\*params) -> SyncCursorPage[FineTuningJobEvent] + +### Checkpoints + +Types: + +```python +from openai.types.fine_tuning.jobs import FineTuningJobCheckpoint ``` Methods: -- client.fine_tuning.jobs.create(\*\*params) -> FineTuningJob -- client.fine_tuning.jobs.retrieve(fine_tuning_job_id) -> FineTuningJob -- client.fine_tuning.jobs.list(\*\*params) -> SyncCursorPage[FineTuningJob] -- client.fine_tuning.jobs.cancel(fine_tuning_job_id) -> FineTuningJob -- client.fine_tuning.jobs.list_events(fine_tuning_job_id, \*\*params) -> SyncCursorPage[FineTuningJobEvent] +- client.fine_tuning.jobs.checkpoints.list(fine_tuning_job_id, \*\*params) -> SyncCursorPage[FineTuningJobCheckpoint] # Beta @@ -220,7 +238,15 @@ Methods: Types: ```python -from openai.types.beta import Thread, ThreadDeleted +from openai.types.beta import ( + AssistantResponseFormat, + AssistantResponseFormatOption, + AssistantToolChoice, + AssistantToolChoiceFunction, + AssistantToolChoiceOption, + Thread, + ThreadDeleted, +) ``` Methods: diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants/assistants.py index 232451ab25..9e88794ebc 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants/assistants.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Iterable, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal import httpx @@ -57,7 +57,29 @@ def with_streaming_response(self) -> AssistantsWithStreamingResponse: def create( self, *, - model: str, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ], description: Optional[str] | NotGiven = NOT_GIVEN, file_ids: List[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -87,7 +109,7 @@ def create( attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. - instructions: The system instructions that the assistant uses. The maximum length is 32768 + instructions: The system instructions that the assistant uses. The maximum length is 256,000 characters. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -194,7 +216,7 @@ def update( file was previously attached to the list but does not show up in the list, it will be deleted from the assistant. - instructions: The system instructions that the assistant uses. The maximum length is 32768 + instructions: The system instructions that the assistant uses. The maximum length is 256,000 characters. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -360,7 +382,29 @@ def with_streaming_response(self) -> AsyncAssistantsWithStreamingResponse: async def create( self, *, - model: str, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ], description: Optional[str] | NotGiven = NOT_GIVEN, file_ids: List[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -390,7 +434,7 @@ async def create( attached to this assistant. There can be a maximum of 20 files attached to the assistant. Files are ordered by their creation date in ascending order. - instructions: The system instructions that the assistant uses. The maximum length is 32768 + instructions: The system instructions that the assistant uses. The maximum length is 256,000 characters. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -497,7 +541,7 @@ async def update( file was previously attached to the list but does not show up in the list, it will be deleted from the assistant. - instructions: The system instructions that the assistant uses. The maximum length is 32768 + instructions: The system instructions that the assistant uses. The maximum length is 256,000 characters. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 8576a5c09a..9fa7239c0b 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -4,7 +4,7 @@ import time import typing_extensions -from typing import Iterable, Optional, overload +from typing import Union, Iterable, Optional, overload from functools import partial from typing_extensions import Literal @@ -31,7 +31,12 @@ from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....._streaming import Stream, AsyncStream from .....pagination import SyncCursorPage, AsyncCursorPage -from .....types.beta import AssistantToolParam, AssistantStreamEvent +from .....types.beta import ( + AssistantToolParam, + AssistantStreamEvent, + AssistantToolChoiceOptionParam, + AssistantResponseFormatOptionParam, +) from ....._base_client import ( AsyncPaginator, make_request_options, @@ -77,11 +82,40 @@ def create( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -107,6 +141,18 @@ def create( [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -117,6 +163,21 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + stream: If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message. @@ -125,6 +186,13 @@ def create( make the output more random, while lower values like 0.2 will make it more focused and deterministic. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -148,10 +216,39 @@ def create( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -181,6 +278,18 @@ def create( [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -191,10 +300,32 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -218,10 +349,39 @@ def create( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -251,6 +411,18 @@ def create( [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -261,10 +433,32 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -287,11 +481,40 @@ def create( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -310,11 +533,16 @@ def create( "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "stream": stream, "temperature": temperature, + "tool_choice": tool_choice, "tools": tools, + "truncation_strategy": truncation_strategy, }, run_create_params.RunCreateParams, ), @@ -518,10 +746,39 @@ def create_and_poll( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -542,12 +799,17 @@ def create_and_poll( additional_instructions=additional_instructions, additional_messages=additional_messages, instructions=instructions, + max_completion_tokens=max_completion_tokens, + max_prompt_tokens=max_prompt_tokens, metadata=metadata, model=model, + response_format=response_format, temperature=temperature, + tool_choice=tool_choice, # We assume we are not streaming when polling stream=False, tools=tools, + truncation_strategy=truncation_strategy, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, @@ -572,10 +834,39 @@ def create_and_stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -596,10 +887,39 @@ def create_and_stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -620,10 +940,39 @@ def create_and_stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -652,11 +1001,16 @@ def create_and_stream( "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "temperature": temperature, + "tool_choice": tool_choice, "stream": True, "tools": tools, + "truncation_strategy": truncation_strategy, }, run_create_params.RunCreateParams, ), @@ -722,10 +1076,39 @@ def stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -745,10 +1128,39 @@ def stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -768,10 +1180,39 @@ def stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -800,11 +1241,16 @@ def stream( "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "temperature": temperature, + "tool_choice": tool_choice, "stream": True, "tools": tools, + "truncation_strategy": truncation_strategy, }, run_create_params.RunCreateParams, ), @@ -1123,11 +1569,40 @@ async def create( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1153,6 +1628,18 @@ async def create( [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -1163,6 +1650,21 @@ async def create( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + stream: If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message. @@ -1171,6 +1673,13 @@ async def create( make the output more random, while lower values like 0.2 will make it more focused and deterministic. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -1194,10 +1703,39 @@ async def create( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1227,6 +1765,18 @@ async def create( [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -1237,10 +1787,32 @@ async def create( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -1264,10 +1836,39 @@ async def create( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1297,6 +1898,18 @@ async def create( [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -1307,10 +1920,32 @@ async def create( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -1333,11 +1968,40 @@ async def create( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1356,11 +2020,16 @@ async def create( "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "stream": stream, "temperature": temperature, + "tool_choice": tool_choice, "tools": tools, + "truncation_strategy": truncation_strategy, }, run_create_params.RunCreateParams, ), @@ -1564,10 +2233,39 @@ async def create_and_poll( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1588,12 +2286,17 @@ async def create_and_poll( additional_instructions=additional_instructions, additional_messages=additional_messages, instructions=instructions, + max_completion_tokens=max_completion_tokens, + max_prompt_tokens=max_prompt_tokens, metadata=metadata, model=model, + response_format=response_format, temperature=temperature, + tool_choice=tool_choice, # We assume we are not streaming when polling stream=False, tools=tools, + truncation_strategy=truncation_strategy, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, @@ -1618,10 +2321,39 @@ def create_and_stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1642,10 +2374,39 @@ def create_and_stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1666,10 +2427,39 @@ def create_and_stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1700,11 +2490,16 @@ def create_and_stream( "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "temperature": temperature, + "tool_choice": tool_choice, "stream": True, "tools": tools, + "truncation_strategy": truncation_strategy, }, run_create_params.RunCreateParams, ), @@ -1770,10 +2565,39 @@ def stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1793,10 +2617,39 @@ def stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1816,10 +2669,39 @@ def stream( additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1850,11 +2732,16 @@ def stream( "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "temperature": temperature, + "tool_choice": tool_choice, "stream": True, "tools": tools, + "truncation_strategy": truncation_strategy, }, run_create_params.RunCreateParams, ), diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 3509267d4f..9c2e2f0043 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterable, Optional, overload +from typing import Union, Iterable, Optional, overload from functools import partial from typing_extensions import Literal @@ -40,6 +40,8 @@ Thread, ThreadDeleted, AssistantStreamEvent, + AssistantToolChoiceOptionParam, + AssistantResponseFormatOptionParam, thread_create_params, thread_update_params, thread_create_and_run_params, @@ -241,12 +243,41 @@ def create_and_run( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -265,6 +296,18 @@ def create_and_run( instructions: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -275,6 +318,21 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + stream: If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message. @@ -285,6 +343,13 @@ def create_and_run( thread: If no thread is provided, an empty thread will be created. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -305,11 +370,40 @@ def create_and_run( assistant_id: str, stream: Literal[True], instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -332,6 +426,18 @@ def create_and_run( instructions: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -342,12 +448,34 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. thread: If no thread is provided, an empty thread will be created. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -368,11 +496,40 @@ def create_and_run( assistant_id: str, stream: bool, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -395,6 +552,18 @@ def create_and_run( instructions: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -405,12 +574,34 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. thread: If no thread is provided, an empty thread will be created. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -430,12 +621,41 @@ def create_and_run( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -450,12 +670,17 @@ def create_and_run( { "assistant_id": assistant_id, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "stream": stream, "temperature": temperature, "thread": thread, + "tool_choice": tool_choice, "tools": tools, + "truncation_strategy": truncation_strategy, }, thread_create_and_run_params.ThreadCreateAndRunParams, ), @@ -472,11 +697,40 @@ def create_and_run_poll( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -493,11 +747,16 @@ def create_and_run_poll( run = self.create_and_run( assistant_id=assistant_id, instructions=instructions, + max_completion_tokens=max_completion_tokens, + max_prompt_tokens=max_prompt_tokens, metadata=metadata, model=model, + response_format=response_format, temperature=temperature, stream=False, thread=thread, + tool_choice=tool_choice, + truncation_strategy=truncation_strategy, tools=tools, extra_headers=extra_headers, extra_query=extra_query, @@ -512,11 +771,40 @@ def create_and_run_stream( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -533,11 +821,40 @@ def create_and_run_stream( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -554,11 +871,40 @@ def create_and_run_stream( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -581,12 +927,17 @@ def create_and_run_stream( { "assistant_id": assistant_id, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "temperature": temperature, + "tool_choice": tool_choice, "stream": True, "thread": thread, "tools": tools, + "truncation_strategy": truncation_strategy, }, thread_create_and_run_params.ThreadCreateAndRunParams, ), @@ -780,12 +1131,41 @@ async def create_and_run( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -804,6 +1184,18 @@ async def create_and_run( instructions: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -814,6 +1206,21 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + stream: If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message. @@ -824,6 +1231,13 @@ async def create_and_run( thread: If no thread is provided, an empty thread will be created. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -844,11 +1258,40 @@ async def create_and_run( assistant_id: str, stream: Literal[True], instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -871,6 +1314,18 @@ async def create_and_run( instructions: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -881,12 +1336,34 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. thread: If no thread is provided, an empty thread will be created. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -907,11 +1384,40 @@ async def create_and_run( assistant_id: str, stream: bool, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -934,6 +1440,18 @@ async def create_and_run( instructions: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + max_completion_tokens: The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + + max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 @@ -944,12 +1462,34 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. thread: If no thread is provided, an empty thread will be created. + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -969,12 +1509,41 @@ async def create_and_run( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -989,12 +1558,17 @@ async def create_and_run( { "assistant_id": assistant_id, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "stream": stream, "temperature": temperature, "thread": thread, + "tool_choice": tool_choice, "tools": tools, + "truncation_strategy": truncation_strategy, }, thread_create_and_run_params.ThreadCreateAndRunParams, ), @@ -1011,11 +1585,40 @@ async def create_and_run_poll( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1032,11 +1635,16 @@ async def create_and_run_poll( run = await self.create_and_run( assistant_id=assistant_id, instructions=instructions, + max_completion_tokens=max_completion_tokens, + max_prompt_tokens=max_prompt_tokens, metadata=metadata, model=model, + response_format=response_format, temperature=temperature, stream=False, thread=thread, + tool_choice=tool_choice, + truncation_strategy=truncation_strategy, tools=tools, extra_headers=extra_headers, extra_query=extra_query, @@ -1053,11 +1661,40 @@ def create_and_run_stream( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1074,11 +1711,40 @@ def create_and_run_stream( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AsyncAssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1095,11 +1761,40 @@ def create_and_run_stream( *, assistant_id: str, instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Optional[str] | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] + | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, + tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AsyncAssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1124,12 +1819,17 @@ def create_and_run_stream( { "assistant_id": assistant_id, "instructions": instructions, + "max_completion_tokens": max_completion_tokens, + "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "response_format": response_format, "temperature": temperature, + "tool_choice": tool_choice, "stream": True, "thread": thread, "tools": tools, + "truncation_strategy": truncation_strategy, }, thread_create_and_run_params.ThreadCreateAndRunParams, ), diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 3000603689..1a23e7876e 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -50,6 +50,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", @@ -137,8 +139,7 @@ def create( logprobs: Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of - `message`. This option is currently not available on the `gpt-4-vision-preview` - model. + `message`. max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat completion. @@ -240,6 +241,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", @@ -334,8 +337,7 @@ def create( logprobs: Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of - `message`. This option is currently not available on the `gpt-4-vision-preview` - model. + `message`. max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat completion. @@ -430,6 +432,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", @@ -524,8 +528,7 @@ def create( logprobs: Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of - `message`. This option is currently not available on the `gpt-4-vision-preview` - model. + `message`. max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat completion. @@ -620,6 +623,8 @@ def create( model: Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", @@ -717,6 +722,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", @@ -804,8 +811,7 @@ async def create( logprobs: Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of - `message`. This option is currently not available on the `gpt-4-vision-preview` - model. + `message`. max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat completion. @@ -907,6 +913,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", @@ -1001,8 +1009,7 @@ async def create( logprobs: Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of - `message`. This option is currently not available on the `gpt-4-vision-preview` - model. + `message`. max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat completion. @@ -1097,6 +1104,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", @@ -1191,8 +1200,7 @@ async def create( logprobs: Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of - `message`. This option is currently not available on the `gpt-4-vision-preview` - model. + `message`. max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat completion. @@ -1287,6 +1295,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index 659b3e8501..0404fed6ec 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -11,6 +11,7 @@ AsyncJobsWithStreamingResponse, ) from ..._compat import cached_property +from .jobs.jobs import Jobs, AsyncJobs from ..._resource import SyncAPIResource, AsyncAPIResource __all__ = ["FineTuning", "AsyncFineTuning"] diff --git a/src/openai/resources/fine_tuning/jobs/__init__.py b/src/openai/resources/fine_tuning/jobs/__init__.py new file mode 100644 index 0000000000..94cd1fb7e7 --- /dev/null +++ b/src/openai/resources/fine_tuning/jobs/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .jobs import ( + Jobs, + AsyncJobs, + JobsWithRawResponse, + AsyncJobsWithRawResponse, + JobsWithStreamingResponse, + AsyncJobsWithStreamingResponse, +) +from .checkpoints import ( + Checkpoints, + AsyncCheckpoints, + CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse, +) + +__all__ = [ + "Checkpoints", + "AsyncCheckpoints", + "CheckpointsWithRawResponse", + "AsyncCheckpointsWithRawResponse", + "CheckpointsWithStreamingResponse", + "AsyncCheckpointsWithStreamingResponse", + "Jobs", + "AsyncJobs", + "JobsWithRawResponse", + "AsyncJobsWithRawResponse", + "JobsWithStreamingResponse", + "AsyncJobsWithStreamingResponse", +] diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py new file mode 100644 index 0000000000..e9ea6aad9a --- /dev/null +++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py @@ -0,0 +1,176 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) +from ....types.fine_tuning.jobs import FineTuningJobCheckpoint, checkpoint_list_params + +__all__ = ["Checkpoints", "AsyncCheckpoints"] + + +class Checkpoints(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CheckpointsWithRawResponse: + return CheckpointsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CheckpointsWithStreamingResponse: + return CheckpointsWithStreamingResponse(self) + + def list( + self, + fine_tuning_job_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[FineTuningJobCheckpoint]: + """ + List checkpoints for a fine-tuning job. + + Args: + after: Identifier for the last checkpoint ID from the previous pagination request. + + limit: Number of checkpoints to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._get_api_list( + f"/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints", + page=SyncCursorPage[FineTuningJobCheckpoint], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + checkpoint_list_params.CheckpointListParams, + ), + ), + model=FineTuningJobCheckpoint, + ) + + +class AsyncCheckpoints(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCheckpointsWithRawResponse: + return AsyncCheckpointsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCheckpointsWithStreamingResponse: + return AsyncCheckpointsWithStreamingResponse(self) + + def list( + self, + fine_tuning_job_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[FineTuningJobCheckpoint, AsyncCursorPage[FineTuningJobCheckpoint]]: + """ + List checkpoints for a fine-tuning job. + + Args: + after: Identifier for the last checkpoint ID from the previous pagination request. + + limit: Number of checkpoints to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._get_api_list( + f"/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints", + page=AsyncCursorPage[FineTuningJobCheckpoint], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + checkpoint_list_params.CheckpointListParams, + ), + ), + model=FineTuningJobCheckpoint, + ) + + +class CheckpointsWithRawResponse: + def __init__(self, checkpoints: Checkpoints) -> None: + self._checkpoints = checkpoints + + self.list = _legacy_response.to_raw_response_wrapper( + checkpoints.list, + ) + + +class AsyncCheckpointsWithRawResponse: + def __init__(self, checkpoints: AsyncCheckpoints) -> None: + self._checkpoints = checkpoints + + self.list = _legacy_response.async_to_raw_response_wrapper( + checkpoints.list, + ) + + +class CheckpointsWithStreamingResponse: + def __init__(self, checkpoints: Checkpoints) -> None: + self._checkpoints = checkpoints + + self.list = to_streamed_response_wrapper( + checkpoints.list, + ) + + +class AsyncCheckpointsWithStreamingResponse: + def __init__(self, checkpoints: AsyncCheckpoints) -> None: + self._checkpoints = checkpoints + + self.list = async_to_streamed_response_wrapper( + checkpoints.list, + ) diff --git a/src/openai/resources/fine_tuning/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py similarity index 89% rename from src/openai/resources/fine_tuning/jobs.py rename to src/openai/resources/fine_tuning/jobs/jobs.py index a0c3e24dac..229f716c48 100644 --- a/src/openai/resources/fine_tuning/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -2,26 +2,34 @@ from __future__ import annotations -from typing import Union, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal import httpx -from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( maybe_transform, async_maybe_transform, ) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import ( +from ...._compat import cached_property +from .checkpoints import ( + Checkpoints, + AsyncCheckpoints, + CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import ( AsyncPaginator, make_request_options, ) -from ...types.fine_tuning import ( +from ....types.fine_tuning import ( FineTuningJob, FineTuningJobEvent, job_list_params, @@ -33,6 +41,10 @@ class Jobs(SyncAPIResource): + @cached_property + def checkpoints(self) -> Checkpoints: + return Checkpoints(self._client) + @cached_property def with_raw_response(self) -> JobsWithRawResponse: return JobsWithRawResponse(self) @@ -47,6 +59,8 @@ def create( model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo"]], training_file: str, hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, + integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, validation_file: Optional[str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -82,6 +96,12 @@ def create( hyperparameters: The hyperparameters used for the fine-tuning job. + integrations: A list of integrations to enable for your fine-tuning job. + + seed: The seed controls the reproducibility of the job. Passing in the same seed and + job parameters should produce the same results, but may differ in rare cases. If + a seed is not specified, one will be generated for you. + suffix: A string of up to 18 characters that will be added to your fine-tuned model name. @@ -116,6 +136,8 @@ def create( "model": model, "training_file": training_file, "hyperparameters": hyperparameters, + "integrations": integrations, + "seed": seed, "suffix": suffix, "validation_file": validation_file, }, @@ -294,6 +316,10 @@ def list_events( class AsyncJobs(AsyncAPIResource): + @cached_property + def checkpoints(self) -> AsyncCheckpoints: + return AsyncCheckpoints(self._client) + @cached_property def with_raw_response(self) -> AsyncJobsWithRawResponse: return AsyncJobsWithRawResponse(self) @@ -308,6 +334,8 @@ async def create( model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo"]], training_file: str, hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, + integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, validation_file: Optional[str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -343,6 +371,12 @@ async def create( hyperparameters: The hyperparameters used for the fine-tuning job. + integrations: A list of integrations to enable for your fine-tuning job. + + seed: The seed controls the reproducibility of the job. Passing in the same seed and + job parameters should produce the same results, but may differ in rare cases. If + a seed is not specified, one will be generated for you. + suffix: A string of up to 18 characters that will be added to your fine-tuned model name. @@ -377,6 +411,8 @@ async def create( "model": model, "training_file": training_file, "hyperparameters": hyperparameters, + "integrations": integrations, + "seed": seed, "suffix": suffix, "validation_file": validation_file, }, @@ -574,6 +610,10 @@ def __init__(self, jobs: Jobs) -> None: jobs.list_events, ) + @cached_property + def checkpoints(self) -> CheckpointsWithRawResponse: + return CheckpointsWithRawResponse(self._jobs.checkpoints) + class AsyncJobsWithRawResponse: def __init__(self, jobs: AsyncJobs) -> None: @@ -595,6 +635,10 @@ def __init__(self, jobs: AsyncJobs) -> None: jobs.list_events, ) + @cached_property + def checkpoints(self) -> AsyncCheckpointsWithRawResponse: + return AsyncCheckpointsWithRawResponse(self._jobs.checkpoints) + class JobsWithStreamingResponse: def __init__(self, jobs: Jobs) -> None: @@ -616,6 +660,10 @@ def __init__(self, jobs: Jobs) -> None: jobs.list_events, ) + @cached_property + def checkpoints(self) -> CheckpointsWithStreamingResponse: + return CheckpointsWithStreamingResponse(self._jobs.checkpoints) + class AsyncJobsWithStreamingResponse: def __init__(self, jobs: AsyncJobs) -> None: @@ -636,3 +684,7 @@ def __init__(self, jobs: AsyncJobs) -> None: self.list_events = async_to_streamed_response_wrapper( jobs.list_events, ) + + @cached_property + def checkpoints(self) -> AsyncCheckpointsWithStreamingResponse: + return AsyncCheckpointsWithStreamingResponse(self._jobs.checkpoints) diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index a7de0272b4..0171694587 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -15,9 +15,21 @@ from .thread_create_params import ThreadCreateParams as ThreadCreateParams from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams from .assistant_list_params import AssistantListParams as AssistantListParams +from .assistant_tool_choice import AssistantToolChoice as AssistantToolChoice from .code_interpreter_tool import CodeInterpreterTool as CodeInterpreterTool from .assistant_stream_event import AssistantStreamEvent as AssistantStreamEvent from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams +from .assistant_response_format import AssistantResponseFormat as AssistantResponseFormat +from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam +from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams +from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction +from .assistant_response_format_param import AssistantResponseFormatParam as AssistantResponseFormatParam +from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption +from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam as AssistantToolChoiceOptionParam +from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam as AssistantToolChoiceFunctionParam +from .assistant_response_format_option_param import ( + AssistantResponseFormatOptionParam as AssistantResponseFormatOptionParam, +) diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 32561a9aa8..0a0d28ed01 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -29,7 +29,7 @@ class Assistant(BaseModel): instructions: Optional[str] = None """The system instructions that the assistant uses. - The maximum length is 32768 characters. + The maximum length is 256,000 characters. """ metadata: Optional[object] = None diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 8bad323640..011121485f 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -2,8 +2,8 @@ from __future__ import annotations -from typing import List, Iterable, Optional -from typing_extensions import Required, TypedDict +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict from .assistant_tool_param import AssistantToolParam @@ -11,7 +11,31 @@ class AssistantCreateParams(TypedDict, total=False): - model: Required[str] + model: Required[ + Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ] + ] """ID of the model to use. You can use the @@ -34,7 +58,7 @@ class AssistantCreateParams(TypedDict, total=False): instructions: Optional[str] """The system instructions that the assistant uses. - The maximum length is 32768 characters. + The maximum length is 256,000 characters. """ metadata: Optional[object] diff --git a/src/openai/types/beta/assistant_response_format.py b/src/openai/types/beta/assistant_response_format.py new file mode 100644 index 0000000000..f53bdaf62a --- /dev/null +++ b/src/openai/types/beta/assistant_response_format.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AssistantResponseFormat"] + + +class AssistantResponseFormat(BaseModel): + type: Optional[Literal["text", "json_object"]] = None + """Must be one of `text` or `json_object`.""" diff --git a/src/openai/types/beta/assistant_response_format_option.py b/src/openai/types/beta/assistant_response_format_option.py new file mode 100644 index 0000000000..d4e05e0ea9 --- /dev/null +++ b/src/openai/types/beta/assistant_response_format_option.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from .assistant_response_format import AssistantResponseFormat + +__all__ = ["AssistantResponseFormatOption"] + +AssistantResponseFormatOption = Union[Literal["none", "auto"], AssistantResponseFormat] diff --git a/src/openai/types/beta/assistant_response_format_option_param.py b/src/openai/types/beta/assistant_response_format_option_param.py new file mode 100644 index 0000000000..46e04125d1 --- /dev/null +++ b/src/openai/types/beta/assistant_response_format_option_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal + +from .assistant_response_format_param import AssistantResponseFormatParam + +__all__ = ["AssistantResponseFormatOptionParam"] + +AssistantResponseFormatOptionParam = Union[Literal["none", "auto"], AssistantResponseFormatParam] diff --git a/src/openai/types/beta/assistant_response_format_param.py b/src/openai/types/beta/assistant_response_format_param.py new file mode 100644 index 0000000000..96e1d02115 --- /dev/null +++ b/src/openai/types/beta/assistant_response_format_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["AssistantResponseFormatParam"] + + +class AssistantResponseFormatParam(TypedDict, total=False): + type: Literal["text", "json_object"] + """Must be one of `text` or `json_object`.""" diff --git a/src/openai/types/beta/assistant_tool_choice.py b/src/openai/types/beta/assistant_tool_choice.py new file mode 100644 index 0000000000..4314d4b41e --- /dev/null +++ b/src/openai/types/beta/assistant_tool_choice.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .assistant_tool_choice_function import AssistantToolChoiceFunction + +__all__ = ["AssistantToolChoice"] + + +class AssistantToolChoice(BaseModel): + type: Literal["function", "code_interpreter", "retrieval"] + """The type of the tool. If type is `function`, the function name must be set""" + + function: Optional[AssistantToolChoiceFunction] = None diff --git a/src/openai/types/beta/assistant_tool_choice_function.py b/src/openai/types/beta/assistant_tool_choice_function.py new file mode 100644 index 0000000000..87f38310ca --- /dev/null +++ b/src/openai/types/beta/assistant_tool_choice_function.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["AssistantToolChoiceFunction"] + + +class AssistantToolChoiceFunction(BaseModel): + name: str + """The name of the function to call.""" diff --git a/src/openai/types/beta/assistant_tool_choice_function_param.py b/src/openai/types/beta/assistant_tool_choice_function_param.py new file mode 100644 index 0000000000..428857de91 --- /dev/null +++ b/src/openai/types/beta/assistant_tool_choice_function_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AssistantToolChoiceFunctionParam"] + + +class AssistantToolChoiceFunctionParam(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" diff --git a/src/openai/types/beta/assistant_tool_choice_option.py b/src/openai/types/beta/assistant_tool_choice_option.py new file mode 100644 index 0000000000..0045a5986e --- /dev/null +++ b/src/openai/types/beta/assistant_tool_choice_option.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from .assistant_tool_choice import AssistantToolChoice + +__all__ = ["AssistantToolChoiceOption"] + +AssistantToolChoiceOption = Union[Literal["none", "auto"], AssistantToolChoice] diff --git a/src/openai/types/beta/assistant_tool_choice_option_param.py b/src/openai/types/beta/assistant_tool_choice_option_param.py new file mode 100644 index 0000000000..618e7bff98 --- /dev/null +++ b/src/openai/types/beta/assistant_tool_choice_option_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal + +from .assistant_tool_choice_param import AssistantToolChoiceParam + +__all__ = ["AssistantToolChoiceOptionParam"] + +AssistantToolChoiceOptionParam = Union[Literal["none", "auto"], AssistantToolChoiceParam] diff --git a/src/openai/types/beta/assistant_tool_choice_param.py b/src/openai/types/beta/assistant_tool_choice_param.py new file mode 100644 index 0000000000..5cf6ea27be --- /dev/null +++ b/src/openai/types/beta/assistant_tool_choice_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam + +__all__ = ["AssistantToolChoiceParam"] + + +class AssistantToolChoiceParam(TypedDict, total=False): + type: Required[Literal["function", "code_interpreter", "retrieval"]] + """The type of the tool. If type is `function`, the function name must be set""" + + function: AssistantToolChoiceFunctionParam diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 7c96aca8c1..6e9d9ed5db 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -26,7 +26,7 @@ class AssistantUpdateParams(TypedDict, total=False): instructions: Optional[str] """The system instructions that the assistant uses. - The maximum length is 32768 characters. + The maximum length is 256,000 characters. """ metadata: Optional[object] diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index d4266fc48c..50f947a40a 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -8,12 +8,15 @@ from .function_tool_param import FunctionToolParam from .retrieval_tool_param import RetrievalToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam +from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from .assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = [ "ThreadCreateAndRunParamsBase", "Thread", "ThreadMessage", "Tool", + "TruncationStrategy", "ThreadCreateAndRunParamsNonStreaming", "ThreadCreateAndRunParamsStreaming", ] @@ -33,6 +36,24 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): This is useful for modifying the behavior on a per-run basis. """ + max_completion_tokens: Optional[int] + """ + The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + """ + + max_prompt_tokens: Optional[int] + """The maximum number of prompt tokens that may be used over the course of the run. + + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + """ + metadata: Optional[object] """Set of 16 key-value pairs that can be attached to an object. @@ -41,7 +62,30 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): a maxium of 512 characters long. """ - model: Optional[str] + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] """ The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -49,6 +93,25 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): assistant will be used. """ + response_format: Optional[AssistantResponseFormatOptionParam] + """Specifies the format that the model must output. + + Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + temperature: Optional[float] """What sampling temperature to use, between 0 and 2. @@ -59,12 +122,24 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): thread: Thread """If no thread is provided, an empty thread will be created.""" + tool_choice: Optional[AssistantToolChoiceOptionParam] + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + """ + tools: Optional[Iterable[Tool]] """Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. """ + truncation_strategy: Optional[TruncationStrategy] + class ThreadMessage(TypedDict, total=False): content: Required[str] @@ -115,6 +190,23 @@ class Thread(TypedDict, total=False): Tool = Union[CodeInterpreterToolParam, RetrievalToolParam, FunctionToolParam] +class TruncationStrategy(TypedDict, total=False): + type: Required[Literal["auto", "last_messages"]] + """The truncation strategy to use for the thread. + + The default is `auto`. If set to `last_messages`, the thread will be truncated + to the n most recent messages in the thread. When set to `auto`, messages in the + middle of the thread will be dropped to fit the context length of the model, + `max_prompt_tokens`. + """ + + last_messages: Optional[int] + """ + The number of most recent messages from the thread when constructing the context + for the run. + """ + + class ThreadCreateAndRunParamsNonStreaming(ThreadCreateAndRunParamsBase): stream: Optional[Literal[False]] """ diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 3ab276245f..2efc3c77fa 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -6,9 +6,28 @@ from ...._models import BaseModel from .run_status import RunStatus from ..assistant_tool import AssistantTool +from ..assistant_tool_choice_option import AssistantToolChoiceOption +from ..assistant_response_format_option import AssistantResponseFormatOption from .required_action_function_tool_call import RequiredActionFunctionToolCall -__all__ = ["Run", "LastError", "RequiredAction", "RequiredActionSubmitToolOutputs", "Usage"] +__all__ = [ + "Run", + "IncompleteDetails", + "LastError", + "RequiredAction", + "RequiredActionSubmitToolOutputs", + "TruncationStrategy", + "Usage", +] + + +class IncompleteDetails(BaseModel): + reason: Optional[Literal["max_completion_tokens", "max_prompt_tokens"]] = None + """The reason why the run is incomplete. + + This will point to which specific token limit was reached over the course of the + run. + """ class LastError(BaseModel): @@ -32,6 +51,23 @@ class RequiredAction(BaseModel): """For now, this is always `submit_tool_outputs`.""" +class TruncationStrategy(BaseModel): + type: Literal["auto", "last_messages"] + """The truncation strategy to use for the thread. + + The default is `auto`. If set to `last_messages`, the thread will be truncated + to the n most recent messages in the thread. When set to `auto`, messages in the + middle of the thread will be dropped to fit the context length of the model, + `max_prompt_tokens`. + """ + + last_messages: Optional[int] = None + """ + The number of most recent messages from the thread when constructing the context + for the run. + """ + + class Usage(BaseModel): completion_tokens: int """Number of completion tokens used over the course of the run.""" @@ -76,6 +112,12 @@ class Run(BaseModel): this run. """ + incomplete_details: Optional[IncompleteDetails] = None + """Details on why the run is incomplete. + + Will be `null` if the run is not incomplete. + """ + instructions: str """ The instructions that the @@ -86,6 +128,18 @@ class Run(BaseModel): last_error: Optional[LastError] = None """The last error associated with this run. Will be `null` if there are no errors.""" + max_completion_tokens: Optional[int] = None + """ + The maximum number of completion tokens specified to have been used over the + course of the run. + """ + + max_prompt_tokens: Optional[int] = None + """ + The maximum number of prompt tokens specified to have been used over the course + of the run. + """ + metadata: Optional[object] = None """Set of 16 key-value pairs that can be attached to an object. @@ -110,6 +164,25 @@ class Run(BaseModel): Will be `null` if no action is required. """ + response_format: Optional[AssistantResponseFormatOption] = None + """Specifies the format that the model must output. + + Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + started_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run was started.""" @@ -126,6 +199,16 @@ class Run(BaseModel): that was executed on as a part of this run. """ + tool_choice: Optional[AssistantToolChoiceOption] = None + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + """ + tools: List[AssistantTool] """ The list of tools that the @@ -133,6 +216,8 @@ class Run(BaseModel): this run. """ + truncation_strategy: Optional[TruncationStrategy] = None + usage: Optional[Usage] = None """Usage statistics related to the run. diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index e9bc19d980..9f2d4ba18b 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -6,8 +6,16 @@ from typing_extensions import Literal, Required, TypedDict from ..assistant_tool_param import AssistantToolParam +from ..assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from ..assistant_response_format_option_param import AssistantResponseFormatOptionParam -__all__ = ["RunCreateParamsBase", "AdditionalMessage", "RunCreateParamsNonStreaming", "RunCreateParamsStreaming"] +__all__ = [ + "RunCreateParamsBase", + "AdditionalMessage", + "TruncationStrategy", + "RunCreateParamsNonStreaming", + "RunCreateParamsStreaming", +] class RunCreateParamsBase(TypedDict, total=False): @@ -35,6 +43,24 @@ class RunCreateParamsBase(TypedDict, total=False): of the assistant. This is useful for modifying the behavior on a per-run basis. """ + max_completion_tokens: Optional[int] + """ + The maximum number of completion tokens that may be used over the course of the + run. The run will make a best effort to use only the number of completion tokens + specified, across multiple turns of the run. If the run exceeds the number of + completion tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + """ + + max_prompt_tokens: Optional[int] + """The maximum number of prompt tokens that may be used over the course of the run. + + The run will make a best effort to use only the number of prompt tokens + specified, across multiple turns of the run. If the run exceeds the number of + prompt tokens specified, the run will end with status `complete`. See + `incomplete_details` for more info. + """ + metadata: Optional[object] """Set of 16 key-value pairs that can be attached to an object. @@ -43,7 +69,30 @@ class RunCreateParamsBase(TypedDict, total=False): a maxium of 512 characters long. """ - model: Optional[str] + model: Union[ + str, + Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + None, + ] """ The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -51,6 +100,25 @@ class RunCreateParamsBase(TypedDict, total=False): assistant will be used. """ + response_format: Optional[AssistantResponseFormatOptionParam] + """Specifies the format that the model must output. + + Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + temperature: Optional[float] """What sampling temperature to use, between 0 and 2. @@ -58,12 +126,24 @@ class RunCreateParamsBase(TypedDict, total=False): 0.2 will make it more focused and deterministic. """ + tool_choice: Optional[AssistantToolChoiceOptionParam] + """ + Controls which (if any) tool is called by the model. `none` means the model will + not call any tools and instead generates a message. `auto` is the default value + and means the model can pick between generating a message or calling a tool. + Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + `{"type": "function", "function": {"name": "my_function"}}` forces the model to + call that tool. + """ + tools: Optional[Iterable[AssistantToolParam]] """Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. """ + truncation_strategy: Optional[TruncationStrategy] + class AdditionalMessage(TypedDict, total=False): content: Required[str] @@ -95,6 +175,23 @@ class AdditionalMessage(TypedDict, total=False): """ +class TruncationStrategy(TypedDict, total=False): + type: Required[Literal["auto", "last_messages"]] + """The truncation strategy to use for the thread. + + The default is `auto`. If set to `last_messages`, the thread will be truncated + to the n most recent messages in the thread. When set to `auto`, messages in the + middle of the thread will be dropped to fit the context length of the model, + `max_prompt_tokens`. + """ + + last_messages: Optional[int] + """ + The number of most recent messages from the thread when constructing the context + for the run. + """ + + class RunCreateParamsNonStreaming(RunCreateParamsBase): stream: Optional[Literal[False]] """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index ab6a747021..1e0f7f8195 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -32,6 +32,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): Union[ str, Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", "gpt-4-turbo-preview", "gpt-4-1106-preview", @@ -102,8 +104,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): """Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the - `content` of `message`. This option is currently not available on the - `gpt-4-vision-preview` model. + `content` of `message`. """ max_tokens: Optional[int] diff --git a/src/openai/types/fine_tuning/__init__.py b/src/openai/types/fine_tuning/__init__.py index 0bb2b90438..92b81329b1 100644 --- a/src/openai/types/fine_tuning/__init__.py +++ b/src/openai/types/fine_tuning/__init__.py @@ -7,3 +7,8 @@ from .job_create_params import JobCreateParams as JobCreateParams from .fine_tuning_job_event import FineTuningJobEvent as FineTuningJobEvent from .job_list_events_params import JobListEventsParams as JobListEventsParams +from .fine_tuning_job_integration import FineTuningJobIntegration as FineTuningJobIntegration +from .fine_tuning_job_wandb_integration import FineTuningJobWandbIntegration as FineTuningJobWandbIntegration +from .fine_tuning_job_wandb_integration_object import ( + FineTuningJobWandbIntegrationObject as FineTuningJobWandbIntegrationObject, +) diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index 23fe96d1a0..1593bf50c7 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject __all__ = ["FineTuningJob", "Error", "Hyperparameters"] @@ -80,6 +81,9 @@ class FineTuningJob(BaseModel): [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents). """ + seed: int + """The seed used for the fine-tuning job.""" + status: Literal["validating_files", "queued", "running", "succeeded", "failed", "cancelled"] """ The current status of the fine-tuning job, which can be either @@ -105,3 +109,6 @@ class FineTuningJob(BaseModel): You can retrieve the validation results with the [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents). """ + + integrations: Optional[List[FineTuningJobWandbIntegrationObject]] = None + """A list of integrations to enable for this fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/fine_tuning_job_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_integration.py new file mode 100644 index 0000000000..8076313cae --- /dev/null +++ b/src/openai/types/fine_tuning/fine_tuning_job_integration.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + + +from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject + +FineTuningJobIntegration = FineTuningJobWandbIntegrationObject diff --git a/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py new file mode 100644 index 0000000000..4ac282eb54 --- /dev/null +++ b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["FineTuningJobWandbIntegration"] + + +class FineTuningJobWandbIntegration(BaseModel): + project: str + """The name of the project that the new run will be created under.""" + + entity: Optional[str] = None + """The entity to use for the run. + + This allows you to set the team or username of the WandB user that you would + like associated with the run. If not set, the default entity for the registered + WandB API key is used. + """ + + name: Optional[str] = None + """A display name to set for the run. + + If not set, we will use the Job ID as the name. + """ + + tags: Optional[List[str]] = None + """A list of tags to be attached to the newly created run. + + These tags are passed through directly to WandB. Some default tags are generated + by OpenAI: "openai/finetune", "openai/{base-model}", "openai/{ftjob-abcdef}". + """ diff --git a/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py new file mode 100644 index 0000000000..5b94354d50 --- /dev/null +++ b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.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 +from .fine_tuning_job_wandb_integration import FineTuningJobWandbIntegration + +__all__ = ["FineTuningJobWandbIntegrationObject"] + + +class FineTuningJobWandbIntegrationObject(BaseModel): + type: Literal["wandb"] + """The type of the integration being enabled for the fine-tuning job""" + + wandb: FineTuningJobWandbIntegration + """The settings for your integration with Weights and Biases. + + This payload specifies the project that metrics will be sent to. Optionally, you + can set an explicit display name for your run, add tags to your run, and set a + default entity (team, username, etc) to be associated with your run. + """ diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 79e0b67e13..892c737fa3 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -2,10 +2,10 @@ from __future__ import annotations -from typing import Union, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["JobCreateParams", "Hyperparameters"] +__all__ = ["JobCreateParams", "Hyperparameters", "Integration", "IntegrationWandb"] class JobCreateParams(TypedDict, total=False): @@ -32,6 +32,17 @@ class JobCreateParams(TypedDict, total=False): hyperparameters: Hyperparameters """The hyperparameters used for the fine-tuning job.""" + integrations: Optional[Iterable[Integration]] + """A list of integrations to enable for your fine-tuning job.""" + + seed: Optional[int] + """The seed controls the reproducibility of the job. + + Passing in the same seed and job parameters should produce the same results, but + may differ in rare cases. If a seed is not specified, one will be generated for + you. + """ + suffix: Optional[str] """ A string of up to 18 characters that will be added to your fine-tuned model @@ -76,3 +87,45 @@ class Hyperparameters(TypedDict, total=False): An epoch refers to one full cycle through the training dataset. """ + + +class IntegrationWandb(TypedDict, total=False): + project: Required[str] + """The name of the project that the new run will be created under.""" + + entity: Optional[str] + """The entity to use for the run. + + This allows you to set the team or username of the WandB user that you would + like associated with the run. If not set, the default entity for the registered + WandB API key is used. + """ + + name: Optional[str] + """A display name to set for the run. + + If not set, we will use the Job ID as the name. + """ + + tags: List[str] + """A list of tags to be attached to the newly created run. + + These tags are passed through directly to WandB. Some default tags are generated + by OpenAI: "openai/finetune", "openai/{base-model}", "openai/{ftjob-abcdef}". + """ + + +class Integration(TypedDict, total=False): + type: Required[Literal["wandb"]] + """The type of integration to enable. + + Currently, only "wandb" (Weights and Biases) is supported. + """ + + wandb: Required[IntegrationWandb] + """The settings for your integration with Weights and Biases. + + This payload specifies the project that metrics will be sent to. Optionally, you + can set an explicit display name for your run, add tags to your run, and set a + default entity (team, username, etc) to be associated with your run. + """ diff --git a/src/openai/types/fine_tuning/jobs/__init__.py b/src/openai/types/fine_tuning/jobs/__init__.py new file mode 100644 index 0000000000..6c93da1b69 --- /dev/null +++ b/src/openai/types/fine_tuning/jobs/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .checkpoint_list_params import CheckpointListParams as CheckpointListParams +from .fine_tuning_job_checkpoint import FineTuningJobCheckpoint as FineTuningJobCheckpoint diff --git a/src/openai/types/fine_tuning/jobs/checkpoint_list_params.py b/src/openai/types/fine_tuning/jobs/checkpoint_list_params.py new file mode 100644 index 0000000000..adceb3b218 --- /dev/null +++ b/src/openai/types/fine_tuning/jobs/checkpoint_list_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["CheckpointListParams"] + + +class CheckpointListParams(TypedDict, total=False): + after: str + """Identifier for the last checkpoint ID from the previous pagination request.""" + + limit: int + """Number of checkpoints to retrieve.""" diff --git a/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py b/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py new file mode 100644 index 0000000000..bd07317a3e --- /dev/null +++ b/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["FineTuningJobCheckpoint", "Metrics"] + + +class Metrics(BaseModel): + full_valid_loss: Optional[float] = None + + full_valid_mean_token_accuracy: Optional[float] = None + + step: Optional[float] = None + + train_loss: Optional[float] = None + + train_mean_token_accuracy: Optional[float] = None + + valid_loss: Optional[float] = None + + valid_mean_token_accuracy: Optional[float] = None + + +class FineTuningJobCheckpoint(BaseModel): + id: str + """The checkpoint identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the checkpoint was created.""" + + fine_tuned_model_checkpoint: str + """The name of the fine-tuned checkpoint model that is created.""" + + fine_tuning_job_id: str + """The name of the fine-tuning job that this checkpoint was created from.""" + + metrics: Metrics + """Metrics at the step number during the fine-tuning job.""" + + object: Literal["fine_tuning.job.checkpoint"] + """The object type, which is always "fine_tuning.job.checkpoint".""" + + step_number: int + """The step number that the checkpoint was created at.""" diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 6edbe4b491..a509627b8e 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -24,14 +24,14 @@ class TestAssistants: @parametrize def test_method_create(self, client: OpenAI) -> None: assistant = client.beta.assistants.create( - model="string", + model="gpt-4-turbo", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: assistant = client.beta.assistants.create( - model="string", + model="gpt-4-turbo", description="string", file_ids=["string", "string", "string"], instructions="string", @@ -44,7 +44,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.create( - model="string", + model="gpt-4-turbo", ) assert response.is_closed is True @@ -55,7 +55,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: with client.beta.assistants.with_streaming_response.create( - model="string", + model="gpt-4-turbo", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -235,14 +235,14 @@ class TestAsyncAssistants: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.create( - model="string", + model="gpt-4-turbo", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.create( - model="string", + model="gpt-4-turbo", description="string", file_ids=["string", "string", "string"], instructions="string", @@ -255,7 +255,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.assistants.with_raw_response.create( - model="string", + model="gpt-4-turbo", ) assert response.is_closed is True @@ -266,7 +266,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.assistants.with_streaming_response.create( - model="string", + model="gpt-4-turbo", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index fd3f7c5102..7c07251433 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -207,8 +207,11 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) thread = client.beta.threads.create_and_run( assistant_id="string", instructions="string", + max_completion_tokens=256, + max_prompt_tokens=256, metadata={}, - model="string", + model="gpt-4-turbo", + response_format="none", stream=False, temperature=1, thread={ @@ -234,7 +237,12 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) ], "metadata": {}, }, + tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + truncation_strategy={ + "type": "auto", + "last_messages": 1, + }, ) assert_matches_type(Run, thread, path=["response"]) @@ -276,8 +284,11 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) assistant_id="string", stream=True, instructions="string", + max_completion_tokens=256, + max_prompt_tokens=256, metadata={}, - model="string", + model="gpt-4-turbo", + response_format="none", temperature=1, thread={ "messages": [ @@ -302,7 +313,12 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) ], "metadata": {}, }, + tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + truncation_strategy={ + "type": "auto", + "last_messages": 1, + }, ) thread_stream.response.close() @@ -521,8 +537,11 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie thread = await async_client.beta.threads.create_and_run( assistant_id="string", instructions="string", + max_completion_tokens=256, + max_prompt_tokens=256, metadata={}, - model="string", + model="gpt-4-turbo", + response_format="none", stream=False, temperature=1, thread={ @@ -548,7 +567,12 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie ], "metadata": {}, }, + tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + truncation_strategy={ + "type": "auto", + "last_messages": 1, + }, ) assert_matches_type(Run, thread, path=["response"]) @@ -590,8 +614,11 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie assistant_id="string", stream=True, instructions="string", + max_completion_tokens=256, + max_prompt_tokens=256, metadata={}, - model="string", + model="gpt-4-turbo", + response_format="none", temperature=1, thread={ "messages": [ @@ -616,7 +643,12 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie ], "metadata": {}, }, + tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + truncation_strategy={ + "type": "auto", + "last_messages": 1, + }, ) await thread_stream.response.aclose() diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 271bcccdd3..cf5b2998b9 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -57,11 +57,19 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, ], instructions="string", + max_completion_tokens=256, + max_prompt_tokens=256, metadata={}, - model="string", + model="gpt-4-turbo", + response_format="none", stream=False, temperature=1, + tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + truncation_strategy={ + "type": "auto", + "last_messages": 1, + }, ) assert_matches_type(Run, run, path=["response"]) @@ -136,10 +144,18 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, ], instructions="string", + max_completion_tokens=256, + max_prompt_tokens=256, metadata={}, - model="string", + model="gpt-4-turbo", + response_format="none", temperature=1, + tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + truncation_strategy={ + "type": "auto", + "last_messages": 1, + }, ) run_stream.response.close() @@ -553,11 +569,19 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn }, ], instructions="string", + max_completion_tokens=256, + max_prompt_tokens=256, metadata={}, - model="string", + model="gpt-4-turbo", + response_format="none", stream=False, temperature=1, + tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + truncation_strategy={ + "type": "auto", + "last_messages": 1, + }, ) assert_matches_type(Run, run, path=["response"]) @@ -632,10 +656,18 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn }, ], instructions="string", + max_completion_tokens=256, + max_prompt_tokens=256, metadata={}, - model="string", + model="gpt-4-turbo", + response_format="none", temperature=1, + tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + truncation_strategy={ + "type": "auto", + "last_messages": 1, + }, ) await run_stream.response.aclose() diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index bb0658f3d9..c54b56a37d 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -26,7 +26,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", ) assert_matches_type(ChatCompletion, completion, path=["response"]) @@ -40,7 +40,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "name": "string", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", frequency_penalty=-2, function_call="none", functions=[ @@ -102,7 +102,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", ) assert response.is_closed is True @@ -119,7 +119,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -138,7 +138,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", stream=True, ) completion_stream.response.close() @@ -153,7 +153,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "name": "string", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", stream=True, frequency_penalty=-2, function_call="none", @@ -215,7 +215,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", stream=True, ) @@ -232,7 +232,7 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", stream=True, ) as response: assert not response.is_closed @@ -256,7 +256,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", ) assert_matches_type(ChatCompletion, completion, path=["response"]) @@ -270,7 +270,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "name": "string", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", frequency_penalty=-2, function_call="none", functions=[ @@ -332,7 +332,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) - "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", ) assert response.is_closed is True @@ -349,7 +349,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -368,7 +368,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", stream=True, ) await completion_stream.response.aclose() @@ -383,7 +383,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "name": "string", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", stream=True, frequency_penalty=-2, function_call="none", @@ -445,7 +445,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) - "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", stream=True, ) @@ -462,7 +462,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpe "role": "system", } ], - model="gpt-3.5-turbo", + model="gpt-4-turbo", stream=True, ) as response: assert not response.is_closed diff --git a/tests/api_resources/fine_tuning/jobs/__init__.py b/tests/api_resources/fine_tuning/jobs/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/fine_tuning/jobs/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py new file mode 100644 index 0000000000..915d5c6f63 --- /dev/null +++ b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py @@ -0,0 +1,117 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.fine_tuning.jobs import FineTuningJobCheckpoint + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestCheckpoints: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + checkpoint = client.fine_tuning.jobs.checkpoints.list( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + checkpoint = client.fine_tuning.jobs.checkpoints.list( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + after="string", + limit=0, + ) + assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.fine_tuning.jobs.checkpoints.with_raw_response.list( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + checkpoint = response.parse() + assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.fine_tuning.jobs.checkpoints.with_streaming_response.list( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + checkpoint = response.parse() + assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + client.fine_tuning.jobs.checkpoints.with_raw_response.list( + "", + ) + + +class TestAsyncCheckpoints: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + checkpoint = await async_client.fine_tuning.jobs.checkpoints.list( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + checkpoint = await async_client.fine_tuning.jobs.checkpoints.list( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + after="string", + limit=0, + ) + assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.jobs.checkpoints.with_raw_response.list( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + checkpoint = response.parse() + assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.jobs.checkpoints.with_streaming_response.list( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + checkpoint = await response.parse() + assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + await async_client.fine_tuning.jobs.checkpoints.with_raw_response.list( + "", + ) diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index f4974ebbcd..1ff6d63b31 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -39,6 +39,36 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "learning_rate_multiplier": "auto", "n_epochs": "auto", }, + integrations=[ + { + "type": "wandb", + "wandb": { + "project": "my-wandb-project", + "name": "string", + "entity": "string", + "tags": ["custom-tag", "custom-tag", "custom-tag"], + }, + }, + { + "type": "wandb", + "wandb": { + "project": "my-wandb-project", + "name": "string", + "entity": "string", + "tags": ["custom-tag", "custom-tag", "custom-tag"], + }, + }, + { + "type": "wandb", + "wandb": { + "project": "my-wandb-project", + "name": "string", + "entity": "string", + "tags": ["custom-tag", "custom-tag", "custom-tag"], + }, + }, + ], + seed=42, suffix="x", validation_file="file-abc123", ) @@ -248,6 +278,36 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "learning_rate_multiplier": "auto", "n_epochs": "auto", }, + integrations=[ + { + "type": "wandb", + "wandb": { + "project": "my-wandb-project", + "name": "string", + "entity": "string", + "tags": ["custom-tag", "custom-tag", "custom-tag"], + }, + }, + { + "type": "wandb", + "wandb": { + "project": "my-wandb-project", + "name": "string", + "entity": "string", + "tags": ["custom-tag", "custom-tag", "custom-tag"], + }, + }, + { + "type": "wandb", + "wandb": { + "project": "my-wandb-project", + "name": "string", + "entity": "string", + "tags": ["custom-tag", "custom-tag", "custom-tag"], + }, + }, + ], + seed=42, suffix="x", validation_file="file-abc123", ) From db069cdacaf7ff8d7c1308db9f2465d38252ea1e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:10:17 -0400 Subject: [PATCH 391/914] feat(api): add batch API (#1316) https://platform.openai.com/docs/api-reference/batch/create --- .stats.yml | 2 +- api.md | 14 + src/openai/__init__.py | 1 + src/openai/_client.py | 8 + src/openai/_module_client.py | 7 + src/openai/resources/__init__.py | 14 + src/openai/resources/batches.py | 354 +++++++++++++++++++++++ src/openai/types/__init__.py | 4 + src/openai/types/batch.py | 85 ++++++ src/openai/types/batch_create_params.py | 35 +++ src/openai/types/batch_error.py | 21 ++ src/openai/types/batch_request_counts.py | 16 + tests/api_resources/test_batches.py | 268 +++++++++++++++++ 13 files changed, 828 insertions(+), 1 deletion(-) create mode 100644 src/openai/resources/batches.py create mode 100644 src/openai/types/batch.py create mode 100644 src/openai/types/batch_create_params.py create mode 100644 src/openai/types/batch_error.py create mode 100644 src/openai/types/batch_request_counts.py create mode 100644 tests/api_resources/test_batches.py diff --git a/.stats.yml b/.stats.yml index 284caebf44..47c2bce1cc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 52 +configured_endpoints: 55 diff --git a/api.md b/api.md index cc3c91a8d5..38f77592e8 100644 --- a/api.md +++ b/api.md @@ -361,3 +361,17 @@ Methods: - client.beta.threads.messages.files.retrieve(file_id, \*, thread_id, message_id) -> MessageFile - client.beta.threads.messages.files.list(message_id, \*, thread_id, \*\*params) -> SyncCursorPage[MessageFile] + +# Batches + +Types: + +```python +from openai.types import Batch, BatchError, BatchRequestCounts +``` + +Methods: + +- client.batches.create(\*\*params) -> Batch +- client.batches.retrieve(batch_id) -> Batch +- client.batches.cancel(batch_id) -> Batch diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 1daa26f7b7..490ba017f0 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -335,6 +335,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] files as files, images as images, models as models, + batches as batches, embeddings as embeddings, completions as completions, fine_tuning as fine_tuning, diff --git a/src/openai/_client.py b/src/openai/_client.py index e9169df72a..5a6852e571 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -57,6 +57,7 @@ class OpenAI(SyncAPIClient): models: resources.Models fine_tuning: resources.FineTuning beta: resources.Beta + batches: resources.Batches with_raw_response: OpenAIWithRawResponse with_streaming_response: OpenAIWithStreamedResponse @@ -134,6 +135,7 @@ def __init__( self.models = resources.Models(self) self.fine_tuning = resources.FineTuning(self) self.beta = resources.Beta(self) + self.batches = resources.Batches(self) self.with_raw_response = OpenAIWithRawResponse(self) self.with_streaming_response = OpenAIWithStreamedResponse(self) @@ -257,6 +259,7 @@ class AsyncOpenAI(AsyncAPIClient): models: resources.AsyncModels fine_tuning: resources.AsyncFineTuning beta: resources.AsyncBeta + batches: resources.AsyncBatches with_raw_response: AsyncOpenAIWithRawResponse with_streaming_response: AsyncOpenAIWithStreamedResponse @@ -334,6 +337,7 @@ def __init__( self.models = resources.AsyncModels(self) self.fine_tuning = resources.AsyncFineTuning(self) self.beta = resources.AsyncBeta(self) + self.batches = resources.AsyncBatches(self) self.with_raw_response = AsyncOpenAIWithRawResponse(self) self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self) @@ -458,6 +462,7 @@ def __init__(self, client: OpenAI) -> None: self.models = resources.ModelsWithRawResponse(client.models) self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning) self.beta = resources.BetaWithRawResponse(client.beta) + self.batches = resources.BatchesWithRawResponse(client.batches) class AsyncOpenAIWithRawResponse: @@ -472,6 +477,7 @@ def __init__(self, client: AsyncOpenAI) -> None: self.models = resources.AsyncModelsWithRawResponse(client.models) self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning) self.beta = resources.AsyncBetaWithRawResponse(client.beta) + self.batches = resources.AsyncBatchesWithRawResponse(client.batches) class OpenAIWithStreamedResponse: @@ -486,6 +492,7 @@ def __init__(self, client: OpenAI) -> None: self.models = resources.ModelsWithStreamingResponse(client.models) self.fine_tuning = resources.FineTuningWithStreamingResponse(client.fine_tuning) self.beta = resources.BetaWithStreamingResponse(client.beta) + self.batches = resources.BatchesWithStreamingResponse(client.batches) class AsyncOpenAIWithStreamedResponse: @@ -500,6 +507,7 @@ def __init__(self, client: AsyncOpenAI) -> None: self.models = resources.AsyncModelsWithStreamingResponse(client.models) self.fine_tuning = resources.AsyncFineTuningWithStreamingResponse(client.fine_tuning) self.beta = resources.AsyncBetaWithStreamingResponse(client.beta) + self.batches = resources.AsyncBatchesWithStreamingResponse(client.batches) Client = OpenAI diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index 9227f5e2b4..6f7356eb3c 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -42,6 +42,12 @@ def __load__(self) -> resources.Models: return _load_client().models +class BatchesProxy(LazyProxy[resources.Batches]): + @override + def __load__(self) -> resources.Batches: + return _load_client().batches + + class EmbeddingsProxy(LazyProxy[resources.Embeddings]): @override def __load__(self) -> resources.Embeddings: @@ -72,6 +78,7 @@ def __load__(self) -> resources.FineTuning: audio: resources.Audio = AudioProxy().__as_proxied__() images: resources.Images = ImagesProxy().__as_proxied__() models: resources.Models = ModelsProxy().__as_proxied__() +batches: resources.Batches = BatchesProxy().__as_proxied__() embeddings: resources.Embeddings = EmbeddingsProxy().__as_proxied__() completions: resources.Completions = CompletionsProxy().__as_proxied__() moderations: resources.Moderations = ModerationsProxy().__as_proxied__() diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index 64aa12d260..ecae4243fc 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -48,6 +48,14 @@ ModelsWithStreamingResponse, AsyncModelsWithStreamingResponse, ) +from .batches import ( + Batches, + AsyncBatches, + BatchesWithRawResponse, + AsyncBatchesWithRawResponse, + BatchesWithStreamingResponse, + AsyncBatchesWithStreamingResponse, +) from .embeddings import ( Embeddings, AsyncEmbeddings, @@ -142,4 +150,10 @@ "AsyncBetaWithRawResponse", "BetaWithStreamingResponse", "AsyncBetaWithStreamingResponse", + "Batches", + "AsyncBatches", + "BatchesWithRawResponse", + "AsyncBatchesWithRawResponse", + "BatchesWithStreamingResponse", + "AsyncBatchesWithStreamingResponse", ] diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py new file mode 100644 index 0000000000..0921ccb194 --- /dev/null +++ b/src/openai/resources/batches.py @@ -0,0 +1,354 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal + +import httpx + +from .. import _legacy_response +from ..types import Batch, batch_create_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + maybe_transform, + async_maybe_transform, +) +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._base_client import ( + make_request_options, +) + +__all__ = ["Batches", "AsyncBatches"] + + +class Batches(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BatchesWithRawResponse: + return BatchesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BatchesWithStreamingResponse: + return BatchesWithStreamingResponse(self) + + def create( + self, + *, + completion_window: Literal["24h"], + endpoint: Literal["/v1/chat/completions"], + input_file_id: str, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Batch: + """ + Creates and executes a batch from an uploaded file of requests + + Args: + completion_window: The time frame within which the batch should be processed. Currently only `24h` + is supported. + + endpoint: The endpoint to be used for all requests in the batch. Currently only + `/v1/chat/completions` is supported. + + input_file_id: The ID of an uploaded file that contains requests for the new batch. + + See [upload file](https://platform.openai.com/docs/api-reference/files/create) + for how to upload a file. + + Your input file must be formatted as a JSONL file, and must be uploaded with the + purpose `batch`. + + metadata: Optional custom metadata for the batch. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/batches", + body=maybe_transform( + { + "completion_window": completion_window, + "endpoint": endpoint, + "input_file_id": input_file_id, + "metadata": metadata, + }, + batch_create_params.BatchCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Batch, + ) + + def retrieve( + self, + batch_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Batch: + """ + Retrieves a batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return self._get( + f"/batches/{batch_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Batch, + ) + + def cancel( + self, + batch_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Batch: + """ + Cancels an in-progress batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return self._post( + f"/batches/{batch_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Batch, + ) + + +class AsyncBatches(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBatchesWithRawResponse: + return AsyncBatchesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBatchesWithStreamingResponse: + return AsyncBatchesWithStreamingResponse(self) + + async def create( + self, + *, + completion_window: Literal["24h"], + endpoint: Literal["/v1/chat/completions"], + input_file_id: str, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Batch: + """ + Creates and executes a batch from an uploaded file of requests + + Args: + completion_window: The time frame within which the batch should be processed. Currently only `24h` + is supported. + + endpoint: The endpoint to be used for all requests in the batch. Currently only + `/v1/chat/completions` is supported. + + input_file_id: The ID of an uploaded file that contains requests for the new batch. + + See [upload file](https://platform.openai.com/docs/api-reference/files/create) + for how to upload a file. + + Your input file must be formatted as a JSONL file, and must be uploaded with the + purpose `batch`. + + metadata: Optional custom metadata for the batch. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/batches", + body=await async_maybe_transform( + { + "completion_window": completion_window, + "endpoint": endpoint, + "input_file_id": input_file_id, + "metadata": metadata, + }, + batch_create_params.BatchCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Batch, + ) + + async def retrieve( + self, + batch_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Batch: + """ + Retrieves a batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return await self._get( + f"/batches/{batch_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Batch, + ) + + async def cancel( + self, + batch_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Batch: + """ + Cancels an in-progress batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + return await self._post( + f"/batches/{batch_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Batch, + ) + + +class BatchesWithRawResponse: + def __init__(self, batches: Batches) -> None: + self._batches = batches + + self.create = _legacy_response.to_raw_response_wrapper( + batches.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + batches.retrieve, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + batches.cancel, + ) + + +class AsyncBatchesWithRawResponse: + def __init__(self, batches: AsyncBatches) -> None: + self._batches = batches + + self.create = _legacy_response.async_to_raw_response_wrapper( + batches.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + batches.retrieve, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + batches.cancel, + ) + + +class BatchesWithStreamingResponse: + def __init__(self, batches: Batches) -> None: + self._batches = batches + + self.create = to_streamed_response_wrapper( + batches.create, + ) + self.retrieve = to_streamed_response_wrapper( + batches.retrieve, + ) + self.cancel = to_streamed_response_wrapper( + batches.cancel, + ) + + +class AsyncBatchesWithStreamingResponse: + def __init__(self, batches: AsyncBatches) -> None: + self._batches = batches + + self.create = async_to_streamed_response_wrapper( + batches.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + batches.retrieve, + ) + self.cancel = async_to_streamed_response_wrapper( + batches.cancel, + ) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 0917e22a8f..4bbcdddc2a 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations +from .batch import Batch as Batch from .image import Image as Image from .model import Model as Model from .shared import ( @@ -12,6 +13,7 @@ from .embedding import Embedding as Embedding from .completion import Completion as Completion from .moderation import Moderation as Moderation +from .batch_error import BatchError as BatchError from .file_object import FileObject as FileObject from .file_content import FileContent as FileContent from .file_deleted import FileDeleted as FileDeleted @@ -22,6 +24,8 @@ from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams from .file_create_params import FileCreateParams as FileCreateParams +from .batch_create_params import BatchCreateParams as BatchCreateParams +from .batch_request_counts import BatchRequestCounts as BatchRequestCounts from .image_generate_params import ImageGenerateParams as ImageGenerateParams from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams from .completion_create_params import CompletionCreateParams as CompletionCreateParams diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py new file mode 100644 index 0000000000..bde04d1a24 --- /dev/null +++ b/src/openai/types/batch.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .batch_error import BatchError +from .batch_request_counts import BatchRequestCounts + +__all__ = ["Batch", "Errors"] + + +class Errors(BaseModel): + data: Optional[List[BatchError]] = None + + object: Optional[str] = None + """The object type, which is always `list`.""" + + +class Batch(BaseModel): + id: str + + completion_window: str + """The time frame within which the batch should be processed.""" + + created_at: str + """The Unix timestamp (in seconds) for when the batch was created.""" + + endpoint: str + """The OpenAI API endpoint used by the batch.""" + + input_file_id: str + """The ID of the input file for the batch.""" + + object: Literal["batch"] + """The object type, which is always `batch`.""" + + status: Literal[ + "validating", "failed", "in_progress", "finalizing", "completed", "expired", "cancelling", "cancelled" + ] + """The current status of the batch.""" + + cancelled_at: Optional[str] = None + """The Unix timestamp (in seconds) for when the batch was cancelled.""" + + cancelling_at: Optional[str] = None + """The Unix timestamp (in seconds) for when the batch started cancelling.""" + + completed_at: Optional[str] = None + """The Unix timestamp (in seconds) for when the batch was completed.""" + + error_file_id: Optional[str] = None + """The ID of the file containing the outputs of requests with errors.""" + + errors: Optional[Errors] = None + + expired_at: Optional[str] = None + """The Unix timestamp (in seconds) for when the batch expired.""" + + expires_at: Optional[str] = None + """The Unix timestamp (in seconds) for when the batch will expire.""" + + failed_at: Optional[str] = None + """The Unix timestamp (in seconds) for when the batch failed.""" + + finalizing_at: Optional[str] = None + """The Unix timestamp (in seconds) for when the batch started finalizing.""" + + in_progress_at: Optional[str] = None + """The Unix timestamp (in seconds) for when the batch started processing.""" + + metadata: Optional[builtins.object] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + output_file_id: Optional[str] = None + """The ID of the file containing the outputs of successfully executed requests.""" + + request_counts: Optional[BatchRequestCounts] = None + """The request counts for different statuses within the batch.""" diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py new file mode 100644 index 0000000000..6a22be8626 --- /dev/null +++ b/src/openai/types/batch_create_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BatchCreateParams"] + + +class BatchCreateParams(TypedDict, total=False): + completion_window: Required[Literal["24h"]] + """The time frame within which the batch should be processed. + + Currently only `24h` is supported. + """ + + endpoint: Required[Literal["/v1/chat/completions"]] + """The endpoint to be used for all requests in the batch. + + Currently only `/v1/chat/completions` is supported. + """ + + input_file_id: Required[str] + """The ID of an uploaded file that contains requests for the new batch. + + See [upload file](https://platform.openai.com/docs/api-reference/files/create) + for how to upload a file. + + Your input file must be formatted as a JSONL file, and must be uploaded with the + purpose `batch`. + """ + + metadata: Optional[Dict[str, str]] + """Optional custom metadata for the batch.""" diff --git a/src/openai/types/batch_error.py b/src/openai/types/batch_error.py new file mode 100644 index 0000000000..1cdd808dbd --- /dev/null +++ b/src/openai/types/batch_error.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["BatchError"] + + +class BatchError(BaseModel): + code: Optional[str] = None + """An error code identifying the error type.""" + + line: Optional[int] = None + """The line number of the input file where the error occurred, if applicable.""" + + message: Optional[str] = None + """A human-readable message providing more details about the error.""" + + param: Optional[str] = None + """The name of the parameter that caused the error, if applicable.""" diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py new file mode 100644 index 0000000000..068b071af1 --- /dev/null +++ b/src/openai/types/batch_request_counts.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["BatchRequestCounts"] + + +class BatchRequestCounts(BaseModel): + completed: int + """Number of requests that have been completed successfully.""" + + failed: int + """Number of requests that have failed.""" + + total: int + """Total number of requests in the batch.""" diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py new file mode 100644 index 0000000000..aafeff8116 --- /dev/null +++ b/tests/api_resources/test_batches.py @@ -0,0 +1,268 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import Batch + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestBatches: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + batch = client.batches.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id="string", + ) + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + batch = client.batches.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id="string", + metadata={"foo": "string"}, + ) + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.batches.with_raw_response.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.batches.with_streaming_response.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + batch = client.batches.retrieve( + "string", + ) + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.batches.with_raw_response.retrieve( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.batches.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + client.batches.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + batch = client.batches.cancel( + "string", + ) + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + response = client.batches.with_raw_response.cancel( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: OpenAI) -> None: + with client.batches.with_streaming_response.cancel( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + client.batches.with_raw_response.cancel( + "", + ) + + +class TestAsyncBatches: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + batch = await async_client.batches.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id="string", + ) + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + batch = await async_client.batches.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id="string", + metadata={"foo": "string"}, + ) + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.batches.with_raw_response.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.batches.with_streaming_response.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + batch = await response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + batch = await async_client.batches.retrieve( + "string", + ) + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.batches.with_raw_response.retrieve( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.batches.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + batch = await response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + await async_client.batches.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: + batch = await async_client.batches.cancel( + "string", + ) + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: + response = await async_client.batches.with_raw_response.cancel( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + batch = response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: + async with async_client.batches.with_streaming_response.cancel( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + batch = await response.parse() + assert_matches_type(Batch, batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + await async_client.batches.with_raw_response.cancel( + "", + ) From 2f759aabf346548fbb09d54ae0c279ce8ce9e148 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:10:45 -0400 Subject: [PATCH 392/914] release: 1.18.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3741b313a5..4ce109ae13 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.17.1" + ".": "1.18.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e18ab5f54..03285021ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.18.0 (2024-04-15) + +Full Changelog: [v1.17.1...v1.18.0](https://github.com/openai/openai-python/compare/v1.17.1...v1.18.0) + +### Features + +* **api:** add batch API ([#1316](https://github.com/openai/openai-python/issues/1316)) ([3e6f19e](https://github.com/openai/openai-python/commit/3e6f19e6e7489bf1c94944a5f8f9b1d4535cdc43)) +* **api:** updates ([#1314](https://github.com/openai/openai-python/issues/1314)) ([8281dc9](https://github.com/openai/openai-python/commit/8281dc956178f5de345645660081f7d0c15a57a6)) + ## 1.17.1 (2024-04-12) Full Changelog: [v1.17.0...v1.17.1](https://github.com/openai/openai-python/compare/v1.17.0...v1.17.1) diff --git a/pyproject.toml b/pyproject.toml index 9eb6330616..505f1a3e7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.17.1" +version = "1.18.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a4ffbb2c35..2957462e3d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.17.1" # x-release-please-version +__version__ = "1.18.0" # x-release-please-version From 3cb47431c9143e9b5f0085938b6d01c818e7c941 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:34:22 -0400 Subject: [PATCH 393/914] feat(errors): add request_id property (#1317) --- src/openai/_exceptions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index 074752c8a1..f6731cfac5 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -76,11 +76,13 @@ class APIStatusError(APIError): response: httpx.Response status_code: int + request_id: str | None def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: super().__init__(message, response.request, body=body) self.response = response self.status_code = response.status_code + self.request_id = response.headers.get("x-request-id") class APIConnectionError(APIError): From 1a83130d7aff056d83c6760e20246a26a400c0d4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:34:48 -0400 Subject: [PATCH 394/914] release: 1.19.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4ce109ae13..de44c40d86 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.18.0" + ".": "1.19.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 03285021ee..bd42e74a05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.19.0 (2024-04-15) + +Full Changelog: [v1.18.0...v1.19.0](https://github.com/openai/openai-python/compare/v1.18.0...v1.19.0) + +### Features + +* **errors:** add request_id property ([#1317](https://github.com/openai/openai-python/issues/1317)) ([f9eb77d](https://github.com/openai/openai-python/commit/f9eb77dca422b9456f4e3b31c7474046235eec1d)) + ## 1.18.0 (2024-04-15) Full Changelog: [v1.17.1...v1.18.0](https://github.com/openai/openai-python/compare/v1.17.1...v1.18.0) diff --git a/pyproject.toml b/pyproject.toml index 505f1a3e7a..66049e22d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.18.0" +version = "1.19.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 2957462e3d..b652844d7a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.18.0" # x-release-please-version +__version__ = "1.19.0" # x-release-please-version From 3408b5d24287f435df8020a6d3f96076c39f6920 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 16 Apr 2024 09:18:14 -0400 Subject: [PATCH 395/914] feat(client): add header OpenAI-Project (#1320) --- src/openai/__init__.py | 14 ++++++++++++++ src/openai/_client.py | 20 ++++++++++++++++++++ src/openai/lib/azure.py | 13 +++++++++++++ tests/test_module_client.py | 1 + 4 files changed, 48 insertions(+) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 490ba017f0..0e87ae9259 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -108,6 +108,8 @@ organization: str | None = None +project: str | None = None + base_url: str | _httpx.URL | None = None timeout: float | Timeout | None = DEFAULT_TIMEOUT @@ -159,6 +161,17 @@ def organization(self, value: str | None) -> None: # type: ignore organization = value + @property # type: ignore + @override + def project(self) -> str | None: + return project + + @project.setter # type: ignore + def project(self, value: str | None) -> None: # type: ignore + global project + + project = value + @property @override def base_url(http://github.com/self) -> _httpx.URL: @@ -310,6 +323,7 @@ def _load_client() -> OpenAI: # type: ignore[reportUnusedFunction] _client = _ModuleClient( api_key=api_key, organization=organization, + project=project, base_url=base_url, timeout=timeout, max_retries=max_retries, diff --git a/src/openai/_client.py b/src/openai/_client.py index 5a6852e571..8f3060c6f6 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -64,12 +64,14 @@ class OpenAI(SyncAPIClient): # client options api_key: str organization: str | None + project: str | None def __init__( self, *, api_key: str | None = None, organization: str | None = None, + project: str | None = None, base_url: str | httpx.URL | None = None, timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, @@ -94,6 +96,7 @@ def __init__( This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `OPENAI_API_KEY` - `organization` from `OPENAI_ORG_ID` + - `project` from `OPENAI_PROJECT_ID` """ if api_key is None: api_key = os.environ.get("OPENAI_API_KEY") @@ -107,6 +110,10 @@ def __init__( organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization + if project is None: + project = os.environ.get("OPENAI_PROJECT_ID") + self.project = project + if base_url is None: base_url = os.environ.get("OPENAI_BASE_URL") if base_url is None: @@ -157,6 +164,7 @@ def default_headers(self) -> dict[str, str | Omit]: **super().default_headers, "X-Stainless-Async": "false", "OpenAI-Organization": self.organization if self.organization is not None else Omit(), + "OpenAI-Project": self.project if self.project is not None else Omit(), **self._custom_headers, } @@ -165,6 +173,7 @@ def copy( *, api_key: str | None = None, organization: str | None = None, + project: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, @@ -200,6 +209,7 @@ def copy( return self.__class__( api_key=api_key or self.api_key, organization=organization or self.organization, + project=project or self.project, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, @@ -266,12 +276,14 @@ class AsyncOpenAI(AsyncAPIClient): # client options api_key: str organization: str | None + project: str | None def __init__( self, *, api_key: str | None = None, organization: str | None = None, + project: str | None = None, base_url: str | httpx.URL | None = None, timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, @@ -296,6 +308,7 @@ def __init__( This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `OPENAI_API_KEY` - `organization` from `OPENAI_ORG_ID` + - `project` from `OPENAI_PROJECT_ID` """ if api_key is None: api_key = os.environ.get("OPENAI_API_KEY") @@ -309,6 +322,10 @@ def __init__( organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization + if project is None: + project = os.environ.get("OPENAI_PROJECT_ID") + self.project = project + if base_url is None: base_url = os.environ.get("OPENAI_BASE_URL") if base_url is None: @@ -359,6 +376,7 @@ def default_headers(self) -> dict[str, str | Omit]: **super().default_headers, "X-Stainless-Async": f"async:{get_async_library()}", "OpenAI-Organization": self.organization if self.organization is not None else Omit(), + "OpenAI-Project": self.project if self.project is not None else Omit(), **self._custom_headers, } @@ -367,6 +385,7 @@ def copy( *, api_key: str | None = None, organization: str | None = None, + project: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, @@ -402,6 +421,7 @@ def copy( return self.__class__( api_key=api_key or self.api_key, organization=organization or self.organization, + project=project or self.project, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index b3b94de80e..b76b83c61c 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -130,6 +130,7 @@ def __init__( azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, + project: str | None = None, base_url: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, @@ -143,6 +144,7 @@ def __init__( This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `AZURE_OPENAI_API_KEY` - `organization` from `OPENAI_ORG_ID` + - `project` from `OPENAI_PROJECT_ID` - `azure_ad_token` from `AZURE_OPENAI_AD_TOKEN` - `api_version` from `OPENAI_API_VERSION` - `azure_endpoint` from `AZURE_OPENAI_ENDPOINT` @@ -205,6 +207,7 @@ def __init__( super().__init__( api_key=api_key, organization=organization, + project=project, base_url=base_url, timeout=timeout, max_retries=max_retries, @@ -223,6 +226,7 @@ def copy( *, api_key: str | None = None, organization: str | None = None, + project: str | None = None, api_version: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, @@ -242,6 +246,7 @@ def copy( return super().copy( api_key=api_key, organization=organization, + project=project, base_url=base_url, timeout=timeout, http_client=http_client, @@ -306,6 +311,7 @@ def __init__( azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, + project: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -325,6 +331,7 @@ def __init__( azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, + project: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -344,6 +351,7 @@ def __init__( azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, + project: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -363,6 +371,7 @@ def __init__( azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, + project: str | None = None, base_url: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, @@ -376,6 +385,7 @@ def __init__( This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `AZURE_OPENAI_API_KEY` - `organization` from `OPENAI_ORG_ID` + - `project` from `OPENAI_PROJECT_ID` - `azure_ad_token` from `AZURE_OPENAI_AD_TOKEN` - `api_version` from `OPENAI_API_VERSION` - `azure_endpoint` from `AZURE_OPENAI_ENDPOINT` @@ -438,6 +448,7 @@ def __init__( super().__init__( api_key=api_key, organization=organization, + project=project, base_url=base_url, timeout=timeout, max_retries=max_retries, @@ -456,6 +467,7 @@ def copy( *, api_key: str | None = None, organization: str | None = None, + project: str | None = None, api_version: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, @@ -475,6 +487,7 @@ def copy( return super().copy( api_key=api_key, organization=organization, + project=project, base_url=base_url, timeout=timeout, http_client=http_client, diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 6de314856b..05b5f81111 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -16,6 +16,7 @@ def reset_state() -> None: openai._reset_client() openai.api_key = None or "My API Key" openai.organization = None + openai.project = None openai.base_url = None openai.timeout = DEFAULT_TIMEOUT openai.max_retries = DEFAULT_MAX_RETRIES From 1dc93403acbfa0405649244520416b263c66edaa Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 16 Apr 2024 12:54:34 -0400 Subject: [PATCH 396/914] feat: extract chat models to a named enum (#1322) --- api.md | 6 + src/openai/resources/chat/completions.py | 201 +----------------- src/openai/types/__init__.py | 1 + .../types/chat/completion_create_params.py | 28 +-- src/openai/types/chat_model.py | 27 +++ 5 files changed, 45 insertions(+), 218 deletions(-) create mode 100644 src/openai/types/chat_model.py diff --git a/api.md b/api.md index 38f77592e8..c772fb7c7b 100644 --- a/api.md +++ b/api.md @@ -18,6 +18,12 @@ Methods: # Chat +Types: + +```python +from openai.types import ChatModel +``` + ## Completions Types: diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 1a23e7876e..3b070b716e 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -8,6 +8,7 @@ import httpx from ... import _legacy_response +from ...types import ChatModel from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import ( required_args, @@ -47,30 +48,7 @@ def create( self, *, messages: Iterable[ChatCompletionMessageParam], - model: Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -238,30 +216,7 @@ def create( self, *, messages: Iterable[ChatCompletionMessageParam], - model: Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], stream: Literal[True], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, @@ -429,30 +384,7 @@ def create( self, *, messages: Iterable[ChatCompletionMessageParam], - model: Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], stream: bool, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, @@ -620,30 +552,7 @@ def create( self, *, messages: Iterable[ChatCompletionMessageParam], - model: Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -719,30 +628,7 @@ async def create( self, *, messages: Iterable[ChatCompletionMessageParam], - model: Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -910,30 +796,7 @@ async def create( self, *, messages: Iterable[ChatCompletionMessageParam], - model: Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], stream: Literal[True], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, @@ -1101,30 +964,7 @@ async def create( self, *, messages: Iterable[ChatCompletionMessageParam], - model: Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], stream: bool, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, @@ -1292,30 +1132,7 @@ async def create( self, *, messages: Iterable[ChatCompletionMessageParam], - model: Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 4bbcdddc2a..b6f35cfecf 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -11,6 +11,7 @@ FunctionParameters as FunctionParameters, ) from .embedding import Embedding as Embedding +from .chat_model import ChatModel as ChatModel from .completion import Completion as Completion from .moderation import Moderation as Moderation from .batch_error import BatchError as BatchError diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 1e0f7f8195..964b246c41 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypedDict from ...types import shared_params +from ..chat_model import ChatModel from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam @@ -28,32 +29,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). """ - model: Required[ - Union[ - str, - Literal[ - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ] - ] + model: Required[Union[str, ChatModel]] """ID of the model to use. See the diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py new file mode 100644 index 0000000000..219dab5138 --- /dev/null +++ b/src/openai/types/chat_model.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +__all__ = ["ChatModel"] + +ChatModel = Literal[ + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", +] From 15e7ac157dcc80d952107f596bb9c38cfa06685d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 16 Apr 2024 12:55:02 -0400 Subject: [PATCH 397/914] release: 1.20.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index de44c40d86..69eb19a7b0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.19.0" + ".": "1.20.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bd42e74a05..a39d5faa30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.20.0 (2024-04-16) + +Full Changelog: [v1.19.0...v1.20.0](https://github.com/openai/openai-python/compare/v1.19.0...v1.20.0) + +### Features + +* **client:** add header OpenAI-Project ([#1320](https://github.com/openai/openai-python/issues/1320)) ([0c489f1](https://github.com/openai/openai-python/commit/0c489f16a7d9e5ac753da87273b223893edefa69)) +* extract chat models to a named enum ([#1322](https://github.com/openai/openai-python/issues/1322)) ([1ccd9b6](https://github.com/openai/openai-python/commit/1ccd9b67322736a4714e58c953d59585322c527d)) + ## 1.19.0 (2024-04-15) Full Changelog: [v1.18.0...v1.19.0](https://github.com/openai/openai-python/compare/v1.18.0...v1.19.0) diff --git a/pyproject.toml b/pyproject.toml index 66049e22d2..11ab55cbe9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.19.0" +version = "1.20.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b652844d7a..32723952ed 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.19.0" # x-release-please-version +__version__ = "1.20.0" # x-release-please-version From 5b20698dc3d023028a7bd447098b949c5bfc3b19 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:35:22 -0400 Subject: [PATCH 398/914] feat(api): add vector stores (#1325) --- .stats.yml | 2 +- README.md | 16 +- api.md | 105 ++- helpers.md | 21 + pyproject.toml | 3 +- requirements-dev.lock | 10 + src/openai/resources/beta/__init__.py | 14 + .../beta/{assistants => }/assistants.py | 265 +++++-- .../resources/beta/assistants/__init__.py | 33 - src/openai/resources/beta/beta.py | 34 +- .../beta/threads/{messages => }/messages.py | 86 +- .../beta/threads/messages/__init__.py | 33 - .../resources/beta/threads/messages/files.py | 312 -------- .../resources/beta/threads/runs/runs.py | 90 ++- .../resources/beta/threads/runs/steps.py | 8 +- src/openai/resources/beta/threads/threads.py | 189 ++++- .../resources/beta/vector_stores/__init__.py | 47 ++ .../beta/vector_stores/file_batches.py | 739 ++++++++++++++++++ .../{assistants => vector_stores}/files.py | 352 +++++++-- .../beta/vector_stores/vector_stores.py | 688 ++++++++++++++++ src/openai/resources/fine_tuning/jobs/jobs.py | 4 +- src/openai/types/beta/__init__.py | 9 +- src/openai/types/beta/assistant.py | 44 +- .../types/beta/assistant_create_params.py | 111 ++- src/openai/types/beta/assistant_tool.py | 4 +- .../types/beta/assistant_tool_choice.py | 2 +- .../types/beta/assistant_tool_choice_param.py | 2 +- src/openai/types/beta/assistant_tool_param.py | 4 +- .../types/beta/assistant_update_params.py | 83 +- src/openai/types/beta/assistants/__init__.py | 8 - .../types/beta/assistants/assistant_file.py | 21 - src/openai/types/beta/file_search_tool.py | 12 + ...ool_param.py => file_search_tool_param.py} | 8 +- src/openai/types/beta/thread.py | 37 +- .../beta/thread_create_and_run_params.py | 130 ++- src/openai/types/beta/thread_create_params.py | 84 +- src/openai/types/beta/thread_update_params.py | 37 +- src/openai/types/beta/threads/message.py | 19 +- .../beta/threads/message_create_params.py | 20 +- .../types/beta/threads/message_delta.py | 7 - .../types/beta/threads/messages/__init__.py | 6 - .../beta/threads/messages/message_file.py | 25 - src/openai/types/beta/threads/run.py | 10 +- .../types/beta/threads/run_create_params.py | 24 +- .../types/beta/threads/runs/__init__.py | 4 +- ..._tool_call.py => file_search_tool_call.py} | 10 +- ...elta.py => file_search_tool_call_delta.py} | 14 +- .../types/beta/threads/runs/tool_call.py | 4 +- .../beta/threads/runs/tool_call_delta.py | 4 +- .../threads/runs/tool_call_delta_object.py | 2 +- .../threads/runs/tool_calls_step_details.py | 2 +- src/openai/types/beta/vector_store.py | 79 ++ .../types/beta/vector_store_create_params.py | 42 + ...rieval_tool.py => vector_store_deleted.py} | 11 +- ..._params.py => vector_store_list_params.py} | 4 +- .../types/beta/vector_store_update_params.py | 35 + .../types/beta/vector_stores/__init__.py | 11 + .../vector_stores/file_batch_create_params.py | 17 + .../file_batch_list_files_params.py | 47 ++ .../file_create_params.py | 6 +- .../file_list_params.py | 10 +- .../beta/vector_stores/vector_store_file.py | 48 ++ .../vector_stores/vector_store_file_batch.py | 54 ++ .../vector_store_file_deleted.py} | 6 +- .../types/fine_tuning/job_create_params.py | 2 +- tests/api_resources/beta/test_assistants.py | 48 +- tests/api_resources/beta/test_threads.py | 370 ++++++++- .../api_resources/beta/test_vector_stores.py | 426 ++++++++++ .../beta/threads/messages/__init__.py | 1 - .../beta/threads/messages/test_files.py | 263 ------- .../beta/threads/test_messages.py | 30 +- tests/api_resources/beta/threads/test_runs.py | 184 ++++- .../{assistants => vector_stores}/__init__.py | 0 .../beta/vector_stores/test_file_batches.py | 424 ++++++++++ .../test_files.py | 219 +++--- 75 files changed, 4830 insertions(+), 1305 deletions(-) rename src/openai/resources/beta/{assistants => }/assistants.py (71%) delete mode 100644 src/openai/resources/beta/assistants/__init__.py rename src/openai/resources/beta/threads/{messages => }/messages.py (88%) delete mode 100644 src/openai/resources/beta/threads/messages/__init__.py delete mode 100644 src/openai/resources/beta/threads/messages/files.py create mode 100644 src/openai/resources/beta/vector_stores/__init__.py create mode 100644 src/openai/resources/beta/vector_stores/file_batches.py rename src/openai/resources/beta/{assistants => vector_stores}/files.py (57%) create mode 100644 src/openai/resources/beta/vector_stores/vector_stores.py delete mode 100644 src/openai/types/beta/assistants/__init__.py delete mode 100644 src/openai/types/beta/assistants/assistant_file.py create mode 100644 src/openai/types/beta/file_search_tool.py rename src/openai/types/beta/{retrieval_tool_param.py => file_search_tool_param.py} (50%) delete mode 100644 src/openai/types/beta/threads/messages/__init__.py delete mode 100644 src/openai/types/beta/threads/messages/message_file.py rename src/openai/types/beta/threads/runs/{retrieval_tool_call.py => file_search_tool_call.py} (61%) rename src/openai/types/beta/threads/runs/{retrieval_tool_call_delta.py => file_search_tool_call_delta.py} (67%) create mode 100644 src/openai/types/beta/vector_store.py create mode 100644 src/openai/types/beta/vector_store_create_params.py rename src/openai/types/beta/{retrieval_tool.py => vector_store_deleted.py} (52%) rename src/openai/types/beta/{assistants/file_list_params.py => vector_store_list_params.py} (92%) create mode 100644 src/openai/types/beta/vector_store_update_params.py create mode 100644 src/openai/types/beta/vector_stores/__init__.py create mode 100644 src/openai/types/beta/vector_stores/file_batch_create_params.py create mode 100644 src/openai/types/beta/vector_stores/file_batch_list_files_params.py rename src/openai/types/beta/{assistants => vector_stores}/file_create_params.py (70%) rename src/openai/types/beta/{threads/messages => vector_stores}/file_list_params.py (84%) create mode 100644 src/openai/types/beta/vector_stores/vector_store_file.py create mode 100644 src/openai/types/beta/vector_stores/vector_store_file_batch.py rename src/openai/types/beta/{assistants/file_delete_response.py => vector_stores/vector_store_file_deleted.py} (60%) create mode 100644 tests/api_resources/beta/test_vector_stores.py delete mode 100644 tests/api_resources/beta/threads/messages/__init__.py delete mode 100644 tests/api_resources/beta/threads/messages/test_files.py rename tests/api_resources/beta/{assistants => vector_stores}/__init__.py (100%) create mode 100644 tests/api_resources/beta/vector_stores/test_file_batches.py rename tests/api_resources/beta/{assistants => vector_stores}/test_files.py (59%) diff --git a/.stats.yml b/.stats.yml index 47c2bce1cc..2814bb7778 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 55 +configured_endpoints: 62 diff --git a/README.md b/README.md index 3bdd6c4a43..84d9017e45 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ so that your API Key is not stored in source control. ### Polling Helpers -When interacting with the API some actions such as starting a Run may take time to complete. The SDK includes +When interacting with the API some actions such as starting a Run and adding files to vector stores are asynchronous and take time to complete. The SDK includes helper functions which will poll the status until it reaches a terminal state and then return the resulting object. If an API method results in an action which could benefit from polling there will be a corresponding version of the method ending in '\_and_poll'. @@ -69,6 +69,20 @@ run = client.beta.threads.runs.create_and_poll( More information on the lifecycle of a Run can be found in the [Run Lifecycle Documentation](https://platform.openai.com/docs/assistants/how-it-works/run-lifecycle) +### Bulk Upload Helpers + +When creating an interacting with vector stores, you can use the polling helpers to monitor the status of operations. +For convenience, we also provide a bulk upload helper to allow you to simultaneously upload several files at once. + +```python +sample_files = [Path("sample-paper.pdf"), ...] + +batch = await client.vector_stores.file_batches.upload_and_poll( + store.id, + files=sample_files, +) +``` + ### Streaming Helpers The SDK also includes helpers to process streams and handle the incoming events. diff --git a/api.md b/api.md index c772fb7c7b..962ed7b7c5 100644 --- a/api.md +++ b/api.md @@ -196,6 +196,59 @@ Methods: # Beta +## VectorStores + +Types: + +```python +from openai.types.beta import VectorStore, VectorStoreDeleted +``` + +Methods: + +- client.beta.vector_stores.create(\*\*params) -> VectorStore +- client.beta.vector_stores.retrieve(vector_store_id) -> VectorStore +- client.beta.vector_stores.update(vector_store_id, \*\*params) -> VectorStore +- client.beta.vector_stores.list(\*\*params) -> SyncCursorPage[VectorStore] +- client.beta.vector_stores.delete(vector_store_id) -> VectorStoreDeleted + +### Files + +Types: + +```python +from openai.types.beta.vector_stores import VectorStoreFile, VectorStoreFileDeleted +``` + +Methods: + +- client.beta.vector_stores.files.create(vector_store_id, \*\*params) -> VectorStoreFile +- client.beta.vector_stores.files.retrieve(file_id, \*, vector_store_id) -> VectorStoreFile +- client.beta.vector_stores.files.list(vector_store_id, \*\*params) -> SyncCursorPage[VectorStoreFile] +- client.beta.vector_stores.files.delete(file_id, \*, vector_store_id) -> VectorStoreFileDeleted +- client.beta.vector_stores.files.create_and_poll(\*args) -> VectorStoreFile +- client.beta.vector_stores.files.poll(\*args) -> VectorStoreFile +- client.beta.vector_stores.files.upload(\*args) -> VectorStoreFile +- client.beta.vector_stores.files.upload_and_poll(\*args) -> VectorStoreFile + +### FileBatches + +Types: + +```python +from openai.types.beta.vector_stores import VectorStoreFileBatch +``` + +Methods: + +- client.beta.vector_stores.file_batches.create(vector_store_id, \*\*params) -> VectorStoreFileBatch +- client.beta.vector_stores.file_batches.retrieve(batch_id, \*, vector_store_id) -> VectorStoreFileBatch +- client.beta.vector_stores.file_batches.cancel(batch_id, \*, vector_store_id) -> VectorStoreFileBatch +- client.beta.vector_stores.file_batches.list_files(batch_id, \*, vector_store_id, \*\*params) -> SyncCursorPage[VectorStoreFile] +- client.beta.vector_stores.file_batches.create_and_poll(\*args) -> VectorStoreFileBatch +- client.beta.vector_stores.file_batches.poll(\*args) -> VectorStoreFileBatch +- client.beta.vector_stores.file_batches.upload_and_poll(\*args) -> VectorStoreFileBatch + ## Assistants Types: @@ -207,9 +260,9 @@ from openai.types.beta import ( AssistantStreamEvent, AssistantTool, CodeInterpreterTool, + FileSearchTool, FunctionTool, MessageStreamEvent, - RetrievalTool, RunStepStreamEvent, RunStreamEvent, ThreadStreamEvent, @@ -218,26 +271,11 @@ from openai.types.beta import ( Methods: -- client.beta.assistants.create(\*\*params) -> Assistant -- client.beta.assistants.retrieve(assistant_id) -> Assistant -- client.beta.assistants.update(assistant_id, \*\*params) -> Assistant -- client.beta.assistants.list(\*\*params) -> SyncCursorPage[Assistant] -- client.beta.assistants.delete(assistant_id) -> AssistantDeleted - -### Files - -Types: - -```python -from openai.types.beta.assistants import AssistantFile, FileDeleteResponse -``` - -Methods: - -- client.beta.assistants.files.create(assistant_id, \*\*params) -> AssistantFile -- client.beta.assistants.files.retrieve(file_id, \*, assistant_id) -> AssistantFile -- client.beta.assistants.files.list(assistant_id, \*\*params) -> SyncCursorPage[AssistantFile] -- client.beta.assistants.files.delete(file_id, \*, assistant_id) -> FileDeleteResponse +- client.beta.assistants.create(\*\*params) -> Assistant +- client.beta.assistants.retrieve(assistant_id) -> Assistant +- client.beta.assistants.update(assistant_id, \*\*params) -> Assistant +- client.beta.assistants.list(\*\*params) -> SyncCursorPage[Assistant] +- client.beta.assistants.delete(assistant_id) -> AssistantDeleted ## Threads @@ -298,11 +336,11 @@ from openai.types.beta.threads.runs import ( CodeInterpreterOutputImage, CodeInterpreterToolCall, CodeInterpreterToolCallDelta, + FileSearchToolCall, + FileSearchToolCallDelta, FunctionToolCall, FunctionToolCallDelta, MessageCreationStepDetails, - RetrievalToolCall, - RetrievalToolCallDelta, RunStep, RunStepDelta, RunStepDeltaEvent, @@ -350,23 +388,10 @@ from openai.types.beta.threads import ( Methods: -- client.beta.threads.messages.create(thread_id, \*\*params) -> Message -- client.beta.threads.messages.retrieve(message_id, \*, thread_id) -> Message -- client.beta.threads.messages.update(message_id, \*, thread_id, \*\*params) -> Message -- client.beta.threads.messages.list(thread_id, \*\*params) -> SyncCursorPage[Message] - -#### Files - -Types: - -```python -from openai.types.beta.threads.messages import MessageFile -``` - -Methods: - -- client.beta.threads.messages.files.retrieve(file_id, \*, thread_id, message_id) -> MessageFile -- client.beta.threads.messages.files.list(message_id, \*, thread_id, \*\*params) -> SyncCursorPage[MessageFile] +- client.beta.threads.messages.create(thread_id, \*\*params) -> Message +- client.beta.threads.messages.retrieve(message_id, \*, thread_id) -> Message +- client.beta.threads.messages.update(message_id, \*, thread_id, \*\*params) -> Message +- client.beta.threads.messages.list(thread_id, \*\*params) -> SyncCursorPage[Message] # Batches diff --git a/helpers.md b/helpers.md index 4271cd9ede..cf738f3f16 100644 --- a/helpers.md +++ b/helpers.md @@ -213,3 +213,24 @@ def get_final_messages(self) -> List[Message] These methods are provided for convenience to collect information at the end of a stream. Calling these events will trigger consumption of the stream until completion and then return the relevant accumulated objects. + +# Polling Helpers + +When interacting with the API some actions such as starting a Run and adding files to vector stores are asynchronous and take time to complete. +The SDK includes helper functions which will poll the status until it reaches a terminal state and then return the resulting object. +If an API method results in an action which could benefit from polling there will be a corresponding version of the +method ending in `_and_poll`. + +All methods also allow you to set the polling frequency, how often the API is checked for an update, via a function argument (`poll_interval_ms`). + +The polling methods are: + +```python +client.beta.threads.create_and_run_poll(...) +client.beta.threads.runs.create_and_poll(...) +client.beta.threads.runs.submit_tool_ouptputs_and_poll(...) +client.beta.vector_stores.files.upload_and_poll(...) +client.beta.vector_stores.files.create_and_poll(...) +client.beta.vector_stores.file_batches.create_and_poll(...) +client.beta.vector_stores.file_batches.upload_and_poll(...) +``` diff --git a/pyproject.toml b/pyproject.toml index 11ab55cbe9..6c3ae2b592 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,8 @@ dev-dependencies = [ "inline-snapshot >=0.7.0", "azure-identity >=1.14.1", "types-tqdm > 4", - "types-pyaudio > 0" + "types-pyaudio > 0", + "trio >=0.22.2" ] [tool.rye.scripts] diff --git a/requirements-dev.lock b/requirements-dev.lock index 4461f65738..657e6cb810 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -18,7 +18,9 @@ argcomplete==3.1.2 asttokens==2.4.1 # via inline-snapshot attrs==23.1.0 + # via outcome # via pytest + # via trio azure-core==1.30.1 # via azure-identity azure-identity==1.15.0 @@ -48,6 +50,7 @@ distro==1.8.0 # via openai exceptiongroup==1.1.3 # via anyio + # via trio executing==2.0.1 # via inline-snapshot filelock==3.12.4 @@ -63,6 +66,7 @@ idna==3.4 # via anyio # via httpx # via requests + # via trio importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -83,6 +87,8 @@ numpy==1.26.3 # via openai # via pandas # via pandas-stubs +outcome==1.3.0.post0 + # via trio packaging==23.2 # via black # via msal-extensions @@ -136,6 +142,9 @@ sniffio==1.3.0 # via anyio # via httpx # via openai + # via trio +sortedcontainers==2.4.0 + # via trio time-machine==2.9.0 toml==0.10.2 # via inline-snapshot @@ -145,6 +154,7 @@ tomli==2.0.1 # via pytest tqdm==4.66.1 # via openai +trio==0.22.2 types-pyaudio==0.2.16.20240106 types-pytz==2024.1.0.20240203 # via pandas-stubs diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 87fea25267..01f5338757 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -24,8 +24,22 @@ AssistantsWithStreamingResponse, AsyncAssistantsWithStreamingResponse, ) +from .vector_stores import ( + VectorStores, + AsyncVectorStores, + VectorStoresWithRawResponse, + AsyncVectorStoresWithRawResponse, + VectorStoresWithStreamingResponse, + AsyncVectorStoresWithStreamingResponse, +) __all__ = [ + "VectorStores", + "AsyncVectorStores", + "VectorStoresWithRawResponse", + "AsyncVectorStoresWithRawResponse", + "VectorStoresWithStreamingResponse", + "AsyncVectorStoresWithStreamingResponse", "Assistants", "AsyncAssistants", "AssistantsWithRawResponse", diff --git a/src/openai/resources/beta/assistants/assistants.py b/src/openai/resources/beta/assistants.py similarity index 71% rename from src/openai/resources/beta/assistants/assistants.py rename to src/openai/resources/beta/assistants.py index 9e88794ebc..8695a949ca 100644 --- a/src/openai/resources/beta/assistants/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -2,38 +2,31 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal import httpx -from .... import _legacy_response -from .files import ( - Files, - AsyncFiles, - FilesWithRawResponse, - AsyncFilesWithRawResponse, - FilesWithStreamingResponse, - AsyncFilesWithStreamingResponse, -) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( maybe_transform, async_maybe_transform, ) -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncCursorPage, AsyncCursorPage -from ....types.beta import ( +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ...types.beta import ( Assistant, AssistantDeleted, AssistantToolParam, + AssistantResponseFormatOptionParam, assistant_list_params, assistant_create_params, assistant_update_params, ) -from ...._base_client import ( +from ..._base_client import ( AsyncPaginator, make_request_options, ) @@ -42,10 +35,6 @@ class Assistants(SyncAPIResource): - @cached_property - def files(self) -> Files: - return Files(self._client) - @cached_property def with_raw_response(self) -> AssistantsWithRawResponse: return AssistantsWithRawResponse(self) @@ -81,11 +70,14 @@ def create( ], ], description: Optional[str] | NotGiven = NOT_GIVEN, - file_ids: List[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_resources: Optional[assistant_create_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -105,10 +97,6 @@ def create( description: The description of the assistant. The maximum length is 512 characters. - file_ids: A list of [file](https://platform.openai.com/docs/api-reference/files) IDs - attached to this assistant. There can be a maximum of 20 files attached to the - assistant. Files are ordered by their creation date in ascending order. - instructions: The system instructions that the assistant uses. The maximum length is 256,000 characters. @@ -119,8 +107,39 @@ def create( name: The name of the assistant. The maximum length is 256 characters. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per - assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. + assistant. Tools can be of types `code_interpreter`, `file_search`, or + `function`. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or temperature but not both. extra_headers: Send extra headers @@ -130,18 +149,21 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( "/assistants", body=maybe_transform( { "model": model, "description": description, - "file_ids": file_ids, "instructions": instructions, "metadata": metadata, "name": name, + "response_format": response_format, + "temperature": temperature, + "tool_resources": tool_resources, "tools": tools, + "top_p": top_p, }, assistant_create_params.AssistantCreateParams, ), @@ -176,7 +198,7 @@ def retrieve( """ if not assistant_id: raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( f"/assistants/{assistant_id}", options=make_request_options( @@ -190,12 +212,15 @@ def update( assistant_id: str, *, description: Optional[str] | NotGiven = NOT_GIVEN, - file_ids: List[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_resources: Optional[assistant_update_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -210,12 +235,6 @@ def update( The maximum length is 512 characters. - file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs - attached to this assistant. There can be a maximum of 20 files attached to the - assistant. Files are ordered by their creation date in ascending order. If a - file was previously attached to the list but does not show up in the list, it - will be deleted from the assistant. - instructions: The system instructions that the assistant uses. The maximum length is 256,000 characters. @@ -232,8 +251,39 @@ def update( name: The name of the assistant. The maximum length is 256 characters. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per - assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. + assistant. Tools can be of types `code_interpreter`, `file_search`, or + `function`. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or temperature but not both. extra_headers: Send extra headers @@ -245,18 +295,21 @@ def update( """ if not assistant_id: raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/assistants/{assistant_id}", body=maybe_transform( { "description": description, - "file_ids": file_ids, "instructions": instructions, "metadata": metadata, "model": model, "name": name, + "response_format": response_format, + "temperature": temperature, + "tool_resources": tool_resources, "tools": tools, + "top_p": top_p, }, assistant_update_params.AssistantUpdateParams, ), @@ -309,7 +362,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( "/assistants", page=SyncCursorPage[Assistant], @@ -356,7 +409,7 @@ def delete( """ if not assistant_id: raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._delete( f"/assistants/{assistant_id}", options=make_request_options( @@ -367,10 +420,6 @@ def delete( class AsyncAssistants(AsyncAPIResource): - @cached_property - def files(self) -> AsyncFiles: - return AsyncFiles(self._client) - @cached_property def with_raw_response(self) -> AsyncAssistantsWithRawResponse: return AsyncAssistantsWithRawResponse(self) @@ -406,11 +455,14 @@ async def create( ], ], description: Optional[str] | NotGiven = NOT_GIVEN, - file_ids: List[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_resources: Optional[assistant_create_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -430,10 +482,6 @@ async def create( description: The description of the assistant. The maximum length is 512 characters. - file_ids: A list of [file](https://platform.openai.com/docs/api-reference/files) IDs - attached to this assistant. There can be a maximum of 20 files attached to the - assistant. Files are ordered by their creation date in ascending order. - instructions: The system instructions that the assistant uses. The maximum length is 256,000 characters. @@ -444,8 +492,39 @@ async def create( name: The name of the assistant. The maximum length is 256 characters. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per - assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. + assistant. Tools can be of types `code_interpreter`, `file_search`, or + `function`. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or temperature but not both. extra_headers: Send extra headers @@ -455,18 +534,21 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( "/assistants", body=await async_maybe_transform( { "model": model, "description": description, - "file_ids": file_ids, "instructions": instructions, "metadata": metadata, "name": name, + "response_format": response_format, + "temperature": temperature, + "tool_resources": tool_resources, "tools": tools, + "top_p": top_p, }, assistant_create_params.AssistantCreateParams, ), @@ -501,7 +583,7 @@ async def retrieve( """ if not assistant_id: raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( f"/assistants/{assistant_id}", options=make_request_options( @@ -515,12 +597,15 @@ async def update( assistant_id: str, *, description: Optional[str] | NotGiven = NOT_GIVEN, - file_ids: List[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_resources: Optional[assistant_update_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -535,12 +620,6 @@ async def update( The maximum length is 512 characters. - file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs - attached to this assistant. There can be a maximum of 20 files attached to the - assistant. Files are ordered by their creation date in ascending order. If a - file was previously attached to the list but does not show up in the list, it - will be deleted from the assistant. - instructions: The system instructions that the assistant uses. The maximum length is 256,000 characters. @@ -557,8 +636,39 @@ async def update( name: The name of the assistant. The maximum length is 256 characters. + response_format: Specifies the format that the model must output. Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. + + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per - assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`. + assistant. Tools can be of types `code_interpreter`, `file_search`, or + `function`. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or temperature but not both. extra_headers: Send extra headers @@ -570,18 +680,21 @@ async def update( """ if not assistant_id: raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/assistants/{assistant_id}", body=await async_maybe_transform( { "description": description, - "file_ids": file_ids, "instructions": instructions, "metadata": metadata, "model": model, "name": name, + "response_format": response_format, + "temperature": temperature, + "tool_resources": tool_resources, "tools": tools, + "top_p": top_p, }, assistant_update_params.AssistantUpdateParams, ), @@ -634,7 +747,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( "/assistants", page=AsyncCursorPage[Assistant], @@ -681,7 +794,7 @@ async def delete( """ if not assistant_id: raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._delete( f"/assistants/{assistant_id}", options=make_request_options( @@ -711,10 +824,6 @@ def __init__(self, assistants: Assistants) -> None: assistants.delete, ) - @cached_property - def files(self) -> FilesWithRawResponse: - return FilesWithRawResponse(self._assistants.files) - class AsyncAssistantsWithRawResponse: def __init__(self, assistants: AsyncAssistants) -> None: @@ -736,10 +845,6 @@ def __init__(self, assistants: AsyncAssistants) -> None: assistants.delete, ) - @cached_property - def files(self) -> AsyncFilesWithRawResponse: - return AsyncFilesWithRawResponse(self._assistants.files) - class AssistantsWithStreamingResponse: def __init__(self, assistants: Assistants) -> None: @@ -761,10 +866,6 @@ def __init__(self, assistants: Assistants) -> None: assistants.delete, ) - @cached_property - def files(self) -> FilesWithStreamingResponse: - return FilesWithStreamingResponse(self._assistants.files) - class AsyncAssistantsWithStreamingResponse: def __init__(self, assistants: AsyncAssistants) -> None: @@ -785,7 +886,3 @@ def __init__(self, assistants: AsyncAssistants) -> None: self.delete = async_to_streamed_response_wrapper( assistants.delete, ) - - @cached_property - def files(self) -> AsyncFilesWithStreamingResponse: - return AsyncFilesWithStreamingResponse(self._assistants.files) diff --git a/src/openai/resources/beta/assistants/__init__.py b/src/openai/resources/beta/assistants/__init__.py deleted file mode 100644 index 736def9388..0000000000 --- a/src/openai/resources/beta/assistants/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .files import ( - Files, - AsyncFiles, - FilesWithRawResponse, - AsyncFilesWithRawResponse, - FilesWithStreamingResponse, - AsyncFilesWithStreamingResponse, -) -from .assistants import ( - Assistants, - AsyncAssistants, - AssistantsWithRawResponse, - AsyncAssistantsWithRawResponse, - AssistantsWithStreamingResponse, - AsyncAssistantsWithStreamingResponse, -) - -__all__ = [ - "Files", - "AsyncFiles", - "FilesWithRawResponse", - "AsyncFilesWithRawResponse", - "FilesWithStreamingResponse", - "AsyncFilesWithStreamingResponse", - "Assistants", - "AsyncAssistants", - "AssistantsWithRawResponse", - "AsyncAssistantsWithRawResponse", - "AssistantsWithStreamingResponse", - "AsyncAssistantsWithStreamingResponse", -] diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 67baad2716..0d9806678f 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -20,13 +20,25 @@ AsyncAssistantsWithStreamingResponse, ) from ..._resource import SyncAPIResource, AsyncAPIResource +from .vector_stores import ( + VectorStores, + AsyncVectorStores, + VectorStoresWithRawResponse, + AsyncVectorStoresWithRawResponse, + VectorStoresWithStreamingResponse, + AsyncVectorStoresWithStreamingResponse, +) from .threads.threads import Threads, AsyncThreads -from .assistants.assistants import Assistants, AsyncAssistants +from .vector_stores.vector_stores import VectorStores, AsyncVectorStores __all__ = ["Beta", "AsyncBeta"] class Beta(SyncAPIResource): + @cached_property + def vector_stores(self) -> VectorStores: + return VectorStores(self._client) + @cached_property def assistants(self) -> Assistants: return Assistants(self._client) @@ -45,6 +57,10 @@ def with_streaming_response(self) -> BetaWithStreamingResponse: class AsyncBeta(AsyncAPIResource): + @cached_property + def vector_stores(self) -> AsyncVectorStores: + return AsyncVectorStores(self._client) + @cached_property def assistants(self) -> AsyncAssistants: return AsyncAssistants(self._client) @@ -66,6 +82,10 @@ class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: self._beta = beta + @cached_property + def vector_stores(self) -> VectorStoresWithRawResponse: + return VectorStoresWithRawResponse(self._beta.vector_stores) + @cached_property def assistants(self) -> AssistantsWithRawResponse: return AssistantsWithRawResponse(self._beta.assistants) @@ -79,6 +99,10 @@ class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta + @cached_property + def vector_stores(self) -> AsyncVectorStoresWithRawResponse: + return AsyncVectorStoresWithRawResponse(self._beta.vector_stores) + @cached_property def assistants(self) -> AsyncAssistantsWithRawResponse: return AsyncAssistantsWithRawResponse(self._beta.assistants) @@ -92,6 +116,10 @@ class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: self._beta = beta + @cached_property + def vector_stores(self) -> VectorStoresWithStreamingResponse: + return VectorStoresWithStreamingResponse(self._beta.vector_stores) + @cached_property def assistants(self) -> AssistantsWithStreamingResponse: return AssistantsWithStreamingResponse(self._beta.assistants) @@ -105,6 +133,10 @@ class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta + @cached_property + def vector_stores(self) -> AsyncVectorStoresWithStreamingResponse: + return AsyncVectorStoresWithStreamingResponse(self._beta.vector_stores) + @cached_property def assistants(self) -> AsyncAssistantsWithStreamingResponse: return AsyncAssistantsWithStreamingResponse(self._beta.assistants) diff --git a/src/openai/resources/beta/threads/messages/messages.py b/src/openai/resources/beta/threads/messages.py similarity index 88% rename from src/openai/resources/beta/threads/messages/messages.py rename to src/openai/resources/beta/threads/messages.py index bbce3e99e4..7a24b80dea 100644 --- a/src/openai/resources/beta/threads/messages/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -2,43 +2,31 @@ from __future__ import annotations -from typing import List, Optional +from typing import Iterable, Optional from typing_extensions import Literal import httpx -from ..... import _legacy_response -from .files import ( - Files, - AsyncFiles, - FilesWithRawResponse, - AsyncFilesWithRawResponse, - FilesWithStreamingResponse, - AsyncFilesWithStreamingResponse, -) -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ....._utils import ( +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( maybe_transform, async_maybe_transform, ) -from ....._compat import cached_property -from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import ( +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import ( AsyncPaginator, make_request_options, ) -from .....types.beta.threads import Message, message_list_params, message_create_params, message_update_params +from ....types.beta.threads import Message, message_list_params, message_create_params, message_update_params __all__ = ["Messages", "AsyncMessages"] class Messages(SyncAPIResource): - @cached_property - def files(self) -> Files: - return Files(self._client) - @cached_property def with_raw_response(self) -> MessagesWithRawResponse: return MessagesWithRawResponse(self) @@ -53,7 +41,7 @@ def create( *, content: str, role: Literal["user", "assistant"], - file_ids: List[str] | NotGiven = NOT_GIVEN, + attachments: Optional[Iterable[message_create_params.Attachment]] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -76,10 +64,7 @@ def create( - `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation. - file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that - the message should use. There can be a maximum of 10 files attached to a - message. Useful for tools like `retrieval` and `code_interpreter` that can - access and use files. + attachments: A list of files attached to the message, and the tools they should be added to. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys @@ -96,14 +81,14 @@ def create( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/messages", body=maybe_transform( { "content": content, "role": role, - "file_ids": file_ids, + "attachments": attachments, "metadata": metadata, }, message_create_params.MessageCreateParams, @@ -142,7 +127,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not message_id: raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( f"/threads/{thread_id}/messages/{message_id}", options=make_request_options( @@ -185,7 +170,7 @@ def update( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not message_id: raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/messages/{message_id}", body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), @@ -243,7 +228,7 @@ def list( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/messages", page=SyncCursorPage[Message], @@ -268,10 +253,6 @@ def list( class AsyncMessages(AsyncAPIResource): - @cached_property - def files(self) -> AsyncFiles: - return AsyncFiles(self._client) - @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: return AsyncMessagesWithRawResponse(self) @@ -286,7 +267,7 @@ async def create( *, content: str, role: Literal["user", "assistant"], - file_ids: List[str] | NotGiven = NOT_GIVEN, + attachments: Optional[Iterable[message_create_params.Attachment]] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -309,10 +290,7 @@ async def create( - `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation. - file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that - the message should use. There can be a maximum of 10 files attached to a - message. Useful for tools like `retrieval` and `code_interpreter` that can - access and use files. + attachments: A list of files attached to the message, and the tools they should be added to. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys @@ -329,14 +307,14 @@ async def create( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/messages", body=await async_maybe_transform( { "content": content, "role": role, - "file_ids": file_ids, + "attachments": attachments, "metadata": metadata, }, message_create_params.MessageCreateParams, @@ -375,7 +353,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not message_id: raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}/messages/{message_id}", options=make_request_options( @@ -418,7 +396,7 @@ async def update( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not message_id: raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/messages/{message_id}", body=await async_maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), @@ -476,7 +454,7 @@ def list( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/messages", page=AsyncCursorPage[Message], @@ -517,10 +495,6 @@ def __init__(self, messages: Messages) -> None: messages.list, ) - @cached_property - def files(self) -> FilesWithRawResponse: - return FilesWithRawResponse(self._messages.files) - class AsyncMessagesWithRawResponse: def __init__(self, messages: AsyncMessages) -> None: @@ -539,10 +513,6 @@ def __init__(self, messages: AsyncMessages) -> None: messages.list, ) - @cached_property - def files(self) -> AsyncFilesWithRawResponse: - return AsyncFilesWithRawResponse(self._messages.files) - class MessagesWithStreamingResponse: def __init__(self, messages: Messages) -> None: @@ -561,10 +531,6 @@ def __init__(self, messages: Messages) -> None: messages.list, ) - @cached_property - def files(self) -> FilesWithStreamingResponse: - return FilesWithStreamingResponse(self._messages.files) - class AsyncMessagesWithStreamingResponse: def __init__(self, messages: AsyncMessages) -> None: @@ -582,7 +548,3 @@ def __init__(self, messages: AsyncMessages) -> None: self.list = async_to_streamed_response_wrapper( messages.list, ) - - @cached_property - def files(self) -> AsyncFilesWithStreamingResponse: - return AsyncFilesWithStreamingResponse(self._messages.files) diff --git a/src/openai/resources/beta/threads/messages/__init__.py b/src/openai/resources/beta/threads/messages/__init__.py deleted file mode 100644 index a3286e6ace..0000000000 --- a/src/openai/resources/beta/threads/messages/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .files import ( - Files, - AsyncFiles, - FilesWithRawResponse, - AsyncFilesWithRawResponse, - FilesWithStreamingResponse, - AsyncFilesWithStreamingResponse, -) -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, - MessagesWithStreamingResponse, - AsyncMessagesWithStreamingResponse, -) - -__all__ = [ - "Files", - "AsyncFiles", - "FilesWithRawResponse", - "AsyncFilesWithRawResponse", - "FilesWithStreamingResponse", - "AsyncFilesWithStreamingResponse", - "Messages", - "AsyncMessages", - "MessagesWithRawResponse", - "AsyncMessagesWithRawResponse", - "MessagesWithStreamingResponse", - "AsyncMessagesWithStreamingResponse", -] diff --git a/src/openai/resources/beta/threads/messages/files.py b/src/openai/resources/beta/threads/messages/files.py deleted file mode 100644 index 349f99725e..0000000000 --- a/src/openai/resources/beta/threads/messages/files.py +++ /dev/null @@ -1,312 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -from ..... import _legacy_response -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ....._utils import maybe_transform -from ....._compat import cached_property -from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import ( - AsyncPaginator, - make_request_options, -) -from .....types.beta.threads.messages import MessageFile, file_list_params - -__all__ = ["Files", "AsyncFiles"] - - -class Files(SyncAPIResource): - @cached_property - def with_raw_response(self) -> FilesWithRawResponse: - return FilesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> FilesWithStreamingResponse: - return FilesWithStreamingResponse(self) - - def retrieve( - self, - file_id: str, - *, - thread_id: str, - message_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> MessageFile: - """ - Retrieves a message file. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not thread_id: - raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - if not message_id: - raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} - return self._get( - f"/threads/{thread_id}/messages/{message_id}/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageFile, - ) - - def list( - self, - message_id: str, - *, - thread_id: str, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncCursorPage[MessageFile]: - """Returns a list of message files. - - Args: - after: A cursor for use in pagination. - - `after` is an object ID that defines your place - in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include after=obj_foo in order to - fetch the next page of the list. - - before: A cursor for use in pagination. `before` is an object ID that defines your place - in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. - - limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the default is 20. - - order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending - order and `desc` for descending order. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not thread_id: - raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - if not message_id: - raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} - return self._get_api_list( - f"/threads/{thread_id}/messages/{message_id}/files", - page=SyncCursorPage[MessageFile], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after": after, - "before": before, - "limit": limit, - "order": order, - }, - file_list_params.FileListParams, - ), - ), - model=MessageFile, - ) - - -class AsyncFiles(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncFilesWithRawResponse: - return AsyncFilesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: - return AsyncFilesWithStreamingResponse(self) - - async def retrieve( - self, - file_id: str, - *, - thread_id: str, - message_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> MessageFile: - """ - Retrieves a message file. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not thread_id: - raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - if not message_id: - raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} - return await self._get( - f"/threads/{thread_id}/messages/{message_id}/files/{file_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageFile, - ) - - def list( - self, - message_id: str, - *, - thread_id: str, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[MessageFile, AsyncCursorPage[MessageFile]]: - """Returns a list of message files. - - Args: - after: A cursor for use in pagination. - - `after` is an object ID that defines your place - in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include after=obj_foo in order to - fetch the next page of the list. - - before: A cursor for use in pagination. `before` is an object ID that defines your place - in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. - - limit: A limit on the number of objects to be returned. Limit can range between 1 and - 100, and the default is 20. - - order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending - order and `desc` for descending order. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not thread_id: - raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - if not message_id: - raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} - return self._get_api_list( - f"/threads/{thread_id}/messages/{message_id}/files", - page=AsyncCursorPage[MessageFile], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after": after, - "before": before, - "limit": limit, - "order": order, - }, - file_list_params.FileListParams, - ), - ), - model=MessageFile, - ) - - -class FilesWithRawResponse: - def __init__(self, files: Files) -> None: - self._files = files - - self.retrieve = _legacy_response.to_raw_response_wrapper( - files.retrieve, - ) - self.list = _legacy_response.to_raw_response_wrapper( - files.list, - ) - - -class AsyncFilesWithRawResponse: - def __init__(self, files: AsyncFiles) -> None: - self._files = files - - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - files.retrieve, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - files.list, - ) - - -class FilesWithStreamingResponse: - def __init__(self, files: Files) -> None: - self._files = files - - self.retrieve = to_streamed_response_wrapper( - files.retrieve, - ) - self.list = to_streamed_response_wrapper( - files.list, - ) - - -class AsyncFilesWithStreamingResponse: - def __init__(self, files: AsyncFiles) -> None: - self._files = files - - self.retrieve = async_to_streamed_response_wrapper( - files.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - files.list, - ) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 9fa7239c0b..7aab17a30d 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -115,6 +115,7 @@ def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -196,6 +197,10 @@ def create( tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -248,6 +253,7 @@ def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -329,6 +335,10 @@ def create( tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -381,6 +391,7 @@ def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -462,6 +473,10 @@ def create( tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -514,6 +529,7 @@ def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -524,7 +540,7 @@ def create( ) -> Run | Stream[AssistantStreamEvent]: if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/runs", body=maybe_transform( @@ -542,6 +558,7 @@ def create( "temperature": temperature, "tool_choice": tool_choice, "tools": tools, + "top_p": top_p, "truncation_strategy": truncation_strategy, }, run_create_params.RunCreateParams, @@ -582,7 +599,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( f"/threads/{thread_id}/runs/{run_id}", options=make_request_options( @@ -625,7 +642,7 @@ def update( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/runs/{run_id}", body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), @@ -680,7 +697,7 @@ def list( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/runs", page=SyncCursorPage[Run], @@ -730,7 +747,7 @@ def cancel( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/runs/{run_id}/cancel", options=make_request_options( @@ -778,6 +795,7 @@ def create_and_poll( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, thread_id: str, @@ -810,6 +828,7 @@ def create_and_poll( stream=False, tools=tools, truncation_strategy=truncation_strategy, + top_p=top_p, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, @@ -866,6 +885,7 @@ def create_and_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -919,6 +939,7 @@ def create_and_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT, @@ -972,6 +993,7 @@ def create_and_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT | None = None, @@ -987,7 +1009,7 @@ def create_and_stream( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { - "OpenAI-Beta": "assistants=v1", + "OpenAI-Beta": "assistants=v2", "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", **(extra_headers or {}), @@ -1011,6 +1033,7 @@ def create_and_stream( "stream": True, "tools": tools, "truncation_strategy": truncation_strategy, + "top_p": top_p, }, run_create_params.RunCreateParams, ), @@ -1108,6 +1131,7 @@ def stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1160,6 +1184,7 @@ def stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT, @@ -1212,6 +1237,7 @@ def stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AssistantEventHandlerT | None = None, @@ -1227,7 +1253,7 @@ def stream( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { - "OpenAI-Beta": "assistants=v1", + "OpenAI-Beta": "assistants=v2", "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", **(extra_headers or {}), @@ -1251,6 +1277,7 @@ def stream( "stream": True, "tools": tools, "truncation_strategy": truncation_strategy, + "top_p": top_p, }, run_create_params.RunCreateParams, ), @@ -1396,7 +1423,7 @@ def submit_tool_outputs( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", body=maybe_transform( @@ -1522,7 +1549,7 @@ def submit_tool_outputs_stream( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { - "OpenAI-Beta": "assistants=v1", + "OpenAI-Beta": "assistants=v2", "X-Stainless-Stream-Helper": "threads.runs.submit_tool_outputs_stream", "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", **(extra_headers or {}), @@ -1602,6 +1629,7 @@ async def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1683,6 +1711,10 @@ async def create( tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1735,6 +1767,7 @@ async def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1816,6 +1849,10 @@ async def create( tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1868,6 +1905,7 @@ async def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1949,6 +1987,10 @@ async def create( tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2001,6 +2043,7 @@ async def create( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2011,7 +2054,7 @@ async def create( ) -> Run | AsyncStream[AssistantStreamEvent]: if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs", body=await async_maybe_transform( @@ -2029,6 +2072,7 @@ async def create( "temperature": temperature, "tool_choice": tool_choice, "tools": tools, + "top_p": top_p, "truncation_strategy": truncation_strategy, }, run_create_params.RunCreateParams, @@ -2069,7 +2113,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}/runs/{run_id}", options=make_request_options( @@ -2112,7 +2156,7 @@ async def update( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs/{run_id}", body=await async_maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), @@ -2167,7 +2211,7 @@ def list( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/runs", page=AsyncCursorPage[Run], @@ -2217,7 +2261,7 @@ async def cancel( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs/{run_id}/cancel", options=make_request_options( @@ -2265,6 +2309,7 @@ async def create_and_poll( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, thread_id: str, @@ -2297,6 +2342,7 @@ async def create_and_poll( stream=False, tools=tools, truncation_strategy=truncation_strategy, + top_p=top_p, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, @@ -2353,6 +2399,7 @@ def create_and_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2406,6 +2453,7 @@ def create_and_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT, @@ -2459,6 +2507,7 @@ def create_and_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT | None = None, @@ -2477,7 +2526,7 @@ def create_and_stream( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { - "OpenAI-Beta": "assistants=v1", + "OpenAI-Beta": "assistants=v2", "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", **(extra_headers or {}), @@ -2500,6 +2549,7 @@ def create_and_stream( "stream": True, "tools": tools, "truncation_strategy": truncation_strategy, + "top_p": top_p, }, run_create_params.RunCreateParams, ), @@ -2597,6 +2647,7 @@ def stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2649,6 +2700,7 @@ def stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT, @@ -2701,6 +2753,7 @@ def stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, thread_id: str, event_handler: AsyncAssistantEventHandlerT | None = None, @@ -2719,7 +2772,7 @@ def stream( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { - "OpenAI-Beta": "assistants=v1", + "OpenAI-Beta": "assistants=v2", "X-Stainless-Stream-Helper": "threads.runs.create_and_stream", "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", **(extra_headers or {}), @@ -2742,6 +2795,7 @@ def stream( "stream": True, "tools": tools, "truncation_strategy": truncation_strategy, + "top_p": top_p, }, run_create_params.RunCreateParams, ), @@ -2887,7 +2941,7 @@ async def submit_tool_outputs( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", body=await async_maybe_transform( @@ -3016,7 +3070,7 @@ def submit_tool_outputs_stream( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { - "OpenAI-Beta": "assistants=v1", + "OpenAI-Beta": "assistants=v2", "X-Stainless-Stream-Helper": "threads.runs.submit_tool_outputs_stream", "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", **(extra_headers or {}), diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 118bd8822a..986ef2997a 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -62,7 +62,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", options=make_request_options( @@ -119,7 +119,7 @@ def list( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/runs/{run_id}/steps", page=SyncCursorPage[RunStep], @@ -182,7 +182,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", options=make_request_options( @@ -239,7 +239,7 @@ def list( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( f"/threads/{thread_id}/runs/{run_id}/steps", page=AsyncCursorPage[RunStep], diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 9c2e2f0043..678c621a10 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -57,7 +57,6 @@ AsyncAssistantEventHandlerT, AsyncAssistantStreamManager, ) -from .messages.messages import Messages, AsyncMessages from ....types.beta.threads import Run __all__ = ["Threads", "AsyncThreads"] @@ -85,6 +84,7 @@ def create( *, messages: Iterable[thread_create_params.Message] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_params.ToolResources] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -104,6 +104,11 @@ def create( can be a maximum of 64 characters long and values can be a maxium of 512 characters long. + tool_resources: A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -112,13 +117,14 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( "/threads", body=maybe_transform( { "messages": messages, "metadata": metadata, + "tool_resources": tool_resources, }, thread_create_params.ThreadCreateParams, ), @@ -153,7 +159,7 @@ def retrieve( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( f"/threads/{thread_id}", options=make_request_options( @@ -167,6 +173,7 @@ def update( thread_id: str, *, metadata: Optional[object] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_update_params.ToolResources] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -183,6 +190,11 @@ def update( can be a maximum of 64 characters long and values can be a maxium of 512 characters long. + tool_resources: A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -193,10 +205,16 @@ def update( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/threads/{thread_id}", - body=maybe_transform({"metadata": metadata}, thread_update_params.ThreadUpdateParams), + body=maybe_transform( + { + "metadata": metadata, + "tool_resources": tool_resources, + }, + thread_update_params.ThreadUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -228,7 +246,7 @@ def delete( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._delete( f"/threads/{thread_id}", options=make_request_options( @@ -276,7 +294,9 @@ def create_and_run( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -299,13 +319,13 @@ def create_and_run( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -350,9 +370,18 @@ def create_and_run( `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -402,7 +431,9 @@ def create_and_run( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -429,13 +460,13 @@ def create_and_run( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -476,9 +507,18 @@ def create_and_run( `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -528,7 +568,9 @@ def create_and_run( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -555,13 +597,13 @@ def create_and_run( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -602,9 +644,18 @@ def create_and_run( `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -654,7 +705,9 @@ def create_and_run( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -663,7 +716,7 @@ def create_and_run( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run | Stream[AssistantStreamEvent]: - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( "/threads/runs", body=maybe_transform( @@ -679,7 +732,9 @@ def create_and_run( "temperature": temperature, "thread": thread, "tool_choice": tool_choice, + "tool_resources": tool_resources, "tools": tools, + "top_p": top_p, "truncation_strategy": truncation_strategy, }, thread_create_and_run_params.ThreadCreateAndRunParams, @@ -729,7 +784,9 @@ def create_and_run_poll( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -755,8 +812,10 @@ def create_and_run_poll( temperature=temperature, stream=False, thread=thread, + tool_resources=tool_resources, tool_choice=tool_choice, truncation_strategy=truncation_strategy, + top_p=top_p, tools=tools, extra_headers=extra_headers, extra_query=extra_query, @@ -803,7 +862,9 @@ def create_and_run_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -853,7 +914,9 @@ def create_and_run_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -903,7 +966,9 @@ def create_and_run_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -915,7 +980,7 @@ def create_and_run_stream( ) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]: """Create a thread and stream the run back""" extra_headers = { - "OpenAI-Beta": "assistants=v1", + "OpenAI-Beta": "assistants=v2", "X-Stainless-Stream-Helper": "threads.create_and_run_stream", "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", **(extra_headers or {}), @@ -937,7 +1002,9 @@ def create_and_run_stream( "stream": True, "thread": thread, "tools": tools, + "tool": tool_resources, "truncation_strategy": truncation_strategy, + "top_p": top_p, }, thread_create_and_run_params.ThreadCreateAndRunParams, ), @@ -973,6 +1040,7 @@ async def create( *, messages: Iterable[thread_create_params.Message] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_params.ToolResources] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -992,6 +1060,11 @@ async def create( can be a maximum of 64 characters long and values can be a maxium of 512 characters long. + tool_resources: A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1000,13 +1073,14 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( "/threads", body=await async_maybe_transform( { "messages": messages, "metadata": metadata, + "tool_resources": tool_resources, }, thread_create_params.ThreadCreateParams, ), @@ -1041,7 +1115,7 @@ async def retrieve( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( f"/threads/{thread_id}", options=make_request_options( @@ -1055,6 +1129,7 @@ async def update( thread_id: str, *, metadata: Optional[object] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_update_params.ToolResources] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1071,6 +1146,11 @@ async def update( can be a maximum of 64 characters long and values can be a maxium of 512 characters long. + tool_resources: A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1081,10 +1161,16 @@ async def update( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/threads/{thread_id}", - body=await async_maybe_transform({"metadata": metadata}, thread_update_params.ThreadUpdateParams), + body=await async_maybe_transform( + { + "metadata": metadata, + "tool_resources": tool_resources, + }, + thread_update_params.ThreadUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1116,7 +1202,7 @@ async def delete( """ if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._delete( f"/threads/{thread_id}", options=make_request_options( @@ -1164,7 +1250,9 @@ async def create_and_run( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1187,13 +1275,13 @@ async def create_and_run( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -1238,9 +1326,18 @@ async def create_and_run( `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1290,7 +1387,9 @@ async def create_and_run( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1317,13 +1416,13 @@ async def create_and_run( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -1364,9 +1463,18 @@ async def create_and_run( `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1416,7 +1524,9 @@ async def create_and_run( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1443,13 +1553,13 @@ async def create_and_run( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -1490,9 +1600,18 @@ async def create_and_run( `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. + tool_resources: A set of resources that are used by the assistant's tools. The resources are + specific to the type of tool. For example, the `code_interpreter` tool requires + a list of file IDs, while the `file_search` tool requires a list of vector store + IDs. + tools: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1542,7 +1661,9 @@ async def create_and_run( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1551,7 +1672,7 @@ async def create_and_run( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Run | AsyncStream[AssistantStreamEvent]: - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( "/threads/runs", body=await async_maybe_transform( @@ -1567,7 +1688,9 @@ async def create_and_run( "temperature": temperature, "thread": thread, "tool_choice": tool_choice, + "tool_resources": tool_resources, "tools": tools, + "top_p": top_p, "truncation_strategy": truncation_strategy, }, thread_create_and_run_params.ThreadCreateAndRunParams, @@ -1617,7 +1740,9 @@ async def create_and_run_poll( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1643,8 +1768,10 @@ async def create_and_run_poll( temperature=temperature, stream=False, thread=thread, + tool_resources=tool_resources, tool_choice=tool_choice, truncation_strategy=truncation_strategy, + top_p=top_p, tools=tools, extra_headers=extra_headers, extra_query=extra_query, @@ -1693,7 +1820,9 @@ def create_and_run_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1743,7 +1872,9 @@ def create_and_run_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AsyncAssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1793,7 +1924,9 @@ def create_and_run_stream( temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AsyncAssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1808,7 +1941,7 @@ def create_and_run_stream( ): """Create a thread and stream the run back""" extra_headers = { - "OpenAI-Beta": "assistants=v1", + "OpenAI-Beta": "assistants=v2", "X-Stainless-Stream-Helper": "threads.create_and_run_stream", "X-Stainless-Custom-Event-Handler": "true" if event_handler else "false", **(extra_headers or {}), @@ -1829,7 +1962,9 @@ def create_and_run_stream( "stream": True, "thread": thread, "tools": tools, + "tool": tool_resources, "truncation_strategy": truncation_strategy, + "top_p": top_p, }, thread_create_and_run_params.ThreadCreateAndRunParams, ), diff --git a/src/openai/resources/beta/vector_stores/__init__.py b/src/openai/resources/beta/vector_stores/__init__.py new file mode 100644 index 0000000000..96ae16c302 --- /dev/null +++ b/src/openai/resources/beta/vector_stores/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) +from .file_batches import ( + FileBatches, + AsyncFileBatches, + FileBatchesWithRawResponse, + AsyncFileBatchesWithRawResponse, + FileBatchesWithStreamingResponse, + AsyncFileBatchesWithStreamingResponse, +) +from .vector_stores import ( + VectorStores, + AsyncVectorStores, + VectorStoresWithRawResponse, + AsyncVectorStoresWithRawResponse, + VectorStoresWithStreamingResponse, + AsyncVectorStoresWithStreamingResponse, +) + +__all__ = [ + "Files", + "AsyncFiles", + "FilesWithRawResponse", + "AsyncFilesWithRawResponse", + "FilesWithStreamingResponse", + "AsyncFilesWithStreamingResponse", + "FileBatches", + "AsyncFileBatches", + "FileBatchesWithRawResponse", + "AsyncFileBatchesWithRawResponse", + "FileBatchesWithStreamingResponse", + "AsyncFileBatchesWithStreamingResponse", + "VectorStores", + "AsyncVectorStores", + "VectorStoresWithRawResponse", + "AsyncVectorStoresWithRawResponse", + "VectorStoresWithStreamingResponse", + "AsyncVectorStoresWithStreamingResponse", +] diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/beta/vector_stores/file_batches.py new file mode 100644 index 0000000000..55b30b08e3 --- /dev/null +++ b/src/openai/resources/beta/vector_stores/file_batches.py @@ -0,0 +1,739 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import asyncio +from typing import List, Iterable +from typing_extensions import Literal +from concurrent.futures import Future, ThreadPoolExecutor, as_completed + +import httpx +import sniffio + +from .... import _legacy_response +from ....types import FileObject +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ...._utils import ( + is_given, + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) +from ....types.beta.vector_stores import ( + VectorStoreFile, + VectorStoreFileBatch, + file_batch_create_params, + file_batch_list_files_params, +) + +__all__ = ["FileBatches", "AsyncFileBatches"] + + +class FileBatches(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FileBatchesWithRawResponse: + return FileBatchesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FileBatchesWithStreamingResponse: + return FileBatchesWithStreamingResponse(self) + + def create( + self, + vector_store_id: str, + *, + file_ids: List[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """ + Create a vector store file batch. + + Args: + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the vector store should use. Useful for tools like `file_search` that can access + files. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + f"/vector_stores/{vector_store_id}/file_batches", + body=maybe_transform({"file_ids": file_ids}, file_batch_create_params.FileBatchCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreFileBatch, + ) + + def retrieve( + self, + batch_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """ + Retrieves a vector store file batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + f"/vector_stores/{vector_store_id}/file_batches/{batch_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreFileBatch, + ) + + def cancel( + self, + batch_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """Cancel a vector store file batch. + + This attempts to cancel the processing of + files in this batch as soon as possible. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + f"/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreFileBatch, + ) + + def create_and_poll( + self, + vector_store_id: str, + *, + file_ids: List[str], + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """Create a vector store batch and poll until all files have been processed.""" + batch = self.create( + vector_store_id=vector_store_id, + file_ids=file_ids, + ) + # TODO: don't poll unless necessary?? + return self.poll( + batch.id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + ) + + def list_files( + self, + batch_id: str, + *, + vector_store_id: str, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[VectorStoreFile]: + """ + Returns a list of vector store files in a batch. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + f"/vector_stores/{vector_store_id}/file_batches/{batch_id}/files", + page=SyncCursorPage[VectorStoreFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "filter": filter, + "limit": limit, + "order": order, + }, + file_batch_list_files_params.FileBatchListFilesParams, + ), + ), + model=VectorStoreFile, + ) + + def poll( + self, + batch_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """Wait for the given file batch to be processed. + + Note: this will return even if one of the files failed to process, you need to + check batch.file_counts.failed_count to handle this case. + """ + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = self.with_raw_response.retrieve( + batch_id, + vector_store_id=vector_store_id, + extra_headers=headers, + ) + + batch = response.parse() + if batch.file_counts.in_progress > 0: + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + self._sleep(poll_interval_ms / 1000) + continue + + return batch + + def upload_and_poll( + self, + vector_store_id: str, + *, + files: Iterable[FileTypes], + max_concurrency: int = 5, + file_ids: List[str] = [], + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """Uploads the given files concurrently and then creates a vector store file batch. + + If you've already uploaded certain files that you want to include in this batch + then you can pass their IDs through the `file_ids` argument. + + By default, if any file upload fails then an exception will be eagerly raised. + + The number of concurrency uploads is configurable using the `max_concurrency` + parameter. + + Note: this method only supports `asyncio` or `trio` as the backing async + runtime. + """ + results: list[FileObject] = [] + + with ThreadPoolExecutor(max_workers=max_concurrency) as executor: + futures: list[Future[FileObject]] = [ + executor.submit( + self._client.files.create, + file=file, + purpose="assistants", + ) + for file in files + ] + + for future in as_completed(futures): + exc = future.exception() + if exc: + raise exc + + results.append(future.result()) + + batch = self.create_and_poll( + vector_store_id=vector_store_id, + file_ids=[*file_ids, *(f.id for f in results)], + poll_interval_ms=poll_interval_ms, + ) + return batch + + +class AsyncFileBatches(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFileBatchesWithRawResponse: + return AsyncFileBatchesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFileBatchesWithStreamingResponse: + return AsyncFileBatchesWithStreamingResponse(self) + + async def create( + self, + vector_store_id: str, + *, + file_ids: List[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """ + Create a vector store file batch. + + Args: + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the vector store should use. Useful for tools like `file_search` that can access + files. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + f"/vector_stores/{vector_store_id}/file_batches", + body=await async_maybe_transform({"file_ids": file_ids}, file_batch_create_params.FileBatchCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreFileBatch, + ) + + async def retrieve( + self, + batch_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """ + Retrieves a vector store file batch. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + f"/vector_stores/{vector_store_id}/file_batches/{batch_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreFileBatch, + ) + + async def cancel( + self, + batch_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """Cancel a vector store file batch. + + This attempts to cancel the processing of + files in this batch as soon as possible. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + f"/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreFileBatch, + ) + + async def create_and_poll( + self, + vector_store_id: str, + *, + file_ids: List[str], + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """Create a vector store batch and poll until all files have been processed.""" + batch = await self.create( + vector_store_id=vector_store_id, + file_ids=file_ids, + ) + # TODO: don't poll unless necessary?? + return await self.poll( + batch.id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + ) + + def list_files( + self, + batch_id: str, + *, + vector_store_id: str, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[VectorStoreFile, AsyncCursorPage[VectorStoreFile]]: + """ + Returns a list of vector store files in a batch. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not batch_id: + raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + f"/vector_stores/{vector_store_id}/file_batches/{batch_id}/files", + page=AsyncCursorPage[VectorStoreFile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "filter": filter, + "limit": limit, + "order": order, + }, + file_batch_list_files_params.FileBatchListFilesParams, + ), + ), + model=VectorStoreFile, + ) + + async def poll( + self, + batch_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """Wait for the given file batch to be processed. + + Note: this will return even if one of the files failed to process, you need to + check batch.file_counts.failed_count to handle this case. + """ + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = await self.with_raw_response.retrieve( + batch_id, + vector_store_id=vector_store_id, + extra_headers=headers, + ) + + batch = response.parse() + if batch.file_counts.in_progress > 0: + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + await self._sleep(poll_interval_ms / 1000) + continue + + return batch + + async def upload_and_poll( + self, + vector_store_id: str, + *, + files: Iterable[FileTypes], + max_concurrency: int = 5, + file_ids: List[str] = [], + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFileBatch: + """Uploads the given files concurrently and then creates a vector store file batch. + + If you've already uploaded certain files that you want to include in this batch + then you can pass their IDs through the `file_ids` argument. + + By default, if any file upload fails then an exception will be eagerly raised. + + The number of concurrency uploads is configurable using the `max_concurrency` + parameter. + + Note: this method only supports `asyncio` or `trio` as the backing async + runtime. + """ + uploaded_files: list[FileObject] = [] + + async_library = sniffio.current_async_library() + + if async_library == "asyncio": + + async def asyncio_upload_file(semaphore: asyncio.Semaphore, file: FileTypes) -> None: + async with semaphore: + file_obj = await self._client.files.create( + file=file, + purpose="assistants", + ) + uploaded_files.append(file_obj) + + semaphore = asyncio.Semaphore(max_concurrency) + + tasks = [asyncio_upload_file(semaphore, file) for file in files] + + await asyncio.gather(*tasks) + elif async_library == "trio": + # We only import if the library is being used. + # We support Python 3.7 so are using an older version of trio that does not have type information + import trio # type: ignore # pyright: ignore[reportMissingTypeStubs] + + async def trio_upload_file(limiter: trio.CapacityLimiter, file: FileTypes) -> None: + async with limiter: + file_obj = await self._client.files.create( + file=file, + purpose="assistants", + ) + uploaded_files.append(file_obj) + + limiter = trio.CapacityLimiter(max_concurrency) + + async with trio.open_nursery() as nursery: + for file in files: + nursery.start_soon(trio_upload_file, limiter, file) # pyright: ignore [reportUnknownMemberType] + else: + raise RuntimeError( + f"Async runtime {async_library} is not supported yet. Only asyncio or trio is supported", + ) + + batch = await self.create_and_poll( + vector_store_id=vector_store_id, + file_ids=[*file_ids, *(f.id for f in uploaded_files)], + poll_interval_ms=poll_interval_ms, + ) + return batch + + +class FileBatchesWithRawResponse: + def __init__(self, file_batches: FileBatches) -> None: + self._file_batches = file_batches + + self.create = _legacy_response.to_raw_response_wrapper( + file_batches.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + file_batches.retrieve, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + file_batches.cancel, + ) + self.list_files = _legacy_response.to_raw_response_wrapper( + file_batches.list_files, + ) + + +class AsyncFileBatchesWithRawResponse: + def __init__(self, file_batches: AsyncFileBatches) -> None: + self._file_batches = file_batches + + self.create = _legacy_response.async_to_raw_response_wrapper( + file_batches.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + file_batches.retrieve, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + file_batches.cancel, + ) + self.list_files = _legacy_response.async_to_raw_response_wrapper( + file_batches.list_files, + ) + + +class FileBatchesWithStreamingResponse: + def __init__(self, file_batches: FileBatches) -> None: + self._file_batches = file_batches + + self.create = to_streamed_response_wrapper( + file_batches.create, + ) + self.retrieve = to_streamed_response_wrapper( + file_batches.retrieve, + ) + self.cancel = to_streamed_response_wrapper( + file_batches.cancel, + ) + self.list_files = to_streamed_response_wrapper( + file_batches.list_files, + ) + + +class AsyncFileBatchesWithStreamingResponse: + def __init__(self, file_batches: AsyncFileBatches) -> None: + self._file_batches = file_batches + + self.create = async_to_streamed_response_wrapper( + file_batches.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + file_batches.retrieve, + ) + self.cancel = async_to_streamed_response_wrapper( + file_batches.cancel, + ) + self.list_files = async_to_streamed_response_wrapper( + file_batches.list_files, + ) diff --git a/src/openai/resources/beta/assistants/files.py b/src/openai/resources/beta/vector_stores/files.py similarity index 57% rename from src/openai/resources/beta/assistants/files.py rename to src/openai/resources/beta/vector_stores/files.py index dc57dfb96c..6404b9d54c 100644 --- a/src/openai/resources/beta/assistants/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -2,13 +2,15 @@ from __future__ import annotations -from typing_extensions import Literal +from typing import TYPE_CHECKING +from typing_extensions import Literal, assert_never import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ...._utils import ( + is_given, maybe_transform, async_maybe_transform, ) @@ -20,7 +22,7 @@ AsyncPaginator, make_request_options, ) -from ....types.beta.assistants import AssistantFile, FileDeleteResponse, file_list_params, file_create_params +from ....types.beta.vector_stores import VectorStoreFile, VectorStoreFileDeleted, file_list_params, file_create_params __all__ = ["Files", "AsyncFiles"] @@ -36,7 +38,7 @@ def with_streaming_response(self) -> FilesWithStreamingResponse: def create( self, - assistant_id: str, + vector_store_id: str, *, file_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -45,16 +47,16 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AssistantFile: + ) -> VectorStoreFile: """ - Create an assistant file by attaching a - [File](https://platform.openai.com/docs/api-reference/files) to an - [assistant](https://platform.openai.com/docs/api-reference/assistants). + Create a vector store file by attaching a + [File](https://platform.openai.com/docs/api-reference/files) to a + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object). Args: - file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID (with - `purpose="assistants"`) that the assistant should use. Useful for tools like - `retrieval` and `code_interpreter` that can access files. + file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID that the + vector store should use. Useful for tools like `file_search` that can access + files. extra_headers: Send extra headers @@ -64,32 +66,32 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ - if not assistant_id: - raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/assistants/{assistant_id}/files", + f"/vector_stores/{vector_store_id}/files", body=maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AssistantFile, + cast_to=VectorStoreFile, ) def retrieve( self, file_id: str, *, - assistant_id: str, + vector_store_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AssistantFile: + ) -> VectorStoreFile: """ - Retrieves an AssistantFile. + Retrieves a vector store file. Args: extra_headers: Send extra headers @@ -100,25 +102,26 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ - if not assistant_id: - raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/assistants/{assistant_id}/files/{file_id}", + f"/vector_stores/{vector_store_id}/files/{file_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AssistantFile, + cast_to=VectorStoreFile, ) def list( self, - assistant_id: str, + vector_store_id: str, *, after: str | NotGiven = NOT_GIVEN, before: str | NotGiven = NOT_GIVEN, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -127,9 +130,9 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncCursorPage[AssistantFile]: + ) -> SyncCursorPage[VectorStoreFile]: """ - Returns a list of assistant files. + Returns a list of vector store files. Args: after: A cursor for use in pagination. `after` is an object ID that defines your place @@ -142,6 +145,8 @@ def list( ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. + filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -156,12 +161,12 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ - if not assistant_id: - raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/assistants/{assistant_id}/files", - page=SyncCursorPage[AssistantFile], + f"/vector_stores/{vector_store_id}/files", + page=SyncCursorPage[VectorStoreFile], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -171,29 +176,34 @@ def list( { "after": after, "before": before, + "filter": filter, "limit": limit, "order": order, }, file_list_params.FileListParams, ), ), - model=AssistantFile, + model=VectorStoreFile, ) def delete( self, file_id: str, *, - assistant_id: str, + vector_store_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FileDeleteResponse: - """ - Delete an assistant file. + ) -> VectorStoreFileDeleted: + """Delete a vector store file. + + This will remove the file from the vector store but + the file itself will not be deleted. To delete the file, use the + [delete file](https://platform.openai.com/docs/api-reference/files/delete) + endpoint. Args: extra_headers: Send extra headers @@ -204,17 +214,103 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ - if not assistant_id: - raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._delete( - f"/assistants/{assistant_id}/files/{file_id}", + f"/vector_stores/{vector_store_id}/files/{file_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=FileDeleteResponse, + cast_to=VectorStoreFileDeleted, + ) + + def create_and_poll( + self, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFile: + """Attach a file to the given vector store and wait for it to be processed.""" + self.create(vector_store_id=vector_store_id, file_id=file_id) + + return self.poll( + file_id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + ) + + def poll( + self, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFile: + """Wait for the vector store file to finish processing. + + Note: this will return even if the file failed to process, you need to check + file.last_error and file.status to handle these cases + """ + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = self.with_raw_response.retrieve( + file_id, + vector_store_id=vector_store_id, + extra_headers=headers, + ) + + file = response.parse() + if file.status == "in_progress": + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + self._sleep(poll_interval_ms / 1000) + elif file.status == "cancelled" or file.status == "completed" or file.status == "failed": + return file + else: + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(file.status) + else: + return file + + def upload( + self, + *, + vector_store_id: str, + file: FileTypes, + ) -> VectorStoreFile: + """Upload a file to the `files` API and then attach it to the given vector store. + + Note the file will be asynchronously processed (you can use the alternative + polling helper method to wait for processing to complete). + """ + file_obj = self._client.files.create(file=file, purpose="assistants") + return self.create(vector_store_id=vector_store_id, file_id=file_obj.id) + + def upload_and_poll( + self, + *, + vector_store_id: str, + file: FileTypes, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFile: + """Add a file to a vector store and poll until processing is complete.""" + file_obj = self._client.files.create(file=file, purpose="assistants") + return self.create_and_poll( + vector_store_id=vector_store_id, + file_id=file_obj.id, + poll_interval_ms=poll_interval_ms, ) @@ -229,7 +325,7 @@ def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: async def create( self, - assistant_id: str, + vector_store_id: str, *, file_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -238,16 +334,16 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AssistantFile: + ) -> VectorStoreFile: """ - Create an assistant file by attaching a - [File](https://platform.openai.com/docs/api-reference/files) to an - [assistant](https://platform.openai.com/docs/api-reference/assistants). + Create a vector store file by attaching a + [File](https://platform.openai.com/docs/api-reference/files) to a + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object). Args: - file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID (with - `purpose="assistants"`) that the assistant should use. Useful for tools like - `retrieval` and `code_interpreter` that can access files. + file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID that the + vector store should use. Useful for tools like `file_search` that can access + files. extra_headers: Send extra headers @@ -257,32 +353,32 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - if not assistant_id: - raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/assistants/{assistant_id}/files", + f"/vector_stores/{vector_store_id}/files", body=await async_maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AssistantFile, + cast_to=VectorStoreFile, ) async def retrieve( self, file_id: str, *, - assistant_id: str, + vector_store_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AssistantFile: + ) -> VectorStoreFile: """ - Retrieves an AssistantFile. + Retrieves a vector store file. Args: extra_headers: Send extra headers @@ -293,25 +389,26 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ - if not assistant_id: - raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/assistants/{assistant_id}/files/{file_id}", + f"/vector_stores/{vector_store_id}/files/{file_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AssistantFile, + cast_to=VectorStoreFile, ) def list( self, - assistant_id: str, + vector_store_id: str, *, after: str | NotGiven = NOT_GIVEN, before: str | NotGiven = NOT_GIVEN, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -320,9 +417,9 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[AssistantFile, AsyncCursorPage[AssistantFile]]: + ) -> AsyncPaginator[VectorStoreFile, AsyncCursorPage[VectorStoreFile]]: """ - Returns a list of assistant files. + Returns a list of vector store files. Args: after: A cursor for use in pagination. `after` is an object ID that defines your place @@ -335,6 +432,8 @@ def list( ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. + filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. + limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -349,12 +448,12 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ - if not assistant_id: - raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/assistants/{assistant_id}/files", - page=AsyncCursorPage[AssistantFile], + f"/vector_stores/{vector_store_id}/files", + page=AsyncCursorPage[VectorStoreFile], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -364,29 +463,34 @@ def list( { "after": after, "before": before, + "filter": filter, "limit": limit, "order": order, }, file_list_params.FileListParams, ), ), - model=AssistantFile, + model=VectorStoreFile, ) async def delete( self, file_id: str, *, - assistant_id: str, + vector_store_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> FileDeleteResponse: - """ - Delete an assistant file. + ) -> VectorStoreFileDeleted: + """Delete a vector store file. + + This will remove the file from the vector store but + the file itself will not be deleted. To delete the file, use the + [delete file](https://platform.openai.com/docs/api-reference/files/delete) + endpoint. Args: extra_headers: Send extra headers @@ -397,17 +501,103 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ - if not assistant_id: - raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})} + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._delete( - f"/assistants/{assistant_id}/files/{file_id}", + f"/vector_stores/{vector_store_id}/files/{file_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=FileDeleteResponse, + cast_to=VectorStoreFileDeleted, + ) + + async def create_and_poll( + self, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFile: + """Attach a file to the given vector store and wait for it to be processed.""" + await self.create(vector_store_id=vector_store_id, file_id=file_id) + + return await self.poll( + file_id, + vector_store_id=vector_store_id, + poll_interval_ms=poll_interval_ms, + ) + + async def poll( + self, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFile: + """Wait for the vector store file to finish processing. + + Note: this will return even if the file failed to process, you need to check + file.last_error and file.status to handle these cases + """ + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = await self.with_raw_response.retrieve( + file_id, + vector_store_id=vector_store_id, + extra_headers=headers, + ) + + file = response.parse() + if file.status == "in_progress": + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + await self._sleep(poll_interval_ms / 1000) + elif file.status == "cancelled" or file.status == "completed" or file.status == "failed": + return file + else: + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(file.status) + else: + return file + + async def upload( + self, + *, + vector_store_id: str, + file: FileTypes, + ) -> VectorStoreFile: + """Upload a file to the `files` API and then attach it to the given vector store. + + Note the file will be asynchronously processed (you can use the alternative + polling helper method to wait for processing to complete). + """ + file_obj = await self._client.files.create(file=file, purpose="assistants") + return await self.create(vector_store_id=vector_store_id, file_id=file_obj.id) + + async def upload_and_poll( + self, + *, + vector_store_id: str, + file: FileTypes, + poll_interval_ms: int | NotGiven = NOT_GIVEN, + ) -> VectorStoreFile: + """Add a file to a vector store and poll until processing is complete.""" + file_obj = await self._client.files.create(file=file, purpose="assistants") + return await self.create_and_poll( + vector_store_id=vector_store_id, + file_id=file_obj.id, + poll_interval_ms=poll_interval_ms, ) diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py new file mode 100644 index 0000000000..6e2c9ab70c --- /dev/null +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -0,0 +1,688 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from .files import ( + Files, + AsyncFiles, + FilesWithRawResponse, + AsyncFilesWithRawResponse, + FilesWithStreamingResponse, + AsyncFilesWithStreamingResponse, +) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .file_batches import ( + FileBatches, + AsyncFileBatches, + FileBatchesWithRawResponse, + AsyncFileBatchesWithRawResponse, + FileBatchesWithStreamingResponse, + AsyncFileBatchesWithStreamingResponse, +) +from ....pagination import SyncCursorPage, AsyncCursorPage +from ....types.beta import ( + VectorStore, + VectorStoreDeleted, + vector_store_list_params, + vector_store_create_params, + vector_store_update_params, +) +from ...._base_client import ( + AsyncPaginator, + make_request_options, +) + +__all__ = ["VectorStores", "AsyncVectorStores"] + + +class VectorStores(SyncAPIResource): + @cached_property + def files(self) -> Files: + return Files(self._client) + + @cached_property + def file_batches(self) -> FileBatches: + return FileBatches(self._client) + + @cached_property + def with_raw_response(self) -> VectorStoresWithRawResponse: + return VectorStoresWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VectorStoresWithStreamingResponse: + return VectorStoresWithStreamingResponse(self) + + def create( + self, + *, + expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, + file_ids: List[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStore: + """ + Create a vector store. + + Args: + expires_after: The expiration policy for a vector store. + + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the vector store should use. Useful for tools like `file_search` that can access + files. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + name: The name of the vector store. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/vector_stores", + body=maybe_transform( + { + "expires_after": expires_after, + "file_ids": file_ids, + "metadata": metadata, + "name": name, + }, + vector_store_create_params.VectorStoreCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStore, + ) + + def retrieve( + self, + vector_store_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStore: + """ + Retrieves a vector store. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get( + f"/vector_stores/{vector_store_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStore, + ) + + def update( + self, + vector_store_id: str, + *, + expires_after: Optional[vector_store_update_params.ExpiresAfter] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStore: + """ + Modifies a vector store. + + Args: + expires_after: The expiration policy for a vector store. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + name: The name of the vector store. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + f"/vector_stores/{vector_store_id}", + body=maybe_transform( + { + "expires_after": expires_after, + "metadata": metadata, + "name": name, + }, + vector_store_update_params.VectorStoreUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStore, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[VectorStore]: + """Returns a list of vector stores. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + "/vector_stores", + page=SyncCursorPage[VectorStore], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + vector_store_list_params.VectorStoreListParams, + ), + ), + model=VectorStore, + ) + + def delete( + self, + vector_store_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreDeleted: + """ + Delete a vector store. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._delete( + f"/vector_stores/{vector_store_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreDeleted, + ) + + +class AsyncVectorStores(AsyncAPIResource): + @cached_property + def files(self) -> AsyncFiles: + return AsyncFiles(self._client) + + @cached_property + def file_batches(self) -> AsyncFileBatches: + return AsyncFileBatches(self._client) + + @cached_property + def with_raw_response(self) -> AsyncVectorStoresWithRawResponse: + return AsyncVectorStoresWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVectorStoresWithStreamingResponse: + return AsyncVectorStoresWithStreamingResponse(self) + + async def create( + self, + *, + expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, + file_ids: List[str] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStore: + """ + Create a vector store. + + Args: + expires_after: The expiration policy for a vector store. + + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the vector store should use. Useful for tools like `file_search` that can access + files. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + name: The name of the vector store. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/vector_stores", + body=await async_maybe_transform( + { + "expires_after": expires_after, + "file_ids": file_ids, + "metadata": metadata, + "name": name, + }, + vector_store_create_params.VectorStoreCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStore, + ) + + async def retrieve( + self, + vector_store_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStore: + """ + Retrieves a vector store. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._get( + f"/vector_stores/{vector_store_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStore, + ) + + async def update( + self, + vector_store_id: str, + *, + expires_after: Optional[vector_store_update_params.ExpiresAfter] | NotGiven = NOT_GIVEN, + metadata: Optional[object] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStore: + """ + Modifies a vector store. + + Args: + expires_after: The expiration policy for a vector store. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format. Keys + can be a maximum of 64 characters long and values can be a maxium of 512 + characters long. + + name: The name of the vector store. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + f"/vector_stores/{vector_store_id}", + body=await async_maybe_transform( + { + "expires_after": expires_after, + "metadata": metadata, + "name": name, + }, + vector_store_update_params.VectorStoreUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStore, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[VectorStore, AsyncCursorPage[VectorStore]]: + """Returns a list of vector stores. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include before=obj_foo in order to + fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + "/vector_stores", + page=AsyncCursorPage[VectorStore], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + vector_store_list_params.VectorStoreListParams, + ), + ), + model=VectorStore, + ) + + async def delete( + self, + vector_store_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreDeleted: + """ + Delete a vector store. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._delete( + f"/vector_stores/{vector_store_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreDeleted, + ) + + +class VectorStoresWithRawResponse: + def __init__(self, vector_stores: VectorStores) -> None: + self._vector_stores = vector_stores + + self.create = _legacy_response.to_raw_response_wrapper( + vector_stores.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + vector_stores.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + vector_stores.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + vector_stores.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + vector_stores.delete, + ) + + @cached_property + def files(self) -> FilesWithRawResponse: + return FilesWithRawResponse(self._vector_stores.files) + + @cached_property + def file_batches(self) -> FileBatchesWithRawResponse: + return FileBatchesWithRawResponse(self._vector_stores.file_batches) + + +class AsyncVectorStoresWithRawResponse: + def __init__(self, vector_stores: AsyncVectorStores) -> None: + self._vector_stores = vector_stores + + self.create = _legacy_response.async_to_raw_response_wrapper( + vector_stores.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + vector_stores.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + vector_stores.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + vector_stores.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + vector_stores.delete, + ) + + @cached_property + def files(self) -> AsyncFilesWithRawResponse: + return AsyncFilesWithRawResponse(self._vector_stores.files) + + @cached_property + def file_batches(self) -> AsyncFileBatchesWithRawResponse: + return AsyncFileBatchesWithRawResponse(self._vector_stores.file_batches) + + +class VectorStoresWithStreamingResponse: + def __init__(self, vector_stores: VectorStores) -> None: + self._vector_stores = vector_stores + + self.create = to_streamed_response_wrapper( + vector_stores.create, + ) + self.retrieve = to_streamed_response_wrapper( + vector_stores.retrieve, + ) + self.update = to_streamed_response_wrapper( + vector_stores.update, + ) + self.list = to_streamed_response_wrapper( + vector_stores.list, + ) + self.delete = to_streamed_response_wrapper( + vector_stores.delete, + ) + + @cached_property + def files(self) -> FilesWithStreamingResponse: + return FilesWithStreamingResponse(self._vector_stores.files) + + @cached_property + def file_batches(self) -> FileBatchesWithStreamingResponse: + return FileBatchesWithStreamingResponse(self._vector_stores.file_batches) + + +class AsyncVectorStoresWithStreamingResponse: + def __init__(self, vector_stores: AsyncVectorStores) -> None: + self._vector_stores = vector_stores + + self.create = async_to_streamed_response_wrapper( + vector_stores.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + vector_stores.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + vector_stores.update, + ) + self.list = async_to_streamed_response_wrapper( + vector_stores.list, + ) + self.delete = async_to_streamed_response_wrapper( + vector_stores.delete, + ) + + @cached_property + def files(self) -> AsyncFilesWithStreamingResponse: + return AsyncFilesWithStreamingResponse(self._vector_stores.files) + + @cached_property + def file_batches(self) -> AsyncFileBatchesWithStreamingResponse: + return AsyncFileBatchesWithStreamingResponse(self._vector_stores.file_batches) diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index 229f716c48..8e49571b14 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -85,7 +85,7 @@ def create( training_file: The ID of an uploaded file that contains training data. - See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + See [upload file](https://platform.openai.com/docs/api-reference/files/create) for how to upload a file. Your dataset must be formatted as a JSONL file. Additionally, you must upload @@ -360,7 +360,7 @@ async def create( training_file: The ID of an uploaded file that contains training data. - See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + See [upload file](https://platform.openai.com/docs/api-reference/files/create) for how to upload a file. Your dataset must be formatted as a JSONL file. Additionally, you must upload diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index 0171694587..d851a3619c 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -4,23 +4,28 @@ from .thread import Thread as Thread from .assistant import Assistant as Assistant +from .vector_store import VectorStore as VectorStore from .function_tool import FunctionTool as FunctionTool from .assistant_tool import AssistantTool as AssistantTool -from .retrieval_tool import RetrievalTool as RetrievalTool from .thread_deleted import ThreadDeleted as ThreadDeleted +from .file_search_tool import FileSearchTool as FileSearchTool from .assistant_deleted import AssistantDeleted as AssistantDeleted from .function_tool_param import FunctionToolParam as FunctionToolParam from .assistant_tool_param import AssistantToolParam as AssistantToolParam -from .retrieval_tool_param import RetrievalToolParam as RetrievalToolParam from .thread_create_params import ThreadCreateParams as ThreadCreateParams from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams +from .vector_store_deleted import VectorStoreDeleted as VectorStoreDeleted from .assistant_list_params import AssistantListParams as AssistantListParams from .assistant_tool_choice import AssistantToolChoice as AssistantToolChoice from .code_interpreter_tool import CodeInterpreterTool as CodeInterpreterTool from .assistant_stream_event import AssistantStreamEvent as AssistantStreamEvent +from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams +from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams from .assistant_response_format import AssistantResponseFormat as AssistantResponseFormat +from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams +from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 0a0d28ed01..fa09efb0cc 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -6,7 +6,32 @@ from ..._models import BaseModel from .assistant_tool import AssistantTool -__all__ = ["Assistant"] +__all__ = ["Assistant", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] + + +class ToolResourcesCodeInterpreter(BaseModel): + file_ids: Optional[List[str]] = None + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made + available to the `code_interpreter`` tool. There can be a maximum of 20 files + associated with the tool. + """ + + +class ToolResourcesFileSearch(BaseModel): + vector_store_ids: Optional[List[str]] = None + """ + The ID of the + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + attached to this assistant. There can be a maximum of 1 vector store attached to + the assistant. + """ + + +class ToolResources(BaseModel): + code_interpreter: Optional[ToolResourcesCodeInterpreter] = None + + file_search: Optional[ToolResourcesFileSearch] = None class Assistant(BaseModel): @@ -19,13 +44,6 @@ class Assistant(BaseModel): description: Optional[str] = None """The description of the assistant. The maximum length is 512 characters.""" - file_ids: List[str] - """ - A list of [file](https://platform.openai.com/docs/api-reference/files) IDs - attached to this assistant. There can be a maximum of 20 files attached to the - assistant. Files are ordered by their creation date in ascending order. - """ - instructions: Optional[str] = None """The system instructions that the assistant uses. @@ -60,5 +78,13 @@ class Assistant(BaseModel): """A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types - `code_interpreter`, `retrieval`, or `function`. + `code_interpreter`, `file_search`, or `function`. + """ + + tool_resources: Optional[ToolResources] = None + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. """ diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 011121485f..925b85050f 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -6,8 +6,15 @@ from typing_extensions import Literal, Required, TypedDict from .assistant_tool_param import AssistantToolParam +from .assistant_response_format_option_param import AssistantResponseFormatOptionParam -__all__ = ["AssistantCreateParams"] +__all__ = [ + "AssistantCreateParams", + "ToolResources", + "ToolResourcesCodeInterpreter", + "ToolResourcesFileSearch", + "ToolResourcesFileSearchVectorStore", +] class AssistantCreateParams(TypedDict, total=False): @@ -48,13 +55,6 @@ class AssistantCreateParams(TypedDict, total=False): description: Optional[str] """The description of the assistant. The maximum length is 512 characters.""" - file_ids: List[str] - """ - A list of [file](https://platform.openai.com/docs/api-reference/files) IDs - attached to this assistant. There can be a maximum of 20 files attached to the - assistant. Files are ordered by their creation date in ascending order. - """ - instructions: Optional[str] """The system instructions that the assistant uses. @@ -72,9 +72,102 @@ class AssistantCreateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" + response_format: Optional[AssistantResponseFormatOptionParam] + """Specifies the format that the model must output. + + Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + + tool_resources: Optional[ToolResources] + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + tools: Iterable[AssistantToolParam] """A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types - `code_interpreter`, `retrieval`, or `function`. + `code_interpreter`, `file_search`, or `function`. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or temperature but not both. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made + available to the `code_interpreter` tool. There can be a maximum of 20 files + associated with the tool. + """ + + +class ToolResourcesFileSearchVectorStore(TypedDict, total=False): + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to + add to the vector store. There can be a maximum of 10000 files in a vector + store. + """ + + metadata: object + """Set of 16 key-value pairs that can be attached to a vector store. + + This can be useful for storing additional information about the vector store in + a structured format. Keys can be a maximum of 64 characters long and values can + be a maxium of 512 characters long. """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: List[str] + """ + The + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + attached to this assistant. There can be a maximum of 1 vector store attached to + the assistant. + """ + + vector_stores: Iterable[ToolResourcesFileSearchVectorStore] + """ + A helper to create a + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + with file_ids and attach it to this assistant. There can be a maximum of 1 + vector store attached to the assistant. + """ + + +class ToolResources(TypedDict, total=False): + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch diff --git a/src/openai/types/beta/assistant_tool.py b/src/openai/types/beta/assistant_tool.py index a4420385e8..7832da48cc 100644 --- a/src/openai/types/beta/assistant_tool.py +++ b/src/openai/types/beta/assistant_tool.py @@ -5,9 +5,9 @@ from ..._utils import PropertyInfo from .function_tool import FunctionTool -from .retrieval_tool import RetrievalTool +from .file_search_tool import FileSearchTool from .code_interpreter_tool import CodeInterpreterTool __all__ = ["AssistantTool"] -AssistantTool = Annotated[Union[CodeInterpreterTool, RetrievalTool, FunctionTool], PropertyInfo(discriminator="type")] +AssistantTool = Annotated[Union[CodeInterpreterTool, FileSearchTool, FunctionTool], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/beta/assistant_tool_choice.py b/src/openai/types/beta/assistant_tool_choice.py index 4314d4b41e..d73439f006 100644 --- a/src/openai/types/beta/assistant_tool_choice.py +++ b/src/openai/types/beta/assistant_tool_choice.py @@ -10,7 +10,7 @@ class AssistantToolChoice(BaseModel): - type: Literal["function", "code_interpreter", "retrieval"] + type: Literal["function", "code_interpreter", "file_search"] """The type of the tool. If type is `function`, the function name must be set""" function: Optional[AssistantToolChoiceFunction] = None diff --git a/src/openai/types/beta/assistant_tool_choice_param.py b/src/openai/types/beta/assistant_tool_choice_param.py index 5cf6ea27be..904f489e26 100644 --- a/src/openai/types/beta/assistant_tool_choice_param.py +++ b/src/openai/types/beta/assistant_tool_choice_param.py @@ -10,7 +10,7 @@ class AssistantToolChoiceParam(TypedDict, total=False): - type: Required[Literal["function", "code_interpreter", "retrieval"]] + type: Required[Literal["function", "code_interpreter", "file_search"]] """The type of the tool. If type is `function`, the function name must be set""" function: AssistantToolChoiceFunctionParam diff --git a/src/openai/types/beta/assistant_tool_param.py b/src/openai/types/beta/assistant_tool_param.py index d5758f169e..5b1d30ba2f 100644 --- a/src/openai/types/beta/assistant_tool_param.py +++ b/src/openai/types/beta/assistant_tool_param.py @@ -5,9 +5,9 @@ from typing import Union from .function_tool_param import FunctionToolParam -from .retrieval_tool_param import RetrievalToolParam +from .file_search_tool_param import FileSearchToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam __all__ = ["AssistantToolParam"] -AssistantToolParam = Union[CodeInterpreterToolParam, RetrievalToolParam, FunctionToolParam] +AssistantToolParam = Union[CodeInterpreterToolParam, FileSearchToolParam, FunctionToolParam] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 6e9d9ed5db..1354b078a8 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -6,23 +6,15 @@ from typing_extensions import TypedDict from .assistant_tool_param import AssistantToolParam +from .assistant_response_format_option_param import AssistantResponseFormatOptionParam -__all__ = ["AssistantUpdateParams"] +__all__ = ["AssistantUpdateParams", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] class AssistantUpdateParams(TypedDict, total=False): description: Optional[str] """The description of the assistant. The maximum length is 512 characters.""" - file_ids: List[str] - """ - A list of [File](https://platform.openai.com/docs/api-reference/files) IDs - attached to this assistant. There can be a maximum of 20 files attached to the - assistant. Files are ordered by their creation date in ascending order. If a - file was previously attached to the list but does not show up in the list, it - will be deleted from the assistant. - """ - instructions: Optional[str] """The system instructions that the assistant uses. @@ -50,9 +42,78 @@ class AssistantUpdateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" + response_format: Optional[AssistantResponseFormatOptionParam] + """Specifies the format that the model must output. + + Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + + tool_resources: Optional[ToolResources] + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + tools: Iterable[AssistantToolParam] """A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types - `code_interpreter`, `retrieval`, or `function`. + `code_interpreter`, `file_search`, or `function`. """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or temperature but not both. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: List[str] + """ + Overrides the list of + [file](https://platform.openai.com/docs/api-reference/files) IDs made available + to the `code_interpreter` tool. There can be a maximum of 20 files associated + with the tool. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: List[str] + """ + Overrides the + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + attached to this assistant. There can be a maximum of 1 vector store attached to + the assistant. + """ + + +class ToolResources(TypedDict, total=False): + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch diff --git a/src/openai/types/beta/assistants/__init__.py b/src/openai/types/beta/assistants/__init__.py deleted file mode 100644 index d4dd2de018..0000000000 --- a/src/openai/types/beta/assistants/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .assistant_file import AssistantFile as AssistantFile -from .file_list_params import FileListParams as FileListParams -from .file_create_params import FileCreateParams as FileCreateParams -from .file_delete_response import FileDeleteResponse as FileDeleteResponse diff --git a/src/openai/types/beta/assistants/assistant_file.py b/src/openai/types/beta/assistants/assistant_file.py deleted file mode 100644 index 25aec07b49..0000000000 --- a/src/openai/types/beta/assistants/assistant_file.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["AssistantFile"] - - -class AssistantFile(BaseModel): - id: str - """The identifier, which can be referenced in API endpoints.""" - - assistant_id: str - """The assistant ID that the file is attached to.""" - - created_at: int - """The Unix timestamp (in seconds) for when the assistant file was created.""" - - object: Literal["assistant.file"] - """The object type, which is always `assistant.file`.""" diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py new file mode 100644 index 0000000000..eea55ea6ac --- /dev/null +++ b/src/openai/types/beta/file_search_tool.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FileSearchTool"] + + +class FileSearchTool(BaseModel): + type: Literal["file_search"] + """The type of tool being defined: `file_search`""" diff --git a/src/openai/types/beta/retrieval_tool_param.py b/src/openai/types/beta/file_search_tool_param.py similarity index 50% rename from src/openai/types/beta/retrieval_tool_param.py rename to src/openai/types/beta/file_search_tool_param.py index d76c0beefc..d33fd06da4 100644 --- a/src/openai/types/beta/retrieval_tool_param.py +++ b/src/openai/types/beta/file_search_tool_param.py @@ -4,9 +4,9 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["RetrievalToolParam"] +__all__ = ["FileSearchToolParam"] -class RetrievalToolParam(TypedDict, total=False): - type: Required[Literal["retrieval"]] - """The type of tool being defined: `retrieval`""" +class FileSearchToolParam(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py index 8fd1423068..6f7a6c7d0c 100644 --- a/src/openai/types/beta/thread.py +++ b/src/openai/types/beta/thread.py @@ -1,11 +1,36 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["Thread"] +__all__ = ["Thread", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] + + +class ToolResourcesCodeInterpreter(BaseModel): + file_ids: Optional[List[str]] = None + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made + available to the `code_interpreter` tool. There can be a maximum of 20 files + associated with the tool. + """ + + +class ToolResourcesFileSearch(BaseModel): + vector_store_ids: Optional[List[str]] = None + """ + The + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + attached to this thread. There can be a maximum of 1 vector store attached to + the thread. + """ + + +class ToolResources(BaseModel): + code_interpreter: Optional[ToolResourcesCodeInterpreter] = None + + file_search: Optional[ToolResourcesFileSearch] = None class Thread(BaseModel): @@ -25,3 +50,11 @@ class Thread(BaseModel): object: Literal["thread"] """The object type, which is always `thread`.""" + + tool_resources: Optional[ToolResources] = None + """ + A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 50f947a40a..d7d5a758e8 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -6,7 +6,7 @@ from typing_extensions import Literal, Required, TypedDict from .function_tool_param import FunctionToolParam -from .retrieval_tool_param import RetrievalToolParam +from .file_search_tool_param import FileSearchToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam from .assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -15,6 +15,14 @@ "ThreadCreateAndRunParamsBase", "Thread", "ThreadMessage", + "ThreadMessageAttachment", + "ThreadToolResources", + "ThreadToolResourcesCodeInterpreter", + "ThreadToolResourcesFileSearch", + "ThreadToolResourcesFileSearchVectorStore", + "ToolResources", + "ToolResourcesCodeInterpreter", + "ToolResourcesFileSearch", "Tool", "TruncationStrategy", "ThreadCreateAndRunParamsNonStreaming", @@ -41,7 +49,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. """ @@ -50,7 +58,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. """ @@ -132,15 +140,37 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): call that tool. """ + tool_resources: Optional[ToolResources] + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + tools: Optional[Iterable[Tool]] """Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. """ + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + """ + truncation_strategy: Optional[TruncationStrategy] +class ThreadMessageAttachment(TypedDict, total=False): + add_to: List[Literal["file_search", "code_interpreter"]] + + file_id: str + """The ID of the file to attach to the message.""" + + class ThreadMessage(TypedDict, total=False): content: Required[str] """The content of the message.""" @@ -154,13 +184,8 @@ class ThreadMessage(TypedDict, total=False): value to insert messages from the assistant into the conversation. """ - file_ids: List[str] - """ - A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that - the message should use. There can be a maximum of 10 files attached to a - message. Useful for tools like `retrieval` and `code_interpreter` that can - access and use files. - """ + attachments: Optional[Iterable[ThreadMessageAttachment]] + """A list of files attached to the message, and the tools they should be added to.""" metadata: Optional[object] """Set of 16 key-value pairs that can be attached to an object. @@ -171,6 +196,56 @@ class ThreadMessage(TypedDict, total=False): """ +class ThreadToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made + available to the `code_interpreter` tool. There can be a maximum of 20 files + associated with the tool. + """ + + +class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to + add to the vector store. There can be a maximum of 10000 files in a vector + store. + """ + + metadata: object + """Set of 16 key-value pairs that can be attached to a vector store. + + This can be useful for storing additional information about the vector store in + a structured format. Keys can be a maximum of 64 characters long and values can + be a maxium of 512 characters long. + """ + + +class ThreadToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: List[str] + """ + The + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + attached to this thread. There can be a maximum of 1 vector store attached to + the thread. + """ + + vector_stores: Iterable[ThreadToolResourcesFileSearchVectorStore] + """ + A helper to create a + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + with file_ids and attach it to this thread. There can be a maximum of 1 vector + store attached to the thread. + """ + + +class ThreadToolResources(TypedDict, total=False): + code_interpreter: ThreadToolResourcesCodeInterpreter + + file_search: ThreadToolResourcesFileSearch + + class Thread(TypedDict, total=False): messages: Iterable[ThreadMessage] """ @@ -186,8 +261,41 @@ class Thread(TypedDict, total=False): a maxium of 512 characters long. """ + tool_resources: Optional[ThreadToolResources] + """ + A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made + available to the `code_interpreter` tool. There can be a maximum of 20 files + associated with the tool. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: List[str] + """ + The ID of the + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + attached to this assistant. There can be a maximum of 1 vector store attached to + the assistant. + """ + + +class ToolResources(TypedDict, total=False): + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch + -Tool = Union[CodeInterpreterToolParam, RetrievalToolParam, FunctionToolParam] +Tool = Union[CodeInterpreterToolParam, FileSearchToolParam, FunctionToolParam] class TruncationStrategy(TypedDict, total=False): diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index 1b382186aa..84a98a74d7 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -5,7 +5,15 @@ from typing import List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["ThreadCreateParams", "Message"] +__all__ = [ + "ThreadCreateParams", + "Message", + "MessageAttachment", + "ToolResources", + "ToolResourcesCodeInterpreter", + "ToolResourcesFileSearch", + "ToolResourcesFileSearchVectorStore", +] class ThreadCreateParams(TypedDict, total=False): @@ -23,6 +31,21 @@ class ThreadCreateParams(TypedDict, total=False): a maxium of 512 characters long. """ + tool_resources: Optional[ToolResources] + """ + A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + +class MessageAttachment(TypedDict, total=False): + add_to: List[Literal["file_search", "code_interpreter"]] + + file_id: str + """The ID of the file to attach to the message.""" + class Message(TypedDict, total=False): content: Required[str] @@ -37,13 +60,8 @@ class Message(TypedDict, total=False): value to insert messages from the assistant into the conversation. """ - file_ids: List[str] - """ - A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that - the message should use. There can be a maximum of 10 files attached to a - message. Useful for tools like `retrieval` and `code_interpreter` that can - access and use files. - """ + attachments: Optional[Iterable[MessageAttachment]] + """A list of files attached to the message, and the tools they should be added to.""" metadata: Optional[object] """Set of 16 key-value pairs that can be attached to an object. @@ -52,3 +70,53 @@ class Message(TypedDict, total=False): structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 characters long. """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made + available to the `code_interpreter` tool. There can be a maximum of 20 files + associated with the tool. + """ + + +class ToolResourcesFileSearchVectorStore(TypedDict, total=False): + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to + add to the vector store. There can be a maximum of 10000 files in a vector + store. + """ + + metadata: object + """Set of 16 key-value pairs that can be attached to a vector store. + + This can be useful for storing additional information about the vector store in + a structured format. Keys can be a maximum of 64 characters long and values can + be a maxium of 512 characters long. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: List[str] + """ + The + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + attached to this thread. There can be a maximum of 1 vector store attached to + the thread. + """ + + vector_stores: Iterable[ToolResourcesFileSearchVectorStore] + """ + A helper to create a + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + with file_ids and attach it to this thread. There can be a maximum of 1 vector + store attached to the thread. + """ + + +class ToolResources(TypedDict, total=False): + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py index 94f1b1e22e..7210ab77c9 100644 --- a/src/openai/types/beta/thread_update_params.py +++ b/src/openai/types/beta/thread_update_params.py @@ -2,10 +2,10 @@ from __future__ import annotations -from typing import Optional +from typing import List, Optional from typing_extensions import TypedDict -__all__ = ["ThreadUpdateParams"] +__all__ = ["ThreadUpdateParams", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] class ThreadUpdateParams(TypedDict, total=False): @@ -16,3 +16,36 @@ class ThreadUpdateParams(TypedDict, total=False): structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 characters long. """ + + tool_resources: Optional[ToolResources] + """ + A set of resources that are made available to the assistant's tools in this + thread. The resources are specific to the type of tool. For example, the + `code_interpreter` tool requires a list of file IDs, while the `file_search` + tool requires a list of vector store IDs. + """ + + +class ToolResourcesCodeInterpreter(TypedDict, total=False): + file_ids: List[str] + """ + A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made + available to the `code_interpreter` tool. There can be a maximum of 20 files + associated with the tool. + """ + + +class ToolResourcesFileSearch(TypedDict, total=False): + vector_store_ids: List[str] + """ + The + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + attached to this thread. There can be a maximum of 1 vector store attached to + the thread. + """ + + +class ToolResources(TypedDict, total=False): + code_interpreter: ToolResourcesCodeInterpreter + + file_search: ToolResourcesFileSearch diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index bde0263975..42f0162734 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -6,7 +6,14 @@ from ...._models import BaseModel from .message_content import MessageContent -__all__ = ["Message", "IncompleteDetails"] +__all__ = ["Message", "Attachment", "IncompleteDetails"] + + +class Attachment(BaseModel): + add_to: Optional[List[Literal["file_search", "code_interpreter"]]] = None + + file_id: Optional[str] = None + """The ID of the file to attach to the message.""" class IncompleteDetails(BaseModel): @@ -25,6 +32,9 @@ class Message(BaseModel): authored this message. """ + attachments: Optional[List[Attachment]] = None + """A list of files attached to the message, and the tools they were added to.""" + completed_at: Optional[int] = None """The Unix timestamp (in seconds) for when the message was completed.""" @@ -34,13 +44,6 @@ class Message(BaseModel): created_at: int """The Unix timestamp (in seconds) for when the message was created.""" - file_ids: List[str] - """ - A list of [file](https://platform.openai.com/docs/api-reference/files) IDs that - the assistant should use. Useful for tools like retrieval and code_interpreter - that can access files. A maximum of 10 files can be attached to a message. - """ - incomplete_at: Optional[int] = None """The Unix timestamp (in seconds) for when the message was marked as incomplete.""" diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index 9b9467ef4d..1ef1d9ae10 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -2,10 +2,10 @@ from __future__ import annotations -from typing import List, Optional +from typing import List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["MessageCreateParams"] +__all__ = ["MessageCreateParams", "Attachment"] class MessageCreateParams(TypedDict, total=False): @@ -21,13 +21,8 @@ class MessageCreateParams(TypedDict, total=False): value to insert messages from the assistant into the conversation. """ - file_ids: List[str] - """ - A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that - the message should use. There can be a maximum of 10 files attached to a - message. Useful for tools like `retrieval` and `code_interpreter` that can - access and use files. - """ + attachments: Optional[Iterable[Attachment]] + """A list of files attached to the message, and the tools they should be added to.""" metadata: Optional[object] """Set of 16 key-value pairs that can be attached to an object. @@ -36,3 +31,10 @@ class MessageCreateParams(TypedDict, total=False): structured format. Keys can be a maximum of 64 characters long and values can be a maxium of 512 characters long. """ + + +class Attachment(TypedDict, total=False): + add_to: List[Literal["file_search", "code_interpreter"]] + + file_id: str + """The ID of the file to attach to the message.""" diff --git a/src/openai/types/beta/threads/message_delta.py b/src/openai/types/beta/threads/message_delta.py index 3a55e1442a..ecd0dfe319 100644 --- a/src/openai/types/beta/threads/message_delta.py +++ b/src/openai/types/beta/threads/message_delta.py @@ -13,12 +13,5 @@ class MessageDelta(BaseModel): content: Optional[List[MessageContentDelta]] = None """The content of the message in array of text and/or images.""" - file_ids: Optional[List[str]] = None - """ - A list of [file](https://platform.openai.com/docs/api-reference/files) IDs that - the assistant should use. Useful for tools like retrieval and code_interpreter - that can access files. A maximum of 10 files can be attached to a message. - """ - role: Optional[Literal["user", "assistant"]] = None """The entity that produced the message. One of `user` or `assistant`.""" diff --git a/src/openai/types/beta/threads/messages/__init__.py b/src/openai/types/beta/threads/messages/__init__.py deleted file mode 100644 index d129297620..0000000000 --- a/src/openai/types/beta/threads/messages/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .message_file import MessageFile as MessageFile -from .file_list_params import FileListParams as FileListParams diff --git a/src/openai/types/beta/threads/messages/message_file.py b/src/openai/types/beta/threads/messages/message_file.py deleted file mode 100644 index 342479ab7b..0000000000 --- a/src/openai/types/beta/threads/messages/message_file.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ....._models import BaseModel - -__all__ = ["MessageFile"] - - -class MessageFile(BaseModel): - id: str - """The identifier, which can be referenced in API endpoints.""" - - created_at: int - """The Unix timestamp (in seconds) for when the message file was created.""" - - message_id: str - """ - The ID of the [message](https://platform.openai.com/docs/api-reference/messages) - that the [File](https://platform.openai.com/docs/api-reference/files) is - attached to. - """ - - object: Literal["thread.message.file"] - """The object type, which is always `thread.message.file`.""" diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 2efc3c77fa..8f427ce6e8 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -105,13 +105,6 @@ class Run(BaseModel): failed_at: Optional[int] = None """The Unix timestamp (in seconds) for when the run failed.""" - file_ids: List[str] - """ - The list of [File](https://platform.openai.com/docs/api-reference/files) IDs the - [assistant](https://platform.openai.com/docs/api-reference/assistants) used for - this run. - """ - incomplete_details: Optional[IncompleteDetails] = None """Details on why the run is incomplete. @@ -227,3 +220,6 @@ class Run(BaseModel): temperature: Optional[float] = None """The sampling temperature used for this run. If not set, defaults to 1.""" + + top_p: Optional[float] = None + """The nucleus sampling value used for this run. If not set, defaults to 1.""" diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 9f2d4ba18b..fd0b4e7920 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -12,6 +12,7 @@ __all__ = [ "RunCreateParamsBase", "AdditionalMessage", + "AdditionalMessageAttachment", "TruncationStrategy", "RunCreateParamsNonStreaming", "RunCreateParamsStreaming", @@ -142,9 +143,23 @@ class RunCreateParamsBase(TypedDict, total=False): This is useful for modifying the behavior on a per-run basis. """ + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + """ + truncation_strategy: Optional[TruncationStrategy] +class AdditionalMessageAttachment(TypedDict, total=False): + add_to: List[Literal["file_search", "code_interpreter"]] + + file_id: str + """The ID of the file to attach to the message.""" + + class AdditionalMessage(TypedDict, total=False): content: Required[str] """The content of the message.""" @@ -158,13 +173,8 @@ class AdditionalMessage(TypedDict, total=False): value to insert messages from the assistant into the conversation. """ - file_ids: List[str] - """ - A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that - the message should use. There can be a maximum of 10 files attached to a - message. Useful for tools like `retrieval` and `code_interpreter` that can - access and use files. - """ + attachments: Optional[Iterable[AdditionalMessageAttachment]] + """A list of files attached to the message, and the tools they should be added to.""" metadata: Optional[object] """Set of 16 key-value pairs that can be attached to an object. diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py index 256510dcc7..a312ce3df2 100644 --- a/src/openai/types/beta/threads/runs/__init__.py +++ b/src/openai/types/beta/threads/runs/__init__.py @@ -8,14 +8,14 @@ from .tool_call_delta import ToolCallDelta as ToolCallDelta from .step_list_params import StepListParams as StepListParams from .function_tool_call import FunctionToolCall as FunctionToolCall -from .retrieval_tool_call import RetrievalToolCall as RetrievalToolCall from .run_step_delta_event import RunStepDeltaEvent as RunStepDeltaEvent from .code_interpreter_logs import CodeInterpreterLogs as CodeInterpreterLogs +from .file_search_tool_call import FileSearchToolCall as FileSearchToolCall from .tool_call_delta_object import ToolCallDeltaObject as ToolCallDeltaObject from .tool_calls_step_details import ToolCallsStepDetails as ToolCallsStepDetails from .function_tool_call_delta import FunctionToolCallDelta as FunctionToolCallDelta -from .retrieval_tool_call_delta import RetrievalToolCallDelta as RetrievalToolCallDelta from .code_interpreter_tool_call import CodeInterpreterToolCall as CodeInterpreterToolCall +from .file_search_tool_call_delta import FileSearchToolCallDelta as FileSearchToolCallDelta from .run_step_delta_message_delta import RunStepDeltaMessageDelta as RunStepDeltaMessageDelta from .code_interpreter_output_image import CodeInterpreterOutputImage as CodeInterpreterOutputImage from .message_creation_step_details import MessageCreationStepDetails as MessageCreationStepDetails diff --git a/src/openai/types/beta/threads/runs/retrieval_tool_call.py b/src/openai/types/beta/threads/runs/file_search_tool_call.py similarity index 61% rename from src/openai/types/beta/threads/runs/retrieval_tool_call.py rename to src/openai/types/beta/threads/runs/file_search_tool_call.py index 48704ed331..57c0ca9a90 100644 --- a/src/openai/types/beta/threads/runs/retrieval_tool_call.py +++ b/src/openai/types/beta/threads/runs/file_search_tool_call.py @@ -4,18 +4,18 @@ from ....._models import BaseModel -__all__ = ["RetrievalToolCall"] +__all__ = ["FileSearchToolCall"] -class RetrievalToolCall(BaseModel): +class FileSearchToolCall(BaseModel): id: str """The ID of the tool call object.""" - retrieval: object + file_search: object """For now, this is always going to be an empty object.""" - type: Literal["retrieval"] + type: Literal["file_search"] """The type of tool call. - This is always going to be `retrieval` for this type of tool call. + This is always going to be `file_search` for this type of tool call. """ diff --git a/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py b/src/openai/types/beta/threads/runs/file_search_tool_call_delta.py similarity index 67% rename from src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py rename to src/openai/types/beta/threads/runs/file_search_tool_call_delta.py index 3310079399..df5ac217dc 100644 --- a/src/openai/types/beta/threads/runs/retrieval_tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/file_search_tool_call_delta.py @@ -5,21 +5,21 @@ from ....._models import BaseModel -__all__ = ["RetrievalToolCallDelta"] +__all__ = ["FileSearchToolCallDelta"] -class RetrievalToolCallDelta(BaseModel): +class FileSearchToolCallDelta(BaseModel): + file_search: object + """For now, this is always going to be an empty object.""" + index: int """The index of the tool call in the tool calls array.""" - type: Literal["retrieval"] + type: Literal["file_search"] """The type of tool call. - This is always going to be `retrieval` for this type of tool call. + This is always going to be `file_search` for this type of tool call. """ id: Optional[str] = None """The ID of the tool call object.""" - - retrieval: Optional[object] = None - """For now, this is always going to be an empty object.""" diff --git a/src/openai/types/beta/threads/runs/tool_call.py b/src/openai/types/beta/threads/runs/tool_call.py index dcca797bf0..77d86b46d9 100644 --- a/src/openai/types/beta/threads/runs/tool_call.py +++ b/src/openai/types/beta/threads/runs/tool_call.py @@ -5,11 +5,11 @@ from ....._utils import PropertyInfo from .function_tool_call import FunctionToolCall -from .retrieval_tool_call import RetrievalToolCall +from .file_search_tool_call import FileSearchToolCall from .code_interpreter_tool_call import CodeInterpreterToolCall __all__ = ["ToolCall"] ToolCall = Annotated[ - Union[CodeInterpreterToolCall, RetrievalToolCall, FunctionToolCall], PropertyInfo(discriminator="type") + Union[CodeInterpreterToolCall, FileSearchToolCall, FunctionToolCall], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/beta/threads/runs/tool_call_delta.py b/src/openai/types/beta/threads/runs/tool_call_delta.py index fc98981abf..90cfe0657e 100644 --- a/src/openai/types/beta/threads/runs/tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/tool_call_delta.py @@ -5,12 +5,12 @@ from ....._utils import PropertyInfo from .function_tool_call_delta import FunctionToolCallDelta -from .retrieval_tool_call_delta import RetrievalToolCallDelta +from .file_search_tool_call_delta import FileSearchToolCallDelta from .code_interpreter_tool_call_delta import CodeInterpreterToolCallDelta __all__ = ["ToolCallDelta"] ToolCallDelta = Annotated[ - Union[CodeInterpreterToolCallDelta, RetrievalToolCallDelta, FunctionToolCallDelta], + Union[CodeInterpreterToolCallDelta, FileSearchToolCallDelta, FunctionToolCallDelta], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/beta/threads/runs/tool_call_delta_object.py b/src/openai/types/beta/threads/runs/tool_call_delta_object.py index 9cd59a6e24..189dce772c 100644 --- a/src/openai/types/beta/threads/runs/tool_call_delta_object.py +++ b/src/openai/types/beta/threads/runs/tool_call_delta_object.py @@ -17,5 +17,5 @@ class ToolCallDeltaObject(BaseModel): """An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, - `retrieval`, or `function`. + `file_search`, or `function`. """ diff --git a/src/openai/types/beta/threads/runs/tool_calls_step_details.py b/src/openai/types/beta/threads/runs/tool_calls_step_details.py index ca08fabd0e..a084d387c7 100644 --- a/src/openai/types/beta/threads/runs/tool_calls_step_details.py +++ b/src/openai/types/beta/threads/runs/tool_calls_step_details.py @@ -14,7 +14,7 @@ class ToolCallsStepDetails(BaseModel): """An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, - `retrieval`, or `function`. + `file_search`, or `function`. """ type: Literal["tool_calls"] diff --git a/src/openai/types/beta/vector_store.py b/src/openai/types/beta/vector_store.py new file mode 100644 index 0000000000..122705734d --- /dev/null +++ b/src/openai/types/beta/vector_store.py @@ -0,0 +1,79 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["VectorStore", "FileCounts", "ExpiresAfter"] + + +class FileCounts(BaseModel): + cancelled: int + """The number of files that were cancelled.""" + + completed: int + """The number of files that have been successfully processed.""" + + failed: int + """The number of files that have failed to process.""" + + in_progress: int + """The number of files that are currently being processed.""" + + total: int + """The total number of files.""" + + +class ExpiresAfter(BaseModel): + anchor: Literal["last_active_at"] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `last_active_at`. + """ + + days: int + """The number of days after the anchor time that the vector store will expire.""" + + +class VectorStore(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + bytes: int + """The byte size of the vector store.""" + + created_at: int + """The Unix timestamp (in seconds) for when the vector store was created.""" + + file_counts: FileCounts + + last_active_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the vector store was last active.""" + + metadata: Optional[object] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + name: str + """The name of the vector store.""" + + object: Literal["vector_store"] + """The object type, which is always `vector_store`.""" + + status: Literal["expired", "in_progress", "completed"] + """ + The status of the vector store, which can be either `expired`, `in_progress`, or + `completed`. A status of `completed` indicates that the vector store is ready + for use. + """ + + expires_after: Optional[ExpiresAfter] = None + """The expiration policy for a vector store.""" + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the vector store will expire.""" diff --git a/src/openai/types/beta/vector_store_create_params.py b/src/openai/types/beta/vector_store_create_params.py new file mode 100644 index 0000000000..f1a3abcbdf --- /dev/null +++ b/src/openai/types/beta/vector_store_create_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["VectorStoreCreateParams", "ExpiresAfter"] + + +class VectorStoreCreateParams(TypedDict, total=False): + expires_after: ExpiresAfter + """The expiration policy for a vector store.""" + + file_ids: List[str] + """ + A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the vector store should use. Useful for tools like `file_search` that can access + files. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + name: str + """The name of the vector store.""" + + +class ExpiresAfter(TypedDict, total=False): + anchor: Required[Literal["last_active_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `last_active_at`. + """ + + days: Required[int] + """The number of days after the anchor time that the vector store will expire.""" diff --git a/src/openai/types/beta/retrieval_tool.py b/src/openai/types/beta/vector_store_deleted.py similarity index 52% rename from src/openai/types/beta/retrieval_tool.py rename to src/openai/types/beta/vector_store_deleted.py index b07b785c66..21ccda1db5 100644 --- a/src/openai/types/beta/retrieval_tool.py +++ b/src/openai/types/beta/vector_store_deleted.py @@ -4,9 +4,12 @@ from ..._models import BaseModel -__all__ = ["RetrievalTool"] +__all__ = ["VectorStoreDeleted"] -class RetrievalTool(BaseModel): - type: Literal["retrieval"] - """The type of tool being defined: `retrieval`""" +class VectorStoreDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["vector_store.deleted"] diff --git a/src/openai/types/beta/assistants/file_list_params.py b/src/openai/types/beta/vector_store_list_params.py similarity index 92% rename from src/openai/types/beta/assistants/file_list_params.py rename to src/openai/types/beta/vector_store_list_params.py index 53c493b36a..f39f67266d 100644 --- a/src/openai/types/beta/assistants/file_list_params.py +++ b/src/openai/types/beta/vector_store_list_params.py @@ -4,10 +4,10 @@ from typing_extensions import Literal, TypedDict -__all__ = ["FileListParams"] +__all__ = ["VectorStoreListParams"] -class FileListParams(TypedDict, total=False): +class VectorStoreListParams(TypedDict, total=False): after: str """A cursor for use in pagination. diff --git a/src/openai/types/beta/vector_store_update_params.py b/src/openai/types/beta/vector_store_update_params.py new file mode 100644 index 0000000000..0f9593e476 --- /dev/null +++ b/src/openai/types/beta/vector_store_update_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["VectorStoreUpdateParams", "ExpiresAfter"] + + +class VectorStoreUpdateParams(TypedDict, total=False): + expires_after: Optional[ExpiresAfter] + """The expiration policy for a vector store.""" + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maxium of 512 characters long. + """ + + name: Optional[str] + """The name of the vector store.""" + + +class ExpiresAfter(TypedDict, total=False): + anchor: Required[Literal["last_active_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `last_active_at`. + """ + + days: Required[int] + """The number of days after the anchor time that the vector store will expire.""" diff --git a/src/openai/types/beta/vector_stores/__init__.py b/src/openai/types/beta/vector_stores/__init__.py new file mode 100644 index 0000000000..ff05dd63d8 --- /dev/null +++ b/src/openai/types/beta/vector_stores/__init__.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .file_list_params import FileListParams as FileListParams +from .vector_store_file import VectorStoreFile as VectorStoreFile +from .file_create_params import FileCreateParams as FileCreateParams +from .vector_store_file_batch import VectorStoreFileBatch as VectorStoreFileBatch +from .file_batch_create_params import FileBatchCreateParams as FileBatchCreateParams +from .vector_store_file_deleted import VectorStoreFileDeleted as VectorStoreFileDeleted +from .file_batch_list_files_params import FileBatchListFilesParams as FileBatchListFilesParams diff --git a/src/openai/types/beta/vector_stores/file_batch_create_params.py b/src/openai/types/beta/vector_stores/file_batch_create_params.py new file mode 100644 index 0000000000..0882829732 --- /dev/null +++ b/src/openai/types/beta/vector_stores/file_batch_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, TypedDict + +__all__ = ["FileBatchCreateParams"] + + +class FileBatchCreateParams(TypedDict, total=False): + file_ids: Required[List[str]] + """ + A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the vector store should use. Useful for tools like `file_search` that can access + files. + """ diff --git a/src/openai/types/beta/vector_stores/file_batch_list_files_params.py b/src/openai/types/beta/vector_stores/file_batch_list_files_params.py new file mode 100644 index 0000000000..24dee7d5a5 --- /dev/null +++ b/src/openai/types/beta/vector_stores/file_batch_list_files_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FileBatchListFilesParams"] + + +class FileBatchListFilesParams(TypedDict, total=False): + vector_store_id: Required[str] + + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + filter: Literal["in_progress", "completed", "failed", "cancelled"] + """Filter by file status. + + One of `in_progress`, `completed`, `failed`, `cancelled`. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/beta/assistants/file_create_params.py b/src/openai/types/beta/vector_stores/file_create_params.py similarity index 70% rename from src/openai/types/beta/assistants/file_create_params.py rename to src/openai/types/beta/vector_stores/file_create_params.py index 55f0e8cda1..2fee588abf 100644 --- a/src/openai/types/beta/assistants/file_create_params.py +++ b/src/openai/types/beta/vector_stores/file_create_params.py @@ -10,7 +10,7 @@ class FileCreateParams(TypedDict, total=False): file_id: Required[str] """ - A [File](https://platform.openai.com/docs/api-reference/files) ID (with - `purpose="assistants"`) that the assistant should use. Useful for tools like - `retrieval` and `code_interpreter` that can access files. + A [File](https://platform.openai.com/docs/api-reference/files) ID that the + vector store should use. Useful for tools like `file_search` that can access + files. """ diff --git a/src/openai/types/beta/threads/messages/file_list_params.py b/src/openai/types/beta/vector_stores/file_list_params.py similarity index 84% rename from src/openai/types/beta/threads/messages/file_list_params.py rename to src/openai/types/beta/vector_stores/file_list_params.py index 7e2d6136ec..23dd7f0d94 100644 --- a/src/openai/types/beta/threads/messages/file_list_params.py +++ b/src/openai/types/beta/vector_stores/file_list_params.py @@ -2,14 +2,12 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, TypedDict __all__ = ["FileListParams"] class FileListParams(TypedDict, total=False): - thread_id: Required[str] - after: str """A cursor for use in pagination. @@ -28,6 +26,12 @@ class FileListParams(TypedDict, total=False): of the list. """ + filter: Literal["in_progress", "completed", "failed", "cancelled"] + """Filter by file status. + + One of `in_progress`, `completed`, `failed`, `cancelled`. + """ + limit: int """A limit on the number of objects to be returned. diff --git a/src/openai/types/beta/vector_stores/vector_store_file.py b/src/openai/types/beta/vector_stores/vector_store_file.py new file mode 100644 index 0000000000..a878b281d5 --- /dev/null +++ b/src/openai/types/beta/vector_stores/vector_store_file.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["VectorStoreFile", "LastError"] + + +class LastError(BaseModel): + code: Literal["internal_error", "file_not_found", "parsing_error", "unhandled_mime_type"] + """One of `server_error` or `rate_limit_exceeded`.""" + + message: str + """A human-readable description of the error.""" + + +class VectorStoreFile(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the vector store file was created.""" + + last_error: Optional[LastError] = None + """The last error associated with this vector store file. + + Will be `null` if there are no errors. + """ + + object: Literal["vector_store.file"] + """The object type, which is always `vector_store.file`.""" + + status: Literal["in_progress", "completed", "cancelled", "failed"] + """ + The status of the vector store file, which can be either `in_progress`, + `completed`, `cancelled`, or `failed`. The status `completed` indicates that the + vector store file is ready for use. + """ + + vector_store_id: str + """ + The ID of the + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + that the [File](https://platform.openai.com/docs/api-reference/files) is + attached to. + """ diff --git a/src/openai/types/beta/vector_stores/vector_store_file_batch.py b/src/openai/types/beta/vector_stores/vector_store_file_batch.py new file mode 100644 index 0000000000..df130a58de --- /dev/null +++ b/src/openai/types/beta/vector_stores/vector_store_file_batch.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["VectorStoreFileBatch", "FileCounts"] + + +class FileCounts(BaseModel): + cancelled: int + """The number of files that where cancelled.""" + + completed: int + """The number of files that have been processed.""" + + failed: int + """The number of files that have failed to process.""" + + in_progress: int + """The number of files that are currently being processed.""" + + total: int + """The total number of files.""" + + +class VectorStoreFileBatch(BaseModel): + id: str + """The identifier, which can be referenced in API endpoints.""" + + created_at: int + """ + The Unix timestamp (in seconds) for when the vector store files batch was + created. + """ + + file_counts: FileCounts + + object: Literal["vector_store.files_batch"] + """The object type, which is always `vector_store.file_batch`.""" + + status: Literal["in_progress", "completed", "cancelled", "failed"] + """ + The status of the vector store files batch, which can be either `in_progress`, + `completed`, `cancelled` or `failed`. + """ + + vector_store_id: str + """ + The ID of the + [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) + that the [File](https://platform.openai.com/docs/api-reference/files) is + attached to. + """ diff --git a/src/openai/types/beta/assistants/file_delete_response.py b/src/openai/types/beta/vector_stores/vector_store_file_deleted.py similarity index 60% rename from src/openai/types/beta/assistants/file_delete_response.py rename to src/openai/types/beta/vector_stores/vector_store_file_deleted.py index 685fb2a75c..ae37f84364 100644 --- a/src/openai/types/beta/assistants/file_delete_response.py +++ b/src/openai/types/beta/vector_stores/vector_store_file_deleted.py @@ -4,12 +4,12 @@ from ...._models import BaseModel -__all__ = ["FileDeleteResponse"] +__all__ = ["VectorStoreFileDeleted"] -class FileDeleteResponse(BaseModel): +class VectorStoreFileDeleted(BaseModel): id: str deleted: bool - object: Literal["assistant.file.deleted"] + object: Literal["vector_store.file.deleted"] diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 892c737fa3..1925f90d12 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -19,7 +19,7 @@ class JobCreateParams(TypedDict, total=False): training_file: Required[str] """The ID of an uploaded file that contains training data. - See [upload file](https://platform.openai.com/docs/api-reference/files/upload) + See [upload file](https://platform.openai.com/docs/api-reference/files/create) for how to upload a file. Your dataset must be formatted as a JSONL file. Additionally, you must upload diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index a509627b8e..a92acb2ca5 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -33,11 +33,25 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: assistant = client.beta.assistants.create( model="gpt-4-turbo", description="string", - file_ids=["string", "string", "string"], instructions="string", metadata={}, name="string", + response_format="none", + temperature=1, + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "file_ids": ["string", "string", "string"], + "metadata": {}, + } + ], + }, + }, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, ) assert_matches_type(Assistant, assistant, path=["response"]) @@ -115,12 +129,18 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: assistant = client.beta.assistants.update( "string", description="string", - file_ids=["string", "string", "string"], instructions="string", metadata={}, model="string", name="string", + response_format="none", + temperature=1, + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, ) assert_matches_type(Assistant, assistant, path=["response"]) @@ -244,11 +264,25 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> assistant = await async_client.beta.assistants.create( model="gpt-4-turbo", description="string", - file_ids=["string", "string", "string"], instructions="string", metadata={}, name="string", + response_format="none", + temperature=1, + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "file_ids": ["string", "string", "string"], + "metadata": {}, + } + ], + }, + }, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, ) assert_matches_type(Assistant, assistant, path=["response"]) @@ -326,12 +360,18 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> assistant = await async_client.beta.assistants.update( "string", description="string", - file_ids=["string", "string", "string"], instructions="string", metadata={}, model="string", name="string", + response_format="none", + temperature=1, + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, ) assert_matches_type(Assistant, assistant, path=["response"]) diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 7c07251433..980fd9a75e 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -33,23 +33,74 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], metadata={}, + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "file_ids": ["string", "string", "string"], + "metadata": {}, + } + ], + }, + }, ) assert_matches_type(Thread, thread, path=["response"]) @@ -123,6 +174,10 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: thread = client.beta.threads.update( "string", metadata={}, + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, ) assert_matches_type(Thread, thread, path=["response"]) @@ -219,26 +274,82 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], + "tool_resources": { + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "file_ids": ["string", "string", "string"], + "metadata": {}, + } + ], + }, + }, "metadata": {}, }, tool_choice="none", + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, truncation_strategy={ "type": "auto", "last_messages": 1, @@ -295,26 +406,82 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], + "tool_resources": { + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "file_ids": ["string", "string", "string"], + "metadata": {}, + } + ], + }, + }, "metadata": {}, }, tool_choice="none", + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, truncation_strategy={ "type": "auto", "last_messages": 1, @@ -363,23 +530,74 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], metadata={}, + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "file_ids": ["string", "string", "string"], + "metadata": {}, + } + ], + }, + }, ) assert_matches_type(Thread, thread, path=["response"]) @@ -453,6 +671,10 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> thread = await async_client.beta.threads.update( "string", metadata={}, + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, ) assert_matches_type(Thread, thread, path=["response"]) @@ -549,26 +771,82 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], + "tool_resources": { + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "file_ids": ["string", "string", "string"], + "metadata": {}, + } + ], + }, + }, "metadata": {}, }, tool_choice="none", + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, truncation_strategy={ "type": "auto", "last_messages": 1, @@ -625,26 +903,82 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], + "tool_resources": { + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "file_ids": ["string", "string", "string"], + "metadata": {}, + } + ], + }, + }, "metadata": {}, }, tool_choice="none", + tool_resources={ + "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, truncation_strategy={ "type": "auto", "last_messages": 1, diff --git a/tests/api_resources/beta/test_vector_stores.py b/tests/api_resources/beta/test_vector_stores.py new file mode 100644 index 0000000000..e671c96a45 --- /dev/null +++ b/tests/api_resources/beta/test_vector_stores.py @@ -0,0 +1,426 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta import ( + VectorStore, + VectorStoreDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestVectorStores: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + vector_store = client.beta.vector_stores.create() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + vector_store = client.beta.vector_stores.create( + expires_after={ + "anchor": "last_active_at", + "days": 1, + }, + file_ids=["string", "string", "string"], + metadata={}, + name="string", + ) + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.vector_stores.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.vector_stores.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + vector_store = client.beta.vector_stores.retrieve( + "string", + ) + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.vector_stores.with_raw_response.retrieve( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.vector_stores.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + vector_store = client.beta.vector_stores.update( + "string", + ) + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + vector_store = client.beta.vector_stores.update( + "string", + expires_after={ + "anchor": "last_active_at", + "days": 1, + }, + metadata={}, + name="string", + ) + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.beta.vector_stores.with_raw_response.update( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.beta.vector_stores.with_streaming_response.update( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.with_raw_response.update( + "", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + vector_store = client.beta.vector_stores.list() + assert_matches_type(SyncCursorPage[VectorStore], vector_store, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + vector_store = client.beta.vector_stores.list( + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[VectorStore], vector_store, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.vector_stores.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(SyncCursorPage[VectorStore], vector_store, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.vector_stores.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = response.parse() + assert_matches_type(SyncCursorPage[VectorStore], vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + vector_store = client.beta.vector_stores.delete( + "string", + ) + assert_matches_type(VectorStoreDeleted, vector_store, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.beta.vector_stores.with_raw_response.delete( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(VectorStoreDeleted, vector_store, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.beta.vector_stores.with_streaming_response.delete( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = response.parse() + assert_matches_type(VectorStoreDeleted, vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.with_raw_response.delete( + "", + ) + + +class TestAsyncVectorStores: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.beta.vector_stores.create() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.beta.vector_stores.create( + expires_after={ + "anchor": "last_active_at", + "days": 1, + }, + file_ids=["string", "string", "string"], + metadata={}, + name="string", + ) + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = await response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.beta.vector_stores.retrieve( + "string", + ) + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.with_raw_response.retrieve( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.with_streaming_response.retrieve( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = await response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.beta.vector_stores.update( + "string", + ) + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.beta.vector_stores.update( + "string", + expires_after={ + "anchor": "last_active_at", + "days": 1, + }, + metadata={}, + name="string", + ) + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.with_raw_response.update( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.with_streaming_response.update( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = await response.parse() + assert_matches_type(VectorStore, vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.with_raw_response.update( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.beta.vector_stores.list() + assert_matches_type(AsyncCursorPage[VectorStore], vector_store, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.beta.vector_stores.list( + after="string", + before="string", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[VectorStore], vector_store, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(AsyncCursorPage[VectorStore], vector_store, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = await response.parse() + assert_matches_type(AsyncCursorPage[VectorStore], vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.beta.vector_stores.delete( + "string", + ) + assert_matches_type(VectorStoreDeleted, vector_store, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.with_raw_response.delete( + "string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(VectorStoreDeleted, vector_store, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.with_streaming_response.delete( + "string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = await response.parse() + assert_matches_type(VectorStoreDeleted, vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/beta/threads/messages/__init__.py b/tests/api_resources/beta/threads/messages/__init__.py deleted file mode 100644 index fd8019a9a1..0000000000 --- a/tests/api_resources/beta/threads/messages/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/threads/messages/test_files.py b/tests/api_resources/beta/threads/messages/test_files.py deleted file mode 100644 index af4eea9377..0000000000 --- a/tests/api_resources/beta/threads/messages/test_files.py +++ /dev/null @@ -1,263 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from openai import OpenAI, AsyncOpenAI -from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads.messages import MessageFile - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") - - -class TestFiles: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: OpenAI) -> None: - file = client.beta.threads.messages.files.retrieve( - "file-abc123", - thread_id="thread_abc123", - message_id="msg_abc123", - ) - assert_matches_type(MessageFile, file, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: OpenAI) -> None: - response = client.beta.threads.messages.files.with_raw_response.retrieve( - "file-abc123", - thread_id="thread_abc123", - message_id="msg_abc123", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(MessageFile, file, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: OpenAI) -> None: - with client.beta.threads.messages.files.with_streaming_response.retrieve( - "file-abc123", - thread_id="thread_abc123", - message_id="msg_abc123", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = response.parse() - assert_matches_type(MessageFile, file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_retrieve(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - client.beta.threads.messages.files.with_raw_response.retrieve( - "file-abc123", - thread_id="", - message_id="msg_abc123", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - client.beta.threads.messages.files.with_raw_response.retrieve( - "file-abc123", - thread_id="thread_abc123", - message_id="", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - client.beta.threads.messages.files.with_raw_response.retrieve( - "", - thread_id="thread_abc123", - message_id="msg_abc123", - ) - - @parametrize - def test_method_list(self, client: OpenAI) -> None: - file = client.beta.threads.messages.files.list( - "string", - thread_id="string", - ) - assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) - - @parametrize - def test_method_list_with_all_params(self, client: OpenAI) -> None: - file = client.beta.threads.messages.files.list( - "string", - thread_id="string", - after="string", - before="string", - limit=0, - order="asc", - ) - assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) - - @parametrize - def test_raw_response_list(self, client: OpenAI) -> None: - response = client.beta.threads.messages.files.with_raw_response.list( - "string", - thread_id="string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) - - @parametrize - def test_streaming_response_list(self, client: OpenAI) -> None: - with client.beta.threads.messages.files.with_streaming_response.list( - "string", - thread_id="string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = response.parse() - assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_list(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - client.beta.threads.messages.files.with_raw_response.list( - "string", - thread_id="", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - client.beta.threads.messages.files.with_raw_response.list( - "", - thread_id="string", - ) - - -class TestAsyncFiles: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.threads.messages.files.retrieve( - "file-abc123", - thread_id="thread_abc123", - message_id="msg_abc123", - ) - assert_matches_type(MessageFile, file, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.threads.messages.files.with_raw_response.retrieve( - "file-abc123", - thread_id="thread_abc123", - message_id="msg_abc123", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(MessageFile, file, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.threads.messages.files.with_streaming_response.retrieve( - "file-abc123", - thread_id="thread_abc123", - message_id="msg_abc123", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = await response.parse() - assert_matches_type(MessageFile, file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await async_client.beta.threads.messages.files.with_raw_response.retrieve( - "file-abc123", - thread_id="", - message_id="msg_abc123", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - await async_client.beta.threads.messages.files.with_raw_response.retrieve( - "file-abc123", - thread_id="thread_abc123", - message_id="", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await async_client.beta.threads.messages.files.with_raw_response.retrieve( - "", - thread_id="thread_abc123", - message_id="msg_abc123", - ) - - @parametrize - async def test_method_list(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.threads.messages.files.list( - "string", - thread_id="string", - ) - assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) - - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.threads.messages.files.list( - "string", - thread_id="string", - after="string", - before="string", - limit=0, - order="asc", - ) - assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) - - @parametrize - async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.threads.messages.files.with_raw_response.list( - "string", - thread_id="string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) - - @parametrize - async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.threads.messages.files.with_streaming_response.list( - "string", - thread_id="string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = await response.parse() - assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): - await async_client.beta.threads.messages.files.with_raw_response.list( - "string", - thread_id="", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - await async_client.beta.threads.messages.files.with_raw_response.list( - "", - thread_id="string", - ) diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index 22198ccbc5..5ea5ac3bd5 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -33,7 +33,20 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "string", content="x", role="user", - file_ids=["string"], + attachments=[ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], metadata={}, ) assert_matches_type(Message, message, path=["response"]) @@ -249,7 +262,20 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "string", content="x", role="user", - file_ids=["string"], + attachments=[ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], metadata={}, ) assert_matches_type(Message, message, path=["response"]) diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index cf5b2998b9..3d8a6ce058 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -40,19 +40,58 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], @@ -66,6 +105,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: temperature=1, tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, truncation_strategy={ "type": "auto", "last_messages": 1, @@ -127,19 +167,58 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], @@ -152,6 +231,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: temperature=1, tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, truncation_strategy={ "type": "auto", "last_messages": 1, @@ -552,19 +632,58 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], @@ -578,6 +697,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn temperature=1, tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, truncation_strategy={ "type": "auto", "last_messages": 1, @@ -639,19 +759,58 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, { "role": "user", "content": "x", - "file_ids": ["string"], + "attachments": [ + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + { + "file_id": "string", + "add_to": ["file_search", "code_interpreter"], + }, + ], "metadata": {}, }, ], @@ -664,6 +823,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn temperature=1, tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + top_p=1, truncation_strategy={ "type": "auto", "last_messages": 1, diff --git a/tests/api_resources/beta/assistants/__init__.py b/tests/api_resources/beta/vector_stores/__init__.py similarity index 100% rename from tests/api_resources/beta/assistants/__init__.py rename to tests/api_resources/beta/vector_stores/__init__.py diff --git a/tests/api_resources/beta/vector_stores/test_file_batches.py b/tests/api_resources/beta/vector_stores/test_file_batches.py new file mode 100644 index 0000000000..9854d1a138 --- /dev/null +++ b/tests/api_resources/beta/vector_stores/test_file_batches.py @@ -0,0 +1,424 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta.vector_stores import ( + VectorStoreFile, + VectorStoreFileBatch, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestFileBatches: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + file_batch = client.beta.vector_stores.file_batches.create( + "vs_abc123", + file_ids=["string"], + ) + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.vector_stores.file_batches.with_raw_response.create( + "vs_abc123", + file_ids=["string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.vector_stores.file_batches.with_streaming_response.create( + "vs_abc123", + file_ids=["string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.file_batches.with_raw_response.create( + "", + file_ids=["string"], + ) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + file_batch = client.beta.vector_stores.file_batches.retrieve( + "vsfb_abc123", + vector_store_id="vs_abc123", + ) + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.vector_stores.file_batches.with_raw_response.retrieve( + "vsfb_abc123", + vector_store_id="vs_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.vector_stores.file_batches.with_streaming_response.retrieve( + "vsfb_abc123", + vector_store_id="vs_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.file_batches.with_raw_response.retrieve( + "vsfb_abc123", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + client.beta.vector_stores.file_batches.with_raw_response.retrieve( + "", + vector_store_id="vs_abc123", + ) + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + file_batch = client.beta.vector_stores.file_batches.cancel( + "string", + vector_store_id="string", + ) + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + response = client.beta.vector_stores.file_batches.with_raw_response.cancel( + "string", + vector_store_id="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: OpenAI) -> None: + with client.beta.vector_stores.file_batches.with_streaming_response.cancel( + "string", + vector_store_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.file_batches.with_raw_response.cancel( + "string", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + client.beta.vector_stores.file_batches.with_raw_response.cancel( + "", + vector_store_id="string", + ) + + @parametrize + def test_method_list_files(self, client: OpenAI) -> None: + file_batch = client.beta.vector_stores.file_batches.list_files( + "string", + vector_store_id="string", + ) + assert_matches_type(SyncCursorPage[VectorStoreFile], file_batch, path=["response"]) + + @parametrize + def test_method_list_files_with_all_params(self, client: OpenAI) -> None: + file_batch = client.beta.vector_stores.file_batches.list_files( + "string", + vector_store_id="string", + after="string", + before="string", + filter="in_progress", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[VectorStoreFile], file_batch, path=["response"]) + + @parametrize + def test_raw_response_list_files(self, client: OpenAI) -> None: + response = client.beta.vector_stores.file_batches.with_raw_response.list_files( + "string", + vector_store_id="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file_batch = response.parse() + assert_matches_type(SyncCursorPage[VectorStoreFile], file_batch, path=["response"]) + + @parametrize + def test_streaming_response_list_files(self, client: OpenAI) -> None: + with client.beta.vector_stores.file_batches.with_streaming_response.list_files( + "string", + vector_store_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file_batch = response.parse() + assert_matches_type(SyncCursorPage[VectorStoreFile], file_batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list_files(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.file_batches.with_raw_response.list_files( + "string", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + client.beta.vector_stores.file_batches.with_raw_response.list_files( + "", + vector_store_id="string", + ) + + +class TestAsyncFileBatches: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + file_batch = await async_client.beta.vector_stores.file_batches.create( + "vs_abc123", + file_ids=["string"], + ) + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.file_batches.with_raw_response.create( + "vs_abc123", + file_ids=["string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.file_batches.with_streaming_response.create( + "vs_abc123", + file_ids=["string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file_batch = await response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.file_batches.with_raw_response.create( + "", + file_ids=["string"], + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + file_batch = await async_client.beta.vector_stores.file_batches.retrieve( + "vsfb_abc123", + vector_store_id="vs_abc123", + ) + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.file_batches.with_raw_response.retrieve( + "vsfb_abc123", + vector_store_id="vs_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.file_batches.with_streaming_response.retrieve( + "vsfb_abc123", + vector_store_id="vs_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file_batch = await response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.file_batches.with_raw_response.retrieve( + "vsfb_abc123", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + await async_client.beta.vector_stores.file_batches.with_raw_response.retrieve( + "", + vector_store_id="vs_abc123", + ) + + @parametrize + async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: + file_batch = await async_client.beta.vector_stores.file_batches.cancel( + "string", + vector_store_id="string", + ) + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.file_batches.with_raw_response.cancel( + "string", + vector_store_id="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file_batch = response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.file_batches.with_streaming_response.cancel( + "string", + vector_store_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file_batch = await response.parse() + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.file_batches.with_raw_response.cancel( + "string", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + await async_client.beta.vector_stores.file_batches.with_raw_response.cancel( + "", + vector_store_id="string", + ) + + @parametrize + async def test_method_list_files(self, async_client: AsyncOpenAI) -> None: + file_batch = await async_client.beta.vector_stores.file_batches.list_files( + "string", + vector_store_id="string", + ) + assert_matches_type(AsyncCursorPage[VectorStoreFile], file_batch, path=["response"]) + + @parametrize + async def test_method_list_files_with_all_params(self, async_client: AsyncOpenAI) -> None: + file_batch = await async_client.beta.vector_stores.file_batches.list_files( + "string", + vector_store_id="string", + after="string", + before="string", + filter="in_progress", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[VectorStoreFile], file_batch, path=["response"]) + + @parametrize + async def test_raw_response_list_files(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.vector_stores.file_batches.with_raw_response.list_files( + "string", + vector_store_id="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file_batch = response.parse() + assert_matches_type(AsyncCursorPage[VectorStoreFile], file_batch, path=["response"]) + + @parametrize + async def test_streaming_response_list_files(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.vector_stores.file_batches.with_streaming_response.list_files( + "string", + vector_store_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file_batch = await response.parse() + assert_matches_type(AsyncCursorPage[VectorStoreFile], file_batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list_files(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.file_batches.with_raw_response.list_files( + "string", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): + await async_client.beta.vector_stores.file_batches.with_raw_response.list_files( + "", + vector_store_id="string", + ) diff --git a/tests/api_resources/beta/assistants/test_files.py b/tests/api_resources/beta/vector_stores/test_files.py similarity index 59% rename from tests/api_resources/beta/assistants/test_files.py rename to tests/api_resources/beta/vector_stores/test_files.py index 50106234aa..58301e2d37 100644 --- a/tests/api_resources/beta/assistants/test_files.py +++ b/tests/api_resources/beta/vector_stores/test_files.py @@ -10,7 +10,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.assistants import AssistantFile, FileDeleteResponse +from openai.types.beta.vector_stores import ( + VectorStoreFile, + VectorStoreFileDeleted, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -20,189 +23,190 @@ class TestFiles: @parametrize def test_method_create(self, client: OpenAI) -> None: - file = client.beta.assistants.files.create( - "file-abc123", + file = client.beta.vector_stores.files.create( + "vs_abc123", file_id="string", ) - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) @parametrize def test_raw_response_create(self, client: OpenAI) -> None: - response = client.beta.assistants.files.with_raw_response.create( - "file-abc123", + response = client.beta.vector_stores.files.with_raw_response.create( + "vs_abc123", file_id="string", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: - with client.beta.assistants.files.with_streaming_response.create( - "file-abc123", + with client.beta.vector_stores.files.with_streaming_response.create( + "vs_abc123", file_id="string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_create(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - client.beta.assistants.files.with_raw_response.create( + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.files.with_raw_response.create( "", file_id="string", ) @parametrize def test_method_retrieve(self, client: OpenAI) -> None: - file = client.beta.assistants.files.retrieve( - "string", - assistant_id="string", + file = client.beta.vector_stores.files.retrieve( + "file-abc123", + vector_store_id="vs_abc123", ) - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: - response = client.beta.assistants.files.with_raw_response.retrieve( - "string", - assistant_id="string", + response = client.beta.vector_stores.files.with_raw_response.retrieve( + "file-abc123", + vector_store_id="vs_abc123", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: OpenAI) -> None: - with client.beta.assistants.files.with_streaming_response.retrieve( - "string", - assistant_id="string", + with client.beta.vector_stores.files.with_streaming_response.retrieve( + "file-abc123", + vector_store_id="vs_abc123", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - client.beta.assistants.files.with_raw_response.retrieve( - "string", - assistant_id="", + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.files.with_raw_response.retrieve( + "file-abc123", + vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - client.beta.assistants.files.with_raw_response.retrieve( + client.beta.vector_stores.files.with_raw_response.retrieve( "", - assistant_id="string", + vector_store_id="vs_abc123", ) @parametrize def test_method_list(self, client: OpenAI) -> None: - file = client.beta.assistants.files.list( + file = client.beta.vector_stores.files.list( "string", ) - assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: - file = client.beta.assistants.files.list( + file = client.beta.vector_stores.files.list( "string", after="string", before="string", + filter="in_progress", limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: - response = client.beta.assistants.files.with_raw_response.list( + response = client.beta.vector_stores.files.with_raw_response.list( "string", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: - with client.beta.assistants.files.with_streaming_response.list( + with client.beta.vector_stores.files.with_streaming_response.list( "string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"]) + assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_list(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - client.beta.assistants.files.with_raw_response.list( + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.files.with_raw_response.list( "", ) @parametrize def test_method_delete(self, client: OpenAI) -> None: - file = client.beta.assistants.files.delete( + file = client.beta.vector_stores.files.delete( "string", - assistant_id="string", + vector_store_id="string", ) - assert_matches_type(FileDeleteResponse, file, path=["response"]) + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: - response = client.beta.assistants.files.with_raw_response.delete( + response = client.beta.vector_stores.files.with_raw_response.delete( "string", - assistant_id="string", + vector_store_id="string", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(FileDeleteResponse, file, path=["response"]) + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) @parametrize def test_streaming_response_delete(self, client: OpenAI) -> None: - with client.beta.assistants.files.with_streaming_response.delete( + with client.beta.vector_stores.files.with_streaming_response.delete( "string", - assistant_id="string", + vector_store_id="string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(FileDeleteResponse, file, path=["response"]) + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - client.beta.assistants.files.with_raw_response.delete( + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.beta.vector_stores.files.with_raw_response.delete( "string", - assistant_id="", + vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - client.beta.assistants.files.with_raw_response.delete( + client.beta.vector_stores.files.with_raw_response.delete( "", - assistant_id="string", + vector_store_id="string", ) @@ -211,187 +215,188 @@ class TestAsyncFiles: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.assistants.files.create( - "file-abc123", + file = await async_client.beta.vector_stores.files.create( + "vs_abc123", file_id="string", ) - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.files.with_raw_response.create( - "file-abc123", + response = await async_client.beta.vector_stores.files.with_raw_response.create( + "vs_abc123", file_id="string", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.files.with_streaming_response.create( - "file-abc123", + async with async_client.beta.vector_stores.files.with_streaming_response.create( + "vs_abc123", file_id="string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await async_client.beta.assistants.files.with_raw_response.create( + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.files.with_raw_response.create( "", file_id="string", ) @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.assistants.files.retrieve( - "string", - assistant_id="string", + file = await async_client.beta.vector_stores.files.retrieve( + "file-abc123", + vector_store_id="vs_abc123", ) - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.files.with_raw_response.retrieve( - "string", - assistant_id="string", + response = await async_client.beta.vector_stores.files.with_raw_response.retrieve( + "file-abc123", + vector_store_id="vs_abc123", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.files.with_streaming_response.retrieve( - "string", - assistant_id="string", + async with async_client.beta.vector_stores.files.with_streaming_response.retrieve( + "file-abc123", + vector_store_id="vs_abc123", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() - assert_matches_type(AssistantFile, file, path=["response"]) + assert_matches_type(VectorStoreFile, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await async_client.beta.assistants.files.with_raw_response.retrieve( - "string", - assistant_id="", + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.files.with_raw_response.retrieve( + "file-abc123", + vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await async_client.beta.assistants.files.with_raw_response.retrieve( + await async_client.beta.vector_stores.files.with_raw_response.retrieve( "", - assistant_id="string", + vector_store_id="vs_abc123", ) @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.assistants.files.list( + file = await async_client.beta.vector_stores.files.list( "string", ) - assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.assistants.files.list( + file = await async_client.beta.vector_stores.files.list( "string", after="string", before="string", + filter="in_progress", limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.files.with_raw_response.list( + response = await async_client.beta.vector_stores.files.with_raw_response.list( "string", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.files.with_streaming_response.list( + async with async_client.beta.vector_stores.files.with_streaming_response.list( "string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() - assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"]) + assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await async_client.beta.assistants.files.with_raw_response.list( + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.files.with_raw_response.list( "", ) @parametrize async def test_method_delete(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.assistants.files.delete( + file = await async_client.beta.vector_stores.files.delete( "string", - assistant_id="string", + vector_store_id="string", ) - assert_matches_type(FileDeleteResponse, file, path=["response"]) + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.files.with_raw_response.delete( + response = await async_client.beta.vector_stores.files.with_raw_response.delete( "string", - assistant_id="string", + vector_store_id="string", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(FileDeleteResponse, file, path=["response"]) + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.files.with_streaming_response.delete( + async with async_client.beta.vector_stores.files.with_streaming_response.delete( "string", - assistant_id="string", + vector_store_id="string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() - assert_matches_type(FileDeleteResponse, file, path=["response"]) + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await async_client.beta.assistants.files.with_raw_response.delete( + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.beta.vector_stores.files.with_raw_response.delete( "string", - assistant_id="", + vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await async_client.beta.assistants.files.with_raw_response.delete( + await async_client.beta.vector_stores.files.with_raw_response.delete( "", - assistant_id="string", + vector_store_id="string", ) From ba3beaf3134ca390f1d9f6633b75f43ea6f4680b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:35:51 -0400 Subject: [PATCH 399/914] release: 1.21.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 69eb19a7b0..ba231b0760 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.20.0" + ".": "1.21.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a39d5faa30..4a3607f6a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.21.0 (2024-04-17) + +Full Changelog: [v1.20.0...v1.21.0](https://github.com/openai/openai-python/compare/v1.20.0...v1.21.0) + +### Features + +* **api:** add vector stores ([#1325](https://github.com/openai/openai-python/issues/1325)) ([038a3c5](https://github.com/openai/openai-python/commit/038a3c50db7b6a88f54ff1cd1ff6cbaef2caf87f)) + ## 1.20.0 (2024-04-16) Full Changelog: [v1.19.0...v1.20.0](https://github.com/openai/openai-python/compare/v1.19.0...v1.20.0) diff --git a/pyproject.toml b/pyproject.toml index 6c3ae2b592..978e82ae86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.20.0" +version = "1.21.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 32723952ed..6f0fc92f2e 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.20.0" # x-release-please-version +__version__ = "1.21.0" # x-release-please-version From bb95fe35ad4b480ebce57aa5b48df620df8d761b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:33:37 -0400 Subject: [PATCH 400/914] release: 1.21.1 (#1328) * chore(api): docs and response_format response property (#1327) * release: 1.21.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/beta/assistants.py | 8 +- .../resources/beta/threads/runs/runs.py | 78 +++++++++++++------ src/openai/resources/beta/threads/threads.py | 54 ++++++++++--- src/openai/types/beta/assistant.py | 36 +++++++++ .../types/beta/assistant_create_params.py | 2 +- .../types/beta/assistant_update_params.py | 2 +- .../beta/thread_create_and_run_params.py | 10 ++- src/openai/types/beta/threads/run.py | 8 +- .../types/beta/threads/run_create_params.py | 14 +++- 13 files changed, 173 insertions(+), 53 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ba231b0760..9e1cdeda32 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.21.0" + ".": "1.21.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a3607f6a2..20e8976936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.21.1 (2024-04-17) + +Full Changelog: [v1.21.0...v1.21.1](https://github.com/openai/openai-python/compare/v1.21.0...v1.21.1) + +### Chores + +* **api:** docs and response_format response property ([#1327](https://github.com/openai/openai-python/issues/1327)) ([7a6d142](https://github.com/openai/openai-python/commit/7a6d142f013994c4eb9a4f55888464c885f8baf0)) + ## 1.21.0 (2024-04-17) Full Changelog: [v1.20.0...v1.21.0](https://github.com/openai/openai-python/compare/v1.20.0...v1.21.0) diff --git a/pyproject.toml b/pyproject.toml index 978e82ae86..0ab25048b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.21.0" +version = "1.21.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6f0fc92f2e..4bb6604548 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.21.0" # x-release-please-version +__version__ = "1.21.1" # x-release-please-version diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 8695a949ca..c0338164e2 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -109,7 +109,7 @@ def create( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -253,7 +253,7 @@ def update( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -494,7 +494,7 @@ async def create( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -638,7 +638,7 @@ async def update( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 7aab17a30d..e2488316b5 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -145,13 +145,13 @@ def create( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -166,7 +166,7 @@ def create( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -190,7 +190,7 @@ def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -201,6 +201,11 @@ def create( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -287,13 +292,13 @@ def create( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -308,7 +313,7 @@ def create( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -328,7 +333,7 @@ def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -339,6 +344,11 @@ def create( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -425,13 +435,13 @@ def create( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -446,7 +456,7 @@ def create( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -466,7 +476,7 @@ def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -477,6 +487,11 @@ def create( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1659,13 +1674,13 @@ async def create( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -1680,7 +1695,7 @@ async def create( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1704,7 +1719,7 @@ async def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1715,6 +1730,11 @@ async def create( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1801,13 +1821,13 @@ async def create( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -1822,7 +1842,7 @@ async def create( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1842,7 +1862,7 @@ async def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1853,6 +1873,11 @@ async def create( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1939,13 +1964,13 @@ async def create( max_completion_tokens: The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. max_prompt_tokens: The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful @@ -1960,7 +1985,7 @@ async def create( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1980,7 +2005,7 @@ async def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1991,6 +2016,11 @@ async def create( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 678c621a10..6e54faf469 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -340,7 +340,7 @@ def create_and_run( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -366,7 +366,7 @@ def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -382,6 +382,11 @@ def create_and_run( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -481,7 +486,7 @@ def create_and_run( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -503,7 +508,7 @@ def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -519,6 +524,11 @@ def create_and_run( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -618,7 +628,7 @@ def create_and_run( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -640,7 +650,7 @@ def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -656,6 +666,11 @@ def create_and_run( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1296,7 +1311,7 @@ async def create_and_run( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1322,7 +1337,7 @@ async def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1338,6 +1353,11 @@ async def create_and_run( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1437,7 +1457,7 @@ async def create_and_run( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1459,7 +1479,7 @@ async def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1475,6 +1495,11 @@ async def create_and_run( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1574,7 +1599,7 @@ async def create_and_run( response_format: Specifies the format that the model must output. Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1596,7 +1621,7 @@ async def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1612,6 +1637,11 @@ async def create_and_run( model 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. + We generally recommend altering this or temperature but not both. + + truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to + control the intial context window of the run. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index fa09efb0cc..0b997e0b0e 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -5,6 +5,7 @@ from ..._models import BaseModel from .assistant_tool import AssistantTool +from .assistant_response_format_option import AssistantResponseFormatOption __all__ = ["Assistant", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] @@ -81,6 +82,32 @@ class Assistant(BaseModel): `code_interpreter`, `file_search`, or `function`. """ + response_format: Optional[AssistantResponseFormatOption] = None + """Specifies the format that the model must output. + + Compatible with + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to + produce JSON yourself via a system or user message. Without this, the model may + generate an unending stream of whitespace until the generation reaches the token + limit, resulting in a long-running and seemingly "stuck" request. Also note that + the message content may be partially cut off if `finish_reason="length"`, which + indicates the generation exceeded `max_tokens` or the conversation exceeded the + max context length. + """ + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. + """ + tool_resources: Optional[ToolResources] = None """A set of resources that are used by the assistant's tools. @@ -88,3 +115,12 @@ class Assistant(BaseModel): `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or temperature but not both. + """ diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 925b85050f..e9ff66dfc3 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -77,7 +77,7 @@ class AssistantCreateParams(TypedDict, total=False): Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 1354b078a8..55c846ce4e 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -47,7 +47,7 @@ class AssistantUpdateParams(TypedDict, total=False): Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index d7d5a758e8..0c102db705 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -106,7 +106,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -135,7 +135,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. """ @@ -159,9 +159,15 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): An alternative to sampling with temperature, called nucleus sampling, where the model 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. + + We generally recommend altering this or temperature but not both. """ truncation_strategy: Optional[TruncationStrategy] + """Controls for how a thread will be truncated prior to the run. + + Use this to control the intial context window of the run. + """ class ThreadMessageAttachment(TypedDict, total=False): diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 8f427ce6e8..4fd5103348 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -162,7 +162,7 @@ class Run(BaseModel): Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -197,7 +197,7 @@ class Run(BaseModel): Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. """ @@ -210,6 +210,10 @@ class Run(BaseModel): """ truncation_strategy: Optional[TruncationStrategy] = None + """Controls for how a thread will be truncated prior to the run. + + Use this to control the intial context window of the run. + """ usage: Optional[Usage] = None """Usage statistics related to the run. diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index fd0b4e7920..c1bb8ba62a 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -49,7 +49,7 @@ class RunCreateParamsBase(TypedDict, total=False): The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of - completion tokens specified, the run will end with status `complete`. See + completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. """ @@ -58,7 +58,7 @@ class RunCreateParamsBase(TypedDict, total=False): The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of - prompt tokens specified, the run will end with status `complete`. See + prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info. """ @@ -106,7 +106,7 @@ class RunCreateParamsBase(TypedDict, total=False): Compatible with [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -132,7 +132,7 @@ class RunCreateParamsBase(TypedDict, total=False): Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "TOOL_TYPE"}` or + Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. """ @@ -148,9 +148,15 @@ class RunCreateParamsBase(TypedDict, total=False): An alternative to sampling with temperature, called nucleus sampling, where the model 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. + + We generally recommend altering this or temperature but not both. """ truncation_strategy: Optional[TruncationStrategy] + """Controls for how a thread will be truncated prior to the run. + + Use this to control the intial context window of the run. + """ class AdditionalMessageAttachment(TypedDict, total=False): From b8f7ee516721cd060c6c19df942e10f2d4d3a580 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:43:20 -0400 Subject: [PATCH 401/914] chore(internal): add lru_cache helper function (#1329) --- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_utils.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 5697894192..31b5b22799 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -6,6 +6,7 @@ is_list as is_list, is_given as is_given, is_tuple as is_tuple, + lru_cache as lru_cache, is_mapping as is_mapping, is_tuple_t as is_tuple_t, parse_date as parse_date, diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 93c95517a9..5123a230f1 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -389,3 +389,11 @@ def get_async_library() -> str: 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(maxsize=maxsize) + return cast(Any, wrapper) # type: ignore[no-any-return] From a853ee20cf3ae3733407d2156b6107fd14a63d00 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:43:46 -0400 Subject: [PATCH 402/914] release: 1.21.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9e1cdeda32..e5c9603757 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.21.1" + ".": "1.21.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 20e8976936..889e26f7d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.21.2 (2024-04-17) + +Full Changelog: [v1.21.1...v1.21.2](https://github.com/openai/openai-python/compare/v1.21.1...v1.21.2) + +### Chores + +* **internal:** add lru_cache helper function ([#1329](https://github.com/openai/openai-python/issues/1329)) ([cbeebfc](https://github.com/openai/openai-python/commit/cbeebfcca8bf1a3feb4462a79e10099bda5bed84)) + ## 1.21.1 (2024-04-17) Full Changelog: [v1.21.0...v1.21.1](https://github.com/openai/openai-python/compare/v1.21.0...v1.21.1) diff --git a/pyproject.toml b/pyproject.toml index 0ab25048b1..b593179128 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.21.1" +version = "1.21.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 4bb6604548..df70bd1a2c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.21.1" # x-release-please-version +__version__ = "1.21.2" # x-release-please-version From 674c6eeb216e4544a940f94b362a926e3a9981cb Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:47:58 -0400 Subject: [PATCH 403/914] chore(internal): ban usage of lru_cache (#1331) --- pyproject.toml | 7 ++++++- src/openai/_base_client.py | 3 +-- src/openai/_models.py | 2 +- src/openai/_utils/_utils.py | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b593179128..dd6d2f10aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -167,7 +167,9 @@ select = [ "T201", "T203", # misuse of typing.TYPE_CHECKING - "TCH004" + "TCH004", + # import rules + "TID251", ] ignore = [ # mutable defaults @@ -183,6 +185,9 @@ ignore-init-module-imports = true [tool.ruff.format] docstring-code-format = true +[tool.ruff.lint.flake8-tidy-imports.banned-api] +"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" + [tool.ruff.lint.isort] length-sort = true length-sort-straight = true diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 0bb284a211..cd8361607e 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -29,7 +29,6 @@ cast, overload, ) -from functools import lru_cache from typing_extensions import Literal, override, get_origin import anyio @@ -61,7 +60,7 @@ RequestOptions, ModelBuilderProtocol, ) -from ._utils import is_dict, is_list, is_given, is_mapping +from ._utils import is_dict, is_list, is_given, lru_cache, is_mapping from ._compat import model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( diff --git a/src/openai/_models.py b/src/openai/_models.py index 80ab51256f..ff93fbd846 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -4,7 +4,6 @@ import inspect from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast from datetime import date, datetime -from functools import lru_cache from typing_extensions import ( Unpack, Literal, @@ -37,6 +36,7 @@ PropertyInfo, is_list, is_given, + lru_cache, is_mapping, parse_date, coerce_boolean, diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 5123a230f1..fd3a8a4d15 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -395,5 +395,7 @@ 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(maxsize=maxsize) + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) return cast(Any, wrapper) # type: ignore[no-any-return] From 7a7e4bcca772620bf6d39c4e20c253ad5af02c48 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:40:51 -0400 Subject: [PATCH 404/914] chore(internal): bump pyright to 1.1.359 (#1337) --- pyproject.toml | 2 +- requirements-dev.lock | 8 ++++---- src/openai/_models.py | 2 +- src/openai/_utils/_utils.py | 2 ++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index dd6d2f10aa..e0ab23a049 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ openai = "openai.cli:main" managed = true # version pins are in requirements-dev.lock dev-dependencies = [ - "pyright", + "pyright>=1.1.359", "mypy", "respx", "pytest", diff --git a/requirements-dev.lock b/requirements-dev.lock index 657e6cb810..8cfefdd93b 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -24,7 +24,7 @@ attrs==23.1.0 azure-core==1.30.1 # via azure-identity azure-identity==1.15.0 -black==24.3.0 +black==24.4.0 # via inline-snapshot certifi==2023.7.22 # via httpcore @@ -109,7 +109,7 @@ portalocker==2.8.2 # via msal-extensions py==1.11.0 # via pytest -pycparser==2.21 +pycparser==2.22 # via cffi pydantic==2.4.2 # via openai @@ -117,7 +117,7 @@ pydantic-core==2.10.1 # via pydantic pyjwt==2.8.0 # via msal -pyright==1.1.353 +pyright==1.1.359 pytest==7.1.1 # via pytest-asyncio pytest-asyncio==0.21.1 @@ -156,7 +156,7 @@ tqdm==4.66.1 # via openai trio==0.22.2 types-pyaudio==0.2.16.20240106 -types-pytz==2024.1.0.20240203 +types-pytz==2024.1.0.20240417 # via pandas-stubs types-toml==0.10.8.20240310 # via inline-snapshot diff --git a/src/openai/_models.py b/src/openai/_models.py index ff93fbd846..ff3f54e2cd 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -378,7 +378,7 @@ def construct_type(*, value: object, type_: object) -> object: # unwrap `Annotated[T, ...]` -> `T` if is_annotated_type(type_): - meta = get_args(type_)[1:] + meta: tuple[Any, ...] = get_args(type_)[1:] type_ = extract_type_arg(type_, 0) else: meta = tuple() diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index fd3a8a4d15..17904ce60d 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -265,6 +265,8 @@ def wrapper(*args: object, **kwargs: object) -> object: ) 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: From 2edeeb1faee1b4ca2b49cdfcb0e493d59d422244 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:49:14 -0400 Subject: [PATCH 405/914] feat(api): batch list endpoint (#1338) --- .stats.yml | 2 +- api.md | 1 + src/openai/resources/batches.py | 120 +++++++++++++++++++++++++- src/openai/types/__init__.py | 1 + src/openai/types/batch_list_params.py | 24 ++++++ tests/api_resources/test_batches.py | 67 ++++++++++++++ 6 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 src/openai/types/batch_list_params.py diff --git a/.stats.yml b/.stats.yml index 2814bb7778..c9a9bfa4a8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 62 +configured_endpoints: 63 diff --git a/api.md b/api.md index 962ed7b7c5..30247e8f7f 100644 --- a/api.md +++ b/api.md @@ -405,4 +405,5 @@ Methods: - client.batches.create(\*\*params) -> Batch - client.batches.retrieve(batch_id) -> Batch +- client.batches.list(\*\*params) -> SyncCursorPage[Batch] - client.batches.cancel(batch_id) -> Batch diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 0921ccb194..dc311b2e12 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -8,7 +8,7 @@ import httpx from .. import _legacy_response -from ..types import Batch, batch_create_params +from ..types import Batch, batch_list_params, batch_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import ( maybe_transform, @@ -17,7 +17,9 @@ from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..pagination import SyncCursorPage, AsyncCursorPage from .._base_client import ( + AsyncPaginator, make_request_options, ) @@ -125,6 +127,58 @@ def retrieve( cast_to=Batch, ) + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[Batch]: + """List your organization's batches. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/batches", + page=SyncCursorPage[Batch], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + batch_list_params.BatchListParams, + ), + ), + model=Batch, + ) + def cancel( self, batch_id: str, @@ -260,6 +314,58 @@ async def retrieve( cast_to=Batch, ) + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Batch, AsyncCursorPage[Batch]]: + """List your organization's batches. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/batches", + page=AsyncCursorPage[Batch], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + batch_list_params.BatchListParams, + ), + ), + model=Batch, + ) + async def cancel( self, batch_id: str, @@ -304,6 +410,9 @@ def __init__(self, batches: Batches) -> None: self.retrieve = _legacy_response.to_raw_response_wrapper( batches.retrieve, ) + self.list = _legacy_response.to_raw_response_wrapper( + batches.list, + ) self.cancel = _legacy_response.to_raw_response_wrapper( batches.cancel, ) @@ -319,6 +428,9 @@ def __init__(self, batches: AsyncBatches) -> None: self.retrieve = _legacy_response.async_to_raw_response_wrapper( batches.retrieve, ) + self.list = _legacy_response.async_to_raw_response_wrapper( + batches.list, + ) self.cancel = _legacy_response.async_to_raw_response_wrapper( batches.cancel, ) @@ -334,6 +446,9 @@ def __init__(self, batches: Batches) -> None: self.retrieve = to_streamed_response_wrapper( batches.retrieve, ) + self.list = to_streamed_response_wrapper( + batches.list, + ) self.cancel = to_streamed_response_wrapper( batches.cancel, ) @@ -349,6 +464,9 @@ def __init__(self, batches: AsyncBatches) -> None: self.retrieve = async_to_streamed_response_wrapper( batches.retrieve, ) + self.list = async_to_streamed_response_wrapper( + batches.list, + ) self.cancel = async_to_streamed_response_wrapper( batches.cancel, ) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index b6f35cfecf..7873efb34f 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -22,6 +22,7 @@ from .images_response import ImagesResponse as ImagesResponse from .completion_usage import CompletionUsage as CompletionUsage from .file_list_params import FileListParams as FileListParams +from .batch_list_params import BatchListParams as BatchListParams from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams from .file_create_params import FileCreateParams as FileCreateParams diff --git a/src/openai/types/batch_list_params.py b/src/openai/types/batch_list_params.py new file mode 100644 index 0000000000..ef5e966b79 --- /dev/null +++ b/src/openai/types/batch_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BatchListParams"] + + +class BatchListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py index aafeff8116..6f9b598e61 100644 --- a/tests/api_resources/test_batches.py +++ b/tests/api_resources/test_batches.py @@ -10,6 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import Batch +from openai.pagination import SyncCursorPage, AsyncCursorPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -102,6 +103,39 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: "", ) + @parametrize + def test_method_list(self, client: OpenAI) -> None: + batch = client.batches.list() + assert_matches_type(SyncCursorPage[Batch], batch, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + batch = client.batches.list( + after="string", + limit=0, + ) + assert_matches_type(SyncCursorPage[Batch], batch, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.batches.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + batch = response.parse() + assert_matches_type(SyncCursorPage[Batch], batch, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.batches.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + batch = response.parse() + assert_matches_type(SyncCursorPage[Batch], batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_cancel(self, client: OpenAI) -> None: batch = client.batches.cancel( @@ -229,6 +263,39 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: "", ) + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + batch = await async_client.batches.list() + assert_matches_type(AsyncCursorPage[Batch], batch, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + batch = await async_client.batches.list( + after="string", + limit=0, + ) + assert_matches_type(AsyncCursorPage[Batch], batch, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.batches.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + batch = response.parse() + assert_matches_type(AsyncCursorPage[Batch], batch, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.batches.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + batch = await response.parse() + assert_matches_type(AsyncCursorPage[Batch], batch, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: batch = await async_client.batches.cancel( From 2390c320f96f6f1f5f0abb73a3d6590a55629d28 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:49:42 -0400 Subject: [PATCH 406/914] release: 1.22.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e5c9603757..397c4203e3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.21.2" + ".": "1.22.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 889e26f7d3..ee52ac72e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.22.0 (2024-04-18) + +Full Changelog: [v1.21.2...v1.22.0](https://github.com/openai/openai-python/compare/v1.21.2...v1.22.0) + +### Features + +* **api:** batch list endpoint ([#1338](https://github.com/openai/openai-python/issues/1338)) ([a776f38](https://github.com/openai/openai-python/commit/a776f387e3159f9a8f4dcaa7d0d3b78c2a884f91)) + + +### Chores + +* **internal:** ban usage of lru_cache ([#1331](https://github.com/openai/openai-python/issues/1331)) ([8f9223b](https://github.com/openai/openai-python/commit/8f9223bfe13200c685fc97c25ada3015a69c6df7)) +* **internal:** bump pyright to 1.1.359 ([#1337](https://github.com/openai/openai-python/issues/1337)) ([feec0dd](https://github.com/openai/openai-python/commit/feec0dd1dd243941a279c3224c5ca1d727d76676)) + ## 1.21.2 (2024-04-17) Full Changelog: [v1.21.1...v1.21.2](https://github.com/openai/openai-python/compare/v1.21.1...v1.21.2) diff --git a/pyproject.toml b/pyproject.toml index e0ab23a049..17f4a86dc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.21.2" +version = "1.22.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index df70bd1a2c..6e11c61a18 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.21.2" # x-release-please-version +__version__ = "1.22.0" # x-release-please-version From 88323c4e3a47bbca498d3b55bada2b579142b410 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Apr 2024 17:18:27 -0400 Subject: [PATCH 407/914] docs(helpers): fix example snippets (#1339) --- helpers.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index cf738f3f16..3508b59a33 100644 --- a/helpers.md +++ b/helpers.md @@ -33,11 +33,13 @@ class EventHandler(AssistantEventHandler): def on_text_delta(self, delta: TextDelta, snapshot: Text): print(delta.value, end="", flush=True) + @override def on_tool_call_created(self, tool_call: ToolCall): print(f"\nassistant > {tool_call.type}\n", flush=True) + @override def on_tool_call_delta(self, delta: ToolCallDelta, snapshot: ToolCall): - if delta.type == 'code_interpreter': + if delta.type == "code_interpreter" and delta.code_interpreter: if delta.code_interpreter.input: print(delta.code_interpreter.input, end="", flush=True) if delta.code_interpreter.outputs: @@ -69,7 +71,7 @@ with client.beta.threads.runs.stream( ) as stream: for event in stream: # Print the text from text delta events - if event.type == "thread.message.delta" and event.data.delta.content: + if event.event == "thread.message.delta" and event.data.delta.content: print(event.data.delta.content[0].text) ``` From 0311d6b5f13682163d49c5981abf9305d11f80d6 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Apr 2024 17:28:03 -0400 Subject: [PATCH 408/914] feat(api): add request id property to response classes (#1341) --- examples/demo.py | 15 +++++++++++++++ src/openai/_legacy_response.py | 4 ++++ src/openai/_response.py | 8 ++++++++ 3 files changed, 27 insertions(+) diff --git a/examples/demo.py b/examples/demo.py index 37830e3e97..ac1710f3e0 100755 --- a/examples/demo.py +++ b/examples/demo.py @@ -36,3 +36,18 @@ print(chunk.choices[0].delta.content, end="") print() + +# Response headers: +print("----- custom response headers test -----") +response = client.chat.completions.with_raw_response.create( + model="gpt-4", + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], +) +completion = response.parse() +print(response.request_id) +print(completion.choices[0].message.content) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 4585cd7423..1de906b167 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -71,6 +71,10 @@ def __init__( self._options = options self.http_response = raw + @property + def request_id(self) -> str | None: + return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] + @overload def parse(self, *, to: type[_T]) -> _T: ... diff --git a/src/openai/_response.py b/src/openai/_response.py index 47f484ef7a..4ba2ae681c 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -258,6 +258,10 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: class APIResponse(BaseAPIResponse[R]): + @property + def request_id(self) -> str | None: + return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] + @overload def parse(self, *, to: type[_T]) -> _T: ... @@ -362,6 +366,10 @@ def iter_lines(self) -> Iterator[str]: class AsyncAPIResponse(BaseAPIResponse[R]): + @property + def request_id(self) -> str | None: + return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] + @overload async def parse(self, *, to: type[_T]) -> _T: ... From 9e0ea648e2059bc0e61c26664f0bc7f67fe7726b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Apr 2024 17:28:29 -0400 Subject: [PATCH 409/914] release: 1.23.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 397c4203e3..cdcf20eb76 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.22.0" + ".": "1.23.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ee52ac72e0..ef3e8ecada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.23.0 (2024-04-18) + +Full Changelog: [v1.22.0...v1.23.0](https://github.com/openai/openai-python/compare/v1.22.0...v1.23.0) + +### Features + +* **api:** add request id property to response classes ([#1341](https://github.com/openai/openai-python/issues/1341)) ([444d680](https://github.com/openai/openai-python/commit/444d680cbb3745adbc27788213ae3312567136a8)) + + +### Documentation + +* **helpers:** fix example snippets ([#1339](https://github.com/openai/openai-python/issues/1339)) ([8929088](https://github.com/openai/openai-python/commit/8929088b206a04b4c5b85fb69b0b983fb56f9b03)) + ## 1.22.0 (2024-04-18) Full Changelog: [v1.21.2...v1.22.0](https://github.com/openai/openai-python/compare/v1.21.2...v1.22.0) diff --git a/pyproject.toml b/pyproject.toml index 17f4a86dc9..6c8a890e71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.22.0" +version = "1.23.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6e11c61a18..3b31c9d5ea 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.22.0" # x-release-please-version +__version__ = "1.23.0" # x-release-please-version From abc8cd07ef4b4540ad87e65febce2a79871d5487 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Apr 2024 18:33:02 -0400 Subject: [PATCH 410/914] fix(api): correct types for attachments (#1342) --- .../beta/thread_create_and_run_params.py | 4 +- src/openai/types/beta/thread_create_params.py | 4 +- src/openai/types/beta/threads/message.py | 4 +- .../beta/threads/message_create_params.py | 4 +- .../types/beta/threads/run_create_params.py | 4 +- tests/api_resources/beta/test_threads.py | 108 +++++++++--------- .../beta/threads/test_messages.py | 12 +- tests/api_resources/beta/threads/test_runs.py | 72 ++++++------ 8 files changed, 106 insertions(+), 106 deletions(-) diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 0c102db705..036d8a78da 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -171,11 +171,11 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): class ThreadMessageAttachment(TypedDict, total=False): - add_to: List[Literal["file_search", "code_interpreter"]] - file_id: str """The ID of the file to attach to the message.""" + tools: List[Literal["file_search", "code_interpreter"]] + class ThreadMessage(TypedDict, total=False): content: Required[str] diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index 84a98a74d7..ac85e3c9e1 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -41,11 +41,11 @@ class ThreadCreateParams(TypedDict, total=False): class MessageAttachment(TypedDict, total=False): - add_to: List[Literal["file_search", "code_interpreter"]] - file_id: str """The ID of the file to attach to the message.""" + tools: List[Literal["file_search", "code_interpreter"]] + class Message(TypedDict, total=False): content: Required[str] diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index 42f0162734..ffc64545db 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -10,11 +10,11 @@ class Attachment(BaseModel): - add_to: Optional[List[Literal["file_search", "code_interpreter"]]] = None - file_id: Optional[str] = None """The ID of the file to attach to the message.""" + tools: Optional[List[Literal["file_search", "code_interpreter"]]] = None + class IncompleteDetails(BaseModel): reason: Literal["content_filter", "max_tokens", "run_cancelled", "run_expired", "run_failed"] diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index 1ef1d9ae10..4d47de84f1 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -34,7 +34,7 @@ class MessageCreateParams(TypedDict, total=False): class Attachment(TypedDict, total=False): - add_to: List[Literal["file_search", "code_interpreter"]] - file_id: str """The ID of the file to attach to the message.""" + + tools: List[Literal["file_search", "code_interpreter"]] diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index c1bb8ba62a..0d62b7949f 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -160,11 +160,11 @@ class RunCreateParamsBase(TypedDict, total=False): class AdditionalMessageAttachment(TypedDict, total=False): - add_to: List[Literal["file_search", "code_interpreter"]] - file_id: str """The ID of the file to attach to the message.""" + tools: List[Literal["file_search", "code_interpreter"]] + class AdditionalMessage(TypedDict, total=False): content: Required[str] diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 980fd9a75e..9b3de393f0 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -36,15 +36,15 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -55,15 +55,15 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -74,15 +74,15 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -277,15 +277,15 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -296,15 +296,15 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -315,15 +315,15 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -409,15 +409,15 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -428,15 +428,15 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -447,15 +447,15 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -533,15 +533,15 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -552,15 +552,15 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -571,15 +571,15 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -774,15 +774,15 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -793,15 +793,15 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -812,15 +812,15 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -906,15 +906,15 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -925,15 +925,15 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -944,15 +944,15 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index 5ea5ac3bd5..c6492464da 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -36,15 +36,15 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: attachments=[ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], metadata={}, @@ -265,15 +265,15 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> attachments=[ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], metadata={}, diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 3d8a6ce058..43065133d6 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -43,15 +43,15 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -62,15 +62,15 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -81,15 +81,15 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -170,15 +170,15 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -189,15 +189,15 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -208,15 +208,15 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -635,15 +635,15 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -654,15 +654,15 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -673,15 +673,15 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -762,15 +762,15 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -781,15 +781,15 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, @@ -800,15 +800,15 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "attachments": [ { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, { "file_id": "string", - "add_to": ["file_search", "code_interpreter"], + "tools": ["file_search", "code_interpreter"], }, ], "metadata": {}, From 10dde6d20312794818cd51d467d6f45d295e6601 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 18 Apr 2024 18:33:25 -0400 Subject: [PATCH 411/914] release: 1.23.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cdcf20eb76..276cb37a71 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.23.0" + ".": "1.23.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ef3e8ecada..3800012663 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.23.1 (2024-04-18) + +Full Changelog: [v1.23.0...v1.23.1](https://github.com/openai/openai-python/compare/v1.23.0...v1.23.1) + +### Bug Fixes + +* **api:** correct types for attachments ([#1342](https://github.com/openai/openai-python/issues/1342)) ([542d30c](https://github.com/openai/openai-python/commit/542d30c6dad4e139bf3eb443936d42b7b42dad54)) + ## 1.23.0 (2024-04-18) Full Changelog: [v1.22.0...v1.23.0](https://github.com/openai/openai-python/compare/v1.22.0...v1.23.0) diff --git a/pyproject.toml b/pyproject.toml index 6c8a890e71..674c120340 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.23.0" +version = "1.23.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 3b31c9d5ea..08c0d250a1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.23.0" # x-release-please-version +__version__ = "1.23.1" # x-release-please-version From f73996b7239246f9df38613c543b39c5178488f5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:43:01 -0400 Subject: [PATCH 412/914] fix(api): correct types for message attachment tools (#1348) --- .../beta/thread_create_and_run_params.py | 7 +- src/openai/types/beta/thread_create_params.py | 12 +- src/openai/types/beta/threads/message.py | 11 +- .../beta/threads/message_create_params.py | 13 +- .../types/beta/threads/run_create_params.py | 11 +- tests/api_resources/beta/test_threads.py | 324 +++++++++++++++--- .../beta/threads/test_messages.py | 12 +- tests/api_resources/beta/threads/test_runs.py | 216 ++++++++++-- 8 files changed, 499 insertions(+), 107 deletions(-) diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 036d8a78da..9adb049843 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -16,6 +16,7 @@ "Thread", "ThreadMessage", "ThreadMessageAttachment", + "ThreadMessageAttachmentTool", "ThreadToolResources", "ThreadToolResourcesCodeInterpreter", "ThreadToolResourcesFileSearch", @@ -170,11 +171,15 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): """ +ThreadMessageAttachmentTool = Union[CodeInterpreterToolParam, FileSearchToolParam] + + class ThreadMessageAttachment(TypedDict, total=False): file_id: str """The ID of the file to attach to the message.""" - tools: List[Literal["file_search", "code_interpreter"]] + tools: Iterable[ThreadMessageAttachmentTool] + """The tools to add this file to.""" class ThreadMessage(TypedDict, total=False): diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index ac85e3c9e1..ab2df21ed7 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -2,13 +2,17 @@ from __future__ import annotations -from typing import List, Iterable, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from .file_search_tool_param import FileSearchToolParam +from .code_interpreter_tool_param import CodeInterpreterToolParam + __all__ = [ "ThreadCreateParams", "Message", "MessageAttachment", + "MessageAttachmentTool", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", @@ -40,11 +44,15 @@ class ThreadCreateParams(TypedDict, total=False): """ +MessageAttachmentTool = Union[CodeInterpreterToolParam, FileSearchToolParam] + + class MessageAttachment(TypedDict, total=False): file_id: str """The ID of the file to attach to the message.""" - tools: List[Literal["file_search", "code_interpreter"]] + tools: Iterable[MessageAttachmentTool] + """The tools to add this file to.""" class Message(TypedDict, total=False): diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index ffc64545db..ebaabdb0f5 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -1,19 +1,24 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import List, Union, Optional from typing_extensions import Literal from ...._models import BaseModel from .message_content import MessageContent +from ..file_search_tool import FileSearchTool +from ..code_interpreter_tool import CodeInterpreterTool -__all__ = ["Message", "Attachment", "IncompleteDetails"] +__all__ = ["Message", "Attachment", "AttachmentTool", "IncompleteDetails"] + +AttachmentTool = Union[CodeInterpreterTool, FileSearchTool] class Attachment(BaseModel): file_id: Optional[str] = None """The ID of the file to attach to the message.""" - tools: Optional[List[Literal["file_search", "code_interpreter"]]] = None + tools: Optional[List[AttachmentTool]] = None + """The tools to add this file to.""" class IncompleteDetails(BaseModel): diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index 4d47de84f1..5cead598f0 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -2,10 +2,13 @@ from __future__ import annotations -from typing import List, Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["MessageCreateParams", "Attachment"] +from ..file_search_tool_param import FileSearchToolParam +from ..code_interpreter_tool_param import CodeInterpreterToolParam + +__all__ = ["MessageCreateParams", "Attachment", "AttachmentTool"] class MessageCreateParams(TypedDict, total=False): @@ -33,8 +36,12 @@ class MessageCreateParams(TypedDict, total=False): """ +AttachmentTool = Union[CodeInterpreterToolParam, FileSearchToolParam] + + class Attachment(TypedDict, total=False): file_id: str """The ID of the file to attach to the message.""" - tools: List[Literal["file_search", "code_interpreter"]] + tools: Iterable[AttachmentTool] + """The tools to add this file to.""" diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 0d62b7949f..f4780b7f09 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -2,10 +2,12 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from ..assistant_tool_param import AssistantToolParam +from ..file_search_tool_param import FileSearchToolParam +from ..code_interpreter_tool_param import CodeInterpreterToolParam from ..assistant_tool_choice_option_param import AssistantToolChoiceOptionParam from ..assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -13,6 +15,7 @@ "RunCreateParamsBase", "AdditionalMessage", "AdditionalMessageAttachment", + "AdditionalMessageAttachmentTool", "TruncationStrategy", "RunCreateParamsNonStreaming", "RunCreateParamsStreaming", @@ -159,11 +162,15 @@ class RunCreateParamsBase(TypedDict, total=False): """ +AdditionalMessageAttachmentTool = Union[CodeInterpreterToolParam, FileSearchToolParam] + + class AdditionalMessageAttachment(TypedDict, total=False): file_id: str """The ID of the file to attach to the message.""" - tools: List[Literal["file_search", "code_interpreter"]] + tools: Iterable[AdditionalMessageAttachmentTool] + """The tools to add this file to.""" class AdditionalMessage(TypedDict, total=False): diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 9b3de393f0..715e3e8726 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -36,15 +36,27 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -55,15 +67,27 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -74,15 +98,27 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -277,15 +313,27 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -296,15 +344,27 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -315,15 +375,27 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -409,15 +481,27 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -428,15 +512,27 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -447,15 +543,27 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -533,15 +641,27 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -552,15 +672,27 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -571,15 +703,27 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -774,15 +918,27 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -793,15 +949,27 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -812,15 +980,27 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -906,15 +1086,27 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -925,15 +1117,27 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -944,15 +1148,27 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index c6492464da..26eb09acdd 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -36,15 +36,15 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: attachments=[ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], }, ], metadata={}, @@ -265,15 +265,15 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> attachments=[ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], }, ], metadata={}, diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 43065133d6..429c9bdeeb 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -43,15 +43,27 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -62,15 +74,27 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -81,15 +105,27 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -170,15 +206,27 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -189,15 +237,27 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -208,15 +268,27 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -635,15 +707,27 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -654,15 +738,27 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -673,15 +769,27 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -762,15 +870,27 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -781,15 +901,27 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, @@ -800,15 +932,27 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "attachments": [ { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, { "file_id": "string", - "tools": ["file_search", "code_interpreter"], + "tools": [ + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + {"type": "code_interpreter"}, + ], }, ], "metadata": {}, From c117779c3ba1069e34ba2142578b15788d157342 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:43:29 -0400 Subject: [PATCH 413/914] release: 1.23.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 276cb37a71..d9381b3d10 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.23.1" + ".": "1.23.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3800012663..21d72ba863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.23.2 (2024-04-19) + +Full Changelog: [v1.23.1...v1.23.2](https://github.com/openai/openai-python/compare/v1.23.1...v1.23.2) + +### Bug Fixes + +* **api:** correct types for message attachment tools ([#1348](https://github.com/openai/openai-python/issues/1348)) ([78a6261](https://github.com/openai/openai-python/commit/78a6261eaad7839284903287d4f647d9cb4ced0b)) + ## 1.23.1 (2024-04-18) Full Changelog: [v1.23.0...v1.23.1](https://github.com/openai/openai-python/compare/v1.23.0...v1.23.1) diff --git a/pyproject.toml b/pyproject.toml index 674c120340..350ccec220 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.23.1" +version = "1.23.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 08c0d250a1..0ab9ce59a2 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.23.1" # x-release-please-version +__version__ = "1.23.2" # x-release-please-version From e4a9553eaedc4fb0ada3917e867d147d03740f60 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 23 Apr 2024 19:16:49 -0400 Subject: [PATCH 414/914] release: 1.23.3 (#1360) * chore(internal): restructure imports (#1359) * release: 1.23.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/audio/transcriptions.py | 3 ++- src/openai/resources/audio/translations.py | 3 ++- src/openai/resources/batches.py | 3 ++- src/openai/resources/beta/assistants.py | 8 ++++---- src/openai/resources/beta/threads/messages.py | 3 ++- src/openai/resources/beta/threads/runs/runs.py | 12 +++++------- src/openai/resources/beta/threads/runs/steps.py | 3 ++- src/openai/resources/beta/threads/threads.py | 12 ++++++------ .../resources/beta/vector_stores/file_batches.py | 9 +++------ src/openai/resources/beta/vector_stores/files.py | 4 +++- .../beta/vector_stores/vector_stores.py | 10 +++------- src/openai/resources/chat/completions.py | 16 +++++++--------- src/openai/resources/completions.py | 3 ++- src/openai/resources/embeddings.py | 3 ++- src/openai/resources/files.py | 4 +++- .../resources/fine_tuning/jobs/checkpoints.py | 3 ++- src/openai/resources/fine_tuning/jobs/jobs.py | 10 +++------- src/openai/resources/images.py | 8 ++------ src/openai/resources/models.py | 3 ++- src/openai/resources/moderations.py | 3 ++- src/openai/types/beta/assistant_stream_event.py | 9 ++++++--- src/openai/types/beta/function_tool.py | 2 +- tests/api_resources/audio/test_transcriptions.py | 2 +- tests/api_resources/audio/test_translations.py | 2 +- tests/api_resources/beta/test_assistants.py | 6 ++---- tests/api_resources/beta/test_threads.py | 8 +++----- tests/api_resources/beta/test_vector_stores.py | 6 ++---- .../beta/threads/runs/test_steps.py | 2 +- .../api_resources/beta/threads/test_messages.py | 2 +- tests/api_resources/beta/threads/test_runs.py | 4 +--- .../beta/vector_stores/test_file_batches.py | 6 ++---- .../beta/vector_stores/test_files.py | 6 ++---- tests/api_resources/chat/test_completions.py | 2 +- .../fine_tuning/jobs/test_checkpoints.py | 2 +- tests/api_resources/fine_tuning/test_jobs.py | 6 ++---- tests/api_resources/test_batches.py | 2 +- tests/api_resources/test_completions.py | 2 +- tests/api_resources/test_embeddings.py | 2 +- tests/api_resources/test_files.py | 3 ++- tests/api_resources/test_images.py | 2 +- tests/api_resources/test_models.py | 3 ++- tests/api_resources/test_moderations.py | 2 +- 46 files changed, 106 insertions(+), 112 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d9381b3d10..75baea2d17 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.23.2" + ".": "1.23.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 21d72ba863..eed20091bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.23.3 (2024-04-23) + +Full Changelog: [v1.23.2...v1.23.3](https://github.com/openai/openai-python/compare/v1.23.2...v1.23.3) + +### Chores + +* **internal:** restructure imports ([#1359](https://github.com/openai/openai-python/issues/1359)) ([4e5eb37](https://github.com/openai/openai-python/commit/4e5eb374ea0545a6117db657bb05f6417bc62d18)) + ## 1.23.2 (2024-04-19) Full Changelog: [v1.23.1...v1.23.2](https://github.com/openai/openai-python/compare/v1.23.1...v1.23.2) diff --git a/pyproject.toml b/pyproject.toml index 350ccec220..fbda5414f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.23.2" +version = "1.23.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0ab9ce59a2..ab45006b24 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.23.2" # x-release-please-version +__version__ = "1.23.3" # x-release-please-version diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 353f28ab05..995680186b 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -18,10 +18,11 @@ from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...types.audio import Transcription, transcription_create_params +from ...types.audio import transcription_create_params from ..._base_client import ( make_request_options, ) +from ...types.audio.transcription import Transcription __all__ = ["Transcriptions", "AsyncTranscriptions"] diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 79020a5ece..d711ee2fbd 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -18,10 +18,11 @@ from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...types.audio import Translation, translation_create_params +from ...types.audio import translation_create_params from ..._base_client import ( make_request_options, ) +from ...types.audio.translation import Translation __all__ = ["Translations", "AsyncTranslations"] diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index dc311b2e12..9b52958efc 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -8,7 +8,7 @@ import httpx from .. import _legacy_response -from ..types import Batch, batch_list_params, batch_create_params +from ..types import batch_list_params, batch_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import ( maybe_transform, @@ -18,6 +18,7 @@ from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncCursorPage, AsyncCursorPage +from ..types.batch import Batch from .._base_client import ( AsyncPaginator, make_request_options, diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index c0338164e2..923ad95a54 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -18,10 +18,6 @@ from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage from ...types.beta import ( - Assistant, - AssistantDeleted, - AssistantToolParam, - AssistantResponseFormatOptionParam, assistant_list_params, assistant_create_params, assistant_update_params, @@ -30,6 +26,10 @@ AsyncPaginator, make_request_options, ) +from ...types.beta.assistant import Assistant +from ...types.beta.assistant_deleted import AssistantDeleted +from ...types.beta.assistant_tool_param import AssistantToolParam +from ...types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = ["Assistants", "AsyncAssistants"] diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index 7a24b80dea..a938c5e15d 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -21,7 +21,8 @@ AsyncPaginator, make_request_options, ) -from ....types.beta.threads import Message, message_list_params, message_create_params, message_update_params +from ....types.beta.threads import message_list_params, message_create_params, message_update_params +from ....types.beta.threads.message import Message __all__ = ["Messages", "AsyncMessages"] diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index e2488316b5..e572a14a19 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -31,12 +31,6 @@ from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....._streaming import Stream, AsyncStream from .....pagination import SyncCursorPage, AsyncCursorPage -from .....types.beta import ( - AssistantToolParam, - AssistantStreamEvent, - AssistantToolChoiceOptionParam, - AssistantResponseFormatOptionParam, -) from ....._base_client import ( AsyncPaginator, make_request_options, @@ -50,12 +44,16 @@ AsyncAssistantStreamManager, ) from .....types.beta.threads import ( - Run, run_list_params, run_create_params, run_update_params, run_submit_tool_outputs_params, ) +from .....types.beta.threads.run import Run +from .....types.beta.assistant_tool_param import AssistantToolParam +from .....types.beta.assistant_stream_event import AssistantStreamEvent +from .....types.beta.assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from .....types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = ["Runs", "AsyncRuns"] diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 986ef2997a..512008939c 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -17,7 +17,8 @@ AsyncPaginator, make_request_options, ) -from .....types.beta.threads.runs import RunStep, step_list_params +from .....types.beta.threads.runs import step_list_params +from .....types.beta.threads.runs.run_step import RunStep __all__ = ["Steps", "AsyncSteps"] diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 6e54faf469..1c516bcea6 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -37,11 +37,6 @@ from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...._streaming import Stream, AsyncStream from ....types.beta import ( - Thread, - ThreadDeleted, - AssistantStreamEvent, - AssistantToolChoiceOptionParam, - AssistantResponseFormatOptionParam, thread_create_params, thread_update_params, thread_create_and_run_params, @@ -57,7 +52,12 @@ AsyncAssistantEventHandlerT, AsyncAssistantStreamManager, ) -from ....types.beta.threads import Run +from ....types.beta.thread import Thread +from ....types.beta.threads.run import Run +from ....types.beta.thread_deleted import ThreadDeleted +from ....types.beta.assistant_stream_event import AssistantStreamEvent +from ....types.beta.assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from ....types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = ["Threads", "AsyncThreads"] diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/beta/vector_stores/file_batches.py index 55b30b08e3..f1ced51700 100644 --- a/src/openai/resources/beta/vector_stores/file_batches.py +++ b/src/openai/resources/beta/vector_stores/file_batches.py @@ -26,12 +26,9 @@ AsyncPaginator, make_request_options, ) -from ....types.beta.vector_stores import ( - VectorStoreFile, - VectorStoreFileBatch, - file_batch_create_params, - file_batch_list_files_params, -) +from ....types.beta.vector_stores import file_batch_create_params, file_batch_list_files_params +from ....types.beta.vector_stores.vector_store_file import VectorStoreFile +from ....types.beta.vector_stores.vector_store_file_batch import VectorStoreFileBatch __all__ = ["FileBatches", "AsyncFileBatches"] diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/beta/vector_stores/files.py index 6404b9d54c..5c3db27619 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -22,7 +22,9 @@ AsyncPaginator, make_request_options, ) -from ....types.beta.vector_stores import VectorStoreFile, VectorStoreFileDeleted, file_list_params, file_create_params +from ....types.beta.vector_stores import file_list_params, file_create_params +from ....types.beta.vector_stores.vector_store_file import VectorStoreFile +from ....types.beta.vector_stores.vector_store_file_deleted import VectorStoreFileDeleted __all__ = ["Files", "AsyncFiles"] diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py index 6e2c9ab70c..8a177c2864 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -33,17 +33,13 @@ AsyncFileBatchesWithStreamingResponse, ) from ....pagination import SyncCursorPage, AsyncCursorPage -from ....types.beta import ( - VectorStore, - VectorStoreDeleted, - vector_store_list_params, - vector_store_create_params, - vector_store_update_params, -) +from ....types.beta import vector_store_list_params, vector_store_create_params, vector_store_update_params from ...._base_client import ( AsyncPaginator, make_request_options, ) +from ....types.beta.vector_store import VectorStore +from ....types.beta.vector_store_deleted import VectorStoreDeleted __all__ = ["VectorStores", "AsyncVectorStores"] diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 3b070b716e..2a6a0e7738 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -8,7 +8,6 @@ import httpx from ... import _legacy_response -from ...types import ChatModel from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import ( required_args, @@ -19,17 +18,16 @@ from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..._streaming import Stream, AsyncStream -from ...types.chat import ( - ChatCompletion, - ChatCompletionChunk, - ChatCompletionToolParam, - ChatCompletionMessageParam, - ChatCompletionToolChoiceOptionParam, - completion_create_params, -) +from ...types.chat import completion_create_params from ..._base_client import ( make_request_options, ) +from ...types.chat_model import ChatModel +from ...types.chat.chat_completion import ChatCompletion +from ...types.chat.chat_completion_chunk import ChatCompletionChunk +from ...types.chat.chat_completion_tool_param import ChatCompletionToolParam +from ...types.chat.chat_completion_message_param import ChatCompletionMessageParam +from ...types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam __all__ = ["Completions", "AsyncCompletions"] diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index db87c83ca2..eb6ca31048 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -8,7 +8,7 @@ import httpx from .. import _legacy_response -from ..types import Completion, completion_create_params +from ..types import completion_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import ( required_args, @@ -22,6 +22,7 @@ from .._base_client import ( make_request_options, ) +from ..types.completion import Completion __all__ = ["Completions", "AsyncCompletions"] diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index a083b6269a..773b6f0968 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -9,7 +9,7 @@ import httpx from .. import _legacy_response -from ..types import CreateEmbeddingResponse, embedding_create_params +from ..types import embedding_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import is_given, maybe_transform from .._compat import cached_property @@ -19,6 +19,7 @@ from .._base_client import ( make_request_options, ) +from ..types.create_embedding_response import CreateEmbeddingResponse __all__ = ["Embeddings", "AsyncEmbeddings"] diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 33860adad5..fa03a9c0e2 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -10,7 +10,7 @@ import httpx from .. import _legacy_response -from ..types import FileObject, FileDeleted, file_list_params, file_create_params +from ..types import file_list_params, file_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import ( extract_files, @@ -33,6 +33,8 @@ AsyncPaginator, make_request_options, ) +from ..types.file_object import FileObject +from ..types.file_deleted import FileDeleted __all__ = ["Files", "AsyncFiles"] diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py index e9ea6aad9a..67f5739a02 100644 --- a/src/openai/resources/fine_tuning/jobs/checkpoints.py +++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py @@ -15,7 +15,8 @@ AsyncPaginator, make_request_options, ) -from ....types.fine_tuning.jobs import FineTuningJobCheckpoint, checkpoint_list_params +from ....types.fine_tuning.jobs import checkpoint_list_params +from ....types.fine_tuning.jobs.fine_tuning_job_checkpoint import FineTuningJobCheckpoint __all__ = ["Checkpoints", "AsyncCheckpoints"] diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index 8e49571b14..f38956e6be 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -29,13 +29,9 @@ AsyncPaginator, make_request_options, ) -from ....types.fine_tuning import ( - FineTuningJob, - FineTuningJobEvent, - job_list_params, - job_create_params, - job_list_events_params, -) +from ....types.fine_tuning import job_list_params, job_create_params, job_list_events_params +from ....types.fine_tuning.fine_tuning_job import FineTuningJob +from ....types.fine_tuning.fine_tuning_job_event import FineTuningJobEvent __all__ = ["Jobs", "AsyncJobs"] diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index e12fa51bd9..74b2a46a3f 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -8,12 +8,7 @@ import httpx from .. import _legacy_response -from ..types import ( - ImagesResponse, - image_edit_params, - image_generate_params, - image_create_variation_params, -) +from ..types import image_edit_params, image_generate_params, image_create_variation_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import ( extract_files, @@ -27,6 +22,7 @@ from .._base_client import ( make_request_options, ) +from ..types.images_response import ImagesResponse __all__ = ["Images", "AsyncImages"] diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index 4e36e20801..e76c496ffa 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -5,16 +5,17 @@ import httpx from .. import _legacy_response -from ..types import Model, ModelDeleted from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncPage, AsyncPage +from ..types.model import Model from .._base_client import ( AsyncPaginator, make_request_options, ) +from ..types.model_deleted import ModelDeleted __all__ = ["Models", "AsyncModels"] diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 385b672f28..9386e50dae 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -8,7 +8,7 @@ import httpx from .. import _legacy_response -from ..types import ModerationCreateResponse, moderation_create_params +from ..types import moderation_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import ( maybe_transform, @@ -20,6 +20,7 @@ from .._base_client import ( make_request_options, ) +from ..types.moderation_create_response import ModerationCreateResponse __all__ = ["Moderations", "AsyncModerations"] diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py index 90471f7daa..91925e93b3 100644 --- a/src/openai/types/beta/assistant_stream_event.py +++ b/src/openai/types/beta/assistant_stream_event.py @@ -4,11 +4,14 @@ from typing_extensions import Literal, Annotated from .thread import Thread -from ..shared import ErrorObject -from .threads import Run, Message, MessageDeltaEvent from ..._utils import PropertyInfo from ..._models import BaseModel -from .threads.runs import RunStep, RunStepDeltaEvent +from .threads.run import Run +from .threads.message import Message +from ..shared.error_object import ErrorObject +from .threads.runs.run_step import RunStep +from .threads.message_delta_event import MessageDeltaEvent +from .threads.runs.run_step_delta_event import RunStepDeltaEvent __all__ = [ "AssistantStreamEvent", diff --git a/src/openai/types/beta/function_tool.py b/src/openai/types/beta/function_tool.py index 5d278e7487..f9227678df 100644 --- a/src/openai/types/beta/function_tool.py +++ b/src/openai/types/beta/function_tool.py @@ -2,8 +2,8 @@ from typing_extensions import Literal -from ..shared import FunctionDefinition from ..._models import BaseModel +from ..shared.function_definition import FunctionDefinition __all__ = ["FunctionTool"] diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index ba8e9e4099..0c59cea09f 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.audio import Transcription +from openai.types.audio.transcription import Transcription base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index f5c6c68f0b..5463fcff63 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.audio import Translation +from openai.types.audio.translation import Translation base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index a92acb2ca5..428fe41e93 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -10,10 +10,8 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta import ( - Assistant, - AssistantDeleted, -) +from openai.types.beta.assistant import Assistant +from openai.types.beta.assistant_deleted import AssistantDeleted base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 715e3e8726..29a0a8d91c 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -9,11 +9,9 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.beta import ( - Thread, - ThreadDeleted, -) -from openai.types.beta.threads import Run +from openai.types.beta.thread import Thread +from openai.types.beta.threads.run import Run +from openai.types.beta.thread_deleted import ThreadDeleted base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/test_vector_stores.py b/tests/api_resources/beta/test_vector_stores.py index e671c96a45..742b5c0ed4 100644 --- a/tests/api_resources/beta/test_vector_stores.py +++ b/tests/api_resources/beta/test_vector_stores.py @@ -10,10 +10,8 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta import ( - VectorStore, - VectorStoreDeleted, -) +from openai.types.beta.vector_store import VectorStore +from openai.types.beta.vector_store_deleted import VectorStoreDeleted base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py index e6108d8dad..3b40b36e37 100644 --- a/tests/api_resources/beta/threads/runs/test_steps.py +++ b/tests/api_resources/beta/threads/runs/test_steps.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads.runs import RunStep +from openai.types.beta.threads.runs.run_step import RunStep base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index 26eb09acdd..f06db4fce5 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads import Message +from openai.types.beta.threads.message import Message base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 429c9bdeeb..f8dbdbf449 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -10,9 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads import ( - Run, -) +from openai.types.beta.threads.run import Run # pyright: reportDeprecated=false diff --git a/tests/api_resources/beta/vector_stores/test_file_batches.py b/tests/api_resources/beta/vector_stores/test_file_batches.py index 9854d1a138..7e9b2e85de 100644 --- a/tests/api_resources/beta/vector_stores/test_file_batches.py +++ b/tests/api_resources/beta/vector_stores/test_file_batches.py @@ -10,10 +10,8 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.vector_stores import ( - VectorStoreFile, - VectorStoreFileBatch, -) +from openai.types.beta.vector_stores.vector_store_file import VectorStoreFile +from openai.types.beta.vector_stores.vector_store_file_batch import VectorStoreFileBatch base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/vector_stores/test_files.py b/tests/api_resources/beta/vector_stores/test_files.py index 58301e2d37..09f5c259bd 100644 --- a/tests/api_resources/beta/vector_stores/test_files.py +++ b/tests/api_resources/beta/vector_stores/test_files.py @@ -10,10 +10,8 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.vector_stores import ( - VectorStoreFile, - VectorStoreFileDeleted, -) +from openai.types.beta.vector_stores.vector_store_file import VectorStoreFile +from openai.types.beta.vector_stores.vector_store_file_deleted import VectorStoreFileDeleted base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index c54b56a37d..ddba1ca085 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.chat import ChatCompletion +from openai.types.chat.chat_completion import ChatCompletion base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py index 915d5c6f63..6ebf2225ae 100644 --- a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py +++ b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.fine_tuning.jobs import FineTuningJobCheckpoint +from openai.types.fine_tuning.jobs.fine_tuning_job_checkpoint import FineTuningJobCheckpoint base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 1ff6d63b31..29a96feb2d 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -10,10 +10,8 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.fine_tuning import ( - FineTuningJob, - FineTuningJobEvent, -) +from openai.types.fine_tuning.fine_tuning_job import FineTuningJob +from openai.types.fine_tuning.fine_tuning_job_event import FineTuningJobEvent base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py index 6f9b598e61..7967634128 100644 --- a/tests/api_resources/test_batches.py +++ b/tests/api_resources/test_batches.py @@ -9,8 +9,8 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types import Batch from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.batch import Batch base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 691c4ff77f..249744b531 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types import Completion +from openai.types.completion import Completion base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py index e75545b4e2..9c4e55a5a8 100644 --- a/tests/api_resources/test_embeddings.py +++ b/tests/api_resources/test_embeddings.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types import CreateEmbeddingResponse +from openai.types.create_embedding_response import CreateEmbeddingResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index e5466e9eda..3b6817e27b 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -12,8 +12,9 @@ import openai._legacy_response as _legacy_response from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types import FileObject, FileDeleted from openai.pagination import SyncPage, AsyncPage +from openai.types.file_object import FileObject +from openai.types.file_deleted import FileDeleted # pyright: reportDeprecated=false diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 2e31f3354a..8d857d2f45 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types import ImagesResponse +from openai.types.images_response import ImagesResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py index 71f8e5834b..2351d64a33 100644 --- a/tests/api_resources/test_models.py +++ b/tests/api_resources/test_models.py @@ -9,8 +9,9 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types import Model, ModelDeleted from openai.pagination import SyncPage, AsyncPage +from openai.types.model import Model +from openai.types.model_deleted import ModelDeleted base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index 94b9ecd31b..52436ad0a9 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types import ModerationCreateResponse +from openai.types.moderation_create_response import ModerationCreateResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") From 7931ebaab06afaf42ed51bf6d6b17397c8ddcfd0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:58:54 -0400 Subject: [PATCH 415/914] fix(docs): doc improvements (#1364) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84d9017e45..b8a0d9b0cb 100644 --- a/README.md +++ b/README.md @@ -521,7 +521,7 @@ The context manager is required so that the response will reliably be closed. ### Making custom/undocumented requests -This library is typed for convenient access the documented API. +This library is typed for convenient access to the documented API. If you need to access undocumented endpoints, params, or response properties, the library can still be used. From 9a1f013120ac9359602286d2547852a8fe60eaff Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:24:04 -0400 Subject: [PATCH 416/914] chore(tests): rename test file (#1366) --- tests/api_resources/beta/chat/__init__.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tests/api_resources/beta/chat/__init__.py diff --git a/tests/api_resources/beta/chat/__init__.py b/tests/api_resources/beta/chat/__init__.py deleted file mode 100644 index fd8019a9a1..0000000000 --- a/tests/api_resources/beta/chat/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. From 55f07f31c9d0cb2886ea37a9d8ed3379b46890e3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:33:44 -0400 Subject: [PATCH 417/914] fix(api): change timestamps to unix integers (#1367) --- src/openai/resources/batches.py | 10 ++++++---- src/openai/types/batch.py | 18 +++++++++--------- src/openai/types/batch_create_params.py | 5 +++-- src/openai/types/beta/vector_store.py | 6 +++--- .../beta/vector_stores/vector_store_file.py | 6 ++++++ 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 9b52958efc..a2a0272a7d 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -65,8 +65,9 @@ def create( See [upload file](https://platform.openai.com/docs/api-reference/files/create) for how to upload a file. - Your input file must be formatted as a JSONL file, and must be uploaded with the - purpose `batch`. + Your input file must be formatted as a + [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), + and must be uploaded with the purpose `batch`. metadata: Optional custom metadata for the batch. @@ -252,8 +253,9 @@ async def create( See [upload file](https://platform.openai.com/docs/api-reference/files/create) for how to upload a file. - Your input file must be formatted as a JSONL file, and must be uploaded with the - purpose `batch`. + Your input file must be formatted as a + [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), + and must be uploaded with the purpose `batch`. metadata: Optional custom metadata for the batch. diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py index bde04d1a24..90f6d79572 100644 --- a/src/openai/types/batch.py +++ b/src/openai/types/batch.py @@ -24,7 +24,7 @@ class Batch(BaseModel): completion_window: str """The time frame within which the batch should be processed.""" - created_at: str + created_at: int """The Unix timestamp (in seconds) for when the batch was created.""" endpoint: str @@ -41,13 +41,13 @@ class Batch(BaseModel): ] """The current status of the batch.""" - cancelled_at: Optional[str] = None + cancelled_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch was cancelled.""" - cancelling_at: Optional[str] = None + cancelling_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch started cancelling.""" - completed_at: Optional[str] = None + completed_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch was completed.""" error_file_id: Optional[str] = None @@ -55,19 +55,19 @@ class Batch(BaseModel): errors: Optional[Errors] = None - expired_at: Optional[str] = None + expired_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch expired.""" - expires_at: Optional[str] = None + expires_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch will expire.""" - failed_at: Optional[str] = None + failed_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch failed.""" - finalizing_at: Optional[str] = None + finalizing_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch started finalizing.""" - in_progress_at: Optional[str] = None + in_progress_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch started processing.""" metadata: Optional[builtins.object] = None diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index 6a22be8626..a67aaa1e5e 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -27,8 +27,9 @@ class BatchCreateParams(TypedDict, total=False): See [upload file](https://platform.openai.com/docs/api-reference/files/create) for how to upload a file. - Your input file must be formatted as a JSONL file, and must be uploaded with the - purpose `batch`. + Your input file must be formatted as a + [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), + and must be uploaded with the purpose `batch`. """ metadata: Optional[Dict[str, str]] diff --git a/src/openai/types/beta/vector_store.py b/src/openai/types/beta/vector_store.py index 122705734d..488961b444 100644 --- a/src/openai/types/beta/vector_store.py +++ b/src/openai/types/beta/vector_store.py @@ -40,9 +40,6 @@ class VectorStore(BaseModel): id: str """The identifier, which can be referenced in API endpoints.""" - bytes: int - """The byte size of the vector store.""" - created_at: int """The Unix timestamp (in seconds) for when the vector store was created.""" @@ -72,6 +69,9 @@ class VectorStore(BaseModel): for use. """ + usage_bytes: int + """The total number of bytes used by the files in the vector store.""" + expires_after: Optional[ExpiresAfter] = None """The expiration policy for a vector store.""" diff --git a/src/openai/types/beta/vector_stores/vector_store_file.py b/src/openai/types/beta/vector_stores/vector_store_file.py index a878b281d5..3fab489602 100644 --- a/src/openai/types/beta/vector_stores/vector_store_file.py +++ b/src/openai/types/beta/vector_stores/vector_store_file.py @@ -39,6 +39,12 @@ class VectorStoreFile(BaseModel): vector store file is ready for use. """ + usage_bytes: int + """The total vector store usage in bytes. + + Note that this may be different from the original file size. + """ + vector_store_id: str """ The ID of the From 19410c6e7ebf9de243c5040681896bdc0277ad07 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:34:10 -0400 Subject: [PATCH 418/914] release: 1.23.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 75baea2d17..9fbf60ba41 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.23.3" + ".": "1.23.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index eed20091bf..48ab946491 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.23.4 (2024-04-24) + +Full Changelog: [v1.23.3...v1.23.4](https://github.com/openai/openai-python/compare/v1.23.3...v1.23.4) + +### Bug Fixes + +* **api:** change timestamps to unix integers ([#1367](https://github.com/openai/openai-python/issues/1367)) ([fbc0e15](https://github.com/openai/openai-python/commit/fbc0e15f422971bd15499d4ea5f42a1c885c7004)) +* **docs:** doc improvements ([#1364](https://github.com/openai/openai-python/issues/1364)) ([8c3a005](https://github.com/openai/openai-python/commit/8c3a005247ea045b9a95e7459eba2a90067daf71)) + + +### Chores + +* **tests:** rename test file ([#1366](https://github.com/openai/openai-python/issues/1366)) ([4204e63](https://github.com/openai/openai-python/commit/4204e63e27584c68ad27825261225603d7a87008)) + ## 1.23.3 (2024-04-23) Full Changelog: [v1.23.2...v1.23.3](https://github.com/openai/openai-python/compare/v1.23.2...v1.23.3) diff --git a/pyproject.toml b/pyproject.toml index fbda5414f6..9cde315623 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.23.3" +version = "1.23.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ab45006b24..943be8e435 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.23.3" # x-release-please-version +__version__ = "1.23.4" # x-release-please-version From 290e7ada3a6f7b4bf1fbbbe285086013853e9418 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 24 Apr 2024 20:25:51 -0400 Subject: [PATCH 419/914] release: 1.23.5 (#1369) * chore(internal): use actions/checkout@v4 for codeflow (#1368) * release: 1.23.5 --- .github/workflows/create-releases.yml | 2 +- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 9e76fcc471..a641be287b 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -14,7 +14,7 @@ jobs: environment: publish steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: stainless-api/trigger-release-please@v1 id: release diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index f779a19ac1..2f88f86407 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rye run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 108aa5973a..e078964a6f 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -13,7 +13,7 @@ jobs: if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check release environment run: | diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9fbf60ba41..dd9e0bd0b2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.23.4" + ".": "1.23.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 48ab946491..9ac6cd139d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.23.5 (2024-04-24) + +Full Changelog: [v1.23.4...v1.23.5](https://github.com/openai/openai-python/compare/v1.23.4...v1.23.5) + +### Chores + +* **internal:** use actions/checkout@v4 for codeflow ([#1368](https://github.com/openai/openai-python/issues/1368)) ([d1edf8b](https://github.com/openai/openai-python/commit/d1edf8beb806ebaefdcc2cb6e39f99e1811a2668)) + ## 1.23.4 (2024-04-24) Full Changelog: [v1.23.3...v1.23.4](https://github.com/openai/openai-python/compare/v1.23.3...v1.23.4) diff --git a/pyproject.toml b/pyproject.toml index 9cde315623..18949f6652 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.23.4" +version = "1.23.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 943be8e435..460a2542b1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.23.4" # x-release-please-version +__version__ = "1.23.5" # x-release-please-version From e9724398d2bdd87ba41f199c3577303f1b80f2c7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 25 Apr 2024 00:19:39 -0400 Subject: [PATCH 420/914] release: 1.23.6 (#1372) * chore(internal): update test helper function (#1371) * release: 1.23.6 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- tests/utils.py | 17 ++++++++++++++++- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index dd9e0bd0b2..89bd0ec2f7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.23.5" + ".": "1.23.6" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ac6cd139d..a25cd549b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.23.6 (2024-04-25) + +Full Changelog: [v1.23.5...v1.23.6](https://github.com/openai/openai-python/compare/v1.23.5...v1.23.6) + +### Chores + +* **internal:** update test helper function ([#1371](https://github.com/openai/openai-python/issues/1371)) ([6607c4a](https://github.com/openai/openai-python/commit/6607c4a491fd1912f9222d6fe464ccef6e865eac)) + ## 1.23.5 (2024-04-24) Full Changelog: [v1.23.4...v1.23.5](https://github.com/openai/openai-python/compare/v1.23.4...v1.23.5) diff --git a/pyproject.toml b/pyproject.toml index 18949f6652..d82615a4ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.23.5" +version = "1.23.6" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 460a2542b1..7ca1a0a78b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.23.5" # x-release-please-version +__version__ = "1.23.6" # x-release-please-version diff --git a/tests/utils.py b/tests/utils.py index 43c3cb5cfe..060b99339f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -97,7 +97,22 @@ def assert_matches_type( assert_matches_type(key_type, key, path=[*path, ""]) assert_matches_type(items_type, item, path=[*path, ""]) elif is_union_type(type_): - for i, variant in enumerate(get_args(type_)): + variants = get_args(type_) + + try: + none_index = variants.index(type(None)) + except ValueError: + pass + else: + # special case Optional[T] for better error messages + if len(variants) == 2: + if value is None: + # valid + return + + return assert_matches_type(type_=variants[not none_index], value=value, path=path) + + for i, variant in enumerate(variants): try: assert_matches_type(variant, value, path=[*path, f"variant {i}"]) return From 4a0f0fa053904d1a9b1ee4e268e59c7799f9c0ac Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 26 Apr 2024 09:48:35 -0400 Subject: [PATCH 421/914] chore(internal): reformat imports (#1375) --- tests/api_resources/audio/test_transcriptions.py | 2 +- tests/api_resources/audio/test_translations.py | 2 +- tests/api_resources/beta/test_assistants.py | 6 ++++-- tests/api_resources/beta/test_threads.py | 8 +++++--- tests/api_resources/beta/test_vector_stores.py | 6 ++++-- tests/api_resources/beta/threads/runs/test_steps.py | 2 +- tests/api_resources/beta/threads/test_messages.py | 2 +- tests/api_resources/beta/threads/test_runs.py | 4 +++- .../api_resources/beta/vector_stores/test_file_batches.py | 6 ++++-- tests/api_resources/beta/vector_stores/test_files.py | 6 ++++-- tests/api_resources/chat/test_completions.py | 2 +- tests/api_resources/fine_tuning/jobs/test_checkpoints.py | 2 +- tests/api_resources/fine_tuning/test_jobs.py | 6 ++++-- tests/api_resources/test_batches.py | 2 +- tests/api_resources/test_completions.py | 2 +- tests/api_resources/test_embeddings.py | 2 +- tests/api_resources/test_files.py | 3 +-- tests/api_resources/test_images.py | 2 +- tests/api_resources/test_models.py | 3 +-- tests/api_resources/test_moderations.py | 2 +- 20 files changed, 41 insertions(+), 29 deletions(-) diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index 0c59cea09f..ba8e9e4099 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.audio.transcription import Transcription +from openai.types.audio import Transcription base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index 5463fcff63..f5c6c68f0b 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.audio.translation import Translation +from openai.types.audio import Translation base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 428fe41e93..a92acb2ca5 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -10,8 +10,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.assistant import Assistant -from openai.types.beta.assistant_deleted import AssistantDeleted +from openai.types.beta import ( + Assistant, + AssistantDeleted, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 29a0a8d91c..715e3e8726 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -9,9 +9,11 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.beta.thread import Thread -from openai.types.beta.threads.run import Run -from openai.types.beta.thread_deleted import ThreadDeleted +from openai.types.beta import ( + Thread, + ThreadDeleted, +) +from openai.types.beta.threads import Run base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/test_vector_stores.py b/tests/api_resources/beta/test_vector_stores.py index 742b5c0ed4..e671c96a45 100644 --- a/tests/api_resources/beta/test_vector_stores.py +++ b/tests/api_resources/beta/test_vector_stores.py @@ -10,8 +10,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.vector_store import VectorStore -from openai.types.beta.vector_store_deleted import VectorStoreDeleted +from openai.types.beta import ( + VectorStore, + VectorStoreDeleted, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py index 3b40b36e37..e6108d8dad 100644 --- a/tests/api_resources/beta/threads/runs/test_steps.py +++ b/tests/api_resources/beta/threads/runs/test_steps.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads.runs.run_step import RunStep +from openai.types.beta.threads.runs import RunStep base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index f06db4fce5..26eb09acdd 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads.message import Message +from openai.types.beta.threads import Message base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index f8dbdbf449..429c9bdeeb 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -10,7 +10,9 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads.run import Run +from openai.types.beta.threads import ( + Run, +) # pyright: reportDeprecated=false diff --git a/tests/api_resources/beta/vector_stores/test_file_batches.py b/tests/api_resources/beta/vector_stores/test_file_batches.py index 7e9b2e85de..9854d1a138 100644 --- a/tests/api_resources/beta/vector_stores/test_file_batches.py +++ b/tests/api_resources/beta/vector_stores/test_file_batches.py @@ -10,8 +10,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.vector_stores.vector_store_file import VectorStoreFile -from openai.types.beta.vector_stores.vector_store_file_batch import VectorStoreFileBatch +from openai.types.beta.vector_stores import ( + VectorStoreFile, + VectorStoreFileBatch, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/beta/vector_stores/test_files.py b/tests/api_resources/beta/vector_stores/test_files.py index 09f5c259bd..58301e2d37 100644 --- a/tests/api_resources/beta/vector_stores/test_files.py +++ b/tests/api_resources/beta/vector_stores/test_files.py @@ -10,8 +10,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.vector_stores.vector_store_file import VectorStoreFile -from openai.types.beta.vector_stores.vector_store_file_deleted import VectorStoreFileDeleted +from openai.types.beta.vector_stores import ( + VectorStoreFile, + VectorStoreFileDeleted, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index ddba1ca085..c54b56a37d 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.chat.chat_completion import ChatCompletion +from openai.types.chat import ChatCompletion base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py index 6ebf2225ae..915d5c6f63 100644 --- a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py +++ b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.fine_tuning.jobs.fine_tuning_job_checkpoint import FineTuningJobCheckpoint +from openai.types.fine_tuning.jobs import FineTuningJobCheckpoint base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 29a96feb2d..1ff6d63b31 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -10,8 +10,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.fine_tuning.fine_tuning_job import FineTuningJob -from openai.types.fine_tuning.fine_tuning_job_event import FineTuningJobEvent +from openai.types.fine_tuning import ( + FineTuningJob, + FineTuningJobEvent, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py index 7967634128..6f9b598e61 100644 --- a/tests/api_resources/test_batches.py +++ b/tests/api_resources/test_batches.py @@ -9,8 +9,8 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type +from openai.types import Batch from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.batch import Batch base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 249744b531..691c4ff77f 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.completion import Completion +from openai.types import Completion base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py index 9c4e55a5a8..e75545b4e2 100644 --- a/tests/api_resources/test_embeddings.py +++ b/tests/api_resources/test_embeddings.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.create_embedding_response import CreateEmbeddingResponse +from openai.types import CreateEmbeddingResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 3b6817e27b..e5466e9eda 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -12,9 +12,8 @@ import openai._legacy_response as _legacy_response from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type +from openai.types import FileObject, FileDeleted from openai.pagination import SyncPage, AsyncPage -from openai.types.file_object import FileObject -from openai.types.file_deleted import FileDeleted # pyright: reportDeprecated=false diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 8d857d2f45..2e31f3354a 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.images_response import ImagesResponse +from openai.types import ImagesResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py index 2351d64a33..71f8e5834b 100644 --- a/tests/api_resources/test_models.py +++ b/tests/api_resources/test_models.py @@ -9,9 +9,8 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type +from openai.types import Model, ModelDeleted from openai.pagination import SyncPage, AsyncPage -from openai.types.model import Model -from openai.types.model_deleted import ModelDeleted base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index 52436ad0a9..94b9ecd31b 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.moderation_create_response import ModerationCreateResponse +from openai.types import ModerationCreateResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") From ffa8483dcf7ff811e8c1522d190a554cbf73fa9e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:49:34 -0400 Subject: [PATCH 422/914] chore(internal): minor reformatting (#1377) --- src/openai/types/audio/transcription.py | 2 ++ src/openai/types/audio/translation.py | 2 ++ src/openai/types/batch_request_counts.py | 2 ++ src/openai/types/beta/assistant_tool_choice_function.py | 2 ++ src/openai/types/beta/threads/image_file.py | 2 ++ src/openai/types/completion_usage.py | 2 ++ src/openai/types/model_deleted.py | 2 ++ src/openai/types/moderation.py | 1 + tests/test_client.py | 1 - 9 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index fa512e27f9..0b6ab39e78 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + from ..._models import BaseModel __all__ = ["Transcription"] diff --git a/src/openai/types/audio/translation.py b/src/openai/types/audio/translation.py index efc56f7f9b..3d9ede2939 100644 --- a/src/openai/types/audio/translation.py +++ b/src/openai/types/audio/translation.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + from ..._models import BaseModel __all__ = ["Translation"] diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py index 068b071af1..ef6c84a0a1 100644 --- a/src/openai/types/batch_request_counts.py +++ b/src/openai/types/batch_request_counts.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + from .._models import BaseModel __all__ = ["BatchRequestCounts"] diff --git a/src/openai/types/beta/assistant_tool_choice_function.py b/src/openai/types/beta/assistant_tool_choice_function.py index 87f38310ca..d0d4255357 100644 --- a/src/openai/types/beta/assistant_tool_choice_function.py +++ b/src/openai/types/beta/assistant_tool_choice_function.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + from ..._models import BaseModel __all__ = ["AssistantToolChoiceFunction"] diff --git a/src/openai/types/beta/threads/image_file.py b/src/openai/types/beta/threads/image_file.py index db0d6e823a..651a247d21 100644 --- a/src/openai/types/beta/threads/image_file.py +++ b/src/openai/types/beta/threads/image_file.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + from ...._models import BaseModel __all__ = ["ImageFile"] diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index e185a5cc38..0d57b96595 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + from .._models import BaseModel __all__ = ["CompletionUsage"] diff --git a/src/openai/types/model_deleted.py b/src/openai/types/model_deleted.py index e7601f74e4..d9a48bb1b5 100644 --- a/src/openai/types/model_deleted.py +++ b/src/openai/types/model_deleted.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + from .._models import BaseModel __all__ = ["ModelDeleted"] diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index 2a2e5c5d7a..5aa691823a 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from pydantic import Field as FieldInfo from .._models import BaseModel diff --git a/tests/test_client.py b/tests/test_client.py index ba85fd9d5f..c1e545e66f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -17,7 +17,6 @@ from pydantic import ValidationError from openai import OpenAI, AsyncOpenAI, APIResponseValidationError -from openai._client import OpenAI, AsyncOpenAI from openai._models import BaseModel, FinalRequestOptions from openai._constants import RAW_RESPONSE_HEADER from openai._streaming import Stream, AsyncStream From a669541138e95c11e6fe7e5e82e3d95f435f89f1 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:02:12 -0400 Subject: [PATCH 423/914] feat(api): add required tool_choice (#1382) --- .../resources/beta/threads/runs/runs.py | 30 ++++--- src/openai/resources/beta/threads/threads.py | 30 ++++--- src/openai/resources/chat/completions.py | 84 +++++++++---------- .../beta/assistant_tool_choice_option.py | 2 +- .../assistant_tool_choice_option_param.py | 2 +- .../beta/thread_create_and_run_params.py | 5 +- src/openai/types/beta/threads/run.py | 5 +- .../types/beta/threads/run_create_params.py | 5 +- ...hat_completion_tool_choice_option_param.py | 2 +- .../types/chat/completion_create_params.py | 14 ++-- 10 files changed, 97 insertions(+), 82 deletions(-) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index e572a14a19..4268d41390 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -187,8 +187,9 @@ def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -330,8 +331,9 @@ def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -473,8 +475,9 @@ def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1716,8 +1719,9 @@ async def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1859,8 +1863,9 @@ async def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -2002,8 +2007,9 @@ async def create( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 1c516bcea6..2455272658 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -365,8 +365,9 @@ def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -507,8 +508,9 @@ def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -649,8 +651,9 @@ def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1336,8 +1339,9 @@ async def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1478,8 +1482,9 @@ async def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. @@ -1620,8 +1625,9 @@ async def create_and_run( tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 2a6a0e7738..5104cd6136 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -171,15 +171,15 @@ def create( We generally recommend altering this or `top_p` but not both. - tool_choice: Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to - call that function. + call that tool. - `none` is the default when no functions are present. `auto` is the default if - functions are present. + `none` is the default when no tools are present. `auto` is the default if tools + are present. tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs @@ -339,15 +339,15 @@ def create( We generally recommend altering this or `top_p` but not both. - tool_choice: Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to - call that function. + call that tool. - `none` is the default when no functions are present. `auto` is the default if - functions are present. + `none` is the default when no tools are present. `auto` is the default if tools + are present. tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs @@ -507,15 +507,15 @@ def create( We generally recommend altering this or `top_p` but not both. - tool_choice: Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to - call that function. + call that tool. - `none` is the default when no functions are present. `auto` is the default if - functions are present. + `none` is the default when no tools are present. `auto` is the default if tools + are present. tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs @@ -751,15 +751,15 @@ async def create( We generally recommend altering this or `top_p` but not both. - tool_choice: Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to - call that function. + call that tool. - `none` is the default when no functions are present. `auto` is the default if - functions are present. + `none` is the default when no tools are present. `auto` is the default if tools + are present. tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs @@ -919,15 +919,15 @@ async def create( We generally recommend altering this or `top_p` but not both. - tool_choice: Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to - call that function. + call that tool. - `none` is the default when no functions are present. `auto` is the default if - functions are present. + `none` is the default when no tools are present. `auto` is the default if tools + are present. tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs @@ -1087,15 +1087,15 @@ async def create( We generally recommend altering this or `top_p` but not both. - tool_choice: Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via + tool_choice: Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to - call that function. + call that tool. - `none` is the default when no functions are present. `auto` is the default if - functions are present. + `none` is the default when no tools are present. `auto` is the default if tools + are present. tools: A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs diff --git a/src/openai/types/beta/assistant_tool_choice_option.py b/src/openai/types/beta/assistant_tool_choice_option.py index 0045a5986e..8958bc8fb0 100644 --- a/src/openai/types/beta/assistant_tool_choice_option.py +++ b/src/openai/types/beta/assistant_tool_choice_option.py @@ -7,4 +7,4 @@ __all__ = ["AssistantToolChoiceOption"] -AssistantToolChoiceOption = Union[Literal["none", "auto"], AssistantToolChoice] +AssistantToolChoiceOption = Union[Literal["none", "auto", "required"], AssistantToolChoice] diff --git a/src/openai/types/beta/assistant_tool_choice_option_param.py b/src/openai/types/beta/assistant_tool_choice_option_param.py index 618e7bff98..81b7f15136 100644 --- a/src/openai/types/beta/assistant_tool_choice_option_param.py +++ b/src/openai/types/beta/assistant_tool_choice_option_param.py @@ -9,4 +9,4 @@ __all__ = ["AssistantToolChoiceOptionParam"] -AssistantToolChoiceOptionParam = Union[Literal["none", "auto"], AssistantToolChoiceParam] +AssistantToolChoiceOptionParam = Union[Literal["none", "auto", "required"], AssistantToolChoiceParam] diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 9adb049843..60510965a2 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -135,8 +135,9 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): """ Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. """ diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 4fd5103348..6c118f27c1 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -196,8 +196,9 @@ class Run(BaseModel): """ Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. """ diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index f4780b7f09..2e4823bacd 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -134,8 +134,9 @@ class RunCreateParamsBase(TypedDict, total=False): """ Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value - and means the model can pick between generating a message or calling a tool. - Specifying a particular tool like `{"type": "file_search"}` or + and means the model can pick between generating a message or calling one or more + tools. `required` means the model must call one or more tools before responding + to the user. Specifying a particular tool like `{"type": "file_search"}` or `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. """ diff --git a/src/openai/types/chat/chat_completion_tool_choice_option_param.py b/src/openai/types/chat/chat_completion_tool_choice_option_param.py index 9c0ae22528..1d3c2506ab 100644 --- a/src/openai/types/chat/chat_completion_tool_choice_option_param.py +++ b/src/openai/types/chat/chat_completion_tool_choice_option_param.py @@ -9,4 +9,4 @@ __all__ = ["ChatCompletionToolChoiceOptionParam"] -ChatCompletionToolChoiceOptionParam = Union[Literal["none", "auto"], ChatCompletionNamedToolChoiceParam] +ChatCompletionToolChoiceOptionParam = Union[Literal["none", "auto", "required"], ChatCompletionNamedToolChoiceParam] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 964b246c41..d30da60b16 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -152,15 +152,15 @@ class CompletionCreateParamsBase(TypedDict, total=False): tool_choice: ChatCompletionToolChoiceOptionParam """ - Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via + Controls which (if any) tool is called by the model. `none` means the model will + not call any tool and instead generates a message. `auto` means the model can + pick between generating a message or calling one or more tools. `required` means + the model must call one or more tools. Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to - call that function. + call that tool. - `none` is the default when no functions are present. `auto` is the default if - functions are present. + `none` is the default when no tools are present. `auto` is the default if tools + are present. """ tools: Iterable[ChatCompletionToolParam] From 155d0de3c032a41d57c719a321f916c65e7e9e89 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:30:24 -0400 Subject: [PATCH 424/914] chore(client): log response headers in debug mode (#1383) --- src/openai/_base_client.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index cd8361607e..5d5d25fca9 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -946,6 +946,8 @@ def _request( if self.custom_auth is not None: kwargs["auth"] = self.custom_auth + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + try: response = self._client.send( request, @@ -984,8 +986,14 @@ def _request( raise APIConnectionError(request=request) from err log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, ) + log.debug("request_id: %s", response.headers.get("x-request-id")) try: response.raise_for_status() From 39845c7a2fc166e1d295a3f9a7d82a13a383b69b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:30:52 -0400 Subject: [PATCH 425/914] release: 1.24.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 89bd0ec2f7..bfaab56f68 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.23.6" + ".": "1.24.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a25cd549b7..da1a4c27a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.24.0 (2024-04-29) + +Full Changelog: [v1.23.6...v1.24.0](https://github.com/openai/openai-python/compare/v1.23.6...v1.24.0) + +### Features + +* **api:** add required tool_choice ([#1382](https://github.com/openai/openai-python/issues/1382)) ([c558f65](https://github.com/openai/openai-python/commit/c558f651df39f61425cd4109318f78ed94cbf163)) + + +### Chores + +* **client:** log response headers in debug mode ([#1383](https://github.com/openai/openai-python/issues/1383)) ([f31a426](https://github.com/openai/openai-python/commit/f31a4261adc4ebd92582cee264e41eb6a6dafc57)) +* **internal:** minor reformatting ([#1377](https://github.com/openai/openai-python/issues/1377)) ([7003dbb](https://github.com/openai/openai-python/commit/7003dbb863b6e16381070b8b86ac24aa070a3799)) +* **internal:** reformat imports ([#1375](https://github.com/openai/openai-python/issues/1375)) ([2ad0c3b](https://github.com/openai/openai-python/commit/2ad0c3b8e0b746ed20db3c84a9c6a369aa10bf5d)) + ## 1.23.6 (2024-04-25) Full Changelog: [v1.23.5...v1.23.6](https://github.com/openai/openai-python/compare/v1.23.5...v1.23.6) diff --git a/pyproject.toml b/pyproject.toml index d82615a4ad..d1006b3b70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.23.6" +version = "1.24.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7ca1a0a78b..5b3383adc3 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.23.6" # x-release-please-version +__version__ = "1.24.0" # x-release-please-version From 11460b5ed97080ee8e7a4311c1e90e495c59d370 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 30 Apr 2024 14:06:35 -0400 Subject: [PATCH 426/914] release: 1.24.1 (#1386) * chore(internal): add link to openapi spec (#1385) * release: 1.24.1 --- .release-please-manifest.json | 2 +- .stats.yml | 1 + CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bfaab56f68..347a18e529 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.24.0" + ".": "1.24.1" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index c9a9bfa4a8..e904583dae 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1,2 @@ configured_endpoints: 63 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-0839c14b2b61dad4e830884410cfc3695546682ced009e50583c8bb5c44512d7.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index da1a4c27a4..53385512db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.24.1 (2024-04-30) + +Full Changelog: [v1.24.0...v1.24.1](https://github.com/openai/openai-python/compare/v1.24.0...v1.24.1) + +### Chores + +* **internal:** add link to openapi spec ([#1385](https://github.com/openai/openai-python/issues/1385)) ([b315d04](https://github.com/openai/openai-python/commit/b315d04e9624ec3a841d7c51813bb553640c23ce)) + ## 1.24.0 (2024-04-29) Full Changelog: [v1.23.6...v1.24.0](https://github.com/openai/openai-python/compare/v1.23.6...v1.24.0) diff --git a/pyproject.toml b/pyproject.toml index d1006b3b70..66fee29b6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.24.0" +version = "1.24.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5b3383adc3..346ec5ec8d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.24.0" # x-release-please-version +__version__ = "1.24.1" # x-release-please-version From d2738d4259aa1c58e206ec23e388855fa218d3f9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 1 May 2024 00:00:17 -0400 Subject: [PATCH 427/914] feat(api): delete messages (#1388) --- .github/workflows/ci.yml | 22 +++- .gitignore | 1 + .stats.yml | 4 +- Brewfile | 2 + api.md | 1 + bin/check-env-state.py | 40 ------- bin/check-test-server | 50 --------- bin/test | 3 - pyproject.toml | 3 +- scripts/bootstrap | 19 ++++ scripts/format | 8 ++ scripts/lint | 8 ++ scripts/mock | 41 +++++++ scripts/test | 57 ++++++++++ {bin => scripts/utils}/ruffen-docs.py | 0 src/openai/resources/batches.py | 12 +-- src/openai/resources/beta/threads/messages.py | 87 +++++++++++++++ src/openai/types/batch_create_params.py | 4 +- src/openai/types/beta/threads/__init__.py | 1 + .../types/beta/threads/message_deleted.py | 15 +++ .../types/fine_tuning/fine_tuning_job.py | 6 ++ .../beta/threads/test_messages.py | 101 +++++++++++++++++- 22 files changed, 379 insertions(+), 106 deletions(-) create mode 100644 Brewfile delete mode 100644 bin/check-env-state.py delete mode 100755 bin/check-test-server delete mode 100755 bin/test create mode 100755 scripts/bootstrap create mode 100755 scripts/format create mode 100755 scripts/lint create mode 100755 scripts/mock create mode 100755 scripts/test rename {bin => scripts/utils}/ruffen-docs.py (100%) create mode 100644 src/openai/types/beta/threads/message_deleted.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c44028d96c..9cbc077a8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,5 +39,25 @@ jobs: - name: Ensure importable run: | rye run python -c 'import openai' + test: + name: test + runs-on: ubuntu-latest + if: github.repository == 'openai/openai-python' + + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye-up.com/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: 0.24.0 + RYE_INSTALL_OPTION: '--yes' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Run tests + run: ./scripts/test - diff --git a/.gitignore b/.gitignore index a4b2f8c0bd..0f9a66a976 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ dist .env .envrc codegen.log +Brewfile.lock.json diff --git a/.stats.yml b/.stats.yml index e904583dae..9797002bf7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 63 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-0839c14b2b61dad4e830884410cfc3695546682ced009e50583c8bb5c44512d7.yml +configured_endpoints: 64 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-97c9a5f089049dc9eb5cee9475558049003e37e42202cab39e59d75e08b4c613.yml diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000000..492ca37bb0 --- /dev/null +++ b/Brewfile @@ -0,0 +1,2 @@ +brew "rye" + diff --git a/api.md b/api.md index 30247e8f7f..9dc42f0f0f 100644 --- a/api.md +++ b/api.md @@ -392,6 +392,7 @@ Methods: - client.beta.threads.messages.retrieve(message_id, \*, thread_id) -> Message - client.beta.threads.messages.update(message_id, \*, thread_id, \*\*params) -> Message - client.beta.threads.messages.list(thread_id, \*\*params) -> SyncCursorPage[Message] +- client.beta.threads.messages.delete(message_id, \*, thread_id) -> MessageDeleted # Batches diff --git a/bin/check-env-state.py b/bin/check-env-state.py deleted file mode 100644 index e1b8b6cb39..0000000000 --- a/bin/check-env-state.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Script that exits 1 if the current environment is not -in sync with the `requirements-dev.lock` file. -""" - -from pathlib import Path - -import importlib_metadata - - -def should_run_sync() -> bool: - dev_lock = Path(__file__).parent.parent.joinpath("requirements-dev.lock") - - for line in dev_lock.read_text().splitlines(): - if not line or line.startswith("#") or line.startswith("-e"): - continue - - dep, lock_version = line.split("==") - - try: - version = importlib_metadata.version(dep) - - if lock_version != version: - print(f"mismatch for {dep} current={version} lock={lock_version}") - return True - except Exception: - print(f"could not import {dep}") - return True - - return False - - -def main() -> None: - if should_run_sync(): - exit(1) - else: - exit(0) - - -if __name__ == "__main__": - main() diff --git a/bin/check-test-server b/bin/check-test-server deleted file mode 100755 index a6fa34950d..0000000000 --- a/bin/check-test-server +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -NC='\033[0m' # No Color - -function prism_is_running() { - curl --silent "http://localhost:4010/" >/dev/null 2>&1 -} - -function is_overriding_api_base_url() { - [ -n "$TEST_API_BASE_URL" ] -} - -if is_overriding_api_base_url ; then - # If someone is running the tests against the live API, we can trust they know - # what they're doing and exit early. - echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" - - exit 0 -elif prism_is_running ; then - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" - echo - - exit 0 -else - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" - echo -e "running against your OpenAPI spec." - echo - echo -e "${YELLOW}To fix:${NC}" - echo - echo -e "1. Install Prism (requires Node 16+):" - echo - echo -e " With npm:" - echo -e " \$ ${YELLOW}npm install -g @stoplight/prism-cli${NC}" - echo - echo -e " With yarn:" - echo -e " \$ ${YELLOW}yarn global add @stoplight/prism-cli${NC}" - echo - echo -e "2. Run the mock server" - echo - echo -e " To run the server, pass in the path of your OpenAPI" - echo -e " spec to the prism command:" - echo - echo -e " \$ ${YELLOW}prism mock path/to/your.openapi.yml${NC}" - echo - - exit 1 -fi diff --git a/bin/test b/bin/test deleted file mode 100755 index 60ede7a842..0000000000 --- a/bin/test +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -bin/check-test-server && rye run pytest "$@" diff --git a/pyproject.toml b/pyproject.toml index 66fee29b6d..10a756a22b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ format = { chain = [ "fix:ruff", ]} "format:black" = "black ." -"format:docs" = "python bin/ruffen-docs.py README.md api.md" +"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" "format:ruff" = "ruff format" "format:isort" = "isort ." @@ -197,5 +197,6 @@ known-first-party = ["openai", "tests"] [tool.ruff.per-file-ignores] "bin/**.py" = ["T201", "T203"] +"scripts/**.py" = ["T201", "T203"] "tests/**.py" = ["T201", "T203"] "examples/**.py" = ["T201", "T203"] diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 0000000000..29df07e77b --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then + brew bundle check >/dev/null 2>&1 || { + echo "==> Installing Homebrew dependencies…" + brew bundle + } +fi + +echo "==> Installing Python dependencies…" + +# experimental uv support makes installations significantly faster +rye config --set-bool behavior.use-uv=true + +rye sync diff --git a/scripts/format b/scripts/format new file mode 100755 index 0000000000..2a9ea4664b --- /dev/null +++ b/scripts/format @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +rye run format + diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 0000000000..0cc68b5157 --- /dev/null +++ b/scripts/lint @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +rye run lint + diff --git a/scripts/mock b/scripts/mock new file mode 100755 index 0000000000..5a8c35b725 --- /dev/null +++ b/scripts/mock @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [[ -n "$1" && "$1" != '--'* ]]; then + URL="$1" + shift +else + URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" +fi + +# Check if the URL is empty +if [ -z "$URL" ]; then + echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" + exit 1 +fi + +echo "==> Starting mock server with URL ${URL}" + +# Run prism mock on the given spec +if [ "$1" == "--daemon" ]; then + npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock "$URL" &> .prism.log & + + # Wait for server to come online + echo -n "Waiting for server" + while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + echo -n "." + sleep 0.1 + done + + if grep -q "✖ fatal" ".prism.log"; then + cat .prism.log + exit 1 + fi + + echo +else + npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock "$URL" +fi diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000000..be01d04473 --- /dev/null +++ b/scripts/test @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +function prism_is_running() { + curl --silent "http://localhost:4010/" >/dev/null 2>&1 +} + +kill_server_on_port() { + pids=$(lsof -t -i tcp:"$1" || echo "") + if [ "$pids" != "" ]; then + kill "$pids" + echo "Stopped $pids." + fi +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if ! is_overriding_api_base_url && ! prism_is_running ; then + # When we exit this script, make sure to kill the background mock server process + trap 'kill_server_on_port 4010' EXIT + + # Start the dev server + ./scripts/mock --daemon +fi + +if is_overriding_api_base_url ; then + echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" + echo +elif ! prism_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "To run the server, pass in the path or url of your OpenAPI" + echo -e "spec to the prism command:" + echo + echo -e " \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +else + echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo +fi + +# Run tests +echo "==> Running tests" +rye run pytest "$@" diff --git a/bin/ruffen-docs.py b/scripts/utils/ruffen-docs.py similarity index 100% rename from bin/ruffen-docs.py rename to scripts/utils/ruffen-docs.py diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index a2a0272a7d..64a3014c37 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -40,7 +40,7 @@ def create( self, *, completion_window: Literal["24h"], - endpoint: Literal["/v1/chat/completions"], + endpoint: Literal["/v1/chat/completions", "/v1/embeddings"], input_file_id: str, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -57,8 +57,8 @@ def create( completion_window: The time frame within which the batch should be processed. Currently only `24h` is supported. - endpoint: The endpoint to be used for all requests in the batch. Currently only - `/v1/chat/completions` is supported. + endpoint: The endpoint to be used for all requests in the batch. Currently + `/v1/chat/completions` and `/v1/embeddings` are supported. input_file_id: The ID of an uploaded file that contains requests for the new batch. @@ -228,7 +228,7 @@ async def create( self, *, completion_window: Literal["24h"], - endpoint: Literal["/v1/chat/completions"], + endpoint: Literal["/v1/chat/completions", "/v1/embeddings"], input_file_id: str, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -245,8 +245,8 @@ async def create( completion_window: The time frame within which the batch should be processed. Currently only `24h` is supported. - endpoint: The endpoint to be used for all requests in the batch. Currently only - `/v1/chat/completions` is supported. + endpoint: The endpoint to be used for all requests in the batch. Currently + `/v1/chat/completions` and `/v1/embeddings` are supported. input_file_id: The ID of an uploaded file that contains requests for the new batch. diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index a938c5e15d..0799feed23 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -23,6 +23,7 @@ ) from ....types.beta.threads import message_list_params, message_create_params, message_update_params from ....types.beta.threads.message import Message +from ....types.beta.threads.message_deleted import MessageDeleted __all__ = ["Messages", "AsyncMessages"] @@ -252,6 +253,43 @@ def list( model=Message, ) + def delete( + self, + message_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MessageDeleted: + """ + Deletes a message. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._delete( + f"/threads/{thread_id}/messages/{message_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MessageDeleted, + ) + class AsyncMessages(AsyncAPIResource): @cached_property @@ -478,6 +516,43 @@ def list( model=Message, ) + async def delete( + self, + message_id: str, + *, + thread_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MessageDeleted: + """ + Deletes a message. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._delete( + f"/threads/{thread_id}/messages/{message_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MessageDeleted, + ) + class MessagesWithRawResponse: def __init__(self, messages: Messages) -> None: @@ -495,6 +570,9 @@ def __init__(self, messages: Messages) -> None: self.list = _legacy_response.to_raw_response_wrapper( messages.list, ) + self.delete = _legacy_response.to_raw_response_wrapper( + messages.delete, + ) class AsyncMessagesWithRawResponse: @@ -513,6 +591,9 @@ def __init__(self, messages: AsyncMessages) -> None: self.list = _legacy_response.async_to_raw_response_wrapper( messages.list, ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + messages.delete, + ) class MessagesWithStreamingResponse: @@ -531,6 +612,9 @@ def __init__(self, messages: Messages) -> None: self.list = to_streamed_response_wrapper( messages.list, ) + self.delete = to_streamed_response_wrapper( + messages.delete, + ) class AsyncMessagesWithStreamingResponse: @@ -549,3 +633,6 @@ def __init__(self, messages: AsyncMessages) -> None: self.list = async_to_streamed_response_wrapper( messages.list, ) + self.delete = async_to_streamed_response_wrapper( + messages.delete, + ) diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index a67aaa1e5e..63b4fae91b 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -15,10 +15,10 @@ class BatchCreateParams(TypedDict, total=False): Currently only `24h` is supported. """ - endpoint: Required[Literal["/v1/chat/completions"]] + endpoint: Required[Literal["/v1/chat/completions", "/v1/embeddings"]] """The endpoint to be used for all requests in the batch. - Currently only `/v1/chat/completions` is supported. + Currently `/v1/chat/completions` and `/v1/embeddings` are supported. """ input_file_id: Required[str] diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index b57ebccb3a..1e38d5eaa1 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -11,6 +11,7 @@ from .text_delta import TextDelta as TextDelta from .message_delta import MessageDelta as MessageDelta from .message_content import MessageContent as MessageContent +from .message_deleted import MessageDeleted as MessageDeleted from .run_list_params import RunListParams as RunListParams from .annotation_delta import AnnotationDelta as AnnotationDelta from .image_file_delta import ImageFileDelta as ImageFileDelta diff --git a/src/openai/types/beta/threads/message_deleted.py b/src/openai/types/beta/threads/message_deleted.py new file mode 100644 index 0000000000..48210777fa --- /dev/null +++ b/src/openai/types/beta/threads/message_deleted.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["MessageDeleted"] + + +class MessageDeleted(BaseModel): + id: str + + deleted: bool + + object: Literal["thread.message.deleted"] diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index 1593bf50c7..7ac8792787 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -110,5 +110,11 @@ class FineTuningJob(BaseModel): [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents). """ + estimated_finish: Optional[int] = None + """ + The Unix timestamp (in seconds) for when the fine-tuning job is estimated to + finish. The value will be null if the fine-tuning job is not running. + """ + integrations: Optional[List[FineTuningJobWandbIntegrationObject]] = None """A list of integrations to enable for this fine-tuning job.""" diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index 26eb09acdd..fb42d509a1 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -10,7 +10,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.threads import Message +from openai.types.beta.threads import ( + Message, + MessageDeleted, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -243,6 +246,54 @@ def test_path_params_list(self, client: OpenAI) -> None: "", ) + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + message = client.beta.threads.messages.delete( + "string", + thread_id="string", + ) + assert_matches_type(MessageDeleted, message, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.beta.threads.messages.with_raw_response.delete( + "string", + thread_id="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(MessageDeleted, message, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.beta.threads.messages.with_streaming_response.delete( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = response.parse() + assert_matches_type(MessageDeleted, message, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.threads.messages.with_raw_response.delete( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + client.beta.threads.messages.with_raw_response.delete( + "", + thread_id="string", + ) + class TestAsyncMessages: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -471,3 +522,51 @@ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: await async_client.beta.threads.messages.with_raw_response.list( "", ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + message = await async_client.beta.threads.messages.delete( + "string", + thread_id="string", + ) + assert_matches_type(MessageDeleted, message, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.threads.messages.with_raw_response.delete( + "string", + thread_id="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(MessageDeleted, message, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.threads.messages.with_streaming_response.delete( + "string", + thread_id="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = await response.parse() + assert_matches_type(MessageDeleted, message, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await async_client.beta.threads.messages.with_raw_response.delete( + "string", + thread_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + await async_client.beta.threads.messages.with_raw_response.delete( + "", + thread_id="string", + ) From ec52e89ccd87744fb38e2ae785ed7013241e476a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 1 May 2024 00:00:46 -0400 Subject: [PATCH 428/914] release: 1.25.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 347a18e529..0c0c0c3576 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.24.1" + ".": "1.25.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 53385512db..c71e1c89ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.25.0 (2024-05-01) + +Full Changelog: [v1.24.1...v1.25.0](https://github.com/openai/openai-python/compare/v1.24.1...v1.25.0) + +### Features + +* **api:** delete messages ([#1388](https://github.com/openai/openai-python/issues/1388)) ([d0597cd](https://github.com/openai/openai-python/commit/d0597cdc1813cddffacbaa50565e86d2420d1873)) + ## 1.24.1 (2024-04-30) Full Changelog: [v1.24.0...v1.24.1](https://github.com/openai/openai-python/compare/v1.24.0...v1.24.1) diff --git a/pyproject.toml b/pyproject.toml index 10a756a22b..ffc98fd2ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.24.1" +version = "1.25.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 346ec5ec8d..8326c22065 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.24.1" # x-release-please-version +__version__ = "1.25.0" # x-release-please-version From aed1e43745cd6358b4bafd3a39b3dfeee5e31a03 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 2 May 2024 11:51:34 -0400 Subject: [PATCH 429/914] release: 1.25.1 (#1391) * chore(internal): bump prism version (#1390) * release: 1.25.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- scripts/mock | 4 ++-- src/openai/_version.py | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0c0c0c3576..f5cfbcb39d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.25.0" + ".": "1.25.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c71e1c89ff..3920956dde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.25.1 (2024-05-02) + +Full Changelog: [v1.25.0...v1.25.1](https://github.com/openai/openai-python/compare/v1.25.0...v1.25.1) + +### Chores + +* **internal:** bump prism version ([#1390](https://github.com/openai/openai-python/issues/1390)) ([a5830fc](https://github.com/openai/openai-python/commit/a5830fc1c5ffd21e2010490905084ad5614212a3)) + ## 1.25.0 (2024-05-01) Full Changelog: [v1.24.1...v1.25.0](https://github.com/openai/openai-python/compare/v1.24.1...v1.25.0) diff --git a/pyproject.toml b/pyproject.toml index ffc98fd2ef..8480f27440 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.25.0" +version = "1.25.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/scripts/mock b/scripts/mock index 5a8c35b725..fe89a1d084 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stoplight/prism-cli@~5.8 -- prism mock "$URL" &> .prism.log & # Wait for server to come online echo -n "Waiting for server" @@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock "$URL" + npm exec --package=@stoplight/prism-cli@~5.8 -- prism mock "$URL" fi diff --git a/src/openai/_version.py b/src/openai/_version.py index 8326c22065..30fa8f2070 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.25.0" # x-release-please-version +__version__ = "1.25.1" # x-release-please-version From 2a678e30a6fdc37d9c944288a8186c8ad4b4b053 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sun, 5 May 2024 21:00:00 +0100 Subject: [PATCH 430/914] release: 1.25.2 (#1394) --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ README.md | 2 +- pyproject.toml | 2 +- src/openai/_version.py | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f5cfbcb39d..6994d4cb49 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.25.1" + ".": "1.25.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3920956dde..3fa4fc4dc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.25.2 (2024-05-05) + +Full Changelog: [v1.25.1...v1.25.2](https://github.com/openai/openai-python/compare/v1.25.1...v1.25.2) + +### Documentation + +* **readme:** fix misleading timeout example value ([#1393](https://github.com/openai/openai-python/issues/1393)) ([3eba8e7](https://github.com/openai/openai-python/commit/3eba8e7573ec1bf4231a304c8eabc8a8d077f46d)) + ## 1.25.1 (2024-05-02) Full Changelog: [v1.25.0...v1.25.1](https://github.com/openai/openai-python/compare/v1.25.0...v1.25.1) diff --git a/README.md b/README.md index b8a0d9b0cb..e566a2f8d0 100644 --- a/README.md +++ b/README.md @@ -424,7 +424,7 @@ client = OpenAI( ) # Override per-request: -client.with_options(timeout=5 * 1000).chat.completions.create( +client.with_options(timeout=5.0).chat.completions.create( messages=[ { "role": "user", diff --git a/pyproject.toml b/pyproject.toml index 8480f27440..de0eb72023 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.25.1" +version = "1.25.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 30fa8f2070..c4c92f77ad 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.25.1" # x-release-please-version +__version__ = "1.25.2" # x-release-please-version From 6cc515874f5f4b26b35f408d6afc3c14b4dfe3b0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 6 May 2024 15:07:30 -0400 Subject: [PATCH 431/914] feat(api): add usage metadata when streaming (#1395) --- .stats.yml | 2 +- api.md | 1 + src/openai/resources/chat/completions.py | 23 +++++++++++++++++++ src/openai/resources/completions.py | 23 +++++++++++++++++++ src/openai/types/chat/__init__.py | 1 + .../types/chat/chat_completion_chunk.py | 12 +++++++++- .../chat_completion_stream_options_param.py | 17 ++++++++++++++ .../types/chat/completion_create_params.py | 4 ++++ src/openai/types/completion_create_params.py | 5 ++++ tests/api_resources/chat/test_completions.py | 8 ++++++- tests/api_resources/test_completions.py | 4 ++++ 11 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 src/openai/types/chat/chat_completion_stream_options_param.py diff --git a/.stats.yml b/.stats.yml index 9797002bf7..49956282b7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-97c9a5f089049dc9eb5cee9475558049003e37e42202cab39e59d75e08b4c613.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-edb5af3ade0cd27cf366b0654b90c7a81c43c433e11fc3f6e621e2c779de10d4.yml diff --git a/api.md b/api.md index 9dc42f0f0f..696075eff3 100644 --- a/api.md +++ b/api.md @@ -43,6 +43,7 @@ from openai.types.chat import ( ChatCompletionMessageToolCall, ChatCompletionNamedToolChoice, ChatCompletionRole, + ChatCompletionStreamOptions, ChatCompletionSystemMessageParam, ChatCompletionTokenLogprob, ChatCompletionTool, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 5104cd6136..aa25bc1858 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -27,6 +27,7 @@ from ...types.chat.chat_completion_chunk import ChatCompletionChunk from ...types.chat.chat_completion_tool_param import ChatCompletionToolParam from ...types.chat.chat_completion_message_param import ChatCompletionMessageParam +from ...types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from ...types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam __all__ = ["Completions", "AsyncCompletions"] @@ -59,6 +60,7 @@ def create( seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, @@ -165,6 +167,8 @@ def create( message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + stream_options: Options for streaming response. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -227,6 +231,7 @@ def create( response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, @@ -333,6 +338,8 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. + stream_options: Options for streaming response. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -395,6 +402,7 @@ def create( response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, @@ -501,6 +509,8 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. + stream_options: Options for streaming response. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -563,6 +573,7 @@ def create( seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, @@ -594,6 +605,7 @@ def create( "seed": seed, "stop": stop, "stream": stream, + "stream_options": stream_options, "temperature": temperature, "tool_choice": tool_choice, "tools": tools, @@ -639,6 +651,7 @@ async def create( seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, @@ -745,6 +758,8 @@ async def create( message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + stream_options: Options for streaming response. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -807,6 +822,7 @@ async def create( response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, @@ -913,6 +929,8 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. + stream_options: Options for streaming response. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -975,6 +993,7 @@ async def create( response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, @@ -1081,6 +1100,8 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. + stream_options: Options for streaming response. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. @@ -1143,6 +1164,7 @@ async def create( seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, @@ -1174,6 +1196,7 @@ async def create( "seed": seed, "stop": stop, "stream": stream, + "stream_options": stream_options, "temperature": temperature, "tool_choice": tool_choice, "tools": tools, diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index eb6ca31048..0812000f78 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -23,6 +23,7 @@ make_request_options, ) from ..types.completion import Completion +from ..types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam __all__ = ["Completions", "AsyncCompletions"] @@ -53,6 +54,7 @@ def create( seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, @@ -156,6 +158,8 @@ def create( message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + stream_options: Options for streaming response. Only set this when you set `stream: true`. + suffix: The suffix that comes after a completion of inserted text. This parameter is only supported for `gpt-3.5-turbo-instruct`. @@ -203,6 +207,7 @@ def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, @@ -306,6 +311,8 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. + stream_options: Options for streaming response. Only set this when you set `stream: true`. + suffix: The suffix that comes after a completion of inserted text. This parameter is only supported for `gpt-3.5-turbo-instruct`. @@ -353,6 +360,7 @@ def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, @@ -456,6 +464,8 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. + stream_options: Options for streaming response. Only set this when you set `stream: true`. + suffix: The suffix that comes after a completion of inserted text. This parameter is only supported for `gpt-3.5-turbo-instruct`. @@ -503,6 +513,7 @@ def create( seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, @@ -531,6 +542,7 @@ def create( "seed": seed, "stop": stop, "stream": stream, + "stream_options": stream_options, "suffix": suffix, "temperature": temperature, "top_p": top_p, @@ -573,6 +585,7 @@ async def create( seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, @@ -676,6 +689,8 @@ async def create( message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + stream_options: Options for streaming response. Only set this when you set `stream: true`. + suffix: The suffix that comes after a completion of inserted text. This parameter is only supported for `gpt-3.5-turbo-instruct`. @@ -723,6 +738,7 @@ async def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, @@ -826,6 +842,8 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. + stream_options: Options for streaming response. Only set this when you set `stream: true`. + suffix: The suffix that comes after a completion of inserted text. This parameter is only supported for `gpt-3.5-turbo-instruct`. @@ -873,6 +891,7 @@ async def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, @@ -976,6 +995,8 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. + stream_options: Options for streaming response. Only set this when you set `stream: true`. + suffix: The suffix that comes after a completion of inserted text. This parameter is only supported for `gpt-3.5-turbo-instruct`. @@ -1023,6 +1044,7 @@ async def create( seed: Optional[int] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, @@ -1051,6 +1073,7 @@ async def create( "seed": seed, "stop": stop, "stream": stream, + "stream_options": stream_options, "suffix": suffix, "temperature": temperature, "top_p": top_p, diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 5d122d2020..0ba812ff9b 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -14,6 +14,7 @@ from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam from .chat_completion_user_message_param import ChatCompletionUserMessageParam as ChatCompletionUserMessageParam +from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam as ChatCompletionStreamOptionsParam from .chat_completion_system_message_param import ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam from .chat_completion_function_message_param import ( ChatCompletionFunctionMessageParam as ChatCompletionFunctionMessageParam, diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index c2f18bcb74..084a5fcc07 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from ..completion_usage import CompletionUsage from .chat_completion_token_logprob import ChatCompletionTokenLogprob __all__ = [ @@ -105,7 +106,8 @@ class ChatCompletionChunk(BaseModel): choices: List[Choice] """A list of chat completion choices. - Can be more than one if `n` is greater than 1. + Can contain more than one elements if `n` is greater than 1. Can also be empty + for the last chunk if you set `stream_options: {"include_usage": true}`. """ created: int @@ -126,3 +128,11 @@ class ChatCompletionChunk(BaseModel): Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism. """ + + usage: Optional[CompletionUsage] = None + """ + An optional field that will only be present when you set + `stream_options: {"include_usage": true}` in your request. When present, it + contains a null value except for the last chunk which contains the token usage + statistics for the entire request. + """ diff --git a/src/openai/types/chat/chat_completion_stream_options_param.py b/src/openai/types/chat/chat_completion_stream_options_param.py new file mode 100644 index 0000000000..fbf7291821 --- /dev/null +++ b/src/openai/types/chat/chat_completion_stream_options_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ChatCompletionStreamOptionsParam"] + + +class ChatCompletionStreamOptionsParam(TypedDict, total=False): + include_usage: bool + """If set, an additional chunk will be streamed before the `data: [DONE]` message. + + The `usage` field on this chunk shows the token usage statistics for the entire + request, and the `choices` field will always be an empty array. All other chunks + will also include a `usage` field, but with a null value. + """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index d30da60b16..226cf15882 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -9,6 +9,7 @@ from ..chat_model import ChatModel from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam +from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam from .chat_completion_function_call_option_param import ChatCompletionFunctionCallOptionParam @@ -141,6 +142,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): stop: Union[Optional[str], List[str]] """Up to 4 sequences where the API will stop generating further tokens.""" + stream_options: Optional[ChatCompletionStreamOptionsParam] + """Options for streaming response. Only set this when you set `stream: true`.""" + temperature: Optional[float] """What sampling temperature to use, between 0 and 2. diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index 36267e9061..9fe22fe3c9 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -5,6 +5,8 @@ from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from .chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam + __all__ = ["CompletionCreateParamsBase", "CompletionCreateParamsNonStreaming", "CompletionCreateParamsStreaming"] @@ -123,6 +125,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): The returned text will not contain the stop sequence. """ + stream_options: Optional[ChatCompletionStreamOptionsParam] + """Options for streaming response. Only set this when you set `stream: true`.""" + suffix: Optional[str] """The suffix that comes after a completion of inserted text. diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index c54b56a37d..1c195c4001 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -9,7 +9,9 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.chat import ChatCompletion +from openai.types.chat import ( + ChatCompletion, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -59,6 +61,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: seed=-9223372036854776000, stop="string", stream=False, + stream_options={"include_usage": True}, temperature=1, tool_choice="none", tools=[ @@ -172,6 +175,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: response_format={"type": "json_object"}, seed=-9223372036854776000, stop="string", + stream_options={"include_usage": True}, temperature=1, tool_choice="none", tools=[ @@ -289,6 +293,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn seed=-9223372036854776000, stop="string", stream=False, + stream_options={"include_usage": True}, temperature=1, tool_choice="none", tools=[ @@ -402,6 +407,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn response_format={"type": "json_object"}, seed=-9223372036854776000, stop="string", + stream_options={"include_usage": True}, temperature=1, tool_choice="none", tools=[ diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 691c4ff77f..69d914200f 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -41,6 +41,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: seed=-9223372036854776000, stop="\n", stream=False, + stream_options={"include_usage": True}, suffix="test.", temperature=1, top_p=1, @@ -99,6 +100,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: presence_penalty=-2, seed=-9223372036854776000, stop="\n", + stream_options={"include_usage": True}, suffix="test.", temperature=1, top_p=1, @@ -161,6 +163,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn seed=-9223372036854776000, stop="\n", stream=False, + stream_options={"include_usage": True}, suffix="test.", temperature=1, top_p=1, @@ -219,6 +222,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn presence_penalty=-2, seed=-9223372036854776000, stop="\n", + stream_options={"include_usage": True}, suffix="test.", temperature=1, top_p=1, From 89478ce06e2161fda416c685b774a97a041ae185 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 6 May 2024 15:07:59 -0400 Subject: [PATCH 432/914] release: 1.26.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6994d4cb49..f3dbfd2ad2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.25.2" + ".": "1.26.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa4fc4dc1..37dacdd9a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.26.0 (2024-05-06) + +Full Changelog: [v1.25.2...v1.26.0](https://github.com/openai/openai-python/compare/v1.25.2...v1.26.0) + +### Features + +* **api:** add usage metadata when streaming ([#1395](https://github.com/openai/openai-python/issues/1395)) ([3cb064b](https://github.com/openai/openai-python/commit/3cb064b10d661dbcc74b6bc1ed7d8e635ab2876a)) + ## 1.25.2 (2024-05-05) Full Changelog: [v1.25.1...v1.25.2](https://github.com/openai/openai-python/compare/v1.25.1...v1.25.2) diff --git a/pyproject.toml b/pyproject.toml index de0eb72023..bdaccf4068 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.25.2" +version = "1.26.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index c4c92f77ad..49495e5c24 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.25.2" # x-release-please-version +__version__ = "1.26.0" # x-release-please-version From c4a1dbc265ba78246e0e412a9c3e4981158c9bba Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 May 2024 16:11:15 -0400 Subject: [PATCH 433/914] feat(api): adding file purposes (#1401) --- .stats.yml | 2 +- src/openai/resources/files.py | 22 ++++++++++------------ src/openai/types/file_create_params.py | 11 +++++------ src/openai/types/file_object.py | 6 +++--- tests/api_resources/test_files.py | 12 ++++++------ 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/.stats.yml b/.stats.yml index 49956282b7..50c6b293dd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-edb5af3ade0cd27cf366b0654b90c7a81c43c433e11fc3f6e621e2c779de10d4.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-2e14236d4015bf3b956290ea8b656224a0c7b206a356c6af2a7ae43fdbceb04c.yml diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index fa03a9c0e2..086745b470 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -52,7 +52,7 @@ def create( self, *, file: FileTypes, - purpose: Literal["fine-tune", "assistants"], + purpose: Literal["assistants", "batch", "fine-tune"], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -79,12 +79,11 @@ def create( purpose: The intended purpose of the uploaded file. - Use "fine-tune" for - [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and - "assistants" for + Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Messages](https://platform.openai.com/docs/api-reference/messages). This allows - us to validate the format of the uploaded file is correct for fine-tuning. + [Messages](https://platform.openai.com/docs/api-reference/messages), "batch" for + [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). extra_headers: Send extra headers @@ -325,7 +324,7 @@ async def create( self, *, file: FileTypes, - purpose: Literal["fine-tune", "assistants"], + purpose: Literal["assistants", "batch", "fine-tune"], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -352,12 +351,11 @@ async def create( purpose: The intended purpose of the uploaded file. - Use "fine-tune" for - [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and - "assistants" for + Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Messages](https://platform.openai.com/docs/api-reference/messages). This allows - us to validate the format of the uploaded file is correct for fine-tuning. + [Messages](https://platform.openai.com/docs/api-reference/messages), "batch" for + [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). extra_headers: Send extra headers diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index 26e2da3372..3867fcb06e 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -13,13 +13,12 @@ class FileCreateParams(TypedDict, total=False): file: Required[FileTypes] """The File object (not file name) to be uploaded.""" - purpose: Required[Literal["fine-tune", "assistants"]] + purpose: Required[Literal["assistants", "batch", "fine-tune"]] """The intended purpose of the uploaded file. - Use "fine-tune" for - [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and - "assistants" for + Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Messages](https://platform.openai.com/docs/api-reference/messages). This allows - us to validate the format of the uploaded file is correct for fine-tuning. + [Messages](https://platform.openai.com/docs/api-reference/messages), "batch" for + [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for + [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). """ diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py index 589a1faf38..d24a5b1a8d 100644 --- a/src/openai/types/file_object.py +++ b/src/openai/types/file_object.py @@ -24,11 +24,11 @@ class FileObject(BaseModel): object: Literal["file"] """The object type, which is always `file`.""" - purpose: Literal["fine-tune", "fine-tune-results", "assistants", "assistants_output"] + purpose: Literal["assistants", "assistants_output", "batch", "batch_output", "fine-tune", "fine-tune-results"] """The intended purpose of the file. - Supported values are `fine-tune`, `fine-tune-results`, `assistants`, and - `assistants_output`. + Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, + `fine-tune`, and `fine-tune-results`. """ status: Literal["uploaded", "processed", "error"] diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index e5466e9eda..882f0ddbe7 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -27,7 +27,7 @@ class TestFiles: def test_method_create(self, client: OpenAI) -> None: file = client.files.create( file=b"raw file contents", - purpose="fine-tune", + purpose="assistants", ) assert_matches_type(FileObject, file, path=["response"]) @@ -35,7 +35,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.files.with_raw_response.create( file=b"raw file contents", - purpose="fine-tune", + purpose="assistants", ) assert response.is_closed is True @@ -47,7 +47,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: def test_streaming_response_create(self, client: OpenAI) -> None: with client.files.with_streaming_response.create( file=b"raw file contents", - purpose="fine-tune", + purpose="assistants", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -263,7 +263,7 @@ class TestAsyncFiles: async def test_method_create(self, async_client: AsyncOpenAI) -> None: file = await async_client.files.create( file=b"raw file contents", - purpose="fine-tune", + purpose="assistants", ) assert_matches_type(FileObject, file, path=["response"]) @@ -271,7 +271,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.files.with_raw_response.create( file=b"raw file contents", - purpose="fine-tune", + purpose="assistants", ) assert response.is_closed is True @@ -283,7 +283,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.files.with_streaming_response.create( file=b"raw file contents", - purpose="fine-tune", + purpose="assistants", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" From 064a34a70b108093efea4c05c51279e217e58afd Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 8 May 2024 16:11:44 -0400 Subject: [PATCH 434/914] release: 1.27.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f3dbfd2ad2..4eb89879b8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.26.0" + ".": "1.27.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 37dacdd9a5..3d451d36f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.27.0 (2024-05-08) + +Full Changelog: [v1.26.0...v1.27.0](https://github.com/openai/openai-python/compare/v1.26.0...v1.27.0) + +### Features + +* **api:** adding file purposes ([#1401](https://github.com/openai/openai-python/issues/1401)) ([2e9d0bd](https://github.com/openai/openai-python/commit/2e9d0bd0e4bf677ed9b21c6448e804313e026441)) + ## 1.26.0 (2024-05-06) Full Changelog: [v1.25.2...v1.26.0](https://github.com/openai/openai-python/compare/v1.25.2...v1.26.0) diff --git a/pyproject.toml b/pyproject.toml index bdaccf4068..81d7e75746 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.26.0" +version = "1.27.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 49495e5c24..4a8c619a1a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.26.0" # x-release-please-version +__version__ = "1.27.0" # x-release-please-version From 79a0b401537c5b1eef0d91487ba5fb37b01e3d5a Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 9 May 2024 16:19:42 -0400 Subject: [PATCH 435/914] feat(api): add message image content (#1405) --- .stats.yml | 2 +- api.md | 6 ++++ src/openai/resources/beta/threads/messages.py | 11 +++--- src/openai/resources/files.py | 6 ++-- .../beta/thread_create_and_run_params.py | 5 +-- src/openai/types/beta/thread_create_params.py | 5 +-- src/openai/types/beta/threads/__init__.py | 10 ++++++ src/openai/types/beta/threads/image_file.py | 12 +++++-- .../threads/image_file_content_block_param.py | 16 +++++++++ .../types/beta/threads/image_file_delta.py | 10 +++++- .../types/beta/threads/image_file_param.py | 22 ++++++++++++ src/openai/types/beta/threads/image_url.py | 23 ++++++++++++ .../beta/threads/image_url_content_block.py | 15 ++++++++ .../threads/image_url_content_block_param.py | 16 +++++++++ .../types/beta/threads/image_url_delta.py | 22 ++++++++++++ .../beta/threads/image_url_delta_block.py | 19 ++++++++++ .../types/beta/threads/image_url_param.py | 22 ++++++++++++ .../types/beta/threads/message_content.py | 5 ++- .../beta/threads/message_content_delta.py | 5 ++- .../threads/message_content_part_param.py | 13 +++++++ .../beta/threads/message_create_params.py | 5 +-- .../types/beta/threads/run_create_params.py | 5 +-- .../beta/threads/text_content_block_param.py | 15 ++++++++ src/openai/types/file_create_params.py | 3 +- src/openai/types/file_object.py | 6 ++-- tests/api_resources/beta/test_threads.py | 36 +++++++++---------- .../beta/threads/test_messages.py | 20 +++++------ tests/api_resources/beta/threads/test_runs.py | 24 ++++++------- 28 files changed, 295 insertions(+), 64 deletions(-) create mode 100644 src/openai/types/beta/threads/image_file_content_block_param.py create mode 100644 src/openai/types/beta/threads/image_file_param.py create mode 100644 src/openai/types/beta/threads/image_url.py create mode 100644 src/openai/types/beta/threads/image_url_content_block.py create mode 100644 src/openai/types/beta/threads/image_url_content_block_param.py create mode 100644 src/openai/types/beta/threads/image_url_delta.py create mode 100644 src/openai/types/beta/threads/image_url_delta_block.py create mode 100644 src/openai/types/beta/threads/image_url_param.py create mode 100644 src/openai/types/beta/threads/message_content_part_param.py create mode 100644 src/openai/types/beta/threads/text_content_block_param.py diff --git a/.stats.yml b/.stats.yml index 50c6b293dd..52e87d1b58 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-2e14236d4015bf3b956290ea8b656224a0c7b206a356c6af2a7ae43fdbceb04c.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-084b8f68408c6b689a55200a78bcf233769bfcd8e999d9fadaeb399152b05bcd.yml diff --git a/api.md b/api.md index 696075eff3..de69f11dca 100644 --- a/api.md +++ b/api.md @@ -374,14 +374,20 @@ from openai.types.beta.threads import ( ImageFileContentBlock, ImageFileDelta, ImageFileDeltaBlock, + ImageURL, + ImageURLContentBlock, + ImageURLDelta, + ImageURLDeltaBlock, Message, MessageContent, MessageContentDelta, + MessageContentPartParam, MessageDeleted, MessageDelta, MessageDeltaEvent, Text, TextContentBlock, + TextContentBlockParam, TextDelta, TextDeltaBlock, ) diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index 0799feed23..f0832515ce 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal import httpx @@ -24,6 +24,7 @@ from ....types.beta.threads import message_list_params, message_create_params, message_update_params from ....types.beta.threads.message import Message from ....types.beta.threads.message_deleted import MessageDeleted +from ....types.beta.threads.message_content_part_param import MessageContentPartParam __all__ = ["Messages", "AsyncMessages"] @@ -41,7 +42,7 @@ def create( self, thread_id: str, *, - content: str, + content: Union[str, Iterable[MessageContentPartParam]], role: Literal["user", "assistant"], attachments: Optional[Iterable[message_create_params.Attachment]] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, @@ -56,7 +57,7 @@ def create( Create a message. Args: - content: The content of the message. + content: The text contents of the message. role: The role of the entity that is creating the message. Allowed values include: @@ -304,7 +305,7 @@ async def create( self, thread_id: str, *, - content: str, + content: Union[str, Iterable[MessageContentPartParam]], role: Literal["user", "assistant"], attachments: Optional[Iterable[message_create_params.Attachment]] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, @@ -319,7 +320,7 @@ async def create( Create a message. Args: - content: The content of the message. + content: The text contents of the message. role: The role of the entity that is creating the message. Allowed values include: diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 086745b470..32f5111340 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -81,7 +81,8 @@ def create( Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Messages](https://platform.openai.com/docs/api-reference/messages), "batch" for + [Message](https://platform.openai.com/docs/api-reference/messages) files, + "vision" for Assistants image file inputs, "batch" for [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). @@ -353,7 +354,8 @@ async def create( Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Messages](https://platform.openai.com/docs/api-reference/messages), "batch" for + [Message](https://platform.openai.com/docs/api-reference/messages) files, + "vision" for Assistants image file inputs, "batch" for [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 60510965a2..80349678c2 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -9,6 +9,7 @@ from .file_search_tool_param import FileSearchToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam +from .threads.message_content_part_param import MessageContentPartParam from .assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = [ @@ -184,8 +185,8 @@ class ThreadMessageAttachment(TypedDict, total=False): class ThreadMessage(TypedDict, total=False): - content: Required[str] - """The content of the message.""" + content: Required[Union[str, Iterable[MessageContentPartParam]]] + """The text contents of the message.""" role: Required[Literal["user", "assistant"]] """The role of the entity that is creating the message. Allowed values include: diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index ab2df21ed7..ccf50d58dc 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -7,6 +7,7 @@ from .file_search_tool_param import FileSearchToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam +from .threads.message_content_part_param import MessageContentPartParam __all__ = [ "ThreadCreateParams", @@ -56,8 +57,8 @@ class MessageAttachment(TypedDict, total=False): class Message(TypedDict, total=False): - content: Required[str] - """The content of the message.""" + content: Required[Union[str, Iterable[MessageContentPartParam]]] + """The text contents of the message.""" role: Required[Literal["user", "assistant"]] """The role of the entity that is creating the message. Allowed values include: diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index 1e38d5eaa1..023d76fc13 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -5,16 +5,20 @@ from .run import Run as Run from .text import Text as Text from .message import Message as Message +from .image_url import ImageURL as ImageURL from .annotation import Annotation as Annotation from .image_file import ImageFile as ImageFile from .run_status import RunStatus as RunStatus from .text_delta import TextDelta as TextDelta from .message_delta import MessageDelta as MessageDelta +from .image_url_delta import ImageURLDelta as ImageURLDelta +from .image_url_param import ImageURLParam as ImageURLParam from .message_content import MessageContent as MessageContent from .message_deleted import MessageDeleted as MessageDeleted from .run_list_params import RunListParams as RunListParams from .annotation_delta import AnnotationDelta as AnnotationDelta from .image_file_delta import ImageFileDelta as ImageFileDelta +from .image_file_param import ImageFileParam as ImageFileParam from .text_delta_block import TextDeltaBlock as TextDeltaBlock from .run_create_params import RunCreateParams as RunCreateParams from .run_update_params import RunUpdateParams as RunUpdateParams @@ -22,13 +26,19 @@ from .message_delta_event import MessageDeltaEvent as MessageDeltaEvent from .message_list_params import MessageListParams as MessageListParams from .file_path_annotation import FilePathAnnotation as FilePathAnnotation +from .image_url_delta_block import ImageURLDeltaBlock as ImageURLDeltaBlock from .message_content_delta import MessageContentDelta as MessageContentDelta from .message_create_params import MessageCreateParams as MessageCreateParams from .message_update_params import MessageUpdateParams as MessageUpdateParams from .image_file_delta_block import ImageFileDeltaBlock as ImageFileDeltaBlock +from .image_url_content_block import ImageURLContentBlock as ImageURLContentBlock from .file_citation_annotation import FileCitationAnnotation as FileCitationAnnotation from .image_file_content_block import ImageFileContentBlock as ImageFileContentBlock +from .text_content_block_param import TextContentBlockParam as TextContentBlockParam from .file_path_delta_annotation import FilePathDeltaAnnotation as FilePathDeltaAnnotation +from .message_content_part_param import MessageContentPartParam as MessageContentPartParam +from .image_url_content_block_param import ImageURLContentBlockParam as ImageURLContentBlockParam from .file_citation_delta_annotation import FileCitationDeltaAnnotation as FileCitationDeltaAnnotation +from .image_file_content_block_param import ImageFileContentBlockParam as ImageFileContentBlockParam from .run_submit_tool_outputs_params import RunSubmitToolOutputsParams as RunSubmitToolOutputsParams from .required_action_function_tool_call import RequiredActionFunctionToolCall as RequiredActionFunctionToolCall diff --git a/src/openai/types/beta/threads/image_file.py b/src/openai/types/beta/threads/image_file.py index 651a247d21..6000d97500 100644 --- a/src/openai/types/beta/threads/image_file.py +++ b/src/openai/types/beta/threads/image_file.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - +from typing import Optional +from typing_extensions import Literal from ...._models import BaseModel @@ -11,5 +12,12 @@ class ImageFile(BaseModel): file_id: str """ The [File](https://platform.openai.com/docs/api-reference/files) ID of the image - in the message content. + in the message content. Set `purpose="vision"` when uploading the File if you + need to later display the file content. + """ + + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image if specified by the user. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. """ diff --git a/src/openai/types/beta/threads/image_file_content_block_param.py b/src/openai/types/beta/threads/image_file_content_block_param.py new file mode 100644 index 0000000000..48d94bee36 --- /dev/null +++ b/src/openai/types/beta/threads/image_file_content_block_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .image_file_param import ImageFileParam + +__all__ = ["ImageFileContentBlockParam"] + + +class ImageFileContentBlockParam(TypedDict, total=False): + image_file: Required[ImageFileParam] + + type: Required[Literal["image_file"]] + """Always `image_file`.""" diff --git a/src/openai/types/beta/threads/image_file_delta.py b/src/openai/types/beta/threads/image_file_delta.py index b0b1d32fa2..4581184c7a 100644 --- a/src/openai/types/beta/threads/image_file_delta.py +++ b/src/openai/types/beta/threads/image_file_delta.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal from ...._models import BaseModel @@ -8,8 +9,15 @@ class ImageFileDelta(BaseModel): + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image if specified by the user. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. + """ + file_id: Optional[str] = None """ The [File](https://platform.openai.com/docs/api-reference/files) ID of the image - in the message content. + in the message content. Set `purpose="vision"` when uploading the File if you + need to later display the file content. """ diff --git a/src/openai/types/beta/threads/image_file_param.py b/src/openai/types/beta/threads/image_file_param.py new file mode 100644 index 0000000000..e4a85358b9 --- /dev/null +++ b/src/openai/types/beta/threads/image_file_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ImageFileParam"] + + +class ImageFileParam(TypedDict, total=False): + file_id: Required[str] + """ + The [File](https://platform.openai.com/docs/api-reference/files) ID of the image + in the message content. Set `purpose="vision"` when uploading the File if you + need to later display the file content. + """ + + detail: Literal["auto", "low", "high"] + """Specifies the detail level of the image if specified by the user. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. + """ diff --git a/src/openai/types/beta/threads/image_url.py b/src/openai/types/beta/threads/image_url.py new file mode 100644 index 0000000000..d1fac147b2 --- /dev/null +++ b/src/openai/types/beta/threads/image_url.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ImageURL"] + + +class ImageURL(BaseModel): + url: str + """ + The external URL of the image, must be a supported image types: jpeg, jpg, png, + gif, webp. + """ + + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. Default + value is `auto` + """ diff --git a/src/openai/types/beta/threads/image_url_content_block.py b/src/openai/types/beta/threads/image_url_content_block.py new file mode 100644 index 0000000000..40a16c1df8 --- /dev/null +++ b/src/openai/types/beta/threads/image_url_content_block.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .image_url import ImageURL +from ...._models import BaseModel + +__all__ = ["ImageURLContentBlock"] + + +class ImageURLContentBlock(BaseModel): + image_url: ImageURL + + type: Literal["image_url"] + """The type of the content part.""" diff --git a/src/openai/types/beta/threads/image_url_content_block_param.py b/src/openai/types/beta/threads/image_url_content_block_param.py new file mode 100644 index 0000000000..585b926c58 --- /dev/null +++ b/src/openai/types/beta/threads/image_url_content_block_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .image_url_param import ImageURLParam + +__all__ = ["ImageURLContentBlockParam"] + + +class ImageURLContentBlockParam(TypedDict, total=False): + image_url: Required[ImageURLParam] + + type: Required[Literal["image_url"]] + """The type of the content part.""" diff --git a/src/openai/types/beta/threads/image_url_delta.py b/src/openai/types/beta/threads/image_url_delta.py new file mode 100644 index 0000000000..e402671908 --- /dev/null +++ b/src/openai/types/beta/threads/image_url_delta.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ImageURLDelta"] + + +class ImageURLDelta(BaseModel): + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. + """ + + url: Optional[str] = None + """ + The URL of the image, must be a supported image types: jpeg, jpg, png, gif, + webp. + """ diff --git a/src/openai/types/beta/threads/image_url_delta_block.py b/src/openai/types/beta/threads/image_url_delta_block.py new file mode 100644 index 0000000000..5252da12dd --- /dev/null +++ b/src/openai/types/beta/threads/image_url_delta_block.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .image_url_delta import ImageURLDelta + +__all__ = ["ImageURLDeltaBlock"] + + +class ImageURLDeltaBlock(BaseModel): + index: int + """The index of the content part in the message.""" + + type: Literal["image_url"] + """Always `image_url`.""" + + image_url: Optional[ImageURLDelta] = None diff --git a/src/openai/types/beta/threads/image_url_param.py b/src/openai/types/beta/threads/image_url_param.py new file mode 100644 index 0000000000..6b7e427edd --- /dev/null +++ b/src/openai/types/beta/threads/image_url_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ImageURLParam"] + + +class ImageURLParam(TypedDict, total=False): + url: Required[str] + """ + The external URL of the image, must be a supported image types: jpeg, jpg, png, + gif, webp. + """ + + detail: Literal["auto", "low", "high"] + """Specifies the detail level of the image. + + `low` uses fewer tokens, you can opt in to high resolution using `high`. Default + value is `auto` + """ diff --git a/src/openai/types/beta/threads/message_content.py b/src/openai/types/beta/threads/message_content.py index bc79b39fd4..4f17d14786 100644 --- a/src/openai/types/beta/threads/message_content.py +++ b/src/openai/types/beta/threads/message_content.py @@ -5,8 +5,11 @@ from ...._utils import PropertyInfo from .text_content_block import TextContentBlock +from .image_url_content_block import ImageURLContentBlock from .image_file_content_block import ImageFileContentBlock __all__ = ["MessageContent"] -MessageContent = Annotated[Union[ImageFileContentBlock, TextContentBlock], PropertyInfo(discriminator="type")] +MessageContent = Annotated[ + Union[ImageFileContentBlock, ImageURLContentBlock, TextContentBlock], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/threads/message_content_delta.py b/src/openai/types/beta/threads/message_content_delta.py index 3cbc22c94b..6c5f732b12 100644 --- a/src/openai/types/beta/threads/message_content_delta.py +++ b/src/openai/types/beta/threads/message_content_delta.py @@ -5,8 +5,11 @@ from ...._utils import PropertyInfo from .text_delta_block import TextDeltaBlock +from .image_url_delta_block import ImageURLDeltaBlock from .image_file_delta_block import ImageFileDeltaBlock __all__ = ["MessageContentDelta"] -MessageContentDelta = Annotated[Union[ImageFileDeltaBlock, TextDeltaBlock], PropertyInfo(discriminator="type")] +MessageContentDelta = Annotated[ + Union[ImageFileDeltaBlock, TextDeltaBlock, ImageURLDeltaBlock], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/threads/message_content_part_param.py b/src/openai/types/beta/threads/message_content_part_param.py new file mode 100644 index 0000000000..d11442a3a9 --- /dev/null +++ b/src/openai/types/beta/threads/message_content_part_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union + +from .text_content_block_param import TextContentBlockParam +from .image_url_content_block_param import ImageURLContentBlockParam +from .image_file_content_block_param import ImageFileContentBlockParam + +__all__ = ["MessageContentPartParam"] + +MessageContentPartParam = Union[ImageFileContentBlockParam, ImageURLContentBlockParam, TextContentBlockParam] diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index 5cead598f0..3668df950d 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -6,14 +6,15 @@ from typing_extensions import Literal, Required, TypedDict from ..file_search_tool_param import FileSearchToolParam +from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam __all__ = ["MessageCreateParams", "Attachment", "AttachmentTool"] class MessageCreateParams(TypedDict, total=False): - content: Required[str] - """The content of the message.""" + content: Required[Union[str, Iterable[MessageContentPartParam]]] + """The text contents of the message.""" role: Required[Literal["user", "assistant"]] """The role of the entity that is creating the message. Allowed values include: diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 2e4823bacd..9f534045ae 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -7,6 +7,7 @@ from ..assistant_tool_param import AssistantToolParam from ..file_search_tool_param import FileSearchToolParam +from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam from ..assistant_tool_choice_option_param import AssistantToolChoiceOptionParam from ..assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -175,8 +176,8 @@ class AdditionalMessageAttachment(TypedDict, total=False): class AdditionalMessage(TypedDict, total=False): - content: Required[str] - """The content of the message.""" + content: Required[Union[str, Iterable[MessageContentPartParam]]] + """The text contents of the message.""" role: Required[Literal["user", "assistant"]] """The role of the entity that is creating the message. Allowed values include: diff --git a/src/openai/types/beta/threads/text_content_block_param.py b/src/openai/types/beta/threads/text_content_block_param.py new file mode 100644 index 0000000000..6313de32cc --- /dev/null +++ b/src/openai/types/beta/threads/text_content_block_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["TextContentBlockParam"] + + +class TextContentBlockParam(TypedDict, total=False): + text: Required[str] + """Text content to be sent to the model""" + + type: Required[Literal["text"]] + """Always `text`.""" diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index 3867fcb06e..caa913d4d2 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -18,7 +18,8 @@ class FileCreateParams(TypedDict, total=False): Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Messages](https://platform.openai.com/docs/api-reference/messages), "batch" for + [Message](https://platform.openai.com/docs/api-reference/messages) files, + "vision" for Assistants image file inputs, "batch" for [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). """ diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py index d24a5b1a8d..6e2bf310a4 100644 --- a/src/openai/types/file_object.py +++ b/src/openai/types/file_object.py @@ -24,11 +24,13 @@ class FileObject(BaseModel): object: Literal["file"] """The object type, which is always `file`.""" - purpose: Literal["assistants", "assistants_output", "batch", "batch_output", "fine-tune", "fine-tune-results"] + purpose: Literal[ + "assistants", "assistants_output", "batch", "batch_output", "fine-tune", "fine-tune-results", "vision" + ] """The intended purpose of the file. Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, - `fine-tune`, and `fine-tune-results`. + `fine-tune`, `fine-tune-results` and `vision`. """ status: Literal["uploaded", "processed", "error"] diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 715e3e8726..02c6e2586e 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -32,7 +32,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: messages=[ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -63,7 +63,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -94,7 +94,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -309,7 +309,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "messages": [ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -340,7 +340,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -371,7 +371,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -477,7 +477,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "messages": [ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -508,7 +508,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -539,7 +539,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -637,7 +637,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> messages=[ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -668,7 +668,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -699,7 +699,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -914,7 +914,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "messages": [ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -945,7 +945,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -976,7 +976,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -1082,7 +1082,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "messages": [ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -1113,7 +1113,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -1144,7 +1144,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index fb42d509a1..b5be32a421 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -25,7 +25,7 @@ class TestMessages: def test_method_create(self, client: OpenAI) -> None: message = client.beta.threads.messages.create( "string", - content="x", + content="string", role="user", ) assert_matches_type(Message, message, path=["response"]) @@ -34,7 +34,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: message = client.beta.threads.messages.create( "string", - content="x", + content="string", role="user", attachments=[ { @@ -58,7 +58,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.beta.threads.messages.with_raw_response.create( "string", - content="x", + content="string", role="user", ) @@ -71,7 +71,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: def test_streaming_response_create(self, client: OpenAI) -> None: with client.beta.threads.messages.with_streaming_response.create( "string", - content="x", + content="string", role="user", ) as response: assert not response.is_closed @@ -87,7 +87,7 @@ def test_path_params_create(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.beta.threads.messages.with_raw_response.create( "", - content="x", + content="string", role="user", ) @@ -302,7 +302,7 @@ class TestAsyncMessages: async def test_method_create(self, async_client: AsyncOpenAI) -> None: message = await async_client.beta.threads.messages.create( "string", - content="x", + content="string", role="user", ) assert_matches_type(Message, message, path=["response"]) @@ -311,7 +311,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: message = await async_client.beta.threads.messages.create( "string", - content="x", + content="string", role="user", attachments=[ { @@ -335,7 +335,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.threads.messages.with_raw_response.create( "string", - content="x", + content="string", role="user", ) @@ -348,7 +348,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.threads.messages.with_streaming_response.create( "string", - content="x", + content="string", role="user", ) as response: assert not response.is_closed @@ -364,7 +364,7 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.beta.threads.messages.with_raw_response.create( "", - content="x", + content="string", role="user", ) diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 429c9bdeeb..089dd1253e 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -39,7 +39,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: additional_messages=[ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -70,7 +70,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -101,7 +101,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -202,7 +202,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: additional_messages=[ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -233,7 +233,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -264,7 +264,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -703,7 +703,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn additional_messages=[ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -734,7 +734,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -765,7 +765,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -866,7 +866,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn additional_messages=[ { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -897,7 +897,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", @@ -928,7 +928,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn }, { "role": "user", - "content": "x", + "content": "string", "attachments": [ { "file_id": "string", From 9111ae02cecee700ad84d2e285f6e7cb1268c3fb Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 9 May 2024 16:20:09 -0400 Subject: [PATCH 436/914] release: 1.28.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4eb89879b8..31d8238881 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.27.0" + ".": "1.28.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d451d36f8..1feb7e4967 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.28.0 (2024-05-09) + +Full Changelog: [v1.27.0...v1.28.0](https://github.com/openai/openai-python/compare/v1.27.0...v1.28.0) + +### Features + +* **api:** add message image content ([#1405](https://github.com/openai/openai-python/issues/1405)) ([a115de6](https://github.com/openai/openai-python/commit/a115de60ce1ca503a7659bb9a19c18699d4d9bcb)) + ## 1.27.0 (2024-05-08) Full Changelog: [v1.26.0...v1.27.0](https://github.com/openai/openai-python/compare/v1.26.0...v1.27.0) diff --git a/pyproject.toml b/pyproject.toml index 81d7e75746..1295864815 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.27.0" +version = "1.28.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 4a8c619a1a..eaeadf5932 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.27.0" # x-release-please-version +__version__ = "1.28.0" # x-release-please-version From 1aeaf02f662e6e925180ddfcaa26508408b2f2a4 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 10 May 2024 15:31:12 -0400 Subject: [PATCH 437/914] chore(docs): add SECURITY.md (#1408) --- SECURITY.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..c54acaf331 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainlessapi.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by OpenAI please follow the respective company's security reporting guidelines. + +### OpenAI Terms and Policies + +Our Security Policy can be found at [Security Policy URL](https://openai.com/policies/coordinated-vulnerability-disclosure-policy). + +Please contact disclosure@openai.com for any questions or concerns regarding security of our services. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. From 906ece0e45235cb170bc062902182eb8b55698b9 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sat, 11 May 2024 01:03:42 -0400 Subject: [PATCH 438/914] release: 1.28.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 31d8238881..a2b1280ba0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.28.0" + ".": "1.28.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1feb7e4967..81b5596198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.28.1 (2024-05-11) + +Full Changelog: [v1.28.0...v1.28.1](https://github.com/openai/openai-python/compare/v1.28.0...v1.28.1) + +### Chores + +* **docs:** add SECURITY.md ([#1408](https://github.com/openai/openai-python/issues/1408)) ([119970a](https://github.com/openai/openai-python/commit/119970a31b67e88c623d50855290ccf3847c10eb)) + ## 1.28.0 (2024-05-09) Full Changelog: [v1.27.0...v1.28.0](https://github.com/openai/openai-python/compare/v1.27.0...v1.28.0) diff --git a/pyproject.toml b/pyproject.toml index 1295864815..3a2351761d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.28.0" +version = "1.28.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index eaeadf5932..893c4e49a2 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.28.0" # x-release-please-version +__version__ = "1.28.1" # x-release-please-version From 3480e5061d1188cd377f34b638665772ff75b90e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 May 2024 08:07:15 -0400 Subject: [PATCH 439/914] chore(internal): bump pydantic dependency (#1413) --- requirements-dev.lock | 8 ++++---- requirements.lock | 8 ++++---- src/openai/_models.py | 20 ++++++++++++++++---- tests/test_models.py | 8 ++++---- tests/test_transform.py | 22 ++++++++++++---------- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 8cfefdd93b..6a4e12022a 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -24,7 +24,7 @@ attrs==23.1.0 azure-core==1.30.1 # via azure-identity azure-identity==1.15.0 -black==24.4.0 +black==24.4.2 # via inline-snapshot certifi==2023.7.22 # via httpcore @@ -39,7 +39,7 @@ click==8.1.7 # via inline-snapshot colorlog==6.7.0 # via nox -cryptography==42.0.5 +cryptography==42.0.7 # via azure-identity # via msal # via pyjwt @@ -111,9 +111,9 @@ py==1.11.0 # via pytest pycparser==2.22 # via cffi -pydantic==2.4.2 +pydantic==2.7.1 # via openai -pydantic-core==2.10.1 +pydantic-core==2.18.2 # via pydantic pyjwt==2.8.0 # via msal diff --git a/requirements.lock b/requirements.lock index c933d6c90e..47cf8a40e9 100644 --- a/requirements.lock +++ b/requirements.lock @@ -33,13 +33,13 @@ numpy==1.26.4 # via openai # via pandas # via pandas-stubs -pandas==2.2.1 +pandas==2.2.2 # via openai pandas-stubs==2.2.1.240316 # via openai -pydantic==2.4.2 +pydantic==2.7.1 # via openai -pydantic-core==2.10.1 +pydantic-core==2.18.2 # via pydantic python-dateutil==2.9.0.post0 # via pandas @@ -53,7 +53,7 @@ sniffio==1.3.0 # via openai tqdm==4.66.1 # via openai -types-pytz==2024.1.0.20240203 +types-pytz==2024.1.0.20240417 # via pandas-stubs typing-extensions==4.8.0 # via openai diff --git a/src/openai/_models.py b/src/openai/_models.py index ff3f54e2cd..75c68cc730 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -62,7 +62,7 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: - from pydantic_core.core_schema import ModelField, ModelFieldsSchema + from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema __all__ = ["BaseModel", "GenericModel"] @@ -251,7 +251,9 @@ def model_dump( exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, - warnings: bool = True, + warnings: bool | Literal["none", "warn", "error"] = True, + context: dict[str, Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -279,6 +281,10 @@ def model_dump( raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") return super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, @@ -300,7 +306,9 @@ def model_dump_json( exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, - warnings: bool = True, + warnings: bool | Literal["none", "warn", "error"] = True, + context: dict[str, Any] | None = None, + serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -324,6 +332,10 @@ def model_dump_json( raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, @@ -550,7 +562,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, field_schema = field["schema"] if field_schema["type"] == "literal": - for entry in field_schema["expected"]: + for entry in cast("LiteralSchema", field_schema)["expected"]: if isinstance(entry, str): mapping[entry] = variant else: diff --git a/tests/test_models.py b/tests/test_models.py index 969e4eb315..b703444248 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -31,7 +31,7 @@ class NestedModel(BaseModel): # mismatched types m = NestedModel.construct(nested="hello!") - assert m.nested == "hello!" + assert cast(Any, m.nested) == "hello!" def test_optional_nested_model() -> None: @@ -48,7 +48,7 @@ class NestedModel(BaseModel): # mismatched types m3 = NestedModel.construct(nested={"foo"}) assert isinstance(cast(Any, m3.nested), set) - assert m3.nested == {"foo"} + assert cast(Any, m3.nested) == {"foo"} def test_list_nested_model() -> None: @@ -323,7 +323,7 @@ class Model(BaseModel): assert len(m.items) == 2 assert isinstance(m.items[0], Submodel1) assert m.items[0].level == -1 - assert m.items[1] == 156 + assert cast(Any, m.items[1]) == 156 def test_union_of_lists() -> None: @@ -355,7 +355,7 @@ class Model(BaseModel): assert len(m.items) == 2 assert isinstance(m.items[0], SubModel1) assert m.items[0].level == -1 - assert m.items[1] == 156 + assert cast(Any, m.items[1]) == 156 def test_dict_of_union() -> None: diff --git a/tests/test_transform.py b/tests/test_transform.py index 0d17e8a972..1eb6cde9d6 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -260,20 +260,22 @@ class MyModel(BaseModel): @parametrize @pytest.mark.asyncio async def test_pydantic_model_to_dictionary(use_async: bool) -> None: - assert await transform(MyModel(foo="hi!"), Any, use_async) == {"foo": "hi!"} - assert await transform(MyModel.construct(foo="hi!"), Any, use_async) == {"foo": "hi!"} + assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"} @parametrize @pytest.mark.asyncio async def test_pydantic_empty_model(use_async: bool) -> None: - assert await transform(MyModel.construct(), Any, use_async) == {} + assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {} @parametrize @pytest.mark.asyncio async def test_pydantic_unknown_field(use_async: bool) -> None: - assert await transform(MyModel.construct(my_untyped_field=True), Any, use_async) == {"my_untyped_field": True} + assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == { + "my_untyped_field": True + } @parametrize @@ -285,7 +287,7 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None: params = await transform(model, Any, use_async) else: params = await transform(model, Any, use_async) - assert params == {"foo": True} + assert cast(Any, params) == {"foo": True} @parametrize @@ -297,7 +299,7 @@ async def test_pydantic_mismatched_object_type(use_async: bool) -> None: params = await transform(model, Any, use_async) else: params = await transform(model, Any, use_async) - assert params == {"foo": {"hello": "world"}} + assert cast(Any, params) == {"foo": {"hello": "world"}} class ModelNestedObjects(BaseModel): @@ -309,7 +311,7 @@ class ModelNestedObjects(BaseModel): async def test_pydantic_nested_objects(use_async: bool) -> None: model = ModelNestedObjects.construct(nested={"foo": "stainless"}) assert isinstance(model.nested, MyModel) - assert await transform(model, Any, use_async) == {"nested": {"foo": "stainless"}} + assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}} class ModelWithDefaultField(BaseModel): @@ -325,19 +327,19 @@ async def test_pydantic_default_field(use_async: bool) -> None: model = ModelWithDefaultField.construct() assert model.with_none_default is None assert model.with_str_default == "foo" - assert await transform(model, Any, use_async) == {} + assert cast(Any, await transform(model, Any, use_async)) == {} # should be included when the default value is explicitly given model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") assert model.with_none_default is None assert model.with_str_default == "foo" - assert await transform(model, Any, use_async) == {"with_none_default": None, "with_str_default": "foo"} + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"} # should be included when a non-default value is explicitly given model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") assert model.with_none_default == "bar" assert model.with_str_default == "baz" - assert await transform(model, Any, use_async) == {"with_none_default": "bar", "with_str_default": "baz"} + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} class TypedDictIterableUnion(TypedDict): From 2dc6d42a35a870068f3bb900731b1798ffe5ea52 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 May 2024 09:46:56 -0400 Subject: [PATCH 440/914] fix(client): accidental blocking sleep in async code (#1415) --- src/openai/resources/beta/threads/runs/runs.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 4268d41390..2cd6e60239 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -2,7 +2,6 @@ from __future__ import annotations -import time import typing_extensions from typing import Union, Iterable, Optional, overload from functools import partial @@ -1105,7 +1104,7 @@ def poll( else: poll_interval_ms = 1000 - time.sleep(poll_interval_ms / 1000) + self._sleep(poll_interval_ms / 1000) @overload def stream( @@ -2639,7 +2638,7 @@ async def poll( else: poll_interval_ms = 1000 - time.sleep(poll_interval_ms / 1000) + await self._sleep(poll_interval_ms / 1000) @overload def stream( From c0afbc75155ce0fe090f759152e0bc683a9b77a5 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 May 2024 09:47:25 -0400 Subject: [PATCH 441/914] release: 1.28.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a2b1280ba0..cad349f4b9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.28.1" + ".": "1.28.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 81b5596198..a68877e8d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.28.2 (2024-05-13) + +Full Changelog: [v1.28.1...v1.28.2](https://github.com/openai/openai-python/compare/v1.28.1...v1.28.2) + +### Bug Fixes + +* **client:** accidental blocking sleep in async code ([#1415](https://github.com/openai/openai-python/issues/1415)) ([0ac6ecb](https://github.com/openai/openai-python/commit/0ac6ecb8d4e52f895bc3ae1f589f22ddaaef6204)) + + +### Chores + +* **internal:** bump pydantic dependency ([#1413](https://github.com/openai/openai-python/issues/1413)) ([ed73d1d](https://github.com/openai/openai-python/commit/ed73d1db540714e29a1ba30e3aa6429aae8b1dd8)) + ## 1.28.1 (2024-05-11) Full Changelog: [v1.28.0...v1.28.1](https://github.com/openai/openai-python/compare/v1.28.0...v1.28.1) diff --git a/pyproject.toml b/pyproject.toml index 3a2351761d..86e30d4097 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.28.1" +version = "1.28.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 893c4e49a2..2ae163b1c6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.28.1" # x-release-please-version +__version__ = "1.28.2" # x-release-please-version From ac6c9004d26e5d553bc84516ebd6ba9de9ec090b Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 May 2024 14:15:32 -0400 Subject: [PATCH 442/914] feat(api): add gpt-4o model (#1417) --- .stats.yml | 2 +- src/openai/resources/beta/assistants.py | 4 ++ .../resources/beta/threads/runs/runs.py | 44 +++++++++++++++++++ src/openai/resources/beta/threads/threads.py | 32 ++++++++++++++ .../types/beta/assistant_create_params.py | 2 + .../beta/thread_create_and_run_params.py | 2 + .../types/beta/threads/run_create_params.py | 2 + src/openai/types/chat_model.py | 2 + 8 files changed, 89 insertions(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 52e87d1b58..f44b9b46af 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-084b8f68408c6b689a55200a78bcf233769bfcd8e999d9fadaeb399152b05bcd.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-47007cc1aa5bc7b74107a99b377925978a0bd376ed67bdae724e80d5d0b63d57.yml diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 923ad95a54..2304452493 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -49,6 +49,8 @@ def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -434,6 +436,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 2cd6e60239..84b0d63c25 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -85,6 +85,8 @@ def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -230,6 +232,8 @@ def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -374,6 +378,8 @@ def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -517,6 +523,8 @@ def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -784,6 +792,8 @@ def create_and_poll( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -874,6 +884,8 @@ def create_and_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -928,6 +940,8 @@ def create_and_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -982,6 +996,8 @@ def create_and_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1120,6 +1136,8 @@ def stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1173,6 +1191,8 @@ def stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1226,6 +1246,8 @@ def stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1617,6 +1639,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1762,6 +1786,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1906,6 +1932,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2049,6 +2077,8 @@ async def create( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2316,6 +2346,8 @@ async def create_and_poll( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2406,6 +2438,8 @@ def create_and_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2460,6 +2494,8 @@ def create_and_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2514,6 +2550,8 @@ def create_and_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2654,6 +2692,8 @@ def stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2707,6 +2747,8 @@ def stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2760,6 +2802,8 @@ def stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 2455272658..a68cbe0ddd 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -267,6 +267,8 @@ def create_and_run( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -411,6 +413,8 @@ def create_and_run( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -554,6 +558,8 @@ def create_and_run( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -696,6 +702,8 @@ def create_and_run( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -776,6 +784,8 @@ def create_and_run_poll( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -854,6 +864,8 @@ def create_and_run_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -906,6 +918,8 @@ def create_and_run_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -958,6 +972,8 @@ def create_and_run_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1241,6 +1257,8 @@ async def create_and_run( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1385,6 +1403,8 @@ async def create_and_run( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1528,6 +1548,8 @@ async def create_and_run( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1670,6 +1692,8 @@ async def create_and_run( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1750,6 +1774,8 @@ async def create_and_run_poll( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1830,6 +1856,8 @@ def create_and_run_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1882,6 +1910,8 @@ def create_and_run_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1934,6 +1964,8 @@ def create_and_run_stream( model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index e9ff66dfc3..d44e15f4ca 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -22,6 +22,8 @@ class AssistantCreateParams(TypedDict, total=False): Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 80349678c2..9567c8320a 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -75,6 +75,8 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 9f534045ae..dca47a5c73 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -77,6 +77,8 @@ class RunCreateParamsBase(TypedDict, total=False): model: Union[ str, Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index 219dab5138..0d2937ea32 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -5,6 +5,8 @@ __all__ = ["ChatModel"] ChatModel = Literal[ + "gpt-4o", + "gpt-4o-2024-05-13", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", From 0b4a180f3da5a018d937686aa70e54000edef878 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 13 May 2024 14:15:58 -0400 Subject: [PATCH 443/914] release: 1.29.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cad349f4b9..b8af36c3aa 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.28.2" + ".": "1.29.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a68877e8d6..d74404aecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.29.0 (2024-05-13) + +Full Changelog: [v1.28.2...v1.29.0](https://github.com/openai/openai-python/compare/v1.28.2...v1.29.0) + +### Features + +* **api:** add gpt-4o model ([#1417](https://github.com/openai/openai-python/issues/1417)) ([4f09f8c](https://github.com/openai/openai-python/commit/4f09f8c6cc4450f5e61f158f1bd54c513063a1a8)) + ## 1.28.2 (2024-05-13) Full Changelog: [v1.28.1...v1.28.2](https://github.com/openai/openai-python/compare/v1.28.1...v1.28.2) diff --git a/pyproject.toml b/pyproject.toml index 86e30d4097..c8f549500d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.28.2" +version = "1.29.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 2ae163b1c6..48332e626e 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.28.2" # x-release-please-version +__version__ = "1.29.0" # x-release-please-version From d4c2c7a34850bd1d28123704f9b9febba8a0781d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 14 May 2024 07:29:45 -0400 Subject: [PATCH 444/914] feat(api): add incomplete state (#1420) --- .stats.yml | 2 +- src/openai/resources/batches.py | 18 ++++++---- src/openai/resources/beta/assistants.py | 20 ++++++----- .../resources/beta/threads/runs/runs.py | 34 ++++++++++-------- src/openai/resources/beta/threads/threads.py | 30 +++++++++------- src/openai/resources/files.py | 36 +++++++++++-------- src/openai/types/batch_create_params.py | 9 +++-- src/openai/types/beta/assistant.py | 6 ++-- .../types/beta/assistant_create_params.py | 6 ++-- .../types/beta/assistant_update_params.py | 6 ++-- .../beta/thread_create_and_run_params.py | 6 ++-- src/openai/types/beta/threads/run.py | 10 +++--- .../types/beta/threads/run_create_params.py | 6 ++-- src/openai/types/beta/threads/run_status.py | 10 +++++- 14 files changed, 120 insertions(+), 79 deletions(-) diff --git a/.stats.yml b/.stats.yml index f44b9b46af..2e5c705a0d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-47007cc1aa5bc7b74107a99b377925978a0bd376ed67bdae724e80d5d0b63d57.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-363dd904e5d6e65b3a323fc88e6b502fb23a6aa319be219273e3ee47c7530993.yml diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 64a3014c37..db4c4da235 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -40,7 +40,7 @@ def create( self, *, completion_window: Literal["24h"], - endpoint: Literal["/v1/chat/completions", "/v1/embeddings"], + endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -58,7 +58,9 @@ def create( is supported. endpoint: The endpoint to be used for all requests in the batch. Currently - `/v1/chat/completions` and `/v1/embeddings` are supported. + `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` are supported. + Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 + embedding inputs across all requests in the batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. @@ -67,7 +69,8 @@ def create( Your input file must be formatted as a [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), - and must be uploaded with the purpose `batch`. + and must be uploaded with the purpose `batch`. The file can contain up to 50,000 + requests, and can be up to 100 MB in size. metadata: Optional custom metadata for the batch. @@ -228,7 +231,7 @@ async def create( self, *, completion_window: Literal["24h"], - endpoint: Literal["/v1/chat/completions", "/v1/embeddings"], + endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -246,7 +249,9 @@ async def create( is supported. endpoint: The endpoint to be used for all requests in the batch. Currently - `/v1/chat/completions` and `/v1/embeddings` are supported. + `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` are supported. + Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 + embedding inputs across all requests in the batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. @@ -255,7 +260,8 @@ async def create( Your input file must be formatted as a [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), - and must be uploaded with the purpose `batch`. + and must be uploaded with the purpose `batch`. The file can contain up to 50,000 + requests, and can be up to 100 MB in size. metadata: Optional custom metadata for the batch. diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 2304452493..5912aff77a 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -110,8 +110,9 @@ def create( name: The name of the assistant. The maximum length is 256 characters. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -254,8 +255,9 @@ def update( name: The name of the assistant. The maximum length is 256 characters. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -497,8 +499,9 @@ async def create( name: The name of the assistant. The maximum length is 256 characters. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -641,8 +644,9 @@ async def update( name: The name of the assistant. The maximum length is 256 characters. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 84b0d63c25..c37071529c 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -164,8 +164,9 @@ def create( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -314,8 +315,9 @@ def create( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -460,8 +462,9 @@ def create( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1097,7 +1100,7 @@ def poll( if is_given(poll_interval_ms): extra_headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) - terminal_states = {"requires_action", "cancelled", "completed", "failed", "expired"} + terminal_states = {"requires_action", "cancelled", "completed", "failed", "expired", "incomplete"} while True: response = self.with_raw_response.retrieve( thread_id=thread_id, @@ -1718,8 +1721,9 @@ async def create( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1868,8 +1872,9 @@ async def create( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -2014,8 +2019,9 @@ async def create( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -2653,7 +2659,7 @@ async def poll( if is_given(poll_interval_ms): extra_headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) - terminal_states = {"requires_action", "cancelled", "completed", "failed", "expired"} + terminal_states = {"requires_action", "cancelled", "completed", "failed", "expired", "incomplete"} while True: response = await self.with_raw_response.retrieve( thread_id=thread_id, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index a68cbe0ddd..36cdd03f91 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -341,8 +341,9 @@ def create_and_run( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -490,8 +491,9 @@ def create_and_run( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -635,8 +637,9 @@ def create_and_run( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1331,8 +1334,9 @@ async def create_and_run( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1480,8 +1484,9 @@ async def create_and_run( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1625,8 +1630,9 @@ async def create_and_run( assistant will be used. response_format: Specifies the format that the model must output. Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 32f5111340..aed0829dfe 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -62,14 +62,18 @@ def create( ) -> FileObject: """Upload a file that can be used across various endpoints. - The size of all the - files uploaded by one organization can be up to 100 GB. + Individual files can be + up to 512 MB, and the size of all files uploaded by one organization can be up + to 100 GB. - The size of individual files can be a maximum of 512 MB or 2 million tokens for - Assistants. See the - [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to - learn more about the types of files supported. The Fine-tuning API only supports - `.jsonl` files. + The Assistants API supports files up to 2 million tokens and of specific file + types. See the + [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for + details. + + The Fine-tuning API only supports `.jsonl` files. + + The Batch API only supports `.jsonl` files up to 100 MB in size. Please [contact us](https://help.openai.com/) if you need to increase these storage limits. @@ -335,14 +339,18 @@ async def create( ) -> FileObject: """Upload a file that can be used across various endpoints. - The size of all the - files uploaded by one organization can be up to 100 GB. + Individual files can be + up to 512 MB, and the size of all files uploaded by one organization can be up + to 100 GB. + + The Assistants API supports files up to 2 million tokens and of specific file + types. See the + [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for + details. + + The Fine-tuning API only supports `.jsonl` files. - The size of individual files can be a maximum of 512 MB or 2 million tokens for - Assistants. See the - [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to - learn more about the types of files supported. The Fine-tuning API only supports - `.jsonl` files. + The Batch API only supports `.jsonl` files up to 100 MB in size. Please [contact us](https://help.openai.com/) if you need to increase these storage limits. diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index 63b4fae91b..140380d417 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -15,10 +15,12 @@ class BatchCreateParams(TypedDict, total=False): Currently only `24h` is supported. """ - endpoint: Required[Literal["/v1/chat/completions", "/v1/embeddings"]] + endpoint: Required[Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"]] """The endpoint to be used for all requests in the batch. - Currently `/v1/chat/completions` and `/v1/embeddings` are supported. + Currently `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` are + supported. Note that `/v1/embeddings` batches are also restricted to a maximum + of 50,000 embedding inputs across all requests in the batch. """ input_file_id: Required[str] @@ -29,7 +31,8 @@ class BatchCreateParams(TypedDict, total=False): Your input file must be formatted as a [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), - and must be uploaded with the purpose `batch`. + and must be uploaded with the purpose `batch`. The file can contain up to 50,000 + requests, and can be up to 100 MB in size. """ metadata: Optional[Dict[str, str]] diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 0b997e0b0e..4e5adc766e 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -85,9 +85,9 @@ class Assistant(BaseModel): response_format: Optional[AssistantResponseFormatOption] = None """Specifies the format that the model must output. - Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index d44e15f4ca..67e7f7e78c 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -77,9 +77,9 @@ class AssistantCreateParams(TypedDict, total=False): response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. - Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 55c846ce4e..b401e1a891 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -45,9 +45,9 @@ class AssistantUpdateParams(TypedDict, total=False): response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. - Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 9567c8320a..6efe6e7aee 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -108,9 +108,9 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. - Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 6c118f27c1..8244ffd598 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -160,9 +160,9 @@ class Run(BaseModel): response_format: Optional[AssistantResponseFormatOption] = None """Specifies the format that the model must output. - Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -182,8 +182,8 @@ class Run(BaseModel): status: RunStatus """ The status of the run, which can be either `queued`, `in_progress`, - `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, or - `expired`. + `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, + `incomplete`, or `expired`. """ thread_id: str diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index dca47a5c73..90c9708596 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -110,9 +110,9 @@ class RunCreateParamsBase(TypedDict, total=False): response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. - Compatible with - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and - all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/threads/run_status.py b/src/openai/types/beta/threads/run_status.py index bf9b4e7bbf..6666d00e5a 100644 --- a/src/openai/types/beta/threads/run_status.py +++ b/src/openai/types/beta/threads/run_status.py @@ -5,5 +5,13 @@ __all__ = ["RunStatus"] RunStatus = Literal[ - "queued", "in_progress", "requires_action", "cancelling", "cancelled", "failed", "completed", "expired" + "queued", + "in_progress", + "requires_action", + "cancelling", + "cancelled", + "failed", + "completed", + "incomplete", + "expired", ] From e2214ce7243c027c4e04e39b5a11986d5e361e37 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 14 May 2024 07:30:14 -0400 Subject: [PATCH 445/914] release: 1.30.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b8af36c3aa..35c30adcff 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.29.0" + ".": "1.30.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d74404aecd..00ba4410e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.30.0 (2024-05-14) + +Full Changelog: [v1.29.0...v1.30.0](https://github.com/openai/openai-python/compare/v1.29.0...v1.30.0) + +### Features + +* **api:** add incomplete state ([#1420](https://github.com/openai/openai-python/issues/1420)) ([6484984](https://github.com/openai/openai-python/commit/648498412d1c7740e6b67ed4d0a55b89ff29d3b1)) + ## 1.29.0 (2024-05-13) Full Changelog: [v1.28.2...v1.29.0](https://github.com/openai/openai-python/compare/v1.28.2...v1.29.0) diff --git a/pyproject.toml b/pyproject.toml index c8f549500d..d7f05594b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.29.0" +version = "1.30.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 48332e626e..817eb239fd 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.29.0" # x-release-please-version +__version__ = "1.30.0" # x-release-please-version From 526a05ebd81256baa6a65fbcb649357e2b70cf51 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 14 May 2024 10:46:55 -0400 Subject: [PATCH 446/914] chore(internal): add slightly better logging to scripts (#1422) --- .github/workflows/ci.yml | 16 +++------------- scripts/format | 2 +- scripts/lint | 4 ++++ scripts/test | 1 - 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9cbc077a8c..76655ed7d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,20 +25,10 @@ jobs: RYE_INSTALL_OPTION: '--yes' - name: Install dependencies - run: | - rye sync --all-features - - - name: Run ruff - run: | - rye run check:ruff + run: rye sync --all-features - - name: Run type checking - run: | - rye run typecheck - - - name: Ensure importable - run: | - rye run python -c 'import openai' + - name: Run lints + run: ./scripts/lint test: name: test runs-on: ubuntu-latest diff --git a/scripts/format b/scripts/format index 2a9ea4664b..667ec2d7af 100755 --- a/scripts/format +++ b/scripts/format @@ -4,5 +4,5 @@ set -e cd "$(dirname "$0")/.." +echo "==> Running formatters" rye run format - diff --git a/scripts/lint b/scripts/lint index 0cc68b5157..64495ee345 100755 --- a/scripts/lint +++ b/scripts/lint @@ -4,5 +4,9 @@ set -e cd "$(dirname "$0")/.." +echo "==> Running lints" rye run lint +echo "==> Making sure it imports" +rye run python -c 'import openai' + diff --git a/scripts/test b/scripts/test index be01d04473..b3ace9013b 100755 --- a/scripts/test +++ b/scripts/test @@ -52,6 +52,5 @@ else echo fi -# Run tests echo "==> Running tests" rye run pytest "$@" From 5a89126ad208c58b9e1dbd1fbdb698e4c00f7d8e Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 14 May 2024 10:47:23 -0400 Subject: [PATCH 447/914] release: 1.30.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 35c30adcff..1f79fd2d11 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.30.0" + ".": "1.30.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 00ba4410e4..e41c8f4a93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.30.1 (2024-05-14) + +Full Changelog: [v1.30.0...v1.30.1](https://github.com/openai/openai-python/compare/v1.30.0...v1.30.1) + +### Chores + +* **internal:** add slightly better logging to scripts ([#1422](https://github.com/openai/openai-python/issues/1422)) ([43dffab](https://github.com/openai/openai-python/commit/43dffabb3bed4edf8a6e523cbb289f733a5f9b24)) + ## 1.30.0 (2024-05-14) Full Changelog: [v1.29.0...v1.30.0](https://github.com/openai/openai-python/compare/v1.29.0...v1.30.0) diff --git a/pyproject.toml b/pyproject.toml index d7f05594b5..a33e167244 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.30.0" +version = "1.30.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 817eb239fd..83411041ae 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.30.0" # x-release-please-version +__version__ = "1.30.1" # x-release-please-version From 534582307ae8cf19d5ebea9e8b030dc0aaf0484f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 23 May 2024 10:49:18 +0100 Subject: [PATCH 448/914] chore(ci): update rye install location (#1436) the site is currently down due to DNS issues --- .devcontainer/Dockerfile | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/create-releases.yml | 2 +- .github/workflows/publish-pypi.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index dd93962010..e9841a168d 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode -RUN curl -sSf https://rye-up.com/get | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash +RUN curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76655ed7d6..c084831fa9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - name: Install Rye run: | - curl -sSf https://rye-up.com/get | bash + curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 @@ -39,7 +39,7 @@ jobs: - name: Install Rye run: | - curl -sSf https://rye-up.com/get | bash + curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index a641be287b..ddc4de19ef 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -25,7 +25,7 @@ jobs: - name: Install Rye if: ${{ steps.release.outputs.releases_created }} run: | - curl -sSf https://rye-up.com/get | bash + curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 2f88f86407..db855cbbd9 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -14,7 +14,7 @@ jobs: - name: Install Rye run: | - curl -sSf https://rye-up.com/get | bash + curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 From 5dfe791aa0760d2641f10945cbfc8875496b16c7 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 23 May 2024 10:49:51 +0100 Subject: [PATCH 449/914] release: 1.30.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1f79fd2d11..623a7e2107 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.30.1" + ".": "1.30.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e41c8f4a93..68b9700f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.30.2 (2024-05-23) + +Full Changelog: [v1.30.1...v1.30.2](https://github.com/openai/openai-python/compare/v1.30.1...v1.30.2) + +### Chores + +* **ci:** update rye install location ([#1436](https://github.com/openai/openai-python/issues/1436)) ([f7cc4e7](https://github.com/openai/openai-python/commit/f7cc4e7d5d0964a4a5d53e602379770c2576e1aa)) + ## 1.30.1 (2024-05-14) Full Changelog: [v1.30.0...v1.30.1](https://github.com/openai/openai-python/compare/v1.30.0...v1.30.1) diff --git a/pyproject.toml b/pyproject.toml index a33e167244..5c16d92b76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.30.1" +version = "1.30.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 83411041ae..fc87a446a4 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.30.1" # x-release-please-version +__version__ = "1.30.2" # x-release-please-version From 001965be0a2e147e9250cf3a94f84af869fc5e05 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 23 May 2024 20:03:38 +0100 Subject: [PATCH 450/914] chore(ci): update rye install location (#1440) --- .devcontainer/Dockerfile | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/create-releases.yml | 2 +- .github/workflows/publish-pypi.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index e9841a168d..83bca8f716 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode -RUN curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash +RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c084831fa9..6fc5b36597 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - name: Install Rye run: | - curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | bash + curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 @@ -39,7 +39,7 @@ jobs: - name: Install Rye run: | - curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | bash + curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index ddc4de19ef..1ac03ede3f 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -25,7 +25,7 @@ jobs: - name: Install Rye if: ${{ steps.release.outputs.releases_created }} run: | - curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | bash + curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index db855cbbd9..aae985b27e 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -14,7 +14,7 @@ jobs: - name: Install Rye run: | - curl -sSf https://raw.githubusercontent.com/astral-sh/rye/main/scripts/install.sh | bash + curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: RYE_VERSION: 0.24.0 From 17c2876d97fb68b9d38d052cff66e414962b7f4e Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Fri, 24 May 2024 08:46:27 +0000 Subject: [PATCH 451/914] chore(internal): bump pyright (#1442) --- requirements-dev.lock | 2 +- src/openai/_utils/_utils.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 6a4e12022a..c5416cd4db 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -117,7 +117,7 @@ pydantic-core==2.18.2 # via pydantic pyjwt==2.8.0 # via msal -pyright==1.1.359 +pyright==1.1.364 pytest==7.1.1 # via pytest-asyncio pytest-asyncio==0.21.1 diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 17904ce60d..34797c2905 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -20,7 +20,7 @@ import sniffio -from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike +from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike from .._compat import parse_date as parse_date, parse_datetime as parse_datetime _T = TypeVar("_T") @@ -370,7 +370,6 @@ def file_from_path(path: str) -> FileTypes: def get_required_header(headers: HeadersLike, header: str) -> str: lower_header = header.lower() if isinstance(headers, Mapping): - headers = cast(Headers, headers) for k, v in headers.items(): if k.lower() == lower_header and isinstance(v, str): return v From 302f45a425850c13f94a2bbdd67618bfe3c33758 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 24 May 2024 15:39:44 +0100 Subject: [PATCH 452/914] chore(internal): fix lint issue (#1444) --- src/openai/lib/azure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index b76b83c61c..165b9b82e2 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -2,7 +2,7 @@ import os import inspect -from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, overload +from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, cast, overload from typing_extensions import Self, override import httpx @@ -515,7 +515,7 @@ async def _get_azure_ad_token(self) -> str | None: token = provider() if inspect.isawaitable(token): token = await token - if not token or not isinstance(token, str): + if not token or not isinstance(cast(Any, token), str): raise ValueError( f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", ) From 99f493961c926e87b5ea8d826301f8be7b07088e Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Fri, 24 May 2024 14:54:22 +0000 Subject: [PATCH 453/914] docs(contributing): update references to rye-up.com --- src/openai/lib/azure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 165b9b82e2..b76b83c61c 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -2,7 +2,7 @@ import os import inspect -from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, cast, overload +from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, overload from typing_extensions import Self, override import httpx @@ -515,7 +515,7 @@ async def _get_azure_ad_token(self) -> str | None: token = provider() if inspect.isawaitable(token): token = await token - if not token or not isinstance(cast(Any, token), str): + if not token or not isinstance(token, str): raise ValueError( f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", ) From 7fff7c842918e718e1e9dc3954f0e66aebfc2601 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 24 May 2024 15:54:48 +0100 Subject: [PATCH 454/914] release: 1.30.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 623a7e2107..6c6045ba02 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.30.2" + ".": "1.30.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 68b9700f77..7276bd8c03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.30.3 (2024-05-24) + +Full Changelog: [v1.30.2...v1.30.3](https://github.com/openai/openai-python/compare/v1.30.2...v1.30.3) + +### Chores + +* **ci:** update rye install location ([#1440](https://github.com/openai/openai-python/issues/1440)) ([8a0e5bf](https://github.com/openai/openai-python/commit/8a0e5bf4c03d9c714799fad43be68ac9c2b1f37a)) +* **internal:** bump pyright ([#1442](https://github.com/openai/openai-python/issues/1442)) ([64a151e](https://github.com/openai/openai-python/commit/64a151eae705d55484f870df461434c0a6961e2b)) +* **internal:** fix lint issue ([#1444](https://github.com/openai/openai-python/issues/1444)) ([b0eb458](https://github.com/openai/openai-python/commit/b0eb4582e050b0a25af3d80d2cb584bfc7cd11ab)) + + +### Documentation + +* **contributing:** update references to rye-up.com ([dcc34a2](https://github.com/openai/openai-python/commit/dcc34a26d1a6a0debf440724fad658c77547048c)) + ## 1.30.2 (2024-05-23) Full Changelog: [v1.30.1...v1.30.2](https://github.com/openai/openai-python/compare/v1.30.1...v1.30.2) diff --git a/pyproject.toml b/pyproject.toml index 5c16d92b76..850cf8419b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.30.2" +version = "1.30.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index fc87a446a4..541680fae0 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.30.2" # x-release-please-version +__version__ = "1.30.3" # x-release-please-version From 59855b24b7d46a087d15bb9dfabce2ae3dfb1300 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 24 May 2024 15:37:45 +0100 Subject: [PATCH 455/914] chore(internal): fix lint issue --- src/openai/lib/azure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index b76b83c61c..165b9b82e2 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -2,7 +2,7 @@ import os import inspect -from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, overload +from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, cast, overload from typing_extensions import Self, override import httpx @@ -515,7 +515,7 @@ async def _get_azure_ad_token(self) -> str | None: token = provider() if inspect.isawaitable(token): token = await token - if not token or not isinstance(token, str): + if not token or not isinstance(cast(Any, token), str): raise ValueError( f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", ) From 379c6a36af4d2afb3df1fa18d112c654226af5b2 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Tue, 28 May 2024 09:22:38 +0000 Subject: [PATCH 456/914] chore: add missing __all__ definitions --- src/openai/lib/azure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 165b9b82e2..b76b83c61c 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -2,7 +2,7 @@ import os import inspect -from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, cast, overload +from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, overload from typing_extensions import Self, override import httpx @@ -515,7 +515,7 @@ async def _get_azure_ad_token(self) -> str | None: token = provider() if inspect.isawaitable(token): token = await token - if not token or not isinstance(cast(Any, token), str): + if not token or not isinstance(token, str): raise ValueError( f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", ) From bb0b38d9882745d51a32dc3fcc0d6a54cdd298cb Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 28 May 2024 05:23:07 -0400 Subject: [PATCH 457/914] release: 1.30.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6c6045ba02..5d67d3563d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.30.3" + ".": "1.30.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7276bd8c03..b72dd9335d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.30.4 (2024-05-28) + +Full Changelog: [v1.30.3...v1.30.4](https://github.com/openai/openai-python/compare/v1.30.3...v1.30.4) + +### Chores + +* add missing __all__ definitions ([7fba60f](https://github.com/openai/openai-python/commit/7fba60f2e8adc26e83080aaf3e436eb9891e1253)) +* **internal:** fix lint issue ([f423cd0](https://github.com/openai/openai-python/commit/f423cd05d33b3e734eda7c0c008faac14ae96bb7)) + ## 1.30.3 (2024-05-24) Full Changelog: [v1.30.2...v1.30.3](https://github.com/openai/openai-python/compare/v1.30.2...v1.30.3) diff --git a/pyproject.toml b/pyproject.toml index 850cf8419b..3b6888a64d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.30.3" +version = "1.30.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 541680fae0..de2bb78a79 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.30.3" # x-release-please-version +__version__ = "1.30.4" # x-release-please-version From 6e7d8548899193d35b041a45d8e1b22cf2057481 Mon Sep 17 00:00:00 2001 From: Scott Addie <10702007+scottaddie@users.noreply.github.com> Date: Tue, 28 May 2024 21:26:47 -0500 Subject: [PATCH 458/914] Update Microsoft Azure OpenAI docs in README (#1439) * Update Microsoft Azure OpenAI docs in README * React to feedback --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e566a2f8d0..5e351ba03c 100644 --- a/README.md +++ b/README.md @@ -581,7 +581,7 @@ By default the library closes underlying HTTP connections whenever the client is ## Microsoft Azure OpenAI -To use this library with [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview), use the `AzureOpenAI` +To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI` class instead of the `OpenAI` class. > [!IMPORTANT] @@ -593,9 +593,9 @@ from openai import AzureOpenAI # gets the API Key from environment variable AZURE_OPENAI_API_KEY client = AzureOpenAI( - # https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning + # https://learn.microsoft.com/azure/ai-services/openai/reference#rest-api-versioning api_version="2023-07-01-preview", - # https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource + # https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource azure_endpoint="https://example-endpoint.openai.azure.com/", ) @@ -619,7 +619,7 @@ In addition to the options provided in the base `OpenAI` client, the following o - `azure_ad_token` (or the `AZURE_OPENAI_AD_TOKEN` environment variable) - `azure_ad_token_provider` -An example of using the client with Azure Active Directory can be found [here](https://github.com/openai/openai-python/blob/main/examples/azure_ad.py). +An example of using the client with Microsoft Entra ID (formerly known as Azure Active Directory) can be found [here](https://github.com/openai/openai-python/blob/main/examples/azure_ad.py). ## Versioning From e565c9425a58f90216b813883e018ceb42b9ca11 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 24 May 2024 15:37:45 +0100 Subject: [PATCH 459/914] chore(internal): fix lint issue --- src/openai/lib/azure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index b76b83c61c..165b9b82e2 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -2,7 +2,7 @@ import os import inspect -from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, overload +from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, cast, overload from typing_extensions import Self, override import httpx @@ -515,7 +515,7 @@ async def _get_azure_ad_token(self) -> str | None: token = provider() if inspect.isawaitable(token): token = await token - if not token or not isinstance(token, str): + if not token or not isinstance(cast(Any, token), str): raise ValueError( f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", ) From e872382c325eb0189432640a822980565290c19e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 17:29:14 +0000 Subject: [PATCH 460/914] release: 1.30.5 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5d67d3563d..4449911fae 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.30.4" + ".": "1.30.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b72dd9335d..8ae0f81ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.30.5 (2024-05-29) + +Full Changelog: [v1.30.4...v1.30.5](https://github.com/openai/openai-python/compare/v1.30.4...v1.30.5) + +### Chores + +* **internal:** fix lint issue ([35a1e80](https://github.com/openai/openai-python/commit/35a1e806891c34d5cc13ac8341751e5b15b52319)) + ## 1.30.4 (2024-05-28) Full Changelog: [v1.30.3...v1.30.4](https://github.com/openai/openai-python/compare/v1.30.3...v1.30.4) diff --git a/pyproject.toml b/pyproject.toml index 3b6888a64d..c09baa6d7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.30.4" +version = "1.30.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index de2bb78a79..1a8a23bfa3 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.30.4" # x-release-please-version +__version__ = "1.30.5" # x-release-please-version From 89dee3f4c3444880cb359e7be2312f1f04caec4f Mon Sep 17 00:00:00 2001 From: Selim Waly Date: Sun, 2 Jun 2024 23:48:46 +0300 Subject: [PATCH 461/914] Fix Azure Ad Token Function's Return (#1460) Identify returned "token" as string. --- src/openai/lib/azure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 165b9b82e2..cbe57b7b98 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -519,7 +519,7 @@ async def _get_azure_ad_token(self) -> str | None: raise ValueError( f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", ) - return token + return str(token) return None From 1ff30bf51858cb2ee8a89a4d7f6912f6ee98ff86 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:55:27 +0100 Subject: [PATCH 462/914] feat(api): updates (#1461) --- .stats.yml | 2 +- src/openai/resources/batches.py | 18 ++++--- .../beta/vector_stores/file_batches.py | 24 ++++++++- .../resources/beta/vector_stores/files.py | 24 ++++++++- .../beta/vector_stores/vector_stores.py | 10 ++++ src/openai/resources/files.py | 24 ++++++--- src/openai/resources/fine_tuning/jobs/jobs.py | 10 ++++ src/openai/types/batch_create_params.py | 2 +- .../types/beta/assistant_create_params.py | 42 ++++++++++++++++ .../types/beta/assistant_stream_event.py | 12 +++++ src/openai/types/beta/file_search_tool.py | 20 +++++++- .../types/beta/file_search_tool_param.py | 19 ++++++- .../beta/thread_create_and_run_params.py | 43 ++++++++++++++++ src/openai/types/beta/thread_create_params.py | 42 ++++++++++++++++ .../types/beta/vector_store_create_params.py | 48 +++++++++++++++++- .../vector_stores/file_batch_create_params.py | 50 +++++++++++++++++-- .../beta/vector_stores/file_create_params.py | 49 +++++++++++++++++- .../beta/vector_stores/vector_store_file.py | 47 +++++++++++++++-- ...chat_completion_assistant_message_param.py | 2 +- .../types/chat/completion_create_params.py | 5 +- src/openai/types/file_create_params.py | 2 +- .../types/fine_tuning/job_create_params.py | 5 ++ .../types/shared/function_definition.py | 5 +- .../shared_params/function_definition.py | 5 +- tests/api_resources/beta/test_assistants.py | 2 + tests/api_resources/beta/test_threads.py | 6 +++ .../api_resources/beta/test_vector_stores.py | 2 + .../beta/vector_stores/test_file_batches.py | 18 +++++++ .../beta/vector_stores/test_files.py | 18 +++++++ 29 files changed, 515 insertions(+), 41 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2e5c705a0d..11d2b0b181 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-363dd904e5d6e65b3a323fc88e6b502fb23a6aa319be219273e3ee47c7530993.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-0577fd0d08da6b867b002a5accd45f7116ef91c4940b41cf45dc479938c77163.yml diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index db4c4da235..7152fac622 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -68,7 +68,7 @@ def create( for how to upload a file. Your input file must be formatted as a - [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), + [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 100 MB in size. @@ -195,8 +195,11 @@ def cancel( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Batch: - """ - Cancels an in-progress batch. + """Cancels an in-progress batch. + + The batch will be in status `cancelling` for up to + 10 minutes, before changing to `cancelled`, where it will have partial results + (if any) available in the output file. Args: extra_headers: Send extra headers @@ -259,7 +262,7 @@ async def create( for how to upload a file. Your input file must be formatted as a - [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), + [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 100 MB in size. @@ -386,8 +389,11 @@ async def cancel( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Batch: - """ - Cancels an in-progress batch. + """Cancels an in-progress batch. + + The batch will be in status `cancelling` for up to + 10 minutes, before changing to `cancelled`, where it will have partial results + (if any) available in the output file. Args: extra_headers: Send extra headers diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/beta/vector_stores/file_batches.py index f1ced51700..21ac68f6de 100644 --- a/src/openai/resources/beta/vector_stores/file_batches.py +++ b/src/openai/resources/beta/vector_stores/file_batches.py @@ -47,6 +47,7 @@ def create( vector_store_id: str, *, file_ids: List[str], + chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -62,6 +63,9 @@ def create( the vector store should use. Useful for tools like `file_search` that can access files. + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -75,7 +79,13 @@ def create( extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/vector_stores/{vector_store_id}/file_batches", - body=maybe_transform({"file_ids": file_ids}, file_batch_create_params.FileBatchCreateParams), + body=maybe_transform( + { + "file_ids": file_ids, + "chunking_strategy": chunking_strategy, + }, + file_batch_create_params.FileBatchCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -351,6 +361,7 @@ async def create( vector_store_id: str, *, file_ids: List[str], + chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -366,6 +377,9 @@ async def create( the vector store should use. Useful for tools like `file_search` that can access files. + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -379,7 +393,13 @@ async def create( extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/vector_stores/{vector_store_id}/file_batches", - body=await async_maybe_transform({"file_ids": file_ids}, file_batch_create_params.FileBatchCreateParams), + body=await async_maybe_transform( + { + "file_ids": file_ids, + "chunking_strategy": chunking_strategy, + }, + file_batch_create_params.FileBatchCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/beta/vector_stores/files.py index 5c3db27619..30f19ef491 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -43,6 +43,7 @@ def create( vector_store_id: str, *, file_id: str, + chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -60,6 +61,9 @@ def create( vector store should use. Useful for tools like `file_search` that can access files. + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -73,7 +77,13 @@ def create( extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( f"/vector_stores/{vector_store_id}/files", - body=maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams), + body=maybe_transform( + { + "file_id": file_id, + "chunking_strategy": chunking_strategy, + }, + file_create_params.FileCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -330,6 +340,7 @@ async def create( vector_store_id: str, *, file_id: str, + chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -347,6 +358,9 @@ async def create( vector store should use. Useful for tools like `file_search` that can access files. + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -360,7 +374,13 @@ async def create( extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( f"/vector_stores/{vector_store_id}/files", - body=await async_maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams), + body=await async_maybe_transform( + { + "file_id": file_id, + "chunking_strategy": chunking_strategy, + }, + file_create_params.FileCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py index 8a177c2864..cbd56a0693 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -64,6 +64,7 @@ def with_streaming_response(self) -> VectorStoresWithStreamingResponse: def create( self, *, + chunking_strategy: vector_store_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, file_ids: List[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, @@ -79,6 +80,9 @@ def create( Create a vector store. Args: + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. Only applicable if `file_ids` is non-empty. + expires_after: The expiration policy for a vector store. file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that @@ -105,6 +109,7 @@ def create( "/vector_stores", body=maybe_transform( { + "chunking_strategy": chunking_strategy, "expires_after": expires_after, "file_ids": file_ids, "metadata": metadata, @@ -326,6 +331,7 @@ def with_streaming_response(self) -> AsyncVectorStoresWithStreamingResponse: async def create( self, *, + chunking_strategy: vector_store_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, file_ids: List[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, @@ -341,6 +347,9 @@ async def create( Create a vector store. Args: + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` + strategy. Only applicable if `file_ids` is non-empty. + expires_after: The expiration policy for a vector store. file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that @@ -367,6 +376,7 @@ async def create( "/vector_stores", body=await async_maybe_transform( { + "chunking_strategy": chunking_strategy, "expires_after": expires_after, "file_ids": file_ids, "metadata": metadata, diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index aed0829dfe..432ac30913 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -52,7 +52,7 @@ def create( self, *, file: FileTypes, - purpose: Literal["assistants", "batch", "fine-tune"], + purpose: Literal["assistants", "batch", "fine-tune", "vision"], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -71,9 +71,15 @@ def create( [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for details. - The Fine-tuning API only supports `.jsonl` files. + The Fine-tuning API only supports `.jsonl` files. The input also has certain + required formats for fine-tuning + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + models. - The Batch API only supports `.jsonl` files up to 100 MB in size. + The Batch API only supports `.jsonl` files up to 100 MB in size. The input also + has a specific required + [format](https://platform.openai.com/docs/api-reference/batch/request-input). Please [contact us](https://help.openai.com/) if you need to increase these storage limits. @@ -329,7 +335,7 @@ async def create( self, *, file: FileTypes, - purpose: Literal["assistants", "batch", "fine-tune"], + purpose: Literal["assistants", "batch", "fine-tune", "vision"], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -348,9 +354,15 @@ async def create( [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for details. - The Fine-tuning API only supports `.jsonl` files. + The Fine-tuning API only supports `.jsonl` files. The input also has certain + required formats for fine-tuning + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + models. - The Batch API only supports `.jsonl` files up to 100 MB in size. + The Batch API only supports `.jsonl` files up to 100 MB in size. The input also + has a specific required + [format](https://platform.openai.com/docs/api-reference/batch/request-input). Please [contact us](https://help.openai.com/) if you need to increase these storage limits. diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index f38956e6be..14b384a88d 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -87,6 +87,11 @@ def create( Your dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`. + The contents of the file should differ depending on if the model uses the + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + format. + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) for more details. @@ -362,6 +367,11 @@ async def create( Your dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`. + The contents of the file should differ depending on if the model uses the + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + format. + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) for more details. diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index 140380d417..55517d285b 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -30,7 +30,7 @@ class BatchCreateParams(TypedDict, total=False): for how to upload a file. Your input file must be formatted as a - [JSONL file](https://platform.openai.com/docs/api-reference/batch/requestInput), + [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 100 MB in size. """ diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 67e7f7e78c..c9b0317831 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -14,6 +14,10 @@ "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", "ToolResourcesFileSearchVectorStore", + "ToolResourcesFileSearchVectorStoreChunkingStrategy", + "ToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", ] @@ -134,7 +138,45 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): """ +class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ToolResourcesFileSearchVectorStoreChunkingStrategy = Union[ + ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic +] + + class ToolResourcesFileSearchVectorStore(TypedDict, total=False): + chunking_strategy: ToolResourcesFileSearchVectorStoreChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. + """ + file_ids: List[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py index 91925e93b3..de66888403 100644 --- a/src/openai/types/beta/assistant_stream_event.py +++ b/src/openai/types/beta/assistant_stream_event.py @@ -21,6 +21,7 @@ "ThreadRunInProgress", "ThreadRunRequiresAction", "ThreadRunCompleted", + "ThreadRunIncomplete", "ThreadRunFailed", "ThreadRunCancelling", "ThreadRunCancelled", @@ -101,6 +102,16 @@ class ThreadRunCompleted(BaseModel): event: Literal["thread.run.completed"] +class ThreadRunIncomplete(BaseModel): + data: Run + """ + Represents an execution run on a + [thread](https://platform.openai.com/docs/api-reference/threads). + """ + + event: Literal["thread.run.incomplete"] + + class ThreadRunFailed(BaseModel): data: Run """ @@ -257,6 +268,7 @@ class ErrorEvent(BaseModel): ThreadRunInProgress, ThreadRunRequiresAction, ThreadRunCompleted, + ThreadRunIncomplete, ThreadRunFailed, ThreadRunCancelling, ThreadRunCancelled, diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py index eea55ea6ac..e2711b9b3d 100644 --- a/src/openai/types/beta/file_search_tool.py +++ b/src/openai/types/beta/file_search_tool.py @@ -1,12 +1,30 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["FileSearchTool"] +__all__ = ["FileSearchTool", "FileSearch"] + + +class FileSearch(BaseModel): + max_num_results: Optional[int] = None + """The maximum number of results the file search tool should output. + + The default is 20 for gpt-4\\** models and 5 for gpt-3.5-turbo. This number should + be between 1 and 50 inclusive. + + Note that the file search tool may output fewer than `max_num_results` results. + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/number-of-chunks-returned) + for more information. + """ class FileSearchTool(BaseModel): type: Literal["file_search"] """The type of tool being defined: `file_search`""" + + file_search: Optional[FileSearch] = None + """Overrides for the file search tool.""" diff --git a/src/openai/types/beta/file_search_tool_param.py b/src/openai/types/beta/file_search_tool_param.py index d33fd06da4..115f86a444 100644 --- a/src/openai/types/beta/file_search_tool_param.py +++ b/src/openai/types/beta/file_search_tool_param.py @@ -4,9 +4,26 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["FileSearchToolParam"] +__all__ = ["FileSearchToolParam", "FileSearch"] + + +class FileSearch(TypedDict, total=False): + max_num_results: int + """The maximum number of results the file search tool should output. + + The default is 20 for gpt-4\\** models and 5 for gpt-3.5-turbo. This number should + be between 1 and 50 inclusive. + + Note that the file search tool may output fewer than `max_num_results` results. + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/number-of-chunks-returned) + for more information. + """ class FileSearchToolParam(TypedDict, total=False): type: Required[Literal["file_search"]] """The type of tool being defined: `file_search`""" + + file_search: FileSearch + """Overrides for the file search tool.""" diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 6efe6e7aee..436c2daddf 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -22,6 +22,10 @@ "ThreadToolResourcesCodeInterpreter", "ThreadToolResourcesFileSearch", "ThreadToolResourcesFileSearchVectorStore", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategy", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", @@ -220,7 +224,46 @@ class ThreadToolResourcesCodeInterpreter(TypedDict, total=False): """ +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ThreadToolResourcesFileSearchVectorStoreChunkingStrategy = Union[ + ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto, + ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic, +] + + class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): + chunking_strategy: ThreadToolResourcesFileSearchVectorStoreChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. + """ + file_ids: List[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index ccf50d58dc..5072ed12d9 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -18,6 +18,10 @@ "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", "ToolResourcesFileSearchVectorStore", + "ToolResourcesFileSearchVectorStoreChunkingStrategy", + "ToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", ] @@ -90,7 +94,45 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): """ +class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ToolResourcesFileSearchVectorStoreChunkingStrategy = Union[ + ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic +] + + class ToolResourcesFileSearchVectorStore(TypedDict, total=False): + chunking_strategy: ToolResourcesFileSearchVectorStoreChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. + """ + file_ids: List[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to diff --git a/src/openai/types/beta/vector_store_create_params.py b/src/openai/types/beta/vector_store_create_params.py index f1a3abcbdf..365d9923b8 100644 --- a/src/openai/types/beta/vector_store_create_params.py +++ b/src/openai/types/beta/vector_store_create_params.py @@ -2,13 +2,27 @@ from __future__ import annotations -from typing import List, Optional +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["VectorStoreCreateParams", "ExpiresAfter"] +__all__ = [ + "VectorStoreCreateParams", + "ChunkingStrategy", + "ChunkingStrategyAuto", + "ChunkingStrategyStatic", + "ChunkingStrategyStaticStatic", + "ExpiresAfter", +] class VectorStoreCreateParams(TypedDict, total=False): + chunking_strategy: ChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. + """ + expires_after: ExpiresAfter """The expiration policy for a vector store.""" @@ -31,6 +45,36 @@ class VectorStoreCreateParams(TypedDict, total=False): """The name of the vector store.""" +class ChunkingStrategyAuto(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ChunkingStrategyStatic(TypedDict, total=False): + static: Required[ChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ChunkingStrategy = Union[ChunkingStrategyAuto, ChunkingStrategyStatic] + + class ExpiresAfter(TypedDict, total=False): anchor: Required[Literal["last_active_at"]] """Anchor timestamp after which the expiration policy applies. diff --git a/src/openai/types/beta/vector_stores/file_batch_create_params.py b/src/openai/types/beta/vector_stores/file_batch_create_params.py index 0882829732..9b98d0699e 100644 --- a/src/openai/types/beta/vector_stores/file_batch_create_params.py +++ b/src/openai/types/beta/vector_stores/file_batch_create_params.py @@ -2,10 +2,16 @@ from __future__ import annotations -from typing import List -from typing_extensions import Required, TypedDict +from typing import List, Union +from typing_extensions import Literal, Required, TypedDict -__all__ = ["FileBatchCreateParams"] +__all__ = [ + "FileBatchCreateParams", + "ChunkingStrategy", + "ChunkingStrategyAutoChunkingStrategyRequestParam", + "ChunkingStrategyStaticChunkingStrategyRequestParam", + "ChunkingStrategyStaticChunkingStrategyRequestParamStatic", +] class FileBatchCreateParams(TypedDict, total=False): @@ -15,3 +21,41 @@ class FileBatchCreateParams(TypedDict, total=False): the vector store should use. Useful for tools like `file_search` that can access files. """ + + chunking_strategy: ChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. + """ + + +class ChunkingStrategyAutoChunkingStrategyRequestParam(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ChunkingStrategyStaticChunkingStrategyRequestParamStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ChunkingStrategyStaticChunkingStrategyRequestParam(TypedDict, total=False): + static: Required[ChunkingStrategyStaticChunkingStrategyRequestParamStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ChunkingStrategy = Union[ + ChunkingStrategyAutoChunkingStrategyRequestParam, ChunkingStrategyStaticChunkingStrategyRequestParam +] diff --git a/src/openai/types/beta/vector_stores/file_create_params.py b/src/openai/types/beta/vector_stores/file_create_params.py index 2fee588abf..2ae63f1462 100644 --- a/src/openai/types/beta/vector_stores/file_create_params.py +++ b/src/openai/types/beta/vector_stores/file_create_params.py @@ -2,9 +2,16 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing import Union +from typing_extensions import Literal, Required, TypedDict -__all__ = ["FileCreateParams"] +__all__ = [ + "FileCreateParams", + "ChunkingStrategy", + "ChunkingStrategyAutoChunkingStrategyRequestParam", + "ChunkingStrategyStaticChunkingStrategyRequestParam", + "ChunkingStrategyStaticChunkingStrategyRequestParamStatic", +] class FileCreateParams(TypedDict, total=False): @@ -14,3 +21,41 @@ class FileCreateParams(TypedDict, total=False): vector store should use. Useful for tools like `file_search` that can access files. """ + + chunking_strategy: ChunkingStrategy + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. + """ + + +class ChunkingStrategyAutoChunkingStrategyRequestParam(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ChunkingStrategyStaticChunkingStrategyRequestParamStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ChunkingStrategyStaticChunkingStrategyRequestParam(TypedDict, total=False): + static: Required[ChunkingStrategyStaticChunkingStrategyRequestParamStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ChunkingStrategy = Union[ + ChunkingStrategyAutoChunkingStrategyRequestParam, ChunkingStrategyStaticChunkingStrategyRequestParam +] diff --git a/src/openai/types/beta/vector_stores/vector_store_file.py b/src/openai/types/beta/vector_stores/vector_store_file.py index 3fab489602..d9d7625f86 100644 --- a/src/openai/types/beta/vector_stores/vector_store_file.py +++ b/src/openai/types/beta/vector_stores/vector_store_file.py @@ -1,11 +1,19 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import Union, Optional +from typing_extensions import Literal, Annotated +from ...._utils import PropertyInfo from ...._models import BaseModel -__all__ = ["VectorStoreFile", "LastError"] +__all__ = [ + "VectorStoreFile", + "LastError", + "ChunkingStrategy", + "ChunkingStrategyStatic", + "ChunkingStrategyStaticStatic", + "ChunkingStrategyOther", +] class LastError(BaseModel): @@ -16,6 +24,36 @@ class LastError(BaseModel): """A human-readable description of the error.""" +class ChunkingStrategyStaticStatic(BaseModel): + chunk_overlap_tokens: int + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: int + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ChunkingStrategyStatic(BaseModel): + static: ChunkingStrategyStaticStatic + + type: Literal["static"] + """Always `static`.""" + + +class ChunkingStrategyOther(BaseModel): + type: Literal["other"] + """Always `other`.""" + + +ChunkingStrategy = Annotated[Union[ChunkingStrategyStatic, ChunkingStrategyOther], PropertyInfo(discriminator="type")] + + class VectorStoreFile(BaseModel): id: str """The identifier, which can be referenced in API endpoints.""" @@ -52,3 +90,6 @@ class VectorStoreFile(BaseModel): that the [File](https://platform.openai.com/docs/api-reference/files) is attached to. """ + + chunking_strategy: Optional[ChunkingStrategy] = None + """The strategy used to chunk the file.""" diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index e1e399486e..8f7357b96c 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -33,7 +33,7 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): Required unless `tool_calls` or `function_call` is specified. """ - function_call: FunctionCall + function_call: Optional[FunctionCall] """Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 226cf15882..a25f2fdd8f 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -219,9 +219,8 @@ class Function(TypedDict, total=False): parameters: shared_params.FunctionParameters """The parameters the functions accepts, described as a JSON Schema object. - See the - [guide](https://platform.openai.com/docs/guides/text-generation/function-calling) - for examples, and the + See the [guide](https://platform.openai.com/docs/guides/function-calling) for + examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index caa913d4d2..8b1c296f39 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -13,7 +13,7 @@ class FileCreateParams(TypedDict, total=False): file: Required[FileTypes] """The File object (not file name) to be uploaded.""" - purpose: Required[Literal["assistants", "batch", "fine-tune"]] + purpose: Required[Literal["assistants", "batch", "fine-tune", "vision"]] """The intended purpose of the uploaded file. Use "assistants" for diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 1925f90d12..c5196e4406 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -25,6 +25,11 @@ class JobCreateParams(TypedDict, total=False): Your dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`. + The contents of the file should differ depending on if the model uses the + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + format. + See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) for more details. """ diff --git a/src/openai/types/shared/function_definition.py b/src/openai/types/shared/function_definition.py index a39116d6bd..49f5e67c50 100644 --- a/src/openai/types/shared/function_definition.py +++ b/src/openai/types/shared/function_definition.py @@ -25,9 +25,8 @@ class FunctionDefinition(BaseModel): parameters: Optional[FunctionParameters] = None """The parameters the functions accepts, described as a JSON Schema object. - See the - [guide](https://platform.openai.com/docs/guides/text-generation/function-calling) - for examples, and the + See the [guide](https://platform.openai.com/docs/guides/function-calling) for + examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. diff --git a/src/openai/types/shared_params/function_definition.py b/src/openai/types/shared_params/function_definition.py index 58d0203b4f..29ccc548d4 100644 --- a/src/openai/types/shared_params/function_definition.py +++ b/src/openai/types/shared_params/function_definition.py @@ -26,9 +26,8 @@ class FunctionDefinition(TypedDict, total=False): parameters: shared_params.FunctionParameters """The parameters the functions accepts, described as a JSON Schema object. - See the - [guide](https://platform.openai.com/docs/guides/text-generation/function-calling) - for examples, and the + See the [guide](https://platform.openai.com/docs/guides/function-calling) for + examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index a92acb2ca5..dd0ce9266e 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -45,6 +45,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "vector_stores": [ { "file_ids": ["string", "string", "string"], + "chunking_strategy": {"type": "auto"}, "metadata": {}, } ], @@ -276,6 +277,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "vector_stores": [ { "file_ids": ["string", "string", "string"], + "chunking_strategy": {"type": "auto"}, "metadata": {}, } ], diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 02c6e2586e..041562cb38 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -132,6 +132,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "vector_stores": [ { "file_ids": ["string", "string", "string"], + "chunking_strategy": {"type": "auto"}, "metadata": {}, } ], @@ -408,6 +409,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "vector_stores": [ { "file_ids": ["string", "string", "string"], + "chunking_strategy": {"type": "auto"}, "metadata": {}, } ], @@ -576,6 +578,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "vector_stores": [ { "file_ids": ["string", "string", "string"], + "chunking_strategy": {"type": "auto"}, "metadata": {}, } ], @@ -737,6 +740,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "vector_stores": [ { "file_ids": ["string", "string", "string"], + "chunking_strategy": {"type": "auto"}, "metadata": {}, } ], @@ -1013,6 +1017,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "vector_stores": [ { "file_ids": ["string", "string", "string"], + "chunking_strategy": {"type": "auto"}, "metadata": {}, } ], @@ -1181,6 +1186,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "vector_stores": [ { "file_ids": ["string", "string", "string"], + "chunking_strategy": {"type": "auto"}, "metadata": {}, } ], diff --git a/tests/api_resources/beta/test_vector_stores.py b/tests/api_resources/beta/test_vector_stores.py index e671c96a45..39fdb9d1d4 100644 --- a/tests/api_resources/beta/test_vector_stores.py +++ b/tests/api_resources/beta/test_vector_stores.py @@ -29,6 +29,7 @@ def test_method_create(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: vector_store = client.beta.vector_stores.create( + chunking_strategy={"type": "auto"}, expires_after={ "anchor": "last_active_at", "days": 1, @@ -233,6 +234,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: vector_store = await async_client.beta.vector_stores.create( + chunking_strategy={"type": "auto"}, expires_after={ "anchor": "last_active_at", "days": 1, diff --git a/tests/api_resources/beta/vector_stores/test_file_batches.py b/tests/api_resources/beta/vector_stores/test_file_batches.py index 9854d1a138..631f2669ad 100644 --- a/tests/api_resources/beta/vector_stores/test_file_batches.py +++ b/tests/api_resources/beta/vector_stores/test_file_batches.py @@ -29,6 +29,15 @@ def test_method_create(self, client: OpenAI) -> None: ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + file_batch = client.beta.vector_stores.file_batches.create( + "vs_abc123", + file_ids=["string"], + chunking_strategy={"type": "auto"}, + ) + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.beta.vector_stores.file_batches.with_raw_response.create( @@ -232,6 +241,15 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + file_batch = await async_client.beta.vector_stores.file_batches.create( + "vs_abc123", + file_ids=["string"], + chunking_strategy={"type": "auto"}, + ) + assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) + @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.vector_stores.file_batches.with_raw_response.create( diff --git a/tests/api_resources/beta/vector_stores/test_files.py b/tests/api_resources/beta/vector_stores/test_files.py index 58301e2d37..36622e699b 100644 --- a/tests/api_resources/beta/vector_stores/test_files.py +++ b/tests/api_resources/beta/vector_stores/test_files.py @@ -29,6 +29,15 @@ def test_method_create(self, client: OpenAI) -> None: ) assert_matches_type(VectorStoreFile, file, path=["response"]) + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + file = client.beta.vector_stores.files.create( + "vs_abc123", + file_id="string", + chunking_strategy={"type": "auto"}, + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.beta.vector_stores.files.with_raw_response.create( @@ -221,6 +230,15 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: ) assert_matches_type(VectorStoreFile, file, path=["response"]) + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + file = await async_client.beta.vector_stores.files.create( + "vs_abc123", + file_id="string", + chunking_strategy={"type": "auto"}, + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.vector_stores.files.with_raw_response.create( From 14448b71b2c65c6e45b9bb4f01bbf9562061731b Mon Sep 17 00:00:00 2001 From: meorphis Date: Mon, 3 Jun 2024 19:01:39 -0400 Subject: [PATCH 463/914] chore: fix lint --- src/openai/lib/streaming/_assistants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openai/lib/streaming/_assistants.py b/src/openai/lib/streaming/_assistants.py index 03d97ec2eb..7445f9a96d 100644 --- a/src/openai/lib/streaming/_assistants.py +++ b/src/openai/lib/streaming/_assistants.py @@ -280,6 +280,7 @@ def _emit_sse_event(self, event: AssistantStreamEvent) -> None: or event.event == "thread.run.expired" or event.event == "thread.run.failed" or event.event == "thread.run.requires_action" + or event.event == "thread.run.incomplete" ): self.__current_run = event.data if self._current_tool_call: @@ -711,6 +712,7 @@ async def _emit_sse_event(self, event: AssistantStreamEvent) -> None: or event.event == "thread.run.expired" or event.event == "thread.run.failed" or event.event == "thread.run.requires_action" + or event.event == "thread.run.incomplete" ): self.__current_run = event.data if self._current_tool_call: From 4eff2db078dfa873b2a9301d62d04ca8f7fc36a4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:02:10 +0000 Subject: [PATCH 464/914] release: 1.31.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4449911fae..81d2de2d26 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.30.5" + ".": "1.31.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ae0f81ffc..d6acfdd066 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.31.0 (2024-06-03) + +Full Changelog: [v1.30.5...v1.31.0](https://github.com/openai/openai-python/compare/v1.30.5...v1.31.0) + +### Features + +* **api:** updates ([#1461](https://github.com/openai/openai-python/issues/1461)) ([0d7cc5e](https://github.com/openai/openai-python/commit/0d7cc5e48c565fe10ee6e8ca4d050175eb543bcb)) + + +### Chores + +* fix lint ([1886dd4](https://github.com/openai/openai-python/commit/1886dd4c98d7a7b3a679bff739cb38badf5ae96c)) + ## 1.30.5 (2024-05-29) Full Changelog: [v1.30.4...v1.30.5](https://github.com/openai/openai-python/compare/v1.30.4...v1.30.5) diff --git a/pyproject.toml b/pyproject.toml index c09baa6d7d..7578ea718c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.30.5" +version = "1.31.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1a8a23bfa3..e87d71b33a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.30.5" # x-release-please-version +__version__ = "1.31.0" # x-release-please-version From 62069d2f3304c74374e8de99c69217f9f03e98c3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 5 Jun 2024 05:33:56 -0400 Subject: [PATCH 465/914] chore(internal): minor change to tests (#1466) --- tests/api_resources/audio/test_speech.py | 16 ++++++------ tests/api_resources/test_completions.py | 32 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 781ebeceb9..1f04a66435 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -27,7 +27,7 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = client.audio.speech.create( input="string", - model="string", + model="tts-1", voice="alloy", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) @@ -39,7 +39,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = client.audio.speech.create( input="string", - model="string", + model="tts-1", voice="alloy", response_format="mp3", speed=0.25, @@ -54,7 +54,7 @@ def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> No response = client.audio.speech.with_raw_response.create( input="string", - model="string", + model="tts-1", voice="alloy", ) @@ -69,7 +69,7 @@ def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) with client.audio.speech.with_streaming_response.create( input="string", - model="string", + model="tts-1", voice="alloy", ) as response: assert not response.is_closed @@ -90,7 +90,7 @@ async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRo respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = await async_client.audio.speech.create( input="string", - model="string", + model="tts-1", voice="alloy", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) @@ -102,7 +102,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = await async_client.audio.speech.create( input="string", - model="string", + model="tts-1", voice="alloy", response_format="mp3", speed=0.25, @@ -117,7 +117,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: response = await async_client.audio.speech.with_raw_response.create( input="string", - model="string", + model="tts-1", voice="alloy", ) @@ -132,7 +132,7 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_ respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) async with async_client.audio.speech.with_streaming_response.create( input="string", - model="string", + model="tts-1", voice="alloy", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 69d914200f..fa7ae52131 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -20,7 +20,7 @@ class TestCompletions: @parametrize def test_method_create_overload_1(self, client: OpenAI) -> None: completion = client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) assert_matches_type(Completion, completion, path=["response"]) @@ -28,7 +28,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: completion = client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", best_of=0, echo=True, @@ -52,7 +52,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_overload_1(self, client: OpenAI) -> None: response = client.completions.with_raw_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) @@ -64,7 +64,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: with client.completions.with_streaming_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) as response: assert not response.is_closed @@ -78,7 +78,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_overload_2(self, client: OpenAI) -> None: completion_stream = client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) @@ -87,7 +87,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: completion_stream = client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, best_of=0, @@ -111,7 +111,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_overload_2(self, client: OpenAI) -> None: response = client.completions.with_raw_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) @@ -123,7 +123,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: with client.completions.with_streaming_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) as response: @@ -142,7 +142,7 @@ class TestAsyncCompletions: @parametrize async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: completion = await async_client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) assert_matches_type(Completion, completion, path=["response"]) @@ -150,7 +150,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: completion = await async_client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", best_of=0, echo=True, @@ -174,7 +174,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.completions.with_raw_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) @@ -186,7 +186,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) - @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.completions.with_streaming_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) as response: assert not response.is_closed @@ -200,7 +200,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe @parametrize async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: completion_stream = await async_client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) @@ -209,7 +209,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: completion_stream = await async_client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, best_of=0, @@ -233,7 +233,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: response = await async_client.completions.with_raw_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) @@ -245,7 +245,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) - @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: async with async_client.completions.with_streaming_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) as response: From 2fcc0e428fbc10fdf1825f7f85bf384534173b4a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:34:25 +0000 Subject: [PATCH 466/914] release: 1.31.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 81d2de2d26..03537feab5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.31.0" + ".": "1.31.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d6acfdd066..dd23f9b017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.31.1 (2024-06-05) + +Full Changelog: [v1.31.0...v1.31.1](https://github.com/openai/openai-python/compare/v1.31.0...v1.31.1) + +### Chores + +* **internal:** minor change to tests ([#1466](https://github.com/openai/openai-python/issues/1466)) ([cb33e71](https://github.com/openai/openai-python/commit/cb33e7152f25fb16cf4c39a6e4714169c62d6af8)) + ## 1.31.0 (2024-06-03) Full Changelog: [v1.30.5...v1.31.0](https://github.com/openai/openai-python/compare/v1.30.5...v1.31.0) diff --git a/pyproject.toml b/pyproject.toml index 7578ea718c..239960f0e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.31.0" +version = "1.31.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e87d71b33a..9fe77c14dc 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.31.0" # x-release-please-version +__version__ = "1.31.1" # x-release-please-version From 156d13e185bf5117bbec8e9778accda28fefb5dd Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 6 Jun 2024 03:39:46 -0400 Subject: [PATCH 467/914] chore(internal): minor refactor of tests (#1471) --- tests/api_resources/audio/test_speech.py | 16 ++++++------ tests/api_resources/test_completions.py | 32 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 1f04a66435..781ebeceb9 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -27,7 +27,7 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = client.audio.speech.create( input="string", - model="tts-1", + model="string", voice="alloy", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) @@ -39,7 +39,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = client.audio.speech.create( input="string", - model="tts-1", + model="string", voice="alloy", response_format="mp3", speed=0.25, @@ -54,7 +54,7 @@ def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> No response = client.audio.speech.with_raw_response.create( input="string", - model="tts-1", + model="string", voice="alloy", ) @@ -69,7 +69,7 @@ def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) with client.audio.speech.with_streaming_response.create( input="string", - model="tts-1", + model="string", voice="alloy", ) as response: assert not response.is_closed @@ -90,7 +90,7 @@ async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRo respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = await async_client.audio.speech.create( input="string", - model="tts-1", + model="string", voice="alloy", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) @@ -102,7 +102,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = await async_client.audio.speech.create( input="string", - model="tts-1", + model="string", voice="alloy", response_format="mp3", speed=0.25, @@ -117,7 +117,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: response = await async_client.audio.speech.with_raw_response.create( input="string", - model="tts-1", + model="string", voice="alloy", ) @@ -132,7 +132,7 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_ respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) async with async_client.audio.speech.with_streaming_response.create( input="string", - model="tts-1", + model="string", voice="alloy", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index fa7ae52131..69d914200f 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -20,7 +20,7 @@ class TestCompletions: @parametrize def test_method_create_overload_1(self, client: OpenAI) -> None: completion = client.completions.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", ) assert_matches_type(Completion, completion, path=["response"]) @@ -28,7 +28,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: completion = client.completions.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", best_of=0, echo=True, @@ -52,7 +52,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_overload_1(self, client: OpenAI) -> None: response = client.completions.with_raw_response.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", ) @@ -64,7 +64,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: with client.completions.with_streaming_response.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", ) as response: assert not response.is_closed @@ -78,7 +78,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_overload_2(self, client: OpenAI) -> None: completion_stream = client.completions.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", stream=True, ) @@ -87,7 +87,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: completion_stream = client.completions.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", stream=True, best_of=0, @@ -111,7 +111,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_overload_2(self, client: OpenAI) -> None: response = client.completions.with_raw_response.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", stream=True, ) @@ -123,7 +123,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: with client.completions.with_streaming_response.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", stream=True, ) as response: @@ -142,7 +142,7 @@ class TestAsyncCompletions: @parametrize async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: completion = await async_client.completions.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", ) assert_matches_type(Completion, completion, path=["response"]) @@ -150,7 +150,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: completion = await async_client.completions.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", best_of=0, echo=True, @@ -174,7 +174,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.completions.with_raw_response.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", ) @@ -186,7 +186,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) - @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.completions.with_streaming_response.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", ) as response: assert not response.is_closed @@ -200,7 +200,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe @parametrize async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: completion_stream = await async_client.completions.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", stream=True, ) @@ -209,7 +209,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: completion_stream = await async_client.completions.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", stream=True, best_of=0, @@ -233,7 +233,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: response = await async_client.completions.with_raw_response.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", stream=True, ) @@ -245,7 +245,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) - @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: async with async_client.completions.with_streaming_response.create( - model="gpt-3.5-turbo-instruct", + model="string", prompt="This is a test.", stream=True, ) as response: From a33712e161eacdcfead297da3f4a5bc491732e3f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Jun 2024 07:40:16 +0000 Subject: [PATCH 468/914] release: 1.31.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 03537feab5..dc28bf349b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.31.1" + ".": "1.31.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dd23f9b017..e8baa0d73d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.31.2 (2024-06-06) + +Full Changelog: [v1.31.1...v1.31.2](https://github.com/openai/openai-python/compare/v1.31.1...v1.31.2) + +### Chores + +* **internal:** minor refactor of tests ([#1471](https://github.com/openai/openai-python/issues/1471)) ([b7f2298](https://github.com/openai/openai-python/commit/b7f229866f249d16e995db361b923bb4c0b7f1d4)) + ## 1.31.1 (2024-06-05) Full Changelog: [v1.31.0...v1.31.1](https://github.com/openai/openai-python/compare/v1.31.0...v1.31.1) diff --git a/pyproject.toml b/pyproject.toml index 239960f0e1..31a8c5ce3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.31.1" +version = "1.31.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9fe77c14dc..f1befbacf9 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.31.1" # x-release-please-version +__version__ = "1.31.2" # x-release-please-version From 7399ffc8c4b5b0754358fc0467bddc2debec3d34 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:55:43 -0400 Subject: [PATCH 469/914] feat(api): updates (#1474) --- .stats.yml | 2 +- .../resources/beta/threads/runs/runs.py | 34 +++++++++++++++++++ src/openai/resources/beta/threads/threads.py | 34 +++++++++++++++++++ src/openai/resources/chat/completions.py | 34 +++++++++++++++++++ .../beta/thread_create_and_run_params.py | 7 ++++ src/openai/types/beta/threads/run.py | 7 ++++ .../types/beta/threads/run_create_params.py | 7 ++++ .../types/chat/completion_create_params.py | 7 ++++ tests/api_resources/beta/test_threads.py | 4 +++ tests/api_resources/beta/threads/test_runs.py | 4 +++ tests/api_resources/chat/test_completions.py | 4 +++ 11 files changed, 143 insertions(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 11d2b0b181..eb81a249f1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-0577fd0d08da6b867b002a5accd45f7116ef91c4940b41cf45dc479938c77163.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-ff436357b12348b7c1c930469332a79cd23ac6ec537e645c411893c42de42e57.yml diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index c37071529c..5976ca4559 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -109,6 +109,7 @@ def create( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -163,6 +164,10 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -257,6 +262,7 @@ def create( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -314,6 +320,10 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -404,6 +414,7 @@ def create( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -461,6 +472,10 @@ def create( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -550,6 +565,7 @@ def create( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -579,6 +595,7 @@ def create( "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "parallel_tool_calls": parallel_tool_calls, "response_format": response_format, "stream": stream, "temperature": temperature, @@ -1666,6 +1683,7 @@ async def create( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1720,6 +1738,10 @@ async def create( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -1814,6 +1836,7 @@ async def create( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1871,6 +1894,10 @@ async def create( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -1961,6 +1988,7 @@ async def create( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2018,6 +2046,10 @@ async def create( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -2107,6 +2139,7 @@ async def create( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -2136,6 +2169,7 @@ async def create( "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "parallel_tool_calls": parallel_tool_calls, "response_format": response_format, "stream": stream, "temperature": temperature, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 36cdd03f91..05c06ff658 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -291,6 +291,7 @@ def create_and_run( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -340,6 +341,10 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -438,6 +443,7 @@ def create_and_run( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -490,6 +496,10 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -584,6 +594,7 @@ def create_and_run( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -636,6 +647,10 @@ def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -729,6 +744,7 @@ def create_and_run( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -756,6 +772,7 @@ def create_and_run( "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "parallel_tool_calls": parallel_tool_calls, "response_format": response_format, "stream": stream, "temperature": temperature, @@ -1284,6 +1301,7 @@ async def create_and_run( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1333,6 +1351,10 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -1431,6 +1453,7 @@ async def create_and_run( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -1483,6 +1506,10 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -1577,6 +1604,7 @@ async def create_and_run( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -1629,6 +1657,10 @@ async def create_and_run( model associated with the assistant. If not, the model associated with the assistant will be used. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), @@ -1722,6 +1754,7 @@ async def create_and_run( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1749,6 +1782,7 @@ async def create_and_run( "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "parallel_tool_calls": parallel_tool_calls, "response_format": response_format, "stream": stream, "temperature": temperature, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index aa25bc1858..ab35b03335 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -55,6 +55,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -131,6 +132,10 @@ def create( you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + presence_penalty: Number between -2.0 and 2.0. 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. @@ -227,6 +232,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -309,6 +315,10 @@ def create( you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + presence_penalty: Number between -2.0 and 2.0. 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. @@ -398,6 +408,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -480,6 +491,10 @@ def create( you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + presence_penalty: Number between -2.0 and 2.0. 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. @@ -568,6 +583,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -600,6 +616,7 @@ def create( "logprobs": logprobs, "max_tokens": max_tokens, "n": n, + "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, "response_format": response_format, "seed": seed, @@ -646,6 +663,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -722,6 +740,10 @@ async def create( you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + presence_penalty: Number between -2.0 and 2.0. 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. @@ -818,6 +840,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -900,6 +923,10 @@ async def create( you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + presence_penalty: Number between -2.0 and 2.0. 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. @@ -989,6 +1016,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -1071,6 +1099,10 @@ async def create( you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + parallel_tool_calls: Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + presence_penalty: Number between -2.0 and 2.0. 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. @@ -1159,6 +1191,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -1191,6 +1224,7 @@ async def create( "logprobs": logprobs, "max_tokens": max_tokens, "n": n, + "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, "response_format": response_format, "seed": seed, diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 436c2daddf..b8c69eb7ac 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -109,6 +109,13 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): assistant will be used. """ + parallel_tool_calls: bool + """ + Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + """ + response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 8244ffd598..ea84f1e97c 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -151,6 +151,13 @@ class Run(BaseModel): object: Literal["thread.run"] """The object type, which is always `thread.run`.""" + parallel_tool_calls: bool + """ + Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + """ + required_action: Optional[RequiredAction] = None """Details on the action required to continue the run. diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 90c9708596..a7aa799e00 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -107,6 +107,13 @@ class RunCreateParamsBase(TypedDict, total=False): assistant will be used. """ + parallel_tool_calls: bool + """ + Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + """ + response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index a25f2fdd8f..47c2a5e24e 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -102,6 +102,13 @@ class CompletionCreateParamsBase(TypedDict, total=False): of the choices. Keep `n` as `1` to minimize costs. """ + parallel_tool_calls: bool + """ + Whether to enable + [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + during tool use. + """ + presence_penalty: Optional[float] """Number between -2.0 and 2.0. diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 041562cb38..9e06b597ef 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -303,6 +303,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) max_prompt_tokens=256, metadata={}, model="gpt-4-turbo", + parallel_tool_calls=True, response_format="none", stream=False, temperature=1, @@ -473,6 +474,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) max_prompt_tokens=256, metadata={}, model="gpt-4-turbo", + parallel_tool_calls=True, response_format="none", temperature=1, thread={ @@ -911,6 +913,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie max_prompt_tokens=256, metadata={}, model="gpt-4-turbo", + parallel_tool_calls=True, response_format="none", stream=False, temperature=1, @@ -1081,6 +1084,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie max_prompt_tokens=256, metadata={}, model="gpt-4-turbo", + parallel_tool_calls=True, response_format="none", temperature=1, thread={ diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 089dd1253e..26862ef1eb 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -136,6 +136,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_prompt_tokens=256, metadata={}, model="gpt-4-turbo", + parallel_tool_calls=True, response_format="none", stream=False, temperature=1, @@ -299,6 +300,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_prompt_tokens=256, metadata={}, model="gpt-4-turbo", + parallel_tool_calls=True, response_format="none", temperature=1, tool_choice="none", @@ -800,6 +802,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_prompt_tokens=256, metadata={}, model="gpt-4-turbo", + parallel_tool_calls=True, response_format="none", stream=False, temperature=1, @@ -963,6 +966,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_prompt_tokens=256, metadata={}, model="gpt-4-turbo", + parallel_tool_calls=True, response_format="none", temperature=1, tool_choice="none", diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 1c195c4001..3099e16815 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -56,6 +56,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: logprobs=True, max_tokens=0, n=1, + parallel_tool_calls=True, presence_penalty=-2, response_format={"type": "json_object"}, seed=-9223372036854776000, @@ -171,6 +172,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: logprobs=True, max_tokens=0, n=1, + parallel_tool_calls=True, presence_penalty=-2, response_format={"type": "json_object"}, seed=-9223372036854776000, @@ -288,6 +290,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn logprobs=True, max_tokens=0, n=1, + parallel_tool_calls=True, presence_penalty=-2, response_format={"type": "json_object"}, seed=-9223372036854776000, @@ -403,6 +406,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn logprobs=True, max_tokens=0, n=1, + parallel_tool_calls=True, presence_penalty=-2, response_format={"type": "json_object"}, seed=-9223372036854776000, From 178afa0b04d233c89285e3fd90c9db226e030ef2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Jun 2024 18:56:15 +0000 Subject: [PATCH 470/914] release: 1.32.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index dc28bf349b..592b0e1529 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.31.2" + ".": "1.32.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e8baa0d73d..17813558d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.32.0 (2024-06-06) + +Full Changelog: [v1.31.2...v1.32.0](https://github.com/openai/openai-python/compare/v1.31.2...v1.32.0) + +### Features + +* **api:** updates ([#1474](https://github.com/openai/openai-python/issues/1474)) ([87ddff0](https://github.com/openai/openai-python/commit/87ddff0e6e64650691a8e32f7477b7a00e06ed23)) + ## 1.31.2 (2024-06-06) Full Changelog: [v1.31.1...v1.31.2](https://github.com/openai/openai-python/compare/v1.31.1...v1.31.2) diff --git a/pyproject.toml b/pyproject.toml index 31a8c5ce3c..0abca8c7ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.31.2" +version = "1.32.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f1befbacf9..d7724070cf 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.31.2" # x-release-please-version +__version__ = "1.32.0" # x-release-please-version From bf8a453f11bacad19baece8f81829944a462f55c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:40:54 -0400 Subject: [PATCH 471/914] fix: remove erroneous thread create argument (#1476) --- .stats.yml | 2 +- src/openai/resources/beta/threads/runs/runs.py | 12 ++++++------ src/openai/resources/beta/threads/threads.py | 12 ++++++------ src/openai/resources/chat/completions.py | 12 ++++++------ .../types/beta/thread_create_and_run_params.py | 10 ++++++++-- src/openai/types/beta/thread_create_params.py | 9 +++++++-- src/openai/types/beta/threads/message.py | 17 ++++++++++++++--- .../types/beta/threads/message_create_params.py | 10 +++++++--- src/openai/types/beta/threads/run.py | 2 +- .../types/beta/threads/run_create_params.py | 11 ++++++++--- .../types/chat/completion_create_params.py | 2 +- 11 files changed, 65 insertions(+), 34 deletions(-) diff --git a/.stats.yml b/.stats.yml index eb81a249f1..a6c08f499b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-ff436357b12348b7c1c930469332a79cd23ac6ec537e645c411893c42de42e57.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-c085faf70d6ff059fbe11b7b6b98123a612524cb9b8a6f649c99526e5b0b1bdb.yml diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 5976ca4559..43069dd1ae 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -165,7 +165,7 @@ def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -321,7 +321,7 @@ def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -473,7 +473,7 @@ def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -1739,7 +1739,7 @@ async def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -1895,7 +1895,7 @@ async def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -2047,7 +2047,7 @@ async def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 05c06ff658..c0a908b7a2 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -342,7 +342,7 @@ def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -497,7 +497,7 @@ def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -648,7 +648,7 @@ def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -1352,7 +1352,7 @@ async def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -1507,7 +1507,7 @@ async def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with @@ -1658,7 +1658,7 @@ async def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index ab35b03335..ed8e9373b0 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -133,7 +133,7 @@ def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on @@ -316,7 +316,7 @@ def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on @@ -492,7 +492,7 @@ def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on @@ -741,7 +741,7 @@ async def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on @@ -924,7 +924,7 @@ async def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on @@ -1100,7 +1100,7 @@ async def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index b8c69eb7ac..dbbff415ec 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -18,6 +18,7 @@ "ThreadMessage", "ThreadMessageAttachment", "ThreadMessageAttachmentTool", + "ThreadMessageAttachmentToolFileSearch", "ThreadToolResources", "ThreadToolResourcesCodeInterpreter", "ThreadToolResourcesFileSearch", @@ -112,7 +113,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): parallel_tool_calls: bool """ Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. """ @@ -186,7 +187,12 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): """ -ThreadMessageAttachmentTool = Union[CodeInterpreterToolParam, FileSearchToolParam] +class ThreadMessageAttachmentToolFileSearch(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + +ThreadMessageAttachmentTool = Union[CodeInterpreterToolParam, ThreadMessageAttachmentToolFileSearch] class ThreadMessageAttachment(TypedDict, total=False): diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index 5072ed12d9..e5ea14a94d 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -5,7 +5,6 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -from .file_search_tool_param import FileSearchToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam from .threads.message_content_part_param import MessageContentPartParam @@ -14,6 +13,7 @@ "Message", "MessageAttachment", "MessageAttachmentTool", + "MessageAttachmentToolFileSearch", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", @@ -49,7 +49,12 @@ class ThreadCreateParams(TypedDict, total=False): """ -MessageAttachmentTool = Union[CodeInterpreterToolParam, FileSearchToolParam] +class MessageAttachmentToolFileSearch(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + +MessageAttachmentTool = Union[CodeInterpreterToolParam, MessageAttachmentToolFileSearch] class MessageAttachment(TypedDict, total=False): diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index ebaabdb0f5..90f083683d 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -5,12 +5,23 @@ from ...._models import BaseModel from .message_content import MessageContent -from ..file_search_tool import FileSearchTool from ..code_interpreter_tool import CodeInterpreterTool -__all__ = ["Message", "Attachment", "AttachmentTool", "IncompleteDetails"] +__all__ = [ + "Message", + "Attachment", + "AttachmentTool", + "AttachmentToolAssistantToolsFileSearchTypeOnly", + "IncompleteDetails", +] -AttachmentTool = Union[CodeInterpreterTool, FileSearchTool] + +class AttachmentToolAssistantToolsFileSearchTypeOnly(BaseModel): + type: Literal["file_search"] + """The type of tool being defined: `file_search`""" + + +AttachmentTool = Union[CodeInterpreterTool, AttachmentToolAssistantToolsFileSearchTypeOnly] class Attachment(BaseModel): diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index 3668df950d..b1b12293b7 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -5,11 +5,10 @@ from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -from ..file_search_tool_param import FileSearchToolParam from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam -__all__ = ["MessageCreateParams", "Attachment", "AttachmentTool"] +__all__ = ["MessageCreateParams", "Attachment", "AttachmentTool", "AttachmentToolFileSearch"] class MessageCreateParams(TypedDict, total=False): @@ -37,7 +36,12 @@ class MessageCreateParams(TypedDict, total=False): """ -AttachmentTool = Union[CodeInterpreterToolParam, FileSearchToolParam] +class AttachmentToolFileSearch(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + +AttachmentTool = Union[CodeInterpreterToolParam, AttachmentToolFileSearch] class Attachment(TypedDict, total=False): diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index ea84f1e97c..81d10d4a56 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -154,7 +154,7 @@ class Run(BaseModel): parallel_tool_calls: bool """ Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. """ diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index a7aa799e00..89da241965 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -6,7 +6,6 @@ from typing_extensions import Literal, Required, TypedDict from ..assistant_tool_param import AssistantToolParam -from ..file_search_tool_param import FileSearchToolParam from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam from ..assistant_tool_choice_option_param import AssistantToolChoiceOptionParam @@ -17,6 +16,7 @@ "AdditionalMessage", "AdditionalMessageAttachment", "AdditionalMessageAttachmentTool", + "AdditionalMessageAttachmentToolFileSearch", "TruncationStrategy", "RunCreateParamsNonStreaming", "RunCreateParamsStreaming", @@ -110,7 +110,7 @@ class RunCreateParamsBase(TypedDict, total=False): parallel_tool_calls: bool """ Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. """ @@ -173,7 +173,12 @@ class RunCreateParamsBase(TypedDict, total=False): """ -AdditionalMessageAttachmentTool = Union[CodeInterpreterToolParam, FileSearchToolParam] +class AdditionalMessageAttachmentToolFileSearch(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of tool being defined: `file_search`""" + + +AdditionalMessageAttachmentTool = Union[CodeInterpreterToolParam, AdditionalMessageAttachmentToolFileSearch] class AdditionalMessageAttachment(TypedDict, total=False): diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 47c2a5e24e..7dd7067f66 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -105,7 +105,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): parallel_tool_calls: bool """ Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) during tool use. """ From b8a85a0baa22e42d79d85fd42ebe44ef4d6cb80c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 19:41:22 +0000 Subject: [PATCH 472/914] release: 1.32.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 592b0e1529..cb8c32fee0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.32.0" + ".": "1.32.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 17813558d2..ecfe0762a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.32.1 (2024-06-07) + +Full Changelog: [v1.32.0...v1.32.1](https://github.com/openai/openai-python/compare/v1.32.0...v1.32.1) + +### Bug Fixes + +* remove erroneous thread create argument ([#1476](https://github.com/openai/openai-python/issues/1476)) ([43175c4](https://github.com/openai/openai-python/commit/43175c40e607d626a77a151691778c35a0e60eec)) + ## 1.32.0 (2024-06-06) Full Changelog: [v1.31.2...v1.32.0](https://github.com/openai/openai-python/compare/v1.31.2...v1.32.0) diff --git a/pyproject.toml b/pyproject.toml index 0abca8c7ef..80b6bc0465 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.32.0" +version = "1.32.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d7724070cf..aa58ad2d57 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.32.0" # x-release-please-version +__version__ = "1.32.1" # x-release-please-version From cc4e1b0021182f0bf4cb00246b23453a923fea76 Mon Sep 17 00:00:00 2001 From: pstern-sl <157847713+pstern-sl@users.noreply.github.com> Date: Fri, 7 Jun 2024 17:36:40 -0400 Subject: [PATCH 473/914] feat(api): adding chunking_strategy to polling helpers (#1478) --- .../resources/beta/vector_stores/file_batches.py | 8 ++++++++ src/openai/resources/beta/vector_stores/files.py | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/beta/vector_stores/file_batches.py index 21ac68f6de..d6862c24ef 100644 --- a/src/openai/resources/beta/vector_stores/file_batches.py +++ b/src/openai/resources/beta/vector_stores/file_batches.py @@ -174,11 +174,13 @@ def create_and_poll( *, file_ids: List[str], poll_interval_ms: int | NotGiven = NOT_GIVEN, + chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: """Create a vector store batch and poll until all files have been processed.""" batch = self.create( vector_store_id=vector_store_id, file_ids=file_ids, + chunking_strategy=chunking_strategy, ) # TODO: don't poll unless necessary?? return self.poll( @@ -306,6 +308,7 @@ def upload_and_poll( max_concurrency: int = 5, file_ids: List[str] = [], poll_interval_ms: int | NotGiven = NOT_GIVEN, + chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: """Uploads the given files concurrently and then creates a vector store file batch. @@ -343,6 +346,7 @@ def upload_and_poll( vector_store_id=vector_store_id, file_ids=[*file_ids, *(f.id for f in results)], poll_interval_ms=poll_interval_ms, + chunking_strategy=chunking_strategy, ) return batch @@ -488,11 +492,13 @@ async def create_and_poll( *, file_ids: List[str], poll_interval_ms: int | NotGiven = NOT_GIVEN, + chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: """Create a vector store batch and poll until all files have been processed.""" batch = await self.create( vector_store_id=vector_store_id, file_ids=file_ids, + chunking_strategy=chunking_strategy, ) # TODO: don't poll unless necessary?? return await self.poll( @@ -620,6 +626,7 @@ async def upload_and_poll( max_concurrency: int = 5, file_ids: List[str] = [], poll_interval_ms: int | NotGiven = NOT_GIVEN, + chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: """Uploads the given files concurrently and then creates a vector store file batch. @@ -680,6 +687,7 @@ async def trio_upload_file(limiter: trio.CapacityLimiter, file: FileTypes) -> No vector_store_id=vector_store_id, file_ids=[*file_ids, *(f.id for f in uploaded_files)], poll_interval_ms=poll_interval_ms, + chunking_strategy=chunking_strategy, ) return batch diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/beta/vector_stores/files.py index 30f19ef491..bc1655027c 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -245,9 +245,10 @@ def create_and_poll( *, vector_store_id: str, poll_interval_ms: int | NotGiven = NOT_GIVEN, + chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Attach a file to the given vector store and wait for it to be processed.""" - self.create(vector_store_id=vector_store_id, file_id=file_id) + self.create(vector_store_id=vector_store_id, file_id=file_id, chunking_strategy=chunking_strategy) return self.poll( file_id, @@ -301,6 +302,7 @@ def upload( *, vector_store_id: str, file: FileTypes, + chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Upload a file to the `files` API and then attach it to the given vector store. @@ -308,7 +310,7 @@ def upload( polling helper method to wait for processing to complete). """ file_obj = self._client.files.create(file=file, purpose="assistants") - return self.create(vector_store_id=vector_store_id, file_id=file_obj.id) + return self.create(vector_store_id=vector_store_id, file_id=file_obj.id, chunking_strategy=chunking_strategy) def upload_and_poll( self, @@ -316,12 +318,14 @@ def upload_and_poll( vector_store_id: str, file: FileTypes, poll_interval_ms: int | NotGiven = NOT_GIVEN, + chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Add a file to a vector store and poll until processing is complete.""" file_obj = self._client.files.create(file=file, purpose="assistants") return self.create_and_poll( vector_store_id=vector_store_id, file_id=file_obj.id, + chunking_strategy=chunking_strategy, poll_interval_ms=poll_interval_ms, ) @@ -542,9 +546,10 @@ async def create_and_poll( *, vector_store_id: str, poll_interval_ms: int | NotGiven = NOT_GIVEN, + chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Attach a file to the given vector store and wait for it to be processed.""" - await self.create(vector_store_id=vector_store_id, file_id=file_id) + await self.create(vector_store_id=vector_store_id, file_id=file_id, chunking_strategy=chunking_strategy) return await self.poll( file_id, @@ -598,6 +603,7 @@ async def upload( *, vector_store_id: str, file: FileTypes, + chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Upload a file to the `files` API and then attach it to the given vector store. @@ -605,7 +611,7 @@ async def upload( polling helper method to wait for processing to complete). """ file_obj = await self._client.files.create(file=file, purpose="assistants") - return await self.create(vector_store_id=vector_store_id, file_id=file_obj.id) + return await self.create(vector_store_id=vector_store_id, file_id=file_obj.id, chunking_strategy=chunking_strategy) async def upload_and_poll( self, @@ -613,6 +619,7 @@ async def upload_and_poll( vector_store_id: str, file: FileTypes, poll_interval_ms: int | NotGiven = NOT_GIVEN, + chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Add a file to a vector store and poll until processing is complete.""" file_obj = await self._client.files.create(file=file, purpose="assistants") @@ -620,6 +627,7 @@ async def upload_and_poll( vector_store_id=vector_store_id, file_id=file_obj.id, poll_interval_ms=poll_interval_ms, + chunking_strategy=chunking_strategy ) From 54a5911f5215148a0bdeb10e2bcfb84f635a75b9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 21:37:01 +0000 Subject: [PATCH 474/914] release: 1.33.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cb8c32fee0..5334cb411c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.32.1" + ".": "1.33.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ecfe0762a1..236ef3ea4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.33.0 (2024-06-07) + +Full Changelog: [v1.32.1...v1.33.0](https://github.com/openai/openai-python/compare/v1.32.1...v1.33.0) + +### Features + +* **api:** adding chunking_strategy to polling helpers ([#1478](https://github.com/openai/openai-python/issues/1478)) ([83be2a1](https://github.com/openai/openai-python/commit/83be2a13e0384d3de52190d86ccb1b5d7a197d84)) + ## 1.32.1 (2024-06-07) Full Changelog: [v1.32.0...v1.32.1](https://github.com/openai/openai-python/compare/v1.32.0...v1.32.1) diff --git a/pyproject.toml b/pyproject.toml index 80b6bc0465..873e52b8ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.32.1" +version = "1.33.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index aa58ad2d57..b4ef7ee2f3 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.32.1" # x-release-please-version +__version__ = "1.33.0" # x-release-please-version From 15c93bad5560b55bb6066db7514dfb1346ae2662 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:45:02 -0400 Subject: [PATCH 475/914] feat(api): updates (#1481) --- .stats.yml | 2 +- src/openai/types/beta/threads/file_citation_annotation.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index a6c08f499b..c5ada3b5df 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-c085faf70d6ff059fbe11b7b6b98123a612524cb9b8a6f649c99526e5b0b1bdb.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-5cb1810135c35c5024698f3365626471a04796e26e393aefe1aa0ba3c0891919.yml diff --git a/src/openai/types/beta/threads/file_citation_annotation.py b/src/openai/types/beta/threads/file_citation_annotation.py index 68571cd477..c3085aed9b 100644 --- a/src/openai/types/beta/threads/file_citation_annotation.py +++ b/src/openai/types/beta/threads/file_citation_annotation.py @@ -11,9 +11,6 @@ class FileCitation(BaseModel): file_id: str """The ID of the specific File the citation is from.""" - quote: str - """The specific quote in the file.""" - class FileCitationAnnotation(BaseModel): end_index: int From 811f4e736b75a418fb9bc579861971b239e6cd0c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:45:34 +0000 Subject: [PATCH 476/914] release: 1.34.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5334cb411c..257e308d6f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.33.0" + ".": "1.34.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 236ef3ea4e..3295921654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.34.0 (2024-06-12) + +Full Changelog: [v1.33.0...v1.34.0](https://github.com/openai/openai-python/compare/v1.33.0...v1.34.0) + +### Features + +* **api:** updates ([#1481](https://github.com/openai/openai-python/issues/1481)) ([b83db36](https://github.com/openai/openai-python/commit/b83db362f0c9a5a4d55588b954fb1df1a68c98e3)) + ## 1.33.0 (2024-06-07) Full Changelog: [v1.32.1...v1.33.0](https://github.com/openai/openai-python/compare/v1.32.1...v1.33.0) diff --git a/pyproject.toml b/pyproject.toml index 873e52b8ff..eb2da149b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.33.0" +version = "1.34.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b4ef7ee2f3..d0c1ef7e17 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.33.0" # x-release-please-version +__version__ = "1.34.0" # x-release-please-version From 6c8fcd541e45a83513123b68de60b25cdd623b07 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 19:53:21 +0000 Subject: [PATCH 477/914] feat(api): add service tier argument for chat completions (#1486) --- .stats.yml | 2 +- src/openai/_base_client.py | 8 ++- src/openai/resources/chat/completions.py | 70 +++++++++++++++++++ src/openai/types/chat/chat_completion.py | 7 ++ .../types/chat/chat_completion_chunk.py | 7 ++ .../types/chat/completion_create_params.py | 13 ++++ tests/api_resources/chat/test_completions.py | 4 ++ 7 files changed, 109 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index c5ada3b5df..aa7e8427b0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-5cb1810135c35c5024698f3365626471a04796e26e393aefe1aa0ba3c0891919.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-8fe357c6b5a425d810d731e4102a052d8e38c5e2d66950e6de1025415160bf88.yml diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 5d5d25fca9..1c9a1a03f2 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -457,7 +457,7 @@ def _build_request( raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") headers = self._build_headers(options) - params = _merge_mappings(self._custom_query, options.params) + params = _merge_mappings(self.default_query, options.params) content_type = headers.get("Content-Type") # If the given Content-Type header is multipart/form-data then it @@ -593,6 +593,12 @@ def default_headers(self) -> dict[str, str | Omit]: **self._custom_headers, } + @property + def default_query(self) -> dict[str, object]: + return { + **self._custom_query, + } + def _validate_headers( self, headers: Headers, # noqa: ARG002 diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index ed8e9373b0..d50bce0757 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -59,6 +59,7 @@ def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -163,6 +164,16 @@ def create( should refer to the `system_fingerprint` response parameter to monitor changes in the backend. + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', the system will utilize scale tier credits until they are + exhausted. + - If set to 'default', the request will be processed in the shared cluster. + + When this parameter is set, the response body will include the `service_tier` + utilized. + stop: Up to 4 sequences where the API will stop generating further tokens. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be @@ -236,6 +247,7 @@ def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -346,6 +358,16 @@ def create( should refer to the `system_fingerprint` response parameter to monitor changes in the backend. + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', the system will utilize scale tier credits until they are + exhausted. + - If set to 'default', the request will be processed in the shared cluster. + + When this parameter is set, the response body will include the `service_tier` + utilized. + stop: Up to 4 sequences where the API will stop generating further tokens. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -412,6 +434,7 @@ def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -522,6 +545,16 @@ def create( should refer to the `system_fingerprint` response parameter to monitor changes in the backend. + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', the system will utilize scale tier credits until they are + exhausted. + - If set to 'default', the request will be processed in the shared cluster. + + When this parameter is set, the response body will include the `service_tier` + utilized. + stop: Up to 4 sequences where the API will stop generating further tokens. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -587,6 +620,7 @@ def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -620,6 +654,7 @@ def create( "presence_penalty": presence_penalty, "response_format": response_format, "seed": seed, + "service_tier": service_tier, "stop": stop, "stream": stream, "stream_options": stream_options, @@ -667,6 +702,7 @@ async def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -771,6 +807,16 @@ async def create( should refer to the `system_fingerprint` response parameter to monitor changes in the backend. + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', the system will utilize scale tier credits until they are + exhausted. + - If set to 'default', the request will be processed in the shared cluster. + + When this parameter is set, the response body will include the `service_tier` + utilized. + stop: Up to 4 sequences where the API will stop generating further tokens. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be @@ -844,6 +890,7 @@ async def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -954,6 +1001,16 @@ async def create( should refer to the `system_fingerprint` response parameter to monitor changes in the backend. + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', the system will utilize scale tier credits until they are + exhausted. + - If set to 'default', the request will be processed in the shared cluster. + + When this parameter is set, the response body will include the `service_tier` + utilized. + stop: Up to 4 sequences where the API will stop generating further tokens. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -1020,6 +1077,7 @@ async def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1130,6 +1188,16 @@ async def create( should refer to the `system_fingerprint` response parameter to monitor changes in the backend. + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', the system will utilize scale tier credits until they are + exhausted. + - If set to 'default', the request will be processed in the shared cluster. + + When this parameter is set, the response body will include the `service_tier` + utilized. + stop: Up to 4 sequences where the API will stop generating further tokens. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -1195,6 +1263,7 @@ async def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -1228,6 +1297,7 @@ async def create( "presence_penalty": presence_penalty, "response_format": response_format, "seed": seed, + "service_tier": service_tier, "stop": stop, "stream": stream, "stream_options": stream_options, diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 61a94a258e..5f4eaf3366 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -56,6 +56,13 @@ class ChatCompletion(BaseModel): object: Literal["chat.completion"] """The object type, which is always `chat.completion`.""" + service_tier: Optional[Literal["scale", "default"]] = None + """The service tier used for processing the request. + + This field is only included if the `service_tier` parameter is specified in the + request. + """ + system_fingerprint: Optional[str] = None """This fingerprint represents the backend configuration that the model runs with. diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 084a5fcc07..65643c7e60 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -122,6 +122,13 @@ class ChatCompletionChunk(BaseModel): object: Literal["chat.completion.chunk"] """The object type, which is always `chat.completion.chunk`.""" + service_tier: Optional[Literal["scale", "default"]] = None + """The service tier used for processing the request. + + This field is only included if the `service_tier` parameter is specified in the + request. + """ + system_fingerprint: Optional[str] = None """ This fingerprint represents the backend configuration that the model runs with. diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 7dd7067f66..21187f3741 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -146,6 +146,19 @@ class CompletionCreateParamsBase(TypedDict, total=False): in the backend. """ + service_tier: Optional[Literal["auto", "default"]] + """Specifies the latency tier to use for processing the request. + + This parameter is relevant for customers subscribed to the scale tier service: + + - If set to 'auto', the system will utilize scale tier credits until they are + exhausted. + - If set to 'default', the request will be processed in the shared cluster. + + When this parameter is set, the response body will include the `service_tier` + utilized. + """ + stop: Union[Optional[str], List[str]] """Up to 4 sequences where the API will stop generating further tokens.""" diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 3099e16815..87df11d1ee 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -60,6 +60,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: presence_penalty=-2, response_format={"type": "json_object"}, seed=-9223372036854776000, + service_tier="auto", stop="string", stream=False, stream_options={"include_usage": True}, @@ -176,6 +177,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: presence_penalty=-2, response_format={"type": "json_object"}, seed=-9223372036854776000, + service_tier="auto", stop="string", stream_options={"include_usage": True}, temperature=1, @@ -294,6 +296,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn presence_penalty=-2, response_format={"type": "json_object"}, seed=-9223372036854776000, + service_tier="auto", stop="string", stream=False, stream_options={"include_usage": True}, @@ -410,6 +413,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn presence_penalty=-2, response_format={"type": "json_object"}, seed=-9223372036854776000, + service_tier="auto", stop="string", stream_options={"include_usage": True}, temperature=1, From 6aa2a8030707e558ca08bf6992df2ad8926ed43a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 19:53:53 +0000 Subject: [PATCH 478/914] release: 1.35.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 257e308d6f..44959ac416 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.34.0" + ".": "1.35.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3295921654..dc259c2ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.0 (2024-06-18) + +Full Changelog: [v1.34.0...v1.35.0](https://github.com/openai/openai-python/compare/v1.34.0...v1.35.0) + +### Features + +* **api:** add service tier argument for chat completions ([#1486](https://github.com/openai/openai-python/issues/1486)) ([b4b4e66](https://github.com/openai/openai-python/commit/b4b4e660b8bb7ae937787fcab9b40feaeba7f711)) + ## 1.34.0 (2024-06-12) Full Changelog: [v1.33.0...v1.34.0](https://github.com/openai/openai-python/compare/v1.33.0...v1.34.0) diff --git a/pyproject.toml b/pyproject.toml index eb2da149b4..5241598939 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.34.0" +version = "1.35.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d0c1ef7e17..1f47c75093 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.34.0" # x-release-please-version +__version__ = "1.35.0" # x-release-please-version From dea7ee14780c9dc5456f20570e5c6d29a303c949 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:18:26 +0000 Subject: [PATCH 479/914] fix(client/async): avoid blocking io call for platform headers (#1488) --- src/openai/_base_client.py | 17 +++++++++++++---- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_reflection.py | 8 ++++++++ src/openai/_utils/_sync.py | 19 ++++++++++++++++++- 4 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 src/openai/_utils/_reflection.py diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 1c9a1a03f2..84004ebba5 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -60,7 +60,7 @@ RequestOptions, ModelBuilderProtocol, ) -from ._utils import is_dict, is_list, is_given, lru_cache, is_mapping +from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping from ._compat import model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( @@ -359,6 +359,7 @@ def __init__( self._custom_query = custom_query or {} self._strict_response_validation = _strict_response_validation self._idempotency_header = None + self._platform: Platform | None = None if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] raise TypeError( @@ -623,7 +624,10 @@ def base_url(http://github.com/self,%20url:%20URL%20|%20str) -> None: self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) def platform_headers(self) -> Dict[str, str]: - return platform_headers(self._version) + # the actual implementation is in a separate `lru_cache` decorated + # function because adding `lru_cache` to methods will leak memory + # https://github.com/python/cpython/issues/88476 + return platform_headers(self._version, platform=self._platform) def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. @@ -1513,6 +1517,11 @@ async def _request( stream_cls: type[_AsyncStreamT] | None, remaining_retries: int | None, ) -> ResponseT | _AsyncStreamT: + if self._platform is None: + # `get_platform` can make blocking IO calls so we + # execute it earlier while we are in an async context + self._platform = await asyncify(get_platform)() + cast_to = self._maybe_override_cast_to(cast_to, options) await self._prepare_options(options) @@ -1949,11 +1958,11 @@ def get_platform() -> Platform: @lru_cache(maxsize=None) -def platform_headers(version: str) -> Dict[str, str]: +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: return { "X-Stainless-Lang": "python", "X-Stainless-Package-Version": version, - "X-Stainless-OS": str(get_platform()), + "X-Stainless-OS": str(platform or get_platform()), "X-Stainless-Arch": str(get_architecture()), "X-Stainless-Runtime": get_python_runtime(), "X-Stainless-Runtime-Version": get_python_version(), diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 31b5b22799..667e2473f6 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -49,3 +49,4 @@ maybe_transform as maybe_transform, async_maybe_transform as async_maybe_transform, ) +from ._reflection import function_has_argument as function_has_argument diff --git a/src/openai/_utils/_reflection.py b/src/openai/_utils/_reflection.py new file mode 100644 index 0000000000..e134f58e08 --- /dev/null +++ b/src/openai/_utils/_reflection.py @@ -0,0 +1,8 @@ +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 diff --git a/src/openai/_utils/_sync.py b/src/openai/_utils/_sync.py index 595924e5b1..d0d810337e 100644 --- a/src/openai/_utils/_sync.py +++ b/src/openai/_utils/_sync.py @@ -7,6 +7,8 @@ import anyio import anyio.to_thread +from ._reflection import function_has_argument + T_Retval = TypeVar("T_Retval") T_ParamSpec = ParamSpec("T_ParamSpec") @@ -59,6 +61,21 @@ def do_work(arg1, arg2, kwarg1="", kwarg2="") -> str: async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: partial_f = functools.partial(function, *args, **kwargs) - return await anyio.to_thread.run_sync(partial_f, cancellable=cancellable, limiter=limiter) + + # In `v4.1.0` anyio added the `abandon_on_cancel` argument and deprecated the old + # `cancellable` argument, so we need to use the new `abandon_on_cancel` to avoid + # surfacing deprecation warnings. + if function_has_argument(anyio.to_thread.run_sync, "abandon_on_cancel"): + return await anyio.to_thread.run_sync( + partial_f, + abandon_on_cancel=cancellable, + limiter=limiter, + ) + + return await anyio.to_thread.run_sync( + partial_f, + cancellable=cancellable, + limiter=limiter, + ) return wrapper From e005a8482d76a9939d88c71f13860ee3764fe2af Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:48:49 +0000 Subject: [PATCH 480/914] release: 1.35.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 44959ac416..21b274b18a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.0" + ".": "1.35.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dc259c2ac8..f442ddd2ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.1 (2024-06-19) + +Full Changelog: [v1.35.0...v1.35.1](https://github.com/openai/openai-python/compare/v1.35.0...v1.35.1) + +### Bug Fixes + +* **client/async:** avoid blocking io call for platform headers ([#1488](https://github.com/openai/openai-python/issues/1488)) ([ae64c05](https://github.com/openai/openai-python/commit/ae64c05cbae76a58b592d913bee6ac1ef9611d4c)) + ## 1.35.0 (2024-06-18) Full Changelog: [v1.34.0...v1.35.0](https://github.com/openai/openai-python/compare/v1.34.0...v1.35.0) diff --git a/pyproject.toml b/pyproject.toml index 5241598939..270c21f4cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.0" +version = "1.35.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1f47c75093..ae8e3fb08d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.0" # x-release-please-version +__version__ = "1.35.1" # x-release-please-version From a1203812c94812858f05d71c9b20a5865287b4fe Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 20 Jun 2024 18:47:34 +0100 Subject: [PATCH 481/914] fix(api): add missing parallel_tool_calls arguments --- src/openai/resources/beta/threads/threads.py | 12 ++++ tests/lib/test_assistants.py | 59 ++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 tests/lib/test_assistants.py diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index c0a908b7a2..a62ee8d1bb 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -828,6 +828,7 @@ def create_and_run_poll( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -856,6 +857,7 @@ def create_and_run_poll( max_prompt_tokens=max_prompt_tokens, metadata=metadata, model=model, + parallel_tool_calls=parallel_tool_calls, response_format=response_format, temperature=temperature, stream=False, @@ -908,6 +910,7 @@ def create_and_run_stream( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -962,6 +965,7 @@ def create_and_run_stream( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -1016,6 +1020,7 @@ def create_and_run_stream( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -1050,6 +1055,7 @@ def create_and_run_stream( "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "parallel_tool_calls": parallel_tool_calls, "response_format": response_format, "temperature": temperature, "tool_choice": tool_choice, @@ -1838,6 +1844,7 @@ async def create_and_run_poll( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -1866,6 +1873,7 @@ async def create_and_run_poll( max_prompt_tokens=max_prompt_tokens, metadata=metadata, model=model, + parallel_tool_calls=parallel_tool_calls, response_format=response_format, temperature=temperature, stream=False, @@ -1920,6 +1928,7 @@ def create_and_run_stream( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -1974,6 +1983,7 @@ def create_and_run_stream( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -2028,6 +2038,7 @@ def create_and_run_stream( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, @@ -2064,6 +2075,7 @@ def create_and_run_stream( "max_prompt_tokens": max_prompt_tokens, "metadata": metadata, "model": model, + "parallel_tool_calls": parallel_tool_calls, "response_format": response_format, "temperature": temperature, "tool_choice": tool_choice, diff --git a/tests/lib/test_assistants.py b/tests/lib/test_assistants.py new file mode 100644 index 0000000000..ac92f17ca3 --- /dev/null +++ b/tests/lib/test_assistants.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import inspect +from typing import Any, Callable + +import pytest + +from openai import OpenAI, AsyncOpenAI + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), +) -> None: + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, generated_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 != generated_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; generated={repr(generated_param.annotation)} custom={repr(generated_param.annotation)}" + ) + continue + + if errors: + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_create_and_run_poll_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client = client if sync else async_client + + assert_signatures_in_sync( + checking_client.beta.threads.create_and_run, + checking_client.beta.threads.create_and_run_poll, + exclude_params={"stream"}, + ) + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_create_and_run_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client = client if sync else async_client + + assert_signatures_in_sync( + checking_client.beta.threads.create_and_run, + checking_client.beta.threads.create_and_run_stream, + exclude_params={"stream"}, + ) From 717e318d6e0c652721c6e7081acd6db226f77820 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:48:10 +0000 Subject: [PATCH 482/914] release: 1.35.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 21b274b18a..5554412dd2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.1" + ".": "1.35.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f442ddd2ce..5eede818c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.2 (2024-06-20) + +Full Changelog: [v1.35.1...v1.35.2](https://github.com/openai/openai-python/compare/v1.35.1...v1.35.2) + +### Bug Fixes + +* **api:** add missing parallel_tool_calls arguments ([4041e4f](https://github.com/openai/openai-python/commit/4041e4f6ea1e2316179a82031001308be23a2524)) + ## 1.35.1 (2024-06-19) Full Changelog: [v1.35.0...v1.35.1](https://github.com/openai/openai-python/compare/v1.35.0...v1.35.1) diff --git a/pyproject.toml b/pyproject.toml index 270c21f4cf..91a3f5d19d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.1" +version = "1.35.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ae8e3fb08d..f053bcea86 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.1" # x-release-please-version +__version__ = "1.35.2" # x-release-please-version From 203f094b2338bff01b2bd513113845a07ded630d Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 20 Jun 2024 18:52:48 +0100 Subject: [PATCH 483/914] fix(tests): add explicit type annotation --- tests/lib/test_assistants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lib/test_assistants.py b/tests/lib/test_assistants.py index ac92f17ca3..487b9938c7 100644 --- a/tests/lib/test_assistants.py +++ b/tests/lib/test_assistants.py @@ -40,7 +40,7 @@ def assert_signatures_in_sync( @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_create_and_run_poll_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: - checking_client = client if sync else async_client + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client assert_signatures_in_sync( checking_client.beta.threads.create_and_run, @@ -50,7 +50,7 @@ def test_create_and_run_poll_method_definition_in_sync(sync: bool, client: OpenA @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_create_and_run_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: - checking_client = client if sync else async_client + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client assert_signatures_in_sync( checking_client.beta.threads.create_and_run, From f3e6e634a86d5789ab1274ae27f43adc842f4ba8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:53:33 +0000 Subject: [PATCH 484/914] release: 1.35.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5554412dd2..7d2dc6aa83 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.2" + ".": "1.35.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eede818c4..4dcfe237ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.3 (2024-06-20) + +Full Changelog: [v1.35.2...v1.35.3](https://github.com/openai/openai-python/compare/v1.35.2...v1.35.3) + +### Bug Fixes + +* **tests:** add explicit type annotation ([9345f10](https://github.com/openai/openai-python/commit/9345f104889056b2ef6646d65375925a0a3bae03)) + ## 1.35.2 (2024-06-20) Full Changelog: [v1.35.1...v1.35.2](https://github.com/openai/openai-python/compare/v1.35.1...v1.35.2) diff --git a/pyproject.toml b/pyproject.toml index 91a3f5d19d..dfe148cc51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.2" +version = "1.35.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f053bcea86..1bba5023e0 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.2" # x-release-please-version +__version__ = "1.35.3" # x-release-please-version From 293c02d0cb095679579b79e2996bc3f830f682e1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 10:16:40 +0000 Subject: [PATCH 485/914] chore(doc): clarify service tier default value (#1496) --- .stats.yml | 2 +- src/openai/resources/chat/completions.py | 18 ++++++++++++------ .../types/chat/completion_create_params.py | 3 ++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index aa7e8427b0..04682ea0a6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-8fe357c6b5a425d810d731e4102a052d8e38c5e2d66950e6de1025415160bf88.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-3a69e1cc9e1efda3fb82d0fb35961749f886a87594dae9d8d2aa5c60f157f5d2.yml diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index d50bce0757..d73ece2109 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -169,7 +169,8 @@ def create( - If set to 'auto', the system will utilize scale tier credits until they are exhausted. - - If set to 'default', the request will be processed in the shared cluster. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. When this parameter is set, the response body will include the `service_tier` utilized. @@ -363,7 +364,8 @@ def create( - If set to 'auto', the system will utilize scale tier credits until they are exhausted. - - If set to 'default', the request will be processed in the shared cluster. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. When this parameter is set, the response body will include the `service_tier` utilized. @@ -550,7 +552,8 @@ def create( - If set to 'auto', the system will utilize scale tier credits until they are exhausted. - - If set to 'default', the request will be processed in the shared cluster. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. When this parameter is set, the response body will include the `service_tier` utilized. @@ -812,7 +815,8 @@ async def create( - If set to 'auto', the system will utilize scale tier credits until they are exhausted. - - If set to 'default', the request will be processed in the shared cluster. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. When this parameter is set, the response body will include the `service_tier` utilized. @@ -1006,7 +1010,8 @@ async def create( - If set to 'auto', the system will utilize scale tier credits until they are exhausted. - - If set to 'default', the request will be processed in the shared cluster. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. When this parameter is set, the response body will include the `service_tier` utilized. @@ -1193,7 +1198,8 @@ async def create( - If set to 'auto', the system will utilize scale tier credits until they are exhausted. - - If set to 'default', the request will be processed in the shared cluster. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. When this parameter is set, the response body will include the `service_tier` utilized. diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 21187f3741..85157653f2 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -153,7 +153,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): - If set to 'auto', the system will utilize scale tier credits until they are exhausted. - - If set to 'default', the request will be processed in the shared cluster. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. When this parameter is set, the response body will include the `service_tier` utilized. From d3254d12f9ae9178dfb471aa963a4ebc670e7b8a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 13:56:05 +0000 Subject: [PATCH 486/914] fix(docs): fix link to advanced python httpx docs (#1499) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e351ba03c..1ea79f8d80 100644 --- a/README.md +++ b/README.md @@ -560,7 +560,7 @@ You can directly override the [httpx client](https://www.python-httpx.org/api/#c - Support for proxies - Custom transports -- Additional [advanced](https://www.python-httpx.org/advanced/#client-instances) functionality +- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality ```python from openai import OpenAI, DefaultHttpxClient From 8061d18dd8bb1f9f17a46ac0d90edb2592a132a0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 01:12:25 +0000 Subject: [PATCH 487/914] fix: temporarily patch upstream version to fix broken release flow (#1500) --- bin/publish-pypi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/publish-pypi b/bin/publish-pypi index 826054e924..05bfccbb71 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -3,4 +3,7 @@ set -eux mkdir -p dist rye build --clean +# Patching importlib-metadata version until upstream library version is updated +# https://github.com/pypa/twine/issues/977#issuecomment-2189800841 +"$HOME/.rye/self/bin/python3" -m pip install 'importlib-metadata==7.2.1' rye publish --yes --token=$PYPI_TOKEN From 254937a733b95366d21f5bda45b2f84693488e42 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 01:13:13 +0000 Subject: [PATCH 488/914] release: 1.35.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7d2dc6aa83..994b53c2bf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.3" + ".": "1.35.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dcfe237ea..f04a26588e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.35.4 (2024-06-26) + +Full Changelog: [v1.35.3...v1.35.4](https://github.com/openai/openai-python/compare/v1.35.3...v1.35.4) + +### Bug Fixes + +* **docs:** fix link to advanced python httpx docs ([#1499](https://github.com/openai/openai-python/issues/1499)) ([cf45cd5](https://github.com/openai/openai-python/commit/cf45cd5942cecec569072146673ddfc0e0ec108e)) +* temporarily patch upstream version to fix broken release flow ([#1500](https://github.com/openai/openai-python/issues/1500)) ([4f10470](https://github.com/openai/openai-python/commit/4f10470f5f74fc258a78fa6d897d8ab5b70dcf52)) + + +### Chores + +* **doc:** clarify service tier default value ([#1496](https://github.com/openai/openai-python/issues/1496)) ([ba39667](https://github.com/openai/openai-python/commit/ba39667c4faa8e10457347be41334ca9639186d4)) + ## 1.35.3 (2024-06-20) Full Changelog: [v1.35.2...v1.35.3](https://github.com/openai/openai-python/compare/v1.35.2...v1.35.3) diff --git a/pyproject.toml b/pyproject.toml index dfe148cc51..afbc56485e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.3" +version = "1.35.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1bba5023e0..6439aacf1d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.3" # x-release-please-version +__version__ = "1.35.4" # x-release-please-version From 612135773016d61b4e064600694cbdf4bd73284d Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 26 Jun 2024 11:09:28 +0100 Subject: [PATCH 489/914] fix(cli/migrate): avoid reliance on Python 3.12 argument --- src/openai/cli/_tools/migrate.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/openai/cli/_tools/migrate.py b/src/openai/cli/_tools/migrate.py index 53073b866f..7c10bb7f85 100644 --- a/src/openai/cli/_tools/migrate.py +++ b/src/openai/cli/_tools/migrate.py @@ -138,7 +138,10 @@ def install() -> Path: unpacked_dir.mkdir(parents=True, exist_ok=True) with tarfile.open(temp_file, "r:gz") as archive: - archive.extractall(unpacked_dir, filter="data") + if sys.version_info >= (3, 12): + archive.extractall(unpacked_dir, filter="data") + else: + archive.extractall(unpacked_dir) for item in unpacked_dir.iterdir(): item.rename(target_dir / item.name) From c34cb641b1334d5a780e45488ad07e3abe1251ee Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:09:53 +0000 Subject: [PATCH 490/914] release: 1.35.5 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 994b53c2bf..940027a677 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.4" + ".": "1.35.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f04a26588e..39672b081b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.5 (2024-06-26) + +Full Changelog: [v1.35.4...v1.35.5](https://github.com/openai/openai-python/compare/v1.35.4...v1.35.5) + +### Bug Fixes + +* **cli/migrate:** avoid reliance on Python 3.12 argument ([be7a06b](https://github.com/openai/openai-python/commit/be7a06b3875e3ecb9229d67a41e290ca218f092d)) + ## 1.35.4 (2024-06-26) Full Changelog: [v1.35.3...v1.35.4](https://github.com/openai/openai-python/compare/v1.35.3...v1.35.4) diff --git a/pyproject.toml b/pyproject.toml index afbc56485e..d2cf1d0b5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.4" +version = "1.35.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6439aacf1d..4e8d735687 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.4" # x-release-please-version +__version__ = "1.35.5" # x-release-please-version From 137f38bab43fe41435c4045c668d749726aac393 Mon Sep 17 00:00:00 2001 From: Nino Risteski <95188570+NinoRisteski@users.noreply.github.com> Date: Thu, 27 Jun 2024 14:57:19 +0200 Subject: [PATCH 491/914] docs(readme): improve some wording (#1392) fixed a few typos --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1ea79f8d80..15853f27c6 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ so that your API Key is not stored in source control. When interacting with the API some actions such as starting a Run and adding files to vector stores are asynchronous and take time to complete. The SDK includes helper functions which will poll the status until it reaches a terminal state and then return the resulting object. -If an API method results in an action which could benefit from polling there will be a corresponding version of the +If an API method results in an action that could benefit from polling there will be a corresponding version of the method ending in '\_and_poll'. For instance to create a Run and poll until it reaches a terminal state you can run: @@ -71,7 +71,7 @@ More information on the lifecycle of a Run can be found in the [Run Lifecycle Do ### Bulk Upload Helpers -When creating an interacting with vector stores, you can use the polling helpers to monitor the status of operations. +When creating and interacting with vector stores, you can use polling helpers to monitor the status of operations. For convenience, we also provide a bulk upload helper to allow you to simultaneously upload several files at once. ```python @@ -85,7 +85,7 @@ batch = await client.vector_stores.file_batches.upload_and_poll( ### Streaming Helpers -The SDK also includes helpers to process streams and handle the incoming events. +The SDK also includes helpers to process streams and handle incoming events. ```python with client.beta.threads.runs.stream( @@ -201,7 +201,7 @@ completion = openai.chat.completions.create( print(completion.choices[0].message.content) ``` -The API is the exact same as the standard client instance based API. +The API is the exact same as the standard client instance-based API. This is intended to be used within REPLs or notebooks for faster iteration, **not** in application code. From 7f067717a0cc0bc371a1ae43d5e69905cdbe32c0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 12:57:42 +0000 Subject: [PATCH 492/914] release: 1.35.6 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 940027a677..964fe0a6ee 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.5" + ".": "1.35.6" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 39672b081b..6cb68a071c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.6 (2024-06-27) + +Full Changelog: [v1.35.5...v1.35.6](https://github.com/openai/openai-python/compare/v1.35.5...v1.35.6) + +### Documentation + +* **readme:** improve some wording ([#1392](https://github.com/openai/openai-python/issues/1392)) ([a58a052](https://github.com/openai/openai-python/commit/a58a05215b560ebcf3ff3eb1dd997259720a48f3)) + ## 1.35.5 (2024-06-26) Full Changelog: [v1.35.4...v1.35.5](https://github.com/openai/openai-python/compare/v1.35.4...v1.35.5) diff --git a/pyproject.toml b/pyproject.toml index d2cf1d0b5e..452f4dc6d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.5" +version = "1.35.6" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 4e8d735687..b93ac747f6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.5" # x-release-please-version +__version__ = "1.35.6" # x-release-please-version From 42fb72fd6b0886d19cf58d358f99187b5113ea34 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:32:04 +0000 Subject: [PATCH 493/914] fix(build): include more files in sdist builds (#1504) --- pyproject.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 452f4dc6d1..e645db6772 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -105,6 +105,21 @@ include = [ [tool.hatch.build.targets.wheel] packages = ["src/openai"] +[tool.hatch.build.targets.sdist] +# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc) +include = [ + "/*.toml", + "/*.json", + "/*.lock", + "/*.md", + "/mypy.ini", + "/noxfile.py", + "bin/*", + "examples/*", + "src/*", + "tests/*", +] + [tool.hatch.metadata.hooks.fancy-pypi-readme] content-type = "text/markdown" From 6316b7c0f182877e578a3dbed2fd129b63538af5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:32:30 +0000 Subject: [PATCH 494/914] release: 1.35.7 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 964fe0a6ee..873dd128c1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.6" + ".": "1.35.7" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb68a071c..37f920b3a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.7 (2024-06-27) + +Full Changelog: [v1.35.6...v1.35.7](https://github.com/openai/openai-python/compare/v1.35.6...v1.35.7) + +### Bug Fixes + +* **build:** include more files in sdist builds ([#1504](https://github.com/openai/openai-python/issues/1504)) ([730c1b5](https://github.com/openai/openai-python/commit/730c1b53b1a61e218a85aa2d1cf3ba4775618755)) + ## 1.35.6 (2024-06-27) Full Changelog: [v1.35.5...v1.35.6](https://github.com/openai/openai-python/compare/v1.35.5...v1.35.6) diff --git a/pyproject.toml b/pyproject.toml index e645db6772..658d5a412b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.6" +version = "1.35.7" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b93ac747f6..b9f757681a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.6" # x-release-please-version +__version__ = "1.35.7" # x-release-please-version From 2dd44460d35dc4d79057d0c27d2324a39048885e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 12:41:56 +0000 Subject: [PATCH 495/914] chore(internal): add reflection helper function (#1508) --- src/openai/_utils/__init__.py | 5 ++++- src/openai/_utils/_reflection.py | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 667e2473f6..3efe66c8e8 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -49,4 +49,7 @@ maybe_transform as maybe_transform, async_maybe_transform as async_maybe_transform, ) -from ._reflection import function_has_argument as function_has_argument +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, +) diff --git a/src/openai/_utils/_reflection.py b/src/openai/_utils/_reflection.py index e134f58e08..9a53c7bd21 100644 --- a/src/openai/_utils/_reflection.py +++ b/src/openai/_utils/_reflection.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import inspect from typing import Any, Callable @@ -6,3 +8,35 @@ 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(), +) -> 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(source_param.annotation)}" + ) + continue + + if errors: + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) From 24347efc6d94faddb10b773b04c2b5afa38b2ea6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 14:11:37 +0000 Subject: [PATCH 496/914] chore: gitignore test server logs (#1509) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0f9a66a976..8779740800 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.prism.log .vscode _dev From edf8d80bea56ee1d530e582f57a75600230e52ab Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:20:52 +0000 Subject: [PATCH 497/914] chore(internal): add helper method for constructing `BaseModel`s (#1517) --- src/openai/_models.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/openai/_models.py b/src/openai/_models.py index 75c68cc730..5d95bb4b2b 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -10,6 +10,7 @@ ClassVar, Protocol, Required, + ParamSpec, TypedDict, TypeGuard, final, @@ -67,6 +68,9 @@ __all__ = ["BaseModel", "GenericModel"] _T = TypeVar("_T") +_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") + +P = ParamSpec("P") @runtime_checkable @@ -379,6 +383,29 @@ def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericMo return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) +def build( + base_model_cls: Callable[P, _BaseModelT], + *args: P.args, + **kwargs: P.kwargs, +) -> _BaseModelT: + """Construct a BaseModel class without validation. + + This is useful for cases where you need to instantiate a `BaseModel` + from an API response as this provides type-safe params which isn't supported + by helpers like `construct_type()`. + + ```py + build(MyModel, my_field_a="foo", my_field_b=123) + ``` + """ + if args: + raise TypeError( + "Received positional arguments which are not supported; Keyword arguments must be used instead", + ) + + return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) + + def construct_type(*, value: object, type_: object) -> object: """Loose coercion to the expected type with construction of nested values. From d9540b4da9c87e229fc53b39288748ab3adce44e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:36:44 +0000 Subject: [PATCH 498/914] chore(internal): add rich as a dev dependency (#1514) it's often very helpful when writing demo scripts --- pyproject.toml | 1 + requirements-dev.lock | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 658d5a412b..d1bd9c19fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,7 @@ dev-dependencies = [ "nox", "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", + "rich>=13.7.1", "inline-snapshot >=0.7.0", "azure-identity >=1.14.1", "types-tqdm > 4", diff --git a/requirements-dev.lock b/requirements-dev.lock index c5416cd4db..3e3284cebc 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -70,11 +70,15 @@ idna==3.4 importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest -inline-snapshot==0.7.0 -msal==1.28.0 +inline-snapshot==0.10.2 +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py +msal==1.29.0 # via azure-identity # via msal-extensions -msal-extensions==1.1.0 +msal-extensions==1.2.0 # via azure-identity mypy==1.7.1 mypy-extensions==1.0.0 @@ -91,7 +95,6 @@ outcome==1.3.0.post0 # via trio packaging==23.2 # via black - # via msal-extensions # via nox # via pytest pandas==2.1.4 @@ -115,6 +118,8 @@ pydantic==2.7.1 # via openai pydantic-core==2.18.2 # via pydantic +pygments==2.18.0 + # via rich pyjwt==2.8.0 # via msal pyright==1.1.364 @@ -131,6 +136,8 @@ requests==2.31.0 # via azure-core # via msal respx==0.20.2 +rich==13.7.1 + # via inline-snapshot ruff==0.1.9 setuptools==68.2.2 # via nodeenv From fee32a0b23fb8ae4e963c1ce8593d387a02a4b3d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:26:45 +0000 Subject: [PATCH 499/914] release: 1.35.8 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 11 +++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 873dd128c1..1ef8c632ae 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.7" + ".": "1.35.8" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 37f920b3a9..1e40cafda7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 1.35.8 (2024-07-02) + +Full Changelog: [v1.35.7...v1.35.8](https://github.com/openai/openai-python/compare/v1.35.7...v1.35.8) + +### Chores + +* gitignore test server logs ([#1509](https://github.com/openai/openai-python/issues/1509)) ([936d840](https://github.com/openai/openai-python/commit/936d84094a28ad0a2b4a20e2b3bbf1674048223e)) +* **internal:** add helper method for constructing `BaseModel`s ([#1517](https://github.com/openai/openai-python/issues/1517)) ([e5ddbf5](https://github.com/openai/openai-python/commit/e5ddbf554ce4b6be4b59114a36e69f02ca724acf)) +* **internal:** add reflection helper function ([#1508](https://github.com/openai/openai-python/issues/1508)) ([6044e1b](https://github.com/openai/openai-python/commit/6044e1bbfa9e46a01faf5a9edf198f86fa4c6dd0)) +* **internal:** add rich as a dev dependency ([#1514](https://github.com/openai/openai-python/issues/1514)) ([8a2b4e4](https://github.com/openai/openai-python/commit/8a2b4e4c1233dca916531ebc65d65a8d35fa7b7b)) + ## 1.35.7 (2024-06-27) Full Changelog: [v1.35.6...v1.35.7](https://github.com/openai/openai-python/compare/v1.35.6...v1.35.7) diff --git a/pyproject.toml b/pyproject.toml index d1bd9c19fb..ea1f76bf42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.7" +version = "1.35.8" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b9f757681a..c612421e90 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.7" # x-release-please-version +__version__ = "1.35.8" # x-release-please-version From 1c59013ad29de370a96b44fcd14da2956cc2a03d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 19:13:30 +0000 Subject: [PATCH 500/914] fix(client): always respect content-type multipart/form-data if provided (#1519) --- src/openai/_base_client.py | 20 +++++++++-- src/openai/resources/audio/transcriptions.py | 18 +++++----- src/openai/resources/audio/translations.py | 18 +++++----- src/openai/resources/files.py | 18 +++++----- src/openai/resources/images.py | 36 +++++++++----------- 5 files changed, 58 insertions(+), 52 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 84004ebba5..2f4b0c7fbd 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -58,6 +58,7 @@ HttpxSendArgs, AsyncTransport, RequestOptions, + HttpxRequestFiles, ModelBuilderProtocol, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping @@ -460,6 +461,7 @@ def _build_request( headers = self._build_headers(options) params = _merge_mappings(self.default_query, options.params) content_type = headers.get("Content-Type") + files = options.files # If the given Content-Type header is multipart/form-data then it # has to be removed so that httpx can generate the header with @@ -473,7 +475,7 @@ def _build_request( headers.pop("Content-Type") # As we are now sending multipart/form-data instead of application/json - # we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding if json_data: if not is_dict(json_data): raise TypeError( @@ -481,6 +483,15 @@ def _build_request( ) kwargs["data"] = self._serialize_multipartform(json_data) + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, @@ -493,7 +504,7 @@ def _build_request( # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, json=json_data, - files=options.files, + files=files, **kwargs, ) @@ -1891,6 +1902,11 @@ def make_request_options( return options +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + class OtherPlatform: def __init__(self, name: str) -> None: self.name = name diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 995680186b..c03137dbfd 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -108,11 +108,10 @@ def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/audio/transcriptions", body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), @@ -205,11 +204,10 @@ async def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/audio/transcriptions", body=await async_maybe_transform(body, transcription_create_params.TranscriptionCreateParams), diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index d711ee2fbd..485e1a33df 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -93,11 +93,10 @@ def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/audio/translations", body=maybe_transform(body, translation_create_params.TranslationCreateParams), @@ -175,11 +174,10 @@ async def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/audio/translations", body=await async_maybe_transform(body, translation_create_params.TranslationCreateParams), diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 432ac30913..75c971a8bc 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -111,11 +111,10 @@ def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/files", body=maybe_transform(body, file_create_params.FileCreateParams), @@ -394,11 +393,10 @@ async def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/files", body=await async_maybe_transform(body, file_create_params.FileCreateParams), diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 74b2a46a3f..3728392f93 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -95,11 +95,10 @@ def create_variation( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/images/variations", body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), @@ -179,11 +178,10 @@ def edit( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["mask"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/images/edits", body=maybe_transform(body, image_edit_params.ImageEditParams), @@ -343,11 +341,10 @@ async def create_variation( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/images/variations", body=await async_maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), @@ -427,11 +424,10 @@ async def edit( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["mask"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/images/edits", body=await async_maybe_transform(body, image_edit_params.ImageEditParams), From 4826fe9dace2765bd79749c3c45dbd4cc2381e34 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 19:50:24 +0000 Subject: [PATCH 501/914] chore: minor change to tests (#1521) --- .stats.yml | 2 +- tests/api_resources/chat/test_completions.py | 8 ++++---- tests/api_resources/test_completions.py | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.stats.yml b/.stats.yml index 04682ea0a6..57f5afaffe 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-3a69e1cc9e1efda3fb82d0fb35961749f886a87594dae9d8d2aa5c60f157f5d2.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-27d8d6da893c1cdd53b491ec05153df22b1e113965f253a1d6eb8d75b628173f.yml diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 87df11d1ee..5cb2a8c717 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -59,7 +59,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: parallel_tool_calls=True, presence_penalty=-2, response_format={"type": "json_object"}, - seed=-9223372036854776000, + seed=-9007199254740991, service_tier="auto", stop="string", stream=False, @@ -176,7 +176,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: parallel_tool_calls=True, presence_penalty=-2, response_format={"type": "json_object"}, - seed=-9223372036854776000, + seed=-9007199254740991, service_tier="auto", stop="string", stream_options={"include_usage": True}, @@ -295,7 +295,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn parallel_tool_calls=True, presence_penalty=-2, response_format={"type": "json_object"}, - seed=-9223372036854776000, + seed=-9007199254740991, service_tier="auto", stop="string", stream=False, @@ -412,7 +412,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn parallel_tool_calls=True, presence_penalty=-2, response_format={"type": "json_object"}, - seed=-9223372036854776000, + seed=-9007199254740991, service_tier="auto", stop="string", stream_options={"include_usage": True}, diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 69d914200f..ad2679cabe 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -38,7 +38,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_tokens=16, n=1, presence_penalty=-2, - seed=-9223372036854776000, + seed=-9007199254740991, stop="\n", stream=False, stream_options={"include_usage": True}, @@ -98,7 +98,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_tokens=16, n=1, presence_penalty=-2, - seed=-9223372036854776000, + seed=-9007199254740991, stop="\n", stream_options={"include_usage": True}, suffix="test.", @@ -160,7 +160,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_tokens=16, n=1, presence_penalty=-2, - seed=-9223372036854776000, + seed=-9007199254740991, stop="\n", stream=False, stream_options={"include_usage": True}, @@ -220,7 +220,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_tokens=16, n=1, presence_penalty=-2, - seed=-9223372036854776000, + seed=-9007199254740991, stop="\n", stream_options={"include_usage": True}, suffix="test.", From 58bec2ffe5274b37fed3adad8da188f0cbc5406c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 19:50:51 +0000 Subject: [PATCH 502/914] release: 1.35.9 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1ef8c632ae..84be15d4a5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.8" + ".": "1.35.9" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e40cafda7..03aa28e2fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.35.9 (2024-07-02) + +Full Changelog: [v1.35.8...v1.35.9](https://github.com/openai/openai-python/compare/v1.35.8...v1.35.9) + +### Bug Fixes + +* **client:** always respect content-type multipart/form-data if provided ([#1519](https://github.com/openai/openai-python/issues/1519)) ([6da55e1](https://github.com/openai/openai-python/commit/6da55e10c4ba8c78687baedc68d5599ea120d05c)) + + +### Chores + +* minor change to tests ([#1521](https://github.com/openai/openai-python/issues/1521)) ([a679c0b](https://github.com/openai/openai-python/commit/a679c0bd1e041434440174daa7a64289746856d1)) + ## 1.35.8 (2024-07-02) Full Changelog: [v1.35.7...v1.35.8](https://github.com/openai/openai-python/compare/v1.35.7...v1.35.8) diff --git a/pyproject.toml b/pyproject.toml index ea1f76bf42..be7a95dd69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.8" +version = "1.35.9" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index c612421e90..0a534db76c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.8" # x-release-please-version +__version__ = "1.35.9" # x-release-please-version From 94fc49d8b198b4b9fe98bf22883ed82b060e865b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 17:37:28 +0000 Subject: [PATCH 503/914] chore(ci): update rye to v0.35.0 (#1523) --- .devcontainer/Dockerfile | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/create-releases.yml | 4 ++-- .github/workflows/publish-pypi.yml | 4 ++-- requirements-dev.lock | 1 + requirements.lock | 1 + 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 83bca8f716..ac9a2e7521 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode -RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash +RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6fc5b36597..7e58412065 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: 0.24.0 + RYE_VERSION: '0.35.0' RYE_INSTALL_OPTION: '--yes' - name: Install dependencies @@ -42,7 +42,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: 0.24.0 + RYE_VERSION: '0.35.0' RYE_INSTALL_OPTION: '--yes' - name: Bootstrap diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 1ac03ede3f..2a97049033 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -28,8 +28,8 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: 0.24.0 - RYE_INSTALL_OPTION: "--yes" + RYE_VERSION: '0.35.0' + RYE_INSTALL_OPTION: '--yes' - name: Publish to PyPI if: ${{ steps.release.outputs.releases_created }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index aae985b27e..44027a3c4c 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -17,8 +17,8 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: 0.24.0 - RYE_INSTALL_OPTION: "--yes" + RYE_VERSION: '0.35.0' + RYE_INSTALL_OPTION: '--yes' - name: Publish to PyPI run: | diff --git a/requirements-dev.lock b/requirements-dev.lock index 3e3284cebc..21a6b8d20c 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -6,6 +6,7 @@ # features: [] # all-features: true # with-sources: false +# generate-hashes: false -e file:. annotated-types==0.6.0 diff --git a/requirements.lock b/requirements.lock index 47cf8a40e9..3c3d6ae702 100644 --- a/requirements.lock +++ b/requirements.lock @@ -6,6 +6,7 @@ # features: [] # all-features: true # with-sources: false +# generate-hashes: false -e file:. annotated-types==0.6.0 From 50371bf3151ebb1a43017abfe205d4d9b2e5faac Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 17:37:56 +0000 Subject: [PATCH 504/914] release: 1.35.10 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 84be15d4a5..cb142b301b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.9" + ".": "1.35.10" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 03aa28e2fd..bd9eebd658 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.10 (2024-07-03) + +Full Changelog: [v1.35.9...v1.35.10](https://github.com/openai/openai-python/compare/v1.35.9...v1.35.10) + +### Chores + +* **ci:** update rye to v0.35.0 ([#1523](https://github.com/openai/openai-python/issues/1523)) ([dd118c4](https://github.com/openai/openai-python/commit/dd118c422019df00b153104b7bddf892c2ec7417)) + ## 1.35.9 (2024-07-02) Full Changelog: [v1.35.8...v1.35.9](https://github.com/openai/openai-python/compare/v1.35.8...v1.35.9) diff --git a/pyproject.toml b/pyproject.toml index be7a95dd69..348abf87a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.9" +version = "1.35.10" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0a534db76c..6765e6c941 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.9" # x-release-please-version +__version__ = "1.35.10" # x-release-please-version From 665f4dcb9da63f2f88f189e05a1c531830e52155 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:52:07 +0000 Subject: [PATCH 505/914] chore(internal): minor request options handling changes (#1534) --- src/openai/_base_client.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 2f4b0c7fbd..7ab2a56169 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -956,6 +956,11 @@ def _request( stream: bool, stream_cls: type[_StreamT] | None, ) -> ResponseT | _StreamT: + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + cast_to = self._maybe_override_cast_to(cast_to, options) self._prepare_options(options) @@ -980,7 +985,7 @@ def _request( if retries > 0: return self._retry_request( - options, + input_options, cast_to, retries, stream=stream, @@ -995,7 +1000,7 @@ def _request( if retries > 0: return self._retry_request( - options, + input_options, cast_to, retries, stream=stream, @@ -1024,7 +1029,7 @@ def _request( if retries > 0 and self._should_retry(err.response): err.response.close() return self._retry_request( - options, + input_options, cast_to, retries, err.response.headers, @@ -1533,6 +1538,11 @@ async def _request( # execute it earlier while we are in an async context self._platform = await asyncify(get_platform)() + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + cast_to = self._maybe_override_cast_to(cast_to, options) await self._prepare_options(options) @@ -1555,7 +1565,7 @@ async def _request( if retries > 0: return await self._retry_request( - options, + input_options, cast_to, retries, stream=stream, @@ -1570,7 +1580,7 @@ async def _request( if retries > 0: return await self._retry_request( - options, + input_options, cast_to, retries, stream=stream, @@ -1593,7 +1603,7 @@ async def _request( if retries > 0 and self._should_retry(err.response): await err.response.aclose() return await self._retry_request( - options, + input_options, cast_to, retries, err.response.headers, From 41f682bc055de55350f025ef4e664949cbf31eb6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:52:35 +0000 Subject: [PATCH 506/914] release: 1.35.11 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cb142b301b..f0ed4549e0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.10" + ".": "1.35.11" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bd9eebd658..52c8216f28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.11 (2024-07-09) + +Full Changelog: [v1.35.10...v1.35.11](https://github.com/openai/openai-python/compare/v1.35.10...v1.35.11) + +### Chores + +* **internal:** minor request options handling changes ([#1534](https://github.com/openai/openai-python/issues/1534)) ([8b0e493](https://github.com/openai/openai-python/commit/8b0e49302b3fcc32cf02393bf28354c577188904)) + ## 1.35.10 (2024-07-03) Full Changelog: [v1.35.9...v1.35.10](https://github.com/openai/openai-python/compare/v1.35.9...v1.35.10) diff --git a/pyproject.toml b/pyproject.toml index 348abf87a7..3098d4c24f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.10" +version = "1.35.11" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6765e6c941..237442ec47 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.10" # x-release-please-version +__version__ = "1.35.11" # x-release-please-version From 6b01ba6af35c3b1fae0cbf4a012c5dcfa97ae10f Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 9 Jul 2024 19:03:49 +0100 Subject: [PATCH 507/914] fix(azure): refresh auth token during retries (#1533) --- tests/lib/test_azure.py | 88 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py index 9360b2925a..a9d3478350 100644 --- a/tests/lib/test_azure.py +++ b/tests/lib/test_azure.py @@ -1,7 +1,9 @@ -from typing import Union -from typing_extensions import Literal +from typing import Union, cast +from typing_extensions import Literal, Protocol +import httpx import pytest +from respx import MockRouter from openai._models import FinalRequestOptions from openai.lib.azure import AzureOpenAI, AsyncAzureOpenAI @@ -22,6 +24,10 @@ ) +class MockRequestCall(Protocol): + request: httpx.Request + + @pytest.mark.parametrize("client", [sync_client, async_client]) def test_implicit_deployment_path(client: Client) -> None: req = client._build_request( @@ -64,3 +70,81 @@ def test_client_copying_override_options(client: Client) -> None: api_version="2022-05-01", ) assert copied._custom_query == {"api-version": "2022-05-01"} + + +@pytest.mark.respx() +def test_client_token_provider_refresh_sync(respx_mock: MockRouter) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01" + ).mock( + side_effect=[ + httpx.Response(500, json={"error": "server error"}), + httpx.Response(200, json={"foo": "bar"}), + ] + ) + + counter = 0 + + def token_provider() -> str: + nonlocal counter + + counter += 1 + + if counter == 1: + return "first" + + return "second" + + client = AzureOpenAI( + api_version="2024-02-01", + azure_ad_token_provider=token_provider, + azure_endpoint="https://example-resource.azure.openai.com/", + ) + client.chat.completions.create(messages=[], model="gpt-4") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + + assert len(calls) == 2 + + assert calls[0].request.headers.get("Authorization") == "Bearer first" + assert calls[1].request.headers.get("Authorization") == "Bearer second" + + +@pytest.mark.asyncio +@pytest.mark.respx() +async def test_client_token_provider_refresh_async(respx_mock: MockRouter) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01" + ).mock( + side_effect=[ + httpx.Response(500, json={"error": "server error"}), + httpx.Response(200, json={"foo": "bar"}), + ] + ) + + counter = 0 + + def token_provider() -> str: + nonlocal counter + + counter += 1 + + if counter == 1: + return "first" + + return "second" + + client = AsyncAzureOpenAI( + api_version="2024-02-01", + azure_ad_token_provider=token_provider, + azure_endpoint="https://example-resource.azure.openai.com/", + ) + + await client.chat.completions.create(messages=[], model="gpt-4") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + + assert len(calls) == 2 + + assert calls[0].request.headers.get("Authorization") == "Bearer first" + assert calls[1].request.headers.get("Authorization") == "Bearer second" From 8cccea6df6174e6eecb3674b474a1da1add94290 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 9 Jul 2024 19:13:30 +0100 Subject: [PATCH 508/914] fix(tests): fresh_env() now resets new environment values --- tests/test_module_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 05b5f81111..6bab33a1d7 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -110,6 +110,7 @@ def fresh_env() -> Iterator[None]: _os.environ.clear() yield finally: + _os.environ.clear() _os.environ.update(old) From e0ed37c19a99462db73ada4ba40ae185442b6cbb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 18:13:54 +0000 Subject: [PATCH 509/914] release: 1.35.12 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f0ed4549e0..7ac9c7d661 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.11" + ".": "1.35.12" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 52c8216f28..7e0be66b84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.35.12 (2024-07-09) + +Full Changelog: [v1.35.11...v1.35.12](https://github.com/openai/openai-python/compare/v1.35.11...v1.35.12) + +### Bug Fixes + +* **azure:** refresh auth token during retries ([#1533](https://github.com/openai/openai-python/issues/1533)) ([287926e](https://github.com/openai/openai-python/commit/287926e4c0920b930af2b9d3d8b46a24e78e2979)) +* **tests:** fresh_env() now resets new environment values ([64da888](https://github.com/openai/openai-python/commit/64da888ca4d13f0b4b6d9e22ec93a897b2d6bb24)) + ## 1.35.11 (2024-07-09) Full Changelog: [v1.35.10...v1.35.11](https://github.com/openai/openai-python/compare/v1.35.10...v1.35.11) diff --git a/pyproject.toml b/pyproject.toml index 3098d4c24f..b81031b1fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.11" +version = "1.35.12" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 237442ec47..d7cdffe676 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.11" # x-release-please-version +__version__ = "1.35.12" # x-release-please-version From c80fe33638956852af890f0358d272620b17212f Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 10 Jul 2024 10:55:35 +0100 Subject: [PATCH 510/914] fix(threads/runs/create_and_run_stream): correct tool_resources param --- src/openai/resources/beta/threads/threads.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index a62ee8d1bb..8d3289658d 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -1062,7 +1062,7 @@ def create_and_run_stream( "stream": True, "thread": thread, "tools": tools, - "tool": tool_resources, + "tool_resources": tool_resources, "truncation_strategy": truncation_strategy, "top_p": top_p, }, @@ -2082,7 +2082,7 @@ def create_and_run_stream( "stream": True, "thread": thread, "tools": tools, - "tool": tool_resources, + "tool_resources": tool_resources, "truncation_strategy": truncation_strategy, "top_p": top_p, }, From 2df8aa3d15b42708bdf7e23da7411946c4f40da2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:38:16 +0000 Subject: [PATCH 511/914] chore(internal): add helper function (#1538) --- src/openai/_models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/openai/_models.py b/src/openai/_models.py index 5d95bb4b2b..eb7ce3bde9 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -643,6 +643,14 @@ def validate_type(*, type_: type[_T], value: object) -> _T: return cast(_T, _validate_non_model_type(type_=type_, value=value)) +def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: + """Add a pydantic config for the given type. + + Note: this is a no-op on Pydantic v1. + """ + setattr(typ, "__pydantic_config__", config) # noqa: B010 + + # our use of subclasssing here causes weirdness for type checkers, # so we just pretend that we don't subclass if TYPE_CHECKING: From ec4511a6fa4862770cf14b2b58d3493e9bacb062 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:38:47 +0000 Subject: [PATCH 512/914] release: 1.35.13 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7ac9c7d661..cbc88f07a5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.12" + ".": "1.35.13" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e0be66b84..ea823dcd66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.35.13 (2024-07-10) + +Full Changelog: [v1.35.12...v1.35.13](https://github.com/openai/openai-python/compare/v1.35.12...v1.35.13) + +### Bug Fixes + +* **threads/runs/create_and_run_stream:** correct tool_resources param ([8effd08](https://github.com/openai/openai-python/commit/8effd08be3ab1cf509bdbfd9f174f9186fdbf71f)) + + +### Chores + +* **internal:** add helper function ([#1538](https://github.com/openai/openai-python/issues/1538)) ([81655a0](https://github.com/openai/openai-python/commit/81655a012e28c0240e71cf74b77ad1f9ac630906)) + ## 1.35.12 (2024-07-09) Full Changelog: [v1.35.11...v1.35.12](https://github.com/openai/openai-python/compare/v1.35.11...v1.35.12) diff --git a/pyproject.toml b/pyproject.toml index b81031b1fa..24fee167ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.12" +version = "1.35.13" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d7cdffe676..40d6e8e31d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.12" # x-release-please-version +__version__ = "1.35.13" # x-release-please-version From f14f859370ac4545e5b46fba3cb4a1fafacfdd15 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 15 Jul 2024 10:42:17 +0100 Subject: [PATCH 513/914] chore(internal): minor formatting changes --- examples/assistant.py | 1 - src/openai/resources/beta/vector_stores/files.py | 6 ++++-- src/openai/types/audio/transcription.py | 1 - src/openai/types/audio/translation.py | 1 - src/openai/types/batch_request_counts.py | 1 - src/openai/types/beta/assistant_tool_choice_function.py | 1 - src/openai/types/completion_usage.py | 1 - src/openai/types/fine_tuning/fine_tuning_job_integration.py | 1 - src/openai/types/model_deleted.py | 1 - tests/lib/test_assistants.py | 1 + 10 files changed, 5 insertions(+), 10 deletions(-) diff --git a/examples/assistant.py b/examples/assistant.py index 0631494ecc..f6924a0c7d 100644 --- a/examples/assistant.py +++ b/examples/assistant.py @@ -1,4 +1,3 @@ - import openai # gets API Key from environment variable OPENAI_API_KEY diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/beta/vector_stores/files.py index bc1655027c..35ca331cc0 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -611,7 +611,9 @@ async def upload( polling helper method to wait for processing to complete). """ file_obj = await self._client.files.create(file=file, purpose="assistants") - return await self.create(vector_store_id=vector_store_id, file_id=file_obj.id, chunking_strategy=chunking_strategy) + return await self.create( + vector_store_id=vector_store_id, file_id=file_obj.id, chunking_strategy=chunking_strategy + ) async def upload_and_poll( self, @@ -627,7 +629,7 @@ async def upload_and_poll( vector_store_id=vector_store_id, file_id=file_obj.id, poll_interval_ms=poll_interval_ms, - chunking_strategy=chunking_strategy + chunking_strategy=chunking_strategy, ) diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index 0b6ab39e78..edb5f227fc 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["Transcription"] diff --git a/src/openai/types/audio/translation.py b/src/openai/types/audio/translation.py index 3d9ede2939..7c0e905189 100644 --- a/src/openai/types/audio/translation.py +++ b/src/openai/types/audio/translation.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["Translation"] diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py index ef6c84a0a1..7e1d49fb88 100644 --- a/src/openai/types/batch_request_counts.py +++ b/src/openai/types/batch_request_counts.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["BatchRequestCounts"] diff --git a/src/openai/types/beta/assistant_tool_choice_function.py b/src/openai/types/beta/assistant_tool_choice_function.py index d0d4255357..0c896d8087 100644 --- a/src/openai/types/beta/assistant_tool_choice_function.py +++ b/src/openai/types/beta/assistant_tool_choice_function.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["AssistantToolChoiceFunction"] diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index 0d57b96595..ac09afd479 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["CompletionUsage"] diff --git a/src/openai/types/fine_tuning/fine_tuning_job_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_integration.py index 8076313cae..9a66aa4f17 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_integration.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_integration.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject FineTuningJobIntegration = FineTuningJobWandbIntegrationObject diff --git a/src/openai/types/model_deleted.py b/src/openai/types/model_deleted.py index d9a48bb1b5..7f81e1b380 100644 --- a/src/openai/types/model_deleted.py +++ b/src/openai/types/model_deleted.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["ModelDeleted"] diff --git a/tests/lib/test_assistants.py b/tests/lib/test_assistants.py index 487b9938c7..38a47d4d12 100644 --- a/tests/lib/test_assistants.py +++ b/tests/lib/test_assistants.py @@ -48,6 +48,7 @@ def test_create_and_run_poll_method_definition_in_sync(sync: bool, client: OpenA exclude_params={"stream"}, ) + @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_create_and_run_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: checking_client: OpenAI | AsyncOpenAI = client if sync else async_client From 83ebf66f16b1664977e01fec0885f29c3588ecc4 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 15 Jul 2024 09:57:42 +0100 Subject: [PATCH 514/914] chore(internal): minor options / compat functions updates (#1549) --- src/openai/_base_client.py | 12 ++++++------ src/openai/_compat.py | 6 +++--- src/openai/lib/azure.py | 13 +++++++++---- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 7ab2a56169..4b93ab298c 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -880,9 +880,9 @@ def __exit__( def _prepare_options( self, options: FinalRequestOptions, # noqa: ARG002 - ) -> None: + ) -> FinalRequestOptions: """Hook for mutating the given options""" - return None + return options def _prepare_request( self, @@ -962,7 +962,7 @@ def _request( input_options = model_copy(options) cast_to = self._maybe_override_cast_to(cast_to, options) - self._prepare_options(options) + options = self._prepare_options(options) retries = self._remaining_retries(remaining_retries, options) request = self._build_request(options) @@ -1457,9 +1457,9 @@ async def __aexit__( async def _prepare_options( self, options: FinalRequestOptions, # noqa: ARG002 - ) -> None: + ) -> FinalRequestOptions: """Hook for mutating the given options""" - return None + return options async def _prepare_request( self, @@ -1544,7 +1544,7 @@ async def _request( input_options = model_copy(options) cast_to = self._maybe_override_cast_to(cast_to, options) - await self._prepare_options(options) + options = await self._prepare_options(options) retries = self._remaining_retries(remaining_retries, options) request = self._build_request(options) diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 74c7639b4c..c919b5adb3 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -118,10 +118,10 @@ def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: return model.__fields__ # type: ignore -def model_copy(model: _ModelT) -> _ModelT: +def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: if PYDANTIC_V2: - return model.model_copy() - return model.copy() # type: ignore + return model.model_copy(deep=deep) + return model.copy(deep=deep) # type: ignore def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index cbe57b7b98..433486fded 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -10,6 +10,7 @@ from .._types import NOT_GIVEN, Omit, Timeout, NotGiven from .._utils import is_given, is_mapping from .._client import OpenAI, AsyncOpenAI +from .._compat import model_copy from .._models import FinalRequestOptions from .._streaming import Stream, AsyncStream from .._exceptions import OpenAIError @@ -281,8 +282,10 @@ def _get_azure_ad_token(self) -> str | None: return None @override - def _prepare_options(self, options: FinalRequestOptions) -> None: + def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} + + options = model_copy(options) options.headers = headers azure_ad_token = self._get_azure_ad_token() @@ -296,7 +299,7 @@ def _prepare_options(self, options: FinalRequestOptions) -> None: # should never be hit raise ValueError("Unable to handle auth") - return super()._prepare_options(options) + return options class AsyncAzureOpenAI(BaseAzureClient[httpx.AsyncClient, AsyncStream[Any]], AsyncOpenAI): @@ -524,8 +527,10 @@ async def _get_azure_ad_token(self) -> str | None: return None @override - async def _prepare_options(self, options: FinalRequestOptions) -> None: + async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} + + options = model_copy(options) options.headers = headers azure_ad_token = await self._get_azure_ad_token() @@ -539,4 +544,4 @@ async def _prepare_options(self, options: FinalRequestOptions) -> None: # should never be hit raise ValueError("Unable to handle auth") - return await super()._prepare_options(options) + return options From 2e8ea52b08a74a06d227d35586c6250814958ecb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 18:20:46 +0000 Subject: [PATCH 515/914] chore(docs): minor update to formatting of API link in README (#1550) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15853f27c6..38d10899ae 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It is generated from our [OpenAPI specification](https://github.com/openai/opena ## Documentation -The REST API documentation can be found [on platform.openai.com](https://platform.openai.com/docs). The full API of this library can be found in [api.md](api.md). +The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). The full API of this library can be found in [api.md](api.md). ## Installation From 435a5805ccbd5939a68f7f359ab72e937ef86e59 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 18:21:14 +0000 Subject: [PATCH 516/914] release: 1.35.14 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 10 ++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cbc88f07a5..968573fdbe 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.13" + ".": "1.35.14" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ea823dcd66..134e36c1ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.35.14 (2024-07-15) + +Full Changelog: [v1.35.13...v1.35.14](https://github.com/openai/openai-python/compare/v1.35.13...v1.35.14) + +### Chores + +* **docs:** minor update to formatting of API link in README ([#1550](https://github.com/openai/openai-python/issues/1550)) ([a6e59c6](https://github.com/openai/openai-python/commit/a6e59c6bbff9e1132aa323c0ecb3be7f0692ae42)) +* **internal:** minor formatting changes ([ee1c62e](https://github.com/openai/openai-python/commit/ee1c62ede01872e76156d886af4aab5f8eb1cc64)) +* **internal:** minor options / compat functions updates ([#1549](https://github.com/openai/openai-python/issues/1549)) ([a0701b5](https://github.com/openai/openai-python/commit/a0701b5dbeda4ac2d8a4b093aee4bdad9d674ee2)) + ## 1.35.13 (2024-07-10) Full Changelog: [v1.35.12...v1.35.13](https://github.com/openai/openai-python/compare/v1.35.12...v1.35.13) diff --git a/pyproject.toml b/pyproject.toml index 24fee167ff..c958c63478 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.13" +version = "1.35.14" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 40d6e8e31d..34da91ad4b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.13" # x-release-please-version +__version__ = "1.35.14" # x-release-please-version From 8ce46febbba6d35b4b7bf2568e2c2b819d9edf99 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:33:58 +0000 Subject: [PATCH 517/914] chore(internal): update formatting (#1553) --- src/openai/types/audio/transcription.py | 1 + src/openai/types/audio/translation.py | 1 + src/openai/types/batch_request_counts.py | 1 + src/openai/types/beta/assistant_tool_choice_function.py | 1 + src/openai/types/completion_usage.py | 1 + src/openai/types/fine_tuning/fine_tuning_job_integration.py | 1 + src/openai/types/model_deleted.py | 1 + 7 files changed, 7 insertions(+) diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index edb5f227fc..0b6ab39e78 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from ..._models import BaseModel __all__ = ["Transcription"] diff --git a/src/openai/types/audio/translation.py b/src/openai/types/audio/translation.py index 7c0e905189..3d9ede2939 100644 --- a/src/openai/types/audio/translation.py +++ b/src/openai/types/audio/translation.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from ..._models import BaseModel __all__ = ["Translation"] diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py index 7e1d49fb88..ef6c84a0a1 100644 --- a/src/openai/types/batch_request_counts.py +++ b/src/openai/types/batch_request_counts.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from .._models import BaseModel __all__ = ["BatchRequestCounts"] diff --git a/src/openai/types/beta/assistant_tool_choice_function.py b/src/openai/types/beta/assistant_tool_choice_function.py index 0c896d8087..d0d4255357 100644 --- a/src/openai/types/beta/assistant_tool_choice_function.py +++ b/src/openai/types/beta/assistant_tool_choice_function.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from ..._models import BaseModel __all__ = ["AssistantToolChoiceFunction"] diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index ac09afd479..0d57b96595 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from .._models import BaseModel __all__ = ["CompletionUsage"] diff --git a/src/openai/types/fine_tuning/fine_tuning_job_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_integration.py index 9a66aa4f17..8076313cae 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_integration.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_integration.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject FineTuningJobIntegration = FineTuningJobWandbIntegrationObject diff --git a/src/openai/types/model_deleted.py b/src/openai/types/model_deleted.py index 7f81e1b380..d9a48bb1b5 100644 --- a/src/openai/types/model_deleted.py +++ b/src/openai/types/model_deleted.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from .._models import BaseModel __all__ = ["ModelDeleted"] From f8f01a6100997d55bf48599d3d766b7249fb65b5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 16:53:49 +0000 Subject: [PATCH 518/914] chore(docs): document how to do per-request http client customization (#1560) --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 38d10899ae..653fb9a2c3 100644 --- a/README.md +++ b/README.md @@ -575,6 +575,12 @@ client = OpenAI( ) ``` +You can also customize the client on a per-request basis by using `with_options()`: + +```python +client.with_options(http_client=DefaultHttpxClient(...)) +``` + ### Managing HTTP resources By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. From 0de7419c962805bbe9db18d99524b66673f7f8e9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 16:54:21 +0000 Subject: [PATCH 519/914] release: 1.35.15 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 968573fdbe..1441c27096 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.14" + ".": "1.35.15" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 134e36c1ba..580daadf44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.35.15 (2024-07-18) + +Full Changelog: [v1.35.14...v1.35.15](https://github.com/openai/openai-python/compare/v1.35.14...v1.35.15) + +### Chores + +* **docs:** document how to do per-request http client customization ([#1560](https://github.com/openai/openai-python/issues/1560)) ([24c0768](https://github.com/openai/openai-python/commit/24c076873c5cb2abe0d3e285b99aa110451b0f19)) +* **internal:** update formatting ([#1553](https://github.com/openai/openai-python/issues/1553)) ([e1389bc](https://github.com/openai/openai-python/commit/e1389bcc26f3aac63fc6bc9bb151c9a330d95b4e)) + ## 1.35.14 (2024-07-15) Full Changelog: [v1.35.13...v1.35.14](https://github.com/openai/openai-python/compare/v1.35.13...v1.35.14) diff --git a/pyproject.toml b/pyproject.toml index c958c63478..a43bfd89d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.14" +version = "1.35.15" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 34da91ad4b..eed4227390 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.14" # x-release-please-version +__version__ = "1.35.15" # x-release-please-version From 38fe1a63795946079649b1b85e7335ca94f7a6c3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:54:42 +0000 Subject: [PATCH 520/914] feat(api): add new gpt-4o-mini models (#1561) --- .stats.yml | 2 +- src/openai/resources/beta/assistants.py | 4 ++++ src/openai/resources/beta/threads/runs/runs.py | 16 ++++++++++++++++ src/openai/resources/beta/threads/threads.py | 16 ++++++++++++++++ src/openai/types/beta/assistant_create_params.py | 2 ++ .../types/beta/thread_create_and_run_params.py | 2 ++ .../types/beta/threads/run_create_params.py | 2 ++ src/openai/types/chat_model.py | 2 ++ 8 files changed, 45 insertions(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 57f5afaffe..27e2ce5ede 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-27d8d6da893c1cdd53b491ec05153df22b1e113965f253a1d6eb8d75b628173f.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-518ca6c60061d3e8bc0971facf40d752f2aea62e3522cc168ad29a1f29cab3dd.yml diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 5912aff77a..066db66913 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -51,6 +51,8 @@ def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -440,6 +442,8 @@ async def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 43069dd1ae..1759120bfe 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -87,6 +87,8 @@ def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -240,6 +242,8 @@ def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -392,6 +396,8 @@ def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -543,6 +549,8 @@ def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1661,6 +1669,8 @@ async def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1814,6 +1824,8 @@ async def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1966,6 +1978,8 @@ async def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2117,6 +2131,8 @@ async def create( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 8d3289658d..3720ab9a25 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -269,6 +269,8 @@ def create_and_run( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -421,6 +423,8 @@ def create_and_run( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -572,6 +576,8 @@ def create_and_run( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -722,6 +728,8 @@ def create_and_run( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1285,6 +1293,8 @@ async def create_and_run( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1437,6 +1447,8 @@ async def create_and_run( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1588,6 +1600,8 @@ async def create_and_run( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1738,6 +1752,8 @@ async def create_and_run( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index c9b0317831..754752ae65 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -28,6 +28,8 @@ class AssistantCreateParams(TypedDict, total=False): Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index dbbff415ec..9421a894d9 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -82,6 +82,8 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 89da241965..81cd85188b 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -79,6 +79,8 @@ class RunCreateParamsBase(TypedDict, total=False): Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index 0d2937ea32..87b2acb90a 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -7,6 +7,8 @@ ChatModel = Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", From 4c6a5ed71775e82a0bbff28bea3d401ec066238e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:55:14 +0000 Subject: [PATCH 521/914] release: 1.36.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1441c27096..f29e96b89e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.35.15" + ".": "1.36.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 580daadf44..92703a485f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.36.0 (2024-07-19) + +Full Changelog: [v1.35.15...v1.36.0](https://github.com/openai/openai-python/compare/v1.35.15...v1.36.0) + +### Features + +* **api:** add new gpt-4o-mini models ([#1561](https://github.com/openai/openai-python/issues/1561)) ([5672ad4](https://github.com/openai/openai-python/commit/5672ad40aaa3498f6143baa48fc22bb1a3475bea)) + ## 1.35.15 (2024-07-18) Full Changelog: [v1.35.14...v1.35.15](https://github.com/openai/openai-python/compare/v1.35.14...v1.35.15) diff --git a/pyproject.toml b/pyproject.toml index a43bfd89d7..0ec0fe5ce3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.35.15" +version = "1.36.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index eed4227390..f3975de68c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.35.15" # x-release-please-version +__version__ = "1.36.0" # x-release-please-version From 1a9124bf89fce69fc3c51a4bbf2f4fedb1749823 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 19 Jul 2024 15:39:29 +0100 Subject: [PATCH 522/914] fix(types): add gpt-4o-mini to more assistants methods --- src/openai/resources/beta/threads/threads.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 3720ab9a25..ff7fa70cf8 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -814,6 +814,8 @@ def create_and_run_poll( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -896,6 +898,8 @@ def create_and_run_stream( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -951,6 +955,8 @@ def create_and_run_stream( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1006,6 +1012,8 @@ def create_and_run_stream( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1838,6 +1846,8 @@ async def create_and_run_poll( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1922,6 +1932,8 @@ def create_and_run_stream( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -1977,6 +1989,8 @@ def create_and_run_stream( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2032,6 +2046,8 @@ def create_and_run_stream( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", From af8f606b3eef1af99f98929847f1b5baf19171ae Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 05:04:03 +0000 Subject: [PATCH 523/914] release: 1.36.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f29e96b89e..46ea083269 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.36.0" + ".": "1.36.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 92703a485f..c7cac5a9a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.36.1 (2024-07-20) + +Full Changelog: [v1.36.0...v1.36.1](https://github.com/openai/openai-python/compare/v1.36.0...v1.36.1) + +### Bug Fixes + +* **types:** add gpt-4o-mini to more assistants methods ([39a8a37](https://github.com/openai/openai-python/commit/39a8a372eb3f2d75fd4310d42294d05175a59fd8)) + ## 1.36.0 (2024-07-19) Full Changelog: [v1.35.15...v1.36.0](https://github.com/openai/openai-python/compare/v1.35.15...v1.36.0) diff --git a/pyproject.toml b/pyproject.toml index 0ec0fe5ce3..03a4a77f56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.36.0" +version = "1.36.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f3975de68c..6021f184f8 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.36.0" # x-release-please-version +__version__ = "1.36.1" # x-release-please-version From c814354fd769aaa3f387d3d1cdcccca3b90b7a2c Mon Sep 17 00:00:00 2001 From: Aurish Hammad Hafeez Date: Mon, 22 Jul 2024 15:30:24 +0500 Subject: [PATCH 524/914] fix(cli/audio): handle non-json response format (#1557) * Fix handling of --response-format in audio transcriptions create command * handle the string case in audio directly --------- Co-authored-by: Robert Craigie --- src/openai/cli/_api/audio.py | 52 +++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/openai/cli/_api/audio.py b/src/openai/cli/_api/audio.py index 90d21b9932..269c67df28 100644 --- a/src/openai/cli/_api/audio.py +++ b/src/openai/cli/_api/audio.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys from typing import TYPE_CHECKING, Any, Optional, cast from argparse import ArgumentParser @@ -7,6 +8,7 @@ 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 @@ -65,30 +67,42 @@ def transcribe(args: CLITranscribeArgs) -> None: with open(args.file, "rb") as file_reader: buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") - model = 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), + 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), + ), ) - print_model(model) + 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 = 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), + 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), + ), ) - print_model(model) + if isinstance(model, str): + sys.stdout.write(model + "\n") + else: + print_model(model) From 24ab406b410633d9df46cc5b0239e66f2e8f69ba Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:10:52 +0000 Subject: [PATCH 525/914] feat(api): add uploads endpoints (#1568) --- .stats.yml | 4 +- api.md | 26 + src/openai/_client.py | 8 + src/openai/resources/__init__.py | 14 + src/openai/resources/chat/completions.py | 6 + src/openai/resources/uploads/__init__.py | 33 ++ src/openai/resources/uploads/parts.py | 188 +++++++ src/openai/resources/uploads/uploads.py | 473 ++++++++++++++++++ src/openai/types/__init__.py | 3 + .../types/chat/completion_create_params.py | 1 + src/openai/types/upload.py | 42 ++ src/openai/types/upload_complete_params.py | 19 + src/openai/types/upload_create_params.py | 29 ++ src/openai/types/uploads/__init__.py | 6 + .../types/uploads/part_create_params.py | 14 + src/openai/types/uploads/upload_part.py | 21 + tests/api_resources/test_uploads.py | 280 +++++++++++ tests/api_resources/uploads/__init__.py | 1 + tests/api_resources/uploads/test_parts.py | 106 ++++ 19 files changed, 1272 insertions(+), 2 deletions(-) create mode 100644 src/openai/resources/uploads/__init__.py create mode 100644 src/openai/resources/uploads/parts.py create mode 100644 src/openai/resources/uploads/uploads.py create mode 100644 src/openai/types/upload.py create mode 100644 src/openai/types/upload_complete_params.py create mode 100644 src/openai/types/upload_create_params.py create mode 100644 src/openai/types/uploads/__init__.py create mode 100644 src/openai/types/uploads/part_create_params.py create mode 100644 src/openai/types/uploads/upload_part.py create mode 100644 tests/api_resources/test_uploads.py create mode 100644 tests/api_resources/uploads/__init__.py create mode 100644 tests/api_resources/uploads/test_parts.py diff --git a/.stats.yml b/.stats.yml index 27e2ce5ede..4e4cb5509c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 64 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-518ca6c60061d3e8bc0971facf40d752f2aea62e3522cc168ad29a1f29cab3dd.yml +configured_endpoints: 68 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-77cfff37114bc9f141c7e6107eb5f1b38d8cc99bc3d4ce03a066db2b6b649c69.yml diff --git a/api.md b/api.md index de69f11dca..82a5360edd 100644 --- a/api.md +++ b/api.md @@ -415,3 +415,29 @@ Methods: - client.batches.retrieve(batch_id) -> Batch - client.batches.list(\*\*params) -> SyncCursorPage[Batch] - client.batches.cancel(batch_id) -> Batch + +# Uploads + +Types: + +```python +from openai.types import Upload +``` + +Methods: + +- client.uploads.create(\*\*params) -> Upload +- client.uploads.cancel(upload_id) -> Upload +- client.uploads.complete(upload_id, \*\*params) -> Upload + +## Parts + +Types: + +```python +from openai.types.uploads import UploadPart +``` + +Methods: + +- client.uploads.parts.create(upload_id, \*\*params) -> UploadPart diff --git a/src/openai/_client.py b/src/openai/_client.py index 8f3060c6f6..8b404e234d 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -58,6 +58,7 @@ class OpenAI(SyncAPIClient): fine_tuning: resources.FineTuning beta: resources.Beta batches: resources.Batches + uploads: resources.Uploads with_raw_response: OpenAIWithRawResponse with_streaming_response: OpenAIWithStreamedResponse @@ -143,6 +144,7 @@ def __init__( self.fine_tuning = resources.FineTuning(self) self.beta = resources.Beta(self) self.batches = resources.Batches(self) + self.uploads = resources.Uploads(self) self.with_raw_response = OpenAIWithRawResponse(self) self.with_streaming_response = OpenAIWithStreamedResponse(self) @@ -270,6 +272,7 @@ class AsyncOpenAI(AsyncAPIClient): fine_tuning: resources.AsyncFineTuning beta: resources.AsyncBeta batches: resources.AsyncBatches + uploads: resources.AsyncUploads with_raw_response: AsyncOpenAIWithRawResponse with_streaming_response: AsyncOpenAIWithStreamedResponse @@ -355,6 +358,7 @@ def __init__( self.fine_tuning = resources.AsyncFineTuning(self) self.beta = resources.AsyncBeta(self) self.batches = resources.AsyncBatches(self) + self.uploads = resources.AsyncUploads(self) self.with_raw_response = AsyncOpenAIWithRawResponse(self) self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self) @@ -483,6 +487,7 @@ def __init__(self, client: OpenAI) -> None: self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning) self.beta = resources.BetaWithRawResponse(client.beta) self.batches = resources.BatchesWithRawResponse(client.batches) + self.uploads = resources.UploadsWithRawResponse(client.uploads) class AsyncOpenAIWithRawResponse: @@ -498,6 +503,7 @@ def __init__(self, client: AsyncOpenAI) -> None: self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning) self.beta = resources.AsyncBetaWithRawResponse(client.beta) self.batches = resources.AsyncBatchesWithRawResponse(client.batches) + self.uploads = resources.AsyncUploadsWithRawResponse(client.uploads) class OpenAIWithStreamedResponse: @@ -513,6 +519,7 @@ def __init__(self, client: OpenAI) -> None: self.fine_tuning = resources.FineTuningWithStreamingResponse(client.fine_tuning) self.beta = resources.BetaWithStreamingResponse(client.beta) self.batches = resources.BatchesWithStreamingResponse(client.batches) + self.uploads = resources.UploadsWithStreamingResponse(client.uploads) class AsyncOpenAIWithStreamedResponse: @@ -528,6 +535,7 @@ def __init__(self, client: AsyncOpenAI) -> None: self.fine_tuning = resources.AsyncFineTuningWithStreamingResponse(client.fine_tuning) self.beta = resources.AsyncBetaWithStreamingResponse(client.beta) self.batches = resources.AsyncBatchesWithStreamingResponse(client.batches) + self.uploads = resources.AsyncUploadsWithStreamingResponse(client.uploads) Client = OpenAI diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index ecae4243fc..e2cc1c4b0c 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -56,6 +56,14 @@ BatchesWithStreamingResponse, AsyncBatchesWithStreamingResponse, ) +from .uploads import ( + Uploads, + AsyncUploads, + UploadsWithRawResponse, + AsyncUploadsWithRawResponse, + UploadsWithStreamingResponse, + AsyncUploadsWithStreamingResponse, +) from .embeddings import ( Embeddings, AsyncEmbeddings, @@ -156,4 +164,10 @@ "AsyncBatchesWithRawResponse", "BatchesWithStreamingResponse", "AsyncBatchesWithStreamingResponse", + "Uploads", + "AsyncUploads", + "UploadsWithRawResponse", + "AsyncUploadsWithRawResponse", + "UploadsWithStreamingResponse", + "AsyncUploadsWithStreamingResponse", ] diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index d73ece2109..88892d1d64 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -171,6 +171,7 @@ def create( exhausted. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. @@ -366,6 +367,7 @@ def create( exhausted. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. @@ -554,6 +556,7 @@ def create( exhausted. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. @@ -817,6 +820,7 @@ async def create( exhausted. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. @@ -1012,6 +1016,7 @@ async def create( exhausted. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. @@ -1200,6 +1205,7 @@ async def create( exhausted. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. diff --git a/src/openai/resources/uploads/__init__.py b/src/openai/resources/uploads/__init__.py new file mode 100644 index 0000000000..12d1056f9e --- /dev/null +++ b/src/openai/resources/uploads/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .parts import ( + Parts, + AsyncParts, + PartsWithRawResponse, + AsyncPartsWithRawResponse, + PartsWithStreamingResponse, + AsyncPartsWithStreamingResponse, +) +from .uploads import ( + Uploads, + AsyncUploads, + UploadsWithRawResponse, + AsyncUploadsWithRawResponse, + UploadsWithStreamingResponse, + AsyncUploadsWithStreamingResponse, +) + +__all__ = [ + "Parts", + "AsyncParts", + "PartsWithRawResponse", + "AsyncPartsWithRawResponse", + "PartsWithStreamingResponse", + "AsyncPartsWithStreamingResponse", + "Uploads", + "AsyncUploads", + "UploadsWithRawResponse", + "AsyncUploadsWithRawResponse", + "UploadsWithStreamingResponse", + "AsyncUploadsWithStreamingResponse", +] diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py new file mode 100644 index 0000000000..3ec2592b1e --- /dev/null +++ b/src/openai/resources/uploads/parts.py @@ -0,0 +1,188 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._utils import ( + extract_files, + maybe_transform, + deepcopy_minimal, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.uploads import part_create_params +from ...types.uploads.upload_part import UploadPart + +__all__ = ["Parts", "AsyncParts"] + + +class Parts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PartsWithRawResponse: + return PartsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PartsWithStreamingResponse: + return PartsWithStreamingResponse(self) + + def create( + self, + upload_id: str, + *, + data: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> UploadPart: + """ + Adds a + [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an + [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object. + A Part represents a chunk of bytes from the file you are trying to upload. + + Each Part can be at most 64 MB, and you can add Parts until you hit the Upload + maximum of 8 GB. + + It is possible to add multiple Parts in parallel. You can decide the intended + order of the Parts when you + [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete). + + Args: + data: The chunk of bytes for this Part. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + body = deepcopy_minimal({"data": data}) + files = extract_files(cast(Mapping[str, object], body), paths=[["data"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + f"/uploads/{upload_id}/parts", + body=maybe_transform(body, part_create_params.PartCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UploadPart, + ) + + +class AsyncParts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPartsWithRawResponse: + return AsyncPartsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPartsWithStreamingResponse: + return AsyncPartsWithStreamingResponse(self) + + async def create( + self, + upload_id: str, + *, + data: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> UploadPart: + """ + Adds a + [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an + [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object. + A Part represents a chunk of bytes from the file you are trying to upload. + + Each Part can be at most 64 MB, and you can add Parts until you hit the Upload + maximum of 8 GB. + + It is possible to add multiple Parts in parallel. You can decide the intended + order of the Parts when you + [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete). + + Args: + data: The chunk of bytes for this Part. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + body = deepcopy_minimal({"data": data}) + files = extract_files(cast(Mapping[str, object], body), paths=[["data"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + f"/uploads/{upload_id}/parts", + body=await async_maybe_transform(body, part_create_params.PartCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=UploadPart, + ) + + +class PartsWithRawResponse: + def __init__(self, parts: Parts) -> None: + self._parts = parts + + self.create = _legacy_response.to_raw_response_wrapper( + parts.create, + ) + + +class AsyncPartsWithRawResponse: + def __init__(self, parts: AsyncParts) -> None: + self._parts = parts + + self.create = _legacy_response.async_to_raw_response_wrapper( + parts.create, + ) + + +class PartsWithStreamingResponse: + def __init__(self, parts: Parts) -> None: + self._parts = parts + + self.create = to_streamed_response_wrapper( + parts.create, + ) + + +class AsyncPartsWithStreamingResponse: + def __init__(self, parts: AsyncParts) -> None: + self._parts = parts + + self.create = async_to_streamed_response_wrapper( + parts.create, + ) diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py new file mode 100644 index 0000000000..4100423d3e --- /dev/null +++ b/src/openai/resources/uploads/uploads.py @@ -0,0 +1,473 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx + +from ... import _legacy_response +from .parts import ( + Parts, + AsyncParts, + PartsWithRawResponse, + AsyncPartsWithRawResponse, + PartsWithStreamingResponse, + AsyncPartsWithStreamingResponse, +) +from ...types import upload_create_params, upload_complete_params +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.upload import Upload + +__all__ = ["Uploads", "AsyncUploads"] + + +class Uploads(SyncAPIResource): + @cached_property + def parts(self) -> Parts: + return Parts(self._client) + + @cached_property + def with_raw_response(self) -> UploadsWithRawResponse: + return UploadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UploadsWithStreamingResponse: + return UploadsWithStreamingResponse(self) + + def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: Literal["assistants", "batch", "fine-tune", "vision"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Upload: + """ + Creates an intermediate + [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object + that you can add + [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to. + Currently, an Upload can accept at most 8 GB in total and expires after an hour + after you create it. + + Once you complete the Upload, we will create a + [File](https://platform.openai.com/docs/api-reference/files/object) object that + contains all the parts you uploaded. This File is usable in the rest of our + platform as a regular File object. + + For certain `purpose`s, the correct `mime_type` must be specified. Please refer + to documentation for the supported MIME types for your use case: + + - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search/supported-files) + + For guidance on the proper filename extensions for each purpose, please follow + the documentation on + [creating a File](https://platform.openai.com/docs/api-reference/files/create). + + Args: + bytes: The number of bytes in the file you are uploading. + + filename: The name of the file to upload. + + mime_type: The MIME type of the file. + + This must fall within the supported MIME types for your file purpose. See the + supported MIME types for assistants and vision. + + purpose: The intended purpose of the uploaded file. + + See the + [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/uploads", + body=maybe_transform( + { + "bytes": bytes, + "filename": filename, + "mime_type": mime_type, + "purpose": purpose, + }, + upload_create_params.UploadCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Upload, + ) + + def cancel( + self, + upload_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Upload: + """Cancels the Upload. + + No Parts may be added after an Upload is cancelled. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + return self._post( + f"/uploads/{upload_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Upload, + ) + + def complete( + self, + upload_id: str, + *, + part_ids: List[str], + md5: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Upload: + """ + Completes the + [Upload](https://platform.openai.com/docs/api-reference/uploads/object). + + Within the returned Upload object, there is a nested + [File](https://platform.openai.com/docs/api-reference/files/object) object that + is ready to use in the rest of the platform. + + You can specify the order of the Parts by passing in an ordered list of the Part + IDs. + + The number of bytes uploaded upon completion must match the number of bytes + initially specified when creating the Upload object. No Parts may be added after + an Upload is completed. + + Args: + part_ids: The ordered list of Part IDs. + + md5: The optional md5 checksum for the file contents to verify if the bytes uploaded + matches what you expect. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + return self._post( + f"/uploads/{upload_id}/complete", + body=maybe_transform( + { + "part_ids": part_ids, + "md5": md5, + }, + upload_complete_params.UploadCompleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Upload, + ) + + +class AsyncUploads(AsyncAPIResource): + @cached_property + def parts(self) -> AsyncParts: + return AsyncParts(self._client) + + @cached_property + def with_raw_response(self) -> AsyncUploadsWithRawResponse: + return AsyncUploadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUploadsWithStreamingResponse: + return AsyncUploadsWithStreamingResponse(self) + + async def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: Literal["assistants", "batch", "fine-tune", "vision"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Upload: + """ + Creates an intermediate + [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object + that you can add + [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to. + Currently, an Upload can accept at most 8 GB in total and expires after an hour + after you create it. + + Once you complete the Upload, we will create a + [File](https://platform.openai.com/docs/api-reference/files/object) object that + contains all the parts you uploaded. This File is usable in the rest of our + platform as a regular File object. + + For certain `purpose`s, the correct `mime_type` must be specified. Please refer + to documentation for the supported MIME types for your use case: + + - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search/supported-files) + + For guidance on the proper filename extensions for each purpose, please follow + the documentation on + [creating a File](https://platform.openai.com/docs/api-reference/files/create). + + Args: + bytes: The number of bytes in the file you are uploading. + + filename: The name of the file to upload. + + mime_type: The MIME type of the file. + + This must fall within the supported MIME types for your file purpose. See the + supported MIME types for assistants and vision. + + purpose: The intended purpose of the uploaded file. + + See the + [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/uploads", + body=await async_maybe_transform( + { + "bytes": bytes, + "filename": filename, + "mime_type": mime_type, + "purpose": purpose, + }, + upload_create_params.UploadCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Upload, + ) + + async def cancel( + self, + upload_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Upload: + """Cancels the Upload. + + No Parts may be added after an Upload is cancelled. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + return await self._post( + f"/uploads/{upload_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Upload, + ) + + async def complete( + self, + upload_id: str, + *, + part_ids: List[str], + md5: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Upload: + """ + Completes the + [Upload](https://platform.openai.com/docs/api-reference/uploads/object). + + Within the returned Upload object, there is a nested + [File](https://platform.openai.com/docs/api-reference/files/object) object that + is ready to use in the rest of the platform. + + You can specify the order of the Parts by passing in an ordered list of the Part + IDs. + + The number of bytes uploaded upon completion must match the number of bytes + initially specified when creating the Upload object. No Parts may be added after + an Upload is completed. + + Args: + part_ids: The ordered list of Part IDs. + + md5: The optional md5 checksum for the file contents to verify if the bytes uploaded + matches what you expect. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not upload_id: + raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") + return await self._post( + f"/uploads/{upload_id}/complete", + body=await async_maybe_transform( + { + "part_ids": part_ids, + "md5": md5, + }, + upload_complete_params.UploadCompleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Upload, + ) + + +class UploadsWithRawResponse: + def __init__(self, uploads: Uploads) -> None: + self._uploads = uploads + + self.create = _legacy_response.to_raw_response_wrapper( + uploads.create, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + uploads.cancel, + ) + self.complete = _legacy_response.to_raw_response_wrapper( + uploads.complete, + ) + + @cached_property + def parts(self) -> PartsWithRawResponse: + return PartsWithRawResponse(self._uploads.parts) + + +class AsyncUploadsWithRawResponse: + def __init__(self, uploads: AsyncUploads) -> None: + self._uploads = uploads + + self.create = _legacy_response.async_to_raw_response_wrapper( + uploads.create, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + uploads.cancel, + ) + self.complete = _legacy_response.async_to_raw_response_wrapper( + uploads.complete, + ) + + @cached_property + def parts(self) -> AsyncPartsWithRawResponse: + return AsyncPartsWithRawResponse(self._uploads.parts) + + +class UploadsWithStreamingResponse: + def __init__(self, uploads: Uploads) -> None: + self._uploads = uploads + + self.create = to_streamed_response_wrapper( + uploads.create, + ) + self.cancel = to_streamed_response_wrapper( + uploads.cancel, + ) + self.complete = to_streamed_response_wrapper( + uploads.complete, + ) + + @cached_property + def parts(self) -> PartsWithStreamingResponse: + return PartsWithStreamingResponse(self._uploads.parts) + + +class AsyncUploadsWithStreamingResponse: + def __init__(self, uploads: AsyncUploads) -> None: + self._uploads = uploads + + self.create = async_to_streamed_response_wrapper( + uploads.create, + ) + self.cancel = async_to_streamed_response_wrapper( + uploads.cancel, + ) + self.complete = async_to_streamed_response_wrapper( + uploads.complete, + ) + + @cached_property + def parts(self) -> AsyncPartsWithStreamingResponse: + return AsyncPartsWithStreamingResponse(self._uploads.parts) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 7873efb34f..71f4a59b9e 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -10,6 +10,7 @@ FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters, ) +from .upload import Upload as Upload from .embedding import Embedding as Embedding from .chat_model import ChatModel as ChatModel from .completion import Completion as Completion @@ -28,7 +29,9 @@ from .file_create_params import FileCreateParams as FileCreateParams from .batch_create_params import BatchCreateParams as BatchCreateParams from .batch_request_counts import BatchRequestCounts as BatchRequestCounts +from .upload_create_params import UploadCreateParams as UploadCreateParams from .image_generate_params import ImageGenerateParams as ImageGenerateParams +from .upload_complete_params import UploadCompleteParams as UploadCompleteParams from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .moderation_create_params import ModerationCreateParams as ModerationCreateParams diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 85157653f2..783922539f 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -155,6 +155,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): exhausted. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. diff --git a/src/openai/types/upload.py b/src/openai/types/upload.py new file mode 100644 index 0000000000..1cf8ee97f8 --- /dev/null +++ b/src/openai/types/upload.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .file_object import FileObject + +__all__ = ["Upload"] + + +class Upload(BaseModel): + id: str + """The Upload unique identifier, which can be referenced in API endpoints.""" + + bytes: int + """The intended number of bytes to be uploaded.""" + + created_at: int + """The Unix timestamp (in seconds) for when the Upload was created.""" + + expires_at: int + """The Unix timestamp (in seconds) for when the Upload was created.""" + + filename: str + """The name of the file to be uploaded.""" + + object: Literal["upload"] + """The object type, which is always "upload".""" + + purpose: str + """The intended purpose of the file. + + [Please refer here](https://platform.openai.com/docs/api-reference/files/object#files/object-purpose) + for acceptable values. + """ + + status: Literal["pending", "completed", "cancelled", "expired"] + """The status of the Upload.""" + + file: Optional[FileObject] = None + """The ready File object after the Upload is completed.""" diff --git a/src/openai/types/upload_complete_params.py b/src/openai/types/upload_complete_params.py new file mode 100644 index 0000000000..cce568d5c6 --- /dev/null +++ b/src/openai/types/upload_complete_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, TypedDict + +__all__ = ["UploadCompleteParams"] + + +class UploadCompleteParams(TypedDict, total=False): + part_ids: Required[List[str]] + """The ordered list of Part IDs.""" + + md5: str + """ + The optional md5 checksum for the file contents to verify if the bytes uploaded + matches what you expect. + """ diff --git a/src/openai/types/upload_create_params.py b/src/openai/types/upload_create_params.py new file mode 100644 index 0000000000..3165ebcc7a --- /dev/null +++ b/src/openai/types/upload_create_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["UploadCreateParams"] + + +class UploadCreateParams(TypedDict, total=False): + bytes: Required[int] + """The number of bytes in the file you are uploading.""" + + filename: Required[str] + """The name of the file to upload.""" + + mime_type: Required[str] + """The MIME type of the file. + + This must fall within the supported MIME types for your file purpose. See the + supported MIME types for assistants and vision. + """ + + purpose: Required[Literal["assistants", "batch", "fine-tune", "vision"]] + """The intended purpose of the uploaded file. + + See the + [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose). + """ diff --git a/src/openai/types/uploads/__init__.py b/src/openai/types/uploads/__init__.py new file mode 100644 index 0000000000..41deb0ab4b --- /dev/null +++ b/src/openai/types/uploads/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .upload_part import UploadPart as UploadPart +from .part_create_params import PartCreateParams as PartCreateParams diff --git a/src/openai/types/uploads/part_create_params.py b/src/openai/types/uploads/part_create_params.py new file mode 100644 index 0000000000..9851ca41e9 --- /dev/null +++ b/src/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/src/openai/types/uploads/upload_part.py b/src/openai/types/uploads/upload_part.py new file mode 100644 index 0000000000..e09621d8f9 --- /dev/null +++ b/src/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.""" diff --git a/tests/api_resources/test_uploads.py b/tests/api_resources/test_uploads.py new file mode 100644 index 0000000000..cb62df6b51 --- /dev/null +++ b/tests/api_resources/test_uploads.py @@ -0,0 +1,280 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import Upload + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestUploads: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + upload = client.uploads.create( + bytes=0, + filename="filename", + mime_type="mime_type", + purpose="assistants", + ) + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.uploads.with_raw_response.create( + bytes=0, + filename="filename", + mime_type="mime_type", + purpose="assistants", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.uploads.with_streaming_response.create( + bytes=0, + filename="filename", + mime_type="mime_type", + purpose="assistants", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + upload = client.uploads.cancel( + "upload_abc123", + ) + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + response = client.uploads.with_raw_response.cancel( + "upload_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: OpenAI) -> None: + with client.uploads.with_streaming_response.cancel( + "upload_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): + client.uploads.with_raw_response.cancel( + "", + ) + + @parametrize + def test_method_complete(self, client: OpenAI) -> None: + upload = client.uploads.complete( + upload_id="upload_abc123", + part_ids=["string", "string", "string"], + ) + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + def test_method_complete_with_all_params(self, client: OpenAI) -> None: + upload = client.uploads.complete( + upload_id="upload_abc123", + part_ids=["string", "string", "string"], + md5="md5", + ) + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + def test_raw_response_complete(self, client: OpenAI) -> None: + response = client.uploads.with_raw_response.complete( + upload_id="upload_abc123", + part_ids=["string", "string", "string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + def test_streaming_response_complete(self, client: OpenAI) -> None: + with client.uploads.with_streaming_response.complete( + upload_id="upload_abc123", + part_ids=["string", "string", "string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_complete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): + client.uploads.with_raw_response.complete( + upload_id="", + part_ids=["string", "string", "string"], + ) + + +class TestAsyncUploads: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + upload = await async_client.uploads.create( + bytes=0, + filename="filename", + mime_type="mime_type", + purpose="assistants", + ) + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.uploads.with_raw_response.create( + bytes=0, + filename="filename", + mime_type="mime_type", + purpose="assistants", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.uploads.with_streaming_response.create( + bytes=0, + filename="filename", + mime_type="mime_type", + purpose="assistants", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + upload = await response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: + upload = await async_client.uploads.cancel( + "upload_abc123", + ) + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: + response = await async_client.uploads.with_raw_response.cancel( + "upload_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: + async with async_client.uploads.with_streaming_response.cancel( + "upload_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + upload = await response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): + await async_client.uploads.with_raw_response.cancel( + "", + ) + + @parametrize + async def test_method_complete(self, async_client: AsyncOpenAI) -> None: + upload = await async_client.uploads.complete( + upload_id="upload_abc123", + part_ids=["string", "string", "string"], + ) + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + async def test_method_complete_with_all_params(self, async_client: AsyncOpenAI) -> None: + upload = await async_client.uploads.complete( + upload_id="upload_abc123", + part_ids=["string", "string", "string"], + md5="md5", + ) + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + async def test_raw_response_complete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.uploads.with_raw_response.complete( + upload_id="upload_abc123", + part_ids=["string", "string", "string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + upload = response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + @parametrize + async def test_streaming_response_complete(self, async_client: AsyncOpenAI) -> None: + async with async_client.uploads.with_streaming_response.complete( + upload_id="upload_abc123", + part_ids=["string", "string", "string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + upload = await response.parse() + assert_matches_type(Upload, upload, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_complete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): + await async_client.uploads.with_raw_response.complete( + upload_id="", + part_ids=["string", "string", "string"], + ) diff --git a/tests/api_resources/uploads/__init__.py b/tests/api_resources/uploads/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/uploads/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/uploads/test_parts.py b/tests/api_resources/uploads/test_parts.py new file mode 100644 index 0000000000..2bba241a6d --- /dev/null +++ b/tests/api_resources/uploads/test_parts.py @@ -0,0 +1,106 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.uploads import UploadPart + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestParts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + part = client.uploads.parts.create( + upload_id="upload_abc123", + data=b"raw file contents", + ) + assert_matches_type(UploadPart, part, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.uploads.parts.with_raw_response.create( + upload_id="upload_abc123", + data=b"raw file contents", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + part = response.parse() + assert_matches_type(UploadPart, part, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.uploads.parts.with_streaming_response.create( + upload_id="upload_abc123", + data=b"raw file contents", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + part = response.parse() + assert_matches_type(UploadPart, part, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): + client.uploads.parts.with_raw_response.create( + upload_id="", + data=b"raw file contents", + ) + + +class TestAsyncParts: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + part = await async_client.uploads.parts.create( + upload_id="upload_abc123", + data=b"raw file contents", + ) + assert_matches_type(UploadPart, part, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.uploads.parts.with_raw_response.create( + upload_id="upload_abc123", + data=b"raw file contents", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + part = response.parse() + assert_matches_type(UploadPart, part, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.uploads.parts.with_streaming_response.create( + upload_id="upload_abc123", + data=b"raw file contents", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + part = await response.parse() + assert_matches_type(UploadPart, part, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): + await async_client.uploads.parts.with_raw_response.create( + upload_id="", + data=b"raw file contents", + ) From ff5add01c7a6129d1977f3d831d02d818a3cbac3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:36:03 +0000 Subject: [PATCH 526/914] docs(readme): fix example snippet imports (#1569) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 653fb9a2c3..525c1b5aaf 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ List methods in the OpenAI API are paginated. This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: ```python -import openai +from openai import OpenAI client = OpenAI() @@ -246,7 +246,7 @@ Or, asynchronously: ```python import asyncio -import openai +from openai import AsyncOpenAI client = AsyncOpenAI() From 1ed0e35679438cc7409f2e3aeb36d637b3c579c0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:36:30 +0000 Subject: [PATCH 527/914] release: 1.37.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 46ea083269..36116e176b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.36.1" + ".": "1.37.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c7cac5a9a0..77acf5f5df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 1.37.0 (2024-07-22) + +Full Changelog: [v1.36.1...v1.37.0](https://github.com/openai/openai-python/compare/v1.36.1...v1.37.0) + +### Features + +* **api:** add uploads endpoints ([#1568](https://github.com/openai/openai-python/issues/1568)) ([d877b6d](https://github.com/openai/openai-python/commit/d877b6dabb9b3e8da6ff2f46de1120af54de398d)) + + +### Bug Fixes + +* **cli/audio:** handle non-json response format ([#1557](https://github.com/openai/openai-python/issues/1557)) ([bb7431f](https://github.com/openai/openai-python/commit/bb7431f602602d4c74d75809c6934a7fd192972d)) + + +### Documentation + +* **readme:** fix example snippet imports ([#1569](https://github.com/openai/openai-python/issues/1569)) ([0c90af6](https://github.com/openai/openai-python/commit/0c90af6412b3314c2257b9b8eb7fabd767f32ef6)) + ## 1.36.1 (2024-07-20) Full Changelog: [v1.36.0...v1.36.1](https://github.com/openai/openai-python/compare/v1.36.0...v1.36.1) diff --git a/pyproject.toml b/pyproject.toml index 03a4a77f56..299e40d2a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.36.1" +version = "1.37.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6021f184f8..1fca52e377 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.36.1" # x-release-please-version +__version__ = "1.37.0" # x-release-please-version From edf0fb0f63adea2d0a6f75d94fa4b1b1f7e91851 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 10:34:47 +0000 Subject: [PATCH 528/914] chore(tests): update prism version (#1572) --- scripts/mock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mock b/scripts/mock index fe89a1d084..f586157699 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stoplight/prism-cli@~5.8 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stainless-api/prism-cli@5.8.4 -- prism mock "$URL" &> .prism.log & # Wait for server to come online echo -n "Waiting for server" @@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stoplight/prism-cli@~5.8 -- prism mock "$URL" + npm exec --package=@stainless-api/prism-cli@5.8.4 -- prism mock "$URL" fi From 195c05a64d39c87b2dfdf1eca2d339597f1fce03 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 05:03:57 +0000 Subject: [PATCH 529/914] release: 1.37.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 36116e176b..6fc89ad7bc 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.37.0" + ".": "1.37.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 77acf5f5df..138180cf6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.37.1 (2024-07-25) + +Full Changelog: [v1.37.0...v1.37.1](https://github.com/openai/openai-python/compare/v1.37.0...v1.37.1) + +### Chores + +* **tests:** update prism version ([#1572](https://github.com/openai/openai-python/issues/1572)) ([af82593](https://github.com/openai/openai-python/commit/af8259393673af1ef6ec711da6297eb4ad55b66e)) + ## 1.37.0 (2024-07-22) Full Changelog: [v1.36.1...v1.37.0](https://github.com/openai/openai-python/compare/v1.36.1...v1.37.0) diff --git a/pyproject.toml b/pyproject.toml index 299e40d2a1..c4d5bbd1aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.37.0" +version = "1.37.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1fca52e377..5e58cb3c83 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.37.0" # x-release-please-version +__version__ = "1.37.1" # x-release-please-version From 1d2288562d3c65c8b07189b0570ca6bf512111b1 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 29 Jul 2024 09:53:24 +0100 Subject: [PATCH 530/914] chore(runs/create_and_poll): add parallel_tool_calls request param --- .../resources/beta/threads/runs/runs.py | 8 ++++ tests/lib/test_assistants.py | 45 +++++-------------- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 1759120bfe..7db8b120f9 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -822,6 +822,8 @@ def create_and_poll( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -844,6 +846,7 @@ def create_and_poll( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -877,6 +880,7 @@ def create_and_poll( response_format=response_format, temperature=temperature, tool_choice=tool_choice, + parallel_tool_calls=parallel_tool_calls, # We assume we are not streaming when polling stream=False, tools=tools, @@ -2404,6 +2408,8 @@ async def create_and_poll( Literal[ "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -2426,6 +2432,7 @@ async def create_and_poll( None, ] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2459,6 +2466,7 @@ async def create_and_poll( response_format=response_format, temperature=temperature, tool_choice=tool_choice, + parallel_tool_calls=parallel_tool_calls, # We assume we are not streaming when polling stream=False, tools=tools, diff --git a/tests/lib/test_assistants.py b/tests/lib/test_assistants.py index 38a47d4d12..b9d4e8927c 100644 --- a/tests/lib/test_assistants.py +++ b/tests/lib/test_assistants.py @@ -1,41 +1,9 @@ from __future__ import annotations -import inspect -from typing import Any, Callable - import pytest from openai import OpenAI, AsyncOpenAI - - -def assert_signatures_in_sync( - source_func: Callable[..., Any], - check_func: Callable[..., Any], - *, - exclude_params: set[str] = set(), -) -> None: - check_sig = inspect.signature(check_func) - source_sig = inspect.signature(source_func) - - errors: list[str] = [] - - for name, generated_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 != generated_param.annotation: - errors.append( - f"types for the `{name}` param are do not match; generated={repr(generated_param.annotation)} custom={repr(generated_param.annotation)}" - ) - continue - - if errors: - raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) +from openai._utils import assert_signatures_in_sync @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) @@ -58,3 +26,14 @@ def test_create_and_run_stream_method_definition_in_sync(sync: bool, client: Ope checking_client.beta.threads.create_and_run_stream, exclude_params={"stream"}, ) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_create_and_poll_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + assert_signatures_in_sync( + checking_client.beta.threads.runs.create, + checking_client.beta.threads.runs.create_and_poll, + exclude_params={"stream"}, + ) From 88e61191c06f8523cbf56fee50a5ad3dd1dad646 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:56:54 +0000 Subject: [PATCH 531/914] chore(internal): add type construction helper (#1584) --- src/openai/_models.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/openai/_models.py b/src/openai/_models.py index eb7ce3bde9..5148d5a7b3 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -406,6 +406,15 @@ def build( return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) +def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: + """Loose coercion to the expected type with construction of nested values. + + Note: the returned value from this function is not guaranteed to match the + given type. + """ + return cast(_T, construct_type(value=value, type_=type_)) + + def construct_type(*, value: object, type_: object) -> object: """Loose coercion to the expected type with construction of nested values. From 5f177dcdb1f455ededa2a9c121436ba72ba7409f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 05:03:59 +0000 Subject: [PATCH 532/914] release: 1.37.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6fc89ad7bc..9baafa1759 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.37.1" + ".": "1.37.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 138180cf6b..824d4d83b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.37.2 (2024-08-01) + +Full Changelog: [v1.37.1...v1.37.2](https://github.com/openai/openai-python/compare/v1.37.1...v1.37.2) + +### Chores + +* **internal:** add type construction helper ([#1584](https://github.com/openai/openai-python/issues/1584)) ([cbb186a](https://github.com/openai/openai-python/commit/cbb186a534b520fa5b11a9b371b175e3f6a6482b)) +* **runs/create_and_poll:** add parallel_tool_calls request param ([04b3e6c](https://github.com/openai/openai-python/commit/04b3e6c39ee5a7088e0e4dfa4c06f3dcce901a57)) + ## 1.37.1 (2024-07-25) Full Changelog: [v1.37.0...v1.37.1](https://github.com/openai/openai-python/compare/v1.37.0...v1.37.1) diff --git a/pyproject.toml b/pyproject.toml index c4d5bbd1aa..3c6dcd409a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.37.1" +version = "1.37.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5e58cb3c83..e36be4473a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.37.1" # x-release-please-version +__version__ = "1.37.2" # x-release-please-version From ece795f0c2dde3f483f9af9126d4c98adf789a8a Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Fri, 2 Aug 2024 09:32:24 +0000 Subject: [PATCH 533/914] feat: extract out `ImageModel`, `AudioModel`, `SpeechModel` (#1586) --- api.md | 16 +- src/openai/resources/audio/speech.py | 9 +- src/openai/resources/audio/transcriptions.py | 9 +- src/openai/resources/audio/translations.py | 10 +- src/openai/resources/beta/assistants.py | 62 +- .../resources/beta/threads/runs/runs.py | 650 +----------------- src/openai/resources/beta/threads/threads.py | 485 +------------ src/openai/resources/images.py | 17 +- src/openai/resources/moderations.py | 10 +- src/openai/types/__init__.py | 3 + src/openai/types/audio/__init__.py | 1 + .../types/audio/speech_create_params.py | 4 +- src/openai/types/audio/speech_model.py | 7 + src/openai/types/audio/transcription.py | 1 - .../audio/transcription_create_params.py | 3 +- src/openai/types/audio/translation.py | 1 - .../types/audio/translation_create_params.py | 5 +- src/openai/types/audio_model.py | 7 + src/openai/types/batch_request_counts.py | 1 - .../types/beta/assistant_create_params.py | 31 +- .../beta/assistant_tool_choice_function.py | 1 - .../beta/thread_create_and_run_params.py | 30 +- .../types/beta/threads/run_create_params.py | 30 +- src/openai/types/completion_usage.py | 1 - .../fine_tuning_job_integration.py | 1 - .../types/image_create_variation_params.py | 3 +- src/openai/types/image_edit_params.py | 3 +- src/openai/types/image_generate_params.py | 4 +- src/openai/types/image_model.py | 7 + src/openai/types/model_deleted.py | 1 - src/openai/types/moderation_create_params.py | 6 +- src/openai/types/moderation_model.py | 7 + 32 files changed, 149 insertions(+), 1277 deletions(-) create mode 100644 src/openai/types/audio/speech_model.py create mode 100644 src/openai/types/audio_model.py create mode 100644 src/openai/types/image_model.py create mode 100644 src/openai/types/moderation_model.py diff --git a/api.md b/api.md index 82a5360edd..85e81467dc 100644 --- a/api.md +++ b/api.md @@ -92,7 +92,7 @@ Methods: Types: ```python -from openai.types import Image, ImagesResponse +from openai.types import Image, ImageModel, ImagesResponse ``` Methods: @@ -103,6 +103,12 @@ Methods: # Audio +Types: + +```python +from openai.types import AudioModel +``` + ## Transcriptions Types: @@ -129,6 +135,12 @@ Methods: ## Speech +Types: + +```python +from openai.types.audio import SpeechModel +``` + Methods: - client.audio.speech.create(\*\*params) -> HttpxBinaryResponseContent @@ -138,7 +150,7 @@ Methods: Types: ```python -from openai.types import Moderation, ModerationCreateResponse +from openai.types import Moderation, ModerationModel, ModerationCreateResponse ``` Methods: diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index e26c58051e..a0df9ec487 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -22,9 +22,8 @@ async_to_custom_streamed_response_wrapper, ) from ...types.audio import speech_create_params -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options +from ...types.audio.speech_model import SpeechModel __all__ = ["Speech", "AsyncSpeech"] @@ -42,7 +41,7 @@ def create( self, *, input: str, - model: Union[str, Literal["tts-1", "tts-1-hd"]], + model: Union[str, SpeechModel], voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, @@ -115,7 +114,7 @@ async def create( self, *, input: str, - model: Union[str, Literal["tts-1", "tts-1-hd"]], + model: Union[str, SpeechModel], voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index c03137dbfd..1ee962411c 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -19,9 +19,8 @@ from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...types.audio import transcription_create_params -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options +from ...types.audio_model import AudioModel from ...types.audio.transcription import Transcription __all__ = ["Transcriptions", "AsyncTranscriptions"] @@ -40,7 +39,7 @@ def create( self, *, file: FileTypes, - model: Union[str, Literal["whisper-1"]], + model: Union[str, AudioModel], language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN, @@ -136,7 +135,7 @@ async def create( self, *, file: FileTypes, - model: Union[str, Literal["whisper-1"]], + model: Union[str, AudioModel], language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 485e1a33df..ed97ccf840 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -3,7 +3,6 @@ from __future__ import annotations from typing import Union, Mapping, cast -from typing_extensions import Literal import httpx @@ -19,9 +18,8 @@ from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...types.audio import translation_create_params -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options +from ...types.audio_model import AudioModel from ...types.audio.translation import Translation __all__ = ["Translations", "AsyncTranslations"] @@ -40,7 +38,7 @@ def create( self, *, file: FileTypes, - model: Union[str, Literal["whisper-1"]], + model: Union[str, AudioModel], prompt: str | NotGiven = NOT_GIVEN, response_format: str | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, @@ -121,7 +119,7 @@ async def create( self, *, file: FileTypes, - model: Union[str, Literal["whisper-1"]], + model: Union[str, AudioModel], prompt: str | NotGiven = NOT_GIVEN, response_format: str | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 066db66913..b4dc3cfdd6 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -22,10 +22,8 @@ assistant_create_params, assistant_update_params, ) -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options +from ...types.chat_model import ChatModel from ...types.beta.assistant import Assistant from ...types.beta.assistant_deleted import AssistantDeleted from ...types.beta.assistant_tool_param import AssistantToolParam @@ -46,33 +44,7 @@ def with_streaming_response(self) -> AssistantsWithStreamingResponse: def create( self, *, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], description: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, @@ -437,33 +409,7 @@ def with_streaming_response(self) -> AsyncAssistantsWithStreamingResponse: async def create( self, *, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ], + model: Union[str, ChatModel], description: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 7db8b120f9..61c6bb486f 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -30,10 +30,7 @@ from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....._streaming import Stream, AsyncStream from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import ( - AsyncPaginator, - make_request_options, -) +from ....._base_client import AsyncPaginator, make_request_options from .....lib.streaming import ( AssistantEventHandler, AssistantEventHandlerT, @@ -42,6 +39,7 @@ AsyncAssistantEventHandlerT, AsyncAssistantStreamManager, ) +from .....types.chat_model import ChatModel from .....types.beta.threads import ( run_list_params, run_create_params, @@ -82,35 +80,7 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, @@ -237,35 +207,7 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -391,35 +333,7 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -544,35 +458,7 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -817,35 +703,7 @@ def create_and_poll( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -913,33 +771,8 @@ def create_and_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -969,33 +802,8 @@ def create_and_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1025,33 +833,8 @@ def create_and_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1096,6 +879,7 @@ def create_and_stream( "stream": True, "tools": tools, "truncation_strategy": truncation_strategy, + "parallel_tool_calls": parallel_tool_calls, "top_p": top_p, }, run_create_params.RunCreateParams, @@ -1165,33 +949,7 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1220,33 +978,7 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1275,33 +1007,7 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1668,35 +1374,7 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, @@ -1823,35 +1501,7 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1977,35 +1627,7 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -2130,35 +1752,7 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -2403,35 +1997,7 @@ async def create_and_poll( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -2499,33 +2065,8 @@ def create_and_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2555,33 +2096,8 @@ def create_and_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2611,33 +2127,8 @@ def create_and_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2685,6 +2176,7 @@ def create_and_stream( "tools": tools, "truncation_strategy": truncation_strategy, "top_p": top_p, + "parallel_tool_calls": parallel_tool_calls, }, run_create_params.RunCreateParams, ), @@ -2753,33 +2245,7 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2808,33 +2274,7 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2863,33 +2303,7 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index ff7fa70cf8..f40e164180 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -41,9 +41,7 @@ thread_update_params, thread_create_and_run_params, ) -from ...._base_client import ( - make_request_options, -) +from ...._base_client import make_request_options from ....lib.streaming import ( AssistantEventHandler, AssistantEventHandlerT, @@ -52,6 +50,7 @@ AsyncAssistantEventHandlerT, AsyncAssistantStreamManager, ) +from ....types.chat_model import ChatModel from ....types.beta.thread import Thread from ....types.beta.threads.run import Run from ....types.beta.thread_deleted import ThreadDeleted @@ -264,35 +263,7 @@ def create_and_run( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, @@ -418,35 +389,7 @@ def create_and_run( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -571,35 +514,7 @@ def create_and_run( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -723,35 +638,7 @@ def create_and_run( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -809,35 +696,7 @@ def create_and_run_poll( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -893,35 +752,7 @@ def create_and_run_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -950,35 +781,7 @@ def create_and_run_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1007,35 +810,7 @@ def create_and_run_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1296,35 +1071,7 @@ async def create_and_run( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, @@ -1450,35 +1197,7 @@ async def create_and_run( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1603,35 +1322,7 @@ async def create_and_run( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1755,35 +1446,7 @@ async def create_and_run( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -1841,35 +1504,7 @@ async def create_and_run_poll( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1927,35 +1562,7 @@ def create_and_run_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1984,35 +1591,7 @@ def create_and_run_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -2041,35 +1620,7 @@ def create_and_run_stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] - | NotGiven = NOT_GIVEN, + model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 3728392f93..0913b572cb 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -19,9 +19,8 @@ from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options +from ..types.image_model import ImageModel from ..types.images_response import ImagesResponse __all__ = ["Images", "AsyncImages"] @@ -40,7 +39,7 @@ def create_variation( self, *, image: FileTypes, - model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, @@ -115,7 +114,7 @@ def edit( image: FileTypes, prompt: str, mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, @@ -196,7 +195,7 @@ def generate( self, *, prompt: str, - model: Union[str, Literal["dall-e-2", "dall-e-3"], None] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, quality: Literal["standard", "hd"] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, @@ -286,7 +285,7 @@ async def create_variation( self, *, image: FileTypes, - model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, @@ -361,7 +360,7 @@ async def edit( image: FileTypes, prompt: str, mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, @@ -442,7 +441,7 @@ async def generate( self, *, prompt: str, - model: Union[str, Literal["dall-e-2", "dall-e-3"], None] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, quality: Literal["standard", "hd"] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 9386e50dae..b9ad9972f0 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -3,7 +3,6 @@ from __future__ import annotations from typing import List, Union -from typing_extensions import Literal import httpx @@ -17,9 +16,8 @@ from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options +from ..types.moderation_model import ModerationModel from ..types.moderation_create_response import ModerationCreateResponse __all__ = ["Moderations", "AsyncModerations"] @@ -38,7 +36,7 @@ def create( self, *, input: Union[str, List[str]], - model: Union[str, Literal["text-moderation-latest", "text-moderation-stable"]] | NotGiven = NOT_GIVEN, + model: Union[str, ModerationModel] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -98,7 +96,7 @@ async def create( self, *, input: Union[str, List[str]], - model: Union[str, Literal["text-moderation-latest", "text-moderation-stable"]] | NotGiven = NOT_GIVEN, + model: Union[str, ModerationModel] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 71f4a59b9e..84916962cc 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -15,14 +15,17 @@ from .chat_model import ChatModel as ChatModel from .completion import Completion as Completion from .moderation import Moderation as Moderation +from .audio_model import AudioModel as AudioModel from .batch_error import BatchError as BatchError from .file_object import FileObject as FileObject +from .image_model import ImageModel as ImageModel from .file_content import FileContent as FileContent from .file_deleted import FileDeleted as FileDeleted from .model_deleted import ModelDeleted as ModelDeleted from .images_response import ImagesResponse as ImagesResponse from .completion_usage import CompletionUsage as CompletionUsage from .file_list_params import FileListParams as FileListParams +from .moderation_model import ModerationModel as ModerationModel from .batch_list_params import BatchListParams as BatchListParams from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index 8d2c44c86a..1de5c0ff82 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations from .translation import Translation as Translation +from .speech_model import SpeechModel as SpeechModel from .transcription import Transcription as Transcription from .speech_create_params import SpeechCreateParams as SpeechCreateParams from .translation_create_params import TranslationCreateParams as TranslationCreateParams diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 8d75ec4ccc..dff66e49c7 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -5,6 +5,8 @@ from typing import Union from typing_extensions import Literal, Required, TypedDict +from .speech_model import SpeechModel + __all__ = ["SpeechCreateParams"] @@ -12,7 +14,7 @@ class SpeechCreateParams(TypedDict, total=False): input: Required[str] """The text to generate audio for. The maximum length is 4096 characters.""" - model: Required[Union[str, Literal["tts-1", "tts-1-hd"]]] + model: Required[Union[str, SpeechModel]] """ One of the available [TTS models](https://platform.openai.com/docs/models/tts): `tts-1` or `tts-1-hd` diff --git a/src/openai/types/audio/speech_model.py b/src/openai/types/audio/speech_model.py new file mode 100644 index 0000000000..e92b898b99 --- /dev/null +++ b/src/openai/types/audio/speech_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +__all__ = ["SpeechModel"] + +SpeechModel = Literal["tts-1", "tts-1-hd"] diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index 0b6ab39e78..edb5f227fc 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["Transcription"] diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index 6b2d5bae79..a825fefecb 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypedDict from ..._types import FileTypes +from ..audio_model import AudioModel __all__ = ["TranscriptionCreateParams"] @@ -17,7 +18,7 @@ class TranscriptionCreateParams(TypedDict, total=False): flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. """ - model: Required[Union[str, Literal["whisper-1"]]] + model: Required[Union[str, AudioModel]] """ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) is diff --git a/src/openai/types/audio/translation.py b/src/openai/types/audio/translation.py index 3d9ede2939..7c0e905189 100644 --- a/src/openai/types/audio/translation.py +++ b/src/openai/types/audio/translation.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["Translation"] diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py index f23a41ed5c..054996a134 100644 --- a/src/openai/types/audio/translation_create_params.py +++ b/src/openai/types/audio/translation_create_params.py @@ -3,9 +3,10 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Required, TypedDict from ..._types import FileTypes +from ..audio_model import AudioModel __all__ = ["TranslationCreateParams"] @@ -17,7 +18,7 @@ class TranslationCreateParams(TypedDict, total=False): mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. """ - model: Required[Union[str, Literal["whisper-1"]]] + model: Required[Union[str, AudioModel]] """ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) is diff --git a/src/openai/types/audio_model.py b/src/openai/types/audio_model.py new file mode 100644 index 0000000000..d48e1c06d3 --- /dev/null +++ b/src/openai/types/audio_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +__all__ = ["AudioModel"] + +AudioModel = Literal["whisper-1"] diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py index ef6c84a0a1..7e1d49fb88 100644 --- a/src/openai/types/batch_request_counts.py +++ b/src/openai/types/batch_request_counts.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["BatchRequestCounts"] diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 754752ae65..42a42ae04e 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -5,6 +5,7 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from ..chat_model import ChatModel from .assistant_tool_param import AssistantToolParam from .assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -22,35 +23,7 @@ class AssistantCreateParams(TypedDict, total=False): - model: Required[ - Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - ] - ] + model: Required[Union[str, ChatModel]] """ID of the model to use. You can use the diff --git a/src/openai/types/beta/assistant_tool_choice_function.py b/src/openai/types/beta/assistant_tool_choice_function.py index d0d4255357..0c896d8087 100644 --- a/src/openai/types/beta/assistant_tool_choice_function.py +++ b/src/openai/types/beta/assistant_tool_choice_function.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["AssistantToolChoiceFunction"] diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 9421a894d9..c3edf34813 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -5,6 +5,7 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from ..chat_model import ChatModel from .function_tool_param import FunctionToolParam from .file_search_tool_param import FileSearchToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam @@ -77,34 +78,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): a maxium of 512 characters long. """ - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] + model: Union[str, ChatModel, None] """ The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 81cd85188b..dca757ab5f 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -5,6 +5,7 @@ from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from ...chat_model import ChatModel from ..assistant_tool_param import AssistantToolParam from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam @@ -74,34 +75,7 @@ class RunCreateParamsBase(TypedDict, total=False): a maxium of 512 characters long. """ - model: Union[ - str, - Literal[ - "gpt-4o", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - ], - None, - ] + model: Union[str, ChatModel, None] """ The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index 0d57b96595..ac09afd479 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["CompletionUsage"] diff --git a/src/openai/types/fine_tuning/fine_tuning_job_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_integration.py index 8076313cae..9a66aa4f17 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_integration.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_integration.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject FineTuningJobIntegration = FineTuningJobWandbIntegrationObject diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py index 2549307372..d6ecf0f1ae 100644 --- a/src/openai/types/image_create_variation_params.py +++ b/src/openai/types/image_create_variation_params.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes +from .image_model import ImageModel __all__ = ["ImageCreateVariationParams"] @@ -17,7 +18,7 @@ class ImageCreateVariationParams(TypedDict, total=False): Must be a valid PNG file, less than 4MB, and square. """ - model: Union[str, Literal["dall-e-2"], None] + model: Union[str, ImageModel, None] """The model to use for image generation. Only `dall-e-2` is supported at this time. diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 073456e349..a596a8692b 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes +from .image_model import ImageModel __all__ = ["ImageEditParams"] @@ -31,7 +32,7 @@ class ImageEditParams(TypedDict, total=False): PNG file, less than 4MB, and have the same dimensions as `image`. """ - model: Union[str, Literal["dall-e-2"], None] + model: Union[str, ImageModel, None] """The model to use for image generation. Only `dall-e-2` is supported at this time. diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 18c56f8ed6..307adeb3da 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -5,6 +5,8 @@ from typing import Union, Optional from typing_extensions import Literal, Required, TypedDict +from .image_model import ImageModel + __all__ = ["ImageGenerateParams"] @@ -16,7 +18,7 @@ class ImageGenerateParams(TypedDict, total=False): `dall-e-3`. """ - model: Union[str, Literal["dall-e-2", "dall-e-3"], None] + model: Union[str, ImageModel, None] """The model to use for image generation.""" n: Optional[int] diff --git a/src/openai/types/image_model.py b/src/openai/types/image_model.py new file mode 100644 index 0000000000..ce6535ff2c --- /dev/null +++ b/src/openai/types/image_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +__all__ = ["ImageModel"] + +ImageModel = Literal["dall-e-2", "dall-e-3"] diff --git a/src/openai/types/model_deleted.py b/src/openai/types/model_deleted.py index d9a48bb1b5..7f81e1b380 100644 --- a/src/openai/types/model_deleted.py +++ b/src/openai/types/model_deleted.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["ModelDeleted"] diff --git a/src/openai/types/moderation_create_params.py b/src/openai/types/moderation_create_params.py index d4608def54..337682194d 100644 --- a/src/openai/types/moderation_create_params.py +++ b/src/openai/types/moderation_create_params.py @@ -3,7 +3,9 @@ from __future__ import annotations from typing import List, Union -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Required, TypedDict + +from .moderation_model import ModerationModel __all__ = ["ModerationCreateParams"] @@ -12,7 +14,7 @@ class ModerationCreateParams(TypedDict, total=False): input: Required[Union[str, List[str]]] """The input text to classify""" - model: Union[str, Literal["text-moderation-latest", "text-moderation-stable"]] + model: Union[str, ModerationModel] """ Two content moderations models are available: `text-moderation-stable` and `text-moderation-latest`. diff --git a/src/openai/types/moderation_model.py b/src/openai/types/moderation_model.py new file mode 100644 index 0000000000..73362596f3 --- /dev/null +++ b/src/openai/types/moderation_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +__all__ = ["ModerationModel"] + +ModerationModel = Literal["text-moderation-latest", "text-moderation-stable"] From 08943dfdb1caf2d3fa67fcb2afb6ca5502da08f4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 04:04:44 +0000 Subject: [PATCH 534/914] feat: make enums not nominal (#1588) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 4e4cb5509c..6cc7757636 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-77cfff37114bc9f141c7e6107eb5f1b38d8cc99bc3d4ce03a066db2b6b649c69.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-b04761ffd2adad3cc19a6dc6fc696ac445878219972f891881a967340fa9a6b0.yml From da48e4cac78d1d4ac749e2aa5cfd619fde1e6c68 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 09:32:49 +0000 Subject: [PATCH 535/914] release: 1.38.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9baafa1759..b90a705e63 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.37.2" + ".": "1.38.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 824d4d83b7..0f62f6689d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.38.0 (2024-08-02) + +Full Changelog: [v1.37.2...v1.38.0](https://github.com/openai/openai-python/compare/v1.37.2...v1.38.0) + +### Features + +* extract out `ImageModel`, `AudioModel`, `SpeechModel` ([#1586](https://github.com/openai/openai-python/issues/1586)) ([b800316](https://github.com/openai/openai-python/commit/b800316aee6c8b2aeb609ca4c41972adccd2fa7a)) +* make enums not nominal ([#1588](https://github.com/openai/openai-python/issues/1588)) ([ab4519b](https://github.com/openai/openai-python/commit/ab4519bc45f5512c8c5165641c217385d999809c)) + ## 1.37.2 (2024-08-01) Full Changelog: [v1.37.1...v1.37.2](https://github.com/openai/openai-python/compare/v1.37.1...v1.37.2) diff --git a/pyproject.toml b/pyproject.toml index 3c6dcd409a..0d736aa444 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.37.2" +version = "1.38.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e36be4473a..ea1c039fab 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.37.2" # x-release-please-version +__version__ = "1.38.0" # x-release-please-version From d4a998e8501d15c5dd990b1914a056044ff5f8f6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:02:33 +0000 Subject: [PATCH 536/914] chore(internal): use `TypeAlias` marker for type assignments (#1597) --- src/openai/types/audio/speech_model.py | 4 ++-- src/openai/types/audio_model.py | 4 ++-- src/openai/types/beta/assistant_create_params.py | 4 ++-- src/openai/types/beta/assistant_response_format_option.py | 4 ++-- .../types/beta/assistant_response_format_option_param.py | 4 ++-- src/openai/types/beta/assistant_stream_event.py | 4 ++-- src/openai/types/beta/assistant_tool.py | 6 ++++-- src/openai/types/beta/assistant_tool_choice_option.py | 4 ++-- .../types/beta/assistant_tool_choice_option_param.py | 4 ++-- src/openai/types/beta/assistant_tool_param.py | 3 ++- src/openai/types/beta/thread_create_and_run_params.py | 8 ++++---- src/openai/types/beta/thread_create_params.py | 6 +++--- src/openai/types/beta/threads/annotation.py | 4 ++-- src/openai/types/beta/threads/annotation_delta.py | 4 ++-- src/openai/types/beta/threads/message.py | 4 ++-- src/openai/types/beta/threads/message_content.py | 4 ++-- src/openai/types/beta/threads/message_content_delta.py | 4 ++-- .../types/beta/threads/message_content_part_param.py | 3 ++- src/openai/types/beta/threads/message_create_params.py | 4 ++-- src/openai/types/beta/threads/run_create_params.py | 4 ++-- src/openai/types/beta/threads/run_status.py | 4 ++-- .../types/beta/threads/runs/code_interpreter_tool_call.py | 4 ++-- .../beta/threads/runs/code_interpreter_tool_call_delta.py | 4 ++-- src/openai/types/beta/threads/runs/run_step.py | 6 ++++-- src/openai/types/beta/threads/runs/run_step_delta.py | 6 ++++-- src/openai/types/beta/threads/runs/tool_call.py | 4 ++-- src/openai/types/beta/threads/runs/tool_call_delta.py | 4 ++-- src/openai/types/beta/vector_store_create_params.py | 4 ++-- .../types/beta/vector_stores/file_batch_create_params.py | 4 ++-- src/openai/types/beta/vector_stores/file_create_params.py | 4 ++-- src/openai/types/beta/vector_stores/vector_store_file.py | 6 ++++-- .../types/chat/chat_completion_content_part_param.py | 5 ++++- src/openai/types/chat/chat_completion_message_param.py | 3 ++- src/openai/types/chat/chat_completion_role.py | 4 ++-- .../chat/chat_completion_tool_choice_option_param.py | 6 ++++-- src/openai/types/chat/completion_create_params.py | 4 ++-- src/openai/types/chat_model.py | 4 ++-- src/openai/types/file_content.py | 3 ++- src/openai/types/image_model.py | 4 ++-- src/openai/types/moderation_model.py | 4 ++-- src/openai/types/shared/function_parameters.py | 3 ++- src/openai/types/shared_params/function_parameters.py | 3 ++- 42 files changed, 99 insertions(+), 80 deletions(-) diff --git a/src/openai/types/audio/speech_model.py b/src/openai/types/audio/speech_model.py index e92b898b99..bd685ab34d 100644 --- a/src/openai/types/audio/speech_model.py +++ b/src/openai/types/audio/speech_model.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["SpeechModel"] -SpeechModel = Literal["tts-1", "tts-1-hd"] +SpeechModel: TypeAlias = Literal["tts-1", "tts-1-hd"] diff --git a/src/openai/types/audio_model.py b/src/openai/types/audio_model.py index d48e1c06d3..94ae84c015 100644 --- a/src/openai/types/audio_model.py +++ b/src/openai/types/audio_model.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["AudioModel"] -AudioModel = Literal["whisper-1"] +AudioModel: TypeAlias = Literal["whisper-1"] diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 42a42ae04e..c10f7f57ad 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..chat_model import ChatModel from .assistant_tool_param import AssistantToolParam @@ -140,7 +140,7 @@ class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total= """Always `static`.""" -ToolResourcesFileSearchVectorStoreChunkingStrategy = Union[ +ToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic ] diff --git a/src/openai/types/beta/assistant_response_format_option.py b/src/openai/types/beta/assistant_response_format_option.py index d4e05e0ea9..6ce390f6d6 100644 --- a/src/openai/types/beta/assistant_response_format_option.py +++ b/src/openai/types/beta/assistant_response_format_option.py @@ -1,10 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from .assistant_response_format import AssistantResponseFormat __all__ = ["AssistantResponseFormatOption"] -AssistantResponseFormatOption = Union[Literal["none", "auto"], AssistantResponseFormat] +AssistantResponseFormatOption: TypeAlias = Union[Literal["none", "auto"], AssistantResponseFormat] diff --git a/src/openai/types/beta/assistant_response_format_option_param.py b/src/openai/types/beta/assistant_response_format_option_param.py index 46e04125d1..8100088723 100644 --- a/src/openai/types/beta/assistant_response_format_option_param.py +++ b/src/openai/types/beta/assistant_response_format_option_param.py @@ -3,10 +3,10 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from .assistant_response_format_param import AssistantResponseFormatParam __all__ = ["AssistantResponseFormatOptionParam"] -AssistantResponseFormatOptionParam = Union[Literal["none", "auto"], AssistantResponseFormatParam] +AssistantResponseFormatOptionParam: TypeAlias = Union[Literal["none", "auto"], AssistantResponseFormatParam] diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py index de66888403..f1d8898ff2 100644 --- a/src/openai/types/beta/assistant_stream_event.py +++ b/src/openai/types/beta/assistant_stream_event.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Literal, Annotated +from typing_extensions import Literal, Annotated, TypeAlias from .thread import Thread from ..._utils import PropertyInfo @@ -260,7 +260,7 @@ class ErrorEvent(BaseModel): event: Literal["error"] -AssistantStreamEvent = Annotated[ +AssistantStreamEvent: TypeAlias = Annotated[ Union[ ThreadCreated, ThreadRunCreated, diff --git a/src/openai/types/beta/assistant_tool.py b/src/openai/types/beta/assistant_tool.py index 7832da48cc..1bde6858b1 100644 --- a/src/openai/types/beta/assistant_tool.py +++ b/src/openai/types/beta/assistant_tool.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Annotated +from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .function_tool import FunctionTool @@ -10,4 +10,6 @@ __all__ = ["AssistantTool"] -AssistantTool = Annotated[Union[CodeInterpreterTool, FileSearchTool, FunctionTool], PropertyInfo(discriminator="type")] +AssistantTool: TypeAlias = Annotated[ + Union[CodeInterpreterTool, FileSearchTool, FunctionTool], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/assistant_tool_choice_option.py b/src/openai/types/beta/assistant_tool_choice_option.py index 8958bc8fb0..e57c3278fb 100644 --- a/src/openai/types/beta/assistant_tool_choice_option.py +++ b/src/openai/types/beta/assistant_tool_choice_option.py @@ -1,10 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from .assistant_tool_choice import AssistantToolChoice __all__ = ["AssistantToolChoiceOption"] -AssistantToolChoiceOption = Union[Literal["none", "auto", "required"], AssistantToolChoice] +AssistantToolChoiceOption: TypeAlias = Union[Literal["none", "auto", "required"], AssistantToolChoice] diff --git a/src/openai/types/beta/assistant_tool_choice_option_param.py b/src/openai/types/beta/assistant_tool_choice_option_param.py index 81b7f15136..cc0053d37e 100644 --- a/src/openai/types/beta/assistant_tool_choice_option_param.py +++ b/src/openai/types/beta/assistant_tool_choice_option_param.py @@ -3,10 +3,10 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from .assistant_tool_choice_param import AssistantToolChoiceParam __all__ = ["AssistantToolChoiceOptionParam"] -AssistantToolChoiceOptionParam = Union[Literal["none", "auto", "required"], AssistantToolChoiceParam] +AssistantToolChoiceOptionParam: TypeAlias = Union[Literal["none", "auto", "required"], AssistantToolChoiceParam] diff --git a/src/openai/types/beta/assistant_tool_param.py b/src/openai/types/beta/assistant_tool_param.py index 5b1d30ba2f..321c4b1ddb 100644 --- a/src/openai/types/beta/assistant_tool_param.py +++ b/src/openai/types/beta/assistant_tool_param.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Union +from typing_extensions import TypeAlias from .function_tool_param import FunctionToolParam from .file_search_tool_param import FileSearchToolParam @@ -10,4 +11,4 @@ __all__ = ["AssistantToolParam"] -AssistantToolParam = Union[CodeInterpreterToolParam, FileSearchToolParam, FunctionToolParam] +AssistantToolParam: TypeAlias = Union[CodeInterpreterToolParam, FileSearchToolParam, FunctionToolParam] diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index c3edf34813..62cff921e2 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..chat_model import ChatModel from .function_tool_param import FunctionToolParam @@ -168,7 +168,7 @@ class ThreadMessageAttachmentToolFileSearch(TypedDict, total=False): """The type of tool being defined: `file_search`""" -ThreadMessageAttachmentTool = Union[CodeInterpreterToolParam, ThreadMessageAttachmentToolFileSearch] +ThreadMessageAttachmentTool: TypeAlias = Union[CodeInterpreterToolParam, ThreadMessageAttachmentToolFileSearch] class ThreadMessageAttachment(TypedDict, total=False): @@ -240,7 +240,7 @@ class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, """Always `static`.""" -ThreadToolResourcesFileSearchVectorStoreChunkingStrategy = Union[ +ThreadToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic, ] @@ -342,7 +342,7 @@ class ToolResources(TypedDict, total=False): file_search: ToolResourcesFileSearch -Tool = Union[CodeInterpreterToolParam, FileSearchToolParam, FunctionToolParam] +Tool: TypeAlias = Union[CodeInterpreterToolParam, FileSearchToolParam, FunctionToolParam] class TruncationStrategy(TypedDict, total=False): diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index e5ea14a94d..f9561aa48c 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .code_interpreter_tool_param import CodeInterpreterToolParam from .threads.message_content_part_param import MessageContentPartParam @@ -54,7 +54,7 @@ class MessageAttachmentToolFileSearch(TypedDict, total=False): """The type of tool being defined: `file_search`""" -MessageAttachmentTool = Union[CodeInterpreterToolParam, MessageAttachmentToolFileSearch] +MessageAttachmentTool: TypeAlias = Union[CodeInterpreterToolParam, MessageAttachmentToolFileSearch] class MessageAttachment(TypedDict, total=False): @@ -126,7 +126,7 @@ class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total= """Always `static`.""" -ToolResourcesFileSearchVectorStoreChunkingStrategy = Union[ +ToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic ] diff --git a/src/openai/types/beta/threads/annotation.py b/src/openai/types/beta/threads/annotation.py index 31e228c831..13c10abf4d 100644 --- a/src/openai/types/beta/threads/annotation.py +++ b/src/openai/types/beta/threads/annotation.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Annotated +from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .file_path_annotation import FilePathAnnotation @@ -9,4 +9,4 @@ __all__ = ["Annotation"] -Annotation = Annotated[Union[FileCitationAnnotation, FilePathAnnotation], PropertyInfo(discriminator="type")] +Annotation: TypeAlias = Annotated[Union[FileCitationAnnotation, FilePathAnnotation], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/beta/threads/annotation_delta.py b/src/openai/types/beta/threads/annotation_delta.py index 912429672f..c7c6c89837 100644 --- a/src/openai/types/beta/threads/annotation_delta.py +++ b/src/openai/types/beta/threads/annotation_delta.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Annotated +from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .file_path_delta_annotation import FilePathDeltaAnnotation @@ -9,6 +9,6 @@ __all__ = ["AnnotationDelta"] -AnnotationDelta = Annotated[ +AnnotationDelta: TypeAlias = Annotated[ Union[FileCitationDeltaAnnotation, FilePathDeltaAnnotation], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index 90f083683d..298a1d4273 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from ...._models import BaseModel from .message_content import MessageContent @@ -21,7 +21,7 @@ class AttachmentToolAssistantToolsFileSearchTypeOnly(BaseModel): """The type of tool being defined: `file_search`""" -AttachmentTool = Union[CodeInterpreterTool, AttachmentToolAssistantToolsFileSearchTypeOnly] +AttachmentTool: TypeAlias = Union[CodeInterpreterTool, AttachmentToolAssistantToolsFileSearchTypeOnly] class Attachment(BaseModel): diff --git a/src/openai/types/beta/threads/message_content.py b/src/openai/types/beta/threads/message_content.py index 4f17d14786..7b718c3ca9 100644 --- a/src/openai/types/beta/threads/message_content.py +++ b/src/openai/types/beta/threads/message_content.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Annotated +from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .text_content_block import TextContentBlock @@ -10,6 +10,6 @@ __all__ = ["MessageContent"] -MessageContent = Annotated[ +MessageContent: TypeAlias = Annotated[ Union[ImageFileContentBlock, ImageURLContentBlock, TextContentBlock], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/beta/threads/message_content_delta.py b/src/openai/types/beta/threads/message_content_delta.py index 6c5f732b12..667172c08f 100644 --- a/src/openai/types/beta/threads/message_content_delta.py +++ b/src/openai/types/beta/threads/message_content_delta.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Annotated +from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .text_delta_block import TextDeltaBlock @@ -10,6 +10,6 @@ __all__ = ["MessageContentDelta"] -MessageContentDelta = Annotated[ +MessageContentDelta: TypeAlias = Annotated[ Union[ImageFileDeltaBlock, TextDeltaBlock, ImageURLDeltaBlock], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/beta/threads/message_content_part_param.py b/src/openai/types/beta/threads/message_content_part_param.py index d11442a3a9..dc09a01c27 100644 --- a/src/openai/types/beta/threads/message_content_part_param.py +++ b/src/openai/types/beta/threads/message_content_part_param.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Union +from typing_extensions import TypeAlias from .text_content_block_param import TextContentBlockParam from .image_url_content_block_param import ImageURLContentBlockParam @@ -10,4 +11,4 @@ __all__ = ["MessageContentPartParam"] -MessageContentPartParam = Union[ImageFileContentBlockParam, ImageURLContentBlockParam, TextContentBlockParam] +MessageContentPartParam: TypeAlias = Union[ImageFileContentBlockParam, ImageURLContentBlockParam, TextContentBlockParam] diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index b1b12293b7..2b450deb5d 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam @@ -41,7 +41,7 @@ class AttachmentToolFileSearch(TypedDict, total=False): """The type of tool being defined: `file_search`""" -AttachmentTool = Union[CodeInterpreterToolParam, AttachmentToolFileSearch] +AttachmentTool: TypeAlias = Union[CodeInterpreterToolParam, AttachmentToolFileSearch] class Attachment(TypedDict, total=False): diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index dca757ab5f..e0c42fd23f 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from ...chat_model import ChatModel from ..assistant_tool_param import AssistantToolParam @@ -154,7 +154,7 @@ class AdditionalMessageAttachmentToolFileSearch(TypedDict, total=False): """The type of tool being defined: `file_search`""" -AdditionalMessageAttachmentTool = Union[CodeInterpreterToolParam, AdditionalMessageAttachmentToolFileSearch] +AdditionalMessageAttachmentTool: TypeAlias = Union[CodeInterpreterToolParam, AdditionalMessageAttachmentToolFileSearch] class AdditionalMessageAttachment(TypedDict, total=False): diff --git a/src/openai/types/beta/threads/run_status.py b/src/openai/types/beta/threads/run_status.py index 6666d00e5a..47c7cbd007 100644 --- a/src/openai/types/beta/threads/run_status.py +++ b/src/openai/types/beta/threads/run_status.py @@ -1,10 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["RunStatus"] -RunStatus = Literal[ +RunStatus: TypeAlias = Literal[ "queued", "in_progress", "requires_action", diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py index 2f07243684..e7df4e19c4 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union -from typing_extensions import Literal, Annotated +from typing_extensions import Literal, Annotated, TypeAlias from ....._utils import PropertyInfo from ....._models import BaseModel @@ -39,7 +39,7 @@ class CodeInterpreterOutputImage(BaseModel): """Always `image`.""" -CodeInterpreterOutput = Annotated[ +CodeInterpreterOutput: TypeAlias = Annotated[ Union[CodeInterpreterOutputLogs, CodeInterpreterOutputImage], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py index eff76355b3..9d7a1563cd 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional -from typing_extensions import Literal, Annotated +from typing_extensions import Literal, Annotated, TypeAlias from ....._utils import PropertyInfo from ....._models import BaseModel @@ -10,7 +10,7 @@ __all__ = ["CodeInterpreterToolCallDelta", "CodeInterpreter", "CodeInterpreterOutput"] -CodeInterpreterOutput = Annotated[ +CodeInterpreterOutput: TypeAlias = Annotated[ Union[CodeInterpreterLogs, CodeInterpreterOutputImage], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 7c81dcac2b..e3163c508b 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional -from typing_extensions import Literal, Annotated +from typing_extensions import Literal, Annotated, TypeAlias from ....._utils import PropertyInfo from ....._models import BaseModel @@ -19,7 +19,9 @@ class LastError(BaseModel): """A human-readable description of the error.""" -StepDetails = Annotated[Union[MessageCreationStepDetails, ToolCallsStepDetails], PropertyInfo(discriminator="type")] +StepDetails: TypeAlias = Annotated[ + Union[MessageCreationStepDetails, ToolCallsStepDetails], PropertyInfo(discriminator="type") +] class Usage(BaseModel): diff --git a/src/openai/types/beta/threads/runs/run_step_delta.py b/src/openai/types/beta/threads/runs/run_step_delta.py index d6b4aefeb9..1139088fb4 100644 --- a/src/openai/types/beta/threads/runs/run_step_delta.py +++ b/src/openai/types/beta/threads/runs/run_step_delta.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional -from typing_extensions import Annotated +from typing_extensions import Annotated, TypeAlias from ....._utils import PropertyInfo from ....._models import BaseModel @@ -10,7 +10,9 @@ __all__ = ["RunStepDelta", "StepDetails"] -StepDetails = Annotated[Union[RunStepDeltaMessageDelta, ToolCallDeltaObject], PropertyInfo(discriminator="type")] +StepDetails: TypeAlias = Annotated[ + Union[RunStepDeltaMessageDelta, ToolCallDeltaObject], PropertyInfo(discriminator="type") +] class RunStepDelta(BaseModel): diff --git a/src/openai/types/beta/threads/runs/tool_call.py b/src/openai/types/beta/threads/runs/tool_call.py index 77d86b46d9..565e3109be 100644 --- a/src/openai/types/beta/threads/runs/tool_call.py +++ b/src/openai/types/beta/threads/runs/tool_call.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Annotated +from typing_extensions import Annotated, TypeAlias from ....._utils import PropertyInfo from .function_tool_call import FunctionToolCall @@ -10,6 +10,6 @@ __all__ = ["ToolCall"] -ToolCall = Annotated[ +ToolCall: TypeAlias = Annotated[ Union[CodeInterpreterToolCall, FileSearchToolCall, FunctionToolCall], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/beta/threads/runs/tool_call_delta.py b/src/openai/types/beta/threads/runs/tool_call_delta.py index 90cfe0657e..f0b8070c97 100644 --- a/src/openai/types/beta/threads/runs/tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/tool_call_delta.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Annotated +from typing_extensions import Annotated, TypeAlias from ....._utils import PropertyInfo from .function_tool_call_delta import FunctionToolCallDelta @@ -10,7 +10,7 @@ __all__ = ["ToolCallDelta"] -ToolCallDelta = Annotated[ +ToolCallDelta: TypeAlias = Annotated[ Union[CodeInterpreterToolCallDelta, FileSearchToolCallDelta, FunctionToolCallDelta], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/beta/vector_store_create_params.py b/src/openai/types/beta/vector_store_create_params.py index 365d9923b8..4f74af49f8 100644 --- a/src/openai/types/beta/vector_store_create_params.py +++ b/src/openai/types/beta/vector_store_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import List, Union, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict __all__ = [ "VectorStoreCreateParams", @@ -72,7 +72,7 @@ class ChunkingStrategyStatic(TypedDict, total=False): """Always `static`.""" -ChunkingStrategy = Union[ChunkingStrategyAuto, ChunkingStrategyStatic] +ChunkingStrategy: TypeAlias = Union[ChunkingStrategyAuto, ChunkingStrategyStatic] class ExpiresAfter(TypedDict, total=False): diff --git a/src/openai/types/beta/vector_stores/file_batch_create_params.py b/src/openai/types/beta/vector_stores/file_batch_create_params.py index 9b98d0699e..e1c3303cf3 100644 --- a/src/openai/types/beta/vector_stores/file_batch_create_params.py +++ b/src/openai/types/beta/vector_stores/file_batch_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import List, Union -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict __all__ = [ "FileBatchCreateParams", @@ -56,6 +56,6 @@ class ChunkingStrategyStaticChunkingStrategyRequestParam(TypedDict, total=False) """Always `static`.""" -ChunkingStrategy = Union[ +ChunkingStrategy: TypeAlias = Union[ ChunkingStrategyAutoChunkingStrategyRequestParam, ChunkingStrategyStaticChunkingStrategyRequestParam ] diff --git a/src/openai/types/beta/vector_stores/file_create_params.py b/src/openai/types/beta/vector_stores/file_create_params.py index 2ae63f1462..cfb80657c6 100644 --- a/src/openai/types/beta/vector_stores/file_create_params.py +++ b/src/openai/types/beta/vector_stores/file_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict __all__ = [ "FileCreateParams", @@ -56,6 +56,6 @@ class ChunkingStrategyStaticChunkingStrategyRequestParam(TypedDict, total=False) """Always `static`.""" -ChunkingStrategy = Union[ +ChunkingStrategy: TypeAlias = Union[ ChunkingStrategyAutoChunkingStrategyRequestParam, ChunkingStrategyStaticChunkingStrategyRequestParam ] diff --git a/src/openai/types/beta/vector_stores/vector_store_file.py b/src/openai/types/beta/vector_stores/vector_store_file.py index d9d7625f86..4762de0ebd 100644 --- a/src/openai/types/beta/vector_stores/vector_store_file.py +++ b/src/openai/types/beta/vector_stores/vector_store_file.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional -from typing_extensions import Literal, Annotated +from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel @@ -51,7 +51,9 @@ class ChunkingStrategyOther(BaseModel): """Always `other`.""" -ChunkingStrategy = Annotated[Union[ChunkingStrategyStatic, ChunkingStrategyOther], PropertyInfo(discriminator="type")] +ChunkingStrategy: TypeAlias = Annotated[ + Union[ChunkingStrategyStatic, ChunkingStrategyOther], PropertyInfo(discriminator="type") +] class VectorStoreFile(BaseModel): diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index f9b5f71e43..e0c6e480f2 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -3,10 +3,13 @@ from __future__ import annotations from typing import Union +from typing_extensions import TypeAlias from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam from .chat_completion_content_part_image_param import ChatCompletionContentPartImageParam __all__ = ["ChatCompletionContentPartParam"] -ChatCompletionContentPartParam = Union[ChatCompletionContentPartTextParam, ChatCompletionContentPartImageParam] +ChatCompletionContentPartParam: TypeAlias = Union[ + ChatCompletionContentPartTextParam, ChatCompletionContentPartImageParam +] diff --git a/src/openai/types/chat/chat_completion_message_param.py b/src/openai/types/chat/chat_completion_message_param.py index a3644a5310..ec65d94cae 100644 --- a/src/openai/types/chat/chat_completion_message_param.py +++ b/src/openai/types/chat/chat_completion_message_param.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Union +from typing_extensions import TypeAlias from .chat_completion_tool_message_param import ChatCompletionToolMessageParam from .chat_completion_user_message_param import ChatCompletionUserMessageParam @@ -12,7 +13,7 @@ __all__ = ["ChatCompletionMessageParam"] -ChatCompletionMessageParam = Union[ +ChatCompletionMessageParam: TypeAlias = Union[ ChatCompletionSystemMessageParam, ChatCompletionUserMessageParam, ChatCompletionAssistantMessageParam, diff --git a/src/openai/types/chat/chat_completion_role.py b/src/openai/types/chat/chat_completion_role.py index 1fd83888d3..c2ebef74c8 100644 --- a/src/openai/types/chat/chat_completion_role.py +++ b/src/openai/types/chat/chat_completion_role.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["ChatCompletionRole"] -ChatCompletionRole = Literal["system", "user", "assistant", "tool", "function"] +ChatCompletionRole: TypeAlias = Literal["system", "user", "assistant", "tool", "function"] diff --git a/src/openai/types/chat/chat_completion_tool_choice_option_param.py b/src/openai/types/chat/chat_completion_tool_choice_option_param.py index 1d3c2506ab..7dedf041b7 100644 --- a/src/openai/types/chat/chat_completion_tool_choice_option_param.py +++ b/src/openai/types/chat/chat_completion_tool_choice_option_param.py @@ -3,10 +3,12 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from .chat_completion_named_tool_choice_param import ChatCompletionNamedToolChoiceParam __all__ = ["ChatCompletionToolChoiceOptionParam"] -ChatCompletionToolChoiceOptionParam = Union[Literal["none", "auto", "required"], ChatCompletionNamedToolChoiceParam] +ChatCompletionToolChoiceOptionParam: TypeAlias = Union[ + Literal["none", "auto", "required"], ChatCompletionNamedToolChoiceParam +] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 783922539f..9e81881b9e 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from ...types import shared_params from ..chat_model import ChatModel @@ -221,7 +221,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ -FunctionCall = Union[Literal["none", "auto"], ChatCompletionFunctionCallOptionParam] +FunctionCall: TypeAlias = Union[Literal["none", "auto"], ChatCompletionFunctionCallOptionParam] class Function(TypedDict, total=False): diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index 87b2acb90a..edb7b732bf 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -1,10 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["ChatModel"] -ChatModel = Literal[ +ChatModel: TypeAlias = Literal[ "gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-mini", diff --git a/src/openai/types/file_content.py b/src/openai/types/file_content.py index b4aa08a9a3..d89eee623e 100644 --- a/src/openai/types/file_content.py +++ b/src/openai/types/file_content.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing_extensions import TypeAlias __all__ = ["FileContent"] -FileContent = str +FileContent: TypeAlias = str diff --git a/src/openai/types/image_model.py b/src/openai/types/image_model.py index ce6535ff2c..1672369bea 100644 --- a/src/openai/types/image_model.py +++ b/src/openai/types/image_model.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["ImageModel"] -ImageModel = Literal["dall-e-2", "dall-e-3"] +ImageModel: TypeAlias = Literal["dall-e-2", "dall-e-3"] diff --git a/src/openai/types/moderation_model.py b/src/openai/types/moderation_model.py index 73362596f3..f549aeeb7a 100644 --- a/src/openai/types/moderation_model.py +++ b/src/openai/types/moderation_model.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["ModerationModel"] -ModerationModel = Literal["text-moderation-latest", "text-moderation-stable"] +ModerationModel: TypeAlias = Literal["text-moderation-latest", "text-moderation-stable"] diff --git a/src/openai/types/shared/function_parameters.py b/src/openai/types/shared/function_parameters.py index c9524e4cb8..a3d83e3496 100644 --- a/src/openai/types/shared/function_parameters.py +++ b/src/openai/types/shared/function_parameters.py @@ -1,7 +1,8 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict +from typing_extensions import TypeAlias __all__ = ["FunctionParameters"] -FunctionParameters = Dict[str, object] +FunctionParameters: TypeAlias = Dict[str, object] diff --git a/src/openai/types/shared_params/function_parameters.py b/src/openai/types/shared_params/function_parameters.py index 5b40efb78f..45fc742d3b 100644 --- a/src/openai/types/shared_params/function_parameters.py +++ b/src/openai/types/shared_params/function_parameters.py @@ -3,7 +3,8 @@ from __future__ import annotations from typing import Dict +from typing_extensions import TypeAlias __all__ = ["FunctionParameters"] -FunctionParameters = Dict[str, object] +FunctionParameters: TypeAlias = Dict[str, object] From 5c7030ed6622f1f10000816a1bc0f98b719afdd7 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 5 Aug 2024 11:51:22 +0000 Subject: [PATCH 537/914] chore(internal): bump pyright (#1599) --- requirements-dev.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 21a6b8d20c..3ad6b88f68 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -123,7 +123,7 @@ pygments==2.18.0 # via rich pyjwt==2.8.0 # via msal -pyright==1.1.364 +pyright==1.1.374 pytest==7.1.1 # via pytest-asyncio pytest-asyncio==0.21.1 From 98d8b2ac13ce7b22fd9ce237157bc1be593c7362 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:20:50 +0000 Subject: [PATCH 538/914] feat(client): add `retries_taken` to raw response class (#1601) --- src/openai/_base_client.py | 10 +++ src/openai/_legacy_response.py | 18 ++++- src/openai/_response.py | 5 ++ tests/test_client.py | 122 +++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 4b93ab298c..c8fce0bea4 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1051,6 +1051,7 @@ def _request( response=response, stream=stream, stream_cls=stream_cls, + retries_taken=options.get_max_retries(self.max_retries) - retries, ) def _retry_request( @@ -1092,6 +1093,7 @@ def _process_response( response: httpx.Response, stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, ) -> ResponseT: if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": return cast( @@ -1103,6 +1105,7 @@ def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ), ) @@ -1122,6 +1125,7 @@ def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ), ) @@ -1135,6 +1139,7 @@ def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ) if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): return cast(ResponseT, api_response) @@ -1625,6 +1630,7 @@ async def _request( response=response, stream=stream, stream_cls=stream_cls, + retries_taken=options.get_max_retries(self.max_retries) - retries, ) async def _retry_request( @@ -1664,6 +1670,7 @@ async def _process_response( response: httpx.Response, stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, ) -> ResponseT: if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": return cast( @@ -1675,6 +1682,7 @@ async def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ), ) @@ -1694,6 +1702,7 @@ async def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ), ) @@ -1707,6 +1716,7 @@ async def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ) if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): return cast(ResponseT, api_response) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 1de906b167..66d7606a60 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -5,7 +5,18 @@ import logging import datetime import functools -from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, Iterator, AsyncIterator, cast, overload +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, + overload, +) from typing_extensions import Awaitable, ParamSpec, override, deprecated, get_origin import anyio @@ -53,6 +64,9 @@ class LegacyAPIResponse(Generic[R]): http_response: httpx.Response + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + def __init__( self, *, @@ -62,6 +76,7 @@ def __init__( stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, options: FinalRequestOptions, + retries_taken: int = 0, ) -> None: self._cast_to = cast_to self._client = client @@ -70,6 +85,7 @@ def __init__( self._stream_cls = stream_cls self._options = options self.http_response = raw + self.retries_taken = retries_taken @property def request_id(self) -> str | None: diff --git a/src/openai/_response.py b/src/openai/_response.py index 4ba2ae681c..3bf4de4287 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -55,6 +55,9 @@ class BaseAPIResponse(Generic[R]): http_response: httpx.Response + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + def __init__( self, *, @@ -64,6 +67,7 @@ def __init__( stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, options: FinalRequestOptions, + retries_taken: int = 0, ) -> None: self._cast_to = cast_to self._client = client @@ -72,6 +76,7 @@ def __init__( self._stream_cls = stream_cls self._options = options self.http_response = raw + self.retries_taken = retries_taken @property def headers(self) -> httpx.Headers: diff --git a/tests/test_client.py b/tests/test_client.py index c1e545e66f..49e71653c5 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -758,6 +758,65 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non assert _get_open_connections(self.client) == 0 + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retries_taken(self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "content", + "role": "system", + } + ], + model="gpt-4-turbo", + ) + + assert response.retries_taken == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retries_taken_new_response_class( + self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + with client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "content", + "role": "system", + } + ], + model="gpt-4-turbo", + ) as response: + assert response.retries_taken == failures_before_success + class TestAsyncOpenAI: client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -1488,3 +1547,66 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) ) assert _get_open_connections(self.client) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_retries_taken( + self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = await client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "content", + "role": "system", + } + ], + model="gpt-4-turbo", + ) + + assert response.retries_taken == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_retries_taken_new_response_class( + self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + async with client.chat.completions.with_streaming_response.create( + messages=[ + { + "content": "content", + "role": "system", + } + ], + model="gpt-4-turbo", + ) as response: + assert response.retries_taken == failures_before_success From d50550bd5b4b6b4385b6327dc2cbc679b8dd29fd Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 5 Aug 2024 15:21:01 +0100 Subject: [PATCH 539/914] fix(assistants): add parallel_tool_calls param to runs.stream --- src/openai/resources/beta/threads/runs/runs.py | 8 ++++++++ tests/lib/test_assistants.py | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 61c6bb486f..23a09d30ce 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -950,6 +950,7 @@ def stream( max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -979,6 +980,7 @@ def stream( max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1008,6 +1010,7 @@ def stream( max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1051,6 +1054,7 @@ def stream( "tool_choice": tool_choice, "stream": True, "tools": tools, + "parallel_tool_calls": parallel_tool_calls, "truncation_strategy": truncation_strategy, "top_p": top_p, }, @@ -2246,6 +2250,7 @@ def stream( max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2275,6 +2280,7 @@ def stream( max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2304,6 +2310,7 @@ def stream( max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2349,6 +2356,7 @@ def stream( "tool_choice": tool_choice, "stream": True, "tools": tools, + "parallel_tool_calls": parallel_tool_calls, "truncation_strategy": truncation_strategy, "top_p": top_p, }, diff --git a/tests/lib/test_assistants.py b/tests/lib/test_assistants.py index b9d4e8927c..67d021ec35 100644 --- a/tests/lib/test_assistants.py +++ b/tests/lib/test_assistants.py @@ -28,6 +28,17 @@ def test_create_and_run_stream_method_definition_in_sync(sync: bool, client: Ope ) +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_run_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + assert_signatures_in_sync( + checking_client.beta.threads.runs.create, + checking_client.beta.threads.runs.stream, + exclude_params={"stream"}, + ) + + @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_create_and_poll_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: checking_client: OpenAI | AsyncOpenAI = client if sync else async_client From 30194f1955f2272e7af8b54fba3732d7a7e60307 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:31:39 +0000 Subject: [PATCH 540/914] chore(internal): test updates (#1602) --- src/openai/_utils/_reflection.py | 2 +- tests/test_client.py | 7 +++++-- tests/utils.py | 10 +++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/openai/_utils/_reflection.py b/src/openai/_utils/_reflection.py index 9a53c7bd21..89aa712ac4 100644 --- a/src/openai/_utils/_reflection.py +++ b/src/openai/_utils/_reflection.py @@ -34,7 +34,7 @@ def assert_signatures_in_sync( 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(source_param.annotation)}" + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" ) continue diff --git a/tests/test_client.py b/tests/test_client.py index 49e71653c5..2402ffa82f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -17,6 +17,7 @@ from pydantic import ValidationError from openai import OpenAI, AsyncOpenAI, APIResponseValidationError +from openai._types import Omit from openai._models import BaseModel, FinalRequestOptions from openai._constants import RAW_RESPONSE_HEADER from openai._streaming import Stream, AsyncStream @@ -328,7 +329,8 @@ def test_validate_headers(self) -> None: assert request.headers.get("Authorization") == f"Bearer {api_key}" with pytest.raises(OpenAIError): - client2 = OpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) + with update_env(**{"OPENAI_API_KEY": Omit()}): + client2 = OpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 def test_default_query_option(self) -> None: @@ -1103,7 +1105,8 @@ def test_validate_headers(self) -> None: assert request.headers.get("Authorization") == f"Bearer {api_key}" with pytest.raises(OpenAIError): - client2 = AsyncOpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) + with update_env(**{"OPENAI_API_KEY": Omit()}): + client2 = AsyncOpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 def test_default_query_option(self) -> None: diff --git a/tests/utils.py b/tests/utils.py index 060b99339f..165f4e5bfd 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,7 +8,7 @@ from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type -from openai._types import NoneType +from openai._types import Omit, NoneType from openai._utils import ( is_dict, is_list, @@ -139,11 +139,15 @@ def _assert_list_type(type_: type[object], value: object) -> None: @contextlib.contextmanager -def update_env(**new_env: str) -> Iterator[None]: +def update_env(**new_env: str | Omit) -> Iterator[None]: old = os.environ.copy() try: - os.environ.update(new_env) + for name, value in new_env.items(): + if isinstance(value, Omit): + os.environ.pop(name, None) + else: + os.environ[name] = value yield None finally: From a5f5c8ed9a0f2658214525011507e3091c186bdf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:32:09 +0000 Subject: [PATCH 541/914] release: 1.39.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b90a705e63..4d14a67e1c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.38.0" + ".": "1.39.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f62f6689d..b9cc30e307 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 1.39.0 (2024-08-05) + +Full Changelog: [v1.38.0...v1.39.0](https://github.com/openai/openai-python/compare/v1.38.0...v1.39.0) + +### Features + +* **client:** add `retries_taken` to raw response class ([#1601](https://github.com/openai/openai-python/issues/1601)) ([777822b](https://github.com/openai/openai-python/commit/777822b39b7f9ebd6272d0af8fc04f9d657bd886)) + + +### Bug Fixes + +* **assistants:** add parallel_tool_calls param to runs.stream ([113e82a](https://github.com/openai/openai-python/commit/113e82a82c7390660ad3324fa8f9842f83b27571)) + + +### Chores + +* **internal:** bump pyright ([#1599](https://github.com/openai/openai-python/issues/1599)) ([27f0f10](https://github.com/openai/openai-python/commit/27f0f107e39d16adc0d5a50ffe4c687e0e3c42e5)) +* **internal:** test updates ([#1602](https://github.com/openai/openai-python/issues/1602)) ([af22d80](https://github.com/openai/openai-python/commit/af22d8079cf44cde5f03a206e78b900f8413dc43)) +* **internal:** use `TypeAlias` marker for type assignments ([#1597](https://github.com/openai/openai-python/issues/1597)) ([5907ea0](https://github.com/openai/openai-python/commit/5907ea04d6f5e0ffd17c38ad6a644a720ece8abe)) + ## 1.38.0 (2024-08-02) Full Changelog: [v1.37.2...v1.38.0](https://github.com/openai/openai-python/compare/v1.37.2...v1.38.0) diff --git a/pyproject.toml b/pyproject.toml index 0d736aa444..d0527bd84e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.38.0" +version = "1.39.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ea1c039fab..aed8ee29b2 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.38.0" # x-release-please-version +__version__ = "1.39.0" # x-release-please-version From 1d8a28e39a9389b36b93770da248926b052b3e28 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Tue, 6 Aug 2024 10:28:23 +0000 Subject: [PATCH 542/914] chore(internal): bump ruff version (#1604) --- pyproject.toml | 12 ++++--- requirements-dev.lock | 2 +- src/openai/_base_client.py | 63 +++++++++++---------------------- src/openai/_compat.py | 24 +++++-------- src/openai/_files.py | 12 +++---- src/openai/_legacy_response.py | 6 ++-- src/openai/_response.py | 12 +++---- src/openai/_types.py | 9 ++--- src/openai/_utils/_proxy.py | 3 +- src/openai/_utils/_utils.py | 18 ++++------ src/openai/cli/_errors.py | 6 ++-- src/openai/lib/azure.py | 18 ++++------ tests/test_deepcopy.py | 3 +- tests/test_legacy_response.py | 3 +- tests/test_response.py | 12 +++---- tests/test_utils/test_typing.py | 15 +++----- 16 files changed, 76 insertions(+), 142 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d0527bd84e..99e0fc6591 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,8 +83,8 @@ format = { chain = [ "check:ruff", "typecheck", ]} -"check:ruff" = "ruff ." -"fix:ruff" = "ruff --fix ." +"check:ruff" = "ruff check ." +"fix:ruff" = "ruff check --fix ." typecheck = { chain = [ "typecheck:pyright", @@ -168,6 +168,11 @@ reportPrivateUsage = false line-length = 120 output-format = "grouped" target-version = "py37" + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint] select = [ # isort "I", @@ -198,9 +203,6 @@ unfixable = [ ] ignore-init-module-imports = true -[tool.ruff.format] -docstring-code-format = true - [tool.ruff.lint.flake8-tidy-imports.banned-api] "functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" diff --git a/requirements-dev.lock b/requirements-dev.lock index 3ad6b88f68..d2ad945343 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -139,7 +139,7 @@ requests==2.31.0 respx==0.20.2 rich==13.7.1 # via inline-snapshot -ruff==0.1.9 +ruff==0.5.6 setuptools==68.2.2 # via nodeenv six==1.16.0 diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index c8fce0bea4..3388d69fab 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -125,16 +125,14 @@ def __init__( self, *, url: URL, - ) -> None: - ... + ) -> None: ... @overload def __init__( self, *, params: Query, - ) -> None: - ... + ) -> None: ... def __init__( self, @@ -167,8 +165,7 @@ def has_next_page(self) -> bool: return False return self.next_page_info() is not None - def next_page_info(self) -> Optional[PageInfo]: - ... + def next_page_info(self) -> Optional[PageInfo]: ... def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] ... @@ -904,8 +901,7 @@ def request( *, stream: Literal[True], stream_cls: Type[_StreamT], - ) -> _StreamT: - ... + ) -> _StreamT: ... @overload def request( @@ -915,8 +911,7 @@ def request( remaining_retries: Optional[int] = None, *, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload def request( @@ -927,8 +922,7 @@ def request( *, stream: bool = False, stream_cls: Type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - ... + ) -> ResponseT | _StreamT: ... def request( self, @@ -1172,8 +1166,7 @@ def get( cast_to: Type[ResponseT], options: RequestOptions = {}, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload def get( @@ -1184,8 +1177,7 @@ def get( options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_StreamT], - ) -> _StreamT: - ... + ) -> _StreamT: ... @overload def get( @@ -1196,8 +1188,7 @@ def get( options: RequestOptions = {}, stream: bool, stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - ... + ) -> ResponseT | _StreamT: ... def get( self, @@ -1223,8 +1214,7 @@ def post( options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload def post( @@ -1237,8 +1227,7 @@ def post( files: RequestFiles | None = None, stream: Literal[True], stream_cls: type[_StreamT], - ) -> _StreamT: - ... + ) -> _StreamT: ... @overload def post( @@ -1251,8 +1240,7 @@ def post( files: RequestFiles | None = None, stream: bool, stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - ... + ) -> ResponseT | _StreamT: ... def post( self, @@ -1485,8 +1473,7 @@ async def request( *, stream: Literal[False] = False, remaining_retries: Optional[int] = None, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload async def request( @@ -1497,8 +1484,7 @@ async def request( stream: Literal[True], stream_cls: type[_AsyncStreamT], remaining_retries: Optional[int] = None, - ) -> _AsyncStreamT: - ... + ) -> _AsyncStreamT: ... @overload async def request( @@ -1509,8 +1495,7 @@ async def request( stream: bool, stream_cls: type[_AsyncStreamT] | None = None, remaining_retries: Optional[int] = None, - ) -> ResponseT | _AsyncStreamT: - ... + ) -> ResponseT | _AsyncStreamT: ... async def request( self, @@ -1739,8 +1724,7 @@ async def get( cast_to: Type[ResponseT], options: RequestOptions = {}, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload async def get( @@ -1751,8 +1735,7 @@ async def get( options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_AsyncStreamT], - ) -> _AsyncStreamT: - ... + ) -> _AsyncStreamT: ... @overload async def get( @@ -1763,8 +1746,7 @@ async def get( options: RequestOptions = {}, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: - ... + ) -> ResponseT | _AsyncStreamT: ... async def get( self, @@ -1788,8 +1770,7 @@ async def post( files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload async def post( @@ -1802,8 +1783,7 @@ async def post( options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_AsyncStreamT], - ) -> _AsyncStreamT: - ... + ) -> _AsyncStreamT: ... @overload async def post( @@ -1816,8 +1796,7 @@ async def post( options: RequestOptions = {}, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: - ... + ) -> ResponseT | _AsyncStreamT: ... async def post( self, diff --git a/src/openai/_compat.py b/src/openai/_compat.py index c919b5adb3..7c6f91a870 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -159,22 +159,19 @@ def model_parse(model: type[_ModelT], data: Any) -> _ModelT: # generic models if TYPE_CHECKING: - class GenericModel(pydantic.BaseModel): - ... + class GenericModel(pydantic.BaseModel): ... else: if PYDANTIC_V2: # there no longer needs to be a distinction in v2 but # we still have to create our own subclass to avoid # inconsistent MRO ordering errors - class GenericModel(pydantic.BaseModel): - ... + class GenericModel(pydantic.BaseModel): ... else: import pydantic.generics - class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): - ... + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... # cached properties @@ -193,26 +190,21 @@ class typed_cached_property(Generic[_T]): func: Callable[[Any], _T] attrname: str | None - def __init__(self, func: Callable[[Any], _T]) -> None: - ... + def __init__(self, func: Callable[[Any], _T]) -> None: ... @overload - def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: - ... + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... @overload - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: - ... + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: raise NotImplementedError() - def __set_name__(self, owner: type[Any], name: str) -> None: - ... + def __set_name__(self, owner: type[Any], name: str) -> None: ... # __set__ is not defined at runtime, but @cached_property is designed to be settable - def __set__(self, instance: object, value: _T) -> None: - ... + def __set__(self, instance: object, value: _T) -> None: ... else: try: from functools import cached_property as cached_property diff --git a/src/openai/_files.py b/src/openai/_files.py index ad7b668b4b..801a0d2928 100644 --- a/src/openai/_files.py +++ b/src/openai/_files.py @@ -39,13 +39,11 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None: @overload -def to_httpx_files(files: None) -> None: - ... +def to_httpx_files(files: None) -> None: ... @overload -def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: - ... +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: @@ -83,13 +81,11 @@ def _read_file_content(file: FileContent) -> HttpxFileContent: @overload -async def async_to_httpx_files(files: None) -> None: - ... +async def async_to_httpx_files(files: None) -> None: ... @overload -async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: - ... +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 66d7606a60..c42fb8b83e 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -92,12 +92,10 @@ def request_id(self) -> str | None: return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] @overload - def parse(self, *, to: type[_T]) -> _T: - ... + def parse(self, *, to: type[_T]) -> _T: ... @overload - def parse(self) -> R: - ... + def parse(self) -> R: ... def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. diff --git a/src/openai/_response.py b/src/openai/_response.py index 3bf4de4287..f9d91786f6 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -268,12 +268,10 @@ def request_id(self) -> str | None: return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] @overload - def parse(self, *, to: type[_T]) -> _T: - ... + def parse(self, *, to: type[_T]) -> _T: ... @overload - def parse(self) -> R: - ... + def parse(self) -> R: ... def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. @@ -376,12 +374,10 @@ def request_id(self) -> str | None: return self.http_response.headers.get("x-request-id") # type: ignore[no-any-return] @overload - async def parse(self, *, to: type[_T]) -> _T: - ... + async def parse(self, *, to: type[_T]) -> _T: ... @overload - async def parse(self) -> R: - ... + async def parse(self) -> R: ... async def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. diff --git a/src/openai/_types.py b/src/openai/_types.py index de9b1dd48b..5611b2d38f 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -112,8 +112,7 @@ class NotGiven: For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: - ... + def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... get(timeout=1) # 1s timeout @@ -163,16 +162,14 @@ def build( *, response: Response, data: object, - ) -> _T: - ... + ) -> _T: ... Headers = Mapping[str, Union[str, Omit]] class HeadersLikeProtocol(Protocol): - def get(self, __key: str) -> str | None: - ... + def get(self, __key: str) -> str | None: ... HeadersLike = Union[Headers, HeadersLikeProtocol] diff --git a/src/openai/_utils/_proxy.py b/src/openai/_utils/_proxy.py index c46a62a698..ffd883e9dd 100644 --- a/src/openai/_utils/_proxy.py +++ b/src/openai/_utils/_proxy.py @@ -59,5 +59,4 @@ def __as_proxied__(self) -> T: return cast(T, self) @abstractmethod - def __load__(self) -> T: - ... + def __load__(self) -> T: ... diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 34797c2905..2fc5a1c65a 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -211,20 +211,17 @@ def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: Example usage: ```py @overload - def foo(*, a: str) -> str: - ... + def foo(*, a: str) -> str: ... @overload - def foo(*, b: bool) -> str: - ... + 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 foo(*, a: str | None = None, b: bool | None = None) -> str: ... ``` """ @@ -286,18 +283,15 @@ def wrapper(*args: object, **kwargs: object) -> object: @overload -def strip_not_given(obj: None) -> None: - ... +def strip_not_given(obj: None) -> None: ... @overload -def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: - ... +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) -> object: ... def strip_not_given(obj: object | None) -> object: diff --git a/src/openai/cli/_errors.py b/src/openai/cli/_errors.py index 2bf06070d6..7d0292dab2 100644 --- a/src/openai/cli/_errors.py +++ b/src/openai/cli/_errors.py @@ -8,12 +8,10 @@ from .._exceptions import APIError, OpenAIError -class CLIError(OpenAIError): - ... +class CLIError(OpenAIError): ... -class SilentCLIError(CLIError): - ... +class SilentCLIError(CLIError): ... def display_error(err: CLIError | APIError | pydantic.ValidationError) -> None: diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 433486fded..ef64137de4 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -80,8 +80,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, _strict_response_validation: bool = False, - ) -> None: - ... + ) -> None: ... @overload def __init__( @@ -99,8 +98,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, _strict_response_validation: bool = False, - ) -> None: - ... + ) -> None: ... @overload def __init__( @@ -118,8 +116,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, _strict_response_validation: bool = False, - ) -> None: - ... + ) -> None: ... def __init__( self, @@ -321,8 +318,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, _strict_response_validation: bool = False, - ) -> None: - ... + ) -> None: ... @overload def __init__( @@ -341,8 +337,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, _strict_response_validation: bool = False, - ) -> None: - ... + ) -> None: ... @overload def __init__( @@ -361,8 +356,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, _strict_response_validation: bool = False, - ) -> None: - ... + ) -> None: ... def __init__( self, diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py index 8cf65ce94e..86a2adb1a2 100644 --- a/tests/test_deepcopy.py +++ b/tests/test_deepcopy.py @@ -41,8 +41,7 @@ def test_nested_list() -> None: assert_different_identities(obj1[1], obj2[1]) -class MyObject: - ... +class MyObject: ... def test_ignores_other_types() -> None: diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py index 45025f81d0..3659ee12c1 100644 --- a/tests/test_legacy_response.py +++ b/tests/test_legacy_response.py @@ -12,8 +12,7 @@ from openai._legacy_response import LegacyAPIResponse -class PydanticModel(pydantic.BaseModel): - ... +class PydanticModel(pydantic.BaseModel): ... def test_response_parse_mismatched_basemodel(client: OpenAI) -> None: diff --git a/tests/test_response.py b/tests/test_response.py index af153b67c4..6ea1be1a1a 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -19,16 +19,13 @@ from openai._base_client import FinalRequestOptions -class ConcreteBaseAPIResponse(APIResponse[bytes]): - ... +class ConcreteBaseAPIResponse(APIResponse[bytes]): ... -class ConcreteAPIResponse(APIResponse[List[str]]): - ... +class ConcreteAPIResponse(APIResponse[List[str]]): ... -class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): - ... +class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): ... def test_extract_response_type_direct_classes() -> None: @@ -56,8 +53,7 @@ def test_extract_response_type_binary_response() -> None: assert extract_response_type(AsyncBinaryAPIResponse) == bytes -class PydanticModel(pydantic.BaseModel): - ... +class PydanticModel(pydantic.BaseModel): ... def test_response_parse_mismatched_basemodel(client: OpenAI) -> None: diff --git a/tests/test_utils/test_typing.py b/tests/test_utils/test_typing.py index 690960802a..535935b9e1 100644 --- a/tests/test_utils/test_typing.py +++ b/tests/test_utils/test_typing.py @@ -9,24 +9,19 @@ _T3 = TypeVar("_T3") -class BaseGeneric(Generic[_T]): - ... +class BaseGeneric(Generic[_T]): ... -class SubclassGeneric(BaseGeneric[_T]): - ... +class SubclassGeneric(BaseGeneric[_T]): ... -class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): - ... +class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): ... -class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): - ... +class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): ... -class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): - ... +class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): ... def test_extract_type_var() -> None: From 762b9058a89235653dd538cfc03b847f6e5b1907 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:02:40 +0000 Subject: [PATCH 543/914] chore(internal): update pydantic compat helper function (#1607) --- src/openai/_compat.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 7c6f91a870..21fe6941ce 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -7,7 +7,7 @@ import pydantic from pydantic.fields import FieldInfo -from ._types import StrBytesIntFloat +from ._types import IncEx, StrBytesIntFloat _T = TypeVar("_T") _ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) @@ -133,17 +133,20 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: def model_dump( model: pydantic.BaseModel, *, + exclude: IncEx = None, exclude_unset: bool = False, exclude_defaults: bool = False, ) -> dict[str, Any]: if PYDANTIC_V2: return model.model_dump( + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, ) return cast( "dict[str, Any]", model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, ), From bf1ca86cf392eb0ffed1e146937c5d73d8a568f0 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 6 Aug 2024 18:11:32 +0100 Subject: [PATCH 544/914] feat(api): add structured outputs support This commit adds support for JSON schema response format & adds a separate `.beta.chat.completions.parse()` method to automatically deserialise the response content into a Pydantic model. For more details on structured outputs, see this guide https://platform.openai.com/docs/guides/structured-outputs --- .github/workflows/ci.yml | 1 - .inline-snapshot/external/.gitignore | 2 + ...9cda31e5a0af80decdbddd21c056545c6d4616.bin | 100 ++ ...8ab9141d709b770a74dc025fb8770a42aabee9.bin | 180 +++ ...c4861e696495d9a45c19be02cf479e28c31316.bin | 12 + ...ec5f581ea9de2524599f06b0d405db8997b826.bin | 8 + ...0e08ddfad221d6632fdb200a95ca6c996238e2.bin | 52 + ...4b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin | 28 + ...abc40785d712248f65c8595c99879080d0eeb9.bin | 36 + ...5521e0258cc2cef0528a17fbdadb9cc76695f0.bin | 72 ++ ...f05bd963fe093622e5bf9a95a3ebede64714bc.bin | 30 + ...194b58fc759adc3685170e0a61033241d2eda5.bin | 32 + ...ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin | 36 + ...13a82f959a175ec05ce3c07412bbc9fd436234.bin | 22 + .stats.yml | 2 +- api.md | 13 +- examples/parsing.py | 36 + examples/parsing_stream.py | 42 + examples/parsing_tools.py | 80 ++ examples/parsing_tools_stream.py | 38 + helpers.md | 276 ++++- pyproject.toml | 5 +- requirements-dev.lock | 4 +- requirements.lock | 4 +- src/openai/__init__.py | 6 +- src/openai/_client.py | 4 +- src/openai/_compat.py | 12 + src/openai/_exceptions.py | 16 + src/openai/lib/__init__.py | 2 + src/openai/lib/_parsing/__init__.py | 12 + src/openai/lib/_parsing/_completions.py | 254 ++++ src/openai/lib/_pydantic.py | 71 ++ src/openai/lib/_tools.py | 54 + src/openai/lib/streaming/_deltas.py | 64 + src/openai/lib/streaming/chat/__init__.py | 26 + src/openai/lib/streaming/chat/_completions.py | 724 ++++++++++++ src/openai/lib/streaming/chat/_events.py | 123 ++ src/openai/lib/streaming/chat/_types.py | 20 + src/openai/resources/beta/assistants.py | 20 + src/openai/resources/beta/beta.py | 9 + src/openai/resources/beta/chat/__init__.py | 11 + src/openai/resources/beta/chat/chat.py | 21 + src/openai/resources/beta/chat/completions.py | 449 +++++++ .../resources/beta/threads/runs/runs.py | 30 + src/openai/resources/beta/threads/threads.py | 30 + src/openai/resources/chat/completions.py | 16 +- src/openai/resources/fine_tuning/jobs/jobs.py | 12 +- src/openai/types/__init__.py | 3 + src/openai/types/beta/__init__.py | 2 - src/openai/types/beta/assistant.py | 5 + .../types/beta/assistant_create_params.py | 5 + .../types/beta/assistant_response_format.py | 13 - .../beta/assistant_response_format_option.py | 8 +- .../assistant_response_format_option_param.py | 9 +- .../beta/assistant_response_format_param.py | 12 - .../types/beta/assistant_update_params.py | 5 + src/openai/types/beta/file_search_tool.py | 4 +- .../types/beta/file_search_tool_param.py | 4 +- .../beta/thread_create_and_run_params.py | 5 + src/openai/types/beta/threads/__init__.py | 2 + .../types/beta/threads/message_content.py | 5 +- .../beta/threads/message_content_delta.py | 4 +- .../beta/threads/refusal_content_block.py | 14 + .../types/beta/threads/refusal_delta_block.py | 18 + src/openai/types/beta/threads/run.py | 5 + .../types/beta/threads/run_create_params.py | 5 + .../beta/vector_stores/vector_store_file.py | 2 +- src/openai/types/chat/__init__.py | 12 + src/openai/types/chat/chat_completion.py | 3 + ...chat_completion_assistant_message_param.py | 15 +- .../types/chat/chat_completion_chunk.py | 6 + ...t_completion_content_part_refusal_param.py | 15 + .../types/chat/chat_completion_message.py | 3 + .../chat_completion_system_message_param.py | 5 +- .../chat_completion_tool_message_param.py | 5 +- .../types/chat/completion_create_params.py | 9 +- .../types/chat/parsed_chat_completion.py | 40 + .../types/chat/parsed_function_tool_call.py | 29 + src/openai/types/chat_model.py | 1 + .../types/fine_tuning/job_create_params.py | 6 +- src/openai/types/shared/__init__.py | 3 + .../types/shared/function_definition.py | 9 + .../shared/response_format_json_object.py | 12 + .../shared/response_format_json_schema.py | 44 + .../types/shared/response_format_text.py | 12 + src/openai/types/shared_params/__init__.py | 3 + .../shared_params/function_definition.py | 10 + .../response_format_json_object.py | 12 + .../response_format_json_schema.py | 42 + .../shared_params/response_format_text.py | 12 + tests/api_resources/beta/test_assistants.py | 104 +- tests/api_resources/beta/test_threads.py | 16 +- tests/api_resources/beta/threads/test_runs.py | 16 +- tests/api_resources/chat/test_completions.py | 52 +- tests/api_resources/fine_tuning/test_jobs.py | 16 +- tests/api_resources/test_models.py | 24 +- tests/lib/__init__.py | 0 tests/lib/chat/__init__.py | 0 tests/lib/chat/_utils.py | 59 + tests/lib/chat/test_completions.py | 633 ++++++++++ tests/lib/chat/test_completions_streaming.py | 1047 +++++++++++++++++ tests/lib/schema_types/query.py | 51 + tests/lib/test_pydantic.py | 161 +++ tests/test_client.py | 16 +- 104 files changed, 5542 insertions(+), 188 deletions(-) create mode 100644 .inline-snapshot/external/.gitignore create mode 100644 .inline-snapshot/external/1437bd06a9d5c414e56fd0840b9cda31e5a0af80decdbddd21c056545c6d4616.bin create mode 100644 .inline-snapshot/external/3e0df46f250db854eacb34e3258ab9141d709b770a74dc025fb8770a42aabee9.bin create mode 100644 .inline-snapshot/external/70c7df71ce729e178fc5e54f0cc4861e696495d9a45c19be02cf479e28c31316.bin create mode 100644 .inline-snapshot/external/7ae6c1a2631bf7444b8f70b592ec5f581ea9de2524599f06b0d405db8997b826.bin create mode 100644 .inline-snapshot/external/a346213bec7a572810bd1ffe290e08ddfad221d6632fdb200a95ca6c996238e2.bin create mode 100644 .inline-snapshot/external/a7097cae6a1f8dea453977a1784b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin create mode 100644 .inline-snapshot/external/ae070a447e1ded1ad4819f7608abc40785d712248f65c8595c99879080d0eeb9.bin create mode 100644 .inline-snapshot/external/b9d6bee9f9b8ee5bdea06cd6955521e0258cc2cef0528a17fbdadb9cc76695f0.bin create mode 100644 .inline-snapshot/external/cb77dc69b6c8289a6f1e88fa24f05bd963fe093622e5bf9a95a3ebede64714bc.bin create mode 100644 .inline-snapshot/external/d79326933c1586e731a8235998194b58fc759adc3685170e0a61033241d2eda5.bin create mode 100644 .inline-snapshot/external/ea9a417d533b9adfece02608f2ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin create mode 100644 .inline-snapshot/external/fb75060ede89cac360ce8baf1513a82f959a175ec05ce3c07412bbc9fd436234.bin create mode 100644 examples/parsing.py create mode 100644 examples/parsing_stream.py create mode 100644 examples/parsing_tools.py create mode 100644 examples/parsing_tools_stream.py create mode 100644 src/openai/lib/__init__.py create mode 100644 src/openai/lib/_parsing/__init__.py create mode 100644 src/openai/lib/_parsing/_completions.py create mode 100644 src/openai/lib/_pydantic.py create mode 100644 src/openai/lib/_tools.py create mode 100644 src/openai/lib/streaming/_deltas.py create mode 100644 src/openai/lib/streaming/chat/__init__.py create mode 100644 src/openai/lib/streaming/chat/_completions.py create mode 100644 src/openai/lib/streaming/chat/_events.py create mode 100644 src/openai/lib/streaming/chat/_types.py create mode 100644 src/openai/resources/beta/chat/__init__.py create mode 100644 src/openai/resources/beta/chat/chat.py create mode 100644 src/openai/resources/beta/chat/completions.py delete mode 100644 src/openai/types/beta/assistant_response_format.py delete mode 100644 src/openai/types/beta/assistant_response_format_param.py create mode 100644 src/openai/types/beta/threads/refusal_content_block.py create mode 100644 src/openai/types/beta/threads/refusal_delta_block.py create mode 100644 src/openai/types/chat/chat_completion_content_part_refusal_param.py create mode 100644 src/openai/types/chat/parsed_chat_completion.py create mode 100644 src/openai/types/chat/parsed_function_tool_call.py create mode 100644 src/openai/types/shared/response_format_json_object.py create mode 100644 src/openai/types/shared/response_format_json_schema.py create mode 100644 src/openai/types/shared/response_format_text.py create mode 100644 src/openai/types/shared_params/response_format_json_object.py create mode 100644 src/openai/types/shared_params/response_format_json_schema.py create mode 100644 src/openai/types/shared_params/response_format_text.py create mode 100644 tests/lib/__init__.py create mode 100644 tests/lib/chat/__init__.py create mode 100644 tests/lib/chat/_utils.py create mode 100644 tests/lib/chat/test_completions.py create mode 100644 tests/lib/chat/test_completions_streaming.py create mode 100644 tests/lib/schema_types/query.py create mode 100644 tests/lib/test_pydantic.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e58412065..de70348b9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,4 +50,3 @@ jobs: - name: Run tests run: ./scripts/test - diff --git a/.inline-snapshot/external/.gitignore b/.inline-snapshot/external/.gitignore new file mode 100644 index 0000000000..45bef68be1 --- /dev/null +++ b/.inline-snapshot/external/.gitignore @@ -0,0 +1,2 @@ +# ignore all snapshots which are not refered in the source +*-new.* diff --git a/.inline-snapshot/external/1437bd06a9d5c414e56fd0840b9cda31e5a0af80decdbddd21c056545c6d4616.bin b/.inline-snapshot/external/1437bd06a9d5c414e56fd0840b9cda31e5a0af80decdbddd21c056545c6d4616.bin new file mode 100644 index 0000000000..f96745e385 --- /dev/null +++ b/.inline-snapshot/external/1437bd06a9d5c414e56fd0840b9cda31e5a0af80decdbddd21c056545c6d4616.bin @@ -0,0 +1,100 @@ +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"64"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"68"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"64"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":42,"total_tokens":59}} + +data: [DONE] + diff --git a/.inline-snapshot/external/3e0df46f250db854eacb34e3258ab9141d709b770a74dc025fb8770a42aabee9.bin b/.inline-snapshot/external/3e0df46f250db854eacb34e3258ab9141d709b770a74dc025fb8770a42aabee9.bin new file mode 100644 index 0000000000..eb1cf9e733 --- /dev/null +++ b/.inline-snapshot/external/3e0df46f250db854eacb34e3258ab9141d709b770a74dc025fb8770a42aabee9.bin @@ -0,0 +1,180 @@ +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"{\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"location"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" CA"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"conditions"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"humidity"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"wind"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"_speed"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"timestamp"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"note"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"Real"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" not"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" available"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Please"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" service"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" up"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"-to"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"-date"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" on"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"'s"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" conditions"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":".\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":19,"completion_tokens":86,"total_tokens":105}} + +data: [DONE] + diff --git a/.inline-snapshot/external/70c7df71ce729e178fc5e54f0cc4861e696495d9a45c19be02cf479e28c31316.bin b/.inline-snapshot/external/70c7df71ce729e178fc5e54f0cc4861e696495d9a45c19be02cf479e28c31316.bin new file mode 100644 index 0000000000..21c41d3958 --- /dev/null +++ b/.inline-snapshot/external/70c7df71ce729e178fc5e54f0cc4861e696495d9a45c19be02cf479e28c31316.bin @@ -0,0 +1,12 @@ +data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":{"content":[],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"Foo"},"logprobs":{"content":[{"token":"Foo","logprob":-0.006764991,"bytes":[70,111,111],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"!"},"logprobs":{"content":[{"token":"!","logprob":-0.31380808,"bytes":[33],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":9,"completion_tokens":2,"total_tokens":11}} + +data: [DONE] + diff --git a/.inline-snapshot/external/7ae6c1a2631bf7444b8f70b592ec5f581ea9de2524599f06b0d405db8997b826.bin b/.inline-snapshot/external/7ae6c1a2631bf7444b8f70b592ec5f581ea9de2524599f06b0d405db8997b826.bin new file mode 100644 index 0000000000..d261ccd0d0 --- /dev/null +++ b/.inline-snapshot/external/7ae6c1a2631bf7444b8f70b592ec5f581ea9de2524599f06b0d405db8997b826.bin @@ -0,0 +1,8 @@ +data: {"id":"chatcmpl-9tAC6v0rUCOp8tty9cizBsGmRcVIx","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6v0rUCOp8tty9cizBsGmRcVIx","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"length"}]} + +data: {"id":"chatcmpl-9tAC6v0rUCOp8tty9cizBsGmRcVIx","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":1,"total_tokens":18}} + +data: [DONE] + diff --git a/.inline-snapshot/external/a346213bec7a572810bd1ffe290e08ddfad221d6632fdb200a95ca6c996238e2.bin b/.inline-snapshot/external/a346213bec7a572810bd1ffe290e08ddfad221d6632fdb200a95ca6c996238e2.bin new file mode 100644 index 0000000000..2ceced2f1c --- /dev/null +++ b/.inline-snapshot/external/a346213bec7a572810bd1ffe290e08ddfad221d6632fdb200a95ca6c996238e2.bin @@ -0,0 +1,52 @@ +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_g4Q1vRbE0CaHGOs5if8mHsBq","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"ci"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ty\": "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Edinb"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"urgh"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\", \"c"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ountry"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"UK\", "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"units"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_gWj3HQxZEHnFvyJLEHIiJKBV","type":"function","function":{"name":"get_stock_price","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"ti"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"cker\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":": \"AAP"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"L\", "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\"exch"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ange\":"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":" \"NA"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"SDAQ\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":149,"completion_tokens":60,"total_tokens":209}} + +data: [DONE] + diff --git a/.inline-snapshot/external/a7097cae6a1f8dea453977a1784b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin b/.inline-snapshot/external/a7097cae6a1f8dea453977a1784b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin new file mode 100644 index 0000000000..de0efe6bab --- /dev/null +++ b/.inline-snapshot/external/a7097cae6a1f8dea453977a1784b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin @@ -0,0 +1,28 @@ +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_rQe3kzGnTr2epjx8HREg3F2a","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"San"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Francisco"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"state"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"CA"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":48,"completion_tokens":19,"total_tokens":67}} + +data: [DONE] + diff --git a/.inline-snapshot/external/ae070a447e1ded1ad4819f7608abc40785d712248f65c8595c99879080d0eeb9.bin b/.inline-snapshot/external/ae070a447e1ded1ad4819f7608abc40785d712248f65c8595c99879080d0eeb9.bin new file mode 100644 index 0000000000..af003a8120 --- /dev/null +++ b/.inline-snapshot/external/ae070a447e1ded1ad4819f7608abc40785d712248f65c8595c99879080d0eeb9.bin @@ -0,0 +1,36 @@ +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_Vz6ZXciy6Y0PYfT4d9W7fYB4","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ed"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"inburgh"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"country"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"UK"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"units"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":76,"completion_tokens":24,"total_tokens":100}} + +data: [DONE] + diff --git a/.inline-snapshot/external/b9d6bee9f9b8ee5bdea06cd6955521e0258cc2cef0528a17fbdadb9cc76695f0.bin b/.inline-snapshot/external/b9d6bee9f9b8ee5bdea06cd6955521e0258cc2cef0528a17fbdadb9cc76695f0.bin new file mode 100644 index 0000000000..b4337f886a --- /dev/null +++ b/.inline-snapshot/external/b9d6bee9f9b8ee5bdea06cd6955521e0258cc2cef0528a17fbdadb9cc76695f0.bin @@ -0,0 +1,72 @@ +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" To"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" get"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" latest"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" recommend"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" checking"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" using"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":32,"total_tokens":46}} + +data: [DONE] + diff --git a/.inline-snapshot/external/cb77dc69b6c8289a6f1e88fa24f05bd963fe093622e5bf9a95a3ebede64714bc.bin b/.inline-snapshot/external/cb77dc69b6c8289a6f1e88fa24f05bd963fe093622e5bf9a95a3ebede64714bc.bin new file mode 100644 index 0000000000..a95f28a54b --- /dev/null +++ b/.inline-snapshot/external/cb77dc69b6c8289a6f1e88fa24f05bd963fe093622e5bf9a95a3ebede64714bc.bin @@ -0,0 +1,30 @@ +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":{"content":null,"refusal":[]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":{"content":null,"refusal":[{"token":"I'm","logprob":-0.0010472201,"bytes":[73,39,109],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" very"},"logprobs":{"content":null,"refusal":[{"token":" very","logprob":-0.7292482,"bytes":[32,118,101,114,121],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":{"content":null,"refusal":[{"token":" sorry","logprob":-5.080963e-6,"bytes":[32,115,111,114,114,121],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":","},"logprobs":{"content":null,"refusal":[{"token":",","logprob":-0.00004048445,"bytes":[44],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" but"},"logprobs":{"content":null,"refusal":[{"token":" but","logprob":-0.038046427,"bytes":[32,98,117,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":{"content":null,"refusal":[{"token":" I","logprob":-0.0019351852,"bytes":[32,73],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" can't"},"logprobs":{"content":null,"refusal":[{"token":" can't","logprob":-0.008995773,"bytes":[32,99,97,110,39,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":{"content":null,"refusal":[{"token":" assist","logprob":-0.0033510819,"bytes":[32,97,115,115,105,115,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":{"content":null,"refusal":[{"token":" with","logprob":-0.0036033941,"bytes":[32,119,105,116,104],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":{"content":null,"refusal":[{"token":" that","logprob":-0.0015974608,"bytes":[32,116,104,97,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":{"content":null,"refusal":[{"token":".","logprob":-0.6339823,"bytes":[46],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":12,"total_tokens":29}} + +data: [DONE] + diff --git a/.inline-snapshot/external/d79326933c1586e731a8235998194b58fc759adc3685170e0a61033241d2eda5.bin b/.inline-snapshot/external/d79326933c1586e731a8235998194b58fc759adc3685170e0a61033241d2eda5.bin new file mode 100644 index 0000000000..895e4828ef --- /dev/null +++ b/.inline-snapshot/external/d79326933c1586e731a8235998194b58fc759adc3685170e0a61033241d2eda5.bin @@ -0,0 +1,32 @@ +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" very"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" but"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" can't"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":13,"total_tokens":30}} + +data: [DONE] + diff --git a/.inline-snapshot/external/ea9a417d533b9adfece02608f2ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin b/.inline-snapshot/external/ea9a417d533b9adfece02608f2ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin new file mode 100644 index 0000000000..869b94de1a --- /dev/null +++ b/.inline-snapshot/external/ea9a417d533b9adfece02608f2ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin @@ -0,0 +1,36 @@ +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"63"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":14,"total_tokens":31}} + +data: [DONE] + diff --git a/.inline-snapshot/external/fb75060ede89cac360ce8baf1513a82f959a175ec05ce3c07412bbc9fd436234.bin b/.inline-snapshot/external/fb75060ede89cac360ce8baf1513a82f959a175ec05ce3c07412bbc9fd436234.bin new file mode 100644 index 0000000000..970b1adf80 --- /dev/null +++ b/.inline-snapshot/external/fb75060ede89cac360ce8baf1513a82f959a175ec05ce3c07412bbc9fd436234.bin @@ -0,0 +1,22 @@ +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_9rqjEc1DQRADTYGVV45LbZwL","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"New"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" York"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":44,"completion_tokens":16,"total_tokens":60}} + +data: [DONE] + diff --git a/.stats.yml b/.stats.yml index 6cc7757636..da26758316 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-b04761ffd2adad3cc19a6dc6fc696ac445878219972f891881a967340fa9a6b0.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-c36d30a94622922f83d56a025cdf0095ff7cb18a5138838c698c8443f21fb3a8.yml diff --git a/api.md b/api.md index 85e81467dc..1687476d86 100644 --- a/api.md +++ b/api.md @@ -1,7 +1,14 @@ # Shared Types ```python -from openai.types import ErrorObject, FunctionDefinition, FunctionParameters +from openai.types import ( + ErrorObject, + FunctionDefinition, + FunctionParameters, + ResponseFormatJSONObject, + ResponseFormatJSONSchema, + ResponseFormatText, +) ``` # Completions @@ -35,6 +42,7 @@ from openai.types.chat import ( ChatCompletionChunk, ChatCompletionContentPart, ChatCompletionContentPartImage, + ChatCompletionContentPartRefusal, ChatCompletionContentPartText, ChatCompletionFunctionCallOption, ChatCompletionFunctionMessageParam, @@ -296,7 +304,6 @@ Types: ```python from openai.types.beta import ( - AssistantResponseFormat, AssistantResponseFormatOption, AssistantToolChoice, AssistantToolChoiceFunction, @@ -397,6 +404,8 @@ from openai.types.beta.threads import ( MessageDeleted, MessageDelta, MessageDeltaEvent, + RefusalContentBlock, + RefusalDeltaBlock, Text, TextContentBlock, TextContentBlockParam, diff --git a/examples/parsing.py b/examples/parsing.py new file mode 100644 index 0000000000..17e5db52ec --- /dev/null +++ b/examples/parsing.py @@ -0,0 +1,36 @@ +from typing import List + +import rich +from pydantic import BaseModel + +from openai import OpenAI + + +class Step(BaseModel): + explanation: str + output: str + + +class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + + +client = OpenAI() + +completion = client.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "system", "content": "You are a helpful math tutor."}, + {"role": "user", "content": "solve 8x + 31 = 2"}, + ], + response_format=MathResponse, +) + +message = completion.choices[0].message +if message.parsed: + rich.print(message.parsed.steps) + + print("answer: ", message.parsed.final_answer) +else: + print(message.refusal) diff --git a/examples/parsing_stream.py b/examples/parsing_stream.py new file mode 100644 index 0000000000..6c6f078f77 --- /dev/null +++ b/examples/parsing_stream.py @@ -0,0 +1,42 @@ +from typing import List + +import rich +from pydantic import BaseModel + +from openai import OpenAI + + +class Step(BaseModel): + explanation: str + output: str + + +class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + + +client = OpenAI() + +with client.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "system", "content": "You are a helpful math tutor."}, + {"role": "user", "content": "solve 8x + 31 = 2"}, + ], + response_format=MathResponse, +) as stream: + for event in stream: + if event.type == "content.delta": + print(event.delta, end="", flush=True) + elif event.type == "content.done": + print("\n") + if event.parsed is not None: + print(f"answer: {event.parsed.final_answer}") + elif event.type == "refusal.delta": + print(event.delta, end="", flush=True) + elif event.type == "refusal.done": + print() + +print("---------------") +rich.print(stream.get_final_completion()) diff --git a/examples/parsing_tools.py b/examples/parsing_tools.py new file mode 100644 index 0000000000..c6065eeb7a --- /dev/null +++ b/examples/parsing_tools.py @@ -0,0 +1,80 @@ +from enum import Enum +from typing import List, Union + +import rich +from pydantic import BaseModel + +import openai +from openai import OpenAI + + +class Table(str, Enum): + orders = "orders" + customers = "customers" + products = "products" + + +class Column(str, Enum): + id = "id" + status = "status" + expected_delivery_date = "expected_delivery_date" + delivered_at = "delivered_at" + shipped_at = "shipped_at" + ordered_at = "ordered_at" + canceled_at = "canceled_at" + + +class Operator(str, Enum): + eq = "=" + gt = ">" + lt = "<" + le = "<=" + ge = ">=" + ne = "!=" + + +class OrderBy(str, Enum): + asc = "asc" + desc = "desc" + + +class DynamicValue(BaseModel): + column_name: str + + +class Condition(BaseModel): + column: str + operator: Operator + value: Union[str, int, DynamicValue] + + +class Query(BaseModel): + table_name: Table + columns: List[Column] + conditions: List[Condition] + order_by: OrderBy + + +client = OpenAI() + +completion = client.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "system", + "content": "You are a helpful assistant. The current date is August 6, 2024. You help users query for the data they are looking for by calling the query function.", + }, + { + "role": "user", + "content": "look up all my orders in november of last year that were fulfilled but not delivered on time", + }, + ], + tools=[ + openai.pydantic_function_tool(Query), + ], +) + +tool_call = (completion.choices[0].message.tool_calls or [])[0] +rich.print(tool_call.function) +assert isinstance(tool_call.function.parsed_arguments, Query) +print(tool_call.function.parsed_arguments.table_name) diff --git a/examples/parsing_tools_stream.py b/examples/parsing_tools_stream.py new file mode 100644 index 0000000000..eea6f6a43a --- /dev/null +++ b/examples/parsing_tools_stream.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import rich +from pydantic import BaseModel + +import openai +from openai import OpenAI + + +class GetWeather(BaseModel): + city: str + country: str + + +client = OpenAI() + + +with client.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF and New York?", + }, + ], + tools=[ + # because we're using `.parse_stream()`, the returned tool calls + # will be automatically deserialized into this `GetWeather` type + openai.pydantic_function_tool(GetWeather, name="get_weather"), + ], + parallel_tool_calls=True, +) as stream: + for event in stream: + if event.type == "tool_calls.function.arguments.delta" or event.type == "tool_calls.function.arguments.done": + rich.get_console().print(event, width=80) + +print("----\n") +rich.print(stream.get_final_completion()) diff --git a/helpers.md b/helpers.md index 3508b59a33..2e0d314b50 100644 --- a/helpers.md +++ b/helpers.md @@ -1,6 +1,280 @@ +# Structured Outputs Parsing Helpers + +The OpenAI API supports extracting JSON from the model with the `response_format` request param, for more details on the API, see [this guide](https://platform.openai.com/docs/guides/structured-outputs). + +The SDK provides a `client.beta.chat.completions.parse()` method which is a wrapper over the `client.chat.completions.create()` that +provides richer integrations with Python specific types & returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class. + +## Auto-parsing response content with Pydantic models + +You can pass a pydantic model to the `.parse()` method and the SDK will automatically convert the model +into a JSON schema, send it to the API and parse the response content back into the given model. + +```py +from typing import List +from pydantic import BaseModel +from openai import OpenAI + +class Step(BaseModel): + explanation: str + output: str + +class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + +client = OpenAI() +completion = client.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "system", "content": "You are a helpful math tutor."}, + {"role": "user", "content": "solve 8x + 31 = 2"}, + ], + response_format=MathResponse, +) + +message = completion.choices[0].message +if message.parsed: + print(message.parsed.steps) + print("answer: ", message.parsed.final_answer) +else: + print(message.refusal) +``` + +## Auto-parsing function tool calls + +The `.parse()` method will also automatically parse `function` tool calls if: +- You use the `openai.pydantic_function_tool()` helper method +- You mark your tool schema with `"strict": True` + +For example: + +```py +from enum import Enum +from typing import List, Union +from pydantic import BaseModel +import openai + +class Table(str, Enum): + orders = "orders" + customers = "customers" + products = "products" + +class Column(str, Enum): + id = "id" + status = "status" + expected_delivery_date = "expected_delivery_date" + delivered_at = "delivered_at" + shipped_at = "shipped_at" + ordered_at = "ordered_at" + canceled_at = "canceled_at" + +class Operator(str, Enum): + eq = "=" + gt = ">" + lt = "<" + le = "<=" + ge = ">=" + ne = "!=" + +class OrderBy(str, Enum): + asc = "asc" + desc = "desc" + +class DynamicValue(BaseModel): + column_name: str + +class Condition(BaseModel): + column: str + operator: Operator + value: Union[str, int, DynamicValue] + +class Query(BaseModel): + table_name: Table + columns: List[Column] + conditions: List[Condition] + order_by: OrderBy + +client = openai.OpenAI() +completion = client.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "system", + "content": "You are a helpful assistant. The current date is August 6, 2024. You help users query for the data they are looking for by calling the query function.", + }, + { + "role": "user", + "content": "look up all my orders in may of last year that were fulfilled but not delivered on time", + }, + ], + tools=[ + openai.pydantic_function_tool(Query), + ], +) + +tool_call = (completion.choices[0].message.tool_calls or [])[0] +print(tool_call.function) +assert isinstance(tool_call.function.parsed_arguments, Query) +print(tool_call.function.parsed_arguments.table_name) +``` + +### Differences from `.create()` + +The `beta.chat.completions.parse()` method imposes some additional restrictions on it's usage that `chat.completions.create()` does not. + +- If the completion completes with `finish_reason` set to `length` or `content_filter`, the `LengthFinishReasonError` / `ContentFilterFinishReasonError` errors will be raised. +- Only strict function tools can be passed, e.g. `{'type': 'function', 'function': {..., 'strict': True}}` + # Streaming Helpers -OpenAI supports streaming responses when interacting with the [Assistant](#assistant-streaming-api) APIs. +OpenAI supports streaming responses when interacting with the [Chat Completion] & [Assistant](#assistant-streaming-api) APIs. + +## Chat Completions API + +The SDK provides a `.beta.chat.completions.stream()` method that wraps the `.chat.completions.create(stream=True)` stream providing a more granular event API & automatic accumulation of each delta. + +It also supports all aforementioned [parsing helpers](#parsing-helpers). + +Unlike `.create(stream=True)`, the `.stream()` method requires usage within a context manager to prevent accidental leakage of the response: + +```py +async with client.beta.chat.completions.stream( + model='gpt-4o-2024-08-06', + messages=[...], +) as stream: + async for event in stream: + if event.type == 'content.delta': + print(event.content, flush=True, end='') +``` + +When the context manager is entered, a `ChatCompletionStream` / `AsyncChatCompletionStream` instance is returned which, like `.create(stream=True)` is an iterator in the sync client and an async iterator in the async client. The full list of events that are yielded by the iterator are outlined [below](#chat-completions-events). + +When the context manager exits, the response will be closed, however the `stream` instance is still available outside +the context manager. + +### Chat Completions Events + +These events allow you to track the progress of the chat completion generation, access partial results, and handle different aspects of the stream separately. + +Below is a list of the different event types you may encounter: + +#### ChunkEvent + +Emitted for every chunk received from the API. + +- `type`: `"chunk"` +- `chunk`: The raw `ChatCompletionChunk` object received from the API +- `snapshot`: The current accumulated state of the chat completion + +#### ContentDeltaEvent + +Emitted for every chunk containing new content. + +- `type`: `"content.delta"` +- `delta`: The new content string received in this chunk +- `snapshot`: The accumulated content so far +- `parsed`: The partially parsed content (if applicable) + +#### ContentDoneEvent + +Emitted when the content generation is complete. May be fired multiple times if there are multiple choices. + +- `type`: `"content.done"` +- `content`: The full generated content +- `parsed`: The fully parsed content (if applicable) + +#### RefusalDeltaEvent + +Emitted when a chunk contains part of a content refusal. + +- `type`: `"refusal.delta"` +- `delta`: The new refusal content string received in this chunk +- `snapshot`: The accumulated refusal content string so far + +#### RefusalDoneEvent + +Emitted when the refusal content is complete. + +- `type`: `"refusal.done"` +- `refusal`: The full refusal content + +#### FunctionToolCallArgumentsDeltaEvent + +Emitted when a chunk contains part of a function tool call's arguments. + +- `type`: `"tool_calls.function.arguments.delta"` +- `name`: The name of the function being called +- `index`: The index of the tool call +- `arguments`: The accumulated raw JSON string of arguments +- `parsed_arguments`: The partially parsed arguments object +- `arguments_delta`: The new JSON string fragment received in this chunk + +#### FunctionToolCallArgumentsDoneEvent + +Emitted when a function tool call's arguments are complete. + +- `type`: `"tool_calls.function.arguments.done"` +- `name`: The name of the function being called +- `index`: The index of the tool call +- `arguments`: The full raw JSON string of arguments +- `parsed_arguments`: The fully parsed arguments object. If you used `openai.pydantic_function_tool()` this will be an instance of the given model. + +#### LogprobsContentDeltaEvent + +Emitted when a chunk contains new content [log probabilities](https://cookbook.openai.com/examples/using_logprobs). + +- `type`: `"logprobs.content.delta"` +- `content`: A list of the new log probabilities received in this chunk +- `snapshot`: A list of the accumulated log probabilities so far + +#### LogprobsContentDoneEvent + +Emitted when all content [log probabilities](https://cookbook.openai.com/examples/using_logprobs) have been received. + +- `type`: `"logprobs.content.done"` +- `content`: The full list of token log probabilities for the content + +#### LogprobsRefusalDeltaEvent + +Emitted when a chunk contains new refusal [log probabilities](https://cookbook.openai.com/examples/using_logprobs). + +- `type`: `"logprobs.refusal.delta"` +- `refusal`: A list of the new log probabilities received in this chunk +- `snapshot`: A list of the accumulated log probabilities so far + +#### LogprobsRefusalDoneEvent + +Emitted when all refusal [log probabilities](https://cookbook.openai.com/examples/using_logprobs) have been received. + +- `type`: `"logprobs.refusal.done"` +- `refusal`: The full list of token log probabilities for the refusal + +### Chat Completions stream methods + +A handful of helper methods are provided on the stream class for additional convenience, + +**`.get_final_completion()`** + +Returns the accumulated `ParsedChatCompletion` object + +```py +async with client.beta.chat.completions.stream(...) as stream: + ... + +completion = await stream.get_final_completion() +print(completion.choices[0].message) +``` + +**`.until_done()`** + +If you want to wait for the stream to complete, you can use the `.until_done()` method. + +```py +async with client.beta.chat.completions.stream(...) as stream: + await stream.until_done() + # stream is now finished +``` ## Assistant Streaming API diff --git a/pyproject.toml b/pyproject.toml index 99e0fc6591..cb02edac0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,12 +10,13 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.7, <5", + "typing-extensions>=4.11, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", "cached-property; python_version < '3.8'", - "tqdm > 4" + "tqdm > 4", + "jiter>=0.4.0, <1", ] requires-python = ">= 3.7.1" classifiers = [ diff --git a/requirements-dev.lock b/requirements-dev.lock index d2ad945343..f4797f432b 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -72,6 +72,8 @@ importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest inline-snapshot==0.10.2 +jiter==0.5.0 + # via openai markdown-it-py==3.0.0 # via rich mdurl==0.1.2 @@ -169,7 +171,7 @@ types-pytz==2024.1.0.20240417 types-toml==0.10.8.20240310 # via inline-snapshot types-tqdm==4.66.0.2 -typing-extensions==4.8.0 +typing-extensions==4.12.2 # via azure-core # via black # via mypy diff --git a/requirements.lock b/requirements.lock index 3c3d6ae702..de632aefbd 100644 --- a/requirements.lock +++ b/requirements.lock @@ -30,6 +30,8 @@ httpx==0.25.2 idna==3.4 # via anyio # via httpx +jiter==0.5.0 + # via openai numpy==1.26.4 # via openai # via pandas @@ -56,7 +58,7 @@ tqdm==4.66.1 # via openai types-pytz==2024.1.0.20240417 # via pandas-stubs -typing-extensions==4.8.0 +typing-extensions==4.12.2 # via openai # via pydantic # via pydantic-core diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 0e87ae9259..3c1ebb573d 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -26,8 +26,10 @@ AuthenticationError, InternalServerError, PermissionDeniedError, + LengthFinishReasonError, UnprocessableEntityError, APIResponseValidationError, + ContentFilterFinishReasonError, ) from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging @@ -55,6 +57,8 @@ "UnprocessableEntityError", "RateLimitError", "InternalServerError", + "LengthFinishReasonError", + "ContentFilterFinishReasonError", "Timeout", "RequestOptions", "Client", @@ -72,7 +76,7 @@ "DefaultAsyncHttpxClient", ] -from .lib import azure as _azure +from .lib import azure as _azure, pydantic_function_tool as pydantic_function_tool from .version import VERSION as VERSION from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI from .lib._old_api import * diff --git a/src/openai/_client.py b/src/openai/_client.py index 8b404e234d..d3ee6cf0f1 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -151,7 +151,7 @@ def __init__( @property @override def qs(self) -> Querystring: - return Querystring(array_format="comma") + return Querystring(array_format="brackets") @property @override @@ -365,7 +365,7 @@ def __init__( @property @override def qs(self) -> Querystring: - return Querystring(array_format="comma") + return Querystring(array_format="brackets") @property @override diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 21fe6941ce..c0dd8c1ee5 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -159,6 +159,18 @@ def model_parse(model: type[_ModelT], data: Any) -> _ModelT: return model.parse_obj(data) # pyright: ignore[reportDeprecated] +def model_parse_json(model: type[_ModelT], data: str | bytes) -> _ModelT: + if PYDANTIC_V2: + return model.model_validate_json(data) + return model.parse_raw(data) # pyright: ignore[reportDeprecated] + + +def model_json_schema(model: type[_ModelT]) -> dict[str, Any]: + if PYDANTIC_V2: + return model.model_json_schema() + return model.schema() # pyright: ignore[reportDeprecated] + + # generic models if TYPE_CHECKING: diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index f6731cfac5..f44f90b52f 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -19,6 +19,8 @@ "UnprocessableEntityError", "RateLimitError", "InternalServerError", + "LengthFinishReasonError", + "ContentFilterFinishReasonError", ] @@ -125,3 +127,17 @@ class RateLimitError(APIStatusError): class InternalServerError(APIStatusError): pass + + +class LengthFinishReasonError(OpenAIError): + def __init__(self) -> None: + super().__init__( + f"Could not parse response content as the length limit was reached", + ) + + +class ContentFilterFinishReasonError(OpenAIError): + def __init__(self) -> None: + super().__init__( + f"Could not parse response content as the request was rejected by the content filter", + ) diff --git a/src/openai/lib/__init__.py b/src/openai/lib/__init__.py new file mode 100644 index 0000000000..5c6cb782c0 --- /dev/null +++ b/src/openai/lib/__init__.py @@ -0,0 +1,2 @@ +from ._tools import pydantic_function_tool as pydantic_function_tool +from ._parsing import ResponseFormatT as ResponseFormatT diff --git a/src/openai/lib/_parsing/__init__.py b/src/openai/lib/_parsing/__init__.py new file mode 100644 index 0000000000..4d454c3a20 --- /dev/null +++ b/src/openai/lib/_parsing/__init__.py @@ -0,0 +1,12 @@ +from ._completions import ( + ResponseFormatT as ResponseFormatT, + has_parseable_input, + has_parseable_input as has_parseable_input, + maybe_parse_content as maybe_parse_content, + validate_input_tools as validate_input_tools, + parse_chat_completion as parse_chat_completion, + get_input_tool_by_name as get_input_tool_by_name, + solve_response_format_t as solve_response_format_t, + parse_function_tool_arguments as parse_function_tool_arguments, + type_to_response_format_param as type_to_response_format_param, +) diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py new file mode 100644 index 0000000000..f9d1d6b351 --- /dev/null +++ b/src/openai/lib/_parsing/_completions.py @@ -0,0 +1,254 @@ +from __future__ import annotations + +import json +from typing import TYPE_CHECKING, Any, Iterable, cast +from typing_extensions import TypeVar, TypeGuard, assert_never + +import pydantic + +from .._tools import PydanticFunctionTool +from ..._types import NOT_GIVEN, NotGiven +from ..._utils import is_dict, is_given +from ..._compat import model_parse_json +from ..._models import construct_type_unchecked +from .._pydantic import to_strict_json_schema +from ...types.chat import ( + ParsedChoice, + ChatCompletion, + ParsedFunction, + ParsedChatCompletion, + ChatCompletionMessage, + ParsedFunctionToolCall, + ChatCompletionToolParam, + ParsedChatCompletionMessage, + completion_create_params, +) +from ..._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError +from ...types.shared_params import FunctionDefinition +from ...types.chat.completion_create_params import ResponseFormat as ResponseFormatParam +from ...types.chat.chat_completion_message_tool_call import Function + +ResponseFormatT = TypeVar( + "ResponseFormatT", + # if it isn't given then we don't do any parsing + default=None, +) +_default_response_format: None = None + + +def validate_input_tools( + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, +) -> None: + if not is_given(tools): + return + + for tool in tools: + if tool["type"] != "function": + raise ValueError( + f'Currently only `function` tool types support auto-parsing; Received `{tool["type"]}`', + ) + + strict = tool["function"].get("strict") + if strict is not True: + raise ValueError( + f'`{tool["function"]["name"]}` is not strict. Only `strict` function tools can be auto-parsed' + ) + + +def parse_chat_completion( + *, + response_format: type[ResponseFormatT] | completion_create_params.ResponseFormat | NotGiven, + input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + chat_completion: ChatCompletion | ParsedChatCompletion[object], +) -> ParsedChatCompletion[ResponseFormatT]: + if is_given(input_tools): + input_tools = [t for t in input_tools] + else: + input_tools = [] + + choices: list[ParsedChoice[ResponseFormatT]] = [] + for choice in chat_completion.choices: + if choice.finish_reason == "length": + raise LengthFinishReasonError() + + if choice.finish_reason == "content_filter": + raise ContentFilterFinishReasonError() + + message = choice.message + + tool_calls: list[ParsedFunctionToolCall] = [] + if message.tool_calls: + for tool_call in message.tool_calls: + if tool_call.type == "function": + tool_call_dict = tool_call.to_dict() + tool_calls.append( + construct_type_unchecked( + value={ + **tool_call_dict, + "function": { + **cast(Any, tool_call_dict["function"]), + "parsed_arguments": parse_function_tool_arguments( + input_tools=input_tools, function=tool_call.function + ), + }, + }, + type_=ParsedFunctionToolCall, + ) + ) + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(tool_call) + else: + tool_calls.append(tool_call) + + choices.append( + construct_type_unchecked( + type_=cast(Any, ParsedChoice)[solve_response_format_t(response_format)], + value={ + **choice.to_dict(), + "message": { + **message.to_dict(), + "parsed": maybe_parse_content( + response_format=response_format, + message=message, + ), + "tool_calls": tool_calls, + }, + }, + ) + ) + + return cast( + ParsedChatCompletion[ResponseFormatT], + construct_type_unchecked( + type_=cast(Any, ParsedChatCompletion)[solve_response_format_t(response_format)], + value={ + **chat_completion.to_dict(), + "choices": choices, + }, + ), + ) + + +def get_input_tool_by_name(*, input_tools: list[ChatCompletionToolParam], name: str) -> ChatCompletionToolParam | None: + return next((t for t in input_tools if t.get("function", {}).get("name") == name), None) + + +def parse_function_tool_arguments( + *, input_tools: list[ChatCompletionToolParam], function: Function | ParsedFunction +) -> object: + input_tool = get_input_tool_by_name(input_tools=input_tools, name=function.name) + if not input_tool: + return None + + input_fn = cast(object, input_tool.get("function")) + if isinstance(input_fn, PydanticFunctionTool): + return model_parse_json(input_fn.model, function.arguments) + + input_fn = cast(FunctionDefinition, input_fn) + + if not input_fn.get("strict"): + return None + + return json.loads(function.arguments) + + +def maybe_parse_content( + *, + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + message: ChatCompletionMessage | ParsedChatCompletionMessage[object], +) -> ResponseFormatT | None: + if has_rich_response_format(response_format) and message.content is not None and not message.refusal: + return _parse_content(response_format, message.content) + + return None + + +def solve_response_format_t( + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, +) -> type[ResponseFormatT]: + """Return the runtime type for the given response format. + + If no response format is given, or if we won't auto-parse the response format + then we default to `None`. + """ + if has_rich_response_format(response_format): + return response_format + + return cast("type[ResponseFormatT]", _default_response_format) + + +def has_parseable_input( + *, + response_format: type | ResponseFormatParam | NotGiven, + input_tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, +) -> bool: + if has_rich_response_format(response_format): + return True + + for input_tool in input_tools or []: + if is_parseable_tool(input_tool): + return True + + return False + + +def has_rich_response_format( + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, +) -> TypeGuard[type[ResponseFormatT]]: + if not is_given(response_format): + return False + + if is_response_format_param(response_format): + return False + + return True + + +def is_response_format_param(response_format: object) -> TypeGuard[ResponseFormatParam]: + return is_dict(response_format) + + +def is_parseable_tool(input_tool: ChatCompletionToolParam) -> bool: + input_fn = cast(object, input_tool.get("function")) + if isinstance(input_fn, PydanticFunctionTool): + return True + + return cast(FunctionDefinition, input_fn).get("strict") or False + + +def is_basemodel_type(typ: type) -> TypeGuard[type[pydantic.BaseModel]]: + return issubclass(typ, pydantic.BaseModel) + + +def _parse_content(response_format: type[ResponseFormatT], content: str) -> ResponseFormatT: + if is_basemodel_type(response_format): + return cast(ResponseFormatT, model_parse_json(response_format, content)) + + raise TypeError(f"Unable to automatically parse response format type {response_format}") + + +def type_to_response_format_param( + response_format: type | completion_create_params.ResponseFormat | NotGiven, +) -> ResponseFormatParam | NotGiven: + if not is_given(response_format): + return NOT_GIVEN + + if is_response_format_param(response_format): + return response_format + + # type checkers don't narrow the negation of a `TypeGuard` as it isn't + # a safe default behaviour but we know that at this point the `response_format` + # can only be a `type` + response_format = cast(type, response_format) + + if not is_basemodel_type(response_format): + raise TypeError(f"Unsupported response_format type - {response_format}") + + return { + "type": "json_schema", + "json_schema": { + "schema": to_strict_json_schema(response_format), + "name": response_format.__name__, + "strict": True, + }, + } diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py new file mode 100644 index 0000000000..967ad5de57 --- /dev/null +++ b/src/openai/lib/_pydantic.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import TypeGuard + +import pydantic + +from .._utils import is_dict as _is_dict, is_list +from .._compat import model_json_schema + + +def to_strict_json_schema(model: type[pydantic.BaseModel]) -> dict[str, Any]: + return _ensure_strict_json_schema(model_json_schema(model), path=()) + + +def _ensure_strict_json_schema( + json_schema: object, + path: tuple[str, ...], +) -> dict[str, Any]: + """Mutates the given JSON schema to ensure it conforms to the `strict` standard + that the API expects. + """ + if not is_dict(json_schema): + raise TypeError(f"Expected {json_schema} to be a dictionary; path={path}") + + typ = json_schema.get("type") + if typ == "object" and "additionalProperties" not in json_schema: + json_schema["additionalProperties"] = False + + # object types + # { 'type': 'object', 'properties': { 'a': {...} } } + properties = json_schema.get("properties") + if is_dict(properties): + json_schema["required"] = [prop for prop in properties.keys()] + json_schema["properties"] = { + key: _ensure_strict_json_schema(prop_schema, path=(*path, "properties", key)) + for key, prop_schema in properties.items() + } + + # arrays + # { 'type': 'array', 'items': {...} } + items = json_schema.get("items") + if is_dict(items): + json_schema["items"] = _ensure_strict_json_schema(items, path=(*path, "items")) + + # unions + any_of = json_schema.get("anyOf") + if is_list(any_of): + json_schema["anyOf"] = [ + _ensure_strict_json_schema(variant, path=(*path, "anyOf", str(i))) for i, variant in enumerate(any_of) + ] + + # intersections + all_of = json_schema.get("allOf") + if is_list(all_of): + json_schema["allOf"] = [ + _ensure_strict_json_schema(entry, path=(*path, "anyOf", str(i))) for i, entry in enumerate(all_of) + ] + + defs = json_schema.get("$defs") + if is_dict(defs): + for def_name, def_schema in defs.items(): + _ensure_strict_json_schema(def_schema, path=(*path, "$defs", def_name)) + + return json_schema + + +def is_dict(obj: object) -> TypeGuard[dict[str, object]]: + # just pretend that we know there are only `str` keys + # as that check is not worth the performance cost + return _is_dict(obj) diff --git a/src/openai/lib/_tools.py b/src/openai/lib/_tools.py new file mode 100644 index 0000000000..8478ed676c --- /dev/null +++ b/src/openai/lib/_tools.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from typing import Any, Dict, cast + +import pydantic + +from ._pydantic import to_strict_json_schema +from ..types.chat import ChatCompletionToolParam +from ..types.shared_params import FunctionDefinition + + +class PydanticFunctionTool(Dict[str, Any]): + """Dictionary wrapper so we can pass the given base model + throughout the entire request stack without having to special + case it. + """ + + model: type[pydantic.BaseModel] + + def __init__(self, defn: FunctionDefinition, model: type[pydantic.BaseModel]) -> None: + super().__init__(defn) + self.model = model + + def cast(self) -> FunctionDefinition: + return cast(FunctionDefinition, self) + + +def pydantic_function_tool( + model: type[pydantic.BaseModel], + *, + name: str | None = None, # inferred from class name by default + description: str | None = None, # inferred from class docstring by default +) -> ChatCompletionToolParam: + if description is None: + # note: we intentionally don't use `.getdoc()` to avoid + # including pydantic's docstrings + description = model.__doc__ + + function = PydanticFunctionTool( + { + "name": name or model.__name__, + "strict": True, + "parameters": to_strict_json_schema(model), + }, + model, + ).cast() + + if description is not None: + function["description"] = description + + return { + "type": "function", + "function": function, + } diff --git a/src/openai/lib/streaming/_deltas.py b/src/openai/lib/streaming/_deltas.py new file mode 100644 index 0000000000..a5e1317612 --- /dev/null +++ b/src/openai/lib/streaming/_deltas.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from ..._utils import is_dict, is_list + + +def accumulate_delta(acc: dict[object, object], delta: dict[object, object]) -> dict[object, object]: + for key, delta_value in delta.items(): + if key not in acc: + acc[key] = delta_value + continue + + acc_value = acc[key] + if acc_value is None: + acc[key] = delta_value + continue + + # the `index` property is used in arrays of objects so it should + # not be accumulated like other values e.g. + # [{'foo': 'bar', 'index': 0}] + # + # the same applies to `type` properties as they're used for + # discriminated unions + if key == "index" or key == "type": + acc[key] = delta_value + continue + + if isinstance(acc_value, str) and isinstance(delta_value, str): + acc_value += delta_value + elif isinstance(acc_value, (int, float)) and isinstance(delta_value, (int, float)): + acc_value += delta_value + elif is_dict(acc_value) and is_dict(delta_value): + acc_value = accumulate_delta(acc_value, delta_value) + elif is_list(acc_value) and is_list(delta_value): + # for lists of non-dictionary items we'll only ever get new entries + # in the array, existing entries will never be changed + if all(isinstance(x, (str, int, float)) for x in acc_value): + acc_value.extend(delta_value) + continue + + for delta_entry in delta_value: + if not is_dict(delta_entry): + raise TypeError(f"Unexpected list delta entry is not a dictionary: {delta_entry}") + + try: + index = delta_entry["index"] + except KeyError as exc: + raise RuntimeError(f"Expected list delta entry to have an `index` key; {delta_entry}") from exc + + if not isinstance(index, int): + raise TypeError(f"Unexpected, list delta entry `index` value is not an integer; {index}") + + try: + acc_entry = acc_value[index] + except IndexError: + acc_value.insert(index, delta_entry) + else: + if not is_dict(acc_entry): + raise TypeError("not handled yet") + + acc_value[index] = accumulate_delta(acc_entry, delta_entry) + + acc[key] = acc_value + + return acc diff --git a/src/openai/lib/streaming/chat/__init__.py b/src/openai/lib/streaming/chat/__init__.py new file mode 100644 index 0000000000..5881c39b9a --- /dev/null +++ b/src/openai/lib/streaming/chat/__init__.py @@ -0,0 +1,26 @@ +from ._types import ( + ParsedChoiceSnapshot as ParsedChoiceSnapshot, + ParsedChatCompletionSnapshot as ParsedChatCompletionSnapshot, + ParsedChatCompletionMessageSnapshot as ParsedChatCompletionMessageSnapshot, +) +from ._events import ( + ChunkEvent as ChunkEvent, + ContentDoneEvent as ContentDoneEvent, + RefusalDoneEvent as RefusalDoneEvent, + ContentDeltaEvent as ContentDeltaEvent, + RefusalDeltaEvent as RefusalDeltaEvent, + LogprobsContentDoneEvent as LogprobsContentDoneEvent, + LogprobsRefusalDoneEvent as LogprobsRefusalDoneEvent, + ChatCompletionStreamEvent as ChatCompletionStreamEvent, + LogprobsContentDeltaEvent as LogprobsContentDeltaEvent, + LogprobsRefusalDeltaEvent as LogprobsRefusalDeltaEvent, + ParsedChatCompletionSnapshot as ParsedChatCompletionSnapshot, + FunctionToolCallArgumentsDoneEvent as FunctionToolCallArgumentsDoneEvent, + FunctionToolCallArgumentsDeltaEvent as FunctionToolCallArgumentsDeltaEvent, +) +from ._completions import ( + ChatCompletionStream as ChatCompletionStream, + AsyncChatCompletionStream as AsyncChatCompletionStream, + ChatCompletionStreamManager as ChatCompletionStreamManager, + AsyncChatCompletionStreamManager as AsyncChatCompletionStreamManager, +) diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py new file mode 100644 index 0000000000..342a5e2b95 --- /dev/null +++ b/src/openai/lib/streaming/chat/_completions.py @@ -0,0 +1,724 @@ +from __future__ import annotations + +import inspect +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, Callable, Iterable, Awaitable, AsyncIterator, cast +from typing_extensions import Self, Iterator, assert_never + +from jiter import from_json + +from ._types import ParsedChoiceSnapshot, ParsedChatCompletionSnapshot, ParsedChatCompletionMessageSnapshot +from ._events import ( + ChunkEvent, + ContentDoneEvent, + RefusalDoneEvent, + ContentDeltaEvent, + RefusalDeltaEvent, + LogprobsContentDoneEvent, + LogprobsRefusalDoneEvent, + ChatCompletionStreamEvent, + LogprobsContentDeltaEvent, + LogprobsRefusalDeltaEvent, + FunctionToolCallArgumentsDoneEvent, + FunctionToolCallArgumentsDeltaEvent, +) +from .._deltas import accumulate_delta +from ...._types import NOT_GIVEN, NotGiven +from ...._utils import is_given, consume_sync_iterator, consume_async_iterator +from ...._compat import model_dump +from ...._models import build, construct_type +from ..._parsing import ( + ResponseFormatT, + has_parseable_input, + maybe_parse_content, + parse_chat_completion, + get_input_tool_by_name, + solve_response_format_t, + parse_function_tool_arguments, +) +from ...._streaming import Stream, AsyncStream +from ....types.chat import ChatCompletionChunk, ParsedChatCompletion, ChatCompletionToolParam +from ...._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError +from ....types.chat.chat_completion import ChoiceLogprobs +from ....types.chat.chat_completion_chunk import Choice as ChoiceChunk +from ....types.chat.completion_create_params import ResponseFormat as ResponseFormatParam + + +class ChatCompletionStream(Generic[ResponseFormatT]): + """Wrapper over the Chat Completions streaming API that adds helpful + events such as `content.done`, supports automatically parsing + responses & tool calls and accumulates a `ChatCompletion` object + from each individual chunk. + + https://platform.openai.com/docs/api-reference/streaming + """ + + def __init__( + self, + *, + raw_stream: Stream[ChatCompletionChunk], + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + ) -> None: + self._raw_stream = raw_stream + self._response = raw_stream.response + self._iterator = self.__stream__() + self._state = ChatCompletionStreamState(response_format=response_format, input_tools=input_tools) + + def __next__(self) -> ChatCompletionStreamEvent[ResponseFormatT]: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[ChatCompletionStreamEvent[ResponseFormatT]]: + for item in self._iterator: + yield item + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self._response.close() + + def get_final_completion(self) -> ParsedChatCompletion[ResponseFormatT]: + """Waits until the stream has been read to completion and returns + the accumulated `ParsedChatCompletion` object. + + If you passed a class type to `.stream()`, the `completion.choices[0].message.parsed` + property will be the content deserialised into that class, if there was any content returned + by the API. + """ + self.until_done() + return self._state.get_final_completion() + + def until_done(self) -> Self: + """Blocks until the stream has been consumed.""" + consume_sync_iterator(self) + return self + + @property + def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: + return self._state.current_completion_snapshot + + def __stream__(self) -> Iterator[ChatCompletionStreamEvent[ResponseFormatT]]: + for sse_event in self._raw_stream: + events_to_fire = self._state.handle_chunk(sse_event) + for event in events_to_fire: + yield event + + +class ChatCompletionStreamManager(Generic[ResponseFormatT]): + """Context manager over a `ChatCompletionStream` that is returned by `.stream()`. + + This context manager ensures the response cannot be leaked if you don't read + the stream to completion. + + Usage: + ```py + with client.beta.chat.completions.stream(...) as stream: + for event in stream: + ... + ``` + """ + + def __init__( + self, + api_request: Callable[[], Stream[ChatCompletionChunk]], + *, + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + ) -> None: + self.__stream: ChatCompletionStream[ResponseFormatT] | None = None + self.__api_request = api_request + self.__response_format = response_format + self.__input_tools = input_tools + + def __enter__(self) -> ChatCompletionStream[ResponseFormatT]: + raw_stream = self.__api_request() + + self.__stream = ChatCompletionStream( + raw_stream=raw_stream, + response_format=self.__response_format, + input_tools=self.__input_tools, + ) + + return self.__stream + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + self.__stream.close() + + +class AsyncChatCompletionStream(Generic[ResponseFormatT]): + """Wrapper over the Chat Completions streaming API that adds helpful + events such as `content.done`, supports automatically parsing + responses & tool calls and accumulates a `ChatCompletion` object + from each individual chunk. + + https://platform.openai.com/docs/api-reference/streaming + """ + + def __init__( + self, + *, + raw_stream: AsyncStream[ChatCompletionChunk], + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + ) -> None: + self._raw_stream = raw_stream + self._response = raw_stream.response + self._iterator = self.__stream__() + self._state = ChatCompletionStreamState(response_format=response_format, input_tools=input_tools) + + async def __anext__(self) -> ChatCompletionStreamEvent[ResponseFormatT]: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[ChatCompletionStreamEvent[ResponseFormatT]]: + async for item in self._iterator: + yield item + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self._response.aclose() + + async def get_final_completion(self) -> ParsedChatCompletion[ResponseFormatT]: + """Waits until the stream has been read to completion and returns + the accumulated `ParsedChatCompletion` object. + + If you passed a class type to `.stream()`, the `completion.choices[0].message.parsed` + property will be the content deserialised into that class, if there was any content returned + by the API. + """ + await self.until_done() + return self._state.get_final_completion() + + async def until_done(self) -> Self: + """Blocks until the stream has been consumed.""" + await consume_async_iterator(self) + return self + + @property + def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: + return self._state.current_completion_snapshot + + async def __stream__(self) -> AsyncIterator[ChatCompletionStreamEvent[ResponseFormatT]]: + async for sse_event in self._raw_stream: + events_to_fire = self._state.handle_chunk(sse_event) + for event in events_to_fire: + yield event + + +class AsyncChatCompletionStreamManager(Generic[ResponseFormatT]): + """Context manager over a `AsyncChatCompletionStream` that is returned by `.stream()`. + + This context manager ensures the response cannot be leaked if you don't read + the stream to completion. + + Usage: + ```py + async with client.beta.chat.completions.stream(...) as stream: + for event in stream: + ... + ``` + """ + + def __init__( + self, + api_request: Awaitable[AsyncStream[ChatCompletionChunk]], + *, + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + ) -> None: + self.__stream: AsyncChatCompletionStream[ResponseFormatT] | None = None + self.__api_request = api_request + self.__response_format = response_format + self.__input_tools = input_tools + + async def __aenter__(self) -> AsyncChatCompletionStream[ResponseFormatT]: + raw_stream = await self.__api_request + + self.__stream = AsyncChatCompletionStream( + raw_stream=raw_stream, + response_format=self.__response_format, + input_tools=self.__input_tools, + ) + + return self.__stream + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + await self.__stream.close() + + +class ChatCompletionStreamState(Generic[ResponseFormatT]): + def __init__( + self, + *, + input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + ) -> None: + self.__current_completion_snapshot: ParsedChatCompletionSnapshot | None = None + self.__choice_event_states: list[ChoiceEventState] = [] + + self._input_tools = [tool for tool in input_tools] if is_given(input_tools) else [] + self._response_format = response_format + self._rich_response_format: type | NotGiven = response_format if inspect.isclass(response_format) else NOT_GIVEN + + def get_final_completion(self) -> ParsedChatCompletion[ResponseFormatT]: + return parse_chat_completion( + chat_completion=self.current_completion_snapshot, + response_format=self._rich_response_format, + input_tools=self._input_tools, + ) + + @property + def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: + assert self.__current_completion_snapshot is not None + return self.__current_completion_snapshot + + def handle_chunk(self, chunk: ChatCompletionChunk) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: + """Accumulate a new chunk into the snapshot and returns a list of events to yield.""" + self.__current_completion_snapshot = self._accumulate_chunk(chunk) + + return self._build_events( + chunk=chunk, + completion_snapshot=self.__current_completion_snapshot, + ) + + def _get_choice_state(self, choice: ChoiceChunk) -> ChoiceEventState: + try: + return self.__choice_event_states[choice.index] + except IndexError: + choice_state = ChoiceEventState(input_tools=self._input_tools) + self.__choice_event_states.append(choice_state) + return choice_state + + def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionSnapshot: + completion_snapshot = self.__current_completion_snapshot + + if completion_snapshot is None: + return _convert_initial_chunk_into_snapshot(chunk) + + for choice in chunk.choices: + try: + choice_snapshot = completion_snapshot.choices[choice.index] + previous_tool_calls = choice_snapshot.message.tool_calls or [] + + choice_snapshot.message = cast( + ParsedChatCompletionMessageSnapshot, + construct_type( + type_=ParsedChatCompletionMessageSnapshot, + value=accumulate_delta( + cast( + "dict[object, object]", + model_dump( + choice_snapshot.message, + # we don't want to serialise / deserialise our custom properties + # as they won't appear in the delta and we don't want to have to + # continuosly reparse the content + exclude={ + "parsed": True, + "tool_calls": { + idx: {"function": {"parsed_arguments": True}} + for idx, _ in enumerate(choice_snapshot.message.tool_calls or []) + }, + }, + ), + ), + cast("dict[object, object]", choice.delta.to_dict()), + ), + ), + ) + + # ensure tools that have already been parsed are added back into the newly + # constructed message snapshot + for tool_index, prev_tool in enumerate(previous_tool_calls): + new_tool = (choice_snapshot.message.tool_calls or [])[tool_index] + + if prev_tool.type == "function": + assert new_tool.type == "function" + new_tool.function.parsed_arguments = prev_tool.function.parsed_arguments + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(prev_tool) + except IndexError: + choice_snapshot = cast( + ParsedChoiceSnapshot, + construct_type( + type_=ParsedChoiceSnapshot, + value={ + **choice.model_dump(exclude_unset=True, exclude={"delta"}), + "message": choice.delta.to_dict(), + }, + ), + ) + completion_snapshot.choices.append(choice_snapshot) + + if choice.finish_reason: + choice_snapshot.finish_reason = choice.finish_reason + + if has_parseable_input(response_format=self._response_format, input_tools=self._input_tools): + if choice.finish_reason == "length": + raise LengthFinishReasonError() + + if choice.finish_reason == "content_filter": + raise ContentFilterFinishReasonError() + + if ( + choice_snapshot.message.content + and not choice_snapshot.message.refusal + and is_given(self._rich_response_format) + ): + choice_snapshot.message.parsed = from_json( + bytes(choice_snapshot.message.content, "utf-8"), + partial_mode=True, + ) + + for tool_call_chunk in choice.delta.tool_calls or []: + tool_call_snapshot = (choice_snapshot.message.tool_calls or [])[tool_call_chunk.index] + + if tool_call_snapshot.type == "function": + input_tool = get_input_tool_by_name( + input_tools=self._input_tools, name=tool_call_snapshot.function.name + ) + + if ( + input_tool + and input_tool.get("function", {}).get("strict") + and tool_call_snapshot.function.arguments + ): + tool_call_snapshot.function.parsed_arguments = from_json( + bytes(tool_call_snapshot.function.arguments, "utf-8"), + partial_mode=True, + ) + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(tool_call_snapshot) + + if choice.logprobs is not None: + if choice_snapshot.logprobs is None: + choice_snapshot.logprobs = build( + ChoiceLogprobs, + content=choice.logprobs.content, + refusal=choice.logprobs.refusal, + ) + else: + if choice.logprobs.content: + if choice_snapshot.logprobs.content is None: + choice_snapshot.logprobs.content = [] + + choice_snapshot.logprobs.content.extend(choice.logprobs.content) + + if choice.logprobs.refusal: + if choice_snapshot.logprobs.refusal is None: + choice_snapshot.logprobs.refusal = [] + + choice_snapshot.logprobs.refusal.extend(choice.logprobs.refusal) + + completion_snapshot.usage = chunk.usage + completion_snapshot.system_fingerprint = chunk.system_fingerprint + + return completion_snapshot + + def _build_events( + self, + *, + chunk: ChatCompletionChunk, + completion_snapshot: ParsedChatCompletionSnapshot, + ) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: + events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] + + events_to_fire.append( + build(ChunkEvent, type="chunk", chunk=chunk, snapshot=completion_snapshot), + ) + + for choice in chunk.choices: + choice_state = self._get_choice_state(choice) + choice_snapshot = completion_snapshot.choices[choice.index] + + if choice.delta.content is not None and choice_snapshot.message.content is not None: + events_to_fire.append( + build( + ContentDeltaEvent, + type="content.delta", + delta=choice.delta.content, + snapshot=choice_snapshot.message.content, + parsed=choice_snapshot.message.parsed, + ) + ) + + if choice.delta.refusal is not None and choice_snapshot.message.refusal is not None: + events_to_fire.append( + build( + RefusalDeltaEvent, + type="refusal.delta", + delta=choice.delta.refusal, + snapshot=choice_snapshot.message.refusal, + ) + ) + + if choice.delta.tool_calls: + tool_calls = choice_snapshot.message.tool_calls + assert tool_calls is not None + + for tool_call_delta in choice.delta.tool_calls: + tool_call = tool_calls[tool_call_delta.index] + + if tool_call.type == "function": + assert tool_call_delta.function is not None + events_to_fire.append( + build( + FunctionToolCallArgumentsDeltaEvent, + type="tool_calls.function.arguments.delta", + name=tool_call.function.name, + index=tool_call_delta.index, + arguments=tool_call.function.arguments, + parsed_arguments=tool_call.function.parsed_arguments, + arguments_delta=tool_call_delta.function.arguments or "", + ) + ) + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(tool_call) + + if choice.logprobs is not None and choice_snapshot.logprobs is not None: + if choice.logprobs.content and choice_snapshot.logprobs.content: + events_to_fire.append( + build( + LogprobsContentDeltaEvent, + type="logprobs.content.delta", + content=choice.logprobs.content, + snapshot=choice_snapshot.logprobs.content, + ), + ) + + if choice.logprobs.refusal and choice_snapshot.logprobs.refusal: + events_to_fire.append( + build( + LogprobsRefusalDeltaEvent, + type="logprobs.refusal.delta", + refusal=choice.logprobs.refusal, + snapshot=choice_snapshot.logprobs.refusal, + ), + ) + + events_to_fire.extend( + choice_state.get_done_events( + choice_chunk=choice, + choice_snapshot=choice_snapshot, + response_format=self._response_format, + ) + ) + + return events_to_fire + + +class ChoiceEventState: + def __init__(self, *, input_tools: list[ChatCompletionToolParam]) -> None: + self._input_tools = input_tools + + self._content_done = False + self._refusal_done = False + self._logprobs_content_done = False + self._logprobs_refusal_done = False + self._done_tool_calls: set[int] = set() + self.__current_tool_call_index: int | None = None + + def get_done_events( + self, + *, + choice_chunk: ChoiceChunk, + choice_snapshot: ParsedChoiceSnapshot, + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + ) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: + events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] + + if choice_snapshot.finish_reason: + events_to_fire.extend( + self._content_done_events(choice_snapshot=choice_snapshot, response_format=response_format) + ) + + if ( + self.__current_tool_call_index is not None + and self.__current_tool_call_index not in self._done_tool_calls + ): + self._add_tool_done_event( + events_to_fire=events_to_fire, + choice_snapshot=choice_snapshot, + tool_index=self.__current_tool_call_index, + ) + + for tool_call in choice_chunk.delta.tool_calls or []: + if self.__current_tool_call_index != tool_call.index: + events_to_fire.extend( + self._content_done_events(choice_snapshot=choice_snapshot, response_format=response_format) + ) + + if self.__current_tool_call_index is not None: + self._add_tool_done_event( + events_to_fire=events_to_fire, + choice_snapshot=choice_snapshot, + tool_index=self.__current_tool_call_index, + ) + + self.__current_tool_call_index = tool_call.index + + return events_to_fire + + def _content_done_events( + self, + *, + choice_snapshot: ParsedChoiceSnapshot, + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + ) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: + events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] + + if choice_snapshot.message.content and not self._content_done: + self._content_done = True + + parsed = maybe_parse_content( + response_format=response_format, + message=choice_snapshot.message, + ) + + # update the parsed content to now use the richer `response_format` + # as opposed to the raw JSON-parsed object as the content is now + # complete and can be fully validated. + choice_snapshot.message.parsed = parsed + + events_to_fire.append( + build( + # we do this dance so that when the `ContentDoneEvent` instance + # is printed at runtime the class name will include the solved + # type variable, e.g. `ContentDoneEvent[MyModelType]` + cast( # pyright: ignore[reportUnnecessaryCast] + "type[ContentDoneEvent[ResponseFormatT]]", + cast(Any, ContentDoneEvent)[solve_response_format_t(response_format)], + ), + type="content.done", + content=choice_snapshot.message.content, + parsed=parsed, + ), + ) + + if choice_snapshot.message.refusal is not None and not self._refusal_done: + self._refusal_done = True + events_to_fire.append( + build(RefusalDoneEvent, type="refusal.done", refusal=choice_snapshot.message.refusal), + ) + + if ( + choice_snapshot.logprobs is not None + and choice_snapshot.logprobs.content is not None + and not self._logprobs_content_done + ): + self._logprobs_content_done = True + events_to_fire.append( + build(LogprobsContentDoneEvent, type="logprobs.content.done", content=choice_snapshot.logprobs.content), + ) + + if ( + choice_snapshot.logprobs is not None + and choice_snapshot.logprobs.refusal is not None + and not self._logprobs_refusal_done + ): + self._logprobs_refusal_done = True + events_to_fire.append( + build(LogprobsRefusalDoneEvent, type="logprobs.refusal.done", refusal=choice_snapshot.logprobs.refusal), + ) + + return events_to_fire + + def _add_tool_done_event( + self, + *, + events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]], + choice_snapshot: ParsedChoiceSnapshot, + tool_index: int, + ) -> None: + if tool_index in self._done_tool_calls: + return + + self._done_tool_calls.add(tool_index) + + assert choice_snapshot.message.tool_calls is not None + tool_call_snapshot = choice_snapshot.message.tool_calls[tool_index] + + if tool_call_snapshot.type == "function": + parsed_arguments = parse_function_tool_arguments( + input_tools=self._input_tools, function=tool_call_snapshot.function + ) + + # update the parsed content to potentially use a richer type + # as opposed to the raw JSON-parsed object as the content is now + # complete and can be fully validated. + tool_call_snapshot.function.parsed_arguments = parsed_arguments + + events_to_fire.append( + build( + FunctionToolCallArgumentsDoneEvent, + type="tool_calls.function.arguments.done", + index=tool_index, + name=tool_call_snapshot.function.name, + arguments=tool_call_snapshot.function.arguments, + parsed_arguments=parsed_arguments, + ) + ) + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(tool_call_snapshot) + + +def _convert_initial_chunk_into_snapshot(chunk: ChatCompletionChunk) -> ParsedChatCompletionSnapshot: + data = chunk.to_dict() + choices = cast("list[object]", data["choices"]) + + for choice in chunk.choices: + choices[choice.index] = { + **choice.model_dump(exclude_unset=True, exclude={"delta"}), + "message": choice.delta.to_dict(), + } + + return cast( + ParsedChatCompletionSnapshot, + construct_type( + type_=ParsedChatCompletionSnapshot, + value={ + "system_fingerprint": None, + **data, + "object": "chat.completion", + }, + ), + ) diff --git a/src/openai/lib/streaming/chat/_events.py b/src/openai/lib/streaming/chat/_events.py new file mode 100644 index 0000000000..d4c1f28300 --- /dev/null +++ b/src/openai/lib/streaming/chat/_events.py @@ -0,0 +1,123 @@ +from typing import List, Union, Generic, Optional +from typing_extensions import Literal + +from ._types import ParsedChatCompletionSnapshot +from ...._models import BaseModel, GenericModel +from ..._parsing import ResponseFormatT +from ....types.chat import ChatCompletionChunk, ChatCompletionTokenLogprob + + +class ChunkEvent(BaseModel): + type: Literal["chunk"] + + chunk: ChatCompletionChunk + + snapshot: ParsedChatCompletionSnapshot + + +class ContentDeltaEvent(BaseModel): + """This event is yielded for every chunk with `choice.delta.content` data.""" + + type: Literal["content.delta"] + + delta: str + + snapshot: str + + parsed: Optional[object] = None + + +class ContentDoneEvent(GenericModel, Generic[ResponseFormatT]): + type: Literal["content.done"] + + content: str + + parsed: Optional[ResponseFormatT] = None + + +class RefusalDeltaEvent(BaseModel): + type: Literal["refusal.delta"] + + delta: str + + snapshot: str + + +class RefusalDoneEvent(BaseModel): + type: Literal["refusal.done"] + + refusal: str + + +class FunctionToolCallArgumentsDeltaEvent(BaseModel): + type: Literal["tool_calls.function.arguments.delta"] + + name: str + + index: int + + arguments: str + """Accumulated raw JSON string""" + + parsed_arguments: object + """The parsed arguments so far""" + + arguments_delta: str + """The JSON string delta""" + + +class FunctionToolCallArgumentsDoneEvent(BaseModel): + type: Literal["tool_calls.function.arguments.done"] + + name: str + + index: int + + arguments: str + """Accumulated raw JSON string""" + + parsed_arguments: object + """The parsed arguments""" + + +class LogprobsContentDeltaEvent(BaseModel): + type: Literal["logprobs.content.delta"] + + content: List[ChatCompletionTokenLogprob] + + snapshot: List[ChatCompletionTokenLogprob] + + +class LogprobsContentDoneEvent(BaseModel): + type: Literal["logprobs.content.done"] + + content: List[ChatCompletionTokenLogprob] + + +class LogprobsRefusalDeltaEvent(BaseModel): + type: Literal["logprobs.refusal.delta"] + + refusal: List[ChatCompletionTokenLogprob] + + snapshot: List[ChatCompletionTokenLogprob] + + +class LogprobsRefusalDoneEvent(BaseModel): + type: Literal["logprobs.refusal.done"] + + refusal: List[ChatCompletionTokenLogprob] + + +ChatCompletionStreamEvent = Union[ + ChunkEvent, + ContentDeltaEvent, + ContentDoneEvent[ResponseFormatT], + RefusalDeltaEvent, + RefusalDoneEvent, + FunctionToolCallArgumentsDeltaEvent, + FunctionToolCallArgumentsDoneEvent, + LogprobsContentDeltaEvent, + LogprobsContentDoneEvent, + LogprobsRefusalDeltaEvent, + LogprobsRefusalDoneEvent, +] diff --git a/src/openai/lib/streaming/chat/_types.py b/src/openai/lib/streaming/chat/_types.py new file mode 100644 index 0000000000..42552893a0 --- /dev/null +++ b/src/openai/lib/streaming/chat/_types.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing_extensions import TypeAlias + +from ....types.chat import ParsedChoice, ParsedChatCompletion, ParsedChatCompletionMessage + +ParsedChatCompletionSnapshot: TypeAlias = ParsedChatCompletion[object] +"""Snapshot type representing an in-progress accumulation of +a `ParsedChatCompletion` object. +""" + +ParsedChatCompletionMessageSnapshot: TypeAlias = ParsedChatCompletionMessage[object] +"""Snapshot type representing an in-progress accumulation of +a `ParsedChatCompletionMessage` object. + +If the content has been fully accumulated, the `.parsed` content will be +the `response_format` instance, otherwise it'll be the raw JSON parsed version. +""" + +ParsedChoiceSnapshot: TypeAlias = ParsedChoice[object] diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index b4dc3cfdd6..441390d24b 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -88,6 +88,11 @@ def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -233,6 +238,11 @@ def update( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -453,6 +463,11 @@ async def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -598,6 +613,11 @@ async def update( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 0d9806678f..479c97c471 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -11,6 +11,7 @@ AsyncThreadsWithStreamingResponse, ) from ..._compat import cached_property +from .chat.chat import Chat, AsyncChat from .assistants import ( Assistants, AsyncAssistants, @@ -35,6 +36,10 @@ class Beta(SyncAPIResource): + @cached_property + def chat(self) -> Chat: + return Chat(self._client) + @cached_property def vector_stores(self) -> VectorStores: return VectorStores(self._client) @@ -57,6 +62,10 @@ def with_streaming_response(self) -> BetaWithStreamingResponse: class AsyncBeta(AsyncAPIResource): + @cached_property + def chat(self) -> AsyncChat: + return AsyncChat(self._client) + @cached_property def vector_stores(self) -> AsyncVectorStores: return AsyncVectorStores(self._client) diff --git a/src/openai/resources/beta/chat/__init__.py b/src/openai/resources/beta/chat/__init__.py new file mode 100644 index 0000000000..072d7867a5 --- /dev/null +++ b/src/openai/resources/beta/chat/__init__.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .chat import Chat, AsyncChat +from .completions import Completions, AsyncCompletions + +__all__ = [ + "Completions", + "AsyncCompletions", + "Chat", + "AsyncChat", +] diff --git a/src/openai/resources/beta/chat/chat.py b/src/openai/resources/beta/chat/chat.py new file mode 100644 index 0000000000..6afdcea381 --- /dev/null +++ b/src/openai/resources/beta/chat/chat.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ...._compat import cached_property +from .completions import Completions, AsyncCompletions +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["Chat", "AsyncChat"] + + +class Chat(SyncAPIResource): + @cached_property + def completions(self) -> Completions: + return Completions(self._client) + + +class AsyncChat(AsyncAPIResource): + @cached_property + def completions(self) -> AsyncCompletions: + return AsyncCompletions(self._client) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py new file mode 100644 index 0000000000..88ea2c0572 --- /dev/null +++ b/src/openai/resources/beta/chat/completions.py @@ -0,0 +1,449 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from functools import partial +from typing_extensions import Literal + +import httpx + +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._streaming import Stream +from ....types.chat import completion_create_params +from ....lib._parsing import ( + ResponseFormatT, + validate_input_tools as _validate_input_tools, + parse_chat_completion as _parse_chat_completion, + type_to_response_format_param as _type_to_response_format, +) +from ....types.chat_model import ChatModel +from ....lib.streaming.chat import ChatCompletionStreamManager, AsyncChatCompletionStreamManager +from ....types.chat.chat_completion_chunk import ChatCompletionChunk +from ....types.chat.parsed_chat_completion import ParsedChatCompletion +from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam +from ....types.chat.chat_completion_message_param import ChatCompletionMessageParam +from ....types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam +from ....types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam + +__all__ = ["Completions", "AsyncCompletions"] + + +class Completions(SyncAPIResource): + def parse( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + response_format: type[ResponseFormatT] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedChatCompletion[ResponseFormatT]: + """Wrapper over the `client.chat.completions.create()` method that provides richer integrations with Python specific types + & returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class. + + You can pass a pydantic model to this method and it will automatically convert the model + into a JSON schema, send it to the API and parse the response content back into the given model. + + This method will also automatically parse `function` tool calls if: + - You use the `openai.pydantic_function_tool()` helper method + - You mark your tool schema with `"strict": True` + + Example usage: + ```py + from pydantic import BaseModel + from openai import OpenAI + + class Step(BaseModel): + explanation: str + output: str + + class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + + client = OpenAI() + completion = client.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "system", "content": "You are a helpful math tutor."}, + {"role": "user", "content": "solve 8x + 31 = 2"}, + ], + response_format=MathResponse, + ) + + message = completion.choices[0].message + if message.parsed: + print(message.parsed.steps) + print("answer: ", message.parsed.final_answer) + ``` + """ + _validate_input_tools(tools) + + extra_headers = { + "X-Stainless-Helper-Method": "beta.chat.completions.parse", + **(extra_headers or {}), + } + + raw_completion = self._client.chat.completions.create( + messages=messages, + model=model, + response_format=_type_to_response_format(response_format), + frequency_penalty=frequency_penalty, + function_call=function_call, + functions=functions, + logit_bias=logit_bias, + logprobs=logprobs, + max_tokens=max_tokens, + n=n, + parallel_tool_calls=parallel_tool_calls, + presence_penalty=presence_penalty, + seed=seed, + service_tier=service_tier, + stop=stop, + stream_options=stream_options, + temperature=temperature, + tool_choice=tool_choice, + tools=tools, + top_logprobs=top_logprobs, + top_p=top_p, + user=user, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return _parse_chat_completion( + response_format=response_format, + chat_completion=raw_completion, + input_tools=tools, + ) + + def stream( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletionStreamManager[ResponseFormatT]: + """Wrapper over the `client.chat.completions.create(stream=True)` method that provides a more granular event API + and automatic accumulation of each delta. + + This also supports all of the parsing utilities that `.parse()` does. + + Unlike `.create(stream=True)`, the `.stream()` method requires usage within a context manager to prevent accidental leakage of the response: + + ```py + with client.beta.chat.completions.stream( + model='gpt-4o-2024-08-06', + messages=[...], + ) as stream: + for event in stream: + if event.type == 'content.delta': + print(event.content, flush=True, end='') + ``` + + When the context manager is entered, a `ChatCompletionStream` instance is returned which, like `.create(stream=True)` is an iterator. The full list of events that are yielded by the iterator are outlined in [these docs](https://github.com/openai/openai-python/blob/main/helpers.md#chat-completions-events). + + When the context manager exits, the response will be closed, however the `stream` instance is still available outside + the context manager. + """ + extra_headers = { + "X-Stainless-Helper-Method": "beta.chat.completions.stream", + **(extra_headers or {}), + } + + api_request: partial[Stream[ChatCompletionChunk]] = partial( + self._client.chat.completions.create, + messages=messages, + model=model, + stream=True, + response_format=_type_to_response_format(response_format), + frequency_penalty=frequency_penalty, + function_call=function_call, + functions=functions, + logit_bias=logit_bias, + logprobs=logprobs, + max_tokens=max_tokens, + n=n, + parallel_tool_calls=parallel_tool_calls, + presence_penalty=presence_penalty, + seed=seed, + service_tier=service_tier, + stop=stop, + stream_options=stream_options, + temperature=temperature, + tool_choice=tool_choice, + tools=tools, + top_logprobs=top_logprobs, + top_p=top_p, + user=user, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return ChatCompletionStreamManager( + api_request, + response_format=response_format, + input_tools=tools, + ) + + +class AsyncCompletions(AsyncAPIResource): + async def parse( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + response_format: type[ResponseFormatT] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedChatCompletion[ResponseFormatT]: + """Wrapper over the `client.chat.completions.create()` method that provides richer integrations with Python specific types + & returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class. + + You can pass a pydantic model to this method and it will automatically convert the model + into a JSON schema, send it to the API and parse the response content back into the given model. + + This method will also automatically parse `function` tool calls if: + - You use the `openai.pydantic_function_tool()` helper method + - You mark your tool schema with `"strict": True` + + Example usage: + ```py + from pydantic import BaseModel + from openai import AsyncOpenAI + + class Step(BaseModel): + explanation: str + output: str + + class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + + client = AsyncOpenAI() + completion = await client.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "system", "content": "You are a helpful math tutor."}, + {"role": "user", "content": "solve 8x + 31 = 2"}, + ], + response_format=MathResponse, + ) + + message = completion.choices[0].message + if message.parsed: + print(message.parsed.steps) + print("answer: ", message.parsed.final_answer) + ``` + """ + _validate_input_tools(tools) + + extra_headers = { + "X-Stainless-Helper-Method": "beta.chat.completions.parse", + **(extra_headers or {}), + } + + raw_completion = await self._client.chat.completions.create( + messages=messages, + model=model, + response_format=_type_to_response_format(response_format), + frequency_penalty=frequency_penalty, + function_call=function_call, + functions=functions, + logit_bias=logit_bias, + logprobs=logprobs, + max_tokens=max_tokens, + n=n, + parallel_tool_calls=parallel_tool_calls, + presence_penalty=presence_penalty, + seed=seed, + service_tier=service_tier, + stop=stop, + stream_options=stream_options, + temperature=temperature, + tool_choice=tool_choice, + tools=tools, + top_logprobs=top_logprobs, + top_p=top_p, + user=user, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return _parse_chat_completion( + response_format=response_format, + chat_completion=raw_completion, + input_tools=tools, + ) + + def stream( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncChatCompletionStreamManager[ResponseFormatT]: + """Wrapper over the `client.chat.completions.create(stream=True)` method that provides a more granular event API + and automatic accumulation of each delta. + + This also supports all of the parsing utilities that `.parse()` does. + + Unlike `.create(stream=True)`, the `.stream()` method requires usage within a context manager to prevent accidental leakage of the response: + + ```py + async with client.beta.chat.completions.stream( + model='gpt-4o-2024-08-06', + messages=[...], + ) as stream: + async for event in stream: + if event.type == 'content.delta': + print(event.content, flush=True, end='') + ``` + + When the context manager is entered, an `AsyncChatCompletionStream` instance is returned which, like `.create(stream=True)` is an async iterator. The full list of events that are yielded by the iterator are outlined in [these docs](https://github.com/openai/openai-python/blob/main/helpers.md#chat-completions-events). + + When the context manager exits, the response will be closed, however the `stream` instance is still available outside + the context manager. + """ + _validate_input_tools(tools) + + extra_headers = { + "X-Stainless-Helper-Method": "beta.chat.completions.stream", + **(extra_headers or {}), + } + + api_request = self._client.chat.completions.create( + messages=messages, + model=model, + stream=True, + response_format=_type_to_response_format(response_format), + frequency_penalty=frequency_penalty, + function_call=function_call, + functions=functions, + logit_bias=logit_bias, + logprobs=logprobs, + max_tokens=max_tokens, + n=n, + parallel_tool_calls=parallel_tool_calls, + presence_penalty=presence_penalty, + seed=seed, + service_tier=service_tier, + stop=stop, + stream_options=stream_options, + temperature=temperature, + tool_choice=tool_choice, + tools=tools, + top_logprobs=top_logprobs, + top_p=top_p, + user=user, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + return AsyncChatCompletionStreamManager( + api_request, + response_format=response_format, + input_tools=tools, + ) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 23a09d30ce..cbfb9546f0 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -145,6 +145,11 @@ def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -275,6 +280,11 @@ def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -401,6 +411,11 @@ def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1443,6 +1458,11 @@ async def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1573,6 +1593,11 @@ async def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1699,6 +1724,11 @@ async def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index f40e164180..4c95c484cc 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -323,6 +323,11 @@ def create_and_run( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -452,6 +457,11 @@ def create_and_run( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -577,6 +587,11 @@ def create_and_run( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1131,6 +1146,11 @@ async def create_and_run( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1260,6 +1280,11 @@ async def create_and_run( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1385,6 +1410,11 @@ async def create_and_run( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 88892d1d64..3dcd3774d7 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -19,9 +19,7 @@ from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..._streaming import Stream, AsyncStream from ...types.chat import completion_create_params -from ..._base_client import ( - make_request_options, -) +from ..._base_client import make_request_options from ...types.chat_model import ChatModel from ...types.chat.chat_completion import ChatCompletion from ...types.chat.chat_completion_chunk import ChatCompletionChunk @@ -144,6 +142,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. @@ -340,6 +340,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. @@ -529,6 +531,8 @@ def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. @@ -793,6 +797,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. @@ -989,6 +995,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. @@ -1178,6 +1186,8 @@ async def create( [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) response_format: An object specifying the format that the model must output. Compatible with + [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index 14b384a88d..5cef7bcd22 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -52,7 +52,7 @@ def with_streaming_response(self) -> JobsWithStreamingResponse: def create( self, *, - model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo"]], + model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]], training_file: str, hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, @@ -77,7 +77,7 @@ def create( Args: model: The name of the model to fine-tune. You can select one of the - [supported models](https://platform.openai.com/docs/guides/fine-tuning/what-models-can-be-fine-tuned). + [supported models](https://platform.openai.com/docs/guides/fine-tuning/which-models-can-be-fine-tuned). training_file: The ID of an uploaded file that contains training data. @@ -107,7 +107,7 @@ def create( name. For example, a `suffix` of "custom-model-name" would produce a model name like - `ft:gpt-3.5-turbo:openai:custom-model-name:7p4lURel`. + `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`. validation_file: The ID of an uploaded file that contains validation data. @@ -332,7 +332,7 @@ def with_streaming_response(self) -> AsyncJobsWithStreamingResponse: async def create( self, *, - model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo"]], + model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]], training_file: str, hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, @@ -357,7 +357,7 @@ async def create( Args: model: The name of the model to fine-tune. You can select one of the - [supported models](https://platform.openai.com/docs/guides/fine-tuning/what-models-can-be-fine-tuned). + [supported models](https://platform.openai.com/docs/guides/fine-tuning/which-models-can-be-fine-tuned). training_file: The ID of an uploaded file that contains training data. @@ -387,7 +387,7 @@ async def create( name. For example, a `suffix` of "custom-model-name" would produce a model name like - `ft:gpt-3.5-turbo:openai:custom-model-name:7p4lURel`. + `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`. validation_file: The ID of an uploaded file that contains validation data. diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 84916962cc..f621fb67c5 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -9,6 +9,9 @@ ErrorObject as ErrorObject, FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters, + ResponseFormatText as ResponseFormatText, + ResponseFormatJSONObject as ResponseFormatJSONObject, + ResponseFormatJSONSchema as ResponseFormatJSONSchema, ) from .upload import Upload as Upload from .embedding import Embedding as Embedding diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index d851a3619c..9c5ddfdbe0 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -23,7 +23,6 @@ from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams -from .assistant_response_format import AssistantResponseFormat as AssistantResponseFormat from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam @@ -31,7 +30,6 @@ from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction -from .assistant_response_format_param import AssistantResponseFormatParam as AssistantResponseFormatParam from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam as AssistantToolChoiceOptionParam from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam as AssistantToolChoiceFunctionParam diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 4e5adc766e..c6a0a4cfcf 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -89,6 +89,11 @@ class Assistant(BaseModel): [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index c10f7f57ad..84cd4425d1 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -60,6 +60,11 @@ class AssistantCreateParams(TypedDict, total=False): [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/assistant_response_format.py b/src/openai/types/beta/assistant_response_format.py deleted file mode 100644 index f53bdaf62a..0000000000 --- a/src/openai/types/beta/assistant_response_format.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["AssistantResponseFormat"] - - -class AssistantResponseFormat(BaseModel): - type: Optional[Literal["text", "json_object"]] = None - """Must be one of `text` or `json_object`.""" diff --git a/src/openai/types/beta/assistant_response_format_option.py b/src/openai/types/beta/assistant_response_format_option.py index 6ce390f6d6..6f06a3442f 100644 --- a/src/openai/types/beta/assistant_response_format_option.py +++ b/src/openai/types/beta/assistant_response_format_option.py @@ -3,8 +3,12 @@ from typing import Union from typing_extensions import Literal, TypeAlias -from .assistant_response_format import AssistantResponseFormat +from ..shared.response_format_text import ResponseFormatText +from ..shared.response_format_json_object import ResponseFormatJSONObject +from ..shared.response_format_json_schema import ResponseFormatJSONSchema __all__ = ["AssistantResponseFormatOption"] -AssistantResponseFormatOption: TypeAlias = Union[Literal["none", "auto"], AssistantResponseFormat] +AssistantResponseFormatOption: TypeAlias = Union[ + Literal["auto"], ResponseFormatText, ResponseFormatJSONObject, ResponseFormatJSONSchema +] diff --git a/src/openai/types/beta/assistant_response_format_option_param.py b/src/openai/types/beta/assistant_response_format_option_param.py index 8100088723..680a060c3c 100644 --- a/src/openai/types/beta/assistant_response_format_option_param.py +++ b/src/openai/types/beta/assistant_response_format_option_param.py @@ -5,8 +5,13 @@ from typing import Union from typing_extensions import Literal, TypeAlias -from .assistant_response_format_param import AssistantResponseFormatParam +from ...types import shared_params __all__ = ["AssistantResponseFormatOptionParam"] -AssistantResponseFormatOptionParam: TypeAlias = Union[Literal["none", "auto"], AssistantResponseFormatParam] +AssistantResponseFormatOptionParam: TypeAlias = Union[ + Literal["auto"], + shared_params.ResponseFormatText, + shared_params.ResponseFormatJSONObject, + shared_params.ResponseFormatJSONSchema, +] diff --git a/src/openai/types/beta/assistant_response_format_param.py b/src/openai/types/beta/assistant_response_format_param.py deleted file mode 100644 index 96e1d02115..0000000000 --- a/src/openai/types/beta/assistant_response_format_param.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, TypedDict - -__all__ = ["AssistantResponseFormatParam"] - - -class AssistantResponseFormatParam(TypedDict, total=False): - type: Literal["text", "json_object"] - """Must be one of `text` or `json_object`.""" diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index b401e1a891..ade565819f 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -49,6 +49,11 @@ class AssistantUpdateParams(TypedDict, total=False): [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py index e2711b9b3d..26ab1cb83f 100644 --- a/src/openai/types/beta/file_search_tool.py +++ b/src/openai/types/beta/file_search_tool.py @@ -12,8 +12,8 @@ class FileSearch(BaseModel): max_num_results: Optional[int] = None """The maximum number of results the file search tool should output. - The default is 20 for gpt-4\\** models and 5 for gpt-3.5-turbo. This number should - be between 1 and 50 inclusive. + The default is 20 for `gpt-4*` models and 5 for `gpt-3.5-turbo`. This number + should be between 1 and 50 inclusive. Note that the file search tool may output fewer than `max_num_results` results. See the diff --git a/src/openai/types/beta/file_search_tool_param.py b/src/openai/types/beta/file_search_tool_param.py index 115f86a444..666719f8cd 100644 --- a/src/openai/types/beta/file_search_tool_param.py +++ b/src/openai/types/beta/file_search_tool_param.py @@ -11,8 +11,8 @@ class FileSearch(TypedDict, total=False): max_num_results: int """The maximum number of results the file search tool should output. - The default is 20 for gpt-4\\** models and 5 for gpt-3.5-turbo. This number should - be between 1 and 50 inclusive. + The default is 20 for `gpt-4*` models and 5 for `gpt-3.5-turbo`. This number + should be between 1 and 50 inclusive. Note that the file search tool may output fewer than `max_num_results` results. See the diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 62cff921e2..7490b25ef3 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -100,6 +100,11 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py index 023d76fc13..70853177bd 100644 --- a/src/openai/types/beta/threads/__init__.py +++ b/src/openai/types/beta/threads/__init__.py @@ -25,11 +25,13 @@ from .text_content_block import TextContentBlock as TextContentBlock from .message_delta_event import MessageDeltaEvent as MessageDeltaEvent from .message_list_params import MessageListParams as MessageListParams +from .refusal_delta_block import RefusalDeltaBlock as RefusalDeltaBlock from .file_path_annotation import FilePathAnnotation as FilePathAnnotation from .image_url_delta_block import ImageURLDeltaBlock as ImageURLDeltaBlock from .message_content_delta import MessageContentDelta as MessageContentDelta from .message_create_params import MessageCreateParams as MessageCreateParams from .message_update_params import MessageUpdateParams as MessageUpdateParams +from .refusal_content_block import RefusalContentBlock as RefusalContentBlock from .image_file_delta_block import ImageFileDeltaBlock as ImageFileDeltaBlock from .image_url_content_block import ImageURLContentBlock as ImageURLContentBlock from .file_citation_annotation import FileCitationAnnotation as FileCitationAnnotation diff --git a/src/openai/types/beta/threads/message_content.py b/src/openai/types/beta/threads/message_content.py index 7b718c3ca9..9523c1e1b9 100644 --- a/src/openai/types/beta/threads/message_content.py +++ b/src/openai/types/beta/threads/message_content.py @@ -5,11 +5,14 @@ from ...._utils import PropertyInfo from .text_content_block import TextContentBlock +from .refusal_content_block import RefusalContentBlock from .image_url_content_block import ImageURLContentBlock from .image_file_content_block import ImageFileContentBlock __all__ = ["MessageContent"] + MessageContent: TypeAlias = Annotated[ - Union[ImageFileContentBlock, ImageURLContentBlock, TextContentBlock], PropertyInfo(discriminator="type") + Union[ImageFileContentBlock, ImageURLContentBlock, TextContentBlock, RefusalContentBlock], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/beta/threads/message_content_delta.py b/src/openai/types/beta/threads/message_content_delta.py index 667172c08f..b6e7dfa45a 100644 --- a/src/openai/types/beta/threads/message_content_delta.py +++ b/src/openai/types/beta/threads/message_content_delta.py @@ -5,11 +5,13 @@ from ...._utils import PropertyInfo from .text_delta_block import TextDeltaBlock +from .refusal_delta_block import RefusalDeltaBlock from .image_url_delta_block import ImageURLDeltaBlock from .image_file_delta_block import ImageFileDeltaBlock __all__ = ["MessageContentDelta"] MessageContentDelta: TypeAlias = Annotated[ - Union[ImageFileDeltaBlock, TextDeltaBlock, ImageURLDeltaBlock], PropertyInfo(discriminator="type") + Union[ImageFileDeltaBlock, TextDeltaBlock, RefusalDeltaBlock, ImageURLDeltaBlock], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/beta/threads/refusal_content_block.py b/src/openai/types/beta/threads/refusal_content_block.py new file mode 100644 index 0000000000..d54f948554 --- /dev/null +++ b/src/openai/types/beta/threads/refusal_content_block.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RefusalContentBlock"] + + +class RefusalContentBlock(BaseModel): + refusal: str + + type: Literal["refusal"] + """Always `refusal`.""" diff --git a/src/openai/types/beta/threads/refusal_delta_block.py b/src/openai/types/beta/threads/refusal_delta_block.py new file mode 100644 index 0000000000..dbd8e62697 --- /dev/null +++ b/src/openai/types/beta/threads/refusal_delta_block.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RefusalDeltaBlock"] + + +class RefusalDeltaBlock(BaseModel): + index: int + """The index of the refusal part in the message.""" + + type: Literal["refusal"] + """Always `refusal`.""" + + refusal: Optional[str] = None diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 81d10d4a56..0579e229d8 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -171,6 +171,11 @@ class Run(BaseModel): [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index e0c42fd23f..d3e6d9c476 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -97,6 +97,11 @@ class RunCreateParamsBase(TypedDict, total=False): [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/beta/vector_stores/vector_store_file.py b/src/openai/types/beta/vector_stores/vector_store_file.py index 4762de0ebd..65096e8dad 100644 --- a/src/openai/types/beta/vector_stores/vector_store_file.py +++ b/src/openai/types/beta/vector_stores/vector_store_file.py @@ -17,7 +17,7 @@ class LastError(BaseModel): - code: Literal["internal_error", "file_not_found", "parsing_error", "unhandled_mime_type"] + code: Literal["server_error", "unsupported_file", "invalid_file"] """One of `server_error` or `rate_limit_exceeded`.""" message: str diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 0ba812ff9b..a5cf3734b8 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -5,8 +5,17 @@ from .chat_completion import ChatCompletion as ChatCompletion from .chat_completion_role import ChatCompletionRole as ChatCompletionRole from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk +from .parsed_chat_completion import ( + ParsedChoice as ParsedChoice, + ParsedChatCompletion as ParsedChatCompletion, + ParsedChatCompletionMessage as ParsedChatCompletionMessage, +) from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .parsed_function_tool_call import ( + ParsedFunction as ParsedFunction, + ParsedFunctionToolCall as ParsedFunctionToolCall, +) from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob @@ -37,6 +46,9 @@ from .chat_completion_tool_choice_option_param import ( ChatCompletionToolChoiceOptionParam as ChatCompletionToolChoiceOptionParam, ) +from .chat_completion_content_part_refusal_param import ( + ChatCompletionContentPartRefusalParam as ChatCompletionContentPartRefusalParam, +) from .chat_completion_function_call_option_param import ( ChatCompletionFunctionCallOptionParam as ChatCompletionFunctionCallOptionParam, ) diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 5f4eaf3366..4b53e70890 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -15,6 +15,9 @@ class ChoiceLogprobs(BaseModel): content: Optional[List[ChatCompletionTokenLogprob]] = None """A list of message content tokens with log probability information.""" + refusal: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message refusal tokens with log probability information.""" + class Choice(BaseModel): finish_reason: Literal["stop", "length", "tool_calls", "content_filter", "function_call"] diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 8f7357b96c..2429d41d33 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -2,12 +2,16 @@ from __future__ import annotations -from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam from .chat_completion_message_tool_call_param import ChatCompletionMessageToolCallParam +from .chat_completion_content_part_refusal_param import ChatCompletionContentPartRefusalParam -__all__ = ["ChatCompletionAssistantMessageParam", "FunctionCall"] +__all__ = ["ChatCompletionAssistantMessageParam", "ContentArrayOfContentPart", "FunctionCall"] + +ContentArrayOfContentPart: TypeAlias = Union[ChatCompletionContentPartTextParam, ChatCompletionContentPartRefusalParam] class FunctionCall(TypedDict, total=False): @@ -27,7 +31,7 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): role: Required[Literal["assistant"]] """The role of the messages author, in this case `assistant`.""" - content: Optional[str] + content: Union[str, Iterable[ContentArrayOfContentPart], None] """The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified. @@ -47,5 +51,8 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): role. """ + refusal: Optional[str] + """The refusal message by the assistant.""" + tool_calls: Iterable[ChatCompletionMessageToolCallParam] """The tool calls generated by the model, such as function calls.""" diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 65643c7e60..9ec6dc4bdb 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -67,6 +67,9 @@ class ChoiceDelta(BaseModel): model. """ + refusal: Optional[str] = None + """The refusal message generated by the model.""" + role: Optional[Literal["system", "user", "assistant", "tool"]] = None """The role of the author of this message.""" @@ -77,6 +80,9 @@ class ChoiceLogprobs(BaseModel): content: Optional[List[ChatCompletionTokenLogprob]] = None """A list of message content tokens with log probability information.""" + refusal: Optional[List[ChatCompletionTokenLogprob]] = None + """A list of message refusal tokens with log probability information.""" + class Choice(BaseModel): delta: ChoiceDelta diff --git a/src/openai/types/chat/chat_completion_content_part_refusal_param.py b/src/openai/types/chat/chat_completion_content_part_refusal_param.py new file mode 100644 index 0000000000..c18c7db770 --- /dev/null +++ b/src/openai/types/chat/chat_completion_content_part_refusal_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionContentPartRefusalParam"] + + +class ChatCompletionContentPartRefusalParam(TypedDict, total=False): + refusal: Required[str] + """The refusal message generated by the model.""" + + type: Required[Literal["refusal"]] + """The type of the content part.""" diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py index 8db7d17d24..492bb68c85 100644 --- a/src/openai/types/chat/chat_completion_message.py +++ b/src/openai/types/chat/chat_completion_message.py @@ -26,6 +26,9 @@ class ChatCompletionMessage(BaseModel): content: Optional[str] = None """The contents of the message.""" + refusal: Optional[str] = None + """The refusal message generated by the model.""" + role: Literal["assistant"] """The role of the author of this message.""" diff --git a/src/openai/types/chat/chat_completion_system_message_param.py b/src/openai/types/chat/chat_completion_system_message_param.py index 94bb3f636c..172ccea09e 100644 --- a/src/openai/types/chat/chat_completion_system_message_param.py +++ b/src/openai/types/chat/chat_completion_system_message_param.py @@ -2,13 +2,16 @@ from __future__ import annotations +from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam + __all__ = ["ChatCompletionSystemMessageParam"] class ChatCompletionSystemMessageParam(TypedDict, total=False): - content: Required[str] + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] """The contents of the system message.""" role: Required[Literal["system"]] diff --git a/src/openai/types/chat/chat_completion_tool_message_param.py b/src/openai/types/chat/chat_completion_tool_message_param.py index 5c590e033f..eb5e270e47 100644 --- a/src/openai/types/chat/chat_completion_tool_message_param.py +++ b/src/openai/types/chat/chat_completion_tool_message_param.py @@ -2,13 +2,16 @@ from __future__ import annotations +from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam + __all__ = ["ChatCompletionToolMessageParam"] class ChatCompletionToolMessageParam(TypedDict, total=False): - content: Required[str] + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] """The contents of the tool message.""" role: Required[Literal["tool"]] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 9e81881b9e..bf648a3858 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -121,7 +121,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): response_format: ResponseFormat """An object specifying the format that the model must output. - Compatible with + Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. @@ -250,9 +251,9 @@ class Function(TypedDict, total=False): """ -class ResponseFormat(TypedDict, total=False): - type: Literal["text", "json_object"] - """Must be one of `text` or `json_object`.""" +ResponseFormat: TypeAlias = Union[ + shared_params.ResponseFormatText, shared_params.ResponseFormatJSONObject, shared_params.ResponseFormatJSONSchema +] class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase): diff --git a/src/openai/types/chat/parsed_chat_completion.py b/src/openai/types/chat/parsed_chat_completion.py new file mode 100644 index 0000000000..4b11dac5a0 --- /dev/null +++ b/src/openai/types/chat/parsed_chat_completion.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Generic, TypeVar, Optional + +from ..._models import GenericModel +from .chat_completion import Choice, ChatCompletion +from .chat_completion_message import ChatCompletionMessage +from .parsed_function_tool_call import ParsedFunctionToolCall + +__all__ = ["ParsedChatCompletion", "ParsedChoice"] + + +ContentType = TypeVar("ContentType") + + +# we need to disable this check because we're overriding properties +# with subclasses of their types which is technically unsound as +# properties can be mutated. +# pyright: reportIncompatibleVariableOverride=false + + +class ParsedChatCompletionMessage(ChatCompletionMessage, GenericModel, Generic[ContentType]): + parsed: Optional[ContentType] = None + """The auto-parsed message contents""" + + tool_calls: Optional[List[ParsedFunctionToolCall]] = None # type: ignore[assignment] + """The tool calls generated by the model, such as function calls.""" + + +class ParsedChoice(Choice, GenericModel, Generic[ContentType]): + message: ParsedChatCompletionMessage[ContentType] + """A chat completion message generated by the model.""" + + +class ParsedChatCompletion(ChatCompletion, GenericModel, Generic[ContentType]): + choices: List[ParsedChoice[ContentType]] # type: ignore[assignment] + """A list of chat completion choices. + + Can be more than one if `n` is greater than 1. + """ diff --git a/src/openai/types/chat/parsed_function_tool_call.py b/src/openai/types/chat/parsed_function_tool_call.py new file mode 100644 index 0000000000..3e90789f85 --- /dev/null +++ b/src/openai/types/chat/parsed_function_tool_call.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .chat_completion_message_tool_call import Function, ChatCompletionMessageToolCall + +__all__ = ["ParsedFunctionToolCall", "ParsedFunction"] + +# we need to disable this check because we're overriding properties +# with subclasses of their types which is technically unsound as +# properties can be mutated. +# pyright: reportIncompatibleVariableOverride=false + + +class ParsedFunction(Function): + parsed_arguments: Optional[object] = None + """ + The arguments to call the function with. + + If you used `openai.pydantic_function_tool()` then this will be an + instance of the given `BaseModel`. + + Otherwise, this will be the parsed JSON arguments. + """ + + +class ParsedFunctionToolCall(ChatCompletionMessageToolCall): + function: ParsedFunction + """The function that the model called.""" diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index edb7b732bf..686f26b783 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -6,6 +6,7 @@ ChatModel: TypeAlias = Literal[ "gpt-4o", + "gpt-4o-2024-08-06", "gpt-4o-2024-05-13", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index c5196e4406..e9be2ef1ca 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -9,11 +9,11 @@ class JobCreateParams(TypedDict, total=False): - model: Required[Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo"]]] + model: Required[Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]]] """The name of the model to fine-tune. You can select one of the - [supported models](https://platform.openai.com/docs/guides/fine-tuning/what-models-can-be-fine-tuned). + [supported models](https://platform.openai.com/docs/guides/fine-tuning/which-models-can-be-fine-tuned). """ training_file: Required[str] @@ -54,7 +54,7 @@ class JobCreateParams(TypedDict, total=False): name. For example, a `suffix` of "custom-model-name" would produce a model name like - `ft:gpt-3.5-turbo:openai:custom-model-name:7p4lURel`. + `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`. """ validation_file: Optional[str] diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index e085744e29..c8776bca0e 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -3,3 +3,6 @@ from .error_object import ErrorObject as ErrorObject from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters +from .response_format_text import ResponseFormatText as ResponseFormatText +from .response_format_json_object import ResponseFormatJSONObject as ResponseFormatJSONObject +from .response_format_json_schema import ResponseFormatJSONSchema as ResponseFormatJSONSchema diff --git a/src/openai/types/shared/function_definition.py b/src/openai/types/shared/function_definition.py index 49f5e67c50..06baa23170 100644 --- a/src/openai/types/shared/function_definition.py +++ b/src/openai/types/shared/function_definition.py @@ -32,3 +32,12 @@ class FunctionDefinition(BaseModel): Omitting `parameters` defines a function with an empty parameter list. """ + + strict: Optional[bool] = None + """Whether to enable strict schema adherence when generating the function call. + + If set to true, the model will follow the exact schema defined in the + `parameters` field. Only a subset of JSON Schema is supported when `strict` is + `true`. Learn more about Structured Outputs in the + [function calling guide](docs/guides/function-calling). + """ diff --git a/src/openai/types/shared/response_format_json_object.py b/src/openai/types/shared/response_format_json_object.py new file mode 100644 index 0000000000..107728dd2e --- /dev/null +++ b/src/openai/types/shared/response_format_json_object.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFormatJSONObject"] + + +class ResponseFormatJSONObject(BaseModel): + type: Literal["json_object"] + """The type of response format being defined: `json_object`""" diff --git a/src/openai/types/shared/response_format_json_schema.py b/src/openai/types/shared/response_format_json_schema.py new file mode 100644 index 0000000000..3194a4fe91 --- /dev/null +++ b/src/openai/types/shared/response_format_json_schema.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ResponseFormatJSONSchema", "JSONSchema"] + + +class JSONSchema(BaseModel): + name: str + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: Optional[str] = None + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + schema_: Optional[Dict[str, object]] = FieldInfo(alias="schema", default=None) + """The schema for the response format, described as a JSON Schema object.""" + + strict: Optional[bool] = None + """Whether to enable strict schema adherence when generating the output. + + If set to true, the model will always follow the exact schema defined in the + `schema` field. Only a subset of JSON Schema is supported when `strict` is + `true`. To learn more, read the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + """ + + +class ResponseFormatJSONSchema(BaseModel): + json_schema: JSONSchema + + type: Literal["json_schema"] + """The type of response format being defined: `json_schema`""" diff --git a/src/openai/types/shared/response_format_text.py b/src/openai/types/shared/response_format_text.py new file mode 100644 index 0000000000..6721fe0973 --- /dev/null +++ b/src/openai/types/shared/response_format_text.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFormatText"] + + +class ResponseFormatText(BaseModel): + type: Literal["text"] + """The type of response format being defined: `text`""" diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index ef638cb279..ab4057d59f 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -2,3 +2,6 @@ from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters +from .response_format_text import ResponseFormatText as ResponseFormatText +from .response_format_json_object import ResponseFormatJSONObject as ResponseFormatJSONObject +from .response_format_json_schema import ResponseFormatJSONSchema as ResponseFormatJSONSchema diff --git a/src/openai/types/shared_params/function_definition.py b/src/openai/types/shared_params/function_definition.py index 29ccc548d4..f41392f154 100644 --- a/src/openai/types/shared_params/function_definition.py +++ b/src/openai/types/shared_params/function_definition.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Required, TypedDict from ...types import shared_params @@ -33,3 +34,12 @@ class FunctionDefinition(TypedDict, total=False): Omitting `parameters` defines a function with an empty parameter list. """ + + strict: Optional[bool] + """Whether to enable strict schema adherence when generating the function call. + + If set to true, the model will follow the exact schema defined in the + `parameters` field. Only a subset of JSON Schema is supported when `strict` is + `true`. Learn more about Structured Outputs in the + [function calling guide](docs/guides/function-calling). + """ diff --git a/src/openai/types/shared_params/response_format_json_object.py b/src/openai/types/shared_params/response_format_json_object.py new file mode 100644 index 0000000000..8419c6cb56 --- /dev/null +++ b/src/openai/types/shared_params/response_format_json_object.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFormatJSONObject"] + + +class ResponseFormatJSONObject(TypedDict, total=False): + type: Required[Literal["json_object"]] + """The type of response format being defined: `json_object`""" diff --git a/src/openai/types/shared_params/response_format_json_schema.py b/src/openai/types/shared_params/response_format_json_schema.py new file mode 100644 index 0000000000..4b60fae8ee --- /dev/null +++ b/src/openai/types/shared_params/response_format_json_schema.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFormatJSONSchema", "JSONSchema"] + + +class JSONSchema(TypedDict, total=False): + name: Required[str] + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: str + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + schema: Dict[str, object] + """The schema for the response format, described as a JSON Schema object.""" + + strict: Optional[bool] + """Whether to enable strict schema adherence when generating the output. + + If set to true, the model will always follow the exact schema defined in the + `schema` field. Only a subset of JSON Schema is supported when `strict` is + `true`. To learn more, read the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + """ + + +class ResponseFormatJSONSchema(TypedDict, total=False): + json_schema: Required[JSONSchema] + + type: Required[Literal["json_schema"]] + """The type of response format being defined: `json_schema`""" diff --git a/src/openai/types/shared_params/response_format_text.py b/src/openai/types/shared_params/response_format_text.py new file mode 100644 index 0000000000..5bec7fc503 --- /dev/null +++ b/src/openai/types/shared_params/response_format_text.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFormatText"] + + +class ResponseFormatText(TypedDict, total=False): + type: Required[Literal["text"]] + """The type of response format being defined: `text`""" diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index dd0ce9266e..fbd5ff0597 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -24,19 +24,19 @@ class TestAssistants: @parametrize def test_method_create(self, client: OpenAI) -> None: assistant = client.beta.assistants.create( - model="gpt-4-turbo", + model="gpt-4o", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: assistant = client.beta.assistants.create( - model="gpt-4-turbo", - description="string", - instructions="string", + model="gpt-4o", + description="description", + instructions="instructions", metadata={}, - name="string", - response_format="none", + name="name", + response_format="auto", temperature=1, tool_resources={ "code_interpreter": {"file_ids": ["string", "string", "string"]}, @@ -59,7 +59,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.create( - model="gpt-4-turbo", + model="gpt-4o", ) assert response.is_closed is True @@ -70,7 +70,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: with client.beta.assistants.with_streaming_response.create( - model="gpt-4-turbo", + model="gpt-4o", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -83,14 +83,14 @@ def test_streaming_response_create(self, client: OpenAI) -> None: @parametrize def test_method_retrieve(self, client: OpenAI) -> None: assistant = client.beta.assistants.retrieve( - "string", + "assistant_id", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.retrieve( - "string", + "assistant_id", ) assert response.is_closed is True @@ -101,7 +101,7 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: @parametrize def test_streaming_response_retrieve(self, client: OpenAI) -> None: with client.beta.assistants.with_streaming_response.retrieve( - "string", + "assistant_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -121,20 +121,20 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: @parametrize def test_method_update(self, client: OpenAI) -> None: assistant = client.beta.assistants.update( - "string", + assistant_id="assistant_id", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: OpenAI) -> None: assistant = client.beta.assistants.update( - "string", - description="string", - instructions="string", + assistant_id="assistant_id", + description="description", + instructions="instructions", metadata={}, - model="string", - name="string", - response_format="none", + model="model", + name="name", + response_format="auto", temperature=1, tool_resources={ "code_interpreter": {"file_ids": ["string", "string", "string"]}, @@ -148,7 +148,7 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_update(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.update( - "string", + assistant_id="assistant_id", ) assert response.is_closed is True @@ -159,7 +159,7 @@ def test_raw_response_update(self, client: OpenAI) -> None: @parametrize def test_streaming_response_update(self, client: OpenAI) -> None: with client.beta.assistants.with_streaming_response.update( - "string", + assistant_id="assistant_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -173,7 +173,7 @@ def test_streaming_response_update(self, client: OpenAI) -> None: def test_path_params_update(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): client.beta.assistants.with_raw_response.update( - "", + assistant_id="", ) @parametrize @@ -184,8 +184,8 @@ def test_method_list(self, client: OpenAI) -> None: @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: assistant = client.beta.assistants.list( - after="string", - before="string", + after="after", + before="before", limit=0, order="asc", ) @@ -214,14 +214,14 @@ def test_streaming_response_list(self, client: OpenAI) -> None: @parametrize def test_method_delete(self, client: OpenAI) -> None: assistant = client.beta.assistants.delete( - "string", + "assistant_id", ) assert_matches_type(AssistantDeleted, assistant, path=["response"]) @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: response = client.beta.assistants.with_raw_response.delete( - "string", + "assistant_id", ) assert response.is_closed is True @@ -232,7 +232,7 @@ def test_raw_response_delete(self, client: OpenAI) -> None: @parametrize def test_streaming_response_delete(self, client: OpenAI) -> None: with client.beta.assistants.with_streaming_response.delete( - "string", + "assistant_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -256,19 +256,19 @@ class TestAsyncAssistants: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.create( - model="gpt-4-turbo", + model="gpt-4o", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.create( - model="gpt-4-turbo", - description="string", - instructions="string", + model="gpt-4o", + description="description", + instructions="instructions", metadata={}, - name="string", - response_format="none", + name="name", + response_format="auto", temperature=1, tool_resources={ "code_interpreter": {"file_ids": ["string", "string", "string"]}, @@ -291,7 +291,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.assistants.with_raw_response.create( - model="gpt-4-turbo", + model="gpt-4o", ) assert response.is_closed is True @@ -302,7 +302,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.assistants.with_streaming_response.create( - model="gpt-4-turbo", + model="gpt-4o", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -315,14 +315,14 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.retrieve( - "string", + "assistant_id", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.assistants.with_raw_response.retrieve( - "string", + "assistant_id", ) assert response.is_closed is True @@ -333,7 +333,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.assistants.with_streaming_response.retrieve( - "string", + "assistant_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -353,20 +353,20 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.update( - "string", + assistant_id="assistant_id", ) assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.update( - "string", - description="string", - instructions="string", + assistant_id="assistant_id", + description="description", + instructions="instructions", metadata={}, - model="string", - name="string", - response_format="none", + model="model", + name="name", + response_format="auto", temperature=1, tool_resources={ "code_interpreter": {"file_ids": ["string", "string", "string"]}, @@ -380,7 +380,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.assistants.with_raw_response.update( - "string", + assistant_id="assistant_id", ) assert response.is_closed is True @@ -391,7 +391,7 @@ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.assistants.with_streaming_response.update( - "string", + assistant_id="assistant_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -405,7 +405,7 @@ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> Non async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): await async_client.beta.assistants.with_raw_response.update( - "", + assistant_id="", ) @parametrize @@ -416,8 +416,8 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.list( - after="string", - before="string", + after="after", + before="before", limit=0, order="asc", ) @@ -446,14 +446,14 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_delete(self, async_client: AsyncOpenAI) -> None: assistant = await async_client.beta.assistants.delete( - "string", + "assistant_id", ) assert_matches_type(AssistantDeleted, assistant, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.assistants.with_raw_response.delete( - "string", + "assistant_id", ) assert response.is_closed is True @@ -464,7 +464,7 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.assistants.with_streaming_response.delete( - "string", + "assistant_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 9e06b597ef..67fff736dd 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -302,9 +302,9 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) max_completion_tokens=256, max_prompt_tokens=256, metadata={}, - model="gpt-4-turbo", + model="gpt-4o", parallel_tool_calls=True, - response_format="none", + response_format="auto", stream=False, temperature=1, thread={ @@ -473,9 +473,9 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) max_completion_tokens=256, max_prompt_tokens=256, metadata={}, - model="gpt-4-turbo", + model="gpt-4o", parallel_tool_calls=True, - response_format="none", + response_format="auto", temperature=1, thread={ "messages": [ @@ -912,9 +912,9 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie max_completion_tokens=256, max_prompt_tokens=256, metadata={}, - model="gpt-4-turbo", + model="gpt-4o", parallel_tool_calls=True, - response_format="none", + response_format="auto", stream=False, temperature=1, thread={ @@ -1083,9 +1083,9 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie max_completion_tokens=256, max_prompt_tokens=256, metadata={}, - model="gpt-4-turbo", + model="gpt-4o", parallel_tool_calls=True, - response_format="none", + response_format="auto", temperature=1, thread={ "messages": [ diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 26862ef1eb..e21c6c2c77 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -135,9 +135,9 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_completion_tokens=256, max_prompt_tokens=256, metadata={}, - model="gpt-4-turbo", + model="gpt-4o", parallel_tool_calls=True, - response_format="none", + response_format="auto", stream=False, temperature=1, tool_choice="none", @@ -299,9 +299,9 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_completion_tokens=256, max_prompt_tokens=256, metadata={}, - model="gpt-4-turbo", + model="gpt-4o", parallel_tool_calls=True, - response_format="none", + response_format="auto", temperature=1, tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], @@ -801,9 +801,9 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_completion_tokens=256, max_prompt_tokens=256, metadata={}, - model="gpt-4-turbo", + model="gpt-4o", parallel_tool_calls=True, - response_format="none", + response_format="auto", stream=False, temperature=1, tool_choice="none", @@ -965,9 +965,9 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_completion_tokens=256, max_prompt_tokens=256, metadata={}, - model="gpt-4-turbo", + model="gpt-4o", parallel_tool_calls=True, - response_format="none", + response_format="auto", temperature=1, tool_choice="none", tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 5cb2a8c717..d744dfe6ea 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -28,7 +28,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) assert_matches_type(ChatCompletion, completion, path=["response"]) @@ -42,7 +42,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "name": "string", } ], - model="gpt-4-turbo", + model="gpt-4o", frequency_penalty=-2, function_call="none", functions=[ @@ -58,7 +58,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: n=1, parallel_tool_calls=True, presence_penalty=-2, - response_format={"type": "json_object"}, + response_format={"type": "text"}, seed=-9007199254740991, service_tier="auto", stop="string", @@ -73,6 +73,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, { @@ -81,6 +82,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, { @@ -89,6 +91,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, ], @@ -107,7 +110,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) assert response.is_closed is True @@ -124,7 +127,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -143,7 +146,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", stream=True, ) completion_stream.response.close() @@ -158,7 +161,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "name": "string", } ], - model="gpt-4-turbo", + model="gpt-4o", stream=True, frequency_penalty=-2, function_call="none", @@ -175,7 +178,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: n=1, parallel_tool_calls=True, presence_penalty=-2, - response_format={"type": "json_object"}, + response_format={"type": "text"}, seed=-9007199254740991, service_tier="auto", stop="string", @@ -189,6 +192,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, { @@ -197,6 +201,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, { @@ -205,6 +210,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, ], @@ -223,7 +229,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", stream=True, ) @@ -240,7 +246,7 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", stream=True, ) as response: assert not response.is_closed @@ -264,7 +270,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) assert_matches_type(ChatCompletion, completion, path=["response"]) @@ -278,7 +284,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "name": "string", } ], - model="gpt-4-turbo", + model="gpt-4o", frequency_penalty=-2, function_call="none", functions=[ @@ -294,7 +300,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn n=1, parallel_tool_calls=True, presence_penalty=-2, - response_format={"type": "json_object"}, + response_format={"type": "text"}, seed=-9007199254740991, service_tier="auto", stop="string", @@ -309,6 +315,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, { @@ -317,6 +324,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, { @@ -325,6 +333,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, ], @@ -343,7 +352,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) - "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) assert response.is_closed is True @@ -360,7 +369,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -379,7 +388,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", stream=True, ) await completion_stream.response.aclose() @@ -394,7 +403,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "name": "string", } ], - model="gpt-4-turbo", + model="gpt-4o", stream=True, frequency_penalty=-2, function_call="none", @@ -411,7 +420,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn n=1, parallel_tool_calls=True, presence_penalty=-2, - response_format={"type": "json_object"}, + response_format={"type": "text"}, seed=-9007199254740991, service_tier="auto", stop="string", @@ -425,6 +434,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, { @@ -433,6 +443,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, { @@ -441,6 +452,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "description": "string", "name": "string", "parameters": {"foo": "bar"}, + "strict": True, }, }, ], @@ -459,7 +471,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) - "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", stream=True, ) @@ -476,7 +488,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpe "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", stream=True, ) as response: assert not response.is_closed diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 1ff6d63b31..68b3d73ac5 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -24,7 +24,7 @@ class TestJobs: @parametrize def test_method_create(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", + model="gpt-4o-mini", training_file="file-abc123", ) assert_matches_type(FineTuningJob, job, path=["response"]) @@ -32,7 +32,7 @@ def test_method_create(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", + model="gpt-4o-mini", training_file="file-abc123", hyperparameters={ "batch_size": "auto", @@ -77,7 +77,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.fine_tuning.jobs.with_raw_response.create( - model="gpt-3.5-turbo", + model="gpt-4o-mini", training_file="file-abc123", ) @@ -89,7 +89,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: with client.fine_tuning.jobs.with_streaming_response.create( - model="gpt-3.5-turbo", + model="gpt-4o-mini", training_file="file-abc123", ) as response: assert not response.is_closed @@ -263,7 +263,7 @@ class TestAsyncJobs: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: job = await async_client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", + model="gpt-4o-mini", training_file="file-abc123", ) assert_matches_type(FineTuningJob, job, path=["response"]) @@ -271,7 +271,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: job = await async_client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", + model="gpt-4o-mini", training_file="file-abc123", hyperparameters={ "batch_size": "auto", @@ -316,7 +316,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.fine_tuning.jobs.with_raw_response.create( - model="gpt-3.5-turbo", + model="gpt-4o-mini", training_file="file-abc123", ) @@ -328,7 +328,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.fine_tuning.jobs.with_streaming_response.create( - model="gpt-3.5-turbo", + model="gpt-4o-mini", training_file="file-abc123", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py index 71f8e5834b..8791507c3e 100644 --- a/tests/api_resources/test_models.py +++ b/tests/api_resources/test_models.py @@ -21,14 +21,14 @@ class TestModels: @parametrize def test_method_retrieve(self, client: OpenAI) -> None: model = client.models.retrieve( - "gpt-3.5-turbo", + "gpt-4o-mini", ) assert_matches_type(Model, model, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.models.with_raw_response.retrieve( - "gpt-3.5-turbo", + "gpt-4o-mini", ) assert response.is_closed is True @@ -39,7 +39,7 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: @parametrize def test_streaming_response_retrieve(self, client: OpenAI) -> None: with client.models.with_streaming_response.retrieve( - "gpt-3.5-turbo", + "gpt-4o-mini", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -84,14 +84,14 @@ def test_streaming_response_list(self, client: OpenAI) -> None: @parametrize def test_method_delete(self, client: OpenAI) -> None: model = client.models.delete( - "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + "ft:gpt-4o-mini:acemeco:suffix:abc123", ) assert_matches_type(ModelDeleted, model, path=["response"]) @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: response = client.models.with_raw_response.delete( - "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + "ft:gpt-4o-mini:acemeco:suffix:abc123", ) assert response.is_closed is True @@ -102,7 +102,7 @@ def test_raw_response_delete(self, client: OpenAI) -> None: @parametrize def test_streaming_response_delete(self, client: OpenAI) -> None: with client.models.with_streaming_response.delete( - "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + "ft:gpt-4o-mini:acemeco:suffix:abc123", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -126,14 +126,14 @@ class TestAsyncModels: @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: model = await async_client.models.retrieve( - "gpt-3.5-turbo", + "gpt-4o-mini", ) assert_matches_type(Model, model, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: response = await async_client.models.with_raw_response.retrieve( - "gpt-3.5-turbo", + "gpt-4o-mini", ) assert response.is_closed is True @@ -144,7 +144,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: async with async_client.models.with_streaming_response.retrieve( - "gpt-3.5-turbo", + "gpt-4o-mini", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -189,14 +189,14 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_delete(self, async_client: AsyncOpenAI) -> None: model = await async_client.models.delete( - "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + "ft:gpt-4o-mini:acemeco:suffix:abc123", ) assert_matches_type(ModelDeleted, model, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: response = await async_client.models.with_raw_response.delete( - "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + "ft:gpt-4o-mini:acemeco:suffix:abc123", ) assert response.is_closed is True @@ -207,7 +207,7 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: async with async_client.models.with_streaming_response.delete( - "ft:gpt-3.5-turbo:acemeco:suffix:abc123", + "ft:gpt-4o-mini:acemeco:suffix:abc123", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/lib/chat/__init__.py b/tests/lib/chat/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/lib/chat/_utils.py b/tests/lib/chat/_utils.py new file mode 100644 index 0000000000..dcc32b17fd --- /dev/null +++ b/tests/lib/chat/_utils.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import io +import inspect +from typing import Any, Iterable +from typing_extensions import TypeAlias + +import rich +import pytest +import pydantic + +ReprArgs: TypeAlias = "Iterable[tuple[str | None, Any]]" + + +def print_obj(obj: object, monkeypatch: pytest.MonkeyPatch) -> str: + """Pretty print an object to a string""" + + # monkeypatch pydantic model printing so that model fields + # are always printed in the same order so we can reliably + # use this for snapshot tests + original_repr = pydantic.BaseModel.__repr_args__ + + def __repr_args__(self: pydantic.BaseModel) -> ReprArgs: + return sorted(original_repr(self), key=lambda arg: arg[0] or arg) + + with monkeypatch.context() as m: + m.setattr(pydantic.BaseModel, "__repr_args__", __repr_args__) + + buf = io.StringIO() + + console = rich.console.Console(file=buf, width=120) + console.print(obj) + + string = buf.getvalue() + + # we remove all `fn_name..` occurences + # so that we can share the same snapshots between + # pydantic v1 and pydantic v2 as their output for + # generic models differs, e.g. + # + # v2: `ParsedChatCompletion[test_parse_pydantic_model..Location]` + # v1: `ParsedChatCompletion[Location]` + return clear_locals(string, stacklevel=2) + + +def get_caller_name(*, stacklevel: int = 1) -> str: + frame = inspect.currentframe() + assert frame is not None + + for i in range(stacklevel): + frame = frame.f_back + assert frame is not None, f"no {i}th frame" + + return frame.f_code.co_name + + +def clear_locals(string: str, *, stacklevel: int) -> str: + caller = get_caller_name(stacklevel=stacklevel + 1) + return string.replace(f"{caller}..", "") diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py new file mode 100644 index 0000000000..db370e4332 --- /dev/null +++ b/tests/lib/chat/test_completions.py @@ -0,0 +1,633 @@ +from __future__ import annotations + +import os +import json +from typing import Any, Callable +from typing_extensions import Literal, TypeVar + +import httpx +import pytest +from respx import MockRouter +from pydantic import BaseModel +from inline_snapshot import snapshot + +import openai +from openai import OpenAI, AsyncOpenAI +from openai._utils import assert_signatures_in_sync + +from ._utils import print_obj +from ...conftest import base_url +from ..schema_types.query import Query + +_T = TypeVar("_T") + +# all the snapshots in this file are auto-generated from the live API +# +# you can update them with +# +# `OPENAI_LIVE=1 pytest --inline-snapshot=fix` + + +@pytest.mark.respx(base_url=base_url) +def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tABLlmqdEOYnmmWATUI3dNKlfXa3", "object": "chat.completion", "created": 1722934207, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "I\'m unable to provide real-time weather updates. For the current weather in San Francisco, I recommend checking a reliable weather website or app.", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 14, "completion_tokens": 27, "total_tokens": 41}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion, monkeypatch) == snapshot( + """\ +ParsedChatCompletion[NoneType]( + choices=[ + ParsedChoice[NoneType]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + content="I'm unable to provide real-time weather updates. For the current weather in San Francisco, I +recommend checking a reliable weather website or app.", + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[] + ) + ) + ], + created=1722934207, + id='chatcmpl-9tABLlmqdEOYnmmWATUI3dNKlfXa3', + model='gpt-4o-2024-08-06', + object='chat.completion', + service_tier=None, + system_fingerprint='fp_e1a05a1dce', + usage=CompletionUsage(completion_tokens=27, prompt_tokens=14, total_tokens=41) +) +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_model(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + response_format=Location, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tABUwdw3Kbe3VPRnMofh9lJkFkLV", "object": "chat.completion", "created": 1722934216, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 14, "total_tokens": 31}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion, monkeypatch) == snapshot( + """\ +ParsedChatCompletion[Location]( + choices=[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":65,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=65.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ) + ], + created=1722934216, + id='chatcmpl-9tABUwdw3Kbe3VPRnMofh9lJkFkLV', + model='gpt-4o-2024-08-06', + object='chat.completion', + service_tier=None, + system_fingerprint='fp_e1a05a1dce', + usage=CompletionUsage(completion_tokens=14, prompt_tokens=17, total_tokens=31) +) +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_model_multiple_choices( + client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch +) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + n=3, + response_format=Location, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tABVfBu4ZdyQFKe8RgsWsyL7UoIj", "object": "chat.completion", "created": 1722934217, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":58.0,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 1, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":61,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 2, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 44, "total_tokens": 61}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion.choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":58.0,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=58.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ), + ParsedChoice[Location]( + finish_reason='stop', + index=1, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":61,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=61.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ), + ParsedChoice[Location]( + finish_reason='stop', + index=2, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":65,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=65.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "look up all my orders in may of last year that were fulfilled but not delivered on time", + }, + ], + tools=[openai.pydantic_function_tool(Query)], + response_format=Query, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tABVRLORZbby5zZjZhyrUdDU1XhB", "object": "chat.completion", "created": 1722934217, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_VcgQcA1C047fQnXDG0PQXG7O", "type": "function", "function": {"name": "Query", "arguments": "{\\"table_name\\":\\"orders\\",\\"columns\\":[\\"id\\",\\"status\\",\\"expected_delivery_date\\",\\"delivered_at\\"],\\"conditions\\":[{\\"column\\":\\"ordered_at\\",\\"operator\\":\\"=\\",\\"value\\":\\"2022-05\\"},{\\"column\\":\\"status\\",\\"operator\\":\\"=\\",\\"value\\":\\"fulfilled\\"},{\\"column\\":\\"delivered_at\\",\\"operator\\":\\">\\",\\"value\\":{\\"column_name\\":\\"expected_delivery_date\\"}}],\\"order_by\\":\\"asc\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 195, "completion_tokens": 85, "total_tokens": 280}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion.choices[0], monkeypatch) == snapshot( + """\ +ParsedChoice[Query]( + finish_reason='tool_calls', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Query]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"table_name":"orders","columns":["id","status","expected_delivery_date","delivered_at"], +"conditions":[{"column":"ordered_at","operator":"=","value":"2022-05"},{"column":"status","operator":"=","value":"fulfil +led"},{"column":"delivered_at","operator":">","value":{"column_name":"expected_delivery_date"}}],"order_by":"asc"}', + name='Query', + parsed_arguments=Query( + columns=[ + , + , + , + + ], + conditions=[ + Condition(column='ordered_at', operator='>, + value=DynamicValue(column_name='expected_delivery_date') + ) + ], + order_by=, + table_name= + ) + ), + id='call_VcgQcA1C047fQnXDG0PQXG7O', + type='function' + ) + ] + ) +) +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_max_tokens_reached(client: OpenAI, respx_mock: MockRouter) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + with pytest.raises(openai.LengthFinishReasonError): + _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + max_tokens=1, + response_format=Location, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tABXbi3qast6oJvdaqQcK9C7k9fn", "object": "chat.completion", "created": 1722934219, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"", "refusal": null}, "logprobs": null, "finish_reason": "length"}], "usage": {"prompt_tokens": 17, "completion_tokens": 1, "total_tokens": 18}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_model_refusal(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "How do I make anthrax?", + }, + ], + response_format=Location, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tABXJEffhEWxp24MeLxkDJCMtWmx", "object": "chat.completion", "created": 1722934219, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "refusal": "I\'m very sorry, but I can\'t assist with that."}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 12, "total_tokens": 29}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion.choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content=None, + function_call=None, + parsed=None, + refusal="I'm very sorry, but I can't assist with that.", + role='assistant', + tool_calls=[] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_tool(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class GetWeatherArgs(BaseModel): + city: str + country: str + units: Literal["c", "f"] = "c" + + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in Edinburgh?", + }, + ], + tools=[ + openai.pydantic_function_tool(GetWeatherArgs), + ], + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tABgtKnF7Gbri4CmpOocmhg0UgBF", "object": "chat.completion", "created": 1722934228, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_9rqjEc1DQRADTYGVV45LbZwL", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\":\\"Edinburgh\\",\\"country\\":\\"UK\\",\\"units\\":\\"c\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 76, "completion_tokens": 24, "total_tokens": 100}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion.choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[NoneType]( + finish_reason='tool_calls', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city":"Edinburgh","country":"UK","units":"c"}', + name='GetWeatherArgs', + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + ), + id='call_9rqjEc1DQRADTYGVV45LbZwL', + type='function' + ) + ] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_multiple_pydantic_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class GetWeatherArgs(BaseModel): + """Get the temperature for the given country/city combo""" + + city: str + country: str + units: Literal["c", "f"] = "c" + + class GetStockPrice(BaseModel): + ticker: str + exchange: str + + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in Edinburgh?", + }, + { + "role": "user", + "content": "What's the price of AAPL?", + }, + ], + tools=[ + openai.pydantic_function_tool(GetWeatherArgs), + openai.pydantic_function_tool( + GetStockPrice, name="get_stock_price", description="Fetch the latest price for a given ticker" + ), + ], + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tABqDpvDTi0Cg8PHtKdNSFoh4UJv", "object": "chat.completion", "created": 1722934238, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_Yeg67XmQbMcohm3NGj0g12ty", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\": \\"Edinburgh\\", \\"country\\": \\"GB\\", \\"units\\": \\"c\\"}"}}, {"id": "call_OGg3UZC2ksjAg7yrLXy8t1MO", "type": "function", "function": {"name": "get_stock_price", "arguments": "{\\"ticker\\": \\"AAPL\\", \\"exchange\\": \\"NASDAQ\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 149, "completion_tokens": 60, "total_tokens": 209}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion.choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[NoneType]( + finish_reason='tool_calls', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city": "Edinburgh", "country": "GB", "units": "c"}', + name='GetWeatherArgs', + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') + ), + id='call_Yeg67XmQbMcohm3NGj0g12ty', + type='function' + ), + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"ticker": "AAPL", "exchange": "NASDAQ"}', + name='get_stock_price', + parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') + ), + id='call_OGg3UZC2ksjAg7yrLXy8t1MO', + type='function' + ) + ] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + tools=[ + { + "type": "function", + "function": { + "name": "get_weather", + "parameters": { + "type": "object", + "properties": { + "city": {"type": "string"}, + "state": {"type": "string"}, + }, + "required": [ + "city", + "state", + ], + "additionalProperties": False, + }, + "strict": True, + }, + } + ], + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9tAC0vDx3MfupXmsduSZavLVaLcrA", "object": "chat.completion", "created": 1722934248, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_iNznvWR4R81mizFFHjgh7o4i", "type": "function", "function": {"name": "get_weather", "arguments": "{\\"city\\":\\"San Francisco\\",\\"state\\":\\"CA\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 48, "completion_tokens": 19, "total_tokens": 67}, "system_fingerprint": "fp_e1a05a1dce"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion.choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[NoneType]( + finish_reason='tool_calls', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city":"San Francisco","state":"CA"}', + name='get_weather', + parsed_arguments={'city': 'San Francisco', 'state': 'CA'} + ), + id='call_iNznvWR4R81mizFFHjgh7o4i', + type='function' + ) + ] + ) + ) +] +""" + ) + + +def test_parse_non_strict_tools(client: OpenAI) -> None: + with pytest.raises( + ValueError, match="`get_weather` is not strict. Only `strict` function tools can be auto-parsed" + ): + client.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[], + tools=[ + { + "type": "function", + "function": { + "name": "get_weather", + "parameters": {}, + }, + } + ], + ) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_parse_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + assert_signatures_in_sync( + checking_client.chat.completions.create, + checking_client.beta.chat.completions.parse, + exclude_params={"response_format", "stream"}, + ) + + +def _make_snapshot_request( + func: Callable[[OpenAI], _T], + *, + content_snapshot: Any, + respx_mock: MockRouter, + mock_client: OpenAI, +) -> _T: + live = os.environ.get("OPENAI_LIVE") == "1" + if live: + + def _on_response(response: httpx.Response) -> None: + # update the content snapshot + assert json.dumps(json.loads(response.read())) == content_snapshot + + respx_mock.stop() + + client = OpenAI( + http_client=httpx.Client( + event_hooks={ + "response": [_on_response], + } + ) + ) + else: + respx_mock.post("/chat/completions").mock( + return_value=httpx.Response( + 200, + content=content_snapshot._old_value, + headers={"content-type": "application/json"}, + ) + ) + + client = mock_client + + result = func(client) + + if live: + client.close() + + return result diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py new file mode 100644 index 0000000000..3aaa9a0f38 --- /dev/null +++ b/tests/lib/chat/test_completions_streaming.py @@ -0,0 +1,1047 @@ +from __future__ import annotations + +import os +from typing import Any, Generic, Callable, Iterator, cast, overload +from typing_extensions import Literal, TypeVar + +import rich +import httpx +import pytest +from respx import MockRouter +from pydantic import BaseModel +from inline_snapshot import external, snapshot, outsource + +import openai +from openai import OpenAI, AsyncOpenAI +from openai._utils import assert_signatures_in_sync +from openai._compat import model_copy +from openai.lib.streaming.chat import ( + ContentDoneEvent, + ChatCompletionStream, + ChatCompletionStreamEvent, + ChatCompletionStreamManager, + ParsedChatCompletionSnapshot, +) +from openai.lib._parsing._completions import ResponseFormatT + +from ._utils import print_obj +from ...conftest import base_url + +_T = TypeVar("_T") + +# all the snapshots in this file are auto-generated from the live API +# +# you can update them with +# +# `OPENAI_LIVE=1 pytest --inline-snapshot=fix` + + +@pytest.mark.respx(base_url=base_url) +def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + ), + content_snapshot=snapshot(external("b9d6bee9f9b8*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[NoneType]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + content="I'm unable to provide real-time weather updates. To get the latest weather information for San +Francisco, I recommend checking a reliable weather website or using a weather app.", + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[] + ) + ) +] +""" + ) + assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot( + """\ +ContentDoneEvent[NoneType]( + content="I'm unable to provide real-time weather updates. To get the latest weather information for San Francisco, I +recommend checking a reliable weather website or using a weather app.", + parsed=None, + type='content.done' +) +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_model(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + done_snapshots: list[ParsedChatCompletionSnapshot] = [] + + def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStreamEvent[Location]) -> None: + if event.type == "content.done": + done_snapshots.append(model_copy(stream.current_completion_snapshot, deep=True)) + + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + response_format=Location, + ), + content_snapshot=snapshot(external("ea9a417d533b*.bin")), + mock_client=client, + respx_mock=respx_mock, + on_event=on_event, + ) + + assert len(done_snapshots) == 1 + assert isinstance(done_snapshots[0].choices[0].message.parsed, Location) + + for event in reversed(listener.events): + if event.type == "content.delta": + data = cast(Any, event.parsed) + assert isinstance(data["city"], str), data + assert isinstance(data["temperature"], (int, float)), data + assert isinstance(data["units"], str), data + break + else: + rich.print(listener.events) + raise AssertionError("Did not find a `content.delta` event") + + assert print_obj(listener.stream.get_final_completion(), monkeypatch) == snapshot( + """\ +ParsedChatCompletion[Location]( + choices=[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":63,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=63.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ) + ], + created=1722934250, + id='chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv', + model='gpt-4o-so', + object='chat.completion', + service_tier=None, + system_fingerprint='fp_e1a05a1dce', + usage=CompletionUsage(completion_tokens=14, prompt_tokens=17, total_tokens=31) +) +""" + ) + assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot( + """\ +ContentDoneEvent[Location]( + content='{"city":"San Francisco","temperature":63,"units":"f"}', + parsed=Location(city='San Francisco', temperature=63.0, units='f'), + type='content.done' +) +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_model_multiple_choices( + client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch +) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + n=3, + response_format=Location, + ), + content_snapshot=snapshot(external("1437bd06a9d5*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert [e.type for e in listener.events] == snapshot( + [ + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.delta", + "chunk", + "content.done", + "chunk", + "content.done", + "chunk", + "content.done", + "chunk", + ] + ) + assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":64,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=64.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ), + ParsedChoice[Location]( + finish_reason='stop', + index=1, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":68,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=68.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ), + ParsedChoice[Location]( + finish_reason='stop', + index=2, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":64,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=64.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_max_tokens_reached(client: OpenAI, respx_mock: MockRouter) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + with pytest.raises(openai.LengthFinishReasonError): + _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + max_tokens=1, + response_format=Location, + ), + content_snapshot=snapshot(external("7ae6c1a2631b*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_model_refusal(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "How do I make anthrax?", + }, + ], + response_format=Location, + ), + content_snapshot=snapshot(external("d79326933c15*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(listener.get_event_by_type("refusal.done"), monkeypatch) == snapshot("""\ +RefusalDoneEvent(refusal="I'm very sorry, but I can't assist with that request.", type='refusal.done') +""") + + assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content=None, + function_call=None, + parsed=None, + refusal="I'm very sorry, but I can't assist with that request.", + role='assistant', + tool_calls=[] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "Say foo", + }, + ], + logprobs=True, + ), + content_snapshot=snapshot(external("70c7df71ce72*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj([e for e in listener.events if e.type.startswith("logprobs")], monkeypatch) == snapshot("""\ +[ + LogprobsContentDeltaEvent( + content=[ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[])], + snapshot=[ + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[]) + ], + type='logprobs.content.delta' + ), + LogprobsContentDeltaEvent( + content=[ChatCompletionTokenLogprob(bytes=[33], logprob=-0.31380808, token='!', top_logprobs=[])], + snapshot=[ + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[33], logprob=-0.31380808, token='!', top_logprobs=[]) + ], + type='logprobs.content.delta' + ), + LogprobsContentDoneEvent( + content=[ + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[33], logprob=-0.31380808, token='!', top_logprobs=[]) + ], + type='logprobs.content.done' + ) +] +""") + + assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot("""\ +[ + ParsedChoice[NoneType]( + finish_reason='stop', + index=0, + logprobs=ChoiceLogprobs( + content=[ + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[33], logprob=-0.31380808, token='!', top_logprobs=[]) + ], + refusal=None + ), + message=ParsedChatCompletionMessage[NoneType]( + content='Foo!', + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[] + ) + ) +] +""") + + +@pytest.mark.respx(base_url=base_url) +def test_refusal_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "How do I make anthrax?", + }, + ], + logprobs=True, + response_format=Location, + ), + content_snapshot=snapshot(external("cb77dc69b6c8*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj([e.type for e in listener.events if e.type.startswith("logprobs")], monkeypatch) == snapshot("""\ +[ + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.delta', + 'logprobs.refusal.done' +] +""") + + assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot("""\ +[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=ChoiceLogprobs( + content=None, + refusal=[ + ChatCompletionTokenLogprob(bytes=[73, 39, 109], logprob=-0.0010472201, token="I'm", top_logprobs=[]), + ChatCompletionTokenLogprob( + bytes=[32, 118, 101, 114, 121], + logprob=-0.7292482, + token=' very', + top_logprobs=[] + ), + ChatCompletionTokenLogprob( + bytes=[32, 115, 111, 114, 114, 121], + logprob=-5.080963e-06, + token=' sorry', + top_logprobs=[] + ), + ChatCompletionTokenLogprob(bytes=[44], logprob=-4.048445e-05, token=',', top_logprobs=[]), + ChatCompletionTokenLogprob( + bytes=[32, 98, 117, 116], + logprob=-0.038046427, + token=' but', + top_logprobs=[] + ), + ChatCompletionTokenLogprob(bytes=[32, 73], logprob=-0.0019351852, token=' I', top_logprobs=[]), + ChatCompletionTokenLogprob( + bytes=[32, 99, 97, 110, 39, 116], + logprob=-0.008995773, + token=" can't", + top_logprobs=[] + ), + ChatCompletionTokenLogprob( + bytes=[32, 97, 115, 115, 105, 115, 116], + logprob=-0.0033510819, + token=' assist', + top_logprobs=[] + ), + ChatCompletionTokenLogprob( + bytes=[32, 119, 105, 116, 104], + logprob=-0.0036033941, + token=' with', + top_logprobs=[] + ), + ChatCompletionTokenLogprob( + bytes=[32, 116, 104, 97, 116], + logprob=-0.0015974608, + token=' that', + top_logprobs=[] + ), + ChatCompletionTokenLogprob(bytes=[46], logprob=-0.6339823, token='.', top_logprobs=[]) + ] + ), + message=ParsedChatCompletionMessage[Location]( + content=None, + function_call=None, + parsed=None, + refusal="I'm very sorry, but I can't assist with that.", + role='assistant', + tool_calls=[] + ) + ) +] +""") + + +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_tool(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class GetWeatherArgs(BaseModel): + city: str + country: str + units: Literal["c", "f"] = "c" + + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in Edinburgh?", + }, + ], + tools=[ + openai.pydantic_function_tool(GetWeatherArgs), + ], + ), + content_snapshot=snapshot(external("ae070a447e1d*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[object]( + finish_reason='tool_calls', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[object]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city":"Edinburgh","country":"UK","units":"c"}', + name='GetWeatherArgs', + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + ), + id='call_Vz6ZXciy6Y0PYfT4d9W7fYB4', + index=0, + type='function' + ) + ] + ) + ) +] +""" + ) + + assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[NoneType]( + finish_reason='tool_calls', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city":"Edinburgh","country":"UK","units":"c"}', + name='GetWeatherArgs', + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + ), + id='call_Vz6ZXciy6Y0PYfT4d9W7fYB4', + index=0, + type='function' + ) + ] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_multiple_pydantic_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class GetWeatherArgs(BaseModel): + """Get the temperature for the given country/city combo""" + + city: str + country: str + units: Literal["c", "f"] = "c" + + class GetStockPrice(BaseModel): + ticker: str + exchange: str + + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in Edinburgh?", + }, + { + "role": "user", + "content": "What's the price of AAPL?", + }, + ], + tools=[ + openai.pydantic_function_tool(GetWeatherArgs), + openai.pydantic_function_tool( + GetStockPrice, name="get_stock_price", description="Fetch the latest price for a given ticker" + ), + ], + ), + content_snapshot=snapshot(external("a346213bec7a*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[object]( + finish_reason='tool_calls', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[object]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city": "Edinburgh", "country": "UK", "units": "c"}', + name='GetWeatherArgs', + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + ), + id='call_g4Q1vRbE0CaHGOs5if8mHsBq', + index=0, + type='function' + ), + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"ticker": "AAPL", "exchange": "NASDAQ"}', + name='get_stock_price', + parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') + ), + id='call_gWj3HQxZEHnFvyJLEHIiJKBV', + index=1, + type='function' + ) + ] + ) + ) +] +""" + ) + completion = listener.stream.get_final_completion() + assert print_obj(completion.choices[0].message.tool_calls, monkeypatch) == snapshot( + """\ +[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city": "Edinburgh", "country": "UK", "units": "c"}', + name='GetWeatherArgs', + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + ), + id='call_g4Q1vRbE0CaHGOs5if8mHsBq', + index=0, + type='function' + ), + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"ticker": "AAPL", "exchange": "NASDAQ"}', + name='get_stock_price', + parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') + ), + id='call_gWj3HQxZEHnFvyJLEHIiJKBV', + index=1, + type='function' + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + tools=[ + { + "type": "function", + "function": { + "name": "get_weather", + "parameters": { + "type": "object", + "properties": { + "city": {"type": "string"}, + "state": {"type": "string"}, + }, + "required": [ + "city", + "state", + ], + "additionalProperties": False, + }, + "strict": True, + }, + } + ], + ), + content_snapshot=snapshot(external("a7097cae6a1f*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[object]( + finish_reason='tool_calls', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[object]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city":"San Francisco","state":"CA"}', + name='get_weather', + parsed_arguments={'city': 'San Francisco', 'state': 'CA'} + ), + id='call_rQe3kzGnTr2epjx8HREg3F2a', + index=0, + type='function' + ) + ] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF? Give me any JSON back", + }, + ], + response_format={"type": "json_object"}, + ), + content_snapshot=snapshot(external("3e0df46f250d*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[NoneType]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + content='{\\n "location": "San Francisco, CA",\\n "temperature": "N/A",\\n "conditions": "N/A",\\n +"humidity": "N/A",\\n "wind_speed": "N/A",\\n "timestamp": "N/A",\\n "note": "Real-time weather data is not available. +Please check a reliable weather service for the most up-to-date information on San Francisco\\'s weather conditions."}', + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[] + ) + ) +] +""" + ) + + +@pytest.mark.respx(base_url=base_url) +def test_allows_non_strict_tools_but_no_parsing( + client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch +) -> None: + listener = _make_stream_snapshot_request( + lambda c: c.beta.chat.completions.stream( + model="gpt-4o-2024-08-06", + messages=[{"role": "user", "content": "what's the weather in NYC?"}], + tools=[ + { + "type": "function", + "function": { + "name": "get_weather", + "parameters": {"type": "object", "properties": {"city": {"type": "string"}}}, + }, + } + ], + ), + content_snapshot=snapshot(external("fb75060ede89*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(listener.get_event_by_type("tool_calls.function.arguments.done"), monkeypatch) == snapshot("""\ +FunctionToolCallArgumentsDoneEvent( + arguments='{"city":"New York City"}', + index=0, + name='get_weather', + parsed_arguments=None, + type='tool_calls.function.arguments.done' +) +""") + + assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[NoneType]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + content=None, + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[ + ParsedFunctionToolCall( + function=ParsedFunction( + arguments='{"city":"New York City"}', + name='get_weather', + parsed_arguments=None + ), + id='call_9rqjEc1DQRADTYGVV45LbZwL', + index=0, + type='function' + ) + ] + ) + ) +] +""" + ) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_stream_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + assert_signatures_in_sync( + checking_client.chat.completions.create, + checking_client.beta.chat.completions.stream, + exclude_params={"response_format", "stream"}, + ) + + +class StreamListener(Generic[ResponseFormatT]): + def __init__(self, stream: ChatCompletionStream[ResponseFormatT]) -> None: + self.stream = stream + self.events: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] + + def __iter__(self) -> Iterator[ChatCompletionStreamEvent[ResponseFormatT]]: + for event in self.stream: + self.events.append(event) + yield event + + @overload + def get_event_by_type(self, event_type: Literal["content.done"]) -> ContentDoneEvent[ResponseFormatT] | None: ... + + @overload + def get_event_by_type(self, event_type: str) -> ChatCompletionStreamEvent[ResponseFormatT] | None: ... + + def get_event_by_type(self, event_type: str) -> ChatCompletionStreamEvent[ResponseFormatT] | None: + return next((e for e in self.events if e.type == event_type), None) + + +def _make_stream_snapshot_request( + func: Callable[[OpenAI], ChatCompletionStreamManager[ResponseFormatT]], + *, + content_snapshot: Any, + respx_mock: MockRouter, + mock_client: OpenAI, + on_event: Callable[[ChatCompletionStream[ResponseFormatT], ChatCompletionStreamEvent[ResponseFormatT]], Any] + | None = None, +) -> StreamListener[ResponseFormatT]: + live = os.environ.get("OPENAI_LIVE") == "1" + if live: + + def _on_response(response: httpx.Response) -> None: + # update the content snapshot + assert outsource(response.read()) == content_snapshot + + respx_mock.stop() + + client = OpenAI( + http_client=httpx.Client( + event_hooks={ + "response": [_on_response], + } + ) + ) + else: + respx_mock.post("/chat/completions").mock( + return_value=httpx.Response( + 200, + content=content_snapshot._old_value._load_value(), + headers={"content-type": "text/event-stream"}, + ) + ) + + client = mock_client + + with func(client) as stream: + listener = StreamListener(stream) + + for event in listener: + if on_event: + on_event(stream, event) + + if live: + client.close() + + return listener diff --git a/tests/lib/schema_types/query.py b/tests/lib/schema_types/query.py new file mode 100644 index 0000000000..d2284424f0 --- /dev/null +++ b/tests/lib/schema_types/query.py @@ -0,0 +1,51 @@ +from enum import Enum +from typing import List, Union + +from pydantic import BaseModel + + +class Table(str, Enum): + orders = "orders" + customers = "customers" + products = "products" + + +class Column(str, Enum): + id = "id" + status = "status" + expected_delivery_date = "expected_delivery_date" + delivered_at = "delivered_at" + shipped_at = "shipped_at" + ordered_at = "ordered_at" + canceled_at = "canceled_at" + + +class Operator(str, Enum): + eq = "=" + gt = ">" + lt = "<" + le = "<=" + ge = ">=" + ne = "!=" + + +class OrderBy(str, Enum): + asc = "asc" + desc = "desc" + + +class DynamicValue(BaseModel): + column_name: str + + +class Condition(BaseModel): + column: str + operator: Operator + value: Union[str, int, DynamicValue] + + +class Query(BaseModel): + table_name: Table + columns: List[Column] + conditions: List[Condition] + order_by: OrderBy diff --git a/tests/lib/test_pydantic.py b/tests/lib/test_pydantic.py new file mode 100644 index 0000000000..dc09596da2 --- /dev/null +++ b/tests/lib/test_pydantic.py @@ -0,0 +1,161 @@ +from __future__ import annotations + +from inline_snapshot import snapshot + +import openai +from openai._compat import PYDANTIC_V2 + +from .schema_types.query import Query + + +def test_most_types() -> None: + if PYDANTIC_V2: + assert openai.pydantic_function_tool(Query)["function"] == snapshot( + { + "name": "Query", + "strict": True, + "parameters": { + "$defs": { + "Column": { + "enum": [ + "id", + "status", + "expected_delivery_date", + "delivered_at", + "shipped_at", + "ordered_at", + "canceled_at", + ], + "title": "Column", + "type": "string", + }, + "Condition": { + "properties": { + "column": {"title": "Column", "type": "string"}, + "operator": {"$ref": "#/$defs/Operator"}, + "value": { + "anyOf": [ + {"type": "string"}, + {"type": "integer"}, + {"$ref": "#/$defs/DynamicValue"}, + ], + "title": "Value", + }, + }, + "required": ["column", "operator", "value"], + "title": "Condition", + "type": "object", + "additionalProperties": False, + }, + "DynamicValue": { + "properties": {"column_name": {"title": "Column Name", "type": "string"}}, + "required": ["column_name"], + "title": "DynamicValue", + "type": "object", + "additionalProperties": False, + }, + "Operator": {"enum": ["=", ">", "<", "<=", ">=", "!="], "title": "Operator", "type": "string"}, + "OrderBy": {"enum": ["asc", "desc"], "title": "OrderBy", "type": "string"}, + "Table": {"enum": ["orders", "customers", "products"], "title": "Table", "type": "string"}, + }, + "properties": { + "table_name": {"$ref": "#/$defs/Table"}, + "columns": { + "items": {"$ref": "#/$defs/Column"}, + "title": "Columns", + "type": "array", + }, + "conditions": { + "items": {"$ref": "#/$defs/Condition"}, + "title": "Conditions", + "type": "array", + }, + "order_by": {"$ref": "#/$defs/OrderBy"}, + }, + "required": ["table_name", "columns", "conditions", "order_by"], + "title": "Query", + "type": "object", + "additionalProperties": False, + }, + } + ) + else: + assert openai.pydantic_function_tool(Query)["function"] == snapshot( + { + "name": "Query", + "strict": True, + "parameters": { + "title": "Query", + "type": "object", + "properties": { + "table_name": {"$ref": "#/definitions/Table"}, + "columns": {"type": "array", "items": {"$ref": "#/definitions/Column"}}, + "conditions": { + "title": "Conditions", + "type": "array", + "items": {"$ref": "#/definitions/Condition"}, + }, + "order_by": {"$ref": "#/definitions/OrderBy"}, + }, + "required": ["table_name", "columns", "conditions", "order_by"], + "definitions": { + "Table": { + "title": "Table", + "description": "An enumeration.", + "enum": ["orders", "customers", "products"], + "type": "string", + }, + "Column": { + "title": "Column", + "description": "An enumeration.", + "enum": [ + "id", + "status", + "expected_delivery_date", + "delivered_at", + "shipped_at", + "ordered_at", + "canceled_at", + ], + "type": "string", + }, + "Operator": { + "title": "Operator", + "description": "An enumeration.", + "enum": ["=", ">", "<", "<=", ">=", "!="], + "type": "string", + }, + "DynamicValue": { + "title": "DynamicValue", + "type": "object", + "properties": {"column_name": {"title": "Column Name", "type": "string"}}, + "required": ["column_name"], + }, + "Condition": { + "title": "Condition", + "type": "object", + "properties": { + "column": {"title": "Column", "type": "string"}, + "operator": {"$ref": "#/definitions/Operator"}, + "value": { + "title": "Value", + "anyOf": [ + {"type": "string"}, + {"type": "integer"}, + {"$ref": "#/definitions/DynamicValue"}, + ], + }, + }, + "required": ["column", "operator", "value"], + }, + "OrderBy": { + "title": "OrderBy", + "description": "An enumeration.", + "enum": ["asc", "desc"], + "type": "string", + }, + }, + "additionalProperties": False, + }, + } + ) diff --git a/tests/test_client.py b/tests/test_client.py index 2402ffa82f..054ae0ff4e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -780,11 +780,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: response = client.chat.completions.with_raw_response.create( messages=[ { - "content": "content", + "content": "string", "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) assert response.retries_taken == failures_before_success @@ -811,11 +811,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: with client.chat.completions.with_streaming_response.create( messages=[ { - "content": "content", + "content": "string", "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) as response: assert response.retries_taken == failures_before_success @@ -1574,11 +1574,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: response = await client.chat.completions.with_raw_response.create( messages=[ { - "content": "content", + "content": "string", "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) assert response.retries_taken == failures_before_success @@ -1606,10 +1606,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: async with client.chat.completions.with_streaming_response.create( messages=[ { - "content": "content", + "content": "string", "role": "system", } ], - model="gpt-4-turbo", + model="gpt-4o", ) as response: assert response.retries_taken == failures_before_success From 90dd21531efe351b72ce0a72150048b6c7f640e0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:11:59 +0000 Subject: [PATCH 545/914] release: 1.40.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4d14a67e1c..0c37ae42ca 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.39.0" + ".": "1.40.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b9cc30e307..2454a9a6cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.40.0 (2024-08-06) + +Full Changelog: [v1.39.0...v1.40.0](https://github.com/openai/openai-python/compare/v1.39.0...v1.40.0) + +### Features + +* **api:** add structured outputs support ([e8dba7d](https://github.com/openai/openai-python/commit/e8dba7d0e08a7d0de5952be716e0efe9ae373759)) + + +### Chores + +* **internal:** bump ruff version ([#1604](https://github.com/openai/openai-python/issues/1604)) ([3e19a87](https://github.com/openai/openai-python/commit/3e19a87255d8e92716689656afaa3f16297773b6)) +* **internal:** update pydantic compat helper function ([#1607](https://github.com/openai/openai-python/issues/1607)) ([973c18b](https://github.com/openai/openai-python/commit/973c18b259a0e4a8134223f50a5f660b86650949)) + ## 1.39.0 (2024-08-05) Full Changelog: [v1.38.0...v1.39.0](https://github.com/openai/openai-python/compare/v1.38.0...v1.39.0) diff --git a/pyproject.toml b/pyproject.toml index cb02edac0c..1e86c44706 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.39.0" +version = "1.40.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index aed8ee29b2..73cd42e5ea 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.39.0" # x-release-please-version +__version__ = "1.40.0" # x-release-please-version From ad56fd14d25e0a704c5f958ab273a241f0548f26 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Tue, 6 Aug 2024 17:33:36 +0000 Subject: [PATCH 546/914] chore(internal): update OpenAPI spec url (#1608) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index da26758316..ac652c9271 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-c36d30a94622922f83d56a025cdf0095ff7cb18a5138838c698c8443f21fb3a8.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-4097c2f86beb3f3bb021775cd1dfa240e960caf842aeefc2e08da4dc0851ea79.yml From 09eba2099734eea49dc7606d48e2f968a036514d Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 7 Aug 2024 11:01:31 +0100 Subject: [PATCH 547/914] chore(internal): update test snapshots --- ...661f4f5bea321c0aac9e164f2ed3e409aebc48.bin | 102 +++++++ ...2625e557f9e6763bd8c03bcd88e220149a3367.bin | 224 ++++++++++++++++ ...cf4aa41fc428937c231e17c3460f3237f6a018.bin | 28 ++ ...9cda31e5a0af80decdbddd21c056545c6d4616.bin | 100 ------- ...6aea6d0356b63140161758a2e576d4e3092cfa.bin | 36 +++ ...fd3deafe041da94d541071009596234d8c84a6.bin | 36 +++ ...8ab9141d709b770a74dc025fb8770a42aabee9.bin | 180 ------------- ...3dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin | 52 ++++ ...f7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin | 28 ++ ...f0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin | 10 + ...c4861e696495d9a45c19be02cf479e28c31316.bin | 12 - ...ec5f581ea9de2524599f06b0d405db8997b826.bin | 8 - ...9d46ee41408a688758219f3f58ac1ee2084db3.bin | 28 ++ ...5dfafc1d712c253b42bafe07991b3058541016.bin | 156 +++++++++++ ...0e08ddfad221d6632fdb200a95ca6c996238e2.bin | 52 ---- ...4b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin | 28 -- ...abc40785d712248f65c8595c99879080d0eeb9.bin | 36 --- ...5521e0258cc2cef0528a17fbdadb9cc76695f0.bin | 72 ----- ...613bdb9c4a2ad8262027d158cc94e6f9765164.bin | 12 + ...f8694b77f608de5e2a3799276be06ce3fbb15b.bin | 30 +++ ...f05bd963fe093622e5bf9a95a3ebede64714bc.bin | 30 --- ...194b58fc759adc3685170e0a61033241d2eda5.bin | 32 --- ...81772c3010c10b37e8af3996fbdbbecb3c32a2.bin | 22 ++ ...ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin | 36 --- ...13a82f959a175ec05ce3c07412bbc9fd436234.bin | 22 -- tests/lib/chat/test_completions.py | 84 +++--- tests/lib/chat/test_completions_streaming.py | 251 +++++++++++------- 27 files changed, 961 insertions(+), 746 deletions(-) create mode 100644 .inline-snapshot/external/038a5c69c34c9513021b52aa61661f4f5bea321c0aac9e164f2ed3e409aebc48.bin create mode 100644 .inline-snapshot/external/0898f3d1651e3244eeb3651d012625e557f9e6763bd8c03bcd88e220149a3367.bin create mode 100644 .inline-snapshot/external/0a00cd46c61030ff70241d432dcf4aa41fc428937c231e17c3460f3237f6a018.bin delete mode 100644 .inline-snapshot/external/1437bd06a9d5c414e56fd0840b9cda31e5a0af80decdbddd21c056545c6d4616.bin create mode 100644 .inline-snapshot/external/15ae68f793c7b390fc8af9e21a6aea6d0356b63140161758a2e576d4e3092cfa.bin create mode 100644 .inline-snapshot/external/24aaf30663f9a568a0e77970b4fd3deafe041da94d541071009596234d8c84a6.bin delete mode 100644 .inline-snapshot/external/3e0df46f250db854eacb34e3258ab9141d709b770a74dc025fb8770a42aabee9.bin create mode 100644 .inline-snapshot/external/453df473e96274dd8ab61ab4d13dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin create mode 100644 .inline-snapshot/external/4d75e4d7c3e0b532a67fb2114ff7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin create mode 100644 .inline-snapshot/external/69363a555f8ea9b6eee0bb022af0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin delete mode 100644 .inline-snapshot/external/70c7df71ce729e178fc5e54f0cc4861e696495d9a45c19be02cf479e28c31316.bin delete mode 100644 .inline-snapshot/external/7ae6c1a2631bf7444b8f70b592ec5f581ea9de2524599f06b0d405db8997b826.bin create mode 100644 .inline-snapshot/external/83d3d003e6fdaa69b7a398440f9d46ee41408a688758219f3f58ac1ee2084db3.bin create mode 100644 .inline-snapshot/external/a0c4f0be184e8234cdc0e3abae5dfafc1d712c253b42bafe07991b3058541016.bin delete mode 100644 .inline-snapshot/external/a346213bec7a572810bd1ffe290e08ddfad221d6632fdb200a95ca6c996238e2.bin delete mode 100644 .inline-snapshot/external/a7097cae6a1f8dea453977a1784b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin delete mode 100644 .inline-snapshot/external/ae070a447e1ded1ad4819f7608abc40785d712248f65c8595c99879080d0eeb9.bin delete mode 100644 .inline-snapshot/external/b9d6bee9f9b8ee5bdea06cd6955521e0258cc2cef0528a17fbdadb9cc76695f0.bin create mode 100644 .inline-snapshot/external/be1089999ca5f1e63b149447f1613bdb9c4a2ad8262027d158cc94e6f9765164.bin create mode 100644 .inline-snapshot/external/ca015b8b1ebaac98be76f2f855f8694b77f608de5e2a3799276be06ce3fbb15b.bin delete mode 100644 .inline-snapshot/external/cb77dc69b6c8289a6f1e88fa24f05bd963fe093622e5bf9a95a3ebede64714bc.bin delete mode 100644 .inline-snapshot/external/d79326933c1586e731a8235998194b58fc759adc3685170e0a61033241d2eda5.bin create mode 100644 .inline-snapshot/external/dae1b261f19722801adc82a13181772c3010c10b37e8af3996fbdbbecb3c32a2.bin delete mode 100644 .inline-snapshot/external/ea9a417d533b9adfece02608f2ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin delete mode 100644 .inline-snapshot/external/fb75060ede89cac360ce8baf1513a82f959a175ec05ce3c07412bbc9fd436234.bin diff --git a/.inline-snapshot/external/038a5c69c34c9513021b52aa61661f4f5bea321c0aac9e164f2ed3e409aebc48.bin b/.inline-snapshot/external/038a5c69c34c9513021b52aa61661f4f5bea321c0aac9e164f2ed3e409aebc48.bin new file mode 100644 index 0000000000..a5a0aeb4c0 --- /dev/null +++ b/.inline-snapshot/external/038a5c69c34c9513021b52aa61661f4f5bea321c0aac9e164f2ed3e409aebc48.bin @@ -0,0 +1,102 @@ +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" including"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" For"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" latest"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" recommend"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" checking"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" such"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" as"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Channel"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" BBC"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" local"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" news"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" station"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":47,"total_tokens":61}} + +data: [DONE] + diff --git a/.inline-snapshot/external/0898f3d1651e3244eeb3651d012625e557f9e6763bd8c03bcd88e220149a3367.bin b/.inline-snapshot/external/0898f3d1651e3244eeb3651d012625e557f9e6763bd8c03bcd88e220149a3367.bin new file mode 100644 index 0000000000..4b42ada8d2 --- /dev/null +++ b/.inline-snapshot/external/0898f3d1651e3244eeb3651d012625e557f9e6763bd8c03bcd88e220149a3367.bin @@ -0,0 +1,224 @@ +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"location"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" CA"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"forecast"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"_date"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"202"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"11"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"02"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"current"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"high"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"low"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" },\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"condition"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"humidity"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"wind"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"_speed"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" },\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"note"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"Please"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" service"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":".\"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" }"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":19,"completion_tokens":108,"total_tokens":127}} + +data: [DONE] + diff --git a/.inline-snapshot/external/0a00cd46c61030ff70241d432dcf4aa41fc428937c231e17c3460f3237f6a018.bin b/.inline-snapshot/external/0a00cd46c61030ff70241d432dcf4aa41fc428937c231e17c3460f3237f6a018.bin new file mode 100644 index 0000000000..73de9d6cbc --- /dev/null +++ b/.inline-snapshot/external/0a00cd46c61030ff70241d432dcf4aa41fc428937c231e17c3460f3237f6a018.bin @@ -0,0 +1,28 @@ +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":{"content":null,"refusal":[]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":{"content":null,"refusal":[{"token":"I'm","logprob":-0.0016157961,"bytes":[73,39,109],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":{"content":null,"refusal":[{"token":" sorry","logprob":-0.78663874,"bytes":[32,115,111,114,114,121],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":","},"logprobs":{"content":null,"refusal":[{"token":",","logprob":-0.0000779144,"bytes":[44],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":{"content":null,"refusal":[{"token":" I","logprob":-0.5234622,"bytes":[32,73],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" cannot"},"logprobs":{"content":null,"refusal":[{"token":" cannot","logprob":-0.52499557,"bytes":[32,99,97,110,110,111,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":{"content":null,"refusal":[{"token":" assist","logprob":-0.015198289,"bytes":[32,97,115,115,105,115,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":{"content":null,"refusal":[{"token":" with","logprob":-0.00071648485,"bytes":[32,119,105,116,104],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":{"content":null,"refusal":[{"token":" that","logprob":-0.008114983,"bytes":[32,116,104,97,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":{"content":null,"refusal":[{"token":" request","logprob":-0.0013802331,"bytes":[32,114,101,113,117,101,115,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":{"content":null,"refusal":[{"token":".","logprob":-3.4121115e-6,"bytes":[46],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":11,"total_tokens":28}} + +data: [DONE] + diff --git a/.inline-snapshot/external/1437bd06a9d5c414e56fd0840b9cda31e5a0af80decdbddd21c056545c6d4616.bin b/.inline-snapshot/external/1437bd06a9d5c414e56fd0840b9cda31e5a0af80decdbddd21c056545c6d4616.bin deleted file mode 100644 index f96745e385..0000000000 --- a/.inline-snapshot/external/1437bd06a9d5c414e56fd0840b9cda31e5a0af80decdbddd21c056545c6d4616.bin +++ /dev/null @@ -1,100 +0,0 @@ -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"64"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"68"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"64"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":1,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":2,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tAC53I4IJcmm22h7tLip6Irb7b6D","object":"chat.completion.chunk","created":1722934253,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":42,"total_tokens":59}} - -data: [DONE] - diff --git a/.inline-snapshot/external/15ae68f793c7b390fc8af9e21a6aea6d0356b63140161758a2e576d4e3092cfa.bin b/.inline-snapshot/external/15ae68f793c7b390fc8af9e21a6aea6d0356b63140161758a2e576d4e3092cfa.bin new file mode 100644 index 0000000000..1bcca1fceb --- /dev/null +++ b/.inline-snapshot/external/15ae68f793c7b390fc8af9e21a6aea6d0356b63140161758a2e576d4e3092cfa.bin @@ -0,0 +1,36 @@ +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"68"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":14,"total_tokens":31}} + +data: [DONE] + diff --git a/.inline-snapshot/external/24aaf30663f9a568a0e77970b4fd3deafe041da94d541071009596234d8c84a6.bin b/.inline-snapshot/external/24aaf30663f9a568a0e77970b4fd3deafe041da94d541071009596234d8c84a6.bin new file mode 100644 index 0000000000..49962cff27 --- /dev/null +++ b/.inline-snapshot/external/24aaf30663f9a568a0e77970b4fd3deafe041da94d541071009596234d8c84a6.bin @@ -0,0 +1,36 @@ +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_7PhhveOvvpPK53s1fV8TWhoV","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ed"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"inburgh"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"country"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"GB"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"units"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":76,"completion_tokens":24,"total_tokens":100}} + +data: [DONE] + diff --git a/.inline-snapshot/external/3e0df46f250db854eacb34e3258ab9141d709b770a74dc025fb8770a42aabee9.bin b/.inline-snapshot/external/3e0df46f250db854eacb34e3258ab9141d709b770a74dc025fb8770a42aabee9.bin deleted file mode 100644 index eb1cf9e733..0000000000 --- a/.inline-snapshot/external/3e0df46f250db854eacb34e3258ab9141d709b770a74dc025fb8770a42aabee9.bin +++ /dev/null @@ -1,180 +0,0 @@ -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"{\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"location"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" CA"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"conditions"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"humidity"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"wind"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"_speed"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"timestamp"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"note"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"Real"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" data"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" not"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" available"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Please"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" service"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" up"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"-to"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"-date"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" on"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"'s"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" conditions"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":".\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"}"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tACAVV5BLjrmHwZhSABB78qPvLg2","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":19,"completion_tokens":86,"total_tokens":105}} - -data: [DONE] - diff --git a/.inline-snapshot/external/453df473e96274dd8ab61ab4d13dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin b/.inline-snapshot/external/453df473e96274dd8ab61ab4d13dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin new file mode 100644 index 0000000000..adcdddd317 --- /dev/null +++ b/.inline-snapshot/external/453df473e96274dd8ab61ab4d13dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin @@ -0,0 +1,52 @@ +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_lQnnsesjFMWMQ5IeWPHzR4th","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"ci"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ty\": "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Edinb"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"urgh"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\", \"c"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ountry"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"UK\", "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"units"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_2xjOUgaCdiwAcl9ZBL9LyMUU","type":"function","function":{"name":"get_stock_price","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"ti"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"cker\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":": \"AAP"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"L\", "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\"exch"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ange\":"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":" \"NA"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"SDAQ\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[],"usage":{"prompt_tokens":149,"completion_tokens":60,"total_tokens":209}} + +data: [DONE] + diff --git a/.inline-snapshot/external/4d75e4d7c3e0b532a67fb2114ff7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin b/.inline-snapshot/external/4d75e4d7c3e0b532a67fb2114ff7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin new file mode 100644 index 0000000000..008d5882ec --- /dev/null +++ b/.inline-snapshot/external/4d75e4d7c3e0b532a67fb2114ff7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin @@ -0,0 +1,28 @@ +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" cannot"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":11,"total_tokens":28}} + +data: [DONE] + diff --git a/.inline-snapshot/external/69363a555f8ea9b6eee0bb022af0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin b/.inline-snapshot/external/69363a555f8ea9b6eee0bb022af0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin new file mode 100644 index 0000000000..852a7758f9 --- /dev/null +++ b/.inline-snapshot/external/69363a555f8ea9b6eee0bb022af0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin @@ -0,0 +1,10 @@ +data: {"id":"chatcmpl-9tXjkSxyTVUSWZRJFSZJgWBHzh2c3","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkSxyTVUSWZRJFSZJgWBHzh2c3","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjkSxyTVUSWZRJFSZJgWBHzh2c3","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"length"}]} + +data: {"id":"chatcmpl-9tXjkSxyTVUSWZRJFSZJgWBHzh2c3","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":1,"total_tokens":18}} + +data: [DONE] + diff --git a/.inline-snapshot/external/70c7df71ce729e178fc5e54f0cc4861e696495d9a45c19be02cf479e28c31316.bin b/.inline-snapshot/external/70c7df71ce729e178fc5e54f0cc4861e696495d9a45c19be02cf479e28c31316.bin deleted file mode 100644 index 21c41d3958..0000000000 --- a/.inline-snapshot/external/70c7df71ce729e178fc5e54f0cc4861e696495d9a45c19be02cf479e28c31316.bin +++ /dev/null @@ -1,12 +0,0 @@ -data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":{"content":[],"refusal":null},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"Foo"},"logprobs":{"content":[{"token":"Foo","logprob":-0.006764991,"bytes":[70,111,111],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"!"},"logprobs":{"content":[{"token":"!","logprob":-0.31380808,"bytes":[33],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tDU7wVJ0lzoNjC1aNIjnP99zMW2C","object":"chat.completion.chunk","created":1722946903,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":9,"completion_tokens":2,"total_tokens":11}} - -data: [DONE] - diff --git a/.inline-snapshot/external/7ae6c1a2631bf7444b8f70b592ec5f581ea9de2524599f06b0d405db8997b826.bin b/.inline-snapshot/external/7ae6c1a2631bf7444b8f70b592ec5f581ea9de2524599f06b0d405db8997b826.bin deleted file mode 100644 index d261ccd0d0..0000000000 --- a/.inline-snapshot/external/7ae6c1a2631bf7444b8f70b592ec5f581ea9de2524599f06b0d405db8997b826.bin +++ /dev/null @@ -1,8 +0,0 @@ -data: {"id":"chatcmpl-9tAC6v0rUCOp8tty9cizBsGmRcVIx","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6v0rUCOp8tty9cizBsGmRcVIx","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"length"}]} - -data: {"id":"chatcmpl-9tAC6v0rUCOp8tty9cizBsGmRcVIx","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":1,"total_tokens":18}} - -data: [DONE] - diff --git a/.inline-snapshot/external/83d3d003e6fdaa69b7a398440f9d46ee41408a688758219f3f58ac1ee2084db3.bin b/.inline-snapshot/external/83d3d003e6fdaa69b7a398440f9d46ee41408a688758219f3f58ac1ee2084db3.bin new file mode 100644 index 0000000000..05e08e3475 --- /dev/null +++ b/.inline-snapshot/external/83d3d003e6fdaa69b7a398440f9d46ee41408a688758219f3f58ac1ee2084db3.bin @@ -0,0 +1,28 @@ +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_pVHYsU0gmSfX5TqxOyVbB2ma","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"San"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Francisco"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"state"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"CA"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[],"usage":{"prompt_tokens":48,"completion_tokens":19,"total_tokens":67}} + +data: [DONE] + diff --git a/.inline-snapshot/external/a0c4f0be184e8234cdc0e3abae5dfafc1d712c253b42bafe07991b3058541016.bin b/.inline-snapshot/external/a0c4f0be184e8234cdc0e3abae5dfafc1d712c253b42bafe07991b3058541016.bin new file mode 100644 index 0000000000..df20d6fda5 --- /dev/null +++ b/.inline-snapshot/external/a0c4f0be184e8234cdc0e3abae5dfafc1d712c253b42bafe07991b3058541016.bin @@ -0,0 +1,156 @@ +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" but"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" can't"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" accurately"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" provide"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"63"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"58"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"6"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" current"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" for"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" as"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" my"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" data"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" is"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" up"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" to"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" October"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"202"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"3"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" You"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" can"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" try"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" checking"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" reliable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" website"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" or"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" app"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" for"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" real"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"-time"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" updates"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":71,"total_tokens":88}} + +data: [DONE] + diff --git a/.inline-snapshot/external/a346213bec7a572810bd1ffe290e08ddfad221d6632fdb200a95ca6c996238e2.bin b/.inline-snapshot/external/a346213bec7a572810bd1ffe290e08ddfad221d6632fdb200a95ca6c996238e2.bin deleted file mode 100644 index 2ceced2f1c..0000000000 --- a/.inline-snapshot/external/a346213bec7a572810bd1ffe290e08ddfad221d6632fdb200a95ca6c996238e2.bin +++ /dev/null @@ -1,52 +0,0 @@ -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_g4Q1vRbE0CaHGOs5if8mHsBq","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"ci"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ty\": "}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Edinb"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"urgh"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\", \"c"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ountry"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"UK\", "}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"units"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c\"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_gWj3HQxZEHnFvyJLEHIiJKBV","type":"function","function":{"name":"get_stock_price","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"ti"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"cker\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":": \"AAP"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"L\", "}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\"exch"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ange\":"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":" \"NA"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"SDAQ\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - -data: {"id":"chatcmpl-9tAC9cJXpJZ5tGpOa9thAumwSCcmm","object":"chat.completion.chunk","created":1722934257,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":149,"completion_tokens":60,"total_tokens":209}} - -data: [DONE] - diff --git a/.inline-snapshot/external/a7097cae6a1f8dea453977a1784b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin b/.inline-snapshot/external/a7097cae6a1f8dea453977a1784b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin deleted file mode 100644 index de0efe6bab..0000000000 --- a/.inline-snapshot/external/a7097cae6a1f8dea453977a1784b7ca16b9fadc5c4551ea066d305eb1607e1c6.bin +++ /dev/null @@ -1,28 +0,0 @@ -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_rQe3kzGnTr2epjx8HREg3F2a","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"San"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Francisco"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"state"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"CA"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - -data: {"id":"chatcmpl-9tACAMQt1guB31uPOzbyivps8944W","object":"chat.completion.chunk","created":1722934258,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":48,"completion_tokens":19,"total_tokens":67}} - -data: [DONE] - diff --git a/.inline-snapshot/external/ae070a447e1ded1ad4819f7608abc40785d712248f65c8595c99879080d0eeb9.bin b/.inline-snapshot/external/ae070a447e1ded1ad4819f7608abc40785d712248f65c8595c99879080d0eeb9.bin deleted file mode 100644 index af003a8120..0000000000 --- a/.inline-snapshot/external/ae070a447e1ded1ad4819f7608abc40785d712248f65c8595c99879080d0eeb9.bin +++ /dev/null @@ -1,36 +0,0 @@ -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_Vz6ZXciy6Y0PYfT4d9W7fYB4","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ed"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"inburgh"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"country"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"UK"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"units"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - -data: {"id":"chatcmpl-9tAC85ZjzRlx3OkdUgSGiR9aBLyL8","object":"chat.completion.chunk","created":1722934256,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":76,"completion_tokens":24,"total_tokens":100}} - -data: [DONE] - diff --git a/.inline-snapshot/external/b9d6bee9f9b8ee5bdea06cd6955521e0258cc2cef0528a17fbdadb9cc76695f0.bin b/.inline-snapshot/external/b9d6bee9f9b8ee5bdea06cd6955521e0258cc2cef0528a17fbdadb9cc76695f0.bin deleted file mode 100644 index b4337f886a..0000000000 --- a/.inline-snapshot/external/b9d6bee9f9b8ee5bdea06cd6955521e0258cc2cef0528a17fbdadb9cc76695f0.bin +++ /dev/null @@ -1,72 +0,0 @@ -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" To"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" get"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" latest"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" recommend"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" checking"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" using"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tAC1e8N6ADc0gjWIhrsjjo4gddxQ","object":"chat.completion.chunk","created":1722934249,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":32,"total_tokens":46}} - -data: [DONE] - diff --git a/.inline-snapshot/external/be1089999ca5f1e63b149447f1613bdb9c4a2ad8262027d158cc94e6f9765164.bin b/.inline-snapshot/external/be1089999ca5f1e63b149447f1613bdb9c4a2ad8262027d158cc94e6f9765164.bin new file mode 100644 index 0000000000..f2a8158310 --- /dev/null +++ b/.inline-snapshot/external/be1089999ca5f1e63b149447f1613bdb9c4a2ad8262027d158cc94e6f9765164.bin @@ -0,0 +1,12 @@ +data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":{"content":[],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"Foo"},"logprobs":{"content":[{"token":"Foo","logprob":-0.0067602484,"bytes":[70,111,111],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"."},"logprobs":{"content":[{"token":".","logprob":-2.4962392,"bytes":[46],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":9,"completion_tokens":2,"total_tokens":11}} + +data: [DONE] + diff --git a/.inline-snapshot/external/ca015b8b1ebaac98be76f2f855f8694b77f608de5e2a3799276be06ce3fbb15b.bin b/.inline-snapshot/external/ca015b8b1ebaac98be76f2f855f8694b77f608de5e2a3799276be06ce3fbb15b.bin new file mode 100644 index 0000000000..c0a355f9d1 --- /dev/null +++ b/.inline-snapshot/external/ca015b8b1ebaac98be76f2f855f8694b77f608de5e2a3799276be06ce3fbb15b.bin @@ -0,0 +1,30 @@ +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" but"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" can't"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":12,"total_tokens":29}} + +data: [DONE] + diff --git a/.inline-snapshot/external/cb77dc69b6c8289a6f1e88fa24f05bd963fe093622e5bf9a95a3ebede64714bc.bin b/.inline-snapshot/external/cb77dc69b6c8289a6f1e88fa24f05bd963fe093622e5bf9a95a3ebede64714bc.bin deleted file mode 100644 index a95f28a54b..0000000000 --- a/.inline-snapshot/external/cb77dc69b6c8289a6f1e88fa24f05bd963fe093622e5bf9a95a3ebede64714bc.bin +++ /dev/null @@ -1,30 +0,0 @@ -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":{"content":null,"refusal":[]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":{"content":null,"refusal":[{"token":"I'm","logprob":-0.0010472201,"bytes":[73,39,109],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" very"},"logprobs":{"content":null,"refusal":[{"token":" very","logprob":-0.7292482,"bytes":[32,118,101,114,121],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":{"content":null,"refusal":[{"token":" sorry","logprob":-5.080963e-6,"bytes":[32,115,111,114,114,121],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":","},"logprobs":{"content":null,"refusal":[{"token":",","logprob":-0.00004048445,"bytes":[44],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" but"},"logprobs":{"content":null,"refusal":[{"token":" but","logprob":-0.038046427,"bytes":[32,98,117,116],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":{"content":null,"refusal":[{"token":" I","logprob":-0.0019351852,"bytes":[32,73],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" can't"},"logprobs":{"content":null,"refusal":[{"token":" can't","logprob":-0.008995773,"bytes":[32,99,97,110,39,116],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":{"content":null,"refusal":[{"token":" assist","logprob":-0.0033510819,"bytes":[32,97,115,115,105,115,116],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":{"content":null,"refusal":[{"token":" with","logprob":-0.0036033941,"bytes":[32,119,105,116,104],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":{"content":null,"refusal":[{"token":" that","logprob":-0.0015974608,"bytes":[32,116,104,97,116],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":{"content":null,"refusal":[{"token":".","logprob":-0.6339823,"bytes":[46],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tDU0lrTPa5PKPJIVzOxKYl2LTTg4","object":"chat.completion.chunk","created":1722946896,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":12,"total_tokens":29}} - -data: [DONE] - diff --git a/.inline-snapshot/external/d79326933c1586e731a8235998194b58fc759adc3685170e0a61033241d2eda5.bin b/.inline-snapshot/external/d79326933c1586e731a8235998194b58fc759adc3685170e0a61033241d2eda5.bin deleted file mode 100644 index 895e4828ef..0000000000 --- a/.inline-snapshot/external/d79326933c1586e731a8235998194b58fc759adc3685170e0a61033241d2eda5.bin +++ /dev/null @@ -1,32 +0,0 @@ -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" very"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" but"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" can't"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tAC6sKqquyhW1Ql3Jaj6KGNDLGNZ","object":"chat.completion.chunk","created":1722934254,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":13,"total_tokens":30}} - -data: [DONE] - diff --git a/.inline-snapshot/external/dae1b261f19722801adc82a13181772c3010c10b37e8af3996fbdbbecb3c32a2.bin b/.inline-snapshot/external/dae1b261f19722801adc82a13181772c3010c10b37e8af3996fbdbbecb3c32a2.bin new file mode 100644 index 0000000000..f0911c575d --- /dev/null +++ b/.inline-snapshot/external/dae1b261f19722801adc82a13181772c3010c10b37e8af3996fbdbbecb3c32a2.bin @@ -0,0 +1,22 @@ +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_5uxEBMFySqqQGu02I5QHA8k6","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"New"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" York"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":44,"completion_tokens":16,"total_tokens":60}} + +data: [DONE] + diff --git a/.inline-snapshot/external/ea9a417d533b9adfece02608f2ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin b/.inline-snapshot/external/ea9a417d533b9adfece02608f2ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin deleted file mode 100644 index 869b94de1a..0000000000 --- a/.inline-snapshot/external/ea9a417d533b9adfece02608f2ca00f3a963d785c6fe78c35d60d038cd7a8ba0.bin +++ /dev/null @@ -1,36 +0,0 @@ -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"63"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv","object":"chat.completion.chunk","created":1722934250,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":14,"total_tokens":31}} - -data: [DONE] - diff --git a/.inline-snapshot/external/fb75060ede89cac360ce8baf1513a82f959a175ec05ce3c07412bbc9fd436234.bin b/.inline-snapshot/external/fb75060ede89cac360ce8baf1513a82f959a175ec05ce3c07412bbc9fd436234.bin deleted file mode 100644 index 970b1adf80..0000000000 --- a/.inline-snapshot/external/fb75060ede89cac360ce8baf1513a82f959a175ec05ce3c07412bbc9fd436234.bin +++ /dev/null @@ -1,22 +0,0 @@ -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_9rqjEc1DQRADTYGVV45LbZwL","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"New"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" York"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tACC99384oWbk9upfFD1gITJehjE","object":"chat.completion.chunk","created":1722934260,"model":"gpt-4o-so","system_fingerprint":"fp_e1a05a1dce","choices":[],"usage":{"prompt_tokens":44,"completion_tokens":16,"total_tokens":60}} - -data: [DONE] - diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index db370e4332..e406a5a3bc 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -41,7 +41,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte ], ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tABLlmqdEOYnmmWATUI3dNKlfXa3", "object": "chat.completion", "created": 1722934207, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "I\'m unable to provide real-time weather updates. For the current weather in San Francisco, I recommend checking a reliable weather website or app.", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 14, "completion_tokens": 27, "total_tokens": 41}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXjSozlYq8oGdlRH3vgLsiUNRg8c", "object": "chat.completion", "created": 1723024734, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "I\'m unable to provide real-time weather updates. To find out the current weather in San Francisco, please check a reliable weather website or app.", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 14, "completion_tokens": 28, "total_tokens": 42}, "system_fingerprint": "fp_845eaabc1f"}' ), mock_client=client, respx_mock=respx_mock, @@ -56,8 +56,8 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( - content="I'm unable to provide real-time weather updates. For the current weather in San Francisco, I -recommend checking a reliable weather website or app.", + content="I'm unable to provide real-time weather updates. To find out the current weather in San +Francisco, please check a reliable weather website or app.", function_call=None, parsed=None, refusal=None, @@ -66,13 +66,13 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte ) ) ], - created=1722934207, - id='chatcmpl-9tABLlmqdEOYnmmWATUI3dNKlfXa3', + created=1723024734, + id='chatcmpl-9tXjSozlYq8oGdlRH3vgLsiUNRg8c', model='gpt-4o-2024-08-06', object='chat.completion', service_tier=None, - system_fingerprint='fp_e1a05a1dce', - usage=CompletionUsage(completion_tokens=27, prompt_tokens=14, total_tokens=41) + system_fingerprint='fp_845eaabc1f', + usage=CompletionUsage(completion_tokens=28, prompt_tokens=14, total_tokens=42) ) """ ) @@ -97,7 +97,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tABUwdw3Kbe3VPRnMofh9lJkFkLV", "object": "chat.completion", "created": 1722934216, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 14, "total_tokens": 31}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXjTNupyDe7nL1Z8eOO6BdSyrHAD", "object": "chat.completion", "created": 1723024735, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":56,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 14, "total_tokens": 31}, "system_fingerprint": "fp_2a322c9ffc"}' ), mock_client=client, respx_mock=respx_mock, @@ -112,21 +112,21 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":65,"units":"f"}', + content='{"city":"San Francisco","temperature":56,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=65.0, units='f'), + parsed=Location(city='San Francisco', temperature=56.0, units='f'), refusal=None, role='assistant', tool_calls=[] ) ) ], - created=1722934216, - id='chatcmpl-9tABUwdw3Kbe3VPRnMofh9lJkFkLV', + created=1723024735, + id='chatcmpl-9tXjTNupyDe7nL1Z8eOO6BdSyrHAD', model='gpt-4o-2024-08-06', object='chat.completion', service_tier=None, - system_fingerprint='fp_e1a05a1dce', + system_fingerprint='fp_2a322c9ffc', usage=CompletionUsage(completion_tokens=14, prompt_tokens=17, total_tokens=31) ) """ @@ -155,7 +155,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tABVfBu4ZdyQFKe8RgsWsyL7UoIj", "object": "chat.completion", "created": 1722934217, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":58.0,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 1, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":61,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 2, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 44, "total_tokens": 61}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXjUrNFyyjSB2FJ842TMDNRM6Gen", "object": "chat.completion", "created": 1723024736, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":58,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 1, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":58,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 2, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":63,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 42, "total_tokens": 59}, "system_fingerprint": "fp_845eaabc1f"}' ), mock_client=client, respx_mock=respx_mock, @@ -169,7 +169,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":58.0,"units":"f"}', + content='{"city":"San Francisco","temperature":58,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=58.0, units='f'), refusal=None, @@ -182,9 +182,9 @@ class Location(BaseModel): index=1, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":61,"units":"f"}', + content='{"city":"San Francisco","temperature":58,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=61.0, units='f'), + parsed=Location(city='San Francisco', temperature=58.0, units='f'), refusal=None, role='assistant', tool_calls=[] @@ -195,9 +195,9 @@ class Location(BaseModel): index=2, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":65,"units":"f"}', + content='{"city":"San Francisco","temperature":63,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=65.0, units='f'), + parsed=Location(city='San Francisco', temperature=63.0, units='f'), refusal=None, role='assistant', tool_calls=[] @@ -223,7 +223,7 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m response_format=Query, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tABVRLORZbby5zZjZhyrUdDU1XhB", "object": "chat.completion", "created": 1722934217, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_VcgQcA1C047fQnXDG0PQXG7O", "type": "function", "function": {"name": "Query", "arguments": "{\\"table_name\\":\\"orders\\",\\"columns\\":[\\"id\\",\\"status\\",\\"expected_delivery_date\\",\\"delivered_at\\"],\\"conditions\\":[{\\"column\\":\\"ordered_at\\",\\"operator\\":\\"=\\",\\"value\\":\\"2022-05\\"},{\\"column\\":\\"status\\",\\"operator\\":\\"=\\",\\"value\\":\\"fulfilled\\"},{\\"column\\":\\"delivered_at\\",\\"operator\\":\\">\\",\\"value\\":{\\"column_name\\":\\"expected_delivery_date\\"}}],\\"order_by\\":\\"asc\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 195, "completion_tokens": 85, "total_tokens": 280}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXjVJVCLTn7CWFhpjETixvvApCk3", "object": "chat.completion", "created": 1723024737, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_Un4g0IXeQGOyqKBS3zhqNCox", "type": "function", "function": {"name": "Query", "arguments": "{\\"table_name\\":\\"orders\\",\\"columns\\":[\\"id\\",\\"status\\",\\"expected_delivery_date\\",\\"delivered_at\\",\\"shipped_at\\",\\"ordered_at\\"],\\"conditions\\":[{\\"column\\":\\"ordered_at\\",\\"operator\\":\\">=\\",\\"value\\":\\"2022-05-01\\"},{\\"column\\":\\"ordered_at\\",\\"operator\\":\\"<=\\",\\"value\\":\\"2022-05-31\\"},{\\"column\\":\\"status\\",\\"operator\\":\\"=\\",\\"value\\":\\"fulfilled\\"},{\\"column\\":\\"delivered_at\\",\\"operator\\":\\">\\",\\"value\\":{\\"column_name\\":\\"expected_delivery_date\\"}}],\\"order_by\\":\\"asc\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 195, "completion_tokens": 114, "total_tokens": 309}, "system_fingerprint": "fp_845eaabc1f"}' ), mock_client=client, respx_mock=respx_mock, @@ -244,19 +244,23 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"table_name":"orders","columns":["id","status","expected_delivery_date","delivered_at"], -"conditions":[{"column":"ordered_at","operator":"=","value":"2022-05"},{"column":"status","operator":"=","value":"fulfil -led"},{"column":"delivered_at","operator":">","value":{"column_name":"expected_delivery_date"}}],"order_by":"asc"}', + arguments='{"table_name":"orders","columns":["id","status","expected_delivery_date","delivered_at"," +shipped_at","ordered_at"],"conditions":[{"column":"ordered_at","operator":">=","value":"2022-05-01"},{"column":"ordered_ +at","operator":"<=","value":"2022-05-31"},{"column":"status","operator":"=","value":"fulfilled"},{"column":"delivered_at +","operator":">","value":{"column_name":"expected_delivery_date"}}],"order_by":"asc"}', name='Query', parsed_arguments=Query( columns=[ , , , - + , + , + ], conditions=[ - Condition(column='ordered_at', operator=='>, value='2022-05-01'), + Condition(column='ordered_at', operator= ) ), - id='call_VcgQcA1C047fQnXDG0PQXG7O', + id='call_Un4g0IXeQGOyqKBS3zhqNCox', type='function' ) ] @@ -299,7 +303,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tABXbi3qast6oJvdaqQcK9C7k9fn", "object": "chat.completion", "created": 1722934219, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"", "refusal": null}, "logprobs": null, "finish_reason": "length"}], "usage": {"prompt_tokens": 17, "completion_tokens": 1, "total_tokens": 18}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXjYACgVKixKdMv2nVQqDVELkdSF", "object": "chat.completion", "created": 1723024740, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"", "refusal": null}, "logprobs": null, "finish_reason": "length"}], "usage": {"prompt_tokens": 17, "completion_tokens": 1, "total_tokens": 18}, "system_fingerprint": "fp_2a322c9ffc"}' ), mock_client=client, respx_mock=respx_mock, @@ -325,7 +329,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tABXJEffhEWxp24MeLxkDJCMtWmx", "object": "chat.completion", "created": 1722934219, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "refusal": "I\'m very sorry, but I can\'t assist with that."}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 12, "total_tokens": 29}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXm7FnIj3hSot5xM4c954MIePle0", "object": "chat.completion", "created": 1723024899, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "refusal": "I\'m very sorry, but I can\'t assist with that request."}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 13, "total_tokens": 30}, "system_fingerprint": "fp_845eaabc1f"}' ), mock_client=client, respx_mock=respx_mock, @@ -342,7 +346,7 @@ class Location(BaseModel): content=None, function_call=None, parsed=None, - refusal="I'm very sorry, but I can't assist with that.", + refusal="I'm very sorry, but I can't assist with that request.", role='assistant', tool_calls=[] ) @@ -373,7 +377,7 @@ class GetWeatherArgs(BaseModel): ], ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tABgtKnF7Gbri4CmpOocmhg0UgBF", "object": "chat.completion", "created": 1722934228, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_9rqjEc1DQRADTYGVV45LbZwL", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\":\\"Edinburgh\\",\\"country\\":\\"UK\\",\\"units\\":\\"c\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 76, "completion_tokens": 24, "total_tokens": 100}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXjbQ9V0l5XPlynOJHKvrWsJQymO", "object": "chat.completion", "created": 1723024743, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_EEaIYq8aTdiDWro8jILNl3XK", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\":\\"Edinburgh\\",\\"country\\":\\"GB\\",\\"units\\":\\"c\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 76, "completion_tokens": 24, "total_tokens": 100}, "system_fingerprint": "fp_2a322c9ffc"}' ), mock_client=client, respx_mock=respx_mock, @@ -395,11 +399,11 @@ class GetWeatherArgs(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city":"Edinburgh","country":"UK","units":"c"}', + arguments='{"city":"Edinburgh","country":"GB","units":"c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') ), - id='call_9rqjEc1DQRADTYGVV45LbZwL', + id='call_EEaIYq8aTdiDWro8jILNl3XK', type='function' ) ] @@ -444,7 +448,7 @@ class GetStockPrice(BaseModel): ], ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tABqDpvDTi0Cg8PHtKdNSFoh4UJv", "object": "chat.completion", "created": 1722934238, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_Yeg67XmQbMcohm3NGj0g12ty", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\": \\"Edinburgh\\", \\"country\\": \\"GB\\", \\"units\\": \\"c\\"}"}}, {"id": "call_OGg3UZC2ksjAg7yrLXy8t1MO", "type": "function", "function": {"name": "get_stock_price", "arguments": "{\\"ticker\\": \\"AAPL\\", \\"exchange\\": \\"NASDAQ\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 149, "completion_tokens": 60, "total_tokens": 209}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXjcnIvzZDXRfLfbVTPNL5963GWw", "object": "chat.completion", "created": 1723024744, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_ECSuZ8gcNPPwgt24me91jHsJ", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\": \\"Edinburgh\\", \\"country\\": \\"UK\\", \\"units\\": \\"c\\"}"}}, {"id": "call_Z3fM2sNBBGILhMtimk5Y3RQk", "type": "function", "function": {"name": "get_stock_price", "arguments": "{\\"ticker\\": \\"AAPL\\", \\"exchange\\": \\"NASDAQ\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 149, "completion_tokens": 60, "total_tokens": 209}, "system_fingerprint": "fp_845eaabc1f"}' ), mock_client=client, respx_mock=respx_mock, @@ -466,11 +470,11 @@ class GetStockPrice(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city": "Edinburgh", "country": "GB", "units": "c"}', + arguments='{"city": "Edinburgh", "country": "UK", "units": "c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') ), - id='call_Yeg67XmQbMcohm3NGj0g12ty', + id='call_ECSuZ8gcNPPwgt24me91jHsJ', type='function' ), ParsedFunctionToolCall( @@ -479,7 +483,7 @@ class GetStockPrice(BaseModel): name='get_stock_price', parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') ), - id='call_OGg3UZC2ksjAg7yrLXy8t1MO', + id='call_Z3fM2sNBBGILhMtimk5Y3RQk', type='function' ) ] @@ -524,7 +528,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: ], ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tAC0vDx3MfupXmsduSZavLVaLcrA", "object": "chat.completion", "created": 1722934248, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_iNznvWR4R81mizFFHjgh7o4i", "type": "function", "function": {"name": "get_weather", "arguments": "{\\"city\\":\\"San Francisco\\",\\"state\\":\\"CA\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 48, "completion_tokens": 19, "total_tokens": 67}, "system_fingerprint": "fp_e1a05a1dce"}' + '{"id": "chatcmpl-9tXjfjETDIqeYvDjsuGACbwdY0xsr", "object": "chat.completion", "created": 1723024747, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_7ZZPctBXQWexQlIHSrIHMVUq", "type": "function", "function": {"name": "get_weather", "arguments": "{\\"city\\":\\"San Francisco\\",\\"state\\":\\"CA\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 48, "completion_tokens": 19, "total_tokens": 67}, "system_fingerprint": "fp_2a322c9ffc"}' ), mock_client=client, respx_mock=respx_mock, @@ -550,7 +554,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: name='get_weather', parsed_arguments={'city': 'San Francisco', 'state': 'CA'} ), - id='call_iNznvWR4R81mizFFHjgh7o4i', + id='call_7ZZPctBXQWexQlIHSrIHMVUq', type='function' ) ] diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 3aaa9a0f38..c3dd69ad57 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -48,7 +48,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte }, ], ), - content_snapshot=snapshot(external("b9d6bee9f9b8*.bin")), + content_snapshot=snapshot(external("038a5c69c34c*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -61,8 +61,9 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( - content="I'm unable to provide real-time weather updates. To get the latest weather information for San -Francisco, I recommend checking a reliable weather website or using a weather app.", + content="I'm unable to provide real-time updates, including current weather information. For the latest +weather in San Francisco, I recommend checking a reliable weather website or app such as the Weather Channel, BBC +Weather, or a local San Francisco news station.", function_call=None, parsed=None, refusal=None, @@ -76,8 +77,9 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot( """\ ContentDoneEvent[NoneType]( - content="I'm unable to provide real-time weather updates. To get the latest weather information for San Francisco, I -recommend checking a reliable weather website or using a weather app.", + content="I'm unable to provide real-time updates, including current weather information. For the latest weather in +San Francisco, I recommend checking a reliable weather website or app such as the Weather Channel, BBC Weather, or a +local San Francisco news station.", parsed=None, type='content.done' ) @@ -109,7 +111,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream ], response_format=Location, ), - content_snapshot=snapshot(external("ea9a417d533b*.bin")), + content_snapshot=snapshot(external("15ae68f793c7*.bin")), mock_client=client, respx_mock=respx_mock, on_event=on_event, @@ -138,21 +140,21 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":63,"units":"f"}', + content='{"city":"San Francisco","temperature":68,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=63.0, units='f'), + parsed=Location(city='San Francisco', temperature=68.0, units='f'), refusal=None, role='assistant', tool_calls=[] ) ) ], - created=1722934250, - id='chatcmpl-9tAC2Fr44W8e4GakwKuKSSsFPhISv', - model='gpt-4o-so', + created=1723024750, + id='chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD', + model='gpt-4o-2024-08-06', object='chat.completion', service_tier=None, - system_fingerprint='fp_e1a05a1dce', + system_fingerprint='fp_845eaabc1f', usage=CompletionUsage(completion_tokens=14, prompt_tokens=17, total_tokens=31) ) """ @@ -160,8 +162,8 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot( """\ ContentDoneEvent[Location]( - content='{"city":"San Francisco","temperature":63,"units":"f"}', - parsed=Location(city='San Francisco', temperature=63.0, units='f'), + content='{"city":"San Francisco","temperature":68,"units":"f"}', + parsed=Location(city='San Francisco', temperature=68.0, units='f'), type='content.done' ) """ @@ -189,7 +191,7 @@ class Location(BaseModel): n=3, response_format=Location, ), - content_snapshot=snapshot(external("1437bd06a9d5*.bin")), + content_snapshot=snapshot(external("a0c4f0be184e*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -209,35 +211,35 @@ class Location(BaseModel): "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", @@ -247,9 +249,9 @@ class Location(BaseModel): "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", @@ -259,40 +261,96 @@ class Location(BaseModel): "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", "content.delta", "chunk", - "content.delta", + "refusal.delta", "chunk", "content.delta", "chunk", "content.delta", "chunk", + "refusal.delta", + "chunk", "content.delta", "chunk", + "refusal.delta", + "chunk", "content.delta", "chunk", - "content.done", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", + "chunk", + "refusal.delta", "chunk", "content.done", "chunk", "content.done", "chunk", + "refusal.done", + "chunk", ] ) assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( @@ -303,9 +361,9 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":64,"units":"f"}', + content='{"city":"San Francisco","temperature":63,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=64.0, units='f'), + parsed=Location(city='San Francisco', temperature=63.0, units='f'), refusal=None, role='assistant', tool_calls=[] @@ -316,9 +374,9 @@ class Location(BaseModel): index=1, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":68,"units":"f"}', + content='{"city":"San Francisco","temperature":58.6,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=68.0, units='f'), + parsed=Location(city='San Francisco', temperature=58.6, units='f'), refusal=None, role='assistant', tool_calls=[] @@ -329,10 +387,11 @@ class Location(BaseModel): index=2, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":64,"units":"f"}', + content=None, function_call=None, - parsed=Location(city='San Francisco', temperature=64.0, units='f'), - refusal=None, + parsed=None, + refusal="I'm sorry, but I can't accurately provide the current weather for San Francisco as my data is up to +October 2023. You can try checking a reliable weather website or app for real-time updates.", role='assistant', tool_calls=[] ) @@ -362,7 +421,7 @@ class Location(BaseModel): max_tokens=1, response_format=Location, ), - content_snapshot=snapshot(external("7ae6c1a2631b*.bin")), + content_snapshot=snapshot(external("69363a555f8e*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -386,13 +445,13 @@ class Location(BaseModel): ], response_format=Location, ), - content_snapshot=snapshot(external("d79326933c15*.bin")), + content_snapshot=snapshot(external("ca015b8b1eba*.bin")), mock_client=client, respx_mock=respx_mock, ) assert print_obj(listener.get_event_by_type("refusal.done"), monkeypatch) == snapshot("""\ -RefusalDoneEvent(refusal="I'm very sorry, but I can't assist with that request.", type='refusal.done') +RefusalDoneEvent(refusal="I'm sorry, but I can't assist with that request.", type='refusal.done') """) assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( @@ -406,7 +465,7 @@ class Location(BaseModel): content=None, function_call=None, parsed=None, - refusal="I'm very sorry, but I can't assist with that request.", + refusal="I'm sorry, but I can't assist with that request.", role='assistant', tool_calls=[] ) @@ -429,7 +488,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp ], logprobs=True, ), - content_snapshot=snapshot(external("70c7df71ce72*.bin")), + content_snapshot=snapshot(external("be1089999ca5*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -437,24 +496,26 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp assert print_obj([e for e in listener.events if e.type.startswith("logprobs")], monkeypatch) == snapshot("""\ [ LogprobsContentDeltaEvent( - content=[ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[])], + content=[ + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]) + ], snapshot=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]) ], type='logprobs.content.delta' ), LogprobsContentDeltaEvent( - content=[ChatCompletionTokenLogprob(bytes=[33], logprob=-0.31380808, token='!', top_logprobs=[])], + content=[ChatCompletionTokenLogprob(bytes=[46], logprob=-2.4962392, token='.', top_logprobs=[])], snapshot=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[]), - ChatCompletionTokenLogprob(bytes=[33], logprob=-0.31380808, token='!', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[46], logprob=-2.4962392, token='.', top_logprobs=[]) ], type='logprobs.content.delta' ), LogprobsContentDoneEvent( content=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[]), - ChatCompletionTokenLogprob(bytes=[33], logprob=-0.31380808, token='!', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[46], logprob=-2.4962392, token='.', top_logprobs=[]) ], type='logprobs.content.done' ) @@ -468,13 +529,13 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp index=0, logprobs=ChoiceLogprobs( content=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.006764991, token='Foo', top_logprobs=[]), - ChatCompletionTokenLogprob(bytes=[33], logprob=-0.31380808, token='!', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[46], logprob=-2.4962392, token='.', top_logprobs=[]) ], refusal=None ), message=ParsedChatCompletionMessage[NoneType]( - content='Foo!', + content='Foo.', function_call=None, parsed=None, refusal=None, @@ -505,7 +566,7 @@ class Location(BaseModel): logprobs=True, response_format=Location, ), - content_snapshot=snapshot(external("cb77dc69b6c8*.bin")), + content_snapshot=snapshot(external("0a00cd46c610*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -522,7 +583,6 @@ class Location(BaseModel): 'logprobs.refusal.delta', 'logprobs.refusal.delta', 'logprobs.refusal.delta', - 'logprobs.refusal.delta', 'logprobs.refusal.done' ] """) @@ -535,59 +595,53 @@ class Location(BaseModel): logprobs=ChoiceLogprobs( content=None, refusal=[ - ChatCompletionTokenLogprob(bytes=[73, 39, 109], logprob=-0.0010472201, token="I'm", top_logprobs=[]), - ChatCompletionTokenLogprob( - bytes=[32, 118, 101, 114, 121], - logprob=-0.7292482, - token=' very', - top_logprobs=[] - ), + ChatCompletionTokenLogprob(bytes=[73, 39, 109], logprob=-0.0016157961, token="I'm", top_logprobs=[]), ChatCompletionTokenLogprob( bytes=[32, 115, 111, 114, 114, 121], - logprob=-5.080963e-06, + logprob=-0.78663874, token=' sorry', top_logprobs=[] ), - ChatCompletionTokenLogprob(bytes=[44], logprob=-4.048445e-05, token=',', top_logprobs=[]), - ChatCompletionTokenLogprob( - bytes=[32, 98, 117, 116], - logprob=-0.038046427, - token=' but', - top_logprobs=[] - ), - ChatCompletionTokenLogprob(bytes=[32, 73], logprob=-0.0019351852, token=' I', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[44], logprob=-7.79144e-05, token=',', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[32, 73], logprob=-0.5234622, token=' I', top_logprobs=[]), ChatCompletionTokenLogprob( - bytes=[32, 99, 97, 110, 39, 116], - logprob=-0.008995773, - token=" can't", + bytes=[32, 99, 97, 110, 110, 111, 116], + logprob=-0.52499557, + token=' cannot', top_logprobs=[] ), ChatCompletionTokenLogprob( bytes=[32, 97, 115, 115, 105, 115, 116], - logprob=-0.0033510819, + logprob=-0.015198289, token=' assist', top_logprobs=[] ), ChatCompletionTokenLogprob( bytes=[32, 119, 105, 116, 104], - logprob=-0.0036033941, + logprob=-0.00071648485, token=' with', top_logprobs=[] ), ChatCompletionTokenLogprob( bytes=[32, 116, 104, 97, 116], - logprob=-0.0015974608, + logprob=-0.008114983, token=' that', top_logprobs=[] ), - ChatCompletionTokenLogprob(bytes=[46], logprob=-0.6339823, token='.', top_logprobs=[]) + ChatCompletionTokenLogprob( + bytes=[32, 114, 101, 113, 117, 101, 115, 116], + logprob=-0.0013802331, + token=' request', + top_logprobs=[] + ), + ChatCompletionTokenLogprob(bytes=[46], logprob=-3.4121115e-06, token='.', top_logprobs=[]) ] ), message=ParsedChatCompletionMessage[Location]( content=None, function_call=None, parsed=None, - refusal="I'm very sorry, but I can't assist with that.", + refusal="I'm sorry, I cannot assist with that request.", role='assistant', tool_calls=[] ) @@ -616,7 +670,7 @@ class GetWeatherArgs(BaseModel): openai.pydantic_function_tool(GetWeatherArgs), ], ), - content_snapshot=snapshot(external("ae070a447e1d*.bin")), + content_snapshot=snapshot(external("24aaf30663f9*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -637,11 +691,11 @@ class GetWeatherArgs(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city":"Edinburgh","country":"UK","units":"c"}', + arguments='{"city":"Edinburgh","country":"GB","units":"c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') ), - id='call_Vz6ZXciy6Y0PYfT4d9W7fYB4', + id='call_7PhhveOvvpPK53s1fV8TWhoV', index=0, type='function' ) @@ -668,11 +722,11 @@ class GetWeatherArgs(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city":"Edinburgh","country":"UK","units":"c"}', + arguments='{"city":"Edinburgh","country":"GB","units":"c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') ), - id='call_Vz6ZXciy6Y0PYfT4d9W7fYB4', + id='call_7PhhveOvvpPK53s1fV8TWhoV', index=0, type='function' ) @@ -717,7 +771,7 @@ class GetStockPrice(BaseModel): ), ], ), - content_snapshot=snapshot(external("a346213bec7a*.bin")), + content_snapshot=snapshot(external("453df473e962*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -742,7 +796,7 @@ class GetStockPrice(BaseModel): name='GetWeatherArgs', parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') ), - id='call_g4Q1vRbE0CaHGOs5if8mHsBq', + id='call_lQnnsesjFMWMQ5IeWPHzR4th', index=0, type='function' ), @@ -752,7 +806,7 @@ class GetStockPrice(BaseModel): name='get_stock_price', parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') ), - id='call_gWj3HQxZEHnFvyJLEHIiJKBV', + id='call_2xjOUgaCdiwAcl9ZBL9LyMUU', index=1, type='function' ) @@ -772,7 +826,7 @@ class GetStockPrice(BaseModel): name='GetWeatherArgs', parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') ), - id='call_g4Q1vRbE0CaHGOs5if8mHsBq', + id='call_lQnnsesjFMWMQ5IeWPHzR4th', index=0, type='function' ), @@ -782,7 +836,7 @@ class GetStockPrice(BaseModel): name='get_stock_price', parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') ), - id='call_gWj3HQxZEHnFvyJLEHIiJKBV', + id='call_2xjOUgaCdiwAcl9ZBL9LyMUU', index=1, type='function' ) @@ -824,7 +878,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: } ], ), - content_snapshot=snapshot(external("a7097cae6a1f*.bin")), + content_snapshot=snapshot(external("83d3d003e6fd*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -849,7 +903,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: name='get_weather', parsed_arguments={'city': 'San Francisco', 'state': 'CA'} ), - id='call_rQe3kzGnTr2epjx8HREg3F2a', + id='call_pVHYsU0gmSfX5TqxOyVbB2ma', index=0, type='function' ) @@ -874,7 +928,7 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo ], response_format={"type": "json_object"}, ), - content_snapshot=snapshot(external("3e0df46f250d*.bin")), + content_snapshot=snapshot(external("0898f3d1651e*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -887,9 +941,10 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( - content='{\\n "location": "San Francisco, CA",\\n "temperature": "N/A",\\n "conditions": "N/A",\\n -"humidity": "N/A",\\n "wind_speed": "N/A",\\n "timestamp": "N/A",\\n "note": "Real-time weather data is not available. -Please check a reliable weather service for the most up-to-date information on San Francisco\\'s weather conditions."}', + content='\\n {\\n "location": "San Francisco, CA",\\n "forecast_date": "2023-11-02",\\n "weather": {\\n +"temperature": {\\n "current": "N/A",\\n "high": "N/A",\\n "low": "N/A"\\n },\\n "condition": +"N/A",\\n "humidity": "N/A",\\n "wind_speed": "N/A"\\n },\\n "note": "Please check a reliable weather +service for the most current information."\\n }', function_call=None, parsed=None, refusal=None, @@ -920,7 +975,7 @@ def test_allows_non_strict_tools_but_no_parsing( } ], ), - content_snapshot=snapshot(external("fb75060ede89*.bin")), + content_snapshot=snapshot(external("dae1b261f197*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -939,7 +994,7 @@ def test_allows_non_strict_tools_but_no_parsing( """\ [ ParsedChoice[NoneType]( - finish_reason='stop', + finish_reason='tool_calls', index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( @@ -955,7 +1010,7 @@ def test_allows_non_strict_tools_but_no_parsing( name='get_weather', parsed_arguments=None ), - id='call_9rqjEc1DQRADTYGVV45LbZwL', + id='call_5uxEBMFySqqQGu02I5QHA8k6', index=0, type='function' ) From 631a2a7156299351874f37c4769308a104ce19ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:04:38 +0000 Subject: [PATCH 548/914] release: 1.40.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0c37ae42ca..4b272115e4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.0" + ".": "1.40.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2454a9a6cc..95b51e8a03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.40.1 (2024-08-07) + +Full Changelog: [v1.40.0...v1.40.1](https://github.com/openai/openai-python/compare/v1.40.0...v1.40.1) + +### Chores + +* **internal:** update OpenAPI spec url ([#1608](https://github.com/openai/openai-python/issues/1608)) ([5392753](https://github.com/openai/openai-python/commit/53927531fc101e96b9e3f5d44f34b298055f496a)) +* **internal:** update test snapshots ([a11d1cb](https://github.com/openai/openai-python/commit/a11d1cb5d04aac0bf69dc10a3a21fa95575c0aa0)) + ## 1.40.0 (2024-08-06) Full Changelog: [v1.39.0...v1.40.0](https://github.com/openai/openai-python/compare/v1.39.0...v1.40.0) diff --git a/pyproject.toml b/pyproject.toml index 1e86c44706..af661cbad7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.0" +version = "1.40.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 73cd42e5ea..f88b8dead1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.0" # x-release-please-version +__version__ = "1.40.1" # x-release-please-version From 5f52e473a9c08dd30e3e956f81b5e7a3d6a802f5 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 7 Aug 2024 10:13:08 +0100 Subject: [PATCH 549/914] fix(client): raise helpful error message for response_format misuse --- src/openai/resources/chat/completions.py | 11 ++++++ tests/api_resources/chat/test_completions.py | 35 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 3dcd3774d7..fb4c71ba99 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -2,10 +2,12 @@ from __future__ import annotations +import inspect from typing import Dict, List, Union, Iterable, Optional, overload from typing_extensions import Literal import httpx +import pydantic from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven @@ -647,6 +649,7 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | Stream[ChatCompletionChunk]: + validate_response_format(response_format) return self._post( "/chat/completions", body=maybe_transform( @@ -1302,6 +1305,7 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: + validate_response_format(response_format) return await self._post( "/chat/completions", body=await async_maybe_transform( @@ -1375,3 +1379,10 @@ def __init__(self, completions: AsyncCompletions) -> None: self.create = async_to_streamed_response_wrapper( completions.create, ) + + +def validate_response_format(response_format: object) -> None: + if inspect.isclass(response_format) and issubclass(response_format, pydantic.BaseModel): + raise TypeError( + "You tried to pass a `BaseModel` class to `chat.completions.create()`; You must use `beta.chat.completions.parse()` instead" + ) diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index d744dfe6ea..9fa3cc8284 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -6,6 +6,7 @@ from typing import Any, cast import pytest +import pydantic from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type @@ -257,6 +258,23 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_create_disallows_pydantic(self, client: OpenAI) -> None: + class MyModel(pydantic.BaseModel): + a: str + + with pytest.raises(TypeError, match=r"You tried to pass a `BaseModel` class"): + client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-4o", + response_format=cast(Any, MyModel), + ) + class TestAsyncCompletions: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -498,3 +516,20 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpe await stream.close() assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_create_disallows_pydantic(self, async_client: AsyncOpenAI) -> None: + class MyModel(pydantic.BaseModel): + a: str + + with pytest.raises(TypeError, match=r"You tried to pass a `BaseModel` class"): + await async_client.chat.completions.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-4o", + response_format=cast(Any, MyModel), + ) From a9b8aa8e1812762ae1d30e1f893d4be1bc825a05 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 7 Aug 2024 10:13:24 +0100 Subject: [PATCH 550/914] chore(internal): format some docstrings --- src/openai/resources/beta/chat/completions.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 88ea2c0572..aee88c3c0f 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -78,14 +78,17 @@ def parse( from pydantic import BaseModel from openai import OpenAI + class Step(BaseModel): explanation: str output: str + class MathResponse(BaseModel): steps: List[Step] final_answer: str + client = OpenAI() completion = client.beta.chat.completions.parse( model="gpt-4o-2024-08-06", @@ -184,12 +187,12 @@ def stream( ```py with client.beta.chat.completions.stream( - model='gpt-4o-2024-08-06', + model="gpt-4o-2024-08-06", messages=[...], ) as stream: for event in stream: - if event.type == 'content.delta': - print(event.content, flush=True, end='') + if event.type == "content.delta": + print(event.content, flush=True, end="") ``` When the context manager is entered, a `ChatCompletionStream` instance is returned which, like `.create(stream=True)` is an iterator. The full list of events that are yielded by the iterator are outlined in [these docs](https://github.com/openai/openai-python/blob/main/helpers.md#chat-completions-events). @@ -287,14 +290,17 @@ async def parse( from pydantic import BaseModel from openai import AsyncOpenAI + class Step(BaseModel): explanation: str output: str + class MathResponse(BaseModel): steps: List[Step] final_answer: str + client = AsyncOpenAI() completion = await client.beta.chat.completions.parse( model="gpt-4o-2024-08-06", @@ -393,12 +399,12 @@ def stream( ```py async with client.beta.chat.completions.stream( - model='gpt-4o-2024-08-06', + model="gpt-4o-2024-08-06", messages=[...], ) as stream: async for event in stream: - if event.type == 'content.delta': - print(event.content, flush=True, end='') + if event.type == "content.delta": + print(event.content, flush=True, end="") ``` When the context manager is entered, an `AsyncChatCompletionStream` instance is returned which, like `.create(stream=True)` is an async iterator. The full list of events that are yielded by the iterator are outlined in [these docs](https://github.com/openai/openai-python/blob/main/helpers.md#chat-completions-events). From ef859c889df8a0e715377ea560a09b5ed1f3c40a Mon Sep 17 00:00:00 2001 From: Alex Protsyk Date: Thu, 8 Aug 2024 15:33:53 +0200 Subject: [PATCH 551/914] fix(json schema): support recursive BaseModels in Pydantic v1 (#1623) --- src/openai/lib/_pydantic.py | 5 +++++ tests/lib/test_pydantic.py | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py index 967ad5de57..a90effdf1a 100644 --- a/src/openai/lib/_pydantic.py +++ b/src/openai/lib/_pydantic.py @@ -62,6 +62,11 @@ def _ensure_strict_json_schema( for def_name, def_schema in defs.items(): _ensure_strict_json_schema(def_schema, path=(*path, "$defs", def_name)) + definitions = json_schema.get("definitions") + if is_dict(definitions): + for definition_name, definition_schema in definitions.items(): + _ensure_strict_json_schema(definition_schema, path=(*path, "definitions", definition_name)) + return json_schema diff --git a/tests/lib/test_pydantic.py b/tests/lib/test_pydantic.py index dc09596da2..a8fe8f4570 100644 --- a/tests/lib/test_pydantic.py +++ b/tests/lib/test_pydantic.py @@ -130,6 +130,7 @@ def test_most_types() -> None: "type": "object", "properties": {"column_name": {"title": "Column Name", "type": "string"}}, "required": ["column_name"], + "additionalProperties": False, }, "Condition": { "title": "Condition", @@ -147,6 +148,7 @@ def test_most_types() -> None: }, }, "required": ["column", "operator", "value"], + "additionalProperties": False, }, "OrderBy": { "title": "OrderBy", From a7f4ba7e976f80fff7e6994df6470249a5f52eb8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:21:24 +0000 Subject: [PATCH 552/914] chore(internal): updates (#1624) --- .stats.yml | 2 +- pyproject.toml | 3 +- src/openai/resources/chat/completions.py | 30 +++++++++++++++++++ .../types/chat/completion_create_params.py | 5 ++++ src/openai/types/chat_model.py | 2 +- 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index ac652c9271..cad2c64cd0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-4097c2f86beb3f3bb021775cd1dfa240e960caf842aeefc2e08da4dc0851ea79.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-97797a9363b9960b5f2fbdc84426a2b91e75533ecd409fe99e37c231180a4339.yml diff --git a/pyproject.toml b/pyproject.toml index af661cbad7..037b63242a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -202,7 +202,6 @@ unfixable = [ "T201", "T203", ] -ignore-init-module-imports = true [tool.ruff.lint.flake8-tidy-imports.banned-api] "functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" @@ -214,7 +213,7 @@ combine-as-imports = true extra-standard-library = ["typing_extensions"] known-first-party = ["openai", "tests"] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "bin/**.py" = ["T201", "T203"] "scripts/**.py" = ["T201", "T203"] "tests/**.py" = ["T201", "T203"] diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index fb4c71ba99..dc577d6251 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -149,6 +149,11 @@ def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -347,6 +352,11 @@ def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -538,6 +548,11 @@ def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -805,6 +820,11 @@ async def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1003,6 +1023,11 @@ async def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. @@ -1194,6 +1219,11 @@ async def create( [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index bf648a3858..61126b37ac 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -126,6 +126,11 @@ class CompletionCreateParamsBase(TypedDict, total=False): [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which guarantees the model will match your supplied JSON schema. Learn + more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index 686f26b783..09bc081f7a 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -6,8 +6,8 @@ ChatModel: TypeAlias = Literal[ "gpt-4o", - "gpt-4o-2024-08-06", "gpt-4o-2024-05-13", + "gpt-4o-2024-08-06", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", "gpt-4-turbo", From 646fff04dbe9458f1c46753031546ccf6415eeef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:21:56 +0000 Subject: [PATCH 553/914] release: 1.40.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4b272115e4..2b233c7b0d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.1" + ".": "1.40.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 95b51e8a03..a6c65f0eb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.40.2 (2024-08-08) + +Full Changelog: [v1.40.1...v1.40.2](https://github.com/openai/openai-python/compare/v1.40.1...v1.40.2) + +### Bug Fixes + +* **client:** raise helpful error message for response_format misuse ([18191da](https://github.com/openai/openai-python/commit/18191dac8e1437a0f708525d474b7ecfe459d966)) +* **json schema:** support recursive BaseModels in Pydantic v1 ([#1623](https://github.com/openai/openai-python/issues/1623)) ([43e10c0](https://github.com/openai/openai-python/commit/43e10c0f251a42f1e6497f360c6c23d3058b3da3)) + + +### Chores + +* **internal:** format some docstrings ([d34a081](https://github.com/openai/openai-python/commit/d34a081c30f869646145919b2256ded115241eb5)) +* **internal:** updates ([#1624](https://github.com/openai/openai-python/issues/1624)) ([598e7a2](https://github.com/openai/openai-python/commit/598e7a23768e7addbe1319ada2e87caee3cf0d14)) + ## 1.40.1 (2024-08-07) Full Changelog: [v1.40.0...v1.40.1](https://github.com/openai/openai-python/compare/v1.40.0...v1.40.1) diff --git a/pyproject.toml b/pyproject.toml index 037b63242a..9600130ca3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.1" +version = "1.40.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f88b8dead1..7e6783b6c6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.1" # x-release-please-version +__version__ = "1.40.2" # x-release-please-version From 4373893397584308faba493cfb6d47195063e4a9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 16:32:50 +0000 Subject: [PATCH 554/914] chore(ci): codeowners file (#1627) --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3ce5f8d004..d58c8454c5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,4 @@ +# This file is used to automatically assign reviewers to PRs +# For more information see: https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners + * @openai/sdks-team From 2a8773659ea9292a75fcab96d72dd40b26b3a5ff Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:43:42 +0000 Subject: [PATCH 555/914] chore(ci): bump prism mock server version (#1630) --- scripts/mock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mock b/scripts/mock index f586157699..d2814ae6a0 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stainless-api/prism-cli@5.8.4 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log & # Wait for server to come online echo -n "Waiting for server" @@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stainless-api/prism-cli@5.8.4 -- prism mock "$URL" + npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" fi From 1d88db0cd80ad10bc403ded04db975df99d9e0c0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:05:02 +0000 Subject: [PATCH 556/914] chore(internal): ensure package is importable in lint cmd (#1631) --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 9600130ca3..12360cbab7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,10 +83,13 @@ format = { chain = [ "lint" = { chain = [ "check:ruff", "typecheck", + "check:importable", ]} "check:ruff" = "ruff check ." "fix:ruff" = "ruff check --fix ." +"check:importable" = "python -c 'import openai'" + typecheck = { chain = [ "typecheck:pyright", "typecheck:mypy" From 1a388a193f7484f685c6c03a997f7c193a3fcb94 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 10 Aug 2024 05:03:53 +0000 Subject: [PATCH 557/914] release: 1.40.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 10 ++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2b233c7b0d..64a418cb06 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.2" + ".": "1.40.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c65f0eb2..825e753924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.40.3 (2024-08-10) + +Full Changelog: [v1.40.2...v1.40.3](https://github.com/openai/openai-python/compare/v1.40.2...v1.40.3) + +### Chores + +* **ci:** bump prism mock server version ([#1630](https://github.com/openai/openai-python/issues/1630)) ([214d8fd](https://github.com/openai/openai-python/commit/214d8fd8d7d43c06c7dfe02680847a6a60988120)) +* **ci:** codeowners file ([#1627](https://github.com/openai/openai-python/issues/1627)) ([c059a20](https://github.com/openai/openai-python/commit/c059a20c8cd2124178641c9d8688e276b1cf1d59)) +* **internal:** ensure package is importable in lint cmd ([#1631](https://github.com/openai/openai-python/issues/1631)) ([779e6d0](https://github.com/openai/openai-python/commit/779e6d081eb55c158f2aa1962190079eb7f1335e)) + ## 1.40.2 (2024-08-08) Full Changelog: [v1.40.1...v1.40.2](https://github.com/openai/openai-python/compare/v1.40.1...v1.40.2) diff --git a/pyproject.toml b/pyproject.toml index 12360cbab7..09a11668a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.2" +version = "1.40.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7e6783b6c6..5eed512d7c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.2" # x-release-please-version +__version__ = "1.40.3" # x-release-please-version From 09350cb5d042b6e20b3a647e55ce129c0530934e Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 12 Aug 2024 08:30:48 +0100 Subject: [PATCH 558/914] fix(json schema): unwrap `allOf`s with one entry --- src/openai/lib/_pydantic.py | 10 ++++-- tests/lib/test_pydantic.py | 63 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py index a90effdf1a..85f147c236 100644 --- a/src/openai/lib/_pydantic.py +++ b/src/openai/lib/_pydantic.py @@ -53,9 +53,13 @@ def _ensure_strict_json_schema( # intersections all_of = json_schema.get("allOf") if is_list(all_of): - json_schema["allOf"] = [ - _ensure_strict_json_schema(entry, path=(*path, "anyOf", str(i))) for i, entry in enumerate(all_of) - ] + if len(all_of) == 1: + json_schema.update(_ensure_strict_json_schema(all_of[0], path=(*path, "allOf", "0"))) + json_schema.pop("allOf") + else: + json_schema["allOf"] = [ + _ensure_strict_json_schema(entry, path=(*path, "allOf", str(i))) for i, entry in enumerate(all_of) + ] defs = json_schema.get("$defs") if is_dict(defs): diff --git a/tests/lib/test_pydantic.py b/tests/lib/test_pydantic.py index a8fe8f4570..568844eada 100644 --- a/tests/lib/test_pydantic.py +++ b/tests/lib/test_pydantic.py @@ -1,5 +1,8 @@ from __future__ import annotations +from enum import Enum + +from pydantic import Field, BaseModel from inline_snapshot import snapshot import openai @@ -161,3 +164,63 @@ def test_most_types() -> None: }, } ) + + +class Color(Enum): + RED = "red" + BLUE = "blue" + GREEN = "green" + + +class ColorDetection(BaseModel): + color: Color = Field(description="The detected color") + hex_color_code: str = Field(description="The hex color code of the detected color") + + +def test_enums() -> None: + if PYDANTIC_V2: + assert openai.pydantic_function_tool(ColorDetection)["function"] == snapshot( + { + "name": "ColorDetection", + "strict": True, + "parameters": { + "$defs": {"Color": {"enum": ["red", "blue", "green"], "title": "Color", "type": "string"}}, + "properties": { + "color": {"description": "The detected color", "$ref": "#/$defs/Color"}, + "hex_color_code": { + "description": "The hex color code of the detected color", + "title": "Hex Color Code", + "type": "string", + }, + }, + "required": ["color", "hex_color_code"], + "title": "ColorDetection", + "type": "object", + "additionalProperties": False, + }, + } + ) + else: + assert openai.pydantic_function_tool(ColorDetection)["function"] == snapshot( + { + "name": "ColorDetection", + "strict": True, + "parameters": { + "properties": { + "color": {"description": "The detected color", "$ref": "#/definitions/Color"}, + "hex_color_code": { + "description": "The hex color code of the detected color", + "title": "Hex Color Code", + "type": "string", + }, + }, + "required": ["color", "hex_color_code"], + "title": "ColorDetection", + "definitions": { + "Color": {"title": "Color", "description": "An enumeration.", "enum": ["red", "blue", "green"]} + }, + "type": "object", + "additionalProperties": False, + }, + } + ) From ad5ac9fc6222710bedca52016b2e7374d680c221 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 12 Aug 2024 08:48:17 +0100 Subject: [PATCH 559/914] fix(json schema): unravel `$ref`s alongside additional keys --- src/openai/lib/_pydantic.py | 73 ++++++++++++++++++++++++------ tests/lib/chat/test_completions.py | 50 +++++++++++++++++++- tests/lib/test_pydantic.py | 13 +++++- 3 files changed, 119 insertions(+), 17 deletions(-) diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py index 85f147c236..ad3b6eb29f 100644 --- a/src/openai/lib/_pydantic.py +++ b/src/openai/lib/_pydantic.py @@ -10,12 +10,15 @@ def to_strict_json_schema(model: type[pydantic.BaseModel]) -> dict[str, Any]: - return _ensure_strict_json_schema(model_json_schema(model), path=()) + schema = model_json_schema(model) + return _ensure_strict_json_schema(schema, path=(), root=schema) def _ensure_strict_json_schema( json_schema: object, + *, path: tuple[str, ...], + root: dict[str, object], ) -> dict[str, Any]: """Mutates the given JSON schema to ensure it conforms to the `strict` standard that the API expects. @@ -23,6 +26,16 @@ def _ensure_strict_json_schema( if not is_dict(json_schema): raise TypeError(f"Expected {json_schema} to be a dictionary; path={path}") + defs = json_schema.get("$defs") + if is_dict(defs): + for def_name, def_schema in defs.items(): + _ensure_strict_json_schema(def_schema, path=(*path, "$defs", def_name), root=root) + + definitions = json_schema.get("definitions") + if is_dict(definitions): + for definition_name, definition_schema in definitions.items(): + _ensure_strict_json_schema(definition_schema, path=(*path, "definitions", definition_name), root=root) + typ = json_schema.get("type") if typ == "object" and "additionalProperties" not in json_schema: json_schema["additionalProperties"] = False @@ -33,7 +46,7 @@ def _ensure_strict_json_schema( if is_dict(properties): json_schema["required"] = [prop for prop in properties.keys()] json_schema["properties"] = { - key: _ensure_strict_json_schema(prop_schema, path=(*path, "properties", key)) + key: _ensure_strict_json_schema(prop_schema, path=(*path, "properties", key), root=root) for key, prop_schema in properties.items() } @@ -41,40 +54,72 @@ def _ensure_strict_json_schema( # { 'type': 'array', 'items': {...} } items = json_schema.get("items") if is_dict(items): - json_schema["items"] = _ensure_strict_json_schema(items, path=(*path, "items")) + json_schema["items"] = _ensure_strict_json_schema(items, path=(*path, "items"), root=root) # unions any_of = json_schema.get("anyOf") if is_list(any_of): json_schema["anyOf"] = [ - _ensure_strict_json_schema(variant, path=(*path, "anyOf", str(i))) for i, variant in enumerate(any_of) + _ensure_strict_json_schema(variant, path=(*path, "anyOf", str(i)), root=root) + for i, variant in enumerate(any_of) ] # intersections all_of = json_schema.get("allOf") if is_list(all_of): if len(all_of) == 1: - json_schema.update(_ensure_strict_json_schema(all_of[0], path=(*path, "allOf", "0"))) + json_schema.update(_ensure_strict_json_schema(all_of[0], path=(*path, "allOf", "0"), root=root)) json_schema.pop("allOf") else: json_schema["allOf"] = [ - _ensure_strict_json_schema(entry, path=(*path, "allOf", str(i))) for i, entry in enumerate(all_of) + _ensure_strict_json_schema(entry, path=(*path, "allOf", str(i)), root=root) + for i, entry in enumerate(all_of) ] - defs = json_schema.get("$defs") - if is_dict(defs): - for def_name, def_schema in defs.items(): - _ensure_strict_json_schema(def_schema, path=(*path, "$defs", def_name)) + # we can't use `$ref`s if there are also other properties defined, e.g. + # `{"$ref": "...", "description": "my description"}` + # + # so we unravel the ref + # `{"type": "string", "description": "my description"}` + ref = json_schema.get("$ref") + if ref and has_more_than_n_keys(json_schema, 1): + assert isinstance(ref, str), f"Received non-string $ref - {ref}" - definitions = json_schema.get("definitions") - if is_dict(definitions): - for definition_name, definition_schema in definitions.items(): - _ensure_strict_json_schema(definition_schema, path=(*path, "definitions", definition_name)) + resolved = resolve_ref(root=root, ref=ref) + if not is_dict(resolved): + raise ValueError(f"Expected `$ref: {ref}` to resolved to a dictionary but got {resolved}") + + # properties from the json schema take priority over the ones on the `$ref` + json_schema.update({**resolved, **json_schema}) + json_schema.pop("$ref") return json_schema +def resolve_ref(*, root: dict[str, object], ref: str) -> object: + if not ref.startswith("#/"): + raise ValueError(f"Unexpected $ref format {ref!r}; Does not start with #/") + + path = ref[2:].split("/") + resolved = root + for key in path: + value = resolved[key] + assert is_dict(value), f"encountered non-dictionary entry while resolving {ref} - {resolved}" + resolved = value + + return resolved + + def is_dict(obj: object) -> TypeGuard[dict[str, object]]: # just pretend that we know there are only `str` keys # as that check is not worth the performance cost return _is_dict(obj) + + +def has_more_than_n_keys(obj: dict[str, object], n: int) -> bool: + i = 0 + for _ in obj.keys(): + i += 1 + if i > n: + return True + return False diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index e406a5a3bc..d2189e7cb6 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -2,13 +2,14 @@ import os import json +from enum import Enum from typing import Any, Callable from typing_extensions import Literal, TypeVar import httpx import pytest from respx import MockRouter -from pydantic import BaseModel +from pydantic import Field, BaseModel from inline_snapshot import snapshot import openai @@ -133,6 +134,53 @@ class Location(BaseModel): ) +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_model_enum(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class Color(Enum): + """The detected color""" + + RED = "red" + BLUE = "blue" + GREEN = "green" + + class ColorDetection(BaseModel): + color: Color + hex_color_code: str = Field(description="The hex color code of the detected color") + + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "user", "content": "What color is a Coke can?"}, + ], + response_format=ColorDetection, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9vK4UZVr385F2UgZlP1ShwPn2nFxG", "object": "chat.completion", "created": 1723448878, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"color\\":\\"red\\",\\"hex_color_code\\":\\"#FF0000\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 18, "completion_tokens": 14, "total_tokens": 32}, "system_fingerprint": "fp_845eaabc1f"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion.choices[0], monkeypatch) == snapshot( + """\ +ParsedChoice[ColorDetection]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[ColorDetection]( + content='{"color":"red","hex_color_code":"#FF0000"}', + function_call=None, + parsed=ColorDetection(color=, hex_color_code='#FF0000'), + refusal=None, + role='assistant', + tool_calls=[] + ) +) +""" + ) + + @pytest.mark.respx(base_url=base_url) def test_parse_pydantic_model_multiple_choices( client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch diff --git a/tests/lib/test_pydantic.py b/tests/lib/test_pydantic.py index 568844eada..531a89df58 100644 --- a/tests/lib/test_pydantic.py +++ b/tests/lib/test_pydantic.py @@ -186,7 +186,12 @@ def test_enums() -> None: "parameters": { "$defs": {"Color": {"enum": ["red", "blue", "green"], "title": "Color", "type": "string"}}, "properties": { - "color": {"description": "The detected color", "$ref": "#/$defs/Color"}, + "color": { + "description": "The detected color", + "enum": ["red", "blue", "green"], + "title": "Color", + "type": "string", + }, "hex_color_code": { "description": "The hex color code of the detected color", "title": "Hex Color Code", @@ -207,7 +212,11 @@ def test_enums() -> None: "strict": True, "parameters": { "properties": { - "color": {"description": "The detected color", "$ref": "#/definitions/Color"}, + "color": { + "description": "The detected color", + "title": "Color", + "enum": ["red", "blue", "green"], + }, "hex_color_code": { "description": "The hex color code of the detected color", "title": "Hex Color Code", From 153e4a45edf2b01f86ea985519e03394e98f68de Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:45:39 +0000 Subject: [PATCH 560/914] release: 1.40.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 64a418cb06..0c2e4f80a4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.3" + ".": "1.40.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 825e753924..aaf31653a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.40.4 (2024-08-12) + +Full Changelog: [v1.40.3...v1.40.4](https://github.com/openai/openai-python/compare/v1.40.3...v1.40.4) + +### Bug Fixes + +* **json schema:** unravel `$ref`s alongside additional keys ([c7a3d29](https://github.com/openai/openai-python/commit/c7a3d2986acaf3b31844b39608d03265ad87bb04)) +* **json schema:** unwrap `allOf`s with one entry ([53d964d](https://github.com/openai/openai-python/commit/53d964defebdf385d7d832ec7f13111b4af13c27)) + ## 1.40.3 (2024-08-10) Full Changelog: [v1.40.2...v1.40.3](https://github.com/openai/openai-python/compare/v1.40.2...v1.40.3) diff --git a/pyproject.toml b/pyproject.toml index 09a11668a3..e7436330a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.3" +version = "1.40.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5eed512d7c..bbf4d8303c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.3" # x-release-please-version +__version__ = "1.40.4" # x-release-please-version From b23980b0354b88a9f7a98bc9018406c3ce0dfa52 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 12 Aug 2024 14:38:38 -0400 Subject: [PATCH 561/914] docs(helpers): make async client usage more clear closes #1639 --- helpers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/helpers.md b/helpers.md index 2e0d314b50..965dd6e23c 100644 --- a/helpers.md +++ b/helpers.md @@ -139,6 +139,10 @@ It also supports all aforementioned [parsing helpers](#parsing-helpers). Unlike `.create(stream=True)`, the `.stream()` method requires usage within a context manager to prevent accidental leakage of the response: ```py +from openai import AsyncOpenAI + +client = AsyncOpenAI() + async with client.beta.chat.completions.stream( model='gpt-4o-2024-08-06', messages=[...], From 22bbd4a092b5248a4a96a08750ca613117fc1a38 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:39:04 +0000 Subject: [PATCH 562/914] release: 1.40.5 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0c2e4f80a4..190c2adda3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.4" + ".": "1.40.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index aaf31653a0..4ee946c4df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.40.5 (2024-08-12) + +Full Changelog: [v1.40.4...v1.40.5](https://github.com/openai/openai-python/compare/v1.40.4...v1.40.5) + +### Documentation + +* **helpers:** make async client usage more clear ([34e1edf](https://github.com/openai/openai-python/commit/34e1edf29d6008df7196aaebc45172fa536c6410)), closes [#1639](https://github.com/openai/openai-python/issues/1639) + ## 1.40.4 (2024-08-12) Full Changelog: [v1.40.3...v1.40.4](https://github.com/openai/openai-python/compare/v1.40.3...v1.40.4) diff --git a/pyproject.toml b/pyproject.toml index e7436330a5..1e094d14a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.4" +version = "1.40.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index bbf4d8303c..d416db5cac 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.4" # x-release-please-version +__version__ = "1.40.5" # x-release-please-version From f1e1060ec8403a7ae56e3a9a1272842d858c60ff Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 12 Aug 2024 15:08:41 -0400 Subject: [PATCH 563/914] chore(tests): fix pydantic v1 tests --- tests/lib/chat/test_completions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index d2189e7cb6..f003866653 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -15,6 +15,7 @@ import openai from openai import OpenAI, AsyncOpenAI from openai._utils import assert_signatures_in_sync +from openai._compat import PYDANTIC_V2 from ._utils import print_obj from ...conftest import base_url @@ -147,6 +148,9 @@ class ColorDetection(BaseModel): color: Color hex_color_code: str = Field(description="The hex color code of the detected color") + if not PYDANTIC_V2: + ColorDetection.update_forward_refs(**locals()) # type: ignore + completion = _make_snapshot_request( lambda c: c.beta.chat.completions.parse( model="gpt-4o-2024-08-06", From ab57d86d07daeab04ee122161f80e00b4b58e580 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:21:15 +0000 Subject: [PATCH 564/914] chore(internal): update some imports (#1642) --- .../beta/assistant_response_format_option_param.py | 9 ++++----- src/openai/types/beta/function_tool_param.py | 4 ++-- src/openai/types/chat/chat_completion_tool_param.py | 4 ++-- src/openai/types/chat/completion_create_params.py | 11 ++++++----- src/openai/types/shared_params/function_definition.py | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/openai/types/beta/assistant_response_format_option_param.py b/src/openai/types/beta/assistant_response_format_option_param.py index 680a060c3c..5e724a4d98 100644 --- a/src/openai/types/beta/assistant_response_format_option_param.py +++ b/src/openai/types/beta/assistant_response_format_option_param.py @@ -5,13 +5,12 @@ from typing import Union from typing_extensions import Literal, TypeAlias -from ...types import shared_params +from ..shared_params.response_format_text import ResponseFormatText +from ..shared_params.response_format_json_object import ResponseFormatJSONObject +from ..shared_params.response_format_json_schema import ResponseFormatJSONSchema __all__ = ["AssistantResponseFormatOptionParam"] AssistantResponseFormatOptionParam: TypeAlias = Union[ - Literal["auto"], - shared_params.ResponseFormatText, - shared_params.ResponseFormatJSONObject, - shared_params.ResponseFormatJSONSchema, + Literal["auto"], ResponseFormatText, ResponseFormatJSONObject, ResponseFormatJSONSchema ] diff --git a/src/openai/types/beta/function_tool_param.py b/src/openai/types/beta/function_tool_param.py index b44c0d47ef..d906e02b88 100644 --- a/src/openai/types/beta/function_tool_param.py +++ b/src/openai/types/beta/function_tool_param.py @@ -4,13 +4,13 @@ from typing_extensions import Literal, Required, TypedDict -from ...types import shared_params +from ..shared_params.function_definition import FunctionDefinition __all__ = ["FunctionToolParam"] class FunctionToolParam(TypedDict, total=False): - function: Required[shared_params.FunctionDefinition] + function: Required[FunctionDefinition] type: Required[Literal["function"]] """The type of tool being defined: `function`""" diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py index 0cf6ea7268..6c2b1a36f0 100644 --- a/src/openai/types/chat/chat_completion_tool_param.py +++ b/src/openai/types/chat/chat_completion_tool_param.py @@ -4,13 +4,13 @@ from typing_extensions import Literal, Required, TypedDict -from ...types import shared_params +from ..shared_params.function_definition import FunctionDefinition __all__ = ["ChatCompletionToolParam"] class ChatCompletionToolParam(TypedDict, total=False): - function: Required[shared_params.FunctionDefinition] + function: Required[FunctionDefinition] type: Required[Literal["function"]] """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 61126b37ac..91435dcedd 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -5,12 +5,15 @@ from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict -from ...types import shared_params from ..chat_model import ChatModel from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_message_param import ChatCompletionMessageParam +from ..shared_params.function_parameters import FunctionParameters +from ..shared_params.response_format_text import ResponseFormatText from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam +from ..shared_params.response_format_json_object import ResponseFormatJSONObject +from ..shared_params.response_format_json_schema import ResponseFormatJSONSchema from .chat_completion_function_call_option_param import ChatCompletionFunctionCallOptionParam __all__ = [ @@ -244,7 +247,7 @@ class Function(TypedDict, total=False): how to call the function. """ - parameters: shared_params.FunctionParameters + parameters: FunctionParameters """The parameters the functions accepts, described as a JSON Schema object. See the [guide](https://platform.openai.com/docs/guides/function-calling) for @@ -256,9 +259,7 @@ class Function(TypedDict, total=False): """ -ResponseFormat: TypeAlias = Union[ - shared_params.ResponseFormatText, shared_params.ResponseFormatJSONObject, shared_params.ResponseFormatJSONSchema -] +ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONObject, ResponseFormatJSONSchema] class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase): diff --git a/src/openai/types/shared_params/function_definition.py b/src/openai/types/shared_params/function_definition.py index f41392f154..d45ec13f1e 100644 --- a/src/openai/types/shared_params/function_definition.py +++ b/src/openai/types/shared_params/function_definition.py @@ -5,7 +5,7 @@ from typing import Optional from typing_extensions import Required, TypedDict -from ...types import shared_params +from .function_parameters import FunctionParameters __all__ = ["FunctionDefinition"] @@ -24,7 +24,7 @@ class FunctionDefinition(TypedDict, total=False): how to call the function. """ - parameters: shared_params.FunctionParameters + parameters: FunctionParameters """The parameters the functions accepts, described as a JSON Schema object. See the [guide](https://platform.openai.com/docs/guides/function-calling) for From 092a8df7a46878c11f4c4fdea0fbc80e8d96d669 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 12 Aug 2024 19:52:09 +0000 Subject: [PATCH 565/914] chore(examples): minor formatting changes (#1644) --- tests/api_resources/beta/test_assistants.py | 4 +- tests/api_resources/beta/test_threads.py | 56 ++++++------ tests/api_resources/beta/threads/test_runs.py | 48 +++++----- tests/api_resources/chat/test_completions.py | 88 +++++++++---------- tests/api_resources/fine_tuning/test_jobs.py | 24 ++--- tests/api_resources/test_images.py | 12 +-- 6 files changed, 116 insertions(+), 116 deletions(-) diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index fbd5ff0597..642935cdaf 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -44,8 +44,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "vector_store_ids": ["string"], "vector_stores": [ { - "file_ids": ["string", "string", "string"], "chunking_strategy": {"type": "auto"}, + "file_ids": ["string", "string", "string"], "metadata": {}, } ], @@ -276,8 +276,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "vector_store_ids": ["string"], "vector_stores": [ { - "file_ids": ["string", "string", "string"], "chunking_strategy": {"type": "auto"}, + "file_ids": ["string", "string", "string"], "metadata": {}, } ], diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 67fff736dd..95bebd84f5 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -31,8 +31,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: thread = client.beta.threads.create( messages=[ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -62,8 +62,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -93,8 +93,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -131,8 +131,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "vector_store_ids": ["string"], "vector_stores": [ { - "file_ids": ["string", "string", "string"], "chunking_strategy": {"type": "auto"}, + "file_ids": ["string", "string", "string"], "metadata": {}, } ], @@ -310,8 +310,8 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) thread={ "messages": [ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -341,8 +341,8 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -372,8 +372,8 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -403,20 +403,20 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "metadata": {}, }, ], + "metadata": {}, "tool_resources": { "code_interpreter": {"file_ids": ["string", "string", "string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { - "file_ids": ["string", "string", "string"], "chunking_strategy": {"type": "auto"}, + "file_ids": ["string", "string", "string"], "metadata": {}, } ], }, }, - "metadata": {}, }, tool_choice="none", tool_resources={ @@ -480,8 +480,8 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) thread={ "messages": [ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -511,8 +511,8 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -542,8 +542,8 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -573,20 +573,20 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "metadata": {}, }, ], + "metadata": {}, "tool_resources": { "code_interpreter": {"file_ids": ["string", "string", "string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { - "file_ids": ["string", "string", "string"], "chunking_strategy": {"type": "auto"}, + "file_ids": ["string", "string", "string"], "metadata": {}, } ], }, }, - "metadata": {}, }, tool_choice="none", tool_resources={ @@ -641,8 +641,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> thread = await async_client.beta.threads.create( messages=[ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -672,8 +672,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -703,8 +703,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -741,8 +741,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "vector_store_ids": ["string"], "vector_stores": [ { - "file_ids": ["string", "string", "string"], "chunking_strategy": {"type": "auto"}, + "file_ids": ["string", "string", "string"], "metadata": {}, } ], @@ -920,8 +920,8 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie thread={ "messages": [ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -951,8 +951,8 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -982,8 +982,8 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -1013,20 +1013,20 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "metadata": {}, }, ], + "metadata": {}, "tool_resources": { "code_interpreter": {"file_ids": ["string", "string", "string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { - "file_ids": ["string", "string", "string"], "chunking_strategy": {"type": "auto"}, + "file_ids": ["string", "string", "string"], "metadata": {}, } ], }, }, - "metadata": {}, }, tool_choice="none", tool_resources={ @@ -1090,8 +1090,8 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie thread={ "messages": [ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -1121,8 +1121,8 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -1152,8 +1152,8 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -1183,20 +1183,20 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "metadata": {}, }, ], + "metadata": {}, "tool_resources": { "code_interpreter": {"file_ids": ["string", "string", "string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { - "file_ids": ["string", "string", "string"], "chunking_strategy": {"type": "auto"}, + "file_ids": ["string", "string", "string"], "metadata": {}, } ], }, }, - "metadata": {}, }, tool_choice="none", tool_resources={ diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index e21c6c2c77..5d16bdb364 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -38,8 +38,8 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: additional_instructions="string", additional_messages=[ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -69,8 +69,8 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -100,8 +100,8 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -202,8 +202,8 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: additional_instructions="string", additional_messages=[ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -233,8 +233,8 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -264,8 +264,8 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -567,16 +567,16 @@ def test_method_submit_tool_outputs_with_all_params_overload_1(self, client: Ope thread_id="string", tool_outputs=[ { - "tool_call_id": "string", - "output": "string", + "output": "output", + "tool_call_id": "tool_call_id", }, { - "tool_call_id": "string", - "output": "string", + "output": "output", + "tool_call_id": "tool_call_id", }, { - "tool_call_id": "string", - "output": "string", + "output": "output", + "tool_call_id": "tool_call_id", }, ], stream=False, @@ -704,8 +704,8 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn additional_instructions="string", additional_messages=[ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -735,8 +735,8 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -766,8 +766,8 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -868,8 +868,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn additional_instructions="string", additional_messages=[ { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -899,8 +899,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -930,8 +930,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "metadata": {}, }, { - "role": "user", "content": "string", + "role": "user", "attachments": [ { "file_id": "string", @@ -1233,16 +1233,16 @@ async def test_method_submit_tool_outputs_with_all_params_overload_1(self, async thread_id="string", tool_outputs=[ { - "tool_call_id": "string", - "output": "string", + "output": "output", + "tool_call_id": "tool_call_id", }, { - "tool_call_id": "string", - "output": "string", + "output": "output", + "tool_call_id": "tool_call_id", }, { - "tool_call_id": "string", - "output": "string", + "output": "output", + "tool_call_id": "tool_call_id", }, ], stream=False, diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 9fa3cc8284..0b89fbf9cd 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -48,8 +48,8 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: function_call="none", functions=[ { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, } ], @@ -69,31 +69,31 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: tool_choice="none", tools=[ { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, ], top_logprobs=0, @@ -168,8 +168,8 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: function_call="none", functions=[ { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, } ], @@ -188,31 +188,31 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: tool_choice="none", tools=[ { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, ], top_logprobs=0, @@ -307,8 +307,8 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn function_call="none", functions=[ { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, } ], @@ -328,31 +328,31 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn tool_choice="none", tools=[ { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, ], top_logprobs=0, @@ -427,8 +427,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn function_call="none", functions=[ { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, } ], @@ -447,31 +447,31 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn tool_choice="none", tools=[ { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, { - "type": "function", "function": { - "description": "string", - "name": "string", + "name": "name", + "description": "description", "parameters": {"foo": "bar"}, "strict": True, }, + "type": "function", }, ], top_logprobs=0, diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 68b3d73ac5..d1ad611219 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -44,8 +44,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "type": "wandb", "wandb": { "project": "my-wandb-project", - "name": "string", - "entity": "string", + "entity": "entity", + "name": "name", "tags": ["custom-tag", "custom-tag", "custom-tag"], }, }, @@ -53,8 +53,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "type": "wandb", "wandb": { "project": "my-wandb-project", - "name": "string", - "entity": "string", + "entity": "entity", + "name": "name", "tags": ["custom-tag", "custom-tag", "custom-tag"], }, }, @@ -62,8 +62,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "type": "wandb", "wandb": { "project": "my-wandb-project", - "name": "string", - "entity": "string", + "entity": "entity", + "name": "name", "tags": ["custom-tag", "custom-tag", "custom-tag"], }, }, @@ -283,8 +283,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "type": "wandb", "wandb": { "project": "my-wandb-project", - "name": "string", - "entity": "string", + "entity": "entity", + "name": "name", "tags": ["custom-tag", "custom-tag", "custom-tag"], }, }, @@ -292,8 +292,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "type": "wandb", "wandb": { "project": "my-wandb-project", - "name": "string", - "entity": "string", + "entity": "entity", + "name": "name", "tags": ["custom-tag", "custom-tag", "custom-tag"], }, }, @@ -301,8 +301,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "type": "wandb", "wandb": { "project": "my-wandb-project", - "name": "string", - "entity": "string", + "entity": "entity", + "name": "name", "tags": ["custom-tag", "custom-tag", "custom-tag"], }, }, diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 2e31f3354a..9bc9719bc5 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -31,7 +31,7 @@ def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: model="dall-e-2", n=1, response_format="url", - size="1024x1024", + size="256x256", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -77,7 +77,7 @@ def test_method_edit_with_all_params(self, client: OpenAI) -> None: model="dall-e-2", n=1, response_format="url", - size="1024x1024", + size="256x256", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -123,7 +123,7 @@ def test_method_generate_with_all_params(self, client: OpenAI) -> None: n=1, quality="standard", response_format="url", - size="1024x1024", + size="256x256", style="vivid", user="user-1234", ) @@ -171,7 +171,7 @@ async def test_method_create_variation_with_all_params(self, async_client: Async model="dall-e-2", n=1, response_format="url", - size="1024x1024", + size="256x256", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -217,7 +217,7 @@ async def test_method_edit_with_all_params(self, async_client: AsyncOpenAI) -> N model="dall-e-2", n=1, response_format="url", - size="1024x1024", + size="256x256", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -263,7 +263,7 @@ async def test_method_generate_with_all_params(self, async_client: AsyncOpenAI) n=1, quality="standard", response_format="url", - size="1024x1024", + size="256x256", style="vivid", user="user-1234", ) From 32677356f371fa86635c5216e8207835d96e2f3c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:25:41 +0000 Subject: [PATCH 566/914] chore: sync openapi url (#1646) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index cad2c64cd0..2371b7b8d4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-97797a9363b9960b5f2fbdc84426a2b91e75533ecd409fe99e37c231180a4339.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-285bce7dcdae7eea5fe84a8d6e5af2c1473d65ea193109370fb2257851eef7eb.yml From 40f4cdb52a7494472c32e26c70f54bb41bb2bb57 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:26:09 +0000 Subject: [PATCH 567/914] release: 1.40.6 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 11 +++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 190c2adda3..ae6438060f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.5" + ".": "1.40.6" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ee946c4df..7dd2a34ef9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 1.40.6 (2024-08-12) + +Full Changelog: [v1.40.5...v1.40.6](https://github.com/openai/openai-python/compare/v1.40.5...v1.40.6) + +### Chores + +* **examples:** minor formatting changes ([#1644](https://github.com/openai/openai-python/issues/1644)) ([e08acf1](https://github.com/openai/openai-python/commit/e08acf1c6edd1501ed70c4634cd884ab1658af0d)) +* **internal:** update some imports ([#1642](https://github.com/openai/openai-python/issues/1642)) ([fce1ea7](https://github.com/openai/openai-python/commit/fce1ea72a89ba2737bc77775fe04f3a21ecb28e7)) +* sync openapi url ([#1646](https://github.com/openai/openai-python/issues/1646)) ([8ae3801](https://github.com/openai/openai-python/commit/8ae380123ada0bfaca9961e222a0e9c8b585e2d4)) +* **tests:** fix pydantic v1 tests ([2623630](https://github.com/openai/openai-python/commit/26236303f0f6de5df887e8ee3e41d5bc39a3abb1)) + ## 1.40.5 (2024-08-12) Full Changelog: [v1.40.4...v1.40.5](https://github.com/openai/openai-python/compare/v1.40.4...v1.40.5) diff --git a/pyproject.toml b/pyproject.toml index 1e094d14a6..a92be494cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.5" +version = "1.40.6" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d416db5cac..d4083f4a69 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.5" # x-release-please-version +__version__ = "1.40.6" # x-release-please-version From 64b859505e5f28cef355a92bf786857e9e169ead Mon Sep 17 00:00:00 2001 From: Morgante Pell Date: Wed, 14 Aug 2024 04:33:15 -0700 Subject: [PATCH 568/914] fix(cli/migrate): change grit binaries download source (#1649) --- src/openai/cli/_tools/migrate.py | 66 +++++++++++--------------------- 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/src/openai/cli/_tools/migrate.py b/src/openai/cli/_tools/migrate.py index 7c10bb7f85..7a0b0f90f6 100644 --- a/src/openai/cli/_tools/migrate.py +++ b/src/openai/cli/_tools/migrate.py @@ -2,7 +2,6 @@ import os import sys -import json import shutil import tarfile import platform @@ -85,7 +84,9 @@ def install() -> Path: if sys.platform == "win32": raise CLIError("Windows is not supported yet in the migration CLI") - platform = "macos" if sys.platform == "darwin" else "linux" + _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" @@ -109,27 +110,14 @@ def install() -> Path: arch = _get_arch() _debug(f"Using architecture {arch}") - file_name = f"marzano-{platform}-{arch}" - meta_url = f"https://api.keygen.sh/v1/accounts/%7BKEYGEN_ACCOUNT%7D/artifacts/%7Bfile_name%7D" + file_name = f"marzano-{arch}-{platform}" + download_url = f"https://github.com/getgrit/gritql/releases/latest/download/%7Bfile_name%7D.tar.gz" - sys.stdout.write(f"Retrieving Grit CLI metadata from {meta_url}\n") + sys.stdout.write(f"Downloading Grit CLI from {download_url}\n") with httpx.Client() as client: - response = client.get(meta_url) # pyright: ignore[reportUnknownMemberType] - - data = response.json() - errors = data.get("errors") - if errors: - for error in errors: - sys.stdout.write(f"{error}\n") - - raise CLIError("Could not locate Grit CLI binary - see above errors") - - write_manifest(install_dir, data["data"]["relationships"]["release"]["data"]["id"]) - - link = data["data"]["links"]["redirect"] - _debug(f"Redirect URL {link}") - - download_response = client.get(link) # pyright: ignore[reportUnknownMemberType] + 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) @@ -143,8 +131,7 @@ def install() -> Path: else: archive.extractall(unpacked_dir) - for item in unpacked_dir.iterdir(): - item.rename(target_dir / item.name) + _move_files_recursively(unpacked_dir, target_dir) shutil.rmtree(unpacked_dir) os.remove(temp_file) @@ -155,30 +142,23 @@ def install() -> Path: 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 Node.js equivalents + # Map the architecture names to Grit equivalents arch_map = { - "x86_64": "x64", - "amd64": "x64", - "armv7l": "arm", - "aarch64": "arm64", + "x86_64": "x86_64", + "amd64": "x86_64", + "armv7l": "aarch64", + "arm64": "aarch64", } return arch_map.get(architecture, architecture) - - -def write_manifest(install_path: Path, release: str) -> None: - manifest = { - "installPath": str(install_path), - "binaries": { - "marzano": { - "name": "marzano", - "release": release, - }, - }, - } - manifest_path = Path(install_path) / "manifests.json" - with open(manifest_path, "w") as f: - json.dump(manifest, f, indent=2) From c58a0a4bc4d9a70d40625110182216e557ccdff9 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 14 Aug 2024 13:10:09 -0400 Subject: [PATCH 569/914] chore(docs): fix typo in example snippet --- src/openai/resources/beta/chat/completions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index aee88c3c0f..07eda27b76 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -192,7 +192,7 @@ def stream( ) as stream: for event in stream: if event.type == "content.delta": - print(event.content, flush=True, end="") + print(event.delta, flush=True, end="") ``` When the context manager is entered, a `ChatCompletionStream` instance is returned which, like `.create(stream=True)` is an iterator. The full list of events that are yielded by the iterator are outlined in [these docs](https://github.com/openai/openai-python/blob/main/helpers.md#chat-completions-events). @@ -404,7 +404,7 @@ def stream( ) as stream: async for event in stream: if event.type == "content.delta": - print(event.content, flush=True, end="") + print(event.delta, flush=True, end="") ``` When the context manager is entered, an `AsyncChatCompletionStream` instance is returned which, like `.create(stream=True)` is an async iterator. The full list of events that are yielded by the iterator are outlined in [these docs](https://github.com/openai/openai-python/blob/main/helpers.md#chat-completions-events). From cd8c3a3d2129da28188c9c71cd77964287f18803 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:19:37 +0000 Subject: [PATCH 570/914] chore(internal): use different 32bit detection method (#1652) --- src/openai/_base_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 3388d69fab..f374449dbc 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys import json import time import uuid @@ -2012,7 +2013,6 @@ def get_python_version() -> str: def get_architecture() -> Arch: try: - python_bitness, _ = platform.architecture() machine = platform.machine().lower() except Exception: return "unknown" @@ -2028,7 +2028,7 @@ def get_architecture() -> Arch: return "x64" # TODO: untested - if python_bitness == "32bit": + if sys.maxsize <= 2**32: return "x32" if machine: From 3bf03f2f7ee1a9e214484524000774532d02cba7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:20:06 +0000 Subject: [PATCH 571/914] release: 1.40.7 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ae6438060f..8e6933f44e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.6" + ".": "1.40.7" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dd2a34ef9..dc07303148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.40.7 (2024-08-15) + +Full Changelog: [v1.40.6...v1.40.7](https://github.com/openai/openai-python/compare/v1.40.6...v1.40.7) + +### Bug Fixes + +* **cli/migrate:** change grit binaries download source ([#1649](https://github.com/openai/openai-python/issues/1649)) ([85e8935](https://github.com/openai/openai-python/commit/85e8935d9a123b92964d39a98334a975a06ab845)) + + +### Chores + +* **docs:** fix typo in example snippet ([4e83b57](https://github.com/openai/openai-python/commit/4e83b57ffbb64e1c98c19968557dc68a0b65d0b3)) +* **internal:** use different 32bit detection method ([#1652](https://github.com/openai/openai-python/issues/1652)) ([5831af6](https://github.com/openai/openai-python/commit/5831af65048af2a5df9e3ea4a48b8fff2e66dd8c)) + ## 1.40.6 (2024-08-12) Full Changelog: [v1.40.5...v1.40.6](https://github.com/openai/openai-python/compare/v1.40.5...v1.40.6) diff --git a/pyproject.toml b/pyproject.toml index a92be494cd..4ea6ceb539 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.6" +version = "1.40.7" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d4083f4a69..bae8a44c96 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.6" # x-release-please-version +__version__ = "1.40.7" # x-release-please-version From 05f0132fb81580e94ca22baf68a804d635248815 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 21:59:01 +0000 Subject: [PATCH 572/914] chore(types): define FilePurpose enum (#1653) --- .stats.yml | 2 +- api.md | 2 +- src/openai/resources/files.py | 8 ++++---- src/openai/resources/uploads/uploads.py | 8 ++++---- src/openai/types/__init__.py | 1 + src/openai/types/file_create_params.py | 5 +++-- src/openai/types/file_purpose.py | 7 +++++++ src/openai/types/upload_create_params.py | 6 ++++-- 8 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 src/openai/types/file_purpose.py diff --git a/.stats.yml b/.stats.yml index 2371b7b8d4..185585b675 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-285bce7dcdae7eea5fe84a8d6e5af2c1473d65ea193109370fb2257851eef7eb.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-8ff62fa1091460d68fbd36d72c17d91b709917bebf2983c9c4de5784bc384a2e.yml diff --git a/api.md b/api.md index 1687476d86..648d0f3708 100644 --- a/api.md +++ b/api.md @@ -82,7 +82,7 @@ Methods: Types: ```python -from openai.types import FileContent, FileDeleted, FileObject +from openai.types import FileContent, FileDeleted, FileObject, FilePurpose ``` Methods: diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 75c971a8bc..a240e1d886 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -5,12 +5,11 @@ import time import typing_extensions from typing import Mapping, cast -from typing_extensions import Literal import httpx from .. import _legacy_response -from ..types import file_list_params, file_create_params +from ..types import FilePurpose, file_list_params, file_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from .._utils import ( extract_files, @@ -35,6 +34,7 @@ ) from ..types.file_object import FileObject from ..types.file_deleted import FileDeleted +from ..types.file_purpose import FilePurpose __all__ = ["Files", "AsyncFiles"] @@ -52,7 +52,7 @@ def create( self, *, file: FileTypes, - purpose: Literal["assistants", "batch", "fine-tune", "vision"], + purpose: FilePurpose, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -334,7 +334,7 @@ async def create( self, *, file: FileTypes, - purpose: Literal["assistants", "batch", "fine-tune", "vision"], + purpose: FilePurpose, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 4100423d3e..3590a3843f 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -3,7 +3,6 @@ from __future__ import annotations from typing import List -from typing_extensions import Literal import httpx @@ -16,7 +15,7 @@ PartsWithStreamingResponse, AsyncPartsWithStreamingResponse, ) -from ...types import upload_create_params, upload_complete_params +from ...types import FilePurpose, upload_create_params, upload_complete_params from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import ( maybe_transform, @@ -27,6 +26,7 @@ from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..._base_client import make_request_options from ...types.upload import Upload +from ...types.file_purpose import FilePurpose __all__ = ["Uploads", "AsyncUploads"] @@ -50,7 +50,7 @@ def create( bytes: int, filename: str, mime_type: str, - purpose: Literal["assistants", "batch", "fine-tune", "vision"], + purpose: FilePurpose, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -233,7 +233,7 @@ async def create( bytes: int, filename: str, mime_type: str, - purpose: Literal["assistants", "batch", "fine-tune", "vision"], + purpose: FilePurpose, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index f621fb67c5..ad9284fbd5 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -24,6 +24,7 @@ from .image_model import ImageModel as ImageModel from .file_content import FileContent as FileContent from .file_deleted import FileDeleted as FileDeleted +from .file_purpose import FilePurpose as FilePurpose from .model_deleted import ModelDeleted as ModelDeleted from .images_response import ImagesResponse as ImagesResponse from .completion_usage import CompletionUsage as CompletionUsage diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index 8b1c296f39..ecf7503358 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Required, TypedDict from .._types import FileTypes +from .file_purpose import FilePurpose __all__ = ["FileCreateParams"] @@ -13,7 +14,7 @@ class FileCreateParams(TypedDict, total=False): file: Required[FileTypes] """The File object (not file name) to be uploaded.""" - purpose: Required[Literal["assistants", "batch", "fine-tune", "vision"]] + purpose: Required[FilePurpose] """The intended purpose of the uploaded file. Use "assistants" for diff --git a/src/openai/types/file_purpose.py b/src/openai/types/file_purpose.py new file mode 100644 index 0000000000..32dc352c62 --- /dev/null +++ b/src/openai/types/file_purpose.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["FilePurpose"] + +FilePurpose: TypeAlias = Literal["assistants", "batch", "fine-tune", "vision"] diff --git a/src/openai/types/upload_create_params.py b/src/openai/types/upload_create_params.py index 3165ebcc7a..2ebabe6c66 100644 --- a/src/openai/types/upload_create_params.py +++ b/src/openai/types/upload_create_params.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Required, TypedDict + +from .file_purpose import FilePurpose __all__ = ["UploadCreateParams"] @@ -21,7 +23,7 @@ class UploadCreateParams(TypedDict, total=False): supported MIME types for assistants and vision. """ - purpose: Required[Literal["assistants", "batch", "fine-tune", "vision"]] + purpose: Required[FilePurpose] """The intended purpose of the uploaded file. See the From b143c164678ad7579161448846ce67be62e7f21f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 21:59:28 +0000 Subject: [PATCH 573/914] release: 1.40.8 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8e6933f44e..5d3167d511 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.7" + ".": "1.40.8" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dc07303148..28fdd36aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.40.8 (2024-08-15) + +Full Changelog: [v1.40.7...v1.40.8](https://github.com/openai/openai-python/compare/v1.40.7...v1.40.8) + +### Chores + +* **types:** define FilePurpose enum ([#1653](https://github.com/openai/openai-python/issues/1653)) ([3c2eeae](https://github.com/openai/openai-python/commit/3c2eeae32adf5d4ab6bc622be6f9a95a1a298dd3)) + ## 1.40.7 (2024-08-15) Full Changelog: [v1.40.6...v1.40.7](https://github.com/openai/openai-python/compare/v1.40.6...v1.40.7) diff --git a/pyproject.toml b/pyproject.toml index 4ea6ceb539..388d63cc09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.7" +version = "1.40.8" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index bae8a44c96..4c087bdf11 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.7" # x-release-please-version +__version__ = "1.40.8" # x-release-please-version From 3bbb271927811194d3b17a4f9968b7fff2d79861 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 18 Jul 2024 13:56:56 +0100 Subject: [PATCH 574/914] feat(client): add uploads.upload_file helper --- examples/generate_file.sh | 10 ++ examples/uploads.py | 46 +++++ src/openai/resources/uploads/uploads.py | 225 +++++++++++++++++++++++- 3 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 examples/generate_file.sh create mode 100644 examples/uploads.py diff --git a/examples/generate_file.sh b/examples/generate_file.sh new file mode 100644 index 0000000000..ff07d096be --- /dev/null +++ b/examples/generate_file.sh @@ -0,0 +1,10 @@ +# generate a text file with random data for testing file uploads +wanted_size=$((1024*2048*512)) +file_size=$(( ((wanted_size/12)+1)*12 )) +read_size=$((file_size*3/4)) + +echo "wanted=$wanted_size file=$file_size read=$read_size" + +dd if=/dev/urandom bs=$read_size count=1 | base64 > /tmp/small_test_file.txt + +truncate -s "$wanted_size" /tmp/big_test_file.txt diff --git a/examples/uploads.py b/examples/uploads.py new file mode 100644 index 0000000000..c3896b365b --- /dev/null +++ b/examples/uploads.py @@ -0,0 +1,46 @@ +import sys +from pathlib import Path + +import rich + +from openai import OpenAI + +# generate this file using `./generate_file.sh` +file = Path("/tmp/big_test_file.txt") + +client = OpenAI() + + +def from_disk() -> None: + print("uploading file from disk") + + upload = client.uploads.upload_file_chunked( + file=file, + mime_type="txt", + purpose="batch", + ) + rich.print(upload) + + +def from_in_memory() -> None: + print("uploading file from memory") + + # read the data into memory ourselves to simulate + # it coming from somewhere else + data = file.read_bytes() + filename = "my_file.txt" + + upload = client.uploads.upload_file_chunked( + file=data, + filename=filename, + bytes=len(data), + mime_type="txt", + purpose="batch", + ) + rich.print(upload) + + +if "memory" in sys.argv: + from_in_memory() +else: + from_disk() diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 3590a3843f..5eecef4d4b 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -2,8 +2,14 @@ from __future__ import annotations -from typing import List - +import io +import os +import logging +import builtins +from typing import List, overload +from pathlib import Path + +import anyio import httpx from ... import _legacy_response @@ -31,6 +37,12 @@ __all__ = ["Uploads", "AsyncUploads"] +# 64MB +DEFAULT_PART_SIZE = 64 * 1024 * 1024 + +log: logging.Logger = logging.getLogger(__name__) + + class Uploads(SyncAPIResource): @cached_property def parts(self) -> Parts: @@ -44,6 +56,105 @@ def with_raw_response(self) -> UploadsWithRawResponse: def with_streaming_response(self) -> UploadsWithStreamingResponse: return UploadsWithStreamingResponse(self) + @overload + def upload_file_chunked( + self, + *, + file: os.PathLike[str], + mime_type: str, + purpose: FilePurpose, + bytes: int | None = None, + part_size: int | None = None, + md5: str | NotGiven = NOT_GIVEN, + ) -> Upload: + """Splits a file into multiple 64MB parts and uploads them sequentially.""" + + @overload + def upload_file_chunked( + self, + *, + file: bytes, + filename: str, + bytes: int, + mime_type: str, + purpose: FilePurpose, + part_size: int | None = None, + md5: str | NotGiven = NOT_GIVEN, + ) -> Upload: + """Splits an in-memory file into multiple 64MB parts and uploads them sequentially.""" + + def upload_file_chunked( + self, + *, + file: os.PathLike[str] | bytes, + mime_type: str, + purpose: FilePurpose, + filename: str | None = None, + bytes: int | None = None, + part_size: int | None = None, + md5: str | NotGiven = NOT_GIVEN, + ) -> Upload: + """Splits the given file into multiple parts and uploads them sequentially. + + ```py + from pathlib import Path + + client.uploads.upload_file( + file=Path("my-paper.pdf"), + mime_type="pdf", + purpose="assistants", + ) + ``` + """ + if isinstance(file, builtins.bytes): + if filename is None: + raise TypeError("The `filename` argument must be given for in-memory files") + + if bytes is None: + raise TypeError("The `bytes` argument must be given for in-memory files") + else: + if not isinstance(file, Path): + file = Path(file) + + if not filename: + filename = file.name + + if bytes is None: + bytes = file.stat().st_size + + upload = self.create( + bytes=bytes, + filename=filename, + mime_type=mime_type, + purpose=purpose, + ) + + part_ids: list[str] = [] + + if part_size is None: + part_size = DEFAULT_PART_SIZE + + if isinstance(file, builtins.bytes): + buf: io.FileIO | io.BytesIO = io.BytesIO(file) + else: + buf = io.FileIO(file) + + try: + while True: + data = buf.read(part_size) + if not data: + # EOF + break + + part = self.parts.create(upload_id=upload.id, data=data) + log.info("Uploaded part %s for upload %s", part.id, upload.id) + part_ids.append(part.id) + except Exception: + buf.close() + raise + + return self.complete(upload_id=upload.id, part_ids=part_ids, md5=md5) + def create( self, *, @@ -227,6 +338,116 @@ def with_raw_response(self) -> AsyncUploadsWithRawResponse: def with_streaming_response(self) -> AsyncUploadsWithStreamingResponse: return AsyncUploadsWithStreamingResponse(self) + @overload + async def upload_file_chunked( + self, + *, + file: os.PathLike[str], + mime_type: str, + purpose: FilePurpose, + bytes: int | None = None, + part_size: int | None = None, + md5: str | NotGiven = NOT_GIVEN, + ) -> Upload: + """Splits a file into multiple 64MB parts and uploads them sequentially.""" + + @overload + async def upload_file_chunked( + self, + *, + file: bytes, + filename: str, + bytes: int, + mime_type: str, + purpose: FilePurpose, + part_size: int | None = None, + md5: str | NotGiven = NOT_GIVEN, + ) -> Upload: + """Splits an in-memory file into multiple 64MB parts and uploads them sequentially.""" + + async def upload_file_chunked( + self, + *, + file: os.PathLike[str] | bytes, + mime_type: str, + purpose: FilePurpose, + filename: str | None = None, + bytes: int | None = None, + part_size: int | None = None, + md5: str | NotGiven = NOT_GIVEN, + ) -> Upload: + """Splits the given file into multiple parts and uploads them sequentially. + + ```py + from pathlib import Path + + client.uploads.upload_file( + file=Path("my-paper.pdf"), + mime_type="pdf", + purpose="assistants", + ) + ``` + """ + if isinstance(file, builtins.bytes): + if filename is None: + raise TypeError("The `filename` argument must be given for in-memory files") + + if bytes is None: + raise TypeError("The `bytes` argument must be given for in-memory files") + else: + if not isinstance(file, anyio.Path): + file = anyio.Path(file) + + if not filename: + filename = file.name + + if bytes is None: + stat = await file.stat() + bytes = stat.st_size + + upload = await self.create( + bytes=bytes, + filename=filename, + mime_type=mime_type, + purpose=purpose, + ) + + part_ids: list[str] = [] + + if part_size is None: + part_size = DEFAULT_PART_SIZE + + if isinstance(file, anyio.Path): + fd = await file.open("rb") + async with fd: + while True: + data = await fd.read(part_size) + if not data: + # EOF + break + + part = await self.parts.create(upload_id=upload.id, data=data) + log.info("Uploaded part %s for upload %s", part.id, upload.id) + part_ids.append(part.id) + else: + buf = io.BytesIO(file) + + try: + while True: + data = buf.read(part_size) + if not data: + # EOF + break + + part = await self.parts.create(upload_id=upload.id, data=data) + log.info("Uploaded part %s for upload %s", part.id, upload.id) + part_ids.append(part.id) + except Exception: + buf.close() + raise + + return await self.complete(upload_id=upload.id, part_ids=part_ids, md5=md5) + async def create( self, *, From 798c6cb8032f4568d0b23f762d479d68be62ce3e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 20:44:45 +0000 Subject: [PATCH 575/914] release: 1.41.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5d3167d511..4bce58a11b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.40.8" + ".": "1.41.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 28fdd36aa6..cfcfdfe3eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.41.0 (2024-08-16) + +Full Changelog: [v1.40.8...v1.41.0](https://github.com/openai/openai-python/compare/v1.40.8...v1.41.0) + +### Features + +* **client:** add uploads.upload_file helper ([aae079d](https://github.com/openai/openai-python/commit/aae079daa3c1763ab0e46bad766ae5261b475806)) + ## 1.40.8 (2024-08-15) Full Changelog: [v1.40.7...v1.40.8](https://github.com/openai/openai-python/compare/v1.40.7...v1.40.8) diff --git a/pyproject.toml b/pyproject.toml index 388d63cc09..052afada08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.40.8" +version = "1.41.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 4c087bdf11..c44c93d3e9 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.40.8" # x-release-please-version +__version__ = "1.41.0" # x-release-please-version From 54156ad1a7d3f24ff66c43bffc5a6325904c6137 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 19 Aug 2024 17:22:47 -0400 Subject: [PATCH 576/914] fix(json schema): remove `None` defaults (#1663) --- src/openai/lib/_pydantic.py | 7 ++++ tests/lib/chat/test_completions.py | 60 +++++++++++++++++++++++++++++- tests/lib/schema_types/query.py | 3 +- tests/lib/test_pydantic.py | 6 ++- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py index ad3b6eb29f..f989ce3ed0 100644 --- a/src/openai/lib/_pydantic.py +++ b/src/openai/lib/_pydantic.py @@ -5,6 +5,7 @@ import pydantic +from .._types import NOT_GIVEN from .._utils import is_dict as _is_dict, is_list from .._compat import model_json_schema @@ -76,6 +77,12 @@ def _ensure_strict_json_schema( for i, entry in enumerate(all_of) ] + # strip `None` defaults as there's no meaningful distinction here + # the schema will still be `nullable` and the model will default + # to using `None` anyway + if json_schema.get("default", NOT_GIVEN) is None: + json_schema.pop("default") + # we can't use `$ref`s if there are also other properties defined, e.g. # `{"$ref": "...", "description": "my description"}` # diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index f003866653..aea449b097 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -3,7 +3,7 @@ import os import json from enum import Enum -from typing import Any, Callable +from typing import Any, Callable, Optional from typing_extensions import Literal, TypeVar import httpx @@ -135,6 +135,63 @@ class Location(BaseModel): ) +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_model_optional_default( + client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch +) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Optional[Literal["c", "f"]] = None + + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + response_format=Location, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9y39Q2jGzWmeEZlm5CoNVOuQzcxP4", "object": "chat.completion", "created": 1724098820, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":62,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 14, "total_tokens": 31}, "system_fingerprint": "fp_2a322c9ffc"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion, monkeypatch) == snapshot( + """\ +ParsedChatCompletion[Location]( + choices=[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":62,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=62.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ) + ], + created=1724098820, + id='chatcmpl-9y39Q2jGzWmeEZlm5CoNVOuQzcxP4', + model='gpt-4o-2024-08-06', + object='chat.completion', + service_tier=None, + system_fingerprint='fp_2a322c9ffc', + usage=CompletionUsage(completion_tokens=14, prompt_tokens=17, total_tokens=31) +) +""" + ) + + @pytest.mark.respx(base_url=base_url) def test_parse_pydantic_model_enum(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: class Color(Enum): @@ -320,6 +377,7 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m value=DynamicValue(column_name='expected_delivery_date') ) ], + name=None, order_by=, table_name= ) diff --git a/tests/lib/schema_types/query.py b/tests/lib/schema_types/query.py index d2284424f0..03439fb17f 100644 --- a/tests/lib/schema_types/query.py +++ b/tests/lib/schema_types/query.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import List, Union +from typing import List, Union, Optional from pydantic import BaseModel @@ -45,6 +45,7 @@ class Condition(BaseModel): class Query(BaseModel): + name: Optional[str] = None table_name: Table columns: List[Column] conditions: List[Condition] diff --git a/tests/lib/test_pydantic.py b/tests/lib/test_pydantic.py index 531a89df58..99b9e96d21 100644 --- a/tests/lib/test_pydantic.py +++ b/tests/lib/test_pydantic.py @@ -62,6 +62,7 @@ def test_most_types() -> None: "Table": {"enum": ["orders", "customers", "products"], "title": "Table", "type": "string"}, }, "properties": { + "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Name"}, "table_name": {"$ref": "#/$defs/Table"}, "columns": { "items": {"$ref": "#/$defs/Column"}, @@ -75,7 +76,7 @@ def test_most_types() -> None: }, "order_by": {"$ref": "#/$defs/OrderBy"}, }, - "required": ["table_name", "columns", "conditions", "order_by"], + "required": ["name", "table_name", "columns", "conditions", "order_by"], "title": "Query", "type": "object", "additionalProperties": False, @@ -91,6 +92,7 @@ def test_most_types() -> None: "title": "Query", "type": "object", "properties": { + "name": {"title": "Name", "type": "string"}, "table_name": {"$ref": "#/definitions/Table"}, "columns": {"type": "array", "items": {"$ref": "#/definitions/Column"}}, "conditions": { @@ -100,7 +102,7 @@ def test_most_types() -> None: }, "order_by": {"$ref": "#/definitions/OrderBy"}, }, - "required": ["table_name", "columns", "conditions", "order_by"], + "required": ["name", "table_name", "columns", "conditions", "order_by"], "definitions": { "Table": { "title": "Table", From fb5e53efa9670aaaba7db40ce331417df05d049e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 21:25:16 +0000 Subject: [PATCH 577/914] chore(client): fix parsing union responses when non-json is returned (#1665) --- src/openai/_models.py | 2 ++ tests/test_legacy_response.py | 22 +++++++++++++++++++- tests/test_response.py | 39 ++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 5148d5a7b3..d386eaa3a4 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -380,6 +380,8 @@ def is_basemodel(type_: type) -> bool: def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: origin = get_origin(type_) or type_ + if not inspect.isclass(origin): + return False return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py index 3659ee12c1..3c2df53e58 100644 --- a/tests/test_legacy_response.py +++ b/tests/test_legacy_response.py @@ -1,5 +1,5 @@ import json -from typing import cast +from typing import Any, Union, cast from typing_extensions import Annotated import httpx @@ -81,3 +81,23 @@ def test_response_parse_annotated_type(client: OpenAI) -> None: ) assert obj.foo == "hello!" assert obj.bar == 2 + + +class OtherModel(pydantic.BaseModel): + a: str + + +@pytest.mark.parametrize("client", [False], indirect=True) # loose validation +def test_response_parse_expect_model_union_non_json_content(client: OpenAI) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" diff --git a/tests/test_response.py b/tests/test_response.py index 6ea1be1a1a..b7d88bdbde 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -1,5 +1,5 @@ import json -from typing import List, cast +from typing import Any, List, Union, cast from typing_extensions import Annotated import httpx @@ -188,3 +188,40 @@ async def test_async_response_parse_annotated_type(async_client: AsyncOpenAI) -> ) assert obj.foo == "hello!" assert obj.bar == 2 + + +class OtherModel(BaseModel): + a: str + + +@pytest.mark.parametrize("client", [False], indirect=True) # loose validation +def test_response_parse_expect_model_union_non_json_content(client: OpenAI) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation +async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncOpenAI) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" From 3d780f260a1d26ab612dde0f870db114a42273bd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 21:25:45 +0000 Subject: [PATCH 578/914] release: 1.41.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4bce58a11b..5a1b26c33e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.41.0" + ".": "1.41.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index cfcfdfe3eb..e80a4fcc8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.41.1 (2024-08-19) + +Full Changelog: [v1.41.0...v1.41.1](https://github.com/openai/openai-python/compare/v1.41.0...v1.41.1) + +### Bug Fixes + +* **json schema:** remove `None` defaults ([#1663](https://github.com/openai/openai-python/issues/1663)) ([30215c1](https://github.com/openai/openai-python/commit/30215c15df613cf9c36cafd717af79158c9db3e5)) + + +### Chores + +* **client:** fix parsing union responses when non-json is returned ([#1665](https://github.com/openai/openai-python/issues/1665)) ([822c37d](https://github.com/openai/openai-python/commit/822c37de49eb2ffe8c05122f7520ba87bd76e30b)) + ## 1.41.0 (2024-08-16) Full Changelog: [v1.40.8...v1.41.0](https://github.com/openai/openai-python/compare/v1.40.8...v1.41.0) diff --git a/pyproject.toml b/pyproject.toml index 052afada08..b6530b8523 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.41.0" +version = "1.41.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index c44c93d3e9..22ed2bff90 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.41.0" # x-release-please-version +__version__ = "1.41.1" # x-release-please-version From e8c28f2ec93063bf73c53e6c8d19bae935a135be Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 01:49:10 +0000 Subject: [PATCH 579/914] chore(ci): also run pydantic v1 tests (#1666) --- scripts/test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/test b/scripts/test index b3ace9013b..4fa5698b8f 100755 --- a/scripts/test +++ b/scripts/test @@ -54,3 +54,6 @@ fi echo "==> Running tests" rye run pytest "$@" + +echo "==> Running Pydantic v1 tests" +rye run nox -s test-pydantic-v1 -- "$@" From ecd6e922b540948c117b26380d63be4148699bf4 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 20 Aug 2024 11:09:41 -0400 Subject: [PATCH 580/914] feat(parsing): add support for pydantic dataclasses (#1655) --- src/openai/lib/_parsing/_completions.py | 28 ++++++++---- src/openai/lib/_pydantic.py | 26 +++++++++-- tests/lib/chat/test_completions.py | 59 ++++++++++++++++++++++++- 3 files changed, 99 insertions(+), 14 deletions(-) diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index f9d1d6b351..2ef1bf3553 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -9,9 +9,9 @@ from .._tools import PydanticFunctionTool from ..._types import NOT_GIVEN, NotGiven from ..._utils import is_dict, is_given -from ..._compat import model_parse_json +from ..._compat import PYDANTIC_V2, model_parse_json from ..._models import construct_type_unchecked -from .._pydantic import to_strict_json_schema +from .._pydantic import is_basemodel_type, to_strict_json_schema, is_dataclass_like_type from ...types.chat import ( ParsedChoice, ChatCompletion, @@ -216,14 +216,16 @@ def is_parseable_tool(input_tool: ChatCompletionToolParam) -> bool: return cast(FunctionDefinition, input_fn).get("strict") or False -def is_basemodel_type(typ: type) -> TypeGuard[type[pydantic.BaseModel]]: - return issubclass(typ, pydantic.BaseModel) - - def _parse_content(response_format: type[ResponseFormatT], content: str) -> ResponseFormatT: if is_basemodel_type(response_format): return cast(ResponseFormatT, model_parse_json(response_format, content)) + if is_dataclass_like_type(response_format): + if not PYDANTIC_V2: + raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {response_format}") + + return pydantic.TypeAdapter(response_format).validate_json(content) + raise TypeError(f"Unable to automatically parse response format type {response_format}") @@ -241,14 +243,22 @@ def type_to_response_format_param( # can only be a `type` response_format = cast(type, response_format) - if not is_basemodel_type(response_format): + json_schema_type: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any] | None = None + + if is_basemodel_type(response_format): + name = response_format.__name__ + json_schema_type = response_format + elif is_dataclass_like_type(response_format): + name = response_format.__name__ + json_schema_type = pydantic.TypeAdapter(response_format) + else: raise TypeError(f"Unsupported response_format type - {response_format}") return { "type": "json_schema", "json_schema": { - "schema": to_strict_json_schema(response_format), - "name": response_format.__name__, + "schema": to_strict_json_schema(json_schema_type), + "name": name, "strict": True, }, } diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py index f989ce3ed0..22c7a1f3cd 100644 --- a/src/openai/lib/_pydantic.py +++ b/src/openai/lib/_pydantic.py @@ -1,17 +1,26 @@ from __future__ import annotations -from typing import Any +import inspect +from typing import Any, TypeVar from typing_extensions import TypeGuard import pydantic from .._types import NOT_GIVEN from .._utils import is_dict as _is_dict, is_list -from .._compat import model_json_schema +from .._compat import PYDANTIC_V2, model_json_schema +_T = TypeVar("_T") + + +def to_strict_json_schema(model: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any]) -> dict[str, Any]: + if inspect.isclass(model) and is_basemodel_type(model): + schema = model_json_schema(model) + elif PYDANTIC_V2 and isinstance(model, pydantic.TypeAdapter): + schema = model.json_schema() + else: + raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {model}") -def to_strict_json_schema(model: type[pydantic.BaseModel]) -> dict[str, Any]: - schema = model_json_schema(model) return _ensure_strict_json_schema(schema, path=(), root=schema) @@ -117,6 +126,15 @@ def resolve_ref(*, root: dict[str, object], ref: str) -> object: return resolved +def is_basemodel_type(typ: type) -> TypeGuard[type[pydantic.BaseModel]]: + return issubclass(typ, pydantic.BaseModel) + + +def is_dataclass_like_type(typ: type) -> bool: + """Returns True if the given type likely used `@pydantic.dataclass`""" + return hasattr(typ, "__pydantic_config__") + + def is_dict(obj: object) -> TypeGuard[dict[str, object]]: # just pretend that we know there are only `str` keys # as that check is not worth the performance cost diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index aea449b097..d67d5129cd 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -3,7 +3,7 @@ import os import json from enum import Enum -from typing import Any, Callable, Optional +from typing import Any, List, Callable, Optional from typing_extensions import Literal, TypeVar import httpx @@ -317,6 +317,63 @@ class Location(BaseModel): ) +@pytest.mark.respx(base_url=base_url) +@pytest.mark.skipif(not PYDANTIC_V2, reason="dataclasses only supported in v2") +def test_parse_pydantic_dataclass(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + from pydantic.dataclasses import dataclass + + @dataclass + class CalendarEvent: + name: str + date: str + participants: List[str] + + completion = _make_snapshot_request( + lambda c: c.beta.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[ + {"role": "system", "content": "Extract the event information."}, + {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}, + ], + response_format=CalendarEvent, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-9wdGqXkJJARAz7rOrLH5u5FBwLjF3", "object": "chat.completion", "created": 1723761008, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"name\\":\\"Science Fair\\",\\"date\\":\\"Friday\\",\\"participants\\":[\\"Alice\\",\\"Bob\\"]}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 32, "completion_tokens": 17, "total_tokens": 49}, "system_fingerprint": "fp_2a322c9ffc"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(completion, monkeypatch) == snapshot( + """\ +ParsedChatCompletion[CalendarEvent]( + choices=[ + ParsedChoice[CalendarEvent]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[CalendarEvent]( + content='{"name":"Science Fair","date":"Friday","participants":["Alice","Bob"]}', + function_call=None, + parsed=CalendarEvent(name='Science Fair', date='Friday', participants=['Alice', 'Bob']), + refusal=None, + role='assistant', + tool_calls=[] + ) + ) + ], + created=1723761008, + id='chatcmpl-9wdGqXkJJARAz7rOrLH5u5FBwLjF3', + model='gpt-4o-2024-08-06', + object='chat.completion', + service_tier=None, + system_fingerprint='fp_2a322c9ffc', + usage=CompletionUsage(completion_tokens=17, prompt_tokens=32, total_tokens=49) +) +""" + ) + + @pytest.mark.respx(base_url=base_url) def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: completion = _make_snapshot_request( From 05fa732c024b55ddda1f5f8b107ce78233acd3ce Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:10:05 +0000 Subject: [PATCH 581/914] release: 1.42.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5a1b26c33e..507912c504 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.41.1" + ".": "1.42.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e80a4fcc8a..6daddd33fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.42.0 (2024-08-20) + +Full Changelog: [v1.41.1...v1.42.0](https://github.com/openai/openai-python/compare/v1.41.1...v1.42.0) + +### Features + +* **parsing:** add support for pydantic dataclasses ([#1655](https://github.com/openai/openai-python/issues/1655)) ([101bee9](https://github.com/openai/openai-python/commit/101bee9844f725d2174796c3d09a58d3aa079fad)) + + +### Chores + +* **ci:** also run pydantic v1 tests ([#1666](https://github.com/openai/openai-python/issues/1666)) ([af2a1ca](https://github.com/openai/openai-python/commit/af2a1ca408a406098c6c79837aa3561b822e08ec)) + ## 1.41.1 (2024-08-19) Full Changelog: [v1.41.0...v1.41.1](https://github.com/openai/openai-python/compare/v1.41.0...v1.41.1) diff --git a/pyproject.toml b/pyproject.toml index b6530b8523..526e46e063 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.41.1" +version = "1.42.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 22ed2bff90..b89e3df342 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.41.1" # x-release-please-version +__version__ = "1.42.0" # x-release-please-version From 5d3111a85b34e369adf76263b3b8ba7447702459 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Thu, 29 Aug 2024 16:27:45 +0000 Subject: [PATCH 582/914] feat(api): add file search result details to run steps (#1681) --- .stats.yml | 2 +- api.md | 3 +- .../resources/beta/threads/runs/runs.py | 83 ++++++++++++++++++- .../resources/beta/threads/runs/steps.py | 64 ++++++++++++-- src/openai/types/beta/file_search_tool.py | 26 +++++- .../types/beta/file_search_tool_param.py | 26 +++++- .../types/beta/threads/run_create_params.py | 15 +++- .../types/beta/threads/runs/__init__.py | 2 + .../threads/runs/file_search_tool_call.py | 58 ++++++++++++- .../beta/threads/runs/run_step_include.py | 7 ++ .../beta/threads/runs/step_list_params.py | 15 ++++ .../beta/threads/runs/step_retrieve_params.py | 28 +++++++ .../beta/threads/runs/test_steps.py | 38 +++++++-- tests/api_resources/beta/threads/test_runs.py | 20 +++-- 14 files changed, 351 insertions(+), 36 deletions(-) create mode 100644 src/openai/types/beta/threads/runs/run_step_include.py create mode 100644 src/openai/types/beta/threads/runs/step_retrieve_params.py diff --git a/.stats.yml b/.stats.yml index 185585b675..fd4f271361 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-8ff62fa1091460d68fbd36d72c17d91b709917bebf2983c9c4de5784bc384a2e.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-1dbac0e95bdb5a89a0dd3d93265475a378214551b7d8c22862928e0d87ace94b.yml diff --git a/api.md b/api.md index 648d0f3708..48778cc57c 100644 --- a/api.md +++ b/api.md @@ -365,6 +365,7 @@ from openai.types.beta.threads.runs import ( RunStepDelta, RunStepDeltaEvent, RunStepDeltaMessageDelta, + RunStepInclude, ToolCall, ToolCallDelta, ToolCallDeltaObject, @@ -374,7 +375,7 @@ from openai.types.beta.threads.runs import ( Methods: -- client.beta.threads.runs.steps.retrieve(step_id, \*, thread_id, run_id) -> RunStep +- client.beta.threads.runs.steps.retrieve(step_id, \*, thread_id, run_id, \*\*params) -> RunStep - client.beta.threads.runs.steps.list(run_id, \*, thread_id, \*\*params) -> SyncCursorPage[RunStep] ### Messages diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index cbfb9546f0..4f39912e62 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -3,7 +3,7 @@ from __future__ import annotations import typing_extensions -from typing import Union, Iterable, Optional, overload +from typing import List, Union, Iterable, Optional, overload from functools import partial from typing_extensions import Literal @@ -49,6 +49,7 @@ from .....types.beta.threads.run import Run from .....types.beta.assistant_tool_param import AssistantToolParam from .....types.beta.assistant_stream_event import AssistantStreamEvent +from .....types.beta.threads.runs.run_step_include import RunStepInclude from .....types.beta.assistant_tool_choice_option_param import AssistantToolChoiceOptionParam from .....types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -74,6 +75,7 @@ def create( thread_id: str, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -104,6 +106,14 @@ def create( [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to execute this run. + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + additional_instructions: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions. @@ -206,6 +216,7 @@ def create( *, assistant_id: str, stream: Literal[True], + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -239,6 +250,14 @@ def create( events, terminating when the Run enters a terminal state with a `data: [DONE]` message. + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + additional_instructions: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions. @@ -337,6 +356,7 @@ def create( *, assistant_id: str, stream: bool, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -370,6 +390,14 @@ def create( events, terminating when the Run enters a terminal state with a `data: [DONE]` message. + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + additional_instructions: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions. @@ -467,6 +495,7 @@ def create( thread_id: str, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -516,7 +545,11 @@ def create( run_create_params.RunCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, run_create_params.RunCreateParams), ), cast_to=Run, stream=stream or False, @@ -712,6 +745,7 @@ def create_and_poll( self, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -743,6 +777,7 @@ def create_and_poll( run = self.create( thread_id=thread_id, assistant_id=assistant_id, + include=include, additional_instructions=additional_instructions, additional_messages=additional_messages, instructions=instructions, @@ -958,6 +993,7 @@ def stream( self, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -988,6 +1024,7 @@ def stream( self, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1018,6 +1055,7 @@ def stream( self, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1057,6 +1095,7 @@ def stream( body=maybe_transform( { "assistant_id": assistant_id, + "include": include, "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, @@ -1387,6 +1426,7 @@ async def create( thread_id: str, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1417,6 +1457,14 @@ async def create( [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to execute this run. + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + additional_instructions: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions. @@ -1519,6 +1567,7 @@ async def create( *, assistant_id: str, stream: Literal[True], + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1552,6 +1601,14 @@ async def create( events, terminating when the Run enters a terminal state with a `data: [DONE]` message. + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + additional_instructions: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions. @@ -1650,6 +1707,7 @@ async def create( *, assistant_id: str, stream: bool, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1683,6 +1741,14 @@ async def create( events, terminating when the Run enters a terminal state with a `data: [DONE]` message. + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + additional_instructions: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions. @@ -1780,6 +1846,7 @@ async def create( thread_id: str, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1810,6 +1877,7 @@ async def create( body=await async_maybe_transform( { "assistant_id": assistant_id, + "include": include, "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, @@ -1829,7 +1897,11 @@ async def create( run_create_params.RunCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"include": include}, run_create_params.RunCreateParams), ), cast_to=Run, stream=stream or False, @@ -2025,6 +2097,7 @@ async def create_and_poll( self, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -2056,6 +2129,7 @@ async def create_and_poll( run = await self.create( thread_id=thread_id, assistant_id=assistant_id, + include=include, additional_instructions=additional_instructions, additional_messages=additional_messages, instructions=instructions, @@ -2303,6 +2377,7 @@ def stream( self, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -2333,6 +2408,7 @@ def stream( self, *, assistant_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -2374,6 +2450,7 @@ def stream( body=maybe_transform( { "assistant_id": assistant_id, + "include": include, "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 512008939c..3d2d40a3fb 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -2,23 +2,25 @@ from __future__ import annotations +from typing import List from typing_extensions import Literal import httpx from ..... import _legacy_response from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ....._utils import maybe_transform +from ....._utils import ( + maybe_transform, + async_maybe_transform, +) from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .....pagination import SyncCursorPage, AsyncCursorPage -from ....._base_client import ( - AsyncPaginator, - make_request_options, -) -from .....types.beta.threads.runs import step_list_params +from ....._base_client import AsyncPaginator, make_request_options +from .....types.beta.threads.runs import step_list_params, step_retrieve_params from .....types.beta.threads.runs.run_step import RunStep +from .....types.beta.threads.runs.run_step_include import RunStepInclude __all__ = ["Steps", "AsyncSteps"] @@ -38,6 +40,7 @@ def retrieve( *, thread_id: str, run_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -49,6 +52,14 @@ def retrieve( Retrieves a run step. Args: + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -67,7 +78,11 @@ def retrieve( return self._get( f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, step_retrieve_params.StepRetrieveParams), ), cast_to=RunStep, ) @@ -79,6 +94,7 @@ def list( thread_id: str, after: str | NotGiven = NOT_GIVEN, before: str | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -102,6 +118,14 @@ def list( ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -133,6 +157,7 @@ def list( { "after": after, "before": before, + "include": include, "limit": limit, "order": order, }, @@ -158,6 +183,7 @@ async def retrieve( *, thread_id: str, run_id: str, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -169,6 +195,14 @@ async def retrieve( Retrieves a run step. Args: + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -187,7 +221,11 @@ async def retrieve( return await self._get( f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"include": include}, step_retrieve_params.StepRetrieveParams), ), cast_to=RunStep, ) @@ -199,6 +237,7 @@ def list( thread_id: str, after: str | NotGiven = NOT_GIVEN, before: str | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -222,6 +261,14 @@ def list( ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. + include: A list of additional fields to include in the response. Currently the only + supported value is `step_details.tool_calls[*].file_search.results[*].content` + to fetch the file search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -253,6 +300,7 @@ def list( { "after": after, "before": before, + "include": include, "limit": limit, "order": order, }, diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py index 26ab1cb83f..4015b3da09 100644 --- a/src/openai/types/beta/file_search_tool.py +++ b/src/openai/types/beta/file_search_tool.py @@ -5,7 +5,21 @@ from ..._models import BaseModel -__all__ = ["FileSearchTool", "FileSearch"] +__all__ = ["FileSearchTool", "FileSearch", "FileSearchRankingOptions"] + + +class FileSearchRankingOptions(BaseModel): + ranker: Optional[Literal["auto", "default_2024_08_21"]] = None + """The ranker to use for the file search. + + If not specified will use the `auto` ranker. + """ + + score_threshold: Optional[float] = None + """The score threshold for the file search. + + All values must be a floating point number between 0 and 1. + """ class FileSearch(BaseModel): @@ -17,7 +31,15 @@ class FileSearch(BaseModel): Note that the file search tool may output fewer than `max_num_results` results. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/number-of-chunks-returned) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + """ + + ranking_options: Optional[FileSearchRankingOptions] = None + """The ranking options for the file search. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) for more information. """ diff --git a/src/openai/types/beta/file_search_tool_param.py b/src/openai/types/beta/file_search_tool_param.py index 666719f8cd..97e651b0da 100644 --- a/src/openai/types/beta/file_search_tool_param.py +++ b/src/openai/types/beta/file_search_tool_param.py @@ -4,7 +4,21 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["FileSearchToolParam", "FileSearch"] +__all__ = ["FileSearchToolParam", "FileSearch", "FileSearchRankingOptions"] + + +class FileSearchRankingOptions(TypedDict, total=False): + ranker: Literal["auto", "default_2024_08_21"] + """The ranker to use for the file search. + + If not specified will use the `auto` ranker. + """ + + score_threshold: float + """The score threshold for the file search. + + All values must be a floating point number between 0 and 1. + """ class FileSearch(TypedDict, total=False): @@ -16,7 +30,15 @@ class FileSearch(TypedDict, total=False): Note that the file search tool may output fewer than `max_num_results` results. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/number-of-chunks-returned) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + """ + + ranking_options: FileSearchRankingOptions + """The ranking options for the file search. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) for more information. """ diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index d3e6d9c476..8bb73ddc78 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -2,11 +2,12 @@ from __future__ import annotations -from typing import Union, Iterable, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ...chat_model import ChatModel from ..assistant_tool_param import AssistantToolParam +from .runs.run_step_include import RunStepInclude from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam from ..assistant_tool_choice_option_param import AssistantToolChoiceOptionParam @@ -32,6 +33,18 @@ class RunCreateParamsBase(TypedDict, total=False): execute this run. """ + include: List[RunStepInclude] + """A list of additional fields to include in the response. + + Currently the only supported value is + `step_details.tool_calls[*].file_search.results[*].content` to fetch the file + search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + """ + additional_instructions: Optional[str] """Appends additional instructions at the end of the instructions for the run. diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py index a312ce3df2..467d5d793d 100644 --- a/src/openai/types/beta/threads/runs/__init__.py +++ b/src/openai/types/beta/threads/runs/__init__.py @@ -6,9 +6,11 @@ from .tool_call import ToolCall as ToolCall from .run_step_delta import RunStepDelta as RunStepDelta from .tool_call_delta import ToolCallDelta as ToolCallDelta +from .run_step_include import RunStepInclude as RunStepInclude from .step_list_params import StepListParams as StepListParams from .function_tool_call import FunctionToolCall as FunctionToolCall from .run_step_delta_event import RunStepDeltaEvent as RunStepDeltaEvent +from .step_retrieve_params import StepRetrieveParams as StepRetrieveParams from .code_interpreter_logs import CodeInterpreterLogs as CodeInterpreterLogs from .file_search_tool_call import FileSearchToolCall as FileSearchToolCall from .tool_call_delta_object import ToolCallDeltaObject as ToolCallDeltaObject diff --git a/src/openai/types/beta/threads/runs/file_search_tool_call.py b/src/openai/types/beta/threads/runs/file_search_tool_call.py index 57c0ca9a90..da4d58dc37 100644 --- a/src/openai/types/beta/threads/runs/file_search_tool_call.py +++ b/src/openai/types/beta/threads/runs/file_search_tool_call.py @@ -1,17 +1,71 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List, Optional from typing_extensions import Literal from ....._models import BaseModel -__all__ = ["FileSearchToolCall"] +__all__ = [ + "FileSearchToolCall", + "FileSearch", + "FileSearchRankingOptions", + "FileSearchResult", + "FileSearchResultContent", +] + + +class FileSearchRankingOptions(BaseModel): + ranker: Literal["default_2024_08_21"] + """The ranker used for the file search.""" + + score_threshold: float + """The score threshold for the file search. + + All values must be a floating point number between 0 and 1. + """ + + +class FileSearchResultContent(BaseModel): + text: Optional[str] = None + """The text content of the file.""" + + type: Optional[Literal["text"]] = None + """The type of the content.""" + + +class FileSearchResult(BaseModel): + file_id: str + """The ID of the file that result was found in.""" + + file_name: str + """The name of the file that result was found in.""" + + score: float + """The score of the result. + + All values must be a floating point number between 0 and 1. + """ + + content: Optional[List[FileSearchResultContent]] = None + """The content of the result that was found. + + The content is only included if requested via the include query parameter. + """ + + +class FileSearch(BaseModel): + ranking_options: Optional[FileSearchRankingOptions] = None + """The ranking options for the file search.""" + + results: Optional[List[FileSearchResult]] = None + """The results of the file search.""" class FileSearchToolCall(BaseModel): id: str """The ID of the tool call object.""" - file_search: object + file_search: FileSearch """For now, this is always going to be an empty object.""" type: Literal["file_search"] diff --git a/src/openai/types/beta/threads/runs/run_step_include.py b/src/openai/types/beta/threads/runs/run_step_include.py new file mode 100644 index 0000000000..8e76c1b716 --- /dev/null +++ b/src/openai/types/beta/threads/runs/run_step_include.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["RunStepInclude"] + +RunStepInclude: TypeAlias = Literal["step_details.tool_calls[*].file_search.results[*].content"] diff --git a/src/openai/types/beta/threads/runs/step_list_params.py b/src/openai/types/beta/threads/runs/step_list_params.py index 606d444539..3931bd7e0c 100644 --- a/src/openai/types/beta/threads/runs/step_list_params.py +++ b/src/openai/types/beta/threads/runs/step_list_params.py @@ -2,8 +2,11 @@ from __future__ import annotations +from typing import List from typing_extensions import Literal, Required, TypedDict +from .run_step_include import RunStepInclude + __all__ = ["StepListParams"] @@ -28,6 +31,18 @@ class StepListParams(TypedDict, total=False): of the list. """ + include: List[RunStepInclude] + """A list of additional fields to include in the response. + + Currently the only supported value is + `step_details.tool_calls[*].file_search.results[*].content` to fetch the file + search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + """ + limit: int """A limit on the number of objects to be returned. diff --git a/src/openai/types/beta/threads/runs/step_retrieve_params.py b/src/openai/types/beta/threads/runs/step_retrieve_params.py new file mode 100644 index 0000000000..22c1c049f4 --- /dev/null +++ b/src/openai/types/beta/threads/runs/step_retrieve_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, TypedDict + +from .run_step_include import RunStepInclude + +__all__ = ["StepRetrieveParams"] + + +class StepRetrieveParams(TypedDict, total=False): + thread_id: Required[str] + + run_id: Required[str] + + include: List[RunStepInclude] + """A list of additional fields to include in the response. + + Currently the only supported value is + `step_details.tool_calls[*].file_search.results[*].content` to fetch the file + search result content. + + See the + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + for more information. + """ diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py index e6108d8dad..f5dc17e0b5 100644 --- a/tests/api_resources/beta/threads/runs/test_steps.py +++ b/tests/api_resources/beta/threads/runs/test_steps.py @@ -27,6 +27,16 @@ def test_method_retrieve(self, client: OpenAI) -> None: ) assert_matches_type(RunStep, step, path=["response"]) + @parametrize + def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: + step = client.beta.threads.runs.steps.retrieve( + step_id="step_id", + thread_id="thread_id", + run_id="run_id", + include=["step_details.tool_calls[*].file_search.results[*].content"], + ) + assert_matches_type(RunStep, step, path=["response"]) + @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.beta.threads.runs.steps.with_raw_response.retrieve( @@ -89,10 +99,11 @@ def test_method_list(self, client: OpenAI) -> None: @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: step = client.beta.threads.runs.steps.list( - "string", - thread_id="string", - after="string", - before="string", + run_id="run_id", + thread_id="thread_id", + after="after", + before="before", + include=["step_details.tool_calls[*].file_search.results[*].content"], limit=0, order="asc", ) @@ -151,6 +162,16 @@ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: ) assert_matches_type(RunStep, step, path=["response"]) + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) -> None: + step = await async_client.beta.threads.runs.steps.retrieve( + step_id="step_id", + thread_id="thread_id", + run_id="run_id", + include=["step_details.tool_calls[*].file_search.results[*].content"], + ) + assert_matches_type(RunStep, step, path=["response"]) + @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.threads.runs.steps.with_raw_response.retrieve( @@ -213,10 +234,11 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: step = await async_client.beta.threads.runs.steps.list( - "string", - thread_id="string", - after="string", - before="string", + run_id="run_id", + thread_id="thread_id", + after="after", + before="before", + include=["step_details.tool_calls[*].file_search.results[*].content"], limit=0, order="asc", ) diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 5d16bdb364..c8d70f5f89 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -33,9 +33,10 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: run = client.beta.threads.runs.create( - "string", - assistant_id="string", - additional_instructions="string", + thread_id="thread_id", + assistant_id="assistant_id", + include=["step_details.tool_calls[*].file_search.results[*].content"], + additional_instructions="additional_instructions", additional_messages=[ { "content": "string", @@ -199,7 +200,8 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "string", assistant_id="string", stream=True, - additional_instructions="string", + include=["step_details.tool_calls[*].file_search.results[*].content"], + additional_instructions="additional_instructions", additional_messages=[ { "content": "string", @@ -699,9 +701,10 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: run = await async_client.beta.threads.runs.create( - "string", - assistant_id="string", - additional_instructions="string", + thread_id="thread_id", + assistant_id="assistant_id", + include=["step_details.tool_calls[*].file_search.results[*].content"], + additional_instructions="additional_instructions", additional_messages=[ { "content": "string", @@ -865,7 +868,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "string", assistant_id="string", stream=True, - additional_instructions="string", + include=["step_details.tool_calls[*].file_search.results[*].content"], + additional_instructions="additional_instructions", additional_messages=[ { "content": "string", From 9850c169c4126fd04dc6796e4685f1b9e4924aa4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:43:50 +0000 Subject: [PATCH 583/914] release: 1.43.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 507912c504..b5fcdb93a4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.42.0" + ".": "1.43.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6daddd33fe..49403cc371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.43.0 (2024-08-29) + +Full Changelog: [v1.42.0...v1.43.0](https://github.com/openai/openai-python/compare/v1.42.0...v1.43.0) + +### Features + +* **api:** add file search result details to run steps ([#1681](https://github.com/openai/openai-python/issues/1681)) ([f5449c0](https://github.com/openai/openai-python/commit/f5449c07580ac9707f0387f86f4772fbf0a874b6)) + ## 1.42.0 (2024-08-20) Full Changelog: [v1.41.1...v1.42.0](https://github.com/openai/openai-python/compare/v1.41.1...v1.42.0) diff --git a/pyproject.toml b/pyproject.toml index 526e46e063..54ae75de32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.42.0" +version = "1.43.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b89e3df342..b027b37a69 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.42.0" # x-release-please-version +__version__ = "1.43.0" # x-release-please-version From b2f58cba092cfb3083dc31de429fa19d89f739dc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:01:40 +0000 Subject: [PATCH 584/914] release: 1.43.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b5fcdb93a4..5b3c8f9482 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.43.0" + ".": "1.43.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 49403cc371..7aa4280d4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.43.1 (2024-09-05) + +Full Changelog: [v1.43.0...v1.43.1](https://github.com/openai/openai-python/compare/v1.43.0...v1.43.1) + +### Chores + +* pyproject.toml formatting changes ([#1687](https://github.com/openai/openai-python/issues/1687)) ([3387ede](https://github.com/openai/openai-python/commit/3387ede0b896788bf1197378b01941c75bd6e179)) + ## 1.43.0 (2024-08-29) Full Changelog: [v1.42.0...v1.43.0](https://github.com/openai/openai-python/compare/v1.42.0...v1.43.0) diff --git a/pyproject.toml b/pyproject.toml index 54ae75de32..04ecef16e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.43.0" +version = "1.43.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b027b37a69..ac6080b318 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.43.0" # x-release-please-version +__version__ = "1.43.1" # x-release-please-version From f161000ff0fe3153cbfaaf9160be0ca28f213871 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Fri, 6 Sep 2024 09:27:05 +0000 Subject: [PATCH 585/914] feat(vector store): improve chunking strategy type names (#1690) --- .stats.yml | 2 +- api.md | 12 ++++- .../beta/vector_stores/file_batches.py | 23 ++++----- .../resources/beta/vector_stores/files.py | 27 +++++----- .../beta/vector_stores/vector_stores.py | 15 +++--- src/openai/types/beta/__init__.py | 7 +++ .../types/beta/assistant_create_params.py | 44 ++-------------- .../beta/auto_file_chunking_strategy_param.py | 12 +++++ .../types/beta/file_chunking_strategy.py | 14 +++++ .../beta/file_chunking_strategy_param.py | 13 +++++ .../other_file_chunking_strategy_object.py | 12 +++++ .../beta/static_file_chunking_strategy.py | 22 ++++++++ .../static_file_chunking_strategy_object.py | 15 ++++++ .../static_file_chunking_strategy_param.py | 22 ++++++++ .../beta/thread_create_and_run_params.py | 43 ++-------------- src/openai/types/beta/thread_create_params.py | 42 ++------------- .../types/beta/vector_store_create_params.py | 47 +++-------------- .../vector_stores/file_batch_create_params.py | 51 +++---------------- .../beta/vector_stores/file_create_params.py | 50 +++--------------- .../beta/vector_stores/vector_store_file.py | 49 ++---------------- 20 files changed, 201 insertions(+), 321 deletions(-) create mode 100644 src/openai/types/beta/auto_file_chunking_strategy_param.py create mode 100644 src/openai/types/beta/file_chunking_strategy.py create mode 100644 src/openai/types/beta/file_chunking_strategy_param.py create mode 100644 src/openai/types/beta/other_file_chunking_strategy_object.py create mode 100644 src/openai/types/beta/static_file_chunking_strategy.py create mode 100644 src/openai/types/beta/static_file_chunking_strategy_object.py create mode 100644 src/openai/types/beta/static_file_chunking_strategy_param.py diff --git a/.stats.yml b/.stats.yml index fd4f271361..903c159960 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-1dbac0e95bdb5a89a0dd3d93265475a378214551b7d8c22862928e0d87ace94b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-85a85e0c08de456441431c0ae4e9c078cc8f9748c29430b9a9058340db6389ee.yml diff --git a/api.md b/api.md index 48778cc57c..32c0fb9efc 100644 --- a/api.md +++ b/api.md @@ -222,7 +222,17 @@ Methods: Types: ```python -from openai.types.beta import VectorStore, VectorStoreDeleted +from openai.types.beta import ( + AutoFileChunkingStrategyParam, + FileChunkingStrategy, + FileChunkingStrategyParam, + OtherFileChunkingStrategyObject, + StaticFileChunkingStrategy, + StaticFileChunkingStrategyObject, + StaticFileChunkingStrategyParam, + VectorStore, + VectorStoreDeleted, +) ``` Methods: diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/beta/vector_stores/file_batches.py index d6862c24ef..a350ed0bea 100644 --- a/src/openai/resources/beta/vector_stores/file_batches.py +++ b/src/openai/resources/beta/vector_stores/file_batches.py @@ -22,11 +22,10 @@ from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage -from ...._base_client import ( - AsyncPaginator, - make_request_options, -) +from ....types.beta import FileChunkingStrategyParam +from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.vector_stores import file_batch_create_params, file_batch_list_files_params +from ....types.beta.file_chunking_strategy_param import FileChunkingStrategyParam from ....types.beta.vector_stores.vector_store_file import VectorStoreFile from ....types.beta.vector_stores.vector_store_file_batch import VectorStoreFileBatch @@ -47,7 +46,7 @@ def create( vector_store_id: str, *, file_ids: List[str], - chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -64,7 +63,7 @@ def create( files. chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` - strategy. + strategy. Only applicable if `file_ids` is non-empty. extra_headers: Send extra headers @@ -174,7 +173,7 @@ def create_and_poll( *, file_ids: List[str], poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: """Create a vector store batch and poll until all files have been processed.""" batch = self.create( @@ -308,7 +307,7 @@ def upload_and_poll( max_concurrency: int = 5, file_ids: List[str] = [], poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: """Uploads the given files concurrently and then creates a vector store file batch. @@ -365,7 +364,7 @@ async def create( vector_store_id: str, *, file_ids: List[str], - chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -382,7 +381,7 @@ async def create( files. chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` - strategy. + strategy. Only applicable if `file_ids` is non-empty. extra_headers: Send extra headers @@ -492,7 +491,7 @@ async def create_and_poll( *, file_ids: List[str], poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: """Create a vector store batch and poll until all files have been processed.""" batch = await self.create( @@ -626,7 +625,7 @@ async def upload_and_poll( max_concurrency: int = 5, file_ids: List[str] = [], poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: file_batch_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: """Uploads the given files concurrently and then creates a vector store file batch. diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/beta/vector_stores/files.py index 35ca331cc0..ba43519c75 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -18,11 +18,10 @@ from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage -from ...._base_client import ( - AsyncPaginator, - make_request_options, -) +from ....types.beta import FileChunkingStrategyParam +from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.vector_stores import file_list_params, file_create_params +from ....types.beta.file_chunking_strategy_param import FileChunkingStrategyParam from ....types.beta.vector_stores.vector_store_file import VectorStoreFile from ....types.beta.vector_stores.vector_store_file_deleted import VectorStoreFileDeleted @@ -43,7 +42,7 @@ def create( vector_store_id: str, *, file_id: str, - chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -62,7 +61,7 @@ def create( files. chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` - strategy. + strategy. Only applicable if `file_ids` is non-empty. extra_headers: Send extra headers @@ -245,7 +244,7 @@ def create_and_poll( *, vector_store_id: str, poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Attach a file to the given vector store and wait for it to be processed.""" self.create(vector_store_id=vector_store_id, file_id=file_id, chunking_strategy=chunking_strategy) @@ -302,7 +301,7 @@ def upload( *, vector_store_id: str, file: FileTypes, - chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Upload a file to the `files` API and then attach it to the given vector store. @@ -318,7 +317,7 @@ def upload_and_poll( vector_store_id: str, file: FileTypes, poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Add a file to a vector store and poll until processing is complete.""" file_obj = self._client.files.create(file=file, purpose="assistants") @@ -344,7 +343,7 @@ async def create( vector_store_id: str, *, file_id: str, - chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -363,7 +362,7 @@ async def create( files. chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` - strategy. + strategy. Only applicable if `file_ids` is non-empty. extra_headers: Send extra headers @@ -546,7 +545,7 @@ async def create_and_poll( *, vector_store_id: str, poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Attach a file to the given vector store and wait for it to be processed.""" await self.create(vector_store_id=vector_store_id, file_id=file_id, chunking_strategy=chunking_strategy) @@ -603,7 +602,7 @@ async def upload( *, vector_store_id: str, file: FileTypes, - chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Upload a file to the `files` API and then attach it to the given vector store. @@ -621,7 +620,7 @@ async def upload_and_poll( vector_store_id: str, file: FileTypes, poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: file_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFile: """Add a file to a vector store and poll until processing is complete.""" file_obj = await self._client.files.create(file=file, purpose="assistants") diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py index cbd56a0693..c93b3bc41f 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -33,13 +33,16 @@ AsyncFileBatchesWithStreamingResponse, ) from ....pagination import SyncCursorPage, AsyncCursorPage -from ....types.beta import vector_store_list_params, vector_store_create_params, vector_store_update_params -from ...._base_client import ( - AsyncPaginator, - make_request_options, +from ....types.beta import ( + FileChunkingStrategyParam, + vector_store_list_params, + vector_store_create_params, + vector_store_update_params, ) +from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.vector_store import VectorStore from ....types.beta.vector_store_deleted import VectorStoreDeleted +from ....types.beta.file_chunking_strategy_param import FileChunkingStrategyParam __all__ = ["VectorStores", "AsyncVectorStores"] @@ -64,7 +67,7 @@ def with_streaming_response(self) -> VectorStoresWithStreamingResponse: def create( self, *, - chunking_strategy: vector_store_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, file_ids: List[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, @@ -331,7 +334,7 @@ def with_streaming_response(self) -> AsyncVectorStoresWithStreamingResponse: async def create( self, *, - chunking_strategy: vector_store_create_params.ChunkingStrategy | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, file_ids: List[str] | NotGiven = NOT_GIVEN, metadata: Optional[object] | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index 9c5ddfdbe0..7f76fed0cd 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -19,6 +19,7 @@ from .assistant_tool_choice import AssistantToolChoice as AssistantToolChoice from .code_interpreter_tool import CodeInterpreterTool as CodeInterpreterTool from .assistant_stream_event import AssistantStreamEvent as AssistantStreamEvent +from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams @@ -28,11 +29,17 @@ from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption +from .file_chunking_strategy_param import FileChunkingStrategyParam as FileChunkingStrategyParam from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams +from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption +from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam as AutoFileChunkingStrategyParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam as AssistantToolChoiceOptionParam +from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject as OtherFileChunkingStrategyObject +from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam as StaticFileChunkingStrategyParam from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam as AssistantToolChoiceFunctionParam +from .static_file_chunking_strategy_object import StaticFileChunkingStrategyObject as StaticFileChunkingStrategyObject from .assistant_response_format_option_param import ( AssistantResponseFormatOptionParam as AssistantResponseFormatOptionParam, ) diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 84cd4425d1..c1360b5b66 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -3,10 +3,11 @@ from __future__ import annotations from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing_extensions import Required, TypedDict from ..chat_model import ChatModel from .assistant_tool_param import AssistantToolParam +from .file_chunking_strategy_param import FileChunkingStrategyParam from .assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = [ @@ -15,10 +16,6 @@ "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", "ToolResourcesFileSearchVectorStore", - "ToolResourcesFileSearchVectorStoreChunkingStrategy", - "ToolResourcesFileSearchVectorStoreChunkingStrategyAuto", - "ToolResourcesFileSearchVectorStoreChunkingStrategyStatic", - "ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", ] @@ -118,43 +115,12 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): """ -class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): - type: Required[Literal["auto"]] - """Always `auto`.""" - - -class ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): - chunk_overlap_tokens: Required[int] - """The number of tokens that overlap between chunks. The default value is `400`. - - Note that the overlap must not exceed half of `max_chunk_size_tokens`. - """ - - max_chunk_size_tokens: Required[int] - """The maximum number of tokens in each chunk. - - The default value is `800`. The minimum value is `100` and the maximum value is - `4096`. - """ - - -class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): - static: Required[ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] - - type: Required[Literal["static"]] - """Always `static`.""" - - -ToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ - ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic -] - - class ToolResourcesFileSearchVectorStore(TypedDict, total=False): - chunking_strategy: ToolResourcesFileSearchVectorStoreChunkingStrategy + chunking_strategy: FileChunkingStrategyParam """The chunking strategy used to chunk the file(s). - If not set, will use the `auto` strategy. + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. """ file_ids: List[str] diff --git a/src/openai/types/beta/auto_file_chunking_strategy_param.py b/src/openai/types/beta/auto_file_chunking_strategy_param.py new file mode 100644 index 0000000000..6f17836bac --- /dev/null +++ b/src/openai/types/beta/auto_file_chunking_strategy_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["AutoFileChunkingStrategyParam"] + + +class AutoFileChunkingStrategyParam(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" diff --git a/src/openai/types/beta/file_chunking_strategy.py b/src/openai/types/beta/file_chunking_strategy.py new file mode 100644 index 0000000000..406d69dd0e --- /dev/null +++ b/src/openai/types/beta/file_chunking_strategy.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject +from .static_file_chunking_strategy_object import StaticFileChunkingStrategyObject + +__all__ = ["FileChunkingStrategy"] + +FileChunkingStrategy: TypeAlias = Annotated[ + Union[StaticFileChunkingStrategyObject, OtherFileChunkingStrategyObject], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/file_chunking_strategy_param.py b/src/openai/types/beta/file_chunking_strategy_param.py new file mode 100644 index 0000000000..46383358e5 --- /dev/null +++ b/src/openai/types/beta/file_chunking_strategy_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam +from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam + +__all__ = ["FileChunkingStrategyParam"] + +FileChunkingStrategyParam: TypeAlias = Union[AutoFileChunkingStrategyParam, StaticFileChunkingStrategyParam] diff --git a/src/openai/types/beta/other_file_chunking_strategy_object.py b/src/openai/types/beta/other_file_chunking_strategy_object.py new file mode 100644 index 0000000000..89da560be4 --- /dev/null +++ b/src/openai/types/beta/other_file_chunking_strategy_object.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["OtherFileChunkingStrategyObject"] + + +class OtherFileChunkingStrategyObject(BaseModel): + type: Literal["other"] + """Always `other`.""" diff --git a/src/openai/types/beta/static_file_chunking_strategy.py b/src/openai/types/beta/static_file_chunking_strategy.py new file mode 100644 index 0000000000..ba80e1a2b9 --- /dev/null +++ b/src/openai/types/beta/static_file_chunking_strategy.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + + +from ..._models import BaseModel + +__all__ = ["StaticFileChunkingStrategy"] + + +class StaticFileChunkingStrategy(BaseModel): + chunk_overlap_tokens: int + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: int + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ diff --git a/src/openai/types/beta/static_file_chunking_strategy_object.py b/src/openai/types/beta/static_file_chunking_strategy_object.py new file mode 100644 index 0000000000..896c4b8320 --- /dev/null +++ b/src/openai/types/beta/static_file_chunking_strategy_object.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .static_file_chunking_strategy import StaticFileChunkingStrategy + +__all__ = ["StaticFileChunkingStrategyObject"] + + +class StaticFileChunkingStrategyObject(BaseModel): + static: StaticFileChunkingStrategy + + type: Literal["static"] + """Always `static`.""" diff --git a/src/openai/types/beta/static_file_chunking_strategy_param.py b/src/openai/types/beta/static_file_chunking_strategy_param.py new file mode 100644 index 0000000000..f917ac5647 --- /dev/null +++ b/src/openai/types/beta/static_file_chunking_strategy_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["StaticFileChunkingStrategyParam"] + + +class StaticFileChunkingStrategyParam(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 7490b25ef3..cd3d9f29d4 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -9,6 +9,7 @@ from .function_tool_param import FunctionToolParam from .file_search_tool_param import FileSearchToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam +from .file_chunking_strategy_param import FileChunkingStrategyParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam from .threads.message_content_part_param import MessageContentPartParam from .assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -24,10 +25,6 @@ "ThreadToolResourcesCodeInterpreter", "ThreadToolResourcesFileSearch", "ThreadToolResourcesFileSearchVectorStore", - "ThreadToolResourcesFileSearchVectorStoreChunkingStrategy", - "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto", - "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic", - "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", @@ -218,44 +215,12 @@ class ThreadToolResourcesCodeInterpreter(TypedDict, total=False): """ -class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): - type: Required[Literal["auto"]] - """Always `auto`.""" - - -class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): - chunk_overlap_tokens: Required[int] - """The number of tokens that overlap between chunks. The default value is `400`. - - Note that the overlap must not exceed half of `max_chunk_size_tokens`. - """ - - max_chunk_size_tokens: Required[int] - """The maximum number of tokens in each chunk. - - The default value is `800`. The minimum value is `100` and the maximum value is - `4096`. - """ - - -class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): - static: Required[ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] - - type: Required[Literal["static"]] - """Always `static`.""" - - -ThreadToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ - ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto, - ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic, -] - - class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): - chunking_strategy: ThreadToolResourcesFileSearchVectorStoreChunkingStrategy + chunking_strategy: FileChunkingStrategyParam """The chunking strategy used to chunk the file(s). - If not set, will use the `auto` strategy. + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. """ file_ids: List[str] diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index f9561aa48c..729164b481 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from .code_interpreter_tool_param import CodeInterpreterToolParam +from .file_chunking_strategy_param import FileChunkingStrategyParam from .threads.message_content_part_param import MessageContentPartParam __all__ = [ @@ -18,10 +19,6 @@ "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", "ToolResourcesFileSearchVectorStore", - "ToolResourcesFileSearchVectorStoreChunkingStrategy", - "ToolResourcesFileSearchVectorStoreChunkingStrategyAuto", - "ToolResourcesFileSearchVectorStoreChunkingStrategyStatic", - "ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", ] @@ -99,43 +96,12 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): """ -class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): - type: Required[Literal["auto"]] - """Always `auto`.""" - - -class ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): - chunk_overlap_tokens: Required[int] - """The number of tokens that overlap between chunks. The default value is `400`. - - Note that the overlap must not exceed half of `max_chunk_size_tokens`. - """ - - max_chunk_size_tokens: Required[int] - """The maximum number of tokens in each chunk. - - The default value is `800`. The minimum value is `100` and the maximum value is - `4096`. - """ - - -class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): - static: Required[ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] - - type: Required[Literal["static"]] - """Always `static`.""" - - -ToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ - ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic -] - - class ToolResourcesFileSearchVectorStore(TypedDict, total=False): - chunking_strategy: ToolResourcesFileSearchVectorStoreChunkingStrategy + chunking_strategy: FileChunkingStrategyParam """The chunking strategy used to chunk the file(s). - If not set, will use the `auto` strategy. + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. """ file_ids: List[str] diff --git a/src/openai/types/beta/vector_store_create_params.py b/src/openai/types/beta/vector_store_create_params.py index 4f74af49f8..a8f03a89b9 100644 --- a/src/openai/types/beta/vector_store_create_params.py +++ b/src/openai/types/beta/vector_store_create_params.py @@ -2,21 +2,16 @@ from __future__ import annotations -from typing import List, Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict -__all__ = [ - "VectorStoreCreateParams", - "ChunkingStrategy", - "ChunkingStrategyAuto", - "ChunkingStrategyStatic", - "ChunkingStrategyStaticStatic", - "ExpiresAfter", -] +from .file_chunking_strategy_param import FileChunkingStrategyParam + +__all__ = ["VectorStoreCreateParams", "ExpiresAfter"] class VectorStoreCreateParams(TypedDict, total=False): - chunking_strategy: ChunkingStrategy + chunking_strategy: FileChunkingStrategyParam """The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is @@ -45,36 +40,6 @@ class VectorStoreCreateParams(TypedDict, total=False): """The name of the vector store.""" -class ChunkingStrategyAuto(TypedDict, total=False): - type: Required[Literal["auto"]] - """Always `auto`.""" - - -class ChunkingStrategyStaticStatic(TypedDict, total=False): - chunk_overlap_tokens: Required[int] - """The number of tokens that overlap between chunks. The default value is `400`. - - Note that the overlap must not exceed half of `max_chunk_size_tokens`. - """ - - max_chunk_size_tokens: Required[int] - """The maximum number of tokens in each chunk. - - The default value is `800`. The minimum value is `100` and the maximum value is - `4096`. - """ - - -class ChunkingStrategyStatic(TypedDict, total=False): - static: Required[ChunkingStrategyStaticStatic] - - type: Required[Literal["static"]] - """Always `static`.""" - - -ChunkingStrategy: TypeAlias = Union[ChunkingStrategyAuto, ChunkingStrategyStatic] - - class ExpiresAfter(TypedDict, total=False): anchor: Required[Literal["last_active_at"]] """Anchor timestamp after which the expiration policy applies. diff --git a/src/openai/types/beta/vector_stores/file_batch_create_params.py b/src/openai/types/beta/vector_stores/file_batch_create_params.py index e1c3303cf3..e42ea99cd1 100644 --- a/src/openai/types/beta/vector_stores/file_batch_create_params.py +++ b/src/openai/types/beta/vector_stores/file_batch_create_params.py @@ -2,16 +2,12 @@ from __future__ import annotations -from typing import List, Union -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing import List +from typing_extensions import Required, TypedDict -__all__ = [ - "FileBatchCreateParams", - "ChunkingStrategy", - "ChunkingStrategyAutoChunkingStrategyRequestParam", - "ChunkingStrategyStaticChunkingStrategyRequestParam", - "ChunkingStrategyStaticChunkingStrategyRequestParamStatic", -] +from ..file_chunking_strategy_param import FileChunkingStrategyParam + +__all__ = ["FileBatchCreateParams"] class FileBatchCreateParams(TypedDict, total=False): @@ -22,40 +18,9 @@ class FileBatchCreateParams(TypedDict, total=False): files. """ - chunking_strategy: ChunkingStrategy + chunking_strategy: FileChunkingStrategyParam """The chunking strategy used to chunk the file(s). - If not set, will use the `auto` strategy. - """ - - -class ChunkingStrategyAutoChunkingStrategyRequestParam(TypedDict, total=False): - type: Required[Literal["auto"]] - """Always `auto`.""" - - -class ChunkingStrategyStaticChunkingStrategyRequestParamStatic(TypedDict, total=False): - chunk_overlap_tokens: Required[int] - """The number of tokens that overlap between chunks. The default value is `400`. - - Note that the overlap must not exceed half of `max_chunk_size_tokens`. + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. """ - - max_chunk_size_tokens: Required[int] - """The maximum number of tokens in each chunk. - - The default value is `800`. The minimum value is `100` and the maximum value is - `4096`. - """ - - -class ChunkingStrategyStaticChunkingStrategyRequestParam(TypedDict, total=False): - static: Required[ChunkingStrategyStaticChunkingStrategyRequestParamStatic] - - type: Required[Literal["static"]] - """Always `static`.""" - - -ChunkingStrategy: TypeAlias = Union[ - ChunkingStrategyAutoChunkingStrategyRequestParam, ChunkingStrategyStaticChunkingStrategyRequestParam -] diff --git a/src/openai/types/beta/vector_stores/file_create_params.py b/src/openai/types/beta/vector_stores/file_create_params.py index cfb80657c6..d074d766e6 100644 --- a/src/openai/types/beta/vector_stores/file_create_params.py +++ b/src/openai/types/beta/vector_stores/file_create_params.py @@ -2,16 +2,11 @@ from __future__ import annotations -from typing import Union -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing_extensions import Required, TypedDict -__all__ = [ - "FileCreateParams", - "ChunkingStrategy", - "ChunkingStrategyAutoChunkingStrategyRequestParam", - "ChunkingStrategyStaticChunkingStrategyRequestParam", - "ChunkingStrategyStaticChunkingStrategyRequestParamStatic", -] +from ..file_chunking_strategy_param import FileChunkingStrategyParam + +__all__ = ["FileCreateParams"] class FileCreateParams(TypedDict, total=False): @@ -22,40 +17,9 @@ class FileCreateParams(TypedDict, total=False): files. """ - chunking_strategy: ChunkingStrategy + chunking_strategy: FileChunkingStrategyParam """The chunking strategy used to chunk the file(s). - If not set, will use the `auto` strategy. - """ - - -class ChunkingStrategyAutoChunkingStrategyRequestParam(TypedDict, total=False): - type: Required[Literal["auto"]] - """Always `auto`.""" - - -class ChunkingStrategyStaticChunkingStrategyRequestParamStatic(TypedDict, total=False): - chunk_overlap_tokens: Required[int] - """The number of tokens that overlap between chunks. The default value is `400`. - - Note that the overlap must not exceed half of `max_chunk_size_tokens`. + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. """ - - max_chunk_size_tokens: Required[int] - """The maximum number of tokens in each chunk. - - The default value is `800`. The minimum value is `100` and the maximum value is - `4096`. - """ - - -class ChunkingStrategyStaticChunkingStrategyRequestParam(TypedDict, total=False): - static: Required[ChunkingStrategyStaticChunkingStrategyRequestParamStatic] - - type: Required[Literal["static"]] - """Always `static`.""" - - -ChunkingStrategy: TypeAlias = Union[ - ChunkingStrategyAutoChunkingStrategyRequestParam, ChunkingStrategyStaticChunkingStrategyRequestParam -] diff --git a/src/openai/types/beta/vector_stores/vector_store_file.py b/src/openai/types/beta/vector_stores/vector_store_file.py index 65096e8dad..e4608e159c 100644 --- a/src/openai/types/beta/vector_stores/vector_store_file.py +++ b/src/openai/types/beta/vector_stores/vector_store_file.py @@ -1,19 +1,12 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias +from typing import Optional +from typing_extensions import Literal -from ...._utils import PropertyInfo from ...._models import BaseModel +from ..file_chunking_strategy import FileChunkingStrategy -__all__ = [ - "VectorStoreFile", - "LastError", - "ChunkingStrategy", - "ChunkingStrategyStatic", - "ChunkingStrategyStaticStatic", - "ChunkingStrategyOther", -] +__all__ = ["VectorStoreFile", "LastError"] class LastError(BaseModel): @@ -24,38 +17,6 @@ class LastError(BaseModel): """A human-readable description of the error.""" -class ChunkingStrategyStaticStatic(BaseModel): - chunk_overlap_tokens: int - """The number of tokens that overlap between chunks. The default value is `400`. - - Note that the overlap must not exceed half of `max_chunk_size_tokens`. - """ - - max_chunk_size_tokens: int - """The maximum number of tokens in each chunk. - - The default value is `800`. The minimum value is `100` and the maximum value is - `4096`. - """ - - -class ChunkingStrategyStatic(BaseModel): - static: ChunkingStrategyStaticStatic - - type: Literal["static"] - """Always `static`.""" - - -class ChunkingStrategyOther(BaseModel): - type: Literal["other"] - """Always `other`.""" - - -ChunkingStrategy: TypeAlias = Annotated[ - Union[ChunkingStrategyStatic, ChunkingStrategyOther], PropertyInfo(discriminator="type") -] - - class VectorStoreFile(BaseModel): id: str """The identifier, which can be referenced in API endpoints.""" @@ -93,5 +54,5 @@ class VectorStoreFile(BaseModel): attached to. """ - chunking_strategy: Optional[ChunkingStrategy] = None + chunking_strategy: Optional[FileChunkingStrategy] = None """The strategy used to chunk the file.""" From 93aa548201af70646fe651cc01fd875f32b00202 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 09:27:30 +0000 Subject: [PATCH 586/914] release: 1.44.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5b3c8f9482..ba2c5854ee 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.43.1" + ".": "1.44.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aa4280d4b..849d7b5e0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.44.0 (2024-09-06) + +Full Changelog: [v1.43.1...v1.44.0](https://github.com/openai/openai-python/compare/v1.43.1...v1.44.0) + +### Features + +* **vector store:** improve chunking strategy type names ([#1690](https://github.com/openai/openai-python/issues/1690)) ([e82cd85](https://github.com/openai/openai-python/commit/e82cd85ac4962e36cb3b139c503069b56918688f)) + ## 1.43.1 (2024-09-05) Full Changelog: [v1.43.0...v1.43.1](https://github.com/openai/openai-python/compare/v1.43.0...v1.43.1) diff --git a/pyproject.toml b/pyproject.toml index 04ecef16e9..5d9d09e7da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.43.1" +version = "1.44.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ac6080b318..6be1f93a0f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.43.1" # x-release-please-version +__version__ = "1.44.0" # x-release-please-version From 38dd5348b37ce3086137c8126a9b22982115c020 Mon Sep 17 00:00:00 2001 From: Adrian Cole <64215+codefromthecrypt@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:17:21 +0800 Subject: [PATCH 587/914] docs(readme): improve custom `base_url` example (#1694) OPENAI_BASE_URL defaults to https://api.openai.com/v1, so if you add a replacement and forget to append the /v1, it will result in 404s --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 525c1b5aaf..d0cc9040a5 100644 --- a/README.md +++ b/README.md @@ -567,7 +567,7 @@ from openai import OpenAI, DefaultHttpxClient client = OpenAI( # Or use the `OPENAI_BASE_URL` env var - base_url="http://my.test.server.example.com:8083/", + base_url="http://my.test.server.example.com:8083/v1", http_client=DefaultHttpxClient( proxies="http://my.test.proxy.example.com/", transport=httpx.HTTPTransport(local_address="0.0.0.0"), From 3cf4acc65466ae77cab9d622fe1b46cbfdf05d45 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:31:32 +0000 Subject: [PATCH 588/914] chore: add docstrings to raw response properties (#1696) --- src/openai/resources/audio/audio.py | 22 +++++++++++++++++++ src/openai/resources/audio/speech.py | 22 +++++++++++++++++++ src/openai/resources/audio/transcriptions.py | 22 +++++++++++++++++++ src/openai/resources/audio/translations.py | 22 +++++++++++++++++++ src/openai/resources/batches.py | 22 +++++++++++++++++++ src/openai/resources/beta/assistants.py | 22 +++++++++++++++++++ src/openai/resources/beta/beta.py | 22 +++++++++++++++++++ src/openai/resources/beta/threads/messages.py | 22 +++++++++++++++++++ .../resources/beta/threads/runs/runs.py | 22 +++++++++++++++++++ .../resources/beta/threads/runs/steps.py | 22 +++++++++++++++++++ src/openai/resources/beta/threads/threads.py | 22 +++++++++++++++++++ .../beta/vector_stores/file_batches.py | 22 +++++++++++++++++++ .../resources/beta/vector_stores/files.py | 22 +++++++++++++++++++ .../beta/vector_stores/vector_stores.py | 22 +++++++++++++++++++ src/openai/resources/chat/chat.py | 22 +++++++++++++++++++ src/openai/resources/chat/completions.py | 22 +++++++++++++++++++ src/openai/resources/completions.py | 22 +++++++++++++++++++ src/openai/resources/embeddings.py | 22 +++++++++++++++++++ src/openai/resources/files.py | 22 +++++++++++++++++++ .../resources/fine_tuning/fine_tuning.py | 22 +++++++++++++++++++ .../resources/fine_tuning/jobs/checkpoints.py | 22 +++++++++++++++++++ src/openai/resources/fine_tuning/jobs/jobs.py | 22 +++++++++++++++++++ src/openai/resources/images.py | 22 +++++++++++++++++++ src/openai/resources/models.py | 22 +++++++++++++++++++ src/openai/resources/moderations.py | 22 +++++++++++++++++++ src/openai/resources/uploads/parts.py | 22 +++++++++++++++++++ src/openai/resources/uploads/uploads.py | 22 +++++++++++++++++++ 27 files changed, 594 insertions(+) diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 537ad573d0..18bd7b812c 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -47,10 +47,21 @@ def speech(self) -> Speech: @cached_property def with_raw_response(self) -> AudioWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AudioWithRawResponse(self) @cached_property def with_streaming_response(self) -> AudioWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AudioWithStreamingResponse(self) @@ -69,10 +80,21 @@ def speech(self) -> AsyncSpeech: @cached_property def with_raw_response(self) -> AsyncAudioWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncAudioWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAudioWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncAudioWithStreamingResponse(self) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index a0df9ec487..6085ae8afe 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -31,10 +31,21 @@ class Speech(SyncAPIResource): @cached_property def with_raw_response(self) -> SpeechWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return SpeechWithRawResponse(self) @cached_property def with_streaming_response(self) -> SpeechWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return SpeechWithStreamingResponse(self) def create( @@ -104,10 +115,21 @@ def create( class AsyncSpeech(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncSpeechWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncSpeechWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncSpeechWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncSpeechWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 1ee962411c..a6009143d4 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -29,10 +29,21 @@ class Transcriptions(SyncAPIResource): @cached_property def with_raw_response(self) -> TranscriptionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return TranscriptionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> TranscriptionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return TranscriptionsWithStreamingResponse(self) def create( @@ -125,10 +136,21 @@ def create( class AsyncTranscriptions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncTranscriptionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncTranscriptionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncTranscriptionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncTranscriptionsWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index ed97ccf840..7ec647fb6b 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -28,10 +28,21 @@ class Translations(SyncAPIResource): @cached_property def with_raw_response(self) -> TranslationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return TranslationsWithRawResponse(self) @cached_property def with_streaming_response(self) -> TranslationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return TranslationsWithStreamingResponse(self) def create( @@ -109,10 +120,21 @@ def create( class AsyncTranslations(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncTranslationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncTranslationsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncTranslationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncTranslationsWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 7152fac622..a8a0ba4bbc 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -30,10 +30,21 @@ class Batches(SyncAPIResource): @cached_property def with_raw_response(self) -> BatchesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return BatchesWithRawResponse(self) @cached_property def with_streaming_response(self) -> BatchesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return BatchesWithStreamingResponse(self) def create( @@ -224,10 +235,21 @@ def cancel( class AsyncBatches(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncBatchesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncBatchesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBatchesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncBatchesWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 441390d24b..1e57944eb3 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -35,10 +35,21 @@ class Assistants(SyncAPIResource): @cached_property def with_raw_response(self) -> AssistantsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AssistantsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AssistantsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AssistantsWithStreamingResponse(self) def create( @@ -410,10 +421,21 @@ def delete( class AsyncAssistants(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncAssistantsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncAssistantsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAssistantsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncAssistantsWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 479c97c471..a7d3e707c8 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -54,10 +54,21 @@ def threads(self) -> Threads: @cached_property def with_raw_response(self) -> BetaWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return BetaWithRawResponse(self) @cached_property def with_streaming_response(self) -> BetaWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return BetaWithStreamingResponse(self) @@ -80,10 +91,21 @@ def threads(self) -> AsyncThreads: @cached_property def with_raw_response(self) -> AsyncBetaWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncBetaWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncBetaWithStreamingResponse(self) diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index f0832515ce..4901174329 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -32,10 +32,21 @@ class Messages(SyncAPIResource): @cached_property def with_raw_response(self) -> MessagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return MessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> MessagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return MessagesWithStreamingResponse(self) def create( @@ -295,10 +306,21 @@ def delete( class AsyncMessages(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncMessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncMessagesWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 4f39912e62..807027a644 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -63,10 +63,21 @@ def steps(self) -> Steps: @cached_property def with_raw_response(self) -> RunsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return RunsWithRawResponse(self) @cached_property def with_streaming_response(self) -> RunsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return RunsWithStreamingResponse(self) @overload @@ -1414,10 +1425,21 @@ def steps(self) -> AsyncSteps: @cached_property def with_raw_response(self) -> AsyncRunsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncRunsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncRunsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncRunsWithStreamingResponse(self) @overload diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 3d2d40a3fb..5d6d55f9d9 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -28,10 +28,21 @@ class Steps(SyncAPIResource): @cached_property def with_raw_response(self) -> StepsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return StepsWithRawResponse(self) @cached_property def with_streaming_response(self) -> StepsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return StepsWithStreamingResponse(self) def retrieve( @@ -171,10 +182,21 @@ def list( class AsyncSteps(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncStepsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncStepsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncStepsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncStepsWithStreamingResponse(self) async def retrieve( diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 4c95c484cc..031121e5cc 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -72,10 +72,21 @@ def messages(self) -> Messages: @cached_property def with_raw_response(self) -> ThreadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return ThreadsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ThreadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return ThreadsWithStreamingResponse(self) def create( @@ -895,10 +906,21 @@ def messages(self) -> AsyncMessages: @cached_property def with_raw_response(self) -> AsyncThreadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncThreadsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncThreadsWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/beta/vector_stores/file_batches.py index a350ed0bea..d1f9c872e4 100644 --- a/src/openai/resources/beta/vector_stores/file_batches.py +++ b/src/openai/resources/beta/vector_stores/file_batches.py @@ -35,10 +35,21 @@ class FileBatches(SyncAPIResource): @cached_property def with_raw_response(self) -> FileBatchesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return FileBatchesWithRawResponse(self) @cached_property def with_streaming_response(self) -> FileBatchesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return FileBatchesWithStreamingResponse(self) def create( @@ -353,10 +364,21 @@ def upload_and_poll( class AsyncFileBatches(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFileBatchesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncFileBatchesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncFileBatchesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncFileBatchesWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/beta/vector_stores/files.py index ba43519c75..fe43bb3488 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -31,10 +31,21 @@ class Files(SyncAPIResource): @cached_property def with_raw_response(self) -> FilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return FilesWithRawResponse(self) @cached_property def with_streaming_response(self) -> FilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return FilesWithStreamingResponse(self) def create( @@ -332,10 +343,21 @@ def upload_and_poll( class AsyncFiles(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncFilesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncFilesWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py index c93b3bc41f..06e26852b4 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -58,10 +58,21 @@ def file_batches(self) -> FileBatches: @cached_property def with_raw_response(self) -> VectorStoresWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return VectorStoresWithRawResponse(self) @cached_property def with_streaming_response(self) -> VectorStoresWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return VectorStoresWithStreamingResponse(self) def create( @@ -325,10 +336,21 @@ def file_batches(self) -> AsyncFileBatches: @cached_property def with_raw_response(self) -> AsyncVectorStoresWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncVectorStoresWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncVectorStoresWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncVectorStoresWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index d14d055506..dc23a15a8e 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -23,10 +23,21 @@ def completions(self) -> Completions: @cached_property def with_raw_response(self) -> ChatWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return ChatWithRawResponse(self) @cached_property def with_streaming_response(self) -> ChatWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return ChatWithStreamingResponse(self) @@ -37,10 +48,21 @@ def completions(self) -> AsyncCompletions: @cached_property def with_raw_response(self) -> AsyncChatWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncChatWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncChatWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncChatWithStreamingResponse(self) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index dc577d6251..ec76bfdf52 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -36,10 +36,21 @@ class Completions(SyncAPIResource): @cached_property def with_raw_response(self) -> CompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return CompletionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> CompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return CompletionsWithStreamingResponse(self) @overload @@ -707,10 +718,21 @@ def create( class AsyncCompletions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncCompletionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncCompletionsWithStreamingResponse(self) @overload diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 0812000f78..091fb5657a 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -31,10 +31,21 @@ class Completions(SyncAPIResource): @cached_property def with_raw_response(self) -> CompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return CompletionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> CompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return CompletionsWithStreamingResponse(self) @overload @@ -562,10 +573,21 @@ def create( class AsyncCompletions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncCompletionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncCompletionsWithStreamingResponse(self) @overload diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 773b6f0968..71c2a18a24 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -27,10 +27,21 @@ class Embeddings(SyncAPIResource): @cached_property def with_raw_response(self) -> EmbeddingsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return EmbeddingsWithRawResponse(self) @cached_property def with_streaming_response(self) -> EmbeddingsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return EmbeddingsWithStreamingResponse(self) def create( @@ -128,10 +139,21 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: class AsyncEmbeddings(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncEmbeddingsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncEmbeddingsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncEmbeddingsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncEmbeddingsWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index a240e1d886..e24eeec711 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -42,10 +42,21 @@ class Files(SyncAPIResource): @cached_property def with_raw_response(self) -> FilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return FilesWithRawResponse(self) @cached_property def with_streaming_response(self) -> FilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return FilesWithStreamingResponse(self) def create( @@ -324,10 +335,21 @@ def wait_for_processing( class AsyncFiles(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFilesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncFilesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncFilesWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index 0404fed6ec..c386de3c2a 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -24,10 +24,21 @@ def jobs(self) -> Jobs: @cached_property def with_raw_response(self) -> FineTuningWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return FineTuningWithRawResponse(self) @cached_property def with_streaming_response(self) -> FineTuningWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return FineTuningWithStreamingResponse(self) @@ -38,10 +49,21 @@ def jobs(self) -> AsyncJobs: @cached_property def with_raw_response(self) -> AsyncFineTuningWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncFineTuningWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncFineTuningWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncFineTuningWithStreamingResponse(self) diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py index 67f5739a02..8b5e905ea5 100644 --- a/src/openai/resources/fine_tuning/jobs/checkpoints.py +++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py @@ -24,10 +24,21 @@ class Checkpoints(SyncAPIResource): @cached_property def with_raw_response(self) -> CheckpointsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return CheckpointsWithRawResponse(self) @cached_property def with_streaming_response(self) -> CheckpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return CheckpointsWithStreamingResponse(self) def list( @@ -84,10 +95,21 @@ def list( class AsyncCheckpoints(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCheckpointsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncCheckpointsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncCheckpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncCheckpointsWithStreamingResponse(self) def list( diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index 5cef7bcd22..ca4799e7ac 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -43,10 +43,21 @@ def checkpoints(self) -> Checkpoints: @cached_property def with_raw_response(self) -> JobsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return JobsWithRawResponse(self) @cached_property def with_streaming_response(self) -> JobsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return JobsWithStreamingResponse(self) def create( @@ -323,10 +334,21 @@ def checkpoints(self) -> AsyncCheckpoints: @cached_property def with_raw_response(self) -> AsyncJobsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncJobsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncJobsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncJobsWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 0913b572cb..e9629d48fd 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -29,10 +29,21 @@ class Images(SyncAPIResource): @cached_property def with_raw_response(self) -> ImagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return ImagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> ImagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return ImagesWithStreamingResponse(self) def create_variation( @@ -275,10 +286,21 @@ def generate( class AsyncImages(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncImagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncImagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncImagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncImagesWithStreamingResponse(self) async def create_variation( diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index e76c496ffa..d6062de230 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -23,10 +23,21 @@ class Models(SyncAPIResource): @cached_property def with_raw_response(self) -> ModelsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return ModelsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ModelsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return ModelsWithStreamingResponse(self) def retrieve( @@ -125,10 +136,21 @@ def delete( class AsyncModels(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncModelsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncModelsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncModelsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncModelsWithStreamingResponse(self) async def retrieve( diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index b9ad9972f0..5283554373 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -26,10 +26,21 @@ class Moderations(SyncAPIResource): @cached_property def with_raw_response(self) -> ModerationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return ModerationsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ModerationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return ModerationsWithStreamingResponse(self) def create( @@ -86,10 +97,21 @@ def create( class AsyncModerations(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncModerationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncModerationsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncModerationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncModerationsWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py index 3ec2592b1e..d46e5ea1bb 100644 --- a/src/openai/resources/uploads/parts.py +++ b/src/openai/resources/uploads/parts.py @@ -27,10 +27,21 @@ class Parts(SyncAPIResource): @cached_property def with_raw_response(self) -> PartsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return PartsWithRawResponse(self) @cached_property def with_streaming_response(self) -> PartsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return PartsWithStreamingResponse(self) def create( @@ -91,10 +102,21 @@ def create( class AsyncParts(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncPartsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncPartsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncPartsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncPartsWithStreamingResponse(self) async def create( diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 5eecef4d4b..96a531a8e4 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -50,10 +50,21 @@ def parts(self) -> Parts: @cached_property def with_raw_response(self) -> UploadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return UploadsWithRawResponse(self) @cached_property def with_streaming_response(self) -> UploadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return UploadsWithStreamingResponse(self) @overload @@ -332,10 +343,21 @@ def parts(self) -> AsyncParts: @cached_property def with_raw_response(self) -> AsyncUploadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ return AsyncUploadsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncUploadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ return AsyncUploadsWithStreamingResponse(self) @overload From fee1040459c4c7b2dbfd43fa2c85192cd3e2eae0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:41:46 +0000 Subject: [PATCH 589/914] docs(readme): add section on determining installed version (#1697) --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index d0cc9040a5..8c73fdd82a 100644 --- a/README.md +++ b/README.md @@ -639,6 +639,17 @@ We take backwards-compatibility seriously and work hard to ensure you can rely o We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions. +### Determining the installed version + +If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version. + +You can determine the version that is being used at runtime with: + +```py +import openai +print(openai.__version__) +``` + ## Requirements Python 3.7 or higher. From 6b07089ae031d64805c2e5eb6a33624ff0e64e84 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:42:18 +0000 Subject: [PATCH 590/914] release: 1.44.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ba2c5854ee..1ee5dee6dd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.44.0" + ".": "1.44.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 849d7b5e0b..47fa2d9208 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.44.1 (2024-09-09) + +Full Changelog: [v1.44.0...v1.44.1](https://github.com/openai/openai-python/compare/v1.44.0...v1.44.1) + +### Chores + +* add docstrings to raw response properties ([#1696](https://github.com/openai/openai-python/issues/1696)) ([1d2a19b](https://github.com/openai/openai-python/commit/1d2a19b0e8acab54c35ef2171d33321943488fdc)) + + +### Documentation + +* **readme:** add section on determining installed version ([#1697](https://github.com/openai/openai-python/issues/1697)) ([0255735](https://github.com/openai/openai-python/commit/0255735930d9c657c78e85e7f03fd1eb98a1e378)) +* **readme:** improve custom `base_url` example ([#1694](https://github.com/openai/openai-python/issues/1694)) ([05eec8a](https://github.com/openai/openai-python/commit/05eec8a0b7fcdc8651021f2e685214a353b861d1)) + ## 1.44.0 (2024-09-06) Full Changelog: [v1.43.1...v1.44.0](https://github.com/openai/openai-python/compare/v1.43.1...v1.44.0) diff --git a/pyproject.toml b/pyproject.toml index 5d9d09e7da..d4b7d2b210 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.44.0" +version = "1.44.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6be1f93a0f..39c7f63e1e 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.44.0" # x-release-please-version +__version__ = "1.44.1" # x-release-please-version From 23b96159c080d6c9b413a69548d325aa40315b95 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 10 Sep 2024 16:46:51 +0100 Subject: [PATCH 591/914] feat(errors): include completion in LengthFinishReasonError (#1701) --- src/openai/_exceptions.py | 23 +++++++++++++++---- src/openai/lib/_parsing/_completions.py | 2 +- src/openai/lib/streaming/chat/_completions.py | 4 +++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index f44f90b52f..e326ed9578 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, Optional, cast +from typing import TYPE_CHECKING, Any, Optional, cast from typing_extensions import Literal import httpx @@ -10,6 +10,9 @@ from ._utils import is_dict from ._models import construct_type +if TYPE_CHECKING: + from .types.chat import ChatCompletion + __all__ = [ "BadRequestError", "AuthenticationError", @@ -130,10 +133,20 @@ class InternalServerError(APIStatusError): class LengthFinishReasonError(OpenAIError): - def __init__(self) -> None: - super().__init__( - f"Could not parse response content as the length limit was reached", - ) + completion: ChatCompletion + """The completion that caused this error. + + Note: this will *not* be a complete `ChatCompletion` object when streaming as `usage` + will not be included. + """ + + def __init__(self, *, completion: ChatCompletion) -> None: + msg = "Could not parse response content as the length limit was reached" + if completion.usage: + msg += f" - {completion.usage}" + + super().__init__(msg) + self.completion = completion class ContentFilterFinishReasonError(OpenAIError): diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index 2ef1bf3553..f1fa9f2b55 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -69,7 +69,7 @@ def parse_chat_completion( choices: list[ParsedChoice[ResponseFormatT]] = [] for choice in chat_completion.choices: if choice.finish_reason == "length": - raise LengthFinishReasonError() + raise LengthFinishReasonError(completion=chat_completion) if choice.finish_reason == "content_filter": raise ContentFilterFinishReasonError() diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index 342a5e2b95..a4b0f856f7 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -394,7 +394,9 @@ def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionS if has_parseable_input(response_format=self._response_format, input_tools=self._input_tools): if choice.finish_reason == "length": - raise LengthFinishReasonError() + # at the time of writing, `.usage` will always be `None` but + # we include it here in case that is changed in the future + raise LengthFinishReasonError(completion=completion_snapshot) if choice.finish_reason == "content_filter": raise ContentFilterFinishReasonError() From a6e9db84c1952c856c8449536714b7a7daa22d2e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:11:58 +0000 Subject: [PATCH 592/914] fix(types): correctly mark stream discriminator as optional (#1706) --- src/openai/types/beta/thread_create_and_run_params.py | 2 +- src/openai/types/beta/threads/run_create_params.py | 2 +- src/openai/types/beta/threads/run_submit_tool_outputs_params.py | 2 +- src/openai/types/chat/completion_create_params.py | 2 +- src/openai/types/completion_create_params.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index cd3d9f29d4..370c2f9bce 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -332,7 +332,7 @@ class TruncationStrategy(TypedDict, total=False): """ -class ThreadCreateAndRunParamsNonStreaming(ThreadCreateAndRunParamsBase): +class ThreadCreateAndRunParamsNonStreaming(ThreadCreateAndRunParamsBase, total=False): stream: Optional[Literal[False]] """ If `true`, returns a stream of events that happen during the Run as server-sent diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 8bb73ddc78..7c5f571d58 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -225,7 +225,7 @@ class TruncationStrategy(TypedDict, total=False): """ -class RunCreateParamsNonStreaming(RunCreateParamsBase): +class RunCreateParamsNonStreaming(RunCreateParamsBase, total=False): stream: Optional[Literal[False]] """ If `true`, returns a stream of events that happen during the Run as server-sent diff --git a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py index ccb5e5e97e..147728603a 100644 --- a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py +++ b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py @@ -31,7 +31,7 @@ class ToolOutput(TypedDict, total=False): """ -class RunSubmitToolOutputsParamsNonStreaming(RunSubmitToolOutputsParamsBase): +class RunSubmitToolOutputsParamsNonStreaming(RunSubmitToolOutputsParamsBase, total=False): stream: Optional[Literal[False]] """ If `true`, returns a stream of events that happen during the Run as server-sent diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 91435dcedd..b86dab742b 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -262,7 +262,7 @@ class Function(TypedDict, total=False): ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONObject, ResponseFormatJSONSchema] -class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase): +class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): stream: Optional[Literal[False]] """If set, partial message deltas will be sent, like in ChatGPT. diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index 9fe22fe3c9..6c112b3902 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -160,7 +160,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ -class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase): +class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): stream: Optional[Literal[False]] """Whether to stream back partial progress. From 2bfec1a2f0be308053924ba673398cd18a038422 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Thu, 12 Sep 2024 16:54:32 +0000 Subject: [PATCH 593/914] feat(api): add o1 models (#1708) See https://platform.openai.com/docs/guides/reasoning for details. --- .stats.yml | 2 +- src/openai/resources/beta/assistants.py | 24 +-- src/openai/resources/beta/chat/completions.py | 8 + .../resources/beta/threads/runs/runs.py | 36 ++-- src/openai/resources/beta/threads/threads.py | 36 ++-- src/openai/resources/chat/completions.py | 172 ++++++++++++------ src/openai/resources/fine_tuning/jobs/jobs.py | 4 +- src/openai/types/beta/assistant.py | 6 +- .../types/beta/assistant_create_params.py | 6 +- .../types/beta/assistant_update_params.py | 6 +- src/openai/types/beta/file_search_tool.py | 15 +- .../types/beta/file_search_tool_param.py | 15 +- .../beta/thread_create_and_run_params.py | 6 +- src/openai/types/beta/threads/run.py | 6 +- .../types/beta/threads/run_create_params.py | 6 +- .../types/chat/completion_create_params.py | 30 ++- src/openai/types/chat_model.py | 7 +- src/openai/types/completion_usage.py | 11 +- .../types/fine_tuning/job_create_params.py | 2 +- tests/api_resources/chat/test_completions.py | 4 + tests/lib/chat/test_completions.py | 8 +- tests/lib/chat/test_completions_streaming.py | 2 +- 22 files changed, 253 insertions(+), 159 deletions(-) diff --git a/.stats.yml b/.stats.yml index 903c159960..de3167f3a8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-85a85e0c08de456441431c0ae4e9c078cc8f9748c29430b9a9058340db6389ee.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-501122aa32adaa2abb3d4487880ab9cdf2141addce2e6c3d1bd9bb6b44c318a8.yml diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 1e57944eb3..5d8c6ec331 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -100,11 +100,11 @@ def create( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -250,11 +250,11 @@ def update( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -486,11 +486,11 @@ async def create( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -636,11 +636,11 @@ async def update( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 07eda27b76..ea3526778d 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -42,6 +42,7 @@ def parse( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -121,6 +122,7 @@ class MathResponse(BaseModel): functions=functions, logit_bias=logit_bias, logprobs=logprobs, + max_completion_tokens=max_completion_tokens, max_tokens=max_tokens, n=n, parallel_tool_calls=parallel_tool_calls, @@ -157,6 +159,7 @@ def stream( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -216,6 +219,7 @@ def stream( functions=functions, logit_bias=logit_bias, logprobs=logprobs, + max_completion_tokens=max_completion_tokens, max_tokens=max_tokens, n=n, parallel_tool_calls=parallel_tool_calls, @@ -254,6 +258,7 @@ async def parse( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -333,6 +338,7 @@ class MathResponse(BaseModel): functions=functions, logit_bias=logit_bias, logprobs=logprobs, + max_completion_tokens=max_completion_tokens, max_tokens=max_tokens, n=n, parallel_tool_calls=parallel_tool_calls, @@ -369,6 +375,7 @@ def stream( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -429,6 +436,7 @@ def stream( functions=functions, logit_bias=logit_bias, logprobs=logprobs, + max_completion_tokens=max_completion_tokens, max_tokens=max_tokens, n=n, parallel_tool_calls=parallel_tool_calls, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 807027a644..3fb1cc77aa 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -167,11 +167,11 @@ def create( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -311,11 +311,11 @@ def create( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -451,11 +451,11 @@ def create( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -1529,11 +1529,11 @@ async def create( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -1673,11 +1673,11 @@ async def create( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -1813,11 +1813,11 @@ async def create( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 031121e5cc..49b0e4b37e 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -335,11 +335,11 @@ def create_and_run( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -469,11 +469,11 @@ def create_and_run( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -599,11 +599,11 @@ def create_and_run( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -1169,11 +1169,11 @@ async def create_and_run( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -1303,11 +1303,11 @@ async def create_and_run( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -1433,11 +1433,11 @@ async def create_and_run( and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index ec76bfdf52..e9267b1f03 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -64,6 +64,7 @@ def create( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -132,13 +133,17 @@ def create( returns the log probabilities of each output token returned in the `content` of `message`. + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat - completion. + completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. - The total length of input tokens and generated tokens is limited by the model's - context length. - [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o1 series models](https://platform.openai.com/docs/guides/reasoning). n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -161,11 +166,11 @@ def create( all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -185,8 +190,11 @@ def create( service_tier: Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service: - - If set to 'auto', the system will utilize scale tier credits until they are - exhausted. + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. @@ -261,6 +269,7 @@ def create( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -335,13 +344,17 @@ def create( returns the log probabilities of each output token returned in the `content` of `message`. + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat - completion. + completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. - The total length of input tokens and generated tokens is limited by the model's - context length. - [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o1 series models](https://platform.openai.com/docs/guides/reasoning). n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -364,11 +377,11 @@ def create( all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -388,8 +401,11 @@ def create( service_tier: Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service: - - If set to 'auto', the system will utilize scale tier credits until they are - exhausted. + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. @@ -457,6 +473,7 @@ def create( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -531,13 +548,17 @@ def create( returns the log probabilities of each output token returned in the `content` of `message`. + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat - completion. + completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. - The total length of input tokens and generated tokens is limited by the model's - context length. - [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o1 series models](https://platform.openai.com/docs/guides/reasoning). n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -560,11 +581,11 @@ def create( all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -584,8 +605,11 @@ def create( service_tier: Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service: - - If set to 'auto', the system will utilize scale tier credits until they are - exhausted. + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. @@ -652,6 +676,7 @@ def create( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -687,6 +712,7 @@ def create( "functions": functions, "logit_bias": logit_bias, "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, "n": n, "parallel_tool_calls": parallel_tool_calls, @@ -746,6 +772,7 @@ async def create( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -814,13 +841,17 @@ async def create( returns the log probabilities of each output token returned in the `content` of `message`. + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat - completion. + completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. - The total length of input tokens and generated tokens is limited by the model's - context length. - [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o1 series models](https://platform.openai.com/docs/guides/reasoning). n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -843,11 +874,11 @@ async def create( all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -867,8 +898,11 @@ async def create( service_tier: Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service: - - If set to 'auto', the system will utilize scale tier credits until they are - exhausted. + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. @@ -943,6 +977,7 @@ async def create( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -1017,13 +1052,17 @@ async def create( returns the log probabilities of each output token returned in the `content` of `message`. + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat - completion. + completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. - The total length of input tokens and generated tokens is limited by the model's - context length. - [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o1 series models](https://platform.openai.com/docs/guides/reasoning). n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -1046,11 +1085,11 @@ async def create( all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -1070,8 +1109,11 @@ async def create( service_tier: Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service: - - If set to 'auto', the system will utilize scale tier credits until they are - exhausted. + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. @@ -1139,6 +1181,7 @@ async def create( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -1213,13 +1256,17 @@ async def create( returns the log probabilities of each output token returned in the `content` of `message`. + max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat - completion. + completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. - The total length of input tokens and generated tokens is limited by the model's - context length. - [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o1 series models](https://platform.openai.com/docs/guides/reasoning). n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -1242,11 +1289,11 @@ async def create( all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -1266,8 +1313,11 @@ async def create( service_tier: Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service: - - If set to 'auto', the system will utilize scale tier credits until they are - exhausted. + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. @@ -1334,6 +1384,7 @@ async def create( functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -1369,6 +1420,7 @@ async def create( "functions": functions, "logit_bias": logit_bias, "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, "n": n, "parallel_tool_calls": parallel_tool_calls, diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index ca4799e7ac..44abf1cfe1 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -114,7 +114,7 @@ def create( job parameters should produce the same results, but may differ in rare cases. If a seed is not specified, one will be generated for you. - suffix: A string of up to 18 characters that will be added to your fine-tuned model + suffix: A string of up to 64 characters that will be added to your fine-tuned model name. For example, a `suffix` of "custom-model-name" would produce a model name like @@ -405,7 +405,7 @@ async def create( job parameters should produce the same results, but may differ in rare cases. If a seed is not specified, one will be generated for you. - suffix: A string of up to 18 characters that will be added to your fine-tuned model + suffix: A string of up to 64 characters that will be added to your fine-tuned model name. For example, a `suffix` of "custom-model-name" would produce a model name like diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index c6a0a4cfcf..b4da08745d 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -90,11 +90,11 @@ class Assistant(BaseModel): and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index c1360b5b66..eca4da0a2b 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -58,11 +58,11 @@ class AssistantCreateParams(TypedDict, total=False): and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index ade565819f..5396233937 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -50,11 +50,11 @@ class AssistantUpdateParams(TypedDict, total=False): and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py index 4015b3da09..aee6593e89 100644 --- a/src/openai/types/beta/file_search_tool.py +++ b/src/openai/types/beta/file_search_tool.py @@ -9,16 +9,16 @@ class FileSearchRankingOptions(BaseModel): - ranker: Optional[Literal["auto", "default_2024_08_21"]] = None - """The ranker to use for the file search. + score_threshold: float + """The score threshold for the file search. - If not specified will use the `auto` ranker. + All values must be a floating point number between 0 and 1. """ - score_threshold: Optional[float] = None - """The score threshold for the file search. + ranker: Optional[Literal["auto", "default_2024_08_21"]] = None + """The ranker to use for the file search. - All values must be a floating point number between 0 and 1. + If not specified will use the `auto` ranker. """ @@ -38,6 +38,9 @@ class FileSearch(BaseModel): ranking_options: Optional[FileSearchRankingOptions] = None """The ranking options for the file search. + If not specified, the file search tool will use the `auto` ranker and a + score_threshold of 0. + See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) for more information. diff --git a/src/openai/types/beta/file_search_tool_param.py b/src/openai/types/beta/file_search_tool_param.py index 97e651b0da..5ce91207ba 100644 --- a/src/openai/types/beta/file_search_tool_param.py +++ b/src/openai/types/beta/file_search_tool_param.py @@ -8,16 +8,16 @@ class FileSearchRankingOptions(TypedDict, total=False): - ranker: Literal["auto", "default_2024_08_21"] - """The ranker to use for the file search. + score_threshold: Required[float] + """The score threshold for the file search. - If not specified will use the `auto` ranker. + All values must be a floating point number between 0 and 1. """ - score_threshold: float - """The score threshold for the file search. + ranker: Literal["auto", "default_2024_08_21"] + """The ranker to use for the file search. - All values must be a floating point number between 0 and 1. + If not specified will use the `auto` ranker. """ @@ -37,6 +37,9 @@ class FileSearch(TypedDict, total=False): ranking_options: FileSearchRankingOptions """The ranking options for the file search. + If not specified, the file search tool will use the `auto` ranker and a + score_threshold of 0. + See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) for more information. diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 370c2f9bce..20d525fa1a 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -98,11 +98,11 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 0579e229d8..5abc1de295 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -172,11 +172,11 @@ class Run(BaseModel): and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 7c5f571d58..824cb1a041 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -111,11 +111,11 @@ class RunCreateParamsBase(TypedDict, total=False): and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index b86dab742b..4ed89b00f5 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -87,15 +87,22 @@ class CompletionCreateParamsBase(TypedDict, total=False): `content` of `message`. """ + max_completion_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a completion, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + max_tokens: Optional[int] """ The maximum number of [tokens](/tokenizer) that can be generated in the chat - completion. + completion. This value can be used to control + [costs](https://openai.com/api/pricing/) for text generated via API. - The total length of input tokens and generated tokens is limited by the model's - context length. - [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + This value is now deprecated in favor of `max_completion_tokens`, and is not + compatible with + [o1 series models](https://platform.openai.com/docs/guides/reasoning). """ n: Optional[int] @@ -130,11 +137,11 @@ class CompletionCreateParamsBase(TypedDict, total=False): all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured - Outputs which guarantees the model will match your supplied JSON schema. Learn - more in the + Outputs which ensures the model will match your supplied JSON schema. Learn more + in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the + Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to @@ -160,8 +167,11 @@ class CompletionCreateParamsBase(TypedDict, total=False): This parameter is relevant for customers subscribed to the scale tier service: - - If set to 'auto', the system will utilize scale tier credits until they are - exhausted. + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index 09bc081f7a..f8438c75c8 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -5,9 +5,14 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", "gpt-4o", - "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "chatgpt-4o-latest", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", "gpt-4-turbo", diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index ac09afd479..a4b9116e35 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -1,9 +1,15 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from .._models import BaseModel -__all__ = ["CompletionUsage"] +__all__ = ["CompletionUsage", "CompletionTokensDetails"] + + +class CompletionTokensDetails(BaseModel): + reasoning_tokens: Optional[int] = None + """Tokens generated by the model for reasoning.""" class CompletionUsage(BaseModel): @@ -15,3 +21,6 @@ class CompletionUsage(BaseModel): total_tokens: int """Total number of tokens used in the request (prompt + completion).""" + + completion_tokens_details: Optional[CompletionTokensDetails] = None + """Breakdown of tokens used in a completion.""" diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index e9be2ef1ca..8f5ea86274 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -50,7 +50,7 @@ class JobCreateParams(TypedDict, total=False): suffix: Optional[str] """ - A string of up to 18 characters that will be added to your fine-tuned model + A string of up to 64 characters that will be added to your fine-tuned model name. For example, a `suffix` of "custom-model-name" would produce a model name like diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 0b89fbf9cd..c44703a434 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -55,6 +55,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: ], logit_bias={"foo": 0}, logprobs=True, + max_completion_tokens=0, max_tokens=0, n=1, parallel_tool_calls=True, @@ -175,6 +176,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: ], logit_bias={"foo": 0}, logprobs=True, + max_completion_tokens=0, max_tokens=0, n=1, parallel_tool_calls=True, @@ -314,6 +316,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn ], logit_bias={"foo": 0}, logprobs=True, + max_completion_tokens=0, max_tokens=0, n=1, parallel_tool_calls=True, @@ -434,6 +437,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn ], logit_bias={"foo": 0}, logprobs=True, + max_completion_tokens=0, max_tokens=0, n=1, parallel_tool_calls=True, diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index d67d5129cd..e7b9c4f1fd 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -74,7 +74,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte object='chat.completion', service_tier=None, system_fingerprint='fp_845eaabc1f', - usage=CompletionUsage(completion_tokens=28, prompt_tokens=14, total_tokens=42) + usage=CompletionUsage(completion_tokens=28, completion_tokens_details=None, prompt_tokens=14, total_tokens=42) ) """ ) @@ -129,7 +129,7 @@ class Location(BaseModel): object='chat.completion', service_tier=None, system_fingerprint='fp_2a322c9ffc', - usage=CompletionUsage(completion_tokens=14, prompt_tokens=17, total_tokens=31) + usage=CompletionUsage(completion_tokens=14, completion_tokens_details=None, prompt_tokens=17, total_tokens=31) ) """ ) @@ -186,7 +186,7 @@ class Location(BaseModel): object='chat.completion', service_tier=None, system_fingerprint='fp_2a322c9ffc', - usage=CompletionUsage(completion_tokens=14, prompt_tokens=17, total_tokens=31) + usage=CompletionUsage(completion_tokens=14, completion_tokens_details=None, prompt_tokens=17, total_tokens=31) ) """ ) @@ -368,7 +368,7 @@ class CalendarEvent: object='chat.completion', service_tier=None, system_fingerprint='fp_2a322c9ffc', - usage=CompletionUsage(completion_tokens=17, prompt_tokens=32, total_tokens=49) + usage=CompletionUsage(completion_tokens=17, completion_tokens_details=None, prompt_tokens=32, total_tokens=49) ) """ ) diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index c3dd69ad57..5ad1f084d2 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -155,7 +155,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream object='chat.completion', service_tier=None, system_fingerprint='fp_845eaabc1f', - usage=CompletionUsage(completion_tokens=14, prompt_tokens=17, total_tokens=31) + usage=CompletionUsage(completion_tokens=14, completion_tokens_details=None, prompt_tokens=17, total_tokens=31) ) """ ) From 48026f3a39b9e1423681ac7a35e68992d230d85a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:03:58 +0000 Subject: [PATCH 594/914] release: 1.45.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1ee5dee6dd..6d2723c72a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.44.1" + ".": "1.45.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 47fa2d9208..8382675b73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.45.0 (2024-09-12) + +Full Changelog: [v1.44.1...v1.45.0](https://github.com/openai/openai-python/compare/v1.44.1...v1.45.0) + +### Features + +* **api:** add o1 models ([#1708](https://github.com/openai/openai-python/issues/1708)) ([06bd42e](https://github.com/openai/openai-python/commit/06bd42e77121a6abd4826a79ce1848812d956576)) +* **errors:** include completion in LengthFinishReasonError ([#1701](https://github.com/openai/openai-python/issues/1701)) ([b0e3256](https://github.com/openai/openai-python/commit/b0e32562aff9aceafec994d3b047f7c2a9f11524)) + + +### Bug Fixes + +* **types:** correctly mark stream discriminator as optional ([#1706](https://github.com/openai/openai-python/issues/1706)) ([80f02f9](https://github.com/openai/openai-python/commit/80f02f9e5f83fac9cd2f4172b733a92ad01399b2)) + ## 1.44.1 (2024-09-09) Full Changelog: [v1.44.0...v1.44.1](https://github.com/openai/openai-python/compare/v1.44.0...v1.44.1) diff --git a/pyproject.toml b/pyproject.toml index d4b7d2b210..178c3db355 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.44.1" +version = "1.45.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 39c7f63e1e..0d8ebf418b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.44.1" # x-release-please-version +__version__ = "1.45.0" # x-release-please-version From f63234732869d0511a7fc19c240ab19bec5de717 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:30:06 +0000 Subject: [PATCH 595/914] docs: update CONTRIBUTING.md (#1710) --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 354d21b2d2..5a6639b8fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,13 +31,13 @@ $ pip install -r requirements-dev.lock ## Modifying/Adding code -Most of the SDK is generated code, and any modified code will be overridden on the next generation. The -`src/openai/lib/` and `examples/` directories are exceptions and will never be overridden. +Most of the SDK is generated code. Modifications to code will be persisted between generations, but may +result in merge conflicts between manual patches and changes from the generator. The generator will never +modify the contents of the `src/openai/lib/` and `examples/` directories. ## Adding and running examples -All files in the `examples/` directory are not modified by the Stainless generator and can be freely edited or -added to. +All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. ```bash # add an example to examples/.py From 803a7f3b16ec959fe04e1ae83ca3808faf8acffb Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 16 Sep 2024 12:05:27 +0000 Subject: [PATCH 596/914] chore(internal): bump ruff (#1714) --- requirements-dev.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index f4797f432b..e5e63ed037 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -141,7 +141,7 @@ requests==2.31.0 respx==0.20.2 rich==13.7.1 # via inline-snapshot -ruff==0.5.6 +ruff==0.6.5 setuptools==68.2.2 # via nodeenv six==1.16.0 From b5897bdf1fe3d300438b87c23d57611f3c2dad3a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:24:14 +0000 Subject: [PATCH 597/914] chore(internal): update spec link (#1716) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index de3167f3a8..2fc39385e9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-501122aa32adaa2abb3d4487880ab9cdf2141addce2e6c3d1bd9bb6b44c318a8.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-ff407aa10917e62f2b0c12d1ad2c4f1258ed083bd45753c70eaaf5b1cf8356ae.yml From dbe51995f2f0431bba63e4422f61f09fddbe0375 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 16 Sep 2024 12:46:46 +0000 Subject: [PATCH 598/914] chore(internal): bump pyright / mypy version (#1717) --- requirements-dev.lock | 4 ++-- src/openai/_utils/_utils.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index e5e63ed037..a47de9656a 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -83,7 +83,7 @@ msal==1.29.0 # via msal-extensions msal-extensions==1.2.0 # via azure-identity -mypy==1.7.1 +mypy==1.11.2 mypy-extensions==1.0.0 # via black # via mypy @@ -125,7 +125,7 @@ pygments==2.18.0 # via rich pyjwt==2.8.0 # via msal -pyright==1.1.374 +pyright==1.1.380 pytest==7.1.1 # via pytest-asyncio pytest-asyncio==0.21.1 diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 2fc5a1c65a..0bba17caad 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -363,12 +363,13 @@ def file_from_path(path: str) -> FileTypes: def get_required_header(headers: HeadersLike, header: str) -> str: lower_header = header.lower() - if isinstance(headers, Mapping): - for k, v in headers.items(): + 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 """ + # 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]: From 73f9fda59561e35ae839c66dcc30b785e1629e58 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:47:09 +0000 Subject: [PATCH 599/914] release: 1.45.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6d2723c72a..c37d66f738 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.45.0" + ".": "1.45.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8382675b73..b1f344cfb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.45.1 (2024-09-16) + +Full Changelog: [v1.45.0...v1.45.1](https://github.com/openai/openai-python/compare/v1.45.0...v1.45.1) + +### Chores + +* **internal:** bump pyright / mypy version ([#1717](https://github.com/openai/openai-python/issues/1717)) ([351af85](https://github.com/openai/openai-python/commit/351af85c5b813391910301a5049edddc8c9e70dd)) +* **internal:** bump ruff ([#1714](https://github.com/openai/openai-python/issues/1714)) ([aceaf64](https://github.com/openai/openai-python/commit/aceaf641eedd092ed42e4aaf031e8cfbf37e4212)) +* **internal:** update spec link ([#1716](https://github.com/openai/openai-python/issues/1716)) ([ca58c7f](https://github.com/openai/openai-python/commit/ca58c7f83a7cede0367dec2500127573c9b00d1f)) + + +### Documentation + +* update CONTRIBUTING.md ([#1710](https://github.com/openai/openai-python/issues/1710)) ([4d45eb5](https://github.com/openai/openai-python/commit/4d45eb5eb794bcc5076c022be09e06fae103abcc)) + ## 1.45.0 (2024-09-12) Full Changelog: [v1.44.1...v1.45.0](https://github.com/openai/openai-python/compare/v1.44.1...v1.45.0) diff --git a/pyproject.toml b/pyproject.toml index 178c3db355..7828d0f0e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.45.0" +version = "1.45.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0d8ebf418b..1b29a53bb6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.45.0" # x-release-please-version +__version__ = "1.45.1" # x-release-please-version From 192b8f2b6a49f462e48c1442858931875524ab49 Mon Sep 17 00:00:00 2001 From: Dan Corin Date: Mon, 16 Sep 2024 09:52:22 -0400 Subject: [PATCH 600/914] docs(readme): add examples for chat with image content (#1703) --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/README.md b/README.md index 8c73fdd82a..9e9628ff83 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,48 @@ we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) to add `OPENAI_API_KEY="My API Key"` to your `.env` file so that your API Key is not stored in source control. +### Vision + +With a hosted image: + +```python +response = client.chat.completions.create( + model="gpt-4o-mini", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": prompt}, + { + "type": "image_url", + "image_url": {"url": f"{img_url}"}, + }, + ], + } + ], +) +``` + +With the image as a base64 encoded string: + +```python +response = client.chat.completions.create( + model="gpt-4o-mini", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": prompt}, + { + "type": "image_url", + "image_url": {"url": f"data:{img_type};base64,{img_b64_str}"}, + }, + ], + } + ], +) +``` + ### Polling Helpers When interacting with the API some actions such as starting a Run and adding files to vector stores are asynchronous and take time to complete. The SDK includes From 4b3023466df27de4d19a93b4efbd14977254ecc0 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 17 Sep 2024 17:30:18 +0100 Subject: [PATCH 601/914] feat(client): add ._request_id property to object responses (#1707) --- README.md | 18 ++++++++++++++ src/openai/_legacy_response.py | 7 ++++-- src/openai/_models.py | 34 ++++++++++++++++++++++++++- src/openai/_response.py | 12 +++++++--- tests/test_legacy_response.py | 21 +++++++++++++++++ tests/test_response.py | 43 ++++++++++++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9e9628ff83..c47bdd54c5 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,24 @@ Error codes are as followed: | >=500 | `InternalServerError` | | N/A | `APIConnectionError` | +## Request IDs + +> For more information on debugging requests, see [these docs](https://platform.openai.com/docs/api-reference/debugging-requests) + +All object responses in the SDK provide a `_request_id` property which is added from the `x-request-id` response header so that you can quickly log failing requests and report them back to OpenAI. + +```python +completion = await client.chat.completions.create( + messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-4" +) +print(completion._request_id) # req_123 +``` + +Note that unlike other properties that use an `_` prefix, the `_request_id` property +*is* public. Unless documented otherwise, *all* other `_` prefix properties, +methods and modules are *private*. + + ### Retries Certain errors are automatically retried 2 times by default, with a short exponential backoff. diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index c42fb8b83e..c7dbd54e23 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -25,7 +25,7 @@ from ._types import NoneType from ._utils import is_given, extract_type_arg, is_annotated_type -from ._models import BaseModel, is_basemodel +from ._models import BaseModel, is_basemodel, add_request_id from ._constants import RAW_RESPONSE_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type from ._exceptions import APIResponseValidationError @@ -138,8 +138,11 @@ class MyModel(BaseModel): if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) + if isinstance(parsed, BaseModel): + add_request_id(parsed, self.request_id) + self._parsed_by_type[cache_key] = parsed - return parsed + return cast(R, parsed) @property def headers(self) -> httpx.Headers: diff --git a/src/openai/_models.py b/src/openai/_models.py index d386eaa3a4..d6f42d3d4d 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -2,7 +2,7 @@ import os import inspect -from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast +from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( Unpack, @@ -94,6 +94,23 @@ def model_fields_set(self) -> set[str]: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] extra: Any = pydantic.Extra.allow # type: ignore + if TYPE_CHECKING: + _request_id: Optional[str] = None + """The ID of the request, returned via the X-Request-ID header. Useful for debugging requests and reporting issues to OpenAI. + + This will **only** be set for the top-level response object, it will not be defined for nested objects. For example: + + ```py + completion = await client.chat.completions.create(...) + completion._request_id # req_id_xxx + completion.usage._request_id # raises `AttributeError` + ``` + + Note: unlike other properties that use an `_` prefix, this property + *is* public. Unless documented otherwise, all other `_` prefix properties, + methods and modules are *private*. + """ + def to_dict( self, *, @@ -662,6 +679,21 @@ def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: setattr(typ, "__pydantic_config__", config) # noqa: B010 +def add_request_id(obj: BaseModel, request_id: str | None) -> None: + obj._request_id = request_id + + # in Pydantic v1, using setattr like we do above causes the attribute + # to be included when serializing the model which we don't want in this + # case so we need to explicitly exclude it + if not PYDANTIC_V2: + try: + exclude_fields = obj.__exclude_fields__ # type: ignore + except AttributeError: + cast(Any, obj).__exclude_fields__ = {"_request_id", "__exclude_fields__"} + else: + cast(Any, obj).__exclude_fields__ = {*(exclude_fields or {}), "_request_id", "__exclude_fields__"} + + # our use of subclasssing here causes weirdness for type checkers, # so we just pretend that we don't subclass if TYPE_CHECKING: diff --git a/src/openai/_response.py b/src/openai/_response.py index f9d91786f6..20ce69ac8a 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -26,7 +26,7 @@ from ._types import NoneType from ._utils import is_given, extract_type_arg, is_annotated_type, extract_type_var_from_base -from ._models import BaseModel, is_basemodel +from ._models import BaseModel, is_basemodel, add_request_id from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type from ._exceptions import OpenAIError, APIResponseValidationError @@ -315,8 +315,11 @@ class MyModel(BaseModel): if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) + if isinstance(parsed, BaseModel): + add_request_id(parsed, self.request_id) + self._parsed_by_type[cache_key] = parsed - return parsed + return cast(R, parsed) def read(self) -> bytes: """Read and return the binary response content.""" @@ -419,8 +422,11 @@ class MyModel(BaseModel): if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) + if isinstance(parsed, BaseModel): + add_request_id(parsed, self.request_id) + self._parsed_by_type[cache_key] = parsed - return parsed + return cast(R, parsed) async def read(self) -> bytes: """Read and return the binary response content.""" diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py index 3c2df53e58..a6fec9f2de 100644 --- a/tests/test_legacy_response.py +++ b/tests/test_legacy_response.py @@ -66,6 +66,27 @@ def test_response_parse_custom_model(client: OpenAI) -> None: assert obj.bar == 2 +def test_response_basemodel_request_id(client: OpenAI) -> None: + response = LegacyAPIResponse( + raw=httpx.Response( + 200, + headers={"x-request-id": "my-req-id"}, + content=json.dumps({"foo": "hello!", "bar": 2}), + ), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=CustomModel) + assert obj._request_id == "my-req-id" + assert obj.foo == "hello!" + assert obj.bar == 2 + assert obj.to_dict() == {"foo": "hello!", "bar": 2} + + def test_response_parse_annotated_type(client: OpenAI) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), diff --git a/tests/test_response.py b/tests/test_response.py index b7d88bdbde..97c56e0035 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -156,6 +156,49 @@ async def test_async_response_parse_custom_model(async_client: AsyncOpenAI) -> N assert obj.bar == 2 +def test_response_basemodel_request_id(client: OpenAI) -> None: + response = APIResponse( + raw=httpx.Response( + 200, + headers={"x-request-id": "my-req-id"}, + content=json.dumps({"foo": "hello!", "bar": 2}), + ), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=CustomModel) + assert obj._request_id == "my-req-id" + assert obj.foo == "hello!" + assert obj.bar == 2 + assert obj.to_dict() == {"foo": "hello!", "bar": 2} + + +@pytest.mark.asyncio +async def test_async_response_basemodel_request_id(client: OpenAI) -> None: + response = AsyncAPIResponse( + raw=httpx.Response( + 200, + headers={"x-request-id": "my-req-id"}, + content=json.dumps({"foo": "hello!", "bar": 2}), + ), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=CustomModel) + assert obj._request_id == "my-req-id" + assert obj.foo == "hello!" + assert obj.bar == 2 + assert obj.to_dict() == {"foo": "hello!", "bar": 2} + + def test_response_parse_annotated_type(client: OpenAI) -> None: response = APIResponse( raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), From bcf9fcc36149921f216d49ac84ee1aa9c15c5a86 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:34:47 +0000 Subject: [PATCH 602/914] release: 1.46.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c37d66f738..4b6cc3c898 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.45.1" + ".": "1.46.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b1f344cfb3..c1f852327c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.46.0 (2024-09-17) + +Full Changelog: [v1.45.1...v1.46.0](https://github.com/openai/openai-python/compare/v1.45.1...v1.46.0) + +### Features + +* **client:** add ._request_id property to object responses ([#1707](https://github.com/openai/openai-python/issues/1707)) ([8b3da05](https://github.com/openai/openai-python/commit/8b3da05a35b33245aec98693a0540ace6218a61b)) + + +### Documentation + +* **readme:** add examples for chat with image content ([#1703](https://github.com/openai/openai-python/issues/1703)) ([192b8f2](https://github.com/openai/openai-python/commit/192b8f2b6a49f462e48c1442858931875524ab49)) + ## 1.45.1 (2024-09-16) Full Changelog: [v1.45.0...v1.45.1](https://github.com/openai/openai-python/compare/v1.45.0...v1.45.1) diff --git a/pyproject.toml b/pyproject.toml index 7828d0f0e1..a3bd81cad5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.45.1" +version = "1.46.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1b29a53bb6..ccd62dc230 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.45.1" # x-release-please-version +__version__ = "1.46.0" # x-release-please-version From 349b7f6c3bbe54bfe0c9e3652782f822ff2f1ab6 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Wed, 18 Sep 2024 11:51:55 -0700 Subject: [PATCH 603/914] chore(streaming): silence pydantic model_dump warnings (#1722) --- src/openai/lib/streaming/_assistants.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/openai/lib/streaming/_assistants.py b/src/openai/lib/streaming/_assistants.py index 7445f9a96d..183754177a 100644 --- a/src/openai/lib/streaming/_assistants.py +++ b/src/openai/lib/streaming/_assistants.py @@ -906,11 +906,11 @@ def accumulate_run_step( merged = accumulate_delta( cast( "dict[object, object]", - snapshot.model_dump(exclude_unset=True), + snapshot.model_dump(exclude_unset=True, warnings=False), ), cast( "dict[object, object]", - data.delta.model_dump(exclude_unset=True), + data.delta.model_dump(exclude_unset=True, warnings=False), ), ) run_step_snapshots[snapshot.id] = cast(RunStep, construct_type(type_=RunStep, value=merged)) @@ -948,7 +948,7 @@ def accumulate_event( construct_type( # mypy doesn't allow Content for some reason type_=cast(Any, MessageContent), - value=content_delta.model_dump(exclude_unset=True), + value=content_delta.model_dump(exclude_unset=True, warnings=False), ), ), ) @@ -957,11 +957,11 @@ def accumulate_event( merged = accumulate_delta( cast( "dict[object, object]", - block.model_dump(exclude_unset=True), + block.model_dump(exclude_unset=True, warnings=False), ), cast( "dict[object, object]", - content_delta.model_dump(exclude_unset=True), + content_delta.model_dump(exclude_unset=True, warnings=False), ), ) current_message_snapshot.content[content_delta.index] = cast( From bd1e9e2fa7869c429f8ff727ba72b9675710edc9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:48:19 +0000 Subject: [PATCH 604/914] fix(client): handle domains with underscores (#1726) --- src/openai/_base_client.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index f374449dbc..2545ddf967 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -490,12 +490,17 @@ def _build_request( if not files: files = cast(HttpxRequestFiles, ForceMultipartDict()) + prepared_url = self._prepare_url(http://github.com/options.url) + if "_" in prepared_url.host: + # work around https://github.com/encode/httpx/discussions/2880 + kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, method=options.method, - url=self._prepare_url(http://github.com/options.url), + url=prepared_url, # the `Query` type that we use is incompatible with qs' # `Params` type as it needs to be typed as `Mapping[str, object]` # so that passing a `TypedDict` doesn't cause an error. From 6172976b16821b24194a05e3e3fe5cb2342a2b4b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:48:52 +0000 Subject: [PATCH 605/914] release: 1.46.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4b6cc3c898..d89af66b28 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.46.0" + ".": "1.46.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c1f852327c..3f1c96e6d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.46.1 (2024-09-19) + +Full Changelog: [v1.46.0...v1.46.1](https://github.com/openai/openai-python/compare/v1.46.0...v1.46.1) + +### Bug Fixes + +* **client:** handle domains with underscores ([#1726](https://github.com/openai/openai-python/issues/1726)) ([cd194df](https://github.com/openai/openai-python/commit/cd194dfdc418a84589bd903357cba349e9ad3e78)) + + +### Chores + +* **streaming:** silence pydantic model_dump warnings ([#1722](https://github.com/openai/openai-python/issues/1722)) ([30f84b9](https://github.com/openai/openai-python/commit/30f84b96081ac37f60e40a75d765dbbf563b61b3)) + ## 1.46.0 (2024-09-17) Full Changelog: [v1.45.1...v1.46.0](https://github.com/openai/openai-python/compare/v1.45.1...v1.46.0) diff --git a/pyproject.toml b/pyproject.toml index a3bd81cad5..b505880f11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.46.0" +version = "1.46.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ccd62dc230..197a24ea67 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.46.0" # x-release-please-version +__version__ = "1.46.1" # x-release-please-version From 3765cc2c6a8c90376a41d5daeb7f2f100f81f8a5 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Thu, 19 Sep 2024 16:11:56 +0000 Subject: [PATCH 606/914] feat(client): send retry count header --- src/openai/_base_client.py | 102 ++++++++++++++++++++----------------- src/openai/lib/azure.py | 4 +- tests/test_client.py | 16 ++++++ 3 files changed, 74 insertions(+), 48 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 2545ddf967..77e82026ef 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -401,14 +401,7 @@ def _make_status_error( ) -> _exceptions.APIStatusError: raise NotImplementedError() - def _remaining_retries( - self, - remaining_retries: Optional[int], - options: FinalRequestOptions, - ) -> int: - return remaining_retries if remaining_retries is not None else options.get_max_retries(self.max_retries) - - def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers: + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: custom_headers = options.headers or {} headers_dict = _merge_mappings(self.default_headers, custom_headers) self._validate_headers(headers_dict, custom_headers) @@ -420,6 +413,9 @@ def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers: if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: headers[idempotency_header] = options.idempotency_key or self._idempotency_key() + if retries_taken > 0: + headers.setdefault("x-stainless-retry-count", str(retries_taken)) + return headers def _prepare_url(http://github.com/self,%20url:%20str) -> URL: @@ -441,6 +437,8 @@ def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: def _build_request( self, options: FinalRequestOptions, + *, + retries_taken: int = 0, ) -> httpx.Request: if log.isEnabledFor(logging.DEBUG): log.debug("Request options: %s", model_dump(options, exclude_unset=True)) @@ -456,7 +454,7 @@ def _build_request( else: raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") - headers = self._build_headers(options) + headers = self._build_headers(options, retries_taken=retries_taken) params = _merge_mappings(self.default_query, options.params) content_type = headers.get("Content-Type") files = options.files @@ -939,12 +937,17 @@ def request( stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: + if remaining_retries is not None: + retries_taken = options.get_max_retries(self.max_retries) - remaining_retries + else: + retries_taken = 0 + return self._request( cast_to=cast_to, options=options, stream=stream, stream_cls=stream_cls, - remaining_retries=remaining_retries, + retries_taken=retries_taken, ) def _request( @@ -952,7 +955,7 @@ def _request( *, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: int | None, + retries_taken: int, stream: bool, stream_cls: type[_StreamT] | None, ) -> ResponseT | _StreamT: @@ -964,8 +967,8 @@ def _request( cast_to = self._maybe_override_cast_to(cast_to, options) options = self._prepare_options(options) - retries = self._remaining_retries(remaining_retries, options) - request = self._build_request(options) + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + request = self._build_request(options, retries_taken=retries_taken) self._prepare_request(request) kwargs: HttpxSendArgs = {} @@ -983,11 +986,11 @@ def _request( except httpx.TimeoutException as err: log.debug("Encountered httpx.TimeoutException", exc_info=True) - if retries > 0: + if remaining_retries > 0: return self._retry_request( input_options, cast_to, - retries, + retries_taken=retries_taken, stream=stream, stream_cls=stream_cls, response_headers=None, @@ -998,11 +1001,11 @@ def _request( except Exception as err: log.debug("Encountered Exception", exc_info=True) - if retries > 0: + if remaining_retries > 0: return self._retry_request( input_options, cast_to, - retries, + retries_taken=retries_taken, stream=stream, stream_cls=stream_cls, response_headers=None, @@ -1026,13 +1029,13 @@ def _request( except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - if retries > 0 and self._should_retry(err.response): + if remaining_retries > 0 and self._should_retry(err.response): err.response.close() return self._retry_request( input_options, cast_to, - retries, - err.response.headers, + retries_taken=retries_taken, + response_headers=err.response.headers, stream=stream, stream_cls=stream_cls, ) @@ -1051,26 +1054,26 @@ def _request( response=response, stream=stream, stream_cls=stream_cls, - retries_taken=options.get_max_retries(self.max_retries) - retries, + retries_taken=retries_taken, ) def _retry_request( self, options: FinalRequestOptions, cast_to: Type[ResponseT], - remaining_retries: int, - response_headers: httpx.Headers | None, *, + retries_taken: int, + response_headers: httpx.Headers | None, stream: bool, stream_cls: type[_StreamT] | None, ) -> ResponseT | _StreamT: - remaining = remaining_retries - 1 - if remaining == 1: + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + if remaining_retries == 1: log.debug("1 retry left") else: - log.debug("%i retries left", remaining) + log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout(remaining, options, response_headers) + timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) log.info("Retrying request to %s in %f seconds", options.url, timeout) # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a @@ -1080,7 +1083,7 @@ def _retry_request( return self._request( options=options, cast_to=cast_to, - remaining_retries=remaining, + retries_taken=retries_taken + 1, stream=stream, stream_cls=stream_cls, ) @@ -1512,12 +1515,17 @@ async def request( stream_cls: type[_AsyncStreamT] | None = None, remaining_retries: Optional[int] = None, ) -> ResponseT | _AsyncStreamT: + if remaining_retries is not None: + retries_taken = options.get_max_retries(self.max_retries) - remaining_retries + else: + retries_taken = 0 + return await self._request( cast_to=cast_to, options=options, stream=stream, stream_cls=stream_cls, - remaining_retries=remaining_retries, + retries_taken=retries_taken, ) async def _request( @@ -1527,7 +1535,7 @@ async def _request( *, stream: bool, stream_cls: type[_AsyncStreamT] | None, - remaining_retries: int | None, + retries_taken: int, ) -> ResponseT | _AsyncStreamT: if self._platform is None: # `get_platform` can make blocking IO calls so we @@ -1542,8 +1550,8 @@ async def _request( cast_to = self._maybe_override_cast_to(cast_to, options) options = await self._prepare_options(options) - retries = self._remaining_retries(remaining_retries, options) - request = self._build_request(options) + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + request = self._build_request(options, retries_taken=retries_taken) await self._prepare_request(request) kwargs: HttpxSendArgs = {} @@ -1559,11 +1567,11 @@ async def _request( except httpx.TimeoutException as err: log.debug("Encountered httpx.TimeoutException", exc_info=True) - if retries > 0: + if remaining_retries > 0: return await self._retry_request( input_options, cast_to, - retries, + retries_taken=retries_taken, stream=stream, stream_cls=stream_cls, response_headers=None, @@ -1574,11 +1582,11 @@ async def _request( except Exception as err: log.debug("Encountered Exception", exc_info=True) - if retries > 0: + if retries_taken > 0: return await self._retry_request( input_options, cast_to, - retries, + retries_taken=retries_taken, stream=stream, stream_cls=stream_cls, response_headers=None, @@ -1596,13 +1604,13 @@ async def _request( except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - if retries > 0 and self._should_retry(err.response): + if remaining_retries > 0 and self._should_retry(err.response): await err.response.aclose() return await self._retry_request( input_options, cast_to, - retries, - err.response.headers, + retries_taken=retries_taken, + response_headers=err.response.headers, stream=stream, stream_cls=stream_cls, ) @@ -1621,26 +1629,26 @@ async def _request( response=response, stream=stream, stream_cls=stream_cls, - retries_taken=options.get_max_retries(self.max_retries) - retries, + retries_taken=retries_taken, ) async def _retry_request( self, options: FinalRequestOptions, cast_to: Type[ResponseT], - remaining_retries: int, - response_headers: httpx.Headers | None, *, + retries_taken: int, + response_headers: httpx.Headers | None, stream: bool, stream_cls: type[_AsyncStreamT] | None, ) -> ResponseT | _AsyncStreamT: - remaining = remaining_retries - 1 - if remaining == 1: + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + if remaining_retries == 1: log.debug("1 retry left") else: - log.debug("%i retries left", remaining) + log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout(remaining, options, response_headers) + timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) log.info("Retrying request to %s in %f seconds", options.url, timeout) await anyio.sleep(timeout) @@ -1648,7 +1656,7 @@ async def _retry_request( return await self._request( options=options, cast_to=cast_to, - remaining_retries=remaining, + retries_taken=retries_taken + 1, stream=stream, stream_cls=stream_cls, ) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index ef64137de4..5d21f10b70 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -53,13 +53,15 @@ class BaseAzureClient(BaseClient[_HttpxClientT, _DefaultStreamT]): def _build_request( self, options: FinalRequestOptions, + *, + retries_taken: int = 0, ) -> httpx.Request: if options.url in _deployments_endpoints and is_mapping(options.json_data): model = options.json_data.get("model") if model is not None and not "/deployments" in str(self.base_url): options.url = f"/deployments/{model}{options.url}" - return super()._build_request(options) + return super()._build_request(options, retries_taken=retries_taken) class AzureOpenAI(BaseAzureClient[httpx.Client, Stream[Any]], OpenAI): diff --git a/tests/test_client.py b/tests/test_client.py index 054ae0ff4e..567a6ec59f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -788,6 +788,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: ) assert response.retries_taken == failures_before_success + if failures_before_success == 0: + assert "x-stainless-retry-count" not in response.http_request.headers + else: + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -818,6 +822,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: model="gpt-4o", ) as response: assert response.retries_taken == failures_before_success + if failures_before_success == 0: + assert "x-stainless-retry-count" not in response.http_request.headers + else: + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success class TestAsyncOpenAI: @@ -1582,6 +1590,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: ) assert response.retries_taken == failures_before_success + if failures_before_success == 0: + assert "x-stainless-retry-count" not in response.http_request.headers + else: + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -1613,3 +1625,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: model="gpt-4o", ) as response: assert response.retries_taken == failures_before_success + if failures_before_success == 0: + assert "x-stainless-retry-count" not in response.http_request.headers + else: + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success From eab2e5a396ae5d114cf63cb26f30070ccad8b0b1 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Thu, 19 Sep 2024 16:26:25 +0000 Subject: [PATCH 607/914] chore(types): improve type name for embedding models (#1730) --- .stats.yml | 2 +- api.md | 2 +- src/openai/resources/embeddings.py | 9 ++++----- src/openai/types/__init__.py | 1 + src/openai/types/embedding_create_params.py | 4 +++- src/openai/types/embedding_model.py | 7 +++++++ 6 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 src/openai/types/embedding_model.py diff --git a/.stats.yml b/.stats.yml index 2fc39385e9..0151c5a105 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-ff407aa10917e62f2b0c12d1ad2c4f1258ed083bd45753c70eaaf5b1cf8356ae.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-de1981b64ac229493473670d618500c6362c195f1057eb7de00bd1bc9184fbd5.yml diff --git a/api.md b/api.md index 32c0fb9efc..2498340328 100644 --- a/api.md +++ b/api.md @@ -70,7 +70,7 @@ Methods: Types: ```python -from openai.types import CreateEmbeddingResponse, Embedding +from openai.types import CreateEmbeddingResponse, Embedding, EmbeddingModel ``` Methods: diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 71c2a18a24..c91e6cc13a 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -16,9 +16,8 @@ from .._extras import numpy as np, has_numpy from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options +from ..types.embedding_model import EmbeddingModel from ..types.create_embedding_response import CreateEmbeddingResponse __all__ = ["Embeddings", "AsyncEmbeddings"] @@ -48,7 +47,7 @@ def create( self, *, input: Union[str, List[str], Iterable[int], Iterable[Iterable[int]]], - model: Union[str, Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]], + model: Union[str, EmbeddingModel], dimensions: int | NotGiven = NOT_GIVEN, encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -160,7 +159,7 @@ async def create( self, *, input: Union[str, List[str], Iterable[int], Iterable[Iterable[int]]], - model: Union[str, Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]], + model: Union[str, EmbeddingModel], dimensions: int | NotGiven = NOT_GIVEN, encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index ad9284fbd5..4dbc1b6b7b 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -26,6 +26,7 @@ from .file_deleted import FileDeleted as FileDeleted from .file_purpose import FilePurpose as FilePurpose from .model_deleted import ModelDeleted as ModelDeleted +from .embedding_model import EmbeddingModel as EmbeddingModel from .images_response import ImagesResponse as ImagesResponse from .completion_usage import CompletionUsage as CompletionUsage from .file_list_params import FileListParams as FileListParams diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py index 930b3b7914..1548cdbd77 100644 --- a/src/openai/types/embedding_create_params.py +++ b/src/openai/types/embedding_create_params.py @@ -5,6 +5,8 @@ from typing import List, Union, Iterable from typing_extensions import Literal, Required, TypedDict +from .embedding_model import EmbeddingModel + __all__ = ["EmbeddingCreateParams"] @@ -20,7 +22,7 @@ class EmbeddingCreateParams(TypedDict, total=False): for counting tokens. """ - model: Required[Union[str, Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]]] + model: Required[Union[str, EmbeddingModel]] """ID of the model to use. You can use the diff --git a/src/openai/types/embedding_model.py b/src/openai/types/embedding_model.py new file mode 100644 index 0000000000..075ff97644 --- /dev/null +++ b/src/openai/types/embedding_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["EmbeddingModel"] + +EmbeddingModel: TypeAlias = Literal["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"] From 45315a7a7fef522a56b4c86558f2e07ebea4f71a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 05:04:20 +0000 Subject: [PATCH 608/914] release: 1.47.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d89af66b28..dc9bcb375c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.46.1" + ".": "1.47.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f1c96e6d5..021be7ef4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.47.0 (2024-09-20) + +Full Changelog: [v1.46.1...v1.47.0](https://github.com/openai/openai-python/compare/v1.46.1...v1.47.0) + +### Features + +* **client:** send retry count header ([21b0c00](https://github.com/openai/openai-python/commit/21b0c0043406d81971f87455e5a48b17935dc346)) + + +### Chores + +* **types:** improve type name for embedding models ([#1730](https://github.com/openai/openai-python/issues/1730)) ([4b4eb2b](https://github.com/openai/openai-python/commit/4b4eb2b37877728d2124ad5651ceebf615c0ab28)) + ## 1.46.1 (2024-09-19) Full Changelog: [v1.46.0...v1.46.1](https://github.com/openai/openai-python/compare/v1.46.0...v1.46.1) diff --git a/pyproject.toml b/pyproject.toml index b505880f11..5288d5aa10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.46.1" +version = "1.47.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 197a24ea67..509df1e880 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.46.1" # x-release-please-version +__version__ = "1.47.0" # x-release-please-version From 5b2dbcd9ba17a4cb897697d90e66175b640e5780 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 23 Sep 2024 11:18:41 +0100 Subject: [PATCH 609/914] fix(pydantic v1): avoid warnings error --- src/openai/_compat.py | 2 ++ src/openai/lib/streaming/_assistants.py | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/openai/_compat.py b/src/openai/_compat.py index c0dd8c1ee5..78851277eb 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -136,12 +136,14 @@ def model_dump( exclude: IncEx = None, exclude_unset: bool = False, exclude_defaults: bool = False, + warnings: bool = True, ) -> dict[str, Any]: if PYDANTIC_V2: return model.model_dump( exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, + warnings=warnings, ) return cast( "dict[str, Any]", diff --git a/src/openai/lib/streaming/_assistants.py b/src/openai/lib/streaming/_assistants.py index 183754177a..103e4c40aa 100644 --- a/src/openai/lib/streaming/_assistants.py +++ b/src/openai/lib/streaming/_assistants.py @@ -8,6 +8,7 @@ import httpx from ..._utils import is_dict, is_list, consume_sync_iterator, consume_async_iterator +from ..._compat import model_dump from ..._models import construct_type from ..._streaming import Stream, AsyncStream from ...types.beta import AssistantStreamEvent @@ -906,11 +907,11 @@ def accumulate_run_step( merged = accumulate_delta( cast( "dict[object, object]", - snapshot.model_dump(exclude_unset=True, warnings=False), + model_dump(snapshot, exclude_unset=True, warnings=False), ), cast( "dict[object, object]", - data.delta.model_dump(exclude_unset=True, warnings=False), + model_dump(data.delta, exclude_unset=True, warnings=False), ), ) run_step_snapshots[snapshot.id] = cast(RunStep, construct_type(type_=RunStep, value=merged)) @@ -948,7 +949,7 @@ def accumulate_event( construct_type( # mypy doesn't allow Content for some reason type_=cast(Any, MessageContent), - value=content_delta.model_dump(exclude_unset=True, warnings=False), + value=model_dump(content_delta, exclude_unset=True, warnings=False), ), ), ) @@ -957,11 +958,11 @@ def accumulate_event( merged = accumulate_delta( cast( "dict[object, object]", - block.model_dump(exclude_unset=True, warnings=False), + model_dump(block, exclude_unset=True, warnings=False), ), cast( "dict[object, object]", - content_delta.model_dump(exclude_unset=True, warnings=False), + model_dump(content_delta, exclude_unset=True, warnings=False), ), ) current_message_snapshot.content[content_delta.index] = cast( From 89e5a010b22136f0f16eff5ec33105f4d3e273eb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:20:25 +0000 Subject: [PATCH 610/914] release: 1.47.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index dc9bcb375c..fea051a9dd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.47.0" + ".": "1.47.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 021be7ef4c..823e9886d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.47.1 (2024-09-23) + +Full Changelog: [v1.47.0...v1.47.1](https://github.com/openai/openai-python/compare/v1.47.0...v1.47.1) + +### Bug Fixes + +* **pydantic v1:** avoid warnings error ([1e8e7d1](https://github.com/openai/openai-python/commit/1e8e7d1f01a4ab4153085bc20484a19613d993b3)) + ## 1.47.0 (2024-09-20) Full Changelog: [v1.46.1...v1.47.0](https://github.com/openai/openai-python/compare/v1.46.1...v1.47.0) diff --git a/pyproject.toml b/pyproject.toml index 5288d5aa10..e4b8a967e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.47.0" +version = "1.47.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 509df1e880..cad4b7f76c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.47.0" # x-release-please-version +__version__ = "1.47.1" # x-release-please-version From fd1ed4b5e3828a14074018a30ce42d3960741ccd Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Tue, 24 Sep 2024 14:58:33 +0000 Subject: [PATCH 611/914] chore(internal): use `typing_extensions.overload` instead of `typing` (#1740) --- src/openai/resources/beta/threads/runs/runs.py | 4 ++-- src/openai/resources/beta/threads/threads.py | 4 ++-- src/openai/resources/chat/completions.py | 4 ++-- src/openai/resources/completions.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 3fb1cc77aa..a4cbcc57e4 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -3,9 +3,9 @@ from __future__ import annotations import typing_extensions -from typing import List, Union, Iterable, Optional, overload +from typing import List, Union, Iterable, Optional from functools import partial -from typing_extensions import Literal +from typing_extensions import Literal, overload import httpx diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 49b0e4b37e..acc0fe9bda 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -2,9 +2,9 @@ from __future__ import annotations -from typing import Union, Iterable, Optional, overload +from typing import Union, Iterable, Optional from functools import partial -from typing_extensions import Literal +from typing_extensions import Literal, overload import httpx diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index e9267b1f03..05cfaacd83 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -3,8 +3,8 @@ from __future__ import annotations import inspect -from typing import Dict, List, Union, Iterable, Optional, overload -from typing_extensions import Literal +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, overload import httpx import pydantic diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 091fb5657a..49198d2687 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -2,8 +2,8 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable, Optional, overload -from typing_extensions import Literal +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, overload import httpx From 102fce4f363d9996ef40b81cbe79fd29c3e7ccc1 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Wed, 25 Sep 2024 12:43:48 +0000 Subject: [PATCH 612/914] fix(audio): correct response_format translations type (#1743) --- .DS_Store | Bin 0 -> 6148 bytes .stats.yml | 2 +- api.md | 2 +- src/openai/resources/audio/transcriptions.py | 14 ++++++++------ src/openai/resources/audio/translations.py | 14 ++++++++------ src/openai/types/__init__.py | 1 + .../types/audio/transcription_create_params.py | 7 ++++--- .../types/audio/translation_create_params.py | 7 ++++--- src/openai/types/audio_response_format.py | 7 +++++++ tests/api_resources/audio/test_translations.py | 8 ++++---- 10 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 .DS_Store create mode 100644 src/openai/types/audio_response_format.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c88a062b05be4fd1d362b3e4c6a7481e718b69d0 GIT binary patch literal 6148 zcmeH~O$x$5422WzLU7Zi%h`AUZ!n0Spcima5J4*Vx1OW>k_m#k8KJGkN^pg011%5 z4-v3?8#bF)Wh4O-Ab}?V`#vPNX$~z_{nLTqBLK8P*$r!-C7{U)&>UK-q5{*H9yD6j z#}KP~J2b_)99pW@cF`C;crrWIXQgOGwy`I%~QMGk}L;X0y%TE9jyNVZZH|!@{KyzrRiVBQB0*--!1inh( E0rZ6u#{d8T literal 0 HcmV?d00001 diff --git a/.stats.yml b/.stats.yml index 0151c5a105..e8bca3c6d8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-de1981b64ac229493473670d618500c6362c195f1057eb7de00bd1bc9184fbd5.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-073331021d48db6af646a3552ab0c682efe31b7fb4e59a109ed1ba539f9b89c5.yml diff --git a/api.md b/api.md index 2498340328..eae17d8d84 100644 --- a/api.md +++ b/api.md @@ -114,7 +114,7 @@ Methods: Types: ```python -from openai.types import AudioModel +from openai.types import AudioModel, AudioResponseFormat ``` ## Transcriptions diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index a6009143d4..fd042d1ac3 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -8,6 +8,7 @@ import httpx from ... import _legacy_response +from ...types import AudioResponseFormat from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import ( extract_files, @@ -22,6 +23,7 @@ from ..._base_client import make_request_options from ...types.audio_model import AudioModel from ...types.audio.transcription import Transcription +from ...types.audio_response_format import AudioResponseFormat __all__ = ["Transcriptions", "AsyncTranscriptions"] @@ -53,7 +55,7 @@ def create( model: Union[str, AudioModel], language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, - response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN, + response_format: AudioResponseFormat | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -83,8 +85,8 @@ def create( [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should match the audio language. - response_format: The format of the transcript output, in one of these options: `json`, `text`, - `srt`, `verbose_json`, or `vtt`. + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -160,7 +162,7 @@ async def create( model: Union[str, AudioModel], language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, - response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven = NOT_GIVEN, + response_format: AudioResponseFormat | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -190,8 +192,8 @@ async def create( [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should match the audio language. - response_format: The format of the transcript output, in one of these options: `json`, `text`, - `srt`, `verbose_json`, or `vtt`. + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 7ec647fb6b..fe08dd550e 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -7,6 +7,7 @@ import httpx from ... import _legacy_response +from ...types import AudioResponseFormat from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import ( extract_files, @@ -21,6 +22,7 @@ from ..._base_client import make_request_options from ...types.audio_model import AudioModel from ...types.audio.translation import Translation +from ...types.audio_response_format import AudioResponseFormat __all__ = ["Translations", "AsyncTranslations"] @@ -51,7 +53,7 @@ def create( file: FileTypes, model: Union[str, AudioModel], prompt: str | NotGiven = NOT_GIVEN, - response_format: str | NotGiven = NOT_GIVEN, + response_format: AudioResponseFormat | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -75,8 +77,8 @@ def create( [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should be in English. - response_format: The format of the transcript output, in one of these options: `json`, `text`, - `srt`, `verbose_json`, or `vtt`. + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -143,7 +145,7 @@ async def create( file: FileTypes, model: Union[str, AudioModel], prompt: str | NotGiven = NOT_GIVEN, - response_format: str | NotGiven = NOT_GIVEN, + response_format: AudioResponseFormat | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -167,8 +169,8 @@ async def create( [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should be in English. - response_format: The format of the transcript output, in one of these options: `json`, `text`, - `srt`, `verbose_json`, or `vtt`. + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 4dbc1b6b7b..6223be883d 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -38,6 +38,7 @@ from .batch_create_params import BatchCreateParams as BatchCreateParams from .batch_request_counts import BatchRequestCounts as BatchRequestCounts from .upload_create_params import UploadCreateParams as UploadCreateParams +from .audio_response_format import AudioResponseFormat as AudioResponseFormat from .image_generate_params import ImageGenerateParams as ImageGenerateParams from .upload_complete_params import UploadCompleteParams as UploadCompleteParams from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index a825fefecb..5ac2bb91e5 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -7,6 +7,7 @@ from ..._types import FileTypes from ..audio_model import AudioModel +from ..audio_response_format import AudioResponseFormat __all__ = ["TranscriptionCreateParams"] @@ -41,10 +42,10 @@ class TranscriptionCreateParams(TypedDict, total=False): should match the audio language. """ - response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] + response_format: AudioResponseFormat """ - The format of the transcript output, in one of these options: `json`, `text`, - `srt`, `verbose_json`, or `vtt`. + The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. """ temperature: float diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py index 054996a134..6859ed9d30 100644 --- a/src/openai/types/audio/translation_create_params.py +++ b/src/openai/types/audio/translation_create_params.py @@ -7,6 +7,7 @@ from ..._types import FileTypes from ..audio_model import AudioModel +from ..audio_response_format import AudioResponseFormat __all__ = ["TranslationCreateParams"] @@ -33,10 +34,10 @@ class TranslationCreateParams(TypedDict, total=False): should be in English. """ - response_format: str + response_format: AudioResponseFormat """ - The format of the transcript output, in one of these options: `json`, `text`, - `srt`, `verbose_json`, or `vtt`. + The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. """ temperature: float diff --git a/src/openai/types/audio_response_format.py b/src/openai/types/audio_response_format.py new file mode 100644 index 0000000000..f8c8d45945 --- /dev/null +++ b/src/openai/types/audio_response_format.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["AudioResponseFormat"] + +AudioResponseFormat: TypeAlias = Literal["json", "text", "srt", "verbose_json", "vtt"] diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index f5c6c68f0b..b048a1af12 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -30,8 +30,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: translation = client.audio.translations.create( file=b"raw file contents", model="whisper-1", - prompt="string", - response_format="string", + prompt="prompt", + response_format="json", temperature=0, ) assert_matches_type(Translation, translation, path=["response"]) @@ -79,8 +79,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> translation = await async_client.audio.translations.create( file=b"raw file contents", model="whisper-1", - prompt="string", - response_format="string", + prompt="prompt", + response_format="json", temperature=0, ) assert_matches_type(Translation, translation, path=["response"]) From 5449e208bcf1bcf6c4a25a33f2144c7bfd8acdb3 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Wed, 25 Sep 2024 12:45:37 +0000 Subject: [PATCH 613/914] feat(client): allow overriding retry count header (#1745) --- src/openai/_base_client.py | 6 +- tests/test_client.py | 150 +++++++++++++++++++++++++++++++++---- 2 files changed, 138 insertions(+), 18 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 77e82026ef..c4c9803e74 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -413,8 +413,10 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0 if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: headers[idempotency_header] = options.idempotency_key or self._idempotency_key() - if retries_taken > 0: - headers.setdefault("x-stainless-retry-count", str(retries_taken)) + # Don't set the retry count header if it was already set or removed by the caller. We check + # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. + if "x-stainless-retry-count" not in (header.lower() for header in custom_headers): + headers["x-stainless-retry-count"] = str(retries_taken) return headers diff --git a/tests/test_client.py b/tests/test_client.py index 567a6ec59f..463174465c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -788,10 +788,71 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: ) assert response.retries_taken == failures_before_success - if failures_before_success == 0: - assert "x-stainless-retry-count" not in response.http_request.headers - else: - assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_omit_retry_count_header( + self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-4o", + extra_headers={"x-stainless-retry-count": Omit()}, + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_overwrite_retry_count_header( + self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-4o", + extra_headers={"x-stainless-retry-count": "42"}, + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -822,10 +883,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: model="gpt-4o", ) as response: assert response.retries_taken == failures_before_success - if failures_before_success == 0: - assert "x-stainless-retry-count" not in response.http_request.headers - else: - assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success class TestAsyncOpenAI: @@ -1590,10 +1648,73 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: ) assert response.retries_taken == failures_before_success - if failures_before_success == 0: - assert "x-stainless-retry-count" not in response.http_request.headers - else: - assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_omit_retry_count_header( + self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = await client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-4o", + extra_headers={"x-stainless-retry-count": Omit()}, + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_overwrite_retry_count_header( + self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/chat/completions").mock(side_effect=retry_handler) + + response = await client.chat.completions.with_raw_response.create( + messages=[ + { + "content": "string", + "role": "system", + } + ], + model="gpt-4o", + extra_headers={"x-stainless-retry-count": "42"}, + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -1625,7 +1746,4 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: model="gpt-4o", ) as response: assert response.retries_taken == failures_before_success - if failures_before_success == 0: - assert "x-stainless-retry-count" not in response.http_request.headers - else: - assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success From 864eafca8f9626c13a5864c133ca620d1cbecf89 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:21:13 +0000 Subject: [PATCH 614/914] release: 1.48.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fea051a9dd..3f47f4bc2b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.47.1" + ".": "1.48.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 823e9886d8..d89151074b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 1.48.0 (2024-09-25) + +Full Changelog: [v1.47.1...v1.48.0](https://github.com/openai/openai-python/compare/v1.47.1...v1.48.0) + +### Features + +* **client:** allow overriding retry count header ([#1745](https://github.com/openai/openai-python/issues/1745)) ([9f07d4d](https://github.com/openai/openai-python/commit/9f07d4dbd6f24108a1f5e0309037318858f5a229)) + + +### Bug Fixes + +* **audio:** correct response_format translations type ([#1743](https://github.com/openai/openai-python/issues/1743)) ([b912108](https://github.com/openai/openai-python/commit/b9121089c696bc943323e2e75d4706401d809aaa)) + + +### Chores + +* **internal:** use `typing_extensions.overload` instead of `typing` ([#1740](https://github.com/openai/openai-python/issues/1740)) ([2522bd5](https://github.com/openai/openai-python/commit/2522bd59f7b5e903e4fc856a4c5dbdbe66bba37f)) + ## 1.47.1 (2024-09-23) Full Changelog: [v1.47.0...v1.47.1](https://github.com/openai/openai-python/compare/v1.47.0...v1.47.1) diff --git a/pyproject.toml b/pyproject.toml index e4b8a967e0..05f957cac0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.47.1" +version = "1.48.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index cad4b7f76c..66109f354b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.47.1" # x-release-please-version +__version__ = "1.48.0" # x-release-please-version From 9feadd8274809fff9ff1e36a0c90d45566ed46e2 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 26 Sep 2024 11:34:40 +0100 Subject: [PATCH 615/914] chore(internal): update test snapshots (#1749) --- ...661f4f5bea321c0aac9e164f2ed3e409aebc48.bin | 102 ----- ...2625e557f9e6763bd8c03bcd88e220149a3367.bin | 224 ----------- ...cf4aa41fc428937c231e17c3460f3237f6a018.bin | 28 -- ...6aea6d0356b63140161758a2e576d4e3092cfa.bin | 36 -- ...f8d591fb691ebac56f5ae39a22cc7d455c5353.bin | 28 ++ ...849decc68d3f63bd38172889367e1afb1e04f7.bin | 22 ++ ...fd3deafe041da94d541071009596234d8c84a6.bin | 36 -- ...3dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin | 52 --- ...af1246f55edb6ad95fa24059f160996b68866d.bin | 10 + ...f7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin | 28 -- ...6dd33878095c68badc6b6654a69769b391a1c1.bin | 30 ++ ...f0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin | 10 - ...5e65923f182256b6e6b752950a3aaa2ad2320a.bin | 36 ++ ...542b954b37d9dfd1910b964ddebc9677e6ae85.bin | 12 + ...9d46ee41408a688758219f3f58ac1ee2084db3.bin | 28 -- ...5dfafc1d712c253b42bafe07991b3058541016.bin | 156 -------- ...6ad615ed8d8f649888ebfddfbc3ee151f44d46.bin | 28 ++ ...f60f7f745f1a56d82e62f58031cc2add502380.bin | 100 +++++ ...613bdb9c4a2ad8262027d158cc94e6f9765164.bin | 12 - ...05d4daf7e8ad6d61ffa406ab5361fe36a8d5b1.bin | 36 ++ ...f8694b77f608de5e2a3799276be06ce3fbb15b.bin | 30 -- ...8bb74642d23ac1ca13fe37cb6e889b66f759f6.bin | 362 ++++++++++++++++++ ...81772c3010c10b37e8af3996fbdbbecb3c32a2.bin | 22 -- ...47020a9d875eb7ce644a0ff355581690a4cbfd.bin | 68 ++++ ...7688be2f6ca0849a6e4f17851b517310841d9b.bin | 52 +++ tests/lib/chat/test_completions.py | 132 ++++--- tests/lib/chat/test_completions_streaming.py | 263 ++++++------- 27 files changed, 970 insertions(+), 973 deletions(-) delete mode 100644 .inline-snapshot/external/038a5c69c34c9513021b52aa61661f4f5bea321c0aac9e164f2ed3e409aebc48.bin delete mode 100644 .inline-snapshot/external/0898f3d1651e3244eeb3651d012625e557f9e6763bd8c03bcd88e220149a3367.bin delete mode 100644 .inline-snapshot/external/0a00cd46c61030ff70241d432dcf4aa41fc428937c231e17c3460f3237f6a018.bin delete mode 100644 .inline-snapshot/external/15ae68f793c7b390fc8af9e21a6aea6d0356b63140161758a2e576d4e3092cfa.bin create mode 100644 .inline-snapshot/external/173417d553406f034f643e5db3f8d591fb691ebac56f5ae39a22cc7d455c5353.bin create mode 100644 .inline-snapshot/external/2018feb66ae13fcf5333d61b95849decc68d3f63bd38172889367e1afb1e04f7.bin delete mode 100644 .inline-snapshot/external/24aaf30663f9a568a0e77970b4fd3deafe041da94d541071009596234d8c84a6.bin delete mode 100644 .inline-snapshot/external/453df473e96274dd8ab61ab4d13dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin create mode 100644 .inline-snapshot/external/4cc50a6135d254573a502310e6af1246f55edb6ad95fa24059f160996b68866d.bin delete mode 100644 .inline-snapshot/external/4d75e4d7c3e0b532a67fb2114ff7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin create mode 100644 .inline-snapshot/external/569c877e69429d4cbc1577d2cd6dd33878095c68badc6b6654a69769b391a1c1.bin delete mode 100644 .inline-snapshot/external/69363a555f8ea9b6eee0bb022af0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin create mode 100644 .inline-snapshot/external/7e5ea4d12e7cc064399b6631415e65923f182256b6e6b752950a3aaa2ad2320a.bin create mode 100644 .inline-snapshot/external/83b060bae42eb41c4f1edbb7c1542b954b37d9dfd1910b964ddebc9677e6ae85.bin delete mode 100644 .inline-snapshot/external/83d3d003e6fdaa69b7a398440f9d46ee41408a688758219f3f58ac1ee2084db3.bin delete mode 100644 .inline-snapshot/external/a0c4f0be184e8234cdc0e3abae5dfafc1d712c253b42bafe07991b3058541016.bin create mode 100644 .inline-snapshot/external/a247c49c5fcd492bfb7a02a3306ad615ed8d8f649888ebfddfbc3ee151f44d46.bin create mode 100644 .inline-snapshot/external/a491adda08c3d4fde95f5b2ee3f60f7f745f1a56d82e62f58031cc2add502380.bin delete mode 100644 .inline-snapshot/external/be1089999ca5f1e63b149447f1613bdb9c4a2ad8262027d158cc94e6f9765164.bin create mode 100644 .inline-snapshot/external/c6aa7e397b7123c3501f25df3a05d4daf7e8ad6d61ffa406ab5361fe36a8d5b1.bin delete mode 100644 .inline-snapshot/external/ca015b8b1ebaac98be76f2f855f8694b77f608de5e2a3799276be06ce3fbb15b.bin create mode 100644 .inline-snapshot/external/d615580118391ee13492193e3a8bb74642d23ac1ca13fe37cb6e889b66f759f6.bin delete mode 100644 .inline-snapshot/external/dae1b261f19722801adc82a13181772c3010c10b37e8af3996fbdbbecb3c32a2.bin create mode 100644 .inline-snapshot/external/e2aad469b71d1d4894ff833ea147020a9d875eb7ce644a0ff355581690a4cbfd.bin create mode 100644 .inline-snapshot/external/f82268f2fefd5cfbc7eeb59c297688be2f6ca0849a6e4f17851b517310841d9b.bin diff --git a/.inline-snapshot/external/038a5c69c34c9513021b52aa61661f4f5bea321c0aac9e164f2ed3e409aebc48.bin b/.inline-snapshot/external/038a5c69c34c9513021b52aa61661f4f5bea321c0aac9e164f2ed3e409aebc48.bin deleted file mode 100644 index a5a0aeb4c0..0000000000 --- a/.inline-snapshot/external/038a5c69c34c9513021b52aa61661f4f5bea321c0aac9e164f2ed3e409aebc48.bin +++ /dev/null @@ -1,102 +0,0 @@ -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" including"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" For"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" latest"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" recommend"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" checking"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" such"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" as"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Channel"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" BBC"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" local"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" news"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" station"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXjg9DdaOfymTPDrSLfxslQEH0C2","object":"chat.completion.chunk","created":1723024748,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":47,"total_tokens":61}} - -data: [DONE] - diff --git a/.inline-snapshot/external/0898f3d1651e3244eeb3651d012625e557f9e6763bd8c03bcd88e220149a3367.bin b/.inline-snapshot/external/0898f3d1651e3244eeb3651d012625e557f9e6763bd8c03bcd88e220149a3367.bin deleted file mode 100644 index 4b42ada8d2..0000000000 --- a/.inline-snapshot/external/0898f3d1651e3244eeb3651d012625e557f9e6763bd8c03bcd88e220149a3367.bin +++ /dev/null @@ -1,224 +0,0 @@ -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"location"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" CA"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"forecast"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"_date"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"202"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"11"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"02"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"current"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"high"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"low"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\"\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" },\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"condition"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"humidity"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"wind"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"_speed"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"N"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"/A"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\"\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" },\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"note"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"Please"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" service"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":".\"\n"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" }"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXjrL8ZwahfIfWjgwcnHRzZrzVL4","object":"chat.completion.chunk","created":1723024759,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":19,"completion_tokens":108,"total_tokens":127}} - -data: [DONE] - diff --git a/.inline-snapshot/external/0a00cd46c61030ff70241d432dcf4aa41fc428937c231e17c3460f3237f6a018.bin b/.inline-snapshot/external/0a00cd46c61030ff70241d432dcf4aa41fc428937c231e17c3460f3237f6a018.bin deleted file mode 100644 index 73de9d6cbc..0000000000 --- a/.inline-snapshot/external/0a00cd46c61030ff70241d432dcf4aa41fc428937c231e17c3460f3237f6a018.bin +++ /dev/null @@ -1,28 +0,0 @@ -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":{"content":null,"refusal":[]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":{"content":null,"refusal":[{"token":"I'm","logprob":-0.0016157961,"bytes":[73,39,109],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":{"content":null,"refusal":[{"token":" sorry","logprob":-0.78663874,"bytes":[32,115,111,114,114,121],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":","},"logprobs":{"content":null,"refusal":[{"token":",","logprob":-0.0000779144,"bytes":[44],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":{"content":null,"refusal":[{"token":" I","logprob":-0.5234622,"bytes":[32,73],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" cannot"},"logprobs":{"content":null,"refusal":[{"token":" cannot","logprob":-0.52499557,"bytes":[32,99,97,110,110,111,116],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":{"content":null,"refusal":[{"token":" assist","logprob":-0.015198289,"bytes":[32,97,115,115,105,115,116],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":{"content":null,"refusal":[{"token":" with","logprob":-0.00071648485,"bytes":[32,119,105,116,104],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":{"content":null,"refusal":[{"token":" that","logprob":-0.008114983,"bytes":[32,116,104,97,116],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":{"content":null,"refusal":[{"token":" request","logprob":-0.0013802331,"bytes":[32,114,101,113,117,101,115,116],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":{"content":null,"refusal":[{"token":".","logprob":-3.4121115e-6,"bytes":[46],"top_logprobs":[]}]},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXjmhJIrvp7TBeVxzzxmx8pp2UGY","object":"chat.completion.chunk","created":1723024754,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":11,"total_tokens":28}} - -data: [DONE] - diff --git a/.inline-snapshot/external/15ae68f793c7b390fc8af9e21a6aea6d0356b63140161758a2e576d4e3092cfa.bin b/.inline-snapshot/external/15ae68f793c7b390fc8af9e21a6aea6d0356b63140161758a2e576d4e3092cfa.bin deleted file mode 100644 index 1bcca1fceb..0000000000 --- a/.inline-snapshot/external/15ae68f793c7b390fc8af9e21a6aea6d0356b63140161758a2e576d4e3092cfa.bin +++ /dev/null @@ -1,36 +0,0 @@ -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"68"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD","object":"chat.completion.chunk","created":1723024750,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":14,"total_tokens":31}} - -data: [DONE] - diff --git a/.inline-snapshot/external/173417d553406f034f643e5db3f8d591fb691ebac56f5ae39a22cc7d455c5353.bin b/.inline-snapshot/external/173417d553406f034f643e5db3f8d591fb691ebac56f5ae39a22cc7d455c5353.bin new file mode 100644 index 0000000000..49c6dce93f --- /dev/null +++ b/.inline-snapshot/external/173417d553406f034f643e5db3f8d591fb691ebac56f5ae39a22cc7d455c5353.bin @@ -0,0 +1,28 @@ +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" can't"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfw4IfQfCCrcuybFm41wJyxjbkz7","object":"chat.completion.chunk","created":1727346172,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[],"usage":{"prompt_tokens":79,"completion_tokens":11,"total_tokens":90,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/2018feb66ae13fcf5333d61b95849decc68d3f63bd38172889367e1afb1e04f7.bin b/.inline-snapshot/external/2018feb66ae13fcf5333d61b95849decc68d3f63bd38172889367e1afb1e04f7.bin new file mode 100644 index 0000000000..871970676f --- /dev/null +++ b/.inline-snapshot/external/2018feb66ae13fcf5333d61b95849decc68d3f63bd38172889367e1afb1e04f7.bin @@ -0,0 +1,22 @@ +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_4XzlGBLtUe9dy3GVNV4jhq7h","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"New"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" York"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-ABfwERreu9s99xXsVuOWtIB2UOx62","object":"chat.completion.chunk","created":1727346182,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_143bb8492c","choices":[],"usage":{"prompt_tokens":44,"completion_tokens":16,"total_tokens":60,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/24aaf30663f9a568a0e77970b4fd3deafe041da94d541071009596234d8c84a6.bin b/.inline-snapshot/external/24aaf30663f9a568a0e77970b4fd3deafe041da94d541071009596234d8c84a6.bin deleted file mode 100644 index 49962cff27..0000000000 --- a/.inline-snapshot/external/24aaf30663f9a568a0e77970b4fd3deafe041da94d541071009596234d8c84a6.bin +++ /dev/null @@ -1,36 +0,0 @@ -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_7PhhveOvvpPK53s1fV8TWhoV","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ed"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"inburgh"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"country"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"GB"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"units"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - -data: {"id":"chatcmpl-9tXjnXkjzholyB3ceNegQC7g5zP57","object":"chat.completion.chunk","created":1723024755,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":76,"completion_tokens":24,"total_tokens":100}} - -data: [DONE] - diff --git a/.inline-snapshot/external/453df473e96274dd8ab61ab4d13dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin b/.inline-snapshot/external/453df473e96274dd8ab61ab4d13dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin deleted file mode 100644 index adcdddd317..0000000000 --- a/.inline-snapshot/external/453df473e96274dd8ab61ab4d13dfcc25dc2f57a5e05eb5cc46c70b51d8845c2.bin +++ /dev/null @@ -1,52 +0,0 @@ -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_lQnnsesjFMWMQ5IeWPHzR4th","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"ci"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ty\": "}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Edinb"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"urgh"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\", \"c"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ountry"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"UK\", "}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"units"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c\"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_2xjOUgaCdiwAcl9ZBL9LyMUU","type":"function","function":{"name":"get_stock_price","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"ti"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"cker\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":": \"AAP"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"L\", "}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\"exch"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ange\":"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":" \"NA"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"SDAQ\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - -data: {"id":"chatcmpl-9tXjpPLJgivc9nyuBCCWX8HNg9L2J","object":"chat.completion.chunk","created":1723024757,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[],"usage":{"prompt_tokens":149,"completion_tokens":60,"total_tokens":209}} - -data: [DONE] - diff --git a/.inline-snapshot/external/4cc50a6135d254573a502310e6af1246f55edb6ad95fa24059f160996b68866d.bin b/.inline-snapshot/external/4cc50a6135d254573a502310e6af1246f55edb6ad95fa24059f160996b68866d.bin new file mode 100644 index 0000000000..c3392883be --- /dev/null +++ b/.inline-snapshot/external/4cc50a6135d254573a502310e6af1246f55edb6ad95fa24059f160996b68866d.bin @@ -0,0 +1,10 @@ +data: {"id":"chatcmpl-ABfw3Oqj8RD0z6aJiiX37oTjV2HFh","object":"chat.completion.chunk","created":1727346171,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw3Oqj8RD0z6aJiiX37oTjV2HFh","object":"chat.completion.chunk","created":1727346171,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw3Oqj8RD0z6aJiiX37oTjV2HFh","object":"chat.completion.chunk","created":1727346171,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"length"}]} + +data: {"id":"chatcmpl-ABfw3Oqj8RD0z6aJiiX37oTjV2HFh","object":"chat.completion.chunk","created":1727346171,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[],"usage":{"prompt_tokens":79,"completion_tokens":1,"total_tokens":80,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/4d75e4d7c3e0b532a67fb2114ff7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin b/.inline-snapshot/external/4d75e4d7c3e0b532a67fb2114ff7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin deleted file mode 100644 index 008d5882ec..0000000000 --- a/.inline-snapshot/external/4d75e4d7c3e0b532a67fb2114ff7868df0b6d8a02dfcd23f6bc7196cf0eadb6e.bin +++ /dev/null @@ -1,28 +0,0 @@ -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" cannot"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXjkxJ4omrCOJoVbZIgaPWZS8TLD","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":11,"total_tokens":28}} - -data: [DONE] - diff --git a/.inline-snapshot/external/569c877e69429d4cbc1577d2cd6dd33878095c68badc6b6654a69769b391a1c1.bin b/.inline-snapshot/external/569c877e69429d4cbc1577d2cd6dd33878095c68badc6b6654a69769b391a1c1.bin new file mode 100644 index 0000000000..47dd73151c --- /dev/null +++ b/.inline-snapshot/external/569c877e69429d4cbc1577d2cd6dd33878095c68badc6b6654a69769b391a1c1.bin @@ -0,0 +1,30 @@ +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":{"content":null,"refusal":[]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":{"content":null,"refusal":[{"token":"I'm","logprob":-0.0012038043,"bytes":[73,39,109],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" very"},"logprobs":{"content":null,"refusal":[{"token":" very","logprob":-0.8438816,"bytes":[32,118,101,114,121],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":{"content":null,"refusal":[{"token":" sorry","logprob":-3.4121115e-6,"bytes":[32,115,111,114,114,121],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":","},"logprobs":{"content":null,"refusal":[{"token":",","logprob":-0.000033809047,"bytes":[44],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" but"},"logprobs":{"content":null,"refusal":[{"token":" but","logprob":-0.038048144,"bytes":[32,98,117,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":{"content":null,"refusal":[{"token":" I","logprob":-0.0016109125,"bytes":[32,73],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" can't"},"logprobs":{"content":null,"refusal":[{"token":" can't","logprob":-0.0073532974,"bytes":[32,99,97,110,39,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":{"content":null,"refusal":[{"token":" assist","logprob":-0.0020837625,"bytes":[32,97,115,115,105,115,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":{"content":null,"refusal":[{"token":" with","logprob":-0.00318354,"bytes":[32,119,105,116,104],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":{"content":null,"refusal":[{"token":" that","logprob":-0.0017186158,"bytes":[32,116,104,97,116],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":{"content":null,"refusal":[{"token":".","logprob":-0.57687104,"bytes":[46],"top_logprobs":[]}]},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfw5GEVqPbLY576l46FZDQoNJ2KC","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[],"usage":{"prompt_tokens":79,"completion_tokens":12,"total_tokens":91,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/69363a555f8ea9b6eee0bb022af0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin b/.inline-snapshot/external/69363a555f8ea9b6eee0bb022af0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin deleted file mode 100644 index 852a7758f9..0000000000 --- a/.inline-snapshot/external/69363a555f8ea9b6eee0bb022af0afcd22d4f0e85418ab38ee24d2a570a84ff0.bin +++ /dev/null @@ -1,10 +0,0 @@ -data: {"id":"chatcmpl-9tXjkSxyTVUSWZRJFSZJgWBHzh2c3","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkSxyTVUSWZRJFSZJgWBHzh2c3","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjkSxyTVUSWZRJFSZJgWBHzh2c3","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"length"}]} - -data: {"id":"chatcmpl-9tXjkSxyTVUSWZRJFSZJgWBHzh2c3","object":"chat.completion.chunk","created":1723024752,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":1,"total_tokens":18}} - -data: [DONE] - diff --git a/.inline-snapshot/external/7e5ea4d12e7cc064399b6631415e65923f182256b6e6b752950a3aaa2ad2320a.bin b/.inline-snapshot/external/7e5ea4d12e7cc064399b6631415e65923f182256b6e6b752950a3aaa2ad2320a.bin new file mode 100644 index 0000000000..801db2adf2 --- /dev/null +++ b/.inline-snapshot/external/7e5ea4d12e7cc064399b6631415e65923f182256b6e6b752950a3aaa2ad2320a.bin @@ -0,0 +1,36 @@ +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"61"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF","object":"chat.completion.chunk","created":1727346169,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[],"usage":{"prompt_tokens":79,"completion_tokens":14,"total_tokens":93,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/83b060bae42eb41c4f1edbb7c1542b954b37d9dfd1910b964ddebc9677e6ae85.bin b/.inline-snapshot/external/83b060bae42eb41c4f1edbb7c1542b954b37d9dfd1910b964ddebc9677e6ae85.bin new file mode 100644 index 0000000000..e9f34b6334 --- /dev/null +++ b/.inline-snapshot/external/83b060bae42eb41c4f1edbb7c1542b954b37d9dfd1910b964ddebc9677e6ae85.bin @@ -0,0 +1,12 @@ +data: {"id":"chatcmpl-ABfw5EzoqmfXjnnsXY7Yd8OC6tb3c","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":{"content":[],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5EzoqmfXjnnsXY7Yd8OC6tb3c","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Foo"},"logprobs":{"content":[{"token":"Foo","logprob":-0.0025094282,"bytes":[70,111,111],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5EzoqmfXjnnsXY7Yd8OC6tb3c","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"!"},"logprobs":{"content":[{"token":"!","logprob":-0.26638845,"bytes":[33],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw5EzoqmfXjnnsXY7Yd8OC6tb3c","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfw5EzoqmfXjnnsXY7Yd8OC6tb3c","object":"chat.completion.chunk","created":1727346173,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[],"usage":{"prompt_tokens":9,"completion_tokens":2,"total_tokens":11,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/83d3d003e6fdaa69b7a398440f9d46ee41408a688758219f3f58ac1ee2084db3.bin b/.inline-snapshot/external/83d3d003e6fdaa69b7a398440f9d46ee41408a688758219f3f58ac1ee2084db3.bin deleted file mode 100644 index 05e08e3475..0000000000 --- a/.inline-snapshot/external/83d3d003e6fdaa69b7a398440f9d46ee41408a688758219f3f58ac1ee2084db3.bin +++ /dev/null @@ -1,28 +0,0 @@ -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_pVHYsU0gmSfX5TqxOyVbB2ma","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"San"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Francisco"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"state"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"CA"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - -data: {"id":"chatcmpl-9tXjq87CydgLGv4TnzV0EVDybqjCA","object":"chat.completion.chunk","created":1723024758,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_baa7103b2c","choices":[],"usage":{"prompt_tokens":48,"completion_tokens":19,"total_tokens":67}} - -data: [DONE] - diff --git a/.inline-snapshot/external/a0c4f0be184e8234cdc0e3abae5dfafc1d712c253b42bafe07991b3058541016.bin b/.inline-snapshot/external/a0c4f0be184e8234cdc0e3abae5dfafc1d712c253b42bafe07991b3058541016.bin deleted file mode 100644 index df20d6fda5..0000000000 --- a/.inline-snapshot/external/a0c4f0be184e8234cdc0e3abae5dfafc1d712c253b42bafe07991b3058541016.bin +++ /dev/null @@ -1,156 +0,0 @@ -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" but"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" can't"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" accurately"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" provide"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"63"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"58"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" the"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"6"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" current"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" for"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" San"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" Francisco"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" as"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" my"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" data"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" is"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" up"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" to"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" October"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" "},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"202"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"3"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" You"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" can"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" try"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" checking"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" a"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" reliable"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" weather"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" website"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" or"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" app"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" for"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" real"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"-time"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":" updates"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":1,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[{"index":2,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXjjpr5ZWilqbUE2tn3H1lwvMnDu","object":"chat.completion.chunk","created":1723024751,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_2a322c9ffc","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":71,"total_tokens":88}} - -data: [DONE] - diff --git a/.inline-snapshot/external/a247c49c5fcd492bfb7a02a3306ad615ed8d8f649888ebfddfbc3ee151f44d46.bin b/.inline-snapshot/external/a247c49c5fcd492bfb7a02a3306ad615ed8d8f649888ebfddfbc3ee151f44d46.bin new file mode 100644 index 0000000000..b44d334ac5 --- /dev/null +++ b/.inline-snapshot/external/a247c49c5fcd492bfb7a02a3306ad615ed8d8f649888ebfddfbc3ee151f44d46.bin @@ -0,0 +1,28 @@ +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_CTf1nWJLqSeRgDqaCG27xZ74","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"San"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Francisco"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"state"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"CA"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-ABfwCgi41eStOcARjZq97ohCEGBPO","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[],"usage":{"prompt_tokens":48,"completion_tokens":19,"total_tokens":67,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/a491adda08c3d4fde95f5b2ee3f60f7f745f1a56d82e62f58031cc2add502380.bin b/.inline-snapshot/external/a491adda08c3d4fde95f5b2ee3f60f7f745f1a56d82e62f58031cc2add502380.bin new file mode 100644 index 0000000000..160e65de49 --- /dev/null +++ b/.inline-snapshot/external/a491adda08c3d4fde95f5b2ee3f60f7f745f1a56d82e62f58031cc2add502380.bin @@ -0,0 +1,100 @@ +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"65"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"61"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"59"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"units"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"f"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":1,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[{"index":2,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfw2KKFuVXmEJgVwYfBvejMAdWtq","object":"chat.completion.chunk","created":1727346170,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_b40fb1c6fb","choices":[],"usage":{"prompt_tokens":79,"completion_tokens":42,"total_tokens":121,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/be1089999ca5f1e63b149447f1613bdb9c4a2ad8262027d158cc94e6f9765164.bin b/.inline-snapshot/external/be1089999ca5f1e63b149447f1613bdb9c4a2ad8262027d158cc94e6f9765164.bin deleted file mode 100644 index f2a8158310..0000000000 --- a/.inline-snapshot/external/be1089999ca5f1e63b149447f1613bdb9c4a2ad8262027d158cc94e6f9765164.bin +++ /dev/null @@ -1,12 +0,0 @@ -data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":{"content":[],"refusal":null},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"Foo"},"logprobs":{"content":[{"token":"Foo","logprob":-0.0067602484,"bytes":[70,111,111],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"content":"."},"logprobs":{"content":[{"token":".","logprob":-2.4962392,"bytes":[46],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXjliCPGY1wrAHNJ4DBnWJxKYyuf","object":"chat.completion.chunk","created":1723024753,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":9,"completion_tokens":2,"total_tokens":11}} - -data: [DONE] - diff --git a/.inline-snapshot/external/c6aa7e397b7123c3501f25df3a05d4daf7e8ad6d61ffa406ab5361fe36a8d5b1.bin b/.inline-snapshot/external/c6aa7e397b7123c3501f25df3a05d4daf7e8ad6d61ffa406ab5361fe36a8d5b1.bin new file mode 100644 index 0000000000..f20333fbef --- /dev/null +++ b/.inline-snapshot/external/c6aa7e397b7123c3501f25df3a05d4daf7e8ad6d61ffa406ab5361fe36a8d5b1.bin @@ -0,0 +1,36 @@ +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_c91SqDXlYFuETYv8mUHzz6pp","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ed"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"inburgh"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"country"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"UK"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"units"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-ABfw8AOXnoa2kzy11vVTSjuQhHCQr","object":"chat.completion.chunk","created":1727346176,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7568d46099","choices":[],"usage":{"prompt_tokens":76,"completion_tokens":24,"total_tokens":100,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/ca015b8b1ebaac98be76f2f855f8694b77f608de5e2a3799276be06ce3fbb15b.bin b/.inline-snapshot/external/ca015b8b1ebaac98be76f2f855f8694b77f608de5e2a3799276be06ce3fbb15b.bin deleted file mode 100644 index c0a355f9d1..0000000000 --- a/.inline-snapshot/external/ca015b8b1ebaac98be76f2f855f8694b77f608de5e2a3799276be06ce3fbb15b.bin +++ /dev/null @@ -1,30 +0,0 @@ -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"refusal":""},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"I'm"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" sorry"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":","},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" but"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" I"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" can't"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" assist"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" with"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" that"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":" request"},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"refusal":"."},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - -data: {"id":"chatcmpl-9tXmMGFPkLS0t0u0895fzYOblnfYa","object":"chat.completion.chunk","created":1723024914,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":12,"total_tokens":29}} - -data: [DONE] - diff --git a/.inline-snapshot/external/d615580118391ee13492193e3a8bb74642d23ac1ca13fe37cb6e889b66f759f6.bin b/.inline-snapshot/external/d615580118391ee13492193e3a8bb74642d23ac1ca13fe37cb6e889b66f759f6.bin new file mode 100644 index 0000000000..aee8650c72 --- /dev/null +++ b/.inline-snapshot/external/d615580118391ee13492193e3a8bb74642d23ac1ca13fe37cb6e889b66f759f6.bin @@ -0,0 +1,362 @@ +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"location"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" CA"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"temperature"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"18"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"°C"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"condition"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Part"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"ly"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" Cloud"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"y"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"humidity"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"72"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"%\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"wind"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Speed"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"15"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" km"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"/h"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"wind"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Direction"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"NW"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" },\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"forecast"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" [\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"day"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Monday"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"high"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"20"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"°C"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"low"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"14"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"°C"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"condition"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Sunny"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" },\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"day"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Tuesday"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"high"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"19"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"°C"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"low"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"15"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"°C"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"condition"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Mostly"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" Cloud"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"y"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" },\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" {\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"day"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Wednesday"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"high"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"18"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"°C"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"low"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"14"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"°C"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\",\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"condition"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"Cloud"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"y"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"\"\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" }\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" ]\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" }\n"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfwCjPMi0ubw56UyMIIeNfJzyogq","object":"chat.completion.chunk","created":1727346180,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[],"usage":{"prompt_tokens":19,"completion_tokens":177,"total_tokens":196,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/dae1b261f19722801adc82a13181772c3010c10b37e8af3996fbdbbecb3c32a2.bin b/.inline-snapshot/external/dae1b261f19722801adc82a13181772c3010c10b37e8af3996fbdbbecb3c32a2.bin deleted file mode 100644 index f0911c575d..0000000000 --- a/.inline-snapshot/external/dae1b261f19722801adc82a13181772c3010c10b37e8af3996fbdbbecb3c32a2.bin +++ /dev/null @@ -1,22 +0,0 @@ -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_5uxEBMFySqqQGu02I5QHA8k6","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"New"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" York"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - -data: {"id":"chatcmpl-9tXjtfxZZh2FYaFVxXKf2jiqNDiSo","object":"chat.completion.chunk","created":1723024761,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_845eaabc1f","choices":[],"usage":{"prompt_tokens":44,"completion_tokens":16,"total_tokens":60}} - -data: [DONE] - diff --git a/.inline-snapshot/external/e2aad469b71d1d4894ff833ea147020a9d875eb7ce644a0ff355581690a4cbfd.bin b/.inline-snapshot/external/e2aad469b71d1d4894ff833ea147020a9d875eb7ce644a0ff355581690a4cbfd.bin new file mode 100644 index 0000000000..b68ca8a3d9 --- /dev/null +++ b/.inline-snapshot/external/e2aad469b71d1d4894ff833ea147020a9d875eb7ce644a0ff355581690a4cbfd.bin @@ -0,0 +1,68 @@ +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"I'm"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" unable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" provide"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" real"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"-time"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" updates"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" To"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" get"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" current"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" San"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" Francisco"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" recommend"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" checking"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" reliable"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" website"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" weather"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":" app"},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + +data: {"id":"chatcmpl-ABfw031mOJeYCSHe4yI2ZjOA6kMJL","object":"chat.completion.chunk","created":1727346168,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":30,"total_tokens":44,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/.inline-snapshot/external/f82268f2fefd5cfbc7eeb59c297688be2f6ca0849a6e4f17851b517310841d9b.bin b/.inline-snapshot/external/f82268f2fefd5cfbc7eeb59c297688be2f6ca0849a6e4f17851b517310841d9b.bin new file mode 100644 index 0000000000..3b111d5e61 --- /dev/null +++ b/.inline-snapshot/external/f82268f2fefd5cfbc7eeb59c297688be2f6ca0849a6e4f17851b517310841d9b.bin @@ -0,0 +1,52 @@ +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_JMW1whyEaYG438VE1OIflxA2","type":"function","function":{"name":"GetWeatherArgs","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"ci"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ty\": "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Edinb"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"urgh"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\", \"c"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ountry"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"GB\", "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"units"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\": \""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"c\"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_DNYTawLBoN8fj3KN6qU9N1Ou","type":"function","function":{"name":"get_stock_price","arguments":""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"ti"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"cker\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":": \"AAP"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"L\", "}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\"exch"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ange\":"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":" \"NA"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"SDAQ\""}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} + +data: {"id":"chatcmpl-ABfwAwrNePHUgBBezonVC6MX3zd63","object":"chat.completion.chunk","created":1727346178,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_5050236cbd","choices":[],"usage":{"prompt_tokens":149,"completion_tokens":60,"total_tokens":209,"completion_tokens_details":{"reasoning_tokens":0}}} + +data: [DONE] + diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index e7b9c4f1fd..d66630fa3a 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -43,7 +43,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte ], ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXjSozlYq8oGdlRH3vgLsiUNRg8c", "object": "chat.completion", "created": 1723024734, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "I\'m unable to provide real-time weather updates. To find out the current weather in San Francisco, please check a reliable weather website or app.", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 14, "completion_tokens": 28, "total_tokens": 42}, "system_fingerprint": "fp_845eaabc1f"}' + '{"id": "chatcmpl-ABfvaueLEMLNYbT8YzpJxsmiQ6HSY", "object": "chat.completion", "created": 1727346142, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "I\'m unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend checking a reliable weather website or app like the Weather Channel or a local news station.", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 14, "completion_tokens": 37, "total_tokens": 51, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_b40fb1c6fb"}' ), mock_client=client, respx_mock=respx_mock, @@ -58,8 +58,8 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( - content="I'm unable to provide real-time weather updates. To find out the current weather in San -Francisco, please check a reliable weather website or app.", + content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I +recommend checking a reliable weather website or app like the Weather Channel or a local news station.", function_call=None, parsed=None, refusal=None, @@ -68,13 +68,18 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte ) ) ], - created=1723024734, - id='chatcmpl-9tXjSozlYq8oGdlRH3vgLsiUNRg8c', + created=1727346142, + id='chatcmpl-ABfvaueLEMLNYbT8YzpJxsmiQ6HSY', model='gpt-4o-2024-08-06', object='chat.completion', service_tier=None, - system_fingerprint='fp_845eaabc1f', - usage=CompletionUsage(completion_tokens=28, completion_tokens_details=None, prompt_tokens=14, total_tokens=42) + system_fingerprint='fp_b40fb1c6fb', + usage=CompletionUsage( + completion_tokens=37, + completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + prompt_tokens=14, + total_tokens=51 + ) ) """ ) @@ -99,7 +104,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXjTNupyDe7nL1Z8eOO6BdSyrHAD", "object": "chat.completion", "created": 1723024735, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":56,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 14, "total_tokens": 31}, "system_fingerprint": "fp_2a322c9ffc"}' + '{"id": "chatcmpl-ABfvbtVnTu5DeC4EFnRYj8mtfOM99", "object": "chat.completion", "created": 1727346143, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 14, "total_tokens": 93, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), mock_client=client, respx_mock=respx_mock, @@ -114,22 +119,27 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":56,"units":"f"}', + content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=56.0, units='f'), + parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', tool_calls=[] ) ) ], - created=1723024735, - id='chatcmpl-9tXjTNupyDe7nL1Z8eOO6BdSyrHAD', + created=1727346143, + id='chatcmpl-ABfvbtVnTu5DeC4EFnRYj8mtfOM99', model='gpt-4o-2024-08-06', object='chat.completion', service_tier=None, - system_fingerprint='fp_2a322c9ffc', - usage=CompletionUsage(completion_tokens=14, completion_tokens_details=None, prompt_tokens=17, total_tokens=31) + system_fingerprint='fp_5050236cbd', + usage=CompletionUsage( + completion_tokens=14, + completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + prompt_tokens=79, + total_tokens=93 + ) ) """ ) @@ -156,7 +166,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9y39Q2jGzWmeEZlm5CoNVOuQzcxP4", "object": "chat.completion", "created": 1724098820, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":62,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 14, "total_tokens": 31}, "system_fingerprint": "fp_2a322c9ffc"}' + '{"id": "chatcmpl-ABfvcC8grKYsRkSoMp9CCAhbXAd0b", "object": "chat.completion", "created": 1727346144, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 88, "completion_tokens": 14, "total_tokens": 102, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_b40fb1c6fb"}' ), mock_client=client, respx_mock=respx_mock, @@ -171,22 +181,27 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":62,"units":"f"}', + content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=62.0, units='f'), + parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', tool_calls=[] ) ) ], - created=1724098820, - id='chatcmpl-9y39Q2jGzWmeEZlm5CoNVOuQzcxP4', + created=1727346144, + id='chatcmpl-ABfvcC8grKYsRkSoMp9CCAhbXAd0b', model='gpt-4o-2024-08-06', object='chat.completion', service_tier=None, - system_fingerprint='fp_2a322c9ffc', - usage=CompletionUsage(completion_tokens=14, completion_tokens_details=None, prompt_tokens=17, total_tokens=31) + system_fingerprint='fp_b40fb1c6fb', + usage=CompletionUsage( + completion_tokens=14, + completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + prompt_tokens=88, + total_tokens=102 + ) ) """ ) @@ -217,7 +232,7 @@ class ColorDetection(BaseModel): response_format=ColorDetection, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9vK4UZVr385F2UgZlP1ShwPn2nFxG", "object": "chat.completion", "created": 1723448878, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"color\\":\\"red\\",\\"hex_color_code\\":\\"#FF0000\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 18, "completion_tokens": 14, "total_tokens": 32}, "system_fingerprint": "fp_845eaabc1f"}' + '{"id": "chatcmpl-ABfvjIatz0zrZu50gRbMtlp0asZpz", "object": "chat.completion", "created": 1727346151, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"color\\":\\"red\\",\\"hex_color_code\\":\\"#FF0000\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 109, "completion_tokens": 14, "total_tokens": 123, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), mock_client=client, respx_mock=respx_mock, @@ -264,7 +279,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXjUrNFyyjSB2FJ842TMDNRM6Gen", "object": "chat.completion", "created": 1723024736, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":58,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 1, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":58,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 2, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":63,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 42, "total_tokens": 59}, "system_fingerprint": "fp_845eaabc1f"}' + '{"id": "chatcmpl-ABfvp8qzboW92q8ONDF4DPHlI7ckC", "object": "chat.completion", "created": 1727346157, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":64,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 1, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 2, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":63.0,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 44, "total_tokens": 123, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_b40fb1c6fb"}' ), mock_client=client, respx_mock=respx_mock, @@ -278,9 +293,9 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":58,"units":"f"}', + content='{"city":"San Francisco","temperature":64,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=58.0, units='f'), + parsed=Location(city='San Francisco', temperature=64.0, units='f'), refusal=None, role='assistant', tool_calls=[] @@ -291,9 +306,9 @@ class Location(BaseModel): index=1, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":58,"units":"f"}', + content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=58.0, units='f'), + parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', tool_calls=[] @@ -304,7 +319,7 @@ class Location(BaseModel): index=2, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":63,"units":"f"}', + content='{"city":"San Francisco","temperature":63.0,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=63.0, units='f'), refusal=None, @@ -338,7 +353,7 @@ class CalendarEvent: response_format=CalendarEvent, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9wdGqXkJJARAz7rOrLH5u5FBwLjF3", "object": "chat.completion", "created": 1723761008, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"name\\":\\"Science Fair\\",\\"date\\":\\"Friday\\",\\"participants\\":[\\"Alice\\",\\"Bob\\"]}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 32, "completion_tokens": 17, "total_tokens": 49}, "system_fingerprint": "fp_2a322c9ffc"}' + '{"id": "chatcmpl-ABfvqhz4uUUWsw8Ohw2Mp9B4sKKV8", "object": "chat.completion", "created": 1727346158, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"name\\":\\"Science Fair\\",\\"date\\":\\"Friday\\",\\"participants\\":[\\"Alice\\",\\"Bob\\"]}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 92, "completion_tokens": 17, "total_tokens": 109, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_7568d46099"}' ), mock_client=client, respx_mock=respx_mock, @@ -362,13 +377,18 @@ class CalendarEvent: ) ) ], - created=1723761008, - id='chatcmpl-9wdGqXkJJARAz7rOrLH5u5FBwLjF3', + created=1727346158, + id='chatcmpl-ABfvqhz4uUUWsw8Ohw2Mp9B4sKKV8', model='gpt-4o-2024-08-06', object='chat.completion', service_tier=None, - system_fingerprint='fp_2a322c9ffc', - usage=CompletionUsage(completion_tokens=17, completion_tokens_details=None, prompt_tokens=32, total_tokens=49) + system_fingerprint='fp_7568d46099', + usage=CompletionUsage( + completion_tokens=17, + completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + prompt_tokens=92, + total_tokens=109 + ) ) """ ) @@ -389,7 +409,7 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m response_format=Query, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXjVJVCLTn7CWFhpjETixvvApCk3", "object": "chat.completion", "created": 1723024737, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_Un4g0IXeQGOyqKBS3zhqNCox", "type": "function", "function": {"name": "Query", "arguments": "{\\"table_name\\":\\"orders\\",\\"columns\\":[\\"id\\",\\"status\\",\\"expected_delivery_date\\",\\"delivered_at\\",\\"shipped_at\\",\\"ordered_at\\"],\\"conditions\\":[{\\"column\\":\\"ordered_at\\",\\"operator\\":\\">=\\",\\"value\\":\\"2022-05-01\\"},{\\"column\\":\\"ordered_at\\",\\"operator\\":\\"<=\\",\\"value\\":\\"2022-05-31\\"},{\\"column\\":\\"status\\",\\"operator\\":\\"=\\",\\"value\\":\\"fulfilled\\"},{\\"column\\":\\"delivered_at\\",\\"operator\\":\\">\\",\\"value\\":{\\"column_name\\":\\"expected_delivery_date\\"}}],\\"order_by\\":\\"asc\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 195, "completion_tokens": 114, "total_tokens": 309}, "system_fingerprint": "fp_845eaabc1f"}' + '{"id": "chatcmpl-ABfvtNiaTNUF6OymZUnEFc9lPq9p1", "object": "chat.completion", "created": 1727346161, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_NKpApJybW1MzOjZO2FzwYw0d", "type": "function", "function": {"name": "Query", "arguments": "{\\"name\\":\\"May 2022 Fulfilled Orders Not Delivered on Time\\",\\"table_name\\":\\"orders\\",\\"columns\\":[\\"id\\",\\"status\\",\\"expected_delivery_date\\",\\"delivered_at\\",\\"shipped_at\\",\\"ordered_at\\",\\"canceled_at\\"],\\"conditions\\":[{\\"column\\":\\"ordered_at\\",\\"operator\\":\\">=\\",\\"value\\":\\"2022-05-01\\"},{\\"column\\":\\"ordered_at\\",\\"operator\\":\\"<=\\",\\"value\\":\\"2022-05-31\\"},{\\"column\\":\\"status\\",\\"operator\\":\\"=\\",\\"value\\":\\"fulfilled\\"},{\\"column\\":\\"delivered_at\\",\\"operator\\":\\">\\",\\"value\\":{\\"column_name\\":\\"expected_delivery_date\\"}}],\\"order_by\\":\\"asc\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 512, "completion_tokens": 132, "total_tokens": 644, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_7568d46099"}' ), mock_client=client, respx_mock=respx_mock, @@ -410,10 +430,11 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"table_name":"orders","columns":["id","status","expected_delivery_date","delivered_at"," -shipped_at","ordered_at"],"conditions":[{"column":"ordered_at","operator":">=","value":"2022-05-01"},{"column":"ordered_ -at","operator":"<=","value":"2022-05-31"},{"column":"status","operator":"=","value":"fulfilled"},{"column":"delivered_at -","operator":">","value":{"column_name":"expected_delivery_date"}}],"order_by":"asc"}', + arguments='{"name":"May 2022 Fulfilled Orders Not Delivered on +Time","table_name":"orders","columns":["id","status","expected_delivery_date","delivered_at","shipped_at","ordered_at"," +canceled_at"],"conditions":[{"column":"ordered_at","operator":">=","value":"2022-05-01"},{"column":"ordered_at","operato +r":"<=","value":"2022-05-31"},{"column":"status","operator":"=","value":"fulfilled"},{"column":"delivered_at","operator" +:">","value":{"column_name":"expected_delivery_date"}}],"order_by":"asc"}', name='Query', parsed_arguments=Query( columns=[ @@ -422,7 +443,8 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m , , , - + , + ], conditions=[ Condition(column='ordered_at', operator=='>, value='2022-05-01'), @@ -434,12 +456,12 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m value=DynamicValue(column_name='expected_delivery_date') ) ], - name=None, + name='May 2022 Fulfilled Orders Not Delivered on Time', order_by=, table_name= ) ), - id='call_Un4g0IXeQGOyqKBS3zhqNCox', + id='call_NKpApJybW1MzOjZO2FzwYw0d', type='function' ) ] @@ -470,7 +492,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXjYACgVKixKdMv2nVQqDVELkdSF", "object": "chat.completion", "created": 1723024740, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"", "refusal": null}, "logprobs": null, "finish_reason": "length"}], "usage": {"prompt_tokens": 17, "completion_tokens": 1, "total_tokens": 18}, "system_fingerprint": "fp_2a322c9ffc"}' + '{"id": "chatcmpl-ABfvvX7eB1KsfeZj8VcF3z7G7SbaA", "object": "chat.completion", "created": 1727346163, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"", "refusal": null}, "logprobs": null, "finish_reason": "length"}], "usage": {"prompt_tokens": 79, "completion_tokens": 1, "total_tokens": 80, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_7568d46099"}' ), mock_client=client, respx_mock=respx_mock, @@ -496,7 +518,7 @@ class Location(BaseModel): response_format=Location, ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXm7FnIj3hSot5xM4c954MIePle0", "object": "chat.completion", "created": 1723024899, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "refusal": "I\'m very sorry, but I can\'t assist with that request."}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 17, "completion_tokens": 13, "total_tokens": 30}, "system_fingerprint": "fp_845eaabc1f"}' + '{"id": "chatcmpl-ABfvwoKVWPQj2UPlAcAKM7s40GsRx", "object": "chat.completion", "created": 1727346164, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "refusal": "I\'m very sorry, but I can\'t assist with that."}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 12, "total_tokens": 91, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), mock_client=client, respx_mock=respx_mock, @@ -513,7 +535,7 @@ class Location(BaseModel): content=None, function_call=None, parsed=None, - refusal="I'm very sorry, but I can't assist with that request.", + refusal="I'm very sorry, but I can't assist with that.", role='assistant', tool_calls=[] ) @@ -544,7 +566,7 @@ class GetWeatherArgs(BaseModel): ], ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXjbQ9V0l5XPlynOJHKvrWsJQymO", "object": "chat.completion", "created": 1723024743, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_EEaIYq8aTdiDWro8jILNl3XK", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\":\\"Edinburgh\\",\\"country\\":\\"GB\\",\\"units\\":\\"c\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 76, "completion_tokens": 24, "total_tokens": 100}, "system_fingerprint": "fp_2a322c9ffc"}' + '{"id": "chatcmpl-ABfvx6Z4dchiW2nya1N8KMsHFrQRE", "object": "chat.completion", "created": 1727346165, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_Y6qJ7ofLgOrBnMD5WbVAeiRV", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\":\\"Edinburgh\\",\\"country\\":\\"UK\\",\\"units\\":\\"c\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 76, "completion_tokens": 24, "total_tokens": 100, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_e45dabd248"}' ), mock_client=client, respx_mock=respx_mock, @@ -566,11 +588,11 @@ class GetWeatherArgs(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city":"Edinburgh","country":"GB","units":"c"}', + arguments='{"city":"Edinburgh","country":"UK","units":"c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') ), - id='call_EEaIYq8aTdiDWro8jILNl3XK', + id='call_Y6qJ7ofLgOrBnMD5WbVAeiRV', type='function' ) ] @@ -615,7 +637,7 @@ class GetStockPrice(BaseModel): ], ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXjcnIvzZDXRfLfbVTPNL5963GWw", "object": "chat.completion", "created": 1723024744, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_ECSuZ8gcNPPwgt24me91jHsJ", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\": \\"Edinburgh\\", \\"country\\": \\"UK\\", \\"units\\": \\"c\\"}"}}, {"id": "call_Z3fM2sNBBGILhMtimk5Y3RQk", "type": "function", "function": {"name": "get_stock_price", "arguments": "{\\"ticker\\": \\"AAPL\\", \\"exchange\\": \\"NASDAQ\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 149, "completion_tokens": 60, "total_tokens": 209}, "system_fingerprint": "fp_845eaabc1f"}' + '{"id": "chatcmpl-ABfvyvfNWKcl7Ohqos4UFrmMs1v4C", "object": "chat.completion", "created": 1727346166, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_fdNz3vOBKYgOIpMdWotB9MjY", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\": \\"Edinburgh\\", \\"country\\": \\"GB\\", \\"units\\": \\"c\\"}"}}, {"id": "call_h1DWI1POMJLb0KwIyQHWXD4p", "type": "function", "function": {"name": "get_stock_price", "arguments": "{\\"ticker\\": \\"AAPL\\", \\"exchange\\": \\"NASDAQ\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 149, "completion_tokens": 60, "total_tokens": 209, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_b40fb1c6fb"}' ), mock_client=client, respx_mock=respx_mock, @@ -637,11 +659,11 @@ class GetStockPrice(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city": "Edinburgh", "country": "UK", "units": "c"}', + arguments='{"city": "Edinburgh", "country": "GB", "units": "c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') ), - id='call_ECSuZ8gcNPPwgt24me91jHsJ', + id='call_fdNz3vOBKYgOIpMdWotB9MjY', type='function' ), ParsedFunctionToolCall( @@ -650,7 +672,7 @@ class GetStockPrice(BaseModel): name='get_stock_price', parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') ), - id='call_Z3fM2sNBBGILhMtimk5Y3RQk', + id='call_h1DWI1POMJLb0KwIyQHWXD4p', type='function' ) ] @@ -695,7 +717,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: ], ), content_snapshot=snapshot( - '{"id": "chatcmpl-9tXjfjETDIqeYvDjsuGACbwdY0xsr", "object": "chat.completion", "created": 1723024747, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_7ZZPctBXQWexQlIHSrIHMVUq", "type": "function", "function": {"name": "get_weather", "arguments": "{\\"city\\":\\"San Francisco\\",\\"state\\":\\"CA\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 48, "completion_tokens": 19, "total_tokens": 67}, "system_fingerprint": "fp_2a322c9ffc"}' + '{"id": "chatcmpl-ABfvzdvCI6RaIkiEFNjqGXCSYnlzf", "object": "chat.completion", "created": 1727346167, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_CUdUoJpsWWVdxXntucvnol1M", "type": "function", "function": {"name": "get_weather", "arguments": "{\\"city\\":\\"San Francisco\\",\\"state\\":\\"CA\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 48, "completion_tokens": 19, "total_tokens": 67, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), mock_client=client, respx_mock=respx_mock, @@ -721,7 +743,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: name='get_weather', parsed_arguments={'city': 'San Francisco', 'state': 'CA'} ), - id='call_7ZZPctBXQWexQlIHSrIHMVUq', + id='call_CUdUoJpsWWVdxXntucvnol1M', type='function' ) ] diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 5ad1f084d2..ce402cd158 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -48,7 +48,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte }, ], ), - content_snapshot=snapshot(external("038a5c69c34c*.bin")), + content_snapshot=snapshot(external("e2aad469b71d*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -61,9 +61,8 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( - content="I'm unable to provide real-time updates, including current weather information. For the latest -weather in San Francisco, I recommend checking a reliable weather website or app such as the Weather Channel, BBC -Weather, or a local San Francisco news station.", + content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I +recommend checking a reliable weather website or a weather app.", function_call=None, parsed=None, refusal=None, @@ -77,9 +76,8 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot( """\ ContentDoneEvent[NoneType]( - content="I'm unable to provide real-time updates, including current weather information. For the latest weather in -San Francisco, I recommend checking a reliable weather website or app such as the Weather Channel, BBC Weather, or a -local San Francisco news station.", + content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend +checking a reliable weather website or a weather app.", parsed=None, type='content.done' ) @@ -111,7 +109,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream ], response_format=Location, ), - content_snapshot=snapshot(external("15ae68f793c7*.bin")), + content_snapshot=snapshot(external("7e5ea4d12e7c*.bin")), mock_client=client, respx_mock=respx_mock, on_event=on_event, @@ -140,30 +138,35 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":68,"units":"f"}', + content='{"city":"San Francisco","temperature":61,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=68.0, units='f'), + parsed=Location(city='San Francisco', temperature=61.0, units='f'), refusal=None, role='assistant', tool_calls=[] ) ) ], - created=1723024750, - id='chatcmpl-9tXji2y8kKxlOO3muVvfdJ7ECJVlD', + created=1727346169, + id='chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF', model='gpt-4o-2024-08-06', object='chat.completion', service_tier=None, - system_fingerprint='fp_845eaabc1f', - usage=CompletionUsage(completion_tokens=14, completion_tokens_details=None, prompt_tokens=17, total_tokens=31) + system_fingerprint='fp_5050236cbd', + usage=CompletionUsage( + completion_tokens=14, + completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + prompt_tokens=79, + total_tokens=93 + ) ) """ ) assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot( """\ ContentDoneEvent[Location]( - content='{"city":"San Francisco","temperature":68,"units":"f"}', - parsed=Location(city='San Francisco', temperature=68.0, units='f'), + content='{"city":"San Francisco","temperature":61,"units":"f"}', + parsed=Location(city='San Francisco', temperature=61.0, units='f'), type='content.done' ) """ @@ -191,7 +194,7 @@ class Location(BaseModel): n=3, response_format=Location, ), - content_snapshot=snapshot(external("a0c4f0be184e*.bin")), + content_snapshot=snapshot(external("a491adda08c3*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -211,36 +214,22 @@ class Location(BaseModel): "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", @@ -249,10 +238,6 @@ class Location(BaseModel): "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", @@ -261,95 +246,57 @@ class Location(BaseModel): "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", "content.delta", "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", - "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", - "refusal.delta", + "content.delta", "chunk", "content.done", "chunk", "content.done", "chunk", - "refusal.done", + "content.done", "chunk", ] ) @@ -361,9 +308,9 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":63,"units":"f"}', + content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=63.0, units='f'), + parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', tool_calls=[] @@ -374,9 +321,9 @@ class Location(BaseModel): index=1, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content='{"city":"San Francisco","temperature":58.6,"units":"f"}', + content='{"city":"San Francisco","temperature":61,"units":"f"}', function_call=None, - parsed=Location(city='San Francisco', temperature=58.6, units='f'), + parsed=Location(city='San Francisco', temperature=61.0, units='f'), refusal=None, role='assistant', tool_calls=[] @@ -387,11 +334,10 @@ class Location(BaseModel): index=2, logprobs=None, message=ParsedChatCompletionMessage[Location]( - content=None, + content='{"city":"San Francisco","temperature":59,"units":"f"}', function_call=None, - parsed=None, - refusal="I'm sorry, but I can't accurately provide the current weather for San Francisco as my data is up to -October 2023. You can try checking a reliable weather website or app for real-time updates.", + parsed=Location(city='San Francisco', temperature=59.0, units='f'), + refusal=None, role='assistant', tool_calls=[] ) @@ -421,7 +367,7 @@ class Location(BaseModel): max_tokens=1, response_format=Location, ), - content_snapshot=snapshot(external("69363a555f8e*.bin")), + content_snapshot=snapshot(external("4cc50a6135d2*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -445,13 +391,13 @@ class Location(BaseModel): ], response_format=Location, ), - content_snapshot=snapshot(external("ca015b8b1eba*.bin")), + content_snapshot=snapshot(external("173417d55340*.bin")), mock_client=client, respx_mock=respx_mock, ) assert print_obj(listener.get_event_by_type("refusal.done"), monkeypatch) == snapshot("""\ -RefusalDoneEvent(refusal="I'm sorry, but I can't assist with that request.", type='refusal.done') +RefusalDoneEvent(refusal="I'm sorry, I can't assist with that request.", type='refusal.done') """) assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( @@ -465,7 +411,7 @@ class Location(BaseModel): content=None, function_call=None, parsed=None, - refusal="I'm sorry, but I can't assist with that request.", + refusal="I'm sorry, I can't assist with that request.", role='assistant', tool_calls=[] ) @@ -488,7 +434,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp ], logprobs=True, ), - content_snapshot=snapshot(external("be1089999ca5*.bin")), + content_snapshot=snapshot(external("83b060bae42e*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -497,25 +443,25 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp [ LogprobsContentDeltaEvent( content=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0025094282, token='Foo', top_logprobs=[]) ], snapshot=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0025094282, token='Foo', top_logprobs=[]) ], type='logprobs.content.delta' ), LogprobsContentDeltaEvent( - content=[ChatCompletionTokenLogprob(bytes=[46], logprob=-2.4962392, token='.', top_logprobs=[])], + content=[ChatCompletionTokenLogprob(bytes=[33], logprob=-0.26638845, token='!', top_logprobs=[])], snapshot=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]), - ChatCompletionTokenLogprob(bytes=[46], logprob=-2.4962392, token='.', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0025094282, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[33], logprob=-0.26638845, token='!', top_logprobs=[]) ], type='logprobs.content.delta' ), LogprobsContentDoneEvent( content=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]), - ChatCompletionTokenLogprob(bytes=[46], logprob=-2.4962392, token='.', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0025094282, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[33], logprob=-0.26638845, token='!', top_logprobs=[]) ], type='logprobs.content.done' ) @@ -529,13 +475,13 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp index=0, logprobs=ChoiceLogprobs( content=[ - ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0067602484, token='Foo', top_logprobs=[]), - ChatCompletionTokenLogprob(bytes=[46], logprob=-2.4962392, token='.', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[70, 111, 111], logprob=-0.0025094282, token='Foo', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[33], logprob=-0.26638845, token='!', top_logprobs=[]) ], refusal=None ), message=ParsedChatCompletionMessage[NoneType]( - content='Foo.', + content='Foo!', function_call=None, parsed=None, refusal=None, @@ -566,7 +512,7 @@ class Location(BaseModel): logprobs=True, response_format=Location, ), - content_snapshot=snapshot(external("0a00cd46c610*.bin")), + content_snapshot=snapshot(external("569c877e6942*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -583,6 +529,7 @@ class Location(BaseModel): 'logprobs.refusal.delta', 'logprobs.refusal.delta', 'logprobs.refusal.delta', + 'logprobs.refusal.delta', 'logprobs.refusal.done' ] """) @@ -595,53 +542,59 @@ class Location(BaseModel): logprobs=ChoiceLogprobs( content=None, refusal=[ - ChatCompletionTokenLogprob(bytes=[73, 39, 109], logprob=-0.0016157961, token="I'm", top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[73, 39, 109], logprob=-0.0012038043, token="I'm", top_logprobs=[]), + ChatCompletionTokenLogprob( + bytes=[32, 118, 101, 114, 121], + logprob=-0.8438816, + token=' very', + top_logprobs=[] + ), ChatCompletionTokenLogprob( bytes=[32, 115, 111, 114, 114, 121], - logprob=-0.78663874, + logprob=-3.4121115e-06, token=' sorry', top_logprobs=[] ), - ChatCompletionTokenLogprob(bytes=[44], logprob=-7.79144e-05, token=',', top_logprobs=[]), - ChatCompletionTokenLogprob(bytes=[32, 73], logprob=-0.5234622, token=' I', top_logprobs=[]), + ChatCompletionTokenLogprob(bytes=[44], logprob=-3.3809047e-05, token=',', top_logprobs=[]), + ChatCompletionTokenLogprob( + bytes=[32, 98, 117, 116], + logprob=-0.038048144, + token=' but', + top_logprobs=[] + ), + ChatCompletionTokenLogprob(bytes=[32, 73], logprob=-0.0016109125, token=' I', top_logprobs=[]), ChatCompletionTokenLogprob( - bytes=[32, 99, 97, 110, 110, 111, 116], - logprob=-0.52499557, - token=' cannot', + bytes=[32, 99, 97, 110, 39, 116], + logprob=-0.0073532974, + token=" can't", top_logprobs=[] ), ChatCompletionTokenLogprob( bytes=[32, 97, 115, 115, 105, 115, 116], - logprob=-0.015198289, + logprob=-0.0020837625, token=' assist', top_logprobs=[] ), ChatCompletionTokenLogprob( bytes=[32, 119, 105, 116, 104], - logprob=-0.00071648485, + logprob=-0.00318354, token=' with', top_logprobs=[] ), ChatCompletionTokenLogprob( bytes=[32, 116, 104, 97, 116], - logprob=-0.008114983, + logprob=-0.0017186158, token=' that', top_logprobs=[] ), - ChatCompletionTokenLogprob( - bytes=[32, 114, 101, 113, 117, 101, 115, 116], - logprob=-0.0013802331, - token=' request', - top_logprobs=[] - ), - ChatCompletionTokenLogprob(bytes=[46], logprob=-3.4121115e-06, token='.', top_logprobs=[]) + ChatCompletionTokenLogprob(bytes=[46], logprob=-0.57687104, token='.', top_logprobs=[]) ] ), message=ParsedChatCompletionMessage[Location]( content=None, function_call=None, parsed=None, - refusal="I'm sorry, I cannot assist with that request.", + refusal="I'm very sorry, but I can't assist with that.", role='assistant', tool_calls=[] ) @@ -670,7 +623,7 @@ class GetWeatherArgs(BaseModel): openai.pydantic_function_tool(GetWeatherArgs), ], ), - content_snapshot=snapshot(external("24aaf30663f9*.bin")), + content_snapshot=snapshot(external("c6aa7e397b71*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -691,11 +644,11 @@ class GetWeatherArgs(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city":"Edinburgh","country":"GB","units":"c"}', + arguments='{"city":"Edinburgh","country":"UK","units":"c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') ), - id='call_7PhhveOvvpPK53s1fV8TWhoV', + id='call_c91SqDXlYFuETYv8mUHzz6pp', index=0, type='function' ) @@ -722,11 +675,11 @@ class GetWeatherArgs(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city":"Edinburgh","country":"GB","units":"c"}', + arguments='{"city":"Edinburgh","country":"UK","units":"c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') ), - id='call_7PhhveOvvpPK53s1fV8TWhoV', + id='call_c91SqDXlYFuETYv8mUHzz6pp', index=0, type='function' ) @@ -771,7 +724,7 @@ class GetStockPrice(BaseModel): ), ], ), - content_snapshot=snapshot(external("453df473e962*.bin")), + content_snapshot=snapshot(external("f82268f2fefd*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -792,11 +745,11 @@ class GetStockPrice(BaseModel): tool_calls=[ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city": "Edinburgh", "country": "UK", "units": "c"}', + arguments='{"city": "Edinburgh", "country": "GB", "units": "c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') ), - id='call_lQnnsesjFMWMQ5IeWPHzR4th', + id='call_JMW1whyEaYG438VE1OIflxA2', index=0, type='function' ), @@ -806,7 +759,7 @@ class GetStockPrice(BaseModel): name='get_stock_price', parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') ), - id='call_2xjOUgaCdiwAcl9ZBL9LyMUU', + id='call_DNYTawLBoN8fj3KN6qU9N1Ou', index=1, type='function' ) @@ -822,11 +775,11 @@ class GetStockPrice(BaseModel): [ ParsedFunctionToolCall( function=ParsedFunction( - arguments='{"city": "Edinburgh", "country": "UK", "units": "c"}', + arguments='{"city": "Edinburgh", "country": "GB", "units": "c"}', name='GetWeatherArgs', - parsed_arguments=GetWeatherArgs(city='Edinburgh', country='UK', units='c') + parsed_arguments=GetWeatherArgs(city='Edinburgh', country='GB', units='c') ), - id='call_lQnnsesjFMWMQ5IeWPHzR4th', + id='call_JMW1whyEaYG438VE1OIflxA2', index=0, type='function' ), @@ -836,7 +789,7 @@ class GetStockPrice(BaseModel): name='get_stock_price', parsed_arguments=GetStockPrice(exchange='NASDAQ', ticker='AAPL') ), - id='call_2xjOUgaCdiwAcl9ZBL9LyMUU', + id='call_DNYTawLBoN8fj3KN6qU9N1Ou', index=1, type='function' ) @@ -878,7 +831,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: } ], ), - content_snapshot=snapshot(external("83d3d003e6fd*.bin")), + content_snapshot=snapshot(external("a247c49c5fcd*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -903,7 +856,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: name='get_weather', parsed_arguments={'city': 'San Francisco', 'state': 'CA'} ), - id='call_pVHYsU0gmSfX5TqxOyVbB2ma', + id='call_CTf1nWJLqSeRgDqaCG27xZ74', index=0, type='function' ) @@ -928,7 +881,7 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo ], response_format={"type": "json_object"}, ), - content_snapshot=snapshot(external("0898f3d1651e*.bin")), + content_snapshot=snapshot(external("d61558011839*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -941,10 +894,12 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( - content='\\n {\\n "location": "San Francisco, CA",\\n "forecast_date": "2023-11-02",\\n "weather": {\\n -"temperature": {\\n "current": "N/A",\\n "high": "N/A",\\n "low": "N/A"\\n },\\n "condition": -"N/A",\\n "humidity": "N/A",\\n "wind_speed": "N/A"\\n },\\n "note": "Please check a reliable weather -service for the most current information."\\n }', + content='\\n {\\n "location": "San Francisco, CA",\\n "weather": {\\n "temperature": "18°C",\\n +"condition": "Partly Cloudy",\\n "humidity": "72%",\\n "windSpeed": "15 km/h",\\n "windDirection": "NW"\\n +},\\n "forecast": [\\n {\\n "day": "Monday",\\n "high": "20°C",\\n "low": "14°C",\\n +"condition": "Sunny"\\n },\\n {\\n "day": "Tuesday",\\n "high": "19°C",\\n "low": "15°C",\\n +"condition": "Mostly Cloudy"\\n },\\n {\\n "day": "Wednesday",\\n "high": "18°C",\\n "low": +"14°C",\\n "condition": "Cloudy"\\n }\\n ]\\n }\\n', function_call=None, parsed=None, refusal=None, @@ -975,7 +930,7 @@ def test_allows_non_strict_tools_but_no_parsing( } ], ), - content_snapshot=snapshot(external("dae1b261f197*.bin")), + content_snapshot=snapshot(external("2018feb66ae1*.bin")), mock_client=client, respx_mock=respx_mock, ) @@ -1010,7 +965,7 @@ def test_allows_non_strict_tools_but_no_parsing( name='get_weather', parsed_arguments=None ), - id='call_5uxEBMFySqqQGu02I5QHA8k6', + id='call_4XzlGBLtUe9dy3GVNV4jhq7h', index=0, type='function' ) From 56e64d9a17024f8ffcaee7172d4d6c6e1614f60c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:25:28 +0000 Subject: [PATCH 616/914] feat(api): add omni-moderation model (#1750) --- .stats.yml | 2 +- api.md | 9 ++- src/openai/resources/moderations.py | 49 +++++++------ src/openai/types/__init__.py | 3 + src/openai/types/moderation.py | 70 ++++++++++++++++++- src/openai/types/moderation_create_params.py | 26 +++---- .../types/moderation_image_url_input_param.py | 20 ++++++ src/openai/types/moderation_model.py | 4 +- .../moderation_multi_modal_input_param.py | 13 ++++ .../types/moderation_text_input_param.py | 15 ++++ tests/api_resources/test_moderations.py | 4 +- 11 files changed, 172 insertions(+), 43 deletions(-) create mode 100644 src/openai/types/moderation_image_url_input_param.py create mode 100644 src/openai/types/moderation_multi_modal_input_param.py create mode 100644 src/openai/types/moderation_text_input_param.py diff --git a/.stats.yml b/.stats.yml index e8bca3c6d8..0998368a4c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-073331021d48db6af646a3552ab0c682efe31b7fb4e59a109ed1ba539f9b89c5.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-17ddd746c775ca4d4fbe64e5621ac30756ef09c061ff6313190b6ec162222d4c.yml diff --git a/api.md b/api.md index eae17d8d84..29db54f0fd 100644 --- a/api.md +++ b/api.md @@ -158,7 +158,14 @@ Methods: Types: ```python -from openai.types import Moderation, ModerationModel, ModerationCreateResponse +from openai.types import ( + Moderation, + ModerationImageURLInput, + ModerationModel, + ModerationMultiModalInput, + ModerationTextInput, + ModerationCreateResponse, +) ``` Methods: diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 5283554373..8b73da57b2 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union +from typing import List, Union, Iterable import httpx @@ -19,6 +19,7 @@ from .._base_client import make_request_options from ..types.moderation_model import ModerationModel from ..types.moderation_create_response import ModerationCreateResponse +from ..types.moderation_multi_modal_input_param import ModerationMultiModalInputParam __all__ = ["Moderations", "AsyncModerations"] @@ -46,7 +47,7 @@ def with_streaming_response(self) -> ModerationsWithStreamingResponse: def create( self, *, - input: Union[str, List[str]], + input: Union[str, List[str], Iterable[ModerationMultiModalInputParam]], model: Union[str, ModerationModel] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -55,20 +56,19 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ModerationCreateResponse: - """ - Classifies if text is potentially harmful. + """Classifies if text and/or image inputs are potentially harmful. - Args: - input: The input text to classify + Learn more in + the [moderation guide](https://platform.openai.com/docs/guides/moderation). - model: Two content moderations models are available: `text-moderation-stable` and - `text-moderation-latest`. + Args: + input: Input (or inputs) to classify. Can be a single string, an array of strings, or + an array of multi-modal input objects similar to other models. - The default is `text-moderation-latest` which will be automatically upgraded - over time. This ensures you are always using our most accurate model. If you use - `text-moderation-stable`, we will provide advanced notice before updating the - model. Accuracy of `text-moderation-stable` may be slightly lower than for - `text-moderation-latest`. + model: The content moderation model you would like to use. Learn more in + [the moderation guide](https://platform.openai.com/docs/guides/moderation), and + learn about available models + [here](https://platform.openai.com/docs/models/moderation). extra_headers: Send extra headers @@ -117,7 +117,7 @@ def with_streaming_response(self) -> AsyncModerationsWithStreamingResponse: async def create( self, *, - input: Union[str, List[str]], + input: Union[str, List[str], Iterable[ModerationMultiModalInputParam]], model: Union[str, ModerationModel] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -126,20 +126,19 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ModerationCreateResponse: - """ - Classifies if text is potentially harmful. + """Classifies if text and/or image inputs are potentially harmful. - Args: - input: The input text to classify + Learn more in + the [moderation guide](https://platform.openai.com/docs/guides/moderation). - model: Two content moderations models are available: `text-moderation-stable` and - `text-moderation-latest`. + Args: + input: Input (or inputs) to classify. Can be a single string, an array of strings, or + an array of multi-modal input objects similar to other models. - The default is `text-moderation-latest` which will be automatically upgraded - over time. This ensures you are always using our most accurate model. If you use - `text-moderation-stable`, we will provide advanced notice before updating the - model. Accuracy of `text-moderation-stable` may be slightly lower than for - `text-moderation-latest`. + model: The content moderation model you would like to use. Learn more in + [the moderation guide](https://platform.openai.com/docs/guides/moderation), and + learn about available models + [here](https://platform.openai.com/docs/models/moderation). extra_headers: Send extra headers diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 6223be883d..7677be01b2 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -46,4 +46,7 @@ from .moderation_create_params import ModerationCreateParams as ModerationCreateParams from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse +from .moderation_text_input_param import ModerationTextInputParam as ModerationTextInputParam from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams +from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam +from .moderation_multi_modal_input_param import ModerationMultiModalInputParam as ModerationMultiModalInputParam diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index 5aa691823a..e4ec182ce2 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -1,11 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List +from typing_extensions import Literal from pydantic import Field as FieldInfo from .._models import BaseModel -__all__ = ["Moderation", "Categories", "CategoryScores"] +__all__ = ["Moderation", "Categories", "CategoryAppliedInputTypes", "CategoryScores"] class Categories(BaseModel): @@ -36,6 +38,20 @@ class Categories(BaseModel): orientation, disability status, or caste. """ + illicit: bool + """ + Content that includes instructions or advice that facilitate the planning or + execution of wrongdoing, or that gives advice or instruction on how to commit + illicit acts. For example, "how to shoplift" would fit this category. + """ + + illicit_violent: bool = FieldInfo(alias="illicit/violent") + """ + Content that includes instructions or advice that facilitate the planning or + execution of wrongdoing that also includes violence, or that gives advice or + instruction on the procurement of any weapon. + """ + self_harm: bool = FieldInfo(alias="self-harm") """ Content that promotes, encourages, or depicts acts of self-harm, such as @@ -72,6 +88,47 @@ class Categories(BaseModel): """Content that depicts death, violence, or physical injury in graphic detail.""" +class CategoryAppliedInputTypes(BaseModel): + harassment: List[Literal["text"]] + """The applied input type(s) for the category 'harassment'.""" + + harassment_threatening: List[Literal["text"]] = FieldInfo(alias="harassment/threatening") + """The applied input type(s) for the category 'harassment/threatening'.""" + + hate: List[Literal["text"]] + """The applied input type(s) for the category 'hate'.""" + + hate_threatening: List[Literal["text"]] = FieldInfo(alias="hate/threatening") + """The applied input type(s) for the category 'hate/threatening'.""" + + illicit: List[Literal["text"]] + """The applied input type(s) for the category 'illicit'.""" + + illicit_violent: List[Literal["text"]] = FieldInfo(alias="illicit/violent") + """The applied input type(s) for the category 'illicit/violent'.""" + + self_harm: List[Literal["text", "image"]] = FieldInfo(alias="self-harm") + """The applied input type(s) for the category 'self-harm'.""" + + self_harm_instructions: List[Literal["text", "image"]] = FieldInfo(alias="self-harm/instructions") + """The applied input type(s) for the category 'self-harm/instructions'.""" + + self_harm_intent: List[Literal["text", "image"]] = FieldInfo(alias="self-harm/intent") + """The applied input type(s) for the category 'self-harm/intent'.""" + + sexual: List[Literal["text", "image"]] + """The applied input type(s) for the category 'sexual'.""" + + sexual_minors: List[Literal["text"]] = FieldInfo(alias="sexual/minors") + """The applied input type(s) for the category 'sexual/minors'.""" + + violence: List[Literal["text", "image"]] + """The applied input type(s) for the category 'violence'.""" + + violence_graphic: List[Literal["text", "image"]] = FieldInfo(alias="violence/graphic") + """The applied input type(s) for the category 'violence/graphic'.""" + + class CategoryScores(BaseModel): harassment: float """The score for the category 'harassment'.""" @@ -85,6 +142,12 @@ class CategoryScores(BaseModel): hate_threatening: float = FieldInfo(alias="hate/threatening") """The score for the category 'hate/threatening'.""" + illicit: float + """The score for the category 'illicit'.""" + + illicit_violent: float = FieldInfo(alias="illicit/violent") + """The score for the category 'illicit/violent'.""" + self_harm: float = FieldInfo(alias="self-harm") """The score for the category 'self-harm'.""" @@ -111,6 +174,11 @@ class Moderation(BaseModel): categories: Categories """A list of the categories, and whether they are flagged or not.""" + category_applied_input_types: CategoryAppliedInputTypes + """ + A list of the categories along with the input type(s) that the score applies to. + """ + category_scores: CategoryScores """A list of the categories along with their scores as predicted by model.""" diff --git a/src/openai/types/moderation_create_params.py b/src/openai/types/moderation_create_params.py index 337682194d..3193fd9c2d 100644 --- a/src/openai/types/moderation_create_params.py +++ b/src/openai/types/moderation_create_params.py @@ -2,26 +2,28 @@ from __future__ import annotations -from typing import List, Union +from typing import List, Union, Iterable from typing_extensions import Required, TypedDict from .moderation_model import ModerationModel +from .moderation_multi_modal_input_param import ModerationMultiModalInputParam __all__ = ["ModerationCreateParams"] class ModerationCreateParams(TypedDict, total=False): - input: Required[Union[str, List[str]]] - """The input text to classify""" + input: Required[Union[str, List[str], Iterable[ModerationMultiModalInputParam]]] + """Input (or inputs) to classify. - model: Union[str, ModerationModel] + Can be a single string, an array of strings, or an array of multi-modal input + objects similar to other models. """ - Two content moderations models are available: `text-moderation-stable` and - `text-moderation-latest`. - - The default is `text-moderation-latest` which will be automatically upgraded - over time. This ensures you are always using our most accurate model. If you use - `text-moderation-stable`, we will provide advanced notice before updating the - model. Accuracy of `text-moderation-stable` may be slightly lower than for - `text-moderation-latest`. + + model: Union[str, ModerationModel] + """The content moderation model you would like to use. + + Learn more in + [the moderation guide](https://platform.openai.com/docs/guides/moderation), and + learn about available models + [here](https://platform.openai.com/docs/models/moderation). """ diff --git a/src/openai/types/moderation_image_url_input_param.py b/src/openai/types/moderation_image_url_input_param.py new file mode 100644 index 0000000000..9a69a6a257 --- /dev/null +++ b/src/openai/types/moderation_image_url_input_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ModerationImageURLInputParam", "ImageURL"] + + +class ImageURL(TypedDict, total=False): + url: Required[str] + """Either a URL of the image or the base64 encoded image data.""" + + +class ModerationImageURLInputParam(TypedDict, total=False): + image_url: Required[ImageURL] + """Contains either an image URL or a data URL for a base64 encoded image.""" + + type: Required[Literal["image_url"]] + """Always `image_url`.""" diff --git a/src/openai/types/moderation_model.py b/src/openai/types/moderation_model.py index f549aeeb7a..64954c4547 100644 --- a/src/openai/types/moderation_model.py +++ b/src/openai/types/moderation_model.py @@ -4,4 +4,6 @@ __all__ = ["ModerationModel"] -ModerationModel: TypeAlias = Literal["text-moderation-latest", "text-moderation-stable"] +ModerationModel: TypeAlias = Literal[ + "omni-moderation-latest", "omni-moderation-2024-09-26", "text-moderation-latest", "text-moderation-stable" +] diff --git a/src/openai/types/moderation_multi_modal_input_param.py b/src/openai/types/moderation_multi_modal_input_param.py new file mode 100644 index 0000000000..4314e7b031 --- /dev/null +++ b/src/openai/types/moderation_multi_modal_input_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .moderation_text_input_param import ModerationTextInputParam +from .moderation_image_url_input_param import ModerationImageURLInputParam + +__all__ = ["ModerationMultiModalInputParam"] + +ModerationMultiModalInputParam: TypeAlias = Union[ModerationImageURLInputParam, ModerationTextInputParam] diff --git a/src/openai/types/moderation_text_input_param.py b/src/openai/types/moderation_text_input_param.py new file mode 100644 index 0000000000..e5da53337b --- /dev/null +++ b/src/openai/types/moderation_text_input_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ModerationTextInputParam"] + + +class ModerationTextInputParam(TypedDict, total=False): + text: Required[str] + """A string of text to classify.""" + + type: Required[Literal["text"]] + """Always `text`.""" diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index 94b9ecd31b..bbdeb63e49 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -28,7 +28,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: moderation = client.moderations.create( input="I want to kill them.", - model="text-moderation-stable", + model="omni-moderation-2024-09-26", ) assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) @@ -71,7 +71,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: moderation = await async_client.moderations.create( input="I want to kill them.", - model="text-moderation-stable", + model="omni-moderation-2024-09-26", ) assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) From 70edb2134913569677b40ed514a3520e31bd9f25 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:26:01 +0000 Subject: [PATCH 617/914] release: 1.49.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3f47f4bc2b..5153ca9d4c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.48.0" + ".": "1.49.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d89151074b..4a8156c611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.49.0 (2024-09-26) + +Full Changelog: [v1.48.0...v1.49.0](https://github.com/openai/openai-python/compare/v1.48.0...v1.49.0) + +### Features + +* **api:** add omni-moderation model ([#1750](https://github.com/openai/openai-python/issues/1750)) ([05b50da](https://github.com/openai/openai-python/commit/05b50da5428d5c7b5ea09626bcd88f8423762bf8)) + + +### Chores + +* **internal:** update test snapshots ([#1749](https://github.com/openai/openai-python/issues/1749)) ([42f054e](https://github.com/openai/openai-python/commit/42f054ee7afa8ce8316c2ecd90608a0f7e13bfdd)) + ## 1.48.0 (2024-09-25) Full Changelog: [v1.47.1...v1.48.0](https://github.com/openai/openai-python/compare/v1.47.1...v1.48.0) diff --git a/pyproject.toml b/pyproject.toml index 05f957cac0..aa4c7c1d76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.48.0" +version = "1.49.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 66109f354b..32d54fb9d0 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.48.0" # x-release-please-version +__version__ = "1.49.0" # x-release-please-version From e1aeeb0ee62965687c8d0dd00e9e39e54be9498c Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 26 Sep 2024 23:12:51 +0100 Subject: [PATCH 618/914] chore(pydantic v1): exclude specific properties when rich printing (#1751) --- src/openai/_models.py | 10 +++++++++- tests/lib/chat/_utils.py | 11 +++-------- tests/test_legacy_response.py | 4 ++++ tests/test_response.py | 4 ++++ tests/utils.py | 13 +++++++++++++ 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index d6f42d3d4d..710401defd 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -2,7 +2,7 @@ import os import inspect -from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast +from typing import TYPE_CHECKING, Any, Type, Tuple, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( Unpack, @@ -10,6 +10,7 @@ ClassVar, Protocol, Required, + Sequence, ParamSpec, TypedDict, TypeGuard, @@ -72,6 +73,8 @@ P = ParamSpec("P") +ReprArgs = Sequence[Tuple[Optional[str], Any]] + @runtime_checkable class _ConfigProtocol(Protocol): @@ -94,6 +97,11 @@ def model_fields_set(self) -> set[str]: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] extra: Any = pydantic.Extra.allow # type: ignore + @override + def __repr_args__(self) -> ReprArgs: + # we don't want these attributes to be included when something like `rich.print` is used + return [arg for arg in super().__repr_args__() if arg[0] not in {"_request_id", "__exclude_fields__"}] + if TYPE_CHECKING: _request_id: Optional[str] = None """The ID of the request, returned via the X-Request-ID header. Useful for debugging requests and reporting issues to OpenAI. diff --git a/tests/lib/chat/_utils.py b/tests/lib/chat/_utils.py index dcc32b17fd..af08db417c 100644 --- a/tests/lib/chat/_utils.py +++ b/tests/lib/chat/_utils.py @@ -1,14 +1,14 @@ from __future__ import annotations -import io import inspect from typing import Any, Iterable from typing_extensions import TypeAlias -import rich import pytest import pydantic +from ...utils import rich_print_str + ReprArgs: TypeAlias = "Iterable[tuple[str | None, Any]]" @@ -26,12 +26,7 @@ def __repr_args__(self: pydantic.BaseModel) -> ReprArgs: with monkeypatch.context() as m: m.setattr(pydantic.BaseModel, "__repr_args__", __repr_args__) - buf = io.StringIO() - - console = rich.console.Console(file=buf, width=120) - console.print(obj) - - string = buf.getvalue() + string = rich_print_str(obj) # we remove all `fn_name..` occurences # so that we can share the same snapshots between diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py index a6fec9f2de..f50a77c24d 100644 --- a/tests/test_legacy_response.py +++ b/tests/test_legacy_response.py @@ -11,6 +11,8 @@ from openai._base_client import FinalRequestOptions from openai._legacy_response import LegacyAPIResponse +from .utils import rich_print_str + class PydanticModel(pydantic.BaseModel): ... @@ -85,6 +87,8 @@ def test_response_basemodel_request_id(client: OpenAI) -> None: assert obj.foo == "hello!" assert obj.bar == 2 assert obj.to_dict() == {"foo": "hello!", "bar": 2} + assert "_request_id" not in rich_print_str(obj) + assert "__exclude_fields__" not in rich_print_str(obj) def test_response_parse_annotated_type(client: OpenAI) -> None: diff --git a/tests/test_response.py b/tests/test_response.py index 97c56e0035..e1fe332f2f 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -18,6 +18,8 @@ from openai._streaming import Stream from openai._base_client import FinalRequestOptions +from .utils import rich_print_str + class ConcreteBaseAPIResponse(APIResponse[bytes]): ... @@ -175,6 +177,8 @@ def test_response_basemodel_request_id(client: OpenAI) -> None: assert obj.foo == "hello!" assert obj.bar == 2 assert obj.to_dict() == {"foo": "hello!", "bar": 2} + assert "_request_id" not in rich_print_str(obj) + assert "__exclude_fields__" not in rich_print_str(obj) @pytest.mark.asyncio diff --git a/tests/utils.py b/tests/utils.py index 165f4e5bfd..8d5397f28e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,5 +1,6 @@ from __future__ import annotations +import io import os import inspect import traceback @@ -8,6 +9,8 @@ from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type +import rich + from openai._types import Omit, NoneType from openai._utils import ( is_dict, @@ -138,6 +141,16 @@ def _assert_list_type(type_: type[object], value: object) -> None: assert_type(inner_type, entry) # type: ignore +def rich_print_str(obj: object) -> str: + """Like `rich.print()` but returns the string instead""" + buf = io.StringIO() + + console = rich.console.Console(file=buf, width=120) + console.print(obj) + + return buf.getvalue() + + @contextlib.contextmanager def update_env(**new_env: str | Omit) -> Iterator[None]: old = os.environ.copy() From 152a8bda1b1db21377496b36ff26ab127a27b221 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 26 Sep 2024 23:32:48 +0100 Subject: [PATCH 619/914] feat(structured outputs): add support for accessing raw responses (#1748) --- src/openai/resources/beta/chat/completions.py | 246 +++++++++++++----- tests/lib/chat/test_completions.py | 177 ++++++++++++- 2 files changed, 355 insertions(+), 68 deletions(-) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index ea3526778d..da6f189929 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -2,16 +2,21 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable, Optional +from typing import Dict, List, Type, Union, Iterable, Optional, cast from functools import partial from typing_extensions import Literal import httpx +from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...._streaming import Stream from ....types.chat import completion_create_params +from ...._base_client import make_request_options from ....lib._parsing import ( ResponseFormatT, validate_input_tools as _validate_input_tools, @@ -20,6 +25,7 @@ ) from ....types.chat_model import ChatModel from ....lib.streaming.chat import ChatCompletionStreamManager, AsyncChatCompletionStreamManager +from ....types.chat.chat_completion import ChatCompletion from ....types.chat.chat_completion_chunk import ChatCompletionChunk from ....types.chat.parsed_chat_completion import ParsedChatCompletion from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam @@ -31,6 +37,25 @@ class Completions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CompletionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CompletionsWithStreamingResponse(self) + def parse( self, *, @@ -113,39 +138,55 @@ class MathResponse(BaseModel): **(extra_headers or {}), } - raw_completion = self._client.chat.completions.create( - messages=messages, - model=model, - response_format=_type_to_response_format(response_format), - frequency_penalty=frequency_penalty, - function_call=function_call, - functions=functions, - logit_bias=logit_bias, - logprobs=logprobs, - max_completion_tokens=max_completion_tokens, - max_tokens=max_tokens, - n=n, - parallel_tool_calls=parallel_tool_calls, - presence_penalty=presence_penalty, - seed=seed, - service_tier=service_tier, - stop=stop, - stream_options=stream_options, - temperature=temperature, - tool_choice=tool_choice, - tools=tools, - top_logprobs=top_logprobs, - top_p=top_p, - user=user, - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ) - return _parse_chat_completion( - response_format=response_format, - chat_completion=raw_completion, - input_tools=tools, + def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseFormatT]: + return _parse_chat_completion( + response_format=response_format, + chat_completion=raw_completion, + input_tools=tools, + ) + + return self._post( + "/chat/completions", + body=maybe_transform( + { + "messages": messages, + "model": model, + "frequency_penalty": frequency_penalty, + "function_call": function_call, + "functions": functions, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "n": n, + "parallel_tool_calls": parallel_tool_calls, + "presence_penalty": presence_penalty, + "response_format": _type_to_response_format(response_format), + "seed": seed, + "service_tier": service_tier, + "stop": stop, + "stream": False, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + ), + # we turn the `ChatCompletion` instance into a `ParsedChatCompletion` + # in the `parser` function above + cast_to=cast(Type[ParsedChatCompletion[ResponseFormatT]], ChatCompletion), + stream=False, ) def stream( @@ -247,6 +288,25 @@ def stream( class AsyncCompletions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCompletionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCompletionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCompletionsWithStreamingResponse(self) + async def parse( self, *, @@ -329,39 +389,55 @@ class MathResponse(BaseModel): **(extra_headers or {}), } - raw_completion = await self._client.chat.completions.create( - messages=messages, - model=model, - response_format=_type_to_response_format(response_format), - frequency_penalty=frequency_penalty, - function_call=function_call, - functions=functions, - logit_bias=logit_bias, - logprobs=logprobs, - max_completion_tokens=max_completion_tokens, - max_tokens=max_tokens, - n=n, - parallel_tool_calls=parallel_tool_calls, - presence_penalty=presence_penalty, - seed=seed, - service_tier=service_tier, - stop=stop, - stream_options=stream_options, - temperature=temperature, - tool_choice=tool_choice, - tools=tools, - top_logprobs=top_logprobs, - top_p=top_p, - user=user, - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ) - return _parse_chat_completion( - response_format=response_format, - chat_completion=raw_completion, - input_tools=tools, + def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseFormatT]: + return _parse_chat_completion( + response_format=response_format, + chat_completion=raw_completion, + input_tools=tools, + ) + + return await self._post( + "/chat/completions", + body=await async_maybe_transform( + { + "messages": messages, + "model": model, + "frequency_penalty": frequency_penalty, + "function_call": function_call, + "functions": functions, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_completion_tokens": max_completion_tokens, + "max_tokens": max_tokens, + "n": n, + "parallel_tool_calls": parallel_tool_calls, + "presence_penalty": presence_penalty, + "response_format": _type_to_response_format(response_format), + "seed": seed, + "service_tier": service_tier, + "stop": stop, + "stream": False, + "stream_options": stream_options, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "user": user, + }, + completion_create_params.CompletionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + ), + # we turn the `ChatCompletion` instance into a `ParsedChatCompletion` + # in the `parser` function above + cast_to=cast(Type[ParsedChatCompletion[ResponseFormatT]], ChatCompletion), + stream=False, ) def stream( @@ -461,3 +537,39 @@ def stream( response_format=response_format, input_tools=tools, ) + + +class CompletionsWithRawResponse: + def __init__(self, completions: Completions) -> None: + self._completions = completions + + self.parse = _legacy_response.to_raw_response_wrapper( + completions.parse, + ) + + +class AsyncCompletionsWithRawResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + + self.parse = _legacy_response.async_to_raw_response_wrapper( + completions.parse, + ) + + +class CompletionsWithStreamingResponse: + def __init__(self, completions: Completions) -> None: + self._completions = completions + + self.parse = to_streamed_response_wrapper( + completions.parse, + ) + + +class AsyncCompletionsWithStreamingResponse: + def __init__(self, completions: AsyncCompletions) -> None: + self._completions = completions + + self.parse = async_to_streamed_response_wrapper( + completions.parse, + ) diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index d66630fa3a..7702a98d49 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -3,7 +3,7 @@ import os import json from enum import Enum -from typing import Any, List, Callable, Optional +from typing import Any, List, Callable, Optional, Awaitable from typing_extensions import Literal, TypeVar import httpx @@ -773,6 +773,139 @@ def test_parse_non_strict_tools(client: OpenAI) -> None: ) +@pytest.mark.respx(base_url=base_url) +def test_parse_pydantic_raw_response(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + response = _make_snapshot_request( + lambda c: c.beta.chat.completions.with_raw_response.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + response_format=Location, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-ABrDYCa8W1w66eUxKDO8TQF1m6trT", "object": "chat.completion", "created": 1727389540, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":58,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 14, "total_tokens": 93, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' + ), + mock_client=client, + respx_mock=respx_mock, + ) + assert response.http_request.headers.get("x-stainless-helper-method") == "beta.chat.completions.parse" + + completion = response.parse() + message = completion.choices[0].message + assert message.parsed is not None + assert isinstance(message.parsed.city, str) + assert print_obj(completion, monkeypatch) == snapshot( + """\ +ParsedChatCompletion[Location]( + choices=[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":58,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=58.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ) + ], + created=1727389540, + id='chatcmpl-ABrDYCa8W1w66eUxKDO8TQF1m6trT', + model='gpt-4o-2024-08-06', + object='chat.completion', + service_tier=None, + system_fingerprint='fp_5050236cbd', + usage=CompletionUsage( + completion_tokens=14, + completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + prompt_tokens=79, + total_tokens=93 + ) +) +""" + ) + + +@pytest.mark.respx(base_url=base_url) +@pytest.mark.asyncio +async def test_async_parse_pydantic_raw_response( + async_client: AsyncOpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch +) -> None: + class Location(BaseModel): + city: str + temperature: float + units: Literal["c", "f"] + + response = await _make_async_snapshot_request( + lambda c: c.beta.chat.completions.with_raw_response.parse( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + response_format=Location, + ), + content_snapshot=snapshot( + '{"id": "chatcmpl-ABrDQWOiw0PK5JOsxl1D9ooeQgznq", "object": "chat.completion", "created": 1727389532, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 14, "total_tokens": 93, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' + ), + mock_client=async_client, + respx_mock=respx_mock, + ) + assert response.http_request.headers.get("x-stainless-helper-method") == "beta.chat.completions.parse" + + completion = response.parse() + message = completion.choices[0].message + assert message.parsed is not None + assert isinstance(message.parsed.city, str) + assert print_obj(completion, monkeypatch) == snapshot( + """\ +ParsedChatCompletion[Location]( + choices=[ + ParsedChoice[Location]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[Location]( + content='{"city":"San Francisco","temperature":65,"units":"f"}', + function_call=None, + parsed=Location(city='San Francisco', temperature=65.0, units='f'), + refusal=None, + role='assistant', + tool_calls=[] + ) + ) + ], + created=1727389532, + id='chatcmpl-ABrDQWOiw0PK5JOsxl1D9ooeQgznq', + model='gpt-4o-2024-08-06', + object='chat.completion', + service_tier=None, + system_fingerprint='fp_5050236cbd', + usage=CompletionUsage( + completion_tokens=14, + completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + prompt_tokens=79, + total_tokens=93 + ) +) +""" + ) + + @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_parse_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: checking_client: OpenAI | AsyncOpenAI = client if sync else async_client @@ -824,3 +957,45 @@ def _on_response(response: httpx.Response) -> None: client.close() return result + + +async def _make_async_snapshot_request( + func: Callable[[AsyncOpenAI], Awaitable[_T]], + *, + content_snapshot: Any, + respx_mock: MockRouter, + mock_client: AsyncOpenAI, +) -> _T: + live = os.environ.get("OPENAI_LIVE") == "1" + if live: + + async def _on_response(response: httpx.Response) -> None: + # update the content snapshot + assert json.dumps(json.loads(await response.aread())) == content_snapshot + + respx_mock.stop() + + client = AsyncOpenAI( + http_client=httpx.AsyncClient( + event_hooks={ + "response": [_on_response], + } + ) + ) + else: + respx_mock.post("/chat/completions").mock( + return_value=httpx.Response( + 200, + content=content_snapshot._old_value, + headers={"content-type": "application/json"}, + ) + ) + + client = mock_client + + result = await func(client) + + if live: + await client.close() + + return result From 37f5615da1f4360710f6f45920dbb81387d1a4c5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 22:33:11 +0000 Subject: [PATCH 620/914] release: 1.50.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5153ca9d4c..97e4d742db 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.49.0" + ".": "1.50.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a8156c611..20be5b530b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.50.0 (2024-09-26) + +Full Changelog: [v1.49.0...v1.50.0](https://github.com/openai/openai-python/compare/v1.49.0...v1.50.0) + +### Features + +* **structured outputs:** add support for accessing raw responses ([#1748](https://github.com/openai/openai-python/issues/1748)) ([0189e28](https://github.com/openai/openai-python/commit/0189e28b0b062a28b16343da0460a4f0f4e17a9a)) + + +### Chores + +* **pydantic v1:** exclude specific properties when rich printing ([#1751](https://github.com/openai/openai-python/issues/1751)) ([af535ce](https://github.com/openai/openai-python/commit/af535ce6a523eca39438f117a3e55f16064567a9)) + ## 1.49.0 (2024-09-26) Full Changelog: [v1.48.0...v1.49.0](https://github.com/openai/openai-python/compare/v1.48.0...v1.49.0) diff --git a/pyproject.toml b/pyproject.toml index aa4c7c1d76..f7061c5059 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.49.0" +version = "1.50.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 32d54fb9d0..e05b612a12 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.49.0" # x-release-please-version +__version__ = "1.50.0" # x-release-please-version From 73fc3b9ce2cc94959bd4efde8cdd4896e3953dee Mon Sep 17 00:00:00 2001 From: Sam El-Borai Date: Fri, 27 Sep 2024 14:20:58 +0200 Subject: [PATCH 621/914] docs(helpers): fix chat completion anchor (#1753) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 965dd6e23c..65515df973 100644 --- a/helpers.md +++ b/helpers.md @@ -128,7 +128,7 @@ The `beta.chat.completions.parse()` method imposes some additional restrictions # Streaming Helpers -OpenAI supports streaming responses when interacting with the [Chat Completion] & [Assistant](#assistant-streaming-api) APIs. +OpenAI supports streaming responses when interacting with the [Chat Completion](#chat-completions-api) & [Assistant](#assistant-streaming-api) APIs. ## Chat Completions API From 7a5632f6c9a64ce77083494ea7e2385e200373cc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 12:21:24 +0000 Subject: [PATCH 622/914] release: 1.50.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 97e4d742db..43515db369 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.50.0" + ".": "1.50.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 20be5b530b..c51de0481b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.50.1 (2024-09-27) + +Full Changelog: [v1.50.0...v1.50.1](https://github.com/openai/openai-python/compare/v1.50.0...v1.50.1) + +### Documentation + +* **helpers:** fix chat completion anchor ([#1753](https://github.com/openai/openai-python/issues/1753)) ([956d4e8](https://github.com/openai/openai-python/commit/956d4e8e32507fbce399f4619e06daa9d37a0532)) + ## 1.50.0 (2024-09-26) Full Changelog: [v1.49.0...v1.50.0](https://github.com/openai/openai-python/compare/v1.49.0...v1.50.0) diff --git a/pyproject.toml b/pyproject.toml index f7061c5059..7ebf741165 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.50.0" +version = "1.50.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e05b612a12..2b0fd31d26 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.50.0" # x-release-please-version +__version__ = "1.50.1" # x-release-please-version From adb6da3aeebac792766bc4d11073e90116f55a47 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 27 Sep 2024 23:51:00 +0100 Subject: [PATCH 623/914] fix(audio): correct types for transcriptions / translations (#1755) fix --- .stats.yml | 2 +- api.md | 14 +- src/openai/_utils/_reflection.py | 5 +- src/openai/resources/audio/transcriptions.py | 155 +++++++++++++++++- src/openai/resources/audio/translations.py | 142 +++++++++++++++- src/openai/types/audio/__init__.py | 6 + .../audio/transcription_create_response.py | 11 ++ .../types/audio/transcription_segment.py | 49 ++++++ .../types/audio/transcription_verbose.py | 26 +++ src/openai/types/audio/transcription_word.py | 17 ++ .../audio/translation_create_response.py | 11 ++ src/openai/types/audio/translation_verbose.py | 22 +++ .../beta/static_file_chunking_strategy.py | 1 - .../audio/test_transcriptions.py | 18 +- .../api_resources/audio/test_translations.py | 18 +- tests/lib/test_audio.py | 83 ++++++++++ tests/utils.py | 6 +- 17 files changed, 543 insertions(+), 43 deletions(-) create mode 100644 src/openai/types/audio/transcription_create_response.py create mode 100644 src/openai/types/audio/transcription_segment.py create mode 100644 src/openai/types/audio/transcription_verbose.py create mode 100644 src/openai/types/audio/transcription_word.py create mode 100644 src/openai/types/audio/translation_create_response.py create mode 100644 src/openai/types/audio/translation_verbose.py create mode 100644 tests/lib/test_audio.py diff --git a/.stats.yml b/.stats.yml index 0998368a4c..68789976bf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-17ddd746c775ca4d4fbe64e5621ac30756ef09c061ff6313190b6ec162222d4c.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-71e58a77027c67e003fdd1b1ac8ac11557d8bfabc7666d1a827c6b1ca8ab98b5.yml diff --git a/api.md b/api.md index 29db54f0fd..813c5cb615 100644 --- a/api.md +++ b/api.md @@ -122,24 +122,30 @@ from openai.types import AudioModel, AudioResponseFormat Types: ```python -from openai.types.audio import Transcription +from openai.types.audio import ( + Transcription, + TranscriptionSegment, + TranscriptionVerbose, + TranscriptionWord, + TranscriptionCreateResponse, +) ``` Methods: -- client.audio.transcriptions.create(\*\*params) -> Transcription +- client.audio.transcriptions.create(\*\*params) -> TranscriptionCreateResponse ## Translations Types: ```python -from openai.types.audio import Translation +from openai.types.audio import Translation, TranslationVerbose, TranslationCreateResponse ``` Methods: -- client.audio.translations.create(\*\*params) -> Translation +- client.audio.translations.create(\*\*params) -> TranslationCreateResponse ## Speech diff --git a/src/openai/_utils/_reflection.py b/src/openai/_utils/_reflection.py index 89aa712ac4..bdaca29e4a 100644 --- a/src/openai/_utils/_reflection.py +++ b/src/openai/_utils/_reflection.py @@ -15,6 +15,7 @@ def assert_signatures_in_sync( check_func: Callable[..., Any], *, exclude_params: set[str] = set(), + description: str = "", ) -> None: """Ensure that the signature of the second function matches the first.""" @@ -39,4 +40,6 @@ def assert_signatures_in_sync( continue if errors: - raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) + raise AssertionError( + f"{len(errors)} errors encountered when comparing signatures{description}:\n\n" + "\n\n".join(errors) + ) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index fd042d1ac3..e6596a480e 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -2,8 +2,9 @@ from __future__ import annotations -from typing import List, Union, Mapping, cast -from typing_extensions import Literal +import logging +from typing import TYPE_CHECKING, List, Union, Mapping, cast +from typing_extensions import Literal, overload, assert_never import httpx @@ -24,9 +25,12 @@ from ...types.audio_model import AudioModel from ...types.audio.transcription import Transcription from ...types.audio_response_format import AudioResponseFormat +from ...types.audio.transcription_verbose import TranscriptionVerbose __all__ = ["Transcriptions", "AsyncTranscriptions"] +log: logging.Logger = logging.getLogger("openai.audio.transcriptions") + class Transcriptions(SyncAPIResource): @cached_property @@ -48,14 +52,53 @@ def with_streaming_response(self) -> TranscriptionsWithStreamingResponse: """ return TranscriptionsWithStreamingResponse(self) + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Transcription: ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["verbose_json"], + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TranscriptionVerbose: ... + + @overload def create( self, *, file: FileTypes, model: Union[str, AudioModel], + response_format: Literal["text", "srt", "vtt"], language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, - response_format: AudioResponseFormat | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -64,7 +107,25 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Transcription: + ) -> str: ... + + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Transcription | TranscriptionVerbose | str: """ Transcribes audio into the input language. @@ -124,14 +185,14 @@ def create( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( + return self._post( # type: ignore[return-value] "/audio/transcriptions", body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=Transcription, + cast_to=_get_response_format_type(response_format), ) @@ -155,14 +216,15 @@ def with_streaming_response(self) -> AsyncTranscriptionsWithStreamingResponse: """ return AsyncTranscriptionsWithStreamingResponse(self) + @overload async def create( self, *, file: FileTypes, model: Union[str, AudioModel], + response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, - response_format: AudioResponseFormat | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -171,7 +233,63 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Transcription: + ) -> Transcription: ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["verbose_json"], + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TranscriptionVerbose: ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["text", "srt", "vtt"], + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: ... + + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Transcription | TranscriptionVerbose | str: """ Transcribes audio into the input language. @@ -238,7 +356,7 @@ async def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=Transcription, + cast_to=_get_response_format_type(response_format), ) @@ -276,3 +394,22 @@ def __init__(self, transcriptions: AsyncTranscriptions) -> None: self.create = async_to_streamed_response_wrapper( transcriptions.create, ) + + +def _get_response_format_type( + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven, +) -> type[Transcription | TranscriptionVerbose | str]: + if isinstance(response_format, NotGiven) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] + return Transcription + + if response_format == "json": + return Transcription + elif response_format == "verbose_json": + return TranscriptionVerbose + elif response_format == "srt" or response_format == "text" or response_format == "vtt": + return str + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(response_format) + else: + log.warn("Unexpected audio response format: %s", response_format) + return Transcription diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index fe08dd550e..53ab625873 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing import Union, Mapping, cast +import logging +from typing import TYPE_CHECKING, Union, Mapping, cast +from typing_extensions import Literal, overload, assert_never import httpx @@ -23,9 +25,12 @@ from ...types.audio_model import AudioModel from ...types.audio.translation import Translation from ...types.audio_response_format import AudioResponseFormat +from ...types.audio.translation_verbose import TranslationVerbose __all__ = ["Translations", "AsyncTranslations"] +log: logging.Logger = logging.getLogger("openai.audio.transcriptions") + class Translations(SyncAPIResource): @cached_property @@ -47,13 +52,64 @@ def with_streaming_response(self) -> TranslationsWithStreamingResponse: """ return TranslationsWithStreamingResponse(self) + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Translation: ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["verbose_json"], + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TranslationVerbose: ... + + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["text", "srt", "vtt"], + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: ... + def create( self, *, file: FileTypes, model: Union[str, AudioModel], prompt: str | NotGiven = NOT_GIVEN, - response_format: AudioResponseFormat | NotGiven = NOT_GIVEN, + response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -61,7 +117,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Translation: + ) -> Translation | TranslationVerbose | str: """ Translates audio into English. @@ -108,14 +164,14 @@ def create( # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( + return self._post( # type: ignore[return-value] "/audio/translations", body=maybe_transform(body, translation_create_params.TranslationCreateParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=Translation, + cast_to=_get_response_format_type(response_format), ) @@ -139,13 +195,14 @@ def with_streaming_response(self) -> AsyncTranslationsWithStreamingResponse: """ return AsyncTranslationsWithStreamingResponse(self) + @overload async def create( self, *, file: FileTypes, model: Union[str, AudioModel], + response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, - response_format: AudioResponseFormat | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -153,7 +210,57 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Translation: + ) -> Translation: ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["verbose_json"], + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TranslationVerbose: ... + + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + response_format: Literal["text", "srt", "vtt"], + prompt: str | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: ... + + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + prompt: str | NotGiven = NOT_GIVEN, + response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Translation | TranslationVerbose | str: """ Translates audio into English. @@ -207,7 +314,7 @@ async def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=Translation, + cast_to=_get_response_format_type(response_format), ) @@ -245,3 +352,22 @@ def __init__(self, translations: AsyncTranslations) -> None: self.create = async_to_streamed_response_wrapper( translations.create, ) + + +def _get_response_format_type( + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven, +) -> type[Translation | TranslationVerbose | str]: + if isinstance(response_format, NotGiven) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] + return Translation + + if response_format == "json": + return Translation + elif response_format == "verbose_json": + return TranslationVerbose + elif response_format == "srt" or response_format == "text" or response_format == "vtt": + return str + elif TYPE_CHECKING: # type: ignore[unreachable] + assert_never(response_format) + else: + log.warn("Unexpected audio response format: %s", response_format) + return Transcription diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index 1de5c0ff82..822e0f3a8d 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -5,6 +5,12 @@ from .translation import Translation as Translation from .speech_model import SpeechModel as SpeechModel from .transcription import Transcription as Transcription +from .transcription_word import TranscriptionWord as TranscriptionWord +from .translation_verbose import TranslationVerbose as TranslationVerbose from .speech_create_params import SpeechCreateParams as SpeechCreateParams +from .transcription_segment import TranscriptionSegment as TranscriptionSegment +from .transcription_verbose import TranscriptionVerbose as TranscriptionVerbose from .translation_create_params import TranslationCreateParams as TranslationCreateParams from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams +from .translation_create_response import TranslationCreateResponse as TranslationCreateResponse +from .transcription_create_response import TranscriptionCreateResponse as TranscriptionCreateResponse diff --git a/src/openai/types/audio/transcription_create_response.py b/src/openai/types/audio/transcription_create_response.py new file mode 100644 index 0000000000..2f7bed8114 --- /dev/null +++ b/src/openai/types/audio/transcription_create_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import TypeAlias + +from .transcription import Transcription +from .transcription_verbose import TranscriptionVerbose + +__all__ = ["TranscriptionCreateResponse"] + +TranscriptionCreateResponse: TypeAlias = Union[Transcription, TranscriptionVerbose] diff --git a/src/openai/types/audio/transcription_segment.py b/src/openai/types/audio/transcription_segment.py new file mode 100644 index 0000000000..522c401ebb --- /dev/null +++ b/src/openai/types/audio/transcription_segment.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["TranscriptionSegment"] + + +class TranscriptionSegment(BaseModel): + id: int + """Unique identifier of the segment.""" + + avg_logprob: float + """Average logprob of the segment. + + If the value is lower than -1, consider the logprobs failed. + """ + + compression_ratio: float + """Compression ratio of the segment. + + If the value is greater than 2.4, consider the compression failed. + """ + + end: float + """End time of the segment in seconds.""" + + no_speech_prob: float + """Probability of no speech in the segment. + + If the value is higher than 1.0 and the `avg_logprob` is below -1, consider this + segment silent. + """ + + seek: int + """Seek offset of the segment.""" + + start: float + """Start time of the segment in seconds.""" + + temperature: float + """Temperature parameter used for generating the segment.""" + + text: str + """Text content of the segment.""" + + tokens: List[int] + """Array of token IDs for the text content.""" diff --git a/src/openai/types/audio/transcription_verbose.py b/src/openai/types/audio/transcription_verbose.py new file mode 100644 index 0000000000..3b18fa4871 --- /dev/null +++ b/src/openai/types/audio/transcription_verbose.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .transcription_word import TranscriptionWord +from .transcription_segment import TranscriptionSegment + +__all__ = ["TranscriptionVerbose"] + + +class TranscriptionVerbose(BaseModel): + duration: str + """The duration of the input audio.""" + + language: str + """The language of the input audio.""" + + text: str + """The transcribed text.""" + + segments: Optional[List[TranscriptionSegment]] = None + """Segments of the transcribed text and their corresponding details.""" + + words: Optional[List[TranscriptionWord]] = None + """Extracted words and their corresponding timestamps.""" diff --git a/src/openai/types/audio/transcription_word.py b/src/openai/types/audio/transcription_word.py new file mode 100644 index 0000000000..969da32509 --- /dev/null +++ b/src/openai/types/audio/transcription_word.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["TranscriptionWord"] + + +class TranscriptionWord(BaseModel): + end: float + """End time of the word in seconds.""" + + start: float + """Start time of the word in seconds.""" + + word: str + """The text content of the word.""" diff --git a/src/openai/types/audio/translation_create_response.py b/src/openai/types/audio/translation_create_response.py new file mode 100644 index 0000000000..9953813c08 --- /dev/null +++ b/src/openai/types/audio/translation_create_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import TypeAlias + +from .translation import Translation +from .translation_verbose import TranslationVerbose + +__all__ = ["TranslationCreateResponse"] + +TranslationCreateResponse: TypeAlias = Union[Translation, TranslationVerbose] diff --git a/src/openai/types/audio/translation_verbose.py b/src/openai/types/audio/translation_verbose.py new file mode 100644 index 0000000000..5901ae7535 --- /dev/null +++ b/src/openai/types/audio/translation_verbose.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel +from .transcription_segment import TranscriptionSegment + +__all__ = ["TranslationVerbose"] + + +class TranslationVerbose(BaseModel): + duration: str + """The duration of the input audio.""" + + language: str + """The language of the output translation (always `english`).""" + + text: str + """The translated text.""" + + segments: Optional[List[TranscriptionSegment]] = None + """Segments of the translated text and their corresponding details.""" diff --git a/src/openai/types/beta/static_file_chunking_strategy.py b/src/openai/types/beta/static_file_chunking_strategy.py index ba80e1a2b9..6080093517 100644 --- a/src/openai/types/beta/static_file_chunking_strategy.py +++ b/src/openai/types/beta/static_file_chunking_strategy.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["StaticFileChunkingStrategy"] diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index ba8e9e4099..0fa91eb152 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.audio import Transcription +from openai.types.audio import TranscriptionCreateResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -23,7 +23,7 @@ def test_method_create(self, client: OpenAI) -> None: file=b"raw file contents", model="whisper-1", ) - assert_matches_type(Transcription, transcription, path=["response"]) + assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: @@ -36,7 +36,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: temperature=0, timestamp_granularities=["word", "segment"], ) - assert_matches_type(Transcription, transcription, path=["response"]) + assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize def test_raw_response_create(self, client: OpenAI) -> None: @@ -48,7 +48,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = response.parse() - assert_matches_type(Transcription, transcription, path=["response"]) + assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: @@ -60,7 +60,7 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = response.parse() - assert_matches_type(Transcription, transcription, path=["response"]) + assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) assert cast(Any, response.is_closed) is True @@ -74,7 +74,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: file=b"raw file contents", model="whisper-1", ) - assert_matches_type(Transcription, transcription, path=["response"]) + assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -87,7 +87,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> temperature=0, timestamp_granularities=["word", "segment"], ) - assert_matches_type(Transcription, transcription, path=["response"]) + assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @@ -99,7 +99,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = response.parse() - assert_matches_type(Transcription, transcription, path=["response"]) + assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: @@ -111,6 +111,6 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert response.http_request.headers.get("X-Stainless-Lang") == "python" transcription = await response.parse() - assert_matches_type(Transcription, transcription, path=["response"]) + assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index b048a1af12..e12ab7e6c0 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.types.audio import Translation +from openai.types.audio import TranslationCreateResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -23,7 +23,7 @@ def test_method_create(self, client: OpenAI) -> None: file=b"raw file contents", model="whisper-1", ) - assert_matches_type(Translation, translation, path=["response"]) + assert_matches_type(TranslationCreateResponse, translation, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: @@ -34,7 +34,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: response_format="json", temperature=0, ) - assert_matches_type(Translation, translation, path=["response"]) + assert_matches_type(TranslationCreateResponse, translation, path=["response"]) @parametrize def test_raw_response_create(self, client: OpenAI) -> None: @@ -46,7 +46,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" translation = response.parse() - assert_matches_type(Translation, translation, path=["response"]) + assert_matches_type(TranslationCreateResponse, translation, path=["response"]) @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: @@ -58,7 +58,7 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" translation = response.parse() - assert_matches_type(Translation, translation, path=["response"]) + assert_matches_type(TranslationCreateResponse, translation, path=["response"]) assert cast(Any, response.is_closed) is True @@ -72,7 +72,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: file=b"raw file contents", model="whisper-1", ) - assert_matches_type(Translation, translation, path=["response"]) + assert_matches_type(TranslationCreateResponse, translation, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -83,7 +83,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> response_format="json", temperature=0, ) - assert_matches_type(Translation, translation, path=["response"]) + assert_matches_type(TranslationCreateResponse, translation, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @@ -95,7 +95,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" translation = response.parse() - assert_matches_type(Translation, translation, path=["response"]) + assert_matches_type(TranslationCreateResponse, translation, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: @@ -107,6 +107,6 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert response.http_request.headers.get("X-Stainless-Lang") == "python" translation = await response.parse() - assert_matches_type(Translation, translation, path=["response"]) + assert_matches_type(TranslationCreateResponse, translation, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/lib/test_audio.py b/tests/lib/test_audio.py new file mode 100644 index 0000000000..0f53b316ba --- /dev/null +++ b/tests/lib/test_audio.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import sys +import inspect +import typing_extensions +from typing import get_args + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import evaluate_forwardref +from openai._utils import assert_signatures_in_sync +from openai._compat import is_literal_type +from openai._utils._typing import is_union_type +from openai.types.audio_response_format import AudioResponseFormat + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_translation_create_overloads_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + fn = checking_client.audio.translations.create + overload_response_formats: set[str] = set() + + for i, overload in enumerate(typing_extensions.get_overloads(fn)): + assert_signatures_in_sync( + fn, + overload, + exclude_params={"response_format"}, + description=f" for overload {i}", + ) + + sig = inspect.signature(overload) + typ = evaluate_forwardref( + sig.parameters["response_format"].annotation, + globalns=sys.modules[fn.__module__].__dict__, + ) + if is_union_type(typ): + for arg in get_args(typ): + if not is_literal_type(arg): + continue + + overload_response_formats.update(get_args(arg)) + elif is_literal_type(typ): + overload_response_formats.update(get_args(typ)) + + src_response_formats: set[str] = set(get_args(AudioResponseFormat)) + diff = src_response_formats.difference(overload_response_formats) + assert len(diff) == 0, f"some response format options don't have overloads" + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_transcription_create_overloads_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + fn = checking_client.audio.transcriptions.create + overload_response_formats: set[str] = set() + + for i, overload in enumerate(typing_extensions.get_overloads(fn)): + assert_signatures_in_sync( + fn, + overload, + exclude_params={"response_format"}, + description=f" for overload {i}", + ) + + sig = inspect.signature(overload) + typ = evaluate_forwardref( + sig.parameters["response_format"].annotation, + globalns=sys.modules[fn.__module__].__dict__, + ) + if is_union_type(typ): + for arg in get_args(typ): + if not is_literal_type(arg): + continue + + overload_response_formats.update(get_args(arg)) + elif is_literal_type(typ): + overload_response_formats.update(get_args(typ)) + + src_response_formats: set[str] = set(get_args(AudioResponseFormat)) + diff = src_response_formats.difference(overload_response_formats) + assert len(diff) == 0, f"some response format options don't have overloads" diff --git a/tests/utils.py b/tests/utils.py index 8d5397f28e..16948a66f2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -5,7 +5,7 @@ import inspect import traceback import contextlib -from typing import Any, TypeVar, Iterator, cast +from typing import Any, TypeVar, Iterator, ForwardRef, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type @@ -26,6 +26,10 @@ BaseModelT = TypeVar("BaseModelT", bound=BaseModel) +def evaluate_forwardref(forwardref: ForwardRef, globalns: dict[str, Any]) -> type: + return eval(str(forwardref), globalns) # type: ignore[no-any-return] + + def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: for name, field in get_model_fields(model).items(): field_value = getattr(value, name) From aeaed488352274a9ca86c834eeb618d732989518 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 23:04:57 +0000 Subject: [PATCH 624/914] release: 1.50.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 43515db369..b11d9e721d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.50.1" + ".": "1.50.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c51de0481b..37571051de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.50.2 (2024-09-27) + +Full Changelog: [v1.50.1...v1.50.2](https://github.com/openai/openai-python/compare/v1.50.1...v1.50.2) + +### Bug Fixes + +* **audio:** correct types for transcriptions / translations ([#1755](https://github.com/openai/openai-python/issues/1755)) ([76c1f3f](https://github.com/openai/openai-python/commit/76c1f3f318b68003aae124c02efc4547a398a864)) + ## 1.50.1 (2024-09-27) Full Changelog: [v1.50.0...v1.50.1](https://github.com/openai/openai-python/compare/v1.50.0...v1.50.1) diff --git a/pyproject.toml b/pyproject.toml index 7ebf741165..90b8c8518c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.50.1" +version = "1.50.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 2b0fd31d26..641dd21648 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.50.1" # x-release-please-version +__version__ = "1.50.2" # x-release-please-version From 3b4821ff79433ba268ef66190747c14e1f1aeb71 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 30 Sep 2024 10:19:21 -0400 Subject: [PATCH 625/914] chore(internal): remove ds store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index c88a062b05be4fd1d362b3e4c6a7481e718b69d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~O$x$5422WzLU7Zi%h`AUZ!n0Spcima5J4*Vx1OW>k_m#k8KJGkN^pg011%5 z4-v3?8#bF)Wh4O-Ab}?V`#vPNX$~z_{nLTqBLK8P*$r!-C7{U)&>UK-q5{*H9yD6j z#}KP~J2b_)99pW@cF`C;crrWIXQgOGwy`I%~QMGk}L;X0y%TE9jyNVZZH|!@{KyzrRiVBQB0*--!1inh( E0rZ6u#{d8T From 4bd80b9f70773ae3a24874f2a82043db0ae7ef77 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:51:54 +0000 Subject: [PATCH 626/914] chore(docs): fix maxium typo (#1762) --- .stats.yml | 2 +- src/openai/resources/beta/assistants.py | 8 ++++---- src/openai/resources/beta/threads/messages.py | 8 ++++---- .../resources/beta/threads/runs/runs.py | 16 +++++++-------- src/openai/resources/beta/threads/threads.py | 20 +++++++++---------- .../beta/vector_stores/vector_stores.py | 8 ++++---- src/openai/types/batch.py | 2 +- src/openai/types/beta/assistant.py | 2 +- .../types/beta/assistant_create_params.py | 4 ++-- .../types/beta/assistant_update_params.py | 2 +- src/openai/types/beta/thread.py | 2 +- .../beta/thread_create_and_run_params.py | 8 ++++---- src/openai/types/beta/thread_create_params.py | 6 +++--- src/openai/types/beta/thread_update_params.py | 2 +- src/openai/types/beta/threads/message.py | 2 +- .../beta/threads/message_create_params.py | 2 +- .../beta/threads/message_update_params.py | 2 +- src/openai/types/beta/threads/run.py | 2 +- .../types/beta/threads/run_create_params.py | 4 ++-- .../types/beta/threads/run_update_params.py | 2 +- .../types/beta/threads/runs/run_step.py | 2 +- src/openai/types/beta/vector_store.py | 2 +- .../types/beta/vector_store_create_params.py | 2 +- .../types/beta/vector_store_update_params.py | 2 +- 24 files changed, 56 insertions(+), 56 deletions(-) diff --git a/.stats.yml b/.stats.yml index 68789976bf..67778eef99 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-71e58a77027c67e003fdd1b1ac8ac11557d8bfabc7666d1a827c6b1ca8ab98b5.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-8ad878332083dd506a478a293db78dc9e7b1b2124f2682e1d991225bc5bbcc3b.yml diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 5d8c6ec331..2ebef183b6 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -89,7 +89,7 @@ def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. name: The name of the assistant. The maximum length is 256 characters. @@ -233,7 +233,7 @@ def update( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: ID of the model to use. You can use the @@ -475,7 +475,7 @@ async def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. name: The name of the assistant. The maximum length is 256 characters. @@ -619,7 +619,7 @@ async def update( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: ID of the model to use. You can use the diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index 4901174329..de7ebcaf4d 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -82,7 +82,7 @@ def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. extra_headers: Send extra headers @@ -169,7 +169,7 @@ def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. extra_headers: Send extra headers @@ -356,7 +356,7 @@ async def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. extra_headers: Send extra headers @@ -443,7 +443,7 @@ async def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. extra_headers: Send extra headers diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index a4cbcc57e4..287c0ecf24 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -149,7 +149,7 @@ def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -293,7 +293,7 @@ def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -433,7 +433,7 @@ def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -623,7 +623,7 @@ def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. extra_headers: Send extra headers @@ -1511,7 +1511,7 @@ async def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -1655,7 +1655,7 @@ async def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -1795,7 +1795,7 @@ async def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -1986,7 +1986,7 @@ async def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. extra_headers: Send extra headers diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index acc0fe9bda..3b8851c03b 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -111,7 +111,7 @@ def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. tool_resources: A set of resources that are made available to the assistant's tools in this @@ -197,7 +197,7 @@ def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. tool_resources: A set of resources that are made available to the assistant's tools in this @@ -317,7 +317,7 @@ def create_and_run( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -451,7 +451,7 @@ def create_and_run( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -581,7 +581,7 @@ def create_and_run( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -945,7 +945,7 @@ async def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. tool_resources: A set of resources that are made available to the assistant's tools in this @@ -1031,7 +1031,7 @@ async def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. tool_resources: A set of resources that are made available to the assistant's tools in this @@ -1151,7 +1151,7 @@ async def create_and_run( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -1285,7 +1285,7 @@ async def create_and_run( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to @@ -1415,7 +1415,7 @@ async def create_and_run( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py index 06e26852b4..d69add7b26 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -105,7 +105,7 @@ def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. name: The name of the vector store. @@ -193,7 +193,7 @@ def update( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. name: The name of the vector store. @@ -383,7 +383,7 @@ async def create( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. name: The name of the vector store. @@ -471,7 +471,7 @@ async def update( metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maxium of 512 + can be a maximum of 64 characters long and values can be a maximum of 512 characters long. name: The name of the vector store. diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py index 90f6d79572..ac3d7ea119 100644 --- a/src/openai/types/batch.py +++ b/src/openai/types/batch.py @@ -75,7 +75,7 @@ class Batch(BaseModel): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ output_file_id: Optional[str] = None diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index b4da08745d..ea97de440f 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -56,7 +56,7 @@ class Assistant(BaseModel): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ model: str diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index eca4da0a2b..e11f842f05 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -44,7 +44,7 @@ class AssistantCreateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ name: Optional[str] @@ -135,7 +135,7 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can - be a maxium of 512 characters long. + be a maximum of 512 characters long. """ diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 5396233937..c4598df507 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -26,7 +26,7 @@ class AssistantUpdateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ model: str diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py index 6f7a6c7d0c..37d50ccb93 100644 --- a/src/openai/types/beta/thread.py +++ b/src/openai/types/beta/thread.py @@ -45,7 +45,7 @@ class Thread(BaseModel): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ object: Literal["thread"] diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 20d525fa1a..64ee6a8710 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -72,7 +72,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ model: Union[str, ChatModel, None] @@ -202,7 +202,7 @@ class ThreadMessage(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ @@ -235,7 +235,7 @@ class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can - be a maxium of 512 characters long. + be a maximum of 512 characters long. """ @@ -275,7 +275,7 @@ class Thread(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ tool_resources: Optional[ThreadToolResources] diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index 729164b481..3ac6c7d69b 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -34,7 +34,7 @@ class ThreadCreateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ tool_resources: Optional[ToolResources] @@ -83,7 +83,7 @@ class Message(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ @@ -116,7 +116,7 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can - be a maxium of 512 characters long. + be a maximum of 512 characters long. """ diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py index 7210ab77c9..78c5ec4f2e 100644 --- a/src/openai/types/beta/thread_update_params.py +++ b/src/openai/types/beta/thread_update_params.py @@ -14,7 +14,7 @@ class ThreadUpdateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ tool_resources: Optional[ToolResources] diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index 298a1d4273..63c5c4800a 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -71,7 +71,7 @@ class Message(BaseModel): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ object: Literal["thread.message"] diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index 2b450deb5d..2c4edfdf71 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -32,7 +32,7 @@ class MessageCreateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ diff --git a/src/openai/types/beta/threads/message_update_params.py b/src/openai/types/beta/threads/message_update_params.py index 7000f33122..e8f8cc910c 100644 --- a/src/openai/types/beta/threads/message_update_params.py +++ b/src/openai/types/beta/threads/message_update_params.py @@ -16,5 +16,5 @@ class MessageUpdateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index 5abc1de295..e8f2b74dee 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -138,7 +138,7 @@ class Run(BaseModel): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ model: str diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 824cb1a041..9767b142e1 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -85,7 +85,7 @@ class RunCreateParamsBase(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ model: Union[str, ChatModel, None] @@ -204,7 +204,7 @@ class AdditionalMessage(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ diff --git a/src/openai/types/beta/threads/run_update_params.py b/src/openai/types/beta/threads/run_update_params.py index e595eac882..cb4f053645 100644 --- a/src/openai/types/beta/threads/run_update_params.py +++ b/src/openai/types/beta/threads/run_update_params.py @@ -16,5 +16,5 @@ class RunUpdateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index e3163c508b..0445ae360d 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -75,7 +75,7 @@ class RunStep(BaseModel): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ object: Literal["thread.run.step"] diff --git a/src/openai/types/beta/vector_store.py b/src/openai/types/beta/vector_store.py index 488961b444..2d3ceea80c 100644 --- a/src/openai/types/beta/vector_store.py +++ b/src/openai/types/beta/vector_store.py @@ -53,7 +53,7 @@ class VectorStore(BaseModel): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ name: str diff --git a/src/openai/types/beta/vector_store_create_params.py b/src/openai/types/beta/vector_store_create_params.py index a8f03a89b9..4fc7c38927 100644 --- a/src/openai/types/beta/vector_store_create_params.py +++ b/src/openai/types/beta/vector_store_create_params.py @@ -33,7 +33,7 @@ class VectorStoreCreateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ name: str diff --git a/src/openai/types/beta/vector_store_update_params.py b/src/openai/types/beta/vector_store_update_params.py index 0f9593e476..ff6c068efb 100644 --- a/src/openai/types/beta/vector_store_update_params.py +++ b/src/openai/types/beta/vector_store_update_params.py @@ -17,7 +17,7 @@ class VectorStoreUpdateParams(TypedDict, total=False): This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be - a maxium of 512 characters long. + a maximum of 512 characters long. """ name: Optional[str] From 94bfe19aaf5a96aed37e6405684743a39892092f Mon Sep 17 00:00:00 2001 From: Laziz Turakulov <4857092+LazaUK@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:19:57 +0100 Subject: [PATCH 627/914] docs(helpers): fix method name typo (#1764) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 65515df973..3f3fafa45c 100644 --- a/helpers.md +++ b/helpers.md @@ -508,7 +508,7 @@ The polling methods are: ```python client.beta.threads.create_and_run_poll(...) client.beta.threads.runs.create_and_poll(...) -client.beta.threads.runs.submit_tool_ouptputs_and_poll(...) +client.beta.threads.runs.submit_tool_outputs_and_poll(...) client.beta.vector_stores.files.upload_and_poll(...) client.beta.vector_stores.files.create_and_poll(...) client.beta.vector_stores.file_batches.create_and_poll(...) From 7c8c11158c4e0b63fef495c32447d6e31870073f Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 1 Oct 2024 12:10:34 -0400 Subject: [PATCH 628/914] feat(api): support storing chat completions, enabling evals and model distillation in the dashboard Learn more at http://openai.com/devday2024 --- .stats.yml | 2 +- src/openai/resources/beta/chat/completions.py | 16 +++ src/openai/resources/chat/completions.py | 104 ++++++++++++++++-- .../types/chat/completion_create_params.py | 18 ++- src/openai/types/chat_model.py | 1 + src/openai/types/completion_usage.py | 16 ++- tests/api_resources/chat/test_completions.py | 8 ++ tests/lib/chat/test_completions.py | 18 ++- tests/lib/chat/test_completions_streaming.py | 3 +- 9 files changed, 164 insertions(+), 22 deletions(-) diff --git a/.stats.yml b/.stats.yml index 67778eef99..ece287351b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-8ad878332083dd506a478a293db78dc9e7b1b2124f2682e1d991225bc5bbcc3b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-52b934aee6468039ec7f4ce046a282b5fbce114afc708e70f17121df654f71da.yml diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index da6f189929..c747464072 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -69,12 +69,14 @@ def parse( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, @@ -158,6 +160,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "logprobs": logprobs, "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, + "metadata": metadata, "n": n, "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, @@ -165,6 +168,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "seed": seed, "service_tier": service_tier, "stop": stop, + "store": store, "stream": False, "stream_options": stream_options, "temperature": temperature, @@ -202,12 +206,14 @@ def stream( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, @@ -262,11 +268,13 @@ def stream( logprobs=logprobs, max_completion_tokens=max_completion_tokens, max_tokens=max_tokens, + metadata=metadata, n=n, parallel_tool_calls=parallel_tool_calls, presence_penalty=presence_penalty, seed=seed, service_tier=service_tier, + store=store, stop=stop, stream_options=stream_options, temperature=temperature, @@ -320,12 +328,14 @@ async def parse( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, @@ -409,12 +419,14 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "logprobs": logprobs, "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, + "metadata": metadata, "n": n, "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, "response_format": _type_to_response_format(response_format), "seed": seed, "service_tier": service_tier, + "store": store, "stop": stop, "stream": False, "stream_options": stream_options, @@ -453,12 +465,14 @@ def stream( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, @@ -514,12 +528,14 @@ def stream( logprobs=logprobs, max_completion_tokens=max_completion_tokens, max_tokens=max_tokens, + metadata=metadata, n=n, parallel_tool_calls=parallel_tool_calls, presence_penalty=presence_penalty, seed=seed, service_tier=service_tier, stop=stop, + store=store, stream_options=stream_options, temperature=temperature, tool_choice=tool_choice, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 05cfaacd83..c8080afaa1 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -66,6 +66,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -73,6 +74,7 @@ def create( seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -92,8 +94,12 @@ def create( Creates a model response for the given chat conversation. Args: - messages: A list of messages comprising the conversation so far. - [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://platform.openai.com/docs/models) you use, different message + types (modalities) are supported, like + [text](https://platform.openai.com/docs/guides/text-generation), + [images](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) @@ -145,6 +151,9 @@ def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). + metadata: Developer-defined tags and values used for filtering completions in the + [dashboard](https://platform.openai.com/completions). + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -204,6 +213,9 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. + store: Whether or not to store the output of this completion request for traffic + logging in the [dashboard](https://platform.openai.com/completions). + stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) @@ -271,6 +283,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -278,6 +291,7 @@ def create( seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, @@ -296,8 +310,12 @@ def create( Creates a model response for the given chat conversation. Args: - messages: A list of messages comprising the conversation so far. - [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://platform.openai.com/docs/models) you use, different message + types (modalities) are supported, like + [text](https://platform.openai.com/docs/guides/text-generation), + [images](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) @@ -356,6 +374,9 @@ def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). + metadata: Developer-defined tags and values used for filtering completions in the + [dashboard](https://platform.openai.com/completions). + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -415,6 +436,9 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. + store: Whether or not to store the output of this completion request for traffic + logging in the [dashboard](https://platform.openai.com/completions). + stream_options: Options for streaming response. Only set this when you set `stream: true`. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -475,6 +499,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -482,6 +507,7 @@ def create( seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, @@ -500,8 +526,12 @@ def create( Creates a model response for the given chat conversation. Args: - messages: A list of messages comprising the conversation so far. - [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://platform.openai.com/docs/models) you use, different message + types (modalities) are supported, like + [text](https://platform.openai.com/docs/guides/text-generation), + [images](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) @@ -560,6 +590,9 @@ def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). + metadata: Developer-defined tags and values used for filtering completions in the + [dashboard](https://platform.openai.com/completions). + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -619,6 +652,9 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. + store: Whether or not to store the output of this completion request for traffic + logging in the [dashboard](https://platform.openai.com/completions). + stream_options: Options for streaming response. Only set this when you set `stream: true`. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -678,6 +714,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -685,6 +722,7 @@ def create( seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -714,6 +752,7 @@ def create( "logprobs": logprobs, "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, + "metadata": metadata, "n": n, "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, @@ -721,6 +760,7 @@ def create( "seed": seed, "service_tier": service_tier, "stop": stop, + "store": store, "stream": stream, "stream_options": stream_options, "temperature": temperature, @@ -774,6 +814,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -781,6 +822,7 @@ async def create( seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -800,8 +842,12 @@ async def create( Creates a model response for the given chat conversation. Args: - messages: A list of messages comprising the conversation so far. - [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://platform.openai.com/docs/models) you use, different message + types (modalities) are supported, like + [text](https://platform.openai.com/docs/guides/text-generation), + [images](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) @@ -853,6 +899,9 @@ async def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). + metadata: Developer-defined tags and values used for filtering completions in the + [dashboard](https://platform.openai.com/completions). + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -912,6 +961,9 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. + store: Whether or not to store the output of this completion request for traffic + logging in the [dashboard](https://platform.openai.com/completions). + stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) @@ -979,6 +1031,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -986,6 +1039,7 @@ async def create( seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, @@ -1004,8 +1058,12 @@ async def create( Creates a model response for the given chat conversation. Args: - messages: A list of messages comprising the conversation so far. - [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://platform.openai.com/docs/models) you use, different message + types (modalities) are supported, like + [text](https://platform.openai.com/docs/guides/text-generation), + [images](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) @@ -1064,6 +1122,9 @@ async def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). + metadata: Developer-defined tags and values used for filtering completions in the + [dashboard](https://platform.openai.com/completions). + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -1123,6 +1184,9 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. + store: Whether or not to store the output of this completion request for traffic + logging in the [dashboard](https://platform.openai.com/completions). + stream_options: Options for streaming response. Only set this when you set `stream: true`. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -1183,6 +1247,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -1190,6 +1255,7 @@ async def create( seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, @@ -1208,8 +1274,12 @@ async def create( Creates a model response for the given chat conversation. Args: - messages: A list of messages comprising the conversation so far. - [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + messages: A list of messages comprising the conversation so far. Depending on the + [model](https://platform.openai.com/docs/models) you use, different message + types (modalities) are supported, like + [text](https://platform.openai.com/docs/guides/text-generation), + [images](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) @@ -1268,6 +1338,9 @@ async def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). + metadata: Developer-defined tags and values used for filtering completions in the + [dashboard](https://platform.openai.com/completions). + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -1327,6 +1400,9 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. + store: Whether or not to store the output of this completion request for traffic + logging in the [dashboard](https://platform.openai.com/completions). + stream_options: Options for streaming response. Only set this when you set `stream: true`. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -1386,6 +1462,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -1393,6 +1470,7 @@ async def create( seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1422,6 +1500,7 @@ async def create( "logprobs": logprobs, "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, + "metadata": metadata, "n": n, "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, @@ -1429,6 +1508,7 @@ async def create( "seed": seed, "service_tier": service_tier, "stop": stop, + "store": store, "stream": stream, "stream_options": stream_options, "temperature": temperature, diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 4ed89b00f5..3f55dfbe6e 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -30,7 +30,11 @@ class CompletionCreateParamsBase(TypedDict, total=False): messages: Required[Iterable[ChatCompletionMessageParam]] """A list of messages comprising the conversation so far. - [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models). + Depending on the [model](https://platform.openai.com/docs/models) you use, + different message types (modalities) are supported, like + [text](https://platform.openai.com/docs/guides/text-generation), + [images](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio). """ model: Required[Union[str, ChatModel]] @@ -105,6 +109,12 @@ class CompletionCreateParamsBase(TypedDict, total=False): [o1 series models](https://platform.openai.com/docs/guides/reasoning). """ + metadata: Optional[Dict[str, str]] + """ + Developer-defined tags and values used for filtering completions in the + [dashboard](https://platform.openai.com/completions). + """ + n: Optional[int] """How many chat completion choices to generate for each input message. @@ -183,6 +193,12 @@ class CompletionCreateParamsBase(TypedDict, total=False): stop: Union[Optional[str], List[str]] """Up to 4 sequences where the API will stop generating further tokens.""" + store: Optional[bool] + """ + Whether or not to store the output of this completion request for traffic + logging in the [dashboard](https://platform.openai.com/completions). + """ + stream_options: Optional[ChatCompletionStreamOptionsParam] """Options for streaming response. Only set this when you set `stream: true`.""" diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index f8438c75c8..f2d5674786 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -12,6 +12,7 @@ "gpt-4o", "gpt-4o-2024-08-06", "gpt-4o-2024-05-13", + "gpt-4o-realtime-preview-2024-10-01", "chatgpt-4o-latest", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index a4b9116e35..fe112833e0 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -4,14 +4,25 @@ from .._models import BaseModel -__all__ = ["CompletionUsage", "CompletionTokensDetails"] +__all__ = ["CompletionUsage", "CompletionTokensDetails", "PromptTokensDetails"] class CompletionTokensDetails(BaseModel): + audio_tokens: Optional[int] = None + """Audio input tokens generated by the model.""" + reasoning_tokens: Optional[int] = None """Tokens generated by the model for reasoning.""" +class PromptTokensDetails(BaseModel): + audio_tokens: Optional[int] = None + """Audio input tokens present in the prompt.""" + + cached_tokens: Optional[int] = None + """Cached tokens present in the prompt.""" + + class CompletionUsage(BaseModel): completion_tokens: int """Number of tokens in the generated completion.""" @@ -24,3 +35,6 @@ class CompletionUsage(BaseModel): completion_tokens_details: Optional[CompletionTokensDetails] = None """Breakdown of tokens used in a completion.""" + + prompt_tokens_details: Optional[PromptTokensDetails] = None + """Breakdown of tokens used in the prompt.""" diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index c44703a434..8c1f263f54 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -57,6 +57,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: logprobs=True, max_completion_tokens=0, max_tokens=0, + metadata={"foo": "string"}, n=1, parallel_tool_calls=True, presence_penalty=-2, @@ -64,6 +65,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: seed=-9007199254740991, service_tier="auto", stop="string", + store=True, stream=False, stream_options={"include_usage": True}, temperature=1, @@ -178,6 +180,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: logprobs=True, max_completion_tokens=0, max_tokens=0, + metadata={"foo": "string"}, n=1, parallel_tool_calls=True, presence_penalty=-2, @@ -185,6 +188,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: seed=-9007199254740991, service_tier="auto", stop="string", + store=True, stream_options={"include_usage": True}, temperature=1, tool_choice="none", @@ -318,6 +322,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn logprobs=True, max_completion_tokens=0, max_tokens=0, + metadata={"foo": "string"}, n=1, parallel_tool_calls=True, presence_penalty=-2, @@ -325,6 +330,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn seed=-9007199254740991, service_tier="auto", stop="string", + store=True, stream=False, stream_options={"include_usage": True}, temperature=1, @@ -439,6 +445,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn logprobs=True, max_completion_tokens=0, max_tokens=0, + metadata={"foo": "string"}, n=1, parallel_tool_calls=True, presence_penalty=-2, @@ -446,6 +453,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn seed=-9007199254740991, service_tier="auto", stop="string", + store=True, stream_options={"include_usage": True}, temperature=1, tool_choice="none", diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index 7702a98d49..3cfea71f11 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -76,8 +76,9 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte system_fingerprint='fp_b40fb1c6fb', usage=CompletionUsage( completion_tokens=37, - completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), prompt_tokens=14, + prompt_tokens_details=None, total_tokens=51 ) ) @@ -136,8 +137,9 @@ class Location(BaseModel): system_fingerprint='fp_5050236cbd', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), prompt_tokens=79, + prompt_tokens_details=None, total_tokens=93 ) ) @@ -198,8 +200,9 @@ class Location(BaseModel): system_fingerprint='fp_b40fb1c6fb', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), prompt_tokens=88, + prompt_tokens_details=None, total_tokens=102 ) ) @@ -385,8 +388,9 @@ class CalendarEvent: system_fingerprint='fp_7568d46099', usage=CompletionUsage( completion_tokens=17, - completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), prompt_tokens=92, + prompt_tokens_details=None, total_tokens=109 ) ) @@ -829,8 +833,9 @@ class Location(BaseModel): system_fingerprint='fp_5050236cbd', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), prompt_tokens=79, + prompt_tokens_details=None, total_tokens=93 ) ) @@ -897,8 +902,9 @@ class Location(BaseModel): system_fingerprint='fp_5050236cbd', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), prompt_tokens=79, + prompt_tokens_details=None, total_tokens=93 ) ) diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index ce402cd158..216fe9ddf5 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -155,8 +155,9 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream system_fingerprint='fp_5050236cbd', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), prompt_tokens=79, + prompt_tokens_details=None, total_tokens=93 ) ) From b7d9ce71669164a3864e170476b2846760f5e35f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:45:47 +0000 Subject: [PATCH 629/914] release: 1.51.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b11d9e721d..3ff0a9eb32 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.50.2" + ".": "1.51.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 37571051de..8db39a51c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.51.0 (2024-10-01) + +Full Changelog: [v1.50.2...v1.51.0](https://github.com/openai/openai-python/compare/v1.50.2...v1.51.0) + +### Features + +* **api:** support storing chat completions, enabling evals and model distillation in the dashboard ([2840c6d](https://github.com/openai/openai-python/commit/2840c6df94afb44cfd80efabe0405898331ee267)) + + +### Chores + +* **docs:** fix maxium typo ([#1762](https://github.com/openai/openai-python/issues/1762)) ([de94553](https://github.com/openai/openai-python/commit/de94553f93d71fc6c8187c8d3fbe924a71cc46dd)) +* **internal:** remove ds store ([47a3968](https://github.com/openai/openai-python/commit/47a3968f9b318eb02d5602f5b10e7d9e69c3ae84)) + + +### Documentation + +* **helpers:** fix method name typo ([#1764](https://github.com/openai/openai-python/issues/1764)) ([e1bcfe8](https://github.com/openai/openai-python/commit/e1bcfe86554017ac63055060153c4fd72e65c0cf)) + ## 1.50.2 (2024-09-27) Full Changelog: [v1.50.1...v1.50.2](https://github.com/openai/openai-python/compare/v1.50.1...v1.50.2) diff --git a/pyproject.toml b/pyproject.toml index 90b8c8518c..d1bffe39a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.50.2" +version = "1.51.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 641dd21648..407f4cb969 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.50.2" # x-release-please-version +__version__ = "1.51.0" # x-release-please-version From a3001d8d91f9f991e2164671fd40cc16d495f006 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Wed, 2 Oct 2024 19:16:58 +0000 Subject: [PATCH 630/914] docs: improve and reference contributing documentation (#1767) --- CONTRIBUTING.md | 44 ++++++++++++++++++++++++-------------------- README.md | 4 ++++ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a6639b8fc..37e060bdcf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,13 @@ ### With Rye -We use [Rye](https://rye-up.com/) to manage dependencies so we highly recommend [installing it](https://rye-up.com/guide/installation/) as it will automatically provision a Python environment with the expected Python version. +We use [Rye](https://rye.astral.sh/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: -After installing Rye, you'll just have to run this command: +```sh +$ ./scripts/bootstrap +``` + +Or [install Rye manually](https://rye.astral.sh/guide/installation/) and run: ```sh $ rye sync --all-features @@ -39,17 +43,17 @@ modify the contents of the `src/openai/lib/` and `examples/` directories. All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. -```bash +```ts # add an example to examples/.py #!/usr/bin/env -S rye run python … ``` -``` -chmod +x examples/.py +```sh +$ chmod +x examples/.py # run the example against your api -./examples/.py +$ ./examples/.py ``` ## Using the repository from source @@ -58,8 +62,8 @@ If you’d like to use the repository from source, you can either install from g To install via git: -```bash -pip install git+ssh://git@github.com/openai/openai-python.git +```sh +$ pip install git+ssh://git@github.com/openai/openai-python.git ``` Alternatively, you can build from source and install the wheel file: @@ -68,29 +72,29 @@ Building this package will create two files in the `dist/` directory, a `.tar.gz To create a distributable version of the library, all you have to do is run this command: -```bash -rye build +```sh +$ rye build # or -python -m build +$ python -m build ``` Then to install: ```sh -pip install ./path-to-wheel-file.whl +$ pip install ./path-to-wheel-file.whl ``` ## Running tests Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. -```bash +```sh # you will need npm installed -npx prism mock path/to/your/openapi.yml +$ npx prism mock path/to/your/openapi.yml ``` -```bash -rye run pytest +```sh +$ ./scripts/test ``` ## Linting and formatting @@ -100,14 +104,14 @@ This repository uses [ruff](https://github.com/astral-sh/ruff) and To lint: -```bash -rye run lint +```sh +$ ./scripts/lint ``` To format and fix all ruff issues automatically: -```bash -rye run format +```sh +$ ./scripts/format ``` ## Publishing and releases diff --git a/README.md b/README.md index c47bdd54c5..dada1abfbb 100644 --- a/README.md +++ b/README.md @@ -713,3 +713,7 @@ print(openai.__version__) ## Requirements Python 3.7 or higher. + +## Contributing + +See [the contributing documentation](./CONTRIBUTING.md). From 50de514b910aced32104833acd892bebfb2cf123 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 20:04:38 +0000 Subject: [PATCH 631/914] docs: fix typo in fenced code block language (#1769) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 37e060bdcf..52c2eb213a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,7 +43,7 @@ modify the contents of the `src/openai/lib/` and `examples/` directories. All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. -```ts +```py # add an example to examples/.py #!/usr/bin/env -S rye run python From a7d9731bb1d79871ee4817faf0f3f558ce3d855c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:39:22 +0000 Subject: [PATCH 632/914] chore(internal): add support for parsing bool response content (#1774) --- src/openai/_legacy_response.py | 3 ++ src/openai/_response.py | 3 ++ tests/test_legacy_response.py | 25 +++++++++++++++++ tests/test_response.py | 50 ++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index c7dbd54e23..5260e90bc1 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -258,6 +258,9 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to == float: return cast(R, float(response.text)) + if cast_to == bool: + return cast(R, response.text.lower() == "true") + origin = get_origin(cast_to) or cast_to if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent): diff --git a/src/openai/_response.py b/src/openai/_response.py index 20ce69ac8a..eac3fbae6c 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -192,6 +192,9 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to == float: return cast(R, float(response.text)) + if cast_to == bool: + return cast(R, response.text.lower() == "true") + origin = get_origin(cast_to) or cast_to # handle the legacy binary response case diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py index f50a77c24d..9da1a80659 100644 --- a/tests/test_legacy_response.py +++ b/tests/test_legacy_response.py @@ -34,6 +34,31 @@ def test_response_parse_mismatched_basemodel(client: OpenAI) -> None: response.parse(to=PydanticModel) +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +def test_response_parse_bool(client: OpenAI, content: str, expected: bool) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = response.parse(to=bool) + assert result is expected + + def test_response_parse_custom_stream(client: OpenAI) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=b"foo"), diff --git a/tests/test_response.py b/tests/test_response.py index e1fe332f2f..43f24c150d 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -237,6 +237,56 @@ async def test_async_response_parse_annotated_type(async_client: AsyncOpenAI) -> assert obj.bar == 2 +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +def test_response_parse_bool(client: OpenAI, content: str, expected: bool) -> None: + response = APIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = response.parse(to=bool) + assert result is expected + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +async def test_async_response_parse_bool(client: AsyncOpenAI, content: str, expected: bool) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = await response.parse(to=bool) + assert result is expected + + class OtherModel(BaseModel): a: str From 4d8fb73c8f1c0aa32364b0ab5000e1078f3a93c5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:52:45 +0000 Subject: [PATCH 633/914] fix(client): avoid OverflowError with very large retry counts (#1779) --- src/openai/_base_client.py | 3 ++- tests/test_client.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index c4c9803e74..931cbd8534 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -690,7 +690,8 @@ def _calculate_retry_timeout( if retry_after is not None and 0 < retry_after <= 60: return retry_after - nb_retries = max_retries - remaining_retries + # Also cap retry count to 1000 to avoid any potential overflows with `pow` + nb_retries = min(max_retries - remaining_retries, 1000) # Apply exponential backoff, but not more than the max. sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) diff --git a/tests/test_client.py b/tests/test_client.py index 463174465c..1da35ddd22 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -697,6 +697,7 @@ class Model(BaseModel): [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], + [-1100, "", 7.8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -1553,6 +1554,7 @@ class Model(BaseModel): [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], + [-1100, "", 7.8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) From 8467d41376c2b17eae1c78d56b39767e7807cb6f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:53:16 +0000 Subject: [PATCH 634/914] release: 1.51.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3ff0a9eb32..3462272dee 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.51.0" + ".": "1.51.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8db39a51c4..08b85350d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.51.1 (2024-10-07) + +Full Changelog: [v1.51.0...v1.51.1](https://github.com/openai/openai-python/compare/v1.51.0...v1.51.1) + +### Bug Fixes + +* **client:** avoid OverflowError with very large retry counts ([#1779](https://github.com/openai/openai-python/issues/1779)) ([fb1dacf](https://github.com/openai/openai-python/commit/fb1dacfa4d9447d123c38ab3d3d433d900d32ec5)) + + +### Chores + +* **internal:** add support for parsing bool response content ([#1774](https://github.com/openai/openai-python/issues/1774)) ([aa2e25f](https://github.com/openai/openai-python/commit/aa2e25f9a4a632357051397ea34d269eafba026d)) + + +### Documentation + +* fix typo in fenced code block language ([#1769](https://github.com/openai/openai-python/issues/1769)) ([57bbc15](https://github.com/openai/openai-python/commit/57bbc155210cc439a36f4e5cbd082e94c3349d78)) +* improve and reference contributing documentation ([#1767](https://github.com/openai/openai-python/issues/1767)) ([a985a8b](https://github.com/openai/openai-python/commit/a985a8b8ab8d0b364bd3c26b6423a7c49ae7b1ce)) + ## 1.51.0 (2024-10-01) Full Changelog: [v1.50.2...v1.51.0](https://github.com/openai/openai-python/compare/v1.50.2...v1.51.0) diff --git a/pyproject.toml b/pyproject.toml index d1bffe39a9..41b3c18f01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.51.0" +version = "1.51.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 407f4cb969..ce7c45ab73 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.51.0" # x-release-please-version +__version__ = "1.51.1" # x-release-please-version From cfd99e7b0cb0a52769a09422dfbcbf39f3b56652 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:43:09 +0000 Subject: [PATCH 635/914] chore: add repr to PageInfo class (#1780) --- src/openai/_base_client.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 931cbd8534..b2929df072 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -144,6 +144,12 @@ def __init__( self.url = url self.params = params + @override + def __repr__(self) -> str: + if self.url: + return f"{self.__class__.__name__}(url={self.url})" + return f"{self.__class__.__name__}(params={self.params})" + class BasePage(GenericModel, Generic[_T]): """ From af8e0ad8b7988e72e1d02478022d80eff4f55b91 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 05:04:18 +0000 Subject: [PATCH 636/914] release: 1.51.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3462272dee..395be07b55 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.51.1" + ".": "1.51.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b85350d7..8f583fe89f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.51.2 (2024-10-08) + +Full Changelog: [v1.51.1...v1.51.2](https://github.com/openai/openai-python/compare/v1.51.1...v1.51.2) + +### Chores + +* add repr to PageInfo class ([#1780](https://github.com/openai/openai-python/issues/1780)) ([63118ee](https://github.com/openai/openai-python/commit/63118ee3c2481d217682e8a31337bdcc16893127)) + ## 1.51.1 (2024-10-07) Full Changelog: [v1.51.0...v1.51.1](https://github.com/openai/openai-python/compare/v1.51.0...v1.51.1) diff --git a/pyproject.toml b/pyproject.toml index 41b3c18f01..8da5e0e2ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.51.1" +version = "1.51.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ce7c45ab73..cb98e08a8a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.51.1" # x-release-please-version +__version__ = "1.51.2" # x-release-please-version From 550b855fe51dd3fad4e1c5c796d7c35b8e603d2f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:49:46 +0000 Subject: [PATCH 637/914] feat(api): add gpt-4o-audio-preview model for chat completions (#1796) This enables audio inputs and outputs. https://platform.openai.com/docs/guides/audio --- .stats.yml | 2 +- api.md | 4 + src/openai/resources/beta/chat/completions.py | 18 ++ src/openai/resources/chat/completions.py | 207 +++++++++++++++--- .../types/beta/assistant_stream_event.py | 5 +- src/openai/types/chat/__init__.py | 6 + ...chat_completion_assistant_message_param.py | 14 +- .../types/chat/chat_completion_audio.py | 27 +++ .../types/chat/chat_completion_audio_param.py | 21 ++ ...mpletion_content_part_input_audio_param.py | 22 ++ .../chat_completion_content_part_param.py | 3 +- .../types/chat/chat_completion_message.py | 8 + .../types/chat/chat_completion_modality.py | 7 + .../types/chat/completion_create_params.py | 30 ++- src/openai/types/chat_model.py | 3 + tests/api_resources/chat/test_completions.py | 20 ++ tests/lib/chat/test_completions.py | 15 ++ tests/lib/chat/test_completions_streaming.py | 14 ++ 18 files changed, 388 insertions(+), 38 deletions(-) create mode 100644 src/openai/types/chat/chat_completion_audio.py create mode 100644 src/openai/types/chat/chat_completion_audio_param.py create mode 100644 src/openai/types/chat/chat_completion_content_part_input_audio_param.py create mode 100644 src/openai/types/chat/chat_completion_modality.py diff --git a/.stats.yml b/.stats.yml index ece287351b..984e8a8d5f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-52b934aee6468039ec7f4ce046a282b5fbce114afc708e70f17121df654f71da.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-8729aaa35436531ab453224af10e67f89677db8f350f0346bb3537489edea649.yml diff --git a/api.md b/api.md index 813c5cb615..f44169665d 100644 --- a/api.md +++ b/api.md @@ -39,9 +39,12 @@ Types: from openai.types.chat import ( ChatCompletion, ChatCompletionAssistantMessageParam, + ChatCompletionAudio, + ChatCompletionAudioParam, ChatCompletionChunk, ChatCompletionContentPart, ChatCompletionContentPartImage, + ChatCompletionContentPartInputAudio, ChatCompletionContentPartRefusal, ChatCompletionContentPartText, ChatCompletionFunctionCallOption, @@ -49,6 +52,7 @@ from openai.types.chat import ( ChatCompletionMessage, ChatCompletionMessageParam, ChatCompletionMessageToolCall, + ChatCompletionModality, ChatCompletionNamedToolChoice, ChatCompletionRole, ChatCompletionStreamOptions, diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index c747464072..47bcf42c16 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -28,7 +28,9 @@ from ....types.chat.chat_completion import ChatCompletion from ....types.chat.chat_completion_chunk import ChatCompletionChunk from ....types.chat.parsed_chat_completion import ParsedChatCompletion +from ....types.chat.chat_completion_modality import ChatCompletionModality from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam +from ....types.chat.chat_completion_audio_param import ChatCompletionAudioParam from ....types.chat.chat_completion_message_param import ChatCompletionMessageParam from ....types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from ....types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam @@ -61,6 +63,7 @@ def parse( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, response_format: type[ResponseFormatT] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, @@ -70,6 +73,7 @@ def parse( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -153,6 +157,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma { "messages": messages, "model": model, + "audio": audio, "frequency_penalty": frequency_penalty, "function_call": function_call, "functions": functions, @@ -161,6 +166,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, "metadata": metadata, + "modalities": modalities, "n": n, "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, @@ -198,6 +204,7 @@ def stream( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, @@ -207,6 +214,7 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -259,6 +267,7 @@ def stream( self._client.chat.completions.create, messages=messages, model=model, + audio=audio, stream=True, response_format=_type_to_response_format(response_format), frequency_penalty=frequency_penalty, @@ -269,6 +278,7 @@ def stream( max_completion_tokens=max_completion_tokens, max_tokens=max_tokens, metadata=metadata, + modalities=modalities, n=n, parallel_tool_calls=parallel_tool_calls, presence_penalty=presence_penalty, @@ -320,6 +330,7 @@ async def parse( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, response_format: type[ResponseFormatT] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, @@ -329,6 +340,7 @@ async def parse( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -412,6 +424,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma { "messages": messages, "model": model, + "audio": audio, "frequency_penalty": frequency_penalty, "function_call": function_call, "functions": functions, @@ -420,6 +433,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, "metadata": metadata, + "modalities": modalities, "n": n, "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, @@ -457,6 +471,7 @@ def stream( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, @@ -466,6 +481,7 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -519,6 +535,7 @@ def stream( api_request = self._client.chat.completions.create( messages=messages, model=model, + audio=audio, stream=True, response_format=_type_to_response_format(response_format), frequency_penalty=frequency_penalty, @@ -529,6 +546,7 @@ def stream( max_completion_tokens=max_completion_tokens, max_tokens=max_tokens, metadata=metadata, + modalities=modalities, n=n, parallel_tool_calls=parallel_tool_calls, presence_penalty=presence_penalty, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index c8080afaa1..ceaf3c2fec 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -20,12 +20,17 @@ from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..._streaming import Stream, AsyncStream -from ...types.chat import completion_create_params +from ...types.chat import ( + ChatCompletionAudioParam, + completion_create_params, +) from ..._base_client import make_request_options from ...types.chat_model import ChatModel from ...types.chat.chat_completion import ChatCompletion from ...types.chat.chat_completion_chunk import ChatCompletionChunk +from ...types.chat.chat_completion_modality import ChatCompletionModality from ...types.chat.chat_completion_tool_param import ChatCompletionToolParam +from ...types.chat.chat_completion_audio_param import ChatCompletionAudioParam from ...types.chat.chat_completion_message_param import ChatCompletionMessageParam from ...types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from ...types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam @@ -59,6 +64,7 @@ def create( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -67,6 +73,7 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -90,8 +97,12 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion: - """ - Creates a model response for the given chat conversation. + """Creates a model response for the given chat conversation. + + Learn more in the + [text generation](https://platform.openai.com/docs/guides/text-generation), + [vision](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio) guides. Args: messages: A list of messages comprising the conversation so far. Depending on the @@ -105,6 +116,10 @@ def create( [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) table for details on which models work with the Chat API. + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://platform.openai.com/docs/guides/audio). + frequency_penalty: Number between -2.0 and 2.0. 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. @@ -152,7 +167,18 @@ def create( [o1 series models](https://platform.openai.com/docs/guides/reasoning). metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/completions). + [dashboard](https://platform.openai.com/chat-completions). + + modalities: Output types that you would like the model to generate for this request. Most + models are capable of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://platform.openai.com/docs/guides/audio). To request that + this model generate both text and audio responses, you can use: + + `["text", "audio"]` n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -213,8 +239,9 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. - store: Whether or not to store the output of this completion request for traffic - logging in the [dashboard](https://platform.openai.com/completions). + store: Whether or not to store the output of this chat completion request for use in + our [model distillation](https://platform.openai.com/docs/guides/distillation) + or [evals](https://platform.openai.com/docs/guides/evals) products. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only @@ -276,6 +303,7 @@ def create( messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], stream: Literal[True], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -284,6 +312,7 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -306,8 +335,12 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Stream[ChatCompletionChunk]: - """ - Creates a model response for the given chat conversation. + """Creates a model response for the given chat conversation. + + Learn more in the + [text generation](https://platform.openai.com/docs/guides/text-generation), + [vision](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio) guides. Args: messages: A list of messages comprising the conversation so far. Depending on the @@ -328,6 +361,10 @@ def create( message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://platform.openai.com/docs/guides/audio). + frequency_penalty: Number between -2.0 and 2.0. 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. @@ -375,7 +412,18 @@ def create( [o1 series models](https://platform.openai.com/docs/guides/reasoning). metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/completions). + [dashboard](https://platform.openai.com/chat-completions). + + modalities: Output types that you would like the model to generate for this request. Most + models are capable of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://platform.openai.com/docs/guides/audio). To request that + this model generate both text and audio responses, you can use: + + `["text", "audio"]` n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -436,8 +484,9 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. - store: Whether or not to store the output of this completion request for traffic - logging in the [dashboard](https://platform.openai.com/completions). + store: Whether or not to store the output of this chat completion request for use in + our [model distillation](https://platform.openai.com/docs/guides/distillation) + or [evals](https://platform.openai.com/docs/guides/evals) products. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -492,6 +541,7 @@ def create( messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], stream: bool, + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -500,6 +550,7 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -522,8 +573,12 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | Stream[ChatCompletionChunk]: - """ - Creates a model response for the given chat conversation. + """Creates a model response for the given chat conversation. + + Learn more in the + [text generation](https://platform.openai.com/docs/guides/text-generation), + [vision](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio) guides. Args: messages: A list of messages comprising the conversation so far. Depending on the @@ -544,6 +599,10 @@ def create( message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://platform.openai.com/docs/guides/audio). + frequency_penalty: Number between -2.0 and 2.0. 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. @@ -591,7 +650,18 @@ def create( [o1 series models](https://platform.openai.com/docs/guides/reasoning). metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/completions). + [dashboard](https://platform.openai.com/chat-completions). + + modalities: Output types that you would like the model to generate for this request. Most + models are capable of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://platform.openai.com/docs/guides/audio). To request that + this model generate both text and audio responses, you can use: + + `["text", "audio"]` n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -652,8 +722,9 @@ def create( stop: Up to 4 sequences where the API will stop generating further tokens. - store: Whether or not to store the output of this completion request for traffic - logging in the [dashboard](https://platform.openai.com/completions). + store: Whether or not to store the output of this chat completion request for use in + our [model distillation](https://platform.openai.com/docs/guides/distillation) + or [evals](https://platform.openai.com/docs/guides/evals) products. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -707,6 +778,7 @@ def create( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -715,6 +787,7 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -745,6 +818,7 @@ def create( { "messages": messages, "model": model, + "audio": audio, "frequency_penalty": frequency_penalty, "function_call": function_call, "functions": functions, @@ -753,6 +827,7 @@ def create( "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, "metadata": metadata, + "modalities": modalities, "n": n, "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, @@ -807,6 +882,7 @@ async def create( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -815,6 +891,7 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -838,8 +915,12 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion: - """ - Creates a model response for the given chat conversation. + """Creates a model response for the given chat conversation. + + Learn more in the + [text generation](https://platform.openai.com/docs/guides/text-generation), + [vision](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio) guides. Args: messages: A list of messages comprising the conversation so far. Depending on the @@ -853,6 +934,10 @@ async def create( [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) table for details on which models work with the Chat API. + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://platform.openai.com/docs/guides/audio). + frequency_penalty: Number between -2.0 and 2.0. 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. @@ -900,7 +985,18 @@ async def create( [o1 series models](https://platform.openai.com/docs/guides/reasoning). metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/completions). + [dashboard](https://platform.openai.com/chat-completions). + + modalities: Output types that you would like the model to generate for this request. Most + models are capable of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://platform.openai.com/docs/guides/audio). To request that + this model generate both text and audio responses, you can use: + + `["text", "audio"]` n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -961,8 +1057,9 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. - store: Whether or not to store the output of this completion request for traffic - logging in the [dashboard](https://platform.openai.com/completions). + store: Whether or not to store the output of this chat completion request for use in + our [model distillation](https://platform.openai.com/docs/guides/distillation) + or [evals](https://platform.openai.com/docs/guides/evals) products. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only @@ -1024,6 +1121,7 @@ async def create( messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], stream: Literal[True], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -1032,6 +1130,7 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -1054,8 +1153,12 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncStream[ChatCompletionChunk]: - """ - Creates a model response for the given chat conversation. + """Creates a model response for the given chat conversation. + + Learn more in the + [text generation](https://platform.openai.com/docs/guides/text-generation), + [vision](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio) guides. Args: messages: A list of messages comprising the conversation so far. Depending on the @@ -1076,6 +1179,10 @@ async def create( message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://platform.openai.com/docs/guides/audio). + frequency_penalty: Number between -2.0 and 2.0. 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. @@ -1123,7 +1230,18 @@ async def create( [o1 series models](https://platform.openai.com/docs/guides/reasoning). metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/completions). + [dashboard](https://platform.openai.com/chat-completions). + + modalities: Output types that you would like the model to generate for this request. Most + models are capable of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://platform.openai.com/docs/guides/audio). To request that + this model generate both text and audio responses, you can use: + + `["text", "audio"]` n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -1184,8 +1302,9 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. - store: Whether or not to store the output of this completion request for traffic - logging in the [dashboard](https://platform.openai.com/completions). + store: Whether or not to store the output of this chat completion request for use in + our [model distillation](https://platform.openai.com/docs/guides/distillation) + or [evals](https://platform.openai.com/docs/guides/evals) products. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -1240,6 +1359,7 @@ async def create( messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], stream: bool, + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -1248,6 +1368,7 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -1270,8 +1391,12 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: - """ - Creates a model response for the given chat conversation. + """Creates a model response for the given chat conversation. + + Learn more in the + [text generation](https://platform.openai.com/docs/guides/text-generation), + [vision](https://platform.openai.com/docs/guides/vision), and + [audio](https://platform.openai.com/docs/guides/audio) guides. Args: messages: A list of messages comprising the conversation so far. Depending on the @@ -1292,6 +1417,10 @@ async def create( message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + audio: Parameters for audio output. Required when audio output is requested with + `modalities: ["audio"]`. + [Learn more](https://platform.openai.com/docs/guides/audio). + frequency_penalty: Number between -2.0 and 2.0. 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. @@ -1339,7 +1468,18 @@ async def create( [o1 series models](https://platform.openai.com/docs/guides/reasoning). metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/completions). + [dashboard](https://platform.openai.com/chat-completions). + + modalities: Output types that you would like the model to generate for this request. Most + models are capable of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://platform.openai.com/docs/guides/audio). To request that + this model generate both text and audio responses, you can use: + + `["text", "audio"]` n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the @@ -1400,8 +1540,9 @@ async def create( stop: Up to 4 sequences where the API will stop generating further tokens. - store: Whether or not to store the output of this completion request for traffic - logging in the [dashboard](https://platform.openai.com/completions). + store: Whether or not to store the output of this chat completion request for use in + our [model distillation](https://platform.openai.com/docs/guides/distillation) + or [evals](https://platform.openai.com/docs/guides/evals) products. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -1455,6 +1596,7 @@ async def create( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, @@ -1463,6 +1605,7 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -1493,6 +1636,7 @@ async def create( { "messages": messages, "model": model, + "audio": audio, "frequency_penalty": frequency_penalty, "function_call": function_call, "functions": functions, @@ -1501,6 +1645,7 @@ async def create( "max_completion_tokens": max_completion_tokens, "max_tokens": max_tokens, "metadata": metadata, + "modalities": modalities, "n": n, "parallel_tool_calls": parallel_tool_calls, "presence_penalty": presence_penalty, diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py index f1d8898ff2..41d3a0c5ea 100644 --- a/src/openai/types/beta/assistant_stream_event.py +++ b/src/openai/types/beta/assistant_stream_event.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union +from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from .thread import Thread @@ -51,6 +51,9 @@ class ThreadCreated(BaseModel): event: Literal["thread.created"] + enabled: Optional[bool] = None + """Whether to enable input audio transcription.""" + class ThreadRunCreated(BaseModel): data: Run diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index a5cf3734b8..eb818a132e 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -4,6 +4,7 @@ from .chat_completion import ChatCompletion as ChatCompletion from .chat_completion_role import ChatCompletionRole as ChatCompletionRole +from .chat_completion_audio import ChatCompletionAudio as ChatCompletionAudio from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk from .parsed_chat_completion import ( ParsedChoice as ParsedChoice, @@ -11,12 +12,14 @@ ParsedChatCompletionMessage as ParsedChatCompletionMessage, ) from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage +from .chat_completion_modality import ChatCompletionModality as ChatCompletionModality from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .parsed_function_tool_call import ( ParsedFunction as ParsedFunction, ParsedFunctionToolCall as ParsedFunctionToolCall, ) from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam +from .chat_completion_audio_param import ChatCompletionAudioParam as ChatCompletionAudioParam from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall @@ -52,3 +55,6 @@ from .chat_completion_function_call_option_param import ( ChatCompletionFunctionCallOptionParam as ChatCompletionFunctionCallOptionParam, ) +from .chat_completion_content_part_input_audio_param import ( + ChatCompletionContentPartInputAudioParam as ChatCompletionContentPartInputAudioParam, +) diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 2429d41d33..35e3a3d784 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -9,7 +9,13 @@ from .chat_completion_message_tool_call_param import ChatCompletionMessageToolCallParam from .chat_completion_content_part_refusal_param import ChatCompletionContentPartRefusalParam -__all__ = ["ChatCompletionAssistantMessageParam", "ContentArrayOfContentPart", "FunctionCall"] +__all__ = ["ChatCompletionAssistantMessageParam", "Audio", "ContentArrayOfContentPart", "FunctionCall"] + + +class Audio(TypedDict, total=False): + id: Required[str] + """Unique identifier for a previous audio response from the model.""" + ContentArrayOfContentPart: TypeAlias = Union[ChatCompletionContentPartTextParam, ChatCompletionContentPartRefusalParam] @@ -31,6 +37,12 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): role: Required[Literal["assistant"]] """The role of the messages author, in this case `assistant`.""" + audio: Optional[Audio] + """Data about a previous audio response from the model. + + [Learn more](https://platform.openai.com/docs/guides/audio). + """ + content: Union[str, Iterable[ContentArrayOfContentPart], None] """The contents of the assistant message. diff --git a/src/openai/types/chat/chat_completion_audio.py b/src/openai/types/chat/chat_completion_audio.py new file mode 100644 index 0000000000..135ee8845c --- /dev/null +++ b/src/openai/types/chat/chat_completion_audio.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + + +from ..._models import BaseModel + +__all__ = ["ChatCompletionAudio"] + + +class ChatCompletionAudio(BaseModel): + id: str + """Unique identifier for this audio response.""" + + data: str + """ + Base64 encoded audio bytes generated by the model, in the format specified in + the request. + """ + + expires_at: int + """ + The Unix timestamp (in seconds) for when this audio response will no longer be + accessible on the server for use in multi-turn conversations. + """ + + transcript: str + """Transcript of the audio generated by the model.""" diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py new file mode 100644 index 0000000000..6a4ce9ac1f --- /dev/null +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionAudioParam"] + + +class ChatCompletionAudioParam(TypedDict, total=False): + format: Required[Literal["wav", "mp3", "flac", "opus", "pcm16"]] + """Specifies the output audio format. + + Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. + """ + + voice: Required[Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]] + """Specifies the voice type. + + Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. + """ diff --git a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py new file mode 100644 index 0000000000..0b1b1a80b1 --- /dev/null +++ b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionContentPartInputAudioParam", "InputAudio"] + + +class InputAudio(TypedDict, total=False): + data: Required[str] + """Base64 encoded audio data.""" + + format: Required[Literal["wav", "mp3"]] + """The format of the encoded audio data. Currently supports "wav" and "mp3".""" + + +class ChatCompletionContentPartInputAudioParam(TypedDict, total=False): + input_audio: Required[InputAudio] + + type: Required[Literal["input_audio"]] + """The type of the content part. Always `input_audio`.""" diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index e0c6e480f2..682d11f4c7 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -7,9 +7,10 @@ from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam from .chat_completion_content_part_image_param import ChatCompletionContentPartImageParam +from .chat_completion_content_part_input_audio_param import ChatCompletionContentPartInputAudioParam __all__ = ["ChatCompletionContentPartParam"] ChatCompletionContentPartParam: TypeAlias = Union[ - ChatCompletionContentPartTextParam, ChatCompletionContentPartImageParam + ChatCompletionContentPartTextParam, ChatCompletionContentPartImageParam, ChatCompletionContentPartInputAudioParam ] diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py index 492bb68c85..704fa5d5d1 100644 --- a/src/openai/types/chat/chat_completion_message.py +++ b/src/openai/types/chat/chat_completion_message.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from .chat_completion_audio import ChatCompletionAudio from .chat_completion_message_tool_call import ChatCompletionMessageToolCall __all__ = ["ChatCompletionMessage", "FunctionCall"] @@ -32,6 +33,13 @@ class ChatCompletionMessage(BaseModel): role: Literal["assistant"] """The role of the author of this message.""" + audio: Optional[ChatCompletionAudio] = None + """ + If the audio output modality is requested, this object contains data about the + audio response from the model. + [Learn more](https://platform.openai.com/docs/guides/audio). + """ + function_call: Optional[FunctionCall] = None """Deprecated and replaced by `tool_calls`. diff --git a/src/openai/types/chat/chat_completion_modality.py b/src/openai/types/chat/chat_completion_modality.py new file mode 100644 index 0000000000..8e3c145979 --- /dev/null +++ b/src/openai/types/chat/chat_completion_modality.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatCompletionModality"] + +ChatCompletionModality: TypeAlias = Literal["text", "audio"] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 3f55dfbe6e..af6a47c219 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -6,7 +6,9 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..chat_model import ChatModel +from .chat_completion_modality import ChatCompletionModality from .chat_completion_tool_param import ChatCompletionToolParam +from .chat_completion_audio_param import ChatCompletionAudioParam from .chat_completion_message_param import ChatCompletionMessageParam from ..shared_params.function_parameters import FunctionParameters from ..shared_params.response_format_text import ResponseFormatText @@ -45,6 +47,13 @@ class CompletionCreateParamsBase(TypedDict, total=False): table for details on which models work with the Chat API. """ + audio: Optional[ChatCompletionAudioParam] + """Parameters for audio output. + + Required when audio output is requested with `modalities: ["audio"]`. + [Learn more](https://platform.openai.com/docs/guides/audio). + """ + frequency_penalty: Optional[float] """Number between -2.0 and 2.0. @@ -112,7 +121,21 @@ class CompletionCreateParamsBase(TypedDict, total=False): metadata: Optional[Dict[str, str]] """ Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/completions). + [dashboard](https://platform.openai.com/chat-completions). + """ + + modalities: Optional[List[ChatCompletionModality]] + """ + Output types that you would like the model to generate for this request. Most + models are capable of generating text, which is the default: + + `["text"]` + + The `gpt-4o-audio-preview` model can also be used to + [generate audio](https://platform.openai.com/docs/guides/audio). To request that + this model generate both text and audio responses, you can use: + + `["text", "audio"]` """ n: Optional[int] @@ -195,8 +218,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): store: Optional[bool] """ - Whether or not to store the output of this completion request for traffic - logging in the [dashboard](https://platform.openai.com/completions). + Whether or not to store the output of this chat completion request for use in + our [model distillation](https://platform.openai.com/docs/guides/distillation) + or [evals](https://platform.openai.com/docs/guides/evals) products. """ stream_options: Optional[ChatCompletionStreamOptionsParam] diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index f2d5674786..b801aa0914 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -12,7 +12,10 @@ "gpt-4o", "gpt-4o-2024-08-06", "gpt-4o-2024-05-13", + "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", "chatgpt-4o-latest", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 8c1f263f54..d7162dc7db 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -44,6 +44,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: } ], model="gpt-4o", + audio={ + "format": "wav", + "voice": "alloy", + }, frequency_penalty=-2, function_call="none", functions=[ @@ -58,6 +62,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_completion_tokens=0, max_tokens=0, metadata={"foo": "string"}, + modalities=["text", "audio"], n=1, parallel_tool_calls=True, presence_penalty=-2, @@ -167,6 +172,10 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: ], model="gpt-4o", stream=True, + audio={ + "format": "wav", + "voice": "alloy", + }, frequency_penalty=-2, function_call="none", functions=[ @@ -181,6 +190,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_completion_tokens=0, max_tokens=0, metadata={"foo": "string"}, + modalities=["text", "audio"], n=1, parallel_tool_calls=True, presence_penalty=-2, @@ -309,6 +319,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn } ], model="gpt-4o", + audio={ + "format": "wav", + "voice": "alloy", + }, frequency_penalty=-2, function_call="none", functions=[ @@ -323,6 +337,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_completion_tokens=0, max_tokens=0, metadata={"foo": "string"}, + modalities=["text", "audio"], n=1, parallel_tool_calls=True, presence_penalty=-2, @@ -432,6 +447,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn ], model="gpt-4o", stream=True, + audio={ + "format": "wav", + "voice": "alloy", + }, frequency_penalty=-2, function_call="none", functions=[ @@ -446,6 +465,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_completion_tokens=0, max_tokens=0, metadata={"foo": "string"}, + modalities=["text", "audio"], n=1, parallel_tool_calls=True, presence_penalty=-2, diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index 3cfea71f11..5cd7b1ee53 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -58,6 +58,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + audio=None, content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend checking a reliable weather website or app like the Weather Channel or a local news station.", function_call=None, @@ -120,6 +121,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=65.0, units='f'), @@ -183,6 +185,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=65.0, units='f'), @@ -248,6 +251,7 @@ class ColorDetection(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[ColorDetection]( + audio=None, content='{"color":"red","hex_color_code":"#FF0000"}', function_call=None, parsed=ColorDetection(color=, hex_color_code='#FF0000'), @@ -296,6 +300,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":64,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=64.0, units='f'), @@ -309,6 +314,7 @@ class Location(BaseModel): index=1, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=65.0, units='f'), @@ -322,6 +328,7 @@ class Location(BaseModel): index=2, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":63.0,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=63.0, units='f'), @@ -371,6 +378,7 @@ class CalendarEvent: index=0, logprobs=None, message=ParsedChatCompletionMessage[CalendarEvent]( + audio=None, content='{"name":"Science Fair","date":"Friday","participants":["Alice","Bob"]}', function_call=None, parsed=CalendarEvent(name='Science Fair', date='Friday', participants=['Alice', 'Bob']), @@ -426,6 +434,7 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m index=0, logprobs=None, message=ParsedChatCompletionMessage[Query]( + audio=None, content=None, function_call=None, parsed=None, @@ -536,6 +545,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content=None, function_call=None, parsed=None, @@ -584,6 +594,7 @@ class GetWeatherArgs(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + audio=None, content=None, function_call=None, parsed=None, @@ -655,6 +666,7 @@ class GetStockPrice(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + audio=None, content=None, function_call=None, parsed=None, @@ -735,6 +747,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + audio=None, content=None, function_call=None, parsed=None, @@ -816,6 +829,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":58,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=58.0, units='f'), @@ -885,6 +899,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=65.0, units='f'), diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 216fe9ddf5..2846e6d2c3 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -61,6 +61,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + audio=None, content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend checking a reliable weather website or a weather app.", function_call=None, @@ -138,6 +139,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":61,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=61.0, units='f'), @@ -309,6 +311,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=65.0, units='f'), @@ -322,6 +325,7 @@ class Location(BaseModel): index=1, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":61,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=61.0, units='f'), @@ -335,6 +339,7 @@ class Location(BaseModel): index=2, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content='{"city":"San Francisco","temperature":59,"units":"f"}', function_call=None, parsed=Location(city='San Francisco', temperature=59.0, units='f'), @@ -409,6 +414,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + audio=None, content=None, function_call=None, parsed=None, @@ -482,6 +488,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp refusal=None ), message=ParsedChatCompletionMessage[NoneType]( + audio=None, content='Foo!', function_call=None, parsed=None, @@ -592,6 +599,7 @@ class Location(BaseModel): ] ), message=ParsedChatCompletionMessage[Location]( + audio=None, content=None, function_call=None, parsed=None, @@ -637,6 +645,7 @@ class GetWeatherArgs(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[object]( + audio=None, content=None, function_call=None, parsed=None, @@ -668,6 +677,7 @@ class GetWeatherArgs(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + audio=None, content=None, function_call=None, parsed=None, @@ -738,6 +748,7 @@ class GetStockPrice(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[object]( + audio=None, content=None, function_call=None, parsed=None, @@ -845,6 +856,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: index=0, logprobs=None, message=ParsedChatCompletionMessage[object]( + audio=None, content=None, function_call=None, parsed=None, @@ -895,6 +907,7 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + audio=None, content='\\n {\\n "location": "San Francisco, CA",\\n "weather": {\\n "temperature": "18°C",\\n "condition": "Partly Cloudy",\\n "humidity": "72%",\\n "windSpeed": "15 km/h",\\n "windDirection": "NW"\\n },\\n "forecast": [\\n {\\n "day": "Monday",\\n "high": "20°C",\\n "low": "14°C",\\n @@ -954,6 +967,7 @@ def test_allows_non_strict_tools_but_no_parsing( index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + audio=None, content=None, function_call=None, parsed=None, From fc4fce30aff7620c32e57301d3c40b4883810825 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:51:33 +0000 Subject: [PATCH 638/914] release: 1.52.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 395be07b55..37d774d68f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.51.2" + ".": "1.52.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f583fe89f..55691f3fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.52.0 (2024-10-17) + +Full Changelog: [v1.51.2...v1.52.0](https://github.com/openai/openai-python/compare/v1.51.2...v1.52.0) + +### Features + +* **api:** add gpt-4o-audio-preview model for chat completions ([#1796](https://github.com/openai/openai-python/issues/1796)) ([fbf1e0c](https://github.com/openai/openai-python/commit/fbf1e0c25c4d163f06b61a43d1a94ce001033a7b)) + ## 1.51.2 (2024-10-08) Full Changelog: [v1.51.1...v1.51.2](https://github.com/openai/openai-python/compare/v1.51.1...v1.51.2) diff --git a/pyproject.toml b/pyproject.toml index 8da5e0e2ce..490685d76d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.51.2" +version = "1.52.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index cb98e08a8a..cb5e5ffd0d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.51.2" # x-release-please-version +__version__ = "1.52.0" # x-release-please-version From 8b5176139e93820d88f89e765685257f175bf77f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:40:28 +0000 Subject: [PATCH 639/914] chore(internal): update test syntax (#1798) --- tests/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_models.py b/tests/test_models.py index b703444248..117a90020e 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -245,7 +245,7 @@ class Model(BaseModel): assert m.foo is True m = Model.construct(foo="CARD_HOLDER") - assert m.foo is "CARD_HOLDER" + assert m.foo == "CARD_HOLDER" m = Model.construct(foo={"bar": False}) assert isinstance(m.foo, Submodel1) From 38d87a3d50b00d460401bb5c7ce023102f843d16 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Fri, 18 Oct 2024 18:16:03 +0000 Subject: [PATCH 640/914] chore(internal): bump ruff dependency (#1801) --- pyproject.toml | 3 ++- requirements-dev.lock | 2 +- src/openai/types/chat/chat_completion_audio.py | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 490685d76d..900fbdda8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,11 +74,12 @@ format = { chain = [ "format:ruff", "format:docs", "fix:ruff", + # run formatting again to fix any inconsistencies when imports are stripped + "format:ruff", ]} "format:black" = "black ." "format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" "format:ruff" = "ruff format" -"format:isort" = "isort ." "lint" = { chain = [ "check:ruff", diff --git a/requirements-dev.lock b/requirements-dev.lock index a47de9656a..902a80c6ed 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -141,7 +141,7 @@ requests==2.31.0 respx==0.20.2 rich==13.7.1 # via inline-snapshot -ruff==0.6.5 +ruff==0.6.9 setuptools==68.2.2 # via nodeenv six==1.16.0 diff --git a/src/openai/types/chat/chat_completion_audio.py b/src/openai/types/chat/chat_completion_audio.py index 135ee8845c..dd15508ebb 100644 --- a/src/openai/types/chat/chat_completion_audio.py +++ b/src/openai/types/chat/chat_completion_audio.py @@ -1,7 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["ChatCompletionAudio"] From a366120a4ef9b0865a039230161a6f7ee0885ff5 Mon Sep 17 00:00:00 2001 From: fzyzcjy <5236035+fzyzcjy@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:43:12 +0800 Subject: [PATCH 641/914] fix(client/async): correctly retry in all cases (#1803) --- src/openai/_base_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index b2929df072..e1d4849ae2 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1591,7 +1591,7 @@ async def _request( except Exception as err: log.debug("Encountered Exception", exc_info=True) - if retries_taken > 0: + if remaining_retries > 0: return await self._retry_request( input_options, cast_to, From 6634f5250c98a2487a29458e997c6f9e9ee66e02 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 07:23:51 +0000 Subject: [PATCH 642/914] chore(tests): add more retry tests (#1806) --- tests/test_client.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 1da35ddd22..ff07ec393b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -10,6 +10,7 @@ import tracemalloc from typing import Any, Union, cast from unittest import mock +from typing_extensions import Literal import httpx import pytest @@ -764,7 +765,14 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retries_taken(self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter) -> None: + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + def test_retries_taken( + self, + client: OpenAI, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: client = client.with_options(max_retries=4) nb_retries = 0 @@ -773,6 +781,8 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") return httpx.Response(500) return httpx.Response(200) @@ -1623,8 +1633,13 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( - self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter + self, + async_client: AsyncOpenAI, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, ) -> None: client = async_client.with_options(max_retries=4) @@ -1634,6 +1649,8 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") return httpx.Response(500) return httpx.Response(200) From b186bc5786b75fb06260eadc32581438c0e61ea1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:54:15 +0000 Subject: [PATCH 643/914] chore(internal): remove unused black config (#1807) --- pyproject.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 900fbdda8c..4de8d3889b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,7 +77,6 @@ format = { chain = [ # run formatting again to fix any inconsistencies when imports are stripped "format:ruff", ]} -"format:black" = "black ." "format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" "format:ruff" = "ruff format" @@ -137,10 +136,6 @@ path = "README.md" pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' replacement = '[\1](https://github.com/openai/openai-python/tree/main/\g<2>)' -[tool.black] -line-length = 120 -target-version = ["py37"] - [tool.pytest.ini_options] testpaths = ["tests"] addopts = "--tb=short" From 73d20deb4792c13cbbf1861aef31c9a1123568d8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:24:19 +0000 Subject: [PATCH 644/914] chore(internal): update spec version (#1810) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 984e8a8d5f..e1a430e50a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-8729aaa35436531ab453224af10e67f89677db8f350f0346bb3537489edea649.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-f9320ebf347140052c7f8b0bc5c7db24f5e367c368c8cb34c3606af4e2b6591b.yml From 262f163cf19f22f45c493de0238a77c9e39f9e0a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:24:50 +0000 Subject: [PATCH 645/914] release: 1.52.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 17 +++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 37d774d68f..89493ef540 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.52.0" + ".": "1.52.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 55691f3fd7..c00cc6e66d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 1.52.1 (2024-10-22) + +Full Changelog: [v1.52.0...v1.52.1](https://github.com/openai/openai-python/compare/v1.52.0...v1.52.1) + +### Bug Fixes + +* **client/async:** correctly retry in all cases ([#1803](https://github.com/openai/openai-python/issues/1803)) ([9fe3f3f](https://github.com/openai/openai-python/commit/9fe3f3f925e06769b7ef6abbf1314a5e82749b4a)) + + +### Chores + +* **internal:** bump ruff dependency ([#1801](https://github.com/openai/openai-python/issues/1801)) ([859c672](https://github.com/openai/openai-python/commit/859c6725865f1b3285698f68693f9491d511f7ea)) +* **internal:** remove unused black config ([#1807](https://github.com/openai/openai-python/issues/1807)) ([112dab0](https://github.com/openai/openai-python/commit/112dab0290342654265db612c37d327d652251bb)) +* **internal:** update spec version ([#1810](https://github.com/openai/openai-python/issues/1810)) ([aa25b7b](https://github.com/openai/openai-python/commit/aa25b7b88823836b418a63da59491f5f3842773c)) +* **internal:** update test syntax ([#1798](https://github.com/openai/openai-python/issues/1798)) ([d3098dd](https://github.com/openai/openai-python/commit/d3098dd0b9fbe627c21a8ad39c119d125b7cdb54)) +* **tests:** add more retry tests ([#1806](https://github.com/openai/openai-python/issues/1806)) ([5525a1b](https://github.com/openai/openai-python/commit/5525a1ba536058ecc13411e1f98e88f7ec4bf8b9)) + ## 1.52.0 (2024-10-17) Full Changelog: [v1.51.2...v1.52.0](https://github.com/openai/openai-python/compare/v1.51.2...v1.52.0) diff --git a/pyproject.toml b/pyproject.toml index 4de8d3889b..3ebb38343e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.52.0" +version = "1.52.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index cb5e5ffd0d..a027560c44 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.52.0" # x-release-please-version +__version__ = "1.52.1" # x-release-please-version From 865e390b537405ff16019ecac141eda9fd3a2a09 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 21:57:02 +0000 Subject: [PATCH 646/914] chore(internal): update spec version (#1816) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index e1a430e50a..0b08725565 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-f9320ebf347140052c7f8b0bc5c7db24f5e367c368c8cb34c3606af4e2b6591b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-b60d5559d5150ecd3b49136064e5e251d832899770ff385b711378389afba370.yml From e1b2f8216cc69e802475a0d438e40e0e74510de4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 21:57:32 +0000 Subject: [PATCH 647/914] release: 1.52.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 89493ef540..2f2a30a82e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.52.1" + ".": "1.52.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c00cc6e66d..21f6136744 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.52.2 (2024-10-23) + +Full Changelog: [v1.52.1...v1.52.2](https://github.com/openai/openai-python/compare/v1.52.1...v1.52.2) + +### Chores + +* **internal:** update spec version ([#1816](https://github.com/openai/openai-python/issues/1816)) ([c23282a](https://github.com/openai/openai-python/commit/c23282a328c48af90a88673ff5f6cc7a866f8758)) + ## 1.52.1 (2024-10-22) Full Changelog: [v1.52.0...v1.52.1](https://github.com/openai/openai-python/compare/v1.52.0...v1.52.1) diff --git a/pyproject.toml b/pyproject.toml index 3ebb38343e..3bd6246e43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.52.1" +version = "1.52.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a027560c44..641d4f5b30 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.52.1" # x-release-please-version +__version__ = "1.52.2" # x-release-please-version From 44ac1cef62316cc6c4548eb2007a93de9ddb6825 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 28 Oct 2024 12:28:17 +0000 Subject: [PATCH 648/914] chore(internal): bump pytest to v8 & pydantic (#1829) --- requirements-dev.lock | 43 +++++++++---------- requirements.lock | 18 ++++---- src/openai/_compat.py | 2 +- src/openai/_models.py | 10 ++--- src/openai/_types.py | 6 ++- src/openai/lib/streaming/chat/_completions.py | 18 +++++--- tests/conftest.py | 14 +++--- tests/lib/test_old_api.py | 2 +- 8 files changed, 60 insertions(+), 53 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 902a80c6ed..2df8a39c76 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -18,14 +18,13 @@ argcomplete==3.1.2 # via nox asttokens==2.4.1 # via inline-snapshot -attrs==23.1.0 +attrs==24.2.0 # via outcome - # via pytest # via trio -azure-core==1.30.1 +azure-core==1.31.0 # via azure-identity -azure-identity==1.15.0 -black==24.4.2 +azure-identity==1.19.0 +black==24.10.0 # via inline-snapshot certifi==2023.7.22 # via httpcore @@ -49,10 +48,11 @@ distlib==0.3.7 # via virtualenv distro==1.8.0 # via openai -exceptiongroup==1.1.3 +exceptiongroup==1.2.2 # via anyio + # via pytest # via trio -executing==2.0.1 +executing==2.1.0 # via inline-snapshot filelock==3.12.4 # via virtualenv @@ -78,7 +78,7 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -msal==1.29.0 +msal==1.31.0 # via azure-identity # via msal-extensions msal-extensions==1.2.0 @@ -109,26 +109,24 @@ pathspec==0.12.1 platformdirs==3.11.0 # via black # via virtualenv -pluggy==1.3.0 +pluggy==1.5.0 # via pytest -portalocker==2.8.2 +portalocker==2.10.1 # via msal-extensions -py==1.11.0 - # via pytest pycparser==2.22 # via cffi -pydantic==2.7.1 +pydantic==2.9.2 # via openai -pydantic-core==2.18.2 +pydantic-core==2.23.4 # via pydantic pygments==2.18.0 # via rich pyjwt==2.8.0 # via msal pyright==1.1.380 -pytest==7.1.1 +pytest==8.3.3 # via pytest-asyncio -pytest-asyncio==0.21.1 +pytest-asyncio==0.24.0 python-dateutil==2.8.2 # via pandas # via time-machine @@ -158,21 +156,22 @@ sortedcontainers==2.4.0 time-machine==2.9.0 toml==0.10.2 # via inline-snapshot -tomli==2.0.1 +tomli==2.0.2 # via black # via mypy # via pytest -tqdm==4.66.1 +tqdm==4.66.5 # via openai -trio==0.22.2 -types-pyaudio==0.2.16.20240106 -types-pytz==2024.1.0.20240417 +trio==0.27.0 +types-pyaudio==0.2.16.20240516 +types-pytz==2024.2.0.20241003 # via pandas-stubs types-toml==0.10.8.20240310 # via inline-snapshot -types-tqdm==4.66.0.2 +types-tqdm==4.66.0.20240417 typing-extensions==4.12.2 # via azure-core + # via azure-identity # via black # via mypy # via openai diff --git a/requirements.lock b/requirements.lock index de632aefbd..019dfcb4c5 100644 --- a/requirements.lock +++ b/requirements.lock @@ -19,7 +19,7 @@ certifi==2023.7.22 # via httpx distro==1.8.0 # via openai -exceptiongroup==1.1.3 +exceptiongroup==1.2.2 # via anyio h11==0.14.0 # via httpcore @@ -30,19 +30,19 @@ httpx==0.25.2 idna==3.4 # via anyio # via httpx -jiter==0.5.0 +jiter==0.6.1 # via openai -numpy==1.26.4 +numpy==2.0.2 # via openai # via pandas # via pandas-stubs -pandas==2.2.2 +pandas==2.2.3 # via openai -pandas-stubs==2.2.1.240316 +pandas-stubs==2.2.2.240807 # via openai -pydantic==2.7.1 +pydantic==2.9.2 # via openai -pydantic-core==2.18.2 +pydantic-core==2.23.4 # via pydantic python-dateutil==2.9.0.post0 # via pandas @@ -54,9 +54,9 @@ sniffio==1.3.0 # via anyio # via httpx # via openai -tqdm==4.66.1 +tqdm==4.66.5 # via openai -types-pytz==2024.1.0.20240417 +types-pytz==2024.2.0.20241003 # via pandas-stubs typing-extensions==4.12.2 # via openai diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 78851277eb..4dfa23845d 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -133,7 +133,7 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: def model_dump( model: pydantic.BaseModel, *, - exclude: IncEx = None, + exclude: IncEx | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, warnings: bool = True, diff --git a/src/openai/_models.py b/src/openai/_models.py index 710401defd..4100328c22 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -201,7 +201,7 @@ def __str__(self) -> str: # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. @classmethod @override - def construct( + def construct( # pyright: ignore[reportIncompatibleMethodOverride] cls: Type[ModelT], _fields_set: set[str] | None = None, **values: object, @@ -273,8 +273,8 @@ def model_dump( self, *, mode: Literal["json", "python"] | str = "python", - include: IncEx = None, - exclude: IncEx = None, + include: IncEx | None = None, + exclude: IncEx | None = None, by_alias: bool = False, exclude_unset: bool = False, exclude_defaults: bool = False, @@ -328,8 +328,8 @@ def model_dump_json( self, *, indent: int | None = None, - include: IncEx = None, - exclude: IncEx = None, + include: IncEx | None = None, + exclude: IncEx | None = None, by_alias: bool = False, exclude_unset: bool = False, exclude_defaults: bool = False, diff --git a/src/openai/_types.py b/src/openai/_types.py index 5611b2d38f..c8f4d5a922 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -16,7 +16,7 @@ Optional, Sequence, ) -from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable +from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable import httpx import pydantic @@ -195,7 +195,9 @@ def get(self, __key: str) -> str | None: ... # Note: copied from Pydantic # https://github.com/pydantic/pydantic/blob/32ea570bf96e84234d2992e1ddf40ab8a565925a/pydantic/main.py#L49 -IncEx: TypeAlias = "set[int] | set[str] | dict[int, Any] | dict[str, Any] | None" +IncEx: TypeAlias = Union[ + Set[int], Set[str], Mapping[int, Union["IncEx", Literal[True]]], Mapping[str, Union["IncEx", Literal[True]]] +] PostParser = Callable[[Any], Any] diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index a4b0f856f7..8518de967f 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -23,7 +23,7 @@ FunctionToolCallArgumentsDeltaEvent, ) from .._deltas import accumulate_delta -from ...._types import NOT_GIVEN, NotGiven +from ...._types import NOT_GIVEN, IncEx, NotGiven from ...._utils import is_given, consume_sync_iterator, consume_async_iterator from ...._compat import model_dump from ...._models import build, construct_type @@ -352,13 +352,17 @@ def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionS # we don't want to serialise / deserialise our custom properties # as they won't appear in the delta and we don't want to have to # continuosly reparse the content - exclude={ - "parsed": True, - "tool_calls": { - idx: {"function": {"parsed_arguments": True}} - for idx, _ in enumerate(choice_snapshot.message.tool_calls or []) + exclude=cast( + # cast required as mypy isn't smart enough to infer `True` here to `Literal[True]` + IncEx, + { + "parsed": True, + "tool_calls": { + idx: {"function": {"parsed_arguments": True}} + for idx, _ in enumerate(choice_snapshot.message.tool_calls or []) + }, }, - }, + ), ), ), cast("dict[object, object]", choice.delta.to_dict()), diff --git a/tests/conftest.py b/tests/conftest.py index 15af57e770..fa82d39d86 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,11 @@ from __future__ import annotations import os -import asyncio import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator import pytest +from pytest_asyncio import is_async_test from openai import OpenAI, AsyncOpenAI @@ -17,11 +17,13 @@ logging.getLogger("openai").setLevel(logging.DEBUG) -@pytest.fixture(scope="session") -def event_loop() -> Iterator[asyncio.AbstractEventLoop]: - loop = asyncio.new_event_loop() - yield loop - loop.close() +# automatically add `pytest.mark.asyncio()` to all of our async tests +# so we don't have to add that boilerplate everywhere +def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: + pytest_asyncio_tests = (item for item in items if is_async_test(item)) + session_scope_marker = pytest.mark.asyncio(loop_scope="session") + for async_test in pytest_asyncio_tests: + async_test.add_marker(session_scope_marker, append=False) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") diff --git a/tests/lib/test_old_api.py b/tests/lib/test_old_api.py index 261b8acb94..bdb2a5398d 100644 --- a/tests/lib/test_old_api.py +++ b/tests/lib/test_old_api.py @@ -6,7 +6,7 @@ def test_basic_attribute_access_works() -> None: for attr in dir(openai): - dir(getattr(openai, attr)) + getattr(openai, attr) def test_helpful_error_is_raised() -> None: From c3ee00623709fcc67e9248af1c907cc7e863a5fd Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Wed, 30 Oct 2024 16:11:41 +0000 Subject: [PATCH 649/914] feat(api): add new, expressive voices for Realtime and Audio in Chat Completions https://platform.openai.com/docs/changelog --- .stats.yml | 2 +- src/openai/types/chat/chat_completion_audio_param.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0b08725565..39413df445 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-b60d5559d5150ecd3b49136064e5e251d832899770ff385b711378389afba370.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-7b0a5d715d94f75ac7795bd4d2175a0e3243af9b935a86c273f371e45583140f.yml diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index 6a4ce9ac1f..b92326d294 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -14,8 +14,9 @@ class ChatCompletionAudioParam(TypedDict, total=False): Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. """ - voice: Required[Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]] - """Specifies the voice type. + voice: Required[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] + """The voice the model uses to respond. - Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. + Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, and `verse`. """ From 891e1c17b7fecbae34d1915ba90c15ddece807f9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:27:56 +0000 Subject: [PATCH 650/914] release: 1.53.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2f2a30a82e..0b97f7533e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.52.2" + ".": "1.53.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 21f6136744..4f5a63ae72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.53.0 (2024-10-30) + +Full Changelog: [v1.52.2...v1.53.0](https://github.com/openai/openai-python/compare/v1.52.2...v1.53.0) + +### Features + +* **api:** add new, expressive voices for Realtime and Audio in Chat Completions ([7cf0a49](https://github.com/openai/openai-python/commit/7cf0a4958e4c985bef4d18bb919fa3948f389a82)) + + +### Chores + +* **internal:** bump pytest to v8 & pydantic ([#1829](https://github.com/openai/openai-python/issues/1829)) ([0e67a8a](https://github.com/openai/openai-python/commit/0e67a8af5daf9da029d2bd4bdf341cc8a494254a)) + ## 1.52.2 (2024-10-23) Full Changelog: [v1.52.1...v1.52.2](https://github.com/openai/openai-python/compare/v1.52.1...v1.52.2) diff --git a/pyproject.toml b/pyproject.toml index 3bd6246e43..46aced6fba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.52.2" +version = "1.53.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 641d4f5b30..afda9b903f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.52.2" # x-release-please-version +__version__ = "1.53.0" # x-release-please-version From c94ead1d0c865bb9a0c8b787149d1f34317ca935 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Fri, 1 Nov 2024 12:34:07 +0000 Subject: [PATCH 651/914] chore(internal): bump mypy (#1839) --- requirements-dev.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 2df8a39c76..5fe1ccad57 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -83,7 +83,7 @@ msal==1.31.0 # via msal-extensions msal-extensions==1.2.0 # via azure-identity -mypy==1.11.2 +mypy==1.13.0 mypy-extensions==1.0.0 # via black # via mypy From 643e500c52831f1966f16d7173651d06cb886a31 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 2 Nov 2024 02:46:36 +0000 Subject: [PATCH 652/914] fix: don't use dicts as iterables in transform (#1842) --- src/openai/_utils/_transform.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 47e262a515..7e9663d369 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -173,6 +173,11 @@ def _transform_recursive( # 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] From d21cd6c042e88629b81ae85c7d7023c973aa14f5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:59:58 +0000 Subject: [PATCH 653/914] fix: support json safe serialization for basemodel subclasses (#1844) --- src/openai/_compat.py | 6 ++++-- src/openai/_models.py | 9 ++++++--- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_transform.py | 4 ++-- src/openai/_utils/_utils.py | 17 +++++++++++++++++ tests/test_models.py | 21 +++++++-------------- tests/test_transform.py | 15 +++++++++++++++ 7 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 4dfa23845d..7c3156a5eb 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload from datetime import date, datetime -from typing_extensions import Self +from typing_extensions import Self, Literal import pydantic from pydantic.fields import FieldInfo @@ -137,9 +137,11 @@ def model_dump( exclude_unset: bool = False, exclude_defaults: bool = False, warnings: bool = True, + mode: Literal["json", "python"] = "python", ) -> dict[str, Any]: - if PYDANTIC_V2: + if PYDANTIC_V2 or hasattr(model, "model_dump"): return model.model_dump( + mode=mode, exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, diff --git a/src/openai/_models.py b/src/openai/_models.py index 4100328c22..20cd4c29bc 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -38,6 +38,7 @@ PropertyInfo, is_list, is_given, + json_safe, lru_cache, is_mapping, parse_date, @@ -304,8 +305,8 @@ def model_dump( Returns: A dictionary representation of the model. """ - if mode != "python": - raise ValueError("mode is only supported in Pydantic v2") + if mode not in {"json", "python"}: + raise ValueError("mode must be either 'json' or 'python'") if round_trip != False: raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: @@ -314,7 +315,7 @@ def model_dump( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") - return super().dict( # pyright: ignore[reportDeprecated] + dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, by_alias=by_alias, @@ -323,6 +324,8 @@ def model_dump( exclude_none=exclude_none, ) + return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped + @override def model_dump_json( self, diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 3efe66c8e8..a7cff3c091 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -6,6 +6,7 @@ is_list as is_list, is_given as is_given, is_tuple as is_tuple, + json_safe as json_safe, lru_cache as lru_cache, is_mapping as is_mapping, is_tuple_t as is_tuple_t, diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 7e9663d369..d7c05345d1 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -191,7 +191,7 @@ def _transform_recursive( return data if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True) + return model_dump(data, exclude_unset=True, mode="json") annotated_type = _get_annotated_type(annotation) if annotated_type is None: @@ -329,7 +329,7 @@ async def _async_transform_recursive( return data if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True) + return model_dump(data, exclude_unset=True, mode="json") annotated_type = _get_annotated_type(annotation) if annotated_type is None: diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 0bba17caad..e5811bba42 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -16,6 +16,7 @@ overload, ) from pathlib import Path +from datetime import date, datetime from typing_extensions import TypeGuard import sniffio @@ -395,3 +396,19 @@ def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: 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 diff --git a/tests/test_models.py b/tests/test_models.py index 117a90020e..84dbce6914 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -520,19 +520,15 @@ class Model(BaseModel): assert m3.to_dict(exclude_none=True) == {} assert m3.to_dict(exclude_defaults=True) == {} - if PYDANTIC_V2: - - class Model2(BaseModel): - created_at: datetime + class Model2(BaseModel): + created_at: datetime - time_str = "2024-03-21T11:39:01.275859" - m4 = Model2.construct(created_at=time_str) - assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} - assert m4.to_dict(mode="json") == {"created_at": time_str} - else: - with pytest.raises(ValueError, match="mode is only supported in Pydantic v2"): - m.to_dict(mode="json") + time_str = "2024-03-21T11:39:01.275859" + m4 = Model2.construct(created_at=time_str) + assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} + assert m4.to_dict(mode="json") == {"created_at": time_str} + if not PYDANTIC_V2: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_dict(warnings=False) @@ -558,9 +554,6 @@ class Model(BaseModel): assert m3.model_dump(exclude_none=True) == {} if not PYDANTIC_V2: - with pytest.raises(ValueError, match="mode is only supported in Pydantic v2"): - m.model_dump(mode="json") - with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump(round_trip=True) diff --git a/tests/test_transform.py b/tests/test_transform.py index 1eb6cde9d6..8c6aba6448 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -177,17 +177,32 @@ class DateDict(TypedDict, total=False): foo: Annotated[date, PropertyInfo(format="iso8601")] +class DatetimeModel(BaseModel): + foo: datetime + + +class DateModel(BaseModel): + foo: Optional[date] + + @parametrize @pytest.mark.asyncio async def test_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + tz = "Z" if PYDANTIC_V2 else "+00:00" assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] dt = dt.replace(tzinfo=None) assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=None), Any, use_async) == {"foo": None} # type: ignore assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=date.fromisoformat("2023-02-23")), DateDict, use_async) == { + "foo": "2023-02-23" + } # type: ignore[comparison-overlap] @parametrize From 6e42e7879a8e22db9268d1ef2f3b80428534e752 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:00:39 +0000 Subject: [PATCH 654/914] release: 1.53.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0b97f7533e..5a86c0906c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.53.0" + ".": "1.53.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f5a63ae72..c3a5a3cecb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.53.1 (2024-11-04) + +Full Changelog: [v1.53.0...v1.53.1](https://github.com/openai/openai-python/compare/v1.53.0...v1.53.1) + +### Bug Fixes + +* don't use dicts as iterables in transform ([#1842](https://github.com/openai/openai-python/issues/1842)) ([258f265](https://github.com/openai/openai-python/commit/258f26535744ab3b2f0746991fd29eae72ebd667)) +* support json safe serialization for basemodel subclasses ([#1844](https://github.com/openai/openai-python/issues/1844)) ([2b80c90](https://github.com/openai/openai-python/commit/2b80c90c21d3b2468dfa3bf40c08c5b0e0eebffa)) + + +### Chores + +* **internal:** bump mypy ([#1839](https://github.com/openai/openai-python/issues/1839)) ([d92f959](https://github.com/openai/openai-python/commit/d92f959aa6f49be56574b4d1d1ac5ac48689dd46)) + ## 1.53.0 (2024-10-30) Full Changelog: [v1.52.2...v1.53.0](https://github.com/openai/openai-python/compare/v1.52.2...v1.53.0) diff --git a/pyproject.toml b/pyproject.toml index 46aced6fba..974e5fc740 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.53.0" +version = "1.53.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index afda9b903f..9d221eb538 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.53.0" # x-release-please-version +__version__ = "1.53.1" # x-release-please-version From cb88c2f01dd02b10ece80974f0b6403ae5921298 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:00:25 +0000 Subject: [PATCH 655/914] feat(project): drop support for Python 3.7 (#1845) 3.7 has been EOL for over a year and accounts for a small number of downloads --- README.md | 4 ++-- pyproject.toml | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dada1abfbb..bc334e7e07 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![PyPI version](https://img.shields.io/pypi/v/openai.svg)](https://pypi.org/project/openai/) -The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.7+ +The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.8+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). @@ -712,7 +712,7 @@ print(openai.__version__) ## Requirements -Python 3.7 or higher. +Python 3.8 or higher. ## Contributing diff --git a/pyproject.toml b/pyproject.toml index 974e5fc740..20c0f3bb15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,11 +18,10 @@ dependencies = [ "tqdm > 4", "jiter>=0.4.0, <1", ] -requires-python = ">= 3.7.1" +requires-python = ">= 3.8" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -150,7 +149,7 @@ filterwarnings = [ # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" -pythonVersion = "3.7" +pythonVersion = "3.8" exclude = [ "_dev", From dfdcf571ced31f7859cd1871be39e2fb3af6bafa Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 4 Nov 2024 22:47:52 +0000 Subject: [PATCH 656/914] feat(api): add support for predicted outputs (#1847) --- .stats.yml | 2 +- api.md | 3 +- src/openai/resources/audio/speech.py | 8 +- src/openai/resources/audio/transcriptions.py | 4 +- src/openai/resources/audio/translations.py | 4 +- src/openai/resources/beta/assistants.py | 40 +++--- src/openai/resources/beta/threads/messages.py | 8 +- .../resources/beta/threads/runs/runs.py | 56 ++++---- .../resources/beta/threads/runs/steps.py | 16 +-- src/openai/resources/beta/threads/threads.py | 36 ++--- .../beta/vector_stores/file_batches.py | 8 +- .../resources/beta/vector_stores/files.py | 8 +- .../beta/vector_stores/vector_stores.py | 8 +- src/openai/resources/chat/completions.py | 125 +++++++++++------- src/openai/resources/completions.py | 60 ++++----- src/openai/resources/embeddings.py | 12 +- src/openai/resources/files.py | 74 ++++++++--- src/openai/resources/fine_tuning/jobs/jobs.py | 4 +- src/openai/resources/images.py | 12 +- src/openai/resources/moderations.py | 4 +- src/openai/resources/uploads/uploads.py | 4 +- .../types/audio/speech_create_params.py | 4 +- .../audio/transcription_create_params.py | 2 +- .../types/audio/translation_create_params.py | 2 +- src/openai/types/beta/assistant.py | 8 +- .../types/beta/assistant_create_params.py | 8 +- .../types/beta/assistant_list_params.py | 2 +- .../types/beta/assistant_update_params.py | 8 +- src/openai/types/beta/file_search_tool.py | 4 +- .../types/beta/file_search_tool_param.py | 4 +- .../beta/thread_create_and_run_params.py | 6 +- .../types/beta/threads/message_list_params.py | 2 +- src/openai/types/beta/threads/run.py | 6 +- .../types/beta/threads/run_create_params.py | 8 +- .../types/beta/threads/run_list_params.py | 2 +- .../beta/threads/runs/step_list_params.py | 4 +- .../beta/threads/runs/step_retrieve_params.py | 2 +- .../types/beta/vector_store_list_params.py | 2 +- .../file_batch_list_files_params.py | 2 +- .../beta/vector_stores/file_list_params.py | 2 +- src/openai/types/chat/__init__.py | 3 + ...hat_completion_content_part_image_param.py | 2 +- ...hat_completion_prediction_content_param.py | 25 ++++ .../types/chat/completion_create_params.py | 23 ++-- src/openai/types/completion_create_params.py | 10 +- src/openai/types/completion_usage.py | 14 ++ src/openai/types/embedding_create_params.py | 6 +- src/openai/types/file_list_params.py | 23 +++- .../types/fine_tuning/job_create_params.py | 2 +- .../types/image_create_variation_params.py | 2 +- src/openai/types/image_edit_params.py | 2 +- src/openai/types/image_generate_params.py | 2 +- src/openai/types/moderation_create_params.py | 2 +- tests/api_resources/chat/test_completions.py | 16 +++ tests/api_resources/test_files.py | 28 ++-- 55 files changed, 450 insertions(+), 284 deletions(-) create mode 100644 src/openai/types/chat/chat_completion_prediction_content_param.py diff --git a/.stats.yml b/.stats.yml index 39413df445..f368bc881d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-7b0a5d715d94f75ac7795bd4d2175a0e3243af9b935a86c273f371e45583140f.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-2f8ca92b9b1879fd535b685e4767338413fcd533d42f3baac13a9c41da3fce35.yml diff --git a/api.md b/api.md index f44169665d..7def07bb79 100644 --- a/api.md +++ b/api.md @@ -54,6 +54,7 @@ from openai.types.chat import ( ChatCompletionMessageToolCall, ChatCompletionModality, ChatCompletionNamedToolChoice, + ChatCompletionPredictionContent, ChatCompletionRole, ChatCompletionStreamOptions, ChatCompletionSystemMessageParam, @@ -93,7 +94,7 @@ Methods: - client.files.create(\*\*params) -> FileObject - client.files.retrieve(file_id) -> FileObject -- client.files.list(\*\*params) -> SyncPage[FileObject] +- client.files.list(\*\*params) -> SyncCursorPage[FileObject] - client.files.delete(file_id) -> FileDeleted - client.files.content(file_id) -> HttpxBinaryResponseContent - client.files.retrieve_content(file_id) -> str diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 6085ae8afe..09faaddda6 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -70,13 +70,13 @@ def create( input: The text to generate audio for. The maximum length is 4096 characters. model: - One of the available [TTS models](https://platform.openai.com/docs/models/tts): + One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` or `tts-1-hd` voice: The voice to use when generating the audio. Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. Previews of the voices are available in the - [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). + [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. @@ -154,13 +154,13 @@ async def create( input: The text to generate audio for. The maximum length is 4096 characters. model: - One of the available [TTS models](https://platform.openai.com/docs/models/tts): + One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` or `tts-1-hd` voice: The voice to use when generating the audio. Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. Previews of the voices are available in the - [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). + [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index e6596a480e..8b5f4404fc 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -143,7 +143,7 @@ def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The - [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match the audio language. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, @@ -307,7 +307,7 @@ async def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The - [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match the audio language. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 53ab625873..a2d28afa03 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -130,7 +130,7 @@ def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The - [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in English. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, @@ -273,7 +273,7 @@ async def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The - [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in English. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 2ebef183b6..7df212f155 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -79,8 +79,8 @@ def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. description: The description of the assistant. The maximum length is 512 characters. @@ -95,8 +95,8 @@ def create( name: The name of the assistant. The maximum length is 256 characters. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -239,14 +239,14 @@ def update( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. name: The name of the assistant. The maximum length is 256 characters. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -344,8 +344,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -465,8 +465,8 @@ async def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. description: The description of the assistant. The maximum length is 512 characters. @@ -481,8 +481,8 @@ async def create( name: The name of the assistant. The maximum length is 256 characters. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -625,14 +625,14 @@ async def update( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. name: The name of the assistant. The maximum length is 256 characters. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -730,8 +730,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index de7ebcaf4d..e848507387 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -221,8 +221,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -495,8 +495,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 287c0ecf24..620cc270e5 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -122,7 +122,7 @@ def create( to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. additional_instructions: Appends additional instructions at the end of the instructions for the run. This @@ -158,12 +158,12 @@ def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -266,7 +266,7 @@ def create( to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. additional_instructions: Appends additional instructions at the end of the instructions for the run. This @@ -302,12 +302,12 @@ def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -406,7 +406,7 @@ def create( to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. additional_instructions: Appends additional instructions at the end of the instructions for the run. This @@ -442,12 +442,12 @@ def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -674,8 +674,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -1484,7 +1484,7 @@ async def create( to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. additional_instructions: Appends additional instructions at the end of the instructions for the run. This @@ -1520,12 +1520,12 @@ async def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1628,7 +1628,7 @@ async def create( to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. additional_instructions: Appends additional instructions at the end of the instructions for the run. This @@ -1664,12 +1664,12 @@ async def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1768,7 +1768,7 @@ async def create( to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. additional_instructions: Appends additional instructions at the end of the instructions for the run. This @@ -1804,12 +1804,12 @@ async def create( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -2037,8 +2037,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 5d6d55f9d9..9bd91e39e0 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -68,7 +68,7 @@ def retrieve( to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. extra_headers: Send extra headers @@ -126,15 +126,15 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. include: A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. limit: A limit on the number of objects to be returned. Limit can range between 1 and @@ -222,7 +222,7 @@ async def retrieve( to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. extra_headers: Send extra headers @@ -280,15 +280,15 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. include: A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. limit: A limit on the number of objects to be returned. Limit can range between 1 and diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 3b8851c03b..058ba71a17 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -326,12 +326,12 @@ def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -460,12 +460,12 @@ def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -590,12 +590,12 @@ def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1160,12 +1160,12 @@ async def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1294,12 +1294,12 @@ async def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1424,12 +1424,12 @@ async def create_and_run( assistant will be used. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. response_format: Specifies the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/beta/vector_stores/file_batches.py index d1f9c872e4..9f9e643bd0 100644 --- a/src/openai/resources/beta/vector_stores/file_batches.py +++ b/src/openai/resources/beta/vector_stores/file_batches.py @@ -227,8 +227,8 @@ def list_files( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. @@ -556,8 +556,8 @@ def list_files( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/beta/vector_stores/files.py index fe43bb3488..7c155ac917 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -164,8 +164,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. @@ -476,8 +476,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. filter: Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`. diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py index d69add7b26..61a2eadc7b 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -251,8 +251,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -529,8 +529,8 @@ def list( before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, - ending with obj_foo, your subsequent call can include before=obj_foo in order to - fetch the previous page of the list. + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index ceaf3c2fec..60ab5138ba 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -33,6 +33,7 @@ from ...types.chat.chat_completion_audio_param import ChatCompletionAudioParam from ...types.chat.chat_completion_message_param import ChatCompletionMessageParam from ...types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam +from ...types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam from ...types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam __all__ = ["Completions", "AsyncCompletions"] @@ -76,6 +77,7 @@ def create( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -113,7 +115,7 @@ def create( [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) table for details on which models work with the Chat API. audio: Parameters for audio output. Required when audio output is requested with @@ -124,7 +126,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) function_call: Deprecated in favor of `tool_choice`. @@ -185,19 +187,22 @@ def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + prediction: Static predicted output content, such as the content of a text file that is + being regenerated. + presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -284,7 +289,7 @@ def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -315,6 +320,7 @@ def create( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -351,7 +357,7 @@ def create( [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) table for details on which models work with the Chat API. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be @@ -369,7 +375,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) function_call: Deprecated in favor of `tool_choice`. @@ -430,19 +436,22 @@ def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + prediction: Static predicted output content, such as the content of a text file that is + being regenerated. + presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -522,7 +531,7 @@ def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -553,6 +562,7 @@ def create( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -589,7 +599,7 @@ def create( [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) table for details on which models work with the Chat API. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be @@ -607,7 +617,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) function_call: Deprecated in favor of `tool_choice`. @@ -668,19 +678,22 @@ def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + prediction: Static predicted output content, such as the content of a text file that is + being regenerated. + presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -760,7 +773,7 @@ def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -790,6 +803,7 @@ def create( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -830,6 +844,7 @@ def create( "modalities": modalities, "n": n, "parallel_tool_calls": parallel_tool_calls, + "prediction": prediction, "presence_penalty": presence_penalty, "response_format": response_format, "seed": seed, @@ -894,6 +909,7 @@ async def create( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -931,7 +947,7 @@ async def create( [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) table for details on which models work with the Chat API. audio: Parameters for audio output. Required when audio output is requested with @@ -942,7 +958,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) function_call: Deprecated in favor of `tool_choice`. @@ -1003,19 +1019,22 @@ async def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + prediction: Static predicted output content, such as the content of a text file that is + being regenerated. + presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1102,7 +1121,7 @@ async def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -1133,6 +1152,7 @@ async def create( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -1169,7 +1189,7 @@ async def create( [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) table for details on which models work with the Chat API. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be @@ -1187,7 +1207,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) function_call: Deprecated in favor of `tool_choice`. @@ -1248,19 +1268,22 @@ async def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + prediction: Static predicted output content, such as the content of a text file that is + being regenerated. + presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1340,7 +1363,7 @@ async def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -1371,6 +1394,7 @@ async def create( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -1407,7 +1431,7 @@ async def create( [audio](https://platform.openai.com/docs/guides/audio). model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) table for details on which models work with the Chat API. stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be @@ -1425,7 +1449,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) function_call: Deprecated in favor of `tool_choice`. @@ -1486,19 +1510,22 @@ async def create( choices. Keep `n` as `1` to minimize costs. parallel_tool_calls: Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + prediction: Static predicted output content, such as the content of a text file that is + being regenerated. + presence_penalty: Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1578,7 +1605,7 @@ async def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -1608,6 +1635,7 @@ async def create( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, @@ -1648,6 +1676,7 @@ async def create( "modalities": modalities, "n": n, "parallel_tool_calls": parallel_tool_calls, + "prediction": prediction, "presence_penalty": presence_penalty, "response_format": response_format, "seed": seed, diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 49198d2687..1ac3575fd5 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -84,8 +84,8 @@ def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. prompt: The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. @@ -110,7 +110,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -150,7 +150,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -189,7 +189,7 @@ def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -237,8 +237,8 @@ def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. prompt: The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. @@ -270,7 +270,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -310,7 +310,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -342,7 +342,7 @@ def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -390,8 +390,8 @@ def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. prompt: The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. @@ -423,7 +423,7 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -463,7 +463,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -495,7 +495,7 @@ def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -626,8 +626,8 @@ async def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. prompt: The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. @@ -652,7 +652,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -692,7 +692,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -731,7 +731,7 @@ async def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -779,8 +779,8 @@ async def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. prompt: The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. @@ -812,7 +812,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -852,7 +852,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -884,7 +884,7 @@ async def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -932,8 +932,8 @@ async def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. prompt: The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. @@ -965,7 +965,7 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) logit_bias: Modify the likelihood of specified tokens appearing in the completion. @@ -1005,7 +1005,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -1037,7 +1037,7 @@ async def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index c91e6cc13a..4ab2278e89 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -73,8 +73,8 @@ def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. dimensions: The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models. @@ -84,7 +84,7 @@ def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -185,8 +185,8 @@ async def create( model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. dimensions: The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models. @@ -196,7 +196,7 @@ async def create( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index e24eeec711..77706a7fd8 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -5,6 +5,7 @@ import time import typing_extensions from typing import Mapping, cast +from typing_extensions import Literal import httpx @@ -27,11 +28,8 @@ to_custom_streamed_response_wrapper, async_to_custom_streamed_response_wrapper, ) -from ..pagination import SyncPage, AsyncPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options from ..types.file_object import FileObject from ..types.file_deleted import FileDeleted from ..types.file_purpose import FilePurpose @@ -172,6 +170,9 @@ def retrieve( def list( self, *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, purpose: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -179,11 +180,23 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncPage[FileObject]: - """ - Returns a list of files that belong to the user's organization. + ) -> SyncCursorPage[FileObject]: + """Returns a list of files. Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 10,000, and the default is 10,000. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + purpose: Only return files with the given purpose. extra_headers: Send extra headers @@ -196,13 +209,21 @@ def list( """ return self._get_api_list( "/files", - page=SyncPage[FileObject], + page=SyncCursorPage[FileObject], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"purpose": purpose}, file_list_params.FileListParams), + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "purpose": purpose, + }, + file_list_params.FileListParams, + ), ), model=FileObject, ) @@ -465,6 +486,9 @@ async def retrieve( def list( self, *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, purpose: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -472,11 +496,23 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[FileObject, AsyncPage[FileObject]]: - """ - Returns a list of files that belong to the user's organization. + ) -> AsyncPaginator[FileObject, AsyncCursorPage[FileObject]]: + """Returns a list of files. Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 10,000, and the default is 10,000. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + purpose: Only return files with the given purpose. extra_headers: Send extra headers @@ -489,13 +525,21 @@ def list( """ return self._get_api_list( "/files", - page=AsyncPage[FileObject], + page=AsyncCursorPage[FileObject], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"purpose": purpose}, file_list_params.FileListParams), + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "purpose": purpose, + }, + file_list_params.FileListParams, + ), ), model=FileObject, ) diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index 44abf1cfe1..0ed5495b0e 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -88,7 +88,7 @@ def create( Args: model: The name of the model to fine-tune. You can select one of the - [supported models](https://platform.openai.com/docs/guides/fine-tuning/which-models-can-be-fine-tuned). + [supported models](https://platform.openai.com/docs/guides/fine-tuning#which-models-can-be-fine-tuned). training_file: The ID of an uploaded file that contains training data. @@ -379,7 +379,7 @@ async def create( Args: model: The name of the model to fine-tune. You can select one of the - [supported models](https://platform.openai.com/docs/guides/fine-tuning/which-models-can-be-fine-tuned). + [supported models](https://platform.openai.com/docs/guides/fine-tuning#which-models-can-be-fine-tuned). training_file: The ID of an uploaded file that contains training data. diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index e9629d48fd..2fbc077dd9 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -84,7 +84,7 @@ def create_variation( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -165,7 +165,7 @@ def edit( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -251,7 +251,7 @@ def generate( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -341,7 +341,7 @@ async def create_variation( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -422,7 +422,7 @@ async def edit( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers @@ -508,7 +508,7 @@ async def generate( user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). extra_headers: Send extra headers diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 8b73da57b2..ce80bb7d55 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -68,7 +68,7 @@ def create( model: The content moderation model you would like to use. Learn more in [the moderation guide](https://platform.openai.com/docs/guides/moderation), and learn about available models - [here](https://platform.openai.com/docs/models/moderation). + [here](https://platform.openai.com/docs/models#moderation). extra_headers: Send extra headers @@ -138,7 +138,7 @@ async def create( model: The content moderation model you would like to use. Learn more in [the moderation guide](https://platform.openai.com/docs/guides/moderation), and learn about available models - [here](https://platform.openai.com/docs/models/moderation). + [here](https://platform.openai.com/docs/models#moderation). extra_headers: Send extra headers diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 96a531a8e4..cfb500b62c 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -196,7 +196,7 @@ def create( For certain `purpose`s, the correct `mime_type` must be specified. Please refer to documentation for the supported MIME types for your use case: - - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search/supported-files) + - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search#supported-files) For guidance on the proper filename extensions for each purpose, please follow the documentation on @@ -500,7 +500,7 @@ async def create( For certain `purpose`s, the correct `mime_type` must be specified. Please refer to documentation for the supported MIME types for your use case: - - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search/supported-files) + - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search#supported-files) For guidance on the proper filename extensions for each purpose, please follow the documentation on diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index dff66e49c7..a60d000708 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -16,7 +16,7 @@ class SpeechCreateParams(TypedDict, total=False): model: Required[Union[str, SpeechModel]] """ - One of the available [TTS models](https://platform.openai.com/docs/models/tts): + One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` or `tts-1-hd` """ @@ -25,7 +25,7 @@ class SpeechCreateParams(TypedDict, total=False): Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. Previews of the voices are available in the - [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech/voice-options). + [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). """ response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index 5ac2bb91e5..88805affbd 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -38,7 +38,7 @@ class TranscriptionCreateParams(TypedDict, total=False): """An optional text to guide the model's style or continue a previous audio segment. - The [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match the audio language. """ diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py index 6859ed9d30..62f85b8757 100644 --- a/src/openai/types/audio/translation_create_params.py +++ b/src/openai/types/audio/translation_create_params.py @@ -30,7 +30,7 @@ class TranslationCreateParams(TypedDict, total=False): """An optional text to guide the model's style or continue a previous audio segment. - The [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in English. """ diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index ea97de440f..3c8b8e403b 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -65,8 +65,8 @@ class Assistant(BaseModel): You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. """ name: Optional[str] = None @@ -85,8 +85,8 @@ class Assistant(BaseModel): response_format: Optional[AssistantResponseFormatOption] = None """Specifies the format that the model must output. - Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index e11f842f05..568b223ce7 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -26,8 +26,8 @@ class AssistantCreateParams(TypedDict, total=False): You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. """ description: Optional[str] @@ -53,8 +53,8 @@ class AssistantCreateParams(TypedDict, total=False): response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. - Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured diff --git a/src/openai/types/beta/assistant_list_params.py b/src/openai/types/beta/assistant_list_params.py index f54f63120b..834ffbcaf8 100644 --- a/src/openai/types/beta/assistant_list_params.py +++ b/src/openai/types/beta/assistant_list_params.py @@ -21,7 +21,7 @@ class AssistantListParams(TypedDict, total=False): """A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if - you make a list request and receive 100 objects, ending with obj_foo, your + you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. """ diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index c4598df507..9a66e41ab3 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -35,8 +35,8 @@ class AssistantUpdateParams(TypedDict, total=False): You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. """ name: Optional[str] @@ -45,8 +45,8 @@ class AssistantUpdateParams(TypedDict, total=False): response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. - Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py index aee6593e89..89fc16c04c 100644 --- a/src/openai/types/beta/file_search_tool.py +++ b/src/openai/types/beta/file_search_tool.py @@ -31,7 +31,7 @@ class FileSearch(BaseModel): Note that the file search tool may output fewer than `max_num_results` results. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. """ @@ -42,7 +42,7 @@ class FileSearch(BaseModel): score_threshold of 0. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. """ diff --git a/src/openai/types/beta/file_search_tool_param.py b/src/openai/types/beta/file_search_tool_param.py index 5ce91207ba..c73d0af79d 100644 --- a/src/openai/types/beta/file_search_tool_param.py +++ b/src/openai/types/beta/file_search_tool_param.py @@ -30,7 +30,7 @@ class FileSearch(TypedDict, total=False): Note that the file search tool may output fewer than `max_num_results` results. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. """ @@ -41,7 +41,7 @@ class FileSearch(TypedDict, total=False): score_threshold of 0. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. """ diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 64ee6a8710..8310ba12f4 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -86,15 +86,15 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): parallel_tool_calls: bool """ Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. """ response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. - Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured diff --git a/src/openai/types/beta/threads/message_list_params.py b/src/openai/types/beta/threads/message_list_params.py index 18c2442fb5..a7c22a66fb 100644 --- a/src/openai/types/beta/threads/message_list_params.py +++ b/src/openai/types/beta/threads/message_list_params.py @@ -21,7 +21,7 @@ class MessageListParams(TypedDict, total=False): """A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if - you make a list request and receive 100 objects, ending with obj_foo, your + you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. """ diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index e8f2b74dee..ad32135b7d 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -154,7 +154,7 @@ class Run(BaseModel): parallel_tool_calls: bool """ Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. """ @@ -167,8 +167,8 @@ class Run(BaseModel): response_format: Optional[AssistantResponseFormatOption] = None """Specifies the format that the model must output. - Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 9767b142e1..88dc39645e 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -41,7 +41,7 @@ class RunCreateParamsBase(TypedDict, total=False): search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. """ @@ -99,15 +99,15 @@ class RunCreateParamsBase(TypedDict, total=False): parallel_tool_calls: bool """ Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. """ response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. - Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-turbo-and-gpt-4), + Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured diff --git a/src/openai/types/beta/threads/run_list_params.py b/src/openai/types/beta/threads/run_list_params.py index 1e32bca4b4..fbea54f6f2 100644 --- a/src/openai/types/beta/threads/run_list_params.py +++ b/src/openai/types/beta/threads/run_list_params.py @@ -21,7 +21,7 @@ class RunListParams(TypedDict, total=False): """A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if - you make a list request and receive 100 objects, ending with obj_foo, your + you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. """ diff --git a/src/openai/types/beta/threads/runs/step_list_params.py b/src/openai/types/beta/threads/runs/step_list_params.py index 3931bd7e0c..a6be771d9f 100644 --- a/src/openai/types/beta/threads/runs/step_list_params.py +++ b/src/openai/types/beta/threads/runs/step_list_params.py @@ -26,7 +26,7 @@ class StepListParams(TypedDict, total=False): """A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if - you make a list request and receive 100 objects, ending with obj_foo, your + you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. """ @@ -39,7 +39,7 @@ class StepListParams(TypedDict, total=False): search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. """ diff --git a/src/openai/types/beta/threads/runs/step_retrieve_params.py b/src/openai/types/beta/threads/runs/step_retrieve_params.py index 22c1c049f4..ecbb72edbd 100644 --- a/src/openai/types/beta/threads/runs/step_retrieve_params.py +++ b/src/openai/types/beta/threads/runs/step_retrieve_params.py @@ -23,6 +23,6 @@ class StepRetrieveParams(TypedDict, total=False): search result content. See the - [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search/customizing-file-search-settings) + [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. """ diff --git a/src/openai/types/beta/vector_store_list_params.py b/src/openai/types/beta/vector_store_list_params.py index f39f67266d..e26ff90a85 100644 --- a/src/openai/types/beta/vector_store_list_params.py +++ b/src/openai/types/beta/vector_store_list_params.py @@ -21,7 +21,7 @@ class VectorStoreListParams(TypedDict, total=False): """A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if - you make a list request and receive 100 objects, ending with obj_foo, your + you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. """ diff --git a/src/openai/types/beta/vector_stores/file_batch_list_files_params.py b/src/openai/types/beta/vector_stores/file_batch_list_files_params.py index 24dee7d5a5..2a0a6c6aa7 100644 --- a/src/openai/types/beta/vector_stores/file_batch_list_files_params.py +++ b/src/openai/types/beta/vector_stores/file_batch_list_files_params.py @@ -23,7 +23,7 @@ class FileBatchListFilesParams(TypedDict, total=False): """A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if - you make a list request and receive 100 objects, ending with obj_foo, your + you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. """ diff --git a/src/openai/types/beta/vector_stores/file_list_params.py b/src/openai/types/beta/vector_stores/file_list_params.py index 23dd7f0d94..867b5fb3bb 100644 --- a/src/openai/types/beta/vector_stores/file_list_params.py +++ b/src/openai/types/beta/vector_stores/file_list_params.py @@ -21,7 +21,7 @@ class FileListParams(TypedDict, total=False): """A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if - you make a list request and receive 100 objects, ending with obj_foo, your + you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list. """ diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index eb818a132e..d0a5403e79 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -46,6 +46,9 @@ from .chat_completion_content_part_image_param import ( ChatCompletionContentPartImageParam as ChatCompletionContentPartImageParam, ) +from .chat_completion_prediction_content_param import ( + ChatCompletionPredictionContentParam as ChatCompletionPredictionContentParam, +) from .chat_completion_tool_choice_option_param import ( ChatCompletionToolChoiceOptionParam as ChatCompletionToolChoiceOptionParam, ) diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py index b1a186aa6d..9d407324d0 100644 --- a/src/openai/types/chat/chat_completion_content_part_image_param.py +++ b/src/openai/types/chat/chat_completion_content_part_image_param.py @@ -15,7 +15,7 @@ class ImageURL(TypedDict, total=False): """Specifies the detail level of the image. Learn more in the - [Vision guide](https://platform.openai.com/docs/guides/vision/low-or-high-fidelity-image-understanding). + [Vision guide](https://platform.openai.com/docs/guides/vision#low-or-high-fidelity-image-understanding). """ diff --git a/src/openai/types/chat/chat_completion_prediction_content_param.py b/src/openai/types/chat/chat_completion_prediction_content_param.py new file mode 100644 index 0000000000..c44e6e3653 --- /dev/null +++ b/src/openai/types/chat/chat_completion_prediction_content_param.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam + +__all__ = ["ChatCompletionPredictionContentParam"] + + +class ChatCompletionPredictionContentParam(TypedDict, total=False): + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] + """ + The content that should be matched when generating a model response. If + generated tokens would match this content, the entire model response can be + returned much more quickly. + """ + + type: Required[Literal["content"]] + """The type of the predicted content you want to provide. + + This type is currently always `content`. + """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index af6a47c219..e838858314 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -13,6 +13,7 @@ from ..shared_params.function_parameters import FunctionParameters from ..shared_params.response_format_text import ResponseFormatText from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam +from .chat_completion_prediction_content_param import ChatCompletionPredictionContentParam from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam from ..shared_params.response_format_json_object import ResponseFormatJSONObject from ..shared_params.response_format_json_schema import ResponseFormatJSONSchema @@ -43,7 +44,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models/model-endpoint-compatibility) + [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) table for details on which models work with the Chat API. """ @@ -60,7 +61,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) """ function_call: FunctionCall @@ -148,25 +149,31 @@ class CompletionCreateParamsBase(TypedDict, total=False): parallel_tool_calls: bool """ Whether to enable - [parallel function calling](https://platform.openai.com/docs/guides/function-calling/parallel-function-calling) + [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. """ + prediction: Optional[ChatCompletionPredictionContentParam] + """ + Static predicted output content, such as the content of a text file that is + being regenerated. + """ + presence_penalty: Optional[float] """Number between -2.0 and 2.0. 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) """ response_format: ResponseFormat """An object specifying the format that the model must output. - Compatible with [GPT-4o](https://platform.openai.com/docs/models/gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models/gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo) and + Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), + [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), + [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -276,7 +283,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index 6c112b3902..fdb1680d26 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -17,8 +17,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. """ prompt: Required[Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None]] @@ -53,7 +53,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) """ logit_bias: Optional[Dict[str, int]] @@ -106,7 +106,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation/parameter-details) + [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) """ seed: Optional[int] @@ -156,7 +156,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index fe112833e0..d8c4e84cf7 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -8,12 +8,26 @@ class CompletionTokensDetails(BaseModel): + accepted_prediction_tokens: Optional[int] = None + """ + When using Predicted Outputs, the number of tokens in the prediction that + appeared in the completion. + """ + audio_tokens: Optional[int] = None """Audio input tokens generated by the model.""" reasoning_tokens: Optional[int] = None """Tokens generated by the model for reasoning.""" + rejected_prediction_tokens: Optional[int] = None + """ + When using Predicted Outputs, the number of tokens in the prediction that did + not appear in the completion. However, like reasoning tokens, these tokens are + still counted in the total completion tokens for purposes of billing, output, + and context window limits. + """ + class PromptTokensDetails(BaseModel): audio_tokens: Optional[int] = None diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py index 1548cdbd77..1385762885 100644 --- a/src/openai/types/embedding_create_params.py +++ b/src/openai/types/embedding_create_params.py @@ -28,8 +28,8 @@ class EmbeddingCreateParams(TypedDict, total=False): You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our - [Model overview](https://platform.openai.com/docs/models/overview) for - descriptions of them. + [Model overview](https://platform.openai.com/docs/models) for descriptions of + them. """ dimensions: int @@ -48,5 +48,5 @@ class EmbeddingCreateParams(TypedDict, total=False): """ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ diff --git a/src/openai/types/file_list_params.py b/src/openai/types/file_list_params.py index 212eca13c0..058d874c29 100644 --- a/src/openai/types/file_list_params.py +++ b/src/openai/types/file_list_params.py @@ -2,11 +2,32 @@ from __future__ import annotations -from typing_extensions import TypedDict +from typing_extensions import Literal, TypedDict __all__ = ["FileListParams"] class FileListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 10,000, and the default is 10,000. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ + purpose: str """Only return files with the given purpose.""" diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 8f5ea86274..8814229b2e 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -13,7 +13,7 @@ class JobCreateParams(TypedDict, total=False): """The name of the model to fine-tune. You can select one of the - [supported models](https://platform.openai.com/docs/guides/fine-tuning/which-models-can-be-fine-tuned). + [supported models](https://platform.openai.com/docs/guides/fine-tuning#which-models-can-be-fine-tuned). """ training_file: Required[str] diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py index d6ecf0f1ae..d20f672912 100644 --- a/src/openai/types/image_create_variation_params.py +++ b/src/openai/types/image_create_variation_params.py @@ -47,5 +47,5 @@ class ImageCreateVariationParams(TypedDict, total=False): """ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index a596a8692b..1cb10611f3 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -58,5 +58,5 @@ class ImageEditParams(TypedDict, total=False): """ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 307adeb3da..c88c45f518 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -61,5 +61,5 @@ class ImageGenerateParams(TypedDict, total=False): """ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ diff --git a/src/openai/types/moderation_create_params.py b/src/openai/types/moderation_create_params.py index 3193fd9c2d..3ea2f3cd88 100644 --- a/src/openai/types/moderation_create_params.py +++ b/src/openai/types/moderation_create_params.py @@ -25,5 +25,5 @@ class ModerationCreateParams(TypedDict, total=False): Learn more in [the moderation guide](https://platform.openai.com/docs/guides/moderation), and learn about available models - [here](https://platform.openai.com/docs/models/moderation). + [here](https://platform.openai.com/docs/models#moderation). """ diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index d7162dc7db..dafedac9fb 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -65,6 +65,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: modalities=["text", "audio"], n=1, parallel_tool_calls=True, + prediction={ + "content": "string", + "type": "content", + }, presence_penalty=-2, response_format={"type": "text"}, seed=-9007199254740991, @@ -193,6 +197,10 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: modalities=["text", "audio"], n=1, parallel_tool_calls=True, + prediction={ + "content": "string", + "type": "content", + }, presence_penalty=-2, response_format={"type": "text"}, seed=-9007199254740991, @@ -340,6 +348,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn modalities=["text", "audio"], n=1, parallel_tool_calls=True, + prediction={ + "content": "string", + "type": "content", + }, presence_penalty=-2, response_format={"type": "text"}, seed=-9007199254740991, @@ -468,6 +480,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn modalities=["text", "audio"], n=1, parallel_tool_calls=True, + prediction={ + "content": "string", + "type": "content", + }, presence_penalty=-2, response_format={"type": "text"}, seed=-9007199254740991, diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 882f0ddbe7..7402566d95 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -13,7 +13,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import FileObject, FileDeleted -from openai.pagination import SyncPage, AsyncPage +from openai.pagination import SyncCursorPage, AsyncCursorPage # pyright: reportDeprecated=false @@ -98,14 +98,17 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: @parametrize def test_method_list(self, client: OpenAI) -> None: file = client.files.list() - assert_matches_type(SyncPage[FileObject], file, path=["response"]) + assert_matches_type(SyncCursorPage[FileObject], file, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: file = client.files.list( - purpose="string", + after="after", + limit=0, + order="asc", + purpose="purpose", ) - assert_matches_type(SyncPage[FileObject], file, path=["response"]) + assert_matches_type(SyncCursorPage[FileObject], file, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -114,7 +117,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(SyncPage[FileObject], file, path=["response"]) + assert_matches_type(SyncCursorPage[FileObject], file, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -123,7 +126,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(SyncPage[FileObject], file, path=["response"]) + assert_matches_type(SyncCursorPage[FileObject], file, path=["response"]) assert cast(Any, response.is_closed) is True @@ -334,14 +337,17 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: file = await async_client.files.list() - assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + assert_matches_type(AsyncCursorPage[FileObject], file, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: file = await async_client.files.list( - purpose="string", + after="after", + limit=0, + order="asc", + purpose="purpose", ) - assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + assert_matches_type(AsyncCursorPage[FileObject], file, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -350,7 +356,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() - assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + assert_matches_type(AsyncCursorPage[FileObject], file, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -359,7 +365,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() - assert_matches_type(AsyncPage[FileObject], file, path=["response"]) + assert_matches_type(AsyncCursorPage[FileObject], file, path=["response"]) assert cast(Any, response.is_closed) is True From b32507dd793b6b1dc218f76f7b485ae441db8841 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:48:18 +0000 Subject: [PATCH 657/914] release: 1.54.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5a86c0906c..7e66027ecd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.53.1" + ".": "1.54.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c3a5a3cecb..a60cd5cd31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.54.0 (2024-11-04) + +Full Changelog: [v1.53.1...v1.54.0](https://github.com/openai/openai-python/compare/v1.53.1...v1.54.0) + +### Features + +* **api:** add support for predicted outputs ([#1847](https://github.com/openai/openai-python/issues/1847)) ([42a4103](https://github.com/openai/openai-python/commit/42a410379a1b5f72424cc2e96dc6ddff22fd00be)) +* **project:** drop support for Python 3.7 ([#1845](https://github.com/openai/openai-python/issues/1845)) ([0ed5b1a](https://github.com/openai/openai-python/commit/0ed5b1a9302ccf2f40c3c751cd777740a4749cda)) + ## 1.53.1 (2024-11-04) Full Changelog: [v1.53.0...v1.53.1](https://github.com/openai/openai-python/compare/v1.53.0...v1.53.1) diff --git a/pyproject.toml b/pyproject.toml index 20c0f3bb15..a88ce5608f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.53.1" +version = "1.54.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9d221eb538..ef881a5f4c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.53.1" # x-release-please-version +__version__ = "1.54.0" # x-release-please-version From 4ae58a7b80022504815c75929cf44be91ba18702 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 5 Nov 2024 09:31:27 +0000 Subject: [PATCH 658/914] fix: add new prediction param to all methods --- src/openai/resources/beta/chat/completions.py | 9 ++++ tests/lib/chat/test_completions.py | 42 ++++++++++++++++--- tests/lib/chat/test_completions_streaming.py | 7 +++- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 47bcf42c16..38c09ce8dd 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -33,6 +33,7 @@ from ....types.chat.chat_completion_audio_param import ChatCompletionAudioParam from ....types.chat.chat_completion_message_param import ChatCompletionMessageParam from ....types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam +from ....types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam from ....types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam __all__ = ["Completions", "AsyncCompletions"] @@ -76,6 +77,7 @@ def parse( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -169,6 +171,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "modalities": modalities, "n": n, "parallel_tool_calls": parallel_tool_calls, + "prediction": prediction, "presence_penalty": presence_penalty, "response_format": _type_to_response_format(response_format), "seed": seed, @@ -217,6 +220,7 @@ def stream( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -281,6 +285,7 @@ def stream( modalities=modalities, n=n, parallel_tool_calls=parallel_tool_calls, + prediction=prediction, presence_penalty=presence_penalty, seed=seed, service_tier=service_tier, @@ -343,6 +348,7 @@ async def parse( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -436,6 +442,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "modalities": modalities, "n": n, "parallel_tool_calls": parallel_tool_calls, + "prediction": prediction, "presence_penalty": presence_penalty, "response_format": _type_to_response_format(response_format), "seed": seed, @@ -484,6 +491,7 @@ def stream( modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -549,6 +557,7 @@ def stream( modalities=modalities, n=n, parallel_tool_calls=parallel_tool_calls, + prediction=prediction, presence_penalty=presence_penalty, seed=seed, service_tier=service_tier, diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index 5cd7b1ee53..48f41eb221 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -77,7 +77,12 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte system_fingerprint='fp_b40fb1c6fb', usage=CompletionUsage( completion_tokens=37, - completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails( + accepted_prediction_tokens=None, + audio_tokens=None, + reasoning_tokens=0, + rejected_prediction_tokens=None + ), prompt_tokens=14, prompt_tokens_details=None, total_tokens=51 @@ -139,7 +144,12 @@ class Location(BaseModel): system_fingerprint='fp_5050236cbd', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails( + accepted_prediction_tokens=None, + audio_tokens=None, + reasoning_tokens=0, + rejected_prediction_tokens=None + ), prompt_tokens=79, prompt_tokens_details=None, total_tokens=93 @@ -203,7 +213,12 @@ class Location(BaseModel): system_fingerprint='fp_b40fb1c6fb', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails( + accepted_prediction_tokens=None, + audio_tokens=None, + reasoning_tokens=0, + rejected_prediction_tokens=None + ), prompt_tokens=88, prompt_tokens_details=None, total_tokens=102 @@ -396,7 +411,12 @@ class CalendarEvent: system_fingerprint='fp_7568d46099', usage=CompletionUsage( completion_tokens=17, - completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails( + accepted_prediction_tokens=None, + audio_tokens=None, + reasoning_tokens=0, + rejected_prediction_tokens=None + ), prompt_tokens=92, prompt_tokens_details=None, total_tokens=109 @@ -847,7 +867,12 @@ class Location(BaseModel): system_fingerprint='fp_5050236cbd', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails( + accepted_prediction_tokens=None, + audio_tokens=None, + reasoning_tokens=0, + rejected_prediction_tokens=None + ), prompt_tokens=79, prompt_tokens_details=None, total_tokens=93 @@ -917,7 +942,12 @@ class Location(BaseModel): system_fingerprint='fp_5050236cbd', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails( + accepted_prediction_tokens=None, + audio_tokens=None, + reasoning_tokens=0, + rejected_prediction_tokens=None + ), prompt_tokens=79, prompt_tokens_details=None, total_tokens=93 diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 2846e6d2c3..ab12de44b3 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -157,7 +157,12 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream system_fingerprint='fp_5050236cbd', usage=CompletionUsage( completion_tokens=14, - completion_tokens_details=CompletionTokensDetails(audio_tokens=None, reasoning_tokens=0), + completion_tokens_details=CompletionTokensDetails( + accepted_prediction_tokens=None, + audio_tokens=None, + reasoning_tokens=0, + rejected_prediction_tokens=None + ), prompt_tokens=79, prompt_tokens_details=None, total_tokens=93 From 22432e2d28a8503ea431b5c2599fd33e4a1cddb4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:32:01 +0000 Subject: [PATCH 659/914] release: 1.54.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7e66027ecd..701df5bbab 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.54.0" + ".": "1.54.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a60cd5cd31..511396ef95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.54.1 (2024-11-05) + +Full Changelog: [v1.54.0...v1.54.1](https://github.com/openai/openai-python/compare/v1.54.0...v1.54.1) + +### Bug Fixes + +* add new prediction param to all methods ([6aa424d](https://github.com/openai/openai-python/commit/6aa424d076098312801febd938bd4b5e8baf4851)) + ## 1.54.0 (2024-11-04) Full Changelog: [v1.53.1...v1.54.0](https://github.com/openai/openai-python/compare/v1.53.1...v1.54.0) diff --git a/pyproject.toml b/pyproject.toml index a88ce5608f..a4ea633fb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.54.0" +version = "1.54.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ef881a5f4c..7d59fea6fe 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.54.0" # x-release-please-version +__version__ = "1.54.1" # x-release-please-version From 7f6a921c5951c4d464cd49a60db52a183cbf78f9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:54:29 +0000 Subject: [PATCH 660/914] chore(tests): adjust retry timeout values (#1851) --- tests/test_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index ff07ec393b..912ea1316c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -698,7 +698,7 @@ class Model(BaseModel): [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], - [-1100, "", 7.8], # test large number potentially overflowing + [-1100, "", 8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -1564,7 +1564,7 @@ class Model(BaseModel): [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], - [-1100, "", 7.8], # test large number potentially overflowing + [-1100, "", 8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) From 74522adecf5d9919be8c797f3eb3da6e98b45229 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:55:21 +0000 Subject: [PATCH 661/914] release: 1.54.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 701df5bbab..11adb14fb9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.54.1" + ".": "1.54.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 511396ef95..61a18e3c8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.54.2 (2024-11-06) + +Full Changelog: [v1.54.1...v1.54.2](https://github.com/openai/openai-python/compare/v1.54.1...v1.54.2) + +### Chores + +* **tests:** adjust retry timeout values ([#1851](https://github.com/openai/openai-python/issues/1851)) ([cc8009c](https://github.com/openai/openai-python/commit/cc8009c9de56fe80f2689f69e7b891ff4ed297a3)) + ## 1.54.1 (2024-11-05) Full Changelog: [v1.54.0...v1.54.1](https://github.com/openai/openai-python/compare/v1.54.0...v1.54.1) diff --git a/pyproject.toml b/pyproject.toml index a4ea633fb6..13087f543f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.54.1" +version = "1.54.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7d59fea6fe..614d97d0f0 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.54.1" # x-release-please-version +__version__ = "1.54.2" # x-release-please-version From 1ca831cf60ead4151b061462f16d1894f02d025c Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Wed, 6 Nov 2024 13:18:00 -0800 Subject: [PATCH 662/914] fix(logs): redact sensitive headers (#1850) --- mypy.ini | 4 +- src/openai/_base_client.py | 3 +- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_logs.py | 17 ++++++ tests/lib/test_azure.py | 101 +++++++++++++++++++++++++++++++ tests/test_utils/test_logging.py | 100 ++++++++++++++++++++++++++++++ 6 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 tests/test_utils/test_logging.py diff --git a/mypy.ini b/mypy.ini index a4517a002d..97e5de4a60 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2,10 +2,10 @@ pretty = True show_error_codes = True -# Exclude _files.py because mypy isn't smart enough to apply +# Exclude _files.py and _logs.py because mypy isn't smart enough to apply # the correct type narrowing and as this is an internal module # it's fine to just use Pyright. -exclude = ^(src/openai/_files\.py|_dev/.*\.py)$ +exclude = ^(src/openai/_files\.py|src/openai/_utils/_logs\.py|_dev/.*\.py)$ strict_equality = True implicit_reexport = True diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index e1d4849ae2..187518787a 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -62,7 +62,7 @@ HttpxRequestFiles, ModelBuilderProtocol, ) -from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping +from ._utils import SensitiveHeadersFilter, is_dict, is_list, asyncify, is_given, lru_cache, is_mapping from ._compat import model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( @@ -90,6 +90,7 @@ from ._legacy_response import LegacyAPIResponse log: logging.Logger = logging.getLogger(__name__) +log.addFilter(SensitiveHeadersFilter()) # TODO: make base page type vars covariant SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index a7cff3c091..5abb34cde4 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -1,3 +1,4 @@ +from ._logs import SensitiveHeadersFilter as SensitiveHeadersFilter from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( diff --git a/src/openai/_utils/_logs.py b/src/openai/_utils/_logs.py index e5113fd8c0..376946933c 100644 --- a/src/openai/_utils/_logs.py +++ b/src/openai/_utils/_logs.py @@ -1,10 +1,16 @@ 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( @@ -23,3 +29,14 @@ def setup_logging() -> None: _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/tests/lib/test_azure.py b/tests/lib/test_azure.py index a9d3478350..626d7df311 100644 --- a/tests/lib/test_azure.py +++ b/tests/lib/test_azure.py @@ -1,3 +1,4 @@ +import logging from typing import Union, cast from typing_extensions import Literal, Protocol @@ -5,6 +6,7 @@ import pytest from respx import MockRouter +from openai._utils import SensitiveHeadersFilter, is_dict from openai._models import FinalRequestOptions from openai.lib.azure import AzureOpenAI, AsyncAzureOpenAI @@ -148,3 +150,102 @@ def token_provider() -> str: assert calls[0].request.headers.get("Authorization") == "Bearer first" assert calls[1].request.headers.get("Authorization") == "Bearer second" + + +class TestAzureLogging: + + @pytest.fixture(autouse=True) + def logger_with_filter(self) -> logging.Logger: + logger = logging.getLogger("openai") + logger.setLevel(logging.DEBUG) + logger.addFilter(SensitiveHeadersFilter()) + return logger + + @pytest.mark.respx() + def test_azure_api_key_redacted(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01" + ).mock( + return_value=httpx.Response(200, json={"model": "gpt-4"}) + ) + + client = AzureOpenAI( + api_version="2024-06-01", + api_key="example_api_key", + azure_endpoint="https://example-resource.azure.openai.com/", + ) + + with caplog.at_level(logging.DEBUG): + client.chat.completions.create(messages=[], model="gpt-4") + + for record in caplog.records: + if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]): + assert record.args["headers"]["api-key"] == "" + + + @pytest.mark.respx() + def test_azure_bearer_token_redacted(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01" + ).mock( + return_value=httpx.Response(200, json={"model": "gpt-4"}) + ) + + client = AzureOpenAI( + api_version="2024-06-01", + azure_ad_token="example_token", + azure_endpoint="https://example-resource.azure.openai.com/", + ) + + with caplog.at_level(logging.DEBUG): + client.chat.completions.create(messages=[], model="gpt-4") + + for record in caplog.records: + if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]): + assert record.args["headers"]["Authorization"] == "" + + + @pytest.mark.asyncio + @pytest.mark.respx() + async def test_azure_api_key_redacted_async(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01" + ).mock( + return_value=httpx.Response(200, json={"model": "gpt-4"}) + ) + + client = AsyncAzureOpenAI( + api_version="2024-06-01", + api_key="example_api_key", + azure_endpoint="https://example-resource.azure.openai.com/", + ) + + with caplog.at_level(logging.DEBUG): + await client.chat.completions.create(messages=[], model="gpt-4") + + for record in caplog.records: + if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]): + assert record.args["headers"]["api-key"] == "" + + + @pytest.mark.asyncio + @pytest.mark.respx() + async def test_azure_bearer_token_redacted_async(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01" + ).mock( + return_value=httpx.Response(200, json={"model": "gpt-4"}) + ) + + client = AsyncAzureOpenAI( + api_version="2024-06-01", + azure_ad_token="example_token", + azure_endpoint="https://example-resource.azure.openai.com/", + ) + + with caplog.at_level(logging.DEBUG): + await client.chat.completions.create(messages=[], model="gpt-4") + + for record in caplog.records: + if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]): + assert record.args["headers"]["Authorization"] == "" diff --git a/tests/test_utils/test_logging.py b/tests/test_utils/test_logging.py new file mode 100644 index 0000000000..cc018012e2 --- /dev/null +++ b/tests/test_utils/test_logging.py @@ -0,0 +1,100 @@ +import logging +from typing import Any, Dict, cast + +import pytest + +from openai._utils import SensitiveHeadersFilter + + +@pytest.fixture +def logger_with_filter() -> logging.Logger: + logger = logging.getLogger("test_logger") + logger.setLevel(logging.DEBUG) + logger.addFilter(SensitiveHeadersFilter()) + return logger + + +def test_keys_redacted(logger_with_filter: logging.Logger, caplog: pytest.LogCaptureFixture) -> None: + with caplog.at_level(logging.DEBUG): + logger_with_filter.debug( + "Request options: %s", + { + "method": "post", + "url": "chat/completions", + "headers": {"api-key": "12345", "Authorization": "Bearer token"}, + }, + ) + + log_record = cast(Dict[str, Any], caplog.records[0].args) + assert log_record["method"] == "post" + assert log_record["url"] == "chat/completions" + assert log_record["headers"]["api-key"] == "" + assert log_record["headers"]["Authorization"] == "" + assert ( + caplog.messages[0] + == "Request options: {'method': 'post', 'url': 'chat/completions', 'headers': {'api-key': '', 'Authorization': ''}}" + ) + + +def test_keys_redacted_case_insensitive(logger_with_filter: logging.Logger, caplog: pytest.LogCaptureFixture) -> None: + with caplog.at_level(logging.DEBUG): + logger_with_filter.debug( + "Request options: %s", + { + "method": "post", + "url": "chat/completions", + "headers": {"Api-key": "12345", "authorization": "Bearer token"}, + }, + ) + + log_record = cast(Dict[str, Any], caplog.records[0].args) + assert log_record["method"] == "post" + assert log_record["url"] == "chat/completions" + assert log_record["headers"]["Api-key"] == "" + assert log_record["headers"]["authorization"] == "" + assert ( + caplog.messages[0] + == "Request options: {'method': 'post', 'url': 'chat/completions', 'headers': {'Api-key': '', 'authorization': ''}}" + ) + + +def test_no_headers(logger_with_filter: logging.Logger, caplog: pytest.LogCaptureFixture) -> None: + with caplog.at_level(logging.DEBUG): + logger_with_filter.debug( + "Request options: %s", + {"method": "post", "url": "chat/completions"}, + ) + + log_record = cast(Dict[str, Any], caplog.records[0].args) + assert log_record["method"] == "post" + assert log_record["url"] == "chat/completions" + assert "api-key" not in log_record + assert "Authorization" not in log_record + assert caplog.messages[0] == "Request options: {'method': 'post', 'url': 'chat/completions'}" + + +def test_headers_without_sensitive_info(logger_with_filter: logging.Logger, caplog: pytest.LogCaptureFixture) -> None: + with caplog.at_level(logging.DEBUG): + logger_with_filter.debug( + "Request options: %s", + { + "method": "post", + "url": "chat/completions", + "headers": {"custom": "value"}, + }, + ) + + log_record = cast(Dict[str, Any], caplog.records[0].args) + assert log_record["method"] == "post" + assert log_record["url"] == "chat/completions" + assert log_record["headers"] == {"custom": "value"} + assert ( + caplog.messages[0] + == "Request options: {'method': 'post', 'url': 'chat/completions', 'headers': {'custom': 'value'}}" + ) + + +def test_standard_debug_msg(logger_with_filter: logging.Logger, caplog: pytest.LogCaptureFixture) -> None: + with caplog.at_level(logging.DEBUG): + logger_with_filter.debug("Sending HTTP Request: %s %s", "POST", "chat/completions") + assert caplog.messages[0] == "Sending HTTP Request: POST chat/completions" From 646a579cdb305a9d3fba6c5f9a96011c5e2c2882 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:18:25 +0000 Subject: [PATCH 663/914] release: 1.54.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 11adb14fb9..2b6bc65c52 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.54.2" + ".": "1.54.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a18e3c8e..4addfb1025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.54.3 (2024-11-06) + +Full Changelog: [v1.54.2...v1.54.3](https://github.com/openai/openai-python/compare/v1.54.2...v1.54.3) + +### Bug Fixes + +* **logs:** redact sensitive headers ([#1850](https://github.com/openai/openai-python/issues/1850)) ([466608f](https://github.com/openai/openai-python/commit/466608fa56b7a9939c08a4c78be2f6fe4a05111b)) + ## 1.54.2 (2024-11-06) Full Changelog: [v1.54.1...v1.54.2](https://github.com/openai/openai-python/compare/v1.54.1...v1.54.2) diff --git a/pyproject.toml b/pyproject.toml index 13087f543f..386f85e491 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.54.2" +version = "1.54.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 614d97d0f0..848cd40935 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.54.2" # x-release-please-version +__version__ = "1.54.3" # x-release-please-version From db0aa22c4299606e6b6b86d6dd3e473e902d274a Mon Sep 17 00:00:00 2001 From: Adel Basli Date: Mon, 11 Nov 2024 11:17:26 +0100 Subject: [PATCH 664/914] docs(readme): add missing asyncio import (#1858) * fix missing import * reorder imports --------- Co-authored-by: Robert Craigie --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bc334e7e07..47504b9137 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,7 @@ for chunk in stream: The async client uses the exact same interface. ```python +import asyncio from openai import AsyncOpenAI client = AsyncOpenAI() From 6c6dfb19e49a604fb35e97b7f5adf654a5397ed6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 22:39:39 +0000 Subject: [PATCH 665/914] docs: move comments in example snippets (#1860) --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 47504b9137..1051f10bf2 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,7 @@ import os from openai import OpenAI client = OpenAI( - # This is the default and can be omitted - api_key=os.environ.get("OPENAI_API_KEY"), + api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted ) chat_completion = client.chat.completions.create( @@ -153,8 +152,7 @@ import asyncio from openai import AsyncOpenAI client = AsyncOpenAI( - # This is the default and can be omitted - api_key=os.environ.get("OPENAI_API_KEY"), + api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted ) From 23444ed92cc81b3b1a8f17432f29b4020b50f023 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Tue, 12 Nov 2024 07:43:40 +0000 Subject: [PATCH 666/914] docs: bump models in example snippets to gpt-4o (#1861) --- README.md | 29 +++++++++++++++++------------ tests/test_client.py | 8 ++++---- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1051f10bf2..f1cd97b96e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ chat_completion = client.chat.completions.create( "content": "Say this is a test", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ) ``` @@ -164,7 +164,7 @@ async def main() -> None: "content": "Say this is a test", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ) @@ -183,8 +183,13 @@ from openai import OpenAI client = OpenAI() stream = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Say this is a test"}], + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-4o", stream=True, ) for chunk in stream: @@ -231,7 +236,7 @@ openai.base_url = "https://.../" openai.default_headers = {"x-foo": "true"} completion = openai.chat.completions.create( - model="gpt-4", + model="gpt-4o", messages=[ { "role": "user", @@ -349,7 +354,7 @@ completion = client.chat.completions.create( "content": "Can you generate an example json object describing a fruit?", } ], - model="gpt-3.5-turbo-1106", + model="gpt-4o", response_format={"type": "json_object"}, ) ``` @@ -389,7 +394,7 @@ client = OpenAI() try: client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", + model="gpt-4o", training_file="file-abc123", ) except openai.APIConnectionError as e: @@ -456,10 +461,10 @@ client.with_options(max_retries=5).chat.completions.create( messages=[ { "role": "user", - "content": "How can I get the name of the current day in Node.js?", + "content": "How can I get the name of the current day in JavaScript?", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ) ``` @@ -490,7 +495,7 @@ client.with_options(timeout=5.0).chat.completions.create( "content": "How can I list all files in a directory using Python?", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ) ``` @@ -535,7 +540,7 @@ response = client.chat.completions.with_raw_response.create( "role": "user", "content": "Say this is a test", }], - model="gpt-3.5-turbo", + model="gpt-4o", ) print(response.headers.get('X-My-Header')) @@ -568,7 +573,7 @@ with client.chat.completions.with_streaming_response.create( "content": "Say this is a test", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ) as response: print(response.headers.get("X-My-Header")) diff --git a/tests/test_client.py b/tests/test_client.py index 912ea1316c..7ea2ab38d1 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -727,7 +727,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No "content": "Say this is a test", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ), ), cast_to=httpx.Response, @@ -753,7 +753,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non "content": "Say this is a test", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ), ), cast_to=httpx.Response, @@ -1594,7 +1594,7 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) "content": "Say this is a test", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ), ), cast_to=httpx.Response, @@ -1620,7 +1620,7 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) "content": "Say this is a test", } ], - model="gpt-3.5-turbo", + model="gpt-4o", ), ), cast_to=httpx.Response, From 9b28850fcd777a249e127ae4080d2720b4b76896 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:11:55 +0000 Subject: [PATCH 667/914] fix: don't use dicts as iterables in transform (#1865) --- src/openai/_utils/_transform.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index d7c05345d1..a6b62cad0c 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -316,6 +316,11 @@ async def _async_transform_recursive( # 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] From 52357cff50bee57ef442e94d78a0de38b4173fc2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:12:24 +0000 Subject: [PATCH 668/914] release: 1.54.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2b6bc65c52..7bfe725d47 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.54.3" + ".": "1.54.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4addfb1025..d82ac42553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.54.4 (2024-11-12) + +Full Changelog: [v1.54.3...v1.54.4](https://github.com/openai/openai-python/compare/v1.54.3...v1.54.4) + +### Bug Fixes + +* don't use dicts as iterables in transform ([#1865](https://github.com/openai/openai-python/issues/1865)) ([76a51b1](https://github.com/openai/openai-python/commit/76a51b11efae50659a562197b1e18c6343964b56)) + + +### Documentation + +* bump models in example snippets to gpt-4o ([#1861](https://github.com/openai/openai-python/issues/1861)) ([adafe08](https://github.com/openai/openai-python/commit/adafe0859178d406fa93b38f3547f3d262651331)) +* move comments in example snippets ([#1860](https://github.com/openai/openai-python/issues/1860)) ([362cf74](https://github.com/openai/openai-python/commit/362cf74d6c34506f98f6c4fb2304357be21f7691)) +* **readme:** add missing asyncio import ([#1858](https://github.com/openai/openai-python/issues/1858)) ([dec9d0c](https://github.com/openai/openai-python/commit/dec9d0c97b702b6bcf9c71f5bdd6172bb5718354)) + ## 1.54.3 (2024-11-06) Full Changelog: [v1.54.2...v1.54.3](https://github.com/openai/openai-python/compare/v1.54.2...v1.54.3) diff --git a/pyproject.toml b/pyproject.toml index 386f85e491..e0a20e8387 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.54.3" +version = "1.54.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 848cd40935..5e531dd083 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.54.3" # x-release-please-version +__version__ = "1.54.4" # x-release-please-version From dd19d4f94a68ccecf38f23d10f5de2568345b79d Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 18 Nov 2024 10:35:41 +0000 Subject: [PATCH 669/914] chore(tests): limit array example length (#1870) --- .../audio/test_transcriptions.py | 4 +- tests/api_resources/beta/test_assistants.py | 20 +- tests/api_resources/beta/test_threads.py | 584 ++---------------- .../api_resources/beta/test_vector_stores.py | 4 +- .../beta/threads/test_messages.py | 28 +- tests/api_resources/beta/threads/test_runs.py | 460 ++------------ tests/api_resources/chat/test_completions.py | 88 +-- tests/api_resources/fine_tuning/test_jobs.py | 44 +- tests/api_resources/test_uploads.py | 20 +- 9 files changed, 146 insertions(+), 1106 deletions(-) diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index 0fa91eb152..bdb7e0dfb6 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -34,7 +34,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: prompt="string", response_format="json", temperature=0, - timestamp_granularities=["word", "segment"], + timestamp_granularities=["word"], ) assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @@ -85,7 +85,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> prompt="string", response_format="json", temperature=0, - timestamp_granularities=["word", "segment"], + timestamp_granularities=["word"], ) assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 642935cdaf..d9944448b7 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -39,19 +39,19 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: response_format="auto", temperature=1, tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { "chunking_strategy": {"type": "auto"}, - "file_ids": ["string", "string", "string"], + "file_ids": ["string"], "metadata": {}, } ], }, }, - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, ) assert_matches_type(Assistant, assistant, path=["response"]) @@ -137,10 +137,10 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: response_format="auto", temperature=1, tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, }, - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, ) assert_matches_type(Assistant, assistant, path=["response"]) @@ -271,19 +271,19 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> response_format="auto", temperature=1, tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { "chunking_strategy": {"type": "auto"}, - "file_ids": ["string", "string", "string"], + "file_ids": ["string"], "metadata": {}, } ], }, }, - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, ) assert_matches_type(Assistant, assistant, path=["response"]) @@ -369,10 +369,10 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> response_format="auto", temperature=1, tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, }, - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, ) assert_matches_type(Assistant, assistant, path=["response"]) diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 95bebd84f5..789f870d6a 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -35,104 +35,22 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, + } ], metadata={}, tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { "chunking_strategy": {"type": "auto"}, - "file_ids": ["string", "string", "string"], + "file_ids": ["string"], "metadata": {}, } ], @@ -212,7 +130,7 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: "string", metadata={}, tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, }, ) @@ -314,104 +232,22 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, + } ], "metadata": {}, "tool_resources": { - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { "chunking_strategy": {"type": "auto"}, - "file_ids": ["string", "string", "string"], + "file_ids": ["string"], "metadata": {}, } ], @@ -420,10 +256,10 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) }, tool_choice="none", tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, }, - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, truncation_strategy={ "type": "auto", @@ -484,104 +320,22 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, + } ], "metadata": {}, "tool_resources": { - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { "chunking_strategy": {"type": "auto"}, - "file_ids": ["string", "string", "string"], + "file_ids": ["string"], "metadata": {}, } ], @@ -590,10 +344,10 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) }, tool_choice="none", tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, }, - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, truncation_strategy={ "type": "auto", @@ -645,104 +399,22 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, + } ], metadata={}, tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { "chunking_strategy": {"type": "auto"}, - "file_ids": ["string", "string", "string"], + "file_ids": ["string"], "metadata": {}, } ], @@ -822,7 +494,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> "string", metadata={}, tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, }, ) @@ -924,104 +596,22 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, + } ], "metadata": {}, "tool_resources": { - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { "chunking_strategy": {"type": "auto"}, - "file_ids": ["string", "string", "string"], + "file_ids": ["string"], "metadata": {}, } ], @@ -1030,10 +620,10 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie }, tool_choice="none", tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, }, - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, truncation_strategy={ "type": "auto", @@ -1094,104 +684,22 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, + } ], "metadata": {}, "tool_resources": { - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": { "vector_store_ids": ["string"], "vector_stores": [ { "chunking_strategy": {"type": "auto"}, - "file_ids": ["string", "string", "string"], + "file_ids": ["string"], "metadata": {}, } ], @@ -1200,10 +708,10 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie }, tool_choice="none", tool_resources={ - "code_interpreter": {"file_ids": ["string", "string", "string"]}, + "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, }, - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, truncation_strategy={ "type": "auto", diff --git a/tests/api_resources/beta/test_vector_stores.py b/tests/api_resources/beta/test_vector_stores.py index 39fdb9d1d4..99e1970c33 100644 --- a/tests/api_resources/beta/test_vector_stores.py +++ b/tests/api_resources/beta/test_vector_stores.py @@ -34,7 +34,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "anchor": "last_active_at", "days": 1, }, - file_ids=["string", "string", "string"], + file_ids=["string"], metadata={}, name="string", ) @@ -239,7 +239,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "anchor": "last_active_at", "days": 1, }, - file_ids=["string", "string", "string"], + file_ids=["string"], metadata={}, name="string", ) diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index b5be32a421..06c37e608a 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -38,17 +38,9 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: role="user", attachments=[ { - "file_id": "string", - "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], - }, - { - "file_id": "string", - "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], - }, - { - "file_id": "string", - "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], metadata={}, ) @@ -315,17 +307,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> role="user", attachments=[ { - "file_id": "string", - "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], - }, - { - "file_id": "string", - "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], - }, - { - "file_id": "string", - "tools": [{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], metadata={}, ) diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index c8d70f5f89..c48cc6de43 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -43,94 +43,12 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, + } ], instructions="string", max_completion_tokens=256, @@ -142,7 +60,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: stream=False, temperature=1, tool_choice="none", - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, truncation_strategy={ "type": "auto", @@ -208,94 +126,12 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, + } ], instructions="string", max_completion_tokens=256, @@ -306,7 +142,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: response_format="auto", temperature=1, tool_choice="none", - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, truncation_strategy={ "type": "auto", @@ -556,9 +392,9 @@ def test_path_params_cancel(self, client: OpenAI) -> None: @parametrize def test_method_submit_tool_outputs_overload_1(self, client: OpenAI) -> None: run = client.beta.threads.runs.submit_tool_outputs( - "string", - thread_id="string", - tool_outputs=[{}, {}, {}], + run_id="run_id", + thread_id="thread_id", + tool_outputs=[{}], ) assert_matches_type(Run, run, path=["response"]) @@ -571,15 +407,7 @@ def test_method_submit_tool_outputs_with_all_params_overload_1(self, client: Ope { "output": "output", "tool_call_id": "tool_call_id", - }, - { - "output": "output", - "tool_call_id": "tool_call_id", - }, - { - "output": "output", - "tool_call_id": "tool_call_id", - }, + } ], stream=False, ) @@ -588,9 +416,9 @@ def test_method_submit_tool_outputs_with_all_params_overload_1(self, client: Ope @parametrize def test_raw_response_submit_tool_outputs_overload_1(self, client: OpenAI) -> None: response = client.beta.threads.runs.with_raw_response.submit_tool_outputs( - "string", - thread_id="string", - tool_outputs=[{}, {}, {}], + run_id="run_id", + thread_id="thread_id", + tool_outputs=[{}], ) assert response.is_closed is True @@ -601,9 +429,9 @@ def test_raw_response_submit_tool_outputs_overload_1(self, client: OpenAI) -> No @parametrize def test_streaming_response_submit_tool_outputs_overload_1(self, client: OpenAI) -> None: with client.beta.threads.runs.with_streaming_response.submit_tool_outputs( - "string", - thread_id="string", - tool_outputs=[{}, {}, {}], + run_id="run_id", + thread_id="thread_id", + tool_outputs=[{}], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -619,14 +447,14 @@ def test_path_params_submit_tool_outputs_overload_1(self, client: OpenAI) -> Non client.beta.threads.runs.with_raw_response.submit_tool_outputs( "string", thread_id="", - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): client.beta.threads.runs.with_raw_response.submit_tool_outputs( - "", - thread_id="string", - tool_outputs=[{}, {}, {}], + run_id="", + thread_id="thread_id", + tool_outputs=[{}], ) @parametrize @@ -635,7 +463,7 @@ def test_method_submit_tool_outputs_overload_2(self, client: OpenAI) -> None: "string", thread_id="string", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) run_stream.response.close() @@ -645,7 +473,7 @@ def test_raw_response_submit_tool_outputs_overload_2(self, client: OpenAI) -> No "string", thread_id="string", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -658,7 +486,7 @@ def test_streaming_response_submit_tool_outputs_overload_2(self, client: OpenAI) "string", thread_id="string", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -675,7 +503,7 @@ def test_path_params_submit_tool_outputs_overload_2(self, client: OpenAI) -> Non "string", thread_id="", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): @@ -683,7 +511,7 @@ def test_path_params_submit_tool_outputs_overload_2(self, client: OpenAI) -> Non "", thread_id="string", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) @@ -711,94 +539,12 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, + } ], instructions="string", max_completion_tokens=256, @@ -810,7 +556,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn stream=False, temperature=1, tool_choice="none", - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, truncation_strategy={ "type": "auto", @@ -876,94 +622,12 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "role": "user", "attachments": [ { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, + "file_id": "file_id", + "tools": [{"type": "code_interpreter"}], + } ], "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, - { - "content": "string", - "role": "user", - "attachments": [ - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - { - "file_id": "string", - "tools": [ - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - {"type": "code_interpreter"}, - ], - }, - ], - "metadata": {}, - }, + } ], instructions="string", max_completion_tokens=256, @@ -974,7 +638,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn response_format="auto", temperature=1, tool_choice="none", - tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}], + tools=[{"type": "code_interpreter"}], top_p=1, truncation_strategy={ "type": "auto", @@ -1224,9 +888,9 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_submit_tool_outputs_overload_1(self, async_client: AsyncOpenAI) -> None: run = await async_client.beta.threads.runs.submit_tool_outputs( - "string", - thread_id="string", - tool_outputs=[{}, {}, {}], + run_id="run_id", + thread_id="thread_id", + tool_outputs=[{}], ) assert_matches_type(Run, run, path=["response"]) @@ -1239,15 +903,7 @@ async def test_method_submit_tool_outputs_with_all_params_overload_1(self, async { "output": "output", "tool_call_id": "tool_call_id", - }, - { - "output": "output", - "tool_call_id": "tool_call_id", - }, - { - "output": "output", - "tool_call_id": "tool_call_id", - }, + } ], stream=False, ) @@ -1256,9 +912,9 @@ async def test_method_submit_tool_outputs_with_all_params_overload_1(self, async @parametrize async def test_raw_response_submit_tool_outputs_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( - "string", - thread_id="string", - tool_outputs=[{}, {}, {}], + run_id="run_id", + thread_id="thread_id", + tool_outputs=[{}], ) assert response.is_closed is True @@ -1269,9 +925,9 @@ async def test_raw_response_submit_tool_outputs_overload_1(self, async_client: A @parametrize async def test_streaming_response_submit_tool_outputs_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.beta.threads.runs.with_streaming_response.submit_tool_outputs( - "string", - thread_id="string", - tool_outputs=[{}, {}, {}], + run_id="run_id", + thread_id="thread_id", + tool_outputs=[{}], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1287,14 +943,14 @@ async def test_path_params_submit_tool_outputs_overload_1(self, async_client: As await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( "string", thread_id="", - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): await async_client.beta.threads.runs.with_raw_response.submit_tool_outputs( - "", - thread_id="string", - tool_outputs=[{}, {}, {}], + run_id="", + thread_id="thread_id", + tool_outputs=[{}], ) @parametrize @@ -1303,7 +959,7 @@ async def test_method_submit_tool_outputs_overload_2(self, async_client: AsyncOp "string", thread_id="string", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) await run_stream.response.aclose() @@ -1313,7 +969,7 @@ async def test_raw_response_submit_tool_outputs_overload_2(self, async_client: A "string", thread_id="string", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1326,7 +982,7 @@ async def test_streaming_response_submit_tool_outputs_overload_2(self, async_cli "string", thread_id="string", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1343,7 +999,7 @@ async def test_path_params_submit_tool_outputs_overload_2(self, async_client: As "string", thread_id="", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): @@ -1351,5 +1007,5 @@ async def test_path_params_submit_tool_outputs_overload_2(self, async_client: As "", thread_id="string", stream=True, - tool_outputs=[{}, {}, {}], + tool_outputs=[{}], ) diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index dafedac9fb..1b52650b1d 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -62,7 +62,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_completion_tokens=0, max_tokens=0, metadata={"foo": "string"}, - modalities=["text", "audio"], + modalities=["text"], n=1, parallel_tool_calls=True, prediction={ @@ -88,25 +88,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "strict": True, }, "type": "function", - }, - { - "function": { - "name": "name", - "description": "description", - "parameters": {"foo": "bar"}, - "strict": True, - }, - "type": "function", - }, - { - "function": { - "name": "name", - "description": "description", - "parameters": {"foo": "bar"}, - "strict": True, - }, - "type": "function", - }, + } ], top_logprobs=0, top_p=1, @@ -194,7 +176,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_completion_tokens=0, max_tokens=0, metadata={"foo": "string"}, - modalities=["text", "audio"], + modalities=["text"], n=1, parallel_tool_calls=True, prediction={ @@ -219,25 +201,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "strict": True, }, "type": "function", - }, - { - "function": { - "name": "name", - "description": "description", - "parameters": {"foo": "bar"}, - "strict": True, - }, - "type": "function", - }, - { - "function": { - "name": "name", - "description": "description", - "parameters": {"foo": "bar"}, - "strict": True, - }, - "type": "function", - }, + } ], top_logprobs=0, top_p=1, @@ -345,7 +309,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_completion_tokens=0, max_tokens=0, metadata={"foo": "string"}, - modalities=["text", "audio"], + modalities=["text"], n=1, parallel_tool_calls=True, prediction={ @@ -371,25 +335,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "strict": True, }, "type": "function", - }, - { - "function": { - "name": "name", - "description": "description", - "parameters": {"foo": "bar"}, - "strict": True, - }, - "type": "function", - }, - { - "function": { - "name": "name", - "description": "description", - "parameters": {"foo": "bar"}, - "strict": True, - }, - "type": "function", - }, + } ], top_logprobs=0, top_p=1, @@ -477,7 +423,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_completion_tokens=0, max_tokens=0, metadata={"foo": "string"}, - modalities=["text", "audio"], + modalities=["text"], n=1, parallel_tool_calls=True, prediction={ @@ -502,25 +448,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "strict": True, }, "type": "function", - }, - { - "function": { - "name": "name", - "description": "description", - "parameters": {"foo": "bar"}, - "strict": True, - }, - "type": "function", - }, - { - "function": { - "name": "name", - "description": "description", - "parameters": {"foo": "bar"}, - "strict": True, - }, - "type": "function", - }, + } ], top_logprobs=0, top_p=1, diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index d1ad611219..aa2bf39528 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -46,27 +46,9 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "project": "my-wandb-project", "entity": "entity", "name": "name", - "tags": ["custom-tag", "custom-tag", "custom-tag"], + "tags": ["custom-tag"], }, - }, - { - "type": "wandb", - "wandb": { - "project": "my-wandb-project", - "entity": "entity", - "name": "name", - "tags": ["custom-tag", "custom-tag", "custom-tag"], - }, - }, - { - "type": "wandb", - "wandb": { - "project": "my-wandb-project", - "entity": "entity", - "name": "name", - "tags": ["custom-tag", "custom-tag", "custom-tag"], - }, - }, + } ], seed=42, suffix="x", @@ -285,27 +267,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "project": "my-wandb-project", "entity": "entity", "name": "name", - "tags": ["custom-tag", "custom-tag", "custom-tag"], - }, - }, - { - "type": "wandb", - "wandb": { - "project": "my-wandb-project", - "entity": "entity", - "name": "name", - "tags": ["custom-tag", "custom-tag", "custom-tag"], - }, - }, - { - "type": "wandb", - "wandb": { - "project": "my-wandb-project", - "entity": "entity", - "name": "name", - "tags": ["custom-tag", "custom-tag", "custom-tag"], + "tags": ["custom-tag"], }, - }, + } ], seed=42, suffix="x", diff --git a/tests/api_resources/test_uploads.py b/tests/api_resources/test_uploads.py index cb62df6b51..a14c4f8da2 100644 --- a/tests/api_resources/test_uploads.py +++ b/tests/api_resources/test_uploads.py @@ -99,7 +99,7 @@ def test_path_params_cancel(self, client: OpenAI) -> None: def test_method_complete(self, client: OpenAI) -> None: upload = client.uploads.complete( upload_id="upload_abc123", - part_ids=["string", "string", "string"], + part_ids=["string"], ) assert_matches_type(Upload, upload, path=["response"]) @@ -107,7 +107,7 @@ def test_method_complete(self, client: OpenAI) -> None: def test_method_complete_with_all_params(self, client: OpenAI) -> None: upload = client.uploads.complete( upload_id="upload_abc123", - part_ids=["string", "string", "string"], + part_ids=["string"], md5="md5", ) assert_matches_type(Upload, upload, path=["response"]) @@ -116,7 +116,7 @@ def test_method_complete_with_all_params(self, client: OpenAI) -> None: def test_raw_response_complete(self, client: OpenAI) -> None: response = client.uploads.with_raw_response.complete( upload_id="upload_abc123", - part_ids=["string", "string", "string"], + part_ids=["string"], ) assert response.is_closed is True @@ -128,7 +128,7 @@ def test_raw_response_complete(self, client: OpenAI) -> None: def test_streaming_response_complete(self, client: OpenAI) -> None: with client.uploads.with_streaming_response.complete( upload_id="upload_abc123", - part_ids=["string", "string", "string"], + part_ids=["string"], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -143,7 +143,7 @@ def test_path_params_complete(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): client.uploads.with_raw_response.complete( upload_id="", - part_ids=["string", "string", "string"], + part_ids=["string"], ) @@ -232,7 +232,7 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: async def test_method_complete(self, async_client: AsyncOpenAI) -> None: upload = await async_client.uploads.complete( upload_id="upload_abc123", - part_ids=["string", "string", "string"], + part_ids=["string"], ) assert_matches_type(Upload, upload, path=["response"]) @@ -240,7 +240,7 @@ async def test_method_complete(self, async_client: AsyncOpenAI) -> None: async def test_method_complete_with_all_params(self, async_client: AsyncOpenAI) -> None: upload = await async_client.uploads.complete( upload_id="upload_abc123", - part_ids=["string", "string", "string"], + part_ids=["string"], md5="md5", ) assert_matches_type(Upload, upload, path=["response"]) @@ -249,7 +249,7 @@ async def test_method_complete_with_all_params(self, async_client: AsyncOpenAI) async def test_raw_response_complete(self, async_client: AsyncOpenAI) -> None: response = await async_client.uploads.with_raw_response.complete( upload_id="upload_abc123", - part_ids=["string", "string", "string"], + part_ids=["string"], ) assert response.is_closed is True @@ -261,7 +261,7 @@ async def test_raw_response_complete(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_complete(self, async_client: AsyncOpenAI) -> None: async with async_client.uploads.with_streaming_response.complete( upload_id="upload_abc123", - part_ids=["string", "string", "string"], + part_ids=["string"], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -276,5 +276,5 @@ async def test_path_params_complete(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): await async_client.uploads.with_raw_response.complete( upload_id="", - part_ids=["string", "string", "string"], + part_ids=["string"], ) From 0d6185edc2eb4155699a28415b8759653f91ef52 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:42:24 +0000 Subject: [PATCH 670/914] chore(internal): spec update (#1873) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index f368bc881d..fdef8d2744 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-2f8ca92b9b1879fd535b685e4767338413fcd533d42f3baac13a9c41da3fce35.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-fb9db2d2c1f0d6b39d8ee042db5d5c59acba6ad1daf47c18792c1f5fb24b3401.yml From d8901d28587ad9db4f1435f1f00ad7b919c232f7 Mon Sep 17 00:00:00 2001 From: Seth Gilchrist Date: Mon, 18 Nov 2024 04:41:32 -0800 Subject: [PATCH 671/914] fix(asyncify): avoid hanging process under certain conditions (#1853) --- pyproject.toml | 4 +- requirements-dev.lock | 2 + requirements.lock | 1 + src/openai/_utils/_sync.py | 90 +++++++++++++++++--------------------- tests/test_client.py | 43 ++++++++++++++++++ 5 files changed, 88 insertions(+), 52 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e0a20e8387..b22ef1927d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,9 @@ dev-dependencies = [ "azure-identity >=1.14.1", "types-tqdm > 4", "types-pyaudio > 0", - "trio >=0.22.2" + "trio >=0.22.2", + "nest_asyncio==1.6.0" + ] [tool.rye.scripts] diff --git a/requirements-dev.lock b/requirements-dev.lock index 5fe1ccad57..4d0ab191a4 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -7,6 +7,7 @@ # all-features: true # with-sources: false # generate-hashes: false +# universal: false -e file:. annotated-types==0.6.0 @@ -87,6 +88,7 @@ mypy==1.13.0 mypy-extensions==1.0.0 # via black # via mypy +nest-asyncio==1.6.0 nodeenv==1.8.0 # via pyright nox==2023.4.22 diff --git a/requirements.lock b/requirements.lock index 019dfcb4c5..aef8bc0a9a 100644 --- a/requirements.lock +++ b/requirements.lock @@ -7,6 +7,7 @@ # all-features: true # with-sources: false # generate-hashes: false +# universal: false -e file:. annotated-types==0.6.0 diff --git a/src/openai/_utils/_sync.py b/src/openai/_utils/_sync.py index d0d810337e..c0a0ae714c 100644 --- a/src/openai/_utils/_sync.py +++ b/src/openai/_utils/_sync.py @@ -1,56 +1,60 @@ from __future__ import annotations +import sys +import asyncio import functools -from typing import TypeVar, Callable, Awaitable +import contextvars +from typing import Any, TypeVar, Callable, Awaitable from typing_extensions import ParamSpec -import anyio -import anyio.to_thread - -from ._reflection import function_has_argument - T_Retval = TypeVar("T_Retval") T_ParamSpec = ParamSpec("T_ParamSpec") -# copied from `asyncer`, https://github.com/tiangolo/asyncer -def asyncify( - function: Callable[T_ParamSpec, T_Retval], - *, - cancellable: bool = False, - limiter: anyio.CapacityLimiter | None = None, -) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: - """ - Take a blocking function and create an async one that receives the same - positional and keyword arguments, and that when called, calls the original function - in a worker thread using `anyio.to_thread.run_sync()`. Internally, - `asyncer.asyncify()` uses the same `anyio.to_thread.run_sync()`, but it supports - keyword arguments additional to positional arguments and it adds better support for - autocompletion and inline errors for the arguments of the function called and the - return value. +if sys.version_info >= (3, 9): + to_thread = asyncio.to_thread +else: + 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. - If the `cancellable` option is enabled and the task waiting for its completion is - cancelled, the thread will still run its course but its return value (or any raised - exception) will be ignored. + 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. - Use it like this: + 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) + + to_thread = _to_thread + +# 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. - ```Python - def do_work(arg1, arg2, kwarg1="", kwarg2="") -> str: - # Do work - return "Some result" + Usage: + ```python + def blocking_func(arg1, arg2, kwarg1=None): + # blocking code + return result - result = await to_thread.asyncify(do_work)("spam", "ham", kwarg1="a", kwarg2="b") - print(result) + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) ``` ## Arguments `function`: a blocking regular callable (e.g. a function) - `cancellable`: `True` to allow cancellation of the operation - `limiter`: capacity limiter to use to limit the total amount of threads running - (if omitted, the default limiter is used) ## Return @@ -60,22 +64,6 @@ def do_work(arg1, arg2, kwarg1="", kwarg2="") -> str: """ async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: - partial_f = functools.partial(function, *args, **kwargs) - - # In `v4.1.0` anyio added the `abandon_on_cancel` argument and deprecated the old - # `cancellable` argument, so we need to use the new `abandon_on_cancel` to avoid - # surfacing deprecation warnings. - if function_has_argument(anyio.to_thread.run_sync, "abandon_on_cancel"): - return await anyio.to_thread.run_sync( - partial_f, - abandon_on_cancel=cancellable, - limiter=limiter, - ) - - return await anyio.to_thread.run_sync( - partial_f, - cancellable=cancellable, - limiter=limiter, - ) + return await to_thread(function, *args, **kwargs) return wrapper diff --git a/tests/test_client.py b/tests/test_client.py index 7ea2ab38d1..08aff23f53 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -4,11 +4,14 @@ import gc import os +import sys import json import asyncio import inspect +import subprocess import tracemalloc from typing import Any, Union, cast +from textwrap import dedent from unittest import mock from typing_extensions import Literal @@ -1766,3 +1769,43 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: ) as response: assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + def test_get_platform(self) -> None: + # Issue https://github.com/openai/openai-python/issues/1827 was caused + # asyncify leaving threads unterminated when used with nest_asyncio. + # Since nest_asyncio.apply() is global and cannot be un-applied, this + # test is run in a separate process to avoid affecting other tests. + test_code = dedent("""\ + import asyncio + import nest_asyncio + + import threading + + from openai._base_client import get_platform + from openai._utils import asyncify + + async def test_main() -> None: + result = await asyncify(get_platform)() + print(result) + for thread in threading.enumerate(): + print(thread.name) + + nest_asyncio.apply() + asyncio.run(test_main()) + """) + with subprocess.Popen( + [sys.executable, "-c", test_code], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) as process: + try: + process.wait(2) + if process.returncode: + print(process.stdout) + print(process.stderr) + raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") + except subprocess.TimeoutExpired as e: + process.kill() + raise AssertionError("calling get_platform using asyncify resulted in a hung process") from e + From 53ab046716631e5539304052162e5b374a2d6f15 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 18 Nov 2024 12:58:39 +0000 Subject: [PATCH 672/914] chore(internal): minor test changes (#1874) --- src/openai/_utils/_sync.py | 5 +++-- tests/test_client.py | 15 +++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/openai/_utils/_sync.py b/src/openai/_utils/_sync.py index c0a0ae714c..5d9e2c2ac9 100644 --- a/src/openai/_utils/_sync.py +++ b/src/openai/_utils/_sync.py @@ -14,7 +14,9 @@ if sys.version_info >= (3, 9): to_thread = asyncio.to_thread else: - async def _to_thread( + # 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. @@ -31,7 +33,6 @@ async def _to_thread( func_call = functools.partial(ctx.run, func, *args, **kwargs) return await loop.run_in_executor(None, func_call) - to_thread = _to_thread # inspired by `asyncer`, https://github.com/tiangolo/asyncer def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: diff --git a/tests/test_client.py b/tests/test_client.py index 08aff23f53..7caa8cb319 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1771,18 +1771,18 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success def test_get_platform(self) -> None: - # Issue https://github.com/openai/openai-python/issues/1827 was caused - # asyncify leaving threads unterminated when used with nest_asyncio. + # A previous implementation of asyncify could leave threads unterminated when + # used with nest_asyncio. + # # Since nest_asyncio.apply() is global and cannot be un-applied, this # test is run in a separate process to avoid affecting other tests. - test_code = dedent("""\ + test_code = dedent(""" import asyncio import nest_asyncio - import threading - from openai._base_client import get_platform from openai._utils import asyncify + from openai._base_client import get_platform async def test_main() -> None: result = await asyncify(get_platform)() @@ -1795,17 +1795,12 @@ async def test_main() -> None: """) with subprocess.Popen( [sys.executable, "-c", test_code], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, text=True, ) as process: try: process.wait(2) if process.returncode: - print(process.stdout) - print(process.stderr) raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") except subprocess.TimeoutExpired as e: process.kill() raise AssertionError("calling get_platform using asyncify resulted in a hung process") from e - From 7cdc6ddbdd3c09e567c5582490aec9d7f99c468e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 05:04:28 +0000 Subject: [PATCH 673/914] release: 1.54.5 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7bfe725d47..68c5231faa 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.54.4" + ".": "1.54.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d82ac42553..c646eca314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.54.5 (2024-11-19) + +Full Changelog: [v1.54.4...v1.54.5](https://github.com/openai/openai-python/compare/v1.54.4...v1.54.5) + +### Bug Fixes + +* **asyncify:** avoid hanging process under certain conditions ([#1853](https://github.com/openai/openai-python/issues/1853)) ([3d23437](https://github.com/openai/openai-python/commit/3d234377e7c9cd19db5186688612eb18e68cec8f)) + + +### Chores + +* **internal:** minor test changes ([#1874](https://github.com/openai/openai-python/issues/1874)) ([189339d](https://github.com/openai/openai-python/commit/189339d2a09d23ea1883286972f366e19b397f91)) +* **internal:** spec update ([#1873](https://github.com/openai/openai-python/issues/1873)) ([24c81f7](https://github.com/openai/openai-python/commit/24c81f729ae09ba3cec5542e5cc955c8b05b0f88)) +* **tests:** limit array example length ([#1870](https://github.com/openai/openai-python/issues/1870)) ([1e550df](https://github.com/openai/openai-python/commit/1e550df708fc3b5d903b7adfa2180058a216b676)) + ## 1.54.4 (2024-11-12) Full Changelog: [v1.54.3...v1.54.4](https://github.com/openai/openai-python/compare/v1.54.3...v1.54.4) diff --git a/pyproject.toml b/pyproject.toml index b22ef1927d..8138ffbaef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.54.4" +version = "1.54.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5e531dd083..75ee06518f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.54.4" # x-release-please-version +__version__ = "1.54.5" # x-release-please-version From 8eba381dc2b062ca41e909adef95540a6234be50 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:17:44 +0000 Subject: [PATCH 674/914] feat(api): add gpt-4o-2024-11-20 model (#1877) --- .stats.yml | 2 +- src/openai/resources/batches.py | 4 ++-- src/openai/resources/files.py | 4 ++-- src/openai/types/batch_create_params.py | 2 +- src/openai/types/chat/chat_completion_audio_param.py | 5 +++-- src/openai/types/chat_model.py | 1 + 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index fdef8d2744..4827e5388f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-fb9db2d2c1f0d6b39d8ee042db5d5c59acba6ad1daf47c18792c1f5fb24b3401.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-aa9b01fc0c17eb0cbc200533fc20d6a49c5e764ceaf8049e08b294532be6e9ff.yml diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index a8a0ba4bbc..7cab75785d 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -81,7 +81,7 @@ def create( Your input file must be formatted as a [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 - requests, and can be up to 100 MB in size. + requests, and can be up to 200 MB in size. metadata: Optional custom metadata for the batch. @@ -286,7 +286,7 @@ async def create( Your input file must be formatted as a [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 - requests, and can be up to 100 MB in size. + requests, and can be up to 200 MB in size. metadata: Optional custom metadata for the batch. diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 77706a7fd8..6eaea1b568 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -86,7 +86,7 @@ def create( [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) models. - The Batch API only supports `.jsonl` files up to 100 MB in size. The input also + The Batch API only supports `.jsonl` files up to 200 MB in size. The input also has a specific required [format](https://platform.openai.com/docs/api-reference/batch/request-input). @@ -402,7 +402,7 @@ async def create( [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) models. - The Batch API only supports `.jsonl` files up to 100 MB in size. The input also + The Batch API only supports `.jsonl` files up to 200 MB in size. The input also has a specific required [format](https://platform.openai.com/docs/api-reference/batch/request-input). diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index 55517d285b..b30c4d4658 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -32,7 +32,7 @@ class BatchCreateParams(TypedDict, total=False): Your input file must be formatted as a [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 - requests, and can be up to 100 MB in size. + requests, and can be up to 200 MB in size. """ metadata: Optional[Dict[str, str]] diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index b92326d294..1e20a52b41 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -17,6 +17,7 @@ class ChatCompletionAudioParam(TypedDict, total=False): voice: Required[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] """The voice the model uses to respond. - Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, - `shimmer`, and `verse`. + Supported voices are `ash`, `ballad`, `coral`, `sage`, and `verse` (also + supported but not recommended are `alloy`, `echo`, and `shimmer`; these voices + are less expressive). """ diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index b801aa0914..3567a3ba65 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -10,6 +10,7 @@ "o1-mini", "o1-mini-2024-09-12", "gpt-4o", + "gpt-4o-2024-11-20", "gpt-4o-2024-08-06", "gpt-4o-2024-05-13", "gpt-4o-realtime-preview", From 83091e96cf43f344d22799c22eea301aeae36d51 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:18:15 +0000 Subject: [PATCH 675/914] release: 1.55.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 68c5231faa..061f355bf3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.54.5" + ".": "1.55.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c646eca314..921f9b7bad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.55.0 (2024-11-20) + +Full Changelog: [v1.54.5...v1.55.0](https://github.com/openai/openai-python/compare/v1.54.5...v1.55.0) + +### Features + +* **api:** add gpt-4o-2024-11-20 model ([#1877](https://github.com/openai/openai-python/issues/1877)) ([ff64c2a](https://github.com/openai/openai-python/commit/ff64c2a0733854ed8cc1d7dd959a8287b2ec8120)) + ## 1.54.5 (2024-11-19) Full Changelog: [v1.54.4...v1.54.5](https://github.com/openai/openai-python/compare/v1.54.4...v1.54.5) diff --git a/pyproject.toml b/pyproject.toml index 8138ffbaef..02b3fbfb93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.54.5" +version = "1.55.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 75ee06518f..093c3e3939 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.54.5" # x-release-please-version +__version__ = "1.55.0" # x-release-please-version From e9cbb256650a07c008e6529778e10cc66e9f7605 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:22:30 +0000 Subject: [PATCH 676/914] fix(pydantic-v1): avoid runtime error for assistants streaming (#1885) --- src/openai/_compat.py | 3 ++- tests/test_models.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 7c3156a5eb..d7196c9193 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -145,7 +145,8 @@ def model_dump( exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, - warnings=warnings, + # warnings are not supported in Pydantic v1 + warnings=warnings if PYDANTIC_V2 else True, ) return cast( "dict[str, Any]", diff --git a/tests/test_models.py b/tests/test_models.py index 84dbce6914..d2884bcbfa 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -561,6 +561,14 @@ class Model(BaseModel): m.model_dump(warnings=False) +def test_compat_method_no_error_for_warnings() -> None: + class Model(BaseModel): + foo: Optional[str] + + m = Model(foo="hello") + assert isinstance(model_dump(m, warnings=False), dict) + + def test_to_json() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) From f6199d60a0384bfb71c0ca2eb24c5765d760715e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:06:42 +0000 Subject: [PATCH 677/914] docs: add info log level to readme (#1887) --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f1cd97b96e..5854e8d3ad 100644 --- a/README.md +++ b/README.md @@ -509,12 +509,14 @@ Note that requests that time out are [retried twice by default](#retries). We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. -You can enable logging by setting the environment variable `OPENAI_LOG` to `debug`. +You can enable logging by setting the environment variable `OPENAI_LOG` to `info`. ```shell -$ export OPENAI_LOG=debug +$ export OPENAI_LOG=info ``` +Or to `debug` for more verbose logging. + ### How to tell whether `None` means `null` or missing In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: From 5dfb00886eb56db340a689380c6a481a7b7ea34f Mon Sep 17 00:00:00 2001 From: Harutaka Kawamura Date: Mon, 25 Nov 2024 21:17:54 +0900 Subject: [PATCH 678/914] chore: remove now unused `cached-property` dep (#1867) --- pyproject.toml | 1 - src/openai/_compat.py | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 02b3fbfb93..4c41631edd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,6 @@ dependencies = [ "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", - "cached-property; python_version < '3.8'", "tqdm > 4", "jiter>=0.4.0, <1", ] diff --git a/src/openai/_compat.py b/src/openai/_compat.py index d7196c9193..87fc370765 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -226,9 +226,6 @@ def __set_name__(self, owner: type[Any], name: str) -> None: ... # __set__ is not defined at runtime, but @cached_property is designed to be settable def __set__(self, instance: object, value: _T) -> None: ... else: - try: - from functools import cached_property as cached_property - except ImportError: - from cached_property import cached_property as cached_property + from functools import cached_property as cached_property typed_cached_property = cached_property From 83f4774156dc3e29c7fe6be9ffd681df68534509 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:25:56 +0000 Subject: [PATCH 679/914] release: 1.55.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 061f355bf3..af721f5395 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.55.0" + ".": "1.55.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 921f9b7bad..409d3c2df0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.55.1 (2024-11-25) + +Full Changelog: [v1.55.0...v1.55.1](https://github.com/openai/openai-python/compare/v1.55.0...v1.55.1) + +### Bug Fixes + +* **pydantic-v1:** avoid runtime error for assistants streaming ([#1885](https://github.com/openai/openai-python/issues/1885)) ([197c94b](https://github.com/openai/openai-python/commit/197c94b9e2620da8902aeed6959d2f871bb70461)) + + +### Chores + +* remove now unused `cached-property` dep ([#1867](https://github.com/openai/openai-python/issues/1867)) ([df5fac1](https://github.com/openai/openai-python/commit/df5fac1e557f79ed8d0935c48ca7f3f0bf77fa98)) +* remove now unused `cached-property` dep ([#1891](https://github.com/openai/openai-python/issues/1891)) ([feebaae](https://github.com/openai/openai-python/commit/feebaae85d76960cb8f1c58dd9b5180136c47962)) + + +### Documentation + +* add info log level to readme ([#1887](https://github.com/openai/openai-python/issues/1887)) ([358255d](https://github.com/openai/openai-python/commit/358255d15ed220f8c80a3c0861b98e61e909a7ae)) + ## 1.55.0 (2024-11-20) Full Changelog: [v1.54.5...v1.55.0](https://github.com/openai/openai-python/compare/v1.54.5...v1.55.0) diff --git a/pyproject.toml b/pyproject.toml index 4c41631edd..fb48acf1f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.55.0" +version = "1.55.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 093c3e3939..f3c5f8db8b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.55.0" # x-release-please-version +__version__ = "1.55.1" # x-release-please-version From 3ad59995e7475fa30007255d2a26bad09392b515 Mon Sep 17 00:00:00 2001 From: Vincent Josse Date: Wed, 27 Nov 2024 11:35:24 +0100 Subject: [PATCH 680/914] docs(assistants): correct on_text_delta example (#1896) --- src/openai/lib/streaming/_assistants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/lib/streaming/_assistants.py b/src/openai/lib/streaming/_assistants.py index 103e4c40aa..6efb3ca3f1 100644 --- a/src/openai/lib/streaming/_assistants.py +++ b/src/openai/lib/streaming/_assistants.py @@ -243,7 +243,7 @@ def on_text_delta(self, delta: TextDelta, snapshot: Text) -> None: on_text_delta(TextDelta(value=" solution"), Text(value="The solution")), on_text_delta(TextDelta(value=" to"), Text(value="The solution to")), on_text_delta(TextDelta(value=" the"), Text(value="The solution to the")), - on_text_delta(TextDelta(value=" equation"), Text(value="The solution to the equivalent")), + on_text_delta(TextDelta(value=" equation"), Text(value="The solution to the equation")), """ def on_text_done(self, text: Text) -> None: From f2607f54b9f51a6f3fcb168834bd6351c1512ab9 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Wed, 27 Nov 2024 14:04:44 +0000 Subject: [PATCH 681/914] chore(internal): exclude mypy from running on tests (#1899) --- mypy.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy.ini b/mypy.ini index 97e5de4a60..50e5add04b 100644 --- a/mypy.ini +++ b/mypy.ini @@ -5,7 +5,10 @@ show_error_codes = True # Exclude _files.py and _logs.py because mypy isn't smart enough to apply # the correct type narrowing and as this is an internal module # it's fine to just use Pyright. -exclude = ^(src/openai/_files\.py|src/openai/_utils/_logs\.py|_dev/.*\.py)$ +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ^(src/openai/_files\.py|src/openai/_utils/_logs\.py|_dev/.*\.py|tests/.*)$ strict_equality = True implicit_reexport = True From 95bd2582a1e37bb35eac429925ffa0aea10078a5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:05:13 +0000 Subject: [PATCH 682/914] release: 1.55.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index af721f5395..488f1adb5e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.55.1" + ".": "1.55.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 409d3c2df0..8009aac671 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.55.2 (2024-11-27) + +Full Changelog: [v1.55.1...v1.55.2](https://github.com/openai/openai-python/compare/v1.55.1...v1.55.2) + +### Chores + +* **internal:** exclude mypy from running on tests ([#1899](https://github.com/openai/openai-python/issues/1899)) ([e2496f1](https://github.com/openai/openai-python/commit/e2496f1d274126bdaa46a8256b3dd384b4ae244b)) + + +### Documentation + +* **assistants:** correct on_text_delta example ([#1896](https://github.com/openai/openai-python/issues/1896)) ([460b663](https://github.com/openai/openai-python/commit/460b663567ed1031467a8d69eb13fd3b3da38827)) + ## 1.55.1 (2024-11-25) Full Changelog: [v1.55.0...v1.55.1](https://github.com/openai/openai-python/compare/v1.55.0...v1.55.1) diff --git a/pyproject.toml b/pyproject.toml index fb48acf1f2..4842cceea4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.55.1" +version = "1.55.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f3c5f8db8b..5b04f5cc00 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.55.1" # x-release-please-version +__version__ = "1.55.2" # x-release-please-version From bb9cf7a6acfd1729fa76247da041a4787a6bfc1a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:16:48 +0000 Subject: [PATCH 683/914] fix(client): compat with new httpx 0.28.0 release (#1904) --- src/openai/_base_client.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 187518787a..cceec903d9 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -794,6 +794,7 @@ def __init__( custom_query: Mapping[str, object] | None = None, _strict_response_validation: bool, ) -> None: + kwargs: dict[str, Any] = {} if limits is not None: warnings.warn( "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", @@ -806,6 +807,7 @@ def __init__( limits = DEFAULT_CONNECTION_LIMITS if transport is not None: + kwargs["transport"] = transport warnings.warn( "The `transport` argument is deprecated. The `http_client` argument should be passed instead", category=DeprecationWarning, @@ -815,6 +817,7 @@ def __init__( raise ValueError("The `http_client` argument is mutually exclusive with `transport`") if proxies is not None: + kwargs["proxies"] = proxies warnings.warn( "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", category=DeprecationWarning, @@ -858,10 +861,9 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, limits=limits, follow_redirects=True, + **kwargs, # type: ignore ) def is_closed(self) -> bool: @@ -1375,6 +1377,7 @@ def __init__( custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: + kwargs: dict[str, Any] = {} if limits is not None: warnings.warn( "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", @@ -1387,6 +1390,7 @@ def __init__( limits = DEFAULT_CONNECTION_LIMITS if transport is not None: + kwargs["transport"] = transport warnings.warn( "The `transport` argument is deprecated. The `http_client` argument should be passed instead", category=DeprecationWarning, @@ -1396,6 +1400,7 @@ def __init__( raise ValueError("The `http_client` argument is mutually exclusive with `transport`") if proxies is not None: + kwargs["proxies"] = proxies warnings.warn( "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", category=DeprecationWarning, @@ -1439,10 +1444,9 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, limits=limits, follow_redirects=True, + **kwargs, # type: ignore ) def is_closed(self) -> bool: From 6974a981aec1814b5abba429a8ea21be9ac58538 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:17:16 +0000 Subject: [PATCH 684/914] release: 1.55.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 488f1adb5e..d23d0104a4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.55.2" + ".": "1.55.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8009aac671..866d34cb4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.55.3 (2024-11-28) + +Full Changelog: [v1.55.2...v1.55.3](https://github.com/openai/openai-python/compare/v1.55.2...v1.55.3) + +### Bug Fixes + +* **client:** compat with new httpx 0.28.0 release ([#1904](https://github.com/openai/openai-python/issues/1904)) ([72b6c63](https://github.com/openai/openai-python/commit/72b6c636c526885ef873580a07eff1c18e76bc10)) + ## 1.55.2 (2024-11-27) Full Changelog: [v1.55.1...v1.55.2](https://github.com/openai/openai-python/compare/v1.55.1...v1.55.2) diff --git a/pyproject.toml b/pyproject.toml index 4842cceea4..06c0e7fd73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.55.2" +version = "1.55.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5b04f5cc00..c6d4c88a6d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.55.2" # x-release-please-version +__version__ = "1.55.3" # x-release-please-version From 778e28e5658b4fcf2b11b51b5d7506bd1884e2d2 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 29 Nov 2024 16:05:14 -0500 Subject: [PATCH 685/914] feat(client): make ChatCompletionStreamState public (#1898) --- src/openai/lib/streaming/chat/__init__.py | 1 + src/openai/lib/streaming/chat/_completions.py | 33 ++++++- tests/lib/chat/test_completions_streaming.py | 94 ++++++++++++++++++- 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/openai/lib/streaming/chat/__init__.py b/src/openai/lib/streaming/chat/__init__.py index 5881c39b9a..dfa3f3f2e3 100644 --- a/src/openai/lib/streaming/chat/__init__.py +++ b/src/openai/lib/streaming/chat/__init__.py @@ -21,6 +21,7 @@ from ._completions import ( ChatCompletionStream as ChatCompletionStream, AsyncChatCompletionStream as AsyncChatCompletionStream, + ChatCompletionStreamState as ChatCompletionStreamState, ChatCompletionStreamManager as ChatCompletionStreamManager, AsyncChatCompletionStreamManager as AsyncChatCompletionStreamManager, ) diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index 8518de967f..2146091354 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -287,11 +287,31 @@ async def __aexit__( class ChatCompletionStreamState(Generic[ResponseFormatT]): + """Helper class for manually accumulating `ChatCompletionChunk`s into a final `ChatCompletion` object. + + This is useful in cases where you can't always use the `.stream()` method, e.g. + + ```py + from openai.lib.streaming.chat import ChatCompletionStreamState + + state = ChatCompletionStreamState() + + stream = client.chat.completions.create(..., stream=True) + for chunk in response: + state.handle_chunk(chunk) + + # can also access the accumulated `ChatCompletion` mid-stream + state.current_completion_snapshot + + print(state.get_final_completion()) + ``` + """ + def __init__( self, *, - input_tools: Iterable[ChatCompletionToolParam] | NotGiven, - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + input_tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven = NOT_GIVEN, ) -> None: self.__current_completion_snapshot: ParsedChatCompletionSnapshot | None = None self.__choice_event_states: list[ChoiceEventState] = [] @@ -301,6 +321,11 @@ def __init__( self._rich_response_format: type | NotGiven = response_format if inspect.isclass(response_format) else NOT_GIVEN def get_final_completion(self) -> ParsedChatCompletion[ResponseFormatT]: + """Parse the final completion object. + + Note this does not provide any guarantees that the stream has actually finished, you must + only call this method when the stream is finished. + """ return parse_chat_completion( chat_completion=self.current_completion_snapshot, response_format=self._rich_response_format, @@ -312,8 +337,8 @@ def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: assert self.__current_completion_snapshot is not None return self.__current_completion_snapshot - def handle_chunk(self, chunk: ChatCompletionChunk) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: - """Accumulate a new chunk into the snapshot and returns a list of events to yield.""" + def handle_chunk(self, chunk: ChatCompletionChunk) -> Iterable[ChatCompletionStreamEvent[ResponseFormatT]]: + """Accumulate a new chunk into the snapshot and returns an iterable of events to yield.""" self.__current_completion_snapshot = self._accumulate_chunk(chunk) return self._build_events( diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index ab12de44b3..1eed031af7 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -13,12 +13,14 @@ import openai from openai import OpenAI, AsyncOpenAI -from openai._utils import assert_signatures_in_sync +from openai._utils import consume_sync_iterator, assert_signatures_in_sync from openai._compat import model_copy +from openai.types.chat import ChatCompletionChunk from openai.lib.streaming.chat import ( ContentDoneEvent, ChatCompletionStream, ChatCompletionStreamEvent, + ChatCompletionStreamState, ChatCompletionStreamManager, ParsedChatCompletionSnapshot, ) @@ -997,6 +999,55 @@ def test_allows_non_strict_tools_but_no_parsing( ) +@pytest.mark.respx(base_url=base_url) +def test_chat_completion_state_helper(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: + state = ChatCompletionStreamState() + + def streamer(client: OpenAI) -> Iterator[ChatCompletionChunk]: + stream = client.chat.completions.create( + model="gpt-4o-2024-08-06", + messages=[ + { + "role": "user", + "content": "What's the weather like in SF?", + }, + ], + stream=True, + ) + for chunk in stream: + state.handle_chunk(chunk) + yield chunk + + _make_raw_stream_snapshot_request( + streamer, + content_snapshot=snapshot(external("e2aad469b71d*.bin")), + mock_client=client, + respx_mock=respx_mock, + ) + + assert print_obj(state.get_final_completion().choices, monkeypatch) == snapshot( + """\ +[ + ParsedChoice[NoneType]( + finish_reason='stop', + index=0, + logprobs=None, + message=ParsedChatCompletionMessage[NoneType]( + audio=None, + content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I +recommend checking a reliable weather website or a weather app.", + function_call=None, + parsed=None, + refusal=None, + role='assistant', + tool_calls=[] + ) + ) +] +""" + ) + + @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_stream_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: checking_client: OpenAI | AsyncOpenAI = client if sync else async_client @@ -1075,3 +1126,44 @@ def _on_response(response: httpx.Response) -> None: client.close() return listener + + +def _make_raw_stream_snapshot_request( + func: Callable[[OpenAI], Iterator[ChatCompletionChunk]], + *, + content_snapshot: Any, + respx_mock: MockRouter, + mock_client: OpenAI, +) -> None: + live = os.environ.get("OPENAI_LIVE") == "1" + if live: + + def _on_response(response: httpx.Response) -> None: + # update the content snapshot + assert outsource(response.read()) == content_snapshot + + respx_mock.stop() + + client = OpenAI( + http_client=httpx.Client( + event_hooks={ + "response": [_on_response], + } + ) + ) + else: + respx_mock.post("/chat/completions").mock( + return_value=httpx.Response( + 200, + content=content_snapshot._old_value._load_value(), + headers={"content-type": "text/event-stream"}, + ) + ) + + client = mock_client + + stream = func(client) + consume_sync_iterator(stream) + + if live: + client.close() From 534d6c58f6c07d219ca74dd336eaca59d48d0ada Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 05:04:35 +0000 Subject: [PATCH 686/914] release: 1.56.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d23d0104a4..24b1176fb1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.55.3" + ".": "1.56.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 866d34cb4f..614dbb5795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.56.0 (2024-12-02) + +Full Changelog: [v1.55.3...v1.56.0](https://github.com/openai/openai-python/compare/v1.55.3...v1.56.0) + +### Features + +* **client:** make ChatCompletionStreamState public ([#1898](https://github.com/openai/openai-python/issues/1898)) ([dc7f6cb](https://github.com/openai/openai-python/commit/dc7f6cb2618686ff04bfdca228913cda3d320884)) + ## 1.55.3 (2024-11-28) Full Changelog: [v1.55.2...v1.55.3](https://github.com/openai/openai-python/compare/v1.55.2...v1.55.3) diff --git a/pyproject.toml b/pyproject.toml index 06c0e7fd73..b5cf535fe5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.55.3" +version = "1.56.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index c6d4c88a6d..8561c9379b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.55.3" # x-release-please-version +__version__ = "1.56.0" # x-release-please-version From 439ab56fe0933077a41290f588a6528f89e05c87 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 2 Dec 2024 15:21:20 -0500 Subject: [PATCH 687/914] fix(cli): remove usage of httpx proxies --- src/openai/cli/_cli.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/openai/cli/_cli.py b/src/openai/cli/_cli.py index 72e5c923bd..fd165f48ab 100644 --- a/src/openai/cli/_cli.py +++ b/src/openai/cli/_cli.py @@ -15,7 +15,6 @@ from .. import _ApiType, __version__ from ._api import register_commands from ._utils import can_use_http2 -from .._types import ProxiesDict from ._errors import CLIError, display_error from .._compat import PYDANTIC_V2, ConfigDict, model_parse from .._models import BaseModel @@ -167,17 +166,17 @@ def _main() -> None: if args.verbosity != 0: sys.stderr.write("Warning: --verbosity isn't supported yet\n") - proxies: ProxiesDict = {} + 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] = proxy + proxies[key] = httpx.HTTPTransport(proxy=httpx.Proxy(httpx.URL(proxy))) http_client = httpx.Client( - proxies=proxies or None, + mounts=proxies or None, http2=can_use_http2(), ) openai.http_client = http_client From 6a692ffb5e00e0e1ff9ae39633a62774c6fb5c31 Mon Sep 17 00:00:00 2001 From: Stainless Bot Date: Mon, 2 Dec 2024 20:47:19 +0000 Subject: [PATCH 688/914] chore(internal): bump pyright (#1917) --- requirements-dev.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 4d0ab191a4..c8e74372aa 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -125,7 +125,7 @@ pygments==2.18.0 # via rich pyjwt==2.8.0 # via msal -pyright==1.1.380 +pyright==1.1.389 pytest==8.3.3 # via pytest-asyncio pytest-asyncio==0.24.0 @@ -179,6 +179,7 @@ typing-extensions==4.12.2 # via openai # via pydantic # via pydantic-core + # via pyright tzdata==2024.1 # via pandas urllib3==2.2.1 From 5e3e4d1b0f16ccc4469a90a5bff09cafe0de7a2e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 05:04:28 +0000 Subject: [PATCH 689/914] release: 1.56.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 24b1176fb1..7e4064260b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.56.0" + ".": "1.56.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 614dbb5795..ad4ea007e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.56.1 (2024-12-03) + +Full Changelog: [v1.56.0...v1.56.1](https://github.com/openai/openai-python/compare/v1.56.0...v1.56.1) + +### Bug Fixes + +* **cli:** remove usage of httpx proxies ([0e9fc3d](https://github.com/openai/openai-python/commit/0e9fc3dfbc7dec5b8c8f84dea9d87aad9f3d9cf6)) + + +### Chores + +* **internal:** bump pyright ([#1917](https://github.com/openai/openai-python/issues/1917)) ([0e87346](https://github.com/openai/openai-python/commit/0e8734637666ab22bc27fe4ec2cf7c39fddb5d08)) + ## 1.56.0 (2024-12-02) Full Changelog: [v1.55.3...v1.56.0](https://github.com/openai/openai-python/compare/v1.55.3...v1.56.0) diff --git a/pyproject.toml b/pyproject.toml index b5cf535fe5..93ababf9b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.56.0" +version = "1.56.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 8561c9379b..c879d22094 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.56.0" # x-release-please-version +__version__ = "1.56.1" # x-release-please-version From f3f2ae529a86b110f97a38977b20794284be1726 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 02:15:40 +0000 Subject: [PATCH 690/914] chore: make the `Omit` type public (#1919) --- src/openai/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 3c1ebb573d..21c60f7e87 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -6,7 +6,7 @@ from typing_extensions import override from . import types -from ._types import NOT_GIVEN, NoneType, NotGiven, Transport, ProxiesTypes +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes from ._utils import file_from_path from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions from ._models import BaseModel @@ -43,6 +43,7 @@ "ProxiesTypes", "NotGiven", "NOT_GIVEN", + "Omit", "OpenAIError", "APIError", "APIStatusError", From bb9c2de913279acc89e79f6154173a422f31de45 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 05:04:28 +0000 Subject: [PATCH 691/914] release: 1.56.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7e4064260b..028ed90273 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.56.1" + ".": "1.56.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4ea007e8..f91f69338b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.56.2 (2024-12-04) + +Full Changelog: [v1.56.1...v1.56.2](https://github.com/openai/openai-python/compare/v1.56.1...v1.56.2) + +### Chores + +* make the `Omit` type public ([#1919](https://github.com/openai/openai-python/issues/1919)) ([4fb8a1c](https://github.com/openai/openai-python/commit/4fb8a1cf1f8df37ce8c027bbaaac85a648bae02a)) + ## 1.56.1 (2024-12-03) Full Changelog: [v1.56.0...v1.56.1](https://github.com/openai/openai-python/compare/v1.56.0...v1.56.1) diff --git a/pyproject.toml b/pyproject.toml index 93ababf9b7..7ca731eb7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.56.1" +version = "1.56.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index c879d22094..190d3a5fe1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.56.1" # x-release-please-version +__version__ = "1.56.2" # x-release-please-version From 52f3525276149fb2375d4af5e5903ffa77330cc3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:20:39 +0000 Subject: [PATCH 692/914] chore: bump openapi url (#1922) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 4827e5388f..19920c8be8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-aa9b01fc0c17eb0cbc200533fc20d6a49c5e764ceaf8049e08b294532be6e9ff.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-d702cba829ceda336f44d0eb89ce61dba353849a40f0193e7007439345daf1bb.yml From afa2b1e089f9b43ff8db35ecb554c688aba5cf01 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:53:44 +0000 Subject: [PATCH 693/914] feat(api): updates (#1924) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 19920c8be8..3cc042fe0a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-d702cba829ceda336f44d0eb89ce61dba353849a40f0193e7007439345daf1bb.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-2e0e0678be19d1118fd796af291822075e40538dba326611e177e9f3dc245a53.yml From ea049cd0c42e115b90f1b9c7db80b2659a0bb92a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 05:04:40 +0000 Subject: [PATCH 694/914] release: 1.57.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 028ed90273..3794816acd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.56.2" + ".": "1.57.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f91f69338b..c5baf5ab80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.57.0 (2024-12-05) + +Full Changelog: [v1.56.2...v1.57.0](https://github.com/openai/openai-python/compare/v1.56.2...v1.57.0) + +### Features + +* **api:** updates ([#1924](https://github.com/openai/openai-python/issues/1924)) ([82ba614](https://github.com/openai/openai-python/commit/82ba6144682b0a6b3a22d4f764231c0c6afdcf6e)) + + +### Chores + +* bump openapi url ([#1922](https://github.com/openai/openai-python/issues/1922)) ([a472a8f](https://github.com/openai/openai-python/commit/a472a8fd0ba36b6897dcd02b6005fcf23f98f056)) + ## 1.56.2 (2024-12-04) Full Changelog: [v1.56.1...v1.56.2](https://github.com/openai/openai-python/compare/v1.56.1...v1.56.2) diff --git a/pyproject.toml b/pyproject.toml index 7ca731eb7d..c488c40622 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.56.2" +version = "1.57.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 190d3a5fe1..58e2de3bd5 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.56.2" # x-release-please-version +__version__ = "1.57.0" # x-release-please-version From 64e3ec0f571cf9b43bbe5d26e6629a8f9d6049fb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:40:47 +0000 Subject: [PATCH 695/914] chore(internal): bump pydantic dependency (#1929) --- requirements-dev.lock | 5 ++--- requirements.lock | 5 ++--- src/openai/_types.py | 6 ++---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index c8e74372aa..d7ecc4fcda 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -7,7 +7,6 @@ # all-features: true # with-sources: false # generate-hashes: false -# universal: false -e file:. annotated-types==0.6.0 @@ -117,9 +116,9 @@ portalocker==2.10.1 # via msal-extensions pycparser==2.22 # via cffi -pydantic==2.9.2 +pydantic==2.10.3 # via openai -pydantic-core==2.23.4 +pydantic-core==2.27.1 # via pydantic pygments==2.18.0 # via rich diff --git a/requirements.lock b/requirements.lock index aef8bc0a9a..826f0bc927 100644 --- a/requirements.lock +++ b/requirements.lock @@ -7,7 +7,6 @@ # all-features: true # with-sources: false # generate-hashes: false -# universal: false -e file:. annotated-types==0.6.0 @@ -41,9 +40,9 @@ pandas==2.2.3 # via openai pandas-stubs==2.2.2.240807 # via openai -pydantic==2.9.2 +pydantic==2.10.3 # via openai -pydantic-core==2.23.4 +pydantic-core==2.27.1 # via pydantic python-dateutil==2.9.0.post0 # via pandas diff --git a/src/openai/_types.py b/src/openai/_types.py index c8f4d5a922..a5cf207aa3 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -194,10 +194,8 @@ def get(self, __key: str) -> str | None: ... StrBytesIntFloat = Union[str, bytes, int, float] # Note: copied from Pydantic -# https://github.com/pydantic/pydantic/blob/32ea570bf96e84234d2992e1ddf40ab8a565925a/pydantic/main.py#L49 -IncEx: TypeAlias = Union[ - Set[int], Set[str], Mapping[int, Union["IncEx", Literal[True]]], Mapping[str, Union["IncEx", Literal[True]]] -] +# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 +IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] PostParser = Callable[[Any], Any] From 995cce048f9427bba4f7ac1e5fc60abbf1f8f0b7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:41:16 +0000 Subject: [PATCH 696/914] release: 1.57.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3794816acd..4a5d7b25e2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.57.0" + ".": "1.57.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c5baf5ab80..b436c25abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.57.1 (2024-12-09) + +Full Changelog: [v1.57.0...v1.57.1](https://github.com/openai/openai-python/compare/v1.57.0...v1.57.1) + +### Chores + +* **internal:** bump pydantic dependency ([#1929](https://github.com/openai/openai-python/issues/1929)) ([5227c95](https://github.com/openai/openai-python/commit/5227c95eff9c7b1395e6d8f14b94652a91ed2ee2)) + ## 1.57.0 (2024-12-05) Full Changelog: [v1.56.2...v1.57.0](https://github.com/openai/openai-python/compare/v1.56.2...v1.57.0) diff --git a/pyproject.toml b/pyproject.toml index c488c40622..9a92574c73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.57.0" +version = "1.57.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 58e2de3bd5..a59207d618 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.57.0" # x-release-please-version +__version__ = "1.57.1" # x-release-please-version From 6a1ab55104822b9e987e1227988c084cd415d294 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:18:18 +0000 Subject: [PATCH 697/914] docs(readme): fix http client proxies example (#1932) --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5854e8d3ad..780ee261fe 100644 --- a/README.md +++ b/README.md @@ -624,18 +624,19 @@ can also get all the extra fields on the Pydantic model as a dict with You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: -- Support for proxies -- Custom transports +- Support for [proxies](https://www.python-httpx.org/advanced/proxies/) +- Custom [transports](https://www.python-httpx.org/advanced/transports/) - Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality ```python +import httpx from openai import OpenAI, DefaultHttpxClient client = OpenAI( # Or use the `OPENAI_BASE_URL` env var base_url="http://my.test.server.example.com:8083/v1", http_client=DefaultHttpxClient( - proxies="http://my.test.proxy.example.com/", + proxy="http://my.test.proxy.example.com/", transport=httpx.HTTPTransport(local_address="0.0.0.0"), ), ) From 3cf3dd7b412414a6ca48a588ee4c7f0ef91c9e92 Mon Sep 17 00:00:00 2001 From: Kenji Hikmatullah <43457338+kenjihikmatullah@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:52:45 +0700 Subject: [PATCH 698/914] fix(azure): handle trailing slash in `azure_endpoint` (#1935) --- src/openai/lib/azure.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 5d21f10b70..54122dbecb 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -193,9 +193,9 @@ def __init__( ) if azure_deployment is not None: - base_url = f"{azure_endpoint}/openai/deployments/{azure_deployment}" + base_url = f"{azure_endpoint.rstrip('/')}/openai/deployments/{azure_deployment}" else: - base_url = f"{azure_endpoint}/openai" + base_url = f"{azure_endpoint.rstrip('/')}/openai" else: if azure_endpoint is not None: raise ValueError("base_url and azure_endpoint are mutually exclusive") @@ -433,9 +433,9 @@ def __init__( ) if azure_deployment is not None: - base_url = f"{azure_endpoint}/openai/deployments/{azure_deployment}" + base_url = f"{azure_endpoint.rstrip('/')}/openai/deployments/{azure_deployment}" else: - base_url = f"{azure_endpoint}/openai" + base_url = f"{azure_endpoint.rstrip('/')}/openai" else: if azure_endpoint is not None: raise ValueError("base_url and azure_endpoint are mutually exclusive") From 6e1161bc3ed20eef070063ddd5ac52fd9a531e88 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:53:14 +0000 Subject: [PATCH 699/914] release: 1.57.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4a5d7b25e2..18d9ec48a9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.57.1" + ".": "1.57.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b436c25abf..7319ebd651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.57.2 (2024-12-10) + +Full Changelog: [v1.57.1...v1.57.2](https://github.com/openai/openai-python/compare/v1.57.1...v1.57.2) + +### Bug Fixes + +* **azure:** handle trailing slash in `azure_endpoint` ([#1935](https://github.com/openai/openai-python/issues/1935)) ([69b73c5](https://github.com/openai/openai-python/commit/69b73c553b1982277c2f1b9d110ed951ddca689e)) + + +### Documentation + +* **readme:** fix http client proxies example ([#1932](https://github.com/openai/openai-python/issues/1932)) ([7a83e0f](https://github.com/openai/openai-python/commit/7a83e0fe4cc29e484ae417448b002c997745e4a3)) + ## 1.57.1 (2024-12-09) Full Changelog: [v1.57.0...v1.57.1](https://github.com/openai/openai-python/compare/v1.57.0...v1.57.1) diff --git a/pyproject.toml b/pyproject.toml index 9a92574c73..6df6f43789 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.57.1" +version = "1.57.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a59207d618..0757da4c78 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.57.1" # x-release-please-version +__version__ = "1.57.2" # x-release-please-version From f32d466b2d69b0ca8fa8a59f2b74ed84448f9459 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:18:04 +0000 Subject: [PATCH 700/914] chore(internal): bump pyright (#1939) --- requirements-dev.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index d7ecc4fcda..2cf6ab5ea9 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -124,7 +124,7 @@ pygments==2.18.0 # via rich pyjwt==2.8.0 # via msal -pyright==1.1.389 +pyright==1.1.390 pytest==8.3.3 # via pytest-asyncio pytest-asyncio==0.24.0 From bed71312acd1658d6128f9142f2882162bb127ec Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:44:38 +0000 Subject: [PATCH 701/914] chore(internal): add support for TypeAliasType (#1942) --- src/openai/_legacy_response.py | 20 ++++++++++---------- src/openai/_models.py | 3 +++ src/openai/_response.py | 20 ++++++++++---------- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_typing.py | 31 ++++++++++++++++++++++++++++++- tests/test_models.py | 18 +++++++++++++++++- tests/utils.py | 4 ++++ 7 files changed, 75 insertions(+), 22 deletions(-) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 5260e90bc1..7a14f27adb 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -24,7 +24,7 @@ import pydantic from ._types import NoneType -from ._utils import is_given, extract_type_arg, is_annotated_type +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type from ._models import BaseModel, is_basemodel, add_request_id from ._constants import RAW_RESPONSE_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type @@ -195,9 +195,15 @@ def elapsed(self) -> datetime.timedelta: return self.http_response.elapsed def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + # unwrap `Annotated[T, ...]` -> `T` - if to and is_annotated_type(to): - to = extract_type_arg(to, 0) + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) if self._stream: if to: @@ -233,18 +239,12 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: return cast( R, stream_cls( - cast_to=self._cast_to, + cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), ), ) - cast_to = to if to is not None else self._cast_to - - # unwrap `Annotated[T, ...]` -> `T` - if is_annotated_type(cast_to): - cast_to = extract_type_arg(cast_to, 0) - if cast_to is NoneType: return cast(R, None) diff --git a/src/openai/_models.py b/src/openai/_models.py index 20cd4c29bc..2f67e5eb4d 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -47,6 +47,7 @@ strip_not_given, extract_type_arg, is_annotated_type, + is_type_alias_type, strip_annotated_type, ) from ._compat import ( @@ -453,6 +454,8 @@ def construct_type(*, value: object, type_: object) -> object: # we allow `object` as the input type because otherwise, passing things like # `Literal['value']` will be reported as a type error by type checkers type_ = cast("type[object]", type_) + if is_type_alias_type(type_): + type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` if is_annotated_type(type_): diff --git a/src/openai/_response.py b/src/openai/_response.py index eac3fbae6c..1527446585 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -25,7 +25,7 @@ import pydantic from ._types import NoneType -from ._utils import is_given, extract_type_arg, is_annotated_type, extract_type_var_from_base +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base from ._models import BaseModel, is_basemodel, add_request_id from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type @@ -126,9 +126,15 @@ def __repr__(self) -> str: ) def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + # unwrap `Annotated[T, ...]` -> `T` - if to and is_annotated_type(to): - to = extract_type_arg(to, 0) + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) if self._is_sse_stream: if to: @@ -164,18 +170,12 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: return cast( R, stream_cls( - cast_to=self._cast_to, + cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), ), ) - cast_to = to if to is not None else self._cast_to - - # unwrap `Annotated[T, ...]` -> `T` - if is_annotated_type(cast_to): - cast_to = extract_type_arg(cast_to, 0) - if cast_to is NoneType: return cast(R, None) diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 5abb34cde4..af2c9bb77e 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -40,6 +40,7 @@ is_iterable_type as is_iterable_type, is_required_type as is_required_type, is_annotated_type as is_annotated_type, + is_type_alias_type as is_type_alias_type, strip_annotated_type as strip_annotated_type, extract_type_var_from_base as extract_type_var_from_base, ) diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py index c036991f04..278749b147 100644 --- a/src/openai/_utils/_typing.py +++ b/src/openai/_utils/_typing.py @@ -1,8 +1,17 @@ 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 Required, Annotated, get_args, get_origin +from typing_extensions import ( + TypeIs, + Required, + Annotated, + get_args, + get_origin, +) from .._types import InheritsGeneric from .._compat import is_union as _is_union @@ -36,6 +45,26 @@ def is_typevar(typ: type) -> bool: 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): diff --git a/tests/test_models.py b/tests/test_models.py index d2884bcbfa..19a71f13ba 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,7 +1,7 @@ import json from typing import Any, Dict, List, Union, Optional, cast from datetime import datetime, timezone -from typing_extensions import Literal, Annotated +from typing_extensions import Literal, Annotated, TypeAliasType import pytest import pydantic @@ -828,3 +828,19 @@ class B(BaseModel): # if the discriminator details object stays the same between invocations then # we hit the cache assert UnionType.__discriminator__ is discriminator + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +def test_type_alias_type() -> None: + Alias = TypeAliasType("Alias", str) + + class Model(BaseModel): + alias: Alias + union: Union[int, Alias] + + m = construct_type(value={"alias": "foo", "union": "bar"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.alias, str) + assert m.alias == "foo" + assert isinstance(m.union, str) + assert m.union == "bar" diff --git a/tests/utils.py b/tests/utils.py index 16948a66f2..4cf5ce171b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -19,6 +19,7 @@ is_union_type, extract_type_arg, is_annotated_type, + is_type_alias_type, ) from openai._compat import PYDANTIC_V2, field_outer_type, get_model_fields from openai._models import BaseModel @@ -58,6 +59,9 @@ def assert_matches_type( path: list[str], allow_none: bool = False, ) -> None: + if is_type_alias_type(type_): + type_ = type_.__value__ + # unwrap `Annotated[T, ...]` -> `T` if is_annotated_type(type_): type_ = extract_type_arg(type_, 0) From 0ae6f6b0ce55b6a9dd7e5caa684dfae2780c0088 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:45:06 +0000 Subject: [PATCH 702/914] release: 1.57.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 18d9ec48a9..58e64c502c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.57.2" + ".": "1.57.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7319ebd651..80ed457618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.57.3 (2024-12-12) + +Full Changelog: [v1.57.2...v1.57.3](https://github.com/openai/openai-python/compare/v1.57.2...v1.57.3) + +### Chores + +* **internal:** add support for TypeAliasType ([#1942](https://github.com/openai/openai-python/issues/1942)) ([d3442ff](https://github.com/openai/openai-python/commit/d3442ff28f2394200e14122f683d1f94686e8231)) +* **internal:** bump pyright ([#1939](https://github.com/openai/openai-python/issues/1939)) ([190d1a8](https://github.com/openai/openai-python/commit/190d1a805dee7c37fb8f9dcb93b1715caa06cf95)) + ## 1.57.2 (2024-12-10) Full Changelog: [v1.57.1...v1.57.2](https://github.com/openai/openai-python/compare/v1.57.1...v1.57.2) diff --git a/pyproject.toml b/pyproject.toml index 6df6f43789..a6a0868405 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.57.2" +version = "1.57.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0757da4c78..94ba432279 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.57.2" # x-release-please-version +__version__ = "1.57.3" # x-release-please-version From e93e3bd45fcef320c4fa70eed3987d852e9c96e5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:17:39 +0000 Subject: [PATCH 703/914] chore(internal): remove some duplicated imports (#1946) --- src/openai/resources/beta/beta.py | 20 +++++++++---------- src/openai/resources/beta/threads/threads.py | 17 ++++++++-------- .../resources/fine_tuning/fine_tuning.py | 5 ++--- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index a7d3e707c8..5079c989a5 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -2,14 +2,6 @@ from __future__ import annotations -from .threads import ( - Threads, - AsyncThreads, - ThreadsWithRawResponse, - AsyncThreadsWithRawResponse, - ThreadsWithStreamingResponse, - AsyncThreadsWithStreamingResponse, -) from ..._compat import cached_property from .chat.chat import Chat, AsyncChat from .assistants import ( @@ -21,7 +13,15 @@ AsyncAssistantsWithStreamingResponse, ) from ..._resource import SyncAPIResource, AsyncAPIResource -from .vector_stores import ( +from .threads.threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse, +) +from .vector_stores.vector_stores import ( VectorStores, AsyncVectorStores, VectorStoresWithRawResponse, @@ -29,8 +29,6 @@ VectorStoresWithStreamingResponse, AsyncVectorStoresWithStreamingResponse, ) -from .threads.threads import Threads, AsyncThreads -from .vector_stores.vector_stores import VectorStores, AsyncVectorStores __all__ = ["Beta", "AsyncBeta"] diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 058ba71a17..e45090abb0 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -9,14 +9,6 @@ import httpx from .... import _legacy_response -from .runs import ( - Runs, - AsyncRuns, - RunsWithRawResponse, - AsyncRunsWithRawResponse, - RunsWithStreamingResponse, - AsyncRunsWithStreamingResponse, -) from .messages import ( Messages, AsyncMessages, @@ -31,7 +23,14 @@ maybe_transform, async_maybe_transform, ) -from .runs.runs import Runs, AsyncRuns +from .runs.runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index c386de3c2a..d2bce87c48 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -2,7 +2,8 @@ from __future__ import annotations -from .jobs import ( +from ..._compat import cached_property +from .jobs.jobs import ( Jobs, AsyncJobs, JobsWithRawResponse, @@ -10,8 +11,6 @@ JobsWithStreamingResponse, AsyncJobsWithStreamingResponse, ) -from ..._compat import cached_property -from .jobs.jobs import Jobs, AsyncJobs from ..._resource import SyncAPIResource, AsyncAPIResource __all__ = ["FineTuning", "AsyncFineTuning"] From 2e2531d944a0c4ff748f9fb0b1c9a015f029dd2f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:01:59 +0000 Subject: [PATCH 704/914] chore(internal): updated imports (#1948) --- src/openai/_client.py | 212 +++++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 108 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index d3ee6cf0f1..5419e88f06 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -8,7 +8,7 @@ import httpx -from . import resources, _exceptions +from . import _exceptions from ._qs import Querystring from ._types import ( NOT_GIVEN, @@ -25,6 +25,7 @@ get_async_library, ) from ._version import __version__ +from .resources import files, images, models, batches, embeddings, completions, moderations from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError from ._base_client import ( @@ -32,33 +33,28 @@ SyncAPIClient, AsyncAPIClient, ) +from .resources.beta import beta +from .resources.chat import chat +from .resources.audio import audio +from .resources.uploads import uploads +from .resources.fine_tuning import fine_tuning -__all__ = [ - "Timeout", - "Transport", - "ProxiesTypes", - "RequestOptions", - "resources", - "OpenAI", - "AsyncOpenAI", - "Client", - "AsyncClient", -] +__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "OpenAI", "AsyncOpenAI", "Client", "AsyncClient"] class OpenAI(SyncAPIClient): - completions: resources.Completions - chat: resources.Chat - embeddings: resources.Embeddings - files: resources.Files - images: resources.Images - audio: resources.Audio - moderations: resources.Moderations - models: resources.Models - fine_tuning: resources.FineTuning - beta: resources.Beta - batches: resources.Batches - uploads: resources.Uploads + completions: completions.Completions + chat: chat.Chat + embeddings: embeddings.Embeddings + files: files.Files + images: images.Images + audio: audio.Audio + moderations: moderations.Moderations + models: models.Models + fine_tuning: fine_tuning.FineTuning + beta: beta.Beta + batches: batches.Batches + uploads: uploads.Uploads with_raw_response: OpenAIWithRawResponse with_streaming_response: OpenAIWithStreamedResponse @@ -133,18 +129,18 @@ def __init__( self._default_stream_cls = Stream - self.completions = resources.Completions(self) - self.chat = resources.Chat(self) - self.embeddings = resources.Embeddings(self) - self.files = resources.Files(self) - self.images = resources.Images(self) - self.audio = resources.Audio(self) - self.moderations = resources.Moderations(self) - self.models = resources.Models(self) - self.fine_tuning = resources.FineTuning(self) - self.beta = resources.Beta(self) - self.batches = resources.Batches(self) - self.uploads = resources.Uploads(self) + self.completions = completions.Completions(self) + self.chat = chat.Chat(self) + self.embeddings = embeddings.Embeddings(self) + self.files = files.Files(self) + self.images = images.Images(self) + self.audio = audio.Audio(self) + self.moderations = moderations.Moderations(self) + self.models = models.Models(self) + self.fine_tuning = fine_tuning.FineTuning(self) + self.beta = beta.Beta(self) + self.batches = batches.Batches(self) + self.uploads = uploads.Uploads(self) self.with_raw_response = OpenAIWithRawResponse(self) self.with_streaming_response = OpenAIWithStreamedResponse(self) @@ -261,18 +257,18 @@ def _make_status_error( class AsyncOpenAI(AsyncAPIClient): - completions: resources.AsyncCompletions - chat: resources.AsyncChat - embeddings: resources.AsyncEmbeddings - files: resources.AsyncFiles - images: resources.AsyncImages - audio: resources.AsyncAudio - moderations: resources.AsyncModerations - models: resources.AsyncModels - fine_tuning: resources.AsyncFineTuning - beta: resources.AsyncBeta - batches: resources.AsyncBatches - uploads: resources.AsyncUploads + completions: completions.AsyncCompletions + chat: chat.AsyncChat + embeddings: embeddings.AsyncEmbeddings + files: files.AsyncFiles + images: images.AsyncImages + audio: audio.AsyncAudio + moderations: moderations.AsyncModerations + models: models.AsyncModels + fine_tuning: fine_tuning.AsyncFineTuning + beta: beta.AsyncBeta + batches: batches.AsyncBatches + uploads: uploads.AsyncUploads with_raw_response: AsyncOpenAIWithRawResponse with_streaming_response: AsyncOpenAIWithStreamedResponse @@ -347,18 +343,18 @@ def __init__( self._default_stream_cls = AsyncStream - self.completions = resources.AsyncCompletions(self) - self.chat = resources.AsyncChat(self) - self.embeddings = resources.AsyncEmbeddings(self) - self.files = resources.AsyncFiles(self) - self.images = resources.AsyncImages(self) - self.audio = resources.AsyncAudio(self) - self.moderations = resources.AsyncModerations(self) - self.models = resources.AsyncModels(self) - self.fine_tuning = resources.AsyncFineTuning(self) - self.beta = resources.AsyncBeta(self) - self.batches = resources.AsyncBatches(self) - self.uploads = resources.AsyncUploads(self) + self.completions = completions.AsyncCompletions(self) + self.chat = chat.AsyncChat(self) + self.embeddings = embeddings.AsyncEmbeddings(self) + self.files = files.AsyncFiles(self) + self.images = images.AsyncImages(self) + self.audio = audio.AsyncAudio(self) + self.moderations = moderations.AsyncModerations(self) + self.models = models.AsyncModels(self) + self.fine_tuning = fine_tuning.AsyncFineTuning(self) + self.beta = beta.AsyncBeta(self) + self.batches = batches.AsyncBatches(self) + self.uploads = uploads.AsyncUploads(self) self.with_raw_response = AsyncOpenAIWithRawResponse(self) self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self) @@ -476,66 +472,66 @@ def _make_status_error( class OpenAIWithRawResponse: def __init__(self, client: OpenAI) -> None: - self.completions = resources.CompletionsWithRawResponse(client.completions) - self.chat = resources.ChatWithRawResponse(client.chat) - self.embeddings = resources.EmbeddingsWithRawResponse(client.embeddings) - self.files = resources.FilesWithRawResponse(client.files) - self.images = resources.ImagesWithRawResponse(client.images) - self.audio = resources.AudioWithRawResponse(client.audio) - self.moderations = resources.ModerationsWithRawResponse(client.moderations) - self.models = resources.ModelsWithRawResponse(client.models) - self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning) - self.beta = resources.BetaWithRawResponse(client.beta) - self.batches = resources.BatchesWithRawResponse(client.batches) - self.uploads = resources.UploadsWithRawResponse(client.uploads) + self.completions = completions.CompletionsWithRawResponse(client.completions) + self.chat = chat.ChatWithRawResponse(client.chat) + self.embeddings = embeddings.EmbeddingsWithRawResponse(client.embeddings) + self.files = files.FilesWithRawResponse(client.files) + self.images = images.ImagesWithRawResponse(client.images) + self.audio = audio.AudioWithRawResponse(client.audio) + self.moderations = moderations.ModerationsWithRawResponse(client.moderations) + self.models = models.ModelsWithRawResponse(client.models) + self.fine_tuning = fine_tuning.FineTuningWithRawResponse(client.fine_tuning) + self.beta = beta.BetaWithRawResponse(client.beta) + self.batches = batches.BatchesWithRawResponse(client.batches) + self.uploads = uploads.UploadsWithRawResponse(client.uploads) class AsyncOpenAIWithRawResponse: def __init__(self, client: AsyncOpenAI) -> None: - self.completions = resources.AsyncCompletionsWithRawResponse(client.completions) - self.chat = resources.AsyncChatWithRawResponse(client.chat) - self.embeddings = resources.AsyncEmbeddingsWithRawResponse(client.embeddings) - self.files = resources.AsyncFilesWithRawResponse(client.files) - self.images = resources.AsyncImagesWithRawResponse(client.images) - self.audio = resources.AsyncAudioWithRawResponse(client.audio) - self.moderations = resources.AsyncModerationsWithRawResponse(client.moderations) - self.models = resources.AsyncModelsWithRawResponse(client.models) - self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning) - self.beta = resources.AsyncBetaWithRawResponse(client.beta) - self.batches = resources.AsyncBatchesWithRawResponse(client.batches) - self.uploads = resources.AsyncUploadsWithRawResponse(client.uploads) + self.completions = completions.AsyncCompletionsWithRawResponse(client.completions) + self.chat = chat.AsyncChatWithRawResponse(client.chat) + self.embeddings = embeddings.AsyncEmbeddingsWithRawResponse(client.embeddings) + self.files = files.AsyncFilesWithRawResponse(client.files) + self.images = images.AsyncImagesWithRawResponse(client.images) + self.audio = audio.AsyncAudioWithRawResponse(client.audio) + self.moderations = moderations.AsyncModerationsWithRawResponse(client.moderations) + self.models = models.AsyncModelsWithRawResponse(client.models) + self.fine_tuning = fine_tuning.AsyncFineTuningWithRawResponse(client.fine_tuning) + self.beta = beta.AsyncBetaWithRawResponse(client.beta) + self.batches = batches.AsyncBatchesWithRawResponse(client.batches) + self.uploads = uploads.AsyncUploadsWithRawResponse(client.uploads) class OpenAIWithStreamedResponse: def __init__(self, client: OpenAI) -> None: - self.completions = resources.CompletionsWithStreamingResponse(client.completions) - self.chat = resources.ChatWithStreamingResponse(client.chat) - self.embeddings = resources.EmbeddingsWithStreamingResponse(client.embeddings) - self.files = resources.FilesWithStreamingResponse(client.files) - self.images = resources.ImagesWithStreamingResponse(client.images) - self.audio = resources.AudioWithStreamingResponse(client.audio) - self.moderations = resources.ModerationsWithStreamingResponse(client.moderations) - self.models = resources.ModelsWithStreamingResponse(client.models) - self.fine_tuning = resources.FineTuningWithStreamingResponse(client.fine_tuning) - self.beta = resources.BetaWithStreamingResponse(client.beta) - self.batches = resources.BatchesWithStreamingResponse(client.batches) - self.uploads = resources.UploadsWithStreamingResponse(client.uploads) + self.completions = completions.CompletionsWithStreamingResponse(client.completions) + self.chat = chat.ChatWithStreamingResponse(client.chat) + self.embeddings = embeddings.EmbeddingsWithStreamingResponse(client.embeddings) + self.files = files.FilesWithStreamingResponse(client.files) + self.images = images.ImagesWithStreamingResponse(client.images) + self.audio = audio.AudioWithStreamingResponse(client.audio) + self.moderations = moderations.ModerationsWithStreamingResponse(client.moderations) + self.models = models.ModelsWithStreamingResponse(client.models) + self.fine_tuning = fine_tuning.FineTuningWithStreamingResponse(client.fine_tuning) + self.beta = beta.BetaWithStreamingResponse(client.beta) + self.batches = batches.BatchesWithStreamingResponse(client.batches) + self.uploads = uploads.UploadsWithStreamingResponse(client.uploads) class AsyncOpenAIWithStreamedResponse: def __init__(self, client: AsyncOpenAI) -> None: - self.completions = resources.AsyncCompletionsWithStreamingResponse(client.completions) - self.chat = resources.AsyncChatWithStreamingResponse(client.chat) - self.embeddings = resources.AsyncEmbeddingsWithStreamingResponse(client.embeddings) - self.files = resources.AsyncFilesWithStreamingResponse(client.files) - self.images = resources.AsyncImagesWithStreamingResponse(client.images) - self.audio = resources.AsyncAudioWithStreamingResponse(client.audio) - self.moderations = resources.AsyncModerationsWithStreamingResponse(client.moderations) - self.models = resources.AsyncModelsWithStreamingResponse(client.models) - self.fine_tuning = resources.AsyncFineTuningWithStreamingResponse(client.fine_tuning) - self.beta = resources.AsyncBetaWithStreamingResponse(client.beta) - self.batches = resources.AsyncBatchesWithStreamingResponse(client.batches) - self.uploads = resources.AsyncUploadsWithStreamingResponse(client.uploads) + self.completions = completions.AsyncCompletionsWithStreamingResponse(client.completions) + self.chat = chat.AsyncChatWithStreamingResponse(client.chat) + self.embeddings = embeddings.AsyncEmbeddingsWithStreamingResponse(client.embeddings) + self.files = files.AsyncFilesWithStreamingResponse(client.files) + self.images = images.AsyncImagesWithStreamingResponse(client.images) + self.audio = audio.AsyncAudioWithStreamingResponse(client.audio) + self.moderations = moderations.AsyncModerationsWithStreamingResponse(client.moderations) + self.models = models.AsyncModelsWithStreamingResponse(client.models) + self.fine_tuning = fine_tuning.AsyncFineTuningWithStreamingResponse(client.fine_tuning) + self.beta = beta.AsyncBetaWithStreamingResponse(client.beta) + self.batches = batches.AsyncBatchesWithStreamingResponse(client.batches) + self.uploads = uploads.AsyncUploadsWithStreamingResponse(client.uploads) Client = OpenAI From e94d98e9bf97a5d2d02d79d58f2abdbab26ff2bd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:02:30 +0000 Subject: [PATCH 705/914] release: 1.57.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 58e64c502c..f9ae229e1a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.57.3" + ".": "1.57.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 80ed457618..02b7d0271d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.57.4 (2024-12-13) + +Full Changelog: [v1.57.3...v1.57.4](https://github.com/openai/openai-python/compare/v1.57.3...v1.57.4) + +### Chores + +* **internal:** remove some duplicated imports ([#1946](https://github.com/openai/openai-python/issues/1946)) ([f94fddd](https://github.com/openai/openai-python/commit/f94fddd377015764b3c82919fdf956f619447b77)) +* **internal:** updated imports ([#1948](https://github.com/openai/openai-python/issues/1948)) ([13971fc](https://github.com/openai/openai-python/commit/13971fc450106746c0ae02ab931e68b770ee105e)) + ## 1.57.3 (2024-12-12) Full Changelog: [v1.57.2...v1.57.3](https://github.com/openai/openai-python/compare/v1.57.2...v1.57.3) diff --git a/pyproject.toml b/pyproject.toml index a6a0868405..e03d4e798f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.57.3" +version = "1.57.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 94ba432279..5b82015017 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.57.3" # x-release-please-version +__version__ = "1.57.4" # x-release-please-version From af791d5188cc142c6ec82fc0f0be90fa3036a85f Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 16 Dec 2024 13:12:53 +0100 Subject: [PATCH 706/914] fix(cli/migrate): change grit binaries prefix (#1951) --- src/openai/cli/_tools/migrate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openai/cli/_tools/migrate.py b/src/openai/cli/_tools/migrate.py index 7a0b0f90f6..841b777528 100644 --- a/src/openai/cli/_tools/migrate.py +++ b/src/openai/cli/_tools/migrate.py @@ -92,8 +92,8 @@ def install() -> Path: install_dir = dir_name / ".install" target_dir = install_dir / "bin" - target_path = target_dir / "marzano" - temp_file = target_dir / "marzano.tmp" + target_path = target_dir / "grit" + temp_file = target_dir / "grit.tmp" if target_path.exists(): _debug(f"{target_path} already exists") @@ -110,7 +110,7 @@ def install() -> Path: arch = _get_arch() _debug(f"Using architecture {arch}") - file_name = f"marzano-{arch}-{platform}" + file_name = f"grit-{arch}-{platform}" download_url = f"https://github.com/getgrit/gritql/releases/latest/download/%7Bfile_name%7D.tar.gz" sys.stdout.write(f"Downloading Grit CLI from {download_url}\n") From 0bfd8c4d2c7377cd14d7be84ee4f4c1d1ed8a40c Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 16 Dec 2024 12:22:00 +0000 Subject: [PATCH 707/914] fix(assistants): correctly send `include` query param --- src/openai/resources/beta/threads/runs/runs.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 620cc270e5..0418d570ba 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -1106,7 +1106,6 @@ def stream( body=maybe_transform( { "assistant_id": assistant_id, - "include": include, "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, @@ -1126,7 +1125,11 @@ def stream( run_create_params.RunCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, run_create_params.RunCreateParams), ), cast_to=Run, stream=True, @@ -1899,7 +1902,6 @@ async def create( body=await async_maybe_transform( { "assistant_id": assistant_id, - "include": include, "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, @@ -2472,7 +2474,6 @@ def stream( body=maybe_transform( { "assistant_id": assistant_id, - "include": include, "additional_instructions": additional_instructions, "additional_messages": additional_messages, "instructions": instructions, @@ -2492,7 +2493,11 @@ def stream( run_create_params.RunCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, run_create_params.RunCreateParams), ), cast_to=Run, stream=True, From 588935e273fc934efddad15c702b3d11987bb44e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:04:05 +0000 Subject: [PATCH 708/914] docs(readme): example snippet for client context manager (#1953) --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 780ee261fe..cbcfdb4447 100644 --- a/README.md +++ b/README.md @@ -652,6 +652,16 @@ client.with_options(http_client=DefaultHttpxClient(...)) By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. +```py +from openai import OpenAI + +with OpenAI() as client: + # make requests here + ... + +# HTTP client is now closed +``` + ## Microsoft Azure OpenAI To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI` From eba67815fd3ae4ab068d244464dcbb389efa9f0b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:26:44 +0000 Subject: [PATCH 709/914] chore(internal): fix some typos (#1955) --- tests/test_client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 7caa8cb319..7751e7d463 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -349,11 +349,11 @@ def test_default_query_option(self) -> None: FinalRequestOptions( method="get", url="/foo", - params={"foo": "baz", "query_param": "overriden"}, + params={"foo": "baz", "query_param": "overridden"}, ) ) url = httpx.URL(request.url) - assert dict(url.params) == {"foo": "baz", "query_param": "overriden"} + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} def test_request_extra_json(self) -> None: request = self.client._build_request( @@ -1201,11 +1201,11 @@ def test_default_query_option(self) -> None: FinalRequestOptions( method="get", url="/foo", - params={"foo": "baz", "query_param": "overriden"}, + params={"foo": "baz", "query_param": "overridden"}, ) ) url = httpx.URL(request.url) - assert dict(url.params) == {"foo": "baz", "query_param": "overriden"} + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} def test_request_extra_json(self) -> None: request = self.client._build_request( From 575ff6078fcf84fba1c4478073969cbfd00ae4b4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:59:13 +0000 Subject: [PATCH 710/914] feat(api): new o1 and GPT-4o models + preference fine-tuning (#1956) learn more here: https://platform.openai.com/docs/changelog --- .stats.yml | 4 +- api.md | 16 + src/openai/resources/beta/beta.py | 32 ++ .../resources/beta/realtime/__init__.py | 33 ++ .../resources/beta/realtime/realtime.py | 102 ++++++ .../resources/beta/realtime/sessions.py | 337 ++++++++++++++++++ src/openai/resources/chat/completions.py | 240 ++++++++----- src/openai/resources/fine_tuning/jobs/jobs.py | 22 +- src/openai/types/beta/realtime/__init__.py | 6 + .../beta/realtime/session_create_params.py | 149 ++++++++ .../beta/realtime/session_create_response.py | 150 ++++++++ src/openai/types/chat/__init__.py | 4 + ...chat_completion_developer_message_param.py | 25 ++ .../chat/chat_completion_message_param.py | 2 + .../chat/chat_completion_reasoning_effort.py | 7 + .../types/chat/completion_create_params.py | 34 +- src/openai/types/chat_model.py | 7 +- .../types/fine_tuning/fine_tuning_job.py | 106 +++++- .../fine_tuning/fine_tuning_job_event.py | 13 + .../types/fine_tuning/job_create_params.py | 94 ++++- tests/api_resources/beta/realtime/__init__.py | 1 + .../beta/realtime/test_sessions.py | 146 ++++++++ tests/api_resources/chat/test_completions.py | 44 +-- tests/api_resources/fine_tuning/test_jobs.py | 36 ++ tests/test_client.py | 16 +- 25 files changed, 1475 insertions(+), 151 deletions(-) create mode 100644 src/openai/resources/beta/realtime/__init__.py create mode 100644 src/openai/resources/beta/realtime/realtime.py create mode 100644 src/openai/resources/beta/realtime/sessions.py create mode 100644 src/openai/types/beta/realtime/__init__.py create mode 100644 src/openai/types/beta/realtime/session_create_params.py create mode 100644 src/openai/types/beta/realtime/session_create_response.py create mode 100644 src/openai/types/chat/chat_completion_developer_message_param.py create mode 100644 src/openai/types/chat/chat_completion_reasoning_effort.py create mode 100644 tests/api_resources/beta/realtime/__init__.py create mode 100644 tests/api_resources/beta/realtime/test_sessions.py diff --git a/.stats.yml b/.stats.yml index 3cc042fe0a..e3a0040a5a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-2e0e0678be19d1118fd796af291822075e40538dba326611e177e9f3dc245a53.yml +configured_endpoints: 69 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-779ea2754025daf5e18eb8ceb203ec321692636bc3a999338556a479178efa6c.yml diff --git a/api.md b/api.md index 7def07bb79..91b2a9c2fd 100644 --- a/api.md +++ b/api.md @@ -47,6 +47,7 @@ from openai.types.chat import ( ChatCompletionContentPartInputAudio, ChatCompletionContentPartRefusal, ChatCompletionContentPartText, + ChatCompletionDeveloperMessageParam, ChatCompletionFunctionCallOption, ChatCompletionFunctionMessageParam, ChatCompletionMessage, @@ -55,6 +56,7 @@ from openai.types.chat import ( ChatCompletionModality, ChatCompletionNamedToolChoice, ChatCompletionPredictionContent, + ChatCompletionReasoningEffort, ChatCompletionRole, ChatCompletionStreamOptions, ChatCompletionSystemMessageParam, @@ -235,6 +237,20 @@ Methods: # Beta +## Realtime + +### Sessions + +Types: + +```python +from openai.types.beta.realtime import Session, SessionCreateResponse +``` + +Methods: + +- client.beta.realtime.sessions.create(\*\*params) -> SessionCreateResponse + ## VectorStores Types: diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 5079c989a5..1ffa6c8e79 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -21,6 +21,14 @@ ThreadsWithStreamingResponse, AsyncThreadsWithStreamingResponse, ) +from .realtime.realtime import ( + Realtime, + AsyncRealtime, + RealtimeWithRawResponse, + AsyncRealtimeWithRawResponse, + RealtimeWithStreamingResponse, + AsyncRealtimeWithStreamingResponse, +) from .vector_stores.vector_stores import ( VectorStores, AsyncVectorStores, @@ -38,6 +46,10 @@ class Beta(SyncAPIResource): def chat(self) -> Chat: return Chat(self._client) + @cached_property + def realtime(self) -> Realtime: + return Realtime(self._client) + @cached_property def vector_stores(self) -> VectorStores: return VectorStores(self._client) @@ -75,6 +87,10 @@ class AsyncBeta(AsyncAPIResource): def chat(self) -> AsyncChat: return AsyncChat(self._client) + @cached_property + def realtime(self) -> AsyncRealtime: + return AsyncRealtime(self._client) + @cached_property def vector_stores(self) -> AsyncVectorStores: return AsyncVectorStores(self._client) @@ -111,6 +127,10 @@ class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: self._beta = beta + @cached_property + def realtime(self) -> RealtimeWithRawResponse: + return RealtimeWithRawResponse(self._beta.realtime) + @cached_property def vector_stores(self) -> VectorStoresWithRawResponse: return VectorStoresWithRawResponse(self._beta.vector_stores) @@ -128,6 +148,10 @@ class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta + @cached_property + def realtime(self) -> AsyncRealtimeWithRawResponse: + return AsyncRealtimeWithRawResponse(self._beta.realtime) + @cached_property def vector_stores(self) -> AsyncVectorStoresWithRawResponse: return AsyncVectorStoresWithRawResponse(self._beta.vector_stores) @@ -145,6 +169,10 @@ class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: self._beta = beta + @cached_property + def realtime(self) -> RealtimeWithStreamingResponse: + return RealtimeWithStreamingResponse(self._beta.realtime) + @cached_property def vector_stores(self) -> VectorStoresWithStreamingResponse: return VectorStoresWithStreamingResponse(self._beta.vector_stores) @@ -162,6 +190,10 @@ class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta + @cached_property + def realtime(self) -> AsyncRealtimeWithStreamingResponse: + return AsyncRealtimeWithStreamingResponse(self._beta.realtime) + @cached_property def vector_stores(self) -> AsyncVectorStoresWithStreamingResponse: return AsyncVectorStoresWithStreamingResponse(self._beta.vector_stores) diff --git a/src/openai/resources/beta/realtime/__init__.py b/src/openai/resources/beta/realtime/__init__.py new file mode 100644 index 0000000000..474434e6e1 --- /dev/null +++ b/src/openai/resources/beta/realtime/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .realtime import ( + Realtime, + AsyncRealtime, + RealtimeWithRawResponse, + AsyncRealtimeWithRawResponse, + RealtimeWithStreamingResponse, + AsyncRealtimeWithStreamingResponse, +) +from .sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) + +__all__ = [ + "Sessions", + "AsyncSessions", + "SessionsWithRawResponse", + "AsyncSessionsWithRawResponse", + "SessionsWithStreamingResponse", + "AsyncSessionsWithStreamingResponse", + "Realtime", + "AsyncRealtime", + "RealtimeWithRawResponse", + "AsyncRealtimeWithRawResponse", + "RealtimeWithStreamingResponse", + "AsyncRealtimeWithStreamingResponse", +] diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py new file mode 100644 index 0000000000..e57e0be503 --- /dev/null +++ b/src/openai/resources/beta/realtime/realtime.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["Realtime", "AsyncRealtime"] + + +class Realtime(SyncAPIResource): + @cached_property + def sessions(self) -> Sessions: + return Sessions(self._client) + + @cached_property + def with_raw_response(self) -> RealtimeWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RealtimeWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RealtimeWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RealtimeWithStreamingResponse(self) + + +class AsyncRealtime(AsyncAPIResource): + @cached_property + def sessions(self) -> AsyncSessions: + return AsyncSessions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRealtimeWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRealtimeWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRealtimeWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRealtimeWithStreamingResponse(self) + + +class RealtimeWithRawResponse: + def __init__(self, realtime: Realtime) -> None: + self._realtime = realtime + + @cached_property + def sessions(self) -> SessionsWithRawResponse: + return SessionsWithRawResponse(self._realtime.sessions) + + +class AsyncRealtimeWithRawResponse: + def __init__(self, realtime: AsyncRealtime) -> None: + self._realtime = realtime + + @cached_property + def sessions(self) -> AsyncSessionsWithRawResponse: + return AsyncSessionsWithRawResponse(self._realtime.sessions) + + +class RealtimeWithStreamingResponse: + def __init__(self, realtime: Realtime) -> None: + self._realtime = realtime + + @cached_property + def sessions(self) -> SessionsWithStreamingResponse: + return SessionsWithStreamingResponse(self._realtime.sessions) + + +class AsyncRealtimeWithStreamingResponse: + def __init__(self, realtime: AsyncRealtime) -> None: + self._realtime = realtime + + @cached_property + def sessions(self) -> AsyncSessionsWithStreamingResponse: + return AsyncSessionsWithStreamingResponse(self._realtime.sessions) diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py new file mode 100644 index 0000000000..1d1ee701e5 --- /dev/null +++ b/src/openai/resources/beta/realtime/sessions.py @@ -0,0 +1,337 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.beta.realtime import session_create_params +from ....types.beta.realtime.session_create_response import SessionCreateResponse + +__all__ = ["Sessions", "AsyncSessions"] + + +class Sessions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SessionsWithStreamingResponse(self) + + def create( + self, + *, + model: Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + instructions: str | NotGiven = NOT_GIVEN, + max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + tool_choice: str | NotGiven = NOT_GIVEN, + tools: Iterable[session_create_params.Tool] | NotGiven = NOT_GIVEN, + turn_detection: session_create_params.TurnDetection | NotGiven = NOT_GIVEN, + voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SessionCreateResponse: + """ + Create an ephemeral API token for use in client-side applications with the + Realtime API. Can be configured with the same session parameters as the + `session.update` client event. + + It responds with a session object, plus a `client_secret` key which contains a + usable ephemeral API token that can be used to authenticate browser clients for + the Realtime API. + + Args: + model: The Realtime model used for this session. + + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + + input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through Whisper and should be treated as rough guidance rather + than the representation understood by the model. + + instructions: The default system instructions (i.e. system message) prepended to model calls. + This field allows the client to guide the model on desired responses. The model + can be instructed on response content and format, (e.g. "be extremely succinct", + "act friendly", "here are examples of good responses") and on audio behavior + (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The + instructions are not guaranteed to be followed by the model, but they provide + guidance to the model on the desired behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + + max_response_output_tokens: Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + + modalities: The set of modalities the model can respond with. To disable audio, set this to + ["text"]. + + output_audio_format: The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + + temperature: Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8. + + tool_choice: How the model chooses tools. Options are `auto`, `none`, `required`, or specify + a function. + + tools: Tools (functions) available to the model. + + turn_detection: Configuration for turn detection. Can be set to `null` to turn off. Server VAD + means that the model will detect the start and end of speech based on audio + volume and respond at the end of user speech. + + voice: The voice the model uses to respond. Voice cannot be changed during the session + once the model has responded with audio at least once. Current voice options are + `alloy`, `ash`, `ballad`, `coral`, `echo` `sage`, `shimmer` and `verse`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/realtime/sessions", + body=maybe_transform( + { + "model": model, + "input_audio_format": input_audio_format, + "input_audio_transcription": input_audio_transcription, + "instructions": instructions, + "max_response_output_tokens": max_response_output_tokens, + "modalities": modalities, + "output_audio_format": output_audio_format, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "turn_detection": turn_detection, + "voice": voice, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SessionCreateResponse, + ) + + +class AsyncSessions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSessionsWithStreamingResponse(self) + + async def create( + self, + *, + model: Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + instructions: str | NotGiven = NOT_GIVEN, + max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + tool_choice: str | NotGiven = NOT_GIVEN, + tools: Iterable[session_create_params.Tool] | NotGiven = NOT_GIVEN, + turn_detection: session_create_params.TurnDetection | NotGiven = NOT_GIVEN, + voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SessionCreateResponse: + """ + Create an ephemeral API token for use in client-side applications with the + Realtime API. Can be configured with the same session parameters as the + `session.update` client event. + + It responds with a session object, plus a `client_secret` key which contains a + usable ephemeral API token that can be used to authenticate browser clients for + the Realtime API. + + Args: + model: The Realtime model used for this session. + + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + + input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through Whisper and should be treated as rough guidance rather + than the representation understood by the model. + + instructions: The default system instructions (i.e. system message) prepended to model calls. + This field allows the client to guide the model on desired responses. The model + can be instructed on response content and format, (e.g. "be extremely succinct", + "act friendly", "here are examples of good responses") and on audio behavior + (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The + instructions are not guaranteed to be followed by the model, but they provide + guidance to the model on the desired behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + + max_response_output_tokens: Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + + modalities: The set of modalities the model can respond with. To disable audio, set this to + ["text"]. + + output_audio_format: The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + + temperature: Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8. + + tool_choice: How the model chooses tools. Options are `auto`, `none`, `required`, or specify + a function. + + tools: Tools (functions) available to the model. + + turn_detection: Configuration for turn detection. Can be set to `null` to turn off. Server VAD + means that the model will detect the start and end of speech based on audio + volume and respond at the end of user speech. + + voice: The voice the model uses to respond. Voice cannot be changed during the session + once the model has responded with audio at least once. Current voice options are + `alloy`, `ash`, `ballad`, `coral`, `echo` `sage`, `shimmer` and `verse`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/realtime/sessions", + body=await async_maybe_transform( + { + "model": model, + "input_audio_format": input_audio_format, + "input_audio_transcription": input_audio_transcription, + "instructions": instructions, + "max_response_output_tokens": max_response_output_tokens, + "modalities": modalities, + "output_audio_format": output_audio_format, + "temperature": temperature, + "tool_choice": tool_choice, + "tools": tools, + "turn_detection": turn_detection, + "voice": voice, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SessionCreateResponse, + ) + + +class SessionsWithRawResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.to_raw_response_wrapper( + sessions.create, + ) + + +class AsyncSessionsWithRawResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.async_to_raw_response_wrapper( + sessions.create, + ) + + +class SessionsWithStreamingResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = to_streamed_response_wrapper( + sessions.create, + ) + + +class AsyncSessionsWithStreamingResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = async_to_streamed_response_wrapper( + sessions.create, + ) diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 60ab5138ba..728c744327 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -22,6 +22,7 @@ from ..._streaming import Stream, AsyncStream from ...types.chat import ( ChatCompletionAudioParam, + ChatCompletionReasoningEffort, completion_create_params, ) from ..._base_client import make_request_options @@ -32,6 +33,7 @@ from ...types.chat.chat_completion_tool_param import ChatCompletionToolParam from ...types.chat.chat_completion_audio_param import ChatCompletionAudioParam from ...types.chat.chat_completion_message_param import ChatCompletionMessageParam +from ...types.chat.chat_completion_reasoning_effort import ChatCompletionReasoningEffort from ...types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from ...types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam from ...types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam @@ -79,6 +81,7 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -106,6 +109,12 @@ def create( [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -126,16 +135,18 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) - function_call: Deprecated in favor of `tool_choice`. - Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via `{"name": "my_function"}` forces the model to call that + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a function. + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + `none` is the default when no functions are present. `auto` is the default if functions are present. @@ -197,13 +208,14 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) + reasoning_effort: **o1 models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. - response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more @@ -259,9 +271,8 @@ def create( temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more - focused and deterministic. - - We generally recommend altering this or `top_p` but not both. + focused and deterministic. We generally recommend altering this or `top_p` but + not both. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tool and instead generates a message. `auto` means the model can @@ -322,6 +333,7 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -348,6 +360,12 @@ def create( [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -375,16 +393,18 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) - function_call: Deprecated in favor of `tool_choice`. - Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via `{"name": "my_function"}` forces the model to call that + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a function. + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + `none` is the default when no functions are present. `auto` is the default if functions are present. @@ -446,13 +466,14 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) + reasoning_effort: **o1 models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. - response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more @@ -501,9 +522,8 @@ def create( temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more - focused and deterministic. - - We generally recommend altering this or `top_p` but not both. + focused and deterministic. We generally recommend altering this or `top_p` but + not both. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tool and instead generates a message. `auto` means the model can @@ -564,6 +584,7 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -590,6 +611,12 @@ def create( [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -617,16 +644,18 @@ def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) - function_call: Deprecated in favor of `tool_choice`. - Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via `{"name": "my_function"}` forces the model to call that + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a function. + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + `none` is the default when no functions are present. `auto` is the default if functions are present. @@ -688,13 +717,14 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) + reasoning_effort: **o1 models only** - response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more @@ -743,9 +773,8 @@ def create( temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more - focused and deterministic. - - We generally recommend altering this or `top_p` but not both. + focused and deterministic. We generally recommend altering this or `top_p` but + not both. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tool and instead generates a message. `auto` means the model can @@ -805,6 +834,7 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -846,6 +876,7 @@ def create( "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, "presence_penalty": presence_penalty, + "reasoning_effort": reasoning_effort, "response_format": response_format, "seed": seed, "service_tier": service_tier, @@ -911,6 +942,7 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -938,6 +970,12 @@ async def create( [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -958,16 +996,18 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) - function_call: Deprecated in favor of `tool_choice`. - Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via `{"name": "my_function"}` forces the model to call that + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a function. + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + `none` is the default when no functions are present. `auto` is the default if functions are present. @@ -1029,13 +1069,14 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) + reasoning_effort: **o1 models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. - response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more @@ -1091,9 +1132,8 @@ async def create( temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more - focused and deterministic. - - We generally recommend altering this or `top_p` but not both. + focused and deterministic. We generally recommend altering this or `top_p` but + not both. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tool and instead generates a message. `auto` means the model can @@ -1154,6 +1194,7 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -1180,6 +1221,12 @@ async def create( [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -1207,16 +1254,18 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) - function_call: Deprecated in favor of `tool_choice`. - Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via `{"name": "my_function"}` forces the model to call that + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a function. + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + `none` is the default when no functions are present. `auto` is the default if functions are present. @@ -1278,13 +1327,14 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) + reasoning_effort: **o1 models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. - response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more @@ -1333,9 +1383,8 @@ async def create( temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more - focused and deterministic. - - We generally recommend altering this or `top_p` but not both. + focused and deterministic. We generally recommend altering this or `top_p` but + not both. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tool and instead generates a message. `auto` means the model can @@ -1396,6 +1445,7 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -1422,6 +1472,12 @@ async def create( [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. + Parameter support can differ depending on the model used to generate the + response, particularly for newer reasoning models. Parameters that are only + supported for reasoning models are noted below. For the current state of + unsupported parameters in reasoning models, + [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -1449,16 +1505,18 @@ async def create( existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) - function_call: Deprecated in favor of `tool_choice`. - Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via `{"name": "my_function"}` forces the model to call that + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a function. + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + `none` is the default when no functions are present. `auto` is the default if functions are present. @@ -1520,13 +1578,14 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) + reasoning_effort: **o1 models only** - response_format: An object specifying the format that the model must output. Compatible with - [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more @@ -1575,9 +1634,8 @@ async def create( temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more - focused and deterministic. - - We generally recommend altering this or `top_p` but not both. + focused and deterministic. We generally recommend altering this or `top_p` but + not both. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tool and instead generates a message. `auto` means the model can @@ -1637,6 +1695,7 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -1678,6 +1737,7 @@ async def create( "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, "presence_penalty": presence_penalty, + "reasoning_effort": reasoning_effort, "response_format": response_format, "seed": seed, "service_tier": service_tier, diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index 0ed5495b0e..78eefc253c 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -67,6 +67,7 @@ def create( training_file: str, hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, + method: job_create_params.Method | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, validation_file: Optional[str] | NotGiven = NOT_GIVEN, @@ -99,17 +100,22 @@ def create( your file with the purpose `fine-tune`. The contents of the file should differ depending on if the model uses the - [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input), [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + format, or if the fine-tuning method uses the + [preference](https://platform.openai.com/docs/api-reference/fine-tuning/preference-input) format. See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) for more details. - hyperparameters: The hyperparameters used for the fine-tuning job. + hyperparameters: The hyperparameters used for the fine-tuning job. This value is now deprecated + in favor of `method`, and should be passed in under the `method` parameter. integrations: A list of integrations to enable for your fine-tuning job. + method: The method used for fine-tuning. + seed: The seed controls the reproducibility of the job. Passing in the same seed and job parameters should produce the same results, but may differ in rare cases. If a seed is not specified, one will be generated for you. @@ -149,6 +155,7 @@ def create( "training_file": training_file, "hyperparameters": hyperparameters, "integrations": integrations, + "method": method, "seed": seed, "suffix": suffix, "validation_file": validation_file, @@ -358,6 +365,7 @@ async def create( training_file: str, hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, + method: job_create_params.Method | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, validation_file: Optional[str] | NotGiven = NOT_GIVEN, @@ -390,17 +398,22 @@ async def create( your file with the purpose `fine-tune`. The contents of the file should differ depending on if the model uses the - [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input), [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + format, or if the fine-tuning method uses the + [preference](https://platform.openai.com/docs/api-reference/fine-tuning/preference-input) format. See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) for more details. - hyperparameters: The hyperparameters used for the fine-tuning job. + hyperparameters: The hyperparameters used for the fine-tuning job. This value is now deprecated + in favor of `method`, and should be passed in under the `method` parameter. integrations: A list of integrations to enable for your fine-tuning job. + method: The method used for fine-tuning. + seed: The seed controls the reproducibility of the job. Passing in the same seed and job parameters should produce the same results, but may differ in rare cases. If a seed is not specified, one will be generated for you. @@ -440,6 +453,7 @@ async def create( "training_file": training_file, "hyperparameters": hyperparameters, "integrations": integrations, + "method": method, "seed": seed, "suffix": suffix, "validation_file": validation_file, diff --git a/src/openai/types/beta/realtime/__init__.py b/src/openai/types/beta/realtime/__init__.py new file mode 100644 index 0000000000..1c5246db7a --- /dev/null +++ b/src/openai/types/beta/realtime/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .session_create_params import SessionCreateParams as SessionCreateParams +from .session_create_response import SessionCreateResponse as SessionCreateResponse diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py new file mode 100644 index 0000000000..f56f2c5c22 --- /dev/null +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -0,0 +1,149 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SessionCreateParams", "InputAudioTranscription", "Tool", "TurnDetection"] + + +class SessionCreateParams(TypedDict, total=False): + model: Required[ + Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + ] + """The Realtime model used for this session.""" + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: InputAudioTranscription + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through Whisper and should be treated as rough guidance rather + than the representation understood by the model. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: float + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: str + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Iterable[Tool] + """Tools (functions) available to the model.""" + + turn_detection: TurnDetection + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ + + voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo` `sage`, `shimmer` and `verse`. + """ + + +class InputAudioTranscription(TypedDict, total=False): + model: str + """ + The model to use for transcription, `whisper-1` is the only currently supported + model. + """ + + +class Tool(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class TurnDetection(TypedDict, total=False): + create_response: bool + """Whether or not to automatically generate a response when VAD is enabled. + + `true` by default. + """ + + prefix_padding_ms: int + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: int + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: float + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: str + """Type of turn detection, only `server_vad` is currently supported.""" diff --git a/src/openai/types/beta/realtime/session_create_response.py b/src/openai/types/beta/realtime/session_create_response.py new file mode 100644 index 0000000000..31f591b261 --- /dev/null +++ b/src/openai/types/beta/realtime/session_create_response.py @@ -0,0 +1,150 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["SessionCreateResponse", "ClientSecret", "InputAudioTranscription", "Tool", "TurnDetection"] + + +class ClientSecret(BaseModel): + expires_at: Optional[int] = None + """Timestamp for when the token expires. + + Currently, all tokens expire after one minute. + """ + + value: Optional[str] = None + """ + Ephemeral key usable in client environments to authenticate connections to the + Realtime API. Use this in client-side environments rather than a standard API + token, which should only be used server-side. + """ + + +class InputAudioTranscription(BaseModel): + model: Optional[str] = None + """ + The model to use for transcription, `whisper-1` is the only currently supported + model. + """ + + +class Tool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class TurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class SessionCreateResponse(BaseModel): + client_secret: Optional[ClientSecret] = None + """Ephemeral key returned by the API.""" + + input_audio_format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: Optional[InputAudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through Whisper and should be treated as rough guidance rather + than the representation understood by the model. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Optional[str] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[Tool]] = None + """Tools (functions) available to the model.""" + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ + + voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo` `sage`, `shimmer` and `verse`. + """ diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index d0a5403e79..c623a982af 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -22,6 +22,7 @@ from .chat_completion_audio_param import ChatCompletionAudioParam as ChatCompletionAudioParam from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob +from .chat_completion_reasoning_effort import ChatCompletionReasoningEffort as ChatCompletionReasoningEffort from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam @@ -37,6 +38,9 @@ from .chat_completion_content_part_text_param import ( ChatCompletionContentPartTextParam as ChatCompletionContentPartTextParam, ) +from .chat_completion_developer_message_param import ( + ChatCompletionDeveloperMessageParam as ChatCompletionDeveloperMessageParam, +) from .chat_completion_message_tool_call_param import ( ChatCompletionMessageToolCallParam as ChatCompletionMessageToolCallParam, ) diff --git a/src/openai/types/chat/chat_completion_developer_message_param.py b/src/openai/types/chat/chat_completion_developer_message_param.py new file mode 100644 index 0000000000..01e4fdb654 --- /dev/null +++ b/src/openai/types/chat/chat_completion_developer_message_param.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam + +__all__ = ["ChatCompletionDeveloperMessageParam"] + + +class ChatCompletionDeveloperMessageParam(TypedDict, total=False): + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] + """The contents of the developer message.""" + + role: Required[Literal["developer"]] + """The role of the messages author, in this case `developer`.""" + + name: str + """An optional name for the participant. + + Provides the model information to differentiate between participants of the same + role. + """ diff --git a/src/openai/types/chat/chat_completion_message_param.py b/src/openai/types/chat/chat_completion_message_param.py index ec65d94cae..942da24304 100644 --- a/src/openai/types/chat/chat_completion_message_param.py +++ b/src/openai/types/chat/chat_completion_message_param.py @@ -10,10 +10,12 @@ from .chat_completion_system_message_param import ChatCompletionSystemMessageParam from .chat_completion_function_message_param import ChatCompletionFunctionMessageParam from .chat_completion_assistant_message_param import ChatCompletionAssistantMessageParam +from .chat_completion_developer_message_param import ChatCompletionDeveloperMessageParam __all__ = ["ChatCompletionMessageParam"] ChatCompletionMessageParam: TypeAlias = Union[ + ChatCompletionDeveloperMessageParam, ChatCompletionSystemMessageParam, ChatCompletionUserMessageParam, ChatCompletionAssistantMessageParam, diff --git a/src/openai/types/chat/chat_completion_reasoning_effort.py b/src/openai/types/chat/chat_completion_reasoning_effort.py new file mode 100644 index 0000000000..9e7946974a --- /dev/null +++ b/src/openai/types/chat/chat_completion_reasoning_effort.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatCompletionReasoningEffort"] + +ChatCompletionReasoningEffort: TypeAlias = Literal["low", "medium", "high"] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index e838858314..f168ddea6e 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -10,6 +10,7 @@ from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_audio_param import ChatCompletionAudioParam from .chat_completion_message_param import ChatCompletionMessageParam +from .chat_completion_reasoning_effort import ChatCompletionReasoningEffort from ..shared_params.function_parameters import FunctionParameters from ..shared_params.response_format_text import ResponseFormatText from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam @@ -60,19 +61,21 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. - - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) """ function_call: FunctionCall """Deprecated in favor of `tool_choice`. - Controls which (if any) function is called by the model. `none` means the model - will not call a function and instead generates a message. `auto` means the model - can pick between generating a message or calling a function. Specifying a - particular function via `{"name": "my_function"}` forces the model to call that + Controls which (if any) function is called by the model. + + `none` means the model will not call a function and instead generates a message. + + `auto` means the model can pick between generating a message or calling a function. + Specifying a particular function via `{"name": "my_function"}` forces the model + to call that function. + `none` is the default when no functions are present. `auto` is the default if functions are present. """ @@ -164,18 +167,20 @@ class CompletionCreateParamsBase(TypedDict, total=False): 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. + """ + + reasoning_effort: ChatCompletionReasoningEffort + """**o1 models only** - [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation) + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. """ response_format: ResponseFormat """An object specifying the format that the model must output. - Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), - [GPT-4o mini](https://platform.openai.com/docs/models#gpt-4o-mini), - [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4) and - all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. - Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more in the @@ -237,9 +242,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): """What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like - 0.2 will make it more focused and deterministic. - - We generally recommend altering this or `top_p` but not both. + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. """ tool_choice: ChatCompletionToolChoiceOptionParam diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index 3567a3ba65..e1ac464320 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -5,6 +5,8 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "o1", + "o1-2024-12-17", "o1-preview", "o1-preview-2024-09-12", "o1-mini", @@ -13,10 +15,11 @@ "gpt-4o-2024-11-20", "gpt-4o-2024-08-06", "gpt-4o-2024-05-13", - "gpt-4o-realtime-preview", - "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-audio-preview", "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", "chatgpt-4o-latest", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index 7ac8792787..f5a11c2107 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -6,7 +6,16 @@ from ..._models import BaseModel from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject -__all__ = ["FineTuningJob", "Error", "Hyperparameters"] +__all__ = [ + "FineTuningJob", + "Error", + "Hyperparameters", + "Method", + "MethodDpo", + "MethodDpoHyperparameters", + "MethodSupervised", + "MethodSupervisedHyperparameters", +] class Error(BaseModel): @@ -24,15 +33,96 @@ class Error(BaseModel): class Hyperparameters(BaseModel): - n_epochs: Union[Literal["auto"], int] + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + +class MethodDpoHyperparameters(BaseModel): + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + beta: Union[Literal["auto"], float, None] = None + """The beta value for the DPO method. + + A higher beta value will increase the weight of the penalty between the policy + and reference model. + """ + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None """The number of epochs to train the model for. - An epoch refers to one full cycle through the training dataset. "auto" decides - the optimal number of epochs based on the size of the dataset. If setting the - number manually, we support any number between 1 and 50 epochs. + An epoch refers to one full cycle through the training dataset. """ +class MethodDpo(BaseModel): + hyperparameters: Optional[MethodDpoHyperparameters] = None + """The hyperparameters used for the fine-tuning job.""" + + +class MethodSupervisedHyperparameters(BaseModel): + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + +class MethodSupervised(BaseModel): + hyperparameters: Optional[MethodSupervisedHyperparameters] = None + """The hyperparameters used for the fine-tuning job.""" + + +class Method(BaseModel): + dpo: Optional[MethodDpo] = None + """Configuration for the DPO fine-tuning method.""" + + supervised: Optional[MethodSupervised] = None + """Configuration for the supervised fine-tuning method.""" + + type: Optional[Literal["supervised", "dpo"]] = None + """The type of method. Is either `supervised` or `dpo`.""" + + class FineTuningJob(BaseModel): id: str """The object identifier, which can be referenced in the API endpoints.""" @@ -61,8 +151,7 @@ class FineTuningJob(BaseModel): hyperparameters: Hyperparameters """The hyperparameters used for the fine-tuning job. - See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) - for more details. + This value will only be returned when running `supervised` jobs. """ model: str @@ -118,3 +207,6 @@ class FineTuningJob(BaseModel): integrations: Optional[List[FineTuningJobWandbIntegrationObject]] = None """A list of integrations to enable for this fine-tuning job.""" + + method: Optional[Method] = None + """The method used for fine-tuning.""" diff --git a/src/openai/types/fine_tuning/fine_tuning_job_event.py b/src/openai/types/fine_tuning/fine_tuning_job_event.py index 2d204bb980..1d728bd765 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_event.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_event.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import builtins +from typing import Optional from typing_extensions import Literal from ..._models import BaseModel @@ -9,11 +11,22 @@ class FineTuningJobEvent(BaseModel): id: str + """The object identifier.""" created_at: int + """The Unix timestamp (in seconds) for when the fine-tuning job was created.""" level: Literal["info", "warn", "error"] + """The log level of the event.""" message: str + """The message of the event.""" object: Literal["fine_tuning.job.event"] + """The object type, which is always "fine_tuning.job.event".""" + + data: Optional[builtins.object] = None + """The data associated with the event.""" + + type: Optional[Literal["message", "metrics"]] = None + """The type of event.""" diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 8814229b2e..09c3f8571c 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -5,7 +5,17 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["JobCreateParams", "Hyperparameters", "Integration", "IntegrationWandb"] +__all__ = [ + "JobCreateParams", + "Hyperparameters", + "Integration", + "IntegrationWandb", + "Method", + "MethodDpo", + "MethodDpoHyperparameters", + "MethodSupervised", + "MethodSupervisedHyperparameters", +] class JobCreateParams(TypedDict, total=False): @@ -26,8 +36,10 @@ class JobCreateParams(TypedDict, total=False): your file with the purpose `fine-tune`. The contents of the file should differ depending on if the model uses the - [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input), [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + format, or if the fine-tuning method uses the + [preference](https://platform.openai.com/docs/api-reference/fine-tuning/preference-input) format. See the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) @@ -35,11 +47,17 @@ class JobCreateParams(TypedDict, total=False): """ hyperparameters: Hyperparameters - """The hyperparameters used for the fine-tuning job.""" + """ + The hyperparameters used for the fine-tuning job. This value is now deprecated + in favor of `method`, and should be passed in under the `method` parameter. + """ integrations: Optional[Iterable[Integration]] """A list of integrations to enable for your fine-tuning job.""" + method: Method + """The method used for fine-tuning.""" + seed: Optional[int] """The seed controls the reproducibility of the job. @@ -134,3 +152,73 @@ class Integration(TypedDict, total=False): can set an explicit display name for your run, add tags to your run, and set a default entity (team, username, etc) to be associated with your run. """ + + +class MethodDpoHyperparameters(TypedDict, total=False): + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + beta: Union[Literal["auto"], float] + """The beta value for the DPO method. + + A higher beta value will increase the weight of the penalty between the policy + and reference model. + """ + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + +class MethodDpo(TypedDict, total=False): + hyperparameters: MethodDpoHyperparameters + """The hyperparameters used for the fine-tuning job.""" + + +class MethodSupervisedHyperparameters(TypedDict, total=False): + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + +class MethodSupervised(TypedDict, total=False): + hyperparameters: MethodSupervisedHyperparameters + """The hyperparameters used for the fine-tuning job.""" + + +class Method(TypedDict, total=False): + dpo: MethodDpo + """Configuration for the DPO fine-tuning method.""" + + supervised: MethodSupervised + """Configuration for the supervised fine-tuning method.""" + + type: Literal["supervised", "dpo"] + """The type of method. Is either `supervised` or `dpo`.""" diff --git a/tests/api_resources/beta/realtime/__init__.py b/tests/api_resources/beta/realtime/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/beta/realtime/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/realtime/test_sessions.py b/tests/api_resources/beta/realtime/test_sessions.py new file mode 100644 index 0000000000..65bfa27572 --- /dev/null +++ b/tests/api_resources/beta/realtime/test_sessions.py @@ -0,0 +1,146 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.beta.realtime import SessionCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestSessions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + session = client.beta.realtime.sessions.create( + model="gpt-4o-realtime-preview", + ) + assert_matches_type(SessionCreateResponse, session, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + session = client.beta.realtime.sessions.create( + model="gpt-4o-realtime-preview", + input_audio_format="pcm16", + input_audio_transcription={"model": "model"}, + instructions="instructions", + max_response_output_tokens=0, + modalities=["text"], + output_audio_format="pcm16", + temperature=0, + tool_choice="tool_choice", + tools=[ + { + "description": "description", + "name": "name", + "parameters": {}, + "type": "function", + } + ], + turn_detection={ + "create_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + "type": "type", + }, + voice="alloy", + ) + assert_matches_type(SessionCreateResponse, session, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.realtime.sessions.with_raw_response.create( + model="gpt-4o-realtime-preview", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(SessionCreateResponse, session, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.realtime.sessions.with_streaming_response.create( + model="gpt-4o-realtime-preview", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = response.parse() + assert_matches_type(SessionCreateResponse, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSessions: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + session = await async_client.beta.realtime.sessions.create( + model="gpt-4o-realtime-preview", + ) + assert_matches_type(SessionCreateResponse, session, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + session = await async_client.beta.realtime.sessions.create( + model="gpt-4o-realtime-preview", + input_audio_format="pcm16", + input_audio_transcription={"model": "model"}, + instructions="instructions", + max_response_output_tokens=0, + modalities=["text"], + output_audio_format="pcm16", + temperature=0, + tool_choice="tool_choice", + tools=[ + { + "description": "description", + "name": "name", + "parameters": {}, + "type": "function", + } + ], + turn_detection={ + "create_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + "type": "type", + }, + voice="alloy", + ) + assert_matches_type(SessionCreateResponse, session, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.realtime.sessions.with_raw_response.create( + model="gpt-4o-realtime-preview", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(SessionCreateResponse, session, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.realtime.sessions.with_streaming_response.create( + model="gpt-4o-realtime-preview", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(SessionCreateResponse, session, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 1b52650b1d..393a790549 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -26,7 +26,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -39,8 +39,8 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: messages=[ { "content": "string", - "role": "system", - "name": "string", + "role": "developer", + "name": "name", } ], model="gpt-4o", @@ -70,6 +70,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "type": "content", }, presence_penalty=-2, + reasoning_effort="low", response_format={"type": "text"}, seed=-9007199254740991, service_tier="auto", @@ -102,7 +103,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -119,7 +120,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -138,7 +139,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -152,8 +153,8 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: messages=[ { "content": "string", - "role": "system", - "name": "string", + "role": "developer", + "name": "name", } ], model="gpt-4o", @@ -184,6 +185,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "type": "content", }, presence_penalty=-2, + reasoning_effort="low", response_format={"type": "text"}, seed=-9007199254740991, service_tier="auto", @@ -215,7 +217,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -232,7 +234,7 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -273,7 +275,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -286,8 +288,8 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn messages=[ { "content": "string", - "role": "system", - "name": "string", + "role": "developer", + "name": "name", } ], model="gpt-4o", @@ -317,6 +319,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "type": "content", }, presence_penalty=-2, + reasoning_effort="low", response_format={"type": "text"}, seed=-9007199254740991, service_tier="auto", @@ -349,7 +352,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) - messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -366,7 +369,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -385,7 +388,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -399,8 +402,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn messages=[ { "content": "string", - "role": "system", - "name": "string", + "role": "developer", + "name": "name", } ], model="gpt-4o", @@ -431,6 +434,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "type": "content", }, presence_penalty=-2, + reasoning_effort="low", response_format={"type": "text"}, seed=-9007199254740991, service_tier="auto", @@ -462,7 +466,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) - messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -479,7 +483,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpe messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index aa2bf39528..1e421c30c0 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -50,6 +50,24 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: }, } ], + method={ + "dpo": { + "hyperparameters": { + "batch_size": "auto", + "beta": "auto", + "learning_rate_multiplier": "auto", + "n_epochs": "auto", + } + }, + "supervised": { + "hyperparameters": { + "batch_size": "auto", + "learning_rate_multiplier": "auto", + "n_epochs": "auto", + } + }, + "type": "supervised", + }, seed=42, suffix="x", validation_file="file-abc123", @@ -271,6 +289,24 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> }, } ], + method={ + "dpo": { + "hyperparameters": { + "batch_size": "auto", + "beta": "auto", + "learning_rate_multiplier": "auto", + "n_epochs": "auto", + } + }, + "supervised": { + "hyperparameters": { + "batch_size": "auto", + "learning_rate_multiplier": "auto", + "n_epochs": "auto", + } + }, + "type": "supervised", + }, seed=42, suffix="x", validation_file="file-abc123", diff --git a/tests/test_client.py b/tests/test_client.py index 7751e7d463..e0d23403b1 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -795,7 +795,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -827,7 +827,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -859,7 +859,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -891,7 +891,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -1663,7 +1663,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -1696,7 +1696,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -1729,7 +1729,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", @@ -1762,7 +1762,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: messages=[ { "content": "string", - "role": "system", + "role": "developer", } ], model="gpt-4o", From 5fdba4864b5a9dc00f50a939fcf40b992a550db9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:09:06 +0000 Subject: [PATCH 711/914] feat: add Realtime API support (#1958) More information on the Realtime API can be found here: https://platform.openai.com/docs/guides/realtime --- .stats.yml | 2 +- api.md | 51 ++ pyproject.toml | 7 +- requirements-dev.lock | 2 + requirements.lock | 2 + src/openai/_client.py | 26 + src/openai/lib/azure.py | 14 + .../resources/beta/realtime/realtime.py | 852 ++++++++++++++++++ src/openai/types/__init__.py | 1 + src/openai/types/beta/realtime/__init__.py | 74 ++ .../realtime/conversation_created_event.py | 27 + .../types/beta/realtime/conversation_item.py | 61 ++ .../realtime/conversation_item_content.py | 28 + .../conversation_item_content_param.py | 27 + .../conversation_item_create_event.py | 28 + .../conversation_item_create_event_param.py | 28 + .../conversation_item_created_event.py | 25 + .../conversation_item_delete_event.py | 19 + .../conversation_item_delete_event_param.py | 18 + .../conversation_item_deleted_event.py | 18 + ...put_audio_transcription_completed_event.py | 26 + ..._input_audio_transcription_failed_event.py | 39 + .../beta/realtime/conversation_item_param.py | 62 ++ .../conversation_item_truncate_event.py | 32 + .../conversation_item_truncate_event_param.py | 31 + .../conversation_item_truncated_event.py | 24 + src/openai/types/beta/realtime/error_event.py | 36 + .../input_audio_buffer_append_event.py | 23 + .../input_audio_buffer_append_event_param.py | 22 + .../input_audio_buffer_clear_event.py | 16 + .../input_audio_buffer_clear_event_param.py | 15 + .../input_audio_buffer_cleared_event.py | 15 + .../input_audio_buffer_commit_event.py | 16 + .../input_audio_buffer_commit_event_param.py | 15 + .../input_audio_buffer_committed_event.py | 21 + ...input_audio_buffer_speech_started_event.py | 26 + ...input_audio_buffer_speech_stopped_event.py | 25 + .../realtime/rate_limits_updated_event.py | 33 + .../beta/realtime/realtime_client_event.py | 32 + .../realtime/realtime_client_event_param.py | 30 + .../beta/realtime/realtime_connect_params.py | 11 + .../types/beta/realtime/realtime_response.py | 42 + .../beta/realtime/realtime_response_status.py | 39 + .../beta/realtime/realtime_response_usage.py | 52 ++ .../beta/realtime/realtime_server_event.py | 72 ++ .../realtime/response_audio_delta_event.py | 30 + .../realtime/response_audio_done_event.py | 27 + .../response_audio_transcript_delta_event.py | 30 + .../response_audio_transcript_done_event.py | 30 + .../beta/realtime/response_cancel_event.py | 22 + .../realtime/response_cancel_event_param.py | 21 + .../response_content_part_added_event.py | 45 + .../response_content_part_done_event.py | 45 + .../beta/realtime/response_create_event.py | 115 +++ .../realtime/response_create_event_param.py | 116 +++ .../beta/realtime/response_created_event.py | 19 + .../beta/realtime/response_done_event.py | 19 + ...nse_function_call_arguments_delta_event.py | 30 + ...onse_function_call_arguments_done_event.py | 30 + .../response_output_item_added_event.py | 25 + .../response_output_item_done_event.py | 25 + .../realtime/response_text_delta_event.py | 30 + .../beta/realtime/response_text_done_event.py | 30 + src/openai/types/beta/realtime/session.py | 148 +++ .../beta/realtime/session_created_event.py | 19 + .../beta/realtime/session_update_event.py | 158 ++++ .../realtime/session_update_event_param.py | 166 ++++ .../beta/realtime/session_updated_event.py | 19 + .../types/websocket_connection_options.py | 36 + tests/api_resources/beta/test_realtime.py | 17 + 70 files changed, 3313 insertions(+), 4 deletions(-) create mode 100644 src/openai/types/beta/realtime/conversation_created_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item.py create mode 100644 src/openai/types/beta/realtime/conversation_item_content.py create mode 100644 src/openai/types/beta/realtime/conversation_item_content_param.py create mode 100644 src/openai/types/beta/realtime/conversation_item_create_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_create_event_param.py create mode 100644 src/openai/types/beta/realtime/conversation_item_created_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_delete_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_delete_event_param.py create mode 100644 src/openai/types/beta/realtime/conversation_item_deleted_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_param.py create mode 100644 src/openai/types/beta/realtime/conversation_item_truncate_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_truncate_event_param.py create mode 100644 src/openai/types/beta/realtime/conversation_item_truncated_event.py create mode 100644 src/openai/types/beta/realtime/error_event.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_append_event.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_append_event_param.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_clear_event.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_cleared_event.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_commit_event.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_committed_event.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py create mode 100644 src/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py create mode 100644 src/openai/types/beta/realtime/rate_limits_updated_event.py create mode 100644 src/openai/types/beta/realtime/realtime_client_event.py create mode 100644 src/openai/types/beta/realtime/realtime_client_event_param.py create mode 100644 src/openai/types/beta/realtime/realtime_connect_params.py create mode 100644 src/openai/types/beta/realtime/realtime_response.py create mode 100644 src/openai/types/beta/realtime/realtime_response_status.py create mode 100644 src/openai/types/beta/realtime/realtime_response_usage.py create mode 100644 src/openai/types/beta/realtime/realtime_server_event.py create mode 100644 src/openai/types/beta/realtime/response_audio_delta_event.py create mode 100644 src/openai/types/beta/realtime/response_audio_done_event.py create mode 100644 src/openai/types/beta/realtime/response_audio_transcript_delta_event.py create mode 100644 src/openai/types/beta/realtime/response_audio_transcript_done_event.py create mode 100644 src/openai/types/beta/realtime/response_cancel_event.py create mode 100644 src/openai/types/beta/realtime/response_cancel_event_param.py create mode 100644 src/openai/types/beta/realtime/response_content_part_added_event.py create mode 100644 src/openai/types/beta/realtime/response_content_part_done_event.py create mode 100644 src/openai/types/beta/realtime/response_create_event.py create mode 100644 src/openai/types/beta/realtime/response_create_event_param.py create mode 100644 src/openai/types/beta/realtime/response_created_event.py create mode 100644 src/openai/types/beta/realtime/response_done_event.py create mode 100644 src/openai/types/beta/realtime/response_function_call_arguments_delta_event.py create mode 100644 src/openai/types/beta/realtime/response_function_call_arguments_done_event.py create mode 100644 src/openai/types/beta/realtime/response_output_item_added_event.py create mode 100644 src/openai/types/beta/realtime/response_output_item_done_event.py create mode 100644 src/openai/types/beta/realtime/response_text_delta_event.py create mode 100644 src/openai/types/beta/realtime/response_text_done_event.py create mode 100644 src/openai/types/beta/realtime/session.py create mode 100644 src/openai/types/beta/realtime/session_created_event.py create mode 100644 src/openai/types/beta/realtime/session_update_event.py create mode 100644 src/openai/types/beta/realtime/session_update_event_param.py create mode 100644 src/openai/types/beta/realtime/session_updated_event.py create mode 100644 src/openai/types/websocket_connection_options.py create mode 100644 tests/api_resources/beta/test_realtime.py diff --git a/.stats.yml b/.stats.yml index e3a0040a5a..12219ccaa1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-779ea2754025daf5e18eb8ceb203ec321692636bc3a999338556a479178efa6c.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-0d64ca9e45f51b4279f87b205eeb3a3576df98407698ce053f2e2302c1c08df1.yml diff --git a/api.md b/api.md index 91b2a9c2fd..ace93e0559 100644 --- a/api.md +++ b/api.md @@ -239,6 +239,57 @@ Methods: ## Realtime +Types: + +```python +from openai.types.beta.realtime import ( + ConversationCreatedEvent, + ConversationItem, + ConversationItemContent, + ConversationItemCreateEvent, + ConversationItemCreatedEvent, + ConversationItemDeleteEvent, + ConversationItemDeletedEvent, + ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemTruncateEvent, + ConversationItemTruncatedEvent, + ErrorEvent, + InputAudioBufferAppendEvent, + InputAudioBufferClearEvent, + InputAudioBufferClearedEvent, + InputAudioBufferCommitEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + RateLimitsUpdatedEvent, + RealtimeClientEvent, + RealtimeResponse, + RealtimeResponseStatus, + RealtimeResponseUsage, + RealtimeServerEvent, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCancelEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreateEvent, + ResponseCreatedEvent, + ResponseDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + SessionCreatedEvent, + SessionUpdateEvent, + SessionUpdatedEvent, +) +``` + ### Sessions Types: diff --git a/pyproject.toml b/pyproject.toml index e03d4e798f..f83aff6fee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,9 +35,6 @@ classifiers = [ "License :: OSI Approved :: Apache Software License" ] -[project.optional-dependencies] -datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] - [project.urls] Homepage = "https://github.com/openai/openai-python" Repository = "https://github.com/openai/openai-python" @@ -45,6 +42,10 @@ Repository = "https://github.com/openai/openai-python" [project.scripts] openai = "openai.cli:main" +[project.optional-dependencies] +realtime = ["websockets >= 13, < 15"] +datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] + [tool.rye] managed = true # version pins are in requirements-dev.lock diff --git a/requirements-dev.lock b/requirements-dev.lock index 2cf6ab5ea9..94cf6aca07 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -185,5 +185,7 @@ urllib3==2.2.1 # via requests virtualenv==20.24.5 # via nox +websockets==14.1 + # via openai zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index 826f0bc927..c10449ac20 100644 --- a/requirements.lock +++ b/requirements.lock @@ -64,3 +64,5 @@ typing-extensions==4.12.2 # via pydantic-core tzdata==2024.1 # via pandas +websockets==14.1 + # via openai diff --git a/src/openai/_client.py b/src/openai/_client.py index 5419e88f06..c784694f20 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -63,6 +63,14 @@ class OpenAI(SyncAPIClient): organization: str | None project: str | None + websocket_base_url: str | httpx.URL | None + """Base URL for WebSocket connections. + + If not specified, the default base URL will be used, with 'wss://' replacing the + 'http://' or 'https://' scheme. For example: 'http://example.com/' becomes + 'wss://example.com' + """ + def __init__( self, *, @@ -70,6 +78,7 @@ def __init__( organization: str | None = None, project: str | None = None, base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -111,6 +120,8 @@ def __init__( project = os.environ.get("OPENAI_PROJECT_ID") self.project = project + self.websocket_base_url = websocket_base_url + if base_url is None: base_url = os.environ.get("OPENAI_BASE_URL") if base_url is None: @@ -172,6 +183,7 @@ def copy( api_key: str | None = None, organization: str | None = None, project: str | None = None, + websocket_base_url: str | httpx.URL | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, @@ -208,6 +220,7 @@ def copy( api_key=api_key or self.api_key, organization=organization or self.organization, project=project or self.project, + websocket_base_url=websocket_base_url or self.websocket_base_url, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, @@ -277,6 +290,14 @@ class AsyncOpenAI(AsyncAPIClient): organization: str | None project: str | None + websocket_base_url: str | httpx.URL | None + """Base URL for WebSocket connections. + + If not specified, the default base URL will be used, with 'wss://' replacing the + 'http://' or 'https://' scheme. For example: 'http://example.com/' becomes + 'wss://example.com' + """ + def __init__( self, *, @@ -284,6 +305,7 @@ def __init__( organization: str | None = None, project: str | None = None, base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -325,6 +347,8 @@ def __init__( project = os.environ.get("OPENAI_PROJECT_ID") self.project = project + self.websocket_base_url = websocket_base_url + if base_url is None: base_url = os.environ.get("OPENAI_BASE_URL") if base_url is None: @@ -386,6 +410,7 @@ def copy( api_key: str | None = None, organization: str | None = None, project: str | None = None, + websocket_base_url: str | httpx.URL | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, @@ -422,6 +447,7 @@ def copy( api_key=api_key or self.api_key, organization=organization or self.organization, project=project or self.project, + websocket_base_url=websocket_base_url or self.websocket_base_url, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 54122dbecb..13d9f31838 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -76,6 +76,7 @@ def __init__( azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -94,6 +95,7 @@ def __init__( azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -112,6 +114,7 @@ def __init__( azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -131,6 +134,7 @@ def __init__( azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, project: str | None = None, + websocket_base_url: str | httpx.URL | None = None, base_url: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, @@ -214,6 +218,7 @@ def __init__( default_headers=default_headers, default_query=default_query, http_client=http_client, + websocket_base_url=websocket_base_url, _strict_response_validation=_strict_response_validation, ) self._api_version = api_version @@ -227,6 +232,7 @@ def copy( api_key: str | None = None, organization: str | None = None, project: str | None = None, + websocket_base_url: str | httpx.URL | None = None, api_version: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, @@ -247,6 +253,7 @@ def copy( api_key=api_key, organization=organization, project=project, + websocket_base_url=websocket_base_url, base_url=base_url, timeout=timeout, http_client=http_client, @@ -314,6 +321,7 @@ def __init__( azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, project: str | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -333,6 +341,7 @@ def __init__( azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, project: str | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -352,6 +361,7 @@ def __init__( azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, project: str | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -372,6 +382,7 @@ def __init__( organization: str | None = None, project: str | None = None, base_url: str | None = None, + websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -454,6 +465,7 @@ def __init__( default_headers=default_headers, default_query=default_query, http_client=http_client, + websocket_base_url=websocket_base_url, _strict_response_validation=_strict_response_validation, ) self._api_version = api_version @@ -467,6 +479,7 @@ def copy( api_key: str | None = None, organization: str | None = None, project: str | None = None, + websocket_base_url: str | httpx.URL | None = None, api_version: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, @@ -487,6 +500,7 @@ def copy( api_key=api_key, organization=organization, project=project, + websocket_base_url=websocket_base_url, base_url=base_url, timeout=timeout, http_client=http_client, diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index e57e0be503..c79fd46217 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -2,6 +2,15 @@ from __future__ import annotations +import json +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Iterator, cast +from typing_extensions import AsyncIterator + +import httpx +from pydantic import BaseModel + from .sessions import ( Sessions, AsyncSessions, @@ -10,11 +19,34 @@ SessionsWithStreamingResponse, AsyncSessionsWithStreamingResponse, ) +from ...._types import NOT_GIVEN, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + strip_not_given, + async_maybe_transform, +) from ...._compat import cached_property +from ...._models import construct_type_unchecked from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._exceptions import OpenAIError +from ...._base_client import _merge_mappings +from ....types.beta.realtime import session_update_event_param, response_create_event_param +from ....types.websocket_connection_options import WebsocketConnectionOptions +from ....types.beta.realtime.realtime_client_event import RealtimeClientEvent +from ....types.beta.realtime.realtime_server_event import RealtimeServerEvent +from ....types.beta.realtime.conversation_item_param import ConversationItemParam +from ....types.beta.realtime.realtime_client_event_param import RealtimeClientEventParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebsocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebsocketConnection + + from ...._client import OpenAI, AsyncOpenAI __all__ = ["Realtime", "AsyncRealtime"] +log: logging.Logger = logging.getLogger(__name__) + class Realtime(SyncAPIResource): @cached_property @@ -40,6 +72,33 @@ def with_streaming_response(self) -> RealtimeWithStreamingResponse: """ return RealtimeWithStreamingResponse(self) + def connect( + self, + *, + model: str, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, + ) -> RealtimeConnectionManager: + """ + The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as function calling. + + Some notable benefits of the API include: + + - Native speech-to-speech: Skipping an intermediate text format means low latency and nuanced output. + - Natural, steerable voices: The models have natural inflection and can laugh, whisper, and adhere to tone direction. + - Simultaneous multimodal output: Text is useful for moderation; faster-than-realtime audio ensures stable playback. + + The Realtime API is a stateful, event-based API that communicates over a WebSocket. + """ + return RealtimeConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + model=model, + ) + class AsyncRealtime(AsyncAPIResource): @cached_property @@ -65,6 +124,33 @@ def with_streaming_response(self) -> AsyncRealtimeWithStreamingResponse: """ return AsyncRealtimeWithStreamingResponse(self) + def connect( + self, + *, + model: str, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, + ) -> AsyncRealtimeConnectionManager: + """ + The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as function calling. + + Some notable benefits of the API include: + + - Native speech-to-speech: Skipping an intermediate text format means low latency and nuanced output. + - Natural, steerable voices: The models have natural inflection and can laugh, whisper, and adhere to tone direction. + - Simultaneous multimodal output: Text is useful for moderation; faster-than-realtime audio ensures stable playback. + + The Realtime API is a stateful, event-based API that communicates over a WebSocket. + """ + return AsyncRealtimeConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + model=model, + ) + class RealtimeWithRawResponse: def __init__(self, realtime: Realtime) -> None: @@ -100,3 +186,769 @@ def __init__(self, realtime: AsyncRealtime) -> None: @cached_property def sessions(self) -> AsyncSessionsWithStreamingResponse: return AsyncSessionsWithStreamingResponse(self._realtime.sessions) + + +class AsyncRealtimeConnection: + """Represents a live websocket connection to the Realtime API""" + + session: AsyncRealtimeSessionResource + response: AsyncRealtimeResponseResource + conversation: AsyncRealtimeConversationResource + input_audio_buffer: AsyncRealtimeInputAudioBufferResource + + _connection: AsyncWebsocketConnection + + def __init__(self, connection: AsyncWebsocketConnection) -> None: + self._connection = connection + + self.session = AsyncRealtimeSessionResource(self) + self.response = AsyncRealtimeResponseResource(self) + self.conversation = AsyncRealtimeConversationResource(self) + self.input_audio_buffer = AsyncRealtimeInputAudioBufferResource(self) + + async def __aiter__(self) -> AsyncIterator[RealtimeServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK + + try: + while True: + yield await self.recv() + except ConnectionClosedOK: + return + + async def recv(self) -> RealtimeServerEvent: + """ + Receive the next message from the connection and parses it into a `RealtimeServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(await self.recv_bytes()) + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `RealtimeServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug(f"Received websocket message: %s", message) + if not isinstance(message, bytes): + # passing `decode=False` should always result in us getting `bytes` back + raise TypeError(f"Expected `.recv(decode=False)` to return `bytes` but got {type(message)}") + + return message + + async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, RealtimeClientEventParam)) + ) + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> RealtimeServerEvent: + """ + Converts a raw `str` or `bytes` message into a `RealtimeServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) + ) + + +class AsyncRealtimeConnectionManager: + """ + Context manager over a `AsyncRealtimeConnection` that is returned by `beta.realtime.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.beta.realtime.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + model: str, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebsocketConnectionOptions, + ) -> None: + self.__client = client + self.__model = model + self.__connection: AsyncRealtimeConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + + async def __aenter__(self) -> AsyncRealtimeConnection: + """ + 👋 If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.beta.realtime.connect(...).enter() + # ... + await connection.close() + ``` + """ + try: + from websockets.asyncio.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + "model": self.__model, + **self.__extra_query, + }, + ) + log.debug("Connecting to %s", url) + if self.__websocket_connection_options: + log.debug("Connection options: %s", self.__websocket_connection_options) + + self.__connection = AsyncRealtimeConnection( + await connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **self.__client.auth_headers, + "OpenAI-Beta": "realtime=v1", + }, + self.__extra_headers, + ), + **self.__websocket_connection_options, + ) + ) + + return self.__connection + + enter = __aenter__ + + def _prepare_url(http://github.com/self) -> httpx.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx.URL(self.__client.websocket_base_url) + else: + base_url = self.__client._base_url.copy_with(scheme="wss") + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class RealtimeConnection: + """Represents a live websocket connection to the Realtime API""" + + session: RealtimeSessionResource + response: RealtimeResponseResource + conversation: RealtimeConversationResource + input_audio_buffer: RealtimeInputAudioBufferResource + + _connection: WebsocketConnection + + def __init__(self, connection: WebsocketConnection) -> None: + self._connection = connection + + self.session = RealtimeSessionResource(self) + self.response = RealtimeResponseResource(self) + self.conversation = RealtimeConversationResource(self) + self.input_audio_buffer = RealtimeInputAudioBufferResource(self) + + def __iter__(self) -> Iterator[RealtimeServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK + + try: + while True: + yield self.recv() + except ConnectionClosedOK: + return + + def recv(self) -> RealtimeServerEvent: + """ + Receive the next message from the connection and parses it into a `RealtimeServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(self.recv_bytes()) + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `RealtimeServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug(f"Received websocket message: %s", message) + if not isinstance(message, bytes): + # passing `decode=False` should always result in us getting `bytes` back + raise TypeError(f"Expected `.recv(decode=False)` to return `bytes` but got {type(message)}") + + return message + + def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) + ) + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> RealtimeServerEvent: + """ + Converts a raw `str` or `bytes` message into a `RealtimeServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) + ) + + +class RealtimeConnectionManager: + """ + Context manager over a `RealtimeConnection` that is returned by `beta.realtime.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.beta.realtime.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + model: str, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebsocketConnectionOptions, + ) -> None: + self.__client = client + self.__model = model + self.__connection: RealtimeConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + + def __enter__(self) -> RealtimeConnection: + """ + 👋 If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.beta.realtime.connect(...).enter() + # ... + connection.close() + ``` + """ + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + "model": self.__model, + **self.__extra_query, + }, + ) + log.debug("Connecting to %s", url) + if self.__websocket_connection_options: + log.debug("Connection options: %s", self.__websocket_connection_options) + + self.__connection = RealtimeConnection( + connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **self.__client.auth_headers, + "OpenAI-Beta": "realtime=v1", + }, + self.__extra_headers, + ), + **self.__websocket_connection_options, + ) + ) + + return self.__connection + + enter = __enter__ + + def _prepare_url(http://github.com/self) -> httpx.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx.URL(self.__client.websocket_base_url) + else: + base_url = self.__client._base_url.copy_with(scheme="wss") + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseRealtimeConnectionResource: + def __init__(self, connection: RealtimeConnection) -> None: + self._connection = connection + + +class RealtimeSessionResource(BaseRealtimeConnectionResource): + def update(self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to update the session’s default configuration. + + The client may + send this event at any time to update the session configuration, and any + field may be updated at any time, except for "voice". The server will respond + with a `session.updated` event that shows the full effective configuration. + Only fields that are present are updated, thus the correct way to clear a + field like "instructions" is to pass an empty string. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class RealtimeResponseResource(BaseRealtimeConnectionResource): + def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.cancelled` event or an error if there is no response to + cancel. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + def create( + self, + *, + event_id: str | NotGiven = NOT_GIVEN, + response: response_create_event_param.Response | NotGiven = NOT_GIVEN, + ) -> None: + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions`, and `temperature`. These fields will override the Session's + configuration for this Response only. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.create", "event_id": event_id, "response": response}), + ) + ) + + +class RealtimeConversationResource(BaseRealtimeConnectionResource): + @cached_property + def item(self) -> RealtimeConversationItemResource: + return RealtimeConversationItemResource(self._connection) + + +class RealtimeConversationItemResource(BaseRealtimeConnectionResource): + def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.delete", "item_id": item_id, "event_id": event_id}), + ) + ) + + def create( + self, + *, + item: ConversationItemParam, + event_id: str | NotGiven = NOT_GIVEN, + previous_item_id: str | NotGiven = NOT_GIVEN, + ) -> None: + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will respond with a `conversation.item.created` + event, otherwise an `error` event will be sent. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.create", + "item": item, + "event_id": event_id, + "previous_item_id": previous_item_id, + } + ), + ) + ) + + def truncate( + self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.truncate", + "audio_end_ms": audio_end_ms, + "content_index": content_index, + "item_id": item_id, + "event_id": event_id, + } + ), + ) + ) + + +class RealtimeInputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to commit the user input audio buffer, which will create a + new user message item in the conversation. This event will produce an error + if the input audio buffer is empty. When in Server VAD mode, the client does + not need to send this event, the server will commit the audio buffer + automatically. + + Committing the input audio buffer will trigger input audio transcription + (if enabled in session configuration), but it will not create a response + from the model. The server will respond with an `input_audio_buffer.committed` + event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. In Server VAD + mode, the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike made other client events, the server will + not send a confirmation response to this event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + + +class BaseAsyncRealtimeConnectionResource: + def __init__(self, connection: AsyncRealtimeConnection) -> None: + self._connection = connection + + +class AsyncRealtimeSessionResource(BaseAsyncRealtimeConnectionResource): + async def update( + self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to update the session’s default configuration. + + The client may + send this event at any time to update the session configuration, and any + field may be updated at any time, except for "voice". The server will respond + with a `session.updated` event that shows the full effective configuration. + Only fields that are present are updated, thus the correct way to clear a + field like "instructions" is to pass an empty string. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeResponseResource(BaseAsyncRealtimeConnectionResource): + async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.cancelled` event or an error if there is no response to + cancel. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + async def create( + self, + *, + event_id: str | NotGiven = NOT_GIVEN, + response: response_create_event_param.Response | NotGiven = NOT_GIVEN, + ) -> None: + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions`, and `temperature`. These fields will override the Session's + configuration for this Response only. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.create", "event_id": event_id, "response": response}), + ) + ) + + +class AsyncRealtimeConversationResource(BaseAsyncRealtimeConnectionResource): + @cached_property + def item(self) -> AsyncRealtimeConversationItemResource: + return AsyncRealtimeConversationItemResource(self._connection) + + +class AsyncRealtimeConversationItemResource(BaseAsyncRealtimeConnectionResource): + async def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.delete", "item_id": item_id, "event_id": event_id}), + ) + ) + + async def create( + self, + *, + item: ConversationItemParam, + event_id: str | NotGiven = NOT_GIVEN, + previous_item_id: str | NotGiven = NOT_GIVEN, + ) -> None: + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will respond with a `conversation.item.created` + event, otherwise an `error` event will be sent. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.create", + "item": item, + "event_id": event_id, + "previous_item_id": previous_item_id, + } + ), + ) + ) + + async def truncate( + self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.truncate", + "audio_end_ms": audio_end_ms, + "content_index": content_index, + "item_id": item_id, + "event_id": event_id, + } + ), + ) + ) + + +class AsyncRealtimeInputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + async def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to commit the user input audio buffer, which will create a + new user message item in the conversation. This event will produce an error + if the input audio buffer is empty. When in Server VAD mode, the client does + not need to send this event, the server will commit the audio buffer + automatically. + + Committing the input audio buffer will trigger input audio transcription + (if enabled in session configuration), but it will not create a response + from the model. The server will respond with an `input_audio_buffer.committed` + event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + async def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. In Server VAD + mode, the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike made other client events, the server will + not send a confirmation response to this event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 7677be01b2..72950f2491 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -47,6 +47,7 @@ from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse from .moderation_text_input_param import ModerationTextInputParam as ModerationTextInputParam +from .websocket_connection_options import WebsocketConnectionOptions as WebsocketConnectionOptions from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam from .moderation_multi_modal_input_param import ModerationMultiModalInputParam as ModerationMultiModalInputParam diff --git a/src/openai/types/beta/realtime/__init__.py b/src/openai/types/beta/realtime/__init__.py index 1c5246db7a..372d4ec19d 100644 --- a/src/openai/types/beta/realtime/__init__.py +++ b/src/openai/types/beta/realtime/__init__.py @@ -2,5 +2,79 @@ from __future__ import annotations +from .session import Session as Session +from .error_event import ErrorEvent as ErrorEvent +from .conversation_item import ConversationItem as ConversationItem +from .realtime_response import RealtimeResponse as RealtimeResponse +from .response_done_event import ResponseDoneEvent as ResponseDoneEvent +from .session_update_event import SessionUpdateEvent as SessionUpdateEvent +from .realtime_client_event import RealtimeClientEvent as RealtimeClientEvent +from .realtime_server_event import RealtimeServerEvent as RealtimeServerEvent +from .response_cancel_event import ResponseCancelEvent as ResponseCancelEvent +from .response_create_event import ResponseCreateEvent as ResponseCreateEvent from .session_create_params import SessionCreateParams as SessionCreateParams +from .session_created_event import SessionCreatedEvent as SessionCreatedEvent +from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent +from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent +from .conversation_item_param import ConversationItemParam as ConversationItemParam +from .realtime_connect_params import RealtimeConnectParams as RealtimeConnectParams +from .realtime_response_usage import RealtimeResponseUsage as RealtimeResponseUsage from .session_create_response import SessionCreateResponse as SessionCreateResponse +from .realtime_response_status import RealtimeResponseStatus as RealtimeResponseStatus +from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent +from .conversation_item_content import ConversationItemContent as ConversationItemContent +from .rate_limits_updated_event import RateLimitsUpdatedEvent as RateLimitsUpdatedEvent +from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent +from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent +from .conversation_created_event import ConversationCreatedEvent as ConversationCreatedEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent +from .session_update_event_param import SessionUpdateEventParam as SessionUpdateEventParam +from .realtime_client_event_param import RealtimeClientEventParam as RealtimeClientEventParam +from .response_cancel_event_param import ResponseCancelEventParam as ResponseCancelEventParam +from .response_create_event_param import ResponseCreateEventParam as ResponseCreateEventParam +from .conversation_item_create_event import ConversationItemCreateEvent as ConversationItemCreateEvent +from .conversation_item_delete_event import ConversationItemDeleteEvent as ConversationItemDeleteEvent +from .input_audio_buffer_clear_event import InputAudioBufferClearEvent as InputAudioBufferClearEvent +from .conversation_item_content_param import ConversationItemContentParam as ConversationItemContentParam +from .conversation_item_created_event import ConversationItemCreatedEvent as ConversationItemCreatedEvent +from .conversation_item_deleted_event import ConversationItemDeletedEvent as ConversationItemDeletedEvent +from .input_audio_buffer_append_event import InputAudioBufferAppendEvent as InputAudioBufferAppendEvent +from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent as InputAudioBufferCommitEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent +from .conversation_item_truncate_event import ConversationItemTruncateEvent as ConversationItemTruncateEvent +from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent as InputAudioBufferClearedEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent +from .conversation_item_truncated_event import ConversationItemTruncatedEvent as ConversationItemTruncatedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent +from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent as InputAudioBufferCommittedEvent +from .conversation_item_create_event_param import ConversationItemCreateEventParam as ConversationItemCreateEventParam +from .conversation_item_delete_event_param import ConversationItemDeleteEventParam as ConversationItemDeleteEventParam +from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam as InputAudioBufferClearEventParam +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent +from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam as InputAudioBufferAppendEventParam +from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam as InputAudioBufferCommitEventParam +from .response_audio_transcript_delta_event import ( + ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, +) +from .conversation_item_truncate_event_param import ( + ConversationItemTruncateEventParam as ConversationItemTruncateEventParam, +) +from .input_audio_buffer_speech_started_event import ( + InputAudioBufferSpeechStartedEvent as InputAudioBufferSpeechStartedEvent, +) +from .input_audio_buffer_speech_stopped_event import ( + InputAudioBufferSpeechStoppedEvent as InputAudioBufferSpeechStoppedEvent, +) +from .response_function_call_arguments_done_event import ( + ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, +) +from .response_function_call_arguments_delta_event import ( + ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, +) +from .conversation_item_input_audio_transcription_failed_event import ( + ConversationItemInputAudioTranscriptionFailedEvent as ConversationItemInputAudioTranscriptionFailedEvent, +) +from .conversation_item_input_audio_transcription_completed_event import ( + ConversationItemInputAudioTranscriptionCompletedEvent as ConversationItemInputAudioTranscriptionCompletedEvent, +) diff --git a/src/openai/types/beta/realtime/conversation_created_event.py b/src/openai/types/beta/realtime/conversation_created_event.py new file mode 100644 index 0000000000..4ba0540867 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_created_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationCreatedEvent", "Conversation"] + + +class Conversation(BaseModel): + id: Optional[str] = None + """The unique ID of the conversation.""" + + object: Optional[Literal["realtime.conversation"]] = None + """The object type, must be `realtime.conversation`.""" + + +class ConversationCreatedEvent(BaseModel): + conversation: Conversation + """The conversation resource.""" + + event_id: str + """The unique ID of the server event.""" + + type: Literal["conversation.created"] + """The event type, must be `conversation.created`.""" diff --git a/src/openai/types/beta/realtime/conversation_item.py b/src/openai/types/beta/realtime/conversation_item.py new file mode 100644 index 0000000000..4edf6c4d5f --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item_content import ConversationItemContent + +__all__ = ["ConversationItem"] + + +class ConversationItem(BaseModel): + id: Optional[str] = None + """ + The unique ID of the item, this can be generated by the client to help manage + server-side context, but is not required because the server will generate one if + not provided. + """ + + arguments: Optional[str] = None + """The arguments of the function call (for `function_call` items).""" + + call_id: Optional[str] = None + """ + The ID of the function call (for `function_call` and `function_call_output` + items). If passed on a `function_call_output` item, the server will check that a + `function_call` item with the same ID exists in the conversation history. + """ + + content: Optional[List[ConversationItemContent]] = None + """The content of the message, applicable for `message` items. + + - Message items of role `system` support only `input_text` content + - Message items of role `user` support `input_text` and `input_audio` content + - Message items of role `assistant` support `text` content. + """ + + name: Optional[str] = None + """The name of the function being called (for `function_call` items).""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + output: Optional[str] = None + """The output of the function call (for `function_call_output` items).""" + + role: Optional[Literal["user", "assistant", "system"]] = None + """ + The role of the message sender (`user`, `assistant`, `system`), only applicable + for `message` items. + """ + + status: Optional[Literal["completed", "incomplete"]] = None + """The status of the item (`completed`, `incomplete`). + + These have no effect on the conversation, but are accepted for consistency with + the `conversation.item.created` event. + """ + + type: Optional[Literal["message", "function_call", "function_call_output"]] = None + """The type of the item (`message`, `function_call`, `function_call_output`).""" diff --git a/src/openai/types/beta/realtime/conversation_item_content.py b/src/openai/types/beta/realtime/conversation_item_content.py new file mode 100644 index 0000000000..b854aa0e0f --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_content.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemContent"] + + +class ConversationItemContent(BaseModel): + id: Optional[str] = None + """ + ID of a previous conversation item (like a model response), used for + `item_reference` content types. + """ + + audio: Optional[str] = None + """Base64-encoded audio bytes, used for `input_audio` content type.""" + + text: Optional[str] = None + """The text content, used for `input_text` and `text` content types.""" + + transcript: Optional[str] = None + """The transcript of the audio, used for `input_audio` content type.""" + + type: Optional[Literal["input_text", "input_audio", "item_reference", "text"]] = None + """The content type (`input_text`, `input_audio`, `item_reference`, `text`).""" diff --git a/src/openai/types/beta/realtime/conversation_item_content_param.py b/src/openai/types/beta/realtime/conversation_item_content_param.py new file mode 100644 index 0000000000..b354d78971 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_content_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ConversationItemContentParam"] + + +class ConversationItemContentParam(TypedDict, total=False): + id: str + """ + ID of a previous conversation item (like a model response), used for + `item_reference` content types. + """ + + audio: str + """Base64-encoded audio bytes, used for `input_audio` content type.""" + + text: str + """The text content, used for `input_text` and `text` content types.""" + + transcript: str + """The transcript of the audio, used for `input_audio` content type.""" + + type: Literal["input_text", "input_audio", "item_reference", "text"] + """The content type (`input_text`, `input_audio`, `item_reference`, `text`).""" diff --git a/src/openai/types/beta/realtime/conversation_item_create_event.py b/src/openai/types/beta/realtime/conversation_item_create_event.py new file mode 100644 index 0000000000..50d309675b --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_create_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemCreateEvent"] + + +class ConversationItemCreateEvent(BaseModel): + item: ConversationItem + """The item to add to the conversation.""" + + type: Literal["conversation.item.create"] + """The event type, must be `conversation.item.create`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + previous_item_id: Optional[str] = None + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. If + set, it allows an item to be inserted mid-conversation. If the ID cannot be + found, an error will be returned and the item will not be added. + """ diff --git a/src/openai/types/beta/realtime/conversation_item_create_event_param.py b/src/openai/types/beta/realtime/conversation_item_create_event_param.py new file mode 100644 index 0000000000..b8c8bbc251 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_create_event_param.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .conversation_item_param import ConversationItemParam + +__all__ = ["ConversationItemCreateEventParam"] + + +class ConversationItemCreateEventParam(TypedDict, total=False): + item: Required[ConversationItemParam] + """The item to add to the conversation.""" + + type: Required[Literal["conversation.item.create"]] + """The event type, must be `conversation.item.create`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + previous_item_id: str + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. If + set, it allows an item to be inserted mid-conversation. If the ID cannot be + found, an error will be returned and the item will not be added. + """ diff --git a/src/openai/types/beta/realtime/conversation_item_created_event.py b/src/openai/types/beta/realtime/conversation_item_created_event.py new file mode 100644 index 0000000000..2f20388246 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_created_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemCreatedEvent"] + + +class ConversationItemCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """The item to add to the conversation.""" + + previous_item_id: str + """ + The ID of the preceding item in the Conversation context, allows the client to + understand the order of the conversation. + """ + + type: Literal["conversation.item.created"] + """The event type, must be `conversation.item.created`.""" diff --git a/src/openai/types/beta/realtime/conversation_item_delete_event.py b/src/openai/types/beta/realtime/conversation_item_delete_event.py new file mode 100644 index 0000000000..02ca8250ce --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_delete_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemDeleteEvent"] + + +class ConversationItemDeleteEvent(BaseModel): + item_id: str + """The ID of the item to delete.""" + + type: Literal["conversation.item.delete"] + """The event type, must be `conversation.item.delete`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/conversation_item_delete_event_param.py b/src/openai/types/beta/realtime/conversation_item_delete_event_param.py new file mode 100644 index 0000000000..c3f88d6627 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_delete_event_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemDeleteEventParam"] + + +class ConversationItemDeleteEventParam(TypedDict, total=False): + item_id: Required[str] + """The ID of the item to delete.""" + + type: Required[Literal["conversation.item.delete"]] + """The event type, must be `conversation.item.delete`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/conversation_item_deleted_event.py b/src/openai/types/beta/realtime/conversation_item_deleted_event.py new file mode 100644 index 0000000000..a35a97817a --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_deleted_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemDeletedEvent"] + + +class ConversationItemDeletedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item that was deleted.""" + + type: Literal["conversation.item.deleted"] + """The event type, must be `conversation.item.deleted`.""" diff --git a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py new file mode 100644 index 0000000000..ded79cc0f7 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionCompletedEvent"] + + +class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): + content_index: int + """The index of the content part containing the audio.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item containing the audio.""" + + transcript: str + """The transcribed text.""" + + type: Literal["conversation.item.input_audio_transcription.completed"] + """ + The event type, must be `conversation.item.input_audio_transcription.completed`. + """ diff --git a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py new file mode 100644 index 0000000000..cecac93e64 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionFailedEvent", "Error"] + + +class Error(BaseModel): + code: Optional[str] = None + """Error code, if any.""" + + message: Optional[str] = None + """A human-readable error message.""" + + param: Optional[str] = None + """Parameter related to the error, if any.""" + + type: Optional[str] = None + """The type of error.""" + + +class ConversationItemInputAudioTranscriptionFailedEvent(BaseModel): + content_index: int + """The index of the content part containing the audio.""" + + error: Error + """Details of the transcription error.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item.""" + + type: Literal["conversation.item.input_audio_transcription.failed"] + """The event type, must be `conversation.item.input_audio_transcription.failed`.""" diff --git a/src/openai/types/beta/realtime/conversation_item_param.py b/src/openai/types/beta/realtime/conversation_item_param.py new file mode 100644 index 0000000000..ac0f8431e5 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_param.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, TypedDict + +from .conversation_item_content_param import ConversationItemContentParam + +__all__ = ["ConversationItemParam"] + + +class ConversationItemParam(TypedDict, total=False): + id: str + """ + The unique ID of the item, this can be generated by the client to help manage + server-side context, but is not required because the server will generate one if + not provided. + """ + + arguments: str + """The arguments of the function call (for `function_call` items).""" + + call_id: str + """ + The ID of the function call (for `function_call` and `function_call_output` + items). If passed on a `function_call_output` item, the server will check that a + `function_call` item with the same ID exists in the conversation history. + """ + + content: Iterable[ConversationItemContentParam] + """The content of the message, applicable for `message` items. + + - Message items of role `system` support only `input_text` content + - Message items of role `user` support `input_text` and `input_audio` content + - Message items of role `assistant` support `text` content. + """ + + name: str + """The name of the function being called (for `function_call` items).""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + output: str + """The output of the function call (for `function_call_output` items).""" + + role: Literal["user", "assistant", "system"] + """ + The role of the message sender (`user`, `assistant`, `system`), only applicable + for `message` items. + """ + + status: Literal["completed", "incomplete"] + """The status of the item (`completed`, `incomplete`). + + These have no effect on the conversation, but are accepted for consistency with + the `conversation.item.created` event. + """ + + type: Literal["message", "function_call", "function_call_output"] + """The type of the item (`message`, `function_call`, `function_call_output`).""" diff --git a/src/openai/types/beta/realtime/conversation_item_truncate_event.py b/src/openai/types/beta/realtime/conversation_item_truncate_event.py new file mode 100644 index 0000000000..cb336bba2c --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_truncate_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemTruncateEvent"] + + +class ConversationItemTruncateEvent(BaseModel): + audio_end_ms: int + """Inclusive duration up to which audio is truncated, in milliseconds. + + If the audio_end_ms is greater than the actual audio duration, the server will + respond with an error. + """ + + content_index: int + """The index of the content part to truncate. Set this to 0.""" + + item_id: str + """The ID of the assistant message item to truncate. + + Only assistant message items can be truncated. + """ + + type: Literal["conversation.item.truncate"] + """The event type, must be `conversation.item.truncate`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/conversation_item_truncate_event_param.py b/src/openai/types/beta/realtime/conversation_item_truncate_event_param.py new file mode 100644 index 0000000000..d3ad1e1e25 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_truncate_event_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemTruncateEventParam"] + + +class ConversationItemTruncateEventParam(TypedDict, total=False): + audio_end_ms: Required[int] + """Inclusive duration up to which audio is truncated, in milliseconds. + + If the audio_end_ms is greater than the actual audio duration, the server will + respond with an error. + """ + + content_index: Required[int] + """The index of the content part to truncate. Set this to 0.""" + + item_id: Required[str] + """The ID of the assistant message item to truncate. + + Only assistant message items can be truncated. + """ + + type: Required[Literal["conversation.item.truncate"]] + """The event type, must be `conversation.item.truncate`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/conversation_item_truncated_event.py b/src/openai/types/beta/realtime/conversation_item_truncated_event.py new file mode 100644 index 0000000000..36368fa28f --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_truncated_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemTruncatedEvent"] + + +class ConversationItemTruncatedEvent(BaseModel): + audio_end_ms: int + """The duration up to which the audio was truncated, in milliseconds.""" + + content_index: int + """The index of the content part that was truncated.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the assistant message item that was truncated.""" + + type: Literal["conversation.item.truncated"] + """The event type, must be `conversation.item.truncated`.""" diff --git a/src/openai/types/beta/realtime/error_event.py b/src/openai/types/beta/realtime/error_event.py new file mode 100644 index 0000000000..e020fc3848 --- /dev/null +++ b/src/openai/types/beta/realtime/error_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ErrorEvent", "Error"] + + +class Error(BaseModel): + message: str + """A human-readable error message.""" + + type: str + """The type of error (e.g., "invalid_request_error", "server_error").""" + + code: Optional[str] = None + """Error code, if any.""" + + event_id: Optional[str] = None + """The event_id of the client event that caused the error, if applicable.""" + + param: Optional[str] = None + """Parameter related to the error, if any.""" + + +class ErrorEvent(BaseModel): + error: Error + """Details of the error.""" + + event_id: str + """The unique ID of the server event.""" + + type: Literal["error"] + """The event type, must be `error`.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_append_event.py b/src/openai/types/beta/realtime/input_audio_buffer_append_event.py new file mode 100644 index 0000000000..a253a6488c --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_append_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferAppendEvent"] + + +class InputAudioBufferAppendEvent(BaseModel): + audio: str + """Base64-encoded audio bytes. + + This must be in the format specified by the `input_audio_format` field in the + session configuration. + """ + + type: Literal["input_audio_buffer.append"] + """The event type, must be `input_audio_buffer.append`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_append_event_param.py b/src/openai/types/beta/realtime/input_audio_buffer_append_event_param.py new file mode 100644 index 0000000000..3ad0bc737d --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_append_event_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferAppendEventParam"] + + +class InputAudioBufferAppendEventParam(TypedDict, total=False): + audio: Required[str] + """Base64-encoded audio bytes. + + This must be in the format specified by the `input_audio_format` field in the + session configuration. + """ + + type: Required[Literal["input_audio_buffer.append"]] + """The event type, must be `input_audio_buffer.append`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_clear_event.py b/src/openai/types/beta/realtime/input_audio_buffer_clear_event.py new file mode 100644 index 0000000000..b0624d34df --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_clear_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferClearEvent"] + + +class InputAudioBufferClearEvent(BaseModel): + type: Literal["input_audio_buffer.clear"] + """The event type, must be `input_audio_buffer.clear`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py b/src/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py new file mode 100644 index 0000000000..2bd6bc5a02 --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferClearEventParam"] + + +class InputAudioBufferClearEventParam(TypedDict, total=False): + type: Required[Literal["input_audio_buffer.clear"]] + """The event type, must be `input_audio_buffer.clear`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_cleared_event.py b/src/openai/types/beta/realtime/input_audio_buffer_cleared_event.py new file mode 100644 index 0000000000..632e1b94bc --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_cleared_event.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferClearedEvent"] + + +class InputAudioBufferClearedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + type: Literal["input_audio_buffer.cleared"] + """The event type, must be `input_audio_buffer.cleared`.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_commit_event.py b/src/openai/types/beta/realtime/input_audio_buffer_commit_event.py new file mode 100644 index 0000000000..7b6f5e46b7 --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_commit_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferCommitEvent"] + + +class InputAudioBufferCommitEvent(BaseModel): + type: Literal["input_audio_buffer.commit"] + """The event type, must be `input_audio_buffer.commit`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py b/src/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py new file mode 100644 index 0000000000..c9c927ab98 --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferCommitEventParam"] + + +class InputAudioBufferCommitEventParam(TypedDict, total=False): + type: Required[Literal["input_audio_buffer.commit"]] + """The event type, must be `input_audio_buffer.commit`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py b/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py new file mode 100644 index 0000000000..3071eff357 --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_committed_event.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__ = ["InputAudioBufferCommittedEvent"] + + +class InputAudioBufferCommittedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created.""" + + previous_item_id: str + """The ID of the preceding item after which the new item will be inserted.""" + + type: Literal["input_audio_buffer.committed"] + """The event type, must be `input_audio_buffer.committed`.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py b/src/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py new file mode 100644 index 0000000000..4f3ab082c4 --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferSpeechStartedEvent"] + + +class InputAudioBufferSpeechStartedEvent(BaseModel): + audio_start_ms: int + """ + Milliseconds from the start of all audio written to the buffer during the + session when speech was first detected. This will correspond to the beginning of + audio sent to the model, and thus includes the `prefix_padding_ms` configured in + the Session. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created when speech stops.""" + + type: Literal["input_audio_buffer.speech_started"] + """The event type, must be `input_audio_buffer.speech_started`.""" diff --git a/src/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py b/src/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py new file mode 100644 index 0000000000..40568170f2 --- /dev/null +++ b/src/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputAudioBufferSpeechStoppedEvent"] + + +class InputAudioBufferSpeechStoppedEvent(BaseModel): + audio_end_ms: int + """Milliseconds since the session started when speech stopped. + + This will correspond to the end of audio sent to the model, and thus includes + the `min_silence_duration_ms` configured in the Session. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created.""" + + type: Literal["input_audio_buffer.speech_stopped"] + """The event type, must be `input_audio_buffer.speech_stopped`.""" diff --git a/src/openai/types/beta/realtime/rate_limits_updated_event.py b/src/openai/types/beta/realtime/rate_limits_updated_event.py new file mode 100644 index 0000000000..7e12283c46 --- /dev/null +++ b/src/openai/types/beta/realtime/rate_limits_updated_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RateLimitsUpdatedEvent", "RateLimit"] + + +class RateLimit(BaseModel): + limit: Optional[int] = None + """The maximum allowed value for the rate limit.""" + + name: Optional[Literal["requests", "tokens"]] = None + """The name of the rate limit (`requests`, `tokens`).""" + + remaining: Optional[int] = None + """The remaining value before the limit is reached.""" + + reset_seconds: Optional[float] = None + """Seconds until the rate limit resets.""" + + +class RateLimitsUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + rate_limits: List[RateLimit] + """List of rate limit information.""" + + type: Literal["rate_limits.updated"] + """The event type, must be `rate_limits.updated`.""" diff --git a/src/openai/types/beta/realtime/realtime_client_event.py b/src/openai/types/beta/realtime/realtime_client_event.py new file mode 100644 index 0000000000..0769184cd0 --- /dev/null +++ b/src/openai/types/beta/realtime/realtime_client_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ...._utils import PropertyInfo +from .session_update_event import SessionUpdateEvent +from .response_cancel_event import ResponseCancelEvent +from .response_create_event import ResponseCreateEvent +from .conversation_item_create_event import ConversationItemCreateEvent +from .conversation_item_delete_event import ConversationItemDeleteEvent +from .input_audio_buffer_clear_event import InputAudioBufferClearEvent +from .input_audio_buffer_append_event import InputAudioBufferAppendEvent +from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent +from .conversation_item_truncate_event import ConversationItemTruncateEvent + +__all__ = ["RealtimeClientEvent"] + +RealtimeClientEvent: TypeAlias = Annotated[ + Union[ + SessionUpdateEvent, + InputAudioBufferAppendEvent, + InputAudioBufferCommitEvent, + InputAudioBufferClearEvent, + ConversationItemCreateEvent, + ConversationItemTruncateEvent, + ConversationItemDeleteEvent, + ResponseCreateEvent, + ResponseCancelEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/realtime/realtime_client_event_param.py b/src/openai/types/beta/realtime/realtime_client_event_param.py new file mode 100644 index 0000000000..4020892c33 --- /dev/null +++ b/src/openai/types/beta/realtime/realtime_client_event_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .session_update_event_param import SessionUpdateEventParam +from .response_cancel_event_param import ResponseCancelEventParam +from .response_create_event_param import ResponseCreateEventParam +from .conversation_item_create_event_param import ConversationItemCreateEventParam +from .conversation_item_delete_event_param import ConversationItemDeleteEventParam +from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam +from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam +from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam +from .conversation_item_truncate_event_param import ConversationItemTruncateEventParam + +__all__ = ["RealtimeClientEventParam"] + +RealtimeClientEventParam: TypeAlias = Union[ + SessionUpdateEventParam, + InputAudioBufferAppendEventParam, + InputAudioBufferCommitEventParam, + InputAudioBufferClearEventParam, + ConversationItemCreateEventParam, + ConversationItemTruncateEventParam, + ConversationItemDeleteEventParam, + ResponseCreateEventParam, + ResponseCancelEventParam, +] diff --git a/src/openai/types/beta/realtime/realtime_connect_params.py b/src/openai/types/beta/realtime/realtime_connect_params.py new file mode 100644 index 0000000000..76474f3de4 --- /dev/null +++ b/src/openai/types/beta/realtime/realtime_connect_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RealtimeConnectParams"] + + +class RealtimeConnectParams(TypedDict, total=False): + model: Required[str] diff --git a/src/openai/types/beta/realtime/realtime_response.py b/src/openai/types/beta/realtime/realtime_response.py new file mode 100644 index 0000000000..3e1b1406c0 --- /dev/null +++ b/src/openai/types/beta/realtime/realtime_response.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem +from .realtime_response_usage import RealtimeResponseUsage +from .realtime_response_status import RealtimeResponseStatus + +__all__ = ["RealtimeResponse"] + + +class RealtimeResponse(BaseModel): + id: Optional[str] = None + """The unique ID of the response.""" + + metadata: Optional[object] = None + """Developer-provided string key-value pairs associated with this response.""" + + object: Optional[Literal["realtime.response"]] = None + """The object type, must be `realtime.response`.""" + + output: Optional[List[ConversationItem]] = None + """The list of output items generated by the response.""" + + status: Optional[Literal["completed", "cancelled", "failed", "incomplete"]] = None + """ + The final status of the response (`completed`, `cancelled`, `failed`, or + `incomplete`). + """ + + status_details: Optional[RealtimeResponseStatus] = None + """Additional details about the status.""" + + usage: Optional[RealtimeResponseUsage] = None + """Usage statistics for the Response, this will correspond to billing. + + A Realtime API session will maintain a conversation context and append new Items + to the Conversation, thus output from previous turns (text and audio tokens) + will become the input for later turns. + """ diff --git a/src/openai/types/beta/realtime/realtime_response_status.py b/src/openai/types/beta/realtime/realtime_response_status.py new file mode 100644 index 0000000000..7189cd58a1 --- /dev/null +++ b/src/openai/types/beta/realtime/realtime_response_status.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RealtimeResponseStatus", "Error"] + + +class Error(BaseModel): + code: Optional[str] = None + """Error code, if any.""" + + type: Optional[str] = None + """The type of error.""" + + +class RealtimeResponseStatus(BaseModel): + error: Optional[Error] = None + """ + A description of the error that caused the response to fail, populated when the + `status` is `failed`. + """ + + reason: Optional[Literal["turn_detected", "client_cancelled", "max_output_tokens", "content_filter"]] = None + """The reason the Response did not complete. + + For a `cancelled` Response, one of `turn_detected` (the server VAD detected a + new start of speech) or `client_cancelled` (the client sent a cancel event). For + an `incomplete` Response, one of `max_output_tokens` or `content_filter` (the + server-side safety filter activated and cut off the response). + """ + + type: Optional[Literal["completed", "cancelled", "incomplete", "failed"]] = None + """ + The type of error that caused the response to fail, corresponding with the + `status` field (`completed`, `cancelled`, `incomplete`, `failed`). + """ diff --git a/src/openai/types/beta/realtime/realtime_response_usage.py b/src/openai/types/beta/realtime/realtime_response_usage.py new file mode 100644 index 0000000000..7ca822e25e --- /dev/null +++ b/src/openai/types/beta/realtime/realtime_response_usage.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["RealtimeResponseUsage", "InputTokenDetails", "OutputTokenDetails"] + + +class InputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """The number of audio tokens used in the Response.""" + + cached_tokens: Optional[int] = None + """The number of cached tokens used in the Response.""" + + text_tokens: Optional[int] = None + """The number of text tokens used in the Response.""" + + +class OutputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """The number of audio tokens used in the Response.""" + + text_tokens: Optional[int] = None + """The number of text tokens used in the Response.""" + + +class RealtimeResponseUsage(BaseModel): + input_token_details: Optional[InputTokenDetails] = None + """Details about the input tokens used in the Response.""" + + input_tokens: Optional[int] = None + """ + The number of input tokens used in the Response, including text and audio + tokens. + """ + + output_token_details: Optional[OutputTokenDetails] = None + """Details about the output tokens used in the Response.""" + + output_tokens: Optional[int] = None + """ + The number of output tokens sent in the Response, including text and audio + tokens. + """ + + total_tokens: Optional[int] = None + """ + The total number of tokens in the Response including input and output text and + audio tokens. + """ diff --git a/src/openai/types/beta/realtime/realtime_server_event.py b/src/openai/types/beta/realtime/realtime_server_event.py new file mode 100644 index 0000000000..5f8ed55b13 --- /dev/null +++ b/src/openai/types/beta/realtime/realtime_server_event.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ...._utils import PropertyInfo +from .error_event import ErrorEvent +from .response_done_event import ResponseDoneEvent +from .session_created_event import SessionCreatedEvent +from .session_updated_event import SessionUpdatedEvent +from .response_created_event import ResponseCreatedEvent +from .response_text_done_event import ResponseTextDoneEvent +from .rate_limits_updated_event import RateLimitsUpdatedEvent +from .response_audio_done_event import ResponseAudioDoneEvent +from .response_text_delta_event import ResponseTextDeltaEvent +from .conversation_created_event import ConversationCreatedEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent +from .conversation_item_created_event import ConversationItemCreatedEvent +from .conversation_item_deleted_event import ConversationItemDeletedEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent +from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent +from .conversation_item_truncated_event import ConversationItemTruncatedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent +from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent +from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent +from .input_audio_buffer_speech_started_event import InputAudioBufferSpeechStartedEvent +from .input_audio_buffer_speech_stopped_event import InputAudioBufferSpeechStoppedEvent +from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .conversation_item_input_audio_transcription_failed_event import ConversationItemInputAudioTranscriptionFailedEvent +from .conversation_item_input_audio_transcription_completed_event import ( + ConversationItemInputAudioTranscriptionCompletedEvent, +) + +__all__ = ["RealtimeServerEvent"] + +RealtimeServerEvent: TypeAlias = Annotated[ + Union[ + ErrorEvent, + SessionCreatedEvent, + SessionUpdatedEvent, + ConversationCreatedEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferClearedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + ConversationItemCreatedEvent, + ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemTruncatedEvent, + ConversationItemDeletedEvent, + ResponseCreatedEvent, + ResponseDoneEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + RateLimitsUpdatedEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/realtime/response_audio_delta_event.py b/src/openai/types/beta/realtime/response_audio_delta_event.py new file mode 100644 index 0000000000..8e0128d942 --- /dev/null +++ b/src/openai/types/beta/realtime/response_audio_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseAudioDeltaEvent"] + + +class ResponseAudioDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """Base64-encoded audio data delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.audio.delta"] + """The event type, must be `response.audio.delta`.""" diff --git a/src/openai/types/beta/realtime/response_audio_done_event.py b/src/openai/types/beta/realtime/response_audio_done_event.py new file mode 100644 index 0000000000..68e78bc778 --- /dev/null +++ b/src/openai/types/beta/realtime/response_audio_done_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseAudioDoneEvent"] + + +class ResponseAudioDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.audio.done"] + """The event type, must be `response.audio.done`.""" diff --git a/src/openai/types/beta/realtime/response_audio_transcript_delta_event.py b/src/openai/types/beta/realtime/response_audio_transcript_delta_event.py new file mode 100644 index 0000000000..3609948d10 --- /dev/null +++ b/src/openai/types/beta/realtime/response_audio_transcript_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDeltaEvent"] + + +class ResponseAudioTranscriptDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """The transcript delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.audio_transcript.delta"] + """The event type, must be `response.audio_transcript.delta`.""" diff --git a/src/openai/types/beta/realtime/response_audio_transcript_done_event.py b/src/openai/types/beta/realtime/response_audio_transcript_done_event.py new file mode 100644 index 0000000000..4e4436a95f --- /dev/null +++ b/src/openai/types/beta/realtime/response_audio_transcript_done_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDoneEvent"] + + +class ResponseAudioTranscriptDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + transcript: str + """The final transcript of the audio.""" + + type: Literal["response.audio_transcript.done"] + """The event type, must be `response.audio_transcript.done`.""" diff --git a/src/openai/types/beta/realtime/response_cancel_event.py b/src/openai/types/beta/realtime/response_cancel_event.py new file mode 100644 index 0000000000..c5ff991e9a --- /dev/null +++ b/src/openai/types/beta/realtime/response_cancel_event.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseCancelEvent"] + + +class ResponseCancelEvent(BaseModel): + type: Literal["response.cancel"] + """The event type, must be `response.cancel`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + response_id: Optional[str] = None + """ + A specific response ID to cancel - if not provided, will cancel an in-progress + response in the default conversation. + """ diff --git a/src/openai/types/beta/realtime/response_cancel_event_param.py b/src/openai/types/beta/realtime/response_cancel_event_param.py new file mode 100644 index 0000000000..f33740730a --- /dev/null +++ b/src/openai/types/beta/realtime/response_cancel_event_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCancelEventParam"] + + +class ResponseCancelEventParam(TypedDict, total=False): + type: Required[Literal["response.cancel"]] + """The event type, must be `response.cancel`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + response_id: str + """ + A specific response ID to cancel - if not provided, will cancel an in-progress + response in the default conversation. + """ diff --git a/src/openai/types/beta/realtime/response_content_part_added_event.py b/src/openai/types/beta/realtime/response_content_part_added_event.py new file mode 100644 index 0000000000..45c8f20f97 --- /dev/null +++ b/src/openai/types/beta/realtime/response_content_part_added_event.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseContentPartAddedEvent", "Part"] + + +class Part(BaseModel): + audio: Optional[str] = None + """Base64-encoded audio data (if type is "audio").""" + + text: Optional[str] = None + """The text content (if type is "text").""" + + transcript: Optional[str] = None + """The transcript of the audio (if type is "audio").""" + + type: Optional[Literal["text", "audio"]] = None + """The content type ("text", "audio").""" + + +class ResponseContentPartAddedEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item to which the content part was added.""" + + output_index: int + """The index of the output item in the response.""" + + part: Part + """The content part that was added.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.content_part.added"] + """The event type, must be `response.content_part.added`.""" diff --git a/src/openai/types/beta/realtime/response_content_part_done_event.py b/src/openai/types/beta/realtime/response_content_part_done_event.py new file mode 100644 index 0000000000..3d16116106 --- /dev/null +++ b/src/openai/types/beta/realtime/response_content_part_done_event.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseContentPartDoneEvent", "Part"] + + +class Part(BaseModel): + audio: Optional[str] = None + """Base64-encoded audio data (if type is "audio").""" + + text: Optional[str] = None + """The text content (if type is "text").""" + + transcript: Optional[str] = None + """The transcript of the audio (if type is "audio").""" + + type: Optional[Literal["text", "audio"]] = None + """The content type ("text", "audio").""" + + +class ResponseContentPartDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + part: Part + """The content part that is done.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.content_part.done"] + """The event type, must be `response.content_part.done`.""" diff --git a/src/openai/types/beta/realtime/response_create_event.py b/src/openai/types/beta/realtime/response_create_event.py new file mode 100644 index 0000000000..00ba1e5dad --- /dev/null +++ b/src/openai/types/beta/realtime/response_create_event.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseCreateEvent", "Response", "ResponseTool"] + + +class ResponseTool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class Response(BaseModel): + conversation: Union[str, Literal["auto", "none"], None] = None + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Optional[List[ConversationItem]] = None + """Input items to include in the prompt for the model. + + Creates a new context for this response, without including the default + conversation. Can include references to items from the default conversation. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[object] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maximum of 512 characters long. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[ResponseTool]] = None + """Tools (functions) available to the model.""" + + voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo` `sage`, `shimmer` and `verse`. + """ + + +class ResponseCreateEvent(BaseModel): + type: Literal["response.create"] + """The event type, must be `response.create`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + response: Optional[Response] = None + """Create a new Realtime response with these parameters""" diff --git a/src/openai/types/beta/realtime/response_create_event_param.py b/src/openai/types/beta/realtime/response_create_event_param.py new file mode 100644 index 0000000000..7c92b32df1 --- /dev/null +++ b/src/openai/types/beta/realtime/response_create_event_param.py @@ -0,0 +1,116 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .conversation_item_param import ConversationItemParam + +__all__ = ["ResponseCreateEventParam", "Response", "ResponseTool"] + + +class ResponseTool(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class Response(TypedDict, total=False): + conversation: Union[str, Literal["auto", "none"]] + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Iterable[ConversationItemParam] + """Input items to include in the prompt for the model. + + Creates a new context for this response, without including the default + conversation. Can include references to items from the default conversation. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[object] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format. Keys can be a maximum of 64 characters long and values can be + a maximum of 512 characters long. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: float + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: str + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Iterable[ResponseTool] + """Tools (functions) available to the model.""" + + voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo` `sage`, `shimmer` and `verse`. + """ + + +class ResponseCreateEventParam(TypedDict, total=False): + type: Required[Literal["response.create"]] + """The event type, must be `response.create`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + response: Response + """Create a new Realtime response with these parameters""" diff --git a/src/openai/types/beta/realtime/response_created_event.py b/src/openai/types/beta/realtime/response_created_event.py new file mode 100644 index 0000000000..a4990cf095 --- /dev/null +++ b/src/openai/types/beta/realtime/response_created_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .realtime_response import RealtimeResponse + +__all__ = ["ResponseCreatedEvent"] + + +class ResponseCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response: RealtimeResponse + """The response resource.""" + + type: Literal["response.created"] + """The event type, must be `response.created`.""" diff --git a/src/openai/types/beta/realtime/response_done_event.py b/src/openai/types/beta/realtime/response_done_event.py new file mode 100644 index 0000000000..9e655184b6 --- /dev/null +++ b/src/openai/types/beta/realtime/response_done_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .realtime_response import RealtimeResponse + +__all__ = ["ResponseDoneEvent"] + + +class ResponseDoneEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response: RealtimeResponse + """The response resource.""" + + type: Literal["response.done"] + """The event type, must be `response.done`.""" diff --git a/src/openai/types/beta/realtime/response_function_call_arguments_delta_event.py b/src/openai/types/beta/realtime/response_function_call_arguments_delta_event.py new file mode 100644 index 0000000000..cdbb64e658 --- /dev/null +++ b/src/openai/types/beta/realtime/response_function_call_arguments_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDeltaEvent"] + + +class ResponseFunctionCallArgumentsDeltaEvent(BaseModel): + call_id: str + """The ID of the function call.""" + + delta: str + """The arguments delta as a JSON string.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the function call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.function_call_arguments.delta"] + """The event type, must be `response.function_call_arguments.delta`.""" diff --git a/src/openai/types/beta/realtime/response_function_call_arguments_done_event.py b/src/openai/types/beta/realtime/response_function_call_arguments_done_event.py new file mode 100644 index 0000000000..0a5db53323 --- /dev/null +++ b/src/openai/types/beta/realtime/response_function_call_arguments_done_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDoneEvent"] + + +class ResponseFunctionCallArgumentsDoneEvent(BaseModel): + arguments: str + """The final arguments as a JSON string.""" + + call_id: str + """The ID of the function call.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the function call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.function_call_arguments.done"] + """The event type, must be `response.function_call_arguments.done`.""" diff --git a/src/openai/types/beta/realtime/response_output_item_added_event.py b/src/openai/types/beta/realtime/response_output_item_added_event.py new file mode 100644 index 0000000000..c89bfdc3be --- /dev/null +++ b/src/openai/types/beta/realtime/response_output_item_added_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseOutputItemAddedEvent"] + + +class ResponseOutputItemAddedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """The item to add to the conversation.""" + + output_index: int + """The index of the output item in the Response.""" + + response_id: str + """The ID of the Response to which the item belongs.""" + + type: Literal["response.output_item.added"] + """The event type, must be `response.output_item.added`.""" diff --git a/src/openai/types/beta/realtime/response_output_item_done_event.py b/src/openai/types/beta/realtime/response_output_item_done_event.py new file mode 100644 index 0000000000..b5910e22aa --- /dev/null +++ b/src/openai/types/beta/realtime/response_output_item_done_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseOutputItemDoneEvent"] + + +class ResponseOutputItemDoneEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """The item to add to the conversation.""" + + output_index: int + """The index of the output item in the Response.""" + + response_id: str + """The ID of the Response to which the item belongs.""" + + type: Literal["response.output_item.done"] + """The event type, must be `response.output_item.done`.""" diff --git a/src/openai/types/beta/realtime/response_text_delta_event.py b/src/openai/types/beta/realtime/response_text_delta_event.py new file mode 100644 index 0000000000..c463b3c3d0 --- /dev/null +++ b/src/openai/types/beta/realtime/response_text_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseTextDeltaEvent"] + + +class ResponseTextDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """The text delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.text.delta"] + """The event type, must be `response.text.delta`.""" diff --git a/src/openai/types/beta/realtime/response_text_done_event.py b/src/openai/types/beta/realtime/response_text_done_event.py new file mode 100644 index 0000000000..020ff41d58 --- /dev/null +++ b/src/openai/types/beta/realtime/response_text_done_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ResponseTextDoneEvent"] + + +class ResponseTextDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + text: str + """The final text content.""" + + type: Literal["response.text.done"] + """The event type, must be `response.text.done`.""" diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py new file mode 100644 index 0000000000..09cdbb02bc --- /dev/null +++ b/src/openai/types/beta/realtime/session.py @@ -0,0 +1,148 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Session", "InputAudioTranscription", "Tool", "TurnDetection"] + + +class InputAudioTranscription(BaseModel): + model: Optional[str] = None + """ + The model to use for transcription, `whisper-1` is the only currently supported + model. + """ + + +class Tool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class TurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[Literal["server_vad"]] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class Session(BaseModel): + id: Optional[str] = None + """Unique identifier for the session object.""" + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: Optional[InputAudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through Whisper and should be treated as rough guidance rather + than the representation understood by the model. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + model: Union[ + str, + Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + None, + ] = None + """The Realtime model used for this session.""" + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[Tool]] = None + """Tools (functions) available to the model.""" + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ + + voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo` `sage`, `shimmer` and `verse`. + """ diff --git a/src/openai/types/beta/realtime/session_created_event.py b/src/openai/types/beta/realtime/session_created_event.py new file mode 100644 index 0000000000..baf6af388b --- /dev/null +++ b/src/openai/types/beta/realtime/session_created_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .session import Session +from ...._models import BaseModel + +__all__ = ["SessionCreatedEvent"] + + +class SessionCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: Session + """Realtime session object configuration.""" + + type: Literal["session.created"] + """The event type, must be `session.created`.""" diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py new file mode 100644 index 0000000000..c04220aa25 --- /dev/null +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -0,0 +1,158 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["SessionUpdateEvent", "Session", "SessionInputAudioTranscription", "SessionTool", "SessionTurnDetection"] + + +class SessionInputAudioTranscription(BaseModel): + model: Optional[str] = None + """ + The model to use for transcription, `whisper-1` is the only currently supported + model. + """ + + +class SessionTool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class SessionTurnDetection(BaseModel): + create_response: Optional[bool] = None + """Whether or not to automatically generate a response when VAD is enabled. + + `true` by default. + """ + + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class Session(BaseModel): + model: Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + """The Realtime model used for this session.""" + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: Optional[SessionInputAudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through Whisper and should be treated as rough guidance rather + than the representation understood by the model. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[SessionTool]] = None + """Tools (functions) available to the model.""" + + turn_detection: Optional[SessionTurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ + + voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo` `sage`, `shimmer` and `verse`. + """ + + +class SessionUpdateEvent(BaseModel): + session: Session + """Realtime session object configuration.""" + + type: Literal["session.update"] + """The event type, must be `session.update`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py new file mode 100644 index 0000000000..aa06069b04 --- /dev/null +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -0,0 +1,166 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "SessionUpdateEventParam", + "Session", + "SessionInputAudioTranscription", + "SessionTool", + "SessionTurnDetection", +] + + +class SessionInputAudioTranscription(TypedDict, total=False): + model: str + """ + The model to use for transcription, `whisper-1` is the only currently supported + model. + """ + + +class SessionTool(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class SessionTurnDetection(TypedDict, total=False): + create_response: bool + """Whether or not to automatically generate a response when VAD is enabled. + + `true` by default. + """ + + prefix_padding_ms: int + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: int + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: float + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: str + """Type of turn detection, only `server_vad` is currently supported.""" + + +class Session(TypedDict, total=False): + model: Required[ + Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + ] + """The Realtime model used for this session.""" + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: SessionInputAudioTranscription + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through Whisper and should be treated as rough guidance rather + than the representation understood by the model. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + temperature: float + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: str + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Iterable[SessionTool] + """Tools (functions) available to the model.""" + + turn_detection: SessionTurnDetection + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ + + voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo` `sage`, `shimmer` and `verse`. + """ + + +class SessionUpdateEventParam(TypedDict, total=False): + session: Required[Session] + """Realtime session object configuration.""" + + type: Required[Literal["session.update"]] + """The event type, must be `session.update`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/session_updated_event.py b/src/openai/types/beta/realtime/session_updated_event.py new file mode 100644 index 0000000000..b9b6488eb3 --- /dev/null +++ b/src/openai/types/beta/realtime/session_updated_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .session import Session +from ...._models import BaseModel + +__all__ = ["SessionUpdatedEvent"] + + +class SessionUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: Session + """Realtime session object configuration.""" + + type: Literal["session.updated"] + """The event type, must be `session.updated`.""" diff --git a/src/openai/types/websocket_connection_options.py b/src/openai/types/websocket_connection_options.py new file mode 100644 index 0000000000..40fd24ab03 --- /dev/null +++ b/src/openai/types/websocket_connection_options.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import TYPE_CHECKING +from typing_extensions import Sequence, TypedDict + +if TYPE_CHECKING: + from websockets import Subprotocol + from websockets.extensions import ClientExtensionFactory + + +class WebsocketConnectionOptions(TypedDict, total=False): + """Websocket connection options copied from `websockets`. + + For example: https://websockets.readthedocs.io/en/stable/reference/asyncio/client.html#websockets.asyncio.client.connect + """ + + extensions: Sequence[ClientExtensionFactory] | None + """List of supported extensions, in order in which they should be negotiated and run.""" + + subprotocols: Sequence[Subprotocol] | None + """List of supported subprotocols, in order of decreasing preference.""" + + compression: str | None + """The “permessage-deflate” extension is enabled by default. Set compression to None to disable it. See the [compression guide](https://websockets.readthedocs.io/en/stable/topics/compression.html) for details.""" + + # limits + max_size: int | None + """Maximum size of incoming messages in bytes. None disables the limit.""" + + max_queue: int | None | tuple[int | None, int | None] + """High-water mark of the buffer where frames are received. It defaults to 16 frames. The low-water mark defaults to max_queue // 4. You may pass a (high, low) tuple to set the high-water and low-water marks. If you want to disable flow control entirely, you may set it to None, although that’s a bad idea.""" + + write_limit: int | tuple[int, int | None] + """High-water mark of write buffer in bytes. It is passed to set_write_buffer_limits(). It defaults to 32 KiB. You may pass a (high, low) tuple to set the high-water and low-water marks.""" diff --git a/tests/api_resources/beta/test_realtime.py b/tests/api_resources/beta/test_realtime.py new file mode 100644 index 0000000000..537017ffd3 --- /dev/null +++ b/tests/api_resources/beta/test_realtime.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os + +import pytest + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestRealtime: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + +class TestAsyncRealtime: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) From a6a94e08741acdfc9b371dc4c47cbc7b8613cd88 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 17 Dec 2024 18:06:18 +0000 Subject: [PATCH 712/914] fix: add reasoning_effort to all methods --- src/openai/resources/beta/chat/completions.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 38c09ce8dd..48cb13f7a6 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -15,7 +15,10 @@ from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...._streaming import Stream -from ....types.chat import completion_create_params +from ....types.chat import ( + ChatCompletionReasoningEffort, + completion_create_params, +) from ...._base_client import make_request_options from ....lib._parsing import ( ResponseFormatT, @@ -79,6 +82,7 @@ def parse( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, @@ -173,6 +177,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, "presence_penalty": presence_penalty, + "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), "seed": seed, "service_tier": service_tier, @@ -222,6 +227,7 @@ def stream( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, @@ -287,6 +293,7 @@ def stream( parallel_tool_calls=parallel_tool_calls, prediction=prediction, presence_penalty=presence_penalty, + reasoning_effort=reasoning_effort, seed=seed, service_tier=service_tier, store=store, @@ -350,6 +357,7 @@ async def parse( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, @@ -444,6 +452,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, "presence_penalty": presence_penalty, + "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), "seed": seed, "service_tier": service_tier, @@ -493,6 +502,7 @@ def stream( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, @@ -559,6 +569,7 @@ def stream( parallel_tool_calls=parallel_tool_calls, prediction=prediction, presence_penalty=presence_penalty, + reasoning_effort=reasoning_effort, seed=seed, service_tier=service_tier, stop=stop, From 488ec04b7b79d3a5597c910fd585df18e922665f Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 16 Dec 2024 19:16:25 +0000 Subject: [PATCH 713/914] docs: add examples + guidance on Realtime API support --- README.md | 61 ++++++ examples/realtime/audio_util.py | 142 +++++++++++++ examples/realtime/push_to_talk_app.py | 281 ++++++++++++++++++++++++++ mypy.ini | 5 +- pyproject.toml | 5 + 5 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 examples/realtime/audio_util.py create mode 100755 examples/realtime/push_to_talk_app.py diff --git a/README.md b/README.md index cbcfdb4447..4c3ba87c97 100644 --- a/README.md +++ b/README.md @@ -258,6 +258,67 @@ We recommend that you always instantiate a client (e.g., with `client = OpenAI() - It's harder to mock for testing purposes - It's not possible to control cleanup of network connections +## Realtime API beta + +The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as [function calling](https://platform.openai.com/docs/guides/function-calling) through a WebSocket connection. + +Under the hood the SDK uses the [`websockets`](https://websockets.readthedocs.io/en/stable/) library to manage connections. + +The Realtime API works through a combination of client-sent events and server-sent events. Clients can send events to do things like update session configuration or send text and audio inputs. Server events confirm when audio responses have completed, or when a text response from the model has been received. A full event reference can be found [here](platform.openai.com/docs/api-reference/realtime-client-events) and a guide can be found [here](https://platform.openai.com/docs/guides/realtime). + +Basic text based example: + +```py +import asyncio +from openai import AsyncOpenAI + +async def main(): + client = AsyncOpenAI() + + async with client.beta.realtime.connect(model="gpt-4o-realtime-preview-2024-10-01") as connection: + await connection.session.update(session={'modalities': ['text']}) + + await connection.conversation.item.create( + item={ + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "Say hello!"}], + } + ) + await connection.response.create() + + async for event in connection: + if event.type == 'response.text.delta': + print(event.delta, flush=True, end="") + + elif event.type == 'response.text.done': + print() + + elif event.type == "response.done": + break + +asyncio.run(main()) +``` + +However the real magic of the Realtime API is handling audio inputs / outputs, see this example [TUI script](https://github.com/stainless-sdks/openai-python/blob/robert/realtime-docs-preview/examples/realtime/push_to_talk_app.py) for a fully fledged example. + +### Realtime error handling + +Whenever an error occurs, the Realtime API will send an [`error` event](https://platform.openai.com/docs/guides/realtime/realtime-api-beta#handling-errors) and the connection will stay open and remain usable. This means you need to handle it yourself, as *no errors are raised directly* by the SDK when an `error` event comes in. + +```py +client = AsyncOpenAI() + +async with client.beta.realtime.connect(model="gpt-4o-realtime-preview-2024-10-01") as connection: + ... + async for event in connection: + if event.type == 'error': + print(event.error.type) + print(event.error.code) + print(event.error.event_id) + print(event.error.message) +``` + ## Using types Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: diff --git a/examples/realtime/audio_util.py b/examples/realtime/audio_util.py new file mode 100644 index 0000000000..b073cc45be --- /dev/null +++ b/examples/realtime/audio_util.py @@ -0,0 +1,142 @@ +from __future__ import annotations + +import io +import base64 +import asyncio +import threading +from typing import Callable, Awaitable + +import numpy as np +import pyaudio +import sounddevice as sd +from pydub import AudioSegment + +from openai.resources.beta.realtime.realtime import AsyncRealtimeConnection + +CHUNK_LENGTH_S = 0.05 # 100ms +SAMPLE_RATE = 24000 +FORMAT = pyaudio.paInt16 +CHANNELS = 1 + +# pyright: reportUnknownMemberType=false, reportUnknownVariableType=false, reportUnknownArgumentType=false + + +def audio_to_pcm16_base64(audio_bytes: bytes) -> bytes: + # load the audio file from the byte stream + audio = AudioSegment.from_file(io.BytesIO(audio_bytes)) + print(f"Loaded audio: {audio.frame_rate=} {audio.channels=} {audio.sample_width=} {audio.frame_width=}") + # resample to 24kHz mono pcm16 + pcm_audio = audio.set_frame_rate(SAMPLE_RATE).set_channels(CHANNELS).set_sample_width(2).raw_data + return pcm_audio + + +class AudioPlayerAsync: + def __init__(self): + self.queue = [] + self.lock = threading.Lock() + self.stream = sd.OutputStream( + callback=self.callback, + samplerate=SAMPLE_RATE, + channels=CHANNELS, + dtype=np.int16, + blocksize=int(CHUNK_LENGTH_S * SAMPLE_RATE), + ) + self.playing = False + self._frame_count = 0 + + def callback(self, outdata, frames, time, status): # noqa + with self.lock: + data = np.empty(0, dtype=np.int16) + + # get next item from queue if there is still space in the buffer + while len(data) < frames and len(self.queue) > 0: + item = self.queue.pop(0) + frames_needed = frames - len(data) + data = np.concatenate((data, item[:frames_needed])) + if len(item) > frames_needed: + self.queue.insert(0, item[frames_needed:]) + + self._frame_count += len(data) + + # fill the rest of the frames with zeros if there is no more data + if len(data) < frames: + data = np.concatenate((data, np.zeros(frames - len(data), dtype=np.int16))) + + outdata[:] = data.reshape(-1, 1) + + def reset_frame_count(self): + self._frame_count = 0 + + def get_frame_count(self): + return self._frame_count + + def add_data(self, data: bytes): + with self.lock: + # bytes is pcm16 single channel audio data, convert to numpy array + np_data = np.frombuffer(data, dtype=np.int16) + self.queue.append(np_data) + if not self.playing: + self.start() + + def start(self): + self.playing = True + self.stream.start() + + def stop(self): + self.playing = False + self.stream.stop() + with self.lock: + self.queue = [] + + def terminate(self): + self.stream.close() + + +async def send_audio_worker_sounddevice( + connection: AsyncRealtimeConnection, + should_send: Callable[[], bool] | None = None, + start_send: Callable[[], Awaitable[None]] | None = None, +): + sent_audio = False + + device_info = sd.query_devices() + print(device_info) + + read_size = int(SAMPLE_RATE * 0.02) + + stream = sd.InputStream( + channels=CHANNELS, + samplerate=SAMPLE_RATE, + dtype="int16", + ) + stream.start() + + try: + while True: + if stream.read_available < read_size: + await asyncio.sleep(0) + continue + + data, _ = stream.read(read_size) + + if should_send() if should_send else True: + if not sent_audio and start_send: + await start_send() + await connection.send( + {"type": "input_audio_buffer.append", "audio": base64.b64encode(data).decode("utf-8")} + ) + sent_audio = True + + elif sent_audio: + print("Done, triggering inference") + await connection.send({"type": "input_audio_buffer.commit"}) + await connection.send({"type": "response.create", "response": {}}) + sent_audio = False + + await asyncio.sleep(0) + + except KeyboardInterrupt: + pass + finally: + stream.stop() + stream.close() diff --git a/examples/realtime/push_to_talk_app.py b/examples/realtime/push_to_talk_app.py new file mode 100755 index 0000000000..d46945a8ed --- /dev/null +++ b/examples/realtime/push_to_talk_app.py @@ -0,0 +1,281 @@ +#!/usr/bin/env uv run +#################################################################### +# Sample TUI app with a push to talk interface to the Realtime API # +# If you have `uv` installed and the `OPENAI_API_KEY` # +# environment variable set, you can run this example with just # +# # +# `./examples/realtime/push_to_talk_app.py` # +#################################################################### +# +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "textual", +# "numpy", +# "pyaudio", +# "pydub", +# "sounddevice", +# "openai[realtime]", +# ] +# +# [tool.uv.sources] +# openai = { path = "../../", editable = true } +# /// +from __future__ import annotations + +import base64 +import asyncio +from typing import Any, cast +from typing_extensions import override + +from textual import events +from audio_util import CHANNELS, SAMPLE_RATE, AudioPlayerAsync +from textual.app import App, ComposeResult +from textual.widgets import Button, Static, RichLog +from textual.reactive import reactive +from textual.containers import Container + +from openai import AsyncOpenAI +from openai.types.beta.realtime.session import Session +from openai.resources.beta.realtime.realtime import AsyncRealtimeConnection + + +class SessionDisplay(Static): + """A widget that shows the current session ID.""" + + session_id = reactive("") + + @override + def render(self) -> str: + return f"Session ID: {self.session_id}" if self.session_id else "Connecting..." + + +class AudioStatusIndicator(Static): + """A widget that shows the current audio recording status.""" + + is_recording = reactive(False) + + @override + def render(self) -> str: + status = ( + "🔴 Recording... (Press K to stop)" if self.is_recording else "⚪ Press K to start recording (Q to quit)" + ) + return status + + +class RealtimeApp(App[None]): + CSS = """ + Screen { + background: #1a1b26; /* Dark blue-grey background */ + } + + Container { + border: double rgb(91, 164, 91); + } + + Horizontal { + width: 100%; + } + + #input-container { + height: 5; /* Explicit height for input container */ + margin: 1 1; + padding: 1 2; + } + + Input { + width: 80%; + height: 3; /* Explicit height for input */ + } + + Button { + width: 20%; + height: 3; /* Explicit height for button */ + } + + #bottom-pane { + width: 100%; + height: 82%; /* Reduced to make room for session display */ + border: round rgb(205, 133, 63); + content-align: center middle; + } + + #status-indicator { + height: 3; + content-align: center middle; + background: #2a2b36; + border: solid rgb(91, 164, 91); + margin: 1 1; + } + + #session-display { + height: 3; + content-align: center middle; + background: #2a2b36; + border: solid rgb(91, 164, 91); + margin: 1 1; + } + + Static { + color: white; + } + """ + + client: AsyncOpenAI + should_send_audio: asyncio.Event + audio_player: AudioPlayerAsync + last_audio_item_id: str | None + connection: AsyncRealtimeConnection | None + session: Session | None + connected: asyncio.Event + + def __init__(self) -> None: + super().__init__() + self.connection = None + self.session = None + self.client = AsyncOpenAI() + self.audio_player = AudioPlayerAsync() + self.last_audio_item_id = None + self.should_send_audio = asyncio.Event() + self.connected = asyncio.Event() + + @override + def compose(self) -> ComposeResult: + """Create child widgets for the app.""" + with Container(): + yield SessionDisplay(id="session-display") + yield AudioStatusIndicator(id="status-indicator") + yield RichLog(id="bottom-pane", wrap=True, highlight=True, markup=True) + + async def on_mount(self) -> None: + self.run_worker(self.handle_realtime_connection()) + self.run_worker(self.send_mic_audio()) + + async def handle_realtime_connection(self) -> None: + async with self.client.beta.realtime.connect(model="gpt-4o-realtime-preview-2024-10-01") as conn: + self.connection = conn + self.connected.set() + + # note: this is the default and can be omitted + # if you want to manually handle VAD yourself, then set `'turn_detection': None` + await conn.session.update(session={"turn_detection": {"type": "server_vad"}}) + + acc_items: dict[str, Any] = {} + + async for event in conn: + if event.type == "session.created": + self.session = event.session + session_display = self.query_one(SessionDisplay) + assert event.session.id is not None + session_display.session_id = event.session.id + continue + + if event.type == "session.updated": + self.session = event.session + continue + + if event.type == "response.audio.delta": + if event.item_id != self.last_audio_item_id: + self.audio_player.reset_frame_count() + self.last_audio_item_id = event.item_id + + bytes_data = base64.b64decode(event.delta) + self.audio_player.add_data(bytes_data) + continue + + if event.type == "response.audio_transcript.delta": + try: + text = acc_items[event.item_id] + except KeyError: + acc_items[event.item_id] = event.delta + else: + acc_items[event.item_id] = text + event.delta + + # Clear and update the entire content because RichLog otherwise treats each delta as a new line + bottom_pane = self.query_one("#bottom-pane", RichLog) + bottom_pane.clear() + bottom_pane.write(acc_items[event.item_id]) + continue + + async def _get_connection(self) -> AsyncRealtimeConnection: + await self.connected.wait() + assert self.connection is not None + return self.connection + + async def send_mic_audio(self) -> None: + import sounddevice as sd # type: ignore + + sent_audio = False + + device_info = sd.query_devices() + print(device_info) + + read_size = int(SAMPLE_RATE * 0.02) + + stream = sd.InputStream( + channels=CHANNELS, + samplerate=SAMPLE_RATE, + dtype="int16", + ) + stream.start() + + status_indicator = self.query_one(AudioStatusIndicator) + + try: + while True: + if stream.read_available < read_size: + await asyncio.sleep(0) + continue + + await self.should_send_audio.wait() + status_indicator.is_recording = True + + data, _ = stream.read(read_size) + + connection = await self._get_connection() + if not sent_audio: + asyncio.create_task(connection.send({"type": "response.cancel"})) + sent_audio = True + + await connection.input_audio_buffer.append(audio=base64.b64encode(cast(Any, data)).decode("utf-8")) + + await asyncio.sleep(0) + except KeyboardInterrupt: + pass + finally: + stream.stop() + stream.close() + + async def on_key(self, event: events.Key) -> None: + """Handle key press events.""" + if event.key == "enter": + self.query_one(Button).press() + return + + if event.key == "q": + self.exit() + return + + if event.key == "k": + status_indicator = self.query_one(AudioStatusIndicator) + if status_indicator.is_recording: + self.should_send_audio.clear() + status_indicator.is_recording = False + + if self.session and self.session.turn_detection is None: + # The default in the API is that the model will automatically detect when the user has + # stopped talking and then start responding itself. + # + # However if we're in manual `turn_detection` mode then we need to + # manually tell the model to commit the audio buffer and start responding. + conn = await self._get_connection() + await conn.input_audio_buffer.commit() + await conn.response.create() + else: + self.should_send_audio.set() + status_indicator.is_recording = True + + +if __name__ == "__main__": + app = RealtimeApp() + app.run() diff --git a/mypy.ini b/mypy.ini index 50e5add04b..1ea1fe909d 100644 --- a/mypy.ini +++ b/mypy.ini @@ -8,7 +8,10 @@ show_error_codes = True # # We also exclude our `tests` as mypy doesn't always infer # types correctly and Pyright will still catch any type errors. -exclude = ^(src/openai/_files\.py|src/openai/_utils/_logs\.py|_dev/.*\.py|tests/.*)$ + +# realtime examples use inline `uv` script dependencies +# which means it can't be type checked +exclude = ^(src/openai/_files\.py|_dev/.*\.py|tests/.*|src/openai/_utils/_logs\.py|examples/realtime/audio_util\.py|examples/realtime/push_to_talk_app\.py)$ strict_equality = True implicit_reexport = True diff --git a/pyproject.toml b/pyproject.toml index f83aff6fee..8e78257e67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -157,6 +157,11 @@ exclude = [ "_dev", ".venv", ".nox", + + # uses inline `uv` script dependencies + # which means it can't be type checked + "examples/realtime/audio_util.py", + "examples/realtime/push_to_talk_app.py" ] reportImplicitOverride = true From 1b78f22ddf4b64542b39804882fca388a62355fb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:19:24 +0000 Subject: [PATCH 714/914] release: 1.58.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 27 +++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f9ae229e1a..452fa092bd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.57.4" + ".": "1.58.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b7d0271d..5699c0cec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 1.58.0 (2024-12-17) + +Full Changelog: [v1.57.4...v1.58.0](https://github.com/openai/openai-python/compare/v1.57.4...v1.58.0) + +### Features + +* add Realtime API support ([#1958](https://github.com/openai/openai-python/issues/1958)) ([97d73cf](https://github.com/openai/openai-python/commit/97d73cf89935ca6098bb889a92f0ec2cdff16989)) +* **api:** new o1 and GPT-4o models + preference fine-tuning ([#1956](https://github.com/openai/openai-python/issues/1956)) ([ec22ffb](https://github.com/openai/openai-python/commit/ec22ffb129c524525caa33b088405d27c271e631)) + + +### Bug Fixes + +* add reasoning_effort to all methods ([8829c32](https://github.com/openai/openai-python/commit/8829c3202dbe790ca3646476c802ec55ed47d864)) +* **assistants:** correctly send `include` query param ([9a4c69c](https://github.com/openai/openai-python/commit/9a4c69c383bc6719b6521a485f2c7e62a9c036a9)) +* **cli/migrate:** change grit binaries prefix ([#1951](https://github.com/openai/openai-python/issues/1951)) ([1c396c9](https://github.com/openai/openai-python/commit/1c396c95b040fb3d1a2523b09eaad4ff62d96846)) + + +### Chores + +* **internal:** fix some typos ([#1955](https://github.com/openai/openai-python/issues/1955)) ([628dead](https://github.com/openai/openai-python/commit/628dead660c00435bf46e09081c7b90b7bbe4a8a)) + + +### Documentation + +* add examples + guidance on Realtime API support ([1cb00f8](https://github.com/openai/openai-python/commit/1cb00f8fed78052aacbb9e0fac997b6ba0d44d2a)) +* **readme:** example snippet for client context manager ([#1953](https://github.com/openai/openai-python/issues/1953)) ([ad80255](https://github.com/openai/openai-python/commit/ad802551d8aaf4e6eff711118676ec4e64392638)) + ## 1.57.4 (2024-12-13) Full Changelog: [v1.57.3...v1.57.4](https://github.com/openai/openai-python/compare/v1.57.3...v1.57.4) diff --git a/pyproject.toml b/pyproject.toml index 8e78257e67..1f2c9fe6c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.57.4" +version = "1.58.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5b82015017..7f2f4cafb8 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.57.4" # x-release-please-version +__version__ = "1.58.0" # x-release-please-version From 6935dfdcce9195a26e9ea83597d0c4f5c7631254 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 17 Dec 2024 19:54:33 +0000 Subject: [PATCH 715/914] docs(readme): fix example script link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c3ba87c97..87837db175 100644 --- a/README.md +++ b/README.md @@ -300,7 +300,7 @@ async def main(): asyncio.run(main()) ``` -However the real magic of the Realtime API is handling audio inputs / outputs, see this example [TUI script](https://github.com/stainless-sdks/openai-python/blob/robert/realtime-docs-preview/examples/realtime/push_to_talk_app.py) for a fully fledged example. +However the real magic of the Realtime API is handling audio inputs / outputs, see this example [TUI script](https://github.com/openai/openai-python/blob/main/examples/realtime/push_to_talk_app.py) for a fully fledged example. ### Realtime error handling From 19ecaafeda91480d0dfd7ce44e7317220b9d48b6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:55:01 +0000 Subject: [PATCH 716/914] release: 1.58.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 452fa092bd..73a4167d2d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.58.0" + ".": "1.58.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5699c0cec2..6519747179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.58.1 (2024-12-17) + +Full Changelog: [v1.58.0...v1.58.1](https://github.com/openai/openai-python/compare/v1.58.0...v1.58.1) + +### Documentation + +* **readme:** fix example script link ([23ba877](https://github.com/openai/openai-python/commit/23ba8778fd55e0f54f36685e9c5950b452d8e10c)) + ## 1.58.0 (2024-12-17) Full Changelog: [v1.57.4...v1.58.0](https://github.com/openai/openai-python/compare/v1.57.4...v1.58.0) diff --git a/pyproject.toml b/pyproject.toml index 1f2c9fe6c4..fd55cf2b5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.58.0" +version = "1.58.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7f2f4cafb8..c08e68e11b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.58.0" # x-release-please-version +__version__ = "1.58.1" # x-release-please-version From cacd374b850407b211d1f1e7740da0cf4e4d68d1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:17:00 +0000 Subject: [PATCH 717/914] chore(realtime): update docstrings (#1964) --- .stats.yml | 2 +- src/openai/types/beta/realtime/conversation_item_content.py | 5 +++-- .../types/beta/realtime/conversation_item_content_param.py | 5 +++-- src/openai/types/beta/realtime/response_create_event.py | 3 ++- .../types/beta/realtime/response_create_event_param.py | 3 ++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.stats.yml b/.stats.yml index 12219ccaa1..1a7a7a5269 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-0d64ca9e45f51b4279f87b205eeb3a3576df98407698ce053f2e2302c1c08df1.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-a39aca84ed97ebafb707ebd5221e2787c5a42ff3d98f2ffaea8a0dcd84cbcbcb.yml diff --git a/src/openai/types/beta/realtime/conversation_item_content.py b/src/openai/types/beta/realtime/conversation_item_content.py index b854aa0e0f..ab40a4a1a7 100644 --- a/src/openai/types/beta/realtime/conversation_item_content.py +++ b/src/openai/types/beta/realtime/conversation_item_content.py @@ -11,8 +11,9 @@ class ConversationItemContent(BaseModel): id: Optional[str] = None """ - ID of a previous conversation item (like a model response), used for - `item_reference` content types. + ID of a previous conversation item to reference (for `item_reference` content + types in `response.create` events). These can reference both client and server + created items. """ audio: Optional[str] = None diff --git a/src/openai/types/beta/realtime/conversation_item_content_param.py b/src/openai/types/beta/realtime/conversation_item_content_param.py index b354d78971..7a3a92a39d 100644 --- a/src/openai/types/beta/realtime/conversation_item_content_param.py +++ b/src/openai/types/beta/realtime/conversation_item_content_param.py @@ -10,8 +10,9 @@ class ConversationItemContentParam(TypedDict, total=False): id: str """ - ID of a previous conversation item (like a model response), used for - `item_reference` content types. + ID of a previous conversation item to reference (for `item_reference` content + types in `response.create` events). These can reference both client and server + created items. """ audio: str diff --git a/src/openai/types/beta/realtime/response_create_event.py b/src/openai/types/beta/realtime/response_create_event.py index 00ba1e5dad..e4e5e7c68f 100644 --- a/src/openai/types/beta/realtime/response_create_event.py +++ b/src/openai/types/beta/realtime/response_create_event.py @@ -89,7 +89,8 @@ class Response(BaseModel): tool_choice: Optional[str] = None """How the model chooses tools. - Options are `auto`, `none`, `required`, or specify a function. + Options are `auto`, `none`, `required`, or specify a function, like + `{"type": "function", "function": {"name": "my_function"}}`. """ tools: Optional[List[ResponseTool]] = None diff --git a/src/openai/types/beta/realtime/response_create_event_param.py b/src/openai/types/beta/realtime/response_create_event_param.py index 7c92b32df1..7a4b5f086a 100644 --- a/src/openai/types/beta/realtime/response_create_event_param.py +++ b/src/openai/types/beta/realtime/response_create_event_param.py @@ -90,7 +90,8 @@ class Response(TypedDict, total=False): tool_choice: str """How the model chooses tools. - Options are `auto`, `none`, `required`, or specify a function. + Options are `auto`, `none`, `required`, or specify a function, like + `{"type": "function", "function": {"name": "my_function"}}`. """ tools: Iterable[ResponseTool] From 5481c2ecd44044eeddbda479abb19ba9f9766fc2 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 19 Dec 2024 09:28:52 -0800 Subject: [PATCH 718/914] feat(azure): support for the Realtime API (#1963) --- src/openai/_utils/__init__.py | 2 ++ src/openai/_utils/_utils.py | 16 ++++++++++ src/openai/lib/azure.py | 32 ++++++++++++++++++- .../resources/beta/realtime/realtime.py | 20 +++++++++--- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index af2c9bb77e..bd01c088dc 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -25,6 +25,7 @@ coerce_integer as coerce_integer, file_from_path as file_from_path, parse_datetime as parse_datetime, + is_azure_client as is_azure_client, strip_not_given as strip_not_given, deepcopy_minimal as deepcopy_minimal, get_async_library as get_async_library, @@ -32,6 +33,7 @@ get_required_header as get_required_header, maybe_coerce_boolean as maybe_coerce_boolean, maybe_coerce_integer as maybe_coerce_integer, + is_async_azure_client as is_async_azure_client, ) from ._typing import ( is_list_type as is_list_type, diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index e5811bba42..d6734e6b8f 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -5,6 +5,7 @@ import inspect import functools from typing import ( + TYPE_CHECKING, Any, Tuple, Mapping, @@ -30,6 +31,9 @@ _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] @@ -412,3 +416,15 @@ def json_safe(data: object) -> object: 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/src/openai/lib/azure.py b/src/openai/lib/azure.py index 13d9f31838..f857d76e51 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -7,7 +7,7 @@ import httpx -from .._types import NOT_GIVEN, Omit, Timeout, NotGiven +from .._types import NOT_GIVEN, Omit, Query, Timeout, NotGiven from .._utils import is_given, is_mapping from .._client import OpenAI, AsyncOpenAI from .._compat import model_copy @@ -307,6 +307,21 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: return options + def _configure_realtime(self, model: str, extra_query: Query) -> tuple[Query, dict[str, str]]: + auth_headers = {} + query = { + **extra_query, + "api-version": self._api_version, + "deployment": model, + } + if self.api_key != "": + auth_headers = {"api-key": self.api_key} + else: + token = self._get_azure_ad_token() + if token: + auth_headers = {"Authorization": f"Bearer {token}"} + return query, auth_headers + class AsyncAzureOpenAI(BaseAzureClient[httpx.AsyncClient, AsyncStream[Any]], AsyncOpenAI): @overload @@ -555,3 +570,18 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp raise ValueError("Unable to handle auth") return options + + async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[Query, dict[str, str]]: + auth_headers = {} + query = { + **extra_query, + "api-version": self._api_version, + "deployment": model, + } + if self.api_key != "": + auth_headers = {"api-key": self.api_key} + else: + token = await self._get_azure_ad_token() + if token: + auth_headers = {"Authorization": f"Bearer {token}"} + return query, auth_headers diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index c79fd46217..b39b410ecf 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -21,9 +21,11 @@ ) from ...._types import NOT_GIVEN, Query, Headers, NotGiven from ...._utils import ( + is_azure_client, maybe_transform, strip_not_given, async_maybe_transform, + is_async_azure_client, ) from ...._compat import cached_property from ...._models import construct_type_unchecked @@ -319,11 +321,16 @@ async def __aenter__(self) -> AsyncRealtimeConnection: except ImportError as exc: raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + extra_query = self.__extra_query + auth_headers = self.__client.auth_headers + if is_async_azure_client(self.__client): + extra_query, auth_headers = await self.__client._configure_realtime(self.__model, extra_query) + url = self._prepare_url().copy_with( params={ **self.__client.base_url.params, "model": self.__model, - **self.__extra_query, + **extra_query, }, ) log.debug("Connecting to %s", url) @@ -336,7 +343,7 @@ async def __aenter__(self) -> AsyncRealtimeConnection: user_agent_header=self.__client.user_agent, additional_headers=_merge_mappings( { - **self.__client.auth_headers, + **auth_headers, "OpenAI-Beta": "realtime=v1", }, self.__extra_headers, @@ -496,11 +503,16 @@ def __enter__(self) -> RealtimeConnection: except ImportError as exc: raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + extra_query = self.__extra_query + auth_headers = self.__client.auth_headers + if is_azure_client(self.__client): + extra_query, auth_headers = self.__client._configure_realtime(self.__model, extra_query) + url = self._prepare_url().copy_with( params={ **self.__client.base_url.params, "model": self.__model, - **self.__extra_query, + **extra_query, }, ) log.debug("Connecting to %s", url) @@ -513,7 +525,7 @@ def __enter__(self) -> RealtimeConnection: user_agent_header=self.__client.user_agent, additional_headers=_merge_mappings( { - **self.__client.auth_headers, + **auth_headers, "OpenAI-Beta": "realtime=v1", }, self.__extra_headers, From 89d49335a02ac231925e5a514659c93322f29526 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 05:03:53 +0000 Subject: [PATCH 719/914] release: 1.59.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 73a4167d2d..451b00c101 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.58.1" + ".": "1.59.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6519747179..1f411fc397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.59.0 (2024-12-21) + +Full Changelog: [v1.58.1...v1.59.0](https://github.com/openai/openai-python/compare/v1.58.1...v1.59.0) + +### Features + +* **azure:** support for the Realtime API ([#1963](https://github.com/openai/openai-python/issues/1963)) ([9fda141](https://github.com/openai/openai-python/commit/9fda14172abdb66fe240aa7b4dc7cfae4faf1d73)) + + +### Chores + +* **realtime:** update docstrings ([#1964](https://github.com/openai/openai-python/issues/1964)) ([3dee863](https://github.com/openai/openai-python/commit/3dee863554d28272103e90a6a199ac196e92ff05)) + ## 1.58.1 (2024-12-17) Full Changelog: [v1.58.0...v1.58.1](https://github.com/openai/openai-python/compare/v1.58.0...v1.58.1) diff --git a/pyproject.toml b/pyproject.toml index fd55cf2b5d..127213c372 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.58.1" +version = "1.59.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index c08e68e11b..7719866b19 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.58.1" # x-release-please-version +__version__ = "1.59.0" # x-release-please-version From 99861632e9bdb1a480d92913d621bded574bf797 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 01:44:32 +0000 Subject: [PATCH 720/914] chore: bump license year (#1981) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 621a6becfb..f011417af6 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2024 OpenAI + Copyright 2025 OpenAI Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 44d6210f101abedeb2dd68507fcffcb329df70ea Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 05:04:23 +0000 Subject: [PATCH 721/914] release: 1.59.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 451b00c101..8dcb014faf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.0" + ".": "1.59.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f411fc397..4f029bf1f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.59.1 (2025-01-02) + +Full Changelog: [v1.59.0...v1.59.1](https://github.com/openai/openai-python/compare/v1.59.0...v1.59.1) + +### Chores + +* bump license year ([#1981](https://github.com/openai/openai-python/issues/1981)) ([f29011a](https://github.com/openai/openai-python/commit/f29011a6426d3fa4844ecd723ee20561ee60c665)) + ## 1.59.0 (2024-12-21) Full Changelog: [v1.58.1...v1.59.0](https://github.com/openai/openai-python/compare/v1.58.1...v1.59.0) diff --git a/pyproject.toml b/pyproject.toml index 127213c372..51166e5eca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.0" +version = "1.59.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7719866b19..98a34f1356 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.0" # x-release-please-version +__version__ = "1.59.1" # x-release-please-version From ccf5753ae01ddee52f102544d992b51e333cb669 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 3 Jan 2025 16:01:50 +0000 Subject: [PATCH 722/914] chore(ci): fix publish workflow --- .github/workflows/publish-pypi.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 44027a3c4c..76d0efca80 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -8,6 +8,7 @@ jobs: publish: name: publish runs-on: ubuntu-latest + environment: publish steps: - uses: actions/checkout@v4 From b3fc5b39594d30e7dc73813ce88c64796b0e8b96 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:02:21 +0000 Subject: [PATCH 723/914] release: 1.59.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8dcb014faf..7676e15413 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.1" + ".": "1.59.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f029bf1f2..2b410e0a5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.59.2 (2025-01-03) + +Full Changelog: [v1.59.1...v1.59.2](https://github.com/openai/openai-python/compare/v1.59.1...v1.59.2) + +### Chores + +* **ci:** fix publish workflow ([0be1f5d](https://github.com/openai/openai-python/commit/0be1f5de0daf807cece564abf061c8bb188bb9aa)) +* **internal:** empty commit ([fe8dc2e](https://github.com/openai/openai-python/commit/fe8dc2e97fc430ea2433ed28cfaa79425af223ec)) + ## 1.59.1 (2025-01-02) Full Changelog: [v1.59.0...v1.59.1](https://github.com/openai/openai-python/compare/v1.59.0...v1.59.1) diff --git a/pyproject.toml b/pyproject.toml index 51166e5eca..7d7c59fa0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.1" +version = "1.59.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 98a34f1356..866a882fc2 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.1" # x-release-please-version +__version__ = "1.59.2" # x-release-please-version From 0189770e1d5ba0db18d7a923e1d562df0ff4fe6d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 22:57:16 +0000 Subject: [PATCH 724/914] chore(api): bump spec version (#1985) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 1a7a7a5269..1ac7a94471 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-a39aca84ed97ebafb707ebd5221e2787c5a42ff3d98f2ffaea8a0dcd84cbcbcb.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-02200a58ed631064b6419711da99fefd6e97bdbbeb577a80a1a6e0c8dbcb18f5.yml From 1e07c9d839e7e96f02d0a4b745f379a43086334c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 22:57:52 +0000 Subject: [PATCH 725/914] release: 1.59.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7676e15413..f701c8396e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.2" + ".": "1.59.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b410e0a5f..75d144cca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.59.3 (2025-01-03) + +Full Changelog: [v1.59.2...v1.59.3](https://github.com/openai/openai-python/compare/v1.59.2...v1.59.3) + +### Chores + +* **api:** bump spec version ([#1985](https://github.com/openai/openai-python/issues/1985)) ([c6f1b35](https://github.com/openai/openai-python/commit/c6f1b357fcf669065f4ed6819d47a528b0787128)) + ## 1.59.2 (2025-01-03) Full Changelog: [v1.59.1...v1.59.2](https://github.com/openai/openai-python/compare/v1.59.1...v1.59.2) diff --git a/pyproject.toml b/pyproject.toml index 7d7c59fa0b..b9d4abf59a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.2" +version = "1.59.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 866a882fc2..342d208d2b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.2" # x-release-please-version +__version__ = "1.59.3" # x-release-please-version From 7117a18f4f25c5d07db8d96dd2930f0381f2309f Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 6 Jan 2025 16:50:25 +0000 Subject: [PATCH 726/914] chore: add missing isclass check for structured outputs --- src/openai/lib/_pydantic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py index 22c7a1f3cd..4e8bc772be 100644 --- a/src/openai/lib/_pydantic.py +++ b/src/openai/lib/_pydantic.py @@ -127,6 +127,8 @@ def resolve_ref(*, root: dict[str, object], ref: str) -> object: def is_basemodel_type(typ: type) -> TypeGuard[type[pydantic.BaseModel]]: + if not inspect.isclass(typ): + return False return issubclass(typ, pydantic.BaseModel) From f5436b147421296c22269e2fe4081c917d7b1658 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:15:50 +0000 Subject: [PATCH 727/914] chore: add missing isclass check (#1988) --- src/openai/_models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 2f67e5eb4d..1bbc5fa4cc 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -513,7 +513,11 @@ def construct_type(*, value: object, type_: object) -> object: _, items_type = get_args(type_) # Dict[_, items_type] return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} - if not is_literal_type(type_) and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)): + if ( + not is_literal_type(type_) + and inspect.isclass(origin) + and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) + ): if is_list(value): return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] From 255677d7091313d2ef32316c6c64983673e077d0 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 6 Jan 2025 17:41:56 +0000 Subject: [PATCH 728/914] docs(realtime): fix event reference link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87837db175..cc1eb1a4f7 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,7 @@ The Realtime API enables you to build low-latency, multi-modal conversational ex Under the hood the SDK uses the [`websockets`](https://websockets.readthedocs.io/en/stable/) library to manage connections. -The Realtime API works through a combination of client-sent events and server-sent events. Clients can send events to do things like update session configuration or send text and audio inputs. Server events confirm when audio responses have completed, or when a text response from the model has been received. A full event reference can be found [here](platform.openai.com/docs/api-reference/realtime-client-events) and a guide can be found [here](https://platform.openai.com/docs/guides/realtime). +The Realtime API works through a combination of client-sent events and server-sent events. Clients can send events to do things like update session configuration or send text and audio inputs. Server events confirm when audio responses have completed, or when a text response from the model has been received. A full event reference can be found [here](https://platform.openai.com/docs/api-reference/realtime-client-events) and a guide can be found [here](https://platform.openai.com/docs/guides/realtime). Basic text based example: From 1fb671ea97b51918fdcdbac8bc9abb68fc6b7506 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:49:30 +0000 Subject: [PATCH 729/914] chore(internal): bump httpx dependency (#1990) --- pyproject.toml | 3 +-- requirements-dev.lock | 5 ++--- requirements.lock | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b9d4abf59a..3a683b0eef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,8 +66,7 @@ dev-dependencies = [ "types-tqdm > 4", "types-pyaudio > 0", "trio >=0.22.2", - "nest_asyncio==1.6.0" - + "nest_asyncio==1.6.0", ] [tool.rye.scripts] diff --git a/requirements-dev.lock b/requirements-dev.lock index 94cf6aca07..15ecbf081a 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -60,7 +60,7 @@ h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx -httpx==0.25.2 +httpx==0.28.1 # via openai # via respx idna==3.4 @@ -137,7 +137,7 @@ pytz==2023.3.post1 requests==2.31.0 # via azure-core # via msal -respx==0.20.2 +respx==0.22.0 rich==13.7.1 # via inline-snapshot ruff==0.6.9 @@ -149,7 +149,6 @@ six==1.16.0 # via python-dateutil sniffio==1.3.0 # via anyio - # via httpx # via openai # via trio sortedcontainers==2.4.0 diff --git a/requirements.lock b/requirements.lock index c10449ac20..a3e3602abe 100644 --- a/requirements.lock +++ b/requirements.lock @@ -25,7 +25,7 @@ h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx -httpx==0.25.2 +httpx==0.28.1 # via openai idna==3.4 # via anyio @@ -52,7 +52,6 @@ six==1.16.0 # via python-dateutil sniffio==1.3.0 # via anyio - # via httpx # via openai tqdm==4.66.5 # via openai From 728100cf7a7aff90bbe274e593bc726030e392ec Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:50:04 +0000 Subject: [PATCH 730/914] release: 1.59.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f701c8396e..b58729ff4e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.3" + ".": "1.59.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 75d144cca6..78a1f2a1cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.59.4 (2025-01-07) + +Full Changelog: [v1.59.3...v1.59.4](https://github.com/openai/openai-python/compare/v1.59.3...v1.59.4) + +### Chores + +* add missing isclass check ([#1988](https://github.com/openai/openai-python/issues/1988)) ([61d9072](https://github.com/openai/openai-python/commit/61d9072fbace58d64910ec7378c3686ac555972e)) +* add missing isclass check for structured outputs ([bcbf013](https://github.com/openai/openai-python/commit/bcbf013e8d825b8b5f88172313dfb6e0313ca34c)) +* **internal:** bump httpx dependency ([#1990](https://github.com/openai/openai-python/issues/1990)) ([288c2c3](https://github.com/openai/openai-python/commit/288c2c30dc405cbaa89924f9243442300e95e100)) + + +### Documentation + +* **realtime:** fix event reference link ([9b6885d](https://github.com/openai/openai-python/commit/9b6885d50f8d65ba5009642046727d291e0f14fa)) + ## 1.59.3 (2025-01-03) Full Changelog: [v1.59.2...v1.59.3](https://github.com/openai/openai-python/compare/v1.59.2...v1.59.3) diff --git a/pyproject.toml b/pyproject.toml index 3a683b0eef..f5309b299a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.3" +version = "1.59.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 342d208d2b..86545ed82a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.3" # x-release-please-version +__version__ = "1.59.4" # x-release-please-version From bd49dd156f2654a14fea2a26fc39b33a068d7900 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:09:36 +0000 Subject: [PATCH 731/914] fix(client): only call .close() when needed (#1992) --- src/openai/_base_client.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index cceec903d9..1fa039c0b1 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -769,6 +769,9 @@ def __init__(self, **kwargs: Any) -> None: class SyncHttpxClientWrapper(DefaultHttpxClient): def __del__(self) -> None: + if self.is_closed: + return + try: self.close() except Exception: @@ -1351,6 +1354,9 @@ def __init__(self, **kwargs: Any) -> None: class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): def __del__(self) -> None: + if self.is_closed: + return + try: # TODO(someday): support non asyncio runtimes here asyncio.get_running_loop().create_task(self.aclose()) From eb02a2c2a7bcd81cc00836c36f178b028df96c33 Mon Sep 17 00:00:00 2001 From: Mustafa <66224841+mustafa-nom@users.noreply.github.com> Date: Wed, 8 Jan 2025 05:22:24 -0800 Subject: [PATCH 732/914] docs: fix typos (#1995) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc1eb1a4f7..3ab509ce42 100644 --- a/README.md +++ b/README.md @@ -655,7 +655,7 @@ If you need to access undocumented endpoints, params, or response properties, th #### Undocumented endpoints To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other -http verbs. Options on the client will be respected (such as retries) will be respected when making this +http verbs. Options on the client (such as retries) will be respected when making this request. ```py From fee9c81bece28f2e145c7abf357a93f52983e119 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:50:20 +0000 Subject: [PATCH 733/914] docs: fix typos (#1996) --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3ab509ce42..32095a22d2 100644 --- a/README.md +++ b/README.md @@ -469,7 +469,7 @@ except openai.APIStatusError as e: print(e.response) ``` -Error codes are as followed: +Error codes are as follows: | Status Code | Error Type | | ----------- | -------------------------- | @@ -611,7 +611,7 @@ completion = response.parse() # get the object that `chat.completions.create()` print(completion) ``` -These methods return an [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. +These methods return a [`LegagcyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. For the sync client this will mostly be the same with the exception of `content` & `text` will be methods instead of properties. In the @@ -655,8 +655,7 @@ If you need to access undocumented endpoints, params, or response properties, th #### Undocumented endpoints To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other -http verbs. Options on the client (such as retries) will be respected when making this -request. +http verbs. Options on the client will be respected (such as retries) when making this request. ```py import httpx From 16315f22b459f96d4785ff5800712e8349811d57 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:08:24 +0000 Subject: [PATCH 734/914] docs: more typo fixes (#1998) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 32095a22d2..42f69f5401 100644 --- a/README.md +++ b/README.md @@ -611,7 +611,7 @@ completion = response.parse() # get the object that `chat.completions.create()` print(completion) ``` -These methods return a [`LegagcyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. +These methods return a [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. For the sync client this will mostly be the same with the exception of `content` & `text` will be methods instead of properties. In the From e502d3014d8520ce3b29c59abf0fe4a27d447163 Mon Sep 17 00:00:00 2001 From: Josiah Altschuler Date: Wed, 8 Jan 2025 09:00:17 -0600 Subject: [PATCH 735/914] docs(readme): moved period to inside parentheses (#1980) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42f69f5401..ad1c9afd10 100644 --- a/README.md +++ b/README.md @@ -769,7 +769,7 @@ An example of using the client with Microsoft Entra ID (formerly known as Azure This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: 1. Changes that only affect static types, without breaking runtime behavior. -2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals)_. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ 3. Changes that we do not expect to impact the vast majority of users in practice. We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. From 52f49794b6ea7c243372870d09a23deae8d019cf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:00:53 +0000 Subject: [PATCH 736/914] release: 1.59.5 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b58729ff4e..802e19924e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.4" + ".": "1.59.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 78a1f2a1cb..e3a67b7ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 1.59.5 (2025-01-08) + +Full Changelog: [v1.59.4...v1.59.5](https://github.com/openai/openai-python/compare/v1.59.4...v1.59.5) + +### Bug Fixes + +* **client:** only call .close() when needed ([#1992](https://github.com/openai/openai-python/issues/1992)) ([bdfd699](https://github.com/openai/openai-python/commit/bdfd699b99522e83f7610b5f98e36fe43ddf8338)) + + +### Documentation + +* fix typos ([#1995](https://github.com/openai/openai-python/issues/1995)) ([be694a0](https://github.com/openai/openai-python/commit/be694a097d6cf2668f08ecf94c882773b2ee1f84)) +* fix typos ([#1996](https://github.com/openai/openai-python/issues/1996)) ([714aed9](https://github.com/openai/openai-python/commit/714aed9d7eb74a19f6e502fb6d4fe83399f82851)) +* more typo fixes ([#1998](https://github.com/openai/openai-python/issues/1998)) ([7bd92f0](https://github.com/openai/openai-python/commit/7bd92f06a75f11f6afc2d1223d2426e186cc74cb)) +* **readme:** moved period to inside parentheses ([#1980](https://github.com/openai/openai-python/issues/1980)) ([e7fae94](https://github.com/openai/openai-python/commit/e7fae948f2ba8db23461e4374308417570196847)) + ## 1.59.4 (2025-01-07) Full Changelog: [v1.59.3...v1.59.4](https://github.com/openai/openai-python/compare/v1.59.3...v1.59.4) diff --git a/pyproject.toml b/pyproject.toml index f5309b299a..7529b69960 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.4" +version = "1.59.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 86545ed82a..f8a67d7937 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.4" # x-release-please-version +__version__ = "1.59.5" # x-release-please-version From 020385c075aa04e4adc284efabb14e38c23d16d5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:41:34 +0000 Subject: [PATCH 737/914] chore(internal): spec update (#2000) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 1ac7a94471..9600edae3b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-02200a58ed631064b6419711da99fefd6e97bdbbeb577a80a1a6e0c8dbcb18f5.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-b5b0e2c794b012919701c3fd43286af10fa25d33ceb8a881bec2636028f446e0.yml From 5ca7876e6a99c0d47bffc2c4167a5faf58673384 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:56:47 +0000 Subject: [PATCH 738/914] fix: correctly handle deserialising `cls` fields (#2002) --- src/openai/_models.py | 8 ++++---- tests/test_models.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 1bbc5fa4cc..23456d9f80 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -204,14 +204,14 @@ def __str__(self) -> str: @classmethod @override def construct( # pyright: ignore[reportIncompatibleMethodOverride] - cls: Type[ModelT], + __cls: Type[ModelT], _fields_set: set[str] | None = None, **values: object, ) -> ModelT: - m = cls.__new__(cls) + m = __cls.__new__(__cls) fields_values: dict[str, object] = {} - config = get_model_config(cls) + config = get_model_config(__cls) populate_by_name = ( config.allow_population_by_field_name if isinstance(config, _ConfigProtocol) @@ -221,7 +221,7 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] if _fields_set is None: _fields_set = set() - model_fields = get_model_fields(cls) + model_fields = get_model_fields(__cls) for name, field in model_fields.items(): key = field.alias if key is None or (key not in values and populate_by_name): diff --git a/tests/test_models.py b/tests/test_models.py index 19a71f13ba..30b17e3ac0 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -844,3 +844,13 @@ class Model(BaseModel): assert m.alias == "foo" assert isinstance(m.union, str) assert m.union == "bar" + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +def test_field_named_cls() -> None: + class Model(BaseModel): + cls: str + + m = construct_type(value={"cls": "foo"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.cls, str) From 33e40854beef0cb18c0790bea953678c30b6fb5c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:57:26 +0000 Subject: [PATCH 739/914] release: 1.59.6 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 802e19924e..fc624851e9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.5" + ".": "1.59.6" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e3a67b7ac9..e65def028e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.59.6 (2025-01-09) + +Full Changelog: [v1.59.5...v1.59.6](https://github.com/openai/openai-python/compare/v1.59.5...v1.59.6) + +### Bug Fixes + +* correctly handle deserialising `cls` fields ([#2002](https://github.com/openai/openai-python/issues/2002)) ([089c820](https://github.com/openai/openai-python/commit/089c820c8a5d20e9db6a171f0a4f11b481fe8465)) + + +### Chores + +* **internal:** spec update ([#2000](https://github.com/openai/openai-python/issues/2000)) ([36548f8](https://github.com/openai/openai-python/commit/36548f871763fdd7b5ce44903d186bc916331549)) + ## 1.59.5 (2025-01-08) Full Changelog: [v1.59.4...v1.59.5](https://github.com/openai/openai-python/compare/v1.59.4...v1.59.5) diff --git a/pyproject.toml b/pyproject.toml index 7529b69960..4131e9c1fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.5" +version = "1.59.6" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f8a67d7937..fa93e603a6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.5" # x-release-please-version +__version__ = "1.59.6" # x-release-please-version From b2dd5e04d6136856c4c00e4ce9af10e45af5f7aa Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 13 Jan 2025 13:29:41 +0000 Subject: [PATCH 740/914] chore: export HttpxBinaryResponseContent class --- src/openai/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 21c60f7e87..fe85956a4a 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -33,6 +33,7 @@ ) from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging +from ._legacy_response import HttpxBinaryResponseContent as HttpxBinaryResponseContent __all__ = [ "types", From d9c966dea77fa3493114865a7f785f3134f1cc1e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:30:18 +0000 Subject: [PATCH 741/914] release: 1.59.7 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fc624851e9..7da3bd4caf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.6" + ".": "1.59.7" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e65def028e..08674b4a36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.59.7 (2025-01-13) + +Full Changelog: [v1.59.6...v1.59.7](https://github.com/openai/openai-python/compare/v1.59.6...v1.59.7) + +### Chores + +* export HttpxBinaryResponseContent class ([7191b71](https://github.com/openai/openai-python/commit/7191b71f3dcbbfcb2f2bec855c3bba93c956384e)) + ## 1.59.6 (2025-01-09) Full Changelog: [v1.59.5...v1.59.6](https://github.com/openai/openai-python/compare/v1.59.5...v1.59.6) diff --git a/pyproject.toml b/pyproject.toml index 4131e9c1fd..e769f4a95f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.6" +version = "1.59.7" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index fa93e603a6..656d17ff63 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.6" # x-release-please-version +__version__ = "1.59.7" # x-release-please-version From babe65f92c8a71efdc8adb7e68205d5906f571cf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:20:02 +0000 Subject: [PATCH 742/914] chore(internal): streaming refactors (#2012) --- src/openai/_streaming.py | 66 +++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 0fda992cff..b275986a46 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -59,23 +59,22 @@ def __stream__(self) -> Iterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None: - data = sse.json() - if is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data=data, cast_to=cast_to, response=response) + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) else: data = sse.json() @@ -161,23 +160,22 @@ async def __stream__(self) -> AsyncIterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None: - data = sse.json() - if is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data=data, cast_to=cast_to, response=response) + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) else: data = sse.json() From 7e88d42289f1327540c3d3f9210fae3c61474053 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 13 Jan 2025 16:59:43 +0000 Subject: [PATCH 743/914] fix: streaming --- src/openai/_streaming.py | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index b275986a46..7aa7b62f6b 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -76,25 +76,6 @@ def __stream__(self) -> Iterator[_T]: yield process_data(data=data, cast_to=cast_to, response=response) - else: - data = sse.json() - - if sse.event == "error" and is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) - # Ensure the entire stream is consumed for _sse in iterator: ... @@ -177,25 +158,6 @@ async def __stream__(self) -> AsyncIterator[_T]: yield process_data(data=data, cast_to=cast_to, response=response) - else: - data = sse.json() - - if sse.event == "error" and is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) - # Ensure the entire stream is consumed async for _sse in iterator: ... From 82ccc9858ac0de22ac874dcf796c17a333ce7b00 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Mon, 13 Jan 2025 10:08:10 -0800 Subject: [PATCH 744/914] docs(examples/azure): example script with realtime API (#1967) --- examples/realtime/azure_realtime.py | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 examples/realtime/azure_realtime.py diff --git a/examples/realtime/azure_realtime.py b/examples/realtime/azure_realtime.py new file mode 100644 index 0000000000..de88d47052 --- /dev/null +++ b/examples/realtime/azure_realtime.py @@ -0,0 +1,57 @@ +import os +import asyncio + +from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider + +from openai import AsyncAzureOpenAI + +# Azure OpenAI Realtime Docs + +# How-to: https://learn.microsoft.com/azure/ai-services/openai/how-to/realtime-audio +# Supported models and API versions: https://learn.microsoft.com/azure/ai-services/openai/how-to/realtime-audio#supported-models +# Entra ID auth: https://learn.microsoft.com/azure/ai-services/openai/how-to/managed-identity + + +async def main() -> None: + """The following example demonstrates how to configure Azure OpenAI to use the Realtime API. + For an audio example, see push_to_talk_app.py and update the client and model parameter accordingly. + + When prompted for user input, type a message and hit enter to send it to the model. + Enter "q" to quit the conversation. + """ + + credential = DefaultAzureCredential() + client = AsyncAzureOpenAI( + azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], + azure_ad_token_provider=get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default"), + api_version="2024-10-01-preview", + ) + async with client.beta.realtime.connect( + model="gpt-4o-realtime-preview", # deployment name for your model + ) as connection: + await connection.session.update(session={"modalities": ["text"]}) # type: ignore + while True: + user_input = input("Enter a message: ") + if user_input == "q": + break + + await connection.conversation.item.create( + item={ + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": user_input}], + } + ) + await connection.response.create() + async for event in connection: + if event.type == "response.text.delta": + print(event.delta, flush=True, end="") + elif event.type == "response.text.done": + print() + elif event.type == "response.done": + break + + await credential.close() + + +asyncio.run(main()) From e081d99e16835a038ef0dbb68500e562e021291f Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 13 Jan 2025 20:23:17 +0000 Subject: [PATCH 745/914] Revert "chore(internal): streaming refactors (#2012)" This reverts commit d76a748f606743407f94dfc26758095560e2082a. --- src/openai/_streaming.py | 104 +++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 32 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 7aa7b62f6b..0fda992cff 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -59,22 +59,42 @@ def __stream__(self) -> Iterator[_T]: if sse.data.startswith("[DONE]"): break - data = sse.json() - if is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data=data, cast_to=cast_to, response=response) + if sse.event is None: + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) + + else: + data = sse.json() + + if sse.event == "error" and is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) # Ensure the entire stream is consumed for _sse in iterator: @@ -141,22 +161,42 @@ async def __stream__(self) -> AsyncIterator[_T]: if sse.data.startswith("[DONE]"): break - data = sse.json() - if is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data=data, cast_to=cast_to, response=response) + if sse.event is None: + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) + + else: + data = sse.json() + + if sse.event == "error" and is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) # Ensure the entire stream is consumed async for _sse in iterator: From 83f11490fa291f9814f3dae6a65b1f62d0177675 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 11:56:21 +0000 Subject: [PATCH 746/914] chore(internal): update deps (#2015) --- mypy.ini | 2 +- requirements-dev.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy.ini b/mypy.ini index 1ea1fe909d..660f1a086e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -44,7 +44,7 @@ cache_fine_grained = True # ``` # Changing this codegen to make mypy happy would increase complexity # and would not be worth it. -disable_error_code = func-returns-value +disable_error_code = func-returns-value,overload-cannot-match # https://github.com/python/mypy/issues/12162 [mypy.overrides] diff --git a/requirements-dev.lock b/requirements-dev.lock index 15ecbf081a..8799e10b06 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -83,7 +83,7 @@ msal==1.31.0 # via msal-extensions msal-extensions==1.2.0 # via azure-identity -mypy==1.13.0 +mypy==1.14.1 mypy-extensions==1.0.0 # via black # via mypy @@ -124,7 +124,7 @@ pygments==2.18.0 # via rich pyjwt==2.8.0 # via msal -pyright==1.1.390 +pyright==1.1.391 pytest==8.3.3 # via pytest-asyncio pytest-asyncio==0.24.0 From d256d83589261e3bb2d2778a4f5bd4dda3248ac9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 11:59:51 +0000 Subject: [PATCH 747/914] fix(types): correct type for vector store chunking strategy (#2017) --- api.md | 2 +- src/openai/types/beta/__init__.py | 3 +++ .../types/beta/file_chunking_strategy_param.py | 4 ++-- ...static_file_chunking_strategy_object_param.py | 16 ++++++++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/openai/types/beta/static_file_chunking_strategy_object_param.py diff --git a/api.md b/api.md index ace93e0559..1edd3f6589 100644 --- a/api.md +++ b/api.md @@ -314,7 +314,7 @@ from openai.types.beta import ( OtherFileChunkingStrategyObject, StaticFileChunkingStrategy, StaticFileChunkingStrategyObject, - StaticFileChunkingStrategyParam, + StaticFileChunkingStrategyObjectParam, VectorStore, VectorStoreDeleted, ) diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index 7f76fed0cd..b9ea792bfa 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -43,3 +43,6 @@ from .assistant_response_format_option_param import ( AssistantResponseFormatOptionParam as AssistantResponseFormatOptionParam, ) +from .static_file_chunking_strategy_object_param import ( + StaticFileChunkingStrategyObjectParam as StaticFileChunkingStrategyObjectParam, +) diff --git a/src/openai/types/beta/file_chunking_strategy_param.py b/src/openai/types/beta/file_chunking_strategy_param.py index 46383358e5..25d94286d8 100644 --- a/src/openai/types/beta/file_chunking_strategy_param.py +++ b/src/openai/types/beta/file_chunking_strategy_param.py @@ -6,8 +6,8 @@ from typing_extensions import TypeAlias from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam -from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam +from .static_file_chunking_strategy_object_param import StaticFileChunkingStrategyObjectParam __all__ = ["FileChunkingStrategyParam"] -FileChunkingStrategyParam: TypeAlias = Union[AutoFileChunkingStrategyParam, StaticFileChunkingStrategyParam] +FileChunkingStrategyParam: TypeAlias = Union[AutoFileChunkingStrategyParam, StaticFileChunkingStrategyObjectParam] diff --git a/src/openai/types/beta/static_file_chunking_strategy_object_param.py b/src/openai/types/beta/static_file_chunking_strategy_object_param.py new file mode 100644 index 0000000000..0cdf35c0df --- /dev/null +++ b/src/openai/types/beta/static_file_chunking_strategy_object_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam + +__all__ = ["StaticFileChunkingStrategyObjectParam"] + + +class StaticFileChunkingStrategyObjectParam(TypedDict, total=False): + static: Required[StaticFileChunkingStrategyParam] + + type: Required[Literal["static"]] + """Always `static`.""" From fd76342499f061bb157e4d5501a1e55f867cbc2c Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 15 Jan 2025 14:06:06 +0000 Subject: [PATCH 748/914] chore(examples): update realtime model closes #2020 --- README.md | 4 ++-- examples/realtime/push_to_talk_app.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ad1c9afd10..ec556bd27a 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,7 @@ from openai import AsyncOpenAI async def main(): client = AsyncOpenAI() - async with client.beta.realtime.connect(model="gpt-4o-realtime-preview-2024-10-01") as connection: + async with client.beta.realtime.connect(model="gpt-4o-realtime-preview") as connection: await connection.session.update(session={'modalities': ['text']}) await connection.conversation.item.create( @@ -309,7 +309,7 @@ Whenever an error occurs, the Realtime API will send an [`error` event](https:// ```py client = AsyncOpenAI() -async with client.beta.realtime.connect(model="gpt-4o-realtime-preview-2024-10-01") as connection: +async with client.beta.realtime.connect(model="gpt-4o-realtime-preview") as connection: ... async for event in connection: if event.type == 'error': diff --git a/examples/realtime/push_to_talk_app.py b/examples/realtime/push_to_talk_app.py index d46945a8ed..8dc303a83a 100755 --- a/examples/realtime/push_to_talk_app.py +++ b/examples/realtime/push_to_talk_app.py @@ -152,7 +152,7 @@ async def on_mount(self) -> None: self.run_worker(self.send_mic_audio()) async def handle_realtime_connection(self) -> None: - async with self.client.beta.realtime.connect(model="gpt-4o-realtime-preview-2024-10-01") as conn: + async with self.client.beta.realtime.connect(model="gpt-4o-realtime-preview") as conn: self.connection = conn self.connected.set() From bf5bebb57d74266d3010a72267298c0c832510a9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:51:10 +0000 Subject: [PATCH 749/914] chore(internal): bump pyright dependency (#2021) --- requirements-dev.lock | 2 +- src/openai/_legacy_response.py | 12 ++++++++++-- src/openai/_response.py | 8 +++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 8799e10b06..ef26591f12 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -124,7 +124,7 @@ pygments==2.18.0 # via rich pyjwt==2.8.0 # via msal -pyright==1.1.391 +pyright==1.1.392.post0 pytest==8.3.3 # via pytest-asyncio pytest-asyncio==0.24.0 diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 7a14f27adb..25680049dc 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -269,7 +269,9 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if origin == LegacyAPIResponse: raise RuntimeError("Unexpected state - cast_to is `APIResponse`") - if inspect.isclass(origin) and issubclass(origin, httpx.Response): + if inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) and issubclass(origin, httpx.Response): # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response # and pass that class to our request functions. We cannot change the variance to be either # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct @@ -279,7 +281,13 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) - if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel): + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): raise TypeError("Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`") if ( diff --git a/src/openai/_response.py b/src/openai/_response.py index 1527446585..36c7ea1281 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -214,7 +214,13 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) - if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel): + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): raise TypeError("Pydantic models must subclass our base model type, e.g. `from openai import BaseModel`") if ( From 08ab62b93a0c7690f00e760172cfefcb81b8e150 Mon Sep 17 00:00:00 2001 From: Rohit Joshi <891456+rjoshi@users.noreply.github.com> Date: Thu, 16 Jan 2025 04:46:22 -0800 Subject: [PATCH 750/914] fix(structured outputs): avoid parsing empty empty content (#2023) Fixing https://github.com/openai/openai-python/issues/1763 where parsing often fails when content is empty string instead of None. --- src/openai/lib/_parsing/_completions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index f1fa9f2b55..33c4ccb946 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -157,7 +157,7 @@ def maybe_parse_content( response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, message: ChatCompletionMessage | ParsedChatCompletionMessage[object], ) -> ResponseFormatT | None: - if has_rich_response_format(response_format) and message.content is not None and not message.refusal: + if has_rich_response_format(response_format) and message.content and not message.refusal: return _parse_content(response_format, message.content) return None From 1bdabb489a86bd721a3e237d8556583ed0d8dfa1 Mon Sep 17 00:00:00 2001 From: kanchi <17161397+KanchiShimono@users.noreply.github.com> Date: Fri, 17 Jan 2025 20:40:26 +0900 Subject: [PATCH 751/914] fix(structured outputs): correct schema coercion for inline ref expansion (#2025) --- src/openai/lib/_pydantic.py | 3 + tests/lib/test_pydantic.py | 174 ++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py index 4e8bc772be..c2d73e5fc6 100644 --- a/src/openai/lib/_pydantic.py +++ b/src/openai/lib/_pydantic.py @@ -108,6 +108,9 @@ def _ensure_strict_json_schema( # properties from the json schema take priority over the ones on the `$ref` json_schema.update({**resolved, **json_schema}) json_schema.pop("$ref") + # Since the schema expanded from `$ref` might not have `additionalProperties: false` applied, + # we call `_ensure_strict_json_schema` again to fix the inlined schema and ensure it's valid. + return _ensure_strict_json_schema(json_schema, path=path, root=root) return json_schema diff --git a/tests/lib/test_pydantic.py b/tests/lib/test_pydantic.py index 99b9e96d21..7e128b70c0 100644 --- a/tests/lib/test_pydantic.py +++ b/tests/lib/test_pydantic.py @@ -7,6 +7,7 @@ import openai from openai._compat import PYDANTIC_V2 +from openai.lib._pydantic import to_strict_json_schema from .schema_types.query import Query @@ -235,3 +236,176 @@ def test_enums() -> None: }, } ) + + +class Star(BaseModel): + name: str = Field(description="The name of the star.") + + +class Galaxy(BaseModel): + name: str = Field(description="The name of the galaxy.") + largest_star: Star = Field(description="The largest star in the galaxy.") + + +class Universe(BaseModel): + name: str = Field(description="The name of the universe.") + galaxy: Galaxy = Field(description="A galaxy in the universe.") + + +def test_nested_inline_ref_expansion() -> None: + if PYDANTIC_V2: + assert to_strict_json_schema(Universe) == snapshot( + { + "title": "Universe", + "type": "object", + "$defs": { + "Star": { + "title": "Star", + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "The name of the star.", + } + }, + "required": ["name"], + "additionalProperties": False, + }, + "Galaxy": { + "title": "Galaxy", + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "The name of the galaxy.", + }, + "largest_star": { + "title": "Star", + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "The name of the star.", + } + }, + "required": ["name"], + "description": "The largest star in the galaxy.", + "additionalProperties": False, + }, + }, + "required": ["name", "largest_star"], + "additionalProperties": False, + }, + }, + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "The name of the universe.", + }, + "galaxy": { + "title": "Galaxy", + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "The name of the galaxy.", + }, + "largest_star": { + "title": "Star", + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "The name of the star.", + } + }, + "required": ["name"], + "description": "The largest star in the galaxy.", + "additionalProperties": False, + }, + }, + "required": ["name", "largest_star"], + "description": "A galaxy in the universe.", + "additionalProperties": False, + }, + }, + "required": ["name", "galaxy"], + "additionalProperties": False, + } + ) + else: + assert to_strict_json_schema(Universe) == snapshot( + { + "title": "Universe", + "type": "object", + "definitions": { + "Star": { + "title": "Star", + "type": "object", + "properties": { + "name": {"title": "Name", "description": "The name of the star.", "type": "string"} + }, + "required": ["name"], + "additionalProperties": False, + }, + "Galaxy": { + "title": "Galaxy", + "type": "object", + "properties": { + "name": {"title": "Name", "description": "The name of the galaxy.", "type": "string"}, + "largest_star": { + "title": "Largest Star", + "description": "The largest star in the galaxy.", + "type": "object", + "properties": { + "name": {"title": "Name", "description": "The name of the star.", "type": "string"} + }, + "required": ["name"], + "additionalProperties": False, + }, + }, + "required": ["name", "largest_star"], + "additionalProperties": False, + }, + }, + "properties": { + "name": { + "title": "Name", + "description": "The name of the universe.", + "type": "string", + }, + "galaxy": { + "title": "Galaxy", + "description": "A galaxy in the universe.", + "type": "object", + "properties": { + "name": { + "title": "Name", + "description": "The name of the galaxy.", + "type": "string", + }, + "largest_star": { + "title": "Largest Star", + "description": "The largest star in the galaxy.", + "type": "object", + "properties": { + "name": {"title": "Name", "description": "The name of the star.", "type": "string"} + }, + "required": ["name"], + "additionalProperties": False, + }, + }, + "required": ["name", "largest_star"], + "additionalProperties": False, + }, + }, + "required": ["name", "galaxy"], + "additionalProperties": False, + } + ) From 4dd5cf25fa58076307a5baed0a87f6d765f20100 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:40:58 +0000 Subject: [PATCH 752/914] release: 1.59.8 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 24 ++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7da3bd4caf..58f8a4601d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.7" + ".": "1.59.8" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 08674b4a36..9f301cedff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 1.59.8 (2025-01-17) + +Full Changelog: [v1.59.7...v1.59.8](https://github.com/openai/openai-python/compare/v1.59.7...v1.59.8) + +### Bug Fixes + +* streaming ([c16f58e](https://github.com/openai/openai-python/commit/c16f58ead0bc85055b164182689ba74b7e939dfa)) +* **structured outputs:** avoid parsing empty empty content ([#2023](https://github.com/openai/openai-python/issues/2023)) ([6d3513c](https://github.com/openai/openai-python/commit/6d3513c86f6e5800f8f73a45e089b7a205327121)) +* **structured outputs:** correct schema coercion for inline ref expansion ([#2025](https://github.com/openai/openai-python/issues/2025)) ([2f4f0b3](https://github.com/openai/openai-python/commit/2f4f0b374207f162060c328b71ec995049dc42e8)) +* **types:** correct type for vector store chunking strategy ([#2017](https://github.com/openai/openai-python/issues/2017)) ([e389279](https://github.com/openai/openai-python/commit/e38927950a5cdad99065853fe7b72aad6bb322e9)) + + +### Chores + +* **examples:** update realtime model ([f26746c](https://github.com/openai/openai-python/commit/f26746cbcd893d66cf8a3fd68a7c3779dc8c833c)), closes [#2020](https://github.com/openai/openai-python/issues/2020) +* **internal:** bump pyright dependency ([#2021](https://github.com/openai/openai-python/issues/2021)) ([0a9a0f5](https://github.com/openai/openai-python/commit/0a9a0f5d8b9d5457643798287f893305006dd518)) +* **internal:** streaming refactors ([#2012](https://github.com/openai/openai-python/issues/2012)) ([d76a748](https://github.com/openai/openai-python/commit/d76a748f606743407f94dfc26758095560e2082a)) +* **internal:** update deps ([#2015](https://github.com/openai/openai-python/issues/2015)) ([514e0e4](https://github.com/openai/openai-python/commit/514e0e415f87ab4510262d29ed6125384e017b84)) + + +### Documentation + +* **examples/azure:** example script with realtime API ([#1967](https://github.com/openai/openai-python/issues/1967)) ([84f2f9c](https://github.com/openai/openai-python/commit/84f2f9c0439229a7db7136fe78419292d34d1f81)) + ## 1.59.7 (2025-01-13) Full Changelog: [v1.59.6...v1.59.7](https://github.com/openai/openai-python/compare/v1.59.6...v1.59.7) diff --git a/pyproject.toml b/pyproject.toml index e769f4a95f..a75d24e1eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.7" +version = "1.59.8" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 656d17ff63..d6f55997e7 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.7" # x-release-please-version +__version__ = "1.59.8" # x-release-please-version From 1e5e19976a02f4f3423cf7e32ad5fa020c857b82 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 09:38:30 +0000 Subject: [PATCH 753/914] chore(internal): update websockets dep (#2036) --- requirements-dev.lock | 2 +- requirements.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index ef26591f12..38cc6e1cf2 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -184,7 +184,7 @@ urllib3==2.2.1 # via requests virtualenv==20.24.5 # via nox -websockets==14.1 +websockets==14.2 # via openai zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index a3e3602abe..cbdff94fa3 100644 --- a/requirements.lock +++ b/requirements.lock @@ -63,5 +63,5 @@ typing-extensions==4.12.2 # via pydantic-core tzdata==2024.1 # via pandas -websockets==14.1 +websockets==14.2 # via openai From fbc88b6206338761019cb6a4258d7de46f578fcb Mon Sep 17 00:00:00 2001 From: Nino Risteski <95188570+NinoRisteski@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:53:29 +0100 Subject: [PATCH 754/914] docs: fix typo (#2031) removed duplicate 'the' twice --- src/openai/resources/chat/chat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index dc23a15a8e..9c4aacc953 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -24,7 +24,7 @@ def completions(self) -> Completions: @cached_property def with_raw_response(self) -> ChatWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -49,7 +49,7 @@ def completions(self) -> AsyncCompletions: @cached_property def with_raw_response(self) -> AsyncChatWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers From de0550420763b918ffe49d2fffd7b76b2dd00ba8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:13:38 +0000 Subject: [PATCH 755/914] docs(raw responses): fix duplicate `the` (#2039) --- src/openai/resources/audio/audio.py | 4 ++-- src/openai/resources/audio/speech.py | 4 ++-- src/openai/resources/audio/transcriptions.py | 4 ++-- src/openai/resources/audio/translations.py | 4 ++-- src/openai/resources/batches.py | 4 ++-- src/openai/resources/beta/assistants.py | 4 ++-- src/openai/resources/beta/beta.py | 4 ++-- src/openai/resources/beta/realtime/realtime.py | 4 ++-- src/openai/resources/beta/realtime/sessions.py | 4 ++-- src/openai/resources/beta/threads/messages.py | 4 ++-- src/openai/resources/beta/threads/runs/runs.py | 4 ++-- src/openai/resources/beta/threads/runs/steps.py | 4 ++-- src/openai/resources/beta/threads/threads.py | 4 ++-- src/openai/resources/beta/vector_stores/file_batches.py | 4 ++-- src/openai/resources/beta/vector_stores/files.py | 4 ++-- src/openai/resources/beta/vector_stores/vector_stores.py | 4 ++-- src/openai/resources/chat/completions.py | 4 ++-- src/openai/resources/completions.py | 4 ++-- src/openai/resources/embeddings.py | 4 ++-- src/openai/resources/files.py | 4 ++-- src/openai/resources/fine_tuning/fine_tuning.py | 4 ++-- src/openai/resources/fine_tuning/jobs/checkpoints.py | 4 ++-- src/openai/resources/fine_tuning/jobs/jobs.py | 4 ++-- src/openai/resources/images.py | 4 ++-- src/openai/resources/models.py | 4 ++-- src/openai/resources/moderations.py | 4 ++-- src/openai/resources/uploads/parts.py | 4 ++-- src/openai/resources/uploads/uploads.py | 4 ++-- 28 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 18bd7b812c..383b7073bf 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -48,7 +48,7 @@ def speech(self) -> Speech: @cached_property def with_raw_response(self) -> AudioWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -81,7 +81,7 @@ def speech(self) -> AsyncSpeech: @cached_property def with_raw_response(self) -> AsyncAudioWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 09faaddda6..805a8c19c9 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -32,7 +32,7 @@ class Speech(SyncAPIResource): @cached_property def with_raw_response(self) -> SpeechWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -116,7 +116,7 @@ class AsyncSpeech(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncSpeechWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 8b5f4404fc..341446c43a 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -36,7 +36,7 @@ class Transcriptions(SyncAPIResource): @cached_property def with_raw_response(self) -> TranscriptionsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -200,7 +200,7 @@ class AsyncTranscriptions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncTranscriptionsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index a2d28afa03..cd3132dc57 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -36,7 +36,7 @@ class Translations(SyncAPIResource): @cached_property def with_raw_response(self) -> TranslationsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -179,7 +179,7 @@ class AsyncTranslations(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncTranslationsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 7cab75785d..4a887642e9 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -31,7 +31,7 @@ class Batches(SyncAPIResource): @cached_property def with_raw_response(self) -> BatchesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -236,7 +236,7 @@ class AsyncBatches(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncBatchesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 7df212f155..2f2482b648 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -36,7 +36,7 @@ class Assistants(SyncAPIResource): @cached_property def with_raw_response(self) -> AssistantsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -422,7 +422,7 @@ class AsyncAssistants(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncAssistantsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 1ffa6c8e79..5d71cff3f1 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -65,7 +65,7 @@ def threads(self) -> Threads: @cached_property def with_raw_response(self) -> BetaWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -106,7 +106,7 @@ def threads(self) -> AsyncThreads: @cached_property def with_raw_response(self) -> AsyncBetaWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index b39b410ecf..235790a9f5 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -58,7 +58,7 @@ def sessions(self) -> Sessions: @cached_property def with_raw_response(self) -> RealtimeWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -110,7 +110,7 @@ def sessions(self) -> AsyncSessions: @cached_property def with_raw_response(self) -> AsyncRealtimeWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index 1d1ee701e5..8d2df30753 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -27,7 +27,7 @@ class Sessions(SyncAPIResource): @cached_property def with_raw_response(self) -> SessionsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -166,7 +166,7 @@ class AsyncSessions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncSessionsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index e848507387..f780f6f558 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -33,7 +33,7 @@ class Messages(SyncAPIResource): @cached_property def with_raw_response(self) -> MessagesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -307,7 +307,7 @@ class AsyncMessages(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 0418d570ba..f32a08f235 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -64,7 +64,7 @@ def steps(self) -> Steps: @cached_property def with_raw_response(self) -> RunsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -1429,7 +1429,7 @@ def steps(self) -> AsyncSteps: @cached_property def with_raw_response(self) -> AsyncRunsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 9bd91e39e0..709c729d45 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -29,7 +29,7 @@ class Steps(SyncAPIResource): @cached_property def with_raw_response(self) -> StepsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -183,7 +183,7 @@ class AsyncSteps(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncStepsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index e45090abb0..186b6f63e2 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -72,7 +72,7 @@ def messages(self) -> Messages: @cached_property def with_raw_response(self) -> ThreadsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -906,7 +906,7 @@ def messages(self) -> AsyncMessages: @cached_property def with_raw_response(self) -> AsyncThreadsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/beta/vector_stores/file_batches.py index 9f9e643bd0..6d61e92c7f 100644 --- a/src/openai/resources/beta/vector_stores/file_batches.py +++ b/src/openai/resources/beta/vector_stores/file_batches.py @@ -36,7 +36,7 @@ class FileBatches(SyncAPIResource): @cached_property def with_raw_response(self) -> FileBatchesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -365,7 +365,7 @@ class AsyncFileBatches(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFileBatchesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/beta/vector_stores/files.py index 7c155ac917..febf27a753 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/beta/vector_stores/files.py @@ -32,7 +32,7 @@ class Files(SyncAPIResource): @cached_property def with_raw_response(self) -> FilesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -344,7 +344,7 @@ class AsyncFiles(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFilesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py index 61a2eadc7b..6b44c602f1 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -59,7 +59,7 @@ def file_batches(self) -> FileBatches: @cached_property def with_raw_response(self) -> VectorStoresWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -337,7 +337,7 @@ def file_batches(self) -> AsyncFileBatches: @cached_property def with_raw_response(self) -> AsyncVectorStoresWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 728c744327..201ae3f4c6 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -45,7 +45,7 @@ class Completions(SyncAPIResource): @cached_property def with_raw_response(self) -> CompletionsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -906,7 +906,7 @@ class AsyncCompletions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCompletionsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 1ac3575fd5..171f509352 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -32,7 +32,7 @@ class Completions(SyncAPIResource): @cached_property def with_raw_response(self) -> CompletionsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -574,7 +574,7 @@ class AsyncCompletions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCompletionsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 4ab2278e89..81a3e354e6 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -27,7 +27,7 @@ class Embeddings(SyncAPIResource): @cached_property def with_raw_response(self) -> EmbeddingsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -139,7 +139,7 @@ class AsyncEmbeddings(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncEmbeddingsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 6eaea1b568..af453e1e21 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -41,7 +41,7 @@ class Files(SyncAPIResource): @cached_property def with_raw_response(self) -> FilesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -357,7 +357,7 @@ class AsyncFiles(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFilesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index d2bce87c48..eebde07d81 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -24,7 +24,7 @@ def jobs(self) -> Jobs: @cached_property def with_raw_response(self) -> FineTuningWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -49,7 +49,7 @@ def jobs(self) -> AsyncJobs: @cached_property def with_raw_response(self) -> AsyncFineTuningWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py index 8b5e905ea5..f86462e513 100644 --- a/src/openai/resources/fine_tuning/jobs/checkpoints.py +++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py @@ -25,7 +25,7 @@ class Checkpoints(SyncAPIResource): @cached_property def with_raw_response(self) -> CheckpointsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -96,7 +96,7 @@ class AsyncCheckpoints(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCheckpointsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index 78eefc253c..e023d28fea 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -44,7 +44,7 @@ def checkpoints(self) -> Checkpoints: @cached_property def with_raw_response(self) -> JobsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -342,7 +342,7 @@ def checkpoints(self) -> AsyncCheckpoints: @cached_property def with_raw_response(self) -> AsyncJobsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 2fbc077dd9..30473c14f7 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -30,7 +30,7 @@ class Images(SyncAPIResource): @cached_property def with_raw_response(self) -> ImagesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -287,7 +287,7 @@ class AsyncImages(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncImagesWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index d6062de230..a9693a6b0a 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -24,7 +24,7 @@ class Models(SyncAPIResource): @cached_property def with_raw_response(self) -> ModelsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -137,7 +137,7 @@ class AsyncModels(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncModelsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index ce80bb7d55..a8f03142bc 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -28,7 +28,7 @@ class Moderations(SyncAPIResource): @cached_property def with_raw_response(self) -> ModerationsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -98,7 +98,7 @@ class AsyncModerations(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncModerationsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py index d46e5ea1bb..777469ac8e 100644 --- a/src/openai/resources/uploads/parts.py +++ b/src/openai/resources/uploads/parts.py @@ -28,7 +28,7 @@ class Parts(SyncAPIResource): @cached_property def with_raw_response(self) -> PartsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -103,7 +103,7 @@ class AsyncParts(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncPartsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index cfb500b62c..2028decef5 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -51,7 +51,7 @@ def parts(self) -> Parts: @cached_property def with_raw_response(self) -> UploadsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers @@ -344,7 +344,7 @@ def parts(self) -> AsyncParts: @cached_property def with_raw_response(self) -> AsyncUploadsWithRawResponse: """ - This property can be used as a prefix for any HTTP method call to return the + This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers From 14543c59df4f56d2004530dfed411154ffc2f632 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:22:16 +0000 Subject: [PATCH 756/914] fix(tests): make test_get_platform less flaky (#2040) --- tests/test_client.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index e0d23403b1..41da2d5d04 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,6 +6,7 @@ import os import sys import json +import time import asyncio import inspect import subprocess @@ -1797,10 +1798,20 @@ async def test_main() -> None: [sys.executable, "-c", test_code], text=True, ) as process: - try: - process.wait(2) - if process.returncode: - raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") - except subprocess.TimeoutExpired as e: - process.kill() - raise AssertionError("calling get_platform using asyncify resulted in a hung process") from e + timeout = 10 # seconds + + start_time = time.monotonic() + while True: + return_code = process.poll() + if return_code is not None: + if return_code != 0: + raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") + + # success + break + + if time.monotonic() - start_time > timeout: + process.kill() + raise AssertionError("calling get_platform using asyncify resulted in a hung process") + + time.sleep(0.1) From 7989b045d2b974e03792e3010d5247b5bbe387fe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 12:11:31 +0000 Subject: [PATCH 757/914] chore(internal): avoid pytest-asyncio deprecation warning (#2041) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index a75d24e1eb..e5beb93ec5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -141,6 +141,7 @@ testpaths = ["tests"] addopts = "--tb=short" xfail_strict = true asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" filterwarnings = [ "error" ] From 348a783523e36a88e24b92faee693db125efc5bf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 12:12:04 +0000 Subject: [PATCH 758/914] release: 1.59.9 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 58f8a4601d..32b4e18516 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.8" + ".": "1.59.9" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f301cedff..86951242c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 1.59.9 (2025-01-20) + +Full Changelog: [v1.59.8...v1.59.9](https://github.com/openai/openai-python/compare/v1.59.8...v1.59.9) + +### Bug Fixes + +* **tests:** make test_get_platform less flaky ([#2040](https://github.com/openai/openai-python/issues/2040)) ([72ea05c](https://github.com/openai/openai-python/commit/72ea05cf18caaa7a5e6fe7e2251ab93fa0ba3140)) + + +### Chores + +* **internal:** avoid pytest-asyncio deprecation warning ([#2041](https://github.com/openai/openai-python/issues/2041)) ([b901046](https://github.com/openai/openai-python/commit/b901046ddda9c79b7f019e2263c02d126a3b2ee2)) +* **internal:** update websockets dep ([#2036](https://github.com/openai/openai-python/issues/2036)) ([642cd11](https://github.com/openai/openai-python/commit/642cd119482c6fbca925ba702ad2579f9dc47bf9)) + + +### Documentation + +* fix typo ([#2031](https://github.com/openai/openai-python/issues/2031)) ([02fcf15](https://github.com/openai/openai-python/commit/02fcf15611953089826a74725cb96201d94658bb)) +* **raw responses:** fix duplicate `the` ([#2039](https://github.com/openai/openai-python/issues/2039)) ([9b8eab9](https://github.com/openai/openai-python/commit/9b8eab99fdc6a581a1f5cc421c6f74b0e2b30415)) + ## 1.59.8 (2025-01-17) Full Changelog: [v1.59.7...v1.59.8](https://github.com/openai/openai-python/compare/v1.59.7...v1.59.8) diff --git a/pyproject.toml b/pyproject.toml index e5beb93ec5..0b142a6bd8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.8" +version = "1.59.9" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d6f55997e7..7c92c3adf3 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.8" # x-release-please-version +__version__ = "1.59.9" # x-release-please-version From a05e8799f9bb5734b46b19d774d808457c737e31 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:08:31 +0000 Subject: [PATCH 759/914] chore(internal): minor style changes (#2043) --- src/openai/_legacy_response.py | 4 ++-- src/openai/_response.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 25680049dc..8880e5f104 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -205,6 +205,8 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to and is_annotated_type(cast_to): cast_to = extract_type_arg(cast_to, 0) + origin = get_origin(cast_to) or cast_to + if self._stream: if to: if not is_stream_class_type(to): @@ -261,8 +263,6 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to == bool: return cast(R, response.text.lower() == "true") - origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent): return cast(R, cast_to(response)) # type: ignore diff --git a/src/openai/_response.py b/src/openai/_response.py index 36c7ea1281..95e94e6537 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -136,6 +136,8 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to and is_annotated_type(cast_to): cast_to = extract_type_arg(cast_to, 0) + origin = get_origin(cast_to) or cast_to + if self._is_sse_stream: if to: if not is_stream_class_type(to): @@ -195,8 +197,6 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to == bool: return cast(R, response.text.lower() == "true") - origin = get_origin(cast_to) or cast_to - # handle the legacy binary response case if inspect.isclass(cast_to) and cast_to.__name__ == "HttpxBinaryResponseContent": return cast(R, cast_to(response)) # type: ignore From 709926fff8761659761c3efb96c1e21a02fc1f5d Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 22 Jan 2025 10:43:52 +0000 Subject: [PATCH 760/914] docs(readme): mention failed requests in request IDs --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index ec556bd27a..5f7d477cc8 100644 --- a/README.md +++ b/README.md @@ -499,6 +499,21 @@ Note that unlike other properties that use an `_` prefix, the `_request_id` prop *is* public. Unless documented otherwise, *all* other `_` prefix properties, methods and modules are *private*. +> [!IMPORTANT] +> If you need to access request IDs for failed requests you must catch the `APIStatusError` exception + +```python +import openai + +try: + completion = await client.chat.completions.create( + messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-4" + ) +except openai.APIStatusError as exc: + print(exc.request_id) # req_123 + raise exc +``` + ### Retries From 339d3151c5b972af690ab45f1055763c52af0e58 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:20:56 +0000 Subject: [PATCH 761/914] feat(api): update enum values, comments, and examples (#2045) --- .stats.yml | 2 +- src/openai/resources/audio/speech.py | 16 +++---- .../resources/beta/realtime/sessions.py | 48 +++++++++++-------- src/openai/resources/chat/completions.py | 18 ------- src/openai/resources/embeddings.py | 6 ++- .../types/audio/speech_create_params.py | 6 +-- .../conversation_item_create_event.py | 11 +++-- .../conversation_item_create_event_param.py | 11 +++-- src/openai/types/beta/realtime/session.py | 13 ++++- .../beta/realtime/session_create_params.py | 35 ++++++++------ .../beta/realtime/session_update_event.py | 33 ++++++++----- .../realtime/session_update_event_param.py | 33 ++++++++----- src/openai/types/chat/chat_completion.py | 6 +-- ...chat_completion_assistant_message_param.py | 4 +- .../types/chat/chat_completion_chunk.py | 6 +-- .../types/chat/completion_create_params.py | 3 -- src/openai/types/embedding_create_params.py | 3 +- .../beta/realtime/test_sessions.py | 28 ++++------- tests/api_resources/chat/test_completions.py | 8 ++-- tests/api_resources/test_completions.py | 8 ++-- 20 files changed, 152 insertions(+), 146 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9600edae3b..d518bac586 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-b5b0e2c794b012919701c3fd43286af10fa25d33ceb8a881bec2636028f446e0.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-3904ef6b29a89c98f93a9b7da19879695f3c440564be6384db7af1b734611ede.yml diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 805a8c19c9..ad01118161 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -53,7 +53,7 @@ def create( *, input: str, model: Union[str, SpeechModel], - voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], + voice: Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"], response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -73,9 +73,9 @@ def create( One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` or `tts-1-hd` - voice: The voice to use when generating the audio. Supported voices are `alloy`, - `echo`, `fable`, `onyx`, `nova`, and `shimmer`. Previews of the voices are - available in the + voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, + `coral`, `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the + voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, @@ -137,7 +137,7 @@ async def create( *, input: str, model: Union[str, SpeechModel], - voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"], + voice: Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"], response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -157,9 +157,9 @@ async def create( One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1` or `tts-1-hd` - voice: The voice to use when generating the audio. Supported voices are `alloy`, - `echo`, `fable`, `onyx`, `nova`, and `shimmer`. Previews of the voices are - available in the + voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, + `coral`, `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the + voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index 8d2df30753..b920c89207 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -46,18 +46,19 @@ def with_streaming_response(self) -> SessionsWithStreamingResponse: def create( self, *, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + instructions: str | NotGiven = NOT_GIVEN, + max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, model: Literal[ "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", - ], - input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, - input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, - instructions: str | NotGiven = NOT_GIVEN, - max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, - modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + ] + | NotGiven = NOT_GIVEN, output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, tool_choice: str | NotGiven = NOT_GIVEN, @@ -81,9 +82,9 @@ def create( the Realtime API. Args: - model: The Realtime model used for this session. - - input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For + `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel + (mono), and little-endian byte order. input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the @@ -110,7 +111,10 @@ def create( modalities: The set of modalities the model can respond with. To disable audio, set this to ["text"]. + model: The Realtime model used for this session. + output_audio_format: The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + For `pcm16`, output audio is sampled at a rate of 24kHz. temperature: Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8. @@ -140,12 +144,12 @@ def create( "/realtime/sessions", body=maybe_transform( { - "model": model, "input_audio_format": input_audio_format, "input_audio_transcription": input_audio_transcription, "instructions": instructions, "max_response_output_tokens": max_response_output_tokens, "modalities": modalities, + "model": model, "output_audio_format": output_audio_format, "temperature": temperature, "tool_choice": tool_choice, @@ -185,18 +189,19 @@ def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: async def create( self, *, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + instructions: str | NotGiven = NOT_GIVEN, + max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, model: Literal[ "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", - ], - input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, - input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, - instructions: str | NotGiven = NOT_GIVEN, - max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, - modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + ] + | NotGiven = NOT_GIVEN, output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, tool_choice: str | NotGiven = NOT_GIVEN, @@ -220,9 +225,9 @@ async def create( the Realtime API. Args: - model: The Realtime model used for this session. - - input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For + `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel + (mono), and little-endian byte order. input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the @@ -249,7 +254,10 @@ async def create( modalities: The set of modalities the model can respond with. To disable audio, set this to ["text"]. + model: The Realtime model used for this session. + output_audio_format: The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. + For `pcm16`, output audio is sampled at a rate of 24kHz. temperature: Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8. @@ -279,12 +287,12 @@ async def create( "/realtime/sessions", body=await async_maybe_transform( { - "model": model, "input_audio_format": input_audio_format, "input_audio_transcription": input_audio_transcription, "instructions": instructions, "max_response_output_tokens": max_response_output_tokens, "modalities": modalities, + "model": model, "output_audio_format": output_audio_format, "temperature": temperature, "tool_choice": tool_choice, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 201ae3f4c6..a9685c507a 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -251,9 +251,6 @@ def create( tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - When this parameter is set, the response body will include the `service_tier` - utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. store: Whether or not to store the output of this chat completion request for use in @@ -509,9 +506,6 @@ def create( tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - When this parameter is set, the response body will include the `service_tier` - utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. store: Whether or not to store the output of this chat completion request for use in @@ -760,9 +754,6 @@ def create( tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - When this parameter is set, the response body will include the `service_tier` - utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. store: Whether or not to store the output of this chat completion request for use in @@ -1112,9 +1103,6 @@ async def create( tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - When this parameter is set, the response body will include the `service_tier` - utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. store: Whether or not to store the output of this chat completion request for use in @@ -1370,9 +1358,6 @@ async def create( tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - When this parameter is set, the response body will include the `service_tier` - utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. store: Whether or not to store the output of this chat completion request for use in @@ -1621,9 +1606,6 @@ async def create( tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - When this parameter is set, the response body will include the `service_tier` - utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. store: Whether or not to store the output of this chat completion request for use in diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 81a3e354e6..382a42340e 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -68,7 +68,8 @@ def create( `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + for counting tokens. Some models may also impose a limit on total number of + tokens summed across inputs. model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to @@ -180,7 +181,8 @@ async def create( `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + for counting tokens. Some models may also impose a limit on total number of + tokens summed across inputs. model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index a60d000708..ed1a1ce748 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -20,11 +20,11 @@ class SpeechCreateParams(TypedDict, total=False): `tts-1` or `tts-1-hd` """ - voice: Required[Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]] + voice: Required[Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"]] """The voice to use when generating the audio. - Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. - Previews of the voices are available in the + Supported voices are `alloy`, `ash`, `coral`, `echo`, `fable`, `onyx`, `nova`, + `sage` and `shimmer`. Previews of the voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). """ diff --git a/src/openai/types/beta/realtime/conversation_item_create_event.py b/src/openai/types/beta/realtime/conversation_item_create_event.py index 50d309675b..c4f72b9aff 100644 --- a/src/openai/types/beta/realtime/conversation_item_create_event.py +++ b/src/openai/types/beta/realtime/conversation_item_create_event.py @@ -20,9 +20,10 @@ class ConversationItemCreateEvent(BaseModel): """Optional client-generated ID used to identify this event.""" previous_item_id: Optional[str] = None - """The ID of the preceding item after which the new item will be inserted. - - If not set, the new item will be appended to the end of the conversation. If - set, it allows an item to be inserted mid-conversation. If the ID cannot be - found, an error will be returned and the item will not be added. + """ + The ID of the preceding item after which the new item will be inserted. If not + set, the new item will be appended to the end of the conversation. If set to + `root`, the new item will be added to the beginning of the conversation. If set + to an existing ID, it allows an item to be inserted mid-conversation. If the ID + cannot be found, an error will be returned and the item will not be added. """ diff --git a/src/openai/types/beta/realtime/conversation_item_create_event_param.py b/src/openai/types/beta/realtime/conversation_item_create_event_param.py index b8c8bbc251..6da5a63a9d 100644 --- a/src/openai/types/beta/realtime/conversation_item_create_event_param.py +++ b/src/openai/types/beta/realtime/conversation_item_create_event_param.py @@ -20,9 +20,10 @@ class ConversationItemCreateEventParam(TypedDict, total=False): """Optional client-generated ID used to identify this event.""" previous_item_id: str - """The ID of the preceding item after which the new item will be inserted. - - If not set, the new item will be appended to the end of the conversation. If - set, it allows an item to be inserted mid-conversation. If the ID cannot be - found, an error will be returned and the item will not be added. + """ + The ID of the preceding item after which the new item will be inserted. If not + set, the new item will be appended to the end of the conversation. If set to + `root`, the new item will be added to the beginning of the conversation. If set + to an existing ID, it allows an item to be inserted mid-conversation. If the ID + cannot be found, an error will be returned and the item will not be added. """ diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py index 09cdbb02bc..2d028f817c 100644 --- a/src/openai/types/beta/realtime/session.py +++ b/src/openai/types/beta/realtime/session.py @@ -63,7 +63,12 @@ class Session(BaseModel): """Unique identifier for the session object.""" input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ input_audio_transcription: Optional[InputAudioTranscription] = None """ @@ -117,7 +122,11 @@ class Session(BaseModel): """The Realtime model used for this session.""" output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ temperature: Optional[float] = None """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py index f56f2c5c22..3708efeecd 100644 --- a/src/openai/types/beta/realtime/session_create_params.py +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -3,25 +3,19 @@ from __future__ import annotations from typing import List, Union, Iterable -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, TypedDict __all__ = ["SessionCreateParams", "InputAudioTranscription", "Tool", "TurnDetection"] class SessionCreateParams(TypedDict, total=False): - model: Required[ - Literal[ - "gpt-4o-realtime-preview", - "gpt-4o-realtime-preview-2024-10-01", - "gpt-4o-realtime-preview-2024-12-17", - "gpt-4o-mini-realtime-preview", - "gpt-4o-mini-realtime-preview-2024-12-17", - ] - ] - """The Realtime model used for this session.""" - input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ input_audio_transcription: InputAudioTranscription """ @@ -61,8 +55,21 @@ class SessionCreateParams(TypedDict, total=False): To disable audio, set this to ["text"]. """ + model: Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + """The Realtime model used for this session.""" + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ temperature: float """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py index c04220aa25..322e588a4e 100644 --- a/src/openai/types/beta/realtime/session_update_event.py +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -65,17 +65,13 @@ class SessionTurnDetection(BaseModel): class Session(BaseModel): - model: Literal[ - "gpt-4o-realtime-preview", - "gpt-4o-realtime-preview-2024-10-01", - "gpt-4o-realtime-preview-2024-12-17", - "gpt-4o-mini-realtime-preview", - "gpt-4o-mini-realtime-preview-2024-12-17", - ] - """The Realtime model used for this session.""" - input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ input_audio_transcription: Optional[SessionInputAudioTranscription] = None """ @@ -115,8 +111,23 @@ class Session(BaseModel): To disable audio, set this to ["text"]. """ + model: Optional[ + Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + ] = None + """The Realtime model used for this session.""" + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ temperature: Optional[float] = None """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py index aa06069b04..c01d9b6887 100644 --- a/src/openai/types/beta/realtime/session_update_event_param.py +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -71,19 +71,13 @@ class SessionTurnDetection(TypedDict, total=False): class Session(TypedDict, total=False): - model: Required[ - Literal[ - "gpt-4o-realtime-preview", - "gpt-4o-realtime-preview-2024-10-01", - "gpt-4o-realtime-preview-2024-12-17", - "gpt-4o-mini-realtime-preview", - "gpt-4o-mini-realtime-preview-2024-12-17", - ] - ] - """The Realtime model used for this session.""" - input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ input_audio_transcription: SessionInputAudioTranscription """ @@ -123,8 +117,21 @@ class Session(TypedDict, total=False): To disable audio, set this to ["text"]. """ + model: Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + """The Realtime model used for this session.""" + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ temperature: float """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 4b53e70890..cb812a2702 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -60,11 +60,7 @@ class ChatCompletion(BaseModel): """The object type, which is always `chat.completion`.""" service_tier: Optional[Literal["scale", "default"]] = None - """The service tier used for processing the request. - - This field is only included if the `service_tier` parameter is specified in the - request. - """ + """The service tier used for processing the request.""" system_fingerprint: Optional[str] = None """This fingerprint represents the backend configuration that the model runs with. diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 35e3a3d784..229fb822f4 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -38,8 +38,8 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): """The role of the messages author, in this case `assistant`.""" audio: Optional[Audio] - """Data about a previous audio response from the model. - + """ + Data about a previous audio response from the model. [Learn more](https://platform.openai.com/docs/guides/audio). """ diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 9ec6dc4bdb..7b0ae2e121 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -129,11 +129,7 @@ class ChatCompletionChunk(BaseModel): """The object type, which is always `chat.completion.chunk`.""" service_tier: Optional[Literal["scale", "default"]] = None - """The service tier used for processing the request. - - This field is only included if the `service_tier` parameter is specified in the - request. - """ + """The service tier used for processing the request.""" system_fingerprint: Optional[str] = None """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index f168ddea6e..30d930b120 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -220,9 +220,6 @@ class CompletionCreateParamsBase(TypedDict, total=False): - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - - When this parameter is set, the response body will include the `service_tier` - utilized. """ stop: Union[Optional[str], List[str]] diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py index 1385762885..a90566449b 100644 --- a/src/openai/types/embedding_create_params.py +++ b/src/openai/types/embedding_create_params.py @@ -19,7 +19,8 @@ class EmbeddingCreateParams(TypedDict, total=False): (8192 tokens for `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) - for counting tokens. + for counting tokens. Some models may also impose a limit on total number of + tokens summed across inputs. """ model: Required[Union[str, EmbeddingModel]] diff --git a/tests/api_resources/beta/realtime/test_sessions.py b/tests/api_resources/beta/realtime/test_sessions.py index 65bfa27572..908aa983be 100644 --- a/tests/api_resources/beta/realtime/test_sessions.py +++ b/tests/api_resources/beta/realtime/test_sessions.py @@ -19,20 +19,18 @@ class TestSessions: @parametrize def test_method_create(self, client: OpenAI) -> None: - session = client.beta.realtime.sessions.create( - model="gpt-4o-realtime-preview", - ) + session = client.beta.realtime.sessions.create() assert_matches_type(SessionCreateResponse, session, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: session = client.beta.realtime.sessions.create( - model="gpt-4o-realtime-preview", input_audio_format="pcm16", input_audio_transcription={"model": "model"}, instructions="instructions", max_response_output_tokens=0, modalities=["text"], + model="gpt-4o-realtime-preview", output_audio_format="pcm16", temperature=0, tool_choice="tool_choice", @@ -57,9 +55,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: - response = client.beta.realtime.sessions.with_raw_response.create( - model="gpt-4o-realtime-preview", - ) + response = client.beta.realtime.sessions.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -68,9 +64,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: - with client.beta.realtime.sessions.with_streaming_response.create( - model="gpt-4o-realtime-preview", - ) as response: + with client.beta.realtime.sessions.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -85,20 +79,18 @@ class TestAsyncSessions: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: - session = await async_client.beta.realtime.sessions.create( - model="gpt-4o-realtime-preview", - ) + session = await async_client.beta.realtime.sessions.create() assert_matches_type(SessionCreateResponse, session, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: session = await async_client.beta.realtime.sessions.create( - model="gpt-4o-realtime-preview", input_audio_format="pcm16", input_audio_transcription={"model": "model"}, instructions="instructions", max_response_output_tokens=0, modalities=["text"], + model="gpt-4o-realtime-preview", output_audio_format="pcm16", temperature=0, tool_choice="tool_choice", @@ -123,9 +115,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.realtime.sessions.with_raw_response.create( - model="gpt-4o-realtime-preview", - ) + response = await async_client.beta.realtime.sessions.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -134,9 +124,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.realtime.sessions.with_streaming_response.create( - model="gpt-4o-realtime-preview", - ) as response: + async with async_client.beta.realtime.sessions.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 393a790549..25c9a36164 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -72,7 +72,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: presence_penalty=-2, reasoning_effort="low", response_format={"type": "text"}, - seed=-9007199254740991, + seed=0, service_tier="auto", stop="string", store=True, @@ -187,7 +187,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: presence_penalty=-2, reasoning_effort="low", response_format={"type": "text"}, - seed=-9007199254740991, + seed=0, service_tier="auto", stop="string", store=True, @@ -321,7 +321,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn presence_penalty=-2, reasoning_effort="low", response_format={"type": "text"}, - seed=-9007199254740991, + seed=0, service_tier="auto", stop="string", store=True, @@ -436,7 +436,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn presence_penalty=-2, reasoning_effort="low", response_format={"type": "text"}, - seed=-9007199254740991, + seed=0, service_tier="auto", stop="string", store=True, diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index ad2679cabe..9ec503c1e3 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -38,7 +38,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_tokens=16, n=1, presence_penalty=-2, - seed=-9007199254740991, + seed=0, stop="\n", stream=False, stream_options={"include_usage": True}, @@ -98,7 +98,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_tokens=16, n=1, presence_penalty=-2, - seed=-9007199254740991, + seed=0, stop="\n", stream_options={"include_usage": True}, suffix="test.", @@ -160,7 +160,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_tokens=16, n=1, presence_penalty=-2, - seed=-9007199254740991, + seed=0, stop="\n", stream=False, stream_options={"include_usage": True}, @@ -220,7 +220,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_tokens=16, n=1, presence_penalty=-2, - seed=-9007199254740991, + seed=0, stop="\n", stream_options={"include_usage": True}, suffix="test.", From c111dab6af1bb40e1f8768c9941dc7c292293e59 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:22:04 +0000 Subject: [PATCH 762/914] release: 1.60.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 32b4e18516..88c2f64985 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.59.9" + ".": "1.60.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 86951242c5..9f90d3dd22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 1.60.0 (2025-01-22) + +Full Changelog: [v1.59.9...v1.60.0](https://github.com/openai/openai-python/compare/v1.59.9...v1.60.0) + +### Features + +* **api:** update enum values, comments, and examples ([#2045](https://github.com/openai/openai-python/issues/2045)) ([e8205fd](https://github.com/openai/openai-python/commit/e8205fd58f0d677f476c577a8d9afb90f5710506)) + + +### Chores + +* **internal:** minor style changes ([#2043](https://github.com/openai/openai-python/issues/2043)) ([89a9dd8](https://github.com/openai/openai-python/commit/89a9dd821eaf5300ad11b0270b61fdfa4fd6e9b6)) + + +### Documentation + +* **readme:** mention failed requests in request IDs ([5f7c30b](https://github.com/openai/openai-python/commit/5f7c30bc006ffb666c324011a68aae357cb33e35)) + ## 1.59.9 (2025-01-20) Full Changelog: [v1.59.8...v1.59.9](https://github.com/openai/openai-python/compare/v1.59.8...v1.59.9) diff --git a/pyproject.toml b/pyproject.toml index 0b142a6bd8..80c79ff585 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.59.9" +version = "1.60.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7c92c3adf3..ef0ddcfc5c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.59.9" # x-release-please-version +__version__ = "1.60.0" # x-release-please-version From abc5459c7504eec25a67b35104e2e09e7d8f232c Mon Sep 17 00:00:00 2001 From: Fernando de Oliveira <5161098+fedeoliv@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:44:26 -0500 Subject: [PATCH 763/914] docs(examples/azure): add async snippet (#1787) --- examples/azure_ad.py | 79 ++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/examples/azure_ad.py b/examples/azure_ad.py index 1b0d81863d..67e2f23713 100755 --- a/examples/azure_ad.py +++ b/examples/azure_ad.py @@ -1,30 +1,67 @@ -from azure.identity import DefaultAzureCredential, get_bearer_token_provider +import asyncio -from openai import AzureOpenAI +from openai.lib.azure import AzureOpenAI, AsyncAzureOpenAI, AzureADTokenProvider, AsyncAzureADTokenProvider -token_provider = get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default") +scopes = "https://cognitiveservices.azure.com/.default" - -# may change in the future +# May change in the future # https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning api_version = "2023-07-01-preview" # https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource endpoint = "https://my-resource.openai.azure.com/" -client = AzureOpenAI( - api_version=api_version, - azure_endpoint=endpoint, - azure_ad_token_provider=token_provider, -) - -completion = client.chat.completions.create( - model="deployment-name", # e.g. gpt-35-instant - messages=[ - { - "role": "user", - "content": "How do I output all files in a directory using Python?", - }, - ], -) -print(completion.to_json()) +deployment_name = "deployment-name" # e.g. gpt-35-instant + + +def sync_main() -> None: + from azure.identity import DefaultAzureCredential, get_bearer_token_provider + + token_provider: AzureADTokenProvider = get_bearer_token_provider(DefaultAzureCredential(), scopes) + + client = AzureOpenAI( + api_version=api_version, + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + ) + + completion = client.chat.completions.create( + model=deployment_name, + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + } + ], + ) + + print(completion.to_json()) + + +async def async_main() -> None: + from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider + + token_provider: AsyncAzureADTokenProvider = get_bearer_token_provider(DefaultAzureCredential(), scopes) + + client = AsyncAzureOpenAI( + api_version=api_version, + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + ) + + completion = await client.chat.completions.create( + model=deployment_name, + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + } + ], + ) + + print(completion.to_json()) + + +sync_main() + +asyncio.run(async_main()) From 27d0e67b1d121ccc5b48c95e1f0bc3f6e93e9bd3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 12:39:04 +0000 Subject: [PATCH 764/914] chore(internal): minor formatting changes (#2050) --- .github/workflows/ci.yml | 1 + scripts/bootstrap | 2 +- scripts/lint | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de70348b9c..26f497db1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,7 @@ jobs: - name: Run lints run: ./scripts/lint + test: name: test runs-on: ubuntu-latest diff --git a/scripts/bootstrap b/scripts/bootstrap index 29df07e77b..9910ec05fc 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then brew bundle check >/dev/null 2>&1 || { echo "==> Installing Homebrew dependencies…" brew bundle diff --git a/scripts/lint b/scripts/lint index 64495ee345..55bc1dd711 100755 --- a/scripts/lint +++ b/scripts/lint @@ -9,4 +9,3 @@ rye run lint echo "==> Making sure it imports" rye run python -c 'import openai' - From b95be16e7c8a76c3d63335df13ab0d55ba3d5c35 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 05:04:08 +0000 Subject: [PATCH 765/914] release: 1.60.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 88c2f64985..0b39405429 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.60.0" + ".": "1.60.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f90d3dd22..873a6a254d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.60.1 (2025-01-24) + +Full Changelog: [v1.60.0...v1.60.1](https://github.com/openai/openai-python/compare/v1.60.0...v1.60.1) + +### Chores + +* **internal:** minor formatting changes ([#2050](https://github.com/openai/openai-python/issues/2050)) ([9c44192](https://github.com/openai/openai-python/commit/9c44192be5776d9252d36dc027a33c60b33d81b2)) + + +### Documentation + +* **examples/azure:** add async snippet ([#1787](https://github.com/openai/openai-python/issues/1787)) ([f60eda1](https://github.com/openai/openai-python/commit/f60eda1c1e8caf0ec2274b18b3fb2252304196db)) + ## 1.60.0 (2025-01-22) Full Changelog: [v1.59.9...v1.60.0](https://github.com/openai/openai-python/compare/v1.59.9...v1.60.0) diff --git a/pyproject.toml b/pyproject.toml index 80c79ff585..fdfc9c73e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.60.0" +version = "1.60.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ef0ddcfc5c..b87aa8abd4 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.60.0" # x-release-please-version +__version__ = "1.60.1" # x-release-please-version From 257d79e8a00144a7317d511401da2432a4201c7b Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 27 Jan 2025 19:20:32 +0000 Subject: [PATCH 766/914] fix(parsing): don't validate input tools in the asynchronous `.parse()` method --- src/openai/resources/beta/chat/completions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 48cb13f7a6..7771d2ff50 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -268,6 +268,8 @@ def stream( When the context manager exits, the response will be closed, however the `stream` instance is still available outside the context manager. """ + _validate_input_tools(tools) + extra_headers = { "X-Stainless-Helper-Method": "beta.chat.completions.stream", **(extra_headers or {}), From d16e6edde5a155626910b5758a0b939bfedb9ced Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 19:26:48 +0000 Subject: [PATCH 767/914] release: 1.60.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0b39405429..73f712c242 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.60.1" + ".": "1.60.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 873a6a254d..168d98e5cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.60.2 (2025-01-27) + +Full Changelog: [v1.60.1...v1.60.2](https://github.com/openai/openai-python/compare/v1.60.1...v1.60.2) + +### Bug Fixes + +* **parsing:** don't validate input tools in the asynchronous `.parse()` method ([6fcfe73](https://github.com/openai/openai-python/commit/6fcfe73cd335853c7dd2cd3151a0d5d1785cfc9c)) + ## 1.60.1 (2025-01-24) Full Changelog: [v1.60.0...v1.60.1](https://github.com/openai/openai-python/compare/v1.60.0...v1.60.1) diff --git a/pyproject.toml b/pyproject.toml index fdfc9c73e3..9657bdc0ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.60.1" +version = "1.60.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b87aa8abd4..c8f825db34 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.60.1" # x-release-please-version +__version__ = "1.60.2" # x-release-please-version From 90e3d39655548c935002dee7ef6f617c846c123c Mon Sep 17 00:00:00 2001 From: Guspan Tanadi <36249910+guspan-tanadi@users.noreply.github.com> Date: Wed, 29 Jan 2025 04:58:34 +0700 Subject: [PATCH 768/914] docs(readme): current section links (#2055) chore(helpers): section links --- README.md | 4 ++-- helpers.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5f7d477cc8..3c103f036c 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ However the real magic of the Realtime API is handling audio inputs / outputs, s ### Realtime error handling -Whenever an error occurs, the Realtime API will send an [`error` event](https://platform.openai.com/docs/guides/realtime/realtime-api-beta#handling-errors) and the connection will stay open and remain usable. This means you need to handle it yourself, as *no errors are raised directly* by the SDK when an `error` event comes in. +Whenever an error occurs, the Realtime API will send an [`error` event](https://platform.openai.com/docs/guides/realtime-model-capabilities#error-handling) and the connection will stay open and remain usable. This means you need to handle it yourself, as *no errors are raised directly* by the SDK when an `error` event comes in. ```py client = AsyncOpenAI() @@ -547,7 +547,7 @@ client.with_options(max_retries=5).chat.completions.create( ### Timeouts By default requests time out after 10 minutes. You can configure this with a `timeout` option, -which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object: +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: ```python from openai import OpenAI diff --git a/helpers.md b/helpers.md index 3f3fafa45c..77823fa750 100644 --- a/helpers.md +++ b/helpers.md @@ -134,7 +134,7 @@ OpenAI supports streaming responses when interacting with the [Chat Completion]( The SDK provides a `.beta.chat.completions.stream()` method that wraps the `.chat.completions.create(stream=True)` stream providing a more granular event API & automatic accumulation of each delta. -It also supports all aforementioned [parsing helpers](#parsing-helpers). +It also supports all aforementioned [parsing helpers](#structured-outputs-parsing-helpers). Unlike `.create(stream=True)`, the `.stream()` method requires usage within a context manager to prevent accidental leakage of the response: From d779e40bc960c1d5fcf1b23b804af9b9ccf43c58 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:09:46 +0000 Subject: [PATCH 769/914] chore: update api.md (#2063) --- api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.md b/api.md index 1edd3f6589..f1e0d023bd 100644 --- a/api.md +++ b/api.md @@ -99,7 +99,7 @@ Methods: - client.files.list(\*\*params) -> SyncCursorPage[FileObject] - client.files.delete(file_id) -> FileDeleted - client.files.content(file_id) -> HttpxBinaryResponseContent -- client.files.retrieve_content(file_id) -> str +- client.files.retrieve_content(file_id) -> str - client.files.wait_for_processing(\*args) -> FileObject # Images From a99096823a878f0725ef1433226b3bc725c4c618 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 31 Jan 2025 11:47:20 +0000 Subject: [PATCH 770/914] Revert "fix(parsing): don't validate input tools in the asynchronous `.parse()` method" This reverts commit 257d79e8a00144a7317d511401da2432a4201c7b. --- src/openai/resources/beta/chat/completions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 7771d2ff50..48cb13f7a6 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -268,8 +268,6 @@ def stream( When the context manager exits, the response will be closed, however the `stream` instance is still available outside the context manager. """ - _validate_input_tools(tools) - extra_headers = { "X-Stainless-Helper-Method": "beta.chat.completions.stream", **(extra_headers or {}), From fdd52476b56cbd57d0cbc27d06f9d2907b537e82 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 31 Jan 2025 19:09:53 +0000 Subject: [PATCH 771/914] feat(api): add o3-mini (#2067) fix(types): correct metadata type + other fixes --- .stats.yml | 2 +- api.md | 1 + src/openai/resources/audio/transcriptions.py | 8 +- src/openai/resources/batches.py | 26 ++-- src/openai/resources/beta/assistants.py | 41 +++--- .../resources/beta/realtime/sessions.py | 14 +- src/openai/resources/beta/threads/messages.py | 41 +++--- .../resources/beta/threads/runs/runs.py | 85 +++++++----- src/openai/resources/beta/threads/threads.py | 123 +++++++++++------- .../beta/vector_stores/vector_stores.py | 41 +++--- src/openai/resources/chat/completions.py | 89 ++++++++----- src/openai/types/__init__.py | 1 + .../audio/transcription_create_params.py | 4 +- src/openai/types/batch.py | 10 +- src/openai/types/batch_create_params.py | 15 ++- src/openai/types/beta/assistant.py | 9 +- .../types/beta/assistant_create_params.py | 21 +-- .../types/beta/assistant_update_params.py | 9 +- .../conversation_item_create_event.py | 12 +- .../conversation_item_create_event_param.py | 12 +- .../types/beta/realtime/realtime_response.py | 51 +++++++- .../beta/realtime/response_create_event.py | 9 +- .../realtime/response_create_event_param.py | 9 +- .../beta/realtime/session_create_params.py | 23 +++- .../beta/realtime/session_create_response.py | 6 +- .../beta/realtime/session_update_event.py | 23 +++- .../realtime/session_update_event_param.py | 23 +++- src/openai/types/beta/thread.py | 9 +- .../beta/thread_create_and_run_params.py | 43 +++--- src/openai/types/beta/thread_create_params.py | 29 +++-- src/openai/types/beta/thread_update_params.py | 10 +- src/openai/types/beta/threads/message.py | 9 +- .../beta/threads/message_create_params.py | 9 +- .../beta/threads/message_update_params.py | 10 +- src/openai/types/beta/threads/run.py | 9 +- .../types/beta/threads/run_create_params.py | 17 ++- .../types/beta/threads/run_update_params.py | 10 +- .../types/beta/threads/runs/run_step.py | 9 +- src/openai/types/beta/vector_store.py | 9 +- .../types/beta/vector_store_create_params.py | 9 +- .../types/beta/vector_store_update_params.py | 10 +- ...chat_completion_assistant_message_param.py | 4 +- .../types/chat/completion_create_params.py | 17 ++- src/openai/types/chat_model.py | 2 + src/openai/types/shared/__init__.py | 1 + src/openai/types/shared/metadata.py | 8 ++ src/openai/types/shared_params/__init__.py | 1 + src/openai/types/shared_params/metadata.py | 10 ++ src/openai/types/upload.py | 2 +- .../beta/realtime/test_sessions.py | 12 +- tests/api_resources/beta/test_assistants.py | 12 +- tests/api_resources/beta/test_threads.py | 52 ++++---- .../api_resources/beta/test_vector_stores.py | 16 +-- .../beta/threads/test_messages.py | 16 +-- tests/api_resources/beta/threads/test_runs.py | 28 ++-- 55 files changed, 710 insertions(+), 371 deletions(-) create mode 100644 src/openai/types/shared/metadata.py create mode 100644 src/openai/types/shared_params/metadata.py diff --git a/.stats.yml b/.stats.yml index d518bac586..e49b5c56e8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-3904ef6b29a89c98f93a9b7da19879695f3c440564be6384db7af1b734611ede.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-6204952a29973265b9c0d66fc67ffaf53c6a90ae4d75cdacf9d147676f5274c9.yml diff --git a/api.md b/api.md index f1e0d023bd..c1262fd2c5 100644 --- a/api.md +++ b/api.md @@ -5,6 +5,7 @@ from openai.types import ( ErrorObject, FunctionDefinition, FunctionParameters, + Metadata, ResponseFormatJSONObject, ResponseFormatJSONSchema, ResponseFormatText, diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 341446c43a..f338ad067d 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -138,8 +138,8 @@ def create( Whisper V2 model) is currently available. language: The language of the input audio. Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will - improve accuracy and latency. + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. prompt: An optional text to guide the model's style or continue a previous audio segment. The @@ -302,8 +302,8 @@ async def create( Whisper V2 model) is currently available. language: The language of the input audio. Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will - improve accuracy and latency. + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. prompt: An optional text to guide the model's style or continue a previous audio segment. The diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 4a887642e9..7e7ec19ec2 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, Optional +from typing import Optional from typing_extensions import Literal import httpx @@ -19,10 +19,8 @@ from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncCursorPage, AsyncCursorPage from ..types.batch import Batch -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.shared_params.metadata import Metadata __all__ = ["Batches", "AsyncBatches"] @@ -53,7 +51,7 @@ def create( completion_window: Literal["24h"], endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -83,7 +81,12 @@ def create( and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 200 MB in size. - metadata: Optional custom metadata for the batch. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers @@ -258,7 +261,7 @@ async def create( completion_window: Literal["24h"], endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -288,7 +291,12 @@ async def create( and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 200 MB in size. - metadata: Optional custom metadata for the batch. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 2f2482b648..65b7c9cfc2 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -26,6 +26,7 @@ from ...types.chat_model import ChatModel from ...types.beta.assistant import Assistant from ...types.beta.assistant_deleted import AssistantDeleted +from ...types.shared_params.metadata import Metadata from ...types.beta.assistant_tool_param import AssistantToolParam from ...types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -58,7 +59,7 @@ def create( model: Union[str, ChatModel], description: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -88,9 +89,11 @@ def create( characters. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. name: The name of the assistant. The maximum length is 256 characters. @@ -206,7 +209,7 @@ def update( *, description: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -232,9 +235,11 @@ def update( characters. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to @@ -444,7 +449,7 @@ async def create( model: Union[str, ChatModel], description: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -474,9 +479,11 @@ async def create( characters. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. name: The name of the assistant. The maximum length is 256 characters. @@ -592,7 +599,7 @@ async def update( *, description: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: str | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -618,9 +625,11 @@ async def update( characters. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index b920c89207..4b337b7c19 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -89,8 +89,11 @@ def create( input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs - asynchronously through Whisper and should be treated as rough guidance rather - than the representation understood by the model. + asynchronously through + [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as rough guidance rather than the representation + understood by the model. The client can optionally set the language and prompt + for transcription, these fields will be passed to the Whisper API. instructions: The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model @@ -232,8 +235,11 @@ async def create( input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs - asynchronously through Whisper and should be treated as rough guidance rather - than the representation understood by the model. + asynchronously through + [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as rough guidance rather than the representation + understood by the model. The client can optionally set the language and prompt + for transcription, these fields will be passed to the Whisper API. instructions: The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index f780f6f558..e3374aba37 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -23,6 +23,7 @@ ) from ....types.beta.threads import message_list_params, message_create_params, message_update_params from ....types.beta.threads.message import Message +from ....types.shared_params.metadata import Metadata from ....types.beta.threads.message_deleted import MessageDeleted from ....types.beta.threads.message_content_part_param import MessageContentPartParam @@ -56,7 +57,7 @@ def create( content: Union[str, Iterable[MessageContentPartParam]], role: Literal["user", "assistant"], attachments: Optional[Iterable[message_create_params.Attachment]] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -81,9 +82,11 @@ def create( attachments: A list of files attached to the message, and the tools they should be added to. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers @@ -155,7 +158,7 @@ def update( message_id: str, *, thread_id: str, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -168,9 +171,11 @@ def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers @@ -330,7 +335,7 @@ async def create( content: Union[str, Iterable[MessageContentPartParam]], role: Literal["user", "assistant"], attachments: Optional[Iterable[message_create_params.Attachment]] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -355,9 +360,11 @@ async def create( attachments: A list of files attached to the message, and the tools they should be added to. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers @@ -429,7 +436,7 @@ async def update( message_id: str, *, thread_id: str, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -442,9 +449,11 @@ async def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index f32a08f235..9cb202a1a2 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -47,6 +47,7 @@ run_submit_tool_outputs_params, ) from .....types.beta.threads.run import Run +from .....types.shared_params.metadata import Metadata from .....types.beta.assistant_tool_param import AssistantToolParam from .....types.beta.assistant_stream_event import AssistantStreamEvent from .....types.beta.threads.runs.run_step_include import RunStepInclude @@ -92,7 +93,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -148,9 +149,11 @@ def create( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -233,7 +236,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -292,9 +295,11 @@ def create( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -373,7 +378,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -432,9 +437,11 @@ def create( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -512,7 +519,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -609,7 +616,7 @@ def update( run_id: str, *, thread_id: str, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -622,9 +629,11 @@ def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers @@ -1457,7 +1466,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1513,9 +1522,11 @@ async def create( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -1598,7 +1609,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1657,9 +1668,11 @@ async def create( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -1738,7 +1751,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1797,9 +1810,11 @@ async def create( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -1877,7 +1892,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1974,7 +1989,7 @@ async def update( run_id: str, *, thread_id: str, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1987,9 +2002,11 @@ async def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 186b6f63e2..0ec59aca55 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -53,6 +53,7 @@ from ....types.beta.thread import Thread from ....types.beta.threads.run import Run from ....types.beta.thread_deleted import ThreadDeleted +from ....types.shared_params.metadata import Metadata from ....types.beta.assistant_stream_event import AssistantStreamEvent from ....types.beta.assistant_tool_choice_option_param import AssistantToolChoiceOptionParam from ....types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -92,7 +93,7 @@ def create( self, *, messages: Iterable[thread_create_params.Message] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_params.ToolResources] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -109,9 +110,11 @@ def create( start the thread with. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. tool_resources: A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the @@ -181,7 +184,7 @@ def update( self, thread_id: str, *, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_update_params.ToolResources] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -195,9 +198,11 @@ def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. tool_resources: A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the @@ -272,7 +277,7 @@ def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -315,9 +320,11 @@ def create_and_run( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -357,7 +364,8 @@ def create_and_run( make the output more random, while lower values like 0.2 will make it more focused and deterministic. - thread: If no thread is provided, an empty thread will be created. + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value @@ -403,7 +411,7 @@ def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -449,9 +457,11 @@ def create_and_run( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -487,7 +497,8 @@ def create_and_run( make the output more random, while lower values like 0.2 will make it more focused and deterministic. - thread: If no thread is provided, an empty thread will be created. + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value @@ -533,7 +544,7 @@ def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -579,9 +590,11 @@ def create_and_run( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -617,7 +630,8 @@ def create_and_run( make the output more random, while lower values like 0.2 will make it more focused and deterministic. - thread: If no thread is provided, an empty thread will be created. + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value @@ -662,7 +676,7 @@ def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -926,7 +940,7 @@ async def create( self, *, messages: Iterable[thread_create_params.Message] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_params.ToolResources] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -943,9 +957,11 @@ async def create( start the thread with. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. tool_resources: A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the @@ -1015,7 +1031,7 @@ async def update( self, thread_id: str, *, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_update_params.ToolResources] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1029,9 +1045,11 @@ async def update( Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. tool_resources: A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the @@ -1106,7 +1124,7 @@ async def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1149,9 +1167,11 @@ async def create_and_run( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -1191,7 +1211,8 @@ async def create_and_run( make the output more random, while lower values like 0.2 will make it more focused and deterministic. - thread: If no thread is provided, an empty thread will be created. + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value @@ -1237,7 +1258,7 @@ async def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1283,9 +1304,11 @@ async def create_and_run( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -1321,7 +1344,8 @@ async def create_and_run( make the output more random, while lower values like 0.2 will make it more focused and deterministic. - thread: If no thread is provided, an empty thread will be created. + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value @@ -1367,7 +1391,7 @@ async def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1413,9 +1437,11 @@ async def create_and_run( `incomplete_details` for more info. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the @@ -1451,7 +1477,8 @@ async def create_and_run( make the output more random, while lower values like 0.2 will make it more focused and deterministic. - thread: If no thread is provided, an empty thread will be created. + thread: Options to create a new thread. If no thread is provided when running a request, + an empty thread will be created. tool_choice: Controls which (if any) tool is called by the model. `none` means the model will not call any tools and instead generates a message. `auto` is the default value @@ -1496,7 +1523,7 @@ async def create_and_run( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/beta/vector_stores/vector_stores.py index 6b44c602f1..1da52fb3c7 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/beta/vector_stores/vector_stores.py @@ -41,6 +41,7 @@ ) from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.vector_store import VectorStore +from ....types.shared_params.metadata import Metadata from ....types.beta.vector_store_deleted import VectorStoreDeleted from ....types.beta.file_chunking_strategy_param import FileChunkingStrategyParam @@ -81,7 +82,7 @@ def create( chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, file_ids: List[str] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -104,9 +105,11 @@ def create( files. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. name: The name of the vector store. @@ -176,7 +179,7 @@ def update( vector_store_id: str, *, expires_after: Optional[vector_store_update_params.ExpiresAfter] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -192,9 +195,11 @@ def update( expires_after: The expiration policy for a vector store. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. name: The name of the vector store. @@ -359,7 +364,7 @@ async def create( chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, file_ids: List[str] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -382,9 +387,11 @@ async def create( files. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. name: The name of the vector store. @@ -454,7 +461,7 @@ async def update( vector_store_id: str, *, expires_after: Optional[vector_store_update_params.ExpiresAfter] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -470,9 +477,11 @@ async def update( expires_after: The expiration policy for a vector store. metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format. Keys - can be a maximum of 64 characters long and values can be a maximum of 512 - characters long. + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. name: The name of the vector store. diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index a9685c507a..34f6b50301 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -28,6 +28,7 @@ from ..._base_client import make_request_options from ...types.chat_model import ChatModel from ...types.chat.chat_completion import ChatCompletion +from ...types.shared_params.metadata import Metadata from ...types.chat.chat_completion_chunk import ChatCompletionChunk from ...types.chat.chat_completion_modality import ChatCompletionModality from ...types.chat.chat_completion_tool_param import ChatCompletionToolParam @@ -75,7 +76,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -179,8 +180,12 @@ def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). - metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/chat-completions). + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. modalities: Output types that you would like the model to generate for this request. Most models are capable of generating text, which is the default: @@ -246,9 +251,9 @@ def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarentee. + latency guarantee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarentee. + tier with a lower uptime SLA and no latency guarantee. - When not set, the default behavior is 'auto'. stop: Up to 4 sequences where the API will stop generating further tokens. @@ -324,7 +329,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -434,8 +439,12 @@ def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). - metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/chat-completions). + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. modalities: Output types that you would like the model to generate for this request. Most models are capable of generating text, which is the default: @@ -501,9 +510,9 @@ def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarentee. + latency guarantee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarentee. + tier with a lower uptime SLA and no latency guarantee. - When not set, the default behavior is 'auto'. stop: Up to 4 sequences where the API will stop generating further tokens. @@ -572,7 +581,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -682,8 +691,12 @@ def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). - metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/chat-completions). + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. modalities: Output types that you would like the model to generate for this request. Most models are capable of generating text, which is the default: @@ -749,9 +762,9 @@ def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarentee. + latency guarantee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarentee. + tier with a lower uptime SLA and no latency guarantee. - When not set, the default behavior is 'auto'. stop: Up to 4 sequences where the API will stop generating further tokens. @@ -819,7 +832,7 @@ def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -927,7 +940,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -1031,8 +1044,12 @@ async def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). - metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/chat-completions). + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. modalities: Output types that you would like the model to generate for this request. Most models are capable of generating text, which is the default: @@ -1098,9 +1115,9 @@ async def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarentee. + latency guarantee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarentee. + tier with a lower uptime SLA and no latency guarantee. - When not set, the default behavior is 'auto'. stop: Up to 4 sequences where the API will stop generating further tokens. @@ -1176,7 +1193,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -1286,8 +1303,12 @@ async def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). - metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/chat-completions). + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. modalities: Output types that you would like the model to generate for this request. Most models are capable of generating text, which is the default: @@ -1353,9 +1374,9 @@ async def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarentee. + latency guarantee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarentee. + tier with a lower uptime SLA and no latency guarantee. - When not set, the default behavior is 'auto'. stop: Up to 4 sequences where the API will stop generating further tokens. @@ -1424,7 +1445,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -1534,8 +1555,12 @@ async def create( compatible with [o1 series models](https://platform.openai.com/docs/guides/reasoning). - metadata: Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/chat-completions). + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. modalities: Output types that you would like the model to generate for this request. Most models are capable of generating text, which is the default: @@ -1601,9 +1626,9 @@ async def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarentee. + latency guarantee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarentee. + tier with a lower uptime SLA and no latency guarantee. - When not set, the default behavior is 'auto'. stop: Up to 4 sequences where the API will stop generating further tokens. @@ -1671,7 +1696,7 @@ async def create( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 72950f2491..7abb22f239 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -6,6 +6,7 @@ from .image import Image as Image from .model import Model as Model from .shared import ( + Metadata as Metadata, ErrorObject as ErrorObject, FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters, diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index 88805affbd..f1779c35e6 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -30,8 +30,8 @@ class TranscriptionCreateParams(TypedDict, total=False): """The language of the input audio. Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will - improve accuracy and latency. + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. """ prompt: str diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py index ac3d7ea119..35de90ac85 100644 --- a/src/openai/types/batch.py +++ b/src/openai/types/batch.py @@ -1,11 +1,11 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import builtins from typing import List, Optional from typing_extensions import Literal from .._models import BaseModel from .batch_error import BatchError +from .shared.metadata import Metadata from .batch_request_counts import BatchRequestCounts __all__ = ["Batch", "Errors"] @@ -70,12 +70,14 @@ class Batch(BaseModel): in_progress_at: Optional[int] = None """The Unix timestamp (in seconds) for when the batch started processing.""" - metadata: Optional[builtins.object] = None + metadata: Optional[Metadata] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ output_file_id: Optional[str] = None diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index b30c4d4658..e5be1d2bac 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing import Dict, Optional +from typing import Optional from typing_extensions import Literal, Required, TypedDict +from .shared_params.metadata import Metadata + __all__ = ["BatchCreateParams"] @@ -35,5 +37,12 @@ class BatchCreateParams(TypedDict, total=False): requests, and can be up to 200 MB in size. """ - metadata: Optional[Dict[str, str]] - """Optional custom metadata for the batch.""" + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 3c8b8e403b..58421e0f66 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -5,6 +5,7 @@ from ..._models import BaseModel from .assistant_tool import AssistantTool +from ..shared.metadata import Metadata from .assistant_response_format_option import AssistantResponseFormatOption __all__ = ["Assistant", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] @@ -51,12 +52,14 @@ class Assistant(BaseModel): The maximum length is 256,000 characters. """ - metadata: Optional[object] = None + metadata: Optional[Metadata] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ model: str diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 568b223ce7..e205856395 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -7,6 +7,7 @@ from ..chat_model import ChatModel from .assistant_tool_param import AssistantToolParam +from ..shared_params.metadata import Metadata from .file_chunking_strategy_param import FileChunkingStrategyParam from .assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -39,12 +40,14 @@ class AssistantCreateParams(TypedDict, total=False): The maximum length is 256,000 characters. """ - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ name: Optional[str] @@ -130,12 +133,14 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): store. """ - metadata: object - """Set of 16 key-value pairs that can be attached to a vector store. + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. - This can be useful for storing additional information about the vector store in - a structured format. Keys can be a maximum of 64 characters long and values can - be a maximum of 512 characters long. + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 9a66e41ab3..35065ef61b 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -6,6 +6,7 @@ from typing_extensions import TypedDict from .assistant_tool_param import AssistantToolParam +from ..shared_params.metadata import Metadata from .assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = ["AssistantUpdateParams", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] @@ -21,12 +22,14 @@ class AssistantUpdateParams(TypedDict, total=False): The maximum length is 256,000 characters. """ - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ model: str diff --git a/src/openai/types/beta/realtime/conversation_item_create_event.py b/src/openai/types/beta/realtime/conversation_item_create_event.py index c4f72b9aff..f19d552a92 100644 --- a/src/openai/types/beta/realtime/conversation_item_create_event.py +++ b/src/openai/types/beta/realtime/conversation_item_create_event.py @@ -20,10 +20,10 @@ class ConversationItemCreateEvent(BaseModel): """Optional client-generated ID used to identify this event.""" previous_item_id: Optional[str] = None - """ - The ID of the preceding item after which the new item will be inserted. If not - set, the new item will be appended to the end of the conversation. If set to - `root`, the new item will be added to the beginning of the conversation. If set - to an existing ID, it allows an item to be inserted mid-conversation. If the ID - cannot be found, an error will be returned and the item will not be added. + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. If set + to `root`, the new item will be added to the beginning of the conversation. If + set to an existing ID, it allows an item to be inserted mid-conversation. If the + ID cannot be found, an error will be returned and the item will not be added. """ diff --git a/src/openai/types/beta/realtime/conversation_item_create_event_param.py b/src/openai/types/beta/realtime/conversation_item_create_event_param.py index 6da5a63a9d..693d0fd54d 100644 --- a/src/openai/types/beta/realtime/conversation_item_create_event_param.py +++ b/src/openai/types/beta/realtime/conversation_item_create_event_param.py @@ -20,10 +20,10 @@ class ConversationItemCreateEventParam(TypedDict, total=False): """Optional client-generated ID used to identify this event.""" previous_item_id: str - """ - The ID of the preceding item after which the new item will be inserted. If not - set, the new item will be appended to the end of the conversation. If set to - `root`, the new item will be added to the beginning of the conversation. If set - to an existing ID, it allows an item to be inserted mid-conversation. If the ID - cannot be found, an error will be returned and the item will not be added. + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. If set + to `root`, the new item will be added to the beginning of the conversation. If + set to an existing ID, it allows an item to be inserted mid-conversation. If the + ID cannot be found, an error will be returned and the item will not be added. """ diff --git a/src/openai/types/beta/realtime/realtime_response.py b/src/openai/types/beta/realtime/realtime_response.py index 3e1b1406c0..4c3c83d666 100644 --- a/src/openai/types/beta/realtime/realtime_response.py +++ b/src/openai/types/beta/realtime/realtime_response.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import List, Union, Optional from typing_extensions import Literal from ...._models import BaseModel +from ...shared.metadata import Metadata from .conversation_item import ConversationItem from .realtime_response_usage import RealtimeResponseUsage from .realtime_response_status import RealtimeResponseStatus @@ -15,8 +16,40 @@ class RealtimeResponse(BaseModel): id: Optional[str] = None """The unique ID of the response.""" - metadata: Optional[object] = None - """Developer-provided string key-value pairs associated with this response.""" + conversation_id: Optional[str] = None + """ + Which conversation the response is added to, determined by the `conversation` + field in the `response.create` event. If `auto`, the response will be added to + the default conversation and the value of `conversation_id` will be an id like + `conv_1234`. If `none`, the response will not be added to any conversation and + the value of `conversation_id` will be `null`. If responses are being triggered + by server VAD, the response will be added to the default conversation, thus the + `conversation_id` will be an id like `conv_1234`. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls, that was used in this response. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model used to respond. + + If there are multiple modalities, the model will pick one, for example if + `modalities` is `["text", "audio"]`, the model could be responding in either + text or audio. + """ object: Optional[Literal["realtime.response"]] = None """The object type, must be `realtime.response`.""" @@ -24,6 +57,9 @@ class RealtimeResponse(BaseModel): output: Optional[List[ConversationItem]] = None """The list of output items generated by the response.""" + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + status: Optional[Literal["completed", "cancelled", "failed", "incomplete"]] = None """ The final status of the response (`completed`, `cancelled`, `failed`, or @@ -33,6 +69,9 @@ class RealtimeResponse(BaseModel): status_details: Optional[RealtimeResponseStatus] = None """Additional details about the status.""" + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + usage: Optional[RealtimeResponseUsage] = None """Usage statistics for the Response, this will correspond to billing. @@ -40,3 +79,9 @@ class RealtimeResponse(BaseModel): to the Conversation, thus output from previous turns (text and audio tokens) will become the input for later turns. """ + + voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + """ + The voice the model used to respond. Current voice options are `alloy`, `ash`, + `ballad`, `coral`, `echo` `sage`, `shimmer` and `verse`. + """ diff --git a/src/openai/types/beta/realtime/response_create_event.py b/src/openai/types/beta/realtime/response_create_event.py index e4e5e7c68f..0801654bd8 100644 --- a/src/openai/types/beta/realtime/response_create_event.py +++ b/src/openai/types/beta/realtime/response_create_event.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ...._models import BaseModel +from ...shared.metadata import Metadata from .conversation_item import ConversationItem __all__ = ["ResponseCreateEvent", "Response", "ResponseTool"] @@ -66,12 +67,14 @@ class Response(BaseModel): `inf` for the maximum available tokens for a given model. Defaults to `inf`. """ - metadata: Optional[object] = None + metadata: Optional[Metadata] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ modalities: Optional[List[Literal["text", "audio"]]] = None diff --git a/src/openai/types/beta/realtime/response_create_event_param.py b/src/openai/types/beta/realtime/response_create_event_param.py index 7a4b5f086a..a87ef955e8 100644 --- a/src/openai/types/beta/realtime/response_create_event_param.py +++ b/src/openai/types/beta/realtime/response_create_event_param.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypedDict from .conversation_item_param import ConversationItemParam +from ...shared_params.metadata import Metadata __all__ = ["ResponseCreateEventParam", "Response", "ResponseTool"] @@ -67,12 +68,14 @@ class Response(TypedDict, total=False): `inf` for the maximum available tokens for a given model. Defaults to `inf`. """ - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ modalities: List[Literal["text", "audio"]] diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py index 3708efeecd..1502d83d39 100644 --- a/src/openai/types/beta/realtime/session_create_params.py +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -22,8 +22,11 @@ class SessionCreateParams(TypedDict, total=False): Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs - asynchronously through Whisper and should be treated as rough guidance rather - than the representation understood by the model. + asynchronously through + [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as rough guidance rather than the representation + understood by the model. The client can optionally set the language and prompt + for transcription, these fields will be passed to the Whisper API. """ instructions: str @@ -101,12 +104,28 @@ class SessionCreateParams(TypedDict, total=False): class InputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + model: str """ The model to use for transcription, `whisper-1` is the only currently supported model. """ + prompt: str + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + """ + class Tool(TypedDict, total=False): description: str diff --git a/src/openai/types/beta/realtime/session_create_response.py b/src/openai/types/beta/realtime/session_create_response.py index 31f591b261..c26e62bef1 100644 --- a/src/openai/types/beta/realtime/session_create_response.py +++ b/src/openai/types/beta/realtime/session_create_response.py @@ -9,13 +9,13 @@ class ClientSecret(BaseModel): - expires_at: Optional[int] = None + expires_at: int """Timestamp for when the token expires. Currently, all tokens expire after one minute. """ - value: Optional[str] = None + value: str """ Ephemeral key usable in client environments to authenticate connections to the Realtime API. Use this in client-side environments rather than a standard API @@ -74,7 +74,7 @@ class TurnDetection(BaseModel): class SessionCreateResponse(BaseModel): - client_secret: Optional[ClientSecret] = None + client_secret: ClientSecret """Ephemeral key returned by the API.""" input_audio_format: Optional[str] = None diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py index 322e588a4e..62fb0a3998 100644 --- a/src/openai/types/beta/realtime/session_update_event.py +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -9,12 +9,28 @@ class SessionInputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + model: Optional[str] = None """ The model to use for transcription, `whisper-1` is the only currently supported model. """ + prompt: Optional[str] = None + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + """ + class SessionTool(BaseModel): description: Optional[str] = None @@ -78,8 +94,11 @@ class Session(BaseModel): Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs - asynchronously through Whisper and should be treated as rough guidance rather - than the representation understood by the model. + asynchronously through + [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as rough guidance rather than the representation + understood by the model. The client can optionally set the language and prompt + for transcription, these fields will be passed to the Whisper API. """ instructions: Optional[str] = None diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py index c01d9b6887..133cdd91a1 100644 --- a/src/openai/types/beta/realtime/session_update_event_param.py +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -15,12 +15,28 @@ class SessionInputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + model: str """ The model to use for transcription, `whisper-1` is the only currently supported model. """ + prompt: str + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + """ + class SessionTool(TypedDict, total=False): description: str @@ -84,8 +100,11 @@ class Session(TypedDict, total=False): Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs - asynchronously through Whisper and should be treated as rough guidance rather - than the representation understood by the model. + asynchronously through + [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as rough guidance rather than the representation + understood by the model. The client can optionally set the language and prompt + for transcription, these fields will be passed to the Whisper API. """ instructions: str diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py index 37d50ccb93..789f66e48b 100644 --- a/src/openai/types/beta/thread.py +++ b/src/openai/types/beta/thread.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from ..shared.metadata import Metadata __all__ = ["Thread", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] @@ -40,12 +41,14 @@ class Thread(BaseModel): created_at: int """The Unix timestamp (in seconds) for when the thread was created.""" - metadata: Optional[object] = None + metadata: Optional[Metadata] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ object: Literal["thread"] diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 8310ba12f4..08f044c1be 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -8,6 +8,7 @@ from ..chat_model import ChatModel from .function_tool_param import FunctionToolParam from .file_search_tool_param import FileSearchToolParam +from ..shared_params.metadata import Metadata from .code_interpreter_tool_param import CodeInterpreterToolParam from .file_chunking_strategy_param import FileChunkingStrategyParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam @@ -67,12 +68,14 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): `incomplete_details` for more info. """ - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ model: Union[str, ChatModel, None] @@ -122,7 +125,11 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): """ thread: Thread - """If no thread is provided, an empty thread will be created.""" + """Options to create a new thread. + + If no thread is provided when running a request, an empty thread will be + created. + """ tool_choice: Optional[AssistantToolChoiceOptionParam] """ @@ -197,12 +204,14 @@ class ThreadMessage(TypedDict, total=False): attachments: Optional[Iterable[ThreadMessageAttachment]] """A list of files attached to the message, and the tools they should be added to.""" - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ @@ -230,12 +239,14 @@ class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): store. """ - metadata: object - """Set of 16 key-value pairs that can be attached to a vector store. + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. - This can be useful for storing additional information about the vector store in - a structured format. Keys can be a maximum of 64 characters long and values can - be a maximum of 512 characters long. + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ @@ -270,12 +281,14 @@ class Thread(TypedDict, total=False): start the thread with. """ - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ tool_resources: Optional[ThreadToolResources] diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index 3ac6c7d69b..127202753c 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -5,6 +5,7 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..shared_params.metadata import Metadata from .code_interpreter_tool_param import CodeInterpreterToolParam from .file_chunking_strategy_param import FileChunkingStrategyParam from .threads.message_content_part_param import MessageContentPartParam @@ -29,12 +30,14 @@ class ThreadCreateParams(TypedDict, total=False): start the thread with. """ - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ tool_resources: Optional[ToolResources] @@ -78,12 +81,14 @@ class Message(TypedDict, total=False): attachments: Optional[Iterable[MessageAttachment]] """A list of files attached to the message, and the tools they should be added to.""" - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ @@ -111,12 +116,14 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): store. """ - metadata: object - """Set of 16 key-value pairs that can be attached to a vector store. + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. - This can be useful for storing additional information about the vector store in - a structured format. Keys can be a maximum of 64 characters long and values can - be a maximum of 512 characters long. + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py index 78c5ec4f2e..b47ea8f3b0 100644 --- a/src/openai/types/beta/thread_update_params.py +++ b/src/openai/types/beta/thread_update_params.py @@ -5,16 +5,20 @@ from typing import List, Optional from typing_extensions import TypedDict +from ..shared_params.metadata import Metadata + __all__ = ["ThreadUpdateParams", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] class ThreadUpdateParams(TypedDict, total=False): - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ tool_resources: Optional[ToolResources] diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index 63c5c4800a..4a05a128eb 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -5,6 +5,7 @@ from ...._models import BaseModel from .message_content import MessageContent +from ...shared.metadata import Metadata from ..code_interpreter_tool import CodeInterpreterTool __all__ = [ @@ -66,12 +67,14 @@ class Message(BaseModel): incomplete_details: Optional[IncompleteDetails] = None """On an incomplete message, details about why the message is incomplete.""" - metadata: Optional[object] = None + metadata: Optional[Metadata] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ object: Literal["thread.message"] diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py index 2c4edfdf71..b52386824a 100644 --- a/src/openai/types/beta/threads/message_create_params.py +++ b/src/openai/types/beta/threads/message_create_params.py @@ -5,6 +5,7 @@ from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ...shared_params.metadata import Metadata from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam @@ -27,12 +28,14 @@ class MessageCreateParams(TypedDict, total=False): attachments: Optional[Iterable[Attachment]] """A list of files attached to the message, and the tools they should be added to.""" - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ diff --git a/src/openai/types/beta/threads/message_update_params.py b/src/openai/types/beta/threads/message_update_params.py index e8f8cc910c..bb078281e6 100644 --- a/src/openai/types/beta/threads/message_update_params.py +++ b/src/openai/types/beta/threads/message_update_params.py @@ -5,16 +5,20 @@ from typing import Optional from typing_extensions import Required, TypedDict +from ...shared_params.metadata import Metadata + __all__ = ["MessageUpdateParams"] class MessageUpdateParams(TypedDict, total=False): thread_id: Required[str] - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index ad32135b7d..da9418d6f9 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -6,6 +6,7 @@ from ...._models import BaseModel from .run_status import RunStatus from ..assistant_tool import AssistantTool +from ...shared.metadata import Metadata from ..assistant_tool_choice_option import AssistantToolChoiceOption from ..assistant_response_format_option import AssistantResponseFormatOption from .required_action_function_tool_call import RequiredActionFunctionToolCall @@ -133,12 +134,14 @@ class Run(BaseModel): of the run. """ - metadata: Optional[object] = None + metadata: Optional[Metadata] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ model: str diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 88dc39645e..091dd3da66 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -8,6 +8,7 @@ from ...chat_model import ChatModel from ..assistant_tool_param import AssistantToolParam from .runs.run_step_include import RunStepInclude +from ...shared_params.metadata import Metadata from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam from ..assistant_tool_choice_option_param import AssistantToolChoiceOptionParam @@ -80,12 +81,14 @@ class RunCreateParamsBase(TypedDict, total=False): `incomplete_details` for more info. """ - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ model: Union[str, ChatModel, None] @@ -199,12 +202,14 @@ class AdditionalMessage(TypedDict, total=False): attachments: Optional[Iterable[AdditionalMessageAttachment]] """A list of files attached to the message, and the tools they should be added to.""" - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ diff --git a/src/openai/types/beta/threads/run_update_params.py b/src/openai/types/beta/threads/run_update_params.py index cb4f053645..fbcbd3fb14 100644 --- a/src/openai/types/beta/threads/run_update_params.py +++ b/src/openai/types/beta/threads/run_update_params.py @@ -5,16 +5,20 @@ from typing import Optional from typing_extensions import Required, TypedDict +from ...shared_params.metadata import Metadata + __all__ = ["RunUpdateParams"] class RunUpdateParams(TypedDict, total=False): thread_id: Required[str] - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index 0445ae360d..b5f380c7b1 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -5,6 +5,7 @@ from ....._utils import PropertyInfo from ....._models import BaseModel +from ....shared.metadata import Metadata from .tool_calls_step_details import ToolCallsStepDetails from .message_creation_step_details import MessageCreationStepDetails @@ -70,12 +71,14 @@ class RunStep(BaseModel): Will be `null` if there are no errors. """ - metadata: Optional[object] = None + metadata: Optional[Metadata] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ object: Literal["thread.run.step"] diff --git a/src/openai/types/beta/vector_store.py b/src/openai/types/beta/vector_store.py index 2d3ceea80c..b947dfb79d 100644 --- a/src/openai/types/beta/vector_store.py +++ b/src/openai/types/beta/vector_store.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from ..shared.metadata import Metadata __all__ = ["VectorStore", "FileCounts", "ExpiresAfter"] @@ -48,12 +49,14 @@ class VectorStore(BaseModel): last_active_at: Optional[int] = None """The Unix timestamp (in seconds) for when the vector store was last active.""" - metadata: Optional[object] = None + metadata: Optional[Metadata] = None """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ name: str diff --git a/src/openai/types/beta/vector_store_create_params.py b/src/openai/types/beta/vector_store_create_params.py index 4fc7c38927..faca6d9000 100644 --- a/src/openai/types/beta/vector_store_create_params.py +++ b/src/openai/types/beta/vector_store_create_params.py @@ -5,6 +5,7 @@ from typing import List, Optional from typing_extensions import Literal, Required, TypedDict +from ..shared_params.metadata import Metadata from .file_chunking_strategy_param import FileChunkingStrategyParam __all__ = ["VectorStoreCreateParams", "ExpiresAfter"] @@ -28,12 +29,14 @@ class VectorStoreCreateParams(TypedDict, total=False): files. """ - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ name: str diff --git a/src/openai/types/beta/vector_store_update_params.py b/src/openai/types/beta/vector_store_update_params.py index ff6c068efb..e91b3ba5ad 100644 --- a/src/openai/types/beta/vector_store_update_params.py +++ b/src/openai/types/beta/vector_store_update_params.py @@ -5,6 +5,8 @@ from typing import Optional from typing_extensions import Literal, Required, TypedDict +from ..shared_params.metadata import Metadata + __all__ = ["VectorStoreUpdateParams", "ExpiresAfter"] @@ -12,12 +14,14 @@ class VectorStoreUpdateParams(TypedDict, total=False): expires_after: Optional[ExpiresAfter] """The expiration policy for a vector store.""" - metadata: Optional[object] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format. Keys can be a maximum of 64 characters long and values can be - a maximum of 512 characters long. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ name: Optional[str] diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 229fb822f4..35e3a3d784 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -38,8 +38,8 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): """The role of the messages author, in this case `assistant`.""" audio: Optional[Audio] - """ - Data about a previous audio response from the model. + """Data about a previous audio response from the model. + [Learn more](https://platform.openai.com/docs/guides/audio). """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 30d930b120..ec88ea1fb0 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..chat_model import ChatModel +from ..shared_params.metadata import Metadata from .chat_completion_modality import ChatCompletionModality from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_audio_param import ChatCompletionAudioParam @@ -122,10 +123,14 @@ class CompletionCreateParamsBase(TypedDict, total=False): [o1 series models](https://platform.openai.com/docs/guides/reasoning). """ - metadata: Optional[Dict[str, str]] - """ - Developer-defined tags and values used for filtering completions in the - [dashboard](https://platform.openai.com/chat-completions). + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ modalities: Optional[List[ChatCompletionModality]] @@ -216,9 +221,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarentee. + latency guarantee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarentee. + tier with a lower uptime SLA and no latency guarantee. - When not set, the default behavior is 'auto'. """ diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index e1ac464320..c191cb9734 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -5,6 +5,8 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "o3-mini", + "o3-mini-2025-01-31", "o1", "o1-2024-12-17", "o1-preview", diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index c8776bca0e..74bf304904 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .metadata import Metadata as Metadata from .error_object import ErrorObject as ErrorObject from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters diff --git a/src/openai/types/shared/metadata.py b/src/openai/types/shared/metadata.py new file mode 100644 index 0000000000..0da88c679c --- /dev/null +++ b/src/openai/types/shared/metadata.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["Metadata"] + +Metadata: TypeAlias = Dict[str, str] diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index ab4057d59f..68a8db75fe 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .metadata import Metadata as Metadata from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters from .response_format_text import ResponseFormatText as ResponseFormatText diff --git a/src/openai/types/shared_params/metadata.py b/src/openai/types/shared_params/metadata.py new file mode 100644 index 0000000000..821650b48b --- /dev/null +++ b/src/openai/types/shared_params/metadata.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["Metadata"] + +Metadata: TypeAlias = Dict[str, str] diff --git a/src/openai/types/upload.py b/src/openai/types/upload.py index 1cf8ee97f8..d8108c62f9 100644 --- a/src/openai/types/upload.py +++ b/src/openai/types/upload.py @@ -39,4 +39,4 @@ class Upload(BaseModel): """The status of the Upload.""" file: Optional[FileObject] = None - """The ready File object after the Upload is completed.""" + """The `File` object represents a document that has been uploaded to OpenAI.""" diff --git a/tests/api_resources/beta/realtime/test_sessions.py b/tests/api_resources/beta/realtime/test_sessions.py index 908aa983be..5a17088ce6 100644 --- a/tests/api_resources/beta/realtime/test_sessions.py +++ b/tests/api_resources/beta/realtime/test_sessions.py @@ -26,7 +26,11 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: session = client.beta.realtime.sessions.create( input_audio_format="pcm16", - input_audio_transcription={"model": "model"}, + input_audio_transcription={ + "language": "language", + "model": "model", + "prompt": "prompt", + }, instructions="instructions", max_response_output_tokens=0, modalities=["text"], @@ -86,7 +90,11 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: session = await async_client.beta.realtime.sessions.create( input_audio_format="pcm16", - input_audio_transcription={"model": "model"}, + input_audio_transcription={ + "language": "language", + "model": "model", + "prompt": "prompt", + }, instructions="instructions", max_response_output_tokens=0, modalities=["text"], diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index d9944448b7..458e3f5e90 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -34,7 +34,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: model="gpt-4o", description="description", instructions="instructions", - metadata={}, + metadata={"foo": "string"}, name="name", response_format="auto", temperature=1, @@ -46,7 +46,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: { "chunking_strategy": {"type": "auto"}, "file_ids": ["string"], - "metadata": {}, + "metadata": {"foo": "string"}, } ], }, @@ -131,7 +131,7 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: assistant_id="assistant_id", description="description", instructions="instructions", - metadata={}, + metadata={"foo": "string"}, model="model", name="name", response_format="auto", @@ -266,7 +266,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> model="gpt-4o", description="description", instructions="instructions", - metadata={}, + metadata={"foo": "string"}, name="name", response_format="auto", temperature=1, @@ -278,7 +278,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> { "chunking_strategy": {"type": "auto"}, "file_ids": ["string"], - "metadata": {}, + "metadata": {"foo": "string"}, } ], }, @@ -363,7 +363,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> assistant_id="assistant_id", description="description", instructions="instructions", - metadata={}, + metadata={"foo": "string"}, model="model", name="name", response_format="auto", diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 789f870d6a..ecf5b11102 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -39,10 +39,10 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], - metadata={}, + metadata={"foo": "string"}, tool_resources={ "code_interpreter": {"file_ids": ["string"]}, "file_search": { @@ -51,7 +51,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: { "chunking_strategy": {"type": "auto"}, "file_ids": ["string"], - "metadata": {}, + "metadata": {"foo": "string"}, } ], }, @@ -127,8 +127,8 @@ def test_method_update(self, client: OpenAI) -> None: @parametrize def test_method_update_with_all_params(self, client: OpenAI) -> None: thread = client.beta.threads.update( - "string", - metadata={}, + thread_id="thread_id", + metadata={"foo": "string"}, tool_resources={ "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, @@ -219,7 +219,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) instructions="string", max_completion_tokens=256, max_prompt_tokens=256, - metadata={}, + metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, response_format="auto", @@ -236,10 +236,10 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], - "metadata": {}, + "metadata": {"foo": "string"}, "tool_resources": { "code_interpreter": {"file_ids": ["string"]}, "file_search": { @@ -248,7 +248,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) { "chunking_strategy": {"type": "auto"}, "file_ids": ["string"], - "metadata": {}, + "metadata": {"foo": "string"}, } ], }, @@ -308,7 +308,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) instructions="string", max_completion_tokens=256, max_prompt_tokens=256, - metadata={}, + metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, response_format="auto", @@ -324,10 +324,10 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], - "metadata": {}, + "metadata": {"foo": "string"}, "tool_resources": { "code_interpreter": {"file_ids": ["string"]}, "file_search": { @@ -336,7 +336,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) { "chunking_strategy": {"type": "auto"}, "file_ids": ["string"], - "metadata": {}, + "metadata": {"foo": "string"}, } ], }, @@ -403,10 +403,10 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], - metadata={}, + metadata={"foo": "string"}, tool_resources={ "code_interpreter": {"file_ids": ["string"]}, "file_search": { @@ -415,7 +415,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> { "chunking_strategy": {"type": "auto"}, "file_ids": ["string"], - "metadata": {}, + "metadata": {"foo": "string"}, } ], }, @@ -491,8 +491,8 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: thread = await async_client.beta.threads.update( - "string", - metadata={}, + thread_id="thread_id", + metadata={"foo": "string"}, tool_resources={ "code_interpreter": {"file_ids": ["string"]}, "file_search": {"vector_store_ids": ["string"]}, @@ -583,7 +583,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie instructions="string", max_completion_tokens=256, max_prompt_tokens=256, - metadata={}, + metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, response_format="auto", @@ -600,10 +600,10 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], - "metadata": {}, + "metadata": {"foo": "string"}, "tool_resources": { "code_interpreter": {"file_ids": ["string"]}, "file_search": { @@ -612,7 +612,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie { "chunking_strategy": {"type": "auto"}, "file_ids": ["string"], - "metadata": {}, + "metadata": {"foo": "string"}, } ], }, @@ -672,7 +672,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie instructions="string", max_completion_tokens=256, max_prompt_tokens=256, - metadata={}, + metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, response_format="auto", @@ -688,10 +688,10 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], - "metadata": {}, + "metadata": {"foo": "string"}, "tool_resources": { "code_interpreter": {"file_ids": ["string"]}, "file_search": { @@ -700,7 +700,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie { "chunking_strategy": {"type": "auto"}, "file_ids": ["string"], - "metadata": {}, + "metadata": {"foo": "string"}, } ], }, diff --git a/tests/api_resources/beta/test_vector_stores.py b/tests/api_resources/beta/test_vector_stores.py index 99e1970c33..e13b8c7613 100644 --- a/tests/api_resources/beta/test_vector_stores.py +++ b/tests/api_resources/beta/test_vector_stores.py @@ -35,8 +35,8 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "days": 1, }, file_ids=["string"], - metadata={}, - name="string", + metadata={"foo": "string"}, + name="name", ) assert_matches_type(VectorStore, vector_store, path=["response"]) @@ -113,8 +113,8 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: "anchor": "last_active_at", "days": 1, }, - metadata={}, - name="string", + metadata={"foo": "string"}, + name="name", ) assert_matches_type(VectorStore, vector_store, path=["response"]) @@ -240,8 +240,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "days": 1, }, file_ids=["string"], - metadata={}, - name="string", + metadata={"foo": "string"}, + name="name", ) assert_matches_type(VectorStore, vector_store, path=["response"]) @@ -318,8 +318,8 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> "anchor": "last_active_at", "days": 1, }, - metadata={}, - name="string", + metadata={"foo": "string"}, + name="name", ) assert_matches_type(VectorStore, vector_store, path=["response"]) diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py index 06c37e608a..9189a2f29e 100644 --- a/tests/api_resources/beta/threads/test_messages.py +++ b/tests/api_resources/beta/threads/test_messages.py @@ -42,7 +42,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "tools": [{"type": "code_interpreter"}], } ], - metadata={}, + metadata={"foo": "string"}, ) assert_matches_type(Message, message, path=["response"]) @@ -142,9 +142,9 @@ def test_method_update(self, client: OpenAI) -> None: @parametrize def test_method_update_with_all_params(self, client: OpenAI) -> None: message = client.beta.threads.messages.update( - "string", - thread_id="string", - metadata={}, + message_id="message_id", + thread_id="thread_id", + metadata={"foo": "string"}, ) assert_matches_type(Message, message, path=["response"]) @@ -311,7 +311,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "tools": [{"type": "code_interpreter"}], } ], - metadata={}, + metadata={"foo": "string"}, ) assert_matches_type(Message, message, path=["response"]) @@ -411,9 +411,9 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: message = await async_client.beta.threads.messages.update( - "string", - thread_id="string", - metadata={}, + message_id="message_id", + thread_id="thread_id", + metadata={"foo": "string"}, ) assert_matches_type(Message, message, path=["response"]) diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index c48cc6de43..48b39cfe5b 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -47,13 +47,13 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], instructions="string", max_completion_tokens=256, max_prompt_tokens=256, - metadata={}, + metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, response_format="auto", @@ -130,13 +130,13 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], instructions="string", max_completion_tokens=256, max_prompt_tokens=256, - metadata={}, + metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, response_format="auto", @@ -246,9 +246,9 @@ def test_method_update(self, client: OpenAI) -> None: @parametrize def test_method_update_with_all_params(self, client: OpenAI) -> None: run = client.beta.threads.runs.update( - "string", - thread_id="string", - metadata={}, + run_id="run_id", + thread_id="thread_id", + metadata={"foo": "string"}, ) assert_matches_type(Run, run, path=["response"]) @@ -543,13 +543,13 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], instructions="string", max_completion_tokens=256, max_prompt_tokens=256, - metadata={}, + metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, response_format="auto", @@ -626,13 +626,13 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "tools": [{"type": "code_interpreter"}], } ], - "metadata": {}, + "metadata": {"foo": "string"}, } ], instructions="string", max_completion_tokens=256, max_prompt_tokens=256, - metadata={}, + metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, response_format="auto", @@ -742,9 +742,9 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: run = await async_client.beta.threads.runs.update( - "string", - thread_id="string", - metadata={}, + run_id="run_id", + thread_id="thread_id", + metadata={"foo": "string"}, ) assert_matches_type(Run, run, path=["response"]) From b56b357e60b9bccdb1ad5ab56a86614cfd4d402d Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 31 Jan 2025 19:14:32 +0000 Subject: [PATCH 772/914] chore(types): fix Metadata types --- src/openai/resources/beta/chat/completions.py | 9 +++--- .../resources/beta/threads/runs/runs.py | 28 +++++++++---------- src/openai/resources/beta/threads/threads.py | 16 +++++------ 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 48cb13f7a6..8a3a20d9e0 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -28,6 +28,7 @@ ) from ....types.chat_model import ChatModel from ....lib.streaming.chat import ChatCompletionStreamManager, AsyncChatCompletionStreamManager +from ....types.shared_params import Metadata from ....types.chat.chat_completion import ChatCompletion from ....types.chat.chat_completion_chunk import ChatCompletionChunk from ....types.chat.parsed_chat_completion import ParsedChatCompletion @@ -76,7 +77,7 @@ def parse( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -221,7 +222,7 @@ def stream( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -351,7 +352,7 @@ async def parse( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, @@ -496,7 +497,7 @@ def stream( logprobs: Optional[bool] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 9cb202a1a2..13301ad507 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -771,7 +771,7 @@ def create_and_poll( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -840,7 +840,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -871,7 +871,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -902,7 +902,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1019,7 +1019,7 @@ def stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1050,7 +1050,7 @@ def stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1081,7 +1081,7 @@ def stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -2144,7 +2144,7 @@ async def create_and_poll( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -2213,7 +2213,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -2244,7 +2244,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -2275,7 +2275,7 @@ def create_and_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -2393,7 +2393,7 @@ def stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -2424,7 +2424,7 @@ def stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -2455,7 +2455,7 @@ def stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 0ec59aca55..6ff8539501 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -734,7 +734,7 @@ def create_and_run_poll( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -790,7 +790,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -819,7 +819,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -848,7 +848,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1581,7 +1581,7 @@ async def create_and_run_poll( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1639,7 +1639,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1668,7 +1668,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, @@ -1697,7 +1697,7 @@ def create_and_run_stream( instructions: Optional[str] | NotGiven = NOT_GIVEN, max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[object] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, From 7a6517d81e4ae9e9e9527cd401bb76937983dfef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 31 Jan 2025 19:18:58 +0000 Subject: [PATCH 773/914] release: 1.61.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 25 +++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 73f712c242..68804e4da0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.60.2" + ".": "1.61.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 168d98e5cd..dcd1c06333 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 1.61.0 (2025-01-31) + +Full Changelog: [v1.60.2...v1.61.0](https://github.com/openai/openai-python/compare/v1.60.2...v1.61.0) + +### Features + +* **api:** add o3-mini ([#2067](https://github.com/openai/openai-python/issues/2067)) ([12b87a4](https://github.com/openai/openai-python/commit/12b87a4a1e6cb071a6b063d089585dec56a5d534)) + + +### Bug Fixes + +* **types:** correct metadata type + other fixes ([12b87a4](https://github.com/openai/openai-python/commit/12b87a4a1e6cb071a6b063d089585dec56a5d534)) + + +### Chores + +* **helpers:** section links ([ef8d3cc](https://github.com/openai/openai-python/commit/ef8d3cce40022d3482d341455be604e5f1afbd70)) +* **types:** fix Metadata types ([82d3156](https://github.com/openai/openai-python/commit/82d3156e74ed2f95edd10cd7ebea53d2b5562794)) +* update api.md ([#2063](https://github.com/openai/openai-python/issues/2063)) ([21964f0](https://github.com/openai/openai-python/commit/21964f00fb104011c4c357544114702052b74548)) + + +### Documentation + +* **readme:** current section links ([#2055](https://github.com/openai/openai-python/issues/2055)) ([ef8d3cc](https://github.com/openai/openai-python/commit/ef8d3cce40022d3482d341455be604e5f1afbd70)) + ## 1.60.2 (2025-01-27) Full Changelog: [v1.60.1...v1.60.2](https://github.com/openai/openai-python/compare/v1.60.1...v1.60.2) diff --git a/pyproject.toml b/pyproject.toml index 9657bdc0ce..07913fcbd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.60.2" +version = "1.61.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index c8f825db34..e9ab8be65e 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.60.2" # x-release-please-version +__version__ = "1.61.0" # x-release-please-version From c27e8cc997212b895743941966530980cd56d9da Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 3 Feb 2025 11:54:55 +0000 Subject: [PATCH 774/914] fix(cli/chat): only send params when set (#2077) --- src/openai/cli/_api/chat/completions.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/openai/cli/_api/chat/completions.py b/src/openai/cli/_api/chat/completions.py index c299741fe0..feedb5ccab 100644 --- a/src/openai/cli/_api/chat/completions.py +++ b/src/openai/cli/_api/chat/completions.py @@ -100,13 +100,17 @@ def create(args: CLIChatCompletionCreateArgs) -> None: "messages": [ {"role": cast(Literal["user"], message.role), "content": message.content} for message in args.message ], - "n": args.n, - "temperature": args.temperature, - "top_p": args.top_p, - "stop": args.stop, # type checkers are not good at inferring union types so we have to set stream afterwards "stream": False, } + if args.temperature is not None: + params['temperature'] = args.temperature + if args.stop is not None: + params['stop'] = args.stop + if args.top_p is not None: + params['top_p'] = args.top_p + if args.n is not None: + params['n'] = args.n if args.stream: params["stream"] = args.stream # type: ignore if args.max_tokens is not None: From 5a1a412b77c4233ca3b147738f63956f09a65fb1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 14:43:52 +0000 Subject: [PATCH 775/914] chore(internal): change default timeout to an int (#2079) --- src/openai/_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_constants.py b/src/openai/_constants.py index 3f82bed037..7029dc72b0 100644 --- a/src/openai/_constants.py +++ b/src/openai/_constants.py @@ -6,7 +6,7 @@ OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" # default timeout is 10 minutes -DEFAULT_TIMEOUT = httpx.Timeout(timeout=600.0, connect=5.0) +DEFAULT_TIMEOUT = httpx.Timeout(timeout=600, connect=5.0) DEFAULT_MAX_RETRIES = 2 DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=1000, max_keepalive_connections=100) From 6afde0dc8512a16ff2eca781fee0395cab254f8c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:27:06 +0000 Subject: [PATCH 776/914] chore(internal): bummp ruff dependency (#2080) --- pyproject.toml | 2 +- requirements-dev.lock | 2 +- scripts/utils/ruffen-docs.py | 4 ++-- src/openai/_models.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 07913fcbd2..dc78d95d3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -194,7 +194,7 @@ select = [ "T201", "T203", # misuse of typing.TYPE_CHECKING - "TCH004", + "TC004", # import rules "TID251", ] diff --git a/requirements-dev.lock b/requirements-dev.lock index 38cc6e1cf2..5599057b66 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -140,7 +140,7 @@ requests==2.31.0 respx==0.22.0 rich==13.7.1 # via inline-snapshot -ruff==0.6.9 +ruff==0.9.4 setuptools==68.2.2 # via nodeenv six==1.16.0 diff --git a/scripts/utils/ruffen-docs.py b/scripts/utils/ruffen-docs.py index 37b3d94f0f..0cf2bd2fd9 100644 --- a/scripts/utils/ruffen-docs.py +++ b/scripts/utils/ruffen-docs.py @@ -47,7 +47,7 @@ def _md_match(match: Match[str]) -> str: with _collect_error(match): code = format_code_block(code) code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' + return f"{match['before']}{code}{match['after']}" def _pycon_match(match: Match[str]) -> str: code = "" @@ -97,7 +97,7 @@ def finish_fragment() -> None: def _md_pycon_match(match: Match[str]) -> str: code = _pycon_match(match) code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' + return f"{match['before']}{code}{match['after']}" src = MD_RE.sub(_md_match, src) src = MD_PYCON_RE.sub(_md_pycon_match, src) diff --git a/src/openai/_models.py b/src/openai/_models.py index 23456d9f80..c6e1305087 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -197,7 +197,7 @@ def to_json( @override def __str__(self) -> str: # mypy complains about an invalid self arg - return f'{self.__repr_name__()}({self.__repr_str__(", ")})' # type: ignore[misc] + return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] # Override the 'construct' method in a way that supports recursive parsing without validation. # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. From f344db250ac3a1f5cb1bb36b6719a2bf4e002d87 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:26:48 +0000 Subject: [PATCH 777/914] fix(api/types): correct audio duration & role types (#2091) --- .stats.yml | 2 +- api.md | 1 + .../types/audio/transcription_verbose.py | 2 +- src/openai/types/audio/translation_verbose.py | 2 +- src/openai/types/beta/realtime/__init__.py | 4 ++ .../conversation_item_with_reference.py | 67 ++++++++++++++++++ .../conversation_item_with_reference_param.py | 68 +++++++++++++++++++ .../beta/realtime/response_create_event.py | 10 +-- .../realtime/response_create_event_param.py | 10 +-- .../types/chat/chat_completion_chunk.py | 2 +- src/openai/types/chat/chat_completion_role.py | 2 +- 11 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 src/openai/types/beta/realtime/conversation_item_with_reference.py create mode 100644 src/openai/types/beta/realtime/conversation_item_with_reference_param.py diff --git a/.stats.yml b/.stats.yml index e49b5c56e8..df7877dfd0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-6204952a29973265b9c0d66fc67ffaf53c6a90ae4d75cdacf9d147676f5274c9.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-fc5dbc19505b0035f9e7f88868619f4fb519b048bde011f6154f3132d4be71fb.yml diff --git a/api.md b/api.md index c1262fd2c5..efbfeaa68f 100644 --- a/api.md +++ b/api.md @@ -255,6 +255,7 @@ from openai.types.beta.realtime import ( ConversationItemInputAudioTranscriptionFailedEvent, ConversationItemTruncateEvent, ConversationItemTruncatedEvent, + ConversationItemWithReference, ErrorEvent, InputAudioBufferAppendEvent, InputAudioBufferClearEvent, diff --git a/src/openai/types/audio/transcription_verbose.py b/src/openai/types/audio/transcription_verbose.py index 3b18fa4871..2a670189e0 100644 --- a/src/openai/types/audio/transcription_verbose.py +++ b/src/openai/types/audio/transcription_verbose.py @@ -10,7 +10,7 @@ class TranscriptionVerbose(BaseModel): - duration: str + duration: float """The duration of the input audio.""" language: str diff --git a/src/openai/types/audio/translation_verbose.py b/src/openai/types/audio/translation_verbose.py index 5901ae7535..27cb02d64f 100644 --- a/src/openai/types/audio/translation_verbose.py +++ b/src/openai/types/audio/translation_verbose.py @@ -9,7 +9,7 @@ class TranslationVerbose(BaseModel): - duration: str + duration: float """The duration of the input audio.""" language: str diff --git a/src/openai/types/beta/realtime/__init__.py b/src/openai/types/beta/realtime/__init__.py index 372d4ec19d..cd0616dcfa 100644 --- a/src/openai/types/beta/realtime/__init__.py +++ b/src/openai/types/beta/realtime/__init__.py @@ -42,6 +42,7 @@ from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent as InputAudioBufferCommitEvent from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent from .conversation_item_truncate_event import ConversationItemTruncateEvent as ConversationItemTruncateEvent +from .conversation_item_with_reference import ConversationItemWithReference as ConversationItemWithReference from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent as InputAudioBufferClearedEvent from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent @@ -60,6 +61,9 @@ from .conversation_item_truncate_event_param import ( ConversationItemTruncateEventParam as ConversationItemTruncateEventParam, ) +from .conversation_item_with_reference_param import ( + ConversationItemWithReferenceParam as ConversationItemWithReferenceParam, +) from .input_audio_buffer_speech_started_event import ( InputAudioBufferSpeechStartedEvent as InputAudioBufferSpeechStartedEvent, ) diff --git a/src/openai/types/beta/realtime/conversation_item_with_reference.py b/src/openai/types/beta/realtime/conversation_item_with_reference.py new file mode 100644 index 0000000000..31806afc33 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_with_reference.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from .conversation_item_content import ConversationItemContent + +__all__ = ["ConversationItemWithReference"] + + +class ConversationItemWithReference(BaseModel): + id: Optional[str] = None + """ + For an item of type (`message` | `function_call` | `function_call_output`) this + field allows the client to assign the unique ID of the item. It is not required + because the server will generate one if not provided. + + For an item of type `item_reference`, this field is required and is a reference + to any item that has previously existed in the conversation. + """ + + arguments: Optional[str] = None + """The arguments of the function call (for `function_call` items).""" + + call_id: Optional[str] = None + """ + The ID of the function call (for `function_call` and `function_call_output` + items). If passed on a `function_call_output` item, the server will check that a + `function_call` item with the same ID exists in the conversation history. + """ + + content: Optional[List[ConversationItemContent]] = None + """The content of the message, applicable for `message` items. + + - Message items of role `system` support only `input_text` content + - Message items of role `user` support `input_text` and `input_audio` content + - Message items of role `assistant` support `text` content. + """ + + name: Optional[str] = None + """The name of the function being called (for `function_call` items).""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + output: Optional[str] = None + """The output of the function call (for `function_call_output` items).""" + + role: Optional[Literal["user", "assistant", "system"]] = None + """ + The role of the message sender (`user`, `assistant`, `system`), only applicable + for `message` items. + """ + + status: Optional[Literal["completed", "incomplete"]] = None + """The status of the item (`completed`, `incomplete`). + + These have no effect on the conversation, but are accepted for consistency with + the `conversation.item.created` event. + """ + + type: Optional[Literal["message", "function_call", "function_call_output", "item_reference"]] = None + """ + The type of the item (`message`, `function_call`, `function_call_output`, + `item_reference`). + """ diff --git a/src/openai/types/beta/realtime/conversation_item_with_reference_param.py b/src/openai/types/beta/realtime/conversation_item_with_reference_param.py new file mode 100644 index 0000000000..e266cdce32 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_with_reference_param.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, TypedDict + +from .conversation_item_content_param import ConversationItemContentParam + +__all__ = ["ConversationItemWithReferenceParam"] + + +class ConversationItemWithReferenceParam(TypedDict, total=False): + id: str + """ + For an item of type (`message` | `function_call` | `function_call_output`) this + field allows the client to assign the unique ID of the item. It is not required + because the server will generate one if not provided. + + For an item of type `item_reference`, this field is required and is a reference + to any item that has previously existed in the conversation. + """ + + arguments: str + """The arguments of the function call (for `function_call` items).""" + + call_id: str + """ + The ID of the function call (for `function_call` and `function_call_output` + items). If passed on a `function_call_output` item, the server will check that a + `function_call` item with the same ID exists in the conversation history. + """ + + content: Iterable[ConversationItemContentParam] + """The content of the message, applicable for `message` items. + + - Message items of role `system` support only `input_text` content + - Message items of role `user` support `input_text` and `input_audio` content + - Message items of role `assistant` support `text` content. + """ + + name: str + """The name of the function being called (for `function_call` items).""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + output: str + """The output of the function call (for `function_call_output` items).""" + + role: Literal["user", "assistant", "system"] + """ + The role of the message sender (`user`, `assistant`, `system`), only applicable + for `message` items. + """ + + status: Literal["completed", "incomplete"] + """The status of the item (`completed`, `incomplete`). + + These have no effect on the conversation, but are accepted for consistency with + the `conversation.item.created` event. + """ + + type: Literal["message", "function_call", "function_call_output", "item_reference"] + """ + The type of the item (`message`, `function_call`, `function_call_output`, + `item_reference`). + """ diff --git a/src/openai/types/beta/realtime/response_create_event.py b/src/openai/types/beta/realtime/response_create_event.py index 0801654bd8..d6c5fda926 100644 --- a/src/openai/types/beta/realtime/response_create_event.py +++ b/src/openai/types/beta/realtime/response_create_event.py @@ -5,7 +5,7 @@ from ...._models import BaseModel from ...shared.metadata import Metadata -from .conversation_item import ConversationItem +from .conversation_item_with_reference import ConversationItemWithReference __all__ = ["ResponseCreateEvent", "Response", "ResponseTool"] @@ -37,11 +37,13 @@ class Response(BaseModel): will not add items to default conversation. """ - input: Optional[List[ConversationItem]] = None + input: Optional[List[ConversationItemWithReference]] = None """Input items to include in the prompt for the model. - Creates a new context for this response, without including the default - conversation. Can include references to items from the default conversation. + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items from the default + conversation. """ instructions: Optional[str] = None diff --git a/src/openai/types/beta/realtime/response_create_event_param.py b/src/openai/types/beta/realtime/response_create_event_param.py index a87ef955e8..c02fe1b34e 100644 --- a/src/openai/types/beta/realtime/response_create_event_param.py +++ b/src/openai/types/beta/realtime/response_create_event_param.py @@ -5,8 +5,8 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -from .conversation_item_param import ConversationItemParam from ...shared_params.metadata import Metadata +from .conversation_item_with_reference_param import ConversationItemWithReferenceParam __all__ = ["ResponseCreateEventParam", "Response", "ResponseTool"] @@ -38,11 +38,13 @@ class Response(TypedDict, total=False): will not add items to default conversation. """ - input: Iterable[ConversationItemParam] + input: Iterable[ConversationItemWithReferenceParam] """Input items to include in the prompt for the model. - Creates a new context for this response, without including the default - conversation. Can include references to items from the default conversation. + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items from the default + conversation. """ instructions: str diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 7b0ae2e121..dede513f1e 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -70,7 +70,7 @@ class ChoiceDelta(BaseModel): refusal: Optional[str] = None """The refusal message generated by the model.""" - role: Optional[Literal["system", "user", "assistant", "tool"]] = None + role: Optional[Literal["developer", "system", "user", "assistant", "tool"]] = None """The role of the author of this message.""" tool_calls: Optional[List[ChoiceDeltaToolCall]] = None diff --git a/src/openai/types/chat/chat_completion_role.py b/src/openai/types/chat/chat_completion_role.py index c2ebef74c8..3ec5e9ad87 100644 --- a/src/openai/types/chat/chat_completion_role.py +++ b/src/openai/types/chat/chat_completion_role.py @@ -4,4 +4,4 @@ __all__ = ["ChatCompletionRole"] -ChatCompletionRole: TypeAlias = Literal["system", "user", "assistant", "tool", "function"] +ChatCompletionRole: TypeAlias = Literal["developer", "system", "user", "assistant", "tool", "function"] From 7193688e364bd726594fe369032e813ced1bdfe2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:27:26 +0000 Subject: [PATCH 778/914] release: 1.61.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 68804e4da0..285741ee32 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.61.0" + ".": "1.61.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dcd1c06333..101e7480b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.61.1 (2025-02-05) + +Full Changelog: [v1.61.0...v1.61.1](https://github.com/openai/openai-python/compare/v1.61.0...v1.61.1) + +### Bug Fixes + +* **api/types:** correct audio duration & role types ([#2091](https://github.com/openai/openai-python/issues/2091)) ([afcea48](https://github.com/openai/openai-python/commit/afcea4891ff85de165ccc2b5497ccf9a90520e9e)) +* **cli/chat:** only send params when set ([#2077](https://github.com/openai/openai-python/issues/2077)) ([688b223](https://github.com/openai/openai-python/commit/688b223d9a733d241d50e5d7df62f346592c537c)) + + +### Chores + +* **internal:** bummp ruff dependency ([#2080](https://github.com/openai/openai-python/issues/2080)) ([b7a80b1](https://github.com/openai/openai-python/commit/b7a80b1994ab86e81485b88531e4aea63b3da594)) +* **internal:** change default timeout to an int ([#2079](https://github.com/openai/openai-python/issues/2079)) ([d3df1c6](https://github.com/openai/openai-python/commit/d3df1c6ca090598701e38fd376a9796aadba88f1)) + ## 1.61.0 (2025-01-31) Full Changelog: [v1.60.2...v1.61.0](https://github.com/openai/openai-python/compare/v1.60.2...v1.61.0) diff --git a/pyproject.toml b/pyproject.toml index dc78d95d3f..6f1a6eb28a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.61.0" +version = "1.61.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e9ab8be65e..7ffe16b95d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.61.0" # x-release-please-version +__version__ = "1.61.1" # x-release-please-version From b99c35c62f3773980ee77179cdad9d8afd46f13b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 17:15:22 +0000 Subject: [PATCH 779/914] feat(client): send `X-Stainless-Read-Timeout` header (#2094) --- src/openai/_base_client.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 1fa039c0b1..8a408d8e58 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -420,10 +420,17 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0 if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: headers[idempotency_header] = options.idempotency_key or self._idempotency_key() - # Don't set the retry count header if it was already set or removed by the caller. We check + # Don't set these headers if they were already set or removed by the caller. We check # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. - if "x-stainless-retry-count" not in (header.lower() for header in custom_headers): + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) return headers From 8640fd837f371e6c6e235bbdc3a6ff395ba632b7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 21:25:56 +0000 Subject: [PATCH 780/914] fix(api): add missing reasoning effort + model enums (#2096) --- .stats.yml | 2 +- src/openai/resources/beta/assistants.py | 106 +++++++++++++++++- src/openai/resources/beta/chat/completions.py | 8 +- .../resources/beta/threads/runs/runs.py | 68 +++++++++++ src/openai/resources/chat/completions.py | 28 ++--- .../types/beta/assistant_create_params.py | 11 +- .../types/beta/assistant_update_params.py | 47 +++++++- .../types/beta/threads/run_create_params.py | 9 ++ .../chat/chat_completion_reasoning_effort.py | 3 +- .../types/chat/completion_create_params.py | 4 +- tests/api_resources/beta/test_assistants.py | 8 +- tests/api_resources/beta/threads/test_runs.py | 4 + 12 files changed, 268 insertions(+), 30 deletions(-) diff --git a/.stats.yml b/.stats.yml index df7877dfd0..8a5d2c06b2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-fc5dbc19505b0035f9e7f88868619f4fb519b048bde011f6154f3132d4be71fb.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-7c699d4503077d06a4a44f52c0c1f902d19a87c766b8be75b97c8dfd484ad4aa.yml diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 65b7c9cfc2..462086f74b 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -61,6 +61,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_resources: Optional[assistant_create_params.ToolResources] | NotGiven = NOT_GIVEN, @@ -97,6 +98,13 @@ def create( name: The name of the assistant. The maximum length is 256 characters. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -155,6 +163,7 @@ def create( "instructions": instructions, "metadata": metadata, "name": name, + "reasoning_effort": reasoning_effort, "response_format": response_format, "temperature": temperature, "tool_resources": tool_resources, @@ -210,8 +219,42 @@ def update( description: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: str | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ] + | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_resources: Optional[assistant_update_params.ToolResources] | NotGiven = NOT_GIVEN, @@ -249,6 +292,13 @@ def update( name: The name of the assistant. The maximum length is 256 characters. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -309,6 +359,7 @@ def update( "metadata": metadata, "model": model, "name": name, + "reasoning_effort": reasoning_effort, "response_format": response_format, "temperature": temperature, "tool_resources": tool_resources, @@ -451,6 +502,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_resources: Optional[assistant_create_params.ToolResources] | NotGiven = NOT_GIVEN, @@ -487,6 +539,13 @@ async def create( name: The name of the assistant. The maximum length is 256 characters. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -545,6 +604,7 @@ async def create( "instructions": instructions, "metadata": metadata, "name": name, + "reasoning_effort": reasoning_effort, "response_format": response_format, "temperature": temperature, "tool_resources": tool_resources, @@ -600,8 +660,42 @@ async def update( description: Optional[str] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: str | NotGiven = NOT_GIVEN, + model: Union[ + str, + Literal[ + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ] + | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_resources: Optional[assistant_update_params.ToolResources] | NotGiven = NOT_GIVEN, @@ -639,6 +733,13 @@ async def update( name: The name of the assistant. The maximum length is 256 characters. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -699,6 +800,7 @@ async def update( "metadata": metadata, "model": model, "name": name, + "reasoning_effort": reasoning_effort, "response_format": response_format, "temperature": temperature, "tool_resources": tool_resources, diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 8a3a20d9e0..0c631b9821 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -83,7 +83,7 @@ def parse( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, @@ -228,7 +228,7 @@ def stream( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, @@ -358,7 +358,7 @@ async def parse( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, @@ -503,7 +503,7 @@ def stream( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 13301ad507..dc364b4e31 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -96,6 +96,7 @@ def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -164,6 +165,13 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -239,6 +247,7 @@ def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -310,6 +319,13 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -381,6 +397,7 @@ def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -452,6 +469,13 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -522,6 +546,7 @@ def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -552,6 +577,7 @@ def create( "metadata": metadata, "model": model, "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, "response_format": response_format, "stream": stream, "temperature": temperature, @@ -774,6 +800,7 @@ def create_and_poll( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -809,6 +836,7 @@ def create_and_poll( temperature=temperature, tool_choice=tool_choice, parallel_tool_calls=parallel_tool_calls, + reasoning_effort=reasoning_effort, # We assume we are not streaming when polling stream=False, tools=tools, @@ -843,6 +871,7 @@ def create_and_stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -874,6 +903,7 @@ def create_and_stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -905,6 +935,7 @@ def create_and_stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -950,6 +981,7 @@ def create_and_stream( "tools": tools, "truncation_strategy": truncation_strategy, "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, "top_p": top_p, }, run_create_params.RunCreateParams, @@ -1022,6 +1054,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1053,6 +1086,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1084,6 +1118,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1128,6 +1163,7 @@ def stream( "stream": True, "tools": tools, "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, "truncation_strategy": truncation_strategy, "top_p": top_p, }, @@ -1469,6 +1505,7 @@ async def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1537,6 +1574,13 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -1612,6 +1656,7 @@ async def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1683,6 +1728,13 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -1754,6 +1806,7 @@ async def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1825,6 +1878,13 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. + reasoning_effort: **o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -1895,6 +1955,7 @@ async def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1925,6 +1986,7 @@ async def create( "metadata": metadata, "model": model, "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, "response_format": response_format, "stream": stream, "temperature": temperature, @@ -2147,6 +2209,7 @@ async def create_and_poll( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2182,6 +2245,7 @@ async def create_and_poll( temperature=temperature, tool_choice=tool_choice, parallel_tool_calls=parallel_tool_calls, + reasoning_effort=reasoning_effort, # We assume we are not streaming when polling stream=False, tools=tools, @@ -2396,6 +2460,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2427,6 +2492,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2458,6 +2524,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2504,6 +2571,7 @@ def stream( "stream": True, "tools": tools, "parallel_tool_calls": parallel_tool_calls, + "reasoning_effort": reasoning_effort, "truncation_strategy": truncation_strategy, "top_p": top_p, }, diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py index 34f6b50301..cc839103a0 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions.py @@ -82,7 +82,7 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -213,7 +213,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 models only** + reasoning_effort: **o1 and o3-mini models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -335,7 +335,7 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -472,7 +472,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 models only** + reasoning_effort: **o1 and o3-mini models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -587,7 +587,7 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -724,7 +724,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 models only** + reasoning_effort: **o1 and o3-mini models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -838,7 +838,7 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -946,7 +946,7 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -1077,7 +1077,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 models only** + reasoning_effort: **o1 and o3-mini models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1199,7 +1199,7 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -1336,7 +1336,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 models only** + reasoning_effort: **o1 and o3-mini models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1451,7 +1451,7 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, @@ -1588,7 +1588,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 models only** + reasoning_effort: **o1 and o3-mini models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1702,7 +1702,7 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: ChatCompletionReasoningEffort | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index e205856395..66bef02ced 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import List, Union, Iterable, Optional -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from ..chat_model import ChatModel from .assistant_tool_param import AssistantToolParam @@ -53,6 +53,15 @@ class AssistantCreateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" + reasoning_effort: Optional[Literal["low", "medium", "high"]] + """**o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + """ + response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 35065ef61b..80fec110cd 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -2,8 +2,8 @@ from __future__ import annotations -from typing import List, Iterable, Optional -from typing_extensions import TypedDict +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, TypedDict from .assistant_tool_param import AssistantToolParam from ..shared_params.metadata import Metadata @@ -32,7 +32,39 @@ class AssistantUpdateParams(TypedDict, total=False): a maximum length of 512 characters. """ - model: str + model: Union[ + str, + Literal[ + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + ], + ] """ID of the model to use. You can use the @@ -45,6 +77,15 @@ class AssistantUpdateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" + reasoning_effort: Optional[Literal["low", "medium", "high"]] + """**o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + """ + response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 091dd3da66..093b4ce321 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -106,6 +106,15 @@ class RunCreateParamsBase(TypedDict, total=False): during tool use. """ + reasoning_effort: Optional[Literal["low", "medium", "high"]] + """**o1 and o3-mini models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + """ + response_format: Optional[AssistantResponseFormatOptionParam] """Specifies the format that the model must output. diff --git a/src/openai/types/chat/chat_completion_reasoning_effort.py b/src/openai/types/chat/chat_completion_reasoning_effort.py index 9e7946974a..85249c53b1 100644 --- a/src/openai/types/chat/chat_completion_reasoning_effort.py +++ b/src/openai/types/chat/chat_completion_reasoning_effort.py @@ -1,7 +1,8 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal, TypeAlias __all__ = ["ChatCompletionReasoningEffort"] -ChatCompletionReasoningEffort: TypeAlias = Literal["low", "medium", "high"] +ChatCompletionReasoningEffort: TypeAlias = Optional[Literal["low", "medium", "high"]] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index ec88ea1fb0..c761cbe07b 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -174,8 +174,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): far, increasing the model's likelihood to talk about new topics. """ - reasoning_effort: ChatCompletionReasoningEffort - """**o1 models only** + reasoning_effort: Optional[ChatCompletionReasoningEffort] + """**o1 and o3-mini models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 458e3f5e90..82aaf87b1c 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -36,6 +36,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: instructions="instructions", metadata={"foo": "string"}, name="name", + reasoning_effort="low", response_format="auto", temperature=1, tool_resources={ @@ -132,8 +133,9 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: description="description", instructions="instructions", metadata={"foo": "string"}, - model="model", + model="string", name="name", + reasoning_effort="low", response_format="auto", temperature=1, tool_resources={ @@ -268,6 +270,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> instructions="instructions", metadata={"foo": "string"}, name="name", + reasoning_effort="low", response_format="auto", temperature=1, tool_resources={ @@ -364,8 +367,9 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> description="description", instructions="instructions", metadata={"foo": "string"}, - model="model", + model="string", name="name", + reasoning_effort="low", response_format="auto", temperature=1, tool_resources={ diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 48b39cfe5b..d05ee96144 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -56,6 +56,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, + reasoning_effort="low", response_format="auto", stream=False, temperature=1, @@ -139,6 +140,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, + reasoning_effort="low", response_format="auto", temperature=1, tool_choice="none", @@ -552,6 +554,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, + reasoning_effort="low", response_format="auto", stream=False, temperature=1, @@ -635,6 +638,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn metadata={"foo": "string"}, model="gpt-4o", parallel_tool_calls=True, + reasoning_effort="low", response_format="auto", temperature=1, tool_choice="none", From 2c20ea7af7bcd531d04122624789402778370c52 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 6 Feb 2025 21:15:14 +1100 Subject: [PATCH 781/914] feat(embeddings): use stdlib array type for improved performance (#2060) --- src/openai/resources/embeddings.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 382a42340e..a392d5eb17 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -2,6 +2,7 @@ from __future__ import annotations +import array import base64 from typing import List, Union, Iterable, cast from typing_extensions import Literal @@ -102,7 +103,7 @@ def create( "dimensions": dimensions, "encoding_format": encoding_format, } - if not is_given(encoding_format) and has_numpy(): + if not is_given(encoding_format): params["encoding_format"] = "base64" def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: @@ -113,12 +114,14 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: for embedding in obj.data: data = cast(object, embedding.embedding) if not isinstance(data, str): - # numpy is not installed / base64 optimisation isn't enabled for this model yet continue - - embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call] - base64.b64decode(data), dtype="float32" - ).tolist() + if not has_numpy(): + # use array for base64 optimisation + embedding.embedding = array.array("f", base64.b64decode(data)).tolist() + else: + embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call] + base64.b64decode(data), dtype="float32" + ).tolist() return obj @@ -215,7 +218,7 @@ async def create( "dimensions": dimensions, "encoding_format": encoding_format, } - if not is_given(encoding_format) and has_numpy(): + if not is_given(encoding_format): params["encoding_format"] = "base64" def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: @@ -226,12 +229,14 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: for embedding in obj.data: data = cast(object, embedding.embedding) if not isinstance(data, str): - # numpy is not installed / base64 optimisation isn't enabled for this model yet continue - - embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call] - base64.b64decode(data), dtype="float32" - ).tolist() + if not has_numpy(): + # use array for base64 optimisation + embedding.embedding = array.array("f", base64.b64decode(data)).tolist() + else: + embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call] + base64.b64decode(data), dtype="float32" + ).tolist() return obj From af6a9437128fc64643178a12d3e700a962f08977 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 13:00:22 +0000 Subject: [PATCH 782/914] chore(internal): fix type traversing dictionary params (#2097) --- src/openai/_utils/_transform.py | 12 +++++++++++- tests/test_transform.py | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index a6b62cad0c..18afd9d8bd 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -25,7 +25,7 @@ is_annotated_type, strip_annotated_type, ) -from .._compat import model_dump, is_typeddict +from .._compat import get_origin, model_dump, is_typeddict _T = TypeVar("_T") @@ -164,9 +164,14 @@ def _transform_recursive( inner_type = annotation stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return _transform_typeddict(data, stripped_type) + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + if ( # List[T] (is_list_type(stripped_type) and is_list(data)) @@ -307,9 +312,14 @@ async def _async_transform_recursive( inner_type = annotation stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return await _async_transform_typeddict(data, stripped_type) + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + if ( # List[T] (is_list_type(stripped_type) and is_list(data)) diff --git a/tests/test_transform.py b/tests/test_transform.py index 8c6aba6448..385fbe2b2c 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -2,7 +2,7 @@ import io import pathlib -from typing import Any, List, Union, TypeVar, Iterable, Optional, cast +from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict @@ -388,6 +388,15 @@ def my_iter() -> Iterable[Baz8]: } +@parametrize +@pytest.mark.asyncio +async def test_dictionary_items(use_async: bool) -> None: + class DictItems(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}} + + class TypedDictIterableUnionStr(TypedDict): foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] From e2f2db8a1c237997a699a28b4192a054a040fc61 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 15:16:44 +0000 Subject: [PATCH 783/914] feat(pagination): avoid fetching when has_more: false (#2098) --- .stats.yml | 2 +- src/openai/pagination.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 8a5d2c06b2..d59a86d22e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-7c699d4503077d06a4a44f52c0c1f902d19a87c766b8be75b97c8dfd484ad4aa.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-dfb00c627f58e5180af7a9b29ed2f2aa0764a3b9daa6a32a1cc45bc8e48dfe15.yml diff --git a/src/openai/pagination.py b/src/openai/pagination.py index 8293638269..a59cced854 100644 --- a/src/openai/pagination.py +++ b/src/openai/pagination.py @@ -61,6 +61,7 @@ def next_page_info(self) -> None: class SyncCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] + has_more: Optional[bool] = None @override def _get_page_items(self) -> List[_T]: @@ -69,6 +70,14 @@ def _get_page_items(self) -> List[_T]: return [] return data + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + @override def next_page_info(self) -> Optional[PageInfo]: data = self.data @@ -85,6 +94,7 @@ def next_page_info(self) -> Optional[PageInfo]: class AsyncCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] + has_more: Optional[bool] = None @override def _get_page_items(self) -> List[_T]: @@ -93,6 +103,14 @@ def _get_page_items(self) -> List[_T]: return [] return data + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + @override def next_page_info(self) -> Optional[PageInfo]: data = self.data From b5f6dc78feafbd3e34457dbf11b00978502823c0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:22:46 +0000 Subject: [PATCH 784/914] chore(internal): minor type handling changes (#2099) --- src/openai/_models.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index c6e1305087..92986bfdf5 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -451,10 +451,16 @@ def construct_type(*, value: object, type_: object) -> object: If the given value does not match the expected type then it is returned as-is. """ + + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + # we allow `object` as the input type because otherwise, passing things like # `Literal['value']` will be reported as a type error by type checkers type_ = cast("type[object]", type_) if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` @@ -471,7 +477,7 @@ def construct_type(*, value: object, type_: object) -> object: if is_union(origin): try: - return validate_type(type_=cast("type[object]", type_), value=value) + return validate_type(type_=cast("type[object]", original_type or type_), value=value) except Exception: pass From b45168e26f9fbbfcd7c1d1bd28f46a267ffcd3f9 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 10 Feb 2025 18:08:55 +0000 Subject: [PATCH 785/914] fix(parsing): don't default to an empty array (#2106) --- src/openai/lib/_parsing/_completions.py | 2 +- tests/lib/chat/test_completions.py | 22 ++++++++++---------- tests/lib/chat/test_completions_streaming.py | 20 +++++++++--------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index 33c4ccb946..14b1745d3d 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -111,7 +111,7 @@ def parse_chat_completion( response_format=response_format, message=message, ), - "tool_calls": tool_calls, + "tool_calls": tool_calls if tool_calls else None, }, }, ) diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index 48f41eb221..74cee27b93 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -65,7 +65,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte parsed=None, refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ], @@ -132,7 +132,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ], @@ -201,7 +201,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ], @@ -272,7 +272,7 @@ class ColorDetection(BaseModel): parsed=ColorDetection(color=, hex_color_code='#FF0000'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) """ @@ -321,7 +321,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=64.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ), ParsedChoice[Location]( @@ -335,7 +335,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ), ParsedChoice[Location]( @@ -349,7 +349,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=63.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ] @@ -399,7 +399,7 @@ class CalendarEvent: parsed=CalendarEvent(name='Science Fair', date='Friday', participants=['Alice', 'Bob']), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ], @@ -571,7 +571,7 @@ class Location(BaseModel): parsed=None, refusal="I'm very sorry, but I can't assist with that.", role='assistant', - tool_calls=[] + tool_calls=None ) ) ] @@ -855,7 +855,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=58.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ], @@ -930,7 +930,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ], diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 1eed031af7..71b4173738 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -70,7 +70,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte parsed=None, refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ] @@ -147,7 +147,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream parsed=Location(city='San Francisco', temperature=61.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ], @@ -324,7 +324,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=65.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ), ParsedChoice[Location]( @@ -338,7 +338,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=61.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ), ParsedChoice[Location]( @@ -352,7 +352,7 @@ class Location(BaseModel): parsed=Location(city='San Francisco', temperature=59.0, units='f'), refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ] @@ -427,7 +427,7 @@ class Location(BaseModel): parsed=None, refusal="I'm sorry, I can't assist with that request.", role='assistant', - tool_calls=[] + tool_calls=None ) ) ] @@ -501,7 +501,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp parsed=None, refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ] @@ -612,7 +612,7 @@ class Location(BaseModel): parsed=None, refusal="I'm very sorry, but I can't assist with that.", role='assistant', - tool_calls=[] + tool_calls=None ) ) ] @@ -925,7 +925,7 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo parsed=None, refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ] @@ -1040,7 +1040,7 @@ def streamer(client: OpenAI) -> Iterator[ChatCompletionChunk]: parsed=None, refusal=None, role='assistant', - tool_calls=[] + tool_calls=None ) ) ] From 3f8d8205ae41c389541e125336b0ae0c5e437661 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 05:04:25 +0000 Subject: [PATCH 786/914] release: 1.62.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 285741ee32..ccd8ea8be5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.61.1" + ".": "1.62.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 101e7480b7..583fbd9add 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 1.62.0 (2025-02-12) + +Full Changelog: [v1.61.1...v1.62.0](https://github.com/openai/openai-python/compare/v1.61.1...v1.62.0) + +### Features + +* **client:** send `X-Stainless-Read-Timeout` header ([#2094](https://github.com/openai/openai-python/issues/2094)) ([0288213](https://github.com/openai/openai-python/commit/0288213fbfa935c9bf9d56416619ea929ae1cf63)) +* **embeddings:** use stdlib array type for improved performance ([#2060](https://github.com/openai/openai-python/issues/2060)) ([9a95db9](https://github.com/openai/openai-python/commit/9a95db9154ac98678970e7f1652a7cacfd2f7fdb)) +* **pagination:** avoid fetching when has_more: false ([#2098](https://github.com/openai/openai-python/issues/2098)) ([1882483](https://github.com/openai/openai-python/commit/18824832d3a676ae49206cd2b5e09d4796fdf033)) + + +### Bug Fixes + +* **api:** add missing reasoning effort + model enums ([#2096](https://github.com/openai/openai-python/issues/2096)) ([e0ca9f0](https://github.com/openai/openai-python/commit/e0ca9f0f6fae40230f8cab97573914ed632920b6)) +* **parsing:** don't default to an empty array ([#2106](https://github.com/openai/openai-python/issues/2106)) ([8e748bb](https://github.com/openai/openai-python/commit/8e748bb08d9c0d1f7e8a1af31452e25eb7154f55)) + + +### Chores + +* **internal:** fix type traversing dictionary params ([#2097](https://github.com/openai/openai-python/issues/2097)) ([4e5b368](https://github.com/openai/openai-python/commit/4e5b368bf576f38d0f125778edde74ed6d101d7d)) +* **internal:** minor type handling changes ([#2099](https://github.com/openai/openai-python/issues/2099)) ([a2c6da0](https://github.com/openai/openai-python/commit/a2c6da0fbc610ee80a2e044a0b20fc1cc2376962)) + ## 1.61.1 (2025-02-05) Full Changelog: [v1.61.0...v1.61.1](https://github.com/openai/openai-python/compare/v1.61.0...v1.61.1) diff --git a/pyproject.toml b/pyproject.toml index 6f1a6eb28a..85cb145673 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.61.1" +version = "1.62.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7ffe16b95d..7dd5163b53 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.61.1" # x-release-please-version +__version__ = "1.62.0" # x-release-please-version From 300f58bbbde749e023dd1cf39de8f5339780a33d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Feb 2025 19:45:16 +0000 Subject: [PATCH 787/914] feat(api): add support for storing chat completions (#2117) --- .stats.yml | 4 +- api.md | 14 +- src/openai/_utils/_sync.py | 20 +- src/openai/cli/_api/chat/completions.py | 8 +- src/openai/lib/_parsing/_completions.py | 4 +- src/openai/resources/chat/chat.py | 2 +- .../resources/chat/completions/__init__.py | 33 ++ .../chat/{ => completions}/completions.py | 486 +++++++++++++++++- .../resources/chat/completions/messages.py | 212 ++++++++ src/openai/types/chat/__init__.py | 4 + .../types/chat/chat_completion_deleted.py | 18 + .../chat/chat_completion_store_message.py | 11 + .../types/chat/completion_list_params.py | 33 ++ .../types/chat/completion_update_params.py | 22 + src/openai/types/chat/completions/__init__.py | 5 + .../chat/completions/message_list_params.py | 21 + src/openai/types/moderation.py | 6 +- .../chat/completions/__init__.py | 1 + .../chat/completions/test_messages.py | 119 +++++ tests/api_resources/chat/test_completions.py | 310 +++++++++++ tests/lib/test_azure.py | 24 +- tests/test_client.py | 78 +-- 22 files changed, 1350 insertions(+), 85 deletions(-) create mode 100644 src/openai/resources/chat/completions/__init__.py rename src/openai/resources/chat/{ => completions}/completions.py (83%) create mode 100644 src/openai/resources/chat/completions/messages.py create mode 100644 src/openai/types/chat/chat_completion_deleted.py create mode 100644 src/openai/types/chat/chat_completion_store_message.py create mode 100644 src/openai/types/chat/completion_list_params.py create mode 100644 src/openai/types/chat/completion_update_params.py create mode 100644 src/openai/types/chat/completions/__init__.py create mode 100644 src/openai/types/chat/completions/message_list_params.py create mode 100644 tests/api_resources/chat/completions/__init__.py create mode 100644 tests/api_resources/chat/completions/test_messages.py diff --git a/.stats.yml b/.stats.yml index d59a86d22e..658877d3b0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 69 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-dfb00c627f58e5180af7a9b29ed2f2aa0764a3b9daa6a32a1cc45bc8e48dfe15.yml +configured_endpoints: 74 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-4aa6ee65ba9efc789e05e6a5ef0883b2cadf06def8efd863dbf75e9e233067e1.yml diff --git a/api.md b/api.md index efbfeaa68f..2db9d1157e 100644 --- a/api.md +++ b/api.md @@ -48,6 +48,7 @@ from openai.types.chat import ( ChatCompletionContentPartInputAudio, ChatCompletionContentPartRefusal, ChatCompletionContentPartText, + ChatCompletionDeleted, ChatCompletionDeveloperMessageParam, ChatCompletionFunctionCallOption, ChatCompletionFunctionMessageParam, @@ -59,6 +60,7 @@ from openai.types.chat import ( ChatCompletionPredictionContent, ChatCompletionReasoningEffort, ChatCompletionRole, + ChatCompletionStoreMessage, ChatCompletionStreamOptions, ChatCompletionSystemMessageParam, ChatCompletionTokenLogprob, @@ -71,7 +73,17 @@ from openai.types.chat import ( Methods: -- client.chat.completions.create(\*\*params) -> ChatCompletion +- client.chat.completions.create(\*\*params) -> ChatCompletion +- client.chat.completions.retrieve(completion_id) -> ChatCompletion +- client.chat.completions.update(completion_id, \*\*params) -> ChatCompletion +- client.chat.completions.list(\*\*params) -> SyncCursorPage[ChatCompletion] +- client.chat.completions.delete(completion_id) -> ChatCompletionDeleted + +### Messages + +Methods: + +- client.chat.completions.messages.list(completion_id, \*\*params) -> SyncCursorPage[ChatCompletionStoreMessage] # Embeddings diff --git a/src/openai/_utils/_sync.py b/src/openai/_utils/_sync.py index 5d9e2c2ac9..ad7ec71b76 100644 --- a/src/openai/_utils/_sync.py +++ b/src/openai/_utils/_sync.py @@ -7,16 +7,20 @@ from typing import Any, TypeVar, Callable, Awaitable from typing_extensions import ParamSpec +import anyio +import sniffio +import anyio.to_thread + T_Retval = TypeVar("T_Retval") T_ParamSpec = ParamSpec("T_ParamSpec") if sys.version_info >= (3, 9): - to_thread = asyncio.to_thread + _asyncio_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( + async def _asyncio_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. @@ -34,6 +38,17 @@ async def to_thread( return await loop.run_in_executor(None, func_call) +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await _asyncio_to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + # inspired by `asyncer`, https://github.com/tiangolo/asyncer def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ @@ -50,6 +65,7 @@ def blocking_func(arg1, arg2, kwarg1=None): # blocking code return result + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) ``` diff --git a/src/openai/cli/_api/chat/completions.py b/src/openai/cli/_api/chat/completions.py index feedb5ccab..344eeff37c 100644 --- a/src/openai/cli/_api/chat/completions.py +++ b/src/openai/cli/_api/chat/completions.py @@ -104,13 +104,13 @@ def create(args: CLIChatCompletionCreateArgs) -> None: "stream": False, } if args.temperature is not None: - params['temperature'] = args.temperature + params["temperature"] = args.temperature if args.stop is not None: - params['stop'] = args.stop + params["stop"] = args.stop if args.top_p is not None: - params['top_p'] = args.top_p + params["top_p"] = args.top_p if args.n is not None: - params['n'] = args.n + params["n"] = args.n if args.stream: params["stream"] = args.stream # type: ignore if args.max_tokens is not None: diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index 14b1745d3d..c160070b66 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -45,13 +45,13 @@ def validate_input_tools( for tool in tools: if tool["type"] != "function": raise ValueError( - f'Currently only `function` tool types support auto-parsing; Received `{tool["type"]}`', + f"Currently only `function` tool types support auto-parsing; Received `{tool['type']}`", ) strict = tool["function"].get("strict") if strict is not True: raise ValueError( - f'`{tool["function"]["name"]}` is not strict. Only `strict` function tools can be auto-parsed' + f"`{tool['function']['name']}` is not strict. Only `strict` function tools can be auto-parsed" ) diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index 9c4aacc953..14f9224b41 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -4,7 +4,7 @@ from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource -from .completions import ( +from .completions.completions import ( Completions, AsyncCompletions, CompletionsWithRawResponse, diff --git a/src/openai/resources/chat/completions/__init__.py b/src/openai/resources/chat/completions/__init__.py new file mode 100644 index 0000000000..12d3b3aa28 --- /dev/null +++ b/src/openai/resources/chat/completions/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse, +) +from .completions import ( + Completions, + AsyncCompletions, + CompletionsWithRawResponse, + AsyncCompletionsWithRawResponse, + CompletionsWithStreamingResponse, + AsyncCompletionsWithStreamingResponse, +) + +__all__ = [ + "Messages", + "AsyncMessages", + "MessagesWithRawResponse", + "AsyncMessagesWithRawResponse", + "MessagesWithStreamingResponse", + "AsyncMessagesWithStreamingResponse", + "Completions", + "AsyncCompletions", + "CompletionsWithRawResponse", + "AsyncCompletionsWithRawResponse", + "CompletionsWithStreamingResponse", + "AsyncCompletionsWithStreamingResponse", +] diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions/completions.py similarity index 83% rename from src/openai/resources/chat/completions.py rename to src/openai/resources/chat/completions/completions.py index cc839103a0..1753f6c990 100644 --- a/src/openai/resources/chat/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -9,40 +9,56 @@ import httpx import pydantic -from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( +from .... import _legacy_response +from .messages import ( + Messages, + AsyncMessages, + MessagesWithRawResponse, + AsyncMessagesWithRawResponse, + MessagesWithStreamingResponse, + AsyncMessagesWithStreamingResponse, +) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( required_args, maybe_transform, async_maybe_transform, ) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..._streaming import Stream, AsyncStream -from ...types.chat import ( +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._streaming import Stream, AsyncStream +from ....pagination import SyncCursorPage, AsyncCursorPage +from ....types.chat import ( ChatCompletionAudioParam, ChatCompletionReasoningEffort, + completion_list_params, completion_create_params, + completion_update_params, ) -from ..._base_client import make_request_options -from ...types.chat_model import ChatModel -from ...types.chat.chat_completion import ChatCompletion -from ...types.shared_params.metadata import Metadata -from ...types.chat.chat_completion_chunk import ChatCompletionChunk -from ...types.chat.chat_completion_modality import ChatCompletionModality -from ...types.chat.chat_completion_tool_param import ChatCompletionToolParam -from ...types.chat.chat_completion_audio_param import ChatCompletionAudioParam -from ...types.chat.chat_completion_message_param import ChatCompletionMessageParam -from ...types.chat.chat_completion_reasoning_effort import ChatCompletionReasoningEffort -from ...types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam -from ...types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam -from ...types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam +from ...._base_client import AsyncPaginator, make_request_options +from ....types.chat_model import ChatModel +from ....types.chat.chat_completion import ChatCompletion +from ....types.shared_params.metadata import Metadata +from ....types.chat.chat_completion_chunk import ChatCompletionChunk +from ....types.chat.chat_completion_deleted import ChatCompletionDeleted +from ....types.chat.chat_completion_modality import ChatCompletionModality +from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam +from ....types.chat.chat_completion_audio_param import ChatCompletionAudioParam +from ....types.chat.chat_completion_message_param import ChatCompletionMessageParam +from ....types.chat.chat_completion_reasoning_effort import ChatCompletionReasoningEffort +from ....types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam +from ....types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam +from ....types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam __all__ = ["Completions", "AsyncCompletions"] class Completions(SyncAPIResource): + @cached_property + def messages(self) -> Messages: + return Messages(self._client) + @cached_property def with_raw_response(self) -> CompletionsWithRawResponse: """ @@ -905,8 +921,192 @@ def create( stream_cls=Stream[ChatCompletionChunk], ) + def retrieve( + self, + completion_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: + """Get a stored chat completion. + + Only chat completions that have been created with + the `store` parameter set to `true` will be returned. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._get( + f"/chat/completions/{completion_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatCompletion, + ) + + def update( + self, + completion_id: str, + *, + metadata: Optional[Metadata], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: + """Modify a stored chat completion. + + Only chat completions that have been created + with the `store` parameter set to `true` can be modified. Currently, the only + supported modification is to update the `metadata` field. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._post( + f"/chat/completions/{completion_id}", + body=maybe_transform({"metadata": metadata}, completion_update_params.CompletionUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatCompletion, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + model: str | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[ChatCompletion]: + """List stored chat completions. + + Only chat completions that have been stored with + the `store` parameter set to `true` will be returned. + + Args: + after: Identifier for the last chat completion from the previous pagination request. + + limit: Number of chat completions to retrieve. + + metadata: + A list of metadata keys to filter the chat completions by. Example: + + `metadata[key1]=value1&metadata[key2]=value2` + + model: The model used to generate the chat completions. + + order: Sort order for chat completions by timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `asc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/chat/completions", + page=SyncCursorPage[ChatCompletion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "metadata": metadata, + "model": model, + "order": order, + }, + completion_list_params.CompletionListParams, + ), + ), + model=ChatCompletion, + ) + + def delete( + self, + completion_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletionDeleted: + """Delete a stored chat completion. + + Only chat completions that have been created + with the `store` parameter set to `true` can be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._delete( + f"/chat/completions/{completion_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatCompletionDeleted, + ) + class AsyncCompletions(AsyncAPIResource): + @cached_property + def messages(self) -> AsyncMessages: + return AsyncMessages(self._client) + @cached_property def with_raw_response(self) -> AsyncCompletionsWithRawResponse: """ @@ -1769,6 +1969,186 @@ async def create( stream_cls=AsyncStream[ChatCompletionChunk], ) + async def retrieve( + self, + completion_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: + """Get a stored chat completion. + + Only chat completions that have been created with + the `store` parameter set to `true` will be returned. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return await self._get( + f"/chat/completions/{completion_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatCompletion, + ) + + async def update( + self, + completion_id: str, + *, + metadata: Optional[Metadata], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: + """Modify a stored chat completion. + + Only chat completions that have been created + with the `store` parameter set to `true` can be modified. Currently, the only + supported modification is to update the `metadata` field. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return await self._post( + f"/chat/completions/{completion_id}", + body=await async_maybe_transform({"metadata": metadata}, completion_update_params.CompletionUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatCompletion, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + model: str | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[ChatCompletion, AsyncCursorPage[ChatCompletion]]: + """List stored chat completions. + + Only chat completions that have been stored with + the `store` parameter set to `true` will be returned. + + Args: + after: Identifier for the last chat completion from the previous pagination request. + + limit: Number of chat completions to retrieve. + + metadata: + A list of metadata keys to filter the chat completions by. Example: + + `metadata[key1]=value1&metadata[key2]=value2` + + model: The model used to generate the chat completions. + + order: Sort order for chat completions by timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `asc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/chat/completions", + page=AsyncCursorPage[ChatCompletion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "metadata": metadata, + "model": model, + "order": order, + }, + completion_list_params.CompletionListParams, + ), + ), + model=ChatCompletion, + ) + + async def delete( + self, + completion_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletionDeleted: + """Delete a stored chat completion. + + Only chat completions that have been created + with the `store` parameter set to `true` can be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return await self._delete( + f"/chat/completions/{completion_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatCompletionDeleted, + ) + class CompletionsWithRawResponse: def __init__(self, completions: Completions) -> None: @@ -1777,6 +2157,22 @@ def __init__(self, completions: Completions) -> None: self.create = _legacy_response.to_raw_response_wrapper( completions.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + completions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + completions.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + completions.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + completions.delete, + ) + + @cached_property + def messages(self) -> MessagesWithRawResponse: + return MessagesWithRawResponse(self._completions.messages) class AsyncCompletionsWithRawResponse: @@ -1786,6 +2182,22 @@ def __init__(self, completions: AsyncCompletions) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( completions.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + completions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + completions.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + completions.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + completions.delete, + ) + + @cached_property + def messages(self) -> AsyncMessagesWithRawResponse: + return AsyncMessagesWithRawResponse(self._completions.messages) class CompletionsWithStreamingResponse: @@ -1795,6 +2207,22 @@ def __init__(self, completions: Completions) -> None: self.create = to_streamed_response_wrapper( completions.create, ) + self.retrieve = to_streamed_response_wrapper( + completions.retrieve, + ) + self.update = to_streamed_response_wrapper( + completions.update, + ) + self.list = to_streamed_response_wrapper( + completions.list, + ) + self.delete = to_streamed_response_wrapper( + completions.delete, + ) + + @cached_property + def messages(self) -> MessagesWithStreamingResponse: + return MessagesWithStreamingResponse(self._completions.messages) class AsyncCompletionsWithStreamingResponse: @@ -1804,6 +2232,22 @@ def __init__(self, completions: AsyncCompletions) -> None: self.create = async_to_streamed_response_wrapper( completions.create, ) + self.retrieve = async_to_streamed_response_wrapper( + completions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + completions.update, + ) + self.list = async_to_streamed_response_wrapper( + completions.list, + ) + self.delete = async_to_streamed_response_wrapper( + completions.delete, + ) + + @cached_property + def messages(self) -> AsyncMessagesWithStreamingResponse: + return AsyncMessagesWithStreamingResponse(self._completions.messages) def validate_response_format(response_format: object) -> None: diff --git a/src/openai/resources/chat/completions/messages.py b/src/openai/resources/chat/completions/messages.py new file mode 100644 index 0000000000..b71d670927 --- /dev/null +++ b/src/openai/resources/chat/completions/messages.py @@ -0,0 +1,212 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.chat.completions import message_list_params +from ....types.chat.chat_completion_store_message import ChatCompletionStoreMessage + +__all__ = ["Messages", "AsyncMessages"] + + +class Messages(SyncAPIResource): + @cached_property + def with_raw_response(self) -> MessagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return MessagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> MessagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return MessagesWithStreamingResponse(self) + + def list( + self, + completion_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[ChatCompletionStoreMessage]: + """Get the messages in a stored chat completion. + + Only chat completions that have + been created with the `store` parameter set to `true` will be returned. + + Args: + after: Identifier for the last message from the previous pagination request. + + limit: Number of messages to retrieve. + + order: Sort order for messages by timestamp. Use `asc` for ascending order or `desc` + for descending order. Defaults to `asc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._get_api_list( + f"/chat/completions/{completion_id}/messages", + page=SyncCursorPage[ChatCompletionStoreMessage], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + message_list_params.MessageListParams, + ), + ), + model=ChatCompletionStoreMessage, + ) + + +class AsyncMessages(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncMessagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncMessagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncMessagesWithStreamingResponse(self) + + def list( + self, + completion_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[ChatCompletionStoreMessage, AsyncCursorPage[ChatCompletionStoreMessage]]: + """Get the messages in a stored chat completion. + + Only chat completions that have + been created with the `store` parameter set to `true` will be returned. + + Args: + after: Identifier for the last message from the previous pagination request. + + limit: Number of messages to retrieve. + + order: Sort order for messages by timestamp. Use `asc` for ascending order or `desc` + for descending order. Defaults to `asc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not completion_id: + raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") + return self._get_api_list( + f"/chat/completions/{completion_id}/messages", + page=AsyncCursorPage[ChatCompletionStoreMessage], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + message_list_params.MessageListParams, + ), + ), + model=ChatCompletionStoreMessage, + ) + + +class MessagesWithRawResponse: + def __init__(self, messages: Messages) -> None: + self._messages = messages + + self.list = _legacy_response.to_raw_response_wrapper( + messages.list, + ) + + +class AsyncMessagesWithRawResponse: + def __init__(self, messages: AsyncMessages) -> None: + self._messages = messages + + self.list = _legacy_response.async_to_raw_response_wrapper( + messages.list, + ) + + +class MessagesWithStreamingResponse: + def __init__(self, messages: Messages) -> None: + self._messages = messages + + self.list = to_streamed_response_wrapper( + messages.list, + ) + + +class AsyncMessagesWithStreamingResponse: + def __init__(self, messages: AsyncMessages) -> None: + self._messages = messages + + self.list = async_to_streamed_response_wrapper( + messages.list, + ) diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index c623a982af..b4f43b298f 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -6,14 +6,17 @@ from .chat_completion_role import ChatCompletionRole as ChatCompletionRole from .chat_completion_audio import ChatCompletionAudio as ChatCompletionAudio from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk +from .completion_list_params import CompletionListParams as CompletionListParams from .parsed_chat_completion import ( ParsedChoice as ParsedChoice, ParsedChatCompletion as ParsedChatCompletion, ParsedChatCompletionMessage as ParsedChatCompletionMessage, ) +from .chat_completion_deleted import ChatCompletionDeleted as ChatCompletionDeleted from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage from .chat_completion_modality import ChatCompletionModality as ChatCompletionModality from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .completion_update_params import CompletionUpdateParams as CompletionUpdateParams from .parsed_function_tool_call import ( ParsedFunction as ParsedFunction, ParsedFunctionToolCall as ParsedFunctionToolCall, @@ -21,6 +24,7 @@ from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam from .chat_completion_audio_param import ChatCompletionAudioParam as ChatCompletionAudioParam from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam +from .chat_completion_store_message import ChatCompletionStoreMessage as ChatCompletionStoreMessage from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob from .chat_completion_reasoning_effort import ChatCompletionReasoningEffort as ChatCompletionReasoningEffort from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall diff --git a/src/openai/types/chat/chat_completion_deleted.py b/src/openai/types/chat/chat_completion_deleted.py new file mode 100644 index 0000000000..0a541cb23d --- /dev/null +++ b/src/openai/types/chat/chat_completion_deleted.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionDeleted"] + + +class ChatCompletionDeleted(BaseModel): + id: str + """The ID of the chat completion that was deleted.""" + + deleted: bool + """Whether the chat completion was deleted.""" + + object: Literal["chat.completion.deleted"] + """The type of object being deleted.""" diff --git a/src/openai/types/chat/chat_completion_store_message.py b/src/openai/types/chat/chat_completion_store_message.py new file mode 100644 index 0000000000..95adc08af8 --- /dev/null +++ b/src/openai/types/chat/chat_completion_store_message.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from .chat_completion_message import ChatCompletionMessage + +__all__ = ["ChatCompletionStoreMessage"] + + +class ChatCompletionStoreMessage(ChatCompletionMessage): + id: str + """The identifier of the chat message.""" diff --git a/src/openai/types/chat/completion_list_params.py b/src/openai/types/chat/completion_list_params.py new file mode 100644 index 0000000000..a8fce900ce --- /dev/null +++ b/src/openai/types/chat/completion_list_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from ..shared_params.metadata import Metadata + +__all__ = ["CompletionListParams"] + + +class CompletionListParams(TypedDict, total=False): + after: str + """Identifier for the last chat completion from the previous pagination request.""" + + limit: int + """Number of chat completions to retrieve.""" + + metadata: Optional[Metadata] + """A list of metadata keys to filter the chat completions by. Example: + + `metadata[key1]=value1&metadata[key2]=value2` + """ + + model: str + """The model used to generate the chat completions.""" + + order: Literal["asc", "desc"] + """Sort order for chat completions by timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. + """ diff --git a/src/openai/types/chat/completion_update_params.py b/src/openai/types/chat/completion_update_params.py new file mode 100644 index 0000000000..fc71733f07 --- /dev/null +++ b/src/openai/types/chat/completion_update_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ..shared_params.metadata import Metadata + +__all__ = ["CompletionUpdateParams"] + + +class CompletionUpdateParams(TypedDict, total=False): + metadata: Required[Optional[Metadata]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ diff --git a/src/openai/types/chat/completions/__init__.py b/src/openai/types/chat/completions/__init__.py new file mode 100644 index 0000000000..b8e62d6a64 --- /dev/null +++ b/src/openai/types/chat/completions/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .message_list_params import MessageListParams as MessageListParams diff --git a/src/openai/types/chat/completions/message_list_params.py b/src/openai/types/chat/completions/message_list_params.py new file mode 100644 index 0000000000..4e694e83ea --- /dev/null +++ b/src/openai/types/chat/completions/message_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["MessageListParams"] + + +class MessageListParams(TypedDict, total=False): + after: str + """Identifier for the last message from the previous pagination request.""" + + limit: int + """Number of messages to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order for messages by timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. + """ diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index e4ec182ce2..608f562218 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List +from typing import List, Optional from typing_extensions import Literal from pydantic import Field as FieldInfo @@ -38,14 +38,14 @@ class Categories(BaseModel): orientation, disability status, or caste. """ - illicit: bool + illicit: Optional[bool] = None """ Content that includes instructions or advice that facilitate the planning or execution of wrongdoing, or that gives advice or instruction on how to commit illicit acts. For example, "how to shoplift" would fit this category. """ - illicit_violent: bool = FieldInfo(alias="illicit/violent") + illicit_violent: Optional[bool] = FieldInfo(alias="illicit/violent", default=None) """ Content that includes instructions or advice that facilitate the planning or execution of wrongdoing that also includes violence, or that gives advice or diff --git a/tests/api_resources/chat/completions/__init__.py b/tests/api_resources/chat/completions/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/chat/completions/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/chat/completions/test_messages.py b/tests/api_resources/chat/completions/test_messages.py new file mode 100644 index 0000000000..5caac9ec6c --- /dev/null +++ b/tests/api_resources/chat/completions/test_messages.py @@ -0,0 +1,119 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.chat import ChatCompletionStoreMessage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestMessages: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + message = client.chat.completions.messages.list( + completion_id="completion_id", + ) + assert_matches_type(SyncCursorPage[ChatCompletionStoreMessage], message, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + message = client.chat.completions.messages.list( + completion_id="completion_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[ChatCompletionStoreMessage], message, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.chat.completions.messages.with_raw_response.list( + completion_id="completion_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(SyncCursorPage[ChatCompletionStoreMessage], message, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.chat.completions.messages.with_streaming_response.list( + completion_id="completion_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = response.parse() + assert_matches_type(SyncCursorPage[ChatCompletionStoreMessage], message, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `completion_id` but received ''"): + client.chat.completions.messages.with_raw_response.list( + completion_id="", + ) + + +class TestAsyncMessages: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + message = await async_client.chat.completions.messages.list( + completion_id="completion_id", + ) + assert_matches_type(AsyncCursorPage[ChatCompletionStoreMessage], message, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + message = await async_client.chat.completions.messages.list( + completion_id="completion_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[ChatCompletionStoreMessage], message, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.chat.completions.messages.with_raw_response.list( + completion_id="completion_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(AsyncCursorPage[ChatCompletionStoreMessage], message, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.chat.completions.messages.with_streaming_response.list( + completion_id="completion_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = await response.parse() + assert_matches_type(AsyncCursorPage[ChatCompletionStoreMessage], message, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `completion_id` but received ''"): + await async_client.chat.completions.messages.with_raw_response.list( + completion_id="", + ) diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 25c9a36164..48b687a70e 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -10,8 +10,10 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.chat import ( ChatCompletion, + ChatCompletionDeleted, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -248,6 +250,160 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + completion = client.chat.completions.retrieve( + "completion_id", + ) + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.chat.completions.with_raw_response.retrieve( + "completion_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.chat.completions.with_streaming_response.retrieve( + "completion_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `completion_id` but received ''"): + client.chat.completions.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + completion = client.chat.completions.update( + completion_id="completion_id", + metadata={"foo": "string"}, + ) + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.chat.completions.with_raw_response.update( + completion_id="completion_id", + metadata={"foo": "string"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.chat.completions.with_streaming_response.update( + completion_id="completion_id", + metadata={"foo": "string"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `completion_id` but received ''"): + client.chat.completions.with_raw_response.update( + completion_id="", + metadata={"foo": "string"}, + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + completion = client.chat.completions.list() + assert_matches_type(SyncCursorPage[ChatCompletion], completion, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + completion = client.chat.completions.list( + after="after", + limit=0, + metadata={"foo": "string"}, + model="model", + order="asc", + ) + assert_matches_type(SyncCursorPage[ChatCompletion], completion, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.chat.completions.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(SyncCursorPage[ChatCompletion], completion, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.chat.completions.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = response.parse() + assert_matches_type(SyncCursorPage[ChatCompletion], completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + completion = client.chat.completions.delete( + "completion_id", + ) + assert_matches_type(ChatCompletionDeleted, completion, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.chat.completions.with_raw_response.delete( + "completion_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(ChatCompletionDeleted, completion, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.chat.completions.with_streaming_response.delete( + "completion_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = response.parse() + assert_matches_type(ChatCompletionDeleted, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `completion_id` but received ''"): + client.chat.completions.with_raw_response.delete( + "", + ) + @parametrize def test_method_create_disallows_pydantic(self, client: OpenAI) -> None: class MyModel(pydantic.BaseModel): @@ -497,6 +653,160 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpe assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.chat.completions.retrieve( + "completion_id", + ) + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.chat.completions.with_raw_response.retrieve( + "completion_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.chat.completions.with_streaming_response.retrieve( + "completion_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = await response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `completion_id` but received ''"): + await async_client.chat.completions.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.chat.completions.update( + completion_id="completion_id", + metadata={"foo": "string"}, + ) + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.chat.completions.with_raw_response.update( + completion_id="completion_id", + metadata={"foo": "string"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.chat.completions.with_streaming_response.update( + completion_id="completion_id", + metadata={"foo": "string"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = await response.parse() + assert_matches_type(ChatCompletion, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `completion_id` but received ''"): + await async_client.chat.completions.with_raw_response.update( + completion_id="", + metadata={"foo": "string"}, + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.chat.completions.list() + assert_matches_type(AsyncCursorPage[ChatCompletion], completion, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.chat.completions.list( + after="after", + limit=0, + metadata={"foo": "string"}, + model="model", + order="asc", + ) + assert_matches_type(AsyncCursorPage[ChatCompletion], completion, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.chat.completions.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(AsyncCursorPage[ChatCompletion], completion, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.chat.completions.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = await response.parse() + assert_matches_type(AsyncCursorPage[ChatCompletion], completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + completion = await async_client.chat.completions.delete( + "completion_id", + ) + assert_matches_type(ChatCompletionDeleted, completion, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.chat.completions.with_raw_response.delete( + "completion_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + completion = response.parse() + assert_matches_type(ChatCompletionDeleted, completion, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.chat.completions.with_streaming_response.delete( + "completion_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + completion = await response.parse() + assert_matches_type(ChatCompletionDeleted, completion, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `completion_id` but received ''"): + await async_client.chat.completions.with_raw_response.delete( + "", + ) + @parametrize async def test_method_create_disallows_pydantic(self, async_client: AsyncOpenAI) -> None: class MyModel(pydantic.BaseModel): diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py index 626d7df311..a28aa8c2f6 100644 --- a/tests/lib/test_azure.py +++ b/tests/lib/test_azure.py @@ -153,7 +153,6 @@ def token_provider() -> str: class TestAzureLogging: - @pytest.fixture(autouse=True) def logger_with_filter(self) -> logging.Logger: logger = logging.getLogger("openai") @@ -165,9 +164,7 @@ def logger_with_filter(self) -> logging.Logger: def test_azure_api_key_redacted(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: respx_mock.post( "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01" - ).mock( - return_value=httpx.Response(200, json={"model": "gpt-4"}) - ) + ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"})) client = AzureOpenAI( api_version="2024-06-01", @@ -182,14 +179,11 @@ def test_azure_api_key_redacted(self, respx_mock: MockRouter, caplog: pytest.Log if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]): assert record.args["headers"]["api-key"] == "" - @pytest.mark.respx() def test_azure_bearer_token_redacted(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: respx_mock.post( "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01" - ).mock( - return_value=httpx.Response(200, json={"model": "gpt-4"}) - ) + ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"})) client = AzureOpenAI( api_version="2024-06-01", @@ -204,15 +198,12 @@ def test_azure_bearer_token_redacted(self, respx_mock: MockRouter, caplog: pytes if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]): assert record.args["headers"]["Authorization"] == "" - @pytest.mark.asyncio @pytest.mark.respx() async def test_azure_api_key_redacted_async(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: respx_mock.post( "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01" - ).mock( - return_value=httpx.Response(200, json={"model": "gpt-4"}) - ) + ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"})) client = AsyncAzureOpenAI( api_version="2024-06-01", @@ -227,15 +218,14 @@ async def test_azure_api_key_redacted_async(self, respx_mock: MockRouter, caplog if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]): assert record.args["headers"]["api-key"] == "" - @pytest.mark.asyncio @pytest.mark.respx() - async def test_azure_bearer_token_redacted_async(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: + async def test_azure_bearer_token_redacted_async( + self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture + ) -> None: respx_mock.post( "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01" - ).mock( - return_value=httpx.Response(200, json={"model": "gpt-4"}) - ) + ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"})) client = AsyncAzureOpenAI( api_version="2024-06-01", diff --git a/tests/test_client.py b/tests/test_client.py index 41da2d5d04..62654afe1e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -23,11 +23,13 @@ from openai import OpenAI, AsyncOpenAI, APIResponseValidationError from openai._types import Omit +from openai._utils import maybe_transform from openai._models import BaseModel, FinalRequestOptions from openai._constants import RAW_RESPONSE_HEADER from openai._streaming import Stream, AsyncStream from openai._exceptions import OpenAIError, APIStatusError, APITimeoutError, APIResponseValidationError from openai._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options +from openai.types.chat.completion_create_params import CompletionCreateParamsNonStreaming from .utils import update_env @@ -724,14 +726,17 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No "/chat/completions", body=cast( object, - dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-4o", + maybe_transform( + dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-4o", + ), + CompletionCreateParamsNonStreaming, ), ), cast_to=httpx.Response, @@ -750,14 +755,17 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non "/chat/completions", body=cast( object, - dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-4o", + maybe_transform( + dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-4o", + ), + CompletionCreateParamsNonStreaming, ), ), cast_to=httpx.Response, @@ -1591,14 +1599,17 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) "/chat/completions", body=cast( object, - dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-4o", + maybe_transform( + dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-4o", + ), + CompletionCreateParamsNonStreaming, ), ), cast_to=httpx.Response, @@ -1617,14 +1628,17 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) "/chat/completions", body=cast( object, - dict( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-4o", + maybe_transform( + dict( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-4o", + ), + CompletionCreateParamsNonStreaming, ), ), cast_to=httpx.Response, From 720ae54414f392202289578c9cc3b84cccc7432c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Feb 2025 19:45:55 +0000 Subject: [PATCH 788/914] release: 1.63.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ccd8ea8be5..7b243c5918 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.62.0" + ".": "1.63.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 583fbd9add..361effb558 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.63.0 (2025-02-13) + +Full Changelog: [v1.62.0...v1.63.0](https://github.com/openai/openai-python/compare/v1.62.0...v1.63.0) + +### Features + +* **api:** add support for storing chat completions ([#2117](https://github.com/openai/openai-python/issues/2117)) ([2357a8f](https://github.com/openai/openai-python/commit/2357a8f97246a3fe17c6ac1fb0d7a67d6f1ffc1d)) + ## 1.62.0 (2025-02-12) Full Changelog: [v1.61.1...v1.62.0](https://github.com/openai/openai-python/compare/v1.61.1...v1.62.0) diff --git a/pyproject.toml b/pyproject.toml index 85cb145673..fed9f20ab3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.62.0" +version = "1.63.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7dd5163b53..f58a5a5da8 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.62.0" # x-release-please-version +__version__ = "1.63.0" # x-release-please-version From a942394481e58a6f8b4a21f1b75af1ca6fcfd809 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Feb 2025 11:11:51 +0000 Subject: [PATCH 789/914] chore(internal): temporary commit (#2121) --- .github/ISSUE_TEMPLATE/bug_report.yml | 64 ---------------------- .github/ISSUE_TEMPLATE/config.yml | 7 --- .github/ISSUE_TEMPLATE/feature_request.yml | 28 ---------- .github/pull_request_template.md | 10 ---- 4 files changed, 109 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index fa09dbe5b0..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Bug report -description: Report an issue or bug with this library -labels: ['bug'] -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this bug report! - - type: checkboxes - id: non_api - attributes: - label: Confirm this is an issue with the Python library and not an underlying OpenAI API - description: Issues with the underlying OpenAI API should be reported on our [Developer Community](https://community.openai.com/c/api/7) - options: - - label: This is an issue with the Python library - required: true - - type: textarea - id: what-happened - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is, and any additional context. - placeholder: Tell us what you see! - validations: - required: true - - type: textarea - id: repro-steps - attributes: - label: To Reproduce - description: Steps to reproduce the behavior. - placeholder: | - 1. Fetch a '...' - 2. Update the '....' - 3. See error - validations: - required: true - - type: textarea - id: code-snippets - attributes: - label: Code snippets - description: If applicable, add code snippets to help explain your problem. - render: Python - validations: - required: false - - type: input - id: os - attributes: - label: OS - placeholder: macOS - validations: - required: true - - type: input - id: language-version - attributes: - label: Python version - placeholder: Python v3.11.4 - validations: - required: true - - type: input - id: lib-version - attributes: - label: Library version - placeholder: openai v1.0.1 - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 0498cf7f6f..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,7 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: OpenAI support - url: https://help.openai.com/ - about: | - Please only file issues here that you believe represent actual bugs or feature requests for the OpenAI Python library. - If you're having general trouble with the OpenAI API, please visit our help center to get support. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index b529547d08..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Feature request -description: Suggest an idea for this library -labels: ['feature-request'] -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this feature request! - - type: checkboxes - id: non_api - attributes: - label: Confirm this is a feature request for the Python library and not the underlying OpenAI API. - description: Feature requests for the underlying OpenAI API should be reported on our [Developer Community](https://community.openai.com/c/api/7) - options: - - label: This is a feature request for the Python library - required: true - - type: textarea - id: feature - attributes: - label: Describe the feature or improvement you're requesting - description: A clear and concise description of what you want to happen. - validations: - required: true - - type: textarea - id: context - attributes: - label: Additional context - description: Add any other context about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 4416b1e547..0000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,10 +0,0 @@ - - - - - -- [ ] I understand that this repository is auto-generated and my pull request may not be merged - -## Changes being requested - -## Additional context & links From fea5e6b0dbc4353fcd35d5fcf11273347c4ee110 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 05:04:23 +0000 Subject: [PATCH 790/914] release: 1.63.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7b243c5918..d9c83dfafb 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.63.0" + ".": "1.63.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 361effb558..1bcb96c22c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.63.1 (2025-02-17) + +Full Changelog: [v1.63.0...v1.63.1](https://github.com/openai/openai-python/compare/v1.63.0...v1.63.1) + +### Chores + +* **internal:** temporary commit ([#2121](https://github.com/openai/openai-python/issues/2121)) ([f7f8361](https://github.com/openai/openai-python/commit/f7f83614c8da84c6725d60936f08f9f1a65f0a9e)) + ## 1.63.0 (2025-02-13) Full Changelog: [v1.62.0...v1.63.0](https://github.com/openai/openai-python/compare/v1.62.0...v1.63.0) diff --git a/pyproject.toml b/pyproject.toml index fed9f20ab3..0e90c2cad7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.63.0" +version = "1.63.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f58a5a5da8..1d08feda0d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.63.0" # x-release-please-version +__version__ = "1.63.1" # x-release-please-version From 7319e6e3f139d68173e03033f077732c4c4bdfa5 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 17 Feb 2025 13:08:59 +0000 Subject: [PATCH 791/914] chore(internal): revert temporary commit (#2121) --- .github/ISSUE_TEMPLATE/bug_report.yml | 64 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 7 +++ .github/ISSUE_TEMPLATE/feature_request.yml | 28 ++++++++++ .github/pull_request_template.md | 10 ++++ 4 files changed, 109 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..fa09dbe5b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,64 @@ +name: Bug report +description: Report an issue or bug with this library +labels: ['bug'] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: checkboxes + id: non_api + attributes: + label: Confirm this is an issue with the Python library and not an underlying OpenAI API + description: Issues with the underlying OpenAI API should be reported on our [Developer Community](https://community.openai.com/c/api/7) + options: + - label: This is an issue with the Python library + required: true + - type: textarea + id: what-happened + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is, and any additional context. + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: repro-steps + attributes: + label: To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. Fetch a '...' + 2. Update the '....' + 3. See error + validations: + required: true + - type: textarea + id: code-snippets + attributes: + label: Code snippets + description: If applicable, add code snippets to help explain your problem. + render: Python + validations: + required: false + - type: input + id: os + attributes: + label: OS + placeholder: macOS + validations: + required: true + - type: input + id: language-version + attributes: + label: Python version + placeholder: Python v3.11.4 + validations: + required: true + - type: input + id: lib-version + attributes: + label: Library version + placeholder: openai v1.0.1 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..0498cf7f6f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +blank_issues_enabled: false +contact_links: + - name: OpenAI support + url: https://help.openai.com/ + about: | + Please only file issues here that you believe represent actual bugs or feature requests for the OpenAI Python library. + If you're having general trouble with the OpenAI API, please visit our help center to get support. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..b529547d08 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,28 @@ +name: Feature request +description: Suggest an idea for this library +labels: ['feature-request'] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request! + - type: checkboxes + id: non_api + attributes: + label: Confirm this is a feature request for the Python library and not the underlying OpenAI API. + description: Feature requests for the underlying OpenAI API should be reported on our [Developer Community](https://community.openai.com/c/api/7) + options: + - label: This is a feature request for the Python library + required: true + - type: textarea + id: feature + attributes: + label: Describe the feature or improvement you're requesting + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..4416b1e547 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ + + + + + +- [ ] I understand that this repository is auto-generated and my pull request may not be merged + +## Changes being requested + +## Additional context & links From 2e56c8da6f163db00a4ca362020148bb391edca9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 13:09:50 +0000 Subject: [PATCH 792/914] release: 1.63.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d9c83dfafb..a9866d99a2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.63.1" + ".": "1.63.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bcb96c22c..7ed87c5875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.63.2 (2025-02-17) + +Full Changelog: [v1.63.1...v1.63.2](https://github.com/openai/openai-python/compare/v1.63.1...v1.63.2) + +### Chores + +* **internal:** revert temporary commit ([#2121](https://github.com/openai/openai-python/issues/2121)) ([72458ab](https://github.com/openai/openai-python/commit/72458abeed3dd95db8aabed94a33bb12a916f8b7)) + ## 1.63.1 (2025-02-17) Full Changelog: [v1.63.0...v1.63.1](https://github.com/openai/openai-python/compare/v1.63.0...v1.63.1) diff --git a/pyproject.toml b/pyproject.toml index 0e90c2cad7..453b3cea33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.63.1" +version = "1.63.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1d08feda0d..1f57a6db7e 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.63.1" # x-release-please-version +__version__ = "1.63.2" # x-release-please-version From 7cc9c9e95511e9d94f3e5ba913bf79623477e10d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 22:37:49 +0000 Subject: [PATCH 793/914] feat(client): allow passing `NotGiven` for body (#2135) fix(client): mark some request bodies as optional --- src/openai/_base_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 8a408d8e58..94a5edd010 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -520,7 +520,7 @@ def _build_request( # so that passing a `TypedDict` doesn't cause an error. # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, - json=json_data, + json=json_data if is_given(json_data) else None, files=files, **kwargs, ) From fe9eb8d1d4c2232aab342e7ae068319e255bf685 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:08:25 +0000 Subject: [PATCH 794/914] chore(internal): fix devcontainers setup (#2137) --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ac9a2e7521..55d20255c9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,4 +6,4 @@ USER vscode RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH -RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc +RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bbeb30b148..c17fdc169f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -24,6 +24,9 @@ } } } + }, + "features": { + "ghcr.io/devcontainers/features/node:1": {} } // Features to add to the dev container. More info: https://containers.dev/features. From 3e69750d47df4f0759d4a28ddc68e4b38756d9ca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 05:03:56 +0000 Subject: [PATCH 795/914] release: 1.64.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a9866d99a2..7ef7bb772e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.63.2" + ".": "1.64.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ed87c5875..1aa32a14bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 1.64.0 (2025-02-22) + +Full Changelog: [v1.63.2...v1.64.0](https://github.com/openai/openai-python/compare/v1.63.2...v1.64.0) + +### Features + +* **client:** allow passing `NotGiven` for body ([#2135](https://github.com/openai/openai-python/issues/2135)) ([4451f56](https://github.com/openai/openai-python/commit/4451f5677f9eaad9b8fee74f71c2e5fe6785c420)) + + +### Bug Fixes + +* **client:** mark some request bodies as optional ([4451f56](https://github.com/openai/openai-python/commit/4451f5677f9eaad9b8fee74f71c2e5fe6785c420)) + + +### Chores + +* **internal:** fix devcontainers setup ([#2137](https://github.com/openai/openai-python/issues/2137)) ([4d88402](https://github.com/openai/openai-python/commit/4d884020cbeb1ca6093dd5317e3e5812551f7a46)) + ## 1.63.2 (2025-02-17) Full Changelog: [v1.63.1...v1.63.2](https://github.com/openai/openai-python/compare/v1.63.1...v1.63.2) diff --git a/pyproject.toml b/pyproject.toml index 453b3cea33..7cd583da3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.63.2" +version = "1.64.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1f57a6db7e..0a898ceeb8 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.63.2" # x-release-please-version +__version__ = "1.64.0" # x-release-please-version From 5bde5722f18e778abb896f9434fd52f0c4284e0a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 11:04:28 +0000 Subject: [PATCH 796/914] chore(internal): properly set __pydantic_private__ (#2144) --- src/openai/_base_client.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 94a5edd010..43dc9ab2a4 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -63,7 +63,7 @@ ModelBuilderProtocol, ) from ._utils import SensitiveHeadersFilter, is_dict, is_list, asyncify, is_given, lru_cache, is_mapping -from ._compat import model_copy, model_dump +from ._compat import PYDANTIC_V2, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -209,6 +209,9 @@ def _set_private_attributes( model: Type[_T], options: FinalRequestOptions, ) -> None: + if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options @@ -294,6 +297,9 @@ def _set_private_attributes( client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: + if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options From 0c62bebe59c93921035459c77719d3d8ae23d0f5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 20:05:36 +0000 Subject: [PATCH 797/914] feat(api): add gpt-4.5-preview (#2149) --- .stats.yml | 2 +- src/openai/resources/beta/assistants.py | 4 +++ .../resources/beta/realtime/realtime.py | 36 +++++++++++-------- .../types/beta/assistant_update_params.py | 2 ++ src/openai/types/beta/realtime/session.py | 14 ++++++++ .../beta/realtime/session_create_params.py | 10 +++++- .../beta/realtime/session_update_event.py | 10 +++++- .../realtime/session_update_event_param.py | 10 +++++- src/openai/types/chat_model.py | 2 ++ src/openai/types/file_object.py | 3 ++ src/openai/types/upload.py | 2 +- .../beta/realtime/test_sessions.py | 2 ++ 12 files changed, 77 insertions(+), 20 deletions(-) diff --git a/.stats.yml b/.stats.yml index 658877d3b0..163146e38d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 74 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-4aa6ee65ba9efc789e05e6a5ef0883b2cadf06def8efd863dbf75e9e233067e1.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-5d30684c3118d049682ea30cdb4dbef39b97d51667da484689193dc40162af32.yml diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 462086f74b..d2bb8d7b92 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -232,6 +232,8 @@ def update( "gpt-4o-2024-05-13", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", @@ -673,6 +675,8 @@ async def update( "gpt-4o-2024-05-13", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index 235790a9f5..0cf7c85799 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -561,14 +561,17 @@ def __init__(self, connection: RealtimeConnection) -> None: class RealtimeSessionResource(BaseRealtimeConnectionResource): def update(self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN) -> None: - """Send this event to update the session’s default configuration. + """ + Send this event to update the session’s default configuration. + The client may send this event at any time to update any field, + except for `voice`. However, note that once a session has been + initialized with a particular `model`, it can’t be changed to + another model using `session.update`. - The client may - send this event at any time to update the session configuration, and any - field may be updated at any time, except for "voice". The server will respond - with a `session.updated` event that shows the full effective configuration. - Only fields that are present are updated, thus the correct way to clear a - field like "instructions" is to pass an empty string. + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present are updated. To clear a field like + `instructions`, pass an empty string. """ self._connection.send( cast( @@ -768,14 +771,17 @@ class AsyncRealtimeSessionResource(BaseAsyncRealtimeConnectionResource): async def update( self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN ) -> None: - """Send this event to update the session’s default configuration. - - The client may - send this event at any time to update the session configuration, and any - field may be updated at any time, except for "voice". The server will respond - with a `session.updated` event that shows the full effective configuration. - Only fields that are present are updated, thus the correct way to clear a - field like "instructions" is to pass an empty string. + """ + Send this event to update the session’s default configuration. + The client may send this event at any time to update any field, + except for `voice`. However, note that once a session has been + initialized with a particular `model`, it can’t be changed to + another model using `session.update`. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present are updated. To clear a field like + `instructions`, pass an empty string. """ await self._connection.send( cast( diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 80fec110cd..12a57a4063 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -45,6 +45,8 @@ class AssistantUpdateParams(TypedDict, total=False): "gpt-4o-2024-05-13", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-0125-preview", diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py index 2d028f817c..aee20fa906 100644 --- a/src/openai/types/beta/realtime/session.py +++ b/src/openai/types/beta/realtime/session.py @@ -34,6 +34,20 @@ class Tool(BaseModel): class TurnDetection(BaseModel): + create_response: Optional[bool] = None + """Whether or not to automatically generate a response when a VAD stop event + occurs. + + `true` by default. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. `true` by default. + """ + prefix_padding_ms: Optional[int] = None """Amount of audio to include before the VAD detected speech (in milliseconds). diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py index 1502d83d39..bbc86d7c7d 100644 --- a/src/openai/types/beta/realtime/session_create_params.py +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -146,11 +146,19 @@ class Tool(TypedDict, total=False): class TurnDetection(TypedDict, total=False): create_response: bool - """Whether or not to automatically generate a response when VAD is enabled. + """Whether or not to automatically generate a response when a VAD stop event + occurs. `true` by default. """ + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. `true` by default. + """ + prefix_padding_ms: int """Amount of audio to include before the VAD detected speech (in milliseconds). diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py index 62fb0a3998..999cd8d660 100644 --- a/src/openai/types/beta/realtime/session_update_event.py +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -51,11 +51,19 @@ class SessionTool(BaseModel): class SessionTurnDetection(BaseModel): create_response: Optional[bool] = None - """Whether or not to automatically generate a response when VAD is enabled. + """Whether or not to automatically generate a response when a VAD stop event + occurs. `true` by default. """ + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. `true` by default. + """ + prefix_padding_ms: Optional[int] = None """Amount of audio to include before the VAD detected speech (in milliseconds). diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py index 133cdd91a1..07fdba9d85 100644 --- a/src/openai/types/beta/realtime/session_update_event_param.py +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -57,11 +57,19 @@ class SessionTool(TypedDict, total=False): class SessionTurnDetection(TypedDict, total=False): create_response: bool - """Whether or not to automatically generate a response when VAD is enabled. + """Whether or not to automatically generate a response when a VAD stop event + occurs. `true` by default. """ + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. `true` by default. + """ + prefix_padding_ms: int """Amount of audio to include before the VAD detected speech (in milliseconds). diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index c191cb9734..6fe705a0b4 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -13,6 +13,8 @@ "o1-preview-2024-09-12", "o1-mini", "o1-mini-2024-09-12", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", "gpt-4o", "gpt-4o-2024-11-20", "gpt-4o-2024-08-06", diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py index 6e2bf310a4..1d65e6987d 100644 --- a/src/openai/types/file_object.py +++ b/src/openai/types/file_object.py @@ -40,6 +40,9 @@ class FileObject(BaseModel): `error`. """ + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) for when the file will expire.""" + status_details: Optional[str] = None """Deprecated. diff --git a/src/openai/types/upload.py b/src/openai/types/upload.py index d8108c62f9..914b69a863 100644 --- a/src/openai/types/upload.py +++ b/src/openai/types/upload.py @@ -20,7 +20,7 @@ class Upload(BaseModel): """The Unix timestamp (in seconds) for when the Upload was created.""" expires_at: int - """The Unix timestamp (in seconds) for when the Upload was created.""" + """The Unix timestamp (in seconds) for when the Upload will expire.""" filename: str """The name of the file to be uploaded.""" diff --git a/tests/api_resources/beta/realtime/test_sessions.py b/tests/api_resources/beta/realtime/test_sessions.py index 5a17088ce6..5ea308ca0d 100644 --- a/tests/api_resources/beta/realtime/test_sessions.py +++ b/tests/api_resources/beta/realtime/test_sessions.py @@ -48,6 +48,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: ], turn_detection={ "create_response": True, + "interrupt_response": True, "prefix_padding_ms": 0, "silence_duration_ms": 0, "threshold": 0, @@ -112,6 +113,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> ], turn_detection={ "create_response": True, + "interrupt_response": True, "prefix_padding_ms": 0, "silence_duration_ms": 0, "threshold": 0, From 939c861263b2b6ed2d086b794261766ddf5b5f65 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 20:07:02 +0000 Subject: [PATCH 798/914] release: 1.65.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7ef7bb772e..045e3819b6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.64.0" + ".": "1.65.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa32a14bd..f6190ab04e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.65.0 (2025-02-27) + +Full Changelog: [v1.64.0...v1.65.0](https://github.com/openai/openai-python/compare/v1.64.0...v1.65.0) + +### Features + +* **api:** add gpt-4.5-preview ([#2149](https://github.com/openai/openai-python/issues/2149)) ([4cee52e](https://github.com/openai/openai-python/commit/4cee52e8d191b0532f28d86446da79b43a58b907)) + + +### Chores + +* **internal:** properly set __pydantic_private__ ([#2144](https://github.com/openai/openai-python/issues/2144)) ([2b1bd16](https://github.com/openai/openai-python/commit/2b1bd1604a038ded67367742a0b1c9d92e29dfc8)) + ## 1.64.0 (2025-02-22) Full Changelog: [v1.63.2...v1.64.0](https://github.com/openai/openai-python/compare/v1.63.2...v1.64.0) diff --git a/pyproject.toml b/pyproject.toml index 7cd583da3f..511da522a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.64.0" +version = "1.65.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0a898ceeb8..31af749758 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.64.0" # x-release-please-version +__version__ = "1.65.0" # x-release-please-version From 06d79fd2cc1edea9af81c8ba4304eefdd1195fc7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 22:01:07 +0000 Subject: [PATCH 799/914] docs: update URLs from stainlessapi.com to stainless.com (#2150) More details at https://www.stainless.com/changelog/stainless-com --- SECURITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index c54acaf331..3b3bd8a662 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Reporting Security Issues -This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. -To report a security issue, please contact the Stainless team at security@stainlessapi.com. +To report a security issue, please contact the Stainless team at security@stainless.com. ## Responsible Disclosure From 724f56c56487578692bec45fca79474e57516308 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 22:01:57 +0000 Subject: [PATCH 800/914] release: 1.65.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 045e3819b6..57b589ea9e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.65.0" + ".": "1.65.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f6190ab04e..ac6f54aa21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.65.1 (2025-02-27) + +Full Changelog: [v1.65.0...v1.65.1](https://github.com/openai/openai-python/compare/v1.65.0...v1.65.1) + +### Documentation + +* update URLs from stainlessapi.com to stainless.com ([#2150](https://github.com/openai/openai-python/issues/2150)) ([dee4298](https://github.com/openai/openai-python/commit/dee42986eff46dd23ba25b3e2a5bb7357aca39d9)) + ## 1.65.0 (2025-02-27) Full Changelog: [v1.64.0...v1.65.0](https://github.com/openai/openai-python/compare/v1.64.0...v1.65.0) diff --git a/pyproject.toml b/pyproject.toml index 511da522a7..13bd84e4f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.65.0" +version = "1.65.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 31af749758..422c9a283d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.65.0" # x-release-please-version +__version__ = "1.65.1" # x-release-please-version From ba2a8a0953c41dc1364221a2009f2a942e4d6f35 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 22:51:14 +0000 Subject: [PATCH 801/914] chore(docs): update client docstring (#2152) --- src/openai/_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index c784694f20..2464c6504c 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -97,7 +97,7 @@ def __init__( # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: - """Construct a new synchronous openai client instance. + """Construct a new synchronous OpenAI client instance. This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `OPENAI_API_KEY` @@ -324,7 +324,7 @@ def __init__( # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: - """Construct a new async openai client instance. + """Construct a new async AsyncOpenAI client instance. This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `OPENAI_API_KEY` From c98d7400785011e0f5de2e33c9bf4dec95332847 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Fri, 28 Feb 2025 06:36:08 -0800 Subject: [PATCH 802/914] fix(azure): azure_deployment use with realtime + non-deployment-based APIs (#2154) * support realtime with azure_deployment * lint * use rsplit * switch approach: save copy of the original url * save azure_endpoint as it was given * docstring * format * remove unnecessary check + add test * fix for websocket_base_url * add another test --- src/openai/lib/azure.py | 67 ++- .../resources/beta/realtime/realtime.py | 36 +- tests/lib/test_azure.py | 563 ++++++++++++++++++ 3 files changed, 637 insertions(+), 29 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index f857d76e51..ea7bd20d99 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -49,6 +49,9 @@ def __init__(self) -> None: class BaseAzureClient(BaseClient[_HttpxClientT, _DefaultStreamT]): + _azure_endpoint: httpx.URL | None + _azure_deployment: str | None + @override def _build_request( self, @@ -58,11 +61,29 @@ def _build_request( ) -> httpx.Request: if options.url in _deployments_endpoints and is_mapping(options.json_data): model = options.json_data.get("model") - if model is not None and not "/deployments" in str(self.base_url): + if model is not None and "/deployments" not in str(self.base_url.path): options.url = f"/deployments/{model}{options.url}" return super()._build_request(options, retries_taken=retries_taken) + @override + def _prepare_url(http://github.com/self,%20url:%20str) -> httpx.URL: + """Adjust the URL if the client was configured with an Azure endpoint + deployment + and the API feature being called is **not** a deployments-based endpoint + (i.e. requires /deployments/deployment-name in the URL path). + """ + if self._azure_deployment and self._azure_endpoint and url not in _deployments_endpoints: + merge_url = httpx.URL(url) + if merge_url.is_relative_url: + merge_raw_path = ( + self._azure_endpoint.raw_path.rstrip(b"/") + b"/openai/" + merge_url.raw_path.lstrip(b"/") + ) + return self._azure_endpoint.copy_with(raw_path=merge_raw_path) + + return merge_url + + return super()._prepare_http://github.com/url(url) + class AzureOpenAI(BaseAzureClient[httpx.Client, Stream[Any]], OpenAI): @overload @@ -160,8 +181,8 @@ def __init__( azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. - azure_deployment: A model deployment, if given sets the base client URL to include `/deployments/{azure_deployment}`. - Note: this means you won't be able to use non-deployment endpoints. Not supported with Assistants APIs. + azure_deployment: A model deployment, if given with `azure_endpoint`, sets the base client URL to include `/deployments/{azure_deployment}`. + Not supported with Assistants APIs. """ if api_key is None: api_key = os.environ.get("AZURE_OPENAI_API_KEY") @@ -224,6 +245,8 @@ def __init__( self._api_version = api_version self._azure_ad_token = azure_ad_token self._azure_ad_token_provider = azure_ad_token_provider + self._azure_deployment = azure_deployment if azure_endpoint else None + self._azure_endpoint = httpx.URL(azure_endpoint) if azure_endpoint else None @override def copy( @@ -307,12 +330,12 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: return options - def _configure_realtime(self, model: str, extra_query: Query) -> tuple[Query, dict[str, str]]: + def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx.URL, dict[str, str]]: auth_headers = {} query = { **extra_query, "api-version": self._api_version, - "deployment": model, + "deployment": self._azure_deployment or model, } if self.api_key != "": auth_headers = {"api-key": self.api_key} @@ -320,7 +343,17 @@ def _configure_realtime(self, model: str, extra_query: Query) -> tuple[Query, di token = self._get_azure_ad_token() if token: auth_headers = {"Authorization": f"Bearer {token}"} - return query, auth_headers + + if self.websocket_base_url is not None: + base_url = httpx.URL(self.websocket_base_url) + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + realtime_url = base_url.copy_with(raw_path=merge_raw_path) + else: + base_url = self._prepare_url("http://github.com/realtime") + realtime_url = base_url.copy_with(scheme="wss") + + url = realtime_url.copy_with(params={**query}) + return url, auth_headers class AsyncAzureOpenAI(BaseAzureClient[httpx.AsyncClient, AsyncStream[Any]], AsyncOpenAI): @@ -422,8 +455,8 @@ def __init__( azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. - azure_deployment: A model deployment, if given sets the base client URL to include `/deployments/{azure_deployment}`. - Note: this means you won't be able to use non-deployment endpoints. Not supported with Assistants APIs. + azure_deployment: A model deployment, if given with `azure_endpoint`, sets the base client URL to include `/deployments/{azure_deployment}`. + Not supported with Assistants APIs. """ if api_key is None: api_key = os.environ.get("AZURE_OPENAI_API_KEY") @@ -486,6 +519,8 @@ def __init__( self._api_version = api_version self._azure_ad_token = azure_ad_token self._azure_ad_token_provider = azure_ad_token_provider + self._azure_deployment = azure_deployment if azure_endpoint else None + self._azure_endpoint = httpx.URL(azure_endpoint) if azure_endpoint else None @override def copy( @@ -571,12 +606,12 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp return options - async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[Query, dict[str, str]]: + async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx.URL, dict[str, str]]: auth_headers = {} query = { **extra_query, "api-version": self._api_version, - "deployment": model, + "deployment": self._azure_deployment or model, } if self.api_key != "": auth_headers = {"api-key": self.api_key} @@ -584,4 +619,14 @@ async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[Que token = await self._get_azure_ad_token() if token: auth_headers = {"Authorization": f"Bearer {token}"} - return query, auth_headers + + if self.websocket_base_url is not None: + base_url = httpx.URL(self.websocket_base_url) + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + realtime_url = base_url.copy_with(raw_path=merge_raw_path) + else: + base_url = self._prepare_url("http://github.com/realtime") + realtime_url = base_url.copy_with(scheme="wss") + + url = realtime_url.copy_with(params={**query}) + return url, auth_headers diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index 0cf7c85799..cd610d9089 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -324,15 +324,15 @@ async def __aenter__(self) -> AsyncRealtimeConnection: extra_query = self.__extra_query auth_headers = self.__client.auth_headers if is_async_azure_client(self.__client): - extra_query, auth_headers = await self.__client._configure_realtime(self.__model, extra_query) - - url = self._prepare_url().copy_with( - params={ - **self.__client.base_url.params, - "model": self.__model, - **extra_query, - }, - ) + url, auth_headers = await self.__client._configure_realtime(self.__model, extra_query) + else: + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + "model": self.__model, + **extra_query, + }, + ) log.debug("Connecting to %s", url) if self.__websocket_connection_options: log.debug("Connection options: %s", self.__websocket_connection_options) @@ -506,15 +506,15 @@ def __enter__(self) -> RealtimeConnection: extra_query = self.__extra_query auth_headers = self.__client.auth_headers if is_azure_client(self.__client): - extra_query, auth_headers = self.__client._configure_realtime(self.__model, extra_query) - - url = self._prepare_url().copy_with( - params={ - **self.__client.base_url.params, - "model": self.__model, - **extra_query, - }, - ) + url, auth_headers = self.__client._configure_realtime(self.__model, extra_query) + else: + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + "model": self.__model, + **extra_query, + }, + ) log.debug("Connecting to %s", url) if self.__websocket_connection_options: log.debug("Connection options: %s", self.__websocket_connection_options) diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py index a28aa8c2f6..52c24eba27 100644 --- a/tests/lib/test_azure.py +++ b/tests/lib/test_azure.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging from typing import Union, cast from typing_extensions import Literal, Protocol @@ -239,3 +241,564 @@ async def test_azure_bearer_token_redacted_async( for record in caplog.records: if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]): assert record.args["headers"]["Authorization"] == "" + + +@pytest.mark.parametrize( + "client,base_url,api,json_data,expected", + [ + # Deployment-based endpoints + # AzureOpenAI: No deployment specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + ), + "https://example-resource.azure.openai.com/openai/", + "/chat/completions", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/deployments/deployment-body/chat/completions?api-version=2024-02-01", + ), + # AzureOpenAI: Deployment specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployment-client", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/", + "/chat/completions", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/chat/completions?api-version=2024-02-01", + ), + # AzureOpenAI: "deployments" in the DNS name + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://deployments.example-resource.azure.openai.com/", + ), + "https://deployments.example-resource.azure.openai.com/openai/", + "/chat/completions", + {"model": "deployment-body"}, + "https://deployments.example-resource.azure.openai.com/openai/deployments/deployment-body/chat/completions?api-version=2024-02-01", + ), + # AzureOpenAI: Deployment called deployments + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployments", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployments/", + "/chat/completions", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/deployments/deployments/chat/completions?api-version=2024-02-01", + ), + # AzureOpenAI: base_url and azure_deployment specified; ignored b/c not supported + ( + AzureOpenAI( # type: ignore + api_version="2024-02-01", + api_key="example API key", + base_url="https://example.azure-api.net/PTU/", + azure_deployment="deployment-client", + ), + "https://example.azure-api.net/PTU/", + "/chat/completions", + {"model": "deployment-body"}, + "https://example.azure-api.net/PTU/deployments/deployment-body/chat/completions?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: No deployment specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + ), + "https://example-resource.azure.openai.com/openai/", + "/chat/completions", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/deployments/deployment-body/chat/completions?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: Deployment specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployment-client", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/", + "/chat/completions", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/chat/completions?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: "deployments" in the DNS name + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://deployments.example-resource.azure.openai.com/", + ), + "https://deployments.example-resource.azure.openai.com/openai/", + "/chat/completions", + {"model": "deployment-body"}, + "https://deployments.example-resource.azure.openai.com/openai/deployments/deployment-body/chat/completions?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: Deployment called deployments + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployments", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployments/", + "/chat/completions", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/deployments/deployments/chat/completions?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported + ( + AsyncAzureOpenAI( # type: ignore + api_version="2024-02-01", + api_key="example API key", + base_url="https://example.azure-api.net/PTU/", + azure_deployment="deployment-client", + ), + "https://example.azure-api.net/PTU/", + "/chat/completions", + {"model": "deployment-body"}, + "https://example.azure-api.net/PTU/deployments/deployment-body/chat/completions?api-version=2024-02-01", + ), + ], +) +def test_prepare_url_deployment_endpoint( + client: Client, base_url: str, api: str, json_data: dict[str, str], expected: str +) -> None: + req = client._build_request( + FinalRequestOptions.construct( + method="post", + url=api, + json_data=json_data, + ) + ) + assert req.url == expected + assert client.base_url == base_url + + +@pytest.mark.parametrize( + "client,base_url,api,json_data,expected", + [ + # Non-deployment endpoints + # AzureOpenAI: No deployment specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + ), + "https://example-resource.azure.openai.com/openai/", + "/models", + {}, + "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01", + ), + # AzureOpenAI: No deployment specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + ), + "https://example-resource.azure.openai.com/openai/", + "/assistants", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/assistants?api-version=2024-02-01", + ), + # AzureOpenAI: Deployment specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployment-client", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/", + "/models", + {}, + "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01", + ), + # AzureOpenAI: Deployment specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployment-client", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/", + "/assistants", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/assistants?api-version=2024-02-01", + ), + # AzureOpenAI: "deployments" in the DNS name + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://deployments.example-resource.azure.openai.com/", + ), + "https://deployments.example-resource.azure.openai.com/openai/", + "/models", + {}, + "https://deployments.example-resource.azure.openai.com/openai/models?api-version=2024-02-01", + ), + # AzureOpenAI: Deployment called "deployments" + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployments", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployments/", + "/models", + {}, + "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01", + ), + # AzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported + ( + AzureOpenAI( # type: ignore + api_version="2024-02-01", + api_key="example API key", + base_url="https://example.azure-api.net/PTU/", + azure_deployment="deployment-client", + ), + "https://example.azure-api.net/PTU/", + "/models", + {}, + "https://example.azure-api.net/PTU/models?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: No deployment specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + ), + "https://example-resource.azure.openai.com/openai/", + "/models", + {}, + "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: No deployment specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + ), + "https://example-resource.azure.openai.com/openai/", + "/assistants", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/assistants?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: Deployment specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployment-client", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/", + "/models", + {}, + "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: Deployment specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployment-client", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/", + "/assistants", + {"model": "deployment-body"}, + "https://example-resource.azure.openai.com/openai/assistants?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: "deployments" in the DNS name + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://deployments.example-resource.azure.openai.com/", + ), + "https://deployments.example-resource.azure.openai.com/openai/", + "/models", + {}, + "https://deployments.example-resource.azure.openai.com/openai/models?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: Deployment called "deployments" + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployments", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployments/", + "/models", + {}, + "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01", + ), + # AsyncAzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported + ( + AsyncAzureOpenAI( # type: ignore + api_version="2024-02-01", + api_key="example API key", + base_url="https://example.azure-api.net/PTU/", + azure_deployment="deployment-client", + ), + "https://example.azure-api.net/PTU/", + "/models", + {}, + "https://example.azure-api.net/PTU/models?api-version=2024-02-01", + ), + ], +) +def test_prepare_url_nondeployment_endpoint( + client: Client, base_url: str, api: str, json_data: dict[str, str], expected: str +) -> None: + req = client._build_request( + FinalRequestOptions.construct( + method="post", + url=api, + json_data=json_data, + ) + ) + assert req.url == expected + assert client.base_url == base_url + + +@pytest.mark.parametrize( + "client,base_url,json_data,expected", + [ + # Realtime endpoint + # AzureOpenAI: No deployment specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + ), + "https://example-resource.azure.openai.com/openai/", + {"model": "deployment-body"}, + "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-body", + ), + # AzureOpenAI: Deployment specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployment-client", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/", + {"model": "deployment-body"}, + "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-client", + ), + # AzureOpenAI: "deployments" in the DNS name + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://deployments.azure.openai.com/", + ), + "https://deployments.azure.openai.com/openai/", + {"model": "deployment-body"}, + "wss://deployments.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-body", + ), + # AzureOpenAI: Deployment called "deployments" + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployments", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployments/", + {"model": "deployment-body"}, + "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployments", + ), + # AzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported + ( + AzureOpenAI( # type: ignore + api_version="2024-02-01", + api_key="example API key", + base_url="https://example.azure-api.net/PTU/", + azure_deployment="my-deployment", + ), + "https://example.azure-api.net/PTU/", + {"model": "deployment-body"}, + "wss://example.azure-api.net/PTU/realtime?api-version=2024-02-01&deployment=deployment-body", + ), + # AzureOpenAI: websocket_base_url specified + ( + AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + websocket_base_url="wss://example-resource.azure.openai.com/base", + ), + "https://example-resource.azure.openai.com/openai/", + {"model": "deployment-body"}, + "wss://example-resource.azure.openai.com/base/realtime?api-version=2024-02-01&deployment=deployment-body", + ), + ], +) +def test_prepare_url_realtime(client: AzureOpenAI, base_url: str, json_data: dict[str, str], expected: str) -> None: + url, _ = client._configure_realtime(json_data["model"], {}) + assert str(url) == expected + assert client.base_url == base_url + + +@pytest.mark.parametrize( + "client,base_url,json_data,expected", + [ + # AsyncAzureOpenAI: No deployment specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + ), + "https://example-resource.azure.openai.com/openai/", + {"model": "deployment-body"}, + "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-body", + ), + # AsyncAzureOpenAI: Deployment specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployment-client", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployment-client/", + {"model": "deployment-body"}, + "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-client", + ), + # AsyncAzureOpenAI: "deployments" in the DNS name + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://deployments.azure.openai.com/", + ), + "https://deployments.azure.openai.com/openai/", + {"model": "deployment-body"}, + "wss://deployments.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-body", + ), + # AsyncAzureOpenAI: Deployment called "deployments" + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="deployments", + ), + "https://example-resource.azure.openai.com/openai/deployments/deployments/", + {"model": "deployment-body"}, + "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployments", + ), + # AsyncAzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported + ( + AsyncAzureOpenAI( # type: ignore + api_version="2024-02-01", + api_key="example API key", + base_url="https://example.azure-api.net/PTU/", + azure_deployment="deployment-client", + ), + "https://example.azure-api.net/PTU/", + {"model": "deployment-body"}, + "wss://example.azure-api.net/PTU/realtime?api-version=2024-02-01&deployment=deployment-body", + ), + # AsyncAzureOpenAI: websocket_base_url specified + ( + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + websocket_base_url="wss://example-resource.azure.openai.com/base", + ), + "https://example-resource.azure.openai.com/openai/", + {"model": "deployment-body"}, + "wss://example-resource.azure.openai.com/base/realtime?api-version=2024-02-01&deployment=deployment-body", + ), + ], +) +async def test_prepare_url_realtime_async( + client: AsyncAzureOpenAI, base_url: str, json_data: dict[str, str], expected: str +) -> None: + url, _ = await client._configure_realtime(json_data["model"], {}) + assert str(url) == expected + assert client.base_url == base_url + + +def test_client_sets_base_url(/service/client: Client) -> None: + client = AzureOpenAI( + api_version="2024-02-01", + api_key="example API key", + azure_endpoint="https://example-resource.azure.openai.com/", + azure_deployment="my-deployment", + ) + assert client.base_url == "https://example-resource.azure.openai.com/openai/deployments/my-deployment/" + + # (not recommended) user sets base_url to target different deployment + client.base_url = "https://example-resource.azure.openai.com/openai/deployments/different-deployment/" + req = client._build_request( + FinalRequestOptions.construct( + method="post", + url="/chat/completions", + json_data={"model": "placeholder"}, + ) + ) + assert ( + req.url + == "https://example-resource.azure.openai.com/openai/deployments/different-deployment/chat/completions?api-version=2024-02-01" + ) + req = client._build_request( + FinalRequestOptions.construct( + method="post", + url="/models", + json_data={}, + ) + ) + assert req.url == "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01" + + # (not recommended) user sets base_url to remove deployment + client.base_url = "https://example-resource.azure.openai.com/openai/" + req = client._build_request( + FinalRequestOptions.construct( + method="post", + url="/chat/completions", + json_data={"model": "deployment"}, + ) + ) + assert ( + req.url + == "https://example-resource.azure.openai.com/openai/deployments/deployment/chat/completions?api-version=2024-02-01" + ) + req = client._build_request( + FinalRequestOptions.construct( + method="post", + url="/models", + json_data={}, + ) + ) + assert req.url == "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01" From 64af9e8f06be4bfe02e0e5e9cb0aa7889a5db6d7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 1 Mar 2025 05:04:12 +0000 Subject: [PATCH 803/914] release: 1.65.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 57b589ea9e..ae5a2791b1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.65.1" + ".": "1.65.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ac6f54aa21..8f7edb2cae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.65.2 (2025-03-01) + +Full Changelog: [v1.65.1...v1.65.2](https://github.com/openai/openai-python/compare/v1.65.1...v1.65.2) + +### Bug Fixes + +* **azure:** azure_deployment use with realtime + non-deployment-based APIs ([#2154](https://github.com/openai/openai-python/issues/2154)) ([5846b55](https://github.com/openai/openai-python/commit/5846b552877f3d278689c521f9a26ce31167e1ea)) + + +### Chores + +* **docs:** update client docstring ([#2152](https://github.com/openai/openai-python/issues/2152)) ([0518c34](https://github.com/openai/openai-python/commit/0518c341ee0e19941c6b1d9d60e2552e1aa17f26)) + ## 1.65.1 (2025-02-27) Full Changelog: [v1.65.0...v1.65.1](https://github.com/openai/openai-python/compare/v1.65.0...v1.65.1) diff --git a/pyproject.toml b/pyproject.toml index 13bd84e4f4..d9a2417194 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.65.1" +version = "1.65.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 422c9a283d..d48f48f4e1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.65.1" # x-release-please-version +__version__ = "1.65.2" # x-release-please-version From 65f2c5cee943d5c2a57f087c7bcc8204449cec51 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 20:43:26 +0000 Subject: [PATCH 804/914] chore(internal): remove unused http client options forwarding (#2158) --- src/openai/_base_client.py | 97 +------------------------------------- 1 file changed, 1 insertion(+), 96 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 43dc9ab2a4..f31e5af54b 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -9,7 +9,6 @@ import inspect import logging import platform -import warnings import email.utils from types import TracebackType from random import random @@ -36,7 +35,7 @@ import httpx import distro import pydantic -from httpx import URL, Limits +from httpx import URL from pydantic import PrivateAttr from . import _exceptions @@ -51,13 +50,10 @@ Timeout, NotGiven, ResponseT, - Transport, AnyMapping, PostParser, - ProxiesTypes, RequestFiles, HttpxSendArgs, - AsyncTransport, RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, @@ -339,9 +335,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): _base_url: URL max_retries: int timeout: Union[float, Timeout, None] - _limits: httpx.Limits - _proxies: ProxiesTypes | None - _transport: Transport | AsyncTransport | None _strict_response_validation: bool _idempotency_header: str | None _default_stream_cls: type[_DefaultStreamT] | None = None @@ -354,9 +347,6 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None = DEFAULT_TIMEOUT, - limits: httpx.Limits, - transport: Transport | AsyncTransport | None, - proxies: ProxiesTypes | None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: @@ -364,9 +354,6 @@ def __init__( self._base_url = self._enforce_trailing_slash(URL(base_url)) self.max_retries = max_retries self.timeout = timeout - self._limits = limits - self._proxies = proxies - self._transport = transport self._custom_headers = custom_headers or {} self._custom_query = custom_query or {} self._strict_response_validation = _strict_response_validation @@ -802,46 +789,11 @@ def __init__( base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: Transport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, _strict_response_validation: bool, ) -> None: - kwargs: dict[str, Any] = {} - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_CONNECTION_LIMITS - - if transport is not None: - kwargs["transport"] = transport - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - kwargs["proxies"] = proxies - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -862,12 +814,9 @@ def __init__( super().__init__( version=version, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, base_url=base_url, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -877,9 +826,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - limits=limits, - follow_redirects=True, - **kwargs, # type: ignore ) def is_closed(self) -> bool: @@ -1389,45 +1335,10 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: AsyncTransport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: - kwargs: dict[str, Any] = {} - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_CONNECTION_LIMITS - - if transport is not None: - kwargs["transport"] = transport - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - kwargs["proxies"] = proxies - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -1449,11 +1360,8 @@ def __init__( super().__init__( version=version, base_url=base_url, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -1463,9 +1371,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - limits=limits, - follow_redirects=True, - **kwargs, # type: ignore ) def is_closed(self) -> bool: From b31f4d4c61cbeecf44b7ea6e0773eeec0748d91f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:09:25 +0000 Subject: [PATCH 805/914] chore(internal): run example files in CI (#2160) --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26f497db1f..d0e0ffe2f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,3 +51,30 @@ jobs: - name: Run tests run: ./scripts/test + + examples: + name: examples + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.35.0' + RYE_INSTALL_OPTION: '--yes' + - name: Install dependencies + run: | + rye sync --all-features + + - env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + rye run python examples/demo.py + - env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + rye run python examples/async_demo.py From d6bb8c14e66605ad2b7ed7bd62951014cd21b576 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:10:03 +0000 Subject: [PATCH 806/914] release: 1.65.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ae5a2791b1..352e389697 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.65.2" + ".": "1.65.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f7edb2cae..95093fb510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.65.3 (2025-03-04) + +Full Changelog: [v1.65.2...v1.65.3](https://github.com/openai/openai-python/compare/v1.65.2...v1.65.3) + +### Chores + +* **internal:** remove unused http client options forwarding ([#2158](https://github.com/openai/openai-python/issues/2158)) ([76ec464](https://github.com/openai/openai-python/commit/76ec464cfe3db3fa59a766259d6d6ee5bb889f86)) +* **internal:** run example files in CI ([#2160](https://github.com/openai/openai-python/issues/2160)) ([9979345](https://github.com/openai/openai-python/commit/9979345038594440eec2f500c0c7cc5417cc7c08)) + ## 1.65.2 (2025-03-01) Full Changelog: [v1.65.1...v1.65.2](https://github.com/openai/openai-python/compare/v1.65.1...v1.65.2) diff --git a/pyproject.toml b/pyproject.toml index d9a2417194..c9e2afbf0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.65.2" +version = "1.65.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d48f48f4e1..5e54102501 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.65.2" # x-release-please-version +__version__ = "1.65.3" # x-release-please-version From 5608d64bb832dc7f8305a4dfb6b8e76f7087c944 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:22:02 +0000 Subject: [PATCH 807/914] fix(api): add missing file rank enum + more metadata (#2164) --- .stats.yml | 2 +- src/openai/resources/fine_tuning/jobs/jobs.py | 31 ++++++++++++++++++- .../threads/runs/file_search_tool_call.py | 7 +++-- .../types/fine_tuning/fine_tuning_job.py | 11 +++++++ .../types/fine_tuning/job_create_params.py | 12 +++++++ .../types/fine_tuning/job_list_params.py | 8 +++++ tests/api_resources/fine_tuning/test_jobs.py | 4 +++ 7 files changed, 71 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 163146e38d..0d7e83be4f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 74 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-5d30684c3118d049682ea30cdb4dbef39b97d51667da484689193dc40162af32.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-b524aed1c2c5c928aa4e2c546f5dbb364e7b4d5027daf05e42e210b05a97c3c6.yml diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index e023d28fea..bbeff60bc6 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal import httpx @@ -30,6 +30,7 @@ make_request_options, ) from ....types.fine_tuning import job_list_params, job_create_params, job_list_events_params +from ....types.shared_params.metadata import Metadata from ....types.fine_tuning.fine_tuning_job import FineTuningJob from ....types.fine_tuning.fine_tuning_job_event import FineTuningJobEvent @@ -67,6 +68,7 @@ def create( training_file: str, hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, method: job_create_params.Method | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -114,6 +116,13 @@ def create( integrations: A list of integrations to enable for your fine-tuning job. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + method: The method used for fine-tuning. seed: The seed controls the reproducibility of the job. Passing in the same seed and @@ -155,6 +164,7 @@ def create( "training_file": training_file, "hyperparameters": hyperparameters, "integrations": integrations, + "metadata": metadata, "method": method, "seed": seed, "suffix": suffix, @@ -208,6 +218,7 @@ def list( *, after: str | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -223,6 +234,9 @@ def list( limit: Number of fine-tuning jobs to retrieve. + metadata: Optional metadata filter. To filter, use the syntax `metadata[k]=v`. + Alternatively, set `metadata=null` to indicate no metadata. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -243,6 +257,7 @@ def list( { "after": after, "limit": limit, + "metadata": metadata, }, job_list_params.JobListParams, ), @@ -365,6 +380,7 @@ async def create( training_file: str, hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, method: job_create_params.Method | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -412,6 +428,13 @@ async def create( integrations: A list of integrations to enable for your fine-tuning job. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + method: The method used for fine-tuning. seed: The seed controls the reproducibility of the job. Passing in the same seed and @@ -453,6 +476,7 @@ async def create( "training_file": training_file, "hyperparameters": hyperparameters, "integrations": integrations, + "metadata": metadata, "method": method, "seed": seed, "suffix": suffix, @@ -506,6 +530,7 @@ def list( *, after: str | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -521,6 +546,9 @@ def list( limit: Number of fine-tuning jobs to retrieve. + metadata: Optional metadata filter. To filter, use the syntax `metadata[k]=v`. + Alternatively, set `metadata=null` to indicate no metadata. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -541,6 +569,7 @@ def list( { "after": after, "limit": limit, + "metadata": metadata, }, job_list_params.JobListParams, ), diff --git a/src/openai/types/beta/threads/runs/file_search_tool_call.py b/src/openai/types/beta/threads/runs/file_search_tool_call.py index da4d58dc37..a2068daad1 100644 --- a/src/openai/types/beta/threads/runs/file_search_tool_call.py +++ b/src/openai/types/beta/threads/runs/file_search_tool_call.py @@ -15,8 +15,11 @@ class FileSearchRankingOptions(BaseModel): - ranker: Literal["default_2024_08_21"] - """The ranker used for the file search.""" + ranker: Literal["auto", "default_2024_08_21"] + """The ranker to use for the file search. + + If not specified will use the `auto` ranker. + """ score_threshold: float """The score threshold for the file search. diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index f5a11c2107..c7fff2b7b1 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from ..shared.metadata import Metadata from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject __all__ = [ @@ -208,5 +209,15 @@ class FineTuningJob(BaseModel): integrations: Optional[List[FineTuningJobWandbIntegrationObject]] = None """A list of integrations to enable for this fine-tuning job.""" + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + method: Optional[Method] = None """The method used for fine-tuning.""" diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 09c3f8571c..f4cf980b08 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -5,6 +5,8 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from ..shared_params.metadata import Metadata + __all__ = [ "JobCreateParams", "Hyperparameters", @@ -55,6 +57,16 @@ class JobCreateParams(TypedDict, total=False): integrations: Optional[Iterable[Integration]] """A list of integrations to enable for your fine-tuning job.""" + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + method: Method """The method used for fine-tuning.""" diff --git a/src/openai/types/fine_tuning/job_list_params.py b/src/openai/types/fine_tuning/job_list_params.py index 5c075ca33f..b79f3ce86a 100644 --- a/src/openai/types/fine_tuning/job_list_params.py +++ b/src/openai/types/fine_tuning/job_list_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Dict, Optional from typing_extensions import TypedDict __all__ = ["JobListParams"] @@ -13,3 +14,10 @@ class JobListParams(TypedDict, total=False): limit: int """Number of fine-tuning jobs to retrieve.""" + + metadata: Optional[Dict[str, str]] + """Optional metadata filter. + + To filter, use the syntax `metadata[k]=v`. Alternatively, set `metadata=null` to + indicate no metadata. + """ diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 1e421c30c0..75f72f9d09 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -50,6 +50,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: }, } ], + metadata={"foo": "string"}, method={ "dpo": { "hyperparameters": { @@ -148,6 +149,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: job = client.fine_tuning.jobs.list( after="string", limit=0, + metadata={"foo": "string"}, ) assert_matches_type(SyncCursorPage[FineTuningJob], job, path=["response"]) @@ -289,6 +291,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> }, } ], + metadata={"foo": "string"}, method={ "dpo": { "hyperparameters": { @@ -387,6 +390,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N job = await async_client.fine_tuning.jobs.list( after="string", limit=0, + metadata={"foo": "string"}, ) assert_matches_type(AsyncCursorPage[FineTuningJob], job, path=["response"]) From dfc4cfabfab3db89c0668c9bfd5f3f6f49093935 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:22:42 +0000 Subject: [PATCH 808/914] release: 1.65.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 352e389697..b31d1b2102 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.65.3" + ".": "1.65.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 95093fb510..759edd60a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.65.4 (2025-03-05) + +Full Changelog: [v1.65.3...v1.65.4](https://github.com/openai/openai-python/compare/v1.65.3...v1.65.4) + +### Bug Fixes + +* **api:** add missing file rank enum + more metadata ([#2164](https://github.com/openai/openai-python/issues/2164)) ([0387e48](https://github.com/openai/openai-python/commit/0387e48e0880e496eb74b60eec9ed76a3171f14d)) + ## 1.65.3 (2025-03-04) Full Changelog: [v1.65.2...v1.65.3](https://github.com/openai/openai-python/compare/v1.65.2...v1.65.3) diff --git a/pyproject.toml b/pyproject.toml index c9e2afbf0c..a44991907d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.65.3" +version = "1.65.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5e54102501..13991c8059 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.65.3" # x-release-please-version +__version__ = "1.65.4" # x-release-please-version From 530f9b80c7c21b1290a6749f6c2c82d72c047585 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 20:10:48 +0000 Subject: [PATCH 809/914] chore: move ChatModel type to shared (#2167) --- api.md | 3 +- src/openai/resources/beta/assistants.py | 2 +- .../resources/beta/threads/runs/runs.py | 2 +- src/openai/resources/beta/threads/threads.py | 2 +- .../resources/chat/completions/completions.py | 2 +- src/openai/types/__init__.py | 2 +- .../types/beta/assistant_create_params.py | 2 +- .../beta/thread_create_and_run_params.py | 2 +- .../types/beta/threads/run_create_params.py | 2 +- .../types/chat/completion_create_params.py | 2 +- src/openai/types/chat_model.py | 47 ++--------------- src/openai/types/shared/__init__.py | 1 + src/openai/types/shared/chat_model.py | 49 ++++++++++++++++++ src/openai/types/shared_params/__init__.py | 1 + src/openai/types/shared_params/chat_model.py | 51 +++++++++++++++++++ 15 files changed, 116 insertions(+), 54 deletions(-) create mode 100644 src/openai/types/shared/chat_model.py create mode 100644 src/openai/types/shared_params/chat_model.py diff --git a/api.md b/api.md index 2db9d1157e..20e776289e 100644 --- a/api.md +++ b/api.md @@ -2,6 +2,7 @@ ```python from openai.types import ( + ChatModel, ErrorObject, FunctionDefinition, FunctionParameters, @@ -222,9 +223,9 @@ Types: from openai.types.fine_tuning import ( FineTuningJob, FineTuningJobEvent, - FineTuningJobIntegration, FineTuningJobWandbIntegration, FineTuningJobWandbIntegrationObject, + FineTuningJobIntegration, ) ``` diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index d2bb8d7b92..ffecd8f9e9 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -23,8 +23,8 @@ assistant_update_params, ) from ..._base_client import AsyncPaginator, make_request_options -from ...types.chat_model import ChatModel from ...types.beta.assistant import Assistant +from ...types.shared.chat_model import ChatModel from ...types.beta.assistant_deleted import AssistantDeleted from ...types.shared_params.metadata import Metadata from ...types.beta.assistant_tool_param import AssistantToolParam diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index dc364b4e31..b819678be6 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -39,7 +39,6 @@ AsyncAssistantEventHandlerT, AsyncAssistantStreamManager, ) -from .....types.chat_model import ChatModel from .....types.beta.threads import ( run_list_params, run_create_params, @@ -47,6 +46,7 @@ run_submit_tool_outputs_params, ) from .....types.beta.threads.run import Run +from .....types.shared.chat_model import ChatModel from .....types.shared_params.metadata import Metadata from .....types.beta.assistant_tool_param import AssistantToolParam from .....types.beta.assistant_stream_event import AssistantStreamEvent diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 6ff8539501..d88559bdeb 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -49,9 +49,9 @@ AsyncAssistantEventHandlerT, AsyncAssistantStreamManager, ) -from ....types.chat_model import ChatModel from ....types.beta.thread import Thread from ....types.beta.threads.run import Run +from ....types.shared.chat_model import ChatModel from ....types.beta.thread_deleted import ThreadDeleted from ....types.shared_params.metadata import Metadata from ....types.beta.assistant_stream_event import AssistantStreamEvent diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 1753f6c990..708b1ff166 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -37,7 +37,7 @@ completion_update_params, ) from ...._base_client import AsyncPaginator, make_request_options -from ....types.chat_model import ChatModel +from ....types.shared.chat_model import ChatModel from ....types.chat.chat_completion import ChatCompletion from ....types.shared_params.metadata import Metadata from ....types.chat.chat_completion_chunk import ChatCompletionChunk diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 7abb22f239..5785877c8a 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -7,6 +7,7 @@ from .model import Model as Model from .shared import ( Metadata as Metadata, + ChatModel as ChatModel, ErrorObject as ErrorObject, FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters, @@ -16,7 +17,6 @@ ) from .upload import Upload as Upload from .embedding import Embedding as Embedding -from .chat_model import ChatModel as ChatModel from .completion import Completion as Completion from .moderation import Moderation as Moderation from .audio_model import AudioModel as AudioModel diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 66bef02ced..e90aabfd3f 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -5,7 +5,7 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -from ..chat_model import ChatModel +from ..shared.chat_model import ChatModel from .assistant_tool_param import AssistantToolParam from ..shared_params.metadata import Metadata from .file_chunking_strategy_param import FileChunkingStrategyParam diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 08f044c1be..d888fb3eee 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -5,7 +5,7 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict -from ..chat_model import ChatModel +from ..shared.chat_model import ChatModel from .function_tool_param import FunctionToolParam from .file_search_tool_param import FileSearchToolParam from ..shared_params.metadata import Metadata diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 093b4ce321..098e50a1d9 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -5,7 +5,7 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict -from ...chat_model import ChatModel +from ...shared.chat_model import ChatModel from ..assistant_tool_param import AssistantToolParam from .runs.run_step_include import RunStepInclude from ...shared_params.metadata import Metadata diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index c761cbe07b..4dd2812aba 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -5,7 +5,7 @@ from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict -from ..chat_model import ChatModel +from ..shared.chat_model import ChatModel from ..shared_params.metadata import Metadata from .chat_completion_modality import ChatCompletionModality from .chat_completion_tool_param import ChatCompletionToolParam diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index 6fe705a0b4..9304d195d6 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -1,49 +1,8 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal, TypeAlias + +from .shared import chat_model __all__ = ["ChatModel"] -ChatModel: TypeAlias = Literal[ - "o3-mini", - "o3-mini-2025-01-31", - "o1", - "o1-2024-12-17", - "o1-preview", - "o1-preview-2024-09-12", - "o1-mini", - "o1-mini-2024-09-12", - "gpt-4.5-preview", - "gpt-4.5-preview-2025-02-27", - "gpt-4o", - "gpt-4o-2024-11-20", - "gpt-4o-2024-08-06", - "gpt-4o-2024-05-13", - "gpt-4o-audio-preview", - "gpt-4o-audio-preview-2024-10-01", - "gpt-4o-audio-preview-2024-12-17", - "gpt-4o-mini-audio-preview", - "gpt-4o-mini-audio-preview-2024-12-17", - "chatgpt-4o-latest", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", -] +ChatModel = chat_model.ChatModel diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index 74bf304904..4cf367b1cc 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .metadata import Metadata as Metadata +from .chat_model import ChatModel as ChatModel from .error_object import ErrorObject as ErrorObject from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py new file mode 100644 index 0000000000..6fe705a0b4 --- /dev/null +++ b/src/openai/types/shared/chat_model.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatModel"] + +ChatModel: TypeAlias = Literal[ + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "chatgpt-4o-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", +] diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index 68a8db75fe..47a747b2d4 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .metadata import Metadata as Metadata +from .chat_model import ChatModel as ChatModel from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters from .response_format_text import ResponseFormatText as ResponseFormatText diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py new file mode 100644 index 0000000000..0ac3f31611 --- /dev/null +++ b/src/openai/types/shared_params/chat_model.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatModel"] + +ChatModel: TypeAlias = Literal[ + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "chatgpt-4o-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", +] From a6b493071b843bec3db807637e441c1768b695f8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 9 Mar 2025 05:03:47 +0000 Subject: [PATCH 810/914] release: 1.65.5 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b31d1b2102..b8446e8608 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.65.4" + ".": "1.65.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 759edd60a8..e2bf62a4df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.65.5 (2025-03-09) + +Full Changelog: [v1.65.4...v1.65.5](https://github.com/openai/openai-python/compare/v1.65.4...v1.65.5) + +### Chores + +* move ChatModel type to shared ([#2167](https://github.com/openai/openai-python/issues/2167)) ([104f02a](https://github.com/openai/openai-python/commit/104f02af371076d5d2498e48ae14d2eacc7df8bd)) + ## 1.65.4 (2025-03-05) Full Changelog: [v1.65.3...v1.65.4](https://github.com/openai/openai-python/compare/v1.65.3...v1.65.4) diff --git a/pyproject.toml b/pyproject.toml index a44991907d..09e79f5592 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.65.4" +version = "1.65.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 13991c8059..859b56580d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.65.4" # x-release-please-version +__version__ = "1.65.5" # x-release-please-version From bf4a7e67d71a10a2644f18aeb110fda1dcba0023 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 01:09:36 +0000 Subject: [PATCH 811/914] test: add DEFER_PYDANTIC_BUILD=false flag to tests (#2174) --- scripts/test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/test b/scripts/test index 4fa5698b8f..2b87845670 100755 --- a/scripts/test +++ b/scripts/test @@ -52,6 +52,8 @@ else echo fi +export DEFER_PYDANTIC_BUILD=false + echo "==> Running tests" rye run pytest "$@" From 71f73540d4f0cb21887bedf2cc43516a0ebbe7c9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:17:10 +0000 Subject: [PATCH 812/914] chore: export more types (#2176) --- src/openai/types/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 5785877c8a..eb71ac6ccc 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -17,6 +17,7 @@ ) from .upload import Upload as Upload from .embedding import Embedding as Embedding +from .chat_model import ChatModel as ChatModel from .completion import Completion as Completion from .moderation import Moderation as Moderation from .audio_model import AudioModel as AudioModel From 2954945ecc185259cfd7cd33c8cbc818a88e4e1b Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 11 Mar 2025 12:22:03 -0400 Subject: [PATCH 813/914] feat(api): add /v1/responses and built-in tools [platform.openai.com/docs/changelog](http://platform.openai.com/docs/changelog) --- .stats.yml | 4 +- README.md | 229 +-- api.md | 232 ++- examples/responses/__init__.py | 0 examples/responses/streaming.py | 30 + examples/responses/streaming_tools.py | 68 + examples/responses/structured_outputs.py | 55 + .../responses/structured_outputs_tools.py | 73 + src/openai/_client.py | 18 + src/openai/_streaming.py | 4 +- src/openai/lib/_parsing/_responses.py | 168 ++ src/openai/lib/_tools.py | 12 + .../lib/streaming/responses/__init__.py | 13 + src/openai/lib/streaming/responses/_events.py | 106 + .../lib/streaming/responses/_responses.py | 354 ++++ src/openai/lib/streaming/responses/_types.py | 10 + src/openai/resources/__init__.py | 28 + src/openai/resources/beta/__init__.py | 14 - src/openai/resources/beta/assistants.py | 17 +- src/openai/resources/beta/beta.py | 32 - src/openai/resources/beta/chat/completions.py | 40 +- .../resources/beta/threads/runs/runs.py | 51 +- .../resources/chat/completions/completions.py | 496 +++-- .../resources/chat/completions/messages.py | 4 +- src/openai/resources/files.py | 24 +- src/openai/resources/responses/__init__.py | 33 + src/openai/resources/responses/input_items.py | 223 ++ src/openai/resources/responses/responses.py | 1790 +++++++++++++++++ src/openai/resources/uploads/uploads.py | 14 +- .../{beta => }/vector_stores/__init__.py | 0 .../{beta => }/vector_stores/file_batches.py | 48 +- .../{beta => }/vector_stores/files.py | 235 ++- .../{beta => }/vector_stores/vector_stores.py | 178 +- src/openai/types/__init__.py | 21 + .../auto_file_chunking_strategy_param.py | 0 src/openai/types/beta/__init__.py | 15 - .../types/beta/assistant_create_params.py | 49 +- .../types/beta/assistant_update_params.py | 5 +- .../beta/thread_create_and_run_params.py | 43 +- src/openai/types/beta/thread_create_params.py | 42 +- .../types/beta/threads/run_create_params.py | 5 +- .../types/chat/chat_completion_audio_param.py | 5 +- .../chat_completion_content_part_param.py | 31 +- .../types/chat/chat_completion_message.py | 30 +- .../chat/chat_completion_reasoning_effort.py | 6 +- .../types/chat/completion_create_params.py | 132 +- .../types/chat/completion_list_params.py | 8 +- .../{beta => }/file_chunking_strategy.py | 2 +- .../file_chunking_strategy_param.py | 0 src/openai/types/file_create_params.py | 10 +- src/openai/types/file_purpose.py | 2 +- .../other_file_chunking_strategy_object.py | 2 +- src/openai/types/responses/__init__.py | 138 ++ src/openai/types/responses/computer_tool.py | 21 + .../types/responses/computer_tool_param.py | 21 + .../responses/easy_input_message_param.py | 27 + .../types/responses/file_search_tool.py | 44 + .../types/responses/file_search_tool_param.py | 45 + src/openai/types/responses/function_tool.py | 28 + .../types/responses/function_tool_param.py | 28 + .../types/responses/input_item_list_params.py | 28 + src/openai/types/responses/parsed_response.py | 77 + src/openai/types/responses/response.py | 204 ++ .../responses/response_audio_delta_event.py | 15 + .../responses/response_audio_done_event.py | 12 + .../response_audio_transcript_delta_event.py | 15 + .../response_audio_transcript_done_event.py | 12 + ..._code_interpreter_call_code_delta_event.py | 18 + ...e_code_interpreter_call_code_done_event.py | 18 + ...e_code_interpreter_call_completed_event.py | 19 + ...code_interpreter_call_in_progress_event.py | 19 + ...ode_interpreter_call_interpreting_event.py | 19 + .../response_code_interpreter_tool_call.py | 52 + .../responses/response_completed_event.py | 16 + .../responses/response_computer_tool_call.py | 212 ++ .../response_computer_tool_call_param.py | 208 ++ .../response_content_part_added_event.py | 30 + .../response_content_part_done_event.py | 30 + .../types/responses/response_create_params.py | 204 ++ .../types/responses/response_created_event.py | 16 + src/openai/types/responses/response_error.py | 34 + .../types/responses/response_error_event.py | 22 + .../types/responses/response_failed_event.py | 16 + ...sponse_file_search_call_completed_event.py | 18 + ...onse_file_search_call_in_progress_event.py | 18 + ...sponse_file_search_call_searching_event.py | 18 + .../response_file_search_tool_call.py | 51 + .../response_file_search_tool_call_param.py | 51 + .../responses/response_format_text_config.py | 16 + .../response_format_text_config_param.py | 16 + ...response_format_text_json_schema_config.py | 43 + ...se_format_text_json_schema_config_param.py | 41 + ...nse_function_call_arguments_delta_event.py | 23 + ...onse_function_call_arguments_done_event.py | 20 + .../responses/response_function_tool_call.py | 32 + .../response_function_tool_call_param.py | 31 + .../responses/response_function_web_search.py | 18 + .../response_function_web_search_param.py | 18 + .../responses/response_in_progress_event.py | 16 + .../types/responses/response_includable.py | 9 + .../responses/response_incomplete_event.py | 16 + .../types/responses/response_input_content.py | 15 + .../responses/response_input_content_param.py | 14 + .../types/responses/response_input_file.py | 22 + .../responses/response_input_file_param.py | 21 + .../types/responses/response_input_image.py | 28 + .../responses/response_input_image_param.py | 28 + .../responses/response_input_item_param.py | 174 ++ .../response_input_message_content_list.py | 10 + ...sponse_input_message_content_list_param.py | 16 + .../types/responses/response_input_param.py | 177 ++ .../types/responses/response_input_text.py | 15 + .../responses/response_input_text_param.py | 15 + .../types/responses/response_item_list.py | 152 ++ .../types/responses/response_output_item.py | 55 + .../response_output_item_added_event.py | 19 + .../response_output_item_done_event.py | 19 + .../responses/response_output_message.py | 34 + .../response_output_message_param.py | 34 + .../responses/response_output_refusal.py | 15 + .../response_output_refusal_param.py | 15 + .../types/responses/response_output_text.py | 64 + .../responses/response_output_text_param.py | 67 + .../responses/response_refusal_delta_event.py | 24 + .../responses/response_refusal_done_event.py | 24 + .../responses/response_retrieve_params.py | 18 + src/openai/types/responses/response_status.py | 7 + .../types/responses/response_stream_event.py | 78 + .../response_text_annotation_delta_event.py | 79 + .../types/responses/response_text_config.py | 26 + .../responses/response_text_config_param.py | 27 + .../responses/response_text_delta_event.py | 24 + .../responses/response_text_done_event.py | 24 + src/openai/types/responses/response_usage.py | 25 + ...esponse_web_search_call_completed_event.py | 18 + ...ponse_web_search_call_in_progress_event.py | 18 + ...esponse_web_search_call_searching_event.py | 18 + src/openai/types/responses/tool.py | 16 + .../types/responses/tool_choice_function.py | 15 + .../responses/tool_choice_function_param.py | 15 + .../types/responses/tool_choice_options.py | 7 + .../types/responses/tool_choice_types.py | 22 + .../responses/tool_choice_types_param.py | 24 + src/openai/types/responses/tool_param.py | 18 + src/openai/types/responses/web_search_tool.py | 48 + .../types/responses/web_search_tool_param.py | 48 + src/openai/types/shared/__init__.py | 4 + src/openai/types/shared/chat_model.py | 3 + src/openai/types/shared/comparison_filter.py | 30 + src/openai/types/shared/compound_filter.py | 22 + src/openai/types/shared/reasoning.py | 28 + src/openai/types/shared/reasoning_effort.py | 8 + .../shared/response_format_json_object.py | 2 +- .../shared/response_format_json_schema.py | 18 +- .../types/shared/response_format_text.py | 2 +- src/openai/types/shared_params/__init__.py | 4 + src/openai/types/shared_params/chat_model.py | 3 + .../types/shared_params/comparison_filter.py | 30 + .../types/shared_params/compound_filter.py | 23 + src/openai/types/shared_params/reasoning.py | 29 + .../types/shared_params/reasoning_effort.py | 10 + .../response_format_json_object.py | 2 +- .../response_format_json_schema.py | 18 +- .../shared_params/response_format_text.py | 2 +- .../static_file_chunking_strategy.py | 2 +- .../static_file_chunking_strategy_object.py | 2 +- ...tic_file_chunking_strategy_object_param.py | 0 .../static_file_chunking_strategy_param.py | 0 src/openai/types/{beta => }/vector_store.py | 4 +- .../{beta => }/vector_store_create_params.py | 2 +- .../types/{beta => }/vector_store_deleted.py | 2 +- .../{beta => }/vector_store_list_params.py | 0 .../types/vector_store_search_params.py | 40 + .../types/vector_store_search_response.py | 39 + .../{beta => }/vector_store_update_params.py | 2 +- .../{beta => }/vector_stores/__init__.py | 2 + .../vector_stores/file_batch_create_params.py | 11 +- .../file_batch_list_files_params.py | 0 .../vector_stores/file_content_response.py | 15 + .../vector_stores/file_create_params.py | 10 + .../vector_stores/file_list_params.py | 0 .../types/vector_stores/file_update_params.py | 21 + .../vector_stores/vector_store_file.py | 13 +- .../vector_stores/vector_store_file_batch.py | 2 +- .../vector_store_file_deleted.py | 2 +- .../beta/vector_stores/test_files.py | 420 ---- tests/api_resources/chat/test_completions.py | 64 +- .../vector_stores => responses}/__init__.py | 0 .../responses/test_input_items.py | 121 ++ tests/api_resources/test_responses.py | 498 +++++ .../{beta => }/test_vector_stores.py | 263 ++- tests/api_resources/vector_stores/__init__.py | 1 + .../vector_stores/test_file_batches.py | 216 +- .../api_resources/vector_stores/test_files.py | 625 ++++++ tests/lib/chat/test_completions.py | 15 + tests/lib/chat/test_completions_streaming.py | 15 + 196 files changed, 10058 insertions(+), 1333 deletions(-) create mode 100644 examples/responses/__init__.py create mode 100644 examples/responses/streaming.py create mode 100644 examples/responses/streaming_tools.py create mode 100644 examples/responses/structured_outputs.py create mode 100644 examples/responses/structured_outputs_tools.py create mode 100644 src/openai/lib/_parsing/_responses.py create mode 100644 src/openai/lib/streaming/responses/__init__.py create mode 100644 src/openai/lib/streaming/responses/_events.py create mode 100644 src/openai/lib/streaming/responses/_responses.py create mode 100644 src/openai/lib/streaming/responses/_types.py create mode 100644 src/openai/resources/responses/__init__.py create mode 100644 src/openai/resources/responses/input_items.py create mode 100644 src/openai/resources/responses/responses.py rename src/openai/resources/{beta => }/vector_stores/__init__.py (100%) rename src/openai/resources/{beta => }/vector_stores/file_batches.py (93%) rename src/openai/resources/{beta => }/vector_stores/files.py (73%) rename src/openai/resources/{beta => }/vector_stores/vector_stores.py (80%) rename src/openai/types/{beta => }/auto_file_chunking_strategy_param.py (100%) rename src/openai/types/{beta => }/file_chunking_strategy.py (93%) rename src/openai/types/{beta => }/file_chunking_strategy_param.py (100%) rename src/openai/types/{beta => }/other_file_chunking_strategy_object.py (89%) create mode 100644 src/openai/types/responses/__init__.py create mode 100644 src/openai/types/responses/computer_tool.py create mode 100644 src/openai/types/responses/computer_tool_param.py create mode 100644 src/openai/types/responses/easy_input_message_param.py create mode 100644 src/openai/types/responses/file_search_tool.py create mode 100644 src/openai/types/responses/file_search_tool_param.py create mode 100644 src/openai/types/responses/function_tool.py create mode 100644 src/openai/types/responses/function_tool_param.py create mode 100644 src/openai/types/responses/input_item_list_params.py create mode 100644 src/openai/types/responses/parsed_response.py create mode 100644 src/openai/types/responses/response.py create mode 100644 src/openai/types/responses/response_audio_delta_event.py create mode 100644 src/openai/types/responses/response_audio_done_event.py create mode 100644 src/openai/types/responses/response_audio_transcript_delta_event.py create mode 100644 src/openai/types/responses/response_audio_transcript_done_event.py create mode 100644 src/openai/types/responses/response_code_interpreter_call_code_delta_event.py create mode 100644 src/openai/types/responses/response_code_interpreter_call_code_done_event.py create mode 100644 src/openai/types/responses/response_code_interpreter_call_completed_event.py create mode 100644 src/openai/types/responses/response_code_interpreter_call_in_progress_event.py create mode 100644 src/openai/types/responses/response_code_interpreter_call_interpreting_event.py create mode 100644 src/openai/types/responses/response_code_interpreter_tool_call.py create mode 100644 src/openai/types/responses/response_completed_event.py create mode 100644 src/openai/types/responses/response_computer_tool_call.py create mode 100644 src/openai/types/responses/response_computer_tool_call_param.py create mode 100644 src/openai/types/responses/response_content_part_added_event.py create mode 100644 src/openai/types/responses/response_content_part_done_event.py create mode 100644 src/openai/types/responses/response_create_params.py create mode 100644 src/openai/types/responses/response_created_event.py create mode 100644 src/openai/types/responses/response_error.py create mode 100644 src/openai/types/responses/response_error_event.py create mode 100644 src/openai/types/responses/response_failed_event.py create mode 100644 src/openai/types/responses/response_file_search_call_completed_event.py create mode 100644 src/openai/types/responses/response_file_search_call_in_progress_event.py create mode 100644 src/openai/types/responses/response_file_search_call_searching_event.py create mode 100644 src/openai/types/responses/response_file_search_tool_call.py create mode 100644 src/openai/types/responses/response_file_search_tool_call_param.py create mode 100644 src/openai/types/responses/response_format_text_config.py create mode 100644 src/openai/types/responses/response_format_text_config_param.py create mode 100644 src/openai/types/responses/response_format_text_json_schema_config.py create mode 100644 src/openai/types/responses/response_format_text_json_schema_config_param.py create mode 100644 src/openai/types/responses/response_function_call_arguments_delta_event.py create mode 100644 src/openai/types/responses/response_function_call_arguments_done_event.py create mode 100644 src/openai/types/responses/response_function_tool_call.py create mode 100644 src/openai/types/responses/response_function_tool_call_param.py create mode 100644 src/openai/types/responses/response_function_web_search.py create mode 100644 src/openai/types/responses/response_function_web_search_param.py create mode 100644 src/openai/types/responses/response_in_progress_event.py create mode 100644 src/openai/types/responses/response_includable.py create mode 100644 src/openai/types/responses/response_incomplete_event.py create mode 100644 src/openai/types/responses/response_input_content.py create mode 100644 src/openai/types/responses/response_input_content_param.py create mode 100644 src/openai/types/responses/response_input_file.py create mode 100644 src/openai/types/responses/response_input_file_param.py create mode 100644 src/openai/types/responses/response_input_image.py create mode 100644 src/openai/types/responses/response_input_image_param.py create mode 100644 src/openai/types/responses/response_input_item_param.py create mode 100644 src/openai/types/responses/response_input_message_content_list.py create mode 100644 src/openai/types/responses/response_input_message_content_list_param.py create mode 100644 src/openai/types/responses/response_input_param.py create mode 100644 src/openai/types/responses/response_input_text.py create mode 100644 src/openai/types/responses/response_input_text_param.py create mode 100644 src/openai/types/responses/response_item_list.py create mode 100644 src/openai/types/responses/response_output_item.py create mode 100644 src/openai/types/responses/response_output_item_added_event.py create mode 100644 src/openai/types/responses/response_output_item_done_event.py create mode 100644 src/openai/types/responses/response_output_message.py create mode 100644 src/openai/types/responses/response_output_message_param.py create mode 100644 src/openai/types/responses/response_output_refusal.py create mode 100644 src/openai/types/responses/response_output_refusal_param.py create mode 100644 src/openai/types/responses/response_output_text.py create mode 100644 src/openai/types/responses/response_output_text_param.py create mode 100644 src/openai/types/responses/response_refusal_delta_event.py create mode 100644 src/openai/types/responses/response_refusal_done_event.py create mode 100644 src/openai/types/responses/response_retrieve_params.py create mode 100644 src/openai/types/responses/response_status.py create mode 100644 src/openai/types/responses/response_stream_event.py create mode 100644 src/openai/types/responses/response_text_annotation_delta_event.py create mode 100644 src/openai/types/responses/response_text_config.py create mode 100644 src/openai/types/responses/response_text_config_param.py create mode 100644 src/openai/types/responses/response_text_delta_event.py create mode 100644 src/openai/types/responses/response_text_done_event.py create mode 100644 src/openai/types/responses/response_usage.py create mode 100644 src/openai/types/responses/response_web_search_call_completed_event.py create mode 100644 src/openai/types/responses/response_web_search_call_in_progress_event.py create mode 100644 src/openai/types/responses/response_web_search_call_searching_event.py create mode 100644 src/openai/types/responses/tool.py create mode 100644 src/openai/types/responses/tool_choice_function.py create mode 100644 src/openai/types/responses/tool_choice_function_param.py create mode 100644 src/openai/types/responses/tool_choice_options.py create mode 100644 src/openai/types/responses/tool_choice_types.py create mode 100644 src/openai/types/responses/tool_choice_types_param.py create mode 100644 src/openai/types/responses/tool_param.py create mode 100644 src/openai/types/responses/web_search_tool.py create mode 100644 src/openai/types/responses/web_search_tool_param.py create mode 100644 src/openai/types/shared/comparison_filter.py create mode 100644 src/openai/types/shared/compound_filter.py create mode 100644 src/openai/types/shared/reasoning.py create mode 100644 src/openai/types/shared/reasoning_effort.py create mode 100644 src/openai/types/shared_params/comparison_filter.py create mode 100644 src/openai/types/shared_params/compound_filter.py create mode 100644 src/openai/types/shared_params/reasoning.py create mode 100644 src/openai/types/shared_params/reasoning_effort.py rename src/openai/types/{beta => }/static_file_chunking_strategy.py (94%) rename src/openai/types/{beta => }/static_file_chunking_strategy_object.py (92%) rename src/openai/types/{beta => }/static_file_chunking_strategy_object_param.py (100%) rename src/openai/types/{beta => }/static_file_chunking_strategy_param.py (100%) rename src/openai/types/{beta => }/vector_store.py (97%) rename src/openai/types/{beta => }/vector_store_create_params.py (97%) rename src/openai/types/{beta => }/vector_store_deleted.py (89%) rename src/openai/types/{beta => }/vector_store_list_params.py (100%) create mode 100644 src/openai/types/vector_store_search_params.py create mode 100644 src/openai/types/vector_store_search_response.py rename src/openai/types/{beta => }/vector_store_update_params.py (96%) rename src/openai/types/{beta => }/vector_stores/__init__.py (82%) rename src/openai/types/{beta => }/vector_stores/file_batch_create_params.py (61%) rename src/openai/types/{beta => }/vector_stores/file_batch_list_files_params.py (100%) create mode 100644 src/openai/types/vector_stores/file_content_response.py rename src/openai/types/{beta => }/vector_stores/file_create_params.py (60%) rename src/openai/types/{beta => }/vector_stores/file_list_params.py (100%) create mode 100644 src/openai/types/vector_stores/file_update_params.py rename src/openai/types/{beta => }/vector_stores/vector_store_file.py (76%) rename src/openai/types/{beta => }/vector_stores/vector_store_file_batch.py (97%) rename src/openai/types/{beta => }/vector_stores/vector_store_file_deleted.py (89%) delete mode 100644 tests/api_resources/beta/vector_stores/test_files.py rename tests/api_resources/{beta/vector_stores => responses}/__init__.py (100%) create mode 100644 tests/api_resources/responses/test_input_items.py create mode 100644 tests/api_resources/test_responses.py rename tests/api_resources/{beta => }/test_vector_stores.py (60%) create mode 100644 tests/api_resources/vector_stores/__init__.py rename tests/api_resources/{beta => }/vector_stores/test_file_batches.py (68%) create mode 100644 tests/api_resources/vector_stores/test_files.py diff --git a/.stats.yml b/.stats.yml index 0d7e83be4f..455874212c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 74 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-b524aed1c2c5c928aa4e2c546f5dbb364e7b4d5027daf05e42e210b05a97c3c6.yml +configured_endpoints: 81 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-be834d63e326a82494e819085137f5eb15866f3fc787db1f3afe7168d419e18a.yml diff --git a/README.md b/README.md index 3c103f036c..c52bffbb5f 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,10 @@ It is generated from our [OpenAPI specification](https://github.com/openai/opena ## Documentation -The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). The full API of this library can be found in [api.md](api.md). +The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs/api-reference). The full API of this library can be found in [api.md](api.md). ## Installation -> [!IMPORTANT] -> The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code. - ```sh # install from PyPI pip install openai @@ -26,46 +23,69 @@ pip install openai The full API of this library can be found in [api.md](api.md). +The primary API for interacting with OpenAI models is the [Responses API](https://platform.openai.com/docs/api-reference/responses). You can generate text from the model with the code below. + ```python import os from openai import OpenAI client = OpenAI( - api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), +) + +response = client.responses.create( + model="gpt-4o", + instructions="You are a coding assistant that talks like a pirate.", + input="How do I check if a Python object is an instance of a class?", ) -chat_completion = client.chat.completions.create( +print(response.output_text) +``` + +The previous standard (supported indefinitely) for generating text is the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). You can use that API to generate text from the model with the code below. + +```python +from openai import OpenAI + +client = OpenAI() + +completion = client.chat.completions.create( + model="gpt-4o", messages=[ + {"role": "developer", "content": "Talk like a pirate."}, { "role": "user", - "content": "Say this is a test", - } + "content": "How do I check if a Python object is an instance of a class?", + }, ], - model="gpt-4o", ) + +print(completion.choices[0].message.content) ``` While you can provide an `api_key` keyword argument, we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) to add `OPENAI_API_KEY="My API Key"` to your `.env` file -so that your API Key is not stored in source control. +so that your API key is not stored in source control. +[Get an API key here](https://platform.openai.com/settings/organization/api-keys). ### Vision -With a hosted image: +With an image URL: ```python -response = client.chat.completions.create( +prompt = "What is in this image?" +img_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/2023_06_08_Raccoon1.jpg/1599px-2023_06_08_Raccoon1.jpg" + +response = client.responses.create( model="gpt-4o-mini", - messages=[ + input=[ { "role": "user", "content": [ - {"type": "text", "text": prompt}, - { - "type": "image_url", - "image_url": {"url": f"{img_url}"}, - }, + {"type": "input_text", "text": prompt}, + {"type": "input_image", "image_url": f"{img_url}"}, ], } ], @@ -75,73 +95,29 @@ response = client.chat.completions.create( With the image as a base64 encoded string: ```python -response = client.chat.completions.create( +import base64 +from openai import OpenAI + +client = OpenAI() + +prompt = "What is in this image?" +with open("path/to/image.png", "rb") as image_file: + b64_image = base64.b64encode(image_file.read()).decode("utf-8") + +response = client.responses.create( model="gpt-4o-mini", - messages=[ + input=[ { "role": "user", "content": [ - {"type": "text", "text": prompt}, - { - "type": "image_url", - "image_url": {"url": f"data:{img_type};base64,{img_b64_str}"}, - }, + {"type": "input_text", "text": prompt}, + {"type": "input_image", "image_url": f"data:image/png;base64,{b64_image}"}, ], } ], ) ``` -### Polling Helpers - -When interacting with the API some actions such as starting a Run and adding files to vector stores are asynchronous and take time to complete. The SDK includes -helper functions which will poll the status until it reaches a terminal state and then return the resulting object. -If an API method results in an action that could benefit from polling there will be a corresponding version of the -method ending in '\_and_poll'. - -For instance to create a Run and poll until it reaches a terminal state you can run: - -```python -run = client.beta.threads.runs.create_and_poll( - thread_id=thread.id, - assistant_id=assistant.id, -) -``` - -More information on the lifecycle of a Run can be found in the [Run Lifecycle Documentation](https://platform.openai.com/docs/assistants/how-it-works/run-lifecycle) - -### Bulk Upload Helpers - -When creating and interacting with vector stores, you can use polling helpers to monitor the status of operations. -For convenience, we also provide a bulk upload helper to allow you to simultaneously upload several files at once. - -```python -sample_files = [Path("sample-paper.pdf"), ...] - -batch = await client.vector_stores.file_batches.upload_and_poll( - store.id, - files=sample_files, -) -``` - -### Streaming Helpers - -The SDK also includes helpers to process streams and handle incoming events. - -```python -with client.beta.threads.runs.stream( - thread_id=thread.id, - assistant_id=assistant.id, - instructions="Please address the user as Jane Doe. The user has a premium account.", -) as stream: - for event in stream: - # Print the text from text delta events - if event.type == "thread.message.delta" and event.data.delta.content: - print(event.data.delta.content[0].text) -``` - -More information on streaming helpers can be found in the dedicated documentation: [helpers.md](helpers.md) - ## Async usage Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call: @@ -152,20 +128,16 @@ import asyncio from openai import AsyncOpenAI client = AsyncOpenAI( - api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), ) async def main() -> None: - chat_completion = await client.chat.completions.create( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], - model="gpt-4o", + response = await client.responses.create( + model="gpt-4o", input="Explain disestablishmentarianism to a smart five year old." ) + print(response.output_text) asyncio.run(main()) @@ -182,18 +154,14 @@ from openai import OpenAI client = OpenAI() -stream = client.chat.completions.create( - messages=[ - { - "role": "user", - "content": "Say this is a test", - } - ], +stream = client.responses.create( model="gpt-4o", + input="Write a one-sentence bedtime story about a unicorn.", stream=True, ) -for chunk in stream: - print(chunk.choices[0].delta.content or "", end="") + +for event in stream: + print(event) ``` The async client uses the exact same interface. @@ -206,58 +174,19 @@ client = AsyncOpenAI() async def main(): - stream = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Say this is a test"}], + stream = client.responses.create( + model="gpt-4o", + input="Write a one-sentence bedtime story about a unicorn.", stream=True, ) - async for chunk in stream: - print(chunk.choices[0].delta.content or "", end="") - - -asyncio.run(main()) -``` - -## Module-level client - -> [!IMPORTANT] -> We highly recommend instantiating client instances instead of relying on the global client. -We also expose a global client instance that is accessible in a similar fashion to versions prior to v1. - -```py -import openai - -# optional; defaults to `os.environ['OPENAI_API_KEY']` -openai.api_key = '...' + for event in stream: + print(event) -# all client options can be configured just like the `OpenAI` instantiation counterpart -openai.base_url = "https://.../" -openai.default_headers = {"x-foo": "true"} -completion = openai.chat.completions.create( - model="gpt-4o", - messages=[ - { - "role": "user", - "content": "How do I output all files in a directory using Python?", - }, - ], -) -print(completion.choices[0].message.content) +asyncio.run(main()) ``` -The API is the exact same as the standard client instance-based API. - -This is intended to be used within REPLs or notebooks for faster iteration, **not** in application code. - -We recommend that you always instantiate a client (e.g., with `client = OpenAI()`) in application code because: - -- It can be difficult to reason about where client options are configured -- It's not possible to change certain client options without potentially causing race conditions -- It's harder to mock for testing purposes -- It's not possible to control cleanup of network connections - ## Realtime API beta The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as [function calling](https://platform.openai.com/docs/guides/function-calling) through a WebSocket connection. @@ -304,7 +233,7 @@ However the real magic of the Realtime API is handling audio inputs / outputs, s ### Realtime error handling -Whenever an error occurs, the Realtime API will send an [`error` event](https://platform.openai.com/docs/guides/realtime-model-capabilities#error-handling) and the connection will stay open and remain usable. This means you need to handle it yourself, as *no errors are raised directly* by the SDK when an `error` event comes in. +Whenever an error occurs, the Realtime API will send an [`error` event](https://platform.openai.com/docs/guides/realtime-model-capabilities#error-handling) and the connection will stay open and remain usable. This means you need to handle it yourself, as _no errors are raised directly_ by the SDK when an `error` event comes in. ```py client = AsyncOpenAI() @@ -408,11 +337,11 @@ from openai import OpenAI client = OpenAI() -completion = client.chat.completions.create( - messages=[ +response = client.chat.responses.create( + input=[ { "role": "user", - "content": "Can you generate an example json object describing a fruit?", + "content": "How much ?", } ], model="gpt-4o", @@ -489,15 +418,16 @@ Error codes are as follows: All object responses in the SDK provide a `_request_id` property which is added from the `x-request-id` response header so that you can quickly log failing requests and report them back to OpenAI. ```python -completion = await client.chat.completions.create( - messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-4" +response = await client.responses.create( + model="gpt-4o-mini", + input="Say 'this is a test'.", ) -print(completion._request_id) # req_123 +print(response._request_id) # req_123 ``` Note that unlike other properties that use an `_` prefix, the `_request_id` property -*is* public. Unless documented otherwise, *all* other `_` prefix properties, -methods and modules are *private*. +_is_ public. Unless documented otherwise, _all_ other `_` prefix properties, +methods and modules are _private_. > [!IMPORTANT] > If you need to access request IDs for failed requests you must catch the `APIStatusError` exception @@ -514,8 +444,7 @@ except openai.APIStatusError as exc: raise exc ``` - -### Retries +## Retries Certain errors are automatically retried 2 times by default, with a short exponential backoff. Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, @@ -544,7 +473,7 @@ client.with_options(max_retries=5).chat.completions.create( ) ``` -### Timeouts +## Timeouts By default requests time out after 10 minutes. You can configure this with a `timeout` option, which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: diff --git a/api.md b/api.md index 20e776289e..6827b88f0b 100644 --- a/api.md +++ b/api.md @@ -3,10 +3,14 @@ ```python from openai.types import ( ChatModel, + ComparisonFilter, + CompoundFilter, ErrorObject, FunctionDefinition, FunctionParameters, Metadata, + Reasoning, + ReasoningEffort, ResponseFormatJSONObject, ResponseFormatJSONSchema, ResponseFormatText, @@ -59,7 +63,6 @@ from openai.types.chat import ( ChatCompletionModality, ChatCompletionNamedToolChoice, ChatCompletionPredictionContent, - ChatCompletionReasoningEffort, ChatCompletionRole, ChatCompletionStoreMessage, ChatCompletionStreamOptions, @@ -69,6 +72,7 @@ from openai.types.chat import ( ChatCompletionToolChoiceOption, ChatCompletionToolMessageParam, ChatCompletionUserMessageParam, + ChatCompletionReasoningEffort, ) ``` @@ -249,6 +253,73 @@ Methods: - client.fine_tuning.jobs.checkpoints.list(fine_tuning_job_id, \*\*params) -> SyncCursorPage[FineTuningJobCheckpoint] +# VectorStores + +Types: + +```python +from openai.types import ( + AutoFileChunkingStrategyParam, + FileChunkingStrategy, + FileChunkingStrategyParam, + OtherFileChunkingStrategyObject, + StaticFileChunkingStrategy, + StaticFileChunkingStrategyObject, + StaticFileChunkingStrategyObjectParam, + VectorStore, + VectorStoreDeleted, + VectorStoreSearchResponse, +) +``` + +Methods: + +- client.vector_stores.create(\*\*params) -> VectorStore +- client.vector_stores.retrieve(vector_store_id) -> VectorStore +- client.vector_stores.update(vector_store_id, \*\*params) -> VectorStore +- client.vector_stores.list(\*\*params) -> SyncCursorPage[VectorStore] +- client.vector_stores.delete(vector_store_id) -> VectorStoreDeleted +- client.vector_stores.search(vector_store_id, \*\*params) -> SyncPage[VectorStoreSearchResponse] + +## Files + +Types: + +```python +from openai.types.vector_stores import VectorStoreFile, VectorStoreFileDeleted, FileContentResponse +``` + +Methods: + +- client.vector_stores.files.create(vector_store_id, \*\*params) -> VectorStoreFile +- client.vector_stores.files.retrieve(file_id, \*, vector_store_id) -> VectorStoreFile +- client.vector_stores.files.update(file_id, \*, vector_store_id, \*\*params) -> VectorStoreFile +- client.vector_stores.files.list(vector_store_id, \*\*params) -> SyncCursorPage[VectorStoreFile] +- client.vector_stores.files.delete(file_id, \*, vector_store_id) -> VectorStoreFileDeleted +- client.vector_stores.files.content(file_id, \*, vector_store_id) -> SyncPage[FileContentResponse] +- client.vector_stores.files.create_and_poll(\*args) -> VectorStoreFile +- client.vector_stores.files.poll(\*args) -> VectorStoreFile +- client.vector_stores.files.upload(\*args) -> VectorStoreFile +- client.vector_stores.files.upload_and_poll(\*args) -> VectorStoreFile + +## FileBatches + +Types: + +```python +from openai.types.vector_stores import VectorStoreFileBatch +``` + +Methods: + +- client.vector_stores.file_batches.create(vector_store_id, \*\*params) -> VectorStoreFileBatch +- client.vector_stores.file_batches.retrieve(batch_id, \*, vector_store_id) -> VectorStoreFileBatch +- client.vector_stores.file_batches.cancel(batch_id, \*, vector_store_id) -> VectorStoreFileBatch +- client.vector_stores.file_batches.list_files(batch_id, \*, vector_store_id, \*\*params) -> SyncCursorPage[VectorStoreFile] +- client.vector_stores.file_batches.create_and_poll(\*args) -> VectorStoreFileBatch +- client.vector_stores.file_batches.poll(\*args) -> VectorStoreFileBatch +- client.vector_stores.file_batches.upload_and_poll(\*args) -> VectorStoreFileBatch + # Beta ## Realtime @@ -317,69 +388,6 @@ Methods: - client.beta.realtime.sessions.create(\*\*params) -> SessionCreateResponse -## VectorStores - -Types: - -```python -from openai.types.beta import ( - AutoFileChunkingStrategyParam, - FileChunkingStrategy, - FileChunkingStrategyParam, - OtherFileChunkingStrategyObject, - StaticFileChunkingStrategy, - StaticFileChunkingStrategyObject, - StaticFileChunkingStrategyObjectParam, - VectorStore, - VectorStoreDeleted, -) -``` - -Methods: - -- client.beta.vector_stores.create(\*\*params) -> VectorStore -- client.beta.vector_stores.retrieve(vector_store_id) -> VectorStore -- client.beta.vector_stores.update(vector_store_id, \*\*params) -> VectorStore -- client.beta.vector_stores.list(\*\*params) -> SyncCursorPage[VectorStore] -- client.beta.vector_stores.delete(vector_store_id) -> VectorStoreDeleted - -### Files - -Types: - -```python -from openai.types.beta.vector_stores import VectorStoreFile, VectorStoreFileDeleted -``` - -Methods: - -- client.beta.vector_stores.files.create(vector_store_id, \*\*params) -> VectorStoreFile -- client.beta.vector_stores.files.retrieve(file_id, \*, vector_store_id) -> VectorStoreFile -- client.beta.vector_stores.files.list(vector_store_id, \*\*params) -> SyncCursorPage[VectorStoreFile] -- client.beta.vector_stores.files.delete(file_id, \*, vector_store_id) -> VectorStoreFileDeleted -- client.beta.vector_stores.files.create_and_poll(\*args) -> VectorStoreFile -- client.beta.vector_stores.files.poll(\*args) -> VectorStoreFile -- client.beta.vector_stores.files.upload(\*args) -> VectorStoreFile -- client.beta.vector_stores.files.upload_and_poll(\*args) -> VectorStoreFile - -### FileBatches - -Types: - -```python -from openai.types.beta.vector_stores import VectorStoreFileBatch -``` - -Methods: - -- client.beta.vector_stores.file_batches.create(vector_store_id, \*\*params) -> VectorStoreFileBatch -- client.beta.vector_stores.file_batches.retrieve(batch_id, \*, vector_store_id) -> VectorStoreFileBatch -- client.beta.vector_stores.file_batches.cancel(batch_id, \*, vector_store_id) -> VectorStoreFileBatch -- client.beta.vector_stores.file_batches.list_files(batch_id, \*, vector_store_id, \*\*params) -> SyncCursorPage[VectorStoreFile] -- client.beta.vector_stores.file_batches.create_and_poll(\*args) -> VectorStoreFileBatch -- client.beta.vector_stores.file_batches.poll(\*args) -> VectorStoreFileBatch -- client.beta.vector_stores.file_batches.upload_and_poll(\*args) -> VectorStoreFileBatch - ## Assistants Types: @@ -573,3 +581,99 @@ from openai.types.uploads import UploadPart Methods: - client.uploads.parts.create(upload_id, \*\*params) -> UploadPart + +# Responses + +Types: + +```python +from openai.types.responses import ( + ComputerTool, + EasyInputMessage, + FileSearchTool, + FunctionTool, + Response, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseCodeInterpreterToolCall, + ResponseCompletedEvent, + ResponseComputerToolCall, + ResponseContent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseError, + ResponseErrorEvent, + ResponseFailedEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFileSearchToolCall, + ResponseFormatTextConfig, + ResponseFormatTextJSONSchemaConfig, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseFunctionToolCall, + ResponseFunctionWebSearch, + ResponseInProgressEvent, + ResponseIncludable, + ResponseIncompleteEvent, + ResponseInput, + ResponseInputAudio, + ResponseInputContent, + ResponseInputFile, + ResponseInputImage, + ResponseInputItem, + ResponseInputMessageContentList, + ResponseInputText, + ResponseOutputAudio, + ResponseOutputItem, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseOutputMessage, + ResponseOutputRefusal, + ResponseOutputText, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseStatus, + ResponseStreamEvent, + ResponseTextAnnotationDeltaEvent, + ResponseTextConfig, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + ResponseUsage, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + Tool, + ToolChoiceFunction, + ToolChoiceOptions, + ToolChoiceTypes, + WebSearchTool, +) +``` + +Methods: + +- client.responses.create(\*\*params) -> Response +- client.responses.retrieve(response_id, \*\*params) -> Response +- client.responses.delete(response_id) -> None + +## InputItems + +Types: + +```python +from openai.types.responses import ResponseItemList +``` + +Methods: + +- client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[Data] diff --git a/examples/responses/__init__.py b/examples/responses/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/responses/streaming.py b/examples/responses/streaming.py new file mode 100644 index 0000000000..39787968d6 --- /dev/null +++ b/examples/responses/streaming.py @@ -0,0 +1,30 @@ +from typing import List + +import rich +from pydantic import BaseModel + +from openai import OpenAI + + +class Step(BaseModel): + explanation: str + output: str + + +class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + + +client = OpenAI() + +with client.responses.stream( + input="solve 8x + 31 = 2", + model="gpt-4o-2024-08-06", + text_format=MathResponse, +) as stream: + for event in stream: + if "output_text" in event.type: + rich.print(event) + +rich.print(stream.get_final_response()) diff --git a/examples/responses/streaming_tools.py b/examples/responses/streaming_tools.py new file mode 100644 index 0000000000..f40cd9356d --- /dev/null +++ b/examples/responses/streaming_tools.py @@ -0,0 +1,68 @@ +from enum import Enum +from typing import List, Union + +import rich +from pydantic import BaseModel + +import openai +from openai import OpenAI + + +class Table(str, Enum): + orders = "orders" + customers = "customers" + products = "products" + + +class Column(str, Enum): + id = "id" + status = "status" + expected_delivery_date = "expected_delivery_date" + delivered_at = "delivered_at" + shipped_at = "shipped_at" + ordered_at = "ordered_at" + canceled_at = "canceled_at" + + +class Operator(str, Enum): + eq = "=" + gt = ">" + lt = "<" + le = "<=" + ge = ">=" + ne = "!=" + + +class OrderBy(str, Enum): + asc = "asc" + desc = "desc" + + +class DynamicValue(BaseModel): + column_name: str + + +class Condition(BaseModel): + column: str + operator: Operator + value: Union[str, int, DynamicValue] + + +class Query(BaseModel): + table_name: Table + columns: List[Column] + conditions: List[Condition] + order_by: OrderBy + + +client = OpenAI() + +with client.responses.stream( + model="gpt-4o-2024-08-06", + input="look up all my orders in november of last year that were fulfilled but not delivered on time", + tools=[ + openai.pydantic_function_tool(Query), + ], +) as stream: + for event in stream: + rich.print(event) diff --git a/examples/responses/structured_outputs.py b/examples/responses/structured_outputs.py new file mode 100644 index 0000000000..0b146bc0bc --- /dev/null +++ b/examples/responses/structured_outputs.py @@ -0,0 +1,55 @@ +from typing import List + +import rich +from pydantic import BaseModel + +from openai import OpenAI + + +class Step(BaseModel): + explanation: str + output: str + + +class MathResponse(BaseModel): + steps: List[Step] + final_answer: str + + +client = OpenAI() + +rsp = client.responses.parse( + input="solve 8x + 31 = 2", + model="gpt-4o-2024-08-06", + text_format=MathResponse, +) + +for output in rsp.output: + if output.type != "message": + raise Exception("Unexpected non message") + + for item in output.content: + if item.type != "output_text": + raise Exception("unexpected output type") + + if not item.parsed: + raise Exception("Could not parse response") + + rich.print(item.parsed) + + print("answer: ", item.parsed.final_answer) + +# or + +message = rsp.output[0] +assert message.type == "message" + +text = message.content[0] +assert text.type == "output_text" + +if not text.parsed: + raise Exception("Could not parse response") + +rich.print(text.parsed) + +print("answer: ", text.parsed.final_answer) diff --git a/examples/responses/structured_outputs_tools.py b/examples/responses/structured_outputs_tools.py new file mode 100644 index 0000000000..918348207d --- /dev/null +++ b/examples/responses/structured_outputs_tools.py @@ -0,0 +1,73 @@ +from enum import Enum +from typing import List, Union + +import rich +from pydantic import BaseModel + +import openai +from openai import OpenAI + + +class Table(str, Enum): + orders = "orders" + customers = "customers" + products = "products" + + +class Column(str, Enum): + id = "id" + status = "status" + expected_delivery_date = "expected_delivery_date" + delivered_at = "delivered_at" + shipped_at = "shipped_at" + ordered_at = "ordered_at" + canceled_at = "canceled_at" + + +class Operator(str, Enum): + eq = "=" + gt = ">" + lt = "<" + le = "<=" + ge = ">=" + ne = "!=" + + +class OrderBy(str, Enum): + asc = "asc" + desc = "desc" + + +class DynamicValue(BaseModel): + column_name: str + + +class Condition(BaseModel): + column: str + operator: Operator + value: Union[str, int, DynamicValue] + + +class Query(BaseModel): + table_name: Table + columns: List[Column] + conditions: List[Condition] + order_by: OrderBy + + +client = OpenAI() + +response = client.responses.parse( + model="gpt-4o-2024-08-06", + input="look up all my orders in november of last year that were fulfilled but not delivered on time", + tools=[ + openai.pydantic_function_tool(Query), + ], +) + +rich.print(response) + +function_call = response.output[0] +assert function_call.type == "function_call" +assert isinstance(function_call.parsed_arguments, Query) +print("table name:", function_call.parsed_arguments.table_name) diff --git a/src/openai/_client.py b/src/openai/_client.py index 2464c6504c..18d96da9a3 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -37,7 +37,9 @@ from .resources.chat import chat from .resources.audio import audio from .resources.uploads import uploads +from .resources.responses import responses from .resources.fine_tuning import fine_tuning +from .resources.vector_stores import vector_stores __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "OpenAI", "AsyncOpenAI", "Client", "AsyncClient"] @@ -52,9 +54,11 @@ class OpenAI(SyncAPIClient): moderations: moderations.Moderations models: models.Models fine_tuning: fine_tuning.FineTuning + vector_stores: vector_stores.VectorStores beta: beta.Beta batches: batches.Batches uploads: uploads.Uploads + responses: responses.Responses with_raw_response: OpenAIWithRawResponse with_streaming_response: OpenAIWithStreamedResponse @@ -149,9 +153,11 @@ def __init__( self.moderations = moderations.Moderations(self) self.models = models.Models(self) self.fine_tuning = fine_tuning.FineTuning(self) + self.vector_stores = vector_stores.VectorStores(self) self.beta = beta.Beta(self) self.batches = batches.Batches(self) self.uploads = uploads.Uploads(self) + self.responses = responses.Responses(self) self.with_raw_response = OpenAIWithRawResponse(self) self.with_streaming_response = OpenAIWithStreamedResponse(self) @@ -279,9 +285,11 @@ class AsyncOpenAI(AsyncAPIClient): moderations: moderations.AsyncModerations models: models.AsyncModels fine_tuning: fine_tuning.AsyncFineTuning + vector_stores: vector_stores.AsyncVectorStores beta: beta.AsyncBeta batches: batches.AsyncBatches uploads: uploads.AsyncUploads + responses: responses.AsyncResponses with_raw_response: AsyncOpenAIWithRawResponse with_streaming_response: AsyncOpenAIWithStreamedResponse @@ -376,9 +384,11 @@ def __init__( self.moderations = moderations.AsyncModerations(self) self.models = models.AsyncModels(self) self.fine_tuning = fine_tuning.AsyncFineTuning(self) + self.vector_stores = vector_stores.AsyncVectorStores(self) self.beta = beta.AsyncBeta(self) self.batches = batches.AsyncBatches(self) self.uploads = uploads.AsyncUploads(self) + self.responses = responses.AsyncResponses(self) self.with_raw_response = AsyncOpenAIWithRawResponse(self) self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self) @@ -507,9 +517,11 @@ def __init__(self, client: OpenAI) -> None: self.moderations = moderations.ModerationsWithRawResponse(client.moderations) self.models = models.ModelsWithRawResponse(client.models) self.fine_tuning = fine_tuning.FineTuningWithRawResponse(client.fine_tuning) + self.vector_stores = vector_stores.VectorStoresWithRawResponse(client.vector_stores) self.beta = beta.BetaWithRawResponse(client.beta) self.batches = batches.BatchesWithRawResponse(client.batches) self.uploads = uploads.UploadsWithRawResponse(client.uploads) + self.responses = responses.ResponsesWithRawResponse(client.responses) class AsyncOpenAIWithRawResponse: @@ -523,9 +535,11 @@ def __init__(self, client: AsyncOpenAI) -> None: self.moderations = moderations.AsyncModerationsWithRawResponse(client.moderations) self.models = models.AsyncModelsWithRawResponse(client.models) self.fine_tuning = fine_tuning.AsyncFineTuningWithRawResponse(client.fine_tuning) + self.vector_stores = vector_stores.AsyncVectorStoresWithRawResponse(client.vector_stores) self.beta = beta.AsyncBetaWithRawResponse(client.beta) self.batches = batches.AsyncBatchesWithRawResponse(client.batches) self.uploads = uploads.AsyncUploadsWithRawResponse(client.uploads) + self.responses = responses.AsyncResponsesWithRawResponse(client.responses) class OpenAIWithStreamedResponse: @@ -539,9 +553,11 @@ def __init__(self, client: OpenAI) -> None: self.moderations = moderations.ModerationsWithStreamingResponse(client.moderations) self.models = models.ModelsWithStreamingResponse(client.models) self.fine_tuning = fine_tuning.FineTuningWithStreamingResponse(client.fine_tuning) + self.vector_stores = vector_stores.VectorStoresWithStreamingResponse(client.vector_stores) self.beta = beta.BetaWithStreamingResponse(client.beta) self.batches = batches.BatchesWithStreamingResponse(client.batches) self.uploads = uploads.UploadsWithStreamingResponse(client.uploads) + self.responses = responses.ResponsesWithStreamingResponse(client.responses) class AsyncOpenAIWithStreamedResponse: @@ -555,9 +571,11 @@ def __init__(self, client: AsyncOpenAI) -> None: self.moderations = moderations.AsyncModerationsWithStreamingResponse(client.moderations) self.models = models.AsyncModelsWithStreamingResponse(client.models) self.fine_tuning = fine_tuning.AsyncFineTuningWithStreamingResponse(client.fine_tuning) + self.vector_stores = vector_stores.AsyncVectorStoresWithStreamingResponse(client.vector_stores) self.beta = beta.AsyncBetaWithStreamingResponse(client.beta) self.batches = batches.AsyncBatchesWithStreamingResponse(client.batches) self.uploads = uploads.AsyncUploadsWithStreamingResponse(client.uploads) + self.responses = responses.AsyncResponsesWithStreamingResponse(client.responses) Client = OpenAI diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 0fda992cff..9cb72ffe17 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -59,7 +59,7 @@ def __stream__(self) -> Iterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None: + if sse.event is None or sse.event.startswith("response."): data = sse.json() if is_mapping(data) and data.get("error"): message = None @@ -161,7 +161,7 @@ async def __stream__(self) -> AsyncIterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None: + if sse.event is None or sse.event.startswith("response."): data = sse.json() if is_mapping(data) and data.get("error"): message = None diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py new file mode 100644 index 0000000000..a189dcf937 --- /dev/null +++ b/src/openai/lib/_parsing/_responses.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +import json +from typing import TYPE_CHECKING, Any, List, Iterable, cast +from typing_extensions import TypeVar, assert_never + +import pydantic + +from .._tools import ResponsesPydanticFunctionTool +from ..._types import NotGiven +from ..._utils import is_given +from ..._compat import PYDANTIC_V2, model_parse_json +from ..._models import construct_type_unchecked +from .._pydantic import is_basemodel_type, is_dataclass_like_type +from ._completions import solve_response_format_t, type_to_response_format_param +from ...types.responses import ( + Response, + ToolParam, + ParsedContent, + ParsedResponse, + FunctionToolParam, + ParsedResponseOutputItem, + ParsedResponseOutputText, + ResponseFunctionToolCall, + ParsedResponseOutputMessage, + ResponseFormatTextConfigParam, + ParsedResponseFunctionToolCall, +) +from ...types.chat.completion_create_params import ResponseFormat + +TextFormatT = TypeVar( + "TextFormatT", + # if it isn't given then we don't do any parsing + default=None, +) + + +def type_to_text_format_param(type_: type) -> ResponseFormatTextConfigParam: + response_format_dict = type_to_response_format_param(type_) + assert is_given(response_format_dict) + response_format_dict = cast(ResponseFormat, response_format_dict) # pyright: ignore[reportUnnecessaryCast] + assert response_format_dict["type"] == "json_schema" + assert "schema" in response_format_dict["json_schema"] + + return { + "type": "json_schema", + "strict": True, + "name": response_format_dict["json_schema"]["name"], + "schema": response_format_dict["json_schema"]["schema"], + } + + +def parse_response( + *, + text_format: type[TextFormatT] | NotGiven, + input_tools: Iterable[ToolParam] | NotGiven | None, + response: Response | ParsedResponse[object], +) -> ParsedResponse[TextFormatT]: + solved_t = solve_response_format_t(text_format) + output_list: List[ParsedResponseOutputItem[TextFormatT]] = [] + + for output in response.output: + if output.type == "message": + content_list: List[ParsedContent[TextFormatT]] = [] + for item in output.content: + if item.type != "output_text": + content_list.append(item) + continue + + content_list.append( + construct_type_unchecked( + type_=cast(Any, ParsedResponseOutputText)[solved_t], + value={ + **item.to_dict(), + "parsed": parse_text(item.text, text_format=text_format), + }, + ) + ) + + output_list.append( + construct_type_unchecked( + type_=cast(Any, ParsedResponseOutputMessage)[solved_t], + value={ + **output.to_dict(), + "content": content_list, + }, + ) + ) + elif output.type == "function_call": + output_list.append( + construct_type_unchecked( + type_=ParsedResponseFunctionToolCall, + value={ + **output.to_dict(), + "parsed_arguments": parse_function_tool_arguments( + input_tools=input_tools, function_call=output + ), + }, + ) + ) + elif ( + output.type == "computer_call" + or output.type == "file_search_call" + or output.type == "web_search_call" + or output.type == "reasoning" + ): + output_list.append(output) + elif TYPE_CHECKING: # type: ignore + assert_never(output) + else: + output_list.append(output) + + return cast( + ParsedResponse[TextFormatT], + construct_type_unchecked( + type_=cast(Any, ParsedResponse)[solved_t], + value={ + **response.to_dict(), + "output": output_list, + }, + ), + ) + + +def parse_text(text: str, text_format: type[TextFormatT] | NotGiven) -> TextFormatT | None: + if not is_given(text_format): + return None + + if is_basemodel_type(text_format): + return cast(TextFormatT, model_parse_json(text_format, text)) + + if is_dataclass_like_type(text_format): + if not PYDANTIC_V2: + raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {text_format}") + + return pydantic.TypeAdapter(text_format).validate_json(text) + + raise TypeError(f"Unable to automatically parse response format type {text_format}") + + +def get_input_tool_by_name(*, input_tools: Iterable[ToolParam], name: str) -> FunctionToolParam | None: + for tool in input_tools: + if tool["type"] == "function" and tool.get("name") == name: + return tool + + return None + + +def parse_function_tool_arguments( + *, + input_tools: Iterable[ToolParam] | NotGiven | None, + function_call: ParsedResponseFunctionToolCall | ResponseFunctionToolCall, +) -> object: + if input_tools is None or not is_given(input_tools): + return None + + input_tool = get_input_tool_by_name(input_tools=input_tools, name=function_call.name) + if not input_tool: + return None + + tool = cast(object, input_tool) + if isinstance(tool, ResponsesPydanticFunctionTool): + return model_parse_json(tool.model, function_call.arguments) + + if not input_tool.get("strict"): + return None + + return json.loads(function_call.arguments) diff --git a/src/openai/lib/_tools.py b/src/openai/lib/_tools.py index 8478ed676c..415d750074 100644 --- a/src/openai/lib/_tools.py +++ b/src/openai/lib/_tools.py @@ -7,6 +7,7 @@ from ._pydantic import to_strict_json_schema from ..types.chat import ChatCompletionToolParam from ..types.shared_params import FunctionDefinition +from ..types.responses.function_tool_param import FunctionToolParam as ResponsesFunctionToolParam class PydanticFunctionTool(Dict[str, Any]): @@ -25,6 +26,17 @@ def cast(self) -> FunctionDefinition: return cast(FunctionDefinition, self) +class ResponsesPydanticFunctionTool(Dict[str, Any]): + model: type[pydantic.BaseModel] + + def __init__(self, tool: ResponsesFunctionToolParam, model: type[pydantic.BaseModel]) -> None: + super().__init__(tool) + self.model = model + + def cast(self) -> ResponsesFunctionToolParam: + return cast(ResponsesFunctionToolParam, self) + + def pydantic_function_tool( model: type[pydantic.BaseModel], *, diff --git a/src/openai/lib/streaming/responses/__init__.py b/src/openai/lib/streaming/responses/__init__.py new file mode 100644 index 0000000000..ff073633bf --- /dev/null +++ b/src/openai/lib/streaming/responses/__init__.py @@ -0,0 +1,13 @@ +from ._events import ( + ResponseTextDoneEvent as ResponseTextDoneEvent, + ResponseTextDeltaEvent as ResponseTextDeltaEvent, + ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, +) +from ._responses import ( + ResponseStream as ResponseStream, + AsyncResponseStream as AsyncResponseStream, + ResponseStreamEvent as ResponseStreamEvent, + ResponseStreamState as ResponseStreamState, + ResponseStreamManager as ResponseStreamManager, + AsyncResponseStreamManager as AsyncResponseStreamManager, +) diff --git a/src/openai/lib/streaming/responses/_events.py b/src/openai/lib/streaming/responses/_events.py new file mode 100644 index 0000000000..fe17edf649 --- /dev/null +++ b/src/openai/lib/streaming/responses/_events.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +from typing import Optional +from typing_extensions import Union, Generic, TypeVar, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._compat import GenericModel +from ....types.responses import ( + ParsedResponse, + ResponseErrorEvent, + ResponseFailedEvent, + ResponseCreatedEvent, + ResponseTextDoneEvent as RawResponseTextDoneEvent, + ResponseAudioDoneEvent, + ResponseCompletedEvent as RawResponseCompletedEvent, + ResponseTextDeltaEvent as RawResponseTextDeltaEvent, + ResponseAudioDeltaEvent, + ResponseIncompleteEvent, + ResponseInProgressEvent, + ResponseRefusalDoneEvent, + ResponseRefusalDeltaEvent, + ResponseOutputItemDoneEvent, + ResponseContentPartDoneEvent, + ResponseOutputItemAddedEvent, + ResponseContentPartAddedEvent, + ResponseAudioTranscriptDoneEvent, + ResponseTextAnnotationDeltaEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallSearchingEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallSearchingEvent, + ResponseWebSearchCallInProgressEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent as RawResponseFunctionCallArgumentsDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, +) + +TextFormatT = TypeVar( + "TextFormatT", + # if it isn't given then we don't do any parsing + default=None, +) + + +class ResponseTextDeltaEvent(RawResponseTextDeltaEvent): + snapshot: str + + +class ResponseTextDoneEvent(RawResponseTextDoneEvent, GenericModel, Generic[TextFormatT]): + parsed: Optional[TextFormatT] = None + + +class ResponseFunctionCallArgumentsDeltaEvent(RawResponseFunctionCallArgumentsDeltaEvent): + snapshot: str + + +class ResponseCompletedEvent(RawResponseCompletedEvent, GenericModel, Generic[TextFormatT]): + response: ParsedResponse[TextFormatT] # type: ignore[assignment] + + +ResponseStreamEvent: TypeAlias = Annotated[ + Union[ + # wrappers with snapshots added on + ResponseTextDeltaEvent, + ResponseTextDoneEvent[TextFormatT], + ResponseFunctionCallArgumentsDeltaEvent, + ResponseCompletedEvent[TextFormatT], + # the same as the non-accumulated API + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseErrorEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseInProgressEvent, + ResponseFailedEvent, + ResponseIncompleteEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseTextAnnotationDeltaEvent, + ResponseTextDoneEvent, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/lib/streaming/responses/_responses.py b/src/openai/lib/streaming/responses/_responses.py new file mode 100644 index 0000000000..f8f4b64174 --- /dev/null +++ b/src/openai/lib/streaming/responses/_responses.py @@ -0,0 +1,354 @@ +from __future__ import annotations + +import inspect +from types import TracebackType +from typing import Any, List, Generic, Iterable, Awaitable, cast +from typing_extensions import Self, Callable, Iterator, AsyncIterator + +from ._types import ParsedResponseSnapshot +from ._events import ( + ResponseStreamEvent, + ResponseTextDoneEvent, + ResponseCompletedEvent, + ResponseTextDeltaEvent, + ResponseFunctionCallArgumentsDeltaEvent, +) +from ...._types import NOT_GIVEN, NotGiven +from ...._utils import is_given, consume_sync_iterator, consume_async_iterator +from ...._models import build, construct_type_unchecked +from ...._streaming import Stream, AsyncStream +from ....types.responses import ParsedResponse, ResponseStreamEvent as RawResponseStreamEvent +from ..._parsing._responses import TextFormatT, parse_text, parse_response +from ....types.responses.tool_param import ToolParam +from ....types.responses.parsed_response import ( + ParsedContent, + ParsedResponseOutputMessage, + ParsedResponseFunctionToolCall, +) + + +class ResponseStream(Generic[TextFormatT]): + def __init__( + self, + *, + raw_stream: Stream[RawResponseStreamEvent], + text_format: type[TextFormatT] | NotGiven, + input_tools: Iterable[ToolParam] | NotGiven, + ) -> None: + self._raw_stream = raw_stream + self._response = raw_stream.response + self._iterator = self.__stream__() + self._state = ResponseStreamState(text_format=text_format, input_tools=input_tools) + + def __next__(self) -> ResponseStreamEvent[TextFormatT]: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[ResponseStreamEvent[TextFormatT]]: + for item in self._iterator: + yield item + + def __enter__(self) -> Self: + return self + + def __stream__(self) -> Iterator[ResponseStreamEvent[TextFormatT]]: + for sse_event in self._raw_stream: + events_to_fire = self._state.handle_event(sse_event) + for event in events_to_fire: + yield event + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self._response.close() + + def get_final_response(self) -> ParsedResponse[TextFormatT]: + """Waits until the stream has been read to completion and returns + the accumulated `ParsedResponse` object. + """ + self.until_done() + response = self._state._completed_response + if not response: + raise RuntimeError("Didn't receive a `response.completed` event.") + + return response + + def until_done(self) -> Self: + """Blocks until the stream has been consumed.""" + consume_sync_iterator(self) + return self + + +class ResponseStreamManager(Generic[TextFormatT]): + def __init__( + self, + api_request: Callable[[], Stream[RawResponseStreamEvent]], + *, + text_format: type[TextFormatT] | NotGiven, + input_tools: Iterable[ToolParam] | NotGiven, + ) -> None: + self.__stream: ResponseStream[TextFormatT] | None = None + self.__api_request = api_request + self.__text_format = text_format + self.__input_tools = input_tools + + def __enter__(self) -> ResponseStream[TextFormatT]: + raw_stream = self.__api_request() + + self.__stream = ResponseStream( + raw_stream=raw_stream, + text_format=self.__text_format, + input_tools=self.__input_tools, + ) + + return self.__stream + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + self.__stream.close() + + +class AsyncResponseStream(Generic[TextFormatT]): + def __init__( + self, + *, + raw_stream: AsyncStream[RawResponseStreamEvent], + text_format: type[TextFormatT] | NotGiven, + input_tools: Iterable[ToolParam] | NotGiven, + ) -> None: + self._raw_stream = raw_stream + self._response = raw_stream.response + self._iterator = self.__stream__() + self._state = ResponseStreamState(text_format=text_format, input_tools=input_tools) + + async def __anext__(self) -> ResponseStreamEvent[TextFormatT]: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[ResponseStreamEvent[TextFormatT]]: + async for item in self._iterator: + yield item + + async def __stream__(self) -> AsyncIterator[ResponseStreamEvent[TextFormatT]]: + async for sse_event in self._raw_stream: + events_to_fire = self._state.handle_event(sse_event) + for event in events_to_fire: + yield event + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self._response.aclose() + + async def get_final_response(self) -> ParsedResponse[TextFormatT]: + """Waits until the stream has been read to completion and returns + the accumulated `ParsedResponse` object. + """ + await self.until_done() + response = self._state._completed_response + if not response: + raise RuntimeError("Didn't receive a `response.completed` event.") + + return response + + async def until_done(self) -> Self: + """Blocks until the stream has been consumed.""" + await consume_async_iterator(self) + return self + + +class AsyncResponseStreamManager(Generic[TextFormatT]): + def __init__( + self, + api_request: Awaitable[AsyncStream[RawResponseStreamEvent]], + *, + text_format: type[TextFormatT] | NotGiven, + input_tools: Iterable[ToolParam] | NotGiven, + ) -> None: + self.__stream: AsyncResponseStream[TextFormatT] | None = None + self.__api_request = api_request + self.__text_format = text_format + self.__input_tools = input_tools + + async def __aenter__(self) -> AsyncResponseStream[TextFormatT]: + raw_stream = await self.__api_request + + self.__stream = AsyncResponseStream( + raw_stream=raw_stream, + text_format=self.__text_format, + input_tools=self.__input_tools, + ) + + return self.__stream + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__stream is not None: + await self.__stream.close() + + +class ResponseStreamState(Generic[TextFormatT]): + def __init__( + self, + *, + input_tools: Iterable[ToolParam] | NotGiven, + text_format: type[TextFormatT] | NotGiven, + ) -> None: + self.__current_snapshot: ParsedResponseSnapshot | None = None + self._completed_response: ParsedResponse[TextFormatT] | None = None + self._input_tools = [tool for tool in input_tools] if is_given(input_tools) else [] + self._text_format = text_format + self._rich_text_format: type | NotGiven = text_format if inspect.isclass(text_format) else NOT_GIVEN + + def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEvent[TextFormatT]]: + self.__current_snapshot = snapshot = self.accumulate_event(event) + + events: List[ResponseStreamEvent[TextFormatT]] = [] + + if event.type == "response.output_text.delta": + output = snapshot.output[event.output_index] + assert output.type == "message" + + content = output.content[event.content_index] + assert content.type == "output_text" + + events.append( + build( + ResponseTextDeltaEvent, + content_index=event.content_index, + delta=event.delta, + item_id=event.item_id, + output_index=event.output_index, + type="response.output_text.delta", + snapshot=content.text, + ) + ) + elif event.type == "response.output_text.done": + output = snapshot.output[event.output_index] + assert output.type == "message" + + content = output.content[event.content_index] + assert content.type == "output_text" + + events.append( + build( + ResponseTextDoneEvent[TextFormatT], + content_index=event.content_index, + item_id=event.item_id, + output_index=event.output_index, + type="response.output_text.done", + text=event.text, + parsed=parse_text(event.text, text_format=self._text_format), + ) + ) + elif event.type == "response.function_call_arguments.delta": + output = snapshot.output[event.output_index] + assert output.type == "function_call" + + events.append( + build( + ResponseFunctionCallArgumentsDeltaEvent, + delta=event.delta, + item_id=event.item_id, + output_index=event.output_index, + type="response.function_call_arguments.delta", + snapshot=output.arguments, + ) + ) + + elif event.type == "response.completed": + response = self._completed_response + assert response is not None + + events.append( + build( + ResponseCompletedEvent, + type="response.completed", + response=response, + ) + ) + else: + events.append(event) + + return events + + def accumulate_event(self, event: RawResponseStreamEvent) -> ParsedResponseSnapshot: + snapshot = self.__current_snapshot + if snapshot is None: + return self._create_initial_response(event) + + if event.type == "response.output_item.added": + if event.item.type == "function_call": + snapshot.output.append( + construct_type_unchecked( + type_=cast(Any, ParsedResponseFunctionToolCall), value=event.item.to_dict() + ) + ) + elif event.item.type == "message": + snapshot.output.append( + construct_type_unchecked(type_=cast(Any, ParsedResponseOutputMessage), value=event.item.to_dict()) + ) + else: + snapshot.output.append(event.item) + elif event.type == "response.content_part.added": + output = snapshot.output[event.output_index] + if output.type == "message": + output.content.append( + construct_type_unchecked(type_=cast(Any, ParsedContent), value=event.part.to_dict()) + ) + elif event.type == "response.output_text.delta": + output = snapshot.output[event.output_index] + if output.type == "message": + content = output.content[event.content_index] + assert content.type == "output_text" + content.text += event.delta + elif event.type == "response.function_call_arguments.delta": + output = snapshot.output[event.output_index] + if output.type == "function_call": + output.arguments += event.delta + elif event.type == "response.completed": + self._completed_response = parse_response( + text_format=self._text_format, + response=event.response, + input_tools=self._input_tools, + ) + + return snapshot + + def _create_initial_response(self, event: RawResponseStreamEvent) -> ParsedResponseSnapshot: + if event.type != "response.created": + raise RuntimeError(f"Expected to have received `response.created` before `{event.type}`") + + return construct_type_unchecked(type_=ParsedResponseSnapshot, value=event.response.to_dict()) diff --git a/src/openai/lib/streaming/responses/_types.py b/src/openai/lib/streaming/responses/_types.py new file mode 100644 index 0000000000..6d3fd90e40 --- /dev/null +++ b/src/openai/lib/streaming/responses/_types.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from typing_extensions import TypeAlias + +from ....types.responses import ParsedResponse + +ParsedResponseSnapshot: TypeAlias = ParsedResponse[object] +"""Snapshot type representing an in-progress accumulation of +a `ParsedResponse` object. +""" diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index e2cc1c4b0c..d3457cf319 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -64,6 +64,14 @@ UploadsWithStreamingResponse, AsyncUploadsWithStreamingResponse, ) +from .responses import ( + Responses, + AsyncResponses, + ResponsesWithRawResponse, + AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse, +) from .embeddings import ( Embeddings, AsyncEmbeddings, @@ -96,6 +104,14 @@ ModerationsWithStreamingResponse, AsyncModerationsWithStreamingResponse, ) +from .vector_stores import ( + VectorStores, + AsyncVectorStores, + VectorStoresWithRawResponse, + AsyncVectorStoresWithRawResponse, + VectorStoresWithStreamingResponse, + AsyncVectorStoresWithStreamingResponse, +) __all__ = [ "Completions", @@ -152,6 +168,12 @@ "AsyncFineTuningWithRawResponse", "FineTuningWithStreamingResponse", "AsyncFineTuningWithStreamingResponse", + "VectorStores", + "AsyncVectorStores", + "VectorStoresWithRawResponse", + "AsyncVectorStoresWithRawResponse", + "VectorStoresWithStreamingResponse", + "AsyncVectorStoresWithStreamingResponse", "Beta", "AsyncBeta", "BetaWithRawResponse", @@ -170,4 +192,10 @@ "AsyncUploadsWithRawResponse", "UploadsWithStreamingResponse", "AsyncUploadsWithStreamingResponse", + "Responses", + "AsyncResponses", + "ResponsesWithRawResponse", + "AsyncResponsesWithRawResponse", + "ResponsesWithStreamingResponse", + "AsyncResponsesWithStreamingResponse", ] diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 01f5338757..87fea25267 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -24,22 +24,8 @@ AssistantsWithStreamingResponse, AsyncAssistantsWithStreamingResponse, ) -from .vector_stores import ( - VectorStores, - AsyncVectorStores, - VectorStoresWithRawResponse, - AsyncVectorStoresWithRawResponse, - VectorStoresWithStreamingResponse, - AsyncVectorStoresWithStreamingResponse, -) __all__ = [ - "VectorStores", - "AsyncVectorStores", - "VectorStoresWithRawResponse", - "AsyncVectorStoresWithRawResponse", - "VectorStoresWithStreamingResponse", - "AsyncVectorStoresWithStreamingResponse", "Assistants", "AsyncAssistants", "AssistantsWithRawResponse", diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index ffecd8f9e9..1c7cbf3737 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -27,6 +27,7 @@ from ...types.shared.chat_model import ChatModel from ...types.beta.assistant_deleted import AssistantDeleted from ...types.shared_params.metadata import Metadata +from ...types.shared.reasoning_effort import ReasoningEffort from ...types.beta.assistant_tool_param import AssistantToolParam from ...types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -61,7 +62,7 @@ def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_resources: Optional[assistant_create_params.ToolResources] | NotGiven = NOT_GIVEN, @@ -98,7 +99,7 @@ def create( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -256,7 +257,7 @@ def update( ] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_resources: Optional[assistant_update_params.ToolResources] | NotGiven = NOT_GIVEN, @@ -294,7 +295,7 @@ def update( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -504,7 +505,7 @@ async def create( instructions: Optional[str] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_resources: Optional[assistant_create_params.ToolResources] | NotGiven = NOT_GIVEN, @@ -541,7 +542,7 @@ async def create( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -699,7 +700,7 @@ async def update( ] | NotGiven = NOT_GIVEN, name: Optional[str] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_resources: Optional[assistant_update_params.ToolResources] | NotGiven = NOT_GIVEN, @@ -737,7 +738,7 @@ async def update( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 5d71cff3f1..62fc8258b9 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -29,14 +29,6 @@ RealtimeWithStreamingResponse, AsyncRealtimeWithStreamingResponse, ) -from .vector_stores.vector_stores import ( - VectorStores, - AsyncVectorStores, - VectorStoresWithRawResponse, - AsyncVectorStoresWithRawResponse, - VectorStoresWithStreamingResponse, - AsyncVectorStoresWithStreamingResponse, -) __all__ = ["Beta", "AsyncBeta"] @@ -50,10 +42,6 @@ def chat(self) -> Chat: def realtime(self) -> Realtime: return Realtime(self._client) - @cached_property - def vector_stores(self) -> VectorStores: - return VectorStores(self._client) - @cached_property def assistants(self) -> Assistants: return Assistants(self._client) @@ -91,10 +79,6 @@ def chat(self) -> AsyncChat: def realtime(self) -> AsyncRealtime: return AsyncRealtime(self._client) - @cached_property - def vector_stores(self) -> AsyncVectorStores: - return AsyncVectorStores(self._client) - @cached_property def assistants(self) -> AsyncAssistants: return AsyncAssistants(self._client) @@ -131,10 +115,6 @@ def __init__(self, beta: Beta) -> None: def realtime(self) -> RealtimeWithRawResponse: return RealtimeWithRawResponse(self._beta.realtime) - @cached_property - def vector_stores(self) -> VectorStoresWithRawResponse: - return VectorStoresWithRawResponse(self._beta.vector_stores) - @cached_property def assistants(self) -> AssistantsWithRawResponse: return AssistantsWithRawResponse(self._beta.assistants) @@ -152,10 +132,6 @@ def __init__(self, beta: AsyncBeta) -> None: def realtime(self) -> AsyncRealtimeWithRawResponse: return AsyncRealtimeWithRawResponse(self._beta.realtime) - @cached_property - def vector_stores(self) -> AsyncVectorStoresWithRawResponse: - return AsyncVectorStoresWithRawResponse(self._beta.vector_stores) - @cached_property def assistants(self) -> AsyncAssistantsWithRawResponse: return AsyncAssistantsWithRawResponse(self._beta.assistants) @@ -173,10 +149,6 @@ def __init__(self, beta: Beta) -> None: def realtime(self) -> RealtimeWithStreamingResponse: return RealtimeWithStreamingResponse(self._beta.realtime) - @cached_property - def vector_stores(self) -> VectorStoresWithStreamingResponse: - return VectorStoresWithStreamingResponse(self._beta.vector_stores) - @cached_property def assistants(self) -> AssistantsWithStreamingResponse: return AssistantsWithStreamingResponse(self._beta.assistants) @@ -194,10 +166,6 @@ def __init__(self, beta: AsyncBeta) -> None: def realtime(self) -> AsyncRealtimeWithStreamingResponse: return AsyncRealtimeWithStreamingResponse(self._beta.realtime) - @cached_property - def vector_stores(self) -> AsyncVectorStoresWithStreamingResponse: - return AsyncVectorStoresWithStreamingResponse(self._beta.vector_stores) - @cached_property def assistants(self) -> AsyncAssistantsWithStreamingResponse: return AsyncAssistantsWithStreamingResponse(self._beta.assistants) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 0c631b9821..545a3f4087 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -15,10 +15,7 @@ from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...._streaming import Stream -from ....types.chat import ( - ChatCompletionReasoningEffort, - completion_create_params, -) +from ....types.chat import completion_create_params from ...._base_client import make_request_options from ....lib._parsing import ( ResponseFormatT, @@ -28,11 +25,10 @@ ) from ....types.chat_model import ChatModel from ....lib.streaming.chat import ChatCompletionStreamManager, AsyncChatCompletionStreamManager -from ....types.shared_params import Metadata +from ....types.shared_params import Metadata, ReasoningEffort from ....types.chat.chat_completion import ChatCompletion from ....types.chat.chat_completion_chunk import ChatCompletionChunk from ....types.chat.parsed_chat_completion import ParsedChatCompletion -from ....types.chat.chat_completion_modality import ChatCompletionModality from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam from ....types.chat.chat_completion_audio_param import ChatCompletionAudioParam from ....types.chat.chat_completion_message_param import ChatCompletionMessageParam @@ -78,15 +74,15 @@ def parse( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -95,6 +91,7 @@ def parse( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -192,6 +189,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "top_logprobs": top_logprobs, "top_p": top_p, "user": user, + "web_search_options": web_search_options, }, completion_create_params.CompletionCreateParams, ), @@ -223,15 +221,15 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -240,6 +238,7 @@ def stream( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -306,6 +305,7 @@ def stream( top_logprobs=top_logprobs, top_p=top_p, user=user, + web_search_options=web_search_options, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, @@ -353,15 +353,15 @@ async def parse( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -370,6 +370,7 @@ async def parse( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -467,6 +468,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "top_logprobs": top_logprobs, "top_p": top_p, "user": user, + "web_search_options": web_search_options, }, completion_create_params.CompletionCreateParams, ), @@ -498,15 +500,15 @@ def stream( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -515,6 +517,7 @@ def stream( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -586,6 +589,7 @@ def stream( extra_query=extra_query, extra_body=extra_body, timeout=timeout, + web_search_options=web_search_options, ) return AsyncChatCompletionStreamManager( api_request, diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index b819678be6..acb1c9b261 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -48,6 +48,7 @@ from .....types.beta.threads.run import Run from .....types.shared.chat_model import ChatModel from .....types.shared_params.metadata import Metadata +from .....types.shared.reasoning_effort import ReasoningEffort from .....types.beta.assistant_tool_param import AssistantToolParam from .....types.beta.assistant_stream_event import AssistantStreamEvent from .....types.beta.threads.runs.run_step_include import RunStepInclude @@ -96,7 +97,7 @@ def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -165,7 +166,7 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -247,7 +248,7 @@ def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -319,7 +320,7 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -397,7 +398,7 @@ def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -469,7 +470,7 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -546,7 +547,7 @@ def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -800,7 +801,7 @@ def create_and_poll( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -871,7 +872,7 @@ def create_and_stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -903,7 +904,7 @@ def create_and_stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -935,7 +936,7 @@ def create_and_stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1054,7 +1055,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1086,7 +1087,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1118,7 +1119,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1505,7 +1506,7 @@ async def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1574,7 +1575,7 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1656,7 +1657,7 @@ async def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1728,7 +1729,7 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1806,7 +1807,7 @@ async def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -1878,7 +1879,7 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1955,7 +1956,7 @@ async def create( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -2209,7 +2210,7 @@ async def create_and_poll( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2460,7 +2461,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2492,7 +2493,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, @@ -2524,7 +2525,7 @@ def stream( metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 708b1ff166..d28be012c9 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -31,7 +31,6 @@ from ....pagination import SyncCursorPage, AsyncCursorPage from ....types.chat import ( ChatCompletionAudioParam, - ChatCompletionReasoningEffort, completion_list_params, completion_create_params, completion_update_params, @@ -40,13 +39,12 @@ from ....types.shared.chat_model import ChatModel from ....types.chat.chat_completion import ChatCompletion from ....types.shared_params.metadata import Metadata +from ....types.shared.reasoning_effort import ReasoningEffort from ....types.chat.chat_completion_chunk import ChatCompletionChunk from ....types.chat.chat_completion_deleted import ChatCompletionDeleted -from ....types.chat.chat_completion_modality import ChatCompletionModality from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam from ....types.chat.chat_completion_audio_param import ChatCompletionAudioParam from ....types.chat.chat_completion_message_param import ChatCompletionMessageParam -from ....types.chat.chat_completion_reasoning_effort import ChatCompletionReasoningEffort from ....types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from ....types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam from ....types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam @@ -93,16 +91,16 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -112,6 +110,7 @@ def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -119,9 +118,15 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion: - """Creates a model response for the given chat conversation. + """ + **Starting a new project?** We recommend trying + [Responses](https://platform.openai.com/docs/api-reference/responses) to take + advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses). + + --- - Learn more in the + Creates a model response for the given chat conversation. Learn more in the [text generation](https://platform.openai.com/docs/guides/text-generation), [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. @@ -140,9 +145,11 @@ def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) - table for details on which models work with the Chat API. + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. audio: Parameters for audio output. Required when audio output is requested with `modalities: ["audio"]`. @@ -203,8 +210,8 @@ def create( Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. - modalities: Output types that you would like the model to generate for this request. Most - models are capable of generating text, which is the default: + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: `["text"]` @@ -229,7 +236,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -243,16 +250,9 @@ def create( in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. Without this, the model may - generate an unending stream of whitespace until the generation reaches the token - limit, resulting in a long-running and seemingly "stuck" request. Also note that - the message content may be partially cut off if `finish_reason="length"`, which - indicates the generation exceeded `max_tokens` or the conversation exceeded the - max context length. + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -267,23 +267,29 @@ def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarantee. + latency guarentee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarantee. + tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - stop: Up to 4 sequences where the API will stop generating further tokens. + When this parameter is set, the response body will include the `service_tier` + utilized. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available, with the stream terminated by a `data: [DONE]` - message. - [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming) + for more information, along with the + [streaming responses](https://platform.openai.com/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -320,6 +326,10 @@ def create( and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -346,16 +356,16 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -364,6 +374,7 @@ def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -371,9 +382,15 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Stream[ChatCompletionChunk]: - """Creates a model response for the given chat conversation. + """ + **Starting a new project?** We recommend trying + [Responses](https://platform.openai.com/docs/api-reference/responses) to take + advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses). + + --- - Learn more in the + Creates a model response for the given chat conversation. Learn more in the [text generation](https://platform.openai.com/docs/guides/text-generation), [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. @@ -392,16 +409,20 @@ def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) - table for details on which models work with the Chat API. - - stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available, with the stream terminated by a `data: [DONE]` - message. - [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming) + for more information, along with the + [streaming responses](https://platform.openai.com/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. audio: Parameters for audio output. Required when audio output is requested with `modalities: ["audio"]`. @@ -462,8 +483,8 @@ def create( Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. - modalities: Output types that you would like the model to generate for this request. Most - models are capable of generating text, which is the default: + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: `["text"]` @@ -488,7 +509,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -502,16 +523,9 @@ def create( in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. Without this, the model may - generate an unending stream of whitespace until the generation reaches the token - limit, resulting in a long-running and seemingly "stuck" request. Also note that - the message content may be partially cut off if `finish_reason="length"`, which - indicates the generation exceeded `max_tokens` or the conversation exceeded the - max context length. + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -526,12 +540,16 @@ def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarantee. + latency guarentee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarantee. + tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - stop: Up to 4 sequences where the API will stop generating further tokens. + When this parameter is set, the response body will include the `service_tier` + utilized. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in our [model distillation](https://platform.openai.com/docs/guides/distillation) @@ -572,6 +590,10 @@ def create( and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -598,16 +620,16 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -616,6 +638,7 @@ def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -623,9 +646,15 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | Stream[ChatCompletionChunk]: - """Creates a model response for the given chat conversation. + """ + **Starting a new project?** We recommend trying + [Responses](https://platform.openai.com/docs/api-reference/responses) to take + advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses). + + --- - Learn more in the + Creates a model response for the given chat conversation. Learn more in the [text generation](https://platform.openai.com/docs/guides/text-generation), [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. @@ -644,16 +673,20 @@ def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) - table for details on which models work with the Chat API. - - stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available, with the stream terminated by a `data: [DONE]` - message. - [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming) + for more information, along with the + [streaming responses](https://platform.openai.com/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. audio: Parameters for audio output. Required when audio output is requested with `modalities: ["audio"]`. @@ -714,8 +747,8 @@ def create( Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. - modalities: Output types that you would like the model to generate for this request. Most - models are capable of generating text, which is the default: + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: `["text"]` @@ -740,7 +773,7 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -754,16 +787,9 @@ def create( in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. Without this, the model may - generate an unending stream of whitespace until the generation reaches the token - limit, resulting in a long-running and seemingly "stuck" request. Also note that - the message content may be partially cut off if `finish_reason="length"`, which - indicates the generation exceeded `max_tokens` or the conversation exceeded the - max context length. + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -778,12 +804,16 @@ def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarantee. + latency guarentee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarantee. + tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - stop: Up to 4 sequences where the API will stop generating further tokens. + When this parameter is set, the response body will include the `service_tier` + utilized. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in our [model distillation](https://platform.openai.com/docs/guides/distillation) @@ -824,6 +854,10 @@ def create( and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -849,16 +883,16 @@ def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -868,6 +902,7 @@ def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -910,6 +945,7 @@ def create( "top_logprobs": top_logprobs, "top_p": top_p, "user": user, + "web_search_options": web_search_options, }, completion_create_params.CompletionCreateParams, ), @@ -934,7 +970,7 @@ def retrieve( ) -> ChatCompletion: """Get a stored chat completion. - Only chat completions that have been created with + Only Chat Completions that have been created with the `store` parameter set to `true` will be returned. Args: @@ -970,7 +1006,7 @@ def update( ) -> ChatCompletion: """Modify a stored chat completion. - Only chat completions that have been created + Only Chat Completions that have been created with the `store` parameter set to `true` can be modified. Currently, the only supported modification is to update the `metadata` field. @@ -1016,24 +1052,24 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[ChatCompletion]: - """List stored chat completions. + """List stored Chat Completions. - Only chat completions that have been stored with + Only Chat Completions that have been stored with the `store` parameter set to `true` will be returned. Args: after: Identifier for the last chat completion from the previous pagination request. - limit: Number of chat completions to retrieve. + limit: Number of Chat Completions to retrieve. metadata: - A list of metadata keys to filter the chat completions by. Example: + A list of metadata keys to filter the Chat Completions by. Example: `metadata[key1]=value1&metadata[key2]=value2` - model: The model used to generate the chat completions. + model: The model used to generate the Chat Completions. - order: Sort order for chat completions by timestamp. Use `asc` for ascending order or + order: Sort order for Chat Completions by timestamp. Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. extra_headers: Send extra headers @@ -1079,7 +1115,7 @@ def delete( ) -> ChatCompletionDeleted: """Delete a stored chat completion. - Only chat completions that have been created + Only Chat Completions that have been created with the `store` parameter set to `true` can be deleted. Args: @@ -1141,16 +1177,16 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -1160,6 +1196,7 @@ async def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1167,9 +1204,15 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion: - """Creates a model response for the given chat conversation. + """ + **Starting a new project?** We recommend trying + [Responses](https://platform.openai.com/docs/api-reference/responses) to take + advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses). + + --- - Learn more in the + Creates a model response for the given chat conversation. Learn more in the [text generation](https://platform.openai.com/docs/guides/text-generation), [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. @@ -1188,9 +1231,11 @@ async def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) - table for details on which models work with the Chat API. + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. audio: Parameters for audio output. Required when audio output is requested with `modalities: ["audio"]`. @@ -1251,8 +1296,8 @@ async def create( Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. - modalities: Output types that you would like the model to generate for this request. Most - models are capable of generating text, which is the default: + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: `["text"]` @@ -1277,7 +1322,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1291,16 +1336,9 @@ async def create( in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. Without this, the model may - generate an unending stream of whitespace until the generation reaches the token - limit, resulting in a long-running and seemingly "stuck" request. Also note that - the message content may be partially cut off if `finish_reason="length"`, which - indicates the generation exceeded `max_tokens` or the conversation exceeded the - max context length. + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -1315,23 +1353,29 @@ async def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarantee. + latency guarentee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarantee. + tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - stop: Up to 4 sequences where the API will stop generating further tokens. + When this parameter is set, the response body will include the `service_tier` + utilized. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available, with the stream terminated by a `data: [DONE]` - message. - [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming) + for more information, along with the + [streaming responses](https://platform.openai.com/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -1368,6 +1412,10 @@ async def create( and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1394,16 +1442,16 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1412,6 +1460,7 @@ async def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1419,9 +1468,15 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncStream[ChatCompletionChunk]: - """Creates a model response for the given chat conversation. + """ + **Starting a new project?** We recommend trying + [Responses](https://platform.openai.com/docs/api-reference/responses) to take + advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses). + + --- - Learn more in the + Creates a model response for the given chat conversation. Learn more in the [text generation](https://platform.openai.com/docs/guides/text-generation), [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. @@ -1440,16 +1495,20 @@ async def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) - table for details on which models work with the Chat API. - - stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available, with the stream terminated by a `data: [DONE]` - message. - [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming) + for more information, along with the + [streaming responses](https://platform.openai.com/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. audio: Parameters for audio output. Required when audio output is requested with `modalities: ["audio"]`. @@ -1510,8 +1569,8 @@ async def create( Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. - modalities: Output types that you would like the model to generate for this request. Most - models are capable of generating text, which is the default: + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: `["text"]` @@ -1536,7 +1595,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1550,16 +1609,9 @@ async def create( in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. Without this, the model may - generate an unending stream of whitespace until the generation reaches the token - limit, resulting in a long-running and seemingly "stuck" request. Also note that - the message content may be partially cut off if `finish_reason="length"`, which - indicates the generation exceeded `max_tokens` or the conversation exceeded the - max context length. + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -1574,12 +1626,16 @@ async def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarantee. + latency guarentee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarantee. + tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - stop: Up to 4 sequences where the API will stop generating further tokens. + When this parameter is set, the response body will include the `service_tier` + utilized. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in our [model distillation](https://platform.openai.com/docs/guides/distillation) @@ -1620,6 +1676,10 @@ async def create( and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1646,16 +1706,16 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1664,6 +1724,7 @@ async def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1671,9 +1732,15 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: - """Creates a model response for the given chat conversation. + """ + **Starting a new project?** We recommend trying + [Responses](https://platform.openai.com/docs/api-reference/responses) to take + advantage of the latest OpenAI platform features. Compare + [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses). + + --- - Learn more in the + Creates a model response for the given chat conversation. Learn more in the [text generation](https://platform.openai.com/docs/guides/text-generation), [vision](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio) guides. @@ -1692,16 +1759,20 @@ async def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: ID of the model to use. See the - [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) - table for details on which models work with the Chat API. - - stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be - sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available, with the stream terminated by a `data: [DONE]` - message. - [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming) + for more information, along with the + [streaming responses](https://platform.openai.com/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. audio: Parameters for audio output. Required when audio output is requested with `modalities: ["audio"]`. @@ -1762,8 +1833,8 @@ async def create( Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. - modalities: Output types that you would like the model to generate for this request. Most - models are capable of generating text, which is the default: + modalities: Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: `["text"]` @@ -1788,7 +1859,7 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. - reasoning_effort: **o1 and o3-mini models only** + reasoning_effort: **o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1802,16 +1873,9 @@ async def create( in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. Without this, the model may - generate an unending stream of whitespace until the generation reaches the token - limit, resulting in a long-running and seemingly "stuck" request. Also note that - the message content may be partially cut off if `finish_reason="length"`, which - indicates the generation exceeded `max_tokens` or the conversation exceeded the - max context length. + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and @@ -1826,12 +1890,16 @@ async def create( utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarantee. + latency guarentee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarantee. + tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. - stop: Up to 4 sequences where the API will stop generating further tokens. + When this parameter is set, the response body will include the `service_tier` + utilized. + + stop: Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in our [model distillation](https://platform.openai.com/docs/guides/distillation) @@ -1872,6 +1940,10 @@ async def create( and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + web_search_options: This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1897,16 +1969,16 @@ async def create( max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, max_tokens: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[ChatCompletionModality]] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ChatCompletionReasoningEffort] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -1916,6 +1988,7 @@ async def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1958,6 +2031,7 @@ async def create( "top_logprobs": top_logprobs, "top_p": top_p, "user": user, + "web_search_options": web_search_options, }, completion_create_params.CompletionCreateParams, ), @@ -1982,7 +2056,7 @@ async def retrieve( ) -> ChatCompletion: """Get a stored chat completion. - Only chat completions that have been created with + Only Chat Completions that have been created with the `store` parameter set to `true` will be returned. Args: @@ -2018,7 +2092,7 @@ async def update( ) -> ChatCompletion: """Modify a stored chat completion. - Only chat completions that have been created + Only Chat Completions that have been created with the `store` parameter set to `true` can be modified. Currently, the only supported modification is to update the `metadata` field. @@ -2064,24 +2138,24 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[ChatCompletion, AsyncCursorPage[ChatCompletion]]: - """List stored chat completions. + """List stored Chat Completions. - Only chat completions that have been stored with + Only Chat Completions that have been stored with the `store` parameter set to `true` will be returned. Args: after: Identifier for the last chat completion from the previous pagination request. - limit: Number of chat completions to retrieve. + limit: Number of Chat Completions to retrieve. metadata: - A list of metadata keys to filter the chat completions by. Example: + A list of metadata keys to filter the Chat Completions by. Example: `metadata[key1]=value1&metadata[key2]=value2` - model: The model used to generate the chat completions. + model: The model used to generate the Chat Completions. - order: Sort order for chat completions by timestamp. Use `asc` for ascending order or + order: Sort order for Chat Completions by timestamp. Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. extra_headers: Send extra headers @@ -2127,7 +2201,7 @@ async def delete( ) -> ChatCompletionDeleted: """Delete a stored chat completion. - Only chat completions that have been created + Only Chat Completions that have been created with the `store` parameter set to `true` can be deleted. Args: diff --git a/src/openai/resources/chat/completions/messages.py b/src/openai/resources/chat/completions/messages.py index b71d670927..fac15fba8b 100644 --- a/src/openai/resources/chat/completions/messages.py +++ b/src/openai/resources/chat/completions/messages.py @@ -56,7 +56,7 @@ def list( ) -> SyncCursorPage[ChatCompletionStoreMessage]: """Get the messages in a stored chat completion. - Only chat completions that have + Only Chat Completions that have been created with the `store` parameter set to `true` will be returned. Args: @@ -134,7 +134,7 @@ def list( ) -> AsyncPaginator[ChatCompletionStoreMessage, AsyncCursorPage[ChatCompletionStoreMessage]]: """Get the messages in a stored chat completion. - Only chat completions that have + Only Chat Completions that have been created with the `store` parameter set to `true` will be returned. Args: diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index af453e1e21..2eaa4a6401 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -96,14 +96,10 @@ def create( Args: file: The File object (not file name) to be uploaded. - purpose: The intended purpose of the uploaded file. - - Use "assistants" for - [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Message](https://platform.openai.com/docs/api-reference/messages) files, - "vision" for Assistants image file inputs, "batch" for - [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for - [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + purpose: The intended purpose of the uploaded file. One of: - `assistants`: Used in the + Assistants API - `batch`: Used in the Batch API - `fine-tune`: Used for + fine-tuning - `vision`: Images used for vision fine-tuning - `user_data`: + Flexible file type for any purpose - `evals`: Used for eval data sets extra_headers: Send extra headers @@ -412,14 +408,10 @@ async def create( Args: file: The File object (not file name) to be uploaded. - purpose: The intended purpose of the uploaded file. - - Use "assistants" for - [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Message](https://platform.openai.com/docs/api-reference/messages) files, - "vision" for Assistants image file inputs, "batch" for - [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for - [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + purpose: The intended purpose of the uploaded file. One of: - `assistants`: Used in the + Assistants API - `batch`: Used in the Batch API - `fine-tune`: Used for + fine-tuning - `vision`: Images used for vision fine-tuning - `user_data`: + Flexible file type for any purpose - `evals`: Used for eval data sets extra_headers: Send extra headers diff --git a/src/openai/resources/responses/__init__.py b/src/openai/resources/responses/__init__.py new file mode 100644 index 0000000000..ad19218b01 --- /dev/null +++ b/src/openai/resources/responses/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .responses import ( + Responses, + AsyncResponses, + ResponsesWithRawResponse, + AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse, +) +from .input_items import ( + InputItems, + AsyncInputItems, + InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse, +) + +__all__ = [ + "InputItems", + "AsyncInputItems", + "InputItemsWithRawResponse", + "AsyncInputItemsWithRawResponse", + "InputItemsWithStreamingResponse", + "AsyncInputItemsWithStreamingResponse", + "Responses", + "AsyncResponses", + "ResponsesWithRawResponse", + "AsyncResponsesWithRawResponse", + "ResponsesWithStreamingResponse", + "AsyncResponsesWithStreamingResponse", +] diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py new file mode 100644 index 0000000000..10e7d545dc --- /dev/null +++ b/src/openai/resources/responses/input_items.py @@ -0,0 +1,223 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, cast +from typing_extensions import Literal + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.responses import input_item_list_params +from ...types.responses.response_item_list import Data + +__all__ = ["InputItems", "AsyncInputItems"] + + +class InputItems(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InputItemsWithStreamingResponse(self) + + def list( + self, + response_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[Data]: + """ + Returns a list of input items for a given response. + + Args: + after: An item ID to list items after, used in pagination. + + before: An item ID to list items before, used in pagination. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `asc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._get_api_list( + f"/responses/{response_id}/input_items", + page=SyncCursorPage[Data], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + input_item_list_params.InputItemListParams, + ), + ), + model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncInputItems(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInputItemsWithStreamingResponse(self) + + def list( + self, + response_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + before: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Data, AsyncCursorPage[Data]]: + """ + Returns a list of input items for a given response. + + Args: + after: An item ID to list items after, used in pagination. + + before: An item ID to list items before, used in pagination. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `asc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._get_api_list( + f"/responses/{response_id}/input_items", + page=AsyncCursorPage[Data], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + input_item_list_params.InputItemListParams, + ), + ), + model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system + ) + + +class InputItemsWithRawResponse: + def __init__(self, input_items: InputItems) -> None: + self._input_items = input_items + + self.list = _legacy_response.to_raw_response_wrapper( + input_items.list, + ) + + +class AsyncInputItemsWithRawResponse: + def __init__(self, input_items: AsyncInputItems) -> None: + self._input_items = input_items + + self.list = _legacy_response.async_to_raw_response_wrapper( + input_items.list, + ) + + +class InputItemsWithStreamingResponse: + def __init__(self, input_items: InputItems) -> None: + self._input_items = input_items + + self.list = to_streamed_response_wrapper( + input_items.list, + ) + + +class AsyncInputItemsWithStreamingResponse: + def __init__(self, input_items: AsyncInputItems) -> None: + self._input_items = input_items + + self.list = async_to_streamed_response_wrapper( + input_items.list, + ) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py new file mode 100644 index 0000000000..0c70a2ef22 --- /dev/null +++ b/src/openai/resources/responses/responses.py @@ -0,0 +1,1790 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, List, Type, Union, Iterable, Optional, cast +from functools import partial +from typing_extensions import Literal, overload + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._utils import ( + is_given, + required_args, + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .input_items import ( + InputItems, + AsyncInputItems, + InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse, +) +from ..._streaming import Stream, AsyncStream +from ...lib._tools import PydanticFunctionTool, ResponsesPydanticFunctionTool +from ..._base_client import make_request_options +from ...types.responses import response_create_params, response_retrieve_params +from ...lib._parsing._responses import ( + TextFormatT, + parse_response, + type_to_text_format_param as _type_to_text_format_param, +) +from ...types.shared.chat_model import ChatModel +from ...types.responses.response import Response +from ...types.responses.tool_param import ToolParam, ParseableToolParam +from ...types.shared_params.metadata import Metadata +from ...types.shared_params.reasoning import Reasoning +from ...types.responses.parsed_response import ParsedResponse +from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager +from ...types.responses.response_includable import ResponseIncludable +from ...types.responses.response_input_param import ResponseInputParam +from ...types.responses.response_stream_event import ResponseStreamEvent +from ...types.responses.response_text_config_param import ResponseTextConfigParam + +__all__ = ["Responses", "AsyncResponses"] + + +class Responses(SyncAPIResource): + @cached_property + def input_items(self) -> InputItems: + return InputItems(self._client) + + @cached_property + def with_raw_response(self) -> ResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ResponsesWithStreamingResponse(self) + + @overload + def create( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + stream: Literal[True], + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + stream: bool, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | Stream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["input", "model"], ["input", "model", "stream"]) + def create( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | Stream[ResponseStreamEvent]: + return self._post( + "/responses", + body=maybe_transform( + { + "input": input, + "model": model, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "metadata": metadata, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "store": store, + "stream": stream, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Response, + stream=stream or False, + stream_cls=Stream[ResponseStreamEvent], + ) + + def stream( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, + tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ResponseStreamManager[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + api_request: partial[Stream[ResponseStreamEvent]] = partial( + self.create, + input=input, + model=model, + tools=tools, + include=include, + instructions=instructions, + max_output_tokens=max_output_tokens, + metadata=metadata, + parallel_tool_calls=parallel_tool_calls, + previous_response_id=previous_response_id, + store=store, + stream=True, + temperature=temperature, + text=text, + tool_choice=tool_choice, + reasoning=reasoning, + top_p=top_p, + truncation=truncation, + user=user, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return ResponseStreamManager( + api_request, + text_format=text_format, + input_tools=tools, + ) + + def parse( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, + tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedResponse[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: + return parse_response( + input_tools=tools, + text_format=text_format, + response=raw_response, + ) + + return self._post( + "/responses", + body=maybe_transform( + { + "input": input, + "model": model, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "metadata": metadata, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "store": store, + "stream": stream, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + ), + # we turn the `Response` instance into a `ParsedResponse` + # in the `parser` function above + cast_to=cast(Type[ParsedResponse[TextFormatT]], Response), + ) + + def retrieve( + self, + response_id: str, + *, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._get( + f"/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, response_retrieve_params.ResponseRetrieveParams), + ), + cast_to=Response, + ) + + def delete( + self, + response_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncResponses(AsyncAPIResource): + @cached_property + def input_items(self) -> AsyncInputItems: + return AsyncInputItems(self._client) + + @cached_property + def with_raw_response(self) -> AsyncResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncResponsesWithStreamingResponse(self) + + @overload + async def create( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + stream: Literal[True], + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + stream: bool, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | AsyncStream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["input", "model"], ["input", "model", "stream"]) + async def create( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | AsyncStream[ResponseStreamEvent]: + return await self._post( + "/responses", + body=await async_maybe_transform( + { + "input": input, + "model": model, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "metadata": metadata, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "store": store, + "stream": stream, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Response, + stream=stream or False, + stream_cls=AsyncStream[ResponseStreamEvent], + ) + + def stream( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, + tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncResponseStreamManager[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + api_request = self.create( + input=input, + model=model, + tools=tools, + include=include, + instructions=instructions, + max_output_tokens=max_output_tokens, + metadata=metadata, + parallel_tool_calls=parallel_tool_calls, + previous_response_id=previous_response_id, + store=store, + stream=True, + temperature=temperature, + text=text, + tool_choice=tool_choice, + reasoning=reasoning, + top_p=top_p, + truncation=truncation, + user=user, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return AsyncResponseStreamManager( + api_request, + text_format=text_format, + input_tools=tools, + ) + + async def parse( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, + tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedResponse[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: + return parse_response( + input_tools=tools, + text_format=text_format, + response=raw_response, + ) + + return await self._post( + "/responses", + body=maybe_transform( + { + "input": input, + "model": model, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "metadata": metadata, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "store": store, + "stream": stream, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + ), + # we turn the `Response` instance into a `ParsedResponse` + # in the `parser` function above + cast_to=cast(Type[ParsedResponse[TextFormatT]], Response), + ) + + async def retrieve( + self, + response_id: str, + *, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return await self._get( + f"/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include": include}, response_retrieve_params.ResponseRetrieveParams + ), + ), + cast_to=Response, + ) + + async def delete( + self, + response_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class ResponsesWithRawResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = _legacy_response.to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + responses.delete, + ) + + @cached_property + def input_items(self) -> InputItemsWithRawResponse: + return InputItemsWithRawResponse(self._responses.input_items) + + +class AsyncResponsesWithRawResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = _legacy_response.async_to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + responses.delete, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithRawResponse: + return AsyncInputItemsWithRawResponse(self._responses.input_items) + + +class ResponsesWithStreamingResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = to_streamed_response_wrapper( + responses.delete, + ) + + @cached_property + def input_items(self) -> InputItemsWithStreamingResponse: + return InputItemsWithStreamingResponse(self._responses.input_items) + + +class AsyncResponsesWithStreamingResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = async_to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = async_to_streamed_response_wrapper( + responses.delete, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithStreamingResponse: + return AsyncInputItemsWithStreamingResponse(self._responses.input_items) + + +def _make_tools(tools: Iterable[ParseableToolParam] | NotGiven) -> List[ToolParam] | NotGiven: + if not is_given(tools): + return NOT_GIVEN + + converted_tools: List[ToolParam] = [] + for tool in tools: + if tool["type"] != "function": + converted_tools.append(tool) + continue + + if "function" not in tool: + # standard Responses API case + converted_tools.append(tool) + continue + + function = cast(Any, tool)["function"] # pyright: ignore[reportUnnecessaryCast] + if not isinstance(function, PydanticFunctionTool): + raise Exception( + "Expected Chat Completions function tool shape to be created using `openai.pydantic_function_tool()`" + ) + + assert "parameters" in function + new_tool = ResponsesPydanticFunctionTool( + { + "type": "function", + "name": function["name"], + "description": function.get("description"), + "parameters": function["parameters"], + "strict": function.get("strict") or False, + }, + function.model, + ) + + converted_tools.append(new_tool.cast()) + + return converted_tools diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 2028decef5..9297dbc2c3 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -193,10 +193,9 @@ def create( contains all the parts you uploaded. This File is usable in the rest of our platform as a regular File object. - For certain `purpose`s, the correct `mime_type` must be specified. Please refer - to documentation for the supported MIME types for your use case: - - - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search#supported-files) + For certain `purpose` values, the correct `mime_type` must be specified. Please + refer to documentation for the + [supported MIME types for your use case](https://platform.openai.com/docs/assistants/tools/file-search#supported-files). For guidance on the proper filename extensions for each purpose, please follow the documentation on @@ -497,10 +496,9 @@ async def create( contains all the parts you uploaded. This File is usable in the rest of our platform as a regular File object. - For certain `purpose`s, the correct `mime_type` must be specified. Please refer - to documentation for the supported MIME types for your use case: - - - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search#supported-files) + For certain `purpose` values, the correct `mime_type` must be specified. Please + refer to documentation for the + [supported MIME types for your use case](https://platform.openai.com/docs/assistants/tools/file-search#supported-files). For guidance on the proper filename extensions for each purpose, please follow the documentation on diff --git a/src/openai/resources/beta/vector_stores/__init__.py b/src/openai/resources/vector_stores/__init__.py similarity index 100% rename from src/openai/resources/beta/vector_stores/__init__.py rename to src/openai/resources/vector_stores/__init__.py diff --git a/src/openai/resources/beta/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py similarity index 93% rename from src/openai/resources/beta/vector_stores/file_batches.py rename to src/openai/resources/vector_stores/file_batches.py index 6d61e92c7f..9b4b64d35e 100644 --- a/src/openai/resources/beta/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -3,31 +3,31 @@ from __future__ import annotations import asyncio -from typing import List, Iterable -from typing_extensions import Literal +from typing import Dict, List, Iterable, Optional +from typing_extensions import Union, Literal from concurrent.futures import Future, ThreadPoolExecutor, as_completed import httpx import sniffio -from .... import _legacy_response -from ....types import FileObject -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ...._utils import ( +from ... import _legacy_response +from ...types import FileChunkingStrategyParam +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._utils import ( is_given, maybe_transform, async_maybe_transform, ) -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncCursorPage, AsyncCursorPage -from ....types.beta import FileChunkingStrategyParam -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.vector_stores import file_batch_create_params, file_batch_list_files_params -from ....types.beta.file_chunking_strategy_param import FileChunkingStrategyParam -from ....types.beta.vector_stores.vector_store_file import VectorStoreFile -from ....types.beta.vector_stores.vector_store_file_batch import VectorStoreFileBatch +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.file_object import FileObject +from ...types.vector_stores import file_batch_create_params, file_batch_list_files_params +from ...types.file_chunking_strategy_param import FileChunkingStrategyParam +from ...types.vector_stores.vector_store_file import VectorStoreFile +from ...types.vector_stores.vector_store_file_batch import VectorStoreFileBatch __all__ = ["FileBatches", "AsyncFileBatches"] @@ -57,6 +57,7 @@ def create( vector_store_id: str, *, file_ids: List[str], + attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -73,6 +74,12 @@ def create( the vector store should use. Useful for tools like `file_search` that can access files. + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty. @@ -92,6 +99,7 @@ def create( body=maybe_transform( { "file_ids": file_ids, + "attributes": attributes, "chunking_strategy": chunking_strategy, }, file_batch_create_params.FileBatchCreateParams, @@ -386,6 +394,7 @@ async def create( vector_store_id: str, *, file_ids: List[str], + attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -402,6 +411,12 @@ async def create( the vector store should use. Useful for tools like `file_search` that can access files. + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty. @@ -421,6 +436,7 @@ async def create( body=await async_maybe_transform( { "file_ids": file_ids, + "attributes": attributes, "chunking_strategy": chunking_strategy, }, file_batch_create_params.FileBatchCreateParams, diff --git a/src/openai/resources/beta/vector_stores/files.py b/src/openai/resources/vector_stores/files.py similarity index 73% rename from src/openai/resources/beta/vector_stores/files.py rename to src/openai/resources/vector_stores/files.py index febf27a753..7d93798adf 100644 --- a/src/openai/resources/beta/vector_stores/files.py +++ b/src/openai/resources/vector_stores/files.py @@ -2,28 +2,29 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Dict, Union, Optional from typing_extensions import Literal, assert_never import httpx -from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ...._utils import ( +from ... import _legacy_response +from ...types import FileChunkingStrategyParam +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._utils import ( is_given, maybe_transform, async_maybe_transform, ) -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncCursorPage, AsyncCursorPage -from ....types.beta import FileChunkingStrategyParam -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.vector_stores import file_list_params, file_create_params -from ....types.beta.file_chunking_strategy_param import FileChunkingStrategyParam -from ....types.beta.vector_stores.vector_store_file import VectorStoreFile -from ....types.beta.vector_stores.vector_store_file_deleted import VectorStoreFileDeleted +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncPage, AsyncPage, SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.vector_stores import file_list_params, file_create_params, file_update_params +from ...types.file_chunking_strategy_param import FileChunkingStrategyParam +from ...types.vector_stores.vector_store_file import VectorStoreFile +from ...types.vector_stores.file_content_response import FileContentResponse +from ...types.vector_stores.vector_store_file_deleted import VectorStoreFileDeleted __all__ = ["Files", "AsyncFiles"] @@ -53,6 +54,7 @@ def create( vector_store_id: str, *, file_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -71,6 +73,12 @@ def create( vector store should use. Useful for tools like `file_search` that can access files. + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty. @@ -90,6 +98,7 @@ def create( body=maybe_transform( { "file_id": file_id, + "attributes": attributes, "chunking_strategy": chunking_strategy, }, file_create_params.FileCreateParams, @@ -137,6 +146,51 @@ def retrieve( cast_to=VectorStoreFile, ) + def update( + self, + file_id: str, + *, + vector_store_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreFile: + """ + Update attributes on a vector store file. + + Args: + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + f"/vector_stores/{vector_store_id}/files/{file_id}", + body=maybe_transform({"attributes": attributes}, file_update_params.FileUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreFile, + ) + def list( self, vector_store_id: str, @@ -339,6 +393,44 @@ def upload_and_poll( poll_interval_ms=poll_interval_ms, ) + def content( + self, + file_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncPage[FileContentResponse]: + """ + Retrieve the parsed contents of a vector store file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + f"/vector_stores/{vector_store_id}/files/{file_id}/content", + page=SyncPage[FileContentResponse], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=FileContentResponse, + ) + class AsyncFiles(AsyncAPIResource): @cached_property @@ -365,6 +457,7 @@ async def create( vector_store_id: str, *, file_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -383,6 +476,12 @@ async def create( vector store should use. Useful for tools like `file_search` that can access files. + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty. @@ -402,6 +501,7 @@ async def create( body=await async_maybe_transform( { "file_id": file_id, + "attributes": attributes, "chunking_strategy": chunking_strategy, }, file_create_params.FileCreateParams, @@ -449,6 +549,51 @@ async def retrieve( cast_to=VectorStoreFile, ) + async def update( + self, + file_id: str, + *, + vector_store_id: str, + attributes: Optional[Dict[str, Union[str, float, bool]]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> VectorStoreFile: + """ + Update attributes on a vector store file. + + Args: + attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters, booleans, or numbers. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + f"/vector_stores/{vector_store_id}/files/{file_id}", + body=await async_maybe_transform({"attributes": attributes}, file_update_params.FileUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VectorStoreFile, + ) + def list( self, vector_store_id: str, @@ -653,6 +798,44 @@ async def upload_and_poll( chunking_strategy=chunking_strategy, ) + def content( + self, + file_id: str, + *, + vector_store_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[FileContentResponse, AsyncPage[FileContentResponse]]: + """ + Retrieve the parsed contents of a vector store file. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + f"/vector_stores/{vector_store_id}/files/{file_id}/content", + page=AsyncPage[FileContentResponse], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=FileContentResponse, + ) + class FilesWithRawResponse: def __init__(self, files: Files) -> None: @@ -664,12 +847,18 @@ def __init__(self, files: Files) -> None: self.retrieve = _legacy_response.to_raw_response_wrapper( files.retrieve, ) + self.update = _legacy_response.to_raw_response_wrapper( + files.update, + ) self.list = _legacy_response.to_raw_response_wrapper( files.list, ) self.delete = _legacy_response.to_raw_response_wrapper( files.delete, ) + self.content = _legacy_response.to_raw_response_wrapper( + files.content, + ) class AsyncFilesWithRawResponse: @@ -682,12 +871,18 @@ def __init__(self, files: AsyncFiles) -> None: self.retrieve = _legacy_response.async_to_raw_response_wrapper( files.retrieve, ) + self.update = _legacy_response.async_to_raw_response_wrapper( + files.update, + ) self.list = _legacy_response.async_to_raw_response_wrapper( files.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( files.delete, ) + self.content = _legacy_response.async_to_raw_response_wrapper( + files.content, + ) class FilesWithStreamingResponse: @@ -700,12 +895,18 @@ def __init__(self, files: Files) -> None: self.retrieve = to_streamed_response_wrapper( files.retrieve, ) + self.update = to_streamed_response_wrapper( + files.update, + ) self.list = to_streamed_response_wrapper( files.list, ) self.delete = to_streamed_response_wrapper( files.delete, ) + self.content = to_streamed_response_wrapper( + files.content, + ) class AsyncFilesWithStreamingResponse: @@ -718,9 +919,15 @@ def __init__(self, files: AsyncFiles) -> None: self.retrieve = async_to_streamed_response_wrapper( files.retrieve, ) + self.update = async_to_streamed_response_wrapper( + files.update, + ) self.list = async_to_streamed_response_wrapper( files.list, ) self.delete = async_to_streamed_response_wrapper( files.delete, ) + self.content = async_to_streamed_response_wrapper( + files.content, + ) diff --git a/src/openai/resources/beta/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py similarity index 80% rename from src/openai/resources/beta/vector_stores/vector_stores.py rename to src/openai/resources/vector_stores/vector_stores.py index 1da52fb3c7..aaa6ed2757 100644 --- a/src/openai/resources/beta/vector_stores/vector_stores.py +++ b/src/openai/resources/vector_stores/vector_stores.py @@ -2,12 +2,12 @@ from __future__ import annotations -from typing import List, Optional +from typing import List, Union, Optional from typing_extensions import Literal import httpx -from .... import _legacy_response +from ... import _legacy_response from .files import ( Files, AsyncFiles, @@ -16,14 +16,22 @@ FilesWithStreamingResponse, AsyncFilesWithStreamingResponse, ) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( +from ...types import ( + FileChunkingStrategyParam, + vector_store_list_params, + vector_store_create_params, + vector_store_search_params, + vector_store_update_params, +) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( maybe_transform, async_maybe_transform, ) -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncPage, AsyncPage, SyncCursorPage, AsyncCursorPage from .file_batches import ( FileBatches, AsyncFileBatches, @@ -32,18 +40,12 @@ FileBatchesWithStreamingResponse, AsyncFileBatchesWithStreamingResponse, ) -from ....pagination import SyncCursorPage, AsyncCursorPage -from ....types.beta import ( - FileChunkingStrategyParam, - vector_store_list_params, - vector_store_create_params, - vector_store_update_params, -) -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.vector_store import VectorStore -from ....types.shared_params.metadata import Metadata -from ....types.beta.vector_store_deleted import VectorStoreDeleted -from ....types.beta.file_chunking_strategy_param import FileChunkingStrategyParam +from ..._base_client import AsyncPaginator, make_request_options +from ...types.vector_store import VectorStore +from ...types.vector_store_deleted import VectorStoreDeleted +from ...types.shared_params.metadata import Metadata +from ...types.file_chunking_strategy_param import FileChunkingStrategyParam +from ...types.vector_store_search_response import VectorStoreSearchResponse __all__ = ["VectorStores", "AsyncVectorStores"] @@ -329,6 +331,69 @@ def delete( cast_to=VectorStoreDeleted, ) + def search( + self, + vector_store_id: str, + *, + query: Union[str, List[str]], + filters: vector_store_search_params.Filters | NotGiven = NOT_GIVEN, + max_num_results: int | NotGiven = NOT_GIVEN, + ranking_options: vector_store_search_params.RankingOptions | NotGiven = NOT_GIVEN, + rewrite_query: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncPage[VectorStoreSearchResponse]: + """ + Search a vector store for relevant chunks based on a query and file attributes + filter. + + Args: + query: A query string for a search + + filters: A filter to apply based on file attributes. + + max_num_results: The maximum number of results to return. This number should be between 1 and 50 + inclusive. + + ranking_options: Ranking options for search. + + rewrite_query: Whether to rewrite the natural language query for vector search. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + f"/vector_stores/{vector_store_id}/search", + page=SyncPage[VectorStoreSearchResponse], + body=maybe_transform( + { + "query": query, + "filters": filters, + "max_num_results": max_num_results, + "ranking_options": ranking_options, + "rewrite_query": rewrite_query, + }, + vector_store_search_params.VectorStoreSearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=VectorStoreSearchResponse, + method="post", + ) + class AsyncVectorStores(AsyncAPIResource): @cached_property @@ -611,6 +676,69 @@ async def delete( cast_to=VectorStoreDeleted, ) + def search( + self, + vector_store_id: str, + *, + query: Union[str, List[str]], + filters: vector_store_search_params.Filters | NotGiven = NOT_GIVEN, + max_num_results: int | NotGiven = NOT_GIVEN, + ranking_options: vector_store_search_params.RankingOptions | NotGiven = NOT_GIVEN, + rewrite_query: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[VectorStoreSearchResponse, AsyncPage[VectorStoreSearchResponse]]: + """ + Search a vector store for relevant chunks based on a query and file attributes + filter. + + Args: + query: A query string for a search + + filters: A filter to apply based on file attributes. + + max_num_results: The maximum number of results to return. This number should be between 1 and 50 + inclusive. + + ranking_options: Ranking options for search. + + rewrite_query: Whether to rewrite the natural language query for vector search. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not vector_store_id: + raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._get_api_list( + f"/vector_stores/{vector_store_id}/search", + page=AsyncPage[VectorStoreSearchResponse], + body=maybe_transform( + { + "query": query, + "filters": filters, + "max_num_results": max_num_results, + "ranking_options": ranking_options, + "rewrite_query": rewrite_query, + }, + vector_store_search_params.VectorStoreSearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=VectorStoreSearchResponse, + method="post", + ) + class VectorStoresWithRawResponse: def __init__(self, vector_stores: VectorStores) -> None: @@ -631,6 +759,9 @@ def __init__(self, vector_stores: VectorStores) -> None: self.delete = _legacy_response.to_raw_response_wrapper( vector_stores.delete, ) + self.search = _legacy_response.to_raw_response_wrapper( + vector_stores.search, + ) @cached_property def files(self) -> FilesWithRawResponse: @@ -660,6 +791,9 @@ def __init__(self, vector_stores: AsyncVectorStores) -> None: self.delete = _legacy_response.async_to_raw_response_wrapper( vector_stores.delete, ) + self.search = _legacy_response.async_to_raw_response_wrapper( + vector_stores.search, + ) @cached_property def files(self) -> AsyncFilesWithRawResponse: @@ -689,6 +823,9 @@ def __init__(self, vector_stores: VectorStores) -> None: self.delete = to_streamed_response_wrapper( vector_stores.delete, ) + self.search = to_streamed_response_wrapper( + vector_stores.search, + ) @cached_property def files(self) -> FilesWithStreamingResponse: @@ -718,6 +855,9 @@ def __init__(self, vector_stores: AsyncVectorStores) -> None: self.delete = async_to_streamed_response_wrapper( vector_stores.delete, ) + self.search = async_to_streamed_response_wrapper( + vector_stores.search, + ) @cached_property def files(self) -> AsyncFilesWithStreamingResponse: diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index eb71ac6ccc..4c337d41c7 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -8,7 +8,11 @@ from .shared import ( Metadata as Metadata, ChatModel as ChatModel, + Reasoning as Reasoning, ErrorObject as ErrorObject, + CompoundFilter as CompoundFilter, + ReasoningEffort as ReasoningEffort, + ComparisonFilter as ComparisonFilter, FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters, ResponseFormatText as ResponseFormatText, @@ -27,6 +31,7 @@ from .file_content import FileContent as FileContent from .file_deleted import FileDeleted as FileDeleted from .file_purpose import FilePurpose as FilePurpose +from .vector_store import VectorStore as VectorStore from .model_deleted import ModelDeleted as ModelDeleted from .embedding_model import EmbeddingModel as EmbeddingModel from .images_response import ImagesResponse as ImagesResponse @@ -40,16 +45,32 @@ from .batch_create_params import BatchCreateParams as BatchCreateParams from .batch_request_counts import BatchRequestCounts as BatchRequestCounts from .upload_create_params import UploadCreateParams as UploadCreateParams +from .vector_store_deleted import VectorStoreDeleted as VectorStoreDeleted from .audio_response_format import AudioResponseFormat as AudioResponseFormat from .image_generate_params import ImageGenerateParams as ImageGenerateParams +from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy from .upload_complete_params import UploadCompleteParams as UploadCompleteParams from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .moderation_create_params import ModerationCreateParams as ModerationCreateParams +from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse +from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams +from .vector_store_search_params import VectorStoreSearchParams as VectorStoreSearchParams +from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams from .moderation_text_input_param import ModerationTextInputParam as ModerationTextInputParam +from .file_chunking_strategy_param import FileChunkingStrategyParam as FileChunkingStrategyParam +from .vector_store_search_response import VectorStoreSearchResponse as VectorStoreSearchResponse from .websocket_connection_options import WebsocketConnectionOptions as WebsocketConnectionOptions from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams +from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam +from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam as AutoFileChunkingStrategyParam from .moderation_multi_modal_input_param import ModerationMultiModalInputParam as ModerationMultiModalInputParam +from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject as OtherFileChunkingStrategyObject +from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam as StaticFileChunkingStrategyParam +from .static_file_chunking_strategy_object import StaticFileChunkingStrategyObject as StaticFileChunkingStrategyObject +from .static_file_chunking_strategy_object_param import ( + StaticFileChunkingStrategyObjectParam as StaticFileChunkingStrategyObjectParam, +) diff --git a/src/openai/types/beta/auto_file_chunking_strategy_param.py b/src/openai/types/auto_file_chunking_strategy_param.py similarity index 100% rename from src/openai/types/beta/auto_file_chunking_strategy_param.py rename to src/openai/types/auto_file_chunking_strategy_param.py diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index b9ea792bfa..5ba3eadf3c 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -4,7 +4,6 @@ from .thread import Thread as Thread from .assistant import Assistant as Assistant -from .vector_store import VectorStore as VectorStore from .function_tool import FunctionTool as FunctionTool from .assistant_tool import AssistantTool as AssistantTool from .thread_deleted import ThreadDeleted as ThreadDeleted @@ -14,35 +13,21 @@ from .assistant_tool_param import AssistantToolParam as AssistantToolParam from .thread_create_params import ThreadCreateParams as ThreadCreateParams from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams -from .vector_store_deleted import VectorStoreDeleted as VectorStoreDeleted from .assistant_list_params import AssistantListParams as AssistantListParams from .assistant_tool_choice import AssistantToolChoice as AssistantToolChoice from .code_interpreter_tool import CodeInterpreterTool as CodeInterpreterTool from .assistant_stream_event import AssistantStreamEvent as AssistantStreamEvent -from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams -from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams -from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams -from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption -from .file_chunking_strategy_param import FileChunkingStrategyParam as FileChunkingStrategyParam from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams -from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption -from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam as AutoFileChunkingStrategyParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam as AssistantToolChoiceOptionParam -from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject as OtherFileChunkingStrategyObject -from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam as StaticFileChunkingStrategyParam from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam as AssistantToolChoiceFunctionParam -from .static_file_chunking_strategy_object import StaticFileChunkingStrategyObject as StaticFileChunkingStrategyObject from .assistant_response_format_option_param import ( AssistantResponseFormatOptionParam as AssistantResponseFormatOptionParam, ) -from .static_file_chunking_strategy_object_param import ( - StaticFileChunkingStrategyObjectParam as StaticFileChunkingStrategyObjectParam, -) diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index e90aabfd3f..8b3c331850 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -3,12 +3,12 @@ from __future__ import annotations from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..shared.chat_model import ChatModel from .assistant_tool_param import AssistantToolParam from ..shared_params.metadata import Metadata -from .file_chunking_strategy_param import FileChunkingStrategyParam +from ..shared.reasoning_effort import ReasoningEffort from .assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = [ @@ -17,6 +17,10 @@ "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", "ToolResourcesFileSearchVectorStore", + "ToolResourcesFileSearchVectorStoreChunkingStrategy", + "ToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", ] @@ -53,8 +57,8 @@ class AssistantCreateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" - reasoning_effort: Optional[Literal["low", "medium", "high"]] - """**o1 and o3-mini models only** + reasoning_effort: Optional[ReasoningEffort] + """**o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -127,12 +131,43 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): """ +class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ + ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic +] + + class ToolResourcesFileSearchVectorStore(TypedDict, total=False): - chunking_strategy: FileChunkingStrategyParam + chunking_strategy: ToolResourcesFileSearchVectorStoreChunkingStrategy """The chunking strategy used to chunk the file(s). - If not set, will use the `auto` strategy. Only applicable if `file_ids` is - non-empty. + If not set, will use the `auto` strategy. """ file_ids: List[str] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 12a57a4063..d3ec7614fd 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -7,6 +7,7 @@ from .assistant_tool_param import AssistantToolParam from ..shared_params.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort from .assistant_response_format_option_param import AssistantResponseFormatOptionParam __all__ = ["AssistantUpdateParams", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] @@ -79,8 +80,8 @@ class AssistantUpdateParams(TypedDict, total=False): name: Optional[str] """The name of the assistant. The maximum length is 256 characters.""" - reasoning_effort: Optional[Literal["low", "medium", "high"]] - """**o1 and o3-mini models only** + reasoning_effort: Optional[ReasoningEffort] + """**o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index d888fb3eee..065c390f4e 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -10,7 +10,6 @@ from .file_search_tool_param import FileSearchToolParam from ..shared_params.metadata import Metadata from .code_interpreter_tool_param import CodeInterpreterToolParam -from .file_chunking_strategy_param import FileChunkingStrategyParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam from .threads.message_content_part_param import MessageContentPartParam from .assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -26,6 +25,10 @@ "ThreadToolResourcesCodeInterpreter", "ThreadToolResourcesFileSearch", "ThreadToolResourcesFileSearchVectorStore", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategy", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", @@ -224,12 +227,44 @@ class ThreadToolResourcesCodeInterpreter(TypedDict, total=False): """ +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ThreadToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ + ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto, + ThreadToolResourcesFileSearchVectorStoreChunkingStrategyStatic, +] + + class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): - chunking_strategy: FileChunkingStrategyParam + chunking_strategy: ThreadToolResourcesFileSearchVectorStoreChunkingStrategy """The chunking strategy used to chunk the file(s). - If not set, will use the `auto` strategy. Only applicable if `file_ids` is - non-empty. + If not set, will use the `auto` strategy. """ file_ids: List[str] diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index 127202753c..ec1ccf19a6 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -7,7 +7,6 @@ from ..shared_params.metadata import Metadata from .code_interpreter_tool_param import CodeInterpreterToolParam -from .file_chunking_strategy_param import FileChunkingStrategyParam from .threads.message_content_part_param import MessageContentPartParam __all__ = [ @@ -20,6 +19,10 @@ "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", "ToolResourcesFileSearchVectorStore", + "ToolResourcesFileSearchVectorStoreChunkingStrategy", + "ToolResourcesFileSearchVectorStoreChunkingStrategyAuto", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStatic", + "ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic", ] @@ -101,12 +104,43 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): """ +class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + type: Required[Literal["auto"]] + """Always `auto`.""" + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic(TypedDict, total=False): + chunk_overlap_tokens: Required[int] + """The number of tokens that overlap between chunks. The default value is `400`. + + Note that the overlap must not exceed half of `max_chunk_size_tokens`. + """ + + max_chunk_size_tokens: Required[int] + """The maximum number of tokens in each chunk. + + The default value is `800`. The minimum value is `100` and the maximum value is + `4096`. + """ + + +class ToolResourcesFileSearchVectorStoreChunkingStrategyStatic(TypedDict, total=False): + static: Required[ToolResourcesFileSearchVectorStoreChunkingStrategyStaticStatic] + + type: Required[Literal["static"]] + """Always `static`.""" + + +ToolResourcesFileSearchVectorStoreChunkingStrategy: TypeAlias = Union[ + ToolResourcesFileSearchVectorStoreChunkingStrategyAuto, ToolResourcesFileSearchVectorStoreChunkingStrategyStatic +] + + class ToolResourcesFileSearchVectorStore(TypedDict, total=False): - chunking_strategy: FileChunkingStrategyParam + chunking_strategy: ToolResourcesFileSearchVectorStoreChunkingStrategy """The chunking strategy used to chunk the file(s). - If not set, will use the `auto` strategy. Only applicable if `file_ids` is - non-empty. + If not set, will use the `auto` strategy. """ file_ids: List[str] diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 098e50a1d9..fc70227862 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -9,6 +9,7 @@ from ..assistant_tool_param import AssistantToolParam from .runs.run_step_include import RunStepInclude from ...shared_params.metadata import Metadata +from ...shared.reasoning_effort import ReasoningEffort from .message_content_part_param import MessageContentPartParam from ..code_interpreter_tool_param import CodeInterpreterToolParam from ..assistant_tool_choice_option_param import AssistantToolChoiceOptionParam @@ -106,8 +107,8 @@ class RunCreateParamsBase(TypedDict, total=False): during tool use. """ - reasoning_effort: Optional[Literal["low", "medium", "high"]] - """**o1 and o3-mini models only** + reasoning_effort: Optional[ReasoningEffort] + """**o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index 1e20a52b41..6321417826 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -17,7 +17,6 @@ class ChatCompletionAudioParam(TypedDict, total=False): voice: Required[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] """The voice the model uses to respond. - Supported voices are `ash`, `ballad`, `coral`, `sage`, and `verse` (also - supported but not recommended are `alloy`, `echo`, and `shimmer`; these voices - are less expressive). + Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, and + `shimmer`. """ diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index 682d11f4c7..1293c54312 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -3,14 +3,39 @@ from __future__ import annotations from typing import Union -from typing_extensions import TypeAlias +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam from .chat_completion_content_part_image_param import ChatCompletionContentPartImageParam from .chat_completion_content_part_input_audio_param import ChatCompletionContentPartInputAudioParam -__all__ = ["ChatCompletionContentPartParam"] +__all__ = ["ChatCompletionContentPartParam", "File", "FileFile"] + + +class FileFile(TypedDict, total=False): + file_data: str + """ + The base64 encoded file data, used when passing the file to the model as a + string. + """ + + file_id: str + """The ID of an uploaded file to use as input.""" + + file_name: str + """The name of the file, used when passing the file to the model as a string.""" + + +class File(TypedDict, total=False): + file: Required[FileFile] + + type: Required[Literal["file"]] + """The type of the content part. Always `file`.""" + ChatCompletionContentPartParam: TypeAlias = Union[ - ChatCompletionContentPartTextParam, ChatCompletionContentPartImageParam, ChatCompletionContentPartInputAudioParam + ChatCompletionContentPartTextParam, + ChatCompletionContentPartImageParam, + ChatCompletionContentPartInputAudioParam, + File, ] diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py index 704fa5d5d1..c659ac3da0 100644 --- a/src/openai/types/chat/chat_completion_message.py +++ b/src/openai/types/chat/chat_completion_message.py @@ -7,7 +7,29 @@ from .chat_completion_audio import ChatCompletionAudio from .chat_completion_message_tool_call import ChatCompletionMessageToolCall -__all__ = ["ChatCompletionMessage", "FunctionCall"] +__all__ = ["ChatCompletionMessage", "Annotation", "AnnotationURLCitation", "FunctionCall"] + + +class AnnotationURLCitation(BaseModel): + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + url: str + """The URL of the web resource.""" + + +class Annotation(BaseModel): + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url_citation: AnnotationURLCitation + """A URL citation when using web search.""" class FunctionCall(BaseModel): @@ -33,6 +55,12 @@ class ChatCompletionMessage(BaseModel): role: Literal["assistant"] """The role of the author of this message.""" + annotations: Optional[List[Annotation]] = None + """ + Annotations for the message, when applicable, as when using the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + """ + audio: Optional[ChatCompletionAudio] = None """ If the audio output modality is requested, this object contains data about the diff --git a/src/openai/types/chat/chat_completion_reasoning_effort.py b/src/openai/types/chat/chat_completion_reasoning_effort.py index 85249c53b1..e4785c90bf 100644 --- a/src/openai/types/chat/chat_completion_reasoning_effort.py +++ b/src/openai/types/chat/chat_completion_reasoning_effort.py @@ -1,8 +1,8 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal, TypeAlias + +from ..shared.reasoning_effort import ReasoningEffort __all__ = ["ChatCompletionReasoningEffort"] -ChatCompletionReasoningEffort: TypeAlias = Optional[Literal["low", "medium", "high"]] +ChatCompletionReasoningEffort = ReasoningEffort diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 4dd2812aba..05103fba91 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -7,11 +7,10 @@ from ..shared.chat_model import ChatModel from ..shared_params.metadata import Metadata -from .chat_completion_modality import ChatCompletionModality +from ..shared.reasoning_effort import ReasoningEffort from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_audio_param import ChatCompletionAudioParam from .chat_completion_message_param import ChatCompletionMessageParam -from .chat_completion_reasoning_effort import ChatCompletionReasoningEffort from ..shared_params.function_parameters import FunctionParameters from ..shared_params.response_format_text import ResponseFormatText from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam @@ -26,6 +25,9 @@ "FunctionCall", "Function", "ResponseFormat", + "WebSearchOptions", + "WebSearchOptionsUserLocation", + "WebSearchOptionsUserLocationApproximate", "CompletionCreateParamsNonStreaming", "CompletionCreateParamsStreaming", ] @@ -43,11 +45,12 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ model: Required[Union[str, ChatModel]] - """ID of the model to use. + """Model ID used to generate the response, like `gpt-4o` or `o1`. - See the - [model endpoint compatibility](https://platform.openai.com/docs/models#model-endpoint-compatibility) - table for details on which models work with the Chat API. + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. """ audio: Optional[ChatCompletionAudioParam] @@ -133,10 +136,10 @@ class CompletionCreateParamsBase(TypedDict, total=False): a maximum length of 512 characters. """ - modalities: Optional[List[ChatCompletionModality]] + modalities: Optional[List[Literal["text", "audio"]]] """ - Output types that you would like the model to generate for this request. Most - models are capable of generating text, which is the default: + Output types that you would like the model to generate. Most models are capable + of generating text, which is the default: `["text"]` @@ -174,8 +177,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): far, increasing the model's likelihood to talk about new topics. """ - reasoning_effort: Optional[ChatCompletionReasoningEffort] - """**o1 and o3-mini models only** + reasoning_effort: Optional[ReasoningEffort] + """**o-series models only** Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -191,16 +194,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. Without this, the model may - generate an unending stream of whitespace until the generation reaches the token - limit, resulting in a long-running and seemingly "stuck" request. Also note that - the message content may be partially cut off if `finish_reason="length"`, which - indicates the generation exceeded `max_tokens` or the conversation exceeded the - max context length. + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. """ seed: Optional[int] @@ -221,14 +217,20 @@ class CompletionCreateParamsBase(TypedDict, total=False): utilize scale tier credits until they are exhausted. - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no - latency guarantee. + latency guarentee. - If set to 'default', the request will be processed using the default service - tier with a lower uptime SLA and no latency guarantee. + tier with a lower uptime SLA and no latency guarentee. - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. """ - stop: Union[Optional[str], List[str]] - """Up to 4 sequences where the API will stop generating further tokens.""" + stop: Union[Optional[str], List[str], None] + """Up to 4 sequences where the API will stop generating further tokens. + + The returned text will not contain the stop sequence. + """ store: Optional[bool] """ @@ -292,6 +294,13 @@ class CompletionCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ + web_search_options: WebSearchOptions + """ + This tool searches the web for relevant results to use in a response. Learn more + about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + """ + FunctionCall: TypeAlias = Union[Literal["none", "auto"], ChatCompletionFunctionCallOptionParam] @@ -322,30 +331,73 @@ class Function(TypedDict, total=False): """ -ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONObject, ResponseFormatJSONSchema] +ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONSchema, ResponseFormatJSONObject] + + +class WebSearchOptionsUserLocationApproximate(TypedDict, total=False): + city: str + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: str + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: str + """Free text input for the region of the user, e.g. `California`.""" + + timezone: str + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class WebSearchOptionsUserLocation(TypedDict, total=False): + approximate: Required[WebSearchOptionsUserLocationApproximate] + """Approximate location parameters for the search.""" + + type: Required[Literal["approximate"]] + """The type of location approximation. Always `approximate`.""" + + +class WebSearchOptions(TypedDict, total=False): + search_context_size: Literal["low", "medium", "high"] + """ + High level guidance for the amount of context window space to use for the + search. One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[WebSearchOptionsUserLocation] + """Approximate location parameters for the search.""" class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): stream: Optional[Literal[False]] - """If set, partial message deltas will be sent, like in ChatGPT. - - Tokens will be sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available, with the stream terminated by a `data: [DONE]` - message. - [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming) + for more information, along with the + [streaming responses](https://platform.openai.com/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. """ class CompletionCreateParamsStreaming(CompletionCreateParamsBase): stream: Required[Literal[True]] - """If set, partial message deltas will be sent, like in ChatGPT. - - Tokens will be sent as data-only - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) - as they become available, with the stream terminated by a `data: [DONE]` - message. - [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming) + for more information, along with the + [streaming responses](https://platform.openai.com/docs/guides/streaming-responses) + guide for more information on how to handle the streaming events. """ diff --git a/src/openai/types/chat/completion_list_params.py b/src/openai/types/chat/completion_list_params.py index a8fce900ce..d93da834a3 100644 --- a/src/openai/types/chat/completion_list_params.py +++ b/src/openai/types/chat/completion_list_params.py @@ -15,19 +15,19 @@ class CompletionListParams(TypedDict, total=False): """Identifier for the last chat completion from the previous pagination request.""" limit: int - """Number of chat completions to retrieve.""" + """Number of Chat Completions to retrieve.""" metadata: Optional[Metadata] - """A list of metadata keys to filter the chat completions by. Example: + """A list of metadata keys to filter the Chat Completions by. Example: `metadata[key1]=value1&metadata[key2]=value2` """ model: str - """The model used to generate the chat completions.""" + """The model used to generate the Chat Completions.""" order: Literal["asc", "desc"] - """Sort order for chat completions by timestamp. + """Sort order for Chat Completions by timestamp. Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. """ diff --git a/src/openai/types/beta/file_chunking_strategy.py b/src/openai/types/file_chunking_strategy.py similarity index 93% rename from src/openai/types/beta/file_chunking_strategy.py rename to src/openai/types/file_chunking_strategy.py index 406d69dd0e..ee96bd7884 100644 --- a/src/openai/types/beta/file_chunking_strategy.py +++ b/src/openai/types/file_chunking_strategy.py @@ -3,7 +3,7 @@ from typing import Union from typing_extensions import Annotated, TypeAlias -from ..._utils import PropertyInfo +from .._utils import PropertyInfo from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject from .static_file_chunking_strategy_object import StaticFileChunkingStrategyObject diff --git a/src/openai/types/beta/file_chunking_strategy_param.py b/src/openai/types/file_chunking_strategy_param.py similarity index 100% rename from src/openai/types/beta/file_chunking_strategy_param.py rename to src/openai/types/file_chunking_strategy_param.py diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index ecf7503358..728dfd350f 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -17,10 +17,8 @@ class FileCreateParams(TypedDict, total=False): purpose: Required[FilePurpose] """The intended purpose of the uploaded file. - Use "assistants" for - [Assistants](https://platform.openai.com/docs/api-reference/assistants) and - [Message](https://platform.openai.com/docs/api-reference/messages) files, - "vision" for Assistants image file inputs, "batch" for - [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for - [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). + One of: - `assistants`: Used in the Assistants API - `batch`: Used in the Batch + API - `fine-tune`: Used for fine-tuning - `vision`: Images used for vision + fine-tuning - `user_data`: Flexible file type for any purpose - `evals`: Used + for eval data sets """ diff --git a/src/openai/types/file_purpose.py b/src/openai/types/file_purpose.py index 32dc352c62..b2c2d5f9fc 100644 --- a/src/openai/types/file_purpose.py +++ b/src/openai/types/file_purpose.py @@ -4,4 +4,4 @@ __all__ = ["FilePurpose"] -FilePurpose: TypeAlias = Literal["assistants", "batch", "fine-tune", "vision"] +FilePurpose: TypeAlias = Literal["assistants", "batch", "fine-tune", "vision", "user_data", "evals"] diff --git a/src/openai/types/beta/other_file_chunking_strategy_object.py b/src/openai/types/other_file_chunking_strategy_object.py similarity index 89% rename from src/openai/types/beta/other_file_chunking_strategy_object.py rename to src/openai/types/other_file_chunking_strategy_object.py index 89da560be4..e4cd61a8fc 100644 --- a/src/openai/types/beta/other_file_chunking_strategy_object.py +++ b/src/openai/types/other_file_chunking_strategy_object.py @@ -2,7 +2,7 @@ from typing_extensions import Literal -from ..._models import BaseModel +from .._models import BaseModel __all__ = ["OtherFileChunkingStrategyObject"] diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py new file mode 100644 index 0000000000..970a167d2c --- /dev/null +++ b/src/openai/types/responses/__init__.py @@ -0,0 +1,138 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .tool import Tool as Tool +from .response import Response as Response +from .tool_param import ToolParam as ToolParam +from .computer_tool import ComputerTool as ComputerTool +from .function_tool import FunctionTool as FunctionTool +from .response_error import ResponseError as ResponseError +from .response_usage import ResponseUsage as ResponseUsage +from .parsed_response import ( + ParsedContent as ParsedContent, + ParsedResponse as ParsedResponse, + ParsedResponseOutputItem as ParsedResponseOutputItem, + ParsedResponseOutputText as ParsedResponseOutputText, + ParsedResponseOutputMessage as ParsedResponseOutputMessage, + ParsedResponseFunctionToolCall as ParsedResponseFunctionToolCall, +) +from .response_status import ResponseStatus as ResponseStatus +from .web_search_tool import WebSearchTool as WebSearchTool +from .file_search_tool import FileSearchTool as FileSearchTool +from .tool_choice_types import ToolChoiceTypes as ToolChoiceTypes +from .response_item_list import ResponseItemList as ResponseItemList +from .computer_tool_param import ComputerToolParam as ComputerToolParam +from .function_tool_param import FunctionToolParam as FunctionToolParam +from .response_includable import ResponseIncludable as ResponseIncludable +from .response_input_file import ResponseInputFile as ResponseInputFile +from .response_input_text import ResponseInputText as ResponseInputText +from .tool_choice_options import ToolChoiceOptions as ToolChoiceOptions +from .response_error_event import ResponseErrorEvent as ResponseErrorEvent +from .response_input_image import ResponseInputImage as ResponseInputImage +from .response_input_param import ResponseInputParam as ResponseInputParam +from .response_output_item import ResponseOutputItem as ResponseOutputItem +from .response_output_text import ResponseOutputText as ResponseOutputText +from .response_text_config import ResponseTextConfig as ResponseTextConfig +from .tool_choice_function import ToolChoiceFunction as ToolChoiceFunction +from .response_failed_event import ResponseFailedEvent as ResponseFailedEvent +from .response_stream_event import ResponseStreamEvent as ResponseStreamEvent +from .web_search_tool_param import WebSearchToolParam as WebSearchToolParam +from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam +from .input_item_list_params import InputItemListParams as InputItemListParams +from .response_create_params import ResponseCreateParams as ResponseCreateParams +from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent +from .response_input_content import ResponseInputContent as ResponseInputContent +from .response_output_message import ResponseOutputMessage as ResponseOutputMessage +from .response_output_refusal import ResponseOutputRefusal as ResponseOutputRefusal +from .tool_choice_types_param import ToolChoiceTypesParam as ToolChoiceTypesParam +from .easy_input_message_param import EasyInputMessageParam as EasyInputMessageParam +from .response_completed_event import ResponseCompletedEvent as ResponseCompletedEvent +from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams +from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent +from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent +from .response_incomplete_event import ResponseIncompleteEvent as ResponseIncompleteEvent +from .response_input_file_param import ResponseInputFileParam as ResponseInputFileParam +from .response_input_item_param import ResponseInputItemParam as ResponseInputItemParam +from .response_input_text_param import ResponseInputTextParam as ResponseInputTextParam +from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent +from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent +from .response_input_image_param import ResponseInputImageParam as ResponseInputImageParam +from .response_output_text_param import ResponseOutputTextParam as ResponseOutputTextParam +from .response_text_config_param import ResponseTextConfigParam as ResponseTextConfigParam +from .tool_choice_function_param import ToolChoiceFunctionParam as ToolChoiceFunctionParam +from .response_computer_tool_call import ResponseComputerToolCall as ResponseComputerToolCall +from .response_format_text_config import ResponseFormatTextConfig as ResponseFormatTextConfig +from .response_function_tool_call import ResponseFunctionToolCall as ResponseFunctionToolCall +from .response_refusal_done_event import ResponseRefusalDoneEvent as ResponseRefusalDoneEvent +from .response_function_web_search import ResponseFunctionWebSearch as ResponseFunctionWebSearch +from .response_input_content_param import ResponseInputContentParam as ResponseInputContentParam +from .response_refusal_delta_event import ResponseRefusalDeltaEvent as ResponseRefusalDeltaEvent +from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam +from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam +from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall +from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent +from .response_computer_tool_call_param import ResponseComputerToolCallParam as ResponseComputerToolCallParam +from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent +from .response_format_text_config_param import ResponseFormatTextConfigParam as ResponseFormatTextConfigParam +from .response_function_tool_call_param import ResponseFunctionToolCallParam as ResponseFunctionToolCallParam +from .response_function_web_search_param import ResponseFunctionWebSearchParam as ResponseFunctionWebSearchParam +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall as ResponseCodeInterpreterToolCall +from .response_input_message_content_list import ResponseInputMessageContentList as ResponseInputMessageContentList +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent +from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam as ResponseFileSearchToolCallParam +from .response_text_annotation_delta_event import ResponseTextAnnotationDeltaEvent as ResponseTextAnnotationDeltaEvent +from .response_audio_transcript_delta_event import ( + ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, +) +from .response_format_text_json_schema_config import ( + ResponseFormatTextJSONSchemaConfig as ResponseFormatTextJSONSchemaConfig, +) +from .response_web_search_call_completed_event import ( + ResponseWebSearchCallCompletedEvent as ResponseWebSearchCallCompletedEvent, +) +from .response_web_search_call_searching_event import ( + ResponseWebSearchCallSearchingEvent as ResponseWebSearchCallSearchingEvent, +) +from .response_file_search_call_completed_event import ( + ResponseFileSearchCallCompletedEvent as ResponseFileSearchCallCompletedEvent, +) +from .response_file_search_call_searching_event import ( + ResponseFileSearchCallSearchingEvent as ResponseFileSearchCallSearchingEvent, +) +from .response_input_message_content_list_param import ( + ResponseInputMessageContentListParam as ResponseInputMessageContentListParam, +) +from .response_web_search_call_in_progress_event import ( + ResponseWebSearchCallInProgressEvent as ResponseWebSearchCallInProgressEvent, +) +from .response_file_search_call_in_progress_event import ( + ResponseFileSearchCallInProgressEvent as ResponseFileSearchCallInProgressEvent, +) +from .response_function_call_arguments_done_event import ( + ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, +) +from .response_function_call_arguments_delta_event import ( + ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, +) +from .response_format_text_json_schema_config_param import ( + ResponseFormatTextJSONSchemaConfigParam as ResponseFormatTextJSONSchemaConfigParam, +) +from .response_code_interpreter_call_code_done_event import ( + ResponseCodeInterpreterCallCodeDoneEvent as ResponseCodeInterpreterCallCodeDoneEvent, +) +from .response_code_interpreter_call_completed_event import ( + ResponseCodeInterpreterCallCompletedEvent as ResponseCodeInterpreterCallCompletedEvent, +) +from .response_code_interpreter_call_code_delta_event import ( + ResponseCodeInterpreterCallCodeDeltaEvent as ResponseCodeInterpreterCallCodeDeltaEvent, +) +from .response_code_interpreter_call_in_progress_event import ( + ResponseCodeInterpreterCallInProgressEvent as ResponseCodeInterpreterCallInProgressEvent, +) +from .response_code_interpreter_call_interpreting_event import ( + ResponseCodeInterpreterCallInterpretingEvent as ResponseCodeInterpreterCallInterpretingEvent, +) diff --git a/src/openai/types/responses/computer_tool.py b/src/openai/types/responses/computer_tool.py new file mode 100644 index 0000000000..f0499cd950 --- /dev/null +++ b/src/openai/types/responses/computer_tool.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__ = ["ComputerTool"] + + +class ComputerTool(BaseModel): + display_height: float + """The height of the computer display.""" + + display_width: float + """The width of the computer display.""" + + environment: Literal["mac", "windows", "ubuntu", "browser"] + """The type of computer environment to control.""" + + type: Literal["computer-preview"] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/src/openai/types/responses/computer_tool_param.py b/src/openai/types/responses/computer_tool_param.py new file mode 100644 index 0000000000..685b471378 --- /dev/null +++ b/src/openai/types/responses/computer_tool_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ComputerToolParam"] + + +class ComputerToolParam(TypedDict, total=False): + display_height: Required[float] + """The height of the computer display.""" + + display_width: Required[float] + """The width of the computer display.""" + + environment: Required[Literal["mac", "windows", "ubuntu", "browser"]] + """The type of computer environment to control.""" + + type: Required[Literal["computer-preview"]] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/src/openai/types/responses/easy_input_message_param.py b/src/openai/types/responses/easy_input_message_param.py new file mode 100644 index 0000000000..ef2f1c5f37 --- /dev/null +++ b/src/openai/types/responses/easy_input_message_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +from .response_input_message_content_list_param import ResponseInputMessageContentListParam + +__all__ = ["EasyInputMessageParam"] + + +class EasyInputMessageParam(TypedDict, total=False): + content: Required[Union[str, ResponseInputMessageContentListParam]] + """ + Text, image, or audio input to the model, used to generate a response. Can also + contain previous assistant responses. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" diff --git a/src/openai/types/responses/file_search_tool.py b/src/openai/types/responses/file_search_tool.py new file mode 100644 index 0000000000..683fc533fe --- /dev/null +++ b/src/openai/types/responses/file_search_tool.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from ..shared.compound_filter import CompoundFilter +from ..shared.comparison_filter import ComparisonFilter + +__all__ = ["FileSearchTool", "Filters", "RankingOptions"] + +Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter] + + +class RankingOptions(BaseModel): + ranker: Optional[Literal["auto", "default-2024-11-15"]] = None + """The ranker to use for the file search.""" + + score_threshold: Optional[float] = None + """ + The score threshold for the file search, a number between 0 and 1. Numbers + closer to 1 will attempt to return only the most relevant results, but may + return fewer results. + """ + + +class FileSearchTool(BaseModel): + type: Literal["file_search"] + """The type of the file search tool. Always `file_search`.""" + + vector_store_ids: List[str] + """The IDs of the vector stores to search.""" + + filters: Optional[Filters] = None + """A filter to apply based on file attributes.""" + + max_num_results: Optional[int] = None + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: Optional[RankingOptions] = None + """Ranking options for search.""" diff --git a/src/openai/types/responses/file_search_tool_param.py b/src/openai/types/responses/file_search_tool_param.py new file mode 100644 index 0000000000..2d6af8536b --- /dev/null +++ b/src/openai/types/responses/file_search_tool_param.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..shared_params.compound_filter import CompoundFilter +from ..shared_params.comparison_filter import ComparisonFilter + +__all__ = ["FileSearchToolParam", "Filters", "RankingOptions"] + +Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter] + + +class RankingOptions(TypedDict, total=False): + ranker: Literal["auto", "default-2024-11-15"] + """The ranker to use for the file search.""" + + score_threshold: float + """ + The score threshold for the file search, a number between 0 and 1. Numbers + closer to 1 will attempt to return only the most relevant results, but may + return fewer results. + """ + + +class FileSearchToolParam(TypedDict, total=False): + type: Required[Literal["file_search"]] + """The type of the file search tool. Always `file_search`.""" + + vector_store_ids: Required[List[str]] + """The IDs of the vector stores to search.""" + + filters: Filters + """A filter to apply based on file attributes.""" + + max_num_results: int + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: RankingOptions + """Ranking options for search.""" diff --git a/src/openai/types/responses/function_tool.py b/src/openai/types/responses/function_tool.py new file mode 100644 index 0000000000..236a2c7c63 --- /dev/null +++ b/src/openai/types/responses/function_tool.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FunctionTool"] + + +class FunctionTool(BaseModel): + name: str + """The name of the function to call.""" + + parameters: Dict[str, object] + """A JSON schema object describing the parameters of the function.""" + + strict: bool + """Whether to enforce strict parameter validation. Default `true`.""" + + type: Literal["function"] + """The type of the function tool. Always `function`.""" + + description: Optional[str] = None + """A description of the function. + + Used by the model to determine whether or not to call the function. + """ diff --git a/src/openai/types/responses/function_tool_param.py b/src/openai/types/responses/function_tool_param.py new file mode 100644 index 0000000000..774a22e336 --- /dev/null +++ b/src/openai/types/responses/function_tool_param.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FunctionToolParam"] + + +class FunctionToolParam(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" + + parameters: Required[Dict[str, object]] + """A JSON schema object describing the parameters of the function.""" + + strict: Required[bool] + """Whether to enforce strict parameter validation. Default `true`.""" + + type: Required[Literal["function"]] + """The type of the function tool. Always `function`.""" + + description: Optional[str] + """A description of the function. + + Used by the model to determine whether or not to call the function. + """ diff --git a/src/openai/types/responses/input_item_list_params.py b/src/openai/types/responses/input_item_list_params.py new file mode 100644 index 0000000000..e0b71f1ac5 --- /dev/null +++ b/src/openai/types/responses/input_item_list_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["InputItemListParams"] + + +class InputItemListParams(TypedDict, total=False): + after: str + """An item ID to list items after, used in pagination.""" + + before: str + """An item ID to list items before, used in pagination.""" + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """The order to return the input items in. Default is `asc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + """ diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py new file mode 100644 index 0000000000..3216a71ba9 --- /dev/null +++ b/src/openai/types/responses/parsed_response.py @@ -0,0 +1,77 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import TYPE_CHECKING, List, Union, Generic, TypeVar, Optional +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response import Response +from ..._models import GenericModel +from ..._utils._transform import PropertyInfo +from .response_output_item import Reasoning +from .response_output_text import ResponseOutputText +from .response_output_message import ResponseOutputMessage +from .response_output_refusal import ResponseOutputRefusal +from .response_computer_tool_call import ResponseComputerToolCall +from .response_function_tool_call import ResponseFunctionToolCall +from .response_function_web_search import ResponseFunctionWebSearch +from .response_file_search_tool_call import ResponseFileSearchToolCall + +__all__ = ["ParsedResponse", "ParsedResponseOutputMessage", "ParsedResponseOutputText"] + +ContentType = TypeVar("ContentType") + +# we need to disable this check because we're overriding properties +# with subclasses of their types which is technically unsound as +# properties can be mutated. +# pyright: reportIncompatibleVariableOverride=false + + +class ParsedResponseOutputText(ResponseOutputText, GenericModel, Generic[ContentType]): + parsed: Optional[ContentType] = None + + +ParsedContent: TypeAlias = Annotated[ + Union[ParsedResponseOutputText[ContentType], ResponseOutputRefusal], + PropertyInfo(discriminator="type"), +] + + +class ParsedResponseOutputMessage(ResponseOutputMessage, GenericModel, Generic[ContentType]): + if TYPE_CHECKING: + content: List[ParsedContent[ContentType]] # type: ignore[assignment] + else: + content: List[ParsedContent] + + +class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): + parsed_arguments: object = None + + +ParsedResponseOutputItem: TypeAlias = Annotated[ + Union[ + ParsedResponseOutputMessage[ContentType], + ParsedResponseFunctionToolCall, + ResponseFileSearchToolCall, + ResponseFunctionWebSearch, + ResponseComputerToolCall, + Reasoning, + ], + PropertyInfo(discriminator="type"), +] + + +class ParsedResponse(Response, GenericModel, Generic[ContentType]): + if TYPE_CHECKING: + output: List[ParsedResponseOutputItem[ContentType]] # type: ignore[assignment] + else: + output: List[ParsedResponseOutputItem] + + @property + def output_parsed(self) -> Optional[ContentType]: + for output in self.output: + if output.type == "message": + for content in output.content: + if content.type == "output_text" and content.parsed: + return content.parsed + + return None diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py new file mode 100644 index 0000000000..66887ae9b5 --- /dev/null +++ b/src/openai/types/responses/response.py @@ -0,0 +1,204 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from .tool import Tool +from ..._models import BaseModel +from .response_error import ResponseError +from .response_usage import ResponseUsage +from .response_status import ResponseStatus +from ..shared.metadata import Metadata +from ..shared.reasoning import Reasoning +from .tool_choice_types import ToolChoiceTypes +from ..shared.chat_model import ChatModel +from .tool_choice_options import ToolChoiceOptions +from .response_output_item import ResponseOutputItem +from .response_text_config import ResponseTextConfig +from .tool_choice_function import ToolChoiceFunction + +__all__ = ["Response", "IncompleteDetails", "ToolChoice"] + + +class IncompleteDetails(BaseModel): + reason: Optional[Literal["max_output_tokens", "content_filter"]] = None + """The reason why the response is incomplete.""" + + +ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceTypes, ToolChoiceFunction] + + +class Response(BaseModel): + id: str + """Unique identifier for this Response.""" + + created_at: float + """Unix timestamp (in seconds) of when this Response was created.""" + + error: Optional[ResponseError] = None + """An error object returned when the model fails to generate a Response.""" + + incomplete_details: Optional[IncompleteDetails] = None + """Details about why the response is incomplete.""" + + instructions: Optional[str] = None + """ + Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[str, ChatModel] + """Model ID used to generate the response, like `gpt-4o` or `o1`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + object: Literal["response"] + """The object type of this resource - always set to `response`.""" + + output: List[ResponseOutputItem] + """An array of content items generated by the model. + + - The length and order of items in the `output` array is dependent on the + model's response. + - Rather than accessing the first item in the `output` array and assuming it's + an `assistant` message with the content generated by the model, you might + consider using the `output_text` property where supported in SDKs. + """ + + parallel_tool_calls: bool + """Whether to allow the model to run tool calls in parallel.""" + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: List[Tool] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + """ + + max_output_tokens: Optional[int] = None + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + + previous_response_id: Optional[str] = None + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + """ + + reasoning: Optional[Reasoning] = None + """**o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + status: Optional[ResponseStatus] = None + """The status of the response generation. + + One of `completed`, `failed`, `in_progress`, or `incomplete`. + """ + + text: Optional[ResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + truncation: Optional[Literal["auto", "disabled"]] = None + """The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + usage: Optional[ResponseUsage] = None + """ + Represents token usage details including input tokens, output tokens, a + breakdown of output tokens, and the total tokens used. + """ + + user: Optional[str] = None + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + """ + + @property + def output_text(self) -> str: + """Convenience property that aggregates all `output_text` items from the `output` + list. + + If no `output_text` content blocks exist, then an empty string is returned. + """ + texts: List[str] = [] + for output in self.output: + if output.type == "message": + for content in output.content: + if content.type == "output_text": + texts.append(content.text) + + return "".join(texts) diff --git a/src/openai/types/responses/response_audio_delta_event.py b/src/openai/types/responses/response_audio_delta_event.py new file mode 100644 index 0000000000..f3d77fac52 --- /dev/null +++ b/src/openai/types/responses/response_audio_delta_event.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioDeltaEvent"] + + +class ResponseAudioDeltaEvent(BaseModel): + delta: str + """A chunk of Base64 encoded response audio bytes.""" + + type: Literal["response.audio.delta"] + """The type of the event. Always `response.audio.delta`.""" diff --git a/src/openai/types/responses/response_audio_done_event.py b/src/openai/types/responses/response_audio_done_event.py new file mode 100644 index 0000000000..5654f8e398 --- /dev/null +++ b/src/openai/types/responses/response_audio_done_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioDoneEvent"] + + +class ResponseAudioDoneEvent(BaseModel): + type: Literal["response.audio.done"] + """The type of the event. Always `response.audio.done`.""" diff --git a/src/openai/types/responses/response_audio_transcript_delta_event.py b/src/openai/types/responses/response_audio_transcript_delta_event.py new file mode 100644 index 0000000000..69b6660f3f --- /dev/null +++ b/src/openai/types/responses/response_audio_transcript_delta_event.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDeltaEvent"] + + +class ResponseAudioTranscriptDeltaEvent(BaseModel): + delta: str + """The partial transcript of the audio response.""" + + type: Literal["response.audio.transcript.delta"] + """The type of the event. Always `response.audio.transcript.delta`.""" diff --git a/src/openai/types/responses/response_audio_transcript_done_event.py b/src/openai/types/responses/response_audio_transcript_done_event.py new file mode 100644 index 0000000000..1a20319f83 --- /dev/null +++ b/src/openai/types/responses/response_audio_transcript_done_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDoneEvent"] + + +class ResponseAudioTranscriptDoneEvent(BaseModel): + type: Literal["response.audio.transcript.done"] + """The type of the event. Always `response.audio.transcript.done`.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py b/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py new file mode 100644 index 0000000000..7527238d06 --- /dev/null +++ b/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterCallCodeDeltaEvent"] + + +class ResponseCodeInterpreterCallCodeDeltaEvent(BaseModel): + delta: str + """The partial code snippet added by the code interpreter.""" + + output_index: int + """The index of the output item that the code interpreter call is in progress.""" + + type: Literal["response.code_interpreter_call.code.delta"] + """The type of the event. Always `response.code_interpreter_call.code.delta`.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_code_done_event.py b/src/openai/types/responses/response_code_interpreter_call_code_done_event.py new file mode 100644 index 0000000000..f84d4cf3e8 --- /dev/null +++ b/src/openai/types/responses/response_code_interpreter_call_code_done_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterCallCodeDoneEvent"] + + +class ResponseCodeInterpreterCallCodeDoneEvent(BaseModel): + code: str + """The final code snippet output by the code interpreter.""" + + output_index: int + """The index of the output item that the code interpreter call is in progress.""" + + type: Literal["response.code_interpreter_call.code.done"] + """The type of the event. Always `response.code_interpreter_call.code.done`.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_completed_event.py b/src/openai/types/responses/response_code_interpreter_call_completed_event.py new file mode 100644 index 0000000000..b0cb73fb72 --- /dev/null +++ b/src/openai/types/responses/response_code_interpreter_call_completed_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall + +__all__ = ["ResponseCodeInterpreterCallCompletedEvent"] + + +class ResponseCodeInterpreterCallCompletedEvent(BaseModel): + code_interpreter_call: ResponseCodeInterpreterToolCall + """A tool call to run code.""" + + output_index: int + """The index of the output item that the code interpreter call is in progress.""" + + type: Literal["response.code_interpreter_call.completed"] + """The type of the event. Always `response.code_interpreter_call.completed`.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py b/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py new file mode 100644 index 0000000000..64b739f308 --- /dev/null +++ b/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall + +__all__ = ["ResponseCodeInterpreterCallInProgressEvent"] + + +class ResponseCodeInterpreterCallInProgressEvent(BaseModel): + code_interpreter_call: ResponseCodeInterpreterToolCall + """A tool call to run code.""" + + output_index: int + """The index of the output item that the code interpreter call is in progress.""" + + type: Literal["response.code_interpreter_call.in_progress"] + """The type of the event. Always `response.code_interpreter_call.in_progress`.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py b/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py new file mode 100644 index 0000000000..3100eac175 --- /dev/null +++ b/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall + +__all__ = ["ResponseCodeInterpreterCallInterpretingEvent"] + + +class ResponseCodeInterpreterCallInterpretingEvent(BaseModel): + code_interpreter_call: ResponseCodeInterpreterToolCall + """A tool call to run code.""" + + output_index: int + """The index of the output item that the code interpreter call is in progress.""" + + type: Literal["response.code_interpreter_call.interpreting"] + """The type of the event. Always `response.code_interpreter_call.interpreting`.""" diff --git a/src/openai/types/responses/response_code_interpreter_tool_call.py b/src/openai/types/responses/response_code_interpreter_tool_call.py new file mode 100644 index 0000000000..d5a5057074 --- /dev/null +++ b/src/openai/types/responses/response_code_interpreter_tool_call.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["ResponseCodeInterpreterToolCall", "Result", "ResultLogs", "ResultFiles", "ResultFilesFile"] + + +class ResultLogs(BaseModel): + logs: str + """The logs of the code interpreter tool call.""" + + type: Literal["logs"] + """The type of the code interpreter text output. Always `logs`.""" + + +class ResultFilesFile(BaseModel): + file_id: str + """The ID of the file.""" + + mime_type: str + """The MIME type of the file.""" + + +class ResultFiles(BaseModel): + files: List[ResultFilesFile] + + type: Literal["files"] + """The type of the code interpreter file output. Always `files`.""" + + +Result: TypeAlias = Annotated[Union[ResultLogs, ResultFiles], PropertyInfo(discriminator="type")] + + +class ResponseCodeInterpreterToolCall(BaseModel): + id: str + """The unique ID of the code interpreter tool call.""" + + code: str + """The code to run.""" + + results: List[Result] + """The results of the code interpreter tool call.""" + + status: Literal["in_progress", "interpreting", "completed"] + """The status of the code interpreter tool call.""" + + type: Literal["code_interpreter_call"] + """The type of the code interpreter tool call. Always `code_interpreter_call`.""" diff --git a/src/openai/types/responses/response_completed_event.py b/src/openai/types/responses/response_completed_event.py new file mode 100644 index 0000000000..a944f248ef --- /dev/null +++ b/src/openai/types/responses/response_completed_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseCompletedEvent"] + + +class ResponseCompletedEvent(BaseModel): + response: Response + """Properties of the completed response.""" + + type: Literal["response.completed"] + """The type of the event. Always `response.completed`.""" diff --git a/src/openai/types/responses/response_computer_tool_call.py b/src/openai/types/responses/response_computer_tool_call.py new file mode 100644 index 0000000000..994837567a --- /dev/null +++ b/src/openai/types/responses/response_computer_tool_call.py @@ -0,0 +1,212 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseComputerToolCall", + "Action", + "ActionClick", + "ActionDoubleClick", + "ActionDrag", + "ActionDragPath", + "ActionKeypress", + "ActionMove", + "ActionScreenshot", + "ActionScroll", + "ActionType", + "ActionWait", + "PendingSafetyCheck", +] + + +class ActionClick(BaseModel): + button: Literal["left", "right", "wheel", "back", "forward"] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Literal["click"] + """Specifies the event type. + + For a click action, this property is always set to `click`. + """ + + x: int + """The x-coordinate where the click occurred.""" + + y: int + """The y-coordinate where the click occurred.""" + + +class ActionDoubleClick(BaseModel): + type: Literal["double_click"] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: int + """The x-coordinate where the double click occurred.""" + + y: int + """The y-coordinate where the double click occurred.""" + + +class ActionDragPath(BaseModel): + x: int + """The x-coordinate.""" + + y: int + """The y-coordinate.""" + + +class ActionDrag(BaseModel): + path: List[ActionDragPath] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Literal["drag"] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + +class ActionKeypress(BaseModel): + keys: List[str] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Literal["keypress"] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class ActionMove(BaseModel): + type: Literal["move"] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: int + """The x-coordinate to move to.""" + + y: int + """The y-coordinate to move to.""" + + +class ActionScreenshot(BaseModel): + type: Literal["screenshot"] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class ActionScroll(BaseModel): + scroll_x: int + """The horizontal scroll distance.""" + + scroll_y: int + """The vertical scroll distance.""" + + type: Literal["scroll"] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: int + """The x-coordinate where the scroll occurred.""" + + y: int + """The y-coordinate where the scroll occurred.""" + + +class ActionType(BaseModel): + text: str + """The text to type.""" + + type: Literal["type"] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class ActionWait(BaseModel): + type: Literal["wait"] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +Action: TypeAlias = Annotated[ + Union[ + ActionClick, + ActionDoubleClick, + ActionDrag, + ActionKeypress, + ActionMove, + ActionScreenshot, + ActionScroll, + ActionType, + ActionWait, + ], + PropertyInfo(discriminator="type"), +] + + +class PendingSafetyCheck(BaseModel): + id: str + """The ID of the pending safety check.""" + + code: str + """The type of the pending safety check.""" + + message: str + """Details about the pending safety check.""" + + +class ResponseComputerToolCall(BaseModel): + id: str + """The unique ID of the computer call.""" + + action: Action + """A click action.""" + + call_id: str + """An identifier used when responding to the tool call with output.""" + + pending_safety_checks: List[PendingSafetyCheck] + """The pending safety checks for the computer call.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["computer_call"] + """The type of the computer call. Always `computer_call`.""" diff --git a/src/openai/types/responses/response_computer_tool_call_param.py b/src/openai/types/responses/response_computer_tool_call_param.py new file mode 100644 index 0000000000..d4ef56ab5c --- /dev/null +++ b/src/openai/types/responses/response_computer_tool_call_param.py @@ -0,0 +1,208 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "ResponseComputerToolCallParam", + "Action", + "ActionClick", + "ActionDoubleClick", + "ActionDrag", + "ActionDragPath", + "ActionKeypress", + "ActionMove", + "ActionScreenshot", + "ActionScroll", + "ActionType", + "ActionWait", + "PendingSafetyCheck", +] + + +class ActionClick(TypedDict, total=False): + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. + + For a click action, this property is always set to `click`. + """ + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + +class ActionDoubleClick(TypedDict, total=False): + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class ActionDragPath(TypedDict, total=False): + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class ActionDrag(TypedDict, total=False): + path: Required[Iterable[ActionDragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + +class ActionKeypress(TypedDict, total=False): + keys: Required[List[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class ActionMove(TypedDict, total=False): + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + +class ActionScreenshot(TypedDict, total=False): + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class ActionScroll(TypedDict, total=False): + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + +class ActionType(TypedDict, total=False): + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class ActionWait(TypedDict, total=False): + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +Action: TypeAlias = Union[ + ActionClick, + ActionDoubleClick, + ActionDrag, + ActionKeypress, + ActionMove, + ActionScreenshot, + ActionScroll, + ActionType, + ActionWait, +] + + +class PendingSafetyCheck(TypedDict, total=False): + id: Required[str] + """The ID of the pending safety check.""" + + code: Required[str] + """The type of the pending safety check.""" + + message: Required[str] + """Details about the pending safety check.""" + + +class ResponseComputerToolCallParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the computer call.""" + + action: Required[Action] + """A click action.""" + + call_id: Required[str] + """An identifier used when responding to the tool call with output.""" + + pending_safety_checks: Required[Iterable[PendingSafetyCheck]] + """The pending safety checks for the computer call.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Required[Literal["computer_call"]] + """The type of the computer call. Always `computer_call`.""" diff --git a/src/openai/types/responses/response_content_part_added_event.py b/src/openai/types/responses/response_content_part_added_event.py new file mode 100644 index 0000000000..93f5ec4b0c --- /dev/null +++ b/src/openai/types/responses/response_content_part_added_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_output_text import ResponseOutputText +from .response_output_refusal import ResponseOutputRefusal + +__all__ = ["ResponseContentPartAddedEvent", "Part"] + +Part: TypeAlias = Annotated[Union[ResponseOutputText, ResponseOutputRefusal], PropertyInfo(discriminator="type")] + + +class ResponseContentPartAddedEvent(BaseModel): + content_index: int + """The index of the content part that was added.""" + + item_id: str + """The ID of the output item that the content part was added to.""" + + output_index: int + """The index of the output item that the content part was added to.""" + + part: Part + """The content part that was added.""" + + type: Literal["response.content_part.added"] + """The type of the event. Always `response.content_part.added`.""" diff --git a/src/openai/types/responses/response_content_part_done_event.py b/src/openai/types/responses/response_content_part_done_event.py new file mode 100644 index 0000000000..4ec0739877 --- /dev/null +++ b/src/openai/types/responses/response_content_part_done_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_output_text import ResponseOutputText +from .response_output_refusal import ResponseOutputRefusal + +__all__ = ["ResponseContentPartDoneEvent", "Part"] + +Part: TypeAlias = Annotated[Union[ResponseOutputText, ResponseOutputRefusal], PropertyInfo(discriminator="type")] + + +class ResponseContentPartDoneEvent(BaseModel): + content_index: int + """The index of the content part that is done.""" + + item_id: str + """The ID of the output item that the content part was added to.""" + + output_index: int + """The index of the output item that the content part was added to.""" + + part: Part + """The content part that is done.""" + + type: Literal["response.content_part.done"] + """The type of the event. Always `response.content_part.done`.""" diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py new file mode 100644 index 0000000000..d5b2fdeb1a --- /dev/null +++ b/src/openai/types/responses/response_create_params.py @@ -0,0 +1,204 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .tool_param import ToolParam +from ..shared.chat_model import ChatModel +from .response_includable import ResponseIncludable +from .tool_choice_options import ToolChoiceOptions +from .response_input_param import ResponseInputParam +from ..shared_params.metadata import Metadata +from .tool_choice_types_param import ToolChoiceTypesParam +from ..shared_params.reasoning import Reasoning +from .response_text_config_param import ResponseTextConfigParam +from .tool_choice_function_param import ToolChoiceFunctionParam + +__all__ = [ + "ResponseCreateParamsBase", + "ToolChoice", + "ResponseCreateParamsNonStreaming", + "ResponseCreateParamsStreaming", +] + + +class ResponseCreateParamsBase(TypedDict, total=False): + input: Required[Union[str, ResponseInputParam]] + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + """ + + model: Required[Union[str, ChatModel]] + """Model ID used to generate the response, like `gpt-4o` or `o1`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + include: Optional[List[ResponseIncludable]] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + """ + + instructions: Optional[str] + """ + Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + """ + + reasoning: Optional[Reasoning] + """**o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + store: Optional[bool] + """Whether to store the generated model response for later retrieval via API.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: ResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Iterable[ToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] + """The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: str + """ + A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + """ + + +ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceTypesParam, ToolChoiceFunctionParam] + + +class ResponseCreateParamsNonStreaming(ResponseCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + +class ResponseCreateParamsStreaming(ResponseCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + +ResponseCreateParams = Union[ResponseCreateParamsNonStreaming, ResponseCreateParamsStreaming] diff --git a/src/openai/types/responses/response_created_event.py b/src/openai/types/responses/response_created_event.py new file mode 100644 index 0000000000..7a524cec87 --- /dev/null +++ b/src/openai/types/responses/response_created_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseCreatedEvent"] + + +class ResponseCreatedEvent(BaseModel): + response: Response + """The response that was created.""" + + type: Literal["response.created"] + """The type of the event. Always `response.created`.""" diff --git a/src/openai/types/responses/response_error.py b/src/openai/types/responses/response_error.py new file mode 100644 index 0000000000..90f1fcf5da --- /dev/null +++ b/src/openai/types/responses/response_error.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseError"] + + +class ResponseError(BaseModel): + code: Literal[ + "server_error", + "rate_limit_exceeded", + "invalid_prompt", + "vector_store_timeout", + "invalid_image", + "invalid_image_format", + "invalid_base64_image", + "invalid_image_url", + "image_too_large", + "image_too_small", + "image_parse_error", + "image_content_policy_violation", + "invalid_image_mode", + "image_file_too_large", + "unsupported_image_media_type", + "empty_image_file", + "failed_to_download_image", + "image_file_not_found", + ] + """The error code for the response.""" + + message: str + """A human-readable description of the error.""" diff --git a/src/openai/types/responses/response_error_event.py b/src/openai/types/responses/response_error_event.py new file mode 100644 index 0000000000..1b7e605d02 --- /dev/null +++ b/src/openai/types/responses/response_error_event.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseErrorEvent"] + + +class ResponseErrorEvent(BaseModel): + code: Optional[str] = None + """The error code.""" + + message: str + """The error message.""" + + param: Optional[str] = None + """The error parameter.""" + + type: Literal["error"] + """The type of the event. Always `error`.""" diff --git a/src/openai/types/responses/response_failed_event.py b/src/openai/types/responses/response_failed_event.py new file mode 100644 index 0000000000..3e8f75d8c4 --- /dev/null +++ b/src/openai/types/responses/response_failed_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseFailedEvent"] + + +class ResponseFailedEvent(BaseModel): + response: Response + """The response that failed.""" + + type: Literal["response.failed"] + """The type of the event. Always `response.failed`.""" diff --git a/src/openai/types/responses/response_file_search_call_completed_event.py b/src/openai/types/responses/response_file_search_call_completed_event.py new file mode 100644 index 0000000000..4b86083369 --- /dev/null +++ b/src/openai/types/responses/response_file_search_call_completed_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFileSearchCallCompletedEvent"] + + +class ResponseFileSearchCallCompletedEvent(BaseModel): + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is initiated.""" + + type: Literal["response.file_search_call.completed"] + """The type of the event. Always `response.file_search_call.completed`.""" diff --git a/src/openai/types/responses/response_file_search_call_in_progress_event.py b/src/openai/types/responses/response_file_search_call_in_progress_event.py new file mode 100644 index 0000000000..eb42e3dad6 --- /dev/null +++ b/src/openai/types/responses/response_file_search_call_in_progress_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFileSearchCallInProgressEvent"] + + +class ResponseFileSearchCallInProgressEvent(BaseModel): + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is initiated.""" + + type: Literal["response.file_search_call.in_progress"] + """The type of the event. Always `response.file_search_call.in_progress`.""" diff --git a/src/openai/types/responses/response_file_search_call_searching_event.py b/src/openai/types/responses/response_file_search_call_searching_event.py new file mode 100644 index 0000000000..3cd8905de6 --- /dev/null +++ b/src/openai/types/responses/response_file_search_call_searching_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFileSearchCallSearchingEvent"] + + +class ResponseFileSearchCallSearchingEvent(BaseModel): + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is searching.""" + + type: Literal["response.file_search_call.searching"] + """The type of the event. Always `response.file_search_call.searching`.""" diff --git a/src/openai/types/responses/response_file_search_tool_call.py b/src/openai/types/responses/response_file_search_tool_call.py new file mode 100644 index 0000000000..ef1c6a5608 --- /dev/null +++ b/src/openai/types/responses/response_file_search_tool_call.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFileSearchToolCall", "Result"] + + +class Result(BaseModel): + attributes: Optional[Dict[str, Union[str, float, bool]]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + file_id: Optional[str] = None + """The unique ID of the file.""" + + filename: Optional[str] = None + """The name of the file.""" + + score: Optional[float] = None + """The relevance score of the file - a value between 0 and 1.""" + + text: Optional[str] = None + """The text that was retrieved from the file.""" + + +class ResponseFileSearchToolCall(BaseModel): + id: str + """The unique ID of the file search tool call.""" + + queries: List[str] + """The queries used to search for files.""" + + status: Literal["in_progress", "searching", "completed", "incomplete", "failed"] + """The status of the file search tool call. + + One of `in_progress`, `searching`, `incomplete` or `failed`, + """ + + type: Literal["file_search_call"] + """The type of the file search tool call. Always `file_search_call`.""" + + results: Optional[List[Result]] = None + """The results of the file search tool call.""" diff --git a/src/openai/types/responses/response_file_search_tool_call_param.py b/src/openai/types/responses/response_file_search_tool_call_param.py new file mode 100644 index 0000000000..9a4177cf81 --- /dev/null +++ b/src/openai/types/responses/response_file_search_tool_call_param.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFileSearchToolCallParam", "Result"] + + +class Result(TypedDict, total=False): + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + file_id: str + """The unique ID of the file.""" + + filename: str + """The name of the file.""" + + score: float + """The relevance score of the file - a value between 0 and 1.""" + + text: str + """The text that was retrieved from the file.""" + + +class ResponseFileSearchToolCallParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the file search tool call.""" + + queries: Required[List[str]] + """The queries used to search for files.""" + + status: Required[Literal["in_progress", "searching", "completed", "incomplete", "failed"]] + """The status of the file search tool call. + + One of `in_progress`, `searching`, `incomplete` or `failed`, + """ + + type: Required[Literal["file_search_call"]] + """The type of the file search tool call. Always `file_search_call`.""" + + results: Optional[Iterable[Result]] + """The results of the file search tool call.""" diff --git a/src/openai/types/responses/response_format_text_config.py b/src/openai/types/responses/response_format_text_config.py new file mode 100644 index 0000000000..a4896bf9fe --- /dev/null +++ b/src/openai/types/responses/response_format_text_config.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..shared.response_format_text import ResponseFormatText +from ..shared.response_format_json_object import ResponseFormatJSONObject +from .response_format_text_json_schema_config import ResponseFormatTextJSONSchemaConfig + +__all__ = ["ResponseFormatTextConfig"] + +ResponseFormatTextConfig: TypeAlias = Annotated[ + Union[ResponseFormatText, ResponseFormatTextJSONSchemaConfig, ResponseFormatJSONObject], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/responses/response_format_text_config_param.py b/src/openai/types/responses/response_format_text_config_param.py new file mode 100644 index 0000000000..fcaf8f3fb6 --- /dev/null +++ b/src/openai/types/responses/response_format_text_config_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from ..shared_params.response_format_text import ResponseFormatText +from ..shared_params.response_format_json_object import ResponseFormatJSONObject +from .response_format_text_json_schema_config_param import ResponseFormatTextJSONSchemaConfigParam + +__all__ = ["ResponseFormatTextConfigParam"] + +ResponseFormatTextConfigParam: TypeAlias = Union[ + ResponseFormatText, ResponseFormatTextJSONSchemaConfigParam, ResponseFormatJSONObject +] diff --git a/src/openai/types/responses/response_format_text_json_schema_config.py b/src/openai/types/responses/response_format_text_json_schema_config.py new file mode 100644 index 0000000000..3cf066370f --- /dev/null +++ b/src/openai/types/responses/response_format_text_json_schema_config.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ResponseFormatTextJSONSchemaConfig"] + + +class ResponseFormatTextJSONSchemaConfig(BaseModel): + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + type: Literal["json_schema"] + """The type of response format being defined. Always `json_schema`.""" + + description: Optional[str] = None + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + name: Optional[str] = None + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + strict: Optional[bool] = None + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + """ diff --git a/src/openai/types/responses/response_format_text_json_schema_config_param.py b/src/openai/types/responses/response_format_text_json_schema_config_param.py new file mode 100644 index 0000000000..211c5d1eff --- /dev/null +++ b/src/openai/types/responses/response_format_text_json_schema_config_param.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFormatTextJSONSchemaConfigParam"] + + +class ResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False): + schema: Required[Dict[str, object]] + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + type: Required[Literal["json_schema"]] + """The type of response format being defined. Always `json_schema`.""" + + description: str + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + name: str + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + strict: Optional[bool] + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + """ diff --git a/src/openai/types/responses/response_function_call_arguments_delta_event.py b/src/openai/types/responses/response_function_call_arguments_delta_event.py new file mode 100644 index 0000000000..0989b7caeb --- /dev/null +++ b/src/openai/types/responses/response_function_call_arguments_delta_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDeltaEvent"] + + +class ResponseFunctionCallArgumentsDeltaEvent(BaseModel): + delta: str + """The function-call arguments delta that is added.""" + + item_id: str + """The ID of the output item that the function-call arguments delta is added to.""" + + output_index: int + """ + The index of the output item that the function-call arguments delta is added to. + """ + + type: Literal["response.function_call_arguments.delta"] + """The type of the event. Always `response.function_call_arguments.delta`.""" diff --git a/src/openai/types/responses/response_function_call_arguments_done_event.py b/src/openai/types/responses/response_function_call_arguments_done_event.py new file mode 100644 index 0000000000..1d805a57c6 --- /dev/null +++ b/src/openai/types/responses/response_function_call_arguments_done_event.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDoneEvent"] + + +class ResponseFunctionCallArgumentsDoneEvent(BaseModel): + arguments: str + """The function-call arguments.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item.""" + + type: Literal["response.function_call_arguments.done"] diff --git a/src/openai/types/responses/response_function_tool_call.py b/src/openai/types/responses/response_function_tool_call.py new file mode 100644 index 0000000000..5d82906cb7 --- /dev/null +++ b/src/openai/types/responses/response_function_tool_call.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionToolCall"] + + +class ResponseFunctionToolCall(BaseModel): + id: str + """The unique ID of the function tool call.""" + + arguments: str + """A JSON string of the arguments to pass to the function.""" + + call_id: str + """The unique ID of the function tool call generated by the model.""" + + name: str + """The name of the function to run.""" + + type: Literal["function_call"] + """The type of the function tool call. Always `function_call`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/responses/response_function_tool_call_param.py b/src/openai/types/responses/response_function_tool_call_param.py new file mode 100644 index 0000000000..51b947a764 --- /dev/null +++ b/src/openai/types/responses/response_function_tool_call_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFunctionToolCallParam"] + + +class ResponseFunctionToolCallParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the function tool call.""" + + arguments: Required[str] + """A JSON string of the arguments to pass to the function.""" + + call_id: Required[str] + """The unique ID of the function tool call generated by the model.""" + + name: Required[str] + """The name of the function to run.""" + + type: Required[Literal["function_call"]] + """The type of the function tool call. Always `function_call`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py new file mode 100644 index 0000000000..44734b681f --- /dev/null +++ b/src/openai/types/responses/response_function_web_search.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionWebSearch"] + + +class ResponseFunctionWebSearch(BaseModel): + id: str + """The unique ID of the web search tool call.""" + + status: Literal["in_progress", "searching", "completed", "failed"] + """The status of the web search tool call.""" + + type: Literal["web_search_call"] + """The type of the web search tool call. Always `web_search_call`.""" diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py new file mode 100644 index 0000000000..d413e60b12 --- /dev/null +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseFunctionWebSearchParam"] + + +class ResponseFunctionWebSearchParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the web search tool call.""" + + status: Required[Literal["in_progress", "searching", "completed", "failed"]] + """The status of the web search tool call.""" + + type: Required[Literal["web_search_call"]] + """The type of the web search tool call. Always `web_search_call`.""" diff --git a/src/openai/types/responses/response_in_progress_event.py b/src/openai/types/responses/response_in_progress_event.py new file mode 100644 index 0000000000..7d96cbb8ad --- /dev/null +++ b/src/openai/types/responses/response_in_progress_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseInProgressEvent"] + + +class ResponseInProgressEvent(BaseModel): + response: Response + """The response that is in progress.""" + + type: Literal["response.in_progress"] + """The type of the event. Always `response.in_progress`.""" diff --git a/src/openai/types/responses/response_includable.py b/src/openai/types/responses/response_includable.py new file mode 100644 index 0000000000..83489fa7f1 --- /dev/null +++ b/src/openai/types/responses/response_includable.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ResponseIncludable"] + +ResponseIncludable: TypeAlias = Literal[ + "file_search_call.results", "message.input_image.image_url", "computer_call_output.output.image_url" +] diff --git a/src/openai/types/responses/response_incomplete_event.py b/src/openai/types/responses/response_incomplete_event.py new file mode 100644 index 0000000000..742b789c7e --- /dev/null +++ b/src/openai/types/responses/response_incomplete_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .response import Response +from ..._models import BaseModel + +__all__ = ["ResponseIncompleteEvent"] + + +class ResponseIncompleteEvent(BaseModel): + response: Response + """The response that was incomplete.""" + + type: Literal["response.incomplete"] + """The type of the event. Always `response.incomplete`.""" diff --git a/src/openai/types/responses/response_input_content.py b/src/openai/types/responses/response_input_content.py new file mode 100644 index 0000000000..1726909a17 --- /dev/null +++ b/src/openai/types/responses/response_input_content.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_input_file import ResponseInputFile +from .response_input_text import ResponseInputText +from .response_input_image import ResponseInputImage + +__all__ = ["ResponseInputContent"] + +ResponseInputContent: TypeAlias = Annotated[ + Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/responses/response_input_content_param.py b/src/openai/types/responses/response_input_content_param.py new file mode 100644 index 0000000000..7791cdfd8e --- /dev/null +++ b/src/openai/types/responses/response_input_content_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .response_input_file_param import ResponseInputFileParam +from .response_input_text_param import ResponseInputTextParam +from .response_input_image_param import ResponseInputImageParam + +__all__ = ["ResponseInputContentParam"] + +ResponseInputContentParam: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] diff --git a/src/openai/types/responses/response_input_file.py b/src/openai/types/responses/response_input_file.py new file mode 100644 index 0000000000..00b35dc844 --- /dev/null +++ b/src/openai/types/responses/response_input_file.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputFile"] + + +class ResponseInputFile(BaseModel): + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + file_data: Optional[str] = None + """The content of the file to be sent to the model.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py new file mode 100644 index 0000000000..dc06a4ea2d --- /dev/null +++ b/src/openai/types/responses/response_input_file_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputFileParam"] + + +class ResponseInputFileParam(TypedDict, total=False): + type: Required[Literal["input_file"]] + """The type of the input item. Always `input_file`.""" + + file_data: str + """The content of the file to be sent to the model.""" + + file_id: str + """The ID of the file to be sent to the model.""" + + filename: str + """The name of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_image.py b/src/openai/types/responses/response_input_image.py new file mode 100644 index 0000000000..d719f44e9b --- /dev/null +++ b/src/openai/types/responses/response_input_image.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputImage"] + + +class ResponseInputImage(BaseModel): + detail: Literal["high", "low", "auto"] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ diff --git a/src/openai/types/responses/response_input_image_param.py b/src/openai/types/responses/response_input_image_param.py new file mode 100644 index 0000000000..5dd4db2b5d --- /dev/null +++ b/src/openai/types/responses/response_input_image_param.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputImageParam"] + + +class ResponseInputImageParam(TypedDict, total=False): + detail: Required[Literal["high", "low", "auto"]] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + type: Required[Literal["input_image"]] + """The type of the input item. Always `input_image`.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py new file mode 100644 index 0000000000..c9daaa6a89 --- /dev/null +++ b/src/openai/types/responses/response_input_item_param.py @@ -0,0 +1,174 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .easy_input_message_param import EasyInputMessageParam +from .response_output_message_param import ResponseOutputMessageParam +from .response_computer_tool_call_param import ResponseComputerToolCallParam +from .response_function_tool_call_param import ResponseFunctionToolCallParam +from .response_function_web_search_param import ResponseFunctionWebSearchParam +from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam +from .response_input_message_content_list_param import ResponseInputMessageContentListParam + +__all__ = [ + "ResponseInputItemParam", + "Message", + "ComputerCallOutput", + "ComputerCallOutputOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "FunctionCallOutput", + "Reasoning", + "ReasoningContent", + "ItemReference", +] + + +class Message(TypedDict, total=False): + content: Required[ResponseInputMessageContentListParam] + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Required[Literal["user", "system", "developer"]] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputOutput(TypedDict, total=False): + type: Required[Literal["computer_screenshot"]] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: str + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: str + """The URL of the screenshot image.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + id: Required[str] + """The ID of the pending safety check.""" + + code: Required[str] + """The type of the pending safety check.""" + + message: Required[str] + """Details about the pending safety check.""" + + +class ComputerCallOutput(TypedDict, total=False): + call_id: Required[str] + """The ID of the computer tool call that produced the output.""" + + output: Required[ComputerCallOutputOutput] + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_call_output"]] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: str + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Iterable[ComputerCallOutputAcknowledgedSafetyCheck] + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutput(TypedDict, total=False): + call_id: Required[str] + """The unique ID of the function tool call generated by the model.""" + + output: Required[str] + """A JSON string of the output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: str + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class ReasoningContent(TypedDict, total=False): + text: Required[str] + """ + A short summary of the reasoning used by the model when generating the response. + """ + + type: Required[Literal["reasoning_summary"]] + """The type of the object. Always `text`.""" + + +class Reasoning(TypedDict, total=False): + id: Required[str] + """The unique identifier of the reasoning content.""" + + content: Required[Iterable[ReasoningContent]] + """Reasoning text contents.""" + + type: Required[Literal["reasoning"]] + """The type of the object. Always `reasoning`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class ItemReference(TypedDict, total=False): + id: Required[str] + """The ID of the item to reference.""" + + type: Required[Literal["item_reference"]] + """The type of item to reference. Always `item_reference`.""" + + +ResponseInputItemParam: TypeAlias = Union[ + EasyInputMessageParam, + Message, + ResponseOutputMessageParam, + ResponseFileSearchToolCallParam, + ResponseComputerToolCallParam, + ComputerCallOutput, + ResponseFunctionWebSearchParam, + ResponseFunctionToolCallParam, + FunctionCallOutput, + Reasoning, + ItemReference, +] diff --git a/src/openai/types/responses/response_input_message_content_list.py b/src/openai/types/responses/response_input_message_content_list.py new file mode 100644 index 0000000000..99b7c10f12 --- /dev/null +++ b/src/openai/types/responses/response_input_message_content_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .response_input_content import ResponseInputContent + +__all__ = ["ResponseInputMessageContentList"] + +ResponseInputMessageContentList: TypeAlias = List[ResponseInputContent] diff --git a/src/openai/types/responses/response_input_message_content_list_param.py b/src/openai/types/responses/response_input_message_content_list_param.py new file mode 100644 index 0000000000..080613df0d --- /dev/null +++ b/src/openai/types/responses/response_input_message_content_list_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import TypeAlias + +from .response_input_file_param import ResponseInputFileParam +from .response_input_text_param import ResponseInputTextParam +from .response_input_image_param import ResponseInputImageParam + +__all__ = ["ResponseInputMessageContentListParam", "ResponseInputContentParam"] + +ResponseInputContentParam: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] + +ResponseInputMessageContentListParam: TypeAlias = List[ResponseInputContentParam] diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py new file mode 100644 index 0000000000..c81308500d --- /dev/null +++ b/src/openai/types/responses/response_input_param.py @@ -0,0 +1,177 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .easy_input_message_param import EasyInputMessageParam +from .response_output_message_param import ResponseOutputMessageParam +from .response_computer_tool_call_param import ResponseComputerToolCallParam +from .response_function_tool_call_param import ResponseFunctionToolCallParam +from .response_function_web_search_param import ResponseFunctionWebSearchParam +from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam +from .response_input_message_content_list_param import ResponseInputMessageContentListParam + +__all__ = [ + "ResponseInputParam", + "ResponseInputItemParam", + "Message", + "ComputerCallOutput", + "ComputerCallOutputOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "FunctionCallOutput", + "Reasoning", + "ReasoningContent", + "ItemReference", +] + + +class Message(TypedDict, total=False): + content: Required[ResponseInputMessageContentListParam] + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Required[Literal["user", "system", "developer"]] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputOutput(TypedDict, total=False): + type: Required[Literal["computer_screenshot"]] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: str + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: str + """The URL of the screenshot image.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + id: Required[str] + """The ID of the pending safety check.""" + + code: Required[str] + """The type of the pending safety check.""" + + message: Required[str] + """Details about the pending safety check.""" + + +class ComputerCallOutput(TypedDict, total=False): + call_id: Required[str] + """The ID of the computer tool call that produced the output.""" + + output: Required[ComputerCallOutputOutput] + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_call_output"]] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: str + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Iterable[ComputerCallOutputAcknowledgedSafetyCheck] + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutput(TypedDict, total=False): + call_id: Required[str] + """The unique ID of the function tool call generated by the model.""" + + output: Required[str] + """A JSON string of the output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: str + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class ReasoningContent(TypedDict, total=False): + text: Required[str] + """ + A short summary of the reasoning used by the model when generating the response. + """ + + type: Required[Literal["reasoning_summary"]] + """The type of the object. Always `text`.""" + + +class Reasoning(TypedDict, total=False): + id: Required[str] + """The unique identifier of the reasoning content.""" + + content: Required[Iterable[ReasoningContent]] + """Reasoning text contents.""" + + type: Required[Literal["reasoning"]] + """The type of the object. Always `reasoning`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class ItemReference(TypedDict, total=False): + id: Required[str] + """The ID of the item to reference.""" + + type: Required[Literal["item_reference"]] + """The type of item to reference. Always `item_reference`.""" + + +ResponseInputItemParam: TypeAlias = Union[ + EasyInputMessageParam, + Message, + ResponseOutputMessageParam, + ResponseFileSearchToolCallParam, + ResponseComputerToolCallParam, + ComputerCallOutput, + ResponseFunctionWebSearchParam, + ResponseFunctionToolCallParam, + FunctionCallOutput, + Reasoning, + ItemReference, +] + +ResponseInputParam: TypeAlias = List[ResponseInputItemParam] diff --git a/src/openai/types/responses/response_input_text.py b/src/openai/types/responses/response_input_text.py new file mode 100644 index 0000000000..ba8d1ea18b --- /dev/null +++ b/src/openai/types/responses/response_input_text.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputText"] + + +class ResponseInputText(BaseModel): + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" diff --git a/src/openai/types/responses/response_input_text_param.py b/src/openai/types/responses/response_input_text_param.py new file mode 100644 index 0000000000..f2ba834082 --- /dev/null +++ b/src/openai/types/responses/response_input_text_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputTextParam"] + + +class ResponseInputTextParam(TypedDict, total=False): + text: Required[str] + """The text input to the model.""" + + type: Required[Literal["input_text"]] + """The type of the input item. Always `input_text`.""" diff --git a/src/openai/types/responses/response_item_list.py b/src/openai/types/responses/response_item_list.py new file mode 100644 index 0000000000..7c3e4d7f82 --- /dev/null +++ b/src/openai/types/responses/response_item_list.py @@ -0,0 +1,152 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_output_message import ResponseOutputMessage +from .response_computer_tool_call import ResponseComputerToolCall +from .response_function_tool_call import ResponseFunctionToolCall +from .response_function_web_search import ResponseFunctionWebSearch +from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_input_message_content_list import ResponseInputMessageContentList + +__all__ = [ + "ResponseItemList", + "Data", + "DataMessage", + "DataComputerCallOutput", + "DataComputerCallOutputOutput", + "DataComputerCallOutputAcknowledgedSafetyCheck", + "DataFunctionCallOutput", +] + + +class DataMessage(BaseModel): + id: str + """The unique ID of the message input.""" + + content: ResponseInputMessageContentList + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Literal["user", "system", "developer"] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always set to `message`.""" + + +class DataComputerCallOutputOutput(BaseModel): + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" + + +class DataComputerCallOutputAcknowledgedSafetyCheck(BaseModel): + id: str + """The ID of the pending safety check.""" + + code: str + """The type of the pending safety check.""" + + message: str + """Details about the pending safety check.""" + + +class DataComputerCallOutput(BaseModel): + id: str + """The unique ID of the computer call tool output.""" + + call_id: str + """The ID of the computer tool call that produced the output.""" + + output: DataComputerCallOutputOutput + """A computer screenshot image used with the computer use tool.""" + + type: Literal["computer_call_output"] + """The type of the computer tool call output. Always `computer_call_output`.""" + + acknowledged_safety_checks: Optional[List[DataComputerCallOutputAcknowledgedSafetyCheck]] = None + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class DataFunctionCallOutput(BaseModel): + id: str + """The unique ID of the function call tool output.""" + + call_id: str + """The unique ID of the function tool call generated by the model.""" + + output: str + """A JSON string of the output of the function tool call.""" + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +Data: TypeAlias = Annotated[ + Union[ + DataMessage, + ResponseOutputMessage, + ResponseFileSearchToolCall, + ResponseComputerToolCall, + DataComputerCallOutput, + ResponseFunctionWebSearch, + ResponseFunctionToolCall, + DataFunctionCallOutput, + ], + PropertyInfo(discriminator="type"), +] + + +class ResponseItemList(BaseModel): + data: List[Data] + """A list of items used to generate this response.""" + + first_id: str + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: str + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py new file mode 100644 index 0000000000..45d5cc0094 --- /dev/null +++ b/src/openai/types/responses/response_output_item.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_output_message import ResponseOutputMessage +from .response_computer_tool_call import ResponseComputerToolCall +from .response_function_tool_call import ResponseFunctionToolCall +from .response_function_web_search import ResponseFunctionWebSearch +from .response_file_search_tool_call import ResponseFileSearchToolCall + +__all__ = ["ResponseOutputItem", "Reasoning", "ReasoningContent"] + + +class ReasoningContent(BaseModel): + text: str + """ + A short summary of the reasoning used by the model when generating the response. + """ + + type: Literal["reasoning_summary"] + """The type of the object. Always `text`.""" + + +class Reasoning(BaseModel): + id: str + """The unique identifier of the reasoning content.""" + + content: List[ReasoningContent] + """Reasoning text contents.""" + + type: Literal["reasoning"] + """The type of the object. Always `reasoning`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +ResponseOutputItem: TypeAlias = Annotated[ + Union[ + ResponseOutputMessage, + ResponseFileSearchToolCall, + ResponseFunctionToolCall, + ResponseFunctionWebSearch, + ResponseComputerToolCall, + Reasoning, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/responses/response_output_item_added_event.py b/src/openai/types/responses/response_output_item_added_event.py new file mode 100644 index 0000000000..7344fb9a6c --- /dev/null +++ b/src/openai/types/responses/response_output_item_added_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_output_item import ResponseOutputItem + +__all__ = ["ResponseOutputItemAddedEvent"] + + +class ResponseOutputItemAddedEvent(BaseModel): + item: ResponseOutputItem + """The output item that was added.""" + + output_index: int + """The index of the output item that was added.""" + + type: Literal["response.output_item.added"] + """The type of the event. Always `response.output_item.added`.""" diff --git a/src/openai/types/responses/response_output_item_done_event.py b/src/openai/types/responses/response_output_item_done_event.py new file mode 100644 index 0000000000..a0a871a019 --- /dev/null +++ b/src/openai/types/responses/response_output_item_done_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_output_item import ResponseOutputItem + +__all__ = ["ResponseOutputItemDoneEvent"] + + +class ResponseOutputItemDoneEvent(BaseModel): + item: ResponseOutputItem + """The output item that was marked done.""" + + output_index: int + """The index of the output item that was marked done.""" + + type: Literal["response.output_item.done"] + """The type of the event. Always `response.output_item.done`.""" diff --git a/src/openai/types/responses/response_output_message.py b/src/openai/types/responses/response_output_message.py new file mode 100644 index 0000000000..3864aa2111 --- /dev/null +++ b/src/openai/types/responses/response_output_message.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .response_output_text import ResponseOutputText +from .response_output_refusal import ResponseOutputRefusal + +__all__ = ["ResponseOutputMessage", "Content"] + +Content: TypeAlias = Annotated[Union[ResponseOutputText, ResponseOutputRefusal], PropertyInfo(discriminator="type")] + + +class ResponseOutputMessage(BaseModel): + id: str + """The unique ID of the output message.""" + + content: List[Content] + """The content of the output message.""" + + role: Literal["assistant"] + """The role of the output message. Always `assistant`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Literal["message"] + """The type of the output message. Always `message`.""" diff --git a/src/openai/types/responses/response_output_message_param.py b/src/openai/types/responses/response_output_message_param.py new file mode 100644 index 0000000000..46cbbd20de --- /dev/null +++ b/src/openai/types/responses/response_output_message_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .response_output_text_param import ResponseOutputTextParam +from .response_output_refusal_param import ResponseOutputRefusalParam + +__all__ = ["ResponseOutputMessageParam", "Content"] + +Content: TypeAlias = Union[ResponseOutputTextParam, ResponseOutputRefusalParam] + + +class ResponseOutputMessageParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the output message.""" + + content: Required[Iterable[Content]] + """The content of the output message.""" + + role: Required[Literal["assistant"]] + """The role of the output message. Always `assistant`.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Required[Literal["message"]] + """The type of the output message. Always `message`.""" diff --git a/src/openai/types/responses/response_output_refusal.py b/src/openai/types/responses/response_output_refusal.py new file mode 100644 index 0000000000..eba581070d --- /dev/null +++ b/src/openai/types/responses/response_output_refusal.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseOutputRefusal"] + + +class ResponseOutputRefusal(BaseModel): + refusal: str + """The refusal explanationfrom the model.""" + + type: Literal["refusal"] + """The type of the refusal. Always `refusal`.""" diff --git a/src/openai/types/responses/response_output_refusal_param.py b/src/openai/types/responses/response_output_refusal_param.py new file mode 100644 index 0000000000..53140a6080 --- /dev/null +++ b/src/openai/types/responses/response_output_refusal_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseOutputRefusalParam"] + + +class ResponseOutputRefusalParam(TypedDict, total=False): + refusal: Required[str] + """The refusal explanationfrom the model.""" + + type: Required[Literal["refusal"]] + """The type of the refusal. Always `refusal`.""" diff --git a/src/openai/types/responses/response_output_text.py b/src/openai/types/responses/response_output_text.py new file mode 100644 index 0000000000..fa653cd1af --- /dev/null +++ b/src/openai/types/responses/response_output_text.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["ResponseOutputText", "Annotation", "AnnotationFileCitation", "AnnotationURLCitation", "AnnotationFilePath"] + + +class AnnotationFileCitation(BaseModel): + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(BaseModel): + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url: str + """The URL of the web resource.""" + + +class AnnotationFilePath(BaseModel): + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_path"] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Annotated[ + Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationFilePath], PropertyInfo(discriminator="type") +] + + +class ResponseOutputText(BaseModel): + annotations: List[Annotation] + """The annotations of the text output.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" diff --git a/src/openai/types/responses/response_output_text_param.py b/src/openai/types/responses/response_output_text_param.py new file mode 100644 index 0000000000..1f0967285f --- /dev/null +++ b/src/openai/types/responses/response_output_text_param.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "ResponseOutputTextParam", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationFilePath", +] + + +class AnnotationFileCitation(TypedDict, total=False): + file_id: Required[str] + """The ID of the file.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_citation"]] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(TypedDict, total=False): + end_index: Required[int] + """The index of the last character of the URL citation in the message.""" + + start_index: Required[int] + """The index of the first character of the URL citation in the message.""" + + title: Required[str] + """The title of the web resource.""" + + type: Required[Literal["url_citation"]] + """The type of the URL citation. Always `url_citation`.""" + + url: Required[str] + """The URL of the web resource.""" + + +class AnnotationFilePath(TypedDict, total=False): + file_id: Required[str] + """The ID of the file.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_path"]] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationFilePath] + + +class ResponseOutputTextParam(TypedDict, total=False): + annotations: Required[Iterable[Annotation]] + """The annotations of the text output.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" diff --git a/src/openai/types/responses/response_refusal_delta_event.py b/src/openai/types/responses/response_refusal_delta_event.py new file mode 100644 index 0000000000..04dcdf1c8c --- /dev/null +++ b/src/openai/types/responses/response_refusal_delta_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseRefusalDeltaEvent"] + + +class ResponseRefusalDeltaEvent(BaseModel): + content_index: int + """The index of the content part that the refusal text is added to.""" + + delta: str + """The refusal text that is added.""" + + item_id: str + """The ID of the output item that the refusal text is added to.""" + + output_index: int + """The index of the output item that the refusal text is added to.""" + + type: Literal["response.refusal.delta"] + """The type of the event. Always `response.refusal.delta`.""" diff --git a/src/openai/types/responses/response_refusal_done_event.py b/src/openai/types/responses/response_refusal_done_event.py new file mode 100644 index 0000000000..a9b6f4b055 --- /dev/null +++ b/src/openai/types/responses/response_refusal_done_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseRefusalDoneEvent"] + + +class ResponseRefusalDoneEvent(BaseModel): + content_index: int + """The index of the content part that the refusal text is finalized.""" + + item_id: str + """The ID of the output item that the refusal text is finalized.""" + + output_index: int + """The index of the output item that the refusal text is finalized.""" + + refusal: str + """The refusal text that is finalized.""" + + type: Literal["response.refusal.done"] + """The type of the event. Always `response.refusal.done`.""" diff --git a/src/openai/types/responses/response_retrieve_params.py b/src/openai/types/responses/response_retrieve_params.py new file mode 100644 index 0000000000..137bf4dcee --- /dev/null +++ b/src/openai/types/responses/response_retrieve_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import TypedDict + +from .response_includable import ResponseIncludable + +__all__ = ["ResponseRetrieveParams"] + + +class ResponseRetrieveParams(TypedDict, total=False): + include: List[ResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for Response creation above for more information. + """ diff --git a/src/openai/types/responses/response_status.py b/src/openai/types/responses/response_status.py new file mode 100644 index 0000000000..934d17cda3 --- /dev/null +++ b/src/openai/types/responses/response_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ResponseStatus"] + +ResponseStatus: TypeAlias = Literal["completed", "failed", "in_progress", "incomplete"] diff --git a/src/openai/types/responses/response_stream_event.py b/src/openai/types/responses/response_stream_event.py new file mode 100644 index 0000000000..446863b175 --- /dev/null +++ b/src/openai/types/responses/response_stream_event.py @@ -0,0 +1,78 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_error_event import ResponseErrorEvent +from .response_failed_event import ResponseFailedEvent +from .response_created_event import ResponseCreatedEvent +from .response_completed_event import ResponseCompletedEvent +from .response_text_done_event import ResponseTextDoneEvent +from .response_audio_done_event import ResponseAudioDoneEvent +from .response_incomplete_event import ResponseIncompleteEvent +from .response_text_delta_event import ResponseTextDeltaEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent +from .response_in_progress_event import ResponseInProgressEvent +from .response_refusal_done_event import ResponseRefusalDoneEvent +from .response_refusal_delta_event import ResponseRefusalDeltaEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent +from .response_text_annotation_delta_event import ResponseTextAnnotationDeltaEvent +from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent +from .response_web_search_call_completed_event import ResponseWebSearchCallCompletedEvent +from .response_web_search_call_searching_event import ResponseWebSearchCallSearchingEvent +from .response_file_search_call_completed_event import ResponseFileSearchCallCompletedEvent +from .response_file_search_call_searching_event import ResponseFileSearchCallSearchingEvent +from .response_web_search_call_in_progress_event import ResponseWebSearchCallInProgressEvent +from .response_file_search_call_in_progress_event import ResponseFileSearchCallInProgressEvent +from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .response_code_interpreter_call_code_done_event import ResponseCodeInterpreterCallCodeDoneEvent +from .response_code_interpreter_call_completed_event import ResponseCodeInterpreterCallCompletedEvent +from .response_code_interpreter_call_code_delta_event import ResponseCodeInterpreterCallCodeDeltaEvent +from .response_code_interpreter_call_in_progress_event import ResponseCodeInterpreterCallInProgressEvent +from .response_code_interpreter_call_interpreting_event import ResponseCodeInterpreterCallInterpretingEvent + +__all__ = ["ResponseStreamEvent"] + +ResponseStreamEvent: TypeAlias = Annotated[ + Union[ + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseCompletedEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseErrorEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseInProgressEvent, + ResponseFailedEvent, + ResponseIncompleteEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseTextAnnotationDeltaEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/responses/response_text_annotation_delta_event.py b/src/openai/types/responses/response_text_annotation_delta_event.py new file mode 100644 index 0000000000..4f2582282a --- /dev/null +++ b/src/openai/types/responses/response_text_annotation_delta_event.py @@ -0,0 +1,79 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseTextAnnotationDeltaEvent", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationFilePath", +] + + +class AnnotationFileCitation(BaseModel): + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(BaseModel): + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url: str + """The URL of the web resource.""" + + +class AnnotationFilePath(BaseModel): + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_path"] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Annotated[ + Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationFilePath], PropertyInfo(discriminator="type") +] + + +class ResponseTextAnnotationDeltaEvent(BaseModel): + annotation: Annotation + """A citation to a file.""" + + annotation_index: int + """The index of the annotation that was added.""" + + content_index: int + """The index of the content part that the text annotation was added to.""" + + item_id: str + """The ID of the output item that the text annotation was added to.""" + + output_index: int + """The index of the output item that the text annotation was added to.""" + + type: Literal["response.output_text.annotation.added"] + """The type of the event. Always `response.output_text.annotation.added`.""" diff --git a/src/openai/types/responses/response_text_config.py b/src/openai/types/responses/response_text_config.py new file mode 100644 index 0000000000..a1894a9176 --- /dev/null +++ b/src/openai/types/responses/response_text_config.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .response_format_text_config import ResponseFormatTextConfig + +__all__ = ["ResponseTextConfig"] + + +class ResponseTextConfig(BaseModel): + format: Optional[ResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ diff --git a/src/openai/types/responses/response_text_config_param.py b/src/openai/types/responses/response_text_config_param.py new file mode 100644 index 0000000000..aec064bf89 --- /dev/null +++ b/src/openai/types/responses/response_text_config_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .response_format_text_config_param import ResponseFormatTextConfigParam + +__all__ = ["ResponseTextConfigParam"] + + +class ResponseTextConfigParam(TypedDict, total=False): + format: ResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ diff --git a/src/openai/types/responses/response_text_delta_event.py b/src/openai/types/responses/response_text_delta_event.py new file mode 100644 index 0000000000..751a5e2a19 --- /dev/null +++ b/src/openai/types/responses/response_text_delta_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseTextDeltaEvent"] + + +class ResponseTextDeltaEvent(BaseModel): + content_index: int + """The index of the content part that the text delta was added to.""" + + delta: str + """The text delta that was added.""" + + item_id: str + """The ID of the output item that the text delta was added to.""" + + output_index: int + """The index of the output item that the text delta was added to.""" + + type: Literal["response.output_text.delta"] + """The type of the event. Always `response.output_text.delta`.""" diff --git a/src/openai/types/responses/response_text_done_event.py b/src/openai/types/responses/response_text_done_event.py new file mode 100644 index 0000000000..9b5c5e020c --- /dev/null +++ b/src/openai/types/responses/response_text_done_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseTextDoneEvent"] + + +class ResponseTextDoneEvent(BaseModel): + content_index: int + """The index of the content part that the text content is finalized.""" + + item_id: str + """The ID of the output item that the text content is finalized.""" + + output_index: int + """The index of the output item that the text content is finalized.""" + + text: str + """The text content that is finalized.""" + + type: Literal["response.output_text.done"] + """The type of the event. Always `response.output_text.done`.""" diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py new file mode 100644 index 0000000000..ef631c5882 --- /dev/null +++ b/src/openai/types/responses/response_usage.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["ResponseUsage", "OutputTokensDetails"] + + +class OutputTokensDetails(BaseModel): + reasoning_tokens: int + """The number of reasoning tokens.""" + + +class ResponseUsage(BaseModel): + input_tokens: int + """The number of input tokens.""" + + output_tokens: int + """The number of output tokens.""" + + output_tokens_details: OutputTokensDetails + """A detailed breakdown of the output tokens.""" + + total_tokens: int + """The total number of tokens used.""" diff --git a/src/openai/types/responses/response_web_search_call_completed_event.py b/src/openai/types/responses/response_web_search_call_completed_event.py new file mode 100644 index 0000000000..76f26766a1 --- /dev/null +++ b/src/openai/types/responses/response_web_search_call_completed_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseWebSearchCallCompletedEvent"] + + +class ResponseWebSearchCallCompletedEvent(BaseModel): + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + type: Literal["response.web_search_call.completed"] + """The type of the event. Always `response.web_search_call.completed`.""" diff --git a/src/openai/types/responses/response_web_search_call_in_progress_event.py b/src/openai/types/responses/response_web_search_call_in_progress_event.py new file mode 100644 index 0000000000..681ce6d94b --- /dev/null +++ b/src/openai/types/responses/response_web_search_call_in_progress_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseWebSearchCallInProgressEvent"] + + +class ResponseWebSearchCallInProgressEvent(BaseModel): + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + type: Literal["response.web_search_call.in_progress"] + """The type of the event. Always `response.web_search_call.in_progress`.""" diff --git a/src/openai/types/responses/response_web_search_call_searching_event.py b/src/openai/types/responses/response_web_search_call_searching_event.py new file mode 100644 index 0000000000..c885d98918 --- /dev/null +++ b/src/openai/types/responses/response_web_search_call_searching_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseWebSearchCallSearchingEvent"] + + +class ResponseWebSearchCallSearchingEvent(BaseModel): + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + type: Literal["response.web_search_call.searching"] + """The type of the event. Always `response.web_search_call.searching`.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py new file mode 100644 index 0000000000..de5d5524d4 --- /dev/null +++ b/src/openai/types/responses/tool.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .computer_tool import ComputerTool +from .function_tool import FunctionTool +from .web_search_tool import WebSearchTool +from .file_search_tool import FileSearchTool + +__all__ = ["Tool"] + +Tool: TypeAlias = Annotated[ + Union[FileSearchTool, FunctionTool, ComputerTool, WebSearchTool], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/responses/tool_choice_function.py b/src/openai/types/responses/tool_choice_function.py new file mode 100644 index 0000000000..8d2a4f2822 --- /dev/null +++ b/src/openai/types/responses/tool_choice_function.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceFunction"] + + +class ToolChoiceFunction(BaseModel): + name: str + """The name of the function to call.""" + + type: Literal["function"] + """For function calling, the type is always `function`.""" diff --git a/src/openai/types/responses/tool_choice_function_param.py b/src/openai/types/responses/tool_choice_function_param.py new file mode 100644 index 0000000000..910537fd97 --- /dev/null +++ b/src/openai/types/responses/tool_choice_function_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceFunctionParam"] + + +class ToolChoiceFunctionParam(TypedDict, total=False): + name: Required[str] + """The name of the function to call.""" + + type: Required[Literal["function"]] + """For function calling, the type is always `function`.""" diff --git a/src/openai/types/responses/tool_choice_options.py b/src/openai/types/responses/tool_choice_options.py new file mode 100644 index 0000000000..c200db54e1 --- /dev/null +++ b/src/openai/types/responses/tool_choice_options.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ToolChoiceOptions"] + +ToolChoiceOptions: TypeAlias = Literal["none", "auto", "required"] diff --git a/src/openai/types/responses/tool_choice_types.py b/src/openai/types/responses/tool_choice_types.py new file mode 100644 index 0000000000..4942808f14 --- /dev/null +++ b/src/openai/types/responses/tool_choice_types.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceTypes"] + + +class ToolChoiceTypes(BaseModel): + type: Literal["file_search", "web_search_preview", "computer_use_preview", "web_search_preview_2025_03_11"] + """The type of hosted tool the model should to use. + + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + + Allowed values are: + + - `file_search` + - `web_search_preview` + - `computer_use_preview` + """ diff --git a/src/openai/types/responses/tool_choice_types_param.py b/src/openai/types/responses/tool_choice_types_param.py new file mode 100644 index 0000000000..b14f2a9eb0 --- /dev/null +++ b/src/openai/types/responses/tool_choice_types_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceTypesParam"] + + +class ToolChoiceTypesParam(TypedDict, total=False): + type: Required[ + Literal["file_search", "web_search_preview", "computer_use_preview", "web_search_preview_2025_03_11"] + ] + """The type of hosted tool the model should to use. + + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + + Allowed values are: + + - `file_search` + - `web_search_preview` + - `computer_use_preview` + """ diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py new file mode 100644 index 0000000000..be1cf82452 --- /dev/null +++ b/src/openai/types/responses/tool_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .computer_tool_param import ComputerToolParam +from .function_tool_param import FunctionToolParam +from .web_search_tool_param import WebSearchToolParam +from .file_search_tool_param import FileSearchToolParam +from ..chat.chat_completion_tool_param import ChatCompletionToolParam + +__all__ = ["ToolParam"] + +ToolParam: TypeAlias = Union[FileSearchToolParam, FunctionToolParam, ComputerToolParam, WebSearchToolParam] + +ParseableToolParam: TypeAlias = Union[ToolParam, ChatCompletionToolParam] diff --git a/src/openai/types/responses/web_search_tool.py b/src/openai/types/responses/web_search_tool.py new file mode 100644 index 0000000000..bee270bf85 --- /dev/null +++ b/src/openai/types/responses/web_search_tool.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["WebSearchTool", "UserLocation"] + + +class UserLocation(BaseModel): + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class WebSearchTool(BaseModel): + type: Literal["web_search_preview", "web_search_preview_2025_03_11"] + """The type of the web search tool. One of: + + - `web_search_preview` + - `web_search_preview_2025_03_11` + """ + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """ + High level guidance for the amount of context window space to use for the + search. One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] = None diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py new file mode 100644 index 0000000000..8ee36ffb47 --- /dev/null +++ b/src/openai/types/responses/web_search_tool_param.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["WebSearchToolParam", "UserLocation"] + + +class UserLocation(TypedDict, total=False): + type: Required[Literal["approximate"]] + """The type of location approximation. Always `approximate`.""" + + city: str + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: str + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: str + """Free text input for the region of the user, e.g. `California`.""" + + timezone: str + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class WebSearchToolParam(TypedDict, total=False): + type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]] + """The type of the web search tool. One of: + + - `web_search_preview` + - `web_search_preview_2025_03_11` + """ + + search_context_size: Literal["low", "medium", "high"] + """ + High level guidance for the amount of context window space to use for the + search. One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index 4cf367b1cc..6ccc2313cc 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -1,8 +1,12 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .metadata import Metadata as Metadata +from .reasoning import Reasoning as Reasoning from .chat_model import ChatModel as ChatModel from .error_object import ErrorObject as ErrorObject +from .compound_filter import CompoundFilter as CompoundFilter +from .reasoning_effort import ReasoningEffort as ReasoningEffort +from .comparison_filter import ComparisonFilter as ComparisonFilter from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters from .response_format_text import ResponseFormatText as ResponseFormatText diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index 6fe705a0b4..31d7104e6e 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -13,6 +13,9 @@ "o1-preview-2024-09-12", "o1-mini", "o1-mini-2024-09-12", + "computer-use-preview", + "computer-use-preview-2025-02-04", + "computer-use-preview-2025-03-11", "gpt-4.5-preview", "gpt-4.5-preview-2025-02-27", "gpt-4o", diff --git a/src/openai/types/shared/comparison_filter.py b/src/openai/types/shared/comparison_filter.py new file mode 100644 index 0000000000..2ec2651ff2 --- /dev/null +++ b/src/openai/types/shared/comparison_filter.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ComparisonFilter"] + + +class ComparisonFilter(BaseModel): + key: str + """The key to compare against the value.""" + + type: Literal["eq", "ne", "gt", "gte", "lt", "lte"] + """Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + """ + + value: Union[str, float, bool] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ diff --git a/src/openai/types/shared/compound_filter.py b/src/openai/types/shared/compound_filter.py new file mode 100644 index 0000000000..3aefa43647 --- /dev/null +++ b/src/openai/types/shared/compound_filter.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .comparison_filter import ComparisonFilter + +__all__ = ["CompoundFilter", "Filter"] + +Filter: TypeAlias = Union[ComparisonFilter, object] + + +class CompoundFilter(BaseModel): + filters: List[Filter] + """Array of filters to combine. + + Items can be `ComparisonFilter` or `CompoundFilter`. + """ + + type: Literal["and", "or"] + """Type of operation: `and` or `or`.""" diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py new file mode 100644 index 0000000000..50821a1727 --- /dev/null +++ b/src/openai/types/shared/reasoning.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .reasoning_effort import ReasoningEffort + +__all__ = ["Reasoning"] + + +class Reasoning(BaseModel): + effort: Optional[ReasoningEffort] = None + """**o-series models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + """ + + generate_summary: Optional[Literal["concise", "detailed"]] = None + """**o-series models only** + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `concise` or + `detailed`. + """ diff --git a/src/openai/types/shared/reasoning_effort.py b/src/openai/types/shared/reasoning_effort.py new file mode 100644 index 0000000000..ace21b67e4 --- /dev/null +++ b/src/openai/types/shared/reasoning_effort.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal, TypeAlias + +__all__ = ["ReasoningEffort"] + +ReasoningEffort: TypeAlias = Optional[Literal["low", "medium", "high"]] diff --git a/src/openai/types/shared/response_format_json_object.py b/src/openai/types/shared/response_format_json_object.py index 107728dd2e..2aaa5dbdfe 100644 --- a/src/openai/types/shared/response_format_json_object.py +++ b/src/openai/types/shared/response_format_json_object.py @@ -9,4 +9,4 @@ class ResponseFormatJSONObject(BaseModel): type: Literal["json_object"] - """The type of response format being defined: `json_object`""" + """The type of response format being defined. Always `json_object`.""" diff --git a/src/openai/types/shared/response_format_json_schema.py b/src/openai/types/shared/response_format_json_schema.py index 3194a4fe91..c7924446f4 100644 --- a/src/openai/types/shared/response_format_json_schema.py +++ b/src/openai/types/shared/response_format_json_schema.py @@ -25,20 +25,24 @@ class JSONSchema(BaseModel): """ schema_: Optional[Dict[str, object]] = FieldInfo(alias="schema", default=None) - """The schema for the response format, described as a JSON Schema object.""" + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ strict: Optional[bool] = None - """Whether to enable strict schema adherence when generating the output. - - If set to true, the model will always follow the exact schema defined in the - `schema` field. Only a subset of JSON Schema is supported when `strict` is - `true`. To learn more, read the + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). """ class ResponseFormatJSONSchema(BaseModel): json_schema: JSONSchema + """Structured Outputs configuration options, including a JSON Schema.""" type: Literal["json_schema"] - """The type of response format being defined: `json_schema`""" + """The type of response format being defined. Always `json_schema`.""" diff --git a/src/openai/types/shared/response_format_text.py b/src/openai/types/shared/response_format_text.py index 6721fe0973..f0c8cfb700 100644 --- a/src/openai/types/shared/response_format_text.py +++ b/src/openai/types/shared/response_format_text.py @@ -9,4 +9,4 @@ class ResponseFormatText(BaseModel): type: Literal["text"] - """The type of response format being defined: `text`""" + """The type of response format being defined. Always `text`.""" diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index 47a747b2d4..4a4a8cdf1e 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -1,7 +1,11 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .metadata import Metadata as Metadata +from .reasoning import Reasoning as Reasoning from .chat_model import ChatModel as ChatModel +from .compound_filter import CompoundFilter as CompoundFilter +from .reasoning_effort import ReasoningEffort as ReasoningEffort +from .comparison_filter import ComparisonFilter as ComparisonFilter from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters from .response_format_text import ResponseFormatText as ResponseFormatText diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index 0ac3f31611..55649876eb 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -15,6 +15,9 @@ "o1-preview-2024-09-12", "o1-mini", "o1-mini-2024-09-12", + "computer-use-preview", + "computer-use-preview-2025-02-04", + "computer-use-preview-2025-03-11", "gpt-4.5-preview", "gpt-4.5-preview-2025-02-27", "gpt-4o", diff --git a/src/openai/types/shared_params/comparison_filter.py b/src/openai/types/shared_params/comparison_filter.py new file mode 100644 index 0000000000..38edd315ed --- /dev/null +++ b/src/openai/types/shared_params/comparison_filter.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ComparisonFilter"] + + +class ComparisonFilter(TypedDict, total=False): + key: Required[str] + """The key to compare against the value.""" + + type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte"]] + """Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + """ + + value: Required[Union[str, float, bool]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ diff --git a/src/openai/types/shared_params/compound_filter.py b/src/openai/types/shared_params/compound_filter.py new file mode 100644 index 0000000000..d12e9b1bda --- /dev/null +++ b/src/openai/types/shared_params/compound_filter.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .comparison_filter import ComparisonFilter + +__all__ = ["CompoundFilter", "Filter"] + +Filter: TypeAlias = Union[ComparisonFilter, object] + + +class CompoundFilter(TypedDict, total=False): + filters: Required[Iterable[Filter]] + """Array of filters to combine. + + Items can be `ComparisonFilter` or `CompoundFilter`. + """ + + type: Required[Literal["and", "or"]] + """Type of operation: `and` or `or`.""" diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py new file mode 100644 index 0000000000..f2b5c5963a --- /dev/null +++ b/src/openai/types/shared_params/reasoning.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ..shared.reasoning_effort import ReasoningEffort + +__all__ = ["Reasoning"] + + +class Reasoning(TypedDict, total=False): + effort: Required[Optional[ReasoningEffort]] + """**o-series models only** + + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `low`, `medium`, and `high`. Reducing reasoning effort can + result in faster responses and fewer tokens used on reasoning in a response. + """ + + generate_summary: Optional[Literal["concise", "detailed"]] + """**o-series models only** + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `concise` or + `detailed`. + """ diff --git a/src/openai/types/shared_params/reasoning_effort.py b/src/openai/types/shared_params/reasoning_effort.py new file mode 100644 index 0000000000..6052c5ae15 --- /dev/null +++ b/src/openai/types/shared_params/reasoning_effort.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypeAlias + +__all__ = ["ReasoningEffort"] + +ReasoningEffort: TypeAlias = Optional[Literal["low", "medium", "high"]] diff --git a/src/openai/types/shared_params/response_format_json_object.py b/src/openai/types/shared_params/response_format_json_object.py index 8419c6cb56..d4d1deaae5 100644 --- a/src/openai/types/shared_params/response_format_json_object.py +++ b/src/openai/types/shared_params/response_format_json_object.py @@ -9,4 +9,4 @@ class ResponseFormatJSONObject(TypedDict, total=False): type: Required[Literal["json_object"]] - """The type of response format being defined: `json_object`""" + """The type of response format being defined. Always `json_object`.""" diff --git a/src/openai/types/shared_params/response_format_json_schema.py b/src/openai/types/shared_params/response_format_json_schema.py index 4b60fae8ee..5b0a13ee06 100644 --- a/src/openai/types/shared_params/response_format_json_schema.py +++ b/src/openai/types/shared_params/response_format_json_schema.py @@ -23,20 +23,24 @@ class JSONSchema(TypedDict, total=False): """ schema: Dict[str, object] - """The schema for the response format, described as a JSON Schema object.""" + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ strict: Optional[bool] - """Whether to enable strict schema adherence when generating the output. - - If set to true, the model will always follow the exact schema defined in the - `schema` field. Only a subset of JSON Schema is supported when `strict` is - `true`. To learn more, read the + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). """ class ResponseFormatJSONSchema(TypedDict, total=False): json_schema: Required[JSONSchema] + """Structured Outputs configuration options, including a JSON Schema.""" type: Required[Literal["json_schema"]] - """The type of response format being defined: `json_schema`""" + """The type of response format being defined. Always `json_schema`.""" diff --git a/src/openai/types/shared_params/response_format_text.py b/src/openai/types/shared_params/response_format_text.py index 5bec7fc503..c3ef2b0816 100644 --- a/src/openai/types/shared_params/response_format_text.py +++ b/src/openai/types/shared_params/response_format_text.py @@ -9,4 +9,4 @@ class ResponseFormatText(TypedDict, total=False): type: Required[Literal["text"]] - """The type of response format being defined: `text`""" + """The type of response format being defined. Always `text`.""" diff --git a/src/openai/types/beta/static_file_chunking_strategy.py b/src/openai/types/static_file_chunking_strategy.py similarity index 94% rename from src/openai/types/beta/static_file_chunking_strategy.py rename to src/openai/types/static_file_chunking_strategy.py index 6080093517..2813bc6630 100644 --- a/src/openai/types/beta/static_file_chunking_strategy.py +++ b/src/openai/types/static_file_chunking_strategy.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from ..._models import BaseModel +from .._models import BaseModel __all__ = ["StaticFileChunkingStrategy"] diff --git a/src/openai/types/beta/static_file_chunking_strategy_object.py b/src/openai/types/static_file_chunking_strategy_object.py similarity index 92% rename from src/openai/types/beta/static_file_chunking_strategy_object.py rename to src/openai/types/static_file_chunking_strategy_object.py index 896c4b8320..2a95dce5b3 100644 --- a/src/openai/types/beta/static_file_chunking_strategy_object.py +++ b/src/openai/types/static_file_chunking_strategy_object.py @@ -2,7 +2,7 @@ from typing_extensions import Literal -from ..._models import BaseModel +from .._models import BaseModel from .static_file_chunking_strategy import StaticFileChunkingStrategy __all__ = ["StaticFileChunkingStrategyObject"] diff --git a/src/openai/types/beta/static_file_chunking_strategy_object_param.py b/src/openai/types/static_file_chunking_strategy_object_param.py similarity index 100% rename from src/openai/types/beta/static_file_chunking_strategy_object_param.py rename to src/openai/types/static_file_chunking_strategy_object_param.py diff --git a/src/openai/types/beta/static_file_chunking_strategy_param.py b/src/openai/types/static_file_chunking_strategy_param.py similarity index 100% rename from src/openai/types/beta/static_file_chunking_strategy_param.py rename to src/openai/types/static_file_chunking_strategy_param.py diff --git a/src/openai/types/beta/vector_store.py b/src/openai/types/vector_store.py similarity index 97% rename from src/openai/types/beta/vector_store.py rename to src/openai/types/vector_store.py index b947dfb79d..2473a442d2 100644 --- a/src/openai/types/beta/vector_store.py +++ b/src/openai/types/vector_store.py @@ -3,8 +3,8 @@ from typing import Optional from typing_extensions import Literal -from ..._models import BaseModel -from ..shared.metadata import Metadata +from .._models import BaseModel +from .shared.metadata import Metadata __all__ = ["VectorStore", "FileCounts", "ExpiresAfter"] diff --git a/src/openai/types/beta/vector_store_create_params.py b/src/openai/types/vector_store_create_params.py similarity index 97% rename from src/openai/types/beta/vector_store_create_params.py rename to src/openai/types/vector_store_create_params.py index faca6d9000..365d0936b1 100644 --- a/src/openai/types/beta/vector_store_create_params.py +++ b/src/openai/types/vector_store_create_params.py @@ -5,7 +5,7 @@ from typing import List, Optional from typing_extensions import Literal, Required, TypedDict -from ..shared_params.metadata import Metadata +from .shared_params.metadata import Metadata from .file_chunking_strategy_param import FileChunkingStrategyParam __all__ = ["VectorStoreCreateParams", "ExpiresAfter"] diff --git a/src/openai/types/beta/vector_store_deleted.py b/src/openai/types/vector_store_deleted.py similarity index 89% rename from src/openai/types/beta/vector_store_deleted.py rename to src/openai/types/vector_store_deleted.py index 21ccda1db5..dfac9ce8bd 100644 --- a/src/openai/types/beta/vector_store_deleted.py +++ b/src/openai/types/vector_store_deleted.py @@ -2,7 +2,7 @@ from typing_extensions import Literal -from ..._models import BaseModel +from .._models import BaseModel __all__ = ["VectorStoreDeleted"] diff --git a/src/openai/types/beta/vector_store_list_params.py b/src/openai/types/vector_store_list_params.py similarity index 100% rename from src/openai/types/beta/vector_store_list_params.py rename to src/openai/types/vector_store_list_params.py diff --git a/src/openai/types/vector_store_search_params.py b/src/openai/types/vector_store_search_params.py new file mode 100644 index 0000000000..17573d0f61 --- /dev/null +++ b/src/openai/types/vector_store_search_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .shared_params.compound_filter import CompoundFilter +from .shared_params.comparison_filter import ComparisonFilter + +__all__ = ["VectorStoreSearchParams", "Filters", "RankingOptions"] + + +class VectorStoreSearchParams(TypedDict, total=False): + query: Required[Union[str, List[str]]] + """A query string for a search""" + + filters: Filters + """A filter to apply based on file attributes.""" + + max_num_results: int + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: RankingOptions + """Ranking options for search.""" + + rewrite_query: bool + """Whether to rewrite the natural language query for vector search.""" + + +Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter] + + +class RankingOptions(TypedDict, total=False): + ranker: Literal["auto", "default-2024-11-15"] + + score_threshold: float diff --git a/src/openai/types/vector_store_search_response.py b/src/openai/types/vector_store_search_response.py new file mode 100644 index 0000000000..d78b71bfba --- /dev/null +++ b/src/openai/types/vector_store_search_response.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["VectorStoreSearchResponse", "Content"] + + +class Content(BaseModel): + text: str + """The text content returned from search.""" + + type: Literal["text"] + """The type of content.""" + + +class VectorStoreSearchResponse(BaseModel): + attributes: Optional[Dict[str, Union[str, float, bool]]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + content: List[Content] + """Content chunks from the file.""" + + file_id: str + """The ID of the vector store file.""" + + filename: str + """The name of the vector store file.""" + + score: float + """The similarity score for the result.""" diff --git a/src/openai/types/beta/vector_store_update_params.py b/src/openai/types/vector_store_update_params.py similarity index 96% rename from src/openai/types/beta/vector_store_update_params.py rename to src/openai/types/vector_store_update_params.py index e91b3ba5ad..4f6ac63963 100644 --- a/src/openai/types/beta/vector_store_update_params.py +++ b/src/openai/types/vector_store_update_params.py @@ -5,7 +5,7 @@ from typing import Optional from typing_extensions import Literal, Required, TypedDict -from ..shared_params.metadata import Metadata +from .shared_params.metadata import Metadata __all__ = ["VectorStoreUpdateParams", "ExpiresAfter"] diff --git a/src/openai/types/beta/vector_stores/__init__.py b/src/openai/types/vector_stores/__init__.py similarity index 82% rename from src/openai/types/beta/vector_stores/__init__.py rename to src/openai/types/vector_stores/__init__.py index ff05dd63d8..96ce301481 100644 --- a/src/openai/types/beta/vector_stores/__init__.py +++ b/src/openai/types/vector_stores/__init__.py @@ -5,6 +5,8 @@ from .file_list_params import FileListParams as FileListParams from .vector_store_file import VectorStoreFile as VectorStoreFile from .file_create_params import FileCreateParams as FileCreateParams +from .file_update_params import FileUpdateParams as FileUpdateParams +from .file_content_response import FileContentResponse as FileContentResponse from .vector_store_file_batch import VectorStoreFileBatch as VectorStoreFileBatch from .file_batch_create_params import FileBatchCreateParams as FileBatchCreateParams from .vector_store_file_deleted import VectorStoreFileDeleted as VectorStoreFileDeleted diff --git a/src/openai/types/beta/vector_stores/file_batch_create_params.py b/src/openai/types/vector_stores/file_batch_create_params.py similarity index 61% rename from src/openai/types/beta/vector_stores/file_batch_create_params.py rename to src/openai/types/vector_stores/file_batch_create_params.py index e42ea99cd1..1a470f757a 100644 --- a/src/openai/types/beta/vector_stores/file_batch_create_params.py +++ b/src/openai/types/vector_stores/file_batch_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List +from typing import Dict, List, Union, Optional from typing_extensions import Required, TypedDict from ..file_chunking_strategy_param import FileChunkingStrategyParam @@ -18,6 +18,15 @@ class FileBatchCreateParams(TypedDict, total=False): files. """ + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + chunking_strategy: FileChunkingStrategyParam """The chunking strategy used to chunk the file(s). diff --git a/src/openai/types/beta/vector_stores/file_batch_list_files_params.py b/src/openai/types/vector_stores/file_batch_list_files_params.py similarity index 100% rename from src/openai/types/beta/vector_stores/file_batch_list_files_params.py rename to src/openai/types/vector_stores/file_batch_list_files_params.py diff --git a/src/openai/types/vector_stores/file_content_response.py b/src/openai/types/vector_stores/file_content_response.py new file mode 100644 index 0000000000..32db2f2ce9 --- /dev/null +++ b/src/openai/types/vector_stores/file_content_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["FileContentResponse"] + + +class FileContentResponse(BaseModel): + text: Optional[str] = None + """The text content""" + + type: Optional[str] = None + """The content type (currently only `"text"`)""" diff --git a/src/openai/types/beta/vector_stores/file_create_params.py b/src/openai/types/vector_stores/file_create_params.py similarity index 60% rename from src/openai/types/beta/vector_stores/file_create_params.py rename to src/openai/types/vector_stores/file_create_params.py index d074d766e6..5b8989251a 100644 --- a/src/openai/types/beta/vector_stores/file_create_params.py +++ b/src/openai/types/vector_stores/file_create_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Dict, Union, Optional from typing_extensions import Required, TypedDict from ..file_chunking_strategy_param import FileChunkingStrategyParam @@ -17,6 +18,15 @@ class FileCreateParams(TypedDict, total=False): files. """ + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + chunking_strategy: FileChunkingStrategyParam """The chunking strategy used to chunk the file(s). diff --git a/src/openai/types/beta/vector_stores/file_list_params.py b/src/openai/types/vector_stores/file_list_params.py similarity index 100% rename from src/openai/types/beta/vector_stores/file_list_params.py rename to src/openai/types/vector_stores/file_list_params.py diff --git a/src/openai/types/vector_stores/file_update_params.py b/src/openai/types/vector_stores/file_update_params.py new file mode 100644 index 0000000000..ebf540d046 --- /dev/null +++ b/src/openai/types/vector_stores/file_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Required, TypedDict + +__all__ = ["FileUpdateParams"] + + +class FileUpdateParams(TypedDict, total=False): + vector_store_id: Required[str] + + attributes: Required[Optional[Dict[str, Union[str, float, bool]]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ diff --git a/src/openai/types/beta/vector_stores/vector_store_file.py b/src/openai/types/vector_stores/vector_store_file.py similarity index 76% rename from src/openai/types/beta/vector_stores/vector_store_file.py rename to src/openai/types/vector_stores/vector_store_file.py index e4608e159c..b59a61dfb0 100644 --- a/src/openai/types/beta/vector_stores/vector_store_file.py +++ b/src/openai/types/vector_stores/vector_store_file.py @@ -1,9 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Union, Optional from typing_extensions import Literal -from ...._models import BaseModel +from ..._models import BaseModel from ..file_chunking_strategy import FileChunkingStrategy __all__ = ["VectorStoreFile", "LastError"] @@ -54,5 +54,14 @@ class VectorStoreFile(BaseModel): attached to. """ + attributes: Optional[Dict[str, Union[str, float, bool]]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + chunking_strategy: Optional[FileChunkingStrategy] = None """The strategy used to chunk the file.""" diff --git a/src/openai/types/beta/vector_stores/vector_store_file_batch.py b/src/openai/types/vector_stores/vector_store_file_batch.py similarity index 97% rename from src/openai/types/beta/vector_stores/vector_store_file_batch.py rename to src/openai/types/vector_stores/vector_store_file_batch.py index df130a58de..57dbfbd809 100644 --- a/src/openai/types/beta/vector_stores/vector_store_file_batch.py +++ b/src/openai/types/vector_stores/vector_store_file_batch.py @@ -2,7 +2,7 @@ from typing_extensions import Literal -from ...._models import BaseModel +from ..._models import BaseModel __all__ = ["VectorStoreFileBatch", "FileCounts"] diff --git a/src/openai/types/beta/vector_stores/vector_store_file_deleted.py b/src/openai/types/vector_stores/vector_store_file_deleted.py similarity index 89% rename from src/openai/types/beta/vector_stores/vector_store_file_deleted.py rename to src/openai/types/vector_stores/vector_store_file_deleted.py index ae37f84364..5c856f26cd 100644 --- a/src/openai/types/beta/vector_stores/vector_store_file_deleted.py +++ b/src/openai/types/vector_stores/vector_store_file_deleted.py @@ -2,7 +2,7 @@ from typing_extensions import Literal -from ...._models import BaseModel +from ..._models import BaseModel __all__ = ["VectorStoreFileDeleted"] diff --git a/tests/api_resources/beta/vector_stores/test_files.py b/tests/api_resources/beta/vector_stores/test_files.py deleted file mode 100644 index 36622e699b..0000000000 --- a/tests/api_resources/beta/vector_stores/test_files.py +++ /dev/null @@ -1,420 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from openai import OpenAI, AsyncOpenAI -from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.vector_stores import ( - VectorStoreFile, - VectorStoreFileDeleted, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") - - -class TestFiles: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: OpenAI) -> None: - file = client.beta.vector_stores.files.create( - "vs_abc123", - file_id="string", - ) - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: OpenAI) -> None: - file = client.beta.vector_stores.files.create( - "vs_abc123", - file_id="string", - chunking_strategy={"type": "auto"}, - ) - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: OpenAI) -> None: - response = client.beta.vector_stores.files.with_raw_response.create( - "vs_abc123", - file_id="string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: OpenAI) -> None: - with client.beta.vector_stores.files.with_streaming_response.create( - "vs_abc123", - file_id="string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = response.parse() - assert_matches_type(VectorStoreFile, file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_create(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.files.with_raw_response.create( - "", - file_id="string", - ) - - @parametrize - def test_method_retrieve(self, client: OpenAI) -> None: - file = client.beta.vector_stores.files.retrieve( - "file-abc123", - vector_store_id="vs_abc123", - ) - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: OpenAI) -> None: - response = client.beta.vector_stores.files.with_raw_response.retrieve( - "file-abc123", - vector_store_id="vs_abc123", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: OpenAI) -> None: - with client.beta.vector_stores.files.with_streaming_response.retrieve( - "file-abc123", - vector_store_id="vs_abc123", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = response.parse() - assert_matches_type(VectorStoreFile, file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_retrieve(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.files.with_raw_response.retrieve( - "file-abc123", - vector_store_id="", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - client.beta.vector_stores.files.with_raw_response.retrieve( - "", - vector_store_id="vs_abc123", - ) - - @parametrize - def test_method_list(self, client: OpenAI) -> None: - file = client.beta.vector_stores.files.list( - "string", - ) - assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) - - @parametrize - def test_method_list_with_all_params(self, client: OpenAI) -> None: - file = client.beta.vector_stores.files.list( - "string", - after="string", - before="string", - filter="in_progress", - limit=0, - order="asc", - ) - assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) - - @parametrize - def test_raw_response_list(self, client: OpenAI) -> None: - response = client.beta.vector_stores.files.with_raw_response.list( - "string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) - - @parametrize - def test_streaming_response_list(self, client: OpenAI) -> None: - with client.beta.vector_stores.files.with_streaming_response.list( - "string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = response.parse() - assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_list(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.files.with_raw_response.list( - "", - ) - - @parametrize - def test_method_delete(self, client: OpenAI) -> None: - file = client.beta.vector_stores.files.delete( - "string", - vector_store_id="string", - ) - assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) - - @parametrize - def test_raw_response_delete(self, client: OpenAI) -> None: - response = client.beta.vector_stores.files.with_raw_response.delete( - "string", - vector_store_id="string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) - - @parametrize - def test_streaming_response_delete(self, client: OpenAI) -> None: - with client.beta.vector_stores.files.with_streaming_response.delete( - "string", - vector_store_id="string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = response.parse() - assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_delete(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.files.with_raw_response.delete( - "string", - vector_store_id="", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - client.beta.vector_stores.files.with_raw_response.delete( - "", - vector_store_id="string", - ) - - -class TestAsyncFiles: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - async def test_method_create(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.vector_stores.files.create( - "vs_abc123", - file_id="string", - ) - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.vector_stores.files.create( - "vs_abc123", - file_id="string", - chunking_strategy={"type": "auto"}, - ) - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.files.with_raw_response.create( - "vs_abc123", - file_id="string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.files.with_streaming_response.create( - "vs_abc123", - file_id="string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = await response.parse() - assert_matches_type(VectorStoreFile, file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.files.with_raw_response.create( - "", - file_id="string", - ) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.vector_stores.files.retrieve( - "file-abc123", - vector_store_id="vs_abc123", - ) - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.files.with_raw_response.retrieve( - "file-abc123", - vector_store_id="vs_abc123", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(VectorStoreFile, file, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.files.with_streaming_response.retrieve( - "file-abc123", - vector_store_id="vs_abc123", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = await response.parse() - assert_matches_type(VectorStoreFile, file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.files.with_raw_response.retrieve( - "file-abc123", - vector_store_id="", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await async_client.beta.vector_stores.files.with_raw_response.retrieve( - "", - vector_store_id="vs_abc123", - ) - - @parametrize - async def test_method_list(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.vector_stores.files.list( - "string", - ) - assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) - - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.vector_stores.files.list( - "string", - after="string", - before="string", - filter="in_progress", - limit=0, - order="asc", - ) - assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) - - @parametrize - async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.files.with_raw_response.list( - "string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) - - @parametrize - async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.files.with_streaming_response.list( - "string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = await response.parse() - assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.files.with_raw_response.list( - "", - ) - - @parametrize - async def test_method_delete(self, async_client: AsyncOpenAI) -> None: - file = await async_client.beta.vector_stores.files.delete( - "string", - vector_store_id="string", - ) - assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) - - @parametrize - async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.files.with_raw_response.delete( - "string", - vector_store_id="string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - file = response.parse() - assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) - - @parametrize - async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.files.with_streaming_response.delete( - "string", - vector_store_id="string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - file = await response.parse() - assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.files.with_raw_response.delete( - "string", - vector_store_id="", - ) - - with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): - await async_client.beta.vector_stores.files.with_raw_response.delete( - "", - vector_store_id="string", - ) diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 48b687a70e..d4ccc494dd 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -74,9 +74,9 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: presence_penalty=-2, reasoning_effort="low", response_format={"type": "text"}, - seed=0, + seed=-9007199254740991, service_tier="auto", - stop="string", + stop="\n", store=True, stream=False, stream_options={"include_usage": True}, @@ -96,6 +96,18 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: top_logprobs=0, top_p=1, user="user-1234", + web_search_options={ + "search_context_size": "low", + "user_location": { + "approximate": { + "city": "city", + "country": "country", + "region": "region", + "timezone": "timezone", + }, + "type": "approximate", + }, + }, ) assert_matches_type(ChatCompletion, completion, path=["response"]) @@ -189,9 +201,9 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: presence_penalty=-2, reasoning_effort="low", response_format={"type": "text"}, - seed=0, + seed=-9007199254740991, service_tier="auto", - stop="string", + stop="\n", store=True, stream_options={"include_usage": True}, temperature=1, @@ -210,6 +222,18 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: top_logprobs=0, top_p=1, user="user-1234", + web_search_options={ + "search_context_size": "low", + "user_location": { + "approximate": { + "city": "city", + "country": "country", + "region": "region", + "timezone": "timezone", + }, + "type": "approximate", + }, + }, ) completion_stream.response.close() @@ -477,9 +501,9 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn presence_penalty=-2, reasoning_effort="low", response_format={"type": "text"}, - seed=0, + seed=-9007199254740991, service_tier="auto", - stop="string", + stop="\n", store=True, stream=False, stream_options={"include_usage": True}, @@ -499,6 +523,18 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn top_logprobs=0, top_p=1, user="user-1234", + web_search_options={ + "search_context_size": "low", + "user_location": { + "approximate": { + "city": "city", + "country": "country", + "region": "region", + "timezone": "timezone", + }, + "type": "approximate", + }, + }, ) assert_matches_type(ChatCompletion, completion, path=["response"]) @@ -592,9 +628,9 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn presence_penalty=-2, reasoning_effort="low", response_format={"type": "text"}, - seed=0, + seed=-9007199254740991, service_tier="auto", - stop="string", + stop="\n", store=True, stream_options={"include_usage": True}, temperature=1, @@ -613,6 +649,18 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn top_logprobs=0, top_p=1, user="user-1234", + web_search_options={ + "search_context_size": "low", + "user_location": { + "approximate": { + "city": "city", + "country": "country", + "region": "region", + "timezone": "timezone", + }, + "type": "approximate", + }, + }, ) await completion_stream.response.aclose() diff --git a/tests/api_resources/beta/vector_stores/__init__.py b/tests/api_resources/responses/__init__.py similarity index 100% rename from tests/api_resources/beta/vector_stores/__init__.py rename to tests/api_resources/responses/__init__.py diff --git a/tests/api_resources/responses/test_input_items.py b/tests/api_resources/responses/test_input_items.py new file mode 100644 index 0000000000..28c5e8ca1f --- /dev/null +++ b/tests/api_resources/responses/test_input_items.py @@ -0,0 +1,121 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.responses.response_item_list import Data + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestInputItems: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + input_item = client.responses.input_items.list( + response_id="response_id", + ) + assert_matches_type(SyncCursorPage[Data], input_item, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + input_item = client.responses.input_items.list( + response_id="response_id", + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[Data], input_item, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.responses.input_items.with_raw_response.list( + response_id="response_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + input_item = response.parse() + assert_matches_type(SyncCursorPage[Data], input_item, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.responses.input_items.with_streaming_response.list( + response_id="response_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + input_item = response.parse() + assert_matches_type(SyncCursorPage[Data], input_item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.responses.input_items.with_raw_response.list( + response_id="", + ) + + +class TestAsyncInputItems: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + input_item = await async_client.responses.input_items.list( + response_id="response_id", + ) + assert_matches_type(AsyncCursorPage[Data], input_item, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + input_item = await async_client.responses.input_items.list( + response_id="response_id", + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[Data], input_item, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.input_items.with_raw_response.list( + response_id="response_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + input_item = response.parse() + assert_matches_type(AsyncCursorPage[Data], input_item, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.responses.input_items.with_streaming_response.list( + response_id="response_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + input_item = await response.parse() + assert_matches_type(AsyncCursorPage[Data], input_item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.responses.input_items.with_raw_response.list( + response_id="", + ) diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py new file mode 100644 index 0000000000..e45a5becf3 --- /dev/null +++ b/tests/api_resources/test_responses.py @@ -0,0 +1,498 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.responses import Response + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestResponses: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create_overload_1(self, client: OpenAI) -> None: + response = client.responses.create( + input="string", + model="gpt-4o", + ) + assert_matches_type(Response, response, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: + response = client.responses.create( + input="string", + model="gpt-4o", + include=["file_search_call.results"], + instructions="instructions", + max_output_tokens=0, + metadata={"foo": "string"}, + parallel_tool_calls=True, + previous_response_id="previous_response_id", + reasoning={ + "effort": "low", + "generate_summary": "concise", + }, + store=True, + stream=False, + temperature=1, + text={"format": {"type": "text"}}, + tool_choice="none", + tools=[ + { + "type": "file_search", + "vector_store_ids": ["string"], + "filters": { + "key": "key", + "type": "eq", + "value": "string", + }, + "max_num_results": 0, + "ranking_options": { + "ranker": "auto", + "score_threshold": 0, + }, + } + ], + top_p=1, + truncation="auto", + user="user-1234", + ) + assert_matches_type(Response, response, path=["response"]) + + @parametrize + def test_raw_response_create_overload_1(self, client: OpenAI) -> None: + http_response = client.responses.with_raw_response.create( + input="string", + model="gpt-4o", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(Response, response, path=["response"]) + + @parametrize + def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: + with client.responses.with_streaming_response.create( + input="string", + model="gpt-4o", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert_matches_type(Response, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + def test_method_create_overload_2(self, client: OpenAI) -> None: + response_stream = client.responses.create( + input="string", + model="gpt-4o", + stream=True, + ) + response_stream.response.close() + + @parametrize + def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: + response_stream = client.responses.create( + input="string", + model="gpt-4o", + stream=True, + include=["file_search_call.results"], + instructions="instructions", + max_output_tokens=0, + metadata={"foo": "string"}, + parallel_tool_calls=True, + previous_response_id="previous_response_id", + reasoning={ + "effort": "low", + "generate_summary": "concise", + }, + store=True, + temperature=1, + text={"format": {"type": "text"}}, + tool_choice="none", + tools=[ + { + "type": "file_search", + "vector_store_ids": ["string"], + "filters": { + "key": "key", + "type": "eq", + "value": "string", + }, + "max_num_results": 0, + "ranking_options": { + "ranker": "auto", + "score_threshold": 0, + }, + } + ], + top_p=1, + truncation="auto", + user="user-1234", + ) + response_stream.response.close() + + @parametrize + def test_raw_response_create_overload_2(self, client: OpenAI) -> None: + response = client.responses.with_raw_response.create( + input="string", + model="gpt-4o", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: + with client.responses.with_streaming_response.create( + input="string", + model="gpt-4o", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + response = client.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + assert_matches_type(Response, response, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: + response = client.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + include=["file_search_call.results"], + ) + assert_matches_type(Response, response, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + http_response = client.responses.with_raw_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(Response, response, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.responses.with_streaming_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert_matches_type(Response, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.responses.with_raw_response.retrieve( + response_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + response = client.responses.delete( + "resp_677efb5139a88190b512bc3fef8e535d", + ) + assert response is None + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + http_response = client.responses.with_raw_response.delete( + "resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert response is None + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.responses.with_streaming_response.delete( + "resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert response is None + + assert cast(Any, http_response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.responses.with_raw_response.delete( + "", + ) + + +class TestAsyncResponses: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.create( + input="string", + model="gpt-4o", + ) + assert_matches_type(Response, response, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.create( + input="string", + model="gpt-4o", + include=["file_search_call.results"], + instructions="instructions", + max_output_tokens=0, + metadata={"foo": "string"}, + parallel_tool_calls=True, + previous_response_id="previous_response_id", + reasoning={ + "effort": "low", + "generate_summary": "concise", + }, + store=True, + stream=False, + temperature=1, + text={"format": {"type": "text"}}, + tool_choice="none", + tools=[ + { + "type": "file_search", + "vector_store_ids": ["string"], + "filters": { + "key": "key", + "type": "eq", + "value": "string", + }, + "max_num_results": 0, + "ranking_options": { + "ranker": "auto", + "score_threshold": 0, + }, + } + ], + top_p=1, + truncation="auto", + user="user-1234", + ) + assert_matches_type(Response, response, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.responses.with_raw_response.create( + input="string", + model="gpt-4o", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(Response, response, path=["response"]) + + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: + async with async_client.responses.with_streaming_response.create( + input="string", + model="gpt-4o", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert_matches_type(Response, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: + response_stream = await async_client.responses.create( + input="string", + model="gpt-4o", + stream=True, + ) + await response_stream.response.aclose() + + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + response_stream = await async_client.responses.create( + input="string", + model="gpt-4o", + stream=True, + include=["file_search_call.results"], + instructions="instructions", + max_output_tokens=0, + metadata={"foo": "string"}, + parallel_tool_calls=True, + previous_response_id="previous_response_id", + reasoning={ + "effort": "low", + "generate_summary": "concise", + }, + store=True, + temperature=1, + text={"format": {"type": "text"}}, + tool_choice="none", + tools=[ + { + "type": "file_search", + "vector_store_ids": ["string"], + "filters": { + "key": "key", + "type": "eq", + "value": "string", + }, + "max_num_results": 0, + "ranking_options": { + "ranker": "auto", + "score_threshold": 0, + }, + } + ], + top_p=1, + truncation="auto", + user="user-1234", + ) + await response_stream.response.aclose() + + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.with_raw_response.create( + input="string", + model="gpt-4o", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.responses.with_streaming_response.create( + input="string", + model="gpt-4o", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + assert_matches_type(Response, response, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + include=["file_search_call.results"], + ) + assert_matches_type(Response, response, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.responses.with_raw_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(Response, response, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.responses.with_streaming_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert_matches_type(Response, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.responses.with_raw_response.retrieve( + response_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.delete( + "resp_677efb5139a88190b512bc3fef8e535d", + ) + assert response is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.responses.with_raw_response.delete( + "resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert response is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.responses.with_streaming_response.delete( + "resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert response is None + + assert cast(Any, http_response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.responses.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/beta/test_vector_stores.py b/tests/api_resources/test_vector_stores.py similarity index 60% rename from tests/api_resources/beta/test_vector_stores.py rename to tests/api_resources/test_vector_stores.py index e13b8c7613..54bb75bc1d 100644 --- a/tests/api_resources/beta/test_vector_stores.py +++ b/tests/api_resources/test_vector_stores.py @@ -9,11 +9,12 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta import ( +from openai.types import ( VectorStore, VectorStoreDeleted, + VectorStoreSearchResponse, ) +from openai.pagination import SyncPage, AsyncPage, SyncCursorPage, AsyncCursorPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -23,12 +24,12 @@ class TestVectorStores: @parametrize def test_method_create(self, client: OpenAI) -> None: - vector_store = client.beta.vector_stores.create() + vector_store = client.vector_stores.create() assert_matches_type(VectorStore, vector_store, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: - vector_store = client.beta.vector_stores.create( + vector_store = client.vector_stores.create( chunking_strategy={"type": "auto"}, expires_after={ "anchor": "last_active_at", @@ -42,7 +43,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: - response = client.beta.vector_stores.with_raw_response.create() + response = client.vector_stores.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -51,7 +52,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: - with client.beta.vector_stores.with_streaming_response.create() as response: + with client.vector_stores.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -62,15 +63,15 @@ def test_streaming_response_create(self, client: OpenAI) -> None: @parametrize def test_method_retrieve(self, client: OpenAI) -> None: - vector_store = client.beta.vector_stores.retrieve( - "string", + vector_store = client.vector_stores.retrieve( + "vector_store_id", ) assert_matches_type(VectorStore, vector_store, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: - response = client.beta.vector_stores.with_raw_response.retrieve( - "string", + response = client.vector_stores.with_raw_response.retrieve( + "vector_store_id", ) assert response.is_closed is True @@ -80,8 +81,8 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: @parametrize def test_streaming_response_retrieve(self, client: OpenAI) -> None: - with client.beta.vector_stores.with_streaming_response.retrieve( - "string", + with client.vector_stores.with_streaming_response.retrieve( + "vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -94,21 +95,21 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: @parametrize def test_path_params_retrieve(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.with_raw_response.retrieve( + client.vector_stores.with_raw_response.retrieve( "", ) @parametrize def test_method_update(self, client: OpenAI) -> None: - vector_store = client.beta.vector_stores.update( - "string", + vector_store = client.vector_stores.update( + vector_store_id="vector_store_id", ) assert_matches_type(VectorStore, vector_store, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: OpenAI) -> None: - vector_store = client.beta.vector_stores.update( - "string", + vector_store = client.vector_stores.update( + vector_store_id="vector_store_id", expires_after={ "anchor": "last_active_at", "days": 1, @@ -120,8 +121,8 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_update(self, client: OpenAI) -> None: - response = client.beta.vector_stores.with_raw_response.update( - "string", + response = client.vector_stores.with_raw_response.update( + vector_store_id="vector_store_id", ) assert response.is_closed is True @@ -131,8 +132,8 @@ def test_raw_response_update(self, client: OpenAI) -> None: @parametrize def test_streaming_response_update(self, client: OpenAI) -> None: - with client.beta.vector_stores.with_streaming_response.update( - "string", + with client.vector_stores.with_streaming_response.update( + vector_store_id="vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -145,20 +146,20 @@ def test_streaming_response_update(self, client: OpenAI) -> None: @parametrize def test_path_params_update(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.with_raw_response.update( - "", + client.vector_stores.with_raw_response.update( + vector_store_id="", ) @parametrize def test_method_list(self, client: OpenAI) -> None: - vector_store = client.beta.vector_stores.list() + vector_store = client.vector_stores.list() assert_matches_type(SyncCursorPage[VectorStore], vector_store, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: - vector_store = client.beta.vector_stores.list( - after="string", - before="string", + vector_store = client.vector_stores.list( + after="after", + before="before", limit=0, order="asc", ) @@ -166,7 +167,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_list(self, client: OpenAI) -> None: - response = client.beta.vector_stores.with_raw_response.list() + response = client.vector_stores.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -175,7 +176,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: - with client.beta.vector_stores.with_streaming_response.list() as response: + with client.vector_stores.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -186,15 +187,15 @@ def test_streaming_response_list(self, client: OpenAI) -> None: @parametrize def test_method_delete(self, client: OpenAI) -> None: - vector_store = client.beta.vector_stores.delete( - "string", + vector_store = client.vector_stores.delete( + "vector_store_id", ) assert_matches_type(VectorStoreDeleted, vector_store, path=["response"]) @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: - response = client.beta.vector_stores.with_raw_response.delete( - "string", + response = client.vector_stores.with_raw_response.delete( + "vector_store_id", ) assert response.is_closed is True @@ -204,8 +205,8 @@ def test_raw_response_delete(self, client: OpenAI) -> None: @parametrize def test_streaming_response_delete(self, client: OpenAI) -> None: - with client.beta.vector_stores.with_streaming_response.delete( - "string", + with client.vector_stores.with_streaming_response.delete( + "vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -218,22 +219,83 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: @parametrize def test_path_params_delete(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.with_raw_response.delete( + client.vector_stores.with_raw_response.delete( "", ) + @parametrize + def test_method_search(self, client: OpenAI) -> None: + vector_store = client.vector_stores.search( + vector_store_id="vs_abc123", + query="string", + ) + assert_matches_type(SyncPage[VectorStoreSearchResponse], vector_store, path=["response"]) + + @parametrize + def test_method_search_with_all_params(self, client: OpenAI) -> None: + vector_store = client.vector_stores.search( + vector_store_id="vs_abc123", + query="string", + filters={ + "key": "key", + "type": "eq", + "value": "string", + }, + max_num_results=1, + ranking_options={ + "ranker": "auto", + "score_threshold": 0, + }, + rewrite_query=True, + ) + assert_matches_type(SyncPage[VectorStoreSearchResponse], vector_store, path=["response"]) + + @parametrize + def test_raw_response_search(self, client: OpenAI) -> None: + response = client.vector_stores.with_raw_response.search( + vector_store_id="vs_abc123", + query="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(SyncPage[VectorStoreSearchResponse], vector_store, path=["response"]) + + @parametrize + def test_streaming_response_search(self, client: OpenAI) -> None: + with client.vector_stores.with_streaming_response.search( + vector_store_id="vs_abc123", + query="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = response.parse() + assert_matches_type(SyncPage[VectorStoreSearchResponse], vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_search(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.vector_stores.with_raw_response.search( + vector_store_id="", + query="string", + ) + class TestAsyncVectorStores: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: - vector_store = await async_client.beta.vector_stores.create() + vector_store = await async_client.vector_stores.create() assert_matches_type(VectorStore, vector_store, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: - vector_store = await async_client.beta.vector_stores.create( + vector_store = await async_client.vector_stores.create( chunking_strategy={"type": "auto"}, expires_after={ "anchor": "last_active_at", @@ -247,7 +309,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.with_raw_response.create() + response = await async_client.vector_stores.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -256,7 +318,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.with_streaming_response.create() as response: + async with async_client.vector_stores.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -267,15 +329,15 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: - vector_store = await async_client.beta.vector_stores.retrieve( - "string", + vector_store = await async_client.vector_stores.retrieve( + "vector_store_id", ) assert_matches_type(VectorStore, vector_store, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.with_raw_response.retrieve( - "string", + response = await async_client.vector_stores.with_raw_response.retrieve( + "vector_store_id", ) assert response.is_closed is True @@ -285,8 +347,8 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.with_streaming_response.retrieve( - "string", + async with async_client.vector_stores.with_streaming_response.retrieve( + "vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -299,21 +361,21 @@ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> N @parametrize async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.with_raw_response.retrieve( + await async_client.vector_stores.with_raw_response.retrieve( "", ) @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: - vector_store = await async_client.beta.vector_stores.update( - "string", + vector_store = await async_client.vector_stores.update( + vector_store_id="vector_store_id", ) assert_matches_type(VectorStore, vector_store, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: - vector_store = await async_client.beta.vector_stores.update( - "string", + vector_store = await async_client.vector_stores.update( + vector_store_id="vector_store_id", expires_after={ "anchor": "last_active_at", "days": 1, @@ -325,8 +387,8 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.with_raw_response.update( - "string", + response = await async_client.vector_stores.with_raw_response.update( + vector_store_id="vector_store_id", ) assert response.is_closed is True @@ -336,8 +398,8 @@ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.with_streaming_response.update( - "string", + async with async_client.vector_stores.with_streaming_response.update( + vector_store_id="vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -350,20 +412,20 @@ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> Non @parametrize async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.with_raw_response.update( - "", + await async_client.vector_stores.with_raw_response.update( + vector_store_id="", ) @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: - vector_store = await async_client.beta.vector_stores.list() + vector_store = await async_client.vector_stores.list() assert_matches_type(AsyncCursorPage[VectorStore], vector_store, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: - vector_store = await async_client.beta.vector_stores.list( - after="string", - before="string", + vector_store = await async_client.vector_stores.list( + after="after", + before="before", limit=0, order="asc", ) @@ -371,7 +433,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.with_raw_response.list() + response = await async_client.vector_stores.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -380,7 +442,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.with_streaming_response.list() as response: + async with async_client.vector_stores.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -391,15 +453,15 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_delete(self, async_client: AsyncOpenAI) -> None: - vector_store = await async_client.beta.vector_stores.delete( - "string", + vector_store = await async_client.vector_stores.delete( + "vector_store_id", ) assert_matches_type(VectorStoreDeleted, vector_store, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.with_raw_response.delete( - "string", + response = await async_client.vector_stores.with_raw_response.delete( + "vector_store_id", ) assert response.is_closed is True @@ -409,8 +471,8 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.with_streaming_response.delete( - "string", + async with async_client.vector_stores.with_streaming_response.delete( + "vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -423,6 +485,67 @@ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> Non @parametrize async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.with_raw_response.delete( + await async_client.vector_stores.with_raw_response.delete( "", ) + + @parametrize + async def test_method_search(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.vector_stores.search( + vector_store_id="vs_abc123", + query="string", + ) + assert_matches_type(AsyncPage[VectorStoreSearchResponse], vector_store, path=["response"]) + + @parametrize + async def test_method_search_with_all_params(self, async_client: AsyncOpenAI) -> None: + vector_store = await async_client.vector_stores.search( + vector_store_id="vs_abc123", + query="string", + filters={ + "key": "key", + "type": "eq", + "value": "string", + }, + max_num_results=1, + ranking_options={ + "ranker": "auto", + "score_threshold": 0, + }, + rewrite_query=True, + ) + assert_matches_type(AsyncPage[VectorStoreSearchResponse], vector_store, path=["response"]) + + @parametrize + async def test_raw_response_search(self, async_client: AsyncOpenAI) -> None: + response = await async_client.vector_stores.with_raw_response.search( + vector_store_id="vs_abc123", + query="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + vector_store = response.parse() + assert_matches_type(AsyncPage[VectorStoreSearchResponse], vector_store, path=["response"]) + + @parametrize + async def test_streaming_response_search(self, async_client: AsyncOpenAI) -> None: + async with async_client.vector_stores.with_streaming_response.search( + vector_store_id="vs_abc123", + query="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + vector_store = await response.parse() + assert_matches_type(AsyncPage[VectorStoreSearchResponse], vector_store, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_search(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.vector_stores.with_raw_response.search( + vector_store_id="", + query="string", + ) diff --git a/tests/api_resources/vector_stores/__init__.py b/tests/api_resources/vector_stores/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/vector_stores/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/vector_stores/test_file_batches.py b/tests/api_resources/vector_stores/test_file_batches.py similarity index 68% rename from tests/api_resources/beta/vector_stores/test_file_batches.py rename to tests/api_resources/vector_stores/test_file_batches.py index 631f2669ad..0587cfc56a 100644 --- a/tests/api_resources/beta/vector_stores/test_file_batches.py +++ b/tests/api_resources/vector_stores/test_file_batches.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.beta.vector_stores import ( +from openai.types.vector_stores import ( VectorStoreFile, VectorStoreFileBatch, ) @@ -23,25 +23,26 @@ class TestFileBatches: @parametrize def test_method_create(self, client: OpenAI) -> None: - file_batch = client.beta.vector_stores.file_batches.create( - "vs_abc123", + file_batch = client.vector_stores.file_batches.create( + vector_store_id="vs_abc123", file_ids=["string"], ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: - file_batch = client.beta.vector_stores.file_batches.create( - "vs_abc123", + file_batch = client.vector_stores.file_batches.create( + vector_store_id="vs_abc123", file_ids=["string"], + attributes={"foo": "string"}, chunking_strategy={"type": "auto"}, ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @parametrize def test_raw_response_create(self, client: OpenAI) -> None: - response = client.beta.vector_stores.file_batches.with_raw_response.create( - "vs_abc123", + response = client.vector_stores.file_batches.with_raw_response.create( + vector_store_id="vs_abc123", file_ids=["string"], ) @@ -52,8 +53,8 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: - with client.beta.vector_stores.file_batches.with_streaming_response.create( - "vs_abc123", + with client.vector_stores.file_batches.with_streaming_response.create( + vector_store_id="vs_abc123", file_ids=["string"], ) as response: assert not response.is_closed @@ -67,23 +68,23 @@ def test_streaming_response_create(self, client: OpenAI) -> None: @parametrize def test_path_params_create(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.file_batches.with_raw_response.create( - "", + client.vector_stores.file_batches.with_raw_response.create( + vector_store_id="", file_ids=["string"], ) @parametrize def test_method_retrieve(self, client: OpenAI) -> None: - file_batch = client.beta.vector_stores.file_batches.retrieve( - "vsfb_abc123", + file_batch = client.vector_stores.file_batches.retrieve( + batch_id="vsfb_abc123", vector_store_id="vs_abc123", ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: - response = client.beta.vector_stores.file_batches.with_raw_response.retrieve( - "vsfb_abc123", + response = client.vector_stores.file_batches.with_raw_response.retrieve( + batch_id="vsfb_abc123", vector_store_id="vs_abc123", ) @@ -94,8 +95,8 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: @parametrize def test_streaming_response_retrieve(self, client: OpenAI) -> None: - with client.beta.vector_stores.file_batches.with_streaming_response.retrieve( - "vsfb_abc123", + with client.vector_stores.file_batches.with_streaming_response.retrieve( + batch_id="vsfb_abc123", vector_store_id="vs_abc123", ) as response: assert not response.is_closed @@ -109,30 +110,30 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: @parametrize def test_path_params_retrieve(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.file_batches.with_raw_response.retrieve( - "vsfb_abc123", + client.vector_stores.file_batches.with_raw_response.retrieve( + batch_id="vsfb_abc123", vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): - client.beta.vector_stores.file_batches.with_raw_response.retrieve( - "", + client.vector_stores.file_batches.with_raw_response.retrieve( + batch_id="", vector_store_id="vs_abc123", ) @parametrize def test_method_cancel(self, client: OpenAI) -> None: - file_batch = client.beta.vector_stores.file_batches.cancel( - "string", - vector_store_id="string", + file_batch = client.vector_stores.file_batches.cancel( + batch_id="batch_id", + vector_store_id="vector_store_id", ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @parametrize def test_raw_response_cancel(self, client: OpenAI) -> None: - response = client.beta.vector_stores.file_batches.with_raw_response.cancel( - "string", - vector_store_id="string", + response = client.vector_stores.file_batches.with_raw_response.cancel( + batch_id="batch_id", + vector_store_id="vector_store_id", ) assert response.is_closed is True @@ -142,9 +143,9 @@ def test_raw_response_cancel(self, client: OpenAI) -> None: @parametrize def test_streaming_response_cancel(self, client: OpenAI) -> None: - with client.beta.vector_stores.file_batches.with_streaming_response.cancel( - "string", - vector_store_id="string", + with client.vector_stores.file_batches.with_streaming_response.cancel( + batch_id="batch_id", + vector_store_id="vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -157,32 +158,32 @@ def test_streaming_response_cancel(self, client: OpenAI) -> None: @parametrize def test_path_params_cancel(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.file_batches.with_raw_response.cancel( - "string", + client.vector_stores.file_batches.with_raw_response.cancel( + batch_id="batch_id", vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): - client.beta.vector_stores.file_batches.with_raw_response.cancel( - "", - vector_store_id="string", + client.vector_stores.file_batches.with_raw_response.cancel( + batch_id="", + vector_store_id="vector_store_id", ) @parametrize def test_method_list_files(self, client: OpenAI) -> None: - file_batch = client.beta.vector_stores.file_batches.list_files( - "string", - vector_store_id="string", + file_batch = client.vector_stores.file_batches.list_files( + batch_id="batch_id", + vector_store_id="vector_store_id", ) assert_matches_type(SyncCursorPage[VectorStoreFile], file_batch, path=["response"]) @parametrize def test_method_list_files_with_all_params(self, client: OpenAI) -> None: - file_batch = client.beta.vector_stores.file_batches.list_files( - "string", - vector_store_id="string", - after="string", - before="string", + file_batch = client.vector_stores.file_batches.list_files( + batch_id="batch_id", + vector_store_id="vector_store_id", + after="after", + before="before", filter="in_progress", limit=0, order="asc", @@ -191,9 +192,9 @@ def test_method_list_files_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_list_files(self, client: OpenAI) -> None: - response = client.beta.vector_stores.file_batches.with_raw_response.list_files( - "string", - vector_store_id="string", + response = client.vector_stores.file_batches.with_raw_response.list_files( + batch_id="batch_id", + vector_store_id="vector_store_id", ) assert response.is_closed is True @@ -203,9 +204,9 @@ def test_raw_response_list_files(self, client: OpenAI) -> None: @parametrize def test_streaming_response_list_files(self, client: OpenAI) -> None: - with client.beta.vector_stores.file_batches.with_streaming_response.list_files( - "string", - vector_store_id="string", + with client.vector_stores.file_batches.with_streaming_response.list_files( + batch_id="batch_id", + vector_store_id="vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -218,15 +219,15 @@ def test_streaming_response_list_files(self, client: OpenAI) -> None: @parametrize def test_path_params_list_files(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - client.beta.vector_stores.file_batches.with_raw_response.list_files( - "string", + client.vector_stores.file_batches.with_raw_response.list_files( + batch_id="batch_id", vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): - client.beta.vector_stores.file_batches.with_raw_response.list_files( - "", - vector_store_id="string", + client.vector_stores.file_batches.with_raw_response.list_files( + batch_id="", + vector_store_id="vector_store_id", ) @@ -235,25 +236,26 @@ class TestAsyncFileBatches: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: - file_batch = await async_client.beta.vector_stores.file_batches.create( - "vs_abc123", + file_batch = await async_client.vector_stores.file_batches.create( + vector_store_id="vs_abc123", file_ids=["string"], ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: - file_batch = await async_client.beta.vector_stores.file_batches.create( - "vs_abc123", + file_batch = await async_client.vector_stores.file_batches.create( + vector_store_id="vs_abc123", file_ids=["string"], + attributes={"foo": "string"}, chunking_strategy={"type": "auto"}, ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.file_batches.with_raw_response.create( - "vs_abc123", + response = await async_client.vector_stores.file_batches.with_raw_response.create( + vector_store_id="vs_abc123", file_ids=["string"], ) @@ -264,8 +266,8 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.file_batches.with_streaming_response.create( - "vs_abc123", + async with async_client.vector_stores.file_batches.with_streaming_response.create( + vector_store_id="vs_abc123", file_ids=["string"], ) as response: assert not response.is_closed @@ -279,23 +281,23 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non @parametrize async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.file_batches.with_raw_response.create( - "", + await async_client.vector_stores.file_batches.with_raw_response.create( + vector_store_id="", file_ids=["string"], ) @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: - file_batch = await async_client.beta.vector_stores.file_batches.retrieve( - "vsfb_abc123", + file_batch = await async_client.vector_stores.file_batches.retrieve( + batch_id="vsfb_abc123", vector_store_id="vs_abc123", ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.file_batches.with_raw_response.retrieve( - "vsfb_abc123", + response = await async_client.vector_stores.file_batches.with_raw_response.retrieve( + batch_id="vsfb_abc123", vector_store_id="vs_abc123", ) @@ -306,8 +308,8 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.file_batches.with_streaming_response.retrieve( - "vsfb_abc123", + async with async_client.vector_stores.file_batches.with_streaming_response.retrieve( + batch_id="vsfb_abc123", vector_store_id="vs_abc123", ) as response: assert not response.is_closed @@ -321,30 +323,30 @@ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> N @parametrize async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.file_batches.with_raw_response.retrieve( - "vsfb_abc123", + await async_client.vector_stores.file_batches.with_raw_response.retrieve( + batch_id="vsfb_abc123", vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): - await async_client.beta.vector_stores.file_batches.with_raw_response.retrieve( - "", + await async_client.vector_stores.file_batches.with_raw_response.retrieve( + batch_id="", vector_store_id="vs_abc123", ) @parametrize async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: - file_batch = await async_client.beta.vector_stores.file_batches.cancel( - "string", - vector_store_id="string", + file_batch = await async_client.vector_stores.file_batches.cancel( + batch_id="batch_id", + vector_store_id="vector_store_id", ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @parametrize async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.file_batches.with_raw_response.cancel( - "string", - vector_store_id="string", + response = await async_client.vector_stores.file_batches.with_raw_response.cancel( + batch_id="batch_id", + vector_store_id="vector_store_id", ) assert response.is_closed is True @@ -354,9 +356,9 @@ async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.file_batches.with_streaming_response.cancel( - "string", - vector_store_id="string", + async with async_client.vector_stores.file_batches.with_streaming_response.cancel( + batch_id="batch_id", + vector_store_id="vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -369,32 +371,32 @@ async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> Non @parametrize async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.file_batches.with_raw_response.cancel( - "string", + await async_client.vector_stores.file_batches.with_raw_response.cancel( + batch_id="batch_id", vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): - await async_client.beta.vector_stores.file_batches.with_raw_response.cancel( - "", - vector_store_id="string", + await async_client.vector_stores.file_batches.with_raw_response.cancel( + batch_id="", + vector_store_id="vector_store_id", ) @parametrize async def test_method_list_files(self, async_client: AsyncOpenAI) -> None: - file_batch = await async_client.beta.vector_stores.file_batches.list_files( - "string", - vector_store_id="string", + file_batch = await async_client.vector_stores.file_batches.list_files( + batch_id="batch_id", + vector_store_id="vector_store_id", ) assert_matches_type(AsyncCursorPage[VectorStoreFile], file_batch, path=["response"]) @parametrize async def test_method_list_files_with_all_params(self, async_client: AsyncOpenAI) -> None: - file_batch = await async_client.beta.vector_stores.file_batches.list_files( - "string", - vector_store_id="string", - after="string", - before="string", + file_batch = await async_client.vector_stores.file_batches.list_files( + batch_id="batch_id", + vector_store_id="vector_store_id", + after="after", + before="before", filter="in_progress", limit=0, order="asc", @@ -403,9 +405,9 @@ async def test_method_list_files_with_all_params(self, async_client: AsyncOpenAI @parametrize async def test_raw_response_list_files(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.vector_stores.file_batches.with_raw_response.list_files( - "string", - vector_store_id="string", + response = await async_client.vector_stores.file_batches.with_raw_response.list_files( + batch_id="batch_id", + vector_store_id="vector_store_id", ) assert response.is_closed is True @@ -415,9 +417,9 @@ async def test_raw_response_list_files(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_list_files(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.vector_stores.file_batches.with_streaming_response.list_files( - "string", - vector_store_id="string", + async with async_client.vector_stores.file_batches.with_streaming_response.list_files( + batch_id="batch_id", + vector_store_id="vector_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -430,13 +432,13 @@ async def test_streaming_response_list_files(self, async_client: AsyncOpenAI) -> @parametrize async def test_path_params_list_files(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): - await async_client.beta.vector_stores.file_batches.with_raw_response.list_files( - "string", + await async_client.vector_stores.file_batches.with_raw_response.list_files( + batch_id="batch_id", vector_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"): - await async_client.beta.vector_stores.file_batches.with_raw_response.list_files( - "", - vector_store_id="string", + await async_client.vector_stores.file_batches.with_raw_response.list_files( + batch_id="", + vector_store_id="vector_store_id", ) diff --git a/tests/api_resources/vector_stores/test_files.py b/tests/api_resources/vector_stores/test_files.py new file mode 100644 index 0000000000..c13442261e --- /dev/null +++ b/tests/api_resources/vector_stores/test_files.py @@ -0,0 +1,625 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncPage, AsyncPage, SyncCursorPage, AsyncCursorPage +from openai.types.vector_stores import ( + VectorStoreFile, + FileContentResponse, + VectorStoreFileDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestFiles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + file = client.vector_stores.files.create( + vector_store_id="vs_abc123", + file_id="file_id", + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + file = client.vector_stores.files.create( + vector_store_id="vs_abc123", + file_id="file_id", + attributes={"foo": "string"}, + chunking_strategy={"type": "auto"}, + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.vector_stores.files.with_raw_response.create( + vector_store_id="vs_abc123", + file_id="file_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.vector_stores.files.with_streaming_response.create( + vector_store_id="vs_abc123", + file_id="file_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.vector_stores.files.with_raw_response.create( + vector_store_id="", + file_id="file_id", + ) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + file = client.vector_stores.files.retrieve( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.vector_stores.files.with_raw_response.retrieve( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.vector_stores.files.with_streaming_response.retrieve( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.vector_stores.files.with_raw_response.retrieve( + file_id="file-abc123", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.vector_stores.files.with_raw_response.retrieve( + file_id="", + vector_store_id="vs_abc123", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + file = client.vector_stores.files.update( + file_id="file-abc123", + vector_store_id="vs_abc123", + attributes={"foo": "string"}, + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.vector_stores.files.with_raw_response.update( + file_id="file-abc123", + vector_store_id="vs_abc123", + attributes={"foo": "string"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.vector_stores.files.with_streaming_response.update( + file_id="file-abc123", + vector_store_id="vs_abc123", + attributes={"foo": "string"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.vector_stores.files.with_raw_response.update( + file_id="file-abc123", + vector_store_id="", + attributes={"foo": "string"}, + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.vector_stores.files.with_raw_response.update( + file_id="", + vector_store_id="vs_abc123", + attributes={"foo": "string"}, + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + file = client.vector_stores.files.list( + vector_store_id="vector_store_id", + ) + assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + file = client.vector_stores.files.list( + vector_store_id="vector_store_id", + after="after", + before="before", + filter="in_progress", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.vector_stores.files.with_raw_response.list( + vector_store_id="vector_store_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.vector_stores.files.with_streaming_response.list( + vector_store_id="vector_store_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(SyncCursorPage[VectorStoreFile], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.vector_stores.files.with_raw_response.list( + vector_store_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + file = client.vector_stores.files.delete( + file_id="file_id", + vector_store_id="vector_store_id", + ) + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.vector_stores.files.with_raw_response.delete( + file_id="file_id", + vector_store_id="vector_store_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.vector_stores.files.with_streaming_response.delete( + file_id="file_id", + vector_store_id="vector_store_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.vector_stores.files.with_raw_response.delete( + file_id="file_id", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.vector_stores.files.with_raw_response.delete( + file_id="", + vector_store_id="vector_store_id", + ) + + @parametrize + def test_method_content(self, client: OpenAI) -> None: + file = client.vector_stores.files.content( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) + assert_matches_type(SyncPage[FileContentResponse], file, path=["response"]) + + @parametrize + def test_raw_response_content(self, client: OpenAI) -> None: + response = client.vector_stores.files.with_raw_response.content( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(SyncPage[FileContentResponse], file, path=["response"]) + + @parametrize + def test_streaming_response_content(self, client: OpenAI) -> None: + with client.vector_stores.files.with_streaming_response.content( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(SyncPage[FileContentResponse], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_content(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + client.vector_stores.files.with_raw_response.content( + file_id="file-abc123", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.vector_stores.files.with_raw_response.content( + file_id="", + vector_store_id="vs_abc123", + ) + + +class TestAsyncFiles: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + file = await async_client.vector_stores.files.create( + vector_store_id="vs_abc123", + file_id="file_id", + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + file = await async_client.vector_stores.files.create( + vector_store_id="vs_abc123", + file_id="file_id", + attributes={"foo": "string"}, + chunking_strategy={"type": "auto"}, + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.vector_stores.files.with_raw_response.create( + vector_store_id="vs_abc123", + file_id="file_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.vector_stores.files.with_streaming_response.create( + vector_store_id="vs_abc123", + file_id="file_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.create( + vector_store_id="", + file_id="file_id", + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + file = await async_client.vector_stores.files.retrieve( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.vector_stores.files.with_raw_response.retrieve( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.vector_stores.files.with_streaming_response.retrieve( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.retrieve( + file_id="file-abc123", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.retrieve( + file_id="", + vector_store_id="vs_abc123", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + file = await async_client.vector_stores.files.update( + file_id="file-abc123", + vector_store_id="vs_abc123", + attributes={"foo": "string"}, + ) + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.vector_stores.files.with_raw_response.update( + file_id="file-abc123", + vector_store_id="vs_abc123", + attributes={"foo": "string"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.vector_stores.files.with_streaming_response.update( + file_id="file-abc123", + vector_store_id="vs_abc123", + attributes={"foo": "string"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(VectorStoreFile, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.update( + file_id="file-abc123", + vector_store_id="", + attributes={"foo": "string"}, + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.update( + file_id="", + vector_store_id="vs_abc123", + attributes={"foo": "string"}, + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + file = await async_client.vector_stores.files.list( + vector_store_id="vector_store_id", + ) + assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + file = await async_client.vector_stores.files.list( + vector_store_id="vector_store_id", + after="after", + before="before", + filter="in_progress", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.vector_stores.files.with_raw_response.list( + vector_store_id="vector_store_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.vector_stores.files.with_streaming_response.list( + vector_store_id="vector_store_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(AsyncCursorPage[VectorStoreFile], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.list( + vector_store_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + file = await async_client.vector_stores.files.delete( + file_id="file_id", + vector_store_id="vector_store_id", + ) + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.vector_stores.files.with_raw_response.delete( + file_id="file_id", + vector_store_id="vector_store_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.vector_stores.files.with_streaming_response.delete( + file_id="file_id", + vector_store_id="vector_store_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(VectorStoreFileDeleted, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.delete( + file_id="file_id", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.delete( + file_id="", + vector_store_id="vector_store_id", + ) + + @parametrize + async def test_method_content(self, async_client: AsyncOpenAI) -> None: + file = await async_client.vector_stores.files.content( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) + assert_matches_type(AsyncPage[FileContentResponse], file, path=["response"]) + + @parametrize + async def test_raw_response_content(self, async_client: AsyncOpenAI) -> None: + response = await async_client.vector_stores.files.with_raw_response.content( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(AsyncPage[FileContentResponse], file, path=["response"]) + + @parametrize + async def test_streaming_response_content(self, async_client: AsyncOpenAI) -> None: + async with async_client.vector_stores.files.with_streaming_response.content( + file_id="file-abc123", + vector_store_id="vs_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(AsyncPage[FileContentResponse], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_content(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.content( + file_id="file-abc123", + vector_store_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.vector_stores.files.with_raw_response.content( + file_id="", + vector_store_id="vs_abc123", + ) diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index 74cee27b93..62fdd34c0a 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -58,6 +58,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend checking a reliable weather website or app like the Weather Channel or a local news station.", @@ -126,6 +127,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, @@ -195,6 +197,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, @@ -266,6 +269,7 @@ class ColorDetection(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[ColorDetection]( + annotations=None, audio=None, content='{"color":"red","hex_color_code":"#FF0000"}', function_call=None, @@ -315,6 +319,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":64,"units":"f"}', function_call=None, @@ -329,6 +334,7 @@ class Location(BaseModel): index=1, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, @@ -343,6 +349,7 @@ class Location(BaseModel): index=2, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":63.0,"units":"f"}', function_call=None, @@ -393,6 +400,7 @@ class CalendarEvent: index=0, logprobs=None, message=ParsedChatCompletionMessage[CalendarEvent]( + annotations=None, audio=None, content='{"name":"Science Fair","date":"Friday","participants":["Alice","Bob"]}', function_call=None, @@ -454,6 +462,7 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m index=0, logprobs=None, message=ParsedChatCompletionMessage[Query]( + annotations=None, audio=None, content=None, function_call=None, @@ -565,6 +574,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content=None, function_call=None, @@ -614,6 +624,7 @@ class GetWeatherArgs(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content=None, function_call=None, @@ -686,6 +697,7 @@ class GetStockPrice(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content=None, function_call=None, @@ -767,6 +779,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content=None, function_call=None, @@ -849,6 +862,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":58,"units":"f"}', function_call=None, @@ -924,6 +938,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 71b4173738..5852c5a343 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -63,6 +63,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend checking a reliable weather website or a weather app.", @@ -141,6 +142,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":61,"units":"f"}', function_call=None, @@ -318,6 +320,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', function_call=None, @@ -332,6 +335,7 @@ class Location(BaseModel): index=1, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":61,"units":"f"}', function_call=None, @@ -346,6 +350,7 @@ class Location(BaseModel): index=2, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content='{"city":"San Francisco","temperature":59,"units":"f"}', function_call=None, @@ -421,6 +426,7 @@ class Location(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content=None, function_call=None, @@ -495,6 +501,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp refusal=None ), message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content='Foo!', function_call=None, @@ -606,6 +613,7 @@ class Location(BaseModel): ] ), message=ParsedChatCompletionMessage[Location]( + annotations=None, audio=None, content=None, function_call=None, @@ -652,6 +660,7 @@ class GetWeatherArgs(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[object]( + annotations=None, audio=None, content=None, function_call=None, @@ -684,6 +693,7 @@ class GetWeatherArgs(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content=None, function_call=None, @@ -755,6 +765,7 @@ class GetStockPrice(BaseModel): index=0, logprobs=None, message=ParsedChatCompletionMessage[object]( + annotations=None, audio=None, content=None, function_call=None, @@ -863,6 +874,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: index=0, logprobs=None, message=ParsedChatCompletionMessage[object]( + annotations=None, audio=None, content=None, function_call=None, @@ -914,6 +926,7 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content='\\n {\\n "location": "San Francisco, CA",\\n "weather": {\\n "temperature": "18°C",\\n "condition": "Partly Cloudy",\\n "humidity": "72%",\\n "windSpeed": "15 km/h",\\n "windDirection": "NW"\\n @@ -974,6 +987,7 @@ def test_allows_non_strict_tools_but_no_parsing( index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content=None, function_call=None, @@ -1033,6 +1047,7 @@ def streamer(client: OpenAI) -> Iterator[ChatCompletionChunk]: index=0, logprobs=None, message=ParsedChatCompletionMessage[NoneType]( + annotations=None, audio=None, content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend checking a reliable weather website or a weather app.", From 849ec26310dde6f7c8a6d0cc8a93a067d3facb89 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 16:31:57 +0000 Subject: [PATCH 814/914] release: 1.66.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b8446e8608..fa1c44bbb5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.65.5" + ".": "1.66.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e2bf62a4df..fb576487cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.66.0 (2025-03-11) + +Full Changelog: [v1.65.5...v1.66.0](https://github.com/openai/openai-python/compare/v1.65.5...v1.66.0) + +### Features + +* **api:** add /v1/responses and built-in tools ([854df97](https://github.com/openai/openai-python/commit/854df97884736244d46060fd3d5a92916826ec8f)) + + +### Chores + +* export more types ([#2176](https://github.com/openai/openai-python/issues/2176)) ([a730f0e](https://github.com/openai/openai-python/commit/a730f0efedd228f96a49467f17fb19b6a219246c)) + ## 1.65.5 (2025-03-09) Full Changelog: [v1.65.4...v1.65.5](https://github.com/openai/openai-python/compare/v1.65.4...v1.65.5) diff --git a/pyproject.toml b/pyproject.toml index 09e79f5592..f362b5e264 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.65.5" +version = "1.66.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 859b56580d..74f5619299 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.65.5" # x-release-please-version +__version__ = "1.66.0" # x-release-please-version From 5f548eaa2ca330f163f9d4fb035e81eb225633b6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 19:52:46 +0000 Subject: [PATCH 815/914] fix(responses): correct computer use enum value (#2180) --- .stats.yml | 2 +- src/openai/types/responses/computer_tool.py | 2 +- src/openai/types/responses/computer_tool_param.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 455874212c..9c4a2e5367 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 81 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-be834d63e326a82494e819085137f5eb15866f3fc787db1f3afe7168d419e18a.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-9ce5257763fb30c6e0e1ee2bef7e13baf661511e09572207e528d643da8e16b3.yml diff --git a/src/openai/types/responses/computer_tool.py b/src/openai/types/responses/computer_tool.py index f0499cd950..dffb7af7b7 100644 --- a/src/openai/types/responses/computer_tool.py +++ b/src/openai/types/responses/computer_tool.py @@ -17,5 +17,5 @@ class ComputerTool(BaseModel): environment: Literal["mac", "windows", "ubuntu", "browser"] """The type of computer environment to control.""" - type: Literal["computer-preview"] + type: Literal["computer_use_preview"] """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/src/openai/types/responses/computer_tool_param.py b/src/openai/types/responses/computer_tool_param.py index 685b471378..6b1072ffd2 100644 --- a/src/openai/types/responses/computer_tool_param.py +++ b/src/openai/types/responses/computer_tool_param.py @@ -17,5 +17,5 @@ class ComputerToolParam(TypedDict, total=False): environment: Required[Literal["mac", "windows", "ubuntu", "browser"]] """The type of computer environment to control.""" - type: Required[Literal["computer-preview"]] + type: Required[Literal["computer_use_preview"]] """The type of the computer use tool. Always `computer_use_preview`.""" From 5a1eded551ecd2e10d2508fc47ed1003e6675872 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 19:53:21 +0000 Subject: [PATCH 816/914] release: 1.66.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fa1c44bbb5..5d08177085 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.66.0" + ".": "1.66.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index fb576487cb..4068372dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.66.1 (2025-03-11) + +Full Changelog: [v1.66.0...v1.66.1](https://github.com/openai/openai-python/compare/v1.66.0...v1.66.1) + +### Bug Fixes + +* **responses:** correct computer use enum value ([#2180](https://github.com/openai/openai-python/issues/2180)) ([48f4628](https://github.com/openai/openai-python/commit/48f4628c5fb18ddd7d71e8730184f3ac50c4ffea)) + + +### Chores + +* **internal:** temporary commit ([afabec1](https://github.com/openai/openai-python/commit/afabec1b5b18b41ac870970d06e6c2f152ef7bbe)) + ## 1.66.0 (2025-03-11) Full Changelog: [v1.65.5...v1.66.0](https://github.com/openai/openai-python/compare/v1.65.5...v1.66.0) diff --git a/pyproject.toml b/pyproject.toml index f362b5e264..04ba80639b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.66.0" +version = "1.66.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 74f5619299..473c6aba83 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.66.0" # x-release-please-version +__version__ = "1.66.1" # x-release-please-version From 27ef73fd301c0e49d43d62fe7fbd17badd0c986d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 21:44:13 +0000 Subject: [PATCH 817/914] fix(responses): correct reasoning output type (#2181) --- .stats.yml | 2 +- api.md | 1 + src/openai/types/responses/__init__.py | 2 + src/openai/types/responses/parsed_response.py | 4 +- .../responses/response_input_item_param.py | 33 +--------------- .../types/responses/response_input_param.py | 33 +--------------- .../types/responses/response_output_item.py | 39 +++---------------- .../responses/response_reasoning_item.py | 36 +++++++++++++++++ .../response_reasoning_item_param.py | 36 +++++++++++++++++ 9 files changed, 87 insertions(+), 99 deletions(-) create mode 100644 src/openai/types/responses/response_reasoning_item.py create mode 100644 src/openai/types/responses/response_reasoning_item_param.py diff --git a/.stats.yml b/.stats.yml index 9c4a2e5367..edc2aaf89f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 81 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-9ce5257763fb30c6e0e1ee2bef7e13baf661511e09572207e528d643da8e16b3.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-c8579861bc21d4d2155a5b9e8e7d54faee8083730673c4d32cbbe573d7fb4116.yml diff --git a/api.md b/api.md index 6827b88f0b..e760fe69c1 100644 --- a/api.md +++ b/api.md @@ -640,6 +640,7 @@ from openai.types.responses import ( ResponseOutputMessage, ResponseOutputRefusal, ResponseOutputText, + ResponseReasoningItem, ResponseRefusalDeltaEvent, ResponseRefusalDoneEvent, ResponseStatus, diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 970a167d2c..7c0cf9e3f2 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -45,6 +45,7 @@ from .response_input_content import ResponseInputContent as ResponseInputContent from .response_output_message import ResponseOutputMessage as ResponseOutputMessage from .response_output_refusal import ResponseOutputRefusal as ResponseOutputRefusal +from .response_reasoning_item import ResponseReasoningItem as ResponseReasoningItem from .tool_choice_types_param import ToolChoiceTypesParam as ToolChoiceTypesParam from .easy_input_message_param import EasyInputMessageParam as EasyInputMessageParam from .response_completed_event import ResponseCompletedEvent as ResponseCompletedEvent @@ -71,6 +72,7 @@ from .response_refusal_delta_event import ResponseRefusalDeltaEvent as ResponseRefusalDeltaEvent from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam +from .response_reasoning_item_param import ResponseReasoningItemParam as ResponseReasoningItemParam from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py index 3216a71ba9..1263dfd648 100644 --- a/src/openai/types/responses/parsed_response.py +++ b/src/openai/types/responses/parsed_response.py @@ -7,10 +7,10 @@ from .response import Response from ..._models import GenericModel from ..._utils._transform import PropertyInfo -from .response_output_item import Reasoning from .response_output_text import ResponseOutputText from .response_output_message import ResponseOutputMessage from .response_output_refusal import ResponseOutputRefusal +from .response_reasoning_item import ResponseReasoningItem from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch @@ -54,7 +54,7 @@ class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): ResponseFileSearchToolCall, ResponseFunctionWebSearch, ResponseComputerToolCall, - Reasoning, + ResponseReasoningItem, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index c9daaa6a89..32ac13cabb 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -7,6 +7,7 @@ from .easy_input_message_param import EasyInputMessageParam from .response_output_message_param import ResponseOutputMessageParam +from .response_reasoning_item_param import ResponseReasoningItemParam from .response_computer_tool_call_param import ResponseComputerToolCallParam from .response_function_tool_call_param import ResponseFunctionToolCallParam from .response_function_web_search_param import ResponseFunctionWebSearchParam @@ -20,8 +21,6 @@ "ComputerCallOutputOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", - "Reasoning", - "ReasoningContent", "ItemReference", ] @@ -123,34 +122,6 @@ class FunctionCallOutput(TypedDict, total=False): """ -class ReasoningContent(TypedDict, total=False): - text: Required[str] - """ - A short summary of the reasoning used by the model when generating the response. - """ - - type: Required[Literal["reasoning_summary"]] - """The type of the object. Always `text`.""" - - -class Reasoning(TypedDict, total=False): - id: Required[str] - """The unique identifier of the reasoning content.""" - - content: Required[Iterable[ReasoningContent]] - """Reasoning text contents.""" - - type: Required[Literal["reasoning"]] - """The type of the object. Always `reasoning`.""" - - status: Literal["in_progress", "completed", "incomplete"] - """The status of the item. - - One of `in_progress`, `completed`, or `incomplete`. Populated when items are - returned via API. - """ - - class ItemReference(TypedDict, total=False): id: Required[str] """The ID of the item to reference.""" @@ -169,6 +140,6 @@ class ItemReference(TypedDict, total=False): ResponseFunctionWebSearchParam, ResponseFunctionToolCallParam, FunctionCallOutput, - Reasoning, + ResponseReasoningItemParam, ItemReference, ] diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index c81308500d..b942f4868a 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -7,6 +7,7 @@ from .easy_input_message_param import EasyInputMessageParam from .response_output_message_param import ResponseOutputMessageParam +from .response_reasoning_item_param import ResponseReasoningItemParam from .response_computer_tool_call_param import ResponseComputerToolCallParam from .response_function_tool_call_param import ResponseFunctionToolCallParam from .response_function_web_search_param import ResponseFunctionWebSearchParam @@ -21,8 +22,6 @@ "ComputerCallOutputOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", - "Reasoning", - "ReasoningContent", "ItemReference", ] @@ -124,34 +123,6 @@ class FunctionCallOutput(TypedDict, total=False): """ -class ReasoningContent(TypedDict, total=False): - text: Required[str] - """ - A short summary of the reasoning used by the model when generating the response. - """ - - type: Required[Literal["reasoning_summary"]] - """The type of the object. Always `text`.""" - - -class Reasoning(TypedDict, total=False): - id: Required[str] - """The unique identifier of the reasoning content.""" - - content: Required[Iterable[ReasoningContent]] - """Reasoning text contents.""" - - type: Required[Literal["reasoning"]] - """The type of the object. Always `reasoning`.""" - - status: Literal["in_progress", "completed", "incomplete"] - """The status of the item. - - One of `in_progress`, `completed`, or `incomplete`. Populated when items are - returned via API. - """ - - class ItemReference(TypedDict, total=False): id: Required[str] """The ID of the item to reference.""" @@ -170,7 +141,7 @@ class ItemReference(TypedDict, total=False): ResponseFunctionWebSearchParam, ResponseFunctionToolCallParam, FunctionCallOutput, - Reasoning, + ResponseReasoningItemParam, ItemReference, ] diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index 45d5cc0094..f1e9693195 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -1,46 +1,17 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias +from typing import Union +from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo -from ..._models import BaseModel from .response_output_message import ResponseOutputMessage +from .response_reasoning_item import ResponseReasoningItem from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch from .response_file_search_tool_call import ResponseFileSearchToolCall -__all__ = ["ResponseOutputItem", "Reasoning", "ReasoningContent"] - - -class ReasoningContent(BaseModel): - text: str - """ - A short summary of the reasoning used by the model when generating the response. - """ - - type: Literal["reasoning_summary"] - """The type of the object. Always `text`.""" - - -class Reasoning(BaseModel): - id: str - """The unique identifier of the reasoning content.""" - - content: List[ReasoningContent] - """Reasoning text contents.""" - - type: Literal["reasoning"] - """The type of the object. Always `reasoning`.""" - - status: Optional[Literal["in_progress", "completed", "incomplete"]] = None - """The status of the item. - - One of `in_progress`, `completed`, or `incomplete`. Populated when items are - returned via API. - """ - +__all__ = ["ResponseOutputItem"] ResponseOutputItem: TypeAlias = Annotated[ Union[ @@ -49,7 +20,7 @@ class Reasoning(BaseModel): ResponseFunctionToolCall, ResponseFunctionWebSearch, ResponseComputerToolCall, - Reasoning, + ResponseReasoningItem, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py new file mode 100644 index 0000000000..57e5fbfe6d --- /dev/null +++ b/src/openai/types/responses/response_reasoning_item.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningItem", "Summary"] + + +class Summary(BaseModel): + text: str + """ + A short summary of the reasoning used by the model when generating the response. + """ + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" + + +class ResponseReasoningItem(BaseModel): + id: str + """The unique identifier of the reasoning content.""" + + summary: List[Summary] + """Reasoning text contents.""" + + type: Literal["reasoning"] + """The type of the object. Always `reasoning`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py new file mode 100644 index 0000000000..adb49d6402 --- /dev/null +++ b/src/openai/types/responses/response_reasoning_item_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseReasoningItemParam", "Summary"] + + +class Summary(TypedDict, total=False): + text: Required[str] + """ + A short summary of the reasoning used by the model when generating the response. + """ + + type: Required[Literal["summary_text"]] + """The type of the object. Always `summary_text`.""" + + +class ResponseReasoningItemParam(TypedDict, total=False): + id: Required[str] + """The unique identifier of the reasoning content.""" + + summary: Required[Iterable[Summary]] + """Reasoning text contents.""" + + type: Required[Literal["reasoning"]] + """The type of the object. Always `reasoning`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ From 16a10604fbd0d82c1382b84b417a1d6a2d33a7f1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 21:47:29 +0000 Subject: [PATCH 818/914] release: 1.66.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5d08177085..4e427aab32 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.66.1" + ".": "1.66.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4068372dd6..460dbb287e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.66.2 (2025-03-11) + +Full Changelog: [v1.66.1...v1.66.2](https://github.com/openai/openai-python/compare/v1.66.1...v1.66.2) + +### Bug Fixes + +* **responses:** correct reasoning output type ([#2181](https://github.com/openai/openai-python/issues/2181)) ([8cb1129](https://github.com/openai/openai-python/commit/8cb11299acc40c80061af275691cd09a2bf30c65)) + ## 1.66.1 (2025-03-11) Full Changelog: [v1.66.0...v1.66.1](https://github.com/openai/openai-python/compare/v1.66.0...v1.66.1) diff --git a/pyproject.toml b/pyproject.toml index 04ba80639b..a9d46a72b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.66.1" +version = "1.66.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 473c6aba83..dc6a545c76 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.66.1" # x-release-please-version +__version__ = "1.66.2" # x-release-please-version From 4fb86f6a5efeac33309d87698d3dc397bec4da88 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:36:15 +0000 Subject: [PATCH 819/914] fix: update module level client (#2185) --- src/openai/__init__.py | 3 +++ src/openai/_module_client.py | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index fe85956a4a..7ce6df0817 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -356,8 +356,11 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] images as images, models as models, batches as batches, + uploads as uploads, + responses as responses, embeddings as embeddings, completions as completions, fine_tuning as fine_tuning, moderations as moderations, + vector_stores as vector_stores, ) diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index 6f7356eb3c..e7d2657860 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -48,6 +48,18 @@ def __load__(self) -> resources.Batches: return _load_client().batches +class UploadsProxy(LazyProxy[resources.Uploads]): + @override + def __load__(self) -> resources.Uploads: + return _load_client().uploads + + +class ResponsesProxy(LazyProxy[resources.Responses]): + @override + def __load__(self) -> resources.Responses: + return _load_client().responses + + class EmbeddingsProxy(LazyProxy[resources.Embeddings]): @override def __load__(self) -> resources.Embeddings: @@ -72,6 +84,12 @@ def __load__(self) -> resources.FineTuning: return _load_client().fine_tuning +class VectorStoresProxy(LazyProxy[resources.VectorStores]): + @override + def __load__(self) -> resources.VectorStores: + return _load_client().vector_stores + + chat: resources.Chat = ChatProxy().__as_proxied__() beta: resources.Beta = BetaProxy().__as_proxied__() files: resources.Files = FilesProxy().__as_proxied__() @@ -79,7 +97,10 @@ def __load__(self) -> resources.FineTuning: images: resources.Images = ImagesProxy().__as_proxied__() models: resources.Models = ModelsProxy().__as_proxied__() batches: resources.Batches = BatchesProxy().__as_proxied__() +uploads: resources.Uploads = UploadsProxy().__as_proxied__() +responses: resources.Responses = ResponsesProxy().__as_proxied__() embeddings: resources.Embeddings = EmbeddingsProxy().__as_proxied__() completions: resources.Completions = CompletionsProxy().__as_proxied__() moderations: resources.Moderations = ModerationsProxy().__as_proxied__() fine_tuning: resources.FineTuning = FineTuningProxy().__as_proxied__() +vector_stores: resources.VectorStores = VectorStoresProxy().__as_proxied__() From 9dea82fb8cdd06683f9e8033b54cff219789af7f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:40:02 +0000 Subject: [PATCH 820/914] release: 1.66.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4e427aab32..6d3d57b7ab 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.66.2" + ".": "1.66.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 460dbb287e..e799f6d117 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.66.3 (2025-03-12) + +Full Changelog: [v1.66.2...v1.66.3](https://github.com/openai/openai-python/compare/v1.66.2...v1.66.3) + +### Bug Fixes + +* update module level client ([#2185](https://github.com/openai/openai-python/issues/2185)) ([456f324](https://github.com/openai/openai-python/commit/456f3240a0c33e71521c6b73c32e8adc1b8cd3bc)) + ## 1.66.2 (2025-03-11) Full Changelog: [v1.66.1...v1.66.2](https://github.com/openai/openai-python/compare/v1.66.1...v1.66.2) diff --git a/pyproject.toml b/pyproject.toml index a9d46a72b4..3088eb2fb2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.66.2" +version = "1.66.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index dc6a545c76..6c4a192efc 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.66.2" # x-release-please-version +__version__ = "1.66.3" # x-release-please-version From 11eea35f9557c2e1f4126b722d3e274d8ef3ea7f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:48:23 +0000 Subject: [PATCH 821/914] chore(internal): remove extra empty newlines (#2195) --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3088eb2fb2..2608de2060 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -169,7 +169,6 @@ reportImplicitOverride = true reportImportCycles = false reportPrivateUsage = false - [tool.ruff] line-length = 120 output-format = "grouped" From d664ff22a9958efb7ccc297e280b6562dd14c6ca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 15:21:04 +0000 Subject: [PATCH 822/914] chore(internal): bump rye to 0.44.0 (#2200) --- .devcontainer/Dockerfile | 2 +- .github/workflows/ci.yml | 6 +++--- .github/workflows/create-releases.yml | 2 +- .github/workflows/publish-pypi.yml | 2 +- requirements-dev.lock | 1 + requirements.lock | 1 + 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 55d20255c9..ff261bad78 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode -RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash +RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.44.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0e0ffe2f3..34dfde36fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.35.0' + RYE_VERSION: '0.44.0' RYE_INSTALL_OPTION: '--yes' - name: Install dependencies @@ -43,7 +43,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.35.0' + RYE_VERSION: '0.44.0' RYE_INSTALL_OPTION: '--yes' - name: Bootstrap @@ -64,7 +64,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.35.0' + RYE_VERSION: '0.44.0' RYE_INSTALL_OPTION: '--yes' - name: Install dependencies run: | diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 2a97049033..b3e1c679d4 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -28,7 +28,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.35.0' + RYE_VERSION: '0.44.0' RYE_INSTALL_OPTION: '--yes' - name: Publish to PyPI diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 76d0efca80..32bd6929e2 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -18,7 +18,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.35.0' + RYE_VERSION: '0.44.0' RYE_INSTALL_OPTION: '--yes' - name: Publish to PyPI diff --git a/requirements-dev.lock b/requirements-dev.lock index 5599057b66..48e49f926c 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -7,6 +7,7 @@ # all-features: true # with-sources: false # generate-hashes: false +# universal: false -e file:. annotated-types==0.6.0 diff --git a/requirements.lock b/requirements.lock index cbdff94fa3..b935c0ee59 100644 --- a/requirements.lock +++ b/requirements.lock @@ -7,6 +7,7 @@ # all-features: true # with-sources: false # generate-hashes: false +# universal: false -e file:. annotated-types==0.6.0 From b6ba4876bb5004dbd79b00dcf8ea345e141e1674 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 19:21:28 +0000 Subject: [PATCH 823/914] chore(internal): remove CI condition (#2203) --- .github/workflows/ci.yml | 2 -- .github/workflows/create-releases.yml | 39 --------------------------- .github/workflows/publish-pypi.yml | 8 ++++-- .github/workflows/release-doctor.yml | 1 - .stats.yml | 2 +- bin/check-release-environment | 4 --- 6 files changed, 7 insertions(+), 49 deletions(-) delete mode 100644 .github/workflows/create-releases.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34dfde36fa..06eb10c5f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,6 @@ jobs: lint: name: lint runs-on: ubuntu-latest - if: github.repository == 'openai/openai-python' steps: - uses: actions/checkout@v4 @@ -33,7 +32,6 @@ jobs: test: name: test runs-on: ubuntu-latest - if: github.repository == 'openai/openai-python' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml deleted file mode 100644 index b3e1c679d4..0000000000 --- a/.github/workflows/create-releases.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Create releases -on: - schedule: - - cron: '0 5 * * *' # every day at 5am UTC - push: - branches: - - main - -jobs: - release: - name: release - if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' - runs-on: ubuntu-latest - environment: publish - - steps: - - uses: actions/checkout@v4 - - - uses: stainless-api/trigger-release-please@v1 - id: release - with: - repo: ${{ github.event.repository.full_name }} - stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} - - - name: Install Rye - if: ${{ steps.release.outputs.releases_created }} - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' - - - name: Publish to PyPI - if: ${{ steps.release.outputs.releases_created }} - run: | - bash ./bin/publish-pypi - env: - PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 32bd6929e2..b395b2f545 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -1,9 +1,13 @@ -# workflow for re-running publishing to PyPI in case it fails for some reason -# you can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to PyPI in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml name: Publish PyPI on: workflow_dispatch: + release: + types: [published] + jobs: publish: name: publish diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index e078964a6f..445f626d93 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -19,5 +19,4 @@ jobs: run: | bash ./bin/check-release-environment env: - STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }} PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.stats.yml b/.stats.yml index edc2aaf89f..53c73037d5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 81 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-c8579861bc21d4d2155a5b9e8e7d54faee8083730673c4d32cbbe573d7fb4116.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-c8579861bc21d4d2155a5b9e8e7d54faee8083730673c4d32cbbe573d7fb4116.yml diff --git a/bin/check-release-environment b/bin/check-release-environment index 2cc5ad6352..5471b69edb 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -2,10 +2,6 @@ errors=() -if [ -z "${STAINLESS_API_KEY}" ]; then - errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.") -fi - if [ -z "${PYPI_TOKEN}" ]; then errors+=("The OPENAI_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi From 1cb138b559fa937440388b5b160fc27561af29f7 Mon Sep 17 00:00:00 2001 From: meorphis Date: Fri, 14 Mar 2025 16:54:21 -0400 Subject: [PATCH 824/914] chore(internal): update release workflows --- .github/workflows/publish-pypi.yml | 8 ++------ .github/workflows/release-doctor.yml | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index b395b2f545..32bd6929e2 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -1,13 +1,9 @@ -# This workflow is triggered when a GitHub release is created. -# It can also be run manually to re-publish to PyPI in case it failed for some reason. -# You can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml +# workflow for re-running publishing to PyPI in case it fails for some reason +# you can run this workflow by navigating to https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml name: Publish PyPI on: workflow_dispatch: - release: - types: [published] - jobs: publish: name: publish diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 445f626d93..e078964a6f 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -19,4 +19,5 @@ jobs: run: | bash ./bin/check-release-environment env: + STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }} PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} From eb3d7ae36d2686645e15840ab369255157247dd9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 22:11:11 +0000 Subject: [PATCH 825/914] fix(types): handle more discriminated union shapes (#2206) --- src/openai/_models.py | 7 +++++-- tests/test_models.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 92986bfdf5..ff7c1f3392 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -66,7 +66,7 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: - from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema + from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema __all__ = ["BaseModel", "GenericModel"] @@ -671,15 +671,18 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: schema = model.__pydantic_core_schema__ + if schema["type"] == "definitions": + schema = schema["schema"] + if schema["type"] != "model": return None + schema = cast("ModelSchema", schema) fields_schema = schema["schema"] if fields_schema["type"] != "model-fields": return None fields_schema = cast("ModelFieldsSchema", fields_schema) - field = fields_schema["fields"].get(field_name) if not field: return None diff --git a/tests/test_models.py b/tests/test_models.py index 30b17e3ac0..b9be1f3ea3 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -854,3 +854,35 @@ class Model(BaseModel): m = construct_type(value={"cls": "foo"}, type_=Model) assert isinstance(m, Model) assert isinstance(m.cls, str) + + +def test_discriminated_union_case() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["b"] + + data: List[Union[A, object]] + + class ModelA(BaseModel): + type: Literal["modelA"] + + data: int + + class ModelB(BaseModel): + type: Literal["modelB"] + + required: str + + data: Union[A, B] + + # when constructing ModelA | ModelB, value data doesn't match ModelB exactly - missing `required` + m = construct_type( + value={"type": "modelB", "data": {"type": "a", "data": True}}, + type_=cast(Any, Annotated[Union[ModelA, ModelB], PropertyInfo(discriminator="type")]), + ) + + assert isinstance(m, ModelB) From 5647865266af923b2e257ea0b5fc77e590542490 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:04:17 +0000 Subject: [PATCH 826/914] fix(ci): ensure pip is always available (#2207) --- bin/publish-pypi | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/publish-pypi b/bin/publish-pypi index 05bfccbb71..ebebf91657 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -5,5 +5,6 @@ mkdir -p dist rye build --clean # Patching importlib-metadata version until upstream library version is updated # https://github.com/pypa/twine/issues/977#issuecomment-2189800841 +"$HOME/.rye/self/bin/python3" -m ensurepip "$HOME/.rye/self/bin/python3" -m pip install 'importlib-metadata==7.2.1' rye publish --yes --token=$PYPI_TOKEN From 69919665d26d32c2c729ac7f1dc8db4e0d14d1a4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 15:52:16 +0000 Subject: [PATCH 827/914] fix(ci): remove publishing patch (#2208) --- bin/publish-pypi | 4 ---- pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/bin/publish-pypi b/bin/publish-pypi index ebebf91657..826054e924 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -3,8 +3,4 @@ set -eux mkdir -p dist rye build --clean -# Patching importlib-metadata version until upstream library version is updated -# https://github.com/pypa/twine/issues/977#issuecomment-2189800841 -"$HOME/.rye/self/bin/python3" -m ensurepip -"$HOME/.rye/self/bin/python3" -m pip install 'importlib-metadata==7.2.1' rye publish --yes --token=$PYPI_TOKEN diff --git a/pyproject.toml b/pyproject.toml index 2608de2060..0a9a931f6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,7 @@ typecheck = { chain = [ "typecheck:mypy" = "mypy ." [build-system] -requires = ["hatchling", "hatch-fancy-pypi-readme"] +requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"] build-backend = "hatchling.build" [tool.hatch.build] From 17d78674d17e74b344f3701eb4ca6f5aa5cf1fc5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 15:52:49 +0000 Subject: [PATCH 828/914] release: 1.66.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6d3d57b7ab..dac37ce406 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.66.3" + ".": "1.66.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e799f6d117..1ed70082c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 1.66.4 (2025-03-17) + +Full Changelog: [v1.66.3...v1.66.4](https://github.com/openai/openai-python/compare/v1.66.3...v1.66.4) + +### Bug Fixes + +* **ci:** ensure pip is always available ([#2207](https://github.com/openai/openai-python/issues/2207)) ([3f08e56](https://github.com/openai/openai-python/commit/3f08e56a48a04c2b7f03a4ad63f38228e25810e6)) +* **ci:** remove publishing patch ([#2208](https://github.com/openai/openai-python/issues/2208)) ([dd2dab7](https://github.com/openai/openai-python/commit/dd2dab7faf2a003da3e6af66780bd250be6e7f3f)) +* **types:** handle more discriminated union shapes ([#2206](https://github.com/openai/openai-python/issues/2206)) ([f85a9c6](https://github.com/openai/openai-python/commit/f85a9c633dcb9b64c0eb47d20151894742bbef22)) + + +### Chores + +* **internal:** bump rye to 0.44.0 ([#2200](https://github.com/openai/openai-python/issues/2200)) ([2dd3139](https://github.com/openai/openai-python/commit/2dd3139df6e7fe6307f9847e6527073e355e5047)) +* **internal:** remove CI condition ([#2203](https://github.com/openai/openai-python/issues/2203)) ([9620fdc](https://github.com/openai/openai-python/commit/9620fdcf4f2d01b6753ecc0abc16e5239c2b41e1)) +* **internal:** remove extra empty newlines ([#2195](https://github.com/openai/openai-python/issues/2195)) ([a1016a7](https://github.com/openai/openai-python/commit/a1016a78fe551e0f0e2562a0e81d1cb724d195da)) +* **internal:** update release workflows ([e2def44](https://github.com/openai/openai-python/commit/e2def4453323aa1cf8077df447fd55eb4c626393)) + ## 1.66.3 (2025-03-12) Full Changelog: [v1.66.2...v1.66.3](https://github.com/openai/openai-python/compare/v1.66.2...v1.66.3) diff --git a/pyproject.toml b/pyproject.toml index 0a9a931f6f..8247861185 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.66.3" +version = "1.66.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6c4a192efc..df2f60a7dc 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.66.3" # x-release-please-version +__version__ = "1.66.4" # x-release-please-version From bff8da95ab1967426b92e0c0b899596a05606130 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 21:50:14 +0000 Subject: [PATCH 829/914] release: 1.66.5 (#2223) * chore(internal): remove extra empty newlines (#2195) * chore(internal): bump rye to 0.44.0 (#2200) * chore(internal): remove CI condition (#2203) * chore(internal): update release workflows * fix(types): handle more discriminated union shapes (#2206) * fix(ci): ensure pip is always available (#2207) * fix(ci): remove publishing patch (#2208) * chore(internal): add back releases workflow * chore(internal): codegen related update (#2222) * fix(types): improve responses type names (#2224) * release: 1.66.5 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: meorphis --- .github/workflows/create-releases.yml | 39 +++++ .release-please-manifest.json | 2 +- .stats.yml | 2 +- CHANGELOG.md | 14 ++ api.md | 8 +- pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/batches.py | 16 +-- src/openai/resources/responses/input_items.py | 14 +- src/openai/types/batch_create_params.py | 9 +- .../types/chat/chat_completion_chunk.py | 7 +- .../chat_completion_content_part_param.py | 2 +- .../chat_completion_stream_options_param.py | 7 +- src/openai/types/responses/__init__.py | 15 ++ ...response_computer_tool_call_output_item.py | 47 ++++++ ...se_computer_tool_call_output_screenshot.py | 22 +++ ...puter_tool_call_output_screenshot_param.py | 21 +++ .../responses/response_function_tool_call.py | 6 +- .../response_function_tool_call_item.py | 11 ++ ...response_function_tool_call_output_item.py | 29 ++++ .../response_function_tool_call_param.py | 6 +- .../responses/response_input_item_param.py | 18 +-- .../responses/response_input_message_item.py | 33 +++++ .../types/responses/response_input_param.py | 18 +-- src/openai/types/responses/response_item.py | 30 ++++ .../types/responses/response_item_list.py | 136 +----------------- src/openai/types/responses/response_usage.py | 13 +- src/openai/types/shared/reasoning.py | 2 +- src/openai/types/shared_params/reasoning.py | 6 +- .../responses/test_input_items.py | 18 +-- tests/api_resources/test_batches.py | 16 +-- 31 files changed, 351 insertions(+), 220 deletions(-) create mode 100644 .github/workflows/create-releases.yml create mode 100644 src/openai/types/responses/response_computer_tool_call_output_item.py create mode 100644 src/openai/types/responses/response_computer_tool_call_output_screenshot.py create mode 100644 src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py create mode 100644 src/openai/types/responses/response_function_tool_call_item.py create mode 100644 src/openai/types/responses/response_function_tool_call_output_item.py create mode 100644 src/openai/types/responses/response_input_message_item.py create mode 100644 src/openai/types/responses/response_item.py diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml new file mode 100644 index 0000000000..b3e1c679d4 --- /dev/null +++ b/.github/workflows/create-releases.yml @@ -0,0 +1,39 @@ +name: Create releases +on: + schedule: + - cron: '0 5 * * *' # every day at 5am UTC + push: + branches: + - main + +jobs: + release: + name: release + if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' + runs-on: ubuntu-latest + environment: publish + + steps: + - uses: actions/checkout@v4 + + - uses: stainless-api/trigger-release-please@v1 + id: release + with: + repo: ${{ github.event.repository.full_name }} + stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} + + - name: Install Rye + if: ${{ steps.release.outputs.releases_created }} + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Publish to PyPI + if: ${{ steps.release.outputs.releases_created }} + run: | + bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index dac37ce406..e567f9cb13 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.66.4" + ".": "1.66.5" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 53c73037d5..b032562238 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 81 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-c8579861bc21d4d2155a5b9e8e7d54faee8083730673c4d32cbbe573d7fb4116.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f3bce04386c4fcfd5037e0477fbaa39010003fd1558eb5185fe4a71dd6a05fdd.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ed70082c7..d8fb019fc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.66.5 (2025-03-18) + +Full Changelog: [v1.66.4...v1.66.5](https://github.com/openai/openai-python/compare/v1.66.4...v1.66.5) + +### Bug Fixes + +* **types:** improve responses type names ([#2224](https://github.com/openai/openai-python/issues/2224)) ([5f7beb8](https://github.com/openai/openai-python/commit/5f7beb873af5ccef2551f34ab3ef098e099ce9c6)) + + +### Chores + +* **internal:** add back releases workflow ([c71d4c9](https://github.com/openai/openai-python/commit/c71d4c918eab3532b36ea944b0c4069db6ac2d38)) +* **internal:** codegen related update ([#2222](https://github.com/openai/openai-python/issues/2222)) ([f570d91](https://github.com/openai/openai-python/commit/f570d914a16cb5092533e32dfd863027d378c0b5)) + ## 1.66.4 (2025-03-17) Full Changelog: [v1.66.3...v1.66.4](https://github.com/openai/openai-python/compare/v1.66.3...v1.66.4) diff --git a/api.md b/api.md index e760fe69c1..6e7f48a645 100644 --- a/api.md +++ b/api.md @@ -605,6 +605,8 @@ from openai.types.responses import ( ResponseCodeInterpreterToolCall, ResponseCompletedEvent, ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseComputerToolCallOutputScreenshot, ResponseContent, ResponseContentPartAddedEvent, ResponseContentPartDoneEvent, @@ -621,6 +623,8 @@ from openai.types.responses import ( ResponseFunctionCallArgumentsDeltaEvent, ResponseFunctionCallArgumentsDoneEvent, ResponseFunctionToolCall, + ResponseFunctionToolCallItem, + ResponseFunctionToolCallOutputItem, ResponseFunctionWebSearch, ResponseInProgressEvent, ResponseIncludable, @@ -632,7 +636,9 @@ from openai.types.responses import ( ResponseInputImage, ResponseInputItem, ResponseInputMessageContentList, + ResponseInputMessageItem, ResponseInputText, + ResponseItem, ResponseOutputAudio, ResponseOutputItem, ResponseOutputItemAddedEvent, @@ -677,4 +683,4 @@ from openai.types.responses import ResponseItemList Methods: -- client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[Data] +- client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem] diff --git a/pyproject.toml b/pyproject.toml index 8247861185..5fdf2a836d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.66.4" +version = "1.66.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index df2f60a7dc..dbefc6ec32 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.66.4" # x-release-please-version +__version__ = "1.66.5" # x-release-please-version diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 7e7ec19ec2..b7a299be12 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -49,7 +49,7 @@ def create( self, *, completion_window: Literal["24h"], - endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"], + endpoint: Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -67,9 +67,9 @@ def create( is supported. endpoint: The endpoint to be used for all requests in the batch. Currently - `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` are supported. - Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 - embedding inputs across all requests in the batch. + `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` + are supported. Note that `/v1/embeddings` batches are also restricted to a + maximum of 50,000 embedding inputs across all requests in the batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. @@ -259,7 +259,7 @@ async def create( self, *, completion_window: Literal["24h"], - endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"], + endpoint: Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -277,9 +277,9 @@ async def create( is supported. endpoint: The endpoint to be used for all requests in the batch. Currently - `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` are supported. - Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 - embedding inputs across all requests in the batch. + `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` + are supported. Note that `/v1/embeddings` batches are also restricted to a + maximum of 50,000 embedding inputs across all requests in the batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py index 10e7d545dc..e341393cd1 100644 --- a/src/openai/resources/responses/input_items.py +++ b/src/openai/resources/responses/input_items.py @@ -16,7 +16,7 @@ from ...pagination import SyncCursorPage, AsyncCursorPage from ..._base_client import AsyncPaginator, make_request_options from ...types.responses import input_item_list_params -from ...types.responses.response_item_list import Data +from ...types.responses.response_item import ResponseItem __all__ = ["InputItems", "AsyncInputItems"] @@ -55,7 +55,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncCursorPage[Data]: + ) -> SyncCursorPage[ResponseItem]: """ Returns a list of input items for a given response. @@ -84,7 +84,7 @@ def list( raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._get_api_list( f"/responses/{response_id}/input_items", - page=SyncCursorPage[Data], + page=SyncCursorPage[ResponseItem], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -100,7 +100,7 @@ def list( input_item_list_params.InputItemListParams, ), ), - model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system + model=cast(Any, ResponseItem), # Union types cannot be passed in as arguments in the type system ) @@ -138,7 +138,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[Data, AsyncCursorPage[Data]]: + ) -> AsyncPaginator[ResponseItem, AsyncCursorPage[ResponseItem]]: """ Returns a list of input items for a given response. @@ -167,7 +167,7 @@ def list( raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._get_api_list( f"/responses/{response_id}/input_items", - page=AsyncCursorPage[Data], + page=AsyncCursorPage[ResponseItem], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -183,7 +183,7 @@ def list( input_item_list_params.InputItemListParams, ), ), - model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system + model=cast(Any, ResponseItem), # Union types cannot be passed in as arguments in the type system ) diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index e5be1d2bac..cc95afd3ba 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -17,12 +17,13 @@ class BatchCreateParams(TypedDict, total=False): Currently only `24h` is supported. """ - endpoint: Required[Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"]] + endpoint: Required[Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"]] """The endpoint to be used for all requests in the batch. - Currently `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` are - supported. Note that `/v1/embeddings` batches are also restricted to a maximum - of 50,000 embedding inputs across all requests in the batch. + Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, and + `/v1/completions` are supported. Note that `/v1/embeddings` batches are also + restricted to a maximum of 50,000 embedding inputs across all requests in the + batch. """ input_file_id: Required[str] diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index dede513f1e..31b9cb5456 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -142,6 +142,9 @@ class ChatCompletionChunk(BaseModel): """ An optional field that will only be present when you set `stream_options: {"include_usage": true}` in your request. When present, it - contains a null value except for the last chunk which contains the token usage - statistics for the entire request. + contains a null value **except for the last chunk** which contains the token + usage statistics for the entire request. + + **NOTE:** If the stream is interrupted or cancelled, you may not receive the + final usage chunk which contains the total token usage for the request. """ diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index 1293c54312..cbedc853ba 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -22,7 +22,7 @@ class FileFile(TypedDict, total=False): file_id: str """The ID of an uploaded file to use as input.""" - file_name: str + filename: str """The name of the file, used when passing the file to the model as a string.""" diff --git a/src/openai/types/chat/chat_completion_stream_options_param.py b/src/openai/types/chat/chat_completion_stream_options_param.py index fbf7291821..471e0eba98 100644 --- a/src/openai/types/chat/chat_completion_stream_options_param.py +++ b/src/openai/types/chat/chat_completion_stream_options_param.py @@ -12,6 +12,9 @@ class ChatCompletionStreamOptionsParam(TypedDict, total=False): """If set, an additional chunk will be streamed before the `data: [DONE]` message. The `usage` field on this chunk shows the token usage statistics for the entire - request, and the `choices` field will always be an empty array. All other chunks - will also include a `usage` field, but with a null value. + request, and the `choices` field will always be an empty array. + + All other chunks will also include a `usage` field, but with a null value. + **NOTE:** If the stream is interrupted, you may not receive the final usage + chunk which contains the total token usage for the request. """ diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 7c0cf9e3f2..4f07a3d097 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -7,6 +7,7 @@ from .tool_param import ToolParam as ToolParam from .computer_tool import ComputerTool as ComputerTool from .function_tool import FunctionTool as FunctionTool +from .response_item import ResponseItem as ResponseItem from .response_error import ResponseError as ResponseError from .response_usage import ResponseUsage as ResponseUsage from .parsed_response import ( @@ -66,6 +67,7 @@ from .response_computer_tool_call import ResponseComputerToolCall as ResponseComputerToolCall from .response_format_text_config import ResponseFormatTextConfig as ResponseFormatTextConfig from .response_function_tool_call import ResponseFunctionToolCall as ResponseFunctionToolCall +from .response_input_message_item import ResponseInputMessageItem as ResponseInputMessageItem from .response_refusal_done_event import ResponseRefusalDoneEvent as ResponseRefusalDoneEvent from .response_function_web_search import ResponseFunctionWebSearch as ResponseFunctionWebSearch from .response_input_content_param import ResponseInputContentParam as ResponseInputContentParam @@ -76,6 +78,7 @@ from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent +from .response_function_tool_call_item import ResponseFunctionToolCallItem as ResponseFunctionToolCallItem from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent from .response_computer_tool_call_param import ResponseComputerToolCallParam as ResponseComputerToolCallParam from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent @@ -90,9 +93,15 @@ from .response_audio_transcript_delta_event import ( ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, ) +from .response_computer_tool_call_output_item import ( + ResponseComputerToolCallOutputItem as ResponseComputerToolCallOutputItem, +) from .response_format_text_json_schema_config import ( ResponseFormatTextJSONSchemaConfig as ResponseFormatTextJSONSchemaConfig, ) +from .response_function_tool_call_output_item import ( + ResponseFunctionToolCallOutputItem as ResponseFunctionToolCallOutputItem, +) from .response_web_search_call_completed_event import ( ResponseWebSearchCallCompletedEvent as ResponseWebSearchCallCompletedEvent, ) @@ -120,6 +129,9 @@ from .response_function_call_arguments_delta_event import ( ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, ) +from .response_computer_tool_call_output_screenshot import ( + ResponseComputerToolCallOutputScreenshot as ResponseComputerToolCallOutputScreenshot, +) from .response_format_text_json_schema_config_param import ( ResponseFormatTextJSONSchemaConfigParam as ResponseFormatTextJSONSchemaConfigParam, ) @@ -138,3 +150,6 @@ from .response_code_interpreter_call_interpreting_event import ( ResponseCodeInterpreterCallInterpretingEvent as ResponseCodeInterpreterCallInterpretingEvent, ) +from .response_computer_tool_call_output_screenshot_param import ( + ResponseComputerToolCallOutputScreenshotParam as ResponseComputerToolCallOutputScreenshotParam, +) diff --git a/src/openai/types/responses/response_computer_tool_call_output_item.py b/src/openai/types/responses/response_computer_tool_call_output_item.py new file mode 100644 index 0000000000..a2dd68f579 --- /dev/null +++ b/src/openai/types/responses/response_computer_tool_call_output_item.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_computer_tool_call_output_screenshot import ResponseComputerToolCallOutputScreenshot + +__all__ = ["ResponseComputerToolCallOutputItem", "AcknowledgedSafetyCheck"] + + +class AcknowledgedSafetyCheck(BaseModel): + id: str + """The ID of the pending safety check.""" + + code: str + """The type of the pending safety check.""" + + message: str + """Details about the pending safety check.""" + + +class ResponseComputerToolCallOutputItem(BaseModel): + id: str + """The unique ID of the computer call tool output.""" + + call_id: str + """The ID of the computer tool call that produced the output.""" + + output: ResponseComputerToolCallOutputScreenshot + """A computer screenshot image used with the computer use tool.""" + + type: Literal["computer_call_output"] + """The type of the computer tool call output. Always `computer_call_output`.""" + + acknowledged_safety_checks: Optional[List[AcknowledgedSafetyCheck]] = None + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ diff --git a/src/openai/types/responses/response_computer_tool_call_output_screenshot.py b/src/openai/types/responses/response_computer_tool_call_output_screenshot.py new file mode 100644 index 0000000000..a500da85c1 --- /dev/null +++ b/src/openai/types/responses/response_computer_tool_call_output_screenshot.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseComputerToolCallOutputScreenshot"] + + +class ResponseComputerToolCallOutputScreenshot(BaseModel): + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" diff --git a/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py b/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py new file mode 100644 index 0000000000..efc2028aa4 --- /dev/null +++ b/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseComputerToolCallOutputScreenshotParam"] + + +class ResponseComputerToolCallOutputScreenshotParam(TypedDict, total=False): + type: Required[Literal["computer_screenshot"]] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: str + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: str + """The URL of the screenshot image.""" diff --git a/src/openai/types/responses/response_function_tool_call.py b/src/openai/types/responses/response_function_tool_call.py index 5d82906cb7..2a8482204e 100644 --- a/src/openai/types/responses/response_function_tool_call.py +++ b/src/openai/types/responses/response_function_tool_call.py @@ -9,9 +9,6 @@ class ResponseFunctionToolCall(BaseModel): - id: str - """The unique ID of the function tool call.""" - arguments: str """A JSON string of the arguments to pass to the function.""" @@ -24,6 +21,9 @@ class ResponseFunctionToolCall(BaseModel): type: Literal["function_call"] """The type of the function tool call. Always `function_call`.""" + id: Optional[str] = None + """The unique ID of the function tool call.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None """The status of the item. diff --git a/src/openai/types/responses/response_function_tool_call_item.py b/src/openai/types/responses/response_function_tool_call_item.py new file mode 100644 index 0000000000..477e9b70aa --- /dev/null +++ b/src/openai/types/responses/response_function_tool_call_item.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from .response_function_tool_call import ResponseFunctionToolCall + +__all__ = ["ResponseFunctionToolCallItem"] + + +class ResponseFunctionToolCallItem(ResponseFunctionToolCall): + id: str # type: ignore + """The unique ID of the function call tool output.""" diff --git a/src/openai/types/responses/response_function_tool_call_output_item.py b/src/openai/types/responses/response_function_tool_call_output_item.py new file mode 100644 index 0000000000..4c8c41a6fe --- /dev/null +++ b/src/openai/types/responses/response_function_tool_call_output_item.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionToolCallOutputItem"] + + +class ResponseFunctionToolCallOutputItem(BaseModel): + id: str + """The unique ID of the function call tool output.""" + + call_id: str + """The unique ID of the function tool call generated by the model.""" + + output: str + """A JSON string of the output of the function tool call.""" + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/responses/response_function_tool_call_param.py b/src/openai/types/responses/response_function_tool_call_param.py index 51b947a764..eaa263cf67 100644 --- a/src/openai/types/responses/response_function_tool_call_param.py +++ b/src/openai/types/responses/response_function_tool_call_param.py @@ -8,9 +8,6 @@ class ResponseFunctionToolCallParam(TypedDict, total=False): - id: Required[str] - """The unique ID of the function tool call.""" - arguments: Required[str] """A JSON string of the arguments to pass to the function.""" @@ -23,6 +20,9 @@ class ResponseFunctionToolCallParam(TypedDict, total=False): type: Required[Literal["function_call"]] """The type of the function tool call. Always `function_call`.""" + id: str + """The unique ID of the function tool call.""" + status: Literal["in_progress", "completed", "incomplete"] """The status of the item. diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 32ac13cabb..2505f7c0b5 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -13,12 +13,12 @@ from .response_function_web_search_param import ResponseFunctionWebSearchParam from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam +from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam __all__ = [ "ResponseInputItemParam", "Message", "ComputerCallOutput", - "ComputerCallOutputOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", "ItemReference", @@ -46,20 +46,6 @@ class Message(TypedDict, total=False): """The type of the message input. Always set to `message`.""" -class ComputerCallOutputOutput(TypedDict, total=False): - type: Required[Literal["computer_screenshot"]] - """Specifies the event type. - - For a computer screenshot, this property is always set to `computer_screenshot`. - """ - - file_id: str - """The identifier of an uploaded file that contains the screenshot.""" - - image_url: str - """The URL of the screenshot image.""" - - class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): id: Required[str] """The ID of the pending safety check.""" @@ -75,7 +61,7 @@ class ComputerCallOutput(TypedDict, total=False): call_id: Required[str] """The ID of the computer tool call that produced the output.""" - output: Required[ComputerCallOutputOutput] + output: Required[ResponseComputerToolCallOutputScreenshotParam] """A computer screenshot image used with the computer use tool.""" type: Required[Literal["computer_call_output"]] diff --git a/src/openai/types/responses/response_input_message_item.py b/src/openai/types/responses/response_input_message_item.py new file mode 100644 index 0000000000..6a788e7fa4 --- /dev/null +++ b/src/openai/types/responses/response_input_message_item.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_input_message_content_list import ResponseInputMessageContentList + +__all__ = ["ResponseInputMessageItem"] + + +class ResponseInputMessageItem(BaseModel): + id: str + """The unique ID of the message input.""" + + content: ResponseInputMessageContentList + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Literal["user", "system", "developer"] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always set to `message`.""" diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index b942f4868a..84a80eb7c2 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -13,13 +13,13 @@ from .response_function_web_search_param import ResponseFunctionWebSearchParam from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam +from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam __all__ = [ "ResponseInputParam", "ResponseInputItemParam", "Message", "ComputerCallOutput", - "ComputerCallOutputOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", "ItemReference", @@ -47,20 +47,6 @@ class Message(TypedDict, total=False): """The type of the message input. Always set to `message`.""" -class ComputerCallOutputOutput(TypedDict, total=False): - type: Required[Literal["computer_screenshot"]] - """Specifies the event type. - - For a computer screenshot, this property is always set to `computer_screenshot`. - """ - - file_id: str - """The identifier of an uploaded file that contains the screenshot.""" - - image_url: str - """The URL of the screenshot image.""" - - class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): id: Required[str] """The ID of the pending safety check.""" @@ -76,7 +62,7 @@ class ComputerCallOutput(TypedDict, total=False): call_id: Required[str] """The ID of the computer tool call that produced the output.""" - output: Required[ComputerCallOutputOutput] + output: Required[ResponseComputerToolCallOutputScreenshotParam] """A computer screenshot image used with the computer use tool.""" type: Required[Literal["computer_call_output"]] diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py new file mode 100644 index 0000000000..dc8d67d0f2 --- /dev/null +++ b/src/openai/types/responses/response_item.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_output_message import ResponseOutputMessage +from .response_computer_tool_call import ResponseComputerToolCall +from .response_input_message_item import ResponseInputMessageItem +from .response_function_web_search import ResponseFunctionWebSearch +from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_function_tool_call_item import ResponseFunctionToolCallItem +from .response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem +from .response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem + +__all__ = ["ResponseItem"] + +ResponseItem: TypeAlias = Annotated[ + Union[ + ResponseInputMessageItem, + ResponseOutputMessage, + ResponseFileSearchToolCall, + ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseFunctionWebSearch, + ResponseFunctionToolCallItem, + ResponseFunctionToolCallOutputItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/responses/response_item_list.py b/src/openai/types/responses/response_item_list.py index 7c3e4d7f82..b43eacdb51 100644 --- a/src/openai/types/responses/response_item_list.py +++ b/src/openai/types/responses/response_item_list.py @@ -1,142 +1,16 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias +from typing import List +from typing_extensions import Literal -from ..._utils import PropertyInfo from ..._models import BaseModel -from .response_output_message import ResponseOutputMessage -from .response_computer_tool_call import ResponseComputerToolCall -from .response_function_tool_call import ResponseFunctionToolCall -from .response_function_web_search import ResponseFunctionWebSearch -from .response_file_search_tool_call import ResponseFileSearchToolCall -from .response_input_message_content_list import ResponseInputMessageContentList +from .response_item import ResponseItem -__all__ = [ - "ResponseItemList", - "Data", - "DataMessage", - "DataComputerCallOutput", - "DataComputerCallOutputOutput", - "DataComputerCallOutputAcknowledgedSafetyCheck", - "DataFunctionCallOutput", -] - - -class DataMessage(BaseModel): - id: str - """The unique ID of the message input.""" - - content: ResponseInputMessageContentList - """ - A list of one or many input items to the model, containing different content - types. - """ - - role: Literal["user", "system", "developer"] - """The role of the message input. One of `user`, `system`, or `developer`.""" - - status: Optional[Literal["in_progress", "completed", "incomplete"]] = None - """The status of item. - - One of `in_progress`, `completed`, or `incomplete`. Populated when items are - returned via API. - """ - - type: Optional[Literal["message"]] = None - """The type of the message input. Always set to `message`.""" - - -class DataComputerCallOutputOutput(BaseModel): - type: Literal["computer_screenshot"] - """Specifies the event type. - - For a computer screenshot, this property is always set to `computer_screenshot`. - """ - - file_id: Optional[str] = None - """The identifier of an uploaded file that contains the screenshot.""" - - image_url: Optional[str] = None - """The URL of the screenshot image.""" - - -class DataComputerCallOutputAcknowledgedSafetyCheck(BaseModel): - id: str - """The ID of the pending safety check.""" - - code: str - """The type of the pending safety check.""" - - message: str - """Details about the pending safety check.""" - - -class DataComputerCallOutput(BaseModel): - id: str - """The unique ID of the computer call tool output.""" - - call_id: str - """The ID of the computer tool call that produced the output.""" - - output: DataComputerCallOutputOutput - """A computer screenshot image used with the computer use tool.""" - - type: Literal["computer_call_output"] - """The type of the computer tool call output. Always `computer_call_output`.""" - - acknowledged_safety_checks: Optional[List[DataComputerCallOutputAcknowledgedSafetyCheck]] = None - """ - The safety checks reported by the API that have been acknowledged by the - developer. - """ - - status: Optional[Literal["in_progress", "completed", "incomplete"]] = None - """The status of the message input. - - One of `in_progress`, `completed`, or `incomplete`. Populated when input items - are returned via API. - """ - - -class DataFunctionCallOutput(BaseModel): - id: str - """The unique ID of the function call tool output.""" - - call_id: str - """The unique ID of the function tool call generated by the model.""" - - output: str - """A JSON string of the output of the function tool call.""" - - type: Literal["function_call_output"] - """The type of the function tool call output. Always `function_call_output`.""" - - status: Optional[Literal["in_progress", "completed", "incomplete"]] = None - """The status of the item. - - One of `in_progress`, `completed`, or `incomplete`. Populated when items are - returned via API. - """ - - -Data: TypeAlias = Annotated[ - Union[ - DataMessage, - ResponseOutputMessage, - ResponseFileSearchToolCall, - ResponseComputerToolCall, - DataComputerCallOutput, - ResponseFunctionWebSearch, - ResponseFunctionToolCall, - DataFunctionCallOutput, - ], - PropertyInfo(discriminator="type"), -] +__all__ = ["ResponseItemList"] class ResponseItemList(BaseModel): - data: List[Data] + data: List[ResponseItem] """A list of items used to generate this response.""" first_id: str diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py index ef631c5882..9ad36bd326 100644 --- a/src/openai/types/responses/response_usage.py +++ b/src/openai/types/responses/response_usage.py @@ -3,7 +3,15 @@ from ..._models import BaseModel -__all__ = ["ResponseUsage", "OutputTokensDetails"] +__all__ = ["ResponseUsage", "InputTokensDetails", "OutputTokensDetails"] + + +class InputTokensDetails(BaseModel): + cached_tokens: int + """The number of tokens that were retrieved from the cache. + + [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching). + """ class OutputTokensDetails(BaseModel): @@ -15,6 +23,9 @@ class ResponseUsage(BaseModel): input_tokens: int """The number of input tokens.""" + input_tokens_details: InputTokensDetails + """A detailed breakdown of the input tokens.""" + output_tokens: int """The number of output tokens.""" diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 50821a1727..78a396d738 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -20,7 +20,7 @@ class Reasoning(BaseModel): """ generate_summary: Optional[Literal["concise", "detailed"]] = None - """**o-series models only** + """**computer_use_preview only** A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. One of `concise` or diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index f2b5c5963a..2953b895c4 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, TypedDict from ..shared.reasoning_effort import ReasoningEffort @@ -11,7 +11,7 @@ class Reasoning(TypedDict, total=False): - effort: Required[Optional[ReasoningEffort]] + effort: Optional[ReasoningEffort] """**o-series models only** Constrains effort on reasoning for @@ -21,7 +21,7 @@ class Reasoning(TypedDict, total=False): """ generate_summary: Optional[Literal["concise", "detailed"]] - """**o-series models only** + """**computer_use_preview only** A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. One of `concise` or diff --git a/tests/api_resources/responses/test_input_items.py b/tests/api_resources/responses/test_input_items.py index 28c5e8ca1f..77a156b5ac 100644 --- a/tests/api_resources/responses/test_input_items.py +++ b/tests/api_resources/responses/test_input_items.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.responses.response_item_list import Data +from openai.types.responses import ResponseItem base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") @@ -23,7 +23,7 @@ def test_method_list(self, client: OpenAI) -> None: input_item = client.responses.input_items.list( response_id="response_id", ) - assert_matches_type(SyncCursorPage[Data], input_item, path=["response"]) + assert_matches_type(SyncCursorPage[ResponseItem], input_item, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -34,7 +34,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[Data], input_item, path=["response"]) + assert_matches_type(SyncCursorPage[ResponseItem], input_item, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -45,7 +45,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" input_item = response.parse() - assert_matches_type(SyncCursorPage[Data], input_item, path=["response"]) + assert_matches_type(SyncCursorPage[ResponseItem], input_item, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -56,7 +56,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" input_item = response.parse() - assert_matches_type(SyncCursorPage[Data], input_item, path=["response"]) + assert_matches_type(SyncCursorPage[ResponseItem], input_item, path=["response"]) assert cast(Any, response.is_closed) is True @@ -76,7 +76,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: input_item = await async_client.responses.input_items.list( response_id="response_id", ) - assert_matches_type(AsyncCursorPage[Data], input_item, path=["response"]) + assert_matches_type(AsyncCursorPage[ResponseItem], input_item, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -87,7 +87,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[Data], input_item, path=["response"]) + assert_matches_type(AsyncCursorPage[ResponseItem], input_item, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -98,7 +98,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" input_item = response.parse() - assert_matches_type(AsyncCursorPage[Data], input_item, path=["response"]) + assert_matches_type(AsyncCursorPage[ResponseItem], input_item, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -109,7 +109,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" input_item = await response.parse() - assert_matches_type(AsyncCursorPage[Data], input_item, path=["response"]) + assert_matches_type(AsyncCursorPage[ResponseItem], input_item, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py index 6f9b598e61..a2f8fb48a3 100644 --- a/tests/api_resources/test_batches.py +++ b/tests/api_resources/test_batches.py @@ -22,7 +22,7 @@ class TestBatches: def test_method_create(self, client: OpenAI) -> None: batch = client.batches.create( completion_window="24h", - endpoint="/v1/chat/completions", + endpoint="/v1/responses", input_file_id="string", ) assert_matches_type(Batch, batch, path=["response"]) @@ -31,7 +31,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: batch = client.batches.create( completion_window="24h", - endpoint="/v1/chat/completions", + endpoint="/v1/responses", input_file_id="string", metadata={"foo": "string"}, ) @@ -41,7 +41,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.batches.with_raw_response.create( completion_window="24h", - endpoint="/v1/chat/completions", + endpoint="/v1/responses", input_file_id="string", ) @@ -54,7 +54,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: def test_streaming_response_create(self, client: OpenAI) -> None: with client.batches.with_streaming_response.create( completion_window="24h", - endpoint="/v1/chat/completions", + endpoint="/v1/responses", input_file_id="string", ) as response: assert not response.is_closed @@ -182,7 +182,7 @@ class TestAsyncBatches: async def test_method_create(self, async_client: AsyncOpenAI) -> None: batch = await async_client.batches.create( completion_window="24h", - endpoint="/v1/chat/completions", + endpoint="/v1/responses", input_file_id="string", ) assert_matches_type(Batch, batch, path=["response"]) @@ -191,7 +191,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: batch = await async_client.batches.create( completion_window="24h", - endpoint="/v1/chat/completions", + endpoint="/v1/responses", input_file_id="string", metadata={"foo": "string"}, ) @@ -201,7 +201,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.batches.with_raw_response.create( completion_window="24h", - endpoint="/v1/chat/completions", + endpoint="/v1/responses", input_file_id="string", ) @@ -214,7 +214,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.batches.with_streaming_response.create( completion_window="24h", - endpoint="/v1/chat/completions", + endpoint="/v1/responses", input_file_id="string", ) as response: assert not response.is_closed From 653dfec4c0abf67c4f90f25044c57bc6acbae77a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 20:39:23 +0000 Subject: [PATCH 830/914] feat(api): o1-pro now available through the API (#2228) --- .stats.yml | 2 +- api.md | 2 + src/openai/resources/responses/responses.py | 17 +- .../resources/responses/responses.py.orig | 1796 +++++++++++++++++ src/openai/types/__init__.py | 2 + src/openai/types/responses/response.py | 4 +- .../types/responses/response_create_params.py | 4 +- .../response_function_tool_call_item.py | 2 +- src/openai/types/shared/__init__.py | 2 + src/openai/types/shared/all_models.py | 16 + src/openai/types/shared/chat_model.py | 9 +- src/openai/types/shared/responses_model.py | 12 + src/openai/types/shared_params/__init__.py | 1 + src/openai/types/shared_params/chat_model.py | 9 +- .../types/shared_params/responses_model.py | 14 + 15 files changed, 1868 insertions(+), 24 deletions(-) create mode 100644 src/openai/resources/responses/responses.py.orig create mode 100644 src/openai/types/shared/all_models.py create mode 100644 src/openai/types/shared/responses_model.py create mode 100644 src/openai/types/shared_params/responses_model.py diff --git a/.stats.yml b/.stats.yml index b032562238..e0b06dc22a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 81 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f3bce04386c4fcfd5037e0477fbaa39010003fd1558eb5185fe4a71dd6a05fdd.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b26121d5df6eb5d3032a45a267473798b15fcfec76dd44a3256cf1238be05fa4.yml diff --git a/api.md b/api.md index 6e7f48a645..7f3a9392a2 100644 --- a/api.md +++ b/api.md @@ -2,6 +2,7 @@ ```python from openai.types import ( + AllModels, ChatModel, ComparisonFilter, CompoundFilter, @@ -14,6 +15,7 @@ from openai.types import ( ResponseFormatJSONObject, ResponseFormatJSONSchema, ResponseFormatText, + ResponsesModel, ) ``` diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 0c70a2ef22..668f4db80a 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -44,6 +44,7 @@ from ...types.responses.parsed_response import ParsedResponse from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager from ...types.responses.response_includable import ResponseIncludable +from ...types.shared_params.responses_model import ResponsesModel from ...types.responses.response_input_param import ResponseInputParam from ...types.responses.response_stream_event import ResponseStreamEvent from ...types.responses.response_text_config_param import ResponseTextConfigParam @@ -80,7 +81,7 @@ def create( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -245,7 +246,7 @@ def create( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, stream: Literal[True], include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -410,7 +411,7 @@ def create( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, stream: bool, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -575,7 +576,7 @@ def create( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -892,7 +893,7 @@ async def create( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, @@ -1057,7 +1058,7 @@ async def create( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, stream: Literal[True], include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1222,7 +1223,7 @@ async def create( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, stream: bool, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1387,7 +1388,7 @@ async def create( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/responses/responses.py.orig b/src/openai/resources/responses/responses.py.orig new file mode 100644 index 0000000000..dec4c19367 --- /dev/null +++ b/src/openai/resources/responses/responses.py.orig @@ -0,0 +1,1796 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, List, Type, Union, Iterable, Optional, cast +from functools import partial +from typing_extensions import Literal, overload + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._utils import ( + is_given, + required_args, + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .input_items import ( + InputItems, + AsyncInputItems, + InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse, +) +from ..._streaming import Stream, AsyncStream +from ...lib._tools import PydanticFunctionTool, ResponsesPydanticFunctionTool +from ..._base_client import make_request_options +from ...types.responses import response_create_params, response_retrieve_params +<<<<<<< HEAD +from ...lib._parsing._responses import ( + TextFormatT, + parse_response, + type_to_text_format_param as _type_to_text_format_param, +) +from ...types.shared.chat_model import ChatModel +||||||| parent of 001707b8 (feat(api): o1-pro now available through the API (#2228)) +from ...types.shared.chat_model import ChatModel +======= +>>>>>>> 001707b8 (feat(api): o1-pro now available through the API (#2228)) +from ...types.responses.response import Response +from ...types.responses.tool_param import ToolParam, ParseableToolParam +from ...types.shared_params.metadata import Metadata +from ...types.shared_params.reasoning import Reasoning +from ...types.responses.parsed_response import ParsedResponse +from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager +from ...types.responses.response_includable import ResponseIncludable +from ...types.shared_params.responses_model import ResponsesModel +from ...types.responses.response_input_param import ResponseInputParam +from ...types.responses.response_stream_event import ResponseStreamEvent +from ...types.responses.response_text_config_param import ResponseTextConfigParam + +__all__ = ["Responses", "AsyncResponses"] + + +class Responses(SyncAPIResource): + @cached_property + def input_items(self) -> InputItems: + return InputItems(self._client) + + @cached_property + def with_raw_response(self) -> ResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ResponsesWithStreamingResponse(self) + + @overload + def create( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + stream: Literal[True], + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + stream: bool, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | Stream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["input", "model"], ["input", "model", "stream"]) + def create( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | Stream[ResponseStreamEvent]: + return self._post( + "/responses", + body=maybe_transform( + { + "input": input, + "model": model, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "metadata": metadata, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "store": store, + "stream": stream, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Response, + stream=stream or False, + stream_cls=Stream[ResponseStreamEvent], + ) + + def stream( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, + tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ResponseStreamManager[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + api_request: partial[Stream[ResponseStreamEvent]] = partial( + self.create, + input=input, + model=model, + tools=tools, + include=include, + instructions=instructions, + max_output_tokens=max_output_tokens, + metadata=metadata, + parallel_tool_calls=parallel_tool_calls, + previous_response_id=previous_response_id, + store=store, + stream=True, + temperature=temperature, + text=text, + tool_choice=tool_choice, + reasoning=reasoning, + top_p=top_p, + truncation=truncation, + user=user, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return ResponseStreamManager( + api_request, + text_format=text_format, + input_tools=tools, + ) + + def parse( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, + tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedResponse[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: + return parse_response( + input_tools=tools, + text_format=text_format, + response=raw_response, + ) + + return self._post( + "/responses", + body=maybe_transform( + { + "input": input, + "model": model, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "metadata": metadata, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "store": store, + "stream": stream, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + ), + # we turn the `Response` instance into a `ParsedResponse` + # in the `parser` function above + cast_to=cast(Type[ParsedResponse[TextFormatT]], Response), + ) + + def retrieve( + self, + response_id: str, + *, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._get( + f"/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, response_retrieve_params.ResponseRetrieveParams), + ), + cast_to=Response, + ) + + def delete( + self, + response_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncResponses(AsyncAPIResource): + @cached_property + def input_items(self) -> AsyncInputItems: + return AsyncInputItems(self._client) + + @cached_property + def with_raw_response(self) -> AsyncResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncResponsesWithStreamingResponse(self) + + @overload + async def create( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + stream: Literal[True], + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + stream: bool, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | AsyncStream[ResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + + instructions: Inserts a system (or developer) message as the first item in the model's + context. + + When using along with `previous_response_id`, the instructions from a previous + response will be not be carried over to the next response. This makes it simple + to swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + + reasoning: **o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + store: Whether to store the generated model response for later retrieval via API. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + The two categories of tools you can provide the model are: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code. Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model 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. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the context of this response and previous ones exceeds the model's + context window size, the model will truncate the response to fit the context + window by dropping input items in the middle of the conversation. + - `disabled` (default): If a model response will exceed the context window size + for a model, the request will fail with a 400 error. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["input", "model"], ["input", "model", "stream"]) + async def create( + self, + *, + input: Union[str, ResponseInputParam], + model: ResponsesModel, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response | AsyncStream[ResponseStreamEvent]: + return await self._post( + "/responses", + body=await async_maybe_transform( + { + "input": input, + "model": model, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "metadata": metadata, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "store": store, + "stream": stream, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Response, + stream=stream or False, + stream_cls=AsyncStream[ResponseStreamEvent], + ) + + def stream( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, + tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncResponseStreamManager[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + api_request = self.create( + input=input, + model=model, + tools=tools, + include=include, + instructions=instructions, + max_output_tokens=max_output_tokens, + metadata=metadata, + parallel_tool_calls=parallel_tool_calls, + previous_response_id=previous_response_id, + store=store, + stream=True, + temperature=temperature, + text=text, + tool_choice=tool_choice, + reasoning=reasoning, + top_p=top_p, + truncation=truncation, + user=user, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return AsyncResponseStreamManager( + api_request, + text_format=text_format, + input_tools=tools, + ) + + async def parse( + self, + *, + input: Union[str, ResponseInputParam], + model: Union[str, ChatModel], + text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, + tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | NotGiven = NOT_GIVEN, + max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, + previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ParsedResponse[TextFormatT]: + if is_given(text_format): + if not text: + text = {} + + if "format" in text: + raise TypeError("Cannot mix and match text.format with text_format") + + text["format"] = _type_to_text_format_param(text_format) + + tools = _make_tools(tools) + + def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: + return parse_response( + input_tools=tools, + text_format=text_format, + response=raw_response, + ) + + return await self._post( + "/responses", + body=maybe_transform( + { + "input": input, + "model": model, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "metadata": metadata, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "store": store, + "stream": stream, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=parser, + ), + # we turn the `Response` instance into a `ParsedResponse` + # in the `parser` function above + cast_to=cast(Type[ParsedResponse[TextFormatT]], Response), + ) + + async def retrieve( + self, + response_id: str, + *, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Response: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return await self._get( + f"/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include": include}, response_retrieve_params.ResponseRetrieveParams + ), + ), + cast_to=Response, + ) + + async def delete( + self, + response_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class ResponsesWithRawResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = _legacy_response.to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + responses.delete, + ) + + @cached_property + def input_items(self) -> InputItemsWithRawResponse: + return InputItemsWithRawResponse(self._responses.input_items) + + +class AsyncResponsesWithRawResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = _legacy_response.async_to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + responses.delete, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithRawResponse: + return AsyncInputItemsWithRawResponse(self._responses.input_items) + + +class ResponsesWithStreamingResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = to_streamed_response_wrapper( + responses.delete, + ) + + @cached_property + def input_items(self) -> InputItemsWithStreamingResponse: + return InputItemsWithStreamingResponse(self._responses.input_items) + + +class AsyncResponsesWithStreamingResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = async_to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = async_to_streamed_response_wrapper( + responses.delete, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithStreamingResponse: + return AsyncInputItemsWithStreamingResponse(self._responses.input_items) + + +def _make_tools(tools: Iterable[ParseableToolParam] | NotGiven) -> List[ToolParam] | NotGiven: + if not is_given(tools): + return NOT_GIVEN + + converted_tools: List[ToolParam] = [] + for tool in tools: + if tool["type"] != "function": + converted_tools.append(tool) + continue + + if "function" not in tool: + # standard Responses API case + converted_tools.append(tool) + continue + + function = cast(Any, tool)["function"] # pyright: ignore[reportUnnecessaryCast] + if not isinstance(function, PydanticFunctionTool): + raise Exception( + "Expected Chat Completions function tool shape to be created using `openai.pydantic_function_tool()`" + ) + + assert "parameters" in function + new_tool = ResponsesPydanticFunctionTool( + { + "type": "function", + "name": function["name"], + "description": function.get("description"), + "parameters": function["parameters"], + "strict": function.get("strict") or False, + }, + function.model, + ) + + converted_tools.append(new_tool.cast()) + + return converted_tools diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 4c337d41c7..11761534c9 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -7,10 +7,12 @@ from .model import Model as Model from .shared import ( Metadata as Metadata, + AllModels as AllModels, ChatModel as ChatModel, Reasoning as Reasoning, ErrorObject as ErrorObject, CompoundFilter as CompoundFilter, + ResponsesModel as ResponsesModel, ReasoningEffort as ReasoningEffort, ComparisonFilter as ComparisonFilter, FunctionDefinition as FunctionDefinition, diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 66887ae9b5..1bedf80889 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -11,11 +11,11 @@ from ..shared.metadata import Metadata from ..shared.reasoning import Reasoning from .tool_choice_types import ToolChoiceTypes -from ..shared.chat_model import ChatModel from .tool_choice_options import ToolChoiceOptions from .response_output_item import ResponseOutputItem from .response_text_config import ResponseTextConfig from .tool_choice_function import ToolChoiceFunction +from ..shared.responses_model import ResponsesModel __all__ = ["Response", "IncompleteDetails", "ToolChoice"] @@ -61,7 +61,7 @@ class Response(BaseModel): a maximum length of 512 characters. """ - model: Union[str, ChatModel] + model: ResponsesModel """Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a wide range of models with different capabilities, performance diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index d5b2fdeb1a..651050c50d 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -6,7 +6,6 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from .tool_param import ToolParam -from ..shared.chat_model import ChatModel from .response_includable import ResponseIncludable from .tool_choice_options import ToolChoiceOptions from .response_input_param import ResponseInputParam @@ -15,6 +14,7 @@ from ..shared_params.reasoning import Reasoning from .response_text_config_param import ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam +from ..shared_params.responses_model import ResponsesModel __all__ = [ "ResponseCreateParamsBase", @@ -37,7 +37,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): - [Function calling](https://platform.openai.com/docs/guides/function-calling) """ - model: Required[Union[str, ChatModel]] + model: Required[ResponsesModel] """Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a wide range of models with different capabilities, performance diff --git a/src/openai/types/responses/response_function_tool_call_item.py b/src/openai/types/responses/response_function_tool_call_item.py index 477e9b70aa..25984f9451 100644 --- a/src/openai/types/responses/response_function_tool_call_item.py +++ b/src/openai/types/responses/response_function_tool_call_item.py @@ -8,4 +8,4 @@ class ResponseFunctionToolCallItem(ResponseFunctionToolCall): id: str # type: ignore - """The unique ID of the function call tool output.""" + """The unique ID of the function tool call.""" diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index 6ccc2313cc..6ad0ed5e01 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -2,9 +2,11 @@ from .metadata import Metadata as Metadata from .reasoning import Reasoning as Reasoning +from .all_models import AllModels as AllModels from .chat_model import ChatModel as ChatModel from .error_object import ErrorObject as ErrorObject from .compound_filter import CompoundFilter as CompoundFilter +from .responses_model import ResponsesModel as ResponsesModel from .reasoning_effort import ReasoningEffort as ReasoningEffort from .comparison_filter import ComparisonFilter as ComparisonFilter from .function_definition import FunctionDefinition as FunctionDefinition diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py new file mode 100644 index 0000000000..c4635e2140 --- /dev/null +++ b/src/openai/types/shared/all_models.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .chat_model import ChatModel + +__all__ = ["AllModels"] + +AllModels: TypeAlias = Union[ + str, + ChatModel, + str, + ChatModel, + Literal["o1-pro", "o1-pro-2025-03-19", "computer-use-preview", "computer-use-preview-2025-03-11"], +] diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index 31d7104e6e..b19375725d 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -13,11 +13,6 @@ "o1-preview-2024-09-12", "o1-mini", "o1-mini-2024-09-12", - "computer-use-preview", - "computer-use-preview-2025-02-04", - "computer-use-preview-2025-03-11", - "gpt-4.5-preview", - "gpt-4.5-preview-2025-02-27", "gpt-4o", "gpt-4o-2024-11-20", "gpt-4o-2024-08-06", @@ -27,6 +22,10 @@ "gpt-4o-audio-preview-2024-12-17", "gpt-4o-mini-audio-preview", "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", "chatgpt-4o-latest", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py new file mode 100644 index 0000000000..85f154fd84 --- /dev/null +++ b/src/openai/types/shared/responses_model.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from .chat_model import ChatModel + +__all__ = ["ResponsesModel"] + +ResponsesModel: TypeAlias = Union[ + str, ChatModel, Literal["o1-pro", "o1-pro-2025-03-19", "computer-use-preview", "computer-use-preview-2025-03-11"] +] diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index 4a4a8cdf1e..8894710807 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -4,6 +4,7 @@ from .reasoning import Reasoning as Reasoning from .chat_model import ChatModel as ChatModel from .compound_filter import CompoundFilter as CompoundFilter +from .responses_model import ResponsesModel as ResponsesModel from .reasoning_effort import ReasoningEffort as ReasoningEffort from .comparison_filter import ComparisonFilter as ComparisonFilter from .function_definition import FunctionDefinition as FunctionDefinition diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index 55649876eb..ff81b07ac3 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -15,11 +15,6 @@ "o1-preview-2024-09-12", "o1-mini", "o1-mini-2024-09-12", - "computer-use-preview", - "computer-use-preview-2025-02-04", - "computer-use-preview-2025-03-11", - "gpt-4.5-preview", - "gpt-4.5-preview-2025-02-27", "gpt-4o", "gpt-4o-2024-11-20", "gpt-4o-2024-08-06", @@ -29,6 +24,10 @@ "gpt-4o-audio-preview-2024-12-17", "gpt-4o-mini-audio-preview", "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", "chatgpt-4o-latest", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py new file mode 100644 index 0000000000..3bf0e13731 --- /dev/null +++ b/src/openai/types/shared_params/responses_model.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +from ..shared.chat_model import ChatModel + +__all__ = ["ResponsesModel"] + +ResponsesModel: TypeAlias = Union[ + str, ChatModel, Literal["o1-pro", "o1-pro-2025-03-19", "computer-use-preview", "computer-use-preview-2025-03-11"] +] From e9f971a71f7820c624d53c4927590dba67b2f71b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 21:02:54 +0000 Subject: [PATCH 831/914] release: 1.67.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e567f9cb13..4556676715 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.66.5" + ".": "1.67.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d8fb019fc8..ddd8b945c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.67.0 (2025-03-19) + +Full Changelog: [v1.66.5...v1.67.0](https://github.com/openai/openai-python/compare/v1.66.5...v1.67.0) + +### Features + +* **api:** o1-pro now available through the API ([#2228](https://github.com/openai/openai-python/issues/2228)) ([40a19d8](https://github.com/openai/openai-python/commit/40a19d8592c1767d6318230fc93e37c360d1bcd1)) + ## 1.66.5 (2025-03-18) Full Changelog: [v1.66.4...v1.66.5](https://github.com/openai/openai-python/compare/v1.66.4...v1.66.5) diff --git a/pyproject.toml b/pyproject.toml index 5fdf2a836d..a0a7eba2f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.66.5" +version = "1.67.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index dbefc6ec32..b63e6ad189 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.66.5" # x-release-please-version +__version__ = "1.67.0" # x-release-please-version From 2b4bc759b49504580cabc5e90b9cd79be4267207 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:25:10 +0000 Subject: [PATCH 832/914] feat(api): new models for TTS, STT, + new audio features for Realtime (#2232) --- .stats.yml | 4 +- api.md | 20 ++ src/openai/resources/audio/speech.py | 14 +- src/openai/resources/audio/transcriptions.py | 285 +++++++++++++++- src/openai/resources/audio/translations.py | 3 +- .../resources/beta/realtime/__init__.py | 14 + .../resources/beta/realtime/realtime.py | 312 ++++++++++++------ .../resources/beta/realtime/sessions.py | 64 +++- .../beta/realtime/transcription_sessions.py | 277 ++++++++++++++++ src/openai/types/audio/__init__.py | 4 + .../types/audio/speech_create_params.py | 8 +- src/openai/types/audio/speech_model.py | 2 +- src/openai/types/audio/transcription.py | 21 +- .../audio/transcription_create_params.py | 58 +++- .../types/audio/transcription_include.py | 7 + .../types/audio/transcription_stream_event.py | 14 + .../audio/transcription_text_delta_event.py | 35 ++ .../audio/transcription_text_done_event.py | 35 ++ .../types/audio/translation_create_params.py | 5 +- src/openai/types/audio_model.py | 2 +- src/openai/types/beta/realtime/__init__.py | 12 + ...put_audio_transcription_completed_event.py | 17 +- ...m_input_audio_transcription_delta_event.py | 39 +++ .../conversation_item_retrieve_event.py | 19 ++ .../conversation_item_retrieve_event_param.py | 18 + .../beta/realtime/realtime_client_event.py | 16 +- .../realtime/realtime_client_event_param.py | 16 +- .../beta/realtime/realtime_server_event.py | 57 ++-- src/openai/types/beta/realtime/session.py | 112 +++++-- .../beta/realtime/session_create_params.py | 97 ++++-- .../beta/realtime/session_update_event.py | 106 ++++-- .../realtime/session_update_event_param.py | 98 ++++-- .../beta/realtime/transcription_session.py | 100 ++++++ .../transcription_session_create_params.py | 143 ++++++++ .../realtime/transcription_session_update.py | 160 +++++++++ .../transcription_session_update_param.py | 160 +++++++++ .../transcription_session_updated_event.py | 24 ++ tests/api_resources/audio/test_speech.py | 2 + .../audio/test_transcriptions.py | 146 ++++++-- .../beta/realtime/test_sessions.py | 8 +- .../realtime/test_transcription_sessions.py | 120 +++++++ tests/lib/test_audio.py | 4 +- 42 files changed, 2333 insertions(+), 325 deletions(-) create mode 100644 src/openai/resources/beta/realtime/transcription_sessions.py create mode 100644 src/openai/types/audio/transcription_include.py create mode 100644 src/openai/types/audio/transcription_stream_event.py create mode 100644 src/openai/types/audio/transcription_text_delta_event.py create mode 100644 src/openai/types/audio/transcription_text_done_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_retrieve_event.py create mode 100644 src/openai/types/beta/realtime/conversation_item_retrieve_event_param.py create mode 100644 src/openai/types/beta/realtime/transcription_session.py create mode 100644 src/openai/types/beta/realtime/transcription_session_create_params.py create mode 100644 src/openai/types/beta/realtime/transcription_session_update.py create mode 100644 src/openai/types/beta/realtime/transcription_session_update_param.py create mode 100644 src/openai/types/beta/realtime/transcription_session_updated_event.py create mode 100644 tests/api_resources/beta/realtime/test_transcription_sessions.py diff --git a/.stats.yml b/.stats.yml index e0b06dc22a..abb9371314 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 81 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b26121d5df6eb5d3032a45a267473798b15fcfec76dd44a3256cf1238be05fa4.yml +configured_endpoints: 82 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-c22f59c66aec7914b6ee653d3098d1c1c8c16c180d2a158e819c8ddbf476f74b.yml diff --git a/api.md b/api.md index 7f3a9392a2..a5f81c624c 100644 --- a/api.md +++ b/api.md @@ -151,7 +151,11 @@ Types: ```python from openai.types.audio import ( Transcription, + TranscriptionInclude, TranscriptionSegment, + TranscriptionStreamEvent, + TranscriptionTextDeltaEvent, + TranscriptionTextDoneEvent, TranscriptionVerbose, TranscriptionWord, TranscriptionCreateResponse, @@ -338,7 +342,9 @@ from openai.types.beta.realtime import ( ConversationItemDeleteEvent, ConversationItemDeletedEvent, ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionDeltaEvent, ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemRetrieveEvent, ConversationItemTruncateEvent, ConversationItemTruncatedEvent, ConversationItemWithReference, @@ -375,6 +381,8 @@ from openai.types.beta.realtime import ( SessionCreatedEvent, SessionUpdateEvent, SessionUpdatedEvent, + TranscriptionSessionUpdate, + TranscriptionSessionUpdatedEvent, ) ``` @@ -390,6 +398,18 @@ Methods: - client.beta.realtime.sessions.create(\*\*params) -> SessionCreateResponse +### TranscriptionSessions + +Types: + +```python +from openai.types.beta.realtime import TranscriptionSession +``` + +Methods: + +- client.beta.realtime.transcription_sessions.create(\*\*params) -> TranscriptionSession + ## Assistants Types: diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index ad01118161..529e3a47ea 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -54,6 +54,7 @@ def create( input: str, model: Union[str, SpeechModel], voice: Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"], + instructions: str | NotGiven = NOT_GIVEN, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -71,13 +72,16 @@ def create( model: One of the available [TTS models](https://platform.openai.com/docs/models#tts): - `tts-1` or `tts-1-hd` + `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + instructions: Control the voice of your generated audio with additional instructions. Does not + work with `tts-1` or `tts-1-hd`. + response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. @@ -100,6 +104,7 @@ def create( "input": input, "model": model, "voice": voice, + "instructions": instructions, "response_format": response_format, "speed": speed, }, @@ -138,6 +143,7 @@ async def create( input: str, model: Union[str, SpeechModel], voice: Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"], + instructions: str | NotGiven = NOT_GIVEN, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -155,13 +161,16 @@ async def create( model: One of the available [TTS models](https://platform.openai.com/docs/models#tts): - `tts-1` or `tts-1-hd` + `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). + instructions: Control the voice of your generated audio with additional instructions. Does not + work with `tts-1` or `tts-1-hd`. + response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`. @@ -184,6 +193,7 @@ async def create( "input": input, "model": model, "voice": voice, + "instructions": instructions, "response_format": response_format, "speed": speed, }, diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index f338ad067d..2a77f91d69 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, List, Union, Mapping, cast +from typing import TYPE_CHECKING, List, Union, Mapping, Optional, cast from typing_extensions import Literal, overload, assert_never import httpx @@ -13,6 +13,7 @@ from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import ( extract_files, + required_args, maybe_transform, deepcopy_minimal, async_maybe_transform, @@ -20,12 +21,16 @@ from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._streaming import Stream, AsyncStream from ...types.audio import transcription_create_params from ..._base_client import make_request_options from ...types.audio_model import AudioModel from ...types.audio.transcription import Transcription from ...types.audio_response_format import AudioResponseFormat +from ...types.audio.transcription_include import TranscriptionInclude from ...types.audio.transcription_verbose import TranscriptionVerbose +from ...types.audio.transcription_stream_event import TranscriptionStreamEvent +from ...types.audio.transcription_create_response import TranscriptionCreateResponse __all__ = ["Transcriptions", "AsyncTranscriptions"] @@ -58,6 +63,7 @@ def create( *, file: FileTypes, model: Union[str, AudioModel], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, @@ -77,6 +83,7 @@ def create( *, file: FileTypes, model: Union[str, AudioModel], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, response_format: Literal["verbose_json"], language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, @@ -97,6 +104,7 @@ def create( file: FileTypes, model: Union[str, AudioModel], response_format: Literal["text", "srt", "vtt"], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, @@ -109,11 +117,96 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> str: ... + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + stream: Literal[True], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[TranscriptionStreamEvent]: + """ + Transcribes audio into the input language. + + Args: + file: + The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + + model: ID of the model to use. The options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source + Whisper V2 model). + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, + the only supported format is `json`. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload def create( self, *, file: FileTypes, model: Union[str, AudioModel], + stream: bool, + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, @@ -125,7 +218,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Transcription | TranscriptionVerbose | str: + ) -> TranscriptionCreateResponse | Stream[TranscriptionStreamEvent]: """ Transcribes audio into the input language. @@ -134,8 +227,24 @@ def create( The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. - model: ID of the model to use. Only `whisper-1` (which is powered by our open source - Whisper V2 model) is currently available. + model: ID of the model to use. The options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source + Whisper V2 model). + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) @@ -147,7 +256,8 @@ def create( should match the audio language. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. + `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, + the only supported format is `json`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -169,13 +279,37 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @required_args(["file", "model"], ["file", "model", "stream"]) + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str | Transcription | TranscriptionVerbose | Stream[TranscriptionStreamEvent]: body = deepcopy_minimal( { "file": file, "model": model, + "include": include, "language": language, "prompt": prompt, "response_format": response_format, + "stream": stream, "temperature": temperature, "timestamp_granularities": timestamp_granularities, } @@ -193,6 +327,8 @@ def create( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=_get_response_format_type(response_format), + stream=stream or False, + stream_cls=Stream[TranscriptionStreamEvent], ) @@ -226,6 +362,7 @@ async def create( language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -241,6 +378,7 @@ async def create( *, file: FileTypes, model: Union[str, AudioModel], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, response_format: Literal["verbose_json"], language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, @@ -260,6 +398,7 @@ async def create( *, file: FileTypes, model: Union[str, AudioModel], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, response_format: Literal["text", "srt", "vtt"], language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, @@ -273,11 +412,96 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> str: ... + @overload + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + stream: Literal[True], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[TranscriptionStreamEvent]: + """ + Transcribes audio into the input language. + + Args: + file: + The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + + model: ID of the model to use. The options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source + Whisper V2 model). + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, + the only supported format is `json`. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload async def create( self, *, file: FileTypes, model: Union[str, AudioModel], + stream: bool, + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, language: str | NotGiven = NOT_GIVEN, prompt: str | NotGiven = NOT_GIVEN, response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, @@ -289,7 +513,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Transcription | TranscriptionVerbose | str: + ) -> TranscriptionCreateResponse | AsyncStream[TranscriptionStreamEvent]: """ Transcribes audio into the input language. @@ -298,8 +522,24 @@ async def create( The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. - model: ID of the model to use. Only `whisper-1` (which is powered by our open source - Whisper V2 model) is currently available. + model: ID of the model to use. The options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source + Whisper V2 model). + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) @@ -311,7 +551,8 @@ async def create( should match the audio language. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. + `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, + the only supported format is `json`. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -333,13 +574,37 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @required_args(["file", "model"], ["file", "model", "stream"]) + async def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, + language: str | NotGiven = NOT_GIVEN, + prompt: str | NotGiven = NOT_GIVEN, + response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + temperature: float | NotGiven = NOT_GIVEN, + timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Transcription | TranscriptionVerbose | str | AsyncStream[TranscriptionStreamEvent]: body = deepcopy_minimal( { "file": file, "model": model, + "include": include, "language": language, "prompt": prompt, "response_format": response_format, + "stream": stream, "temperature": temperature, "timestamp_granularities": timestamp_granularities, } @@ -357,6 +622,8 @@ async def create( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=_get_response_format_type(response_format), + stream=stream or False, + stream_cls=AsyncStream[TranscriptionStreamEvent], ) diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index cd3132dc57..f55dbd0ee5 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -9,7 +9,6 @@ import httpx from ... import _legacy_response -from ...types import AudioResponseFormat from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes from ..._utils import ( extract_files, @@ -109,7 +108,7 @@ def create( file: FileTypes, model: Union[str, AudioModel], prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, + response_format: Union[Literal["json", "text", "srt", "verbose_json", "vtt"], NotGiven] = NOT_GIVEN, temperature: float | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/openai/resources/beta/realtime/__init__.py b/src/openai/resources/beta/realtime/__init__.py index 474434e6e1..7ab3d9931c 100644 --- a/src/openai/resources/beta/realtime/__init__.py +++ b/src/openai/resources/beta/realtime/__init__.py @@ -16,6 +16,14 @@ SessionsWithStreamingResponse, AsyncSessionsWithStreamingResponse, ) +from .transcription_sessions import ( + TranscriptionSessions, + AsyncTranscriptionSessions, + TranscriptionSessionsWithRawResponse, + AsyncTranscriptionSessionsWithRawResponse, + TranscriptionSessionsWithStreamingResponse, + AsyncTranscriptionSessionsWithStreamingResponse, +) __all__ = [ "Sessions", @@ -24,6 +32,12 @@ "AsyncSessionsWithRawResponse", "SessionsWithStreamingResponse", "AsyncSessionsWithStreamingResponse", + "TranscriptionSessions", + "AsyncTranscriptionSessions", + "TranscriptionSessionsWithRawResponse", + "AsyncTranscriptionSessionsWithRawResponse", + "TranscriptionSessionsWithStreamingResponse", + "AsyncTranscriptionSessionsWithStreamingResponse", "Realtime", "AsyncRealtime", "RealtimeWithRawResponse", diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index cd610d9089..76e57f8cb7 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -32,7 +32,19 @@ from ...._resource import SyncAPIResource, AsyncAPIResource from ...._exceptions import OpenAIError from ...._base_client import _merge_mappings -from ....types.beta.realtime import session_update_event_param, response_create_event_param +from ....types.beta.realtime import ( + session_update_event_param, + response_create_event_param, + transcription_session_update_param, +) +from .transcription_sessions import ( + TranscriptionSessions, + AsyncTranscriptionSessions, + TranscriptionSessionsWithRawResponse, + AsyncTranscriptionSessionsWithRawResponse, + TranscriptionSessionsWithStreamingResponse, + AsyncTranscriptionSessionsWithStreamingResponse, +) from ....types.websocket_connection_options import WebsocketConnectionOptions from ....types.beta.realtime.realtime_client_event import RealtimeClientEvent from ....types.beta.realtime.realtime_server_event import RealtimeServerEvent @@ -55,6 +67,10 @@ class Realtime(SyncAPIResource): def sessions(self) -> Sessions: return Sessions(self._client) + @cached_property + def transcription_sessions(self) -> TranscriptionSessions: + return TranscriptionSessions(self._client) + @cached_property def with_raw_response(self) -> RealtimeWithRawResponse: """ @@ -107,6 +123,10 @@ class AsyncRealtime(AsyncAPIResource): def sessions(self) -> AsyncSessions: return AsyncSessions(self._client) + @cached_property + def transcription_sessions(self) -> AsyncTranscriptionSessions: + return AsyncTranscriptionSessions(self._client) + @cached_property def with_raw_response(self) -> AsyncRealtimeWithRawResponse: """ @@ -162,6 +182,10 @@ def __init__(self, realtime: Realtime) -> None: def sessions(self) -> SessionsWithRawResponse: return SessionsWithRawResponse(self._realtime.sessions) + @cached_property + def transcription_sessions(self) -> TranscriptionSessionsWithRawResponse: + return TranscriptionSessionsWithRawResponse(self._realtime.transcription_sessions) + class AsyncRealtimeWithRawResponse: def __init__(self, realtime: AsyncRealtime) -> None: @@ -171,6 +195,10 @@ def __init__(self, realtime: AsyncRealtime) -> None: def sessions(self) -> AsyncSessionsWithRawResponse: return AsyncSessionsWithRawResponse(self._realtime.sessions) + @cached_property + def transcription_sessions(self) -> AsyncTranscriptionSessionsWithRawResponse: + return AsyncTranscriptionSessionsWithRawResponse(self._realtime.transcription_sessions) + class RealtimeWithStreamingResponse: def __init__(self, realtime: Realtime) -> None: @@ -180,6 +208,10 @@ def __init__(self, realtime: Realtime) -> None: def sessions(self) -> SessionsWithStreamingResponse: return SessionsWithStreamingResponse(self._realtime.sessions) + @cached_property + def transcription_sessions(self) -> TranscriptionSessionsWithStreamingResponse: + return TranscriptionSessionsWithStreamingResponse(self._realtime.transcription_sessions) + class AsyncRealtimeWithStreamingResponse: def __init__(self, realtime: AsyncRealtime) -> None: @@ -189,14 +221,19 @@ def __init__(self, realtime: AsyncRealtime) -> None: def sessions(self) -> AsyncSessionsWithStreamingResponse: return AsyncSessionsWithStreamingResponse(self._realtime.sessions) + @cached_property + def transcription_sessions(self) -> AsyncTranscriptionSessionsWithStreamingResponse: + return AsyncTranscriptionSessionsWithStreamingResponse(self._realtime.transcription_sessions) + class AsyncRealtimeConnection: """Represents a live websocket connection to the Realtime API""" session: AsyncRealtimeSessionResource response: AsyncRealtimeResponseResource - conversation: AsyncRealtimeConversationResource input_audio_buffer: AsyncRealtimeInputAudioBufferResource + conversation: AsyncRealtimeConversationResource + transcription_session: AsyncRealtimeTranscriptionSessionResource _connection: AsyncWebsocketConnection @@ -205,8 +242,9 @@ def __init__(self, connection: AsyncWebsocketConnection) -> None: self.session = AsyncRealtimeSessionResource(self) self.response = AsyncRealtimeResponseResource(self) - self.conversation = AsyncRealtimeConversationResource(self) self.input_audio_buffer = AsyncRealtimeInputAudioBufferResource(self) + self.conversation = AsyncRealtimeConversationResource(self) + self.transcription_session = AsyncRealtimeTranscriptionSessionResource(self) async def __aiter__(self) -> AsyncIterator[RealtimeServerEvent]: """ @@ -377,8 +415,9 @@ class RealtimeConnection: session: RealtimeSessionResource response: RealtimeResponseResource - conversation: RealtimeConversationResource input_audio_buffer: RealtimeInputAudioBufferResource + conversation: RealtimeConversationResource + transcription_session: RealtimeTranscriptionSessionResource _connection: WebsocketConnection @@ -387,8 +426,9 @@ def __init__(self, connection: WebsocketConnection) -> None: self.session = RealtimeSessionResource(self) self.response = RealtimeResponseResource(self) - self.conversation = RealtimeConversationResource(self) self.input_audio_buffer = RealtimeInputAudioBufferResource(self) + self.conversation = RealtimeConversationResource(self) + self.transcription_session = RealtimeTranscriptionSessionResource(self) def __iter__(self) -> Iterator[RealtimeServerEvent]: """ @@ -582,20 +622,6 @@ def update(self, *, session: session_update_event_param.Session, event_id: str | class RealtimeResponseResource(BaseRealtimeConnectionResource): - def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: - """Send this event to cancel an in-progress response. - - The server will respond - with a `response.cancelled` event or an error if there is no response to - cancel. - """ - self._connection.send( - cast( - RealtimeClientEventParam, - strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), - ) - ) - def create( self, *, @@ -626,6 +652,70 @@ def create( ) ) + def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.cancelled` event or an error if there is no response to + cancel. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + +class RealtimeInputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to commit the user input audio buffer, which will create a + new user message item in the conversation. This event will produce an error + if the input audio buffer is empty. When in Server VAD mode, the client does + not need to send this event, the server will commit the audio buffer + automatically. + + Committing the input audio buffer will trigger input audio transcription + (if enabled in session configuration), but it will not create a response + from the model. The server will respond with an `input_audio_buffer.committed` + event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. In Server VAD + mode, the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike made other client events, the server will + not send a confirmation response to this event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + class RealtimeConversationResource(BaseRealtimeConnectionResource): @cached_property @@ -711,53 +801,30 @@ def truncate( ) ) - -class RealtimeInputAudioBufferResource(BaseRealtimeConnectionResource): - def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: - """Send this event to clear the audio bytes in the buffer. - - The server will - respond with an `input_audio_buffer.cleared` event. + def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: """ - self._connection.send( - cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) - ) - - def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: - """ - Send this event to commit the user input audio buffer, which will create a - new user message item in the conversation. This event will produce an error - if the input audio buffer is empty. When in Server VAD mode, the client does - not need to send this event, the server will commit the audio buffer - automatically. - - Committing the input audio buffer will trigger input audio transcription - (if enabled in session configuration), but it will not create a response - from the model. The server will respond with an `input_audio_buffer.committed` - event. + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. """ self._connection.send( - cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.retrieve", "item_id": item_id, "event_id": event_id}), + ) ) - def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: - """Send this event to append audio bytes to the input audio buffer. - The audio - buffer is temporary storage you can write to and later commit. In Server VAD - mode, the audio buffer is used to detect speech and the server will decide - when to commit. When Server VAD is disabled, you must commit the audio buffer - manually. - - The client may choose how much audio to place in each event up to a maximum - of 15 MiB, for example streaming smaller chunks from the client may allow the - VAD to be more responsive. Unlike made other client events, the server will - not send a confirmation response to this event. - """ +class RealtimeTranscriptionSessionResource(BaseRealtimeConnectionResource): + def update( + self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to update a transcription session.""" self._connection.send( cast( RealtimeClientEventParam, - strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + strip_not_given({"type": "transcription_session.update", "session": session, "event_id": event_id}), ) ) @@ -792,20 +859,6 @@ async def update( class AsyncRealtimeResponseResource(BaseAsyncRealtimeConnectionResource): - async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: - """Send this event to cancel an in-progress response. - - The server will respond - with a `response.cancelled` event or an error if there is no response to - cancel. - """ - await self._connection.send( - cast( - RealtimeClientEventParam, - strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), - ) - ) - async def create( self, *, @@ -836,6 +889,70 @@ async def create( ) ) + async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.cancelled` event or an error if there is no response to + cancel. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + +class AsyncRealtimeInputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + async def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to commit the user input audio buffer, which will create a + new user message item in the conversation. This event will produce an error + if the input audio buffer is empty. When in Server VAD mode, the client does + not need to send this event, the server will commit the audio buffer + automatically. + + Committing the input audio buffer will trigger input audio transcription + (if enabled in session configuration), but it will not create a response + from the model. The server will respond with an `input_audio_buffer.committed` + event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + async def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. In Server VAD + mode, the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike made other client events, the server will + not send a confirmation response to this event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + class AsyncRealtimeConversationResource(BaseAsyncRealtimeConnectionResource): @cached_property @@ -921,52 +1038,29 @@ async def truncate( ) ) - -class AsyncRealtimeInputAudioBufferResource(BaseAsyncRealtimeConnectionResource): - async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: - """Send this event to clear the audio bytes in the buffer. - - The server will - respond with an `input_audio_buffer.cleared` event. - """ - await self._connection.send( - cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) - ) - - async def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + async def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: """ - Send this event to commit the user input audio buffer, which will create a - new user message item in the conversation. This event will produce an error - if the input audio buffer is empty. When in Server VAD mode, the client does - not need to send this event, the server will commit the audio buffer - automatically. - - Committing the input audio buffer will trigger input audio transcription - (if enabled in session configuration), but it will not create a response - from the model. The server will respond with an `input_audio_buffer.committed` - event. + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. """ await self._connection.send( - cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.retrieve", "item_id": item_id, "event_id": event_id}), + ) ) - async def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: - """Send this event to append audio bytes to the input audio buffer. - - The audio - buffer is temporary storage you can write to and later commit. In Server VAD - mode, the audio buffer is used to detect speech and the server will decide - when to commit. When Server VAD is disabled, you must commit the audio buffer - manually. - The client may choose how much audio to place in each event up to a maximum - of 15 MiB, for example streaming smaller chunks from the client may allow the - VAD to be more responsive. Unlike made other client events, the server will - not send a confirmation response to this event. - """ +class AsyncRealtimeTranscriptionSessionResource(BaseAsyncRealtimeConnectionResource): + async def update( + self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to update a transcription session.""" await self._connection.send( cast( RealtimeClientEventParam, - strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + strip_not_given({"type": "transcription_session.update", "session": session, "event_id": event_id}), ) ) diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index 4b337b7c19..5884e54de2 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -47,6 +47,7 @@ def create( self, *, input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_noise_reduction: session_create_params.InputAudioNoiseReduction | NotGiven = NOT_GIVEN, input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, instructions: str | NotGiven = NOT_GIVEN, max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, @@ -86,14 +87,20 @@ def create( `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian byte order. + input_audio_noise_reduction: Configuration for input audio noise reduction. This can be set to `null` to turn + off. Noise reduction filters audio added to the input audio buffer before it is + sent to VAD and the model. Filtering the audio can improve VAD and turn + detection accuracy (reducing false positives) and model performance by improving + perception of the input audio. + input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through - [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as rough guidance rather than the representation - understood by the model. The client can optionally set the language and prompt - for transcription, these fields will be passed to the Whisper API. + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. instructions: The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model @@ -119,16 +126,24 @@ def create( output_audio_format: The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is sampled at a rate of 24kHz. - temperature: Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8. + temperature: Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a + temperature of 0.8 is highly recommended for best performance. tool_choice: How the model chooses tools. Options are `auto`, `none`, `required`, or specify a function. tools: Tools (functions) available to the model. - turn_detection: Configuration for turn detection. Can be set to `null` to turn off. Server VAD - means that the model will detect the start and end of speech based on audio - volume and respond at the end of user speech. + turn_detection: Configuration for turn detection, ether Server VAD or Semantic VAD. This can be + set to `null` to turn off, in which case the client must manually trigger model + response. Server VAD means that the model will detect the start and end of + speech based on audio volume and respond at the end of user speech. Semantic VAD + is more advanced and uses a turn detection model (in conjuction with VAD) to + semantically estimate whether the user has finished speaking, then dynamically + sets a timeout based on this probability. For example, if user audio trails off + with "uhhm", the model will score a low probability of turn end and wait longer + for the user to continue speaking. This can be useful for more natural + conversations, but may have a higher latency. voice: The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are @@ -148,6 +163,7 @@ def create( body=maybe_transform( { "input_audio_format": input_audio_format, + "input_audio_noise_reduction": input_audio_noise_reduction, "input_audio_transcription": input_audio_transcription, "instructions": instructions, "max_response_output_tokens": max_response_output_tokens, @@ -193,6 +209,7 @@ async def create( self, *, input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_noise_reduction: session_create_params.InputAudioNoiseReduction | NotGiven = NOT_GIVEN, input_audio_transcription: session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, instructions: str | NotGiven = NOT_GIVEN, max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, @@ -232,14 +249,20 @@ async def create( `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian byte order. + input_audio_noise_reduction: Configuration for input audio noise reduction. This can be set to `null` to turn + off. Noise reduction filters audio added to the input audio buffer before it is + sent to VAD and the model. Filtering the audio can improve VAD and turn + detection accuracy (reducing false positives) and model performance by improving + perception of the input audio. + input_audio_transcription: Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through - [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as rough guidance rather than the representation - understood by the model. The client can optionally set the language and prompt - for transcription, these fields will be passed to the Whisper API. + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. instructions: The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model @@ -265,16 +288,24 @@ async def create( output_audio_format: The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is sampled at a rate of 24kHz. - temperature: Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8. + temperature: Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a + temperature of 0.8 is highly recommended for best performance. tool_choice: How the model chooses tools. Options are `auto`, `none`, `required`, or specify a function. tools: Tools (functions) available to the model. - turn_detection: Configuration for turn detection. Can be set to `null` to turn off. Server VAD - means that the model will detect the start and end of speech based on audio - volume and respond at the end of user speech. + turn_detection: Configuration for turn detection, ether Server VAD or Semantic VAD. This can be + set to `null` to turn off, in which case the client must manually trigger model + response. Server VAD means that the model will detect the start and end of + speech based on audio volume and respond at the end of user speech. Semantic VAD + is more advanced and uses a turn detection model (in conjuction with VAD) to + semantically estimate whether the user has finished speaking, then dynamically + sets a timeout based on this probability. For example, if user audio trails off + with "uhhm", the model will score a low probability of turn end and wait longer + for the user to continue speaking. This can be useful for more natural + conversations, but may have a higher latency. voice: The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are @@ -294,6 +325,7 @@ async def create( body=await async_maybe_transform( { "input_audio_format": input_audio_format, + "input_audio_noise_reduction": input_audio_noise_reduction, "input_audio_transcription": input_audio_transcription, "instructions": instructions, "max_response_output_tokens": max_response_output_tokens, diff --git a/src/openai/resources/beta/realtime/transcription_sessions.py b/src/openai/resources/beta/realtime/transcription_sessions.py new file mode 100644 index 0000000000..0917da71fa --- /dev/null +++ b/src/openai/resources/beta/realtime/transcription_sessions.py @@ -0,0 +1,277 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.beta.realtime import transcription_session_create_params +from ....types.beta.realtime.transcription_session import TranscriptionSession + +__all__ = ["TranscriptionSessions", "AsyncTranscriptionSessions"] + + +class TranscriptionSessions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TranscriptionSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return TranscriptionSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TranscriptionSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return TranscriptionSessionsWithStreamingResponse(self) + + def create( + self, + *, + include: List[str] | NotGiven = NOT_GIVEN, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_noise_reduction: transcription_session_create_params.InputAudioNoiseReduction + | NotGiven = NOT_GIVEN, + input_audio_transcription: transcription_session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + turn_detection: transcription_session_create_params.TurnDetection | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TranscriptionSession: + """ + Create an ephemeral API token for use in client-side applications with the + Realtime API specifically for realtime transcriptions. Can be configured with + the same session parameters as the `transcription_session.update` client event. + + It responds with a session object, plus a `client_secret` key which contains a + usable ephemeral API token that can be used to authenticate browser clients for + the Realtime API. + + Args: + include: + The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For + `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel + (mono), and little-endian byte order. + + input_audio_noise_reduction: Configuration for input audio noise reduction. This can be set to `null` to turn + off. Noise reduction filters audio added to the input audio buffer before it is + sent to VAD and the model. Filtering the audio can improve VAD and turn + detection accuracy (reducing false positives) and model performance by improving + perception of the input audio. + + input_audio_transcription: Configuration for input audio transcription. The client can optionally set the + language and prompt for transcription, these offer additional guidance to the + transcription service. + + modalities: The set of modalities the model can respond with. To disable audio, set this to + ["text"]. + + turn_detection: Configuration for turn detection, ether Server VAD or Semantic VAD. This can be + set to `null` to turn off, in which case the client must manually trigger model + response. Server VAD means that the model will detect the start and end of + speech based on audio volume and respond at the end of user speech. Semantic VAD + is more advanced and uses a turn detection model (in conjuction with VAD) to + semantically estimate whether the user has finished speaking, then dynamically + sets a timeout based on this probability. For example, if user audio trails off + with "uhhm", the model will score a low probability of turn end and wait longer + for the user to continue speaking. This can be useful for more natural + conversations, but may have a higher latency. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return self._post( + "/realtime/transcription_sessions", + body=maybe_transform( + { + "include": include, + "input_audio_format": input_audio_format, + "input_audio_noise_reduction": input_audio_noise_reduction, + "input_audio_transcription": input_audio_transcription, + "modalities": modalities, + "turn_detection": turn_detection, + }, + transcription_session_create_params.TranscriptionSessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TranscriptionSession, + ) + + +class AsyncTranscriptionSessions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTranscriptionSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncTranscriptionSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTranscriptionSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncTranscriptionSessionsWithStreamingResponse(self) + + async def create( + self, + *, + include: List[str] | NotGiven = NOT_GIVEN, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | NotGiven = NOT_GIVEN, + input_audio_noise_reduction: transcription_session_create_params.InputAudioNoiseReduction + | NotGiven = NOT_GIVEN, + input_audio_transcription: transcription_session_create_params.InputAudioTranscription | NotGiven = NOT_GIVEN, + modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, + turn_detection: transcription_session_create_params.TurnDetection | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TranscriptionSession: + """ + Create an ephemeral API token for use in client-side applications with the + Realtime API specifically for realtime transcriptions. Can be configured with + the same session parameters as the `transcription_session.update` client event. + + It responds with a session object, plus a `client_secret` key which contains a + usable ephemeral API token that can be used to authenticate browser clients for + the Realtime API. + + Args: + include: + The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + + input_audio_format: The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For + `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel + (mono), and little-endian byte order. + + input_audio_noise_reduction: Configuration for input audio noise reduction. This can be set to `null` to turn + off. Noise reduction filters audio added to the input audio buffer before it is + sent to VAD and the model. Filtering the audio can improve VAD and turn + detection accuracy (reducing false positives) and model performance by improving + perception of the input audio. + + input_audio_transcription: Configuration for input audio transcription. The client can optionally set the + language and prompt for transcription, these offer additional guidance to the + transcription service. + + modalities: The set of modalities the model can respond with. To disable audio, set this to + ["text"]. + + turn_detection: Configuration for turn detection, ether Server VAD or Semantic VAD. This can be + set to `null` to turn off, in which case the client must manually trigger model + response. Server VAD means that the model will detect the start and end of + speech based on audio volume and respond at the end of user speech. Semantic VAD + is more advanced and uses a turn detection model (in conjuction with VAD) to + semantically estimate whether the user has finished speaking, then dynamically + sets a timeout based on this probability. For example, if user audio trails off + with "uhhm", the model will score a low probability of turn end and wait longer + for the user to continue speaking. This can be useful for more natural + conversations, but may have a higher latency. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} + return await self._post( + "/realtime/transcription_sessions", + body=await async_maybe_transform( + { + "include": include, + "input_audio_format": input_audio_format, + "input_audio_noise_reduction": input_audio_noise_reduction, + "input_audio_transcription": input_audio_transcription, + "modalities": modalities, + "turn_detection": turn_detection, + }, + transcription_session_create_params.TranscriptionSessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TranscriptionSession, + ) + + +class TranscriptionSessionsWithRawResponse: + def __init__(self, transcription_sessions: TranscriptionSessions) -> None: + self._transcription_sessions = transcription_sessions + + self.create = _legacy_response.to_raw_response_wrapper( + transcription_sessions.create, + ) + + +class AsyncTranscriptionSessionsWithRawResponse: + def __init__(self, transcription_sessions: AsyncTranscriptionSessions) -> None: + self._transcription_sessions = transcription_sessions + + self.create = _legacy_response.async_to_raw_response_wrapper( + transcription_sessions.create, + ) + + +class TranscriptionSessionsWithStreamingResponse: + def __init__(self, transcription_sessions: TranscriptionSessions) -> None: + self._transcription_sessions = transcription_sessions + + self.create = to_streamed_response_wrapper( + transcription_sessions.create, + ) + + +class AsyncTranscriptionSessionsWithStreamingResponse: + def __init__(self, transcription_sessions: AsyncTranscriptionSessions) -> None: + self._transcription_sessions = transcription_sessions + + self.create = async_to_streamed_response_wrapper( + transcription_sessions.create, + ) diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index 822e0f3a8d..396944ee47 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -8,9 +8,13 @@ from .transcription_word import TranscriptionWord as TranscriptionWord from .translation_verbose import TranslationVerbose as TranslationVerbose from .speech_create_params import SpeechCreateParams as SpeechCreateParams +from .transcription_include import TranscriptionInclude as TranscriptionInclude from .transcription_segment import TranscriptionSegment as TranscriptionSegment from .transcription_verbose import TranscriptionVerbose as TranscriptionVerbose from .translation_create_params import TranslationCreateParams as TranslationCreateParams +from .transcription_stream_event import TranscriptionStreamEvent as TranscriptionStreamEvent from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams from .translation_create_response import TranslationCreateResponse as TranslationCreateResponse from .transcription_create_response import TranscriptionCreateResponse as TranscriptionCreateResponse +from .transcription_text_done_event import TranscriptionTextDoneEvent as TranscriptionTextDoneEvent +from .transcription_text_delta_event import TranscriptionTextDeltaEvent as TranscriptionTextDeltaEvent diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index ed1a1ce748..958680710b 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -17,7 +17,7 @@ class SpeechCreateParams(TypedDict, total=False): model: Required[Union[str, SpeechModel]] """ One of the available [TTS models](https://platform.openai.com/docs/models#tts): - `tts-1` or `tts-1-hd` + `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. """ voice: Required[Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"]] @@ -28,6 +28,12 @@ class SpeechCreateParams(TypedDict, total=False): [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). """ + instructions: str + """Control the voice of your generated audio with additional instructions. + + Does not work with `tts-1` or `tts-1-hd`. + """ + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] """The format to audio in. diff --git a/src/openai/types/audio/speech_model.py b/src/openai/types/audio/speech_model.py index bd685ab34d..f004f805da 100644 --- a/src/openai/types/audio/speech_model.py +++ b/src/openai/types/audio/speech_model.py @@ -4,4 +4,4 @@ __all__ = ["SpeechModel"] -SpeechModel: TypeAlias = Literal["tts-1", "tts-1-hd"] +SpeechModel: TypeAlias = Literal["tts-1", "tts-1-hd", "gpt-4o-mini-tts"] diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index edb5f227fc..1576385404 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -1,11 +1,30 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List, Optional from ..._models import BaseModel -__all__ = ["Transcription"] +__all__ = ["Transcription", "Logprob"] + + +class Logprob(BaseModel): + token: Optional[str] = None + """The token in the transcription.""" + + bytes: Optional[List[float]] = None + """The bytes of the token.""" + + logprob: Optional[float] = None + """The log probability of the token.""" class Transcription(BaseModel): text: str """The transcribed text.""" + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the tokens in the transcription. + + Only returned with the models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe` + if `logprobs` is added to the `include` array. + """ diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index f1779c35e6..0cda4c7907 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -2,17 +2,22 @@ from __future__ import annotations -from typing import List, Union +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import FileTypes from ..audio_model import AudioModel +from .transcription_include import TranscriptionInclude from ..audio_response_format import AudioResponseFormat -__all__ = ["TranscriptionCreateParams"] +__all__ = [ + "TranscriptionCreateParamsBase", + "TranscriptionCreateParamsNonStreaming", + "TranscriptionCreateParamsStreaming", +] -class TranscriptionCreateParams(TypedDict, total=False): +class TranscriptionCreateParamsBase(TypedDict, total=False): file: Required[FileTypes] """ The audio file object (not file name) to transcribe, in one of these formats: @@ -22,8 +27,17 @@ class TranscriptionCreateParams(TypedDict, total=False): model: Required[Union[str, AudioModel]] """ID of the model to use. - Only `whisper-1` (which is powered by our open source Whisper V2 model) is - currently available. + The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1` + (which is powered by our open source Whisper V2 model). + """ + + include: List[TranscriptionInclude] + """Additional information to include in the transcription response. + + `logprobs` will return the log probabilities of the tokens in the response to + understand the model's confidence in the transcription. `logprobs` only works + with response_format set to `json` and only with the models `gpt-4o-transcribe` + and `gpt-4o-mini-transcribe`. """ language: str @@ -45,7 +59,8 @@ class TranscriptionCreateParams(TypedDict, total=False): response_format: AudioResponseFormat """ The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. + `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, + the only supported format is `json`. """ temperature: float @@ -65,3 +80,34 @@ class TranscriptionCreateParams(TypedDict, total=False): is no additional latency for segment timestamps, but generating word timestamps incurs additional latency. """ + + +class TranscriptionCreateParamsNonStreaming(TranscriptionCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + """ + + +class TranscriptionCreateParamsStreaming(TranscriptionCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + """ + + +TranscriptionCreateParams = Union[TranscriptionCreateParamsNonStreaming, TranscriptionCreateParamsStreaming] diff --git a/src/openai/types/audio/transcription_include.py b/src/openai/types/audio/transcription_include.py new file mode 100644 index 0000000000..0e464ac934 --- /dev/null +++ b/src/openai/types/audio/transcription_include.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["TranscriptionInclude"] + +TranscriptionInclude: TypeAlias = Literal["logprobs"] diff --git a/src/openai/types/audio/transcription_stream_event.py b/src/openai/types/audio/transcription_stream_event.py new file mode 100644 index 0000000000..757077a280 --- /dev/null +++ b/src/openai/types/audio/transcription_stream_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .transcription_text_done_event import TranscriptionTextDoneEvent +from .transcription_text_delta_event import TranscriptionTextDeltaEvent + +__all__ = ["TranscriptionStreamEvent"] + +TranscriptionStreamEvent: TypeAlias = Annotated[ + Union[TranscriptionTextDeltaEvent, TranscriptionTextDoneEvent], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/audio/transcription_text_delta_event.py b/src/openai/types/audio/transcription_text_delta_event.py new file mode 100644 index 0000000000..f8d5355491 --- /dev/null +++ b/src/openai/types/audio/transcription_text_delta_event.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TranscriptionTextDeltaEvent", "Logprob"] + + +class Logprob(BaseModel): + token: Optional[str] = None + """The token that was used to generate the log probability.""" + + bytes: Optional[List[object]] = None + """The bytes that were used to generate the log probability.""" + + logprob: Optional[float] = None + """The log probability of the token.""" + + +class TranscriptionTextDeltaEvent(BaseModel): + delta: str + """The text delta that was additionally transcribed.""" + + type: Literal["transcript.text.delta"] + """The type of the event. Always `transcript.text.delta`.""" + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the delta. + + Only included if you + [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) + with the `include[]` parameter set to `logprobs`. + """ diff --git a/src/openai/types/audio/transcription_text_done_event.py b/src/openai/types/audio/transcription_text_done_event.py new file mode 100644 index 0000000000..3f1a713a52 --- /dev/null +++ b/src/openai/types/audio/transcription_text_done_event.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TranscriptionTextDoneEvent", "Logprob"] + + +class Logprob(BaseModel): + token: Optional[str] = None + """The token that was used to generate the log probability.""" + + bytes: Optional[List[object]] = None + """The bytes that were used to generate the log probability.""" + + logprob: Optional[float] = None + """The log probability of the token.""" + + +class TranscriptionTextDoneEvent(BaseModel): + text: str + """The text that was transcribed.""" + + type: Literal["transcript.text.done"] + """The type of the event. Always `transcript.text.done`.""" + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the individual tokens in the transcription. + + Only included if you + [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) + with the `include[]` parameter set to `logprobs`. + """ diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py index 62f85b8757..b23a185375 100644 --- a/src/openai/types/audio/translation_create_params.py +++ b/src/openai/types/audio/translation_create_params.py @@ -3,11 +3,10 @@ from __future__ import annotations from typing import Union -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from ..._types import FileTypes from ..audio_model import AudioModel -from ..audio_response_format import AudioResponseFormat __all__ = ["TranslationCreateParams"] @@ -34,7 +33,7 @@ class TranslationCreateParams(TypedDict, total=False): should be in English. """ - response_format: AudioResponseFormat + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] """ The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or `vtt`. diff --git a/src/openai/types/audio_model.py b/src/openai/types/audio_model.py index 94ae84c015..4d14d60181 100644 --- a/src/openai/types/audio_model.py +++ b/src/openai/types/audio_model.py @@ -4,4 +4,4 @@ __all__ = ["AudioModel"] -AudioModel: TypeAlias = Literal["whisper-1"] +AudioModel: TypeAlias = Literal["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe"] diff --git a/src/openai/types/beta/realtime/__init__.py b/src/openai/types/beta/realtime/__init__.py index cd0616dcfa..0374b9b457 100644 --- a/src/openai/types/beta/realtime/__init__.py +++ b/src/openai/types/beta/realtime/__init__.py @@ -15,6 +15,7 @@ from .session_create_params import SessionCreateParams as SessionCreateParams from .session_created_event import SessionCreatedEvent as SessionCreatedEvent from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent +from .transcription_session import TranscriptionSession as TranscriptionSession from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent from .conversation_item_param import ConversationItemParam as ConversationItemParam from .realtime_connect_params import RealtimeConnectParams as RealtimeConnectParams @@ -32,6 +33,7 @@ from .realtime_client_event_param import RealtimeClientEventParam as RealtimeClientEventParam from .response_cancel_event_param import ResponseCancelEventParam as ResponseCancelEventParam from .response_create_event_param import ResponseCreateEventParam as ResponseCreateEventParam +from .transcription_session_update import TranscriptionSessionUpdate as TranscriptionSessionUpdate from .conversation_item_create_event import ConversationItemCreateEvent as ConversationItemCreateEvent from .conversation_item_delete_event import ConversationItemDeleteEvent as ConversationItemDeleteEvent from .input_audio_buffer_clear_event import InputAudioBufferClearEvent as InputAudioBufferClearEvent @@ -41,6 +43,7 @@ from .input_audio_buffer_append_event import InputAudioBufferAppendEvent as InputAudioBufferAppendEvent from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent as InputAudioBufferCommitEvent from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent +from .conversation_item_retrieve_event import ConversationItemRetrieveEvent as ConversationItemRetrieveEvent from .conversation_item_truncate_event import ConversationItemTruncateEvent as ConversationItemTruncateEvent from .conversation_item_with_reference import ConversationItemWithReference as ConversationItemWithReference from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent as InputAudioBufferClearedEvent @@ -49,6 +52,9 @@ from .conversation_item_truncated_event import ConversationItemTruncatedEvent as ConversationItemTruncatedEvent from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent as InputAudioBufferCommittedEvent +from .transcription_session_update_param import TranscriptionSessionUpdateParam as TranscriptionSessionUpdateParam +from .transcription_session_create_params import TranscriptionSessionCreateParams as TranscriptionSessionCreateParams +from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent as TranscriptionSessionUpdatedEvent from .conversation_item_create_event_param import ConversationItemCreateEventParam as ConversationItemCreateEventParam from .conversation_item_delete_event_param import ConversationItemDeleteEventParam as ConversationItemDeleteEventParam from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam as InputAudioBufferClearEventParam @@ -58,6 +64,9 @@ from .response_audio_transcript_delta_event import ( ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, ) +from .conversation_item_retrieve_event_param import ( + ConversationItemRetrieveEventParam as ConversationItemRetrieveEventParam, +) from .conversation_item_truncate_event_param import ( ConversationItemTruncateEventParam as ConversationItemTruncateEventParam, ) @@ -76,6 +85,9 @@ from .response_function_call_arguments_delta_event import ( ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, ) +from .conversation_item_input_audio_transcription_delta_event import ( + ConversationItemInputAudioTranscriptionDeltaEvent as ConversationItemInputAudioTranscriptionDeltaEvent, +) from .conversation_item_input_audio_transcription_failed_event import ( ConversationItemInputAudioTranscriptionFailedEvent as ConversationItemInputAudioTranscriptionFailedEvent, ) diff --git a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py index ded79cc0f7..469811693c 100644 --- a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py +++ b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py @@ -1,10 +1,22 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List, Optional from typing_extensions import Literal from ...._models import BaseModel -__all__ = ["ConversationItemInputAudioTranscriptionCompletedEvent"] +__all__ = ["ConversationItemInputAudioTranscriptionCompletedEvent", "Logprob"] + + +class Logprob(BaseModel): + token: str + """The token that was used to generate the log probability.""" + + bytes: List[int] + """The bytes that were used to generate the log probability.""" + + logprob: float + """The log probability of the token.""" class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): @@ -24,3 +36,6 @@ class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): """ The event type, must be `conversation.item.input_audio_transcription.completed`. """ + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the transcription.""" diff --git a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py new file mode 100644 index 0000000000..924d06d98a --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionDeltaEvent", "Logprob"] + + +class Logprob(BaseModel): + token: str + """The token that was used to generate the log probability.""" + + bytes: List[int] + """The bytes that were used to generate the log probability.""" + + logprob: float + """The log probability of the token.""" + + +class ConversationItemInputAudioTranscriptionDeltaEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + type: Literal["conversation.item.input_audio_transcription.delta"] + """The event type, must be `conversation.item.input_audio_transcription.delta`.""" + + content_index: Optional[int] = None + """The index of the content part in the item's content array.""" + + delta: Optional[str] = None + """The text delta.""" + + logprobs: Optional[List[Logprob]] = None + """The log probabilities of the transcription.""" diff --git a/src/openai/types/beta/realtime/conversation_item_retrieve_event.py b/src/openai/types/beta/realtime/conversation_item_retrieve_event.py new file mode 100644 index 0000000000..822386055c --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_retrieve_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ConversationItemRetrieveEvent"] + + +class ConversationItemRetrieveEvent(BaseModel): + item_id: str + """The ID of the item to retrieve.""" + + type: Literal["conversation.item.retrieve"] + """The event type, must be `conversation.item.retrieve`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/conversation_item_retrieve_event_param.py b/src/openai/types/beta/realtime/conversation_item_retrieve_event_param.py new file mode 100644 index 0000000000..71b3ffa499 --- /dev/null +++ b/src/openai/types/beta/realtime/conversation_item_retrieve_event_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemRetrieveEventParam"] + + +class ConversationItemRetrieveEventParam(TypedDict, total=False): + item_id: Required[str] + """The ID of the item to retrieve.""" + + type: Required[Literal["conversation.item.retrieve"]] + """The event type, must be `conversation.item.retrieve`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/realtime_client_event.py b/src/openai/types/beta/realtime/realtime_client_event.py index 0769184cd0..f962a505cd 100644 --- a/src/openai/types/beta/realtime/realtime_client_event.py +++ b/src/openai/types/beta/realtime/realtime_client_event.py @@ -7,26 +7,30 @@ from .session_update_event import SessionUpdateEvent from .response_cancel_event import ResponseCancelEvent from .response_create_event import ResponseCreateEvent +from .transcription_session_update import TranscriptionSessionUpdate from .conversation_item_create_event import ConversationItemCreateEvent from .conversation_item_delete_event import ConversationItemDeleteEvent from .input_audio_buffer_clear_event import InputAudioBufferClearEvent from .input_audio_buffer_append_event import InputAudioBufferAppendEvent from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent +from .conversation_item_retrieve_event import ConversationItemRetrieveEvent from .conversation_item_truncate_event import ConversationItemTruncateEvent __all__ = ["RealtimeClientEvent"] RealtimeClientEvent: TypeAlias = Annotated[ Union[ - SessionUpdateEvent, - InputAudioBufferAppendEvent, - InputAudioBufferCommitEvent, - InputAudioBufferClearEvent, ConversationItemCreateEvent, - ConversationItemTruncateEvent, ConversationItemDeleteEvent, - ResponseCreateEvent, + ConversationItemRetrieveEvent, + ConversationItemTruncateEvent, + InputAudioBufferAppendEvent, + InputAudioBufferClearEvent, + InputAudioBufferCommitEvent, ResponseCancelEvent, + ResponseCreateEvent, + SessionUpdateEvent, + TranscriptionSessionUpdate, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/beta/realtime/realtime_client_event_param.py b/src/openai/types/beta/realtime/realtime_client_event_param.py index 4020892c33..6fdba4b87c 100644 --- a/src/openai/types/beta/realtime/realtime_client_event_param.py +++ b/src/openai/types/beta/realtime/realtime_client_event_param.py @@ -8,23 +8,27 @@ from .session_update_event_param import SessionUpdateEventParam from .response_cancel_event_param import ResponseCancelEventParam from .response_create_event_param import ResponseCreateEventParam +from .transcription_session_update_param import TranscriptionSessionUpdateParam from .conversation_item_create_event_param import ConversationItemCreateEventParam from .conversation_item_delete_event_param import ConversationItemDeleteEventParam from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam +from .conversation_item_retrieve_event_param import ConversationItemRetrieveEventParam from .conversation_item_truncate_event_param import ConversationItemTruncateEventParam __all__ = ["RealtimeClientEventParam"] RealtimeClientEventParam: TypeAlias = Union[ - SessionUpdateEventParam, - InputAudioBufferAppendEventParam, - InputAudioBufferCommitEventParam, - InputAudioBufferClearEventParam, ConversationItemCreateEventParam, - ConversationItemTruncateEventParam, ConversationItemDeleteEventParam, - ResponseCreateEventParam, + ConversationItemRetrieveEventParam, + ConversationItemTruncateEventParam, + InputAudioBufferAppendEventParam, + InputAudioBufferClearEventParam, + InputAudioBufferCommitEventParam, ResponseCancelEventParam, + ResponseCreateEventParam, + SessionUpdateEventParam, + TranscriptionSessionUpdateParam, ] diff --git a/src/openai/types/beta/realtime/realtime_server_event.py b/src/openai/types/beta/realtime/realtime_server_event.py index 5f8ed55b13..ba1d324445 100644 --- a/src/openai/types/beta/realtime/realtime_server_event.py +++ b/src/openai/types/beta/realtime/realtime_server_event.py @@ -1,10 +1,12 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import Annotated, TypeAlias +from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo +from ...._models import BaseModel from .error_event import ErrorEvent +from .conversation_item import ConversationItem from .response_done_event import ResponseDoneEvent from .session_created_event import SessionCreatedEvent from .session_updated_event import SessionUpdatedEvent @@ -24,49 +26,66 @@ from .conversation_item_truncated_event import ConversationItemTruncatedEvent from .response_content_part_added_event import ResponseContentPartAddedEvent from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent +from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent from .input_audio_buffer_speech_started_event import InputAudioBufferSpeechStartedEvent from .input_audio_buffer_speech_stopped_event import InputAudioBufferSpeechStoppedEvent from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .conversation_item_input_audio_transcription_delta_event import ConversationItemInputAudioTranscriptionDeltaEvent from .conversation_item_input_audio_transcription_failed_event import ConversationItemInputAudioTranscriptionFailedEvent from .conversation_item_input_audio_transcription_completed_event import ( ConversationItemInputAudioTranscriptionCompletedEvent, ) -__all__ = ["RealtimeServerEvent"] +__all__ = ["RealtimeServerEvent", "ConversationItemRetrieved"] + + +class ConversationItemRetrieved(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """The item to add to the conversation.""" + + type: Literal["conversation.item.retrieved"] + """The event type, must be `conversation.item.retrieved`.""" + RealtimeServerEvent: TypeAlias = Annotated[ Union[ - ErrorEvent, - SessionCreatedEvent, - SessionUpdatedEvent, ConversationCreatedEvent, - InputAudioBufferCommittedEvent, - InputAudioBufferClearedEvent, - InputAudioBufferSpeechStartedEvent, - InputAudioBufferSpeechStoppedEvent, ConversationItemCreatedEvent, + ConversationItemDeletedEvent, ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionDeltaEvent, ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemRetrieved, ConversationItemTruncatedEvent, - ConversationItemDeletedEvent, + ErrorEvent, + InputAudioBufferClearedEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + RateLimitsUpdatedEvent, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, ResponseCreatedEvent, ResponseDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, ResponseOutputItemAddedEvent, ResponseOutputItemDoneEvent, - ResponseContentPartAddedEvent, - ResponseContentPartDoneEvent, ResponseTextDeltaEvent, ResponseTextDoneEvent, - ResponseAudioTranscriptDeltaEvent, - ResponseAudioTranscriptDoneEvent, - ResponseAudioDeltaEvent, - ResponseAudioDoneEvent, - ResponseFunctionCallArgumentsDeltaEvent, - ResponseFunctionCallArgumentsDoneEvent, - RateLimitsUpdatedEvent, + SessionCreatedEvent, + SessionUpdatedEvent, + TranscriptionSessionUpdatedEvent, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py index aee20fa906..3ed53ff5f8 100644 --- a/src/openai/types/beta/realtime/session.py +++ b/src/openai/types/beta/realtime/session.py @@ -5,14 +5,40 @@ from ...._models import BaseModel -__all__ = ["Session", "InputAudioTranscription", "Tool", "TurnDetection"] +__all__ = ["Session", "InputAudioNoiseReduction", "InputAudioTranscription", "Tool", "TurnDetection"] + + +class InputAudioNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ class InputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + model: Optional[str] = None """ - The model to use for transcription, `whisper-1` is the only currently supported - model. + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". """ @@ -35,46 +61,56 @@ class Tool(BaseModel): class TurnDetection(BaseModel): create_response: Optional[bool] = None - """Whether or not to automatically generate a response when a VAD stop event + """ + Whether or not to automatically generate a response when a VAD stop event occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. - `true` by default. + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. """ interrupt_response: Optional[bool] = None """ Whether or not to automatically interrupt any ongoing response with output to the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. `true` by default. + occurs. """ prefix_padding_ms: Optional[int] = None - """Amount of audio to include before the VAD detected speech (in milliseconds). + """Used only for `server_vad` mode. + Amount of audio to include before the VAD detected speech (in milliseconds). Defaults to 300ms. """ silence_duration_ms: Optional[int] = None - """Duration of silence to detect speech stop (in milliseconds). + """Used only for `server_vad` mode. - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. """ threshold: Optional[float] = None - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + """Used only for `server_vad` mode. - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. """ - type: Optional[Literal["server_vad"]] = None - """Type of turn detection, only `server_vad` is currently supported.""" + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" class Session(BaseModel): id: Optional[str] = None - """Unique identifier for the session object.""" + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None """The format of input audio. @@ -84,13 +120,25 @@ class Session(BaseModel): byte order. """ + input_audio_noise_reduction: Optional[InputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + input_audio_transcription: Optional[InputAudioTranscription] = None """ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs - asynchronously through Whisper and should be treated as rough guidance rather - than the representation understood by the model. + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. """ instructions: Optional[str] = None @@ -122,16 +170,14 @@ class Session(BaseModel): To disable audio, set this to ["text"]. """ - model: Union[ - str, + model: Optional[ Literal[ "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", - ], - None, + ] ] = None """The Realtime model used for this session.""" @@ -143,7 +189,11 @@ class Session(BaseModel): """ temperature: Optional[float] = None - """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ tool_choice: Optional[str] = None """How the model chooses tools. @@ -155,11 +205,17 @@ class Session(BaseModel): """Tools (functions) available to the model.""" turn_detection: Optional[TurnDetection] = None - """Configuration for turn detection. - - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjuction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. """ voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py index bbc86d7c7d..fe4a1c8636 100644 --- a/src/openai/types/beta/realtime/session_create_params.py +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -5,7 +5,7 @@ from typing import List, Union, Iterable from typing_extensions import Literal, TypedDict -__all__ = ["SessionCreateParams", "InputAudioTranscription", "Tool", "TurnDetection"] +__all__ = ["SessionCreateParams", "InputAudioNoiseReduction", "InputAudioTranscription", "Tool", "TurnDetection"] class SessionCreateParams(TypedDict, total=False): @@ -17,16 +17,25 @@ class SessionCreateParams(TypedDict, total=False): byte order. """ + input_audio_noise_reduction: InputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + input_audio_transcription: InputAudioTranscription """ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through - [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as rough guidance rather than the representation - understood by the model. The client can optionally set the language and prompt - for transcription, these fields will be passed to the Whisper API. + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. """ instructions: str @@ -75,7 +84,11 @@ class SessionCreateParams(TypedDict, total=False): """ temperature: float - """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ tool_choice: str """How the model chooses tools. @@ -87,11 +100,17 @@ class SessionCreateParams(TypedDict, total=False): """Tools (functions) available to the model.""" turn_detection: TurnDetection - """Configuration for turn detection. + """Configuration for turn detection, ether Server VAD or Semantic VAD. - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjuction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. """ voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] @@ -103,6 +122,15 @@ class SessionCreateParams(TypedDict, total=False): """ +class InputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + class InputAudioTranscription(TypedDict, total=False): language: str """The language of the input audio. @@ -114,16 +142,17 @@ class InputAudioTranscription(TypedDict, total=False): model: str """ - The model to use for transcription, `whisper-1` is the only currently supported - model. + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. """ prompt: str - """An optional text to guide the model's style or continue a previous audio - segment. - - The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". """ @@ -146,38 +175,48 @@ class Tool(TypedDict, total=False): class TurnDetection(TypedDict, total=False): create_response: bool - """Whether or not to automatically generate a response when a VAD stop event + """ + Whether or not to automatically generate a response when a VAD stop event occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. - `true` by default. + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. """ interrupt_response: bool """ Whether or not to automatically interrupt any ongoing response with output to the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. `true` by default. + occurs. """ prefix_padding_ms: int - """Amount of audio to include before the VAD detected speech (in milliseconds). + """Used only for `server_vad` mode. + Amount of audio to include before the VAD detected speech (in milliseconds). Defaults to 300ms. """ silence_duration_ms: int - """Duration of silence to detect speech stop (in milliseconds). + """Used only for `server_vad` mode. - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. """ threshold: float - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + """Used only for `server_vad` mode. - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. """ - type: str - """Type of turn detection, only `server_vad` is currently supported.""" + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py index 999cd8d660..00180f593d 100644 --- a/src/openai/types/beta/realtime/session_update_event.py +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -5,7 +5,23 @@ from ...._models import BaseModel -__all__ = ["SessionUpdateEvent", "Session", "SessionInputAudioTranscription", "SessionTool", "SessionTurnDetection"] +__all__ = [ + "SessionUpdateEvent", + "Session", + "SessionInputAudioNoiseReduction", + "SessionInputAudioTranscription", + "SessionTool", + "SessionTurnDetection", +] + + +class SessionInputAudioNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ class SessionInputAudioTranscription(BaseModel): @@ -19,16 +35,17 @@ class SessionInputAudioTranscription(BaseModel): model: Optional[str] = None """ - The model to use for transcription, `whisper-1` is the only currently supported - model. + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. """ prompt: Optional[str] = None - """An optional text to guide the model's style or continue a previous audio - segment. - - The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". """ @@ -51,41 +68,51 @@ class SessionTool(BaseModel): class SessionTurnDetection(BaseModel): create_response: Optional[bool] = None - """Whether or not to automatically generate a response when a VAD stop event + """ + Whether or not to automatically generate a response when a VAD stop event occurs. + """ - `true` by default. + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. """ interrupt_response: Optional[bool] = None """ Whether or not to automatically interrupt any ongoing response with output to the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. `true` by default. + occurs. """ prefix_padding_ms: Optional[int] = None - """Amount of audio to include before the VAD detected speech (in milliseconds). + """Used only for `server_vad` mode. + Amount of audio to include before the VAD detected speech (in milliseconds). Defaults to 300ms. """ silence_duration_ms: Optional[int] = None - """Duration of silence to detect speech stop (in milliseconds). + """Used only for `server_vad` mode. - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. """ threshold: Optional[float] = None - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + """Used only for `server_vad` mode. - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. """ - type: Optional[str] = None - """Type of turn detection, only `server_vad` is currently supported.""" + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" class Session(BaseModel): @@ -97,16 +124,25 @@ class Session(BaseModel): byte order. """ + input_audio_noise_reduction: Optional[SessionInputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + input_audio_transcription: Optional[SessionInputAudioTranscription] = None """ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through - [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as rough guidance rather than the representation - understood by the model. The client can optionally set the language and prompt - for transcription, these fields will be passed to the Whisper API. + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. """ instructions: Optional[str] = None @@ -157,7 +193,11 @@ class Session(BaseModel): """ temperature: Optional[float] = None - """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ tool_choice: Optional[str] = None """How the model chooses tools. @@ -169,11 +209,17 @@ class Session(BaseModel): """Tools (functions) available to the model.""" turn_detection: Optional[SessionTurnDetection] = None - """Configuration for turn detection. - - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjuction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. """ voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py index 07fdba9d85..b8bce8fbd0 100644 --- a/src/openai/types/beta/realtime/session_update_event_param.py +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -8,12 +8,22 @@ __all__ = [ "SessionUpdateEventParam", "Session", + "SessionInputAudioNoiseReduction", "SessionInputAudioTranscription", "SessionTool", "SessionTurnDetection", ] +class SessionInputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + class SessionInputAudioTranscription(TypedDict, total=False): language: str """The language of the input audio. @@ -25,16 +35,17 @@ class SessionInputAudioTranscription(TypedDict, total=False): model: str """ - The model to use for transcription, `whisper-1` is the only currently supported - model. + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. """ prompt: str - """An optional text to guide the model's style or continue a previous audio - segment. - - The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". """ @@ -57,41 +68,51 @@ class SessionTool(TypedDict, total=False): class SessionTurnDetection(TypedDict, total=False): create_response: bool - """Whether or not to automatically generate a response when a VAD stop event + """ + Whether or not to automatically generate a response when a VAD stop event occurs. + """ - `true` by default. + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. """ interrupt_response: bool """ Whether or not to automatically interrupt any ongoing response with output to the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. `true` by default. + occurs. """ prefix_padding_ms: int - """Amount of audio to include before the VAD detected speech (in milliseconds). + """Used only for `server_vad` mode. + Amount of audio to include before the VAD detected speech (in milliseconds). Defaults to 300ms. """ silence_duration_ms: int - """Duration of silence to detect speech stop (in milliseconds). + """Used only for `server_vad` mode. - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. """ threshold: float - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + """Used only for `server_vad` mode. - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. """ - type: str - """Type of turn detection, only `server_vad` is currently supported.""" + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" class Session(TypedDict, total=False): @@ -103,16 +124,25 @@ class Session(TypedDict, total=False): byte order. """ + input_audio_noise_reduction: SessionInputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + input_audio_transcription: SessionInputAudioTranscription """ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through - [OpenAI Whisper transcription](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as rough guidance rather than the representation - understood by the model. The client can optionally set the language and prompt - for transcription, these fields will be passed to the Whisper API. + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. """ instructions: str @@ -161,7 +191,11 @@ class Session(TypedDict, total=False): """ temperature: float - """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ tool_choice: str """How the model chooses tools. @@ -173,11 +207,17 @@ class Session(TypedDict, total=False): """Tools (functions) available to the model.""" turn_detection: SessionTurnDetection - """Configuration for turn detection. - - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjuction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. """ voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] diff --git a/src/openai/types/beta/realtime/transcription_session.py b/src/openai/types/beta/realtime/transcription_session.py new file mode 100644 index 0000000000..7c7abf37b6 --- /dev/null +++ b/src/openai/types/beta/realtime/transcription_session.py @@ -0,0 +1,100 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["TranscriptionSession", "ClientSecret", "InputAudioTranscription", "TurnDetection"] + + +class ClientSecret(BaseModel): + expires_at: int + """Timestamp for when the token expires. + + Currently, all tokens expire after one minute. + """ + + value: str + """ + Ephemeral key usable in client environments to authenticate connections to the + Realtime API. Use this in client-side environments rather than a standard API + token, which should only be used server-side. + """ + + +class InputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None + """The model to use for transcription. + + Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`. + """ + + prompt: Optional[str] = None + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + """ + + +class TurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class TranscriptionSession(BaseModel): + client_secret: ClientSecret + """Ephemeral key returned by the API. + + Only present when the session is created on the server via REST API. + """ + + input_audio_format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: Optional[InputAudioTranscription] = None + """Configuration of the transcription model.""" + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ diff --git a/src/openai/types/beta/realtime/transcription_session_create_params.py b/src/openai/types/beta/realtime/transcription_session_create_params.py new file mode 100644 index 0000000000..4066dc4c5d --- /dev/null +++ b/src/openai/types/beta/realtime/transcription_session_create_params.py @@ -0,0 +1,143 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = ["TranscriptionSessionCreateParams", "InputAudioNoiseReduction", "InputAudioTranscription", "TurnDetection"] + + +class TranscriptionSessionCreateParams(TypedDict, total=False): + include: List[str] + """The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: InputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: InputAudioTranscription + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: TurnDetection + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjuction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + +class InputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"] + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class TurnDetection(TypedDict, total=False): + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" diff --git a/src/openai/types/beta/realtime/transcription_session_update.py b/src/openai/types/beta/realtime/transcription_session_update.py new file mode 100644 index 0000000000..043ac02e07 --- /dev/null +++ b/src/openai/types/beta/realtime/transcription_session_update.py @@ -0,0 +1,160 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = [ + "TranscriptionSessionUpdate", + "Session", + "SessionInputAudioNoiseReduction", + "SessionInputAudioTranscription", + "SessionTurnDetection", +] + + +class SessionInputAudioNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class SessionInputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class SessionTurnDetection(BaseModel): + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" + + +class Session(BaseModel): + include: Optional[List[str]] = None + """The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: Optional[SessionInputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: Optional[SessionInputAudioTranscription] = None + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: Optional[SessionTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjuction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + +class TranscriptionSessionUpdate(BaseModel): + session: Session + """Realtime transcription session object configuration.""" + + type: Literal["transcription_session.update"] + """The event type, must be `transcription_session.update`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/transcription_session_update_param.py b/src/openai/types/beta/realtime/transcription_session_update_param.py new file mode 100644 index 0000000000..997a36d77b --- /dev/null +++ b/src/openai/types/beta/realtime/transcription_session_update_param.py @@ -0,0 +1,160 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "TranscriptionSessionUpdateParam", + "Session", + "SessionInputAudioNoiseReduction", + "SessionInputAudioTranscription", + "SessionTurnDetection", +] + + +class SessionInputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class SessionInputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"] + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class SessionTurnDetection(TypedDict, total=False): + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" + + +class Session(TypedDict, total=False): + include: List[str] + """The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: SessionInputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: SessionInputAudioTranscription + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: SessionTurnDetection + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjuction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + +class TranscriptionSessionUpdateParam(TypedDict, total=False): + session: Required[Session] + """Realtime transcription session object configuration.""" + + type: Required[Literal["transcription_session.update"]] + """The event type, must be `transcription_session.update`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/beta/realtime/transcription_session_updated_event.py b/src/openai/types/beta/realtime/transcription_session_updated_event.py new file mode 100644 index 0000000000..ffc100bcc2 --- /dev/null +++ b/src/openai/types/beta/realtime/transcription_session_updated_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from .transcription_session import TranscriptionSession + +__all__ = ["TranscriptionSessionUpdatedEvent"] + + +class TranscriptionSessionUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: TranscriptionSession + """A new Realtime transcription session configuration. + + When a session is created on the server via REST API, the session object also + contains an ephemeral key. Default TTL for keys is one minute. This property is + not present when a session is updated via the WebSocket API. + """ + + type: Literal["transcription_session.updated"] + """The event type, must be `transcription_session.updated`.""" diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 781ebeceb9..808f6ef66c 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -41,6 +41,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou input="string", model="string", voice="alloy", + instructions="instructions", response_format="mp3", speed=0.25, ) @@ -104,6 +105,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re input="string", model="string", voice="alloy", + instructions="instructions", response_format="mp3", speed=0.25, ) diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index bdb7e0dfb6..19215e11df 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -18,31 +18,33 @@ class TestTranscriptions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - def test_method_create(self, client: OpenAI) -> None: + def test_method_create_overload_1(self, client: OpenAI) -> None: transcription = client.audio.transcriptions.create( file=b"raw file contents", - model="whisper-1", + model="gpt-4o-transcribe", ) assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize - def test_method_create_with_all_params(self, client: OpenAI) -> None: + def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: transcription = client.audio.transcriptions.create( file=b"raw file contents", - model="whisper-1", - language="string", - prompt="string", + model="gpt-4o-transcribe", + include=["logprobs"], + language="language", + prompt="prompt", response_format="json", + stream=False, temperature=0, timestamp_granularities=["word"], ) assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize - def test_raw_response_create(self, client: OpenAI) -> None: + def test_raw_response_create_overload_1(self, client: OpenAI) -> None: response = client.audio.transcriptions.with_raw_response.create( file=b"raw file contents", - model="whisper-1", + model="gpt-4o-transcribe", ) assert response.is_closed is True @@ -51,10 +53,10 @@ def test_raw_response_create(self, client: OpenAI) -> None: assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize - def test_streaming_response_create(self, client: OpenAI) -> None: + def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: with client.audio.transcriptions.with_streaming_response.create( file=b"raw file contents", - model="whisper-1", + model="gpt-4o-transcribe", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -64,36 +66,89 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_create_overload_2(self, client: OpenAI) -> None: + transcription_stream = client.audio.transcriptions.create( + file=b"raw file contents", + model="gpt-4o-transcribe", + stream=True, + ) + transcription_stream.response.close() + + @parametrize + def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: + transcription_stream = client.audio.transcriptions.create( + file=b"raw file contents", + model="gpt-4o-transcribe", + stream=True, + include=["logprobs"], + language="language", + prompt="prompt", + response_format="json", + temperature=0, + timestamp_granularities=["word"], + ) + transcription_stream.response.close() + + @parametrize + def test_raw_response_create_overload_2(self, client: OpenAI) -> None: + response = client.audio.transcriptions.with_raw_response.create( + file=b"raw file contents", + model="gpt-4o-transcribe", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: + with client.audio.transcriptions.with_streaming_response.create( + file=b"raw file contents", + model="gpt-4o-transcribe", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + class TestAsyncTranscriptions: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - async def test_method_create(self, async_client: AsyncOpenAI) -> None: + async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: transcription = await async_client.audio.transcriptions.create( file=b"raw file contents", - model="whisper-1", + model="gpt-4o-transcribe", ) assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: transcription = await async_client.audio.transcriptions.create( file=b"raw file contents", - model="whisper-1", - language="string", - prompt="string", + model="gpt-4o-transcribe", + include=["logprobs"], + language="language", + prompt="prompt", response_format="json", + stream=False, temperature=0, timestamp_granularities=["word"], ) assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize - async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.audio.transcriptions.with_raw_response.create( file=b"raw file contents", - model="whisper-1", + model="gpt-4o-transcribe", ) assert response.is_closed is True @@ -102,10 +157,10 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @parametrize - async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.audio.transcriptions.with_streaming_response.create( file=b"raw file contents", - model="whisper-1", + model="gpt-4o-transcribe", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -114,3 +169,54 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: + transcription_stream = await async_client.audio.transcriptions.create( + file=b"raw file contents", + model="gpt-4o-transcribe", + stream=True, + ) + await transcription_stream.response.aclose() + + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + transcription_stream = await async_client.audio.transcriptions.create( + file=b"raw file contents", + model="gpt-4o-transcribe", + stream=True, + include=["logprobs"], + language="language", + prompt="prompt", + response_format="json", + temperature=0, + timestamp_granularities=["word"], + ) + await transcription_stream.response.aclose() + + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.audio.transcriptions.with_raw_response.create( + file=b"raw file contents", + model="gpt-4o-transcribe", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.audio.transcriptions.with_streaming_response.create( + file=b"raw file contents", + model="gpt-4o-transcribe", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/realtime/test_sessions.py b/tests/api_resources/beta/realtime/test_sessions.py index 5ea308ca0d..c0a426a417 100644 --- a/tests/api_resources/beta/realtime/test_sessions.py +++ b/tests/api_resources/beta/realtime/test_sessions.py @@ -26,6 +26,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: session = client.beta.realtime.sessions.create( input_audio_format="pcm16", + input_audio_noise_reduction={"type": "near_field"}, input_audio_transcription={ "language": "language", "model": "model", @@ -48,11 +49,12 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: ], turn_detection={ "create_response": True, + "eagerness": "low", "interrupt_response": True, "prefix_padding_ms": 0, "silence_duration_ms": 0, "threshold": 0, - "type": "type", + "type": "server_vad", }, voice="alloy", ) @@ -91,6 +93,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: session = await async_client.beta.realtime.sessions.create( input_audio_format="pcm16", + input_audio_noise_reduction={"type": "near_field"}, input_audio_transcription={ "language": "language", "model": "model", @@ -113,11 +116,12 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> ], turn_detection={ "create_response": True, + "eagerness": "low", "interrupt_response": True, "prefix_padding_ms": 0, "silence_duration_ms": 0, "threshold": 0, - "type": "type", + "type": "server_vad", }, voice="alloy", ) diff --git a/tests/api_resources/beta/realtime/test_transcription_sessions.py b/tests/api_resources/beta/realtime/test_transcription_sessions.py new file mode 100644 index 0000000000..4826185bea --- /dev/null +++ b/tests/api_resources/beta/realtime/test_transcription_sessions.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.beta.realtime import TranscriptionSession + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestTranscriptionSessions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + transcription_session = client.beta.realtime.transcription_sessions.create() + assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + transcription_session = client.beta.realtime.transcription_sessions.create( + include=["string"], + input_audio_format="pcm16", + input_audio_noise_reduction={"type": "near_field"}, + input_audio_transcription={ + "language": "language", + "model": "gpt-4o-transcribe", + "prompt": "prompt", + }, + modalities=["text"], + turn_detection={ + "create_response": True, + "eagerness": "low", + "interrupt_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + "type": "server_vad", + }, + ) + assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.realtime.transcription_sessions.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + transcription_session = response.parse() + assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.realtime.transcription_sessions.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + transcription_session = response.parse() + assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncTranscriptionSessions: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + transcription_session = await async_client.beta.realtime.transcription_sessions.create() + assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + transcription_session = await async_client.beta.realtime.transcription_sessions.create( + include=["string"], + input_audio_format="pcm16", + input_audio_noise_reduction={"type": "near_field"}, + input_audio_transcription={ + "language": "language", + "model": "gpt-4o-transcribe", + "prompt": "prompt", + }, + modalities=["text"], + turn_detection={ + "create_response": True, + "eagerness": "low", + "interrupt_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + "type": "server_vad", + }, + ) + assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.realtime.transcription_sessions.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + transcription_session = response.parse() + assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.realtime.transcription_sessions.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + transcription_session = await response.parse() + assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/lib/test_audio.py b/tests/lib/test_audio.py index 0f53b316ba..ff8dba4714 100644 --- a/tests/lib/test_audio.py +++ b/tests/lib/test_audio.py @@ -26,7 +26,7 @@ def test_translation_create_overloads_in_sync(sync: bool, client: OpenAI, async_ assert_signatures_in_sync( fn, overload, - exclude_params={"response_format"}, + exclude_params={"response_format", "stream"}, description=f" for overload {i}", ) @@ -60,7 +60,7 @@ def test_transcription_create_overloads_in_sync(sync: bool, client: OpenAI, asyn assert_signatures_in_sync( fn, overload, - exclude_params={"response_format"}, + exclude_params={"response_format", "stream"}, description=f" for overload {i}", ) From 8136a21637df5d79442efcb26459d2dd6154db77 Mon Sep 17 00:00:00 2001 From: Kevin Whinnery Date: Thu, 20 Mar 2025 11:31:58 -0500 Subject: [PATCH 833/914] feat: add audio helpers * add audio helpers * update ignore, lockfile, add execute * fix examples, lint errors * lint and export errors * temp: ignore type errors --- .gitignore | 4 + examples/audio.py | 26 ---- examples/speech_to_text.py | 25 ++++ examples/text_to_speech.py | 31 +++++ pyproject.toml | 2 + requirements-dev.lock | 7 +- requirements.lock | 6 + src/openai/helpers.py | 4 + src/openai/helpers/__init__.py | 4 + src/openai/helpers/local_audio_player.py | 162 +++++++++++++++++++++++ src/openai/helpers/microphone.py | 98 ++++++++++++++ 11 files changed, 341 insertions(+), 28 deletions(-) create mode 100755 examples/speech_to_text.py create mode 100755 examples/text_to_speech.py create mode 100644 src/openai/helpers.py create mode 100644 src/openai/helpers/__init__.py create mode 100644 src/openai/helpers/local_audio_player.py create mode 100644 src/openai/helpers/microphone.py diff --git a/.gitignore b/.gitignore index 8779740800..70815df7f6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,7 @@ dist .envrc codegen.log Brewfile.lock.json + +.DS_Store + +examples/*.mp3 diff --git a/examples/audio.py b/examples/audio.py index 85f47bfb06..af41fe601b 100755 --- a/examples/audio.py +++ b/examples/audio.py @@ -1,6 +1,5 @@ #!/usr/bin/env rye run python -import time from pathlib import Path from openai import OpenAI @@ -12,8 +11,6 @@ def main() -> None: - stream_to_speakers() - # Create text-to-speech audio file with openai.audio.speech.with_streaming_response.create( model="tts-1", @@ -37,28 +34,5 @@ def main() -> None: print(translation.text) -def stream_to_speakers() -> None: - import pyaudio - - player_stream = pyaudio.PyAudio().open(format=pyaudio.paInt16, channels=1, rate=24000, output=True) - - start_time = time.time() - - with openai.audio.speech.with_streaming_response.create( - model="tts-1", - voice="alloy", - response_format="pcm", # similar to WAV, but without a header chunk at the start. - input="""I see skies of blue and clouds of white - The bright blessed days, the dark sacred nights - And I think to myself - What a wonderful world""", - ) as response: - print(f"Time to first byte: {int((time.time() - start_time) * 1000)}ms") - for chunk in response.iter_bytes(chunk_size=1024): - player_stream.write(chunk) - - print(f"Done in {int((time.time() - start_time) * 1000)}ms.") - - if __name__ == "__main__": main() diff --git a/examples/speech_to_text.py b/examples/speech_to_text.py new file mode 100755 index 0000000000..cc3f56b424 --- /dev/null +++ b/examples/speech_to_text.py @@ -0,0 +1,25 @@ +#!/usr/bin/env rye run python + +import asyncio + +from openai import AsyncOpenAI +from openai.helpers import Microphone + +# gets OPENAI_API_KEY from your environment variables +openai = AsyncOpenAI() + + +async def main() -> None: + print("Recording for the next 10 seconds...") + recording = await Microphone(timeout=10).record() + print("Recording complete") + transcription = await openai.audio.transcriptions.create( + model="whisper-1", + file=recording, + ) + + print(transcription.text) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/text_to_speech.py b/examples/text_to_speech.py new file mode 100755 index 0000000000..ac8b12b0ab --- /dev/null +++ b/examples/text_to_speech.py @@ -0,0 +1,31 @@ +#!/usr/bin/env rye run python + +import time +import asyncio + +from openai import AsyncOpenAI +from openai.helpers import LocalAudioPlayer + +# gets OPENAI_API_KEY from your environment variables +openai = AsyncOpenAI() + + +async def main() -> None: + start_time = time.time() + + async with openai.audio.speech.with_streaming_response.create( + model="tts-1", + voice="alloy", + response_format="pcm", # similar to WAV, but without a header chunk at the start. + input="""I see skies of blue and clouds of white + The bright blessed days, the dark sacred nights + And I think to myself + What a wonderful world""", + ) as response: + print(f"Time to first byte: {int((time.time() - start_time) * 1000)}ms") + await LocalAudioPlayer().play(response) + print(f"Time to play: {int((time.time() - start_time) * 1000)}ms") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/pyproject.toml b/pyproject.toml index a0a7eba2f5..dcec9ad3c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,8 @@ dependencies = [ "sniffio", "tqdm > 4", "jiter>=0.4.0, <1", + "sounddevice>=0.5.1", + "numpy>=2.0.2", ] requires-python = ">= 3.8" classifiers = [ diff --git a/requirements-dev.lock b/requirements-dev.lock index 48e49f926c..0755ddb3c5 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -33,6 +33,7 @@ certifi==2023.7.22 # via requests cffi==1.16.0 # via cryptography + # via sounddevice charset-normalizer==3.3.2 # via requests click==8.1.7 @@ -92,7 +93,7 @@ nest-asyncio==1.6.0 nodeenv==1.8.0 # via pyright nox==2023.4.22 -numpy==1.26.3 +numpy==2.0.2 # via openai # via pandas # via pandas-stubs @@ -102,7 +103,7 @@ packaging==23.2 # via black # via nox # via pytest -pandas==2.1.4 +pandas==2.2.3 # via openai pandas-stubs==2.1.4.231227 # via openai @@ -154,6 +155,8 @@ sniffio==1.3.0 # via trio sortedcontainers==2.4.0 # via trio +sounddevice==0.5.1 + # via openai time-machine==2.9.0 toml==0.10.2 # via inline-snapshot diff --git a/requirements.lock b/requirements.lock index b935c0ee59..fa88e26c0f 100644 --- a/requirements.lock +++ b/requirements.lock @@ -18,6 +18,8 @@ anyio==4.1.0 certifi==2023.7.22 # via httpcore # via httpx +cffi==1.17.1 + # via sounddevice distro==1.8.0 # via openai exceptiongroup==1.2.2 @@ -41,6 +43,8 @@ pandas==2.2.3 # via openai pandas-stubs==2.2.2.240807 # via openai +pycparser==2.22 + # via cffi pydantic==2.10.3 # via openai pydantic-core==2.27.1 @@ -54,6 +58,8 @@ six==1.16.0 sniffio==1.3.0 # via anyio # via openai +sounddevice==0.5.1 + # via openai tqdm==4.66.5 # via openai types-pytz==2024.2.0.20241003 diff --git a/src/openai/helpers.py b/src/openai/helpers.py new file mode 100644 index 0000000000..1a10168a96 --- /dev/null +++ b/src/openai/helpers.py @@ -0,0 +1,4 @@ +from .helpers.microphone import Microphone +from .helpers.local_audio_player import LocalAudioPlayer + +__all__ = ["LocalAudioPlayer", "Microphone"] diff --git a/src/openai/helpers/__init__.py b/src/openai/helpers/__init__.py new file mode 100644 index 0000000000..ab3044da59 --- /dev/null +++ b/src/openai/helpers/__init__.py @@ -0,0 +1,4 @@ +from .microphone import Microphone +from .local_audio_player import LocalAudioPlayer + +__all__ = ["Microphone", "LocalAudioPlayer"] diff --git a/src/openai/helpers/local_audio_player.py b/src/openai/helpers/local_audio_player.py new file mode 100644 index 0000000000..46a16ce6bb --- /dev/null +++ b/src/openai/helpers/local_audio_player.py @@ -0,0 +1,162 @@ +# mypy: ignore-errors +import queue +import asyncio +from typing import Any, Union, Callable, AsyncGenerator, cast + +import numpy as np +import sounddevice as sd # type: ignore +import numpy.typing as npt + +from .. import _legacy_response +from .._response import StreamedBinaryAPIResponse, AsyncStreamedBinaryAPIResponse + +SAMPLE_RATE = 24000 + + +class LocalAudioPlayer: + def __init__( + self, + should_stop: Union[Callable[[], bool], None] = None, + ): + self.channels = 1 + self.dtype = np.float32 + self.should_stop = should_stop + + async def _tts_response_to_buffer( + self, + response: Union[ + _legacy_response.HttpxBinaryResponseContent, + AsyncStreamedBinaryAPIResponse, + StreamedBinaryAPIResponse, + ], + ) -> npt.NDArray[np.float32]: + chunks: list[bytes] = [] + if isinstance(response, _legacy_response.HttpxBinaryResponseContent) or isinstance( + response, StreamedBinaryAPIResponse + ): + for chunk in response.iter_bytes(chunk_size=1024): + if chunk: + chunks.append(chunk) + else: + async for chunk in response.iter_bytes(chunk_size=1024): + if chunk: + chunks.append(chunk) + + audio_bytes = b"".join(chunks) + audio_np = np.frombuffer(audio_bytes, dtype=np.int16).astype(np.float32) / 32767.0 + audio_np = audio_np.reshape(-1, 1) + return audio_np + + async def play( + self, + input: Union[ + npt.NDArray[np.int16], + npt.NDArray[np.float32], + _legacy_response.HttpxBinaryResponseContent, + AsyncStreamedBinaryAPIResponse, + StreamedBinaryAPIResponse, + ], + ) -> None: + audio_content: npt.NDArray[np.float32] + if isinstance(input, np.ndarray): + if input.dtype == np.int16 and self.dtype == np.float32: + audio_content = (input.astype(np.float32) / 32767.0).reshape(-1, self.channels) + elif input.dtype == np.float32: + audio_content = cast(npt.NDArray[np.float32], input) + else: + raise ValueError(f"Unsupported dtype: {input.dtype}") + else: + audio_content = await self._tts_response_to_buffer(input) + + loop = asyncio.get_event_loop() + event = asyncio.Event() + idx = 0 + + def callback( + outdata: npt.NDArray[np.float32], + frame_count: int, + _time_info: Any, + _status: Any, + ): + nonlocal idx + + remainder = len(audio_content) - idx + if remainder == 0 or (callable(self.should_stop) and self.should_stop()): + loop.call_soon_threadsafe(event.set) + raise sd.CallbackStop + valid_frames = frame_count if remainder >= frame_count else remainder + outdata[:valid_frames] = audio_content[idx : idx + valid_frames] + outdata[valid_frames:] = 0 + idx += valid_frames + + stream = sd.OutputStream( + samplerate=SAMPLE_RATE, + callback=callback, + dtype=audio_content.dtype, + channels=audio_content.shape[1], + ) + with stream: + await event.wait() + + async def play_stream( + self, + buffer_stream: AsyncGenerator[Union[npt.NDArray[np.float32], npt.NDArray[np.int16], None], None], + ) -> None: + loop = asyncio.get_event_loop() + event = asyncio.Event() + buffer_queue: queue.Queue[Union[npt.NDArray[np.float32], npt.NDArray[np.int16], None]] = queue.Queue(maxsize=50) + + async def buffer_producer(): + async for buffer in buffer_stream: + if buffer is None: + break + await loop.run_in_executor(None, buffer_queue.put, buffer) + await loop.run_in_executor(None, buffer_queue.put, None) # Signal completion + + def callback( + outdata: npt.NDArray[np.float32], + frame_count: int, + _time_info: Any, + _status: Any, + ): + nonlocal current_buffer, buffer_pos + + frames_written = 0 + while frames_written < frame_count: + if current_buffer is None or buffer_pos >= len(current_buffer): + try: + current_buffer = buffer_queue.get(timeout=0.1) + if current_buffer is None: + loop.call_soon_threadsafe(event.set) + raise sd.CallbackStop + buffer_pos = 0 + + if current_buffer.dtype == np.int16 and self.dtype == np.float32: + current_buffer = (current_buffer.astype(np.float32) / 32767.0).reshape(-1, self.channels) + + except queue.Empty: + outdata[frames_written:] = 0 + return + + remaining_frames = len(current_buffer) - buffer_pos + frames_to_write = min(frame_count - frames_written, remaining_frames) + outdata[frames_written : frames_written + frames_to_write] = current_buffer[ + buffer_pos : buffer_pos + frames_to_write + ] + buffer_pos += frames_to_write + frames_written += frames_to_write + + current_buffer = None + buffer_pos = 0 + + producer_task = asyncio.create_task(buffer_producer()) + + with sd.OutputStream( + samplerate=SAMPLE_RATE, + channels=self.channels, + dtype=self.dtype, + callback=callback, + ): + await event.wait() + + await producer_task diff --git a/src/openai/helpers/microphone.py b/src/openai/helpers/microphone.py new file mode 100644 index 0000000000..18650909aa --- /dev/null +++ b/src/openai/helpers/microphone.py @@ -0,0 +1,98 @@ +# mypy: ignore-errors +import io +import time +import wave +import asyncio +from typing import Any, Type, Union, Generic, TypeVar, Callable, overload +from typing_extensions import Literal + +import numpy as np +import sounddevice as sd # type: ignore +import numpy.typing as npt + +from openai._types import FileTypes, FileContent + +SAMPLE_RATE = 24000 + +DType = TypeVar("DType", bound=np.generic) + + +class Microphone(Generic[DType]): + def __init__( + self, + channels: int = 1, + dtype: Type[DType] = np.int16, + should_record: Union[Callable[[], bool], None] = None, + timeout: Union[float, None] = None, + ): + self.channels = channels + self.dtype = dtype + self.should_record = should_record + self.buffer_chunks = [] + self.timeout = timeout + self.has_record_function = callable(should_record) + + def _ndarray_to_wav(self, audio_data: npt.NDArray[DType]) -> FileTypes: + buffer: FileContent = io.BytesIO() + with wave.open(buffer, "w") as wav_file: + wav_file.setnchannels(self.channels) + wav_file.setsampwidth(np.dtype(self.dtype).itemsize) + wav_file.setframerate(SAMPLE_RATE) + wav_file.writeframes(audio_data.tobytes()) + buffer.seek(0) + return ("audio.wav", buffer, "audio/wav") + + @overload + async def record(self, return_ndarray: Literal[True]) -> npt.NDArray[DType]: ... + + @overload + async def record(self, return_ndarray: Literal[False]) -> FileTypes: ... + + @overload + async def record(self, return_ndarray: None = ...) -> FileTypes: ... + + async def record(self, return_ndarray: Union[bool, None] = False) -> Union[npt.NDArray[DType], FileTypes]: + loop = asyncio.get_event_loop() + event = asyncio.Event() + self.buffer_chunks: list[npt.NDArray[DType]] = [] + start_time = time.perf_counter() + + def callback( + indata: npt.NDArray[DType], + _frame_count: int, + _time_info: Any, + _status: Any, + ): + execution_time = time.perf_counter() - start_time + reached_recording_timeout = execution_time > self.timeout if self.timeout is not None else False + if reached_recording_timeout: + loop.call_soon_threadsafe(event.set) + raise sd.CallbackStop + + should_be_recording = self.should_record() if callable(self.should_record) else True + if not should_be_recording: + loop.call_soon_threadsafe(event.set) + raise sd.CallbackStop + + self.buffer_chunks.append(indata.copy()) + + stream = sd.InputStream( + callback=callback, + dtype=self.dtype, + samplerate=SAMPLE_RATE, + channels=self.channels, + ) + with stream: + await event.wait() + + # Concatenate all chunks into a single buffer, handle empty case + concatenated_chunks: npt.NDArray[DType] = ( + np.concatenate(self.buffer_chunks, axis=0) + if len(self.buffer_chunks) > 0 + else np.array([], dtype=self.dtype) + ) + + if return_ndarray: + return concatenated_chunks + else: + return self._ndarray_to_wav(concatenated_chunks) From 6d0ecdd8ecbface903cf93c7571398b90b803b0b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:32:31 +0000 Subject: [PATCH 834/914] release: 1.68.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4556676715..42bc7e250e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.67.0" + ".": "1.68.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ddd8b945c6..78ae21f27f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.68.0 (2025-03-20) + +Full Changelog: [v1.67.0...v1.68.0](https://github.com/openai/openai-python/compare/v1.67.0...v1.68.0) + +### Features + +* add audio helpers ([423655c](https://github.com/openai/openai-python/commit/423655ca9077cfd258f1e52f6eb386fc8307fa5f)) +* **api:** new models for TTS, STT, + new audio features for Realtime ([#2232](https://github.com/openai/openai-python/issues/2232)) ([ab5192d](https://github.com/openai/openai-python/commit/ab5192d0a7b417ade622ec94dd48f86beb90692c)) + ## 1.67.0 (2025-03-19) Full Changelog: [v1.66.5...v1.67.0](https://github.com/openai/openai-python/compare/v1.66.5...v1.67.0) diff --git a/pyproject.toml b/pyproject.toml index dcec9ad3c4..5ee7157038 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.67.0" +version = "1.68.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b63e6ad189..23e4e7ffb7 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.67.0" # x-release-please-version +__version__ = "1.68.0" # x-release-please-version From 916641e801d2b0bf0ec7a6ea1d171c2a1931fdef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:58:42 +0000 Subject: [PATCH 835/914] fix(client): remove duplicate types (#2235) --- src/openai/types/shared/all_models.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py index c4635e2140..db8410773e 100644 --- a/src/openai/types/shared/all_models.py +++ b/src/openai/types/shared/all_models.py @@ -8,9 +8,5 @@ __all__ = ["AllModels"] AllModels: TypeAlias = Union[ - str, - ChatModel, - str, - ChatModel, - Literal["o1-pro", "o1-pro-2025-03-19", "computer-use-preview", "computer-use-preview-2025-03-11"], + str, ChatModel, Literal["o1-pro", "o1-pro-2025-03-19", "computer-use-preview", "computer-use-preview-2025-03-11"] ] From 35e0e11d85038d7c5350afe534e8c7f0f46b4f05 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 18:06:44 -0400 Subject: [PATCH 836/914] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index abb9371314..2df281d34f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 82 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-c22f59c66aec7914b6ee653d3098d1c1c8c16c180d2a158e819c8ddbf476f74b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5ad6884898c07591750dde560118baf7074a59aecd1f367f930c5e42b04e848a.yml From dbf975c84b02ffdd13de183f6af9a88890a367b3 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 21 Mar 2025 13:32:27 +0000 Subject: [PATCH 837/914] fix(package): make sounddevice and numpy optional dependencies --- pyproject.toml | 3 +-- src/openai/_extras/__init__.py | 1 + src/openai/_extras/numpy_proxy.py | 2 +- src/openai/_extras/sounddevice_proxy.py | 28 ++++++++++++++++++++++++ src/openai/helpers/local_audio_player.py | 13 ++++++----- src/openai/helpers/microphone.py | 12 +++++----- 6 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 src/openai/_extras/sounddevice_proxy.py diff --git a/pyproject.toml b/pyproject.toml index 5ee7157038..f34bf6bfa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,8 +16,6 @@ dependencies = [ "sniffio", "tqdm > 4", "jiter>=0.4.0, <1", - "sounddevice>=0.5.1", - "numpy>=2.0.2", ] requires-python = ">= 3.8" classifiers = [ @@ -47,6 +45,7 @@ openai = "openai.cli:main" [project.optional-dependencies] realtime = ["websockets >= 13, < 15"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] +audio = ["sounddevice>=0.5.1", "numpy>=2.0.2"] [tool.rye] managed = true diff --git a/src/openai/_extras/__init__.py b/src/openai/_extras/__init__.py index 864dac4171..692de248c0 100644 --- a/src/openai/_extras/__init__.py +++ b/src/openai/_extras/__init__.py @@ -1,2 +1,3 @@ from .numpy_proxy import numpy as numpy, has_numpy as has_numpy from .pandas_proxy import pandas as pandas +from .sounddevice_proxy import sounddevice as sounddevice diff --git a/src/openai/_extras/numpy_proxy.py b/src/openai/_extras/numpy_proxy.py index 27880bf132..8412965e44 100644 --- a/src/openai/_extras/numpy_proxy.py +++ b/src/openai/_extras/numpy_proxy.py @@ -10,7 +10,7 @@ import numpy as numpy -NUMPY_INSTRUCTIONS = format_instructions(library="numpy", extra="datalib") +NUMPY_INSTRUCTIONS = format_instructions(library="numpy", extra="audio") class NumpyProxy(LazyProxy[Any]): diff --git a/src/openai/_extras/sounddevice_proxy.py b/src/openai/_extras/sounddevice_proxy.py new file mode 100644 index 0000000000..0894782bd5 --- /dev/null +++ b/src/openai/_extras/sounddevice_proxy.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any +from typing_extensions import override + +from .._utils import LazyProxy +from ._common import MissingDependencyError, format_instructions + +if TYPE_CHECKING: + import sounddevice as sounddevice # type: ignore + + +SOUNDDEVICE_INSTRUCTIONS = format_instructions(library="sounddevice", extra="audio") + + +class SounddeviceProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + try: + import sounddevice # type: ignore + except ImportError as err: + raise MissingDependencyError(SOUNDDEVICE_INSTRUCTIONS) from err + + return sounddevice + + +if not TYPE_CHECKING: + sounddevice = SounddeviceProxy() diff --git a/src/openai/helpers/local_audio_player.py b/src/openai/helpers/local_audio_player.py index 46a16ce6bb..eed68aa21d 100644 --- a/src/openai/helpers/local_audio_player.py +++ b/src/openai/helpers/local_audio_player.py @@ -1,15 +1,18 @@ # mypy: ignore-errors +from __future__ import annotations + import queue import asyncio from typing import Any, Union, Callable, AsyncGenerator, cast - -import numpy as np -import sounddevice as sd # type: ignore -import numpy.typing as npt +from typing_extensions import TYPE_CHECKING from .. import _legacy_response +from .._extras import numpy as np, sounddevice as sd from .._response import StreamedBinaryAPIResponse, AsyncStreamedBinaryAPIResponse +if TYPE_CHECKING: + import numpy.typing as npt + SAMPLE_RATE = 24000 @@ -62,7 +65,7 @@ async def play( if input.dtype == np.int16 and self.dtype == np.float32: audio_content = (input.astype(np.float32) / 32767.0).reshape(-1, self.channels) elif input.dtype == np.float32: - audio_content = cast(npt.NDArray[np.float32], input) + audio_content = cast('npt.NDArray[np.float32]', input) else: raise ValueError(f"Unsupported dtype: {input.dtype}") else: diff --git a/src/openai/helpers/microphone.py b/src/openai/helpers/microphone.py index 18650909aa..62a6d8d8a9 100644 --- a/src/openai/helpers/microphone.py +++ b/src/openai/helpers/microphone.py @@ -1,16 +1,18 @@ # mypy: ignore-errors +from __future__ import annotations + import io import time import wave import asyncio from typing import Any, Type, Union, Generic, TypeVar, Callable, overload -from typing_extensions import Literal +from typing_extensions import TYPE_CHECKING, Literal -import numpy as np -import sounddevice as sd # type: ignore -import numpy.typing as npt +from .._types import FileTypes, FileContent +from .._extras import numpy as np, sounddevice as sd -from openai._types import FileTypes, FileContent +if TYPE_CHECKING: + import numpy.typing as npt SAMPLE_RATE = 24000 From 751d739eb3dd6c759537c809d61e789f57bef4bf Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 21 Mar 2025 13:33:45 +0000 Subject: [PATCH 838/914] chore(ci): run workflows on next too --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06eb10c5f0..d86fc0ea53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: pull_request: branches: - main + - next jobs: lint: From 044f192e41831f4b01fe47944a2248a554cfdd34 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 21 Mar 2025 13:41:53 +0000 Subject: [PATCH 839/914] fix(helpers/audio): remove duplicative module --- src/openai/helpers.py | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/openai/helpers.py diff --git a/src/openai/helpers.py b/src/openai/helpers.py deleted file mode 100644 index 1a10168a96..0000000000 --- a/src/openai/helpers.py +++ /dev/null @@ -1,4 +0,0 @@ -from .helpers.microphone import Microphone -from .helpers.local_audio_player import LocalAudioPlayer - -__all__ = ["LocalAudioPlayer", "Microphone"] From d55062a3c474dfa2aa5964e997304aac080a4dd1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:42:29 +0000 Subject: [PATCH 840/914] release: 1.68.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 42bc7e250e..2ec6ee54df 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.68.0" + ".": "1.68.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 78ae21f27f..d26a769784 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.68.1 (2025-03-21) + +Full Changelog: [v1.68.0...v1.68.1](https://github.com/openai/openai-python/compare/v1.68.0...v1.68.1) + +### Bug Fixes + +* **client:** remove duplicate types ([#2235](https://github.com/openai/openai-python/issues/2235)) ([063f7d0](https://github.com/openai/openai-python/commit/063f7d0684c350ca9d766e2cb150233a22a623c8)) +* **helpers/audio:** remove duplicative module ([f253d04](https://github.com/openai/openai-python/commit/f253d0415145f2c4904ea2e7b389d31d94e45a54)) +* **package:** make sounddevice and numpy optional dependencies ([8b04453](https://github.com/openai/openai-python/commit/8b04453f0483736c13f0209a9f8f3618bc0e86c9)) + + +### Chores + +* **ci:** run workflows on next too ([67f89d4](https://github.com/openai/openai-python/commit/67f89d478aab780d1481c9bf6682c6633e431137)) + ## 1.68.0 (2025-03-20) Full Changelog: [v1.67.0...v1.68.0](https://github.com/openai/openai-python/compare/v1.67.0...v1.68.0) diff --git a/pyproject.toml b/pyproject.toml index f34bf6bfa3..57871a46fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.68.0" +version = "1.68.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 23e4e7ffb7..1f00359eb1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.68.0" # x-release-please-version +__version__ = "1.68.1" # x-release-please-version From 257152bb1bbbce965ef37b9d349a0027742525f5 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 21 Mar 2025 14:41:50 +0000 Subject: [PATCH 841/914] refactor(package): rename audio extra to voice_helpers --- pyproject.toml | 2 +- src/openai/_extras/numpy_proxy.py | 2 +- src/openai/_extras/sounddevice_proxy.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 57871a46fe..e40060400a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ openai = "openai.cli:main" [project.optional-dependencies] realtime = ["websockets >= 13, < 15"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] -audio = ["sounddevice>=0.5.1", "numpy>=2.0.2"] +voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] [tool.rye] managed = true diff --git a/src/openai/_extras/numpy_proxy.py b/src/openai/_extras/numpy_proxy.py index 8412965e44..2b0669576e 100644 --- a/src/openai/_extras/numpy_proxy.py +++ b/src/openai/_extras/numpy_proxy.py @@ -10,7 +10,7 @@ import numpy as numpy -NUMPY_INSTRUCTIONS = format_instructions(library="numpy", extra="audio") +NUMPY_INSTRUCTIONS = format_instructions(library="numpy", extra="voice_helpers") class NumpyProxy(LazyProxy[Any]): diff --git a/src/openai/_extras/sounddevice_proxy.py b/src/openai/_extras/sounddevice_proxy.py index 0894782bd5..482d4c6874 100644 --- a/src/openai/_extras/sounddevice_proxy.py +++ b/src/openai/_extras/sounddevice_proxy.py @@ -10,7 +10,7 @@ import sounddevice as sounddevice # type: ignore -SOUNDDEVICE_INSTRUCTIONS = format_instructions(library="sounddevice", extra="audio") +SOUNDDEVICE_INSTRUCTIONS = format_instructions(library="sounddevice", extra="voice_helpers") class SounddeviceProxy(LazyProxy[Any]): From f66d2e6fdc51c4528c99bb25a8fbca6f9b9b872d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:42:25 +0000 Subject: [PATCH 842/914] release: 1.68.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2ec6ee54df..e280020f03 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.68.1" + ".": "1.68.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d26a769784..ee22cfe7fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.68.2 (2025-03-21) + +Full Changelog: [v1.68.1...v1.68.2](https://github.com/openai/openai-python/compare/v1.68.1...v1.68.2) + +### Refactors + +* **package:** rename audio extra to voice_helpers ([2dd6cb8](https://github.com/openai/openai-python/commit/2dd6cb87489fe12c5e45128f44d985c3f49aba1d)) + ## 1.68.1 (2025-03-21) Full Changelog: [v1.68.0...v1.68.1](https://github.com/openai/openai-python/compare/v1.68.0...v1.68.1) diff --git a/pyproject.toml b/pyproject.toml index e40060400a..b1917922cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.68.1" +version = "1.68.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 1f00359eb1..a29ce4e818 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.68.1" # x-release-please-version +__version__ = "1.68.2" # x-release-please-version From 2706bdd779d3fca61b68ebd956ecd8eb1db421ae Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:35:37 +0000 Subject: [PATCH 843/914] chore: fix typos (#2259) --- src/openai/_models.py | 2 +- src/openai/_utils/_transform.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index ff7c1f3392..fc4f201e4e 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -721,7 +721,7 @@ def add_request_id(obj: BaseModel, request_id: str | None) -> None: cast(Any, obj).__exclude_fields__ = {*(exclude_fields or {}), "_request_id", "__exclude_fields__"} -# our use of subclasssing here causes weirdness for type checkers, +# our use of subclassing here causes weirdness for type checkers, # so we just pretend that we don't subclass if TYPE_CHECKING: GenericModel = BaseModel diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 18afd9d8bd..7ac2e17fbb 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -126,7 +126,7 @@ def _get_annotated_type(type_: type) -> type | 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. + Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. """ annotated_type = _get_annotated_type(type_) if annotated_type is None: From a4b9f4075ebcf54a97489bc55995e308ccb62a1b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 17:32:51 +0000 Subject: [PATCH 844/914] chore: add hash of OpenAPI spec/config inputs to .stats.yml --- .stats.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.stats.yml b/.stats.yml index 2df281d34f..fe93204292 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,4 @@ configured_endpoints: 82 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5ad6884898c07591750dde560118baf7074a59aecd1f367f930c5e42b04e848a.yml +openapi_spec_hash: 0c255269b89767eae26f4d4dc22d3cbd +config_hash: d36e491b0afc4f79e3afad4b3c9bec70 From 2e73b5291ba6256714daea346c935dd01dbb6bb2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 20:32:36 +0000 Subject: [PATCH 845/914] chore(api): updates to supported Voice IDs (#2261) --- .stats.yml | 4 ++-- src/openai/resources/audio/speech.py | 16 ++++++++----- .../resources/beta/realtime/sessions.py | 16 +++++++++---- src/openai/resources/responses/input_items.py | 13 +++++++++- src/openai/resources/responses/responses.py | 24 +++++++++---------- .../types/audio/speech_create_params.py | 11 ++++++--- .../types/beta/realtime/realtime_response.py | 9 +++++-- .../beta/realtime/response_create_event.py | 8 +++++-- .../realtime/response_create_event_param.py | 6 +++-- src/openai/types/beta/realtime/session.py | 6 ++++- .../beta/realtime/session_create_params.py | 6 +++-- .../beta/realtime/session_create_response.py | 6 ++++- .../beta/realtime/session_update_event.py | 8 +++++-- .../realtime/session_update_event_param.py | 6 +++-- .../transcription_session_create_params.py | 7 +++--- .../realtime/transcription_session_update.py | 7 +++--- .../transcription_session_update_param.py | 7 +++--- .../types/chat/chat_completion_audio_param.py | 7 +++++- .../types/responses/input_item_list_params.py | 9 +++++++ src/openai/types/responses/response.py | 4 ++-- .../types/responses/response_create_params.py | 4 ++-- ...response_format_text_json_schema_config.py | 14 +++++------ ...se_format_text_json_schema_config_param.py | 14 +++++------ tests/api_resources/audio/test_speech.py | 16 ++++++------- .../beta/realtime/test_sessions.py | 4 ++-- tests/api_resources/chat/test_completions.py | 8 +++---- .../responses/test_input_items.py | 2 ++ 27 files changed, 158 insertions(+), 84 deletions(-) diff --git a/.stats.yml b/.stats.yml index fe93204292..4d1276a5e6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5ad6884898c07591750dde560118baf7074a59aecd1f367f930c5e42b04e848a.yml -openapi_spec_hash: 0c255269b89767eae26f4d4dc22d3cbd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6663c59193eb95b201e492de17dcbd5e126ba03d18ce66287a3e2c632ca56fe7.yml +openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e config_hash: d36e491b0afc4f79e3afad4b3c9bec70 diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 529e3a47ea..1ee53db9d5 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -53,7 +53,9 @@ def create( *, input: str, model: Union[str, SpeechModel], - voice: Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"], + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ], instructions: str | NotGiven = NOT_GIVEN, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, @@ -75,8 +77,8 @@ def create( `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, - `coral`, `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the - voices are available in the + `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and + `verse`. Previews of the voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). instructions: Control the voice of your generated audio with additional instructions. Does not @@ -142,7 +144,9 @@ async def create( *, input: str, model: Union[str, SpeechModel], - voice: Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"], + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ], instructions: str | NotGiven = NOT_GIVEN, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, @@ -164,8 +168,8 @@ async def create( `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, - `coral`, `echo`, `fable`, `onyx`, `nova`, `sage` and `shimmer`. Previews of the - voices are available in the + `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and + `verse`. Previews of the voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). instructions: Control the voice of your generated audio with additional instructions. Does not diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index 5884e54de2..3e1c956fe4 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -65,7 +65,10 @@ def create( tool_choice: str | NotGiven = NOT_GIVEN, tools: Iterable[session_create_params.Tool] | NotGiven = NOT_GIVEN, turn_detection: session_create_params.TurnDetection | NotGiven = NOT_GIVEN, - voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] | NotGiven = NOT_GIVEN, + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ] + | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -147,7 +150,8 @@ def create( voice: The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are - `alloy`, `ash`, `ballad`, `coral`, `echo` `sage`, `shimmer` and `verse`. + `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, + `shimmer`, and `verse`. extra_headers: Send extra headers @@ -227,7 +231,10 @@ async def create( tool_choice: str | NotGiven = NOT_GIVEN, tools: Iterable[session_create_params.Tool] | NotGiven = NOT_GIVEN, turn_detection: session_create_params.TurnDetection | NotGiven = NOT_GIVEN, - voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] | NotGiven = NOT_GIVEN, + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ] + | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -309,7 +316,8 @@ async def create( voice: The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are - `alloy`, `ash`, `ballad`, `coral`, `echo` `sage`, `shimmer` and `verse`. + `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, + `shimmer`, and `verse`. extra_headers: Send extra headers diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py index e341393cd1..ee0e628169 100644 --- a/src/openai/resources/responses/input_items.py +++ b/src/openai/resources/responses/input_items.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, cast +from typing import Any, List, cast from typing_extensions import Literal import httpx @@ -17,6 +17,7 @@ from ..._base_client import AsyncPaginator, make_request_options from ...types.responses import input_item_list_params from ...types.responses.response_item import ResponseItem +from ...types.responses.response_includable import ResponseIncludable __all__ = ["InputItems", "AsyncInputItems"] @@ -47,6 +48,7 @@ def list( *, after: str | NotGiven = NOT_GIVEN, before: str | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -64,6 +66,9 @@ def list( before: An item ID to list items before, used in pagination. + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -94,6 +99,7 @@ def list( { "after": after, "before": before, + "include": include, "limit": limit, "order": order, }, @@ -130,6 +136,7 @@ def list( *, after: str | NotGiven = NOT_GIVEN, before: str | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -147,6 +154,9 @@ def list( before: An item ID to list items before, used in pagination. + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. @@ -177,6 +187,7 @@ def list( { "after": after, "before": before, + "include": include, "limit": limit, "order": order, }, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 668f4db80a..29ed3de42a 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -149,8 +149,8 @@ def create( context. When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. max_output_tokens: An upper bound for the number of tokens that can be generated for a response, including visible output tokens and @@ -321,8 +321,8 @@ def create( context. When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. max_output_tokens: An upper bound for the number of tokens that can be generated for a response, including visible output tokens and @@ -486,8 +486,8 @@ def create( context. When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. max_output_tokens: An upper bound for the number of tokens that can be generated for a response, including visible output tokens and @@ -961,8 +961,8 @@ async def create( context. When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. max_output_tokens: An upper bound for the number of tokens that can be generated for a response, including visible output tokens and @@ -1133,8 +1133,8 @@ async def create( context. When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. max_output_tokens: An upper bound for the number of tokens that can be generated for a response, including visible output tokens and @@ -1298,8 +1298,8 @@ async def create( context. When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. max_output_tokens: An upper bound for the number of tokens that can be generated for a response, including visible output tokens and diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 958680710b..a4fc020532 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -20,11 +20,16 @@ class SpeechCreateParams(TypedDict, total=False): `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. """ - voice: Required[Literal["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"]] + voice: Required[ + Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ] + ] """The voice to use when generating the audio. - Supported voices are `alloy`, `ash`, `coral`, `echo`, `fable`, `onyx`, `nova`, - `sage` and `shimmer`. Previews of the voices are available in the + Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, + `nova`, `sage`, `shimmer`, and `verse`. Previews of the voices are available in + the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). """ diff --git a/src/openai/types/beta/realtime/realtime_response.py b/src/openai/types/beta/realtime/realtime_response.py index 4c3c83d666..8ecfb91c31 100644 --- a/src/openai/types/beta/realtime/realtime_response.py +++ b/src/openai/types/beta/realtime/realtime_response.py @@ -80,8 +80,13 @@ class RealtimeResponse(BaseModel): will become the input for later turns. """ - voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + voice: Union[ + str, + Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], + None, + ] = None """ The voice the model used to respond. Current voice options are `alloy`, `ash`, - `ballad`, `coral`, `echo` `sage`, `shimmer` and `verse`. + `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and + `verse`. """ diff --git a/src/openai/types/beta/realtime/response_create_event.py b/src/openai/types/beta/realtime/response_create_event.py index d6c5fda926..3b8a6de8df 100644 --- a/src/openai/types/beta/realtime/response_create_event.py +++ b/src/openai/types/beta/realtime/response_create_event.py @@ -101,12 +101,16 @@ class Response(BaseModel): tools: Optional[List[ResponseTool]] = None """Tools (functions) available to the model.""" - voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + voice: Union[ + str, + Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], + None, + ] = None """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo` `sage`, `shimmer` and `verse`. + `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/response_create_event_param.py b/src/openai/types/beta/realtime/response_create_event_param.py index c02fe1b34e..c569d507a0 100644 --- a/src/openai/types/beta/realtime/response_create_event_param.py +++ b/src/openai/types/beta/realtime/response_create_event_param.py @@ -102,12 +102,14 @@ class Response(TypedDict, total=False): tools: Iterable[ResponseTool] """Tools (functions) available to the model.""" - voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ] """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo` `sage`, `shimmer` and `verse`. + `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py index 3ed53ff5f8..6acde57f09 100644 --- a/src/openai/types/beta/realtime/session.py +++ b/src/openai/types/beta/realtime/session.py @@ -218,7 +218,11 @@ class Session(BaseModel): natural conversations, but may have a higher latency. """ - voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + voice: Union[ + str, + Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], + None, + ] = None """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py index fe4a1c8636..eadee29b28 100644 --- a/src/openai/types/beta/realtime/session_create_params.py +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -113,12 +113,14 @@ class SessionCreateParams(TypedDict, total=False): natural conversations, but may have a higher latency. """ - voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ] """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo` `sage`, `shimmer` and `verse`. + `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/session_create_response.py b/src/openai/types/beta/realtime/session_create_response.py index c26e62bef1..3cc8ca15ce 100644 --- a/src/openai/types/beta/realtime/session_create_response.py +++ b/src/openai/types/beta/realtime/session_create_response.py @@ -141,7 +141,11 @@ class SessionCreateResponse(BaseModel): speech. """ - voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + voice: Union[ + str, + Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], + None, + ] = None """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py index 00180f593d..ba34b0260b 100644 --- a/src/openai/types/beta/realtime/session_update_event.py +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -222,12 +222,16 @@ class Session(BaseModel): natural conversations, but may have a higher latency. """ - voice: Optional[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] = None + voice: Union[ + str, + Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], + None, + ] = None """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo` `sage`, `shimmer` and `verse`. + `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py index b8bce8fbd0..0984d39e91 100644 --- a/src/openai/types/beta/realtime/session_update_event_param.py +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -220,12 +220,14 @@ class Session(TypedDict, total=False): natural conversations, but may have a higher latency. """ - voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"] + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ] """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo` `sage`, `shimmer` and `verse`. + `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/transcription_session_create_params.py b/src/openai/types/beta/realtime/transcription_session_create_params.py index 4066dc4c5d..1cf511f0b5 100644 --- a/src/openai/types/beta/realtime/transcription_session_create_params.py +++ b/src/openai/types/beta/realtime/transcription_session_create_params.py @@ -96,9 +96,10 @@ class InputAudioTranscription(TypedDict, total=False): class TurnDetection(TypedDict, total=False): create_response: bool - """ - Whether or not to automatically generate a response when a VAD stop event + """Whether or not to automatically generate a response when a VAD stop event occurs. + + Not available for transcription sessions. """ eagerness: Literal["low", "medium", "high", "auto"] @@ -113,7 +114,7 @@ class TurnDetection(TypedDict, total=False): """ Whether or not to automatically interrupt any ongoing response with output to the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. + occurs. Not available for transcription sessions. """ prefix_padding_ms: int diff --git a/src/openai/types/beta/realtime/transcription_session_update.py b/src/openai/types/beta/realtime/transcription_session_update.py index 043ac02e07..c3e8f011c8 100644 --- a/src/openai/types/beta/realtime/transcription_session_update.py +++ b/src/openai/types/beta/realtime/transcription_session_update.py @@ -50,9 +50,10 @@ class SessionInputAudioTranscription(BaseModel): class SessionTurnDetection(BaseModel): create_response: Optional[bool] = None - """ - Whether or not to automatically generate a response when a VAD stop event + """Whether or not to automatically generate a response when a VAD stop event occurs. + + Not available for transcription sessions. """ eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None @@ -67,7 +68,7 @@ class SessionTurnDetection(BaseModel): """ Whether or not to automatically interrupt any ongoing response with output to the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. + occurs. Not available for transcription sessions. """ prefix_padding_ms: Optional[int] = None diff --git a/src/openai/types/beta/realtime/transcription_session_update_param.py b/src/openai/types/beta/realtime/transcription_session_update_param.py index 997a36d77b..549c49011b 100644 --- a/src/openai/types/beta/realtime/transcription_session_update_param.py +++ b/src/openai/types/beta/realtime/transcription_session_update_param.py @@ -50,9 +50,10 @@ class SessionInputAudioTranscription(TypedDict, total=False): class SessionTurnDetection(TypedDict, total=False): create_response: bool - """ - Whether or not to automatically generate a response when a VAD stop event + """Whether or not to automatically generate a response when a VAD stop event occurs. + + Not available for transcription sessions. """ eagerness: Literal["low", "medium", "high", "auto"] @@ -67,7 +68,7 @@ class SessionTurnDetection(TypedDict, total=False): """ Whether or not to automatically interrupt any ongoing response with output to the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. + occurs. Not available for transcription sessions. """ prefix_padding_ms: int diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index 6321417826..b902f2667f 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Union from typing_extensions import Literal, Required, TypedDict __all__ = ["ChatCompletionAudioParam"] @@ -14,7 +15,11 @@ class ChatCompletionAudioParam(TypedDict, total=False): Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. """ - voice: Required[Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] + voice: Required[ + Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] + ] + ] """The voice the model uses to respond. Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, and diff --git a/src/openai/types/responses/input_item_list_params.py b/src/openai/types/responses/input_item_list_params.py index e0b71f1ac5..6555d26788 100644 --- a/src/openai/types/responses/input_item_list_params.py +++ b/src/openai/types/responses/input_item_list_params.py @@ -2,8 +2,11 @@ from __future__ import annotations +from typing import List from typing_extensions import Literal, TypedDict +from .response_includable import ResponseIncludable + __all__ = ["InputItemListParams"] @@ -14,6 +17,12 @@ class InputItemListParams(TypedDict, total=False): before: str """An item ID to list items before, used in pagination.""" + include: List[ResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for Response creation above for more information. + """ + limit: int """A limit on the number of objects to be returned. diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 1bedf80889..8cd1e01144 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -47,8 +47,8 @@ class Response(BaseModel): context. When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. """ metadata: Optional[Metadata] = None diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 651050c50d..ed82e678e5 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -64,8 +64,8 @@ class ResponseCreateParamsBase(TypedDict, total=False): context. When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. """ max_output_tokens: Optional[int] diff --git a/src/openai/types/responses/response_format_text_json_schema_config.py b/src/openai/types/responses/response_format_text_json_schema_config.py index 3cf066370f..001fcf5bab 100644 --- a/src/openai/types/responses/response_format_text_json_schema_config.py +++ b/src/openai/types/responses/response_format_text_json_schema_config.py @@ -11,6 +11,13 @@ class ResponseFormatTextJSONSchemaConfig(BaseModel): + name: str + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + schema_: Dict[str, object] = FieldInfo(alias="schema") """ The schema for the response format, described as a JSON Schema object. Learn how @@ -26,13 +33,6 @@ class ResponseFormatTextJSONSchemaConfig(BaseModel): how to respond in the format. """ - name: Optional[str] = None - """The name of the response format. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - strict: Optional[bool] = None """ Whether to enable strict schema adherence when generating the output. If set to diff --git a/src/openai/types/responses/response_format_text_json_schema_config_param.py b/src/openai/types/responses/response_format_text_json_schema_config_param.py index 211c5d1eff..f293a80c5a 100644 --- a/src/openai/types/responses/response_format_text_json_schema_config_param.py +++ b/src/openai/types/responses/response_format_text_json_schema_config_param.py @@ -9,6 +9,13 @@ class ResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False): + name: Required[str] + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + schema: Required[Dict[str, object]] """ The schema for the response format, described as a JSON Schema object. Learn how @@ -24,13 +31,6 @@ class ResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False): how to respond in the format. """ - name: str - """The name of the response format. - - Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length - of 64. - """ - strict: Optional[bool] """ Whether to enable strict schema adherence when generating the output. If set to diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 808f6ef66c..ce9ed59ce3 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -28,7 +28,7 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: speech = client.audio.speech.create( input="string", model="string", - voice="alloy", + voice="ash", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} @@ -40,7 +40,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou speech = client.audio.speech.create( input="string", model="string", - voice="alloy", + voice="ash", instructions="instructions", response_format="mp3", speed=0.25, @@ -56,7 +56,7 @@ def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> No response = client.audio.speech.with_raw_response.create( input="string", model="string", - voice="alloy", + voice="ash", ) assert response.is_closed is True @@ -71,7 +71,7 @@ def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) with client.audio.speech.with_streaming_response.create( input="string", model="string", - voice="alloy", + voice="ash", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -92,7 +92,7 @@ async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRo speech = await async_client.audio.speech.create( input="string", model="string", - voice="alloy", + voice="ash", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} @@ -104,7 +104,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re speech = await async_client.audio.speech.create( input="string", model="string", - voice="alloy", + voice="ash", instructions="instructions", response_format="mp3", speed=0.25, @@ -120,7 +120,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: response = await async_client.audio.speech.with_raw_response.create( input="string", model="string", - voice="alloy", + voice="ash", ) assert response.is_closed is True @@ -135,7 +135,7 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_ async with async_client.audio.speech.with_streaming_response.create( input="string", model="string", - voice="alloy", + voice="ash", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/beta/realtime/test_sessions.py b/tests/api_resources/beta/realtime/test_sessions.py index c0a426a417..f432b7d277 100644 --- a/tests/api_resources/beta/realtime/test_sessions.py +++ b/tests/api_resources/beta/realtime/test_sessions.py @@ -56,7 +56,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "threshold": 0, "type": "server_vad", }, - voice="alloy", + voice="ash", ) assert_matches_type(SessionCreateResponse, session, path=["response"]) @@ -123,7 +123,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "threshold": 0, "type": "server_vad", }, - voice="alloy", + voice="ash", ) assert_matches_type(SessionCreateResponse, session, path=["response"]) diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index d4ccc494dd..aaef82e8c5 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -48,7 +48,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: model="gpt-4o", audio={ "format": "wav", - "voice": "alloy", + "voice": "ash", }, frequency_penalty=-2, function_call="none", @@ -175,7 +175,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: stream=True, audio={ "format": "wav", - "voice": "alloy", + "voice": "ash", }, frequency_penalty=-2, function_call="none", @@ -475,7 +475,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn model="gpt-4o", audio={ "format": "wav", - "voice": "alloy", + "voice": "ash", }, frequency_penalty=-2, function_call="none", @@ -602,7 +602,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn stream=True, audio={ "format": "wav", - "voice": "alloy", + "voice": "ash", }, frequency_penalty=-2, function_call="none", diff --git a/tests/api_resources/responses/test_input_items.py b/tests/api_resources/responses/test_input_items.py index 77a156b5ac..2528943c06 100644 --- a/tests/api_resources/responses/test_input_items.py +++ b/tests/api_resources/responses/test_input_items.py @@ -31,6 +31,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: response_id="response_id", after="after", before="before", + include=["file_search_call.results"], limit=0, order="asc", ) @@ -84,6 +85,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N response_id="response_id", after="after", before="before", + include=["file_search_call.results"], limit=0, order="asc", ) From 8677d3ca5350c7ad07a66dcde78152bccb1655c0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:07:22 +0000 Subject: [PATCH 846/914] feat(api): add `get /chat/completions` endpoint --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 4d1276a5e6..1e1104a062 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6663c59193eb95b201e492de17dcbd5e126ba03d18ce66287a3e2c632ca56fe7.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: d36e491b0afc4f79e3afad4b3c9bec70 +config_hash: 9351ea829c2b41da3b48a38c934c92ee From a6393219a6d4bd1fbcafdabfe757de093a87e2af Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 27 Mar 2025 17:21:23 +0000 Subject: [PATCH 847/914] fix(audio): correctly parse transcription stream events --- src/openai/_streaming.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 9cb72ffe17..641c3a7a72 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -59,7 +59,7 @@ def __stream__(self) -> Iterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None or sse.event.startswith("response."): + if sse.event is None or sse.event.startswith("response.") or sse.event.startswith('transcript.'): data = sse.json() if is_mapping(data) and data.get("error"): message = None @@ -161,7 +161,7 @@ async def __stream__(self) -> AsyncIterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None or sse.event.startswith("response."): + if sse.event is None or sse.event.startswith("response.") or sse.event.startswith('transcript.'): data = sse.json() if is_mapping(data) and data.get("error"): message = None From 46ed48e793ff9f5a088918ce9b1ad7bef13b9b37 Mon Sep 17 00:00:00 2001 From: Lucas Grzegorczyk Date: Thu, 27 Mar 2025 18:30:19 +0100 Subject: [PATCH 848/914] Remove stray responses.py.orig file (#2258) --- .../resources/responses/responses.py.orig | 1796 ----------------- 1 file changed, 1796 deletions(-) delete mode 100644 src/openai/resources/responses/responses.py.orig diff --git a/src/openai/resources/responses/responses.py.orig b/src/openai/resources/responses/responses.py.orig deleted file mode 100644 index dec4c19367..0000000000 --- a/src/openai/resources/responses/responses.py.orig +++ /dev/null @@ -1,1796 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Any, List, Type, Union, Iterable, Optional, cast -from functools import partial -from typing_extensions import Literal, overload - -import httpx - -from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven -from ..._utils import ( - is_given, - required_args, - maybe_transform, - async_maybe_transform, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .input_items import ( - InputItems, - AsyncInputItems, - InputItemsWithRawResponse, - AsyncInputItemsWithRawResponse, - InputItemsWithStreamingResponse, - AsyncInputItemsWithStreamingResponse, -) -from ..._streaming import Stream, AsyncStream -from ...lib._tools import PydanticFunctionTool, ResponsesPydanticFunctionTool -from ..._base_client import make_request_options -from ...types.responses import response_create_params, response_retrieve_params -<<<<<<< HEAD -from ...lib._parsing._responses import ( - TextFormatT, - parse_response, - type_to_text_format_param as _type_to_text_format_param, -) -from ...types.shared.chat_model import ChatModel -||||||| parent of 001707b8 (feat(api): o1-pro now available through the API (#2228)) -from ...types.shared.chat_model import ChatModel -======= ->>>>>>> 001707b8 (feat(api): o1-pro now available through the API (#2228)) -from ...types.responses.response import Response -from ...types.responses.tool_param import ToolParam, ParseableToolParam -from ...types.shared_params.metadata import Metadata -from ...types.shared_params.reasoning import Reasoning -from ...types.responses.parsed_response import ParsedResponse -from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager -from ...types.responses.response_includable import ResponseIncludable -from ...types.shared_params.responses_model import ResponsesModel -from ...types.responses.response_input_param import ResponseInputParam -from ...types.responses.response_stream_event import ResponseStreamEvent -from ...types.responses.response_text_config_param import ResponseTextConfigParam - -__all__ = ["Responses", "AsyncResponses"] - - -class Responses(SyncAPIResource): - @cached_property - def input_items(self) -> InputItems: - return InputItems(self._client) - - @cached_property - def with_raw_response(self) -> ResponsesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers - """ - return ResponsesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ResponsesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/openai/openai-python#with_streaming_response - """ - return ResponsesWithStreamingResponse(self) - - @overload - def create( - self, - *, - input: Union[str, ResponseInputParam], - model: ResponsesModel, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Response: - """Creates a model response. - - Provide - [text](https://platform.openai.com/docs/guides/text) or - [image](https://platform.openai.com/docs/guides/images) inputs to generate - [text](https://platform.openai.com/docs/guides/text) or - [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have - the model call your own - [custom code](https://platform.openai.com/docs/guides/function-calling) or use - built-in [tools](https://platform.openai.com/docs/guides/tools) like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search) to use - your own data as input for the model's response. - - Args: - input: Text, image, or file inputs to the model, used to generate a response. - - Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Image inputs](https://platform.openai.com/docs/guides/images) - - [File inputs](https://platform.openai.com/docs/guides/pdf-files) - - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - - [Function calling](https://platform.openai.com/docs/guides/function-calling) - - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a - wide range of models with different capabilities, performance characteristics, - and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - - include: Specify additional output data to include in the model response. Currently - supported values are: - - - `file_search_call.results`: Include the search results of the file search tool - call. - - `message.input_image.image_url`: Include image urls from the input message. - - `computer_call_output.output.image_url`: Include image urls from the computer - call output. - - instructions: Inserts a system (or developer) message as the first item in the model's - context. - - When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. - - max_output_tokens: An upper bound for the number of tokens that can be generated for a response, - including visible output tokens and - [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). - - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - - parallel_tool_calls: Whether to allow the model to run tool calls in parallel. - - previous_response_id: The unique ID of the previous response to the model. Use this to create - multi-turn conversations. Learn more about - [conversation state](https://platform.openai.com/docs/guides/conversation-state). - - reasoning: **o-series models only** - - Configuration options for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). - - store: Whether to store the generated model response for later retrieval via API. - - stream: If set to true, the model response data will be streamed to the client as it is - generated using - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). - See the - [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) - for more information. - - temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will - make the output more random, while lower values like 0.2 will make it more - focused and deterministic. We generally recommend altering this or `top_p` but - not both. - - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - - tool_choice: How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. - - tools: An array of tools the model may call while generating a response. You can - specify which tool to use by setting the `tool_choice` parameter. - - The two categories of tools you can provide the model are: - - - **Built-in tools**: Tools that are provided by OpenAI that extend the model's - capabilities, like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search). - Learn more about - [built-in tools](https://platform.openai.com/docs/guides/tools). - - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about - [function calling](https://platform.openai.com/docs/guides/function-calling). - - top_p: An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - - truncation: The truncation strategy to use for the model response. - - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size - for a model, the request will fail with a 400 error. - - user: A unique identifier representing your end-user, which can help OpenAI to monitor - and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def create( - self, - *, - input: Union[str, ResponseInputParam], - model: ResponsesModel, - stream: Literal[True], - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Stream[ResponseStreamEvent]: - """Creates a model response. - - Provide - [text](https://platform.openai.com/docs/guides/text) or - [image](https://platform.openai.com/docs/guides/images) inputs to generate - [text](https://platform.openai.com/docs/guides/text) or - [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have - the model call your own - [custom code](https://platform.openai.com/docs/guides/function-calling) or use - built-in [tools](https://platform.openai.com/docs/guides/tools) like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search) to use - your own data as input for the model's response. - - Args: - input: Text, image, or file inputs to the model, used to generate a response. - - Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Image inputs](https://platform.openai.com/docs/guides/images) - - [File inputs](https://platform.openai.com/docs/guides/pdf-files) - - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - - [Function calling](https://platform.openai.com/docs/guides/function-calling) - - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a - wide range of models with different capabilities, performance characteristics, - and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - - stream: If set to true, the model response data will be streamed to the client as it is - generated using - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). - See the - [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) - for more information. - - include: Specify additional output data to include in the model response. Currently - supported values are: - - - `file_search_call.results`: Include the search results of the file search tool - call. - - `message.input_image.image_url`: Include image urls from the input message. - - `computer_call_output.output.image_url`: Include image urls from the computer - call output. - - instructions: Inserts a system (or developer) message as the first item in the model's - context. - - When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. - - max_output_tokens: An upper bound for the number of tokens that can be generated for a response, - including visible output tokens and - [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). - - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - - parallel_tool_calls: Whether to allow the model to run tool calls in parallel. - - previous_response_id: The unique ID of the previous response to the model. Use this to create - multi-turn conversations. Learn more about - [conversation state](https://platform.openai.com/docs/guides/conversation-state). - - reasoning: **o-series models only** - - Configuration options for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). - - store: Whether to store the generated model response for later retrieval via API. - - temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will - make the output more random, while lower values like 0.2 will make it more - focused and deterministic. We generally recommend altering this or `top_p` but - not both. - - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - - tool_choice: How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. - - tools: An array of tools the model may call while generating a response. You can - specify which tool to use by setting the `tool_choice` parameter. - - The two categories of tools you can provide the model are: - - - **Built-in tools**: Tools that are provided by OpenAI that extend the model's - capabilities, like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search). - Learn more about - [built-in tools](https://platform.openai.com/docs/guides/tools). - - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about - [function calling](https://platform.openai.com/docs/guides/function-calling). - - top_p: An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - - truncation: The truncation strategy to use for the model response. - - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size - for a model, the request will fail with a 400 error. - - user: A unique identifier representing your end-user, which can help OpenAI to monitor - and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def create( - self, - *, - input: Union[str, ResponseInputParam], - model: ResponsesModel, - stream: bool, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Response | Stream[ResponseStreamEvent]: - """Creates a model response. - - Provide - [text](https://platform.openai.com/docs/guides/text) or - [image](https://platform.openai.com/docs/guides/images) inputs to generate - [text](https://platform.openai.com/docs/guides/text) or - [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have - the model call your own - [custom code](https://platform.openai.com/docs/guides/function-calling) or use - built-in [tools](https://platform.openai.com/docs/guides/tools) like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search) to use - your own data as input for the model's response. - - Args: - input: Text, image, or file inputs to the model, used to generate a response. - - Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Image inputs](https://platform.openai.com/docs/guides/images) - - [File inputs](https://platform.openai.com/docs/guides/pdf-files) - - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - - [Function calling](https://platform.openai.com/docs/guides/function-calling) - - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a - wide range of models with different capabilities, performance characteristics, - and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - - stream: If set to true, the model response data will be streamed to the client as it is - generated using - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). - See the - [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) - for more information. - - include: Specify additional output data to include in the model response. Currently - supported values are: - - - `file_search_call.results`: Include the search results of the file search tool - call. - - `message.input_image.image_url`: Include image urls from the input message. - - `computer_call_output.output.image_url`: Include image urls from the computer - call output. - - instructions: Inserts a system (or developer) message as the first item in the model's - context. - - When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. - - max_output_tokens: An upper bound for the number of tokens that can be generated for a response, - including visible output tokens and - [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). - - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - - parallel_tool_calls: Whether to allow the model to run tool calls in parallel. - - previous_response_id: The unique ID of the previous response to the model. Use this to create - multi-turn conversations. Learn more about - [conversation state](https://platform.openai.com/docs/guides/conversation-state). - - reasoning: **o-series models only** - - Configuration options for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). - - store: Whether to store the generated model response for later retrieval via API. - - temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will - make the output more random, while lower values like 0.2 will make it more - focused and deterministic. We generally recommend altering this or `top_p` but - not both. - - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - - tool_choice: How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. - - tools: An array of tools the model may call while generating a response. You can - specify which tool to use by setting the `tool_choice` parameter. - - The two categories of tools you can provide the model are: - - - **Built-in tools**: Tools that are provided by OpenAI that extend the model's - capabilities, like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search). - Learn more about - [built-in tools](https://platform.openai.com/docs/guides/tools). - - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about - [function calling](https://platform.openai.com/docs/guides/function-calling). - - top_p: An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - - truncation: The truncation strategy to use for the model response. - - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size - for a model, the request will fail with a 400 error. - - user: A unique identifier representing your end-user, which can help OpenAI to monitor - and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @required_args(["input", "model"], ["input", "model", "stream"]) - def create( - self, - *, - input: Union[str, ResponseInputParam], - model: ResponsesModel, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Response | Stream[ResponseStreamEvent]: - return self._post( - "/responses", - body=maybe_transform( - { - "input": input, - "model": model, - "include": include, - "instructions": instructions, - "max_output_tokens": max_output_tokens, - "metadata": metadata, - "parallel_tool_calls": parallel_tool_calls, - "previous_response_id": previous_response_id, - "reasoning": reasoning, - "store": store, - "stream": stream, - "temperature": temperature, - "text": text, - "tool_choice": tool_choice, - "tools": tools, - "top_p": top_p, - "truncation": truncation, - "user": user, - }, - response_create_params.ResponseCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Response, - stream=stream or False, - stream_cls=Stream[ResponseStreamEvent], - ) - - def stream( - self, - *, - input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ResponseStreamManager[TextFormatT]: - if is_given(text_format): - if not text: - text = {} - - if "format" in text: - raise TypeError("Cannot mix and match text.format with text_format") - - text["format"] = _type_to_text_format_param(text_format) - - tools = _make_tools(tools) - - api_request: partial[Stream[ResponseStreamEvent]] = partial( - self.create, - input=input, - model=model, - tools=tools, - include=include, - instructions=instructions, - max_output_tokens=max_output_tokens, - metadata=metadata, - parallel_tool_calls=parallel_tool_calls, - previous_response_id=previous_response_id, - store=store, - stream=True, - temperature=temperature, - text=text, - tool_choice=tool_choice, - reasoning=reasoning, - top_p=top_p, - truncation=truncation, - user=user, - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ) - - return ResponseStreamManager( - api_request, - text_format=text_format, - input_tools=tools, - ) - - def parse( - self, - *, - input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ParsedResponse[TextFormatT]: - if is_given(text_format): - if not text: - text = {} - - if "format" in text: - raise TypeError("Cannot mix and match text.format with text_format") - - text["format"] = _type_to_text_format_param(text_format) - - tools = _make_tools(tools) - - def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: - return parse_response( - input_tools=tools, - text_format=text_format, - response=raw_response, - ) - - return self._post( - "/responses", - body=maybe_transform( - { - "input": input, - "model": model, - "include": include, - "instructions": instructions, - "max_output_tokens": max_output_tokens, - "metadata": metadata, - "parallel_tool_calls": parallel_tool_calls, - "previous_response_id": previous_response_id, - "reasoning": reasoning, - "store": store, - "stream": stream, - "temperature": temperature, - "text": text, - "tool_choice": tool_choice, - "tools": tools, - "top_p": top_p, - "truncation": truncation, - "user": user, - }, - response_create_params.ResponseCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=parser, - ), - # we turn the `Response` instance into a `ParsedResponse` - # in the `parser` function above - cast_to=cast(Type[ParsedResponse[TextFormatT]], Response), - ) - - def retrieve( - self, - response_id: str, - *, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Response: - """ - Retrieves a model response with the given ID. - - Args: - include: Additional fields to include in the response. See the `include` parameter for - Response creation above for more information. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not response_id: - raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") - return self._get( - f"/responses/{response_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"include": include}, response_retrieve_params.ResponseRetrieveParams), - ), - cast_to=Response, - ) - - def delete( - self, - response_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> None: - """ - Deletes a model response with the given ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not response_id: - raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/responses/{response_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - -class AsyncResponses(AsyncAPIResource): - @cached_property - def input_items(self) -> AsyncInputItems: - return AsyncInputItems(self._client) - - @cached_property - def with_raw_response(self) -> AsyncResponsesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers - """ - return AsyncResponsesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/openai/openai-python#with_streaming_response - """ - return AsyncResponsesWithStreamingResponse(self) - - @overload - async def create( - self, - *, - input: Union[str, ResponseInputParam], - model: ResponsesModel, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Response: - """Creates a model response. - - Provide - [text](https://platform.openai.com/docs/guides/text) or - [image](https://platform.openai.com/docs/guides/images) inputs to generate - [text](https://platform.openai.com/docs/guides/text) or - [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have - the model call your own - [custom code](https://platform.openai.com/docs/guides/function-calling) or use - built-in [tools](https://platform.openai.com/docs/guides/tools) like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search) to use - your own data as input for the model's response. - - Args: - input: Text, image, or file inputs to the model, used to generate a response. - - Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Image inputs](https://platform.openai.com/docs/guides/images) - - [File inputs](https://platform.openai.com/docs/guides/pdf-files) - - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - - [Function calling](https://platform.openai.com/docs/guides/function-calling) - - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a - wide range of models with different capabilities, performance characteristics, - and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - - include: Specify additional output data to include in the model response. Currently - supported values are: - - - `file_search_call.results`: Include the search results of the file search tool - call. - - `message.input_image.image_url`: Include image urls from the input message. - - `computer_call_output.output.image_url`: Include image urls from the computer - call output. - - instructions: Inserts a system (or developer) message as the first item in the model's - context. - - When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. - - max_output_tokens: An upper bound for the number of tokens that can be generated for a response, - including visible output tokens and - [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). - - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - - parallel_tool_calls: Whether to allow the model to run tool calls in parallel. - - previous_response_id: The unique ID of the previous response to the model. Use this to create - multi-turn conversations. Learn more about - [conversation state](https://platform.openai.com/docs/guides/conversation-state). - - reasoning: **o-series models only** - - Configuration options for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). - - store: Whether to store the generated model response for later retrieval via API. - - stream: If set to true, the model response data will be streamed to the client as it is - generated using - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). - See the - [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) - for more information. - - temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will - make the output more random, while lower values like 0.2 will make it more - focused and deterministic. We generally recommend altering this or `top_p` but - not both. - - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - - tool_choice: How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. - - tools: An array of tools the model may call while generating a response. You can - specify which tool to use by setting the `tool_choice` parameter. - - The two categories of tools you can provide the model are: - - - **Built-in tools**: Tools that are provided by OpenAI that extend the model's - capabilities, like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search). - Learn more about - [built-in tools](https://platform.openai.com/docs/guides/tools). - - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about - [function calling](https://platform.openai.com/docs/guides/function-calling). - - top_p: An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - - truncation: The truncation strategy to use for the model response. - - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size - for a model, the request will fail with a 400 error. - - user: A unique identifier representing your end-user, which can help OpenAI to monitor - and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def create( - self, - *, - input: Union[str, ResponseInputParam], - model: ResponsesModel, - stream: Literal[True], - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncStream[ResponseStreamEvent]: - """Creates a model response. - - Provide - [text](https://platform.openai.com/docs/guides/text) or - [image](https://platform.openai.com/docs/guides/images) inputs to generate - [text](https://platform.openai.com/docs/guides/text) or - [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have - the model call your own - [custom code](https://platform.openai.com/docs/guides/function-calling) or use - built-in [tools](https://platform.openai.com/docs/guides/tools) like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search) to use - your own data as input for the model's response. - - Args: - input: Text, image, or file inputs to the model, used to generate a response. - - Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Image inputs](https://platform.openai.com/docs/guides/images) - - [File inputs](https://platform.openai.com/docs/guides/pdf-files) - - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - - [Function calling](https://platform.openai.com/docs/guides/function-calling) - - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a - wide range of models with different capabilities, performance characteristics, - and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - - stream: If set to true, the model response data will be streamed to the client as it is - generated using - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). - See the - [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) - for more information. - - include: Specify additional output data to include in the model response. Currently - supported values are: - - - `file_search_call.results`: Include the search results of the file search tool - call. - - `message.input_image.image_url`: Include image urls from the input message. - - `computer_call_output.output.image_url`: Include image urls from the computer - call output. - - instructions: Inserts a system (or developer) message as the first item in the model's - context. - - When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. - - max_output_tokens: An upper bound for the number of tokens that can be generated for a response, - including visible output tokens and - [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). - - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - - parallel_tool_calls: Whether to allow the model to run tool calls in parallel. - - previous_response_id: The unique ID of the previous response to the model. Use this to create - multi-turn conversations. Learn more about - [conversation state](https://platform.openai.com/docs/guides/conversation-state). - - reasoning: **o-series models only** - - Configuration options for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). - - store: Whether to store the generated model response for later retrieval via API. - - temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will - make the output more random, while lower values like 0.2 will make it more - focused and deterministic. We generally recommend altering this or `top_p` but - not both. - - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - - tool_choice: How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. - - tools: An array of tools the model may call while generating a response. You can - specify which tool to use by setting the `tool_choice` parameter. - - The two categories of tools you can provide the model are: - - - **Built-in tools**: Tools that are provided by OpenAI that extend the model's - capabilities, like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search). - Learn more about - [built-in tools](https://platform.openai.com/docs/guides/tools). - - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about - [function calling](https://platform.openai.com/docs/guides/function-calling). - - top_p: An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - - truncation: The truncation strategy to use for the model response. - - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size - for a model, the request will fail with a 400 error. - - user: A unique identifier representing your end-user, which can help OpenAI to monitor - and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def create( - self, - *, - input: Union[str, ResponseInputParam], - model: ResponsesModel, - stream: bool, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Response | AsyncStream[ResponseStreamEvent]: - """Creates a model response. - - Provide - [text](https://platform.openai.com/docs/guides/text) or - [image](https://platform.openai.com/docs/guides/images) inputs to generate - [text](https://platform.openai.com/docs/guides/text) or - [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have - the model call your own - [custom code](https://platform.openai.com/docs/guides/function-calling) or use - built-in [tools](https://platform.openai.com/docs/guides/tools) like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search) to use - your own data as input for the model's response. - - Args: - input: Text, image, or file inputs to the model, used to generate a response. - - Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Image inputs](https://platform.openai.com/docs/guides/images) - - [File inputs](https://platform.openai.com/docs/guides/pdf-files) - - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - - [Function calling](https://platform.openai.com/docs/guides/function-calling) - - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a - wide range of models with different capabilities, performance characteristics, - and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - - stream: If set to true, the model response data will be streamed to the client as it is - generated using - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). - See the - [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) - for more information. - - include: Specify additional output data to include in the model response. Currently - supported values are: - - - `file_search_call.results`: Include the search results of the file search tool - call. - - `message.input_image.image_url`: Include image urls from the input message. - - `computer_call_output.output.image_url`: Include image urls from the computer - call output. - - instructions: Inserts a system (or developer) message as the first item in the model's - context. - - When using along with `previous_response_id`, the instructions from a previous - response will be not be carried over to the next response. This makes it simple - to swap out system (or developer) messages in new responses. - - max_output_tokens: An upper bound for the number of tokens that can be generated for a response, - including visible output tokens and - [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). - - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - - parallel_tool_calls: Whether to allow the model to run tool calls in parallel. - - previous_response_id: The unique ID of the previous response to the model. Use this to create - multi-turn conversations. Learn more about - [conversation state](https://platform.openai.com/docs/guides/conversation-state). - - reasoning: **o-series models only** - - Configuration options for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). - - store: Whether to store the generated model response for later retrieval via API. - - temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will - make the output more random, while lower values like 0.2 will make it more - focused and deterministic. We generally recommend altering this or `top_p` but - not both. - - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - - tool_choice: How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. - - tools: An array of tools the model may call while generating a response. You can - specify which tool to use by setting the `tool_choice` parameter. - - The two categories of tools you can provide the model are: - - - **Built-in tools**: Tools that are provided by OpenAI that extend the model's - capabilities, like - [web search](https://platform.openai.com/docs/guides/tools-web-search) or - [file search](https://platform.openai.com/docs/guides/tools-file-search). - Learn more about - [built-in tools](https://platform.openai.com/docs/guides/tools). - - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about - [function calling](https://platform.openai.com/docs/guides/function-calling). - - top_p: An alternative to sampling with temperature, called nucleus sampling, where the - model 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. - - We generally recommend altering this or `temperature` but not both. - - truncation: The truncation strategy to use for the model response. - - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size - for a model, the request will fail with a 400 error. - - user: A unique identifier representing your end-user, which can help OpenAI to monitor - and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @required_args(["input", "model"], ["input", "model", "stream"]) - async def create( - self, - *, - input: Union[str, ResponseInputParam], - model: ResponsesModel, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Response | AsyncStream[ResponseStreamEvent]: - return await self._post( - "/responses", - body=await async_maybe_transform( - { - "input": input, - "model": model, - "include": include, - "instructions": instructions, - "max_output_tokens": max_output_tokens, - "metadata": metadata, - "parallel_tool_calls": parallel_tool_calls, - "previous_response_id": previous_response_id, - "reasoning": reasoning, - "store": store, - "stream": stream, - "temperature": temperature, - "text": text, - "tool_choice": tool_choice, - "tools": tools, - "top_p": top_p, - "truncation": truncation, - "user": user, - }, - response_create_params.ResponseCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Response, - stream=stream or False, - stream_cls=AsyncStream[ResponseStreamEvent], - ) - - def stream( - self, - *, - input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncResponseStreamManager[TextFormatT]: - if is_given(text_format): - if not text: - text = {} - - if "format" in text: - raise TypeError("Cannot mix and match text.format with text_format") - - text["format"] = _type_to_text_format_param(text_format) - - tools = _make_tools(tools) - - api_request = self.create( - input=input, - model=model, - tools=tools, - include=include, - instructions=instructions, - max_output_tokens=max_output_tokens, - metadata=metadata, - parallel_tool_calls=parallel_tool_calls, - previous_response_id=previous_response_id, - store=store, - stream=True, - temperature=temperature, - text=text, - tool_choice=tool_choice, - reasoning=reasoning, - top_p=top_p, - truncation=truncation, - user=user, - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - ) - - return AsyncResponseStreamManager( - api_request, - text_format=text_format, - input_tools=tools, - ) - - async def parse( - self, - *, - input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ParsedResponse[TextFormatT]: - if is_given(text_format): - if not text: - text = {} - - if "format" in text: - raise TypeError("Cannot mix and match text.format with text_format") - - text["format"] = _type_to_text_format_param(text_format) - - tools = _make_tools(tools) - - def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: - return parse_response( - input_tools=tools, - text_format=text_format, - response=raw_response, - ) - - return await self._post( - "/responses", - body=maybe_transform( - { - "input": input, - "model": model, - "include": include, - "instructions": instructions, - "max_output_tokens": max_output_tokens, - "metadata": metadata, - "parallel_tool_calls": parallel_tool_calls, - "previous_response_id": previous_response_id, - "reasoning": reasoning, - "store": store, - "stream": stream, - "temperature": temperature, - "text": text, - "tool_choice": tool_choice, - "tools": tools, - "top_p": top_p, - "truncation": truncation, - "user": user, - }, - response_create_params.ResponseCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=parser, - ), - # we turn the `Response` instance into a `ParsedResponse` - # in the `parser` function above - cast_to=cast(Type[ParsedResponse[TextFormatT]], Response), - ) - - async def retrieve( - self, - response_id: str, - *, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Response: - """ - Retrieves a model response with the given ID. - - Args: - include: Additional fields to include in the response. See the `include` parameter for - Response creation above for more information. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not response_id: - raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") - return await self._get( - f"/responses/{response_id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"include": include}, response_retrieve_params.ResponseRetrieveParams - ), - ), - cast_to=Response, - ) - - async def delete( - self, - response_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> None: - """ - Deletes a model response with the given ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not response_id: - raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/responses/{response_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - -class ResponsesWithRawResponse: - def __init__(self, responses: Responses) -> None: - self._responses = responses - - self.create = _legacy_response.to_raw_response_wrapper( - responses.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - responses.retrieve, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - responses.delete, - ) - - @cached_property - def input_items(self) -> InputItemsWithRawResponse: - return InputItemsWithRawResponse(self._responses.input_items) - - -class AsyncResponsesWithRawResponse: - def __init__(self, responses: AsyncResponses) -> None: - self._responses = responses - - self.create = _legacy_response.async_to_raw_response_wrapper( - responses.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - responses.retrieve, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - responses.delete, - ) - - @cached_property - def input_items(self) -> AsyncInputItemsWithRawResponse: - return AsyncInputItemsWithRawResponse(self._responses.input_items) - - -class ResponsesWithStreamingResponse: - def __init__(self, responses: Responses) -> None: - self._responses = responses - - self.create = to_streamed_response_wrapper( - responses.create, - ) - self.retrieve = to_streamed_response_wrapper( - responses.retrieve, - ) - self.delete = to_streamed_response_wrapper( - responses.delete, - ) - - @cached_property - def input_items(self) -> InputItemsWithStreamingResponse: - return InputItemsWithStreamingResponse(self._responses.input_items) - - -class AsyncResponsesWithStreamingResponse: - def __init__(self, responses: AsyncResponses) -> None: - self._responses = responses - - self.create = async_to_streamed_response_wrapper( - responses.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - responses.retrieve, - ) - self.delete = async_to_streamed_response_wrapper( - responses.delete, - ) - - @cached_property - def input_items(self) -> AsyncInputItemsWithStreamingResponse: - return AsyncInputItemsWithStreamingResponse(self._responses.input_items) - - -def _make_tools(tools: Iterable[ParseableToolParam] | NotGiven) -> List[ToolParam] | NotGiven: - if not is_given(tools): - return NOT_GIVEN - - converted_tools: List[ToolParam] = [] - for tool in tools: - if tool["type"] != "function": - converted_tools.append(tool) - continue - - if "function" not in tool: - # standard Responses API case - converted_tools.append(tool) - continue - - function = cast(Any, tool)["function"] # pyright: ignore[reportUnnecessaryCast] - if not isinstance(function, PydanticFunctionTool): - raise Exception( - "Expected Chat Completions function tool shape to be created using `openai.pydantic_function_tool()`" - ) - - assert "parameters" in function - new_tool = ResponsesPydanticFunctionTool( - { - "type": "function", - "name": function["name"], - "description": function.get("description"), - "parameters": function["parameters"], - "strict": function.get("strict") or False, - }, - function.model, - ) - - converted_tools.append(new_tool.cast()) - - return converted_tools From a8fa0def5cd999044dae39b6cdff7a54db25c627 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:30:46 +0000 Subject: [PATCH 849/914] release: 1.69.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e280020f03..5df3c6496b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.68.2" + ".": "1.69.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ee22cfe7fb..773c20d2af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 1.69.0 (2025-03-27) + +Full Changelog: [v1.68.2...v1.69.0](https://github.com/openai/openai-python/compare/v1.68.2...v1.69.0) + +### Features + +* **api:** add `get /chat/completions` endpoint ([e6b8a42](https://github.com/openai/openai-python/commit/e6b8a42fc4286656cc86c2acd83692b170e77b68)) + + +### Bug Fixes + +* **audio:** correctly parse transcription stream events ([16a3a19](https://github.com/openai/openai-python/commit/16a3a195ff31f099fbe46043a12d2380c2c01f83)) + + +### Chores + +* add hash of OpenAPI spec/config inputs to .stats.yml ([515e1cd](https://github.com/openai/openai-python/commit/515e1cdd4a3109e5b29618df813656e17f22b52a)) +* **api:** updates to supported Voice IDs ([#2261](https://github.com/openai/openai-python/issues/2261)) ([64956f9](https://github.com/openai/openai-python/commit/64956f9d9889b04380c7f5eb926509d1efd523e6)) +* fix typos ([#2259](https://github.com/openai/openai-python/issues/2259)) ([6160de3](https://github.com/openai/openai-python/commit/6160de3e099f09c2d6ee5eeee4cbcc55b67a8f87)) + ## 1.68.2 (2025-03-21) Full Changelog: [v1.68.1...v1.68.2](https://github.com/openai/openai-python/compare/v1.68.1...v1.68.2) diff --git a/pyproject.toml b/pyproject.toml index b1917922cd..e50c5d6c1f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.68.2" +version = "1.69.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a29ce4e818..50c0e42d78 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.68.2" # x-release-please-version +__version__ = "1.69.0" # x-release-please-version From 972753a5d38f78149f33b9e768fb426e21456b9d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 19:41:51 +0000 Subject: [PATCH 850/914] feat(api): add `get /responses/{response_id}/input_items` endpoint --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 1e1104a062..f6a90d2438 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6663c59193eb95b201e492de17dcbd5e126ba03d18ce66287a3e2c632ca56fe7.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: 9351ea829c2b41da3b48a38c934c92ee +config_hash: e25e31d8446b6bc0e3ef7103b6993cce From 384e6b23ce0366d6b2f31cc98d35525da5b22c10 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 05:03:58 +0000 Subject: [PATCH 851/914] release: 1.70.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5df3c6496b..ba5cbfb627 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.69.0" + ".": "1.70.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 773c20d2af..8954d86571 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.70.0 (2025-03-31) + +Full Changelog: [v1.69.0...v1.70.0](https://github.com/openai/openai-python/compare/v1.69.0...v1.70.0) + +### Features + +* **api:** add `get /responses/{response_id}/input_items` endpoint ([4c6a35d](https://github.com/openai/openai-python/commit/4c6a35dec65362a6a738c3387dae57bf8cbfcbb2)) + ## 1.69.0 (2025-03-27) Full Changelog: [v1.68.2...v1.69.0](https://github.com/openai/openai-python/compare/v1.68.2...v1.69.0) diff --git a/pyproject.toml b/pyproject.toml index e50c5d6c1f..296d02e40b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.69.0" +version = "1.70.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 50c0e42d78..6b4385ec3c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.69.0" # x-release-please-version +__version__ = "1.70.0" # x-release-please-version From a718999f751b2fb4574ebfdc1d66d6234334367c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 00:26:24 +0000 Subject: [PATCH 852/914] chore: Remove deprecated/unused remote spec feature --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index f6a90d2438..2ccfd3411d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6663c59193eb95b201e492de17dcbd5e126ba03d18ce66287a3e2c632ca56fe7.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: e25e31d8446b6bc0e3ef7103b6993cce +config_hash: 2daae06cc598821ccf87201de0861e40 From 7feb73e3ef2759d4c1dc9169b21fa3d51e694d00 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 23:12:33 +0000 Subject: [PATCH 853/914] feat(api): manual updates --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2ccfd3411d..71ac95541b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6663c59193eb95b201e492de17dcbd5e126ba03d18ce66287a3e2c632ca56fe7.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-4bce8217a697c729ac98046d4caf2c9e826b54c427fb0ab4f98e549a2e0ce31c.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: 2daae06cc598821ccf87201de0861e40 +config_hash: 31a12443afeef2933b34e2de23c40954 From 6747d4276a994e02e9c09cf87f620aadc7182fc6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 23:18:34 +0000 Subject: [PATCH 854/914] feat(api): manual updates --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 71ac95541b..baad2afc1b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-4bce8217a697c729ac98046d4caf2c9e826b54c427fb0ab4f98e549a2e0ce31c.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: 31a12443afeef2933b34e2de23c40954 +config_hash: 178ba1bfb1237bf6b94abb3408072aa7 From c87b46135f9351147d80b5a65e61108c26cd0405 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:49:55 +0000 Subject: [PATCH 855/914] feat(api): manual updates --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index baad2afc1b..675edb075a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-4bce8217a697c729ac98046d4caf2c9e826b54c427fb0ab4f98e549a2e0ce31c.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: 178ba1bfb1237bf6b94abb3408072aa7 +config_hash: 578c5bff4208d560c0c280f13324409f From fb69e674f3caaf451ad55ad92d430d0a50e7c0a4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:55:21 +0000 Subject: [PATCH 856/914] chore(internal): remove trailing character (#2277) --- tests/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index 62654afe1e..616255af3c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1797,7 +1797,7 @@ def test_get_platform(self) -> None: import threading from openai._utils import asyncify - from openai._base_client import get_platform + from openai._base_client import get_platform async def test_main() -> None: result = await asyncify(get_platform)() From 41e74d45396659b4883fee6353c7abc88870f1f5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:37:53 +0000 Subject: [PATCH 857/914] feat(api): manual updates --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 675edb075a..aebb90c8cf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 82 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-4bce8217a697c729ac98046d4caf2c9e826b54c427fb0ab4f98e549a2e0ce31c.yml openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: 578c5bff4208d560c0c280f13324409f +config_hash: bcd2cacdcb9fae9938f273cd167f613c From f24c9820397bd5a42980986ac04706625bba78ce Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 13:29:13 +0000 Subject: [PATCH 858/914] chore(deps): allow websockets v15 (#2281) --- pyproject.toml | 2 +- requirements-dev.lock | 2 +- requirements.lock | 2 +- src/openai/resources/beta/realtime/realtime.py | 8 -------- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 296d02e40b..0b7d1d41b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ Repository = "https://github.com/openai/openai-python" openai = "openai.cli:main" [project.optional-dependencies] -realtime = ["websockets >= 13, < 15"] +realtime = ["websockets >= 13, < 16"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 0755ddb3c5..11bb5c1b30 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -188,7 +188,7 @@ urllib3==2.2.1 # via requests virtualenv==20.24.5 # via nox -websockets==14.2 +websockets==15.0.1 # via openai zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index fa88e26c0f..467abc6e90 100644 --- a/requirements.lock +++ b/requirements.lock @@ -70,5 +70,5 @@ typing-extensions==4.12.2 # via pydantic-core tzdata==2024.1 # via pandas -websockets==14.2 +websockets==15.0.1 # via openai diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index 76e57f8cb7..5cafce1322 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -277,10 +277,6 @@ async def recv_bytes(self) -> bytes: """ message = await self._connection.recv(decode=False) log.debug(f"Received websocket message: %s", message) - if not isinstance(message, bytes): - # passing `decode=False` should always result in us getting `bytes` back - raise TypeError(f"Expected `.recv(decode=False)` to return `bytes` but got {type(message)}") - return message async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: @@ -461,10 +457,6 @@ def recv_bytes(self) -> bytes: """ message = self._connection.recv(decode=False) log.debug(f"Received websocket message: %s", message) - if not isinstance(message, bytes): - # passing `decode=False` should always result in us getting `bytes` back - raise TypeError(f"Expected `.recv(decode=False)` to return `bytes` but got {type(message)}") - return message def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: From a764253788df8a57fd05759aafd42c3c722d361c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 14:51:50 +0000 Subject: [PATCH 859/914] chore(internal): only run examples workflow in main repo (#2282) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d86fc0ea53..6d2699cca8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,6 +54,7 @@ jobs: examples: name: examples runs-on: ubuntu-latest + if: github.repository == 'openai/openai-python' steps: - uses: actions/checkout@v4 From 692fd082b41047529b55a9c7e2047bdcbc2cccdd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 14:52:31 +0000 Subject: [PATCH 860/914] release: 1.71.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ba5cbfb627..c7704ce953 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.70.0" + ".": "1.71.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8954d86571..e8f2e22cb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.71.0 (2025-04-07) + +Full Changelog: [v1.70.0...v1.71.0](https://github.com/openai/openai-python/compare/v1.70.0...v1.71.0) + +### Features + +* **api:** manual updates ([bf8b4b6](https://github.com/openai/openai-python/commit/bf8b4b69906bfaea622c9c644270e985d92e2df2)) +* **api:** manual updates ([3e37aa3](https://github.com/openai/openai-python/commit/3e37aa3e151d9738625a1daf75d6243d6fdbe8f2)) +* **api:** manual updates ([dba9b65](https://github.com/openai/openai-python/commit/dba9b656fa5955b6eba8f6910da836a34de8d59d)) +* **api:** manual updates ([f0c463b](https://github.com/openai/openai-python/commit/f0c463b47836666d091b5f616871f1b94646d346)) + + +### Chores + +* **deps:** allow websockets v15 ([#2281](https://github.com/openai/openai-python/issues/2281)) ([19c619e](https://github.com/openai/openai-python/commit/19c619ea95839129a86c19d5b60133e1ed9f2746)) +* **internal:** only run examples workflow in main repo ([#2282](https://github.com/openai/openai-python/issues/2282)) ([c3e0927](https://github.com/openai/openai-python/commit/c3e0927d3fbbb9f753ba12adfa682a4235ba530a)) +* **internal:** remove trailing character ([#2277](https://github.com/openai/openai-python/issues/2277)) ([5a21a2d](https://github.com/openai/openai-python/commit/5a21a2d7994e39bb0c86271eeb807983a9ae874a)) +* Remove deprecated/unused remote spec feature ([23f76eb](https://github.com/openai/openai-python/commit/23f76eb0b9ddf12bcb04a6ad3f3ec5e956d2863f)) + ## 1.70.0 (2025-03-31) Full Changelog: [v1.69.0...v1.70.0](https://github.com/openai/openai-python/compare/v1.69.0...v1.70.0) diff --git a/pyproject.toml b/pyproject.toml index 0b7d1d41b4..4583a5531f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.70.0" +version = "1.71.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6b4385ec3c..12e9d20bb1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.70.0" # x-release-please-version +__version__ = "1.71.0" # x-release-please-version From 039b1bf54a2fe34c2ff2d669ee23515dae52d743 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 12:09:51 +0000 Subject: [PATCH 861/914] chore(internal): slight transform perf improvement (#2284) --- src/openai/_utils/_transform.py | 22 ++++++++++++++++++++++ tests/test_transform.py | 12 ++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 7ac2e17fbb..3ec620818c 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -142,6 +142,10 @@ def _maybe_transform_key(key: str, type_: type) -> str: return key +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + def _transform_recursive( data: object, *, @@ -184,6 +188,15 @@ def _transform_recursive( return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): @@ -332,6 +345,15 @@ async def _async_transform_recursive( return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): diff --git a/tests/test_transform.py b/tests/test_transform.py index 385fbe2b2c..cd584756d7 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -432,3 +432,15 @@ async def test_base64_file_input(use_async: bool) -> None: assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { "foo": "SGVsbG8sIHdvcmxkIQ==" } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_transform_skipping(use_async: bool) -> None: + # lists of ints are left as-is + data = [1, 2, 3] + assert await transform(data, List[int], use_async) is data + + # iterables of ints are converted to a list + data = iter([1, 2, 3]) + assert await transform(data, Iterable[int], use_async) == [1, 2, 3] From 48c200ac0d732b030a87012575c1843957e98718 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:25:27 +0000 Subject: [PATCH 862/914] chore(tests): improve enum examples (#2286) --- tests/api_resources/test_images.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 9bc9719bc5..2e31f3354a 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -31,7 +31,7 @@ def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: model="dall-e-2", n=1, response_format="url", - size="256x256", + size="1024x1024", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -77,7 +77,7 @@ def test_method_edit_with_all_params(self, client: OpenAI) -> None: model="dall-e-2", n=1, response_format="url", - size="256x256", + size="1024x1024", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -123,7 +123,7 @@ def test_method_generate_with_all_params(self, client: OpenAI) -> None: n=1, quality="standard", response_format="url", - size="256x256", + size="1024x1024", style="vivid", user="user-1234", ) @@ -171,7 +171,7 @@ async def test_method_create_variation_with_all_params(self, async_client: Async model="dall-e-2", n=1, response_format="url", - size="256x256", + size="1024x1024", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -217,7 +217,7 @@ async def test_method_edit_with_all_params(self, async_client: AsyncOpenAI) -> N model="dall-e-2", n=1, response_format="url", - size="256x256", + size="1024x1024", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -263,7 +263,7 @@ async def test_method_generate_with_all_params(self, async_client: AsyncOpenAI) n=1, quality="standard", response_format="url", - size="256x256", + size="1024x1024", style="vivid", user="user-1234", ) From 8da9f46bedc954cb4ceba2eb5acf5f9d3c22df57 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 18:50:02 +0000 Subject: [PATCH 863/914] feat(api): Add evalapi to sdk (#2287) Adding the evalsapi to the sdk. --- .stats.yml | 8 +- api.md | 85 + src/openai/__init__.py | 1 + src/openai/_client.py | 9 + src/openai/_module_client.py | 7 + src/openai/resources/__init__.py | 14 + src/openai/resources/evals/__init__.py | 33 + src/openai/resources/evals/evals.py | 663 +++++++ src/openai/resources/evals/runs/__init__.py | 33 + .../resources/evals/runs/output_items.py | 315 +++ src/openai/resources/evals/runs/runs.py | 635 ++++++ src/openai/resources/fine_tuning/__init__.py | 14 + .../fine_tuning/checkpoints/__init__.py | 33 + .../fine_tuning/checkpoints/checkpoints.py | 102 + .../fine_tuning/checkpoints/permissions.py | 416 ++++ .../resources/fine_tuning/fine_tuning.py | 32 + src/openai/types/__init__.py | 17 + src/openai/types/eval_create_params.py | 153 ++ src/openai/types/eval_create_response.py | 56 + .../types/eval_custom_data_source_config.py | 21 + src/openai/types/eval_delete_response.py | 14 + src/openai/types/eval_label_model_grader.py | 74 + src/openai/types/eval_list_params.py | 27 + src/openai/types/eval_list_response.py | 56 + src/openai/types/eval_retrieve_response.py | 56 + ...l_stored_completions_data_source_config.py | 32 + src/openai/types/eval_string_check_grader.py | 24 + .../types/eval_string_check_grader_param.py | 24 + .../types/eval_text_similarity_grader.py | 44 + .../eval_text_similarity_grader_param.py | 45 + src/openai/types/eval_update_params.py | 25 + src/openai/types/eval_update_response.py | 56 + src/openai/types/evals/__init__.py | 22 + ...create_eval_completions_run_data_source.py | 185 ++ ..._eval_completions_run_data_source_param.py | 181 ++ .../create_eval_jsonl_run_data_source.py | 41 + ...create_eval_jsonl_run_data_source_param.py | 46 + src/openai/types/evals/eval_api_error.py | 14 + src/openai/types/evals/run_cancel_response.py | 115 ++ src/openai/types/evals/run_create_params.py | 33 + src/openai/types/evals/run_create_response.py | 115 ++ src/openai/types/evals/run_delete_response.py | 15 + src/openai/types/evals/run_list_params.py | 27 + src/openai/types/evals/run_list_response.py | 115 ++ .../types/evals/run_retrieve_response.py | 115 ++ src/openai/types/evals/runs/__init__.py | 7 + .../evals/runs/output_item_list_params.py | 30 + .../evals/runs/output_item_list_response.py | 104 + .../runs/output_item_retrieve_response.py | 104 + .../types/fine_tuning/checkpoints/__init__.py | 9 + .../checkpoints/permission_create_params.py | 13 + .../checkpoints/permission_create_response.py | 21 + .../checkpoints/permission_delete_response.py | 18 + .../checkpoints/permission_retrieve_params.py | 21 + .../permission_retrieve_response.py | 34 + tests/api_resources/evals/__init__.py | 1 + tests/api_resources/evals/runs/__init__.py | 1 + .../evals/runs/test_output_items.py | 263 +++ tests/api_resources/evals/test_runs.py | 589 ++++++ .../fine_tuning/checkpoints/__init__.py | 1 + .../checkpoints/test_permissions.py | 297 +++ tests/api_resources/test_evals.py | 1701 +++++++++++++++++ 62 files changed, 7358 insertions(+), 4 deletions(-) create mode 100644 src/openai/resources/evals/__init__.py create mode 100644 src/openai/resources/evals/evals.py create mode 100644 src/openai/resources/evals/runs/__init__.py create mode 100644 src/openai/resources/evals/runs/output_items.py create mode 100644 src/openai/resources/evals/runs/runs.py create mode 100644 src/openai/resources/fine_tuning/checkpoints/__init__.py create mode 100644 src/openai/resources/fine_tuning/checkpoints/checkpoints.py create mode 100644 src/openai/resources/fine_tuning/checkpoints/permissions.py create mode 100644 src/openai/types/eval_create_params.py create mode 100644 src/openai/types/eval_create_response.py create mode 100644 src/openai/types/eval_custom_data_source_config.py create mode 100644 src/openai/types/eval_delete_response.py create mode 100644 src/openai/types/eval_label_model_grader.py create mode 100644 src/openai/types/eval_list_params.py create mode 100644 src/openai/types/eval_list_response.py create mode 100644 src/openai/types/eval_retrieve_response.py create mode 100644 src/openai/types/eval_stored_completions_data_source_config.py create mode 100644 src/openai/types/eval_string_check_grader.py create mode 100644 src/openai/types/eval_string_check_grader_param.py create mode 100644 src/openai/types/eval_text_similarity_grader.py create mode 100644 src/openai/types/eval_text_similarity_grader_param.py create mode 100644 src/openai/types/eval_update_params.py create mode 100644 src/openai/types/eval_update_response.py create mode 100644 src/openai/types/evals/__init__.py create mode 100644 src/openai/types/evals/create_eval_completions_run_data_source.py create mode 100644 src/openai/types/evals/create_eval_completions_run_data_source_param.py create mode 100644 src/openai/types/evals/create_eval_jsonl_run_data_source.py create mode 100644 src/openai/types/evals/create_eval_jsonl_run_data_source_param.py create mode 100644 src/openai/types/evals/eval_api_error.py create mode 100644 src/openai/types/evals/run_cancel_response.py create mode 100644 src/openai/types/evals/run_create_params.py create mode 100644 src/openai/types/evals/run_create_response.py create mode 100644 src/openai/types/evals/run_delete_response.py create mode 100644 src/openai/types/evals/run_list_params.py create mode 100644 src/openai/types/evals/run_list_response.py create mode 100644 src/openai/types/evals/run_retrieve_response.py create mode 100644 src/openai/types/evals/runs/__init__.py create mode 100644 src/openai/types/evals/runs/output_item_list_params.py create mode 100644 src/openai/types/evals/runs/output_item_list_response.py create mode 100644 src/openai/types/evals/runs/output_item_retrieve_response.py create mode 100644 src/openai/types/fine_tuning/checkpoints/__init__.py create mode 100644 src/openai/types/fine_tuning/checkpoints/permission_create_params.py create mode 100644 src/openai/types/fine_tuning/checkpoints/permission_create_response.py create mode 100644 src/openai/types/fine_tuning/checkpoints/permission_delete_response.py create mode 100644 src/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py create mode 100644 src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py create mode 100644 tests/api_resources/evals/__init__.py create mode 100644 tests/api_resources/evals/runs/__init__.py create mode 100644 tests/api_resources/evals/runs/test_output_items.py create mode 100644 tests/api_resources/evals/test_runs.py create mode 100644 tests/api_resources/fine_tuning/checkpoints/__init__.py create mode 100644 tests/api_resources/fine_tuning/checkpoints/test_permissions.py create mode 100644 tests/api_resources/test_evals.py diff --git a/.stats.yml b/.stats.yml index aebb90c8cf..ebe07c1372 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 82 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-4bce8217a697c729ac98046d4caf2c9e826b54c427fb0ab4f98e549a2e0ce31c.yml -openapi_spec_hash: 7996d2c34cc44fe2ce9ffe93c0ab774e -config_hash: bcd2cacdcb9fae9938f273cd167f613c +configured_endpoints: 97 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-472fe3036ea745365257fe870c0330917fb3153705c2826f49873cd631319b0a.yml +openapi_spec_hash: ea86343b5e9858a74e85da8ab2c532f6 +config_hash: ef19d36c307306f14f2e1cd5c834a151 diff --git a/api.md b/api.md index a5f81c624c..e06f55c2cc 100644 --- a/api.md +++ b/api.md @@ -259,6 +259,26 @@ Methods: - client.fine_tuning.jobs.checkpoints.list(fine_tuning_job_id, \*\*params) -> SyncCursorPage[FineTuningJobCheckpoint] +## Checkpoints + +### Permissions + +Types: + +```python +from openai.types.fine_tuning.checkpoints import ( + PermissionCreateResponse, + PermissionRetrieveResponse, + PermissionDeleteResponse, +) +``` + +Methods: + +- client.fine_tuning.checkpoints.permissions.create(fine_tuned_model_checkpoint, \*\*params) -> SyncPage[PermissionCreateResponse] +- client.fine_tuning.checkpoints.permissions.retrieve(fine_tuned_model_checkpoint, \*\*params) -> PermissionRetrieveResponse +- client.fine_tuning.checkpoints.permissions.delete(fine_tuned_model_checkpoint) -> PermissionDeleteResponse + # VectorStores Types: @@ -706,3 +726,68 @@ from openai.types.responses import ResponseItemList Methods: - client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem] + +# Evals + +Types: + +```python +from openai.types import ( + EvalCustomDataSourceConfig, + EvalLabelModelGrader, + EvalStoredCompletionsDataSourceConfig, + EvalStringCheckGrader, + EvalTextSimilarityGrader, + EvalCreateResponse, + EvalRetrieveResponse, + EvalUpdateResponse, + EvalListResponse, + EvalDeleteResponse, +) +``` + +Methods: + +- client.evals.create(\*\*params) -> EvalCreateResponse +- client.evals.retrieve(eval_id) -> EvalRetrieveResponse +- client.evals.update(eval_id, \*\*params) -> EvalUpdateResponse +- client.evals.list(\*\*params) -> SyncCursorPage[EvalListResponse] +- client.evals.delete(eval_id) -> EvalDeleteResponse + +## Runs + +Types: + +```python +from openai.types.evals import ( + CreateEvalCompletionsRunDataSource, + CreateEvalJSONLRunDataSource, + EvalAPIError, + RunCreateResponse, + RunRetrieveResponse, + RunListResponse, + RunDeleteResponse, + RunCancelResponse, +) +``` + +Methods: + +- client.evals.runs.create(eval_id, \*\*params) -> RunCreateResponse +- client.evals.runs.retrieve(run_id, \*, eval_id) -> RunRetrieveResponse +- client.evals.runs.list(eval_id, \*\*params) -> SyncCursorPage[RunListResponse] +- client.evals.runs.delete(run_id, \*, eval_id) -> RunDeleteResponse +- client.evals.runs.cancel(run_id, \*, eval_id) -> RunCancelResponse + +### OutputItems + +Types: + +```python +from openai.types.evals.runs import OutputItemRetrieveResponse, OutputItemListResponse +``` + +Methods: + +- client.evals.runs.output_items.retrieve(output_item_id, \*, eval_id, run_id) -> OutputItemRetrieveResponse +- client.evals.runs.output_items.list(run_id, \*, eval_id, \*\*params) -> SyncCursorPage[OutputItemListResponse] diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 7ce6df0817..9e97098bb0 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -352,6 +352,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] beta as beta, chat as chat, audio as audio, + evals as evals, files as files, images as images, models as models, diff --git a/src/openai/_client.py b/src/openai/_client.py index 18d96da9a3..3aca6cb124 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -36,6 +36,7 @@ from .resources.beta import beta from .resources.chat import chat from .resources.audio import audio +from .resources.evals import evals from .resources.uploads import uploads from .resources.responses import responses from .resources.fine_tuning import fine_tuning @@ -59,6 +60,7 @@ class OpenAI(SyncAPIClient): batches: batches.Batches uploads: uploads.Uploads responses: responses.Responses + evals: evals.Evals with_raw_response: OpenAIWithRawResponse with_streaming_response: OpenAIWithStreamedResponse @@ -158,6 +160,7 @@ def __init__( self.batches = batches.Batches(self) self.uploads = uploads.Uploads(self) self.responses = responses.Responses(self) + self.evals = evals.Evals(self) self.with_raw_response = OpenAIWithRawResponse(self) self.with_streaming_response = OpenAIWithStreamedResponse(self) @@ -290,6 +293,7 @@ class AsyncOpenAI(AsyncAPIClient): batches: batches.AsyncBatches uploads: uploads.AsyncUploads responses: responses.AsyncResponses + evals: evals.AsyncEvals with_raw_response: AsyncOpenAIWithRawResponse with_streaming_response: AsyncOpenAIWithStreamedResponse @@ -389,6 +393,7 @@ def __init__( self.batches = batches.AsyncBatches(self) self.uploads = uploads.AsyncUploads(self) self.responses = responses.AsyncResponses(self) + self.evals = evals.AsyncEvals(self) self.with_raw_response = AsyncOpenAIWithRawResponse(self) self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self) @@ -522,6 +527,7 @@ def __init__(self, client: OpenAI) -> None: self.batches = batches.BatchesWithRawResponse(client.batches) self.uploads = uploads.UploadsWithRawResponse(client.uploads) self.responses = responses.ResponsesWithRawResponse(client.responses) + self.evals = evals.EvalsWithRawResponse(client.evals) class AsyncOpenAIWithRawResponse: @@ -540,6 +546,7 @@ def __init__(self, client: AsyncOpenAI) -> None: self.batches = batches.AsyncBatchesWithRawResponse(client.batches) self.uploads = uploads.AsyncUploadsWithRawResponse(client.uploads) self.responses = responses.AsyncResponsesWithRawResponse(client.responses) + self.evals = evals.AsyncEvalsWithRawResponse(client.evals) class OpenAIWithStreamedResponse: @@ -558,6 +565,7 @@ def __init__(self, client: OpenAI) -> None: self.batches = batches.BatchesWithStreamingResponse(client.batches) self.uploads = uploads.UploadsWithStreamingResponse(client.uploads) self.responses = responses.ResponsesWithStreamingResponse(client.responses) + self.evals = evals.EvalsWithStreamingResponse(client.evals) class AsyncOpenAIWithStreamedResponse: @@ -576,6 +584,7 @@ def __init__(self, client: AsyncOpenAI) -> None: self.batches = batches.AsyncBatchesWithStreamingResponse(client.batches) self.uploads = uploads.AsyncUploadsWithStreamingResponse(client.uploads) self.responses = responses.AsyncResponsesWithStreamingResponse(client.responses) + self.evals = evals.AsyncEvalsWithStreamingResponse(client.evals) Client = OpenAI diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index e7d2657860..cf12f7a31e 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -30,6 +30,12 @@ def __load__(self) -> resources.Audio: return _load_client().audio +class EvalsProxy(LazyProxy[resources.Evals]): + @override + def __load__(self) -> resources.Evals: + return _load_client().evals + + class ImagesProxy(LazyProxy[resources.Images]): @override def __load__(self) -> resources.Images: @@ -94,6 +100,7 @@ def __load__(self) -> resources.VectorStores: beta: resources.Beta = BetaProxy().__as_proxied__() files: resources.Files = FilesProxy().__as_proxied__() audio: resources.Audio = AudioProxy().__as_proxied__() +evals: resources.Evals = EvalsProxy().__as_proxied__() images: resources.Images = ImagesProxy().__as_proxied__() models: resources.Models = ModelsProxy().__as_proxied__() batches: resources.Batches = BatchesProxy().__as_proxied__() diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index d3457cf319..ab9cd73e81 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -24,6 +24,14 @@ AudioWithStreamingResponse, AsyncAudioWithStreamingResponse, ) +from .evals import ( + Evals, + AsyncEvals, + EvalsWithRawResponse, + AsyncEvalsWithRawResponse, + EvalsWithStreamingResponse, + AsyncEvalsWithStreamingResponse, +) from .files import ( Files, AsyncFiles, @@ -198,4 +206,10 @@ "AsyncResponsesWithRawResponse", "ResponsesWithStreamingResponse", "AsyncResponsesWithStreamingResponse", + "Evals", + "AsyncEvals", + "EvalsWithRawResponse", + "AsyncEvalsWithRawResponse", + "EvalsWithStreamingResponse", + "AsyncEvalsWithStreamingResponse", ] diff --git a/src/openai/resources/evals/__init__.py b/src/openai/resources/evals/__init__.py new file mode 100644 index 0000000000..84f707511d --- /dev/null +++ b/src/openai/resources/evals/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) +from .evals import ( + Evals, + AsyncEvals, + EvalsWithRawResponse, + AsyncEvalsWithRawResponse, + EvalsWithStreamingResponse, + AsyncEvalsWithStreamingResponse, +) + +__all__ = [ + "Runs", + "AsyncRuns", + "RunsWithRawResponse", + "AsyncRunsWithRawResponse", + "RunsWithStreamingResponse", + "AsyncRunsWithStreamingResponse", + "Evals", + "AsyncEvals", + "EvalsWithRawResponse", + "AsyncEvalsWithRawResponse", + "EvalsWithStreamingResponse", + "AsyncEvalsWithStreamingResponse", +] diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py new file mode 100644 index 0000000000..24a0350cfb --- /dev/null +++ b/src/openai/resources/evals/evals.py @@ -0,0 +1,663 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal + +import httpx + +from ... import _legacy_response +from ...types import eval_list_params, eval_create_params, eval_update_params +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from .runs.runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.eval_list_response import EvalListResponse +from ...types.eval_create_response import EvalCreateResponse +from ...types.eval_delete_response import EvalDeleteResponse +from ...types.eval_update_response import EvalUpdateResponse +from ...types.eval_retrieve_response import EvalRetrieveResponse +from ...types.shared_params.metadata import Metadata + +__all__ = ["Evals", "AsyncEvals"] + + +class Evals(SyncAPIResource): + @cached_property + def runs(self) -> Runs: + return Runs(self._client) + + @cached_property + def with_raw_response(self) -> EvalsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return EvalsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EvalsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return EvalsWithStreamingResponse(self) + + def create( + self, + *, + data_source_config: eval_create_params.DataSourceConfig, + testing_criteria: Iterable[eval_create_params.TestingCriterion], + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + share_with_openai: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> EvalCreateResponse: + """ + Create the structure of an evaluation that can be used to test a model's + performance. An evaluation is a set of testing criteria and a datasource. After + creating an evaluation, you can run it on different models and model parameters. + We support several types of graders and datasources. For more information, see + the [Evals guide](https://platform.openai.com/docs/guides/evals). + + Args: + data_source_config: The configuration for the data source used for the evaluation runs. + + testing_criteria: A list of graders for all eval runs in this group. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the evaluation. + + share_with_openai: Indicates whether the evaluation is shared with OpenAI. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/evals", + body=maybe_transform( + { + "data_source_config": data_source_config, + "testing_criteria": testing_criteria, + "metadata": metadata, + "name": name, + "share_with_openai": share_with_openai, + }, + eval_create_params.EvalCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvalCreateResponse, + ) + + def retrieve( + self, + eval_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> EvalRetrieveResponse: + """ + Get an evaluation by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._get( + f"/evals/{eval_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvalRetrieveResponse, + ) + + def update( + self, + eval_id: str, + *, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> EvalUpdateResponse: + """ + Update certain properties of an evaluation. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: Rename the evaluation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._post( + f"/evals/{eval_id}", + body=maybe_transform( + { + "metadata": metadata, + "name": name, + }, + eval_update_params.EvalUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvalUpdateResponse, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + order_by: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[EvalListResponse]: + """ + List evaluations for a project. + + Args: + after: Identifier for the last eval from the previous pagination request. + + limit: Number of evals to retrieve. + + order: Sort order for evals by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + order_by: Evals can be ordered by creation time or last updated time. Use `created_at` for + creation time or `updated_at` for last updated time. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/evals", + page=SyncCursorPage[EvalListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "order_by": order_by, + }, + eval_list_params.EvalListParams, + ), + ), + model=EvalListResponse, + ) + + def delete( + self, + eval_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> EvalDeleteResponse: + """ + Delete an evaluation. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._delete( + f"/evals/{eval_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvalDeleteResponse, + ) + + +class AsyncEvals(AsyncAPIResource): + @cached_property + def runs(self) -> AsyncRuns: + return AsyncRuns(self._client) + + @cached_property + def with_raw_response(self) -> AsyncEvalsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncEvalsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEvalsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncEvalsWithStreamingResponse(self) + + async def create( + self, + *, + data_source_config: eval_create_params.DataSourceConfig, + testing_criteria: Iterable[eval_create_params.TestingCriterion], + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + share_with_openai: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> EvalCreateResponse: + """ + Create the structure of an evaluation that can be used to test a model's + performance. An evaluation is a set of testing criteria and a datasource. After + creating an evaluation, you can run it on different models and model parameters. + We support several types of graders and datasources. For more information, see + the [Evals guide](https://platform.openai.com/docs/guides/evals). + + Args: + data_source_config: The configuration for the data source used for the evaluation runs. + + testing_criteria: A list of graders for all eval runs in this group. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the evaluation. + + share_with_openai: Indicates whether the evaluation is shared with OpenAI. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/evals", + body=await async_maybe_transform( + { + "data_source_config": data_source_config, + "testing_criteria": testing_criteria, + "metadata": metadata, + "name": name, + "share_with_openai": share_with_openai, + }, + eval_create_params.EvalCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvalCreateResponse, + ) + + async def retrieve( + self, + eval_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> EvalRetrieveResponse: + """ + Get an evaluation by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return await self._get( + f"/evals/{eval_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvalRetrieveResponse, + ) + + async def update( + self, + eval_id: str, + *, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> EvalUpdateResponse: + """ + Update certain properties of an evaluation. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: Rename the evaluation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return await self._post( + f"/evals/{eval_id}", + body=await async_maybe_transform( + { + "metadata": metadata, + "name": name, + }, + eval_update_params.EvalUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvalUpdateResponse, + ) + + def list( + self, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + order_by: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[EvalListResponse, AsyncCursorPage[EvalListResponse]]: + """ + List evaluations for a project. + + Args: + after: Identifier for the last eval from the previous pagination request. + + limit: Number of evals to retrieve. + + order: Sort order for evals by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + order_by: Evals can be ordered by creation time or last updated time. Use `created_at` for + creation time or `updated_at` for last updated time. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/evals", + page=AsyncCursorPage[EvalListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "order_by": order_by, + }, + eval_list_params.EvalListParams, + ), + ), + model=EvalListResponse, + ) + + async def delete( + self, + eval_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> EvalDeleteResponse: + """ + Delete an evaluation. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return await self._delete( + f"/evals/{eval_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EvalDeleteResponse, + ) + + +class EvalsWithRawResponse: + def __init__(self, evals: Evals) -> None: + self._evals = evals + + self.create = _legacy_response.to_raw_response_wrapper( + evals.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + evals.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + evals.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + evals.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + evals.delete, + ) + + @cached_property + def runs(self) -> RunsWithRawResponse: + return RunsWithRawResponse(self._evals.runs) + + +class AsyncEvalsWithRawResponse: + def __init__(self, evals: AsyncEvals) -> None: + self._evals = evals + + self.create = _legacy_response.async_to_raw_response_wrapper( + evals.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + evals.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + evals.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + evals.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + evals.delete, + ) + + @cached_property + def runs(self) -> AsyncRunsWithRawResponse: + return AsyncRunsWithRawResponse(self._evals.runs) + + +class EvalsWithStreamingResponse: + def __init__(self, evals: Evals) -> None: + self._evals = evals + + self.create = to_streamed_response_wrapper( + evals.create, + ) + self.retrieve = to_streamed_response_wrapper( + evals.retrieve, + ) + self.update = to_streamed_response_wrapper( + evals.update, + ) + self.list = to_streamed_response_wrapper( + evals.list, + ) + self.delete = to_streamed_response_wrapper( + evals.delete, + ) + + @cached_property + def runs(self) -> RunsWithStreamingResponse: + return RunsWithStreamingResponse(self._evals.runs) + + +class AsyncEvalsWithStreamingResponse: + def __init__(self, evals: AsyncEvals) -> None: + self._evals = evals + + self.create = async_to_streamed_response_wrapper( + evals.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + evals.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + evals.update, + ) + self.list = async_to_streamed_response_wrapper( + evals.list, + ) + self.delete = async_to_streamed_response_wrapper( + evals.delete, + ) + + @cached_property + def runs(self) -> AsyncRunsWithStreamingResponse: + return AsyncRunsWithStreamingResponse(self._evals.runs) diff --git a/src/openai/resources/evals/runs/__init__.py b/src/openai/resources/evals/runs/__init__.py new file mode 100644 index 0000000000..d189f16fb7 --- /dev/null +++ b/src/openai/resources/evals/runs/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .runs import ( + Runs, + AsyncRuns, + RunsWithRawResponse, + AsyncRunsWithRawResponse, + RunsWithStreamingResponse, + AsyncRunsWithStreamingResponse, +) +from .output_items import ( + OutputItems, + AsyncOutputItems, + OutputItemsWithRawResponse, + AsyncOutputItemsWithRawResponse, + OutputItemsWithStreamingResponse, + AsyncOutputItemsWithStreamingResponse, +) + +__all__ = [ + "OutputItems", + "AsyncOutputItems", + "OutputItemsWithRawResponse", + "AsyncOutputItemsWithRawResponse", + "OutputItemsWithStreamingResponse", + "AsyncOutputItemsWithStreamingResponse", + "Runs", + "AsyncRuns", + "RunsWithRawResponse", + "AsyncRunsWithRawResponse", + "RunsWithStreamingResponse", + "AsyncRunsWithStreamingResponse", +] diff --git a/src/openai/resources/evals/runs/output_items.py b/src/openai/resources/evals/runs/output_items.py new file mode 100644 index 0000000000..8fd0fdea92 --- /dev/null +++ b/src/openai/resources/evals/runs/output_items.py @@ -0,0 +1,315 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.evals.runs import output_item_list_params +from ....types.evals.runs.output_item_list_response import OutputItemListResponse +from ....types.evals.runs.output_item_retrieve_response import OutputItemRetrieveResponse + +__all__ = ["OutputItems", "AsyncOutputItems"] + + +class OutputItems(SyncAPIResource): + @cached_property + def with_raw_response(self) -> OutputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return OutputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OutputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return OutputItemsWithStreamingResponse(self) + + def retrieve( + self, + output_item_id: str, + *, + eval_id: str, + run_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> OutputItemRetrieveResponse: + """ + Get an evaluation run output item by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + if not output_item_id: + raise ValueError(f"Expected a non-empty value for `output_item_id` but received {output_item_id!r}") + return self._get( + f"/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OutputItemRetrieveResponse, + ) + + def list( + self, + run_id: str, + *, + eval_id: str, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + status: Literal["fail", "pass"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[OutputItemListResponse]: + """ + Get a list of output items for an evaluation run. + + Args: + after: Identifier for the last output item from the previous pagination request. + + limit: Number of output items to retrieve. + + order: Sort order for output items by timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `asc`. + + status: Filter output items by status. Use `failed` to filter by failed output items or + `pass` to filter by passed output items. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._get_api_list( + f"/evals/{eval_id}/runs/{run_id}/output_items", + page=SyncCursorPage[OutputItemListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + output_item_list_params.OutputItemListParams, + ), + ), + model=OutputItemListResponse, + ) + + +class AsyncOutputItems(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncOutputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncOutputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOutputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncOutputItemsWithStreamingResponse(self) + + async def retrieve( + self, + output_item_id: str, + *, + eval_id: str, + run_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> OutputItemRetrieveResponse: + """ + Get an evaluation run output item by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + if not output_item_id: + raise ValueError(f"Expected a non-empty value for `output_item_id` but received {output_item_id!r}") + return await self._get( + f"/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=OutputItemRetrieveResponse, + ) + + def list( + self, + run_id: str, + *, + eval_id: str, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + status: Literal["fail", "pass"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[OutputItemListResponse, AsyncCursorPage[OutputItemListResponse]]: + """ + Get a list of output items for an evaluation run. + + Args: + after: Identifier for the last output item from the previous pagination request. + + limit: Number of output items to retrieve. + + order: Sort order for output items by timestamp. Use `asc` for ascending order or + `desc` for descending order. Defaults to `asc`. + + status: Filter output items by status. Use `failed` to filter by failed output items or + `pass` to filter by passed output items. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._get_api_list( + f"/evals/{eval_id}/runs/{run_id}/output_items", + page=AsyncCursorPage[OutputItemListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + output_item_list_params.OutputItemListParams, + ), + ), + model=OutputItemListResponse, + ) + + +class OutputItemsWithRawResponse: + def __init__(self, output_items: OutputItems) -> None: + self._output_items = output_items + + self.retrieve = _legacy_response.to_raw_response_wrapper( + output_items.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + output_items.list, + ) + + +class AsyncOutputItemsWithRawResponse: + def __init__(self, output_items: AsyncOutputItems) -> None: + self._output_items = output_items + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + output_items.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + output_items.list, + ) + + +class OutputItemsWithStreamingResponse: + def __init__(self, output_items: OutputItems) -> None: + self._output_items = output_items + + self.retrieve = to_streamed_response_wrapper( + output_items.retrieve, + ) + self.list = to_streamed_response_wrapper( + output_items.list, + ) + + +class AsyncOutputItemsWithStreamingResponse: + def __init__(self, output_items: AsyncOutputItems) -> None: + self._output_items = output_items + + self.retrieve = async_to_streamed_response_wrapper( + output_items.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + output_items.list, + ) diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py new file mode 100644 index 0000000000..6df0b6d121 --- /dev/null +++ b/src/openai/resources/evals/runs/runs.py @@ -0,0 +1,635 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .output_items import ( + OutputItems, + AsyncOutputItems, + OutputItemsWithRawResponse, + AsyncOutputItemsWithRawResponse, + OutputItemsWithStreamingResponse, + AsyncOutputItemsWithStreamingResponse, +) +from ....pagination import SyncCursorPage, AsyncCursorPage +from ....types.evals import run_list_params, run_create_params +from ...._base_client import AsyncPaginator, make_request_options +from ....types.shared_params.metadata import Metadata +from ....types.evals.run_list_response import RunListResponse +from ....types.evals.run_cancel_response import RunCancelResponse +from ....types.evals.run_create_response import RunCreateResponse +from ....types.evals.run_delete_response import RunDeleteResponse +from ....types.evals.run_retrieve_response import RunRetrieveResponse + +__all__ = ["Runs", "AsyncRuns"] + + +class Runs(SyncAPIResource): + @cached_property + def output_items(self) -> OutputItems: + return OutputItems(self._client) + + @cached_property + def with_raw_response(self) -> RunsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RunsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RunsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RunsWithStreamingResponse(self) + + def create( + self, + eval_id: str, + *, + data_source: run_create_params.DataSource, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> RunCreateResponse: + """Create a new evaluation run. + + This is the endpoint that will kick off grading. + + Args: + data_source: Details about the run's data source. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._post( + f"/evals/{eval_id}/runs", + body=maybe_transform( + { + "data_source": data_source, + "metadata": metadata, + "name": name, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunCreateResponse, + ) + + def retrieve( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> RunRetrieveResponse: + """ + Get an evaluation run by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._get( + f"/evals/{eval_id}/runs/{run_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunRetrieveResponse, + ) + + def list( + self, + eval_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorPage[RunListResponse]: + """ + Get a list of runs for an evaluation. + + Args: + after: Identifier for the last run from the previous pagination request. + + limit: Number of runs to retrieve. + + order: Sort order for runs by timestamp. Use `asc` for ascending order or `desc` for + descending order. Defaults to `asc`. + + status: Filter runs by status. Use "queued" | "in_progress" | "failed" | "completed" | + "canceled". + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._get_api_list( + f"/evals/{eval_id}/runs", + page=SyncCursorPage[RunListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + run_list_params.RunListParams, + ), + ), + model=RunListResponse, + ) + + def delete( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> RunDeleteResponse: + """ + Delete an eval run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._delete( + f"/evals/{eval_id}/runs/{run_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunDeleteResponse, + ) + + def cancel( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> RunCancelResponse: + """ + Cancel an ongoing evaluation run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._post( + f"/evals/{eval_id}/runs/{run_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunCancelResponse, + ) + + +class AsyncRuns(AsyncAPIResource): + @cached_property + def output_items(self) -> AsyncOutputItems: + return AsyncOutputItems(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRunsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRunsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRunsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRunsWithStreamingResponse(self) + + async def create( + self, + eval_id: str, + *, + data_source: run_create_params.DataSource, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> RunCreateResponse: + """Create a new evaluation run. + + This is the endpoint that will kick off grading. + + Args: + data_source: Details about the run's data source. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + name: The name of the run. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return await self._post( + f"/evals/{eval_id}/runs", + body=await async_maybe_transform( + { + "data_source": data_source, + "metadata": metadata, + "name": name, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunCreateResponse, + ) + + async def retrieve( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> RunRetrieveResponse: + """ + Get an evaluation run by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._get( + f"/evals/{eval_id}/runs/{run_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunRetrieveResponse, + ) + + def list( + self, + eval_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[RunListResponse, AsyncCursorPage[RunListResponse]]: + """ + Get a list of runs for an evaluation. + + Args: + after: Identifier for the last run from the previous pagination request. + + limit: Number of runs to retrieve. + + order: Sort order for runs by timestamp. Use `asc` for ascending order or `desc` for + descending order. Defaults to `asc`. + + status: Filter runs by status. Use "queued" | "in_progress" | "failed" | "completed" | + "canceled". + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + return self._get_api_list( + f"/evals/{eval_id}/runs", + page=AsyncCursorPage[RunListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "status": status, + }, + run_list_params.RunListParams, + ), + ), + model=RunListResponse, + ) + + async def delete( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> RunDeleteResponse: + """ + Delete an eval run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._delete( + f"/evals/{eval_id}/runs/{run_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunDeleteResponse, + ) + + async def cancel( + self, + run_id: str, + *, + eval_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> RunCancelResponse: + """ + Cancel an ongoing evaluation run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not eval_id: + raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._post( + f"/evals/{eval_id}/runs/{run_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunCancelResponse, + ) + + +class RunsWithRawResponse: + def __init__(self, runs: Runs) -> None: + self._runs = runs + + self.create = _legacy_response.to_raw_response_wrapper( + runs.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + runs.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + runs.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + runs.delete, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + runs.cancel, + ) + + @cached_property + def output_items(self) -> OutputItemsWithRawResponse: + return OutputItemsWithRawResponse(self._runs.output_items) + + +class AsyncRunsWithRawResponse: + def __init__(self, runs: AsyncRuns) -> None: + self._runs = runs + + self.create = _legacy_response.async_to_raw_response_wrapper( + runs.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + runs.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + runs.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + runs.delete, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + runs.cancel, + ) + + @cached_property + def output_items(self) -> AsyncOutputItemsWithRawResponse: + return AsyncOutputItemsWithRawResponse(self._runs.output_items) + + +class RunsWithStreamingResponse: + def __init__(self, runs: Runs) -> None: + self._runs = runs + + self.create = to_streamed_response_wrapper( + runs.create, + ) + self.retrieve = to_streamed_response_wrapper( + runs.retrieve, + ) + self.list = to_streamed_response_wrapper( + runs.list, + ) + self.delete = to_streamed_response_wrapper( + runs.delete, + ) + self.cancel = to_streamed_response_wrapper( + runs.cancel, + ) + + @cached_property + def output_items(self) -> OutputItemsWithStreamingResponse: + return OutputItemsWithStreamingResponse(self._runs.output_items) + + +class AsyncRunsWithStreamingResponse: + def __init__(self, runs: AsyncRuns) -> None: + self._runs = runs + + self.create = async_to_streamed_response_wrapper( + runs.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + runs.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + runs.list, + ) + self.delete = async_to_streamed_response_wrapper( + runs.delete, + ) + self.cancel = async_to_streamed_response_wrapper( + runs.cancel, + ) + + @cached_property + def output_items(self) -> AsyncOutputItemsWithStreamingResponse: + return AsyncOutputItemsWithStreamingResponse(self._runs.output_items) diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py index 7765231fee..ed7db4f4e0 100644 --- a/src/openai/resources/fine_tuning/__init__.py +++ b/src/openai/resources/fine_tuning/__init__.py @@ -8,6 +8,14 @@ JobsWithStreamingResponse, AsyncJobsWithStreamingResponse, ) +from .checkpoints import ( + Checkpoints, + AsyncCheckpoints, + CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse, +) from .fine_tuning import ( FineTuning, AsyncFineTuning, @@ -24,6 +32,12 @@ "AsyncJobsWithRawResponse", "JobsWithStreamingResponse", "AsyncJobsWithStreamingResponse", + "Checkpoints", + "AsyncCheckpoints", + "CheckpointsWithRawResponse", + "AsyncCheckpointsWithRawResponse", + "CheckpointsWithStreamingResponse", + "AsyncCheckpointsWithStreamingResponse", "FineTuning", "AsyncFineTuning", "FineTuningWithRawResponse", diff --git a/src/openai/resources/fine_tuning/checkpoints/__init__.py b/src/openai/resources/fine_tuning/checkpoints/__init__.py new file mode 100644 index 0000000000..fdc37940f9 --- /dev/null +++ b/src/openai/resources/fine_tuning/checkpoints/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .checkpoints import ( + Checkpoints, + AsyncCheckpoints, + CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse, +) +from .permissions import ( + Permissions, + AsyncPermissions, + PermissionsWithRawResponse, + AsyncPermissionsWithRawResponse, + PermissionsWithStreamingResponse, + AsyncPermissionsWithStreamingResponse, +) + +__all__ = [ + "Permissions", + "AsyncPermissions", + "PermissionsWithRawResponse", + "AsyncPermissionsWithRawResponse", + "PermissionsWithStreamingResponse", + "AsyncPermissionsWithStreamingResponse", + "Checkpoints", + "AsyncCheckpoints", + "CheckpointsWithRawResponse", + "AsyncCheckpointsWithRawResponse", + "CheckpointsWithStreamingResponse", + "AsyncCheckpointsWithStreamingResponse", +] diff --git a/src/openai/resources/fine_tuning/checkpoints/checkpoints.py b/src/openai/resources/fine_tuning/checkpoints/checkpoints.py new file mode 100644 index 0000000000..f59976a264 --- /dev/null +++ b/src/openai/resources/fine_tuning/checkpoints/checkpoints.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ...._compat import cached_property +from .permissions import ( + Permissions, + AsyncPermissions, + PermissionsWithRawResponse, + AsyncPermissionsWithRawResponse, + PermissionsWithStreamingResponse, + AsyncPermissionsWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["Checkpoints", "AsyncCheckpoints"] + + +class Checkpoints(SyncAPIResource): + @cached_property + def permissions(self) -> Permissions: + return Permissions(self._client) + + @cached_property + def with_raw_response(self) -> CheckpointsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CheckpointsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CheckpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CheckpointsWithStreamingResponse(self) + + +class AsyncCheckpoints(AsyncAPIResource): + @cached_property + def permissions(self) -> AsyncPermissions: + return AsyncPermissions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncCheckpointsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCheckpointsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCheckpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCheckpointsWithStreamingResponse(self) + + +class CheckpointsWithRawResponse: + def __init__(self, checkpoints: Checkpoints) -> None: + self._checkpoints = checkpoints + + @cached_property + def permissions(self) -> PermissionsWithRawResponse: + return PermissionsWithRawResponse(self._checkpoints.permissions) + + +class AsyncCheckpointsWithRawResponse: + def __init__(self, checkpoints: AsyncCheckpoints) -> None: + self._checkpoints = checkpoints + + @cached_property + def permissions(self) -> AsyncPermissionsWithRawResponse: + return AsyncPermissionsWithRawResponse(self._checkpoints.permissions) + + +class CheckpointsWithStreamingResponse: + def __init__(self, checkpoints: Checkpoints) -> None: + self._checkpoints = checkpoints + + @cached_property + def permissions(self) -> PermissionsWithStreamingResponse: + return PermissionsWithStreamingResponse(self._checkpoints.permissions) + + +class AsyncCheckpointsWithStreamingResponse: + def __init__(self, checkpoints: AsyncCheckpoints) -> None: + self._checkpoints = checkpoints + + @cached_property + def permissions(self) -> AsyncPermissionsWithStreamingResponse: + return AsyncPermissionsWithStreamingResponse(self._checkpoints.permissions) diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py new file mode 100644 index 0000000000..beb7b099d3 --- /dev/null +++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py @@ -0,0 +1,416 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncPage, AsyncPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.fine_tuning.checkpoints import permission_create_params, permission_retrieve_params +from ....types.fine_tuning.checkpoints.permission_create_response import PermissionCreateResponse +from ....types.fine_tuning.checkpoints.permission_delete_response import PermissionDeleteResponse +from ....types.fine_tuning.checkpoints.permission_retrieve_response import PermissionRetrieveResponse + +__all__ = ["Permissions", "AsyncPermissions"] + + +class Permissions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return PermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return PermissionsWithStreamingResponse(self) + + def create( + self, + fine_tuned_model_checkpoint: str, + *, + project_ids: List[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncPage[PermissionCreateResponse]: + """ + **NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys). + + This enables organization owners to share fine-tuned models with other projects + in their organization. + + Args: + project_ids: The project identifiers to grant access to. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get_api_list( + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + page=SyncPage[PermissionCreateResponse], + body=maybe_transform({"project_ids": project_ids}, permission_create_params.PermissionCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=PermissionCreateResponse, + method="post", + ) + + def retrieve( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["ascending", "descending"] | NotGiven = NOT_GIVEN, + project_id: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PermissionRetrieveResponse: + """ + **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). + + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + after: Identifier for the last permission ID from the previous pagination request. + + limit: Number of permissions to retrieve. + + order: The order in which to retrieve permissions. + + project_id: The ID of the project to get permissions for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get( + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + permission_retrieve_params.PermissionRetrieveParams, + ), + ), + cast_to=PermissionRetrieveResponse, + ) + + def delete( + self, + fine_tuned_model_checkpoint: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PermissionDeleteResponse: + """ + **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). + + Organization owners can use this endpoint to delete a permission for a + fine-tuned model checkpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._delete( + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PermissionDeleteResponse, + ) + + +class AsyncPermissions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncPermissionsWithStreamingResponse(self) + + def create( + self, + fine_tuned_model_checkpoint: str, + *, + project_ids: List[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[PermissionCreateResponse, AsyncPage[PermissionCreateResponse]]: + """ + **NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys). + + This enables organization owners to share fine-tuned models with other projects + in their organization. + + Args: + project_ids: The project identifiers to grant access to. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get_api_list( + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + page=AsyncPage[PermissionCreateResponse], + body=maybe_transform({"project_ids": project_ids}, permission_create_params.PermissionCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=PermissionCreateResponse, + method="post", + ) + + async def retrieve( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["ascending", "descending"] | NotGiven = NOT_GIVEN, + project_id: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PermissionRetrieveResponse: + """ + **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). + + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + after: Identifier for the last permission ID from the previous pagination request. + + limit: Number of permissions to retrieve. + + order: The order in which to retrieve permissions. + + project_id: The ID of the project to get permissions for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return await self._get( + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + permission_retrieve_params.PermissionRetrieveParams, + ), + ), + cast_to=PermissionRetrieveResponse, + ) + + async def delete( + self, + fine_tuned_model_checkpoint: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PermissionDeleteResponse: + """ + **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). + + Organization owners can use this endpoint to delete a permission for a + fine-tuned model checkpoint. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return await self._delete( + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PermissionDeleteResponse, + ) + + +class PermissionsWithRawResponse: + def __init__(self, permissions: Permissions) -> None: + self._permissions = permissions + + self.create = _legacy_response.to_raw_response_wrapper( + permissions.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + permissions.retrieve, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + permissions.delete, + ) + + +class AsyncPermissionsWithRawResponse: + def __init__(self, permissions: AsyncPermissions) -> None: + self._permissions = permissions + + self.create = _legacy_response.async_to_raw_response_wrapper( + permissions.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + permissions.retrieve, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + permissions.delete, + ) + + +class PermissionsWithStreamingResponse: + def __init__(self, permissions: Permissions) -> None: + self._permissions = permissions + + self.create = to_streamed_response_wrapper( + permissions.create, + ) + self.retrieve = to_streamed_response_wrapper( + permissions.retrieve, + ) + self.delete = to_streamed_response_wrapper( + permissions.delete, + ) + + +class AsyncPermissionsWithStreamingResponse: + def __init__(self, permissions: AsyncPermissions) -> None: + self._permissions = permissions + + self.create = async_to_streamed_response_wrapper( + permissions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + permissions.retrieve, + ) + self.delete = async_to_streamed_response_wrapper( + permissions.delete, + ) diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index eebde07d81..1388c8230c 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -12,6 +12,14 @@ AsyncJobsWithStreamingResponse, ) from ..._resource import SyncAPIResource, AsyncAPIResource +from .checkpoints.checkpoints import ( + Checkpoints, + AsyncCheckpoints, + CheckpointsWithRawResponse, + AsyncCheckpointsWithRawResponse, + CheckpointsWithStreamingResponse, + AsyncCheckpointsWithStreamingResponse, +) __all__ = ["FineTuning", "AsyncFineTuning"] @@ -21,6 +29,10 @@ class FineTuning(SyncAPIResource): def jobs(self) -> Jobs: return Jobs(self._client) + @cached_property + def checkpoints(self) -> Checkpoints: + return Checkpoints(self._client) + @cached_property def with_raw_response(self) -> FineTuningWithRawResponse: """ @@ -46,6 +58,10 @@ class AsyncFineTuning(AsyncAPIResource): def jobs(self) -> AsyncJobs: return AsyncJobs(self._client) + @cached_property + def checkpoints(self) -> AsyncCheckpoints: + return AsyncCheckpoints(self._client) + @cached_property def with_raw_response(self) -> AsyncFineTuningWithRawResponse: """ @@ -74,6 +90,10 @@ def __init__(self, fine_tuning: FineTuning) -> None: def jobs(self) -> JobsWithRawResponse: return JobsWithRawResponse(self._fine_tuning.jobs) + @cached_property + def checkpoints(self) -> CheckpointsWithRawResponse: + return CheckpointsWithRawResponse(self._fine_tuning.checkpoints) + class AsyncFineTuningWithRawResponse: def __init__(self, fine_tuning: AsyncFineTuning) -> None: @@ -83,6 +103,10 @@ def __init__(self, fine_tuning: AsyncFineTuning) -> None: def jobs(self) -> AsyncJobsWithRawResponse: return AsyncJobsWithRawResponse(self._fine_tuning.jobs) + @cached_property + def checkpoints(self) -> AsyncCheckpointsWithRawResponse: + return AsyncCheckpointsWithRawResponse(self._fine_tuning.checkpoints) + class FineTuningWithStreamingResponse: def __init__(self, fine_tuning: FineTuning) -> None: @@ -92,6 +116,10 @@ def __init__(self, fine_tuning: FineTuning) -> None: def jobs(self) -> JobsWithStreamingResponse: return JobsWithStreamingResponse(self._fine_tuning.jobs) + @cached_property + def checkpoints(self) -> CheckpointsWithStreamingResponse: + return CheckpointsWithStreamingResponse(self._fine_tuning.checkpoints) + class AsyncFineTuningWithStreamingResponse: def __init__(self, fine_tuning: AsyncFineTuning) -> None: @@ -100,3 +128,7 @@ def __init__(self, fine_tuning: AsyncFineTuning) -> None: @cached_property def jobs(self) -> AsyncJobsWithStreamingResponse: return AsyncJobsWithStreamingResponse(self._fine_tuning.jobs) + + @cached_property + def checkpoints(self) -> AsyncCheckpointsWithStreamingResponse: + return AsyncCheckpointsWithStreamingResponse(self._fine_tuning.checkpoints) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 11761534c9..57c91811b9 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -38,22 +38,32 @@ from .embedding_model import EmbeddingModel as EmbeddingModel from .images_response import ImagesResponse as ImagesResponse from .completion_usage import CompletionUsage as CompletionUsage +from .eval_list_params import EvalListParams as EvalListParams from .file_list_params import FileListParams as FileListParams from .moderation_model import ModerationModel as ModerationModel from .batch_list_params import BatchListParams as BatchListParams from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams +from .eval_create_params import EvalCreateParams as EvalCreateParams +from .eval_list_response import EvalListResponse as EvalListResponse +from .eval_update_params import EvalUpdateParams as EvalUpdateParams from .file_create_params import FileCreateParams as FileCreateParams from .batch_create_params import BatchCreateParams as BatchCreateParams from .batch_request_counts import BatchRequestCounts as BatchRequestCounts +from .eval_create_response import EvalCreateResponse as EvalCreateResponse +from .eval_delete_response import EvalDeleteResponse as EvalDeleteResponse +from .eval_update_response import EvalUpdateResponse as EvalUpdateResponse from .upload_create_params import UploadCreateParams as UploadCreateParams from .vector_store_deleted import VectorStoreDeleted as VectorStoreDeleted from .audio_response_format import AudioResponseFormat as AudioResponseFormat from .image_generate_params import ImageGenerateParams as ImageGenerateParams +from .eval_retrieve_response import EvalRetrieveResponse as EvalRetrieveResponse from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy from .upload_complete_params import UploadCompleteParams as UploadCompleteParams from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams +from .eval_label_model_grader import EvalLabelModelGrader as EvalLabelModelGrader from .completion_create_params import CompletionCreateParams as CompletionCreateParams +from .eval_string_check_grader import EvalStringCheckGrader as EvalStringCheckGrader from .moderation_create_params import ModerationCreateParams as ModerationCreateParams from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse @@ -61,18 +71,25 @@ from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams from .vector_store_search_params import VectorStoreSearchParams as VectorStoreSearchParams from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams +from .eval_text_similarity_grader import EvalTextSimilarityGrader as EvalTextSimilarityGrader from .moderation_text_input_param import ModerationTextInputParam as ModerationTextInputParam from .file_chunking_strategy_param import FileChunkingStrategyParam as FileChunkingStrategyParam from .vector_store_search_response import VectorStoreSearchResponse as VectorStoreSearchResponse from .websocket_connection_options import WebsocketConnectionOptions as WebsocketConnectionOptions from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy +from .eval_custom_data_source_config import EvalCustomDataSourceConfig as EvalCustomDataSourceConfig +from .eval_string_check_grader_param import EvalStringCheckGraderParam as EvalStringCheckGraderParam from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam as AutoFileChunkingStrategyParam +from .eval_text_similarity_grader_param import EvalTextSimilarityGraderParam as EvalTextSimilarityGraderParam from .moderation_multi_modal_input_param import ModerationMultiModalInputParam as ModerationMultiModalInputParam from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject as OtherFileChunkingStrategyObject from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam as StaticFileChunkingStrategyParam from .static_file_chunking_strategy_object import StaticFileChunkingStrategyObject as StaticFileChunkingStrategyObject +from .eval_stored_completions_data_source_config import ( + EvalStoredCompletionsDataSourceConfig as EvalStoredCompletionsDataSourceConfig, +) from .static_file_chunking_strategy_object_param import ( StaticFileChunkingStrategyObjectParam as StaticFileChunkingStrategyObjectParam, ) diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py new file mode 100644 index 0000000000..8b28e51a6b --- /dev/null +++ b/src/openai/types/eval_create_params.py @@ -0,0 +1,153 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .shared_params.metadata import Metadata +from .eval_string_check_grader_param import EvalStringCheckGraderParam +from .eval_text_similarity_grader_param import EvalTextSimilarityGraderParam + +__all__ = [ + "EvalCreateParams", + "DataSourceConfig", + "DataSourceConfigCustom", + "DataSourceConfigStoredCompletions", + "TestingCriterion", + "TestingCriterionLabelModel", + "TestingCriterionLabelModelInput", + "TestingCriterionLabelModelInputSimpleInputMessage", + "TestingCriterionLabelModelInputInputMessage", + "TestingCriterionLabelModelInputInputMessageContent", + "TestingCriterionLabelModelInputOutputMessage", + "TestingCriterionLabelModelInputOutputMessageContent", +] + + +class EvalCreateParams(TypedDict, total=False): + data_source_config: Required[DataSourceConfig] + """The configuration for the data source used for the evaluation runs.""" + + testing_criteria: Required[Iterable[TestingCriterion]] + """A list of graders for all eval runs in this group.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + share_with_openai: bool + """Indicates whether the evaluation is shared with OpenAI.""" + + +class DataSourceConfigCustom(TypedDict, total=False): + item_schema: Required[Dict[str, object]] + """The json schema for the run data source items.""" + + type: Required[Literal["custom"]] + """The type of data source. Always `custom`.""" + + include_sample_schema: bool + """Whether to include the sample schema in the data source.""" + + +class DataSourceConfigStoredCompletions(TypedDict, total=False): + type: Required[Literal["stored_completions"]] + """The type of data source. Always `stored_completions`.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + +DataSourceConfig: TypeAlias = Union[DataSourceConfigCustom, DataSourceConfigStoredCompletions] + + +class TestingCriterionLabelModelInputSimpleInputMessage(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[str] + """The role of the message (e.g. "system", "assistant", "user").""" + + +class TestingCriterionLabelModelInputInputMessageContent(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["input_text"]] + """The type of content, which is always `input_text`.""" + + +class TestingCriterionLabelModelInputInputMessage(TypedDict, total=False): + content: Required[TestingCriterionLabelModelInputInputMessageContent] + + role: Required[Literal["user", "system", "developer"]] + """The role of the message. One of `user`, `system`, or `developer`.""" + + type: Required[Literal["message"]] + """The type of item, which is always `message`.""" + + +class TestingCriterionLabelModelInputOutputMessageContent(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["output_text"]] + """The type of content, which is always `output_text`.""" + + +class TestingCriterionLabelModelInputOutputMessage(TypedDict, total=False): + content: Required[TestingCriterionLabelModelInputOutputMessageContent] + + role: Required[Literal["assistant"]] + """The role of the message. Must be `assistant` for output.""" + + type: Required[Literal["message"]] + """The type of item, which is always `message`.""" + + +TestingCriterionLabelModelInput: TypeAlias = Union[ + TestingCriterionLabelModelInputSimpleInputMessage, + TestingCriterionLabelModelInputInputMessage, + TestingCriterionLabelModelInputOutputMessage, +] + + +class TestingCriterionLabelModel(TypedDict, total=False): + input: Required[Iterable[TestingCriterionLabelModelInput]] + + labels: Required[List[str]] + """The labels to classify to each item in the evaluation.""" + + model: Required[str] + """The model to use for the evaluation. Must support structured outputs.""" + + name: Required[str] + """The name of the grader.""" + + passing_labels: Required[List[str]] + """The labels that indicate a passing result. Must be a subset of labels.""" + + type: Required[Literal["label_model"]] + """The object type, which is always `label_model`.""" + + +TestingCriterion: TypeAlias = Union[ + TestingCriterionLabelModel, EvalStringCheckGraderParam, EvalTextSimilarityGraderParam +] diff --git a/src/openai/types/eval_create_response.py b/src/openai/types/eval_create_response.py new file mode 100644 index 0000000000..a1c2853a2a --- /dev/null +++ b/src/openai/types/eval_create_response.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.metadata import Metadata +from .eval_label_model_grader import EvalLabelModelGrader +from .eval_string_check_grader import EvalStringCheckGrader +from .eval_text_similarity_grader import EvalTextSimilarityGrader +from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig + +__all__ = ["EvalCreateResponse", "DataSourceConfig", "TestingCriterion"] + +DataSourceConfig: TypeAlias = Annotated[ + Union[EvalCustomDataSourceConfig, EvalStoredCompletionsDataSourceConfig], PropertyInfo(discriminator="type") +] + +TestingCriterion: TypeAlias = Annotated[ + Union[EvalLabelModelGrader, EvalStringCheckGrader, EvalTextSimilarityGrader], PropertyInfo(discriminator="type") +] + + +class EvalCreateResponse(BaseModel): + id: str + """Unique identifier for the evaluation.""" + + created_at: int + """The Unix timestamp (in seconds) for when the eval was created.""" + + data_source_config: DataSourceConfig + """Configuration of data sources used in runs of the evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + object: Literal["eval"] + """The object type.""" + + share_with_openai: bool + """Indicates whether the evaluation is shared with OpenAI.""" + + testing_criteria: List[TestingCriterion] + """A list of testing criteria.""" diff --git a/src/openai/types/eval_custom_data_source_config.py b/src/openai/types/eval_custom_data_source_config.py new file mode 100644 index 0000000000..d99701cc71 --- /dev/null +++ b/src/openai/types/eval_custom_data_source_config.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EvalCustomDataSourceConfig"] + + +class EvalCustomDataSourceConfig(BaseModel): + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The json schema for the run data source items. Learn how to build JSON schemas + [here](https://json-schema.org/). + """ + + type: Literal["custom"] + """The type of data source. Always `custom`.""" diff --git a/src/openai/types/eval_delete_response.py b/src/openai/types/eval_delete_response.py new file mode 100644 index 0000000000..adb460ddbb --- /dev/null +++ b/src/openai/types/eval_delete_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from .._models import BaseModel + +__all__ = ["EvalDeleteResponse"] + + +class EvalDeleteResponse(BaseModel): + deleted: bool + + eval_id: str + + object: str diff --git a/src/openai/types/eval_label_model_grader.py b/src/openai/types/eval_label_model_grader.py new file mode 100644 index 0000000000..826b116287 --- /dev/null +++ b/src/openai/types/eval_label_model_grader.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel + +__all__ = [ + "EvalLabelModelGrader", + "Input", + "InputInputMessage", + "InputInputMessageContent", + "InputAssistant", + "InputAssistantContent", +] + + +class InputInputMessageContent(BaseModel): + text: str + """The text content.""" + + type: Literal["input_text"] + """The type of content, which is always `input_text`.""" + + +class InputInputMessage(BaseModel): + content: InputInputMessageContent + + role: Literal["user", "system", "developer"] + """The role of the message. One of `user`, `system`, or `developer`.""" + + type: Literal["message"] + """The type of item, which is always `message`.""" + + +class InputAssistantContent(BaseModel): + text: str + """The text content.""" + + type: Literal["output_text"] + """The type of content, which is always `output_text`.""" + + +class InputAssistant(BaseModel): + content: InputAssistantContent + + role: Literal["assistant"] + """The role of the message. Must be `assistant` for output.""" + + type: Literal["message"] + """The type of item, which is always `message`.""" + + +Input: TypeAlias = Annotated[Union[InputInputMessage, InputAssistant], PropertyInfo(discriminator="role")] + + +class EvalLabelModelGrader(BaseModel): + input: List[Input] + + labels: List[str] + """The labels to assign to each item in the evaluation.""" + + model: str + """The model to use for the evaluation. Must support structured outputs.""" + + name: str + """The name of the grader.""" + + passing_labels: List[str] + """The labels that indicate a passing result. Must be a subset of labels.""" + + type: Literal["label_model"] + """The object type, which is always `label_model`.""" diff --git a/src/openai/types/eval_list_params.py b/src/openai/types/eval_list_params.py new file mode 100644 index 0000000000..d9a12d0ddf --- /dev/null +++ b/src/openai/types/eval_list_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["EvalListParams"] + + +class EvalListParams(TypedDict, total=False): + after: str + """Identifier for the last eval from the previous pagination request.""" + + limit: int + """Number of evals to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order for evals by timestamp. + + Use `asc` for ascending order or `desc` for descending order. + """ + + order_by: Literal["created_at", "updated_at"] + """Evals can be ordered by creation time or last updated time. + + Use `created_at` for creation time or `updated_at` for last updated time. + """ diff --git a/src/openai/types/eval_list_response.py b/src/openai/types/eval_list_response.py new file mode 100644 index 0000000000..eb54569011 --- /dev/null +++ b/src/openai/types/eval_list_response.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.metadata import Metadata +from .eval_label_model_grader import EvalLabelModelGrader +from .eval_string_check_grader import EvalStringCheckGrader +from .eval_text_similarity_grader import EvalTextSimilarityGrader +from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig + +__all__ = ["EvalListResponse", "DataSourceConfig", "TestingCriterion"] + +DataSourceConfig: TypeAlias = Annotated[ + Union[EvalCustomDataSourceConfig, EvalStoredCompletionsDataSourceConfig], PropertyInfo(discriminator="type") +] + +TestingCriterion: TypeAlias = Annotated[ + Union[EvalLabelModelGrader, EvalStringCheckGrader, EvalTextSimilarityGrader], PropertyInfo(discriminator="type") +] + + +class EvalListResponse(BaseModel): + id: str + """Unique identifier for the evaluation.""" + + created_at: int + """The Unix timestamp (in seconds) for when the eval was created.""" + + data_source_config: DataSourceConfig + """Configuration of data sources used in runs of the evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + object: Literal["eval"] + """The object type.""" + + share_with_openai: bool + """Indicates whether the evaluation is shared with OpenAI.""" + + testing_criteria: List[TestingCriterion] + """A list of testing criteria.""" diff --git a/src/openai/types/eval_retrieve_response.py b/src/openai/types/eval_retrieve_response.py new file mode 100644 index 0000000000..8f3bfdf902 --- /dev/null +++ b/src/openai/types/eval_retrieve_response.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.metadata import Metadata +from .eval_label_model_grader import EvalLabelModelGrader +from .eval_string_check_grader import EvalStringCheckGrader +from .eval_text_similarity_grader import EvalTextSimilarityGrader +from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig + +__all__ = ["EvalRetrieveResponse", "DataSourceConfig", "TestingCriterion"] + +DataSourceConfig: TypeAlias = Annotated[ + Union[EvalCustomDataSourceConfig, EvalStoredCompletionsDataSourceConfig], PropertyInfo(discriminator="type") +] + +TestingCriterion: TypeAlias = Annotated[ + Union[EvalLabelModelGrader, EvalStringCheckGrader, EvalTextSimilarityGrader], PropertyInfo(discriminator="type") +] + + +class EvalRetrieveResponse(BaseModel): + id: str + """Unique identifier for the evaluation.""" + + created_at: int + """The Unix timestamp (in seconds) for when the eval was created.""" + + data_source_config: DataSourceConfig + """Configuration of data sources used in runs of the evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + object: Literal["eval"] + """The object type.""" + + share_with_openai: bool + """Indicates whether the evaluation is shared with OpenAI.""" + + testing_criteria: List[TestingCriterion] + """A list of testing criteria.""" diff --git a/src/openai/types/eval_stored_completions_data_source_config.py b/src/openai/types/eval_stored_completions_data_source_config.py new file mode 100644 index 0000000000..98f86a4719 --- /dev/null +++ b/src/openai/types/eval_stored_completions_data_source_config.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel +from .shared.metadata import Metadata + +__all__ = ["EvalStoredCompletionsDataSourceConfig"] + + +class EvalStoredCompletionsDataSourceConfig(BaseModel): + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The json schema for the run data source items. Learn how to build JSON schemas + [here](https://json-schema.org/). + """ + + type: Literal["stored_completions"] + """The type of data source. Always `stored_completions`.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ diff --git a/src/openai/types/eval_string_check_grader.py b/src/openai/types/eval_string_check_grader.py new file mode 100644 index 0000000000..4dfc8035f9 --- /dev/null +++ b/src/openai/types/eval_string_check_grader.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["EvalStringCheckGrader"] + + +class EvalStringCheckGrader(BaseModel): + input: str + """The input text. This may include template strings.""" + + name: str + """The name of the grader.""" + + operation: Literal["eq", "ne", "like", "ilike"] + """The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`.""" + + reference: str + """The reference text. This may include template strings.""" + + type: Literal["string_check"] + """The object type, which is always `string_check`.""" diff --git a/src/openai/types/eval_string_check_grader_param.py b/src/openai/types/eval_string_check_grader_param.py new file mode 100644 index 0000000000..3511329f8b --- /dev/null +++ b/src/openai/types/eval_string_check_grader_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["EvalStringCheckGraderParam"] + + +class EvalStringCheckGraderParam(TypedDict, total=False): + input: Required[str] + """The input text. This may include template strings.""" + + name: Required[str] + """The name of the grader.""" + + operation: Required[Literal["eq", "ne", "like", "ilike"]] + """The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`.""" + + reference: Required[str] + """The reference text. This may include template strings.""" + + type: Required[Literal["string_check"]] + """The object type, which is always `string_check`.""" diff --git a/src/openai/types/eval_text_similarity_grader.py b/src/openai/types/eval_text_similarity_grader.py new file mode 100644 index 0000000000..7c6897a4a7 --- /dev/null +++ b/src/openai/types/eval_text_similarity_grader.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["EvalTextSimilarityGrader"] + + +class EvalTextSimilarityGrader(BaseModel): + evaluation_metric: Literal[ + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l", + "cosine", + ] + """The evaluation metric to use. + + One of `cosine`, `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, + `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. + """ + + input: str + """The text being graded.""" + + pass_threshold: float + """A float score where a value greater than or equal indicates a passing grade.""" + + reference: str + """The text being graded against.""" + + type: Literal["text_similarity"] + """The type of grader.""" + + name: Optional[str] = None + """The name of the grader.""" diff --git a/src/openai/types/eval_text_similarity_grader_param.py b/src/openai/types/eval_text_similarity_grader_param.py new file mode 100644 index 0000000000..4bf5d586f3 --- /dev/null +++ b/src/openai/types/eval_text_similarity_grader_param.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["EvalTextSimilarityGraderParam"] + + +class EvalTextSimilarityGraderParam(TypedDict, total=False): + evaluation_metric: Required[ + Literal[ + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l", + "cosine", + ] + ] + """The evaluation metric to use. + + One of `cosine`, `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, + `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. + """ + + input: Required[str] + """The text being graded.""" + + pass_threshold: Required[float] + """A float score where a value greater than or equal indicates a passing grade.""" + + reference: Required[str] + """The text being graded against.""" + + type: Required[Literal["text_similarity"]] + """The type of grader.""" + + name: str + """The name of the grader.""" diff --git a/src/openai/types/eval_update_params.py b/src/openai/types/eval_update_params.py new file mode 100644 index 0000000000..042db29af5 --- /dev/null +++ b/src/openai/types/eval_update_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from .shared_params.metadata import Metadata + +__all__ = ["EvalUpdateParams"] + + +class EvalUpdateParams(TypedDict, total=False): + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """Rename the evaluation.""" diff --git a/src/openai/types/eval_update_response.py b/src/openai/types/eval_update_response.py new file mode 100644 index 0000000000..728a291736 --- /dev/null +++ b/src/openai/types/eval_update_response.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.metadata import Metadata +from .eval_label_model_grader import EvalLabelModelGrader +from .eval_string_check_grader import EvalStringCheckGrader +from .eval_text_similarity_grader import EvalTextSimilarityGrader +from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig + +__all__ = ["EvalUpdateResponse", "DataSourceConfig", "TestingCriterion"] + +DataSourceConfig: TypeAlias = Annotated[ + Union[EvalCustomDataSourceConfig, EvalStoredCompletionsDataSourceConfig], PropertyInfo(discriminator="type") +] + +TestingCriterion: TypeAlias = Annotated[ + Union[EvalLabelModelGrader, EvalStringCheckGrader, EvalTextSimilarityGrader], PropertyInfo(discriminator="type") +] + + +class EvalUpdateResponse(BaseModel): + id: str + """Unique identifier for the evaluation.""" + + created_at: int + """The Unix timestamp (in seconds) for when the eval was created.""" + + data_source_config: DataSourceConfig + """Configuration of data sources used in runs of the evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the evaluation.""" + + object: Literal["eval"] + """The object type.""" + + share_with_openai: bool + """Indicates whether the evaluation is shared with OpenAI.""" + + testing_criteria: List[TestingCriterion] + """A list of testing criteria.""" diff --git a/src/openai/types/evals/__init__.py b/src/openai/types/evals/__init__.py new file mode 100644 index 0000000000..ebf84c6b8d --- /dev/null +++ b/src/openai/types/evals/__init__.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .eval_api_error import EvalAPIError as EvalAPIError +from .run_list_params import RunListParams as RunListParams +from .run_create_params import RunCreateParams as RunCreateParams +from .run_list_response import RunListResponse as RunListResponse +from .run_cancel_response import RunCancelResponse as RunCancelResponse +from .run_create_response import RunCreateResponse as RunCreateResponse +from .run_delete_response import RunDeleteResponse as RunDeleteResponse +from .run_retrieve_response import RunRetrieveResponse as RunRetrieveResponse +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource as CreateEvalJSONLRunDataSource +from .create_eval_completions_run_data_source import ( + CreateEvalCompletionsRunDataSource as CreateEvalCompletionsRunDataSource, +) +from .create_eval_jsonl_run_data_source_param import ( + CreateEvalJSONLRunDataSourceParam as CreateEvalJSONLRunDataSourceParam, +) +from .create_eval_completions_run_data_source_param import ( + CreateEvalCompletionsRunDataSourceParam as CreateEvalCompletionsRunDataSourceParam, +) diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py new file mode 100644 index 0000000000..07b88129e2 --- /dev/null +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -0,0 +1,185 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from ..shared.metadata import Metadata + +__all__ = [ + "CreateEvalCompletionsRunDataSource", + "InputMessages", + "InputMessagesTemplate", + "InputMessagesTemplateTemplate", + "InputMessagesTemplateTemplateChatMessage", + "InputMessagesTemplateTemplateInputMessage", + "InputMessagesTemplateTemplateInputMessageContent", + "InputMessagesTemplateTemplateOutputMessage", + "InputMessagesTemplateTemplateOutputMessageContent", + "InputMessagesItemReference", + "Source", + "SourceFileContent", + "SourceFileContentContent", + "SourceFileID", + "SourceStoredCompletions", + "SamplingParams", +] + + +class InputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class InputMessagesTemplateTemplateInputMessageContent(BaseModel): + text: str + """The text content.""" + + type: Literal["input_text"] + """The type of content, which is always `input_text`.""" + + +class InputMessagesTemplateTemplateInputMessage(BaseModel): + content: InputMessagesTemplateTemplateInputMessageContent + + role: Literal["user", "system", "developer"] + """The role of the message. One of `user`, `system`, or `developer`.""" + + type: Literal["message"] + """The type of item, which is always `message`.""" + + +class InputMessagesTemplateTemplateOutputMessageContent(BaseModel): + text: str + """The text content.""" + + type: Literal["output_text"] + """The type of content, which is always `output_text`.""" + + +class InputMessagesTemplateTemplateOutputMessage(BaseModel): + content: InputMessagesTemplateTemplateOutputMessageContent + + role: Literal["assistant"] + """The role of the message. Must be `assistant` for output.""" + + type: Literal["message"] + """The type of item, which is always `message`.""" + + +InputMessagesTemplateTemplate: TypeAlias = Union[ + InputMessagesTemplateTemplateChatMessage, + InputMessagesTemplateTemplateInputMessage, + InputMessagesTemplateTemplateOutputMessage, +] + + +class InputMessagesTemplate(BaseModel): + template: List[InputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class InputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +InputMessages: TypeAlias = Annotated[ + Union[InputMessagesTemplate, InputMessagesItemReference], PropertyInfo(discriminator="type") +] + + +class SourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class SourceFileContent(BaseModel): + content: List[SourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class SourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class SourceStoredCompletions(BaseModel): + created_after: Optional[int] = None + """An optional Unix timestamp to filter items created after this time.""" + + created_before: Optional[int] = None + """An optional Unix timestamp to filter items created before this time.""" + + limit: Optional[int] = None + """An optional maximum number of items to return.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Optional[str] = None + """An optional model to filter by (e.g., 'gpt-4o').""" + + type: Literal["stored_completions"] + """The type of source. Always `stored_completions`.""" + + +Source: TypeAlias = Annotated[ + Union[SourceFileContent, SourceFileID, SourceStoredCompletions], PropertyInfo(discriminator="type") +] + + +class SamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class CreateEvalCompletionsRunDataSource(BaseModel): + input_messages: InputMessages + + model: str + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + source: Source + """A StoredCompletionsRunDataSource configuration describing a set of filters""" + + type: Literal["completions"] + """The type of run data source. Always `completions`.""" + + sampling_params: Optional[SamplingParams] = None diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py new file mode 100644 index 0000000000..be4a6f1ec6 --- /dev/null +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -0,0 +1,181 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..shared_params.metadata import Metadata + +__all__ = [ + "CreateEvalCompletionsRunDataSourceParam", + "InputMessages", + "InputMessagesTemplate", + "InputMessagesTemplateTemplate", + "InputMessagesTemplateTemplateChatMessage", + "InputMessagesTemplateTemplateInputMessage", + "InputMessagesTemplateTemplateInputMessageContent", + "InputMessagesTemplateTemplateOutputMessage", + "InputMessagesTemplateTemplateOutputMessageContent", + "InputMessagesItemReference", + "Source", + "SourceFileContent", + "SourceFileContentContent", + "SourceFileID", + "SourceStoredCompletions", + "SamplingParams", +] + + +class InputMessagesTemplateTemplateChatMessage(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[str] + """The role of the message (e.g. "system", "assistant", "user").""" + + +class InputMessagesTemplateTemplateInputMessageContent(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["input_text"]] + """The type of content, which is always `input_text`.""" + + +class InputMessagesTemplateTemplateInputMessage(TypedDict, total=False): + content: Required[InputMessagesTemplateTemplateInputMessageContent] + + role: Required[Literal["user", "system", "developer"]] + """The role of the message. One of `user`, `system`, or `developer`.""" + + type: Required[Literal["message"]] + """The type of item, which is always `message`.""" + + +class InputMessagesTemplateTemplateOutputMessageContent(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["output_text"]] + """The type of content, which is always `output_text`.""" + + +class InputMessagesTemplateTemplateOutputMessage(TypedDict, total=False): + content: Required[InputMessagesTemplateTemplateOutputMessageContent] + + role: Required[Literal["assistant"]] + """The role of the message. Must be `assistant` for output.""" + + type: Required[Literal["message"]] + """The type of item, which is always `message`.""" + + +InputMessagesTemplateTemplate: TypeAlias = Union[ + InputMessagesTemplateTemplateChatMessage, + InputMessagesTemplateTemplateInputMessage, + InputMessagesTemplateTemplateOutputMessage, +] + + +class InputMessagesTemplate(TypedDict, total=False): + template: Required[Iterable[InputMessagesTemplateTemplate]] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Required[Literal["template"]] + """The type of input messages. Always `template`.""" + + +class InputMessagesItemReference(TypedDict, total=False): + item_reference: Required[str] + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Required[Literal["item_reference"]] + """The type of input messages. Always `item_reference`.""" + + +InputMessages: TypeAlias = Union[InputMessagesTemplate, InputMessagesItemReference] + + +class SourceFileContentContent(TypedDict, total=False): + item: Required[Dict[str, object]] + + sample: Dict[str, object] + + +class SourceFileContent(TypedDict, total=False): + content: Required[Iterable[SourceFileContentContent]] + """The content of the jsonl file.""" + + type: Required[Literal["file_content"]] + """The type of jsonl source. Always `file_content`.""" + + +class SourceFileID(TypedDict, total=False): + id: Required[str] + """The identifier of the file.""" + + type: Required[Literal["file_id"]] + """The type of jsonl source. Always `file_id`.""" + + +class SourceStoredCompletions(TypedDict, total=False): + created_after: Required[Optional[int]] + """An optional Unix timestamp to filter items created after this time.""" + + created_before: Required[Optional[int]] + """An optional Unix timestamp to filter items created before this time.""" + + limit: Required[Optional[int]] + """An optional maximum number of items to return.""" + + metadata: Required[Optional[Metadata]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Required[Optional[str]] + """An optional model to filter by (e.g., 'gpt-4o').""" + + type: Required[Literal["stored_completions"]] + """The type of source. Always `stored_completions`.""" + + +Source: TypeAlias = Union[SourceFileContent, SourceFileID, SourceStoredCompletions] + + +class SamplingParams(TypedDict, total=False): + max_completion_tokens: int + """The maximum number of tokens in the generated output.""" + + seed: int + """A seed value to initialize the randomness, during sampling.""" + + temperature: float + """A higher temperature increases randomness in the outputs.""" + + top_p: float + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class CreateEvalCompletionsRunDataSourceParam(TypedDict, total=False): + input_messages: Required[InputMessages] + + model: Required[str] + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + source: Required[Source] + """A StoredCompletionsRunDataSource configuration describing a set of filters""" + + type: Required[Literal["completions"]] + """The type of run data source. Always `completions`.""" + + sampling_params: SamplingParams diff --git a/src/openai/types/evals/create_eval_jsonl_run_data_source.py b/src/openai/types/evals/create_eval_jsonl_run_data_source.py new file mode 100644 index 0000000000..d2be56243b --- /dev/null +++ b/src/openai/types/evals/create_eval_jsonl_run_data_source.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["CreateEvalJSONLRunDataSource", "Source", "SourceFileContent", "SourceFileContentContent", "SourceFileID"] + + +class SourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class SourceFileContent(BaseModel): + content: List[SourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class SourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +Source: TypeAlias = Annotated[Union[SourceFileContent, SourceFileID], PropertyInfo(discriminator="type")] + + +class CreateEvalJSONLRunDataSource(BaseModel): + source: Source + + type: Literal["jsonl"] + """The type of data source. Always `jsonl`.""" diff --git a/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py b/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py new file mode 100644 index 0000000000..b8ba48a666 --- /dev/null +++ b/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "CreateEvalJSONLRunDataSourceParam", + "Source", + "SourceFileContent", + "SourceFileContentContent", + "SourceFileID", +] + + +class SourceFileContentContent(TypedDict, total=False): + item: Required[Dict[str, object]] + + sample: Dict[str, object] + + +class SourceFileContent(TypedDict, total=False): + content: Required[Iterable[SourceFileContentContent]] + """The content of the jsonl file.""" + + type: Required[Literal["file_content"]] + """The type of jsonl source. Always `file_content`.""" + + +class SourceFileID(TypedDict, total=False): + id: Required[str] + """The identifier of the file.""" + + type: Required[Literal["file_id"]] + """The type of jsonl source. Always `file_id`.""" + + +Source: TypeAlias = Union[SourceFileContent, SourceFileID] + + +class CreateEvalJSONLRunDataSourceParam(TypedDict, total=False): + source: Required[Source] + + type: Required[Literal["jsonl"]] + """The type of data source. Always `jsonl`.""" diff --git a/src/openai/types/evals/eval_api_error.py b/src/openai/types/evals/eval_api_error.py new file mode 100644 index 0000000000..d67185e981 --- /dev/null +++ b/src/openai/types/evals/eval_api_error.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["EvalAPIError"] + + +class EvalAPIError(BaseModel): + code: str + """The error code.""" + + message: str + """The error message.""" diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py new file mode 100644 index 0000000000..90e52241a6 --- /dev/null +++ b/src/openai/types/evals/run_cancel_response.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .eval_api_error import EvalAPIError +from ..shared.metadata import Metadata +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource +from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource + +__all__ = ["RunCancelResponse", "DataSource", "PerModelUsage", "PerTestingCriteriaResult", "ResultCounts"] + +DataSource: TypeAlias = Annotated[ + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource], PropertyInfo(discriminator="type") +] + + +class PerModelUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + invocation_count: int + """The number of invocations.""" + + run_model_name: str = FieldInfo(alias="model_name") + """The name of the model.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class PerTestingCriteriaResult(BaseModel): + failed: int + """Number of tests failed for this criteria.""" + + passed: int + """Number of tests passed for this criteria.""" + + testing_criteria: str + """A description of the testing criteria.""" + + +class ResultCounts(BaseModel): + errored: int + """Number of output items that resulted in an error.""" + + failed: int + """Number of output items that failed to pass the evaluation.""" + + passed: int + """Number of output items that passed the evaluation.""" + + total: int + """Total number of executed output items.""" + + +class RunCancelResponse(BaseModel): + id: str + """Unique identifier for the evaluation run.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + data_source: DataSource + """Information about the run's data source.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + eval_id: str + """The identifier of the associated evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """The model that is evaluated, if applicable.""" + + name: str + """The name of the evaluation run.""" + + object: Literal["eval.run"] + """The type of the object. Always "eval.run".""" + + per_model_usage: List[PerModelUsage] + """Usage statistics for each model during the evaluation run.""" + + per_testing_criteria_results: List[PerTestingCriteriaResult] + """Results per testing criteria applied during the evaluation run.""" + + report_url: str + """The URL to the rendered evaluation run report on the UI dashboard.""" + + result_counts: ResultCounts + """Counters summarizing the outcomes of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py new file mode 100644 index 0000000000..acf7b1b126 --- /dev/null +++ b/src/openai/types/evals/run_create_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Required, TypeAlias, TypedDict + +from ..shared_params.metadata import Metadata +from .create_eval_jsonl_run_data_source_param import CreateEvalJSONLRunDataSourceParam +from .create_eval_completions_run_data_source_param import CreateEvalCompletionsRunDataSourceParam + +__all__ = ["RunCreateParams", "DataSource"] + + +class RunCreateParams(TypedDict, total=False): + data_source: Required[DataSource] + """Details about the run's data source.""" + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + name: str + """The name of the run.""" + + +DataSource: TypeAlias = Union[CreateEvalJSONLRunDataSourceParam, CreateEvalCompletionsRunDataSourceParam] diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py new file mode 100644 index 0000000000..14ca426427 --- /dev/null +++ b/src/openai/types/evals/run_create_response.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .eval_api_error import EvalAPIError +from ..shared.metadata import Metadata +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource +from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource + +__all__ = ["RunCreateResponse", "DataSource", "PerModelUsage", "PerTestingCriteriaResult", "ResultCounts"] + +DataSource: TypeAlias = Annotated[ + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource], PropertyInfo(discriminator="type") +] + + +class PerModelUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + invocation_count: int + """The number of invocations.""" + + run_model_name: str = FieldInfo(alias="model_name") + """The name of the model.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class PerTestingCriteriaResult(BaseModel): + failed: int + """Number of tests failed for this criteria.""" + + passed: int + """Number of tests passed for this criteria.""" + + testing_criteria: str + """A description of the testing criteria.""" + + +class ResultCounts(BaseModel): + errored: int + """Number of output items that resulted in an error.""" + + failed: int + """Number of output items that failed to pass the evaluation.""" + + passed: int + """Number of output items that passed the evaluation.""" + + total: int + """Total number of executed output items.""" + + +class RunCreateResponse(BaseModel): + id: str + """Unique identifier for the evaluation run.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + data_source: DataSource + """Information about the run's data source.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + eval_id: str + """The identifier of the associated evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """The model that is evaluated, if applicable.""" + + name: str + """The name of the evaluation run.""" + + object: Literal["eval.run"] + """The type of the object. Always "eval.run".""" + + per_model_usage: List[PerModelUsage] + """Usage statistics for each model during the evaluation run.""" + + per_testing_criteria_results: List[PerTestingCriteriaResult] + """Results per testing criteria applied during the evaluation run.""" + + report_url: str + """The URL to the rendered evaluation run report on the UI dashboard.""" + + result_counts: ResultCounts + """Counters summarizing the outcomes of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/src/openai/types/evals/run_delete_response.py b/src/openai/types/evals/run_delete_response.py new file mode 100644 index 0000000000..d48d01f86c --- /dev/null +++ b/src/openai/types/evals/run_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RunDeleteResponse"] + + +class RunDeleteResponse(BaseModel): + deleted: Optional[bool] = None + + object: Optional[str] = None + + run_id: Optional[str] = None diff --git a/src/openai/types/evals/run_list_params.py b/src/openai/types/evals/run_list_params.py new file mode 100644 index 0000000000..6060eafb97 --- /dev/null +++ b/src/openai/types/evals/run_list_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RunListParams"] + + +class RunListParams(TypedDict, total=False): + after: str + """Identifier for the last run from the previous pagination request.""" + + limit: int + """Number of runs to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order for runs by timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. + """ + + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] + """Filter runs by status. + + Use "queued" | "in_progress" | "failed" | "completed" | "canceled". + """ diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py new file mode 100644 index 0000000000..a1022f542f --- /dev/null +++ b/src/openai/types/evals/run_list_response.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .eval_api_error import EvalAPIError +from ..shared.metadata import Metadata +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource +from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource + +__all__ = ["RunListResponse", "DataSource", "PerModelUsage", "PerTestingCriteriaResult", "ResultCounts"] + +DataSource: TypeAlias = Annotated[ + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource], PropertyInfo(discriminator="type") +] + + +class PerModelUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + invocation_count: int + """The number of invocations.""" + + run_model_name: str = FieldInfo(alias="model_name") + """The name of the model.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class PerTestingCriteriaResult(BaseModel): + failed: int + """Number of tests failed for this criteria.""" + + passed: int + """Number of tests passed for this criteria.""" + + testing_criteria: str + """A description of the testing criteria.""" + + +class ResultCounts(BaseModel): + errored: int + """Number of output items that resulted in an error.""" + + failed: int + """Number of output items that failed to pass the evaluation.""" + + passed: int + """Number of output items that passed the evaluation.""" + + total: int + """Total number of executed output items.""" + + +class RunListResponse(BaseModel): + id: str + """Unique identifier for the evaluation run.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + data_source: DataSource + """Information about the run's data source.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + eval_id: str + """The identifier of the associated evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """The model that is evaluated, if applicable.""" + + name: str + """The name of the evaluation run.""" + + object: Literal["eval.run"] + """The type of the object. Always "eval.run".""" + + per_model_usage: List[PerModelUsage] + """Usage statistics for each model during the evaluation run.""" + + per_testing_criteria_results: List[PerTestingCriteriaResult] + """Results per testing criteria applied during the evaluation run.""" + + report_url: str + """The URL to the rendered evaluation run report on the UI dashboard.""" + + result_counts: ResultCounts + """Counters summarizing the outcomes of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py new file mode 100644 index 0000000000..461ed43dda --- /dev/null +++ b/src/openai/types/evals/run_retrieve_response.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .eval_api_error import EvalAPIError +from ..shared.metadata import Metadata +from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource +from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource + +__all__ = ["RunRetrieveResponse", "DataSource", "PerModelUsage", "PerTestingCriteriaResult", "ResultCounts"] + +DataSource: TypeAlias = Annotated[ + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource], PropertyInfo(discriminator="type") +] + + +class PerModelUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + invocation_count: int + """The number of invocations.""" + + run_model_name: str = FieldInfo(alias="model_name") + """The name of the model.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class PerTestingCriteriaResult(BaseModel): + failed: int + """Number of tests failed for this criteria.""" + + passed: int + """Number of tests passed for this criteria.""" + + testing_criteria: str + """A description of the testing criteria.""" + + +class ResultCounts(BaseModel): + errored: int + """Number of output items that resulted in an error.""" + + failed: int + """Number of output items that failed to pass the evaluation.""" + + passed: int + """Number of output items that passed the evaluation.""" + + total: int + """Total number of executed output items.""" + + +class RunRetrieveResponse(BaseModel): + id: str + """Unique identifier for the evaluation run.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + data_source: DataSource + """Information about the run's data source.""" + + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + eval_id: str + """The identifier of the associated evaluation.""" + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: str + """The model that is evaluated, if applicable.""" + + name: str + """The name of the evaluation run.""" + + object: Literal["eval.run"] + """The type of the object. Always "eval.run".""" + + per_model_usage: List[PerModelUsage] + """Usage statistics for each model during the evaluation run.""" + + per_testing_criteria_results: List[PerTestingCriteriaResult] + """Results per testing criteria applied during the evaluation run.""" + + report_url: str + """The URL to the rendered evaluation run report on the UI dashboard.""" + + result_counts: ResultCounts + """Counters summarizing the outcomes of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/src/openai/types/evals/runs/__init__.py b/src/openai/types/evals/runs/__init__.py new file mode 100644 index 0000000000..b77cbb6acd --- /dev/null +++ b/src/openai/types/evals/runs/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .output_item_list_params import OutputItemListParams as OutputItemListParams +from .output_item_list_response import OutputItemListResponse as OutputItemListResponse +from .output_item_retrieve_response import OutputItemRetrieveResponse as OutputItemRetrieveResponse diff --git a/src/openai/types/evals/runs/output_item_list_params.py b/src/openai/types/evals/runs/output_item_list_params.py new file mode 100644 index 0000000000..073bfc69a7 --- /dev/null +++ b/src/openai/types/evals/runs/output_item_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["OutputItemListParams"] + + +class OutputItemListParams(TypedDict, total=False): + eval_id: Required[str] + + after: str + """Identifier for the last output item from the previous pagination request.""" + + limit: int + """Number of output items to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order for output items by timestamp. + + Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. + """ + + status: Literal["fail", "pass"] + """Filter output items by status. + + Use `failed` to filter by failed output items or `pass` to filter by passed + output items. + """ diff --git a/src/openai/types/evals/runs/output_item_list_response.py b/src/openai/types/evals/runs/output_item_list_response.py new file mode 100644 index 0000000000..72b1049f7b --- /dev/null +++ b/src/openai/types/evals/runs/output_item_list_response.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from ..eval_api_error import EvalAPIError + +__all__ = ["OutputItemListResponse", "Sample", "SampleInput", "SampleOutput", "SampleUsage"] + + +class SampleInput(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message sender (e.g., system, user, developer).""" + + +class SampleOutput(BaseModel): + content: Optional[str] = None + """The content of the message.""" + + role: Optional[str] = None + """The role of the message (e.g. "system", "assistant", "user").""" + + +class SampleUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class Sample(BaseModel): + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + finish_reason: str + """The reason why the sample generation was finished.""" + + input: List[SampleInput] + """An array of input messages.""" + + max_completion_tokens: int + """The maximum number of tokens allowed for completion.""" + + model: str + """The model used for generating the sample.""" + + output: List[SampleOutput] + """An array of output messages.""" + + seed: int + """The seed used for generating the sample.""" + + temperature: float + """The sampling temperature used.""" + + top_p: float + """The top_p value used for sampling.""" + + usage: SampleUsage + """Token usage details for the sample.""" + + +class OutputItemListResponse(BaseModel): + id: str + """Unique identifier for the evaluation run output item.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + datasource_item: Dict[str, object] + """Details of the input data source item.""" + + datasource_item_id: int + """The identifier for the data source item.""" + + eval_id: str + """The identifier of the evaluation group.""" + + object: Literal["eval.run.output_item"] + """The type of the object. Always "eval.run.output_item".""" + + results: List[Dict[str, builtins.object]] + """A list of results from the evaluation run.""" + + run_id: str + """The identifier of the evaluation run associated with this output item.""" + + sample: Sample + """A sample containing the input and output of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/src/openai/types/evals/runs/output_item_retrieve_response.py b/src/openai/types/evals/runs/output_item_retrieve_response.py new file mode 100644 index 0000000000..63aab5565f --- /dev/null +++ b/src/openai/types/evals/runs/output_item_retrieve_response.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel +from ..eval_api_error import EvalAPIError + +__all__ = ["OutputItemRetrieveResponse", "Sample", "SampleInput", "SampleOutput", "SampleUsage"] + + +class SampleInput(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message sender (e.g., system, user, developer).""" + + +class SampleOutput(BaseModel): + content: Optional[str] = None + """The content of the message.""" + + role: Optional[str] = None + """The role of the message (e.g. "system", "assistant", "user").""" + + +class SampleUsage(BaseModel): + cached_tokens: int + """The number of tokens retrieved from cache.""" + + completion_tokens: int + """The number of completion tokens generated.""" + + prompt_tokens: int + """The number of prompt tokens used.""" + + total_tokens: int + """The total number of tokens used.""" + + +class Sample(BaseModel): + error: EvalAPIError + """An object representing an error response from the Eval API.""" + + finish_reason: str + """The reason why the sample generation was finished.""" + + input: List[SampleInput] + """An array of input messages.""" + + max_completion_tokens: int + """The maximum number of tokens allowed for completion.""" + + model: str + """The model used for generating the sample.""" + + output: List[SampleOutput] + """An array of output messages.""" + + seed: int + """The seed used for generating the sample.""" + + temperature: float + """The sampling temperature used.""" + + top_p: float + """The top_p value used for sampling.""" + + usage: SampleUsage + """Token usage details for the sample.""" + + +class OutputItemRetrieveResponse(BaseModel): + id: str + """Unique identifier for the evaluation run output item.""" + + created_at: int + """Unix timestamp (in seconds) when the evaluation run was created.""" + + datasource_item: Dict[str, object] + """Details of the input data source item.""" + + datasource_item_id: int + """The identifier for the data source item.""" + + eval_id: str + """The identifier of the evaluation group.""" + + object: Literal["eval.run.output_item"] + """The type of the object. Always "eval.run.output_item".""" + + results: List[Dict[str, builtins.object]] + """A list of results from the evaluation run.""" + + run_id: str + """The identifier of the evaluation run associated with this output item.""" + + sample: Sample + """A sample containing the input and output of the evaluation run.""" + + status: str + """The status of the evaluation run.""" diff --git a/src/openai/types/fine_tuning/checkpoints/__init__.py b/src/openai/types/fine_tuning/checkpoints/__init__.py new file mode 100644 index 0000000000..2947b33145 --- /dev/null +++ b/src/openai/types/fine_tuning/checkpoints/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .permission_create_params import PermissionCreateParams as PermissionCreateParams +from .permission_create_response import PermissionCreateResponse as PermissionCreateResponse +from .permission_delete_response import PermissionDeleteResponse as PermissionDeleteResponse +from .permission_retrieve_params import PermissionRetrieveParams as PermissionRetrieveParams +from .permission_retrieve_response import PermissionRetrieveResponse as PermissionRetrieveResponse diff --git a/src/openai/types/fine_tuning/checkpoints/permission_create_params.py b/src/openai/types/fine_tuning/checkpoints/permission_create_params.py new file mode 100644 index 0000000000..92f98f21b9 --- /dev/null +++ b/src/openai/types/fine_tuning/checkpoints/permission_create_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, TypedDict + +__all__ = ["PermissionCreateParams"] + + +class PermissionCreateParams(TypedDict, total=False): + project_ids: Required[List[str]] + """The project identifiers to grant access to.""" diff --git a/src/openai/types/fine_tuning/checkpoints/permission_create_response.py b/src/openai/types/fine_tuning/checkpoints/permission_create_response.py new file mode 100644 index 0000000000..9bc14c00cc --- /dev/null +++ b/src/openai/types/fine_tuning/checkpoints/permission_create_response.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__ = ["PermissionCreateResponse"] + + +class PermissionCreateResponse(BaseModel): + id: str + """The permission identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the permission was created.""" + + object: Literal["checkpoint.permission"] + """The object type, which is always "checkpoint.permission".""" + + project_id: str + """The project identifier that the permission is for.""" diff --git a/src/openai/types/fine_tuning/checkpoints/permission_delete_response.py b/src/openai/types/fine_tuning/checkpoints/permission_delete_response.py new file mode 100644 index 0000000000..1a92d912fa --- /dev/null +++ b/src/openai/types/fine_tuning/checkpoints/permission_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["PermissionDeleteResponse"] + + +class PermissionDeleteResponse(BaseModel): + id: str + """The ID of the fine-tuned model checkpoint permission that was deleted.""" + + deleted: bool + """Whether the fine-tuned model checkpoint permission was successfully deleted.""" + + object: Literal["checkpoint.permission"] + """The object type, which is always "checkpoint.permission".""" diff --git a/src/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py new file mode 100644 index 0000000000..6e66a867ca --- /dev/null +++ b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["PermissionRetrieveParams"] + + +class PermissionRetrieveParams(TypedDict, total=False): + after: str + """Identifier for the last permission ID from the previous pagination request.""" + + limit: int + """Number of permissions to retrieve.""" + + order: Literal["ascending", "descending"] + """The order in which to retrieve permissions.""" + + project_id: str + """The ID of the project to get permissions for.""" diff --git a/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py new file mode 100644 index 0000000000..14c73b55d0 --- /dev/null +++ b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["PermissionRetrieveResponse", "Data"] + + +class Data(BaseModel): + id: str + """The permission identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the permission was created.""" + + object: Literal["checkpoint.permission"] + """The object type, which is always "checkpoint.permission".""" + + project_id: str + """The project identifier that the permission is for.""" + + +class PermissionRetrieveResponse(BaseModel): + data: List[Data] + + has_more: bool + + object: Literal["list"] + + first_id: Optional[str] = None + + last_id: Optional[str] = None diff --git a/tests/api_resources/evals/__init__.py b/tests/api_resources/evals/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/evals/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/evals/runs/__init__.py b/tests/api_resources/evals/runs/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/evals/runs/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/evals/runs/test_output_items.py b/tests/api_resources/evals/runs/test_output_items.py new file mode 100644 index 0000000000..f764f0336e --- /dev/null +++ b/tests/api_resources/evals/runs/test_output_items.py @@ -0,0 +1,263 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.evals.runs import OutputItemListResponse, OutputItemRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestOutputItems: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + output_item = client.evals.runs.output_items.retrieve( + output_item_id="output_item_id", + eval_id="eval_id", + run_id="run_id", + ) + assert_matches_type(OutputItemRetrieveResponse, output_item, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.evals.runs.output_items.with_raw_response.retrieve( + output_item_id="output_item_id", + eval_id="eval_id", + run_id="run_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + output_item = response.parse() + assert_matches_type(OutputItemRetrieveResponse, output_item, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.evals.runs.output_items.with_streaming_response.retrieve( + output_item_id="output_item_id", + eval_id="eval_id", + run_id="run_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + output_item = response.parse() + assert_matches_type(OutputItemRetrieveResponse, output_item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.runs.output_items.with_raw_response.retrieve( + output_item_id="output_item_id", + eval_id="", + run_id="run_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.evals.runs.output_items.with_raw_response.retrieve( + output_item_id="output_item_id", + eval_id="eval_id", + run_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `output_item_id` but received ''"): + client.evals.runs.output_items.with_raw_response.retrieve( + output_item_id="", + eval_id="eval_id", + run_id="run_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + output_item = client.evals.runs.output_items.list( + run_id="run_id", + eval_id="eval_id", + ) + assert_matches_type(SyncCursorPage[OutputItemListResponse], output_item, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + output_item = client.evals.runs.output_items.list( + run_id="run_id", + eval_id="eval_id", + after="after", + limit=0, + order="asc", + status="fail", + ) + assert_matches_type(SyncCursorPage[OutputItemListResponse], output_item, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.evals.runs.output_items.with_raw_response.list( + run_id="run_id", + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + output_item = response.parse() + assert_matches_type(SyncCursorPage[OutputItemListResponse], output_item, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.evals.runs.output_items.with_streaming_response.list( + run_id="run_id", + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + output_item = response.parse() + assert_matches_type(SyncCursorPage[OutputItemListResponse], output_item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.runs.output_items.with_raw_response.list( + run_id="run_id", + eval_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.evals.runs.output_items.with_raw_response.list( + run_id="", + eval_id="eval_id", + ) + + +class TestAsyncOutputItems: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + output_item = await async_client.evals.runs.output_items.retrieve( + output_item_id="output_item_id", + eval_id="eval_id", + run_id="run_id", + ) + assert_matches_type(OutputItemRetrieveResponse, output_item, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.runs.output_items.with_raw_response.retrieve( + output_item_id="output_item_id", + eval_id="eval_id", + run_id="run_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + output_item = response.parse() + assert_matches_type(OutputItemRetrieveResponse, output_item, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.runs.output_items.with_streaming_response.retrieve( + output_item_id="output_item_id", + eval_id="eval_id", + run_id="run_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + output_item = await response.parse() + assert_matches_type(OutputItemRetrieveResponse, output_item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.runs.output_items.with_raw_response.retrieve( + output_item_id="output_item_id", + eval_id="", + run_id="run_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await async_client.evals.runs.output_items.with_raw_response.retrieve( + output_item_id="output_item_id", + eval_id="eval_id", + run_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `output_item_id` but received ''"): + await async_client.evals.runs.output_items.with_raw_response.retrieve( + output_item_id="", + eval_id="eval_id", + run_id="run_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + output_item = await async_client.evals.runs.output_items.list( + run_id="run_id", + eval_id="eval_id", + ) + assert_matches_type(AsyncCursorPage[OutputItemListResponse], output_item, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + output_item = await async_client.evals.runs.output_items.list( + run_id="run_id", + eval_id="eval_id", + after="after", + limit=0, + order="asc", + status="fail", + ) + assert_matches_type(AsyncCursorPage[OutputItemListResponse], output_item, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.runs.output_items.with_raw_response.list( + run_id="run_id", + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + output_item = response.parse() + assert_matches_type(AsyncCursorPage[OutputItemListResponse], output_item, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.runs.output_items.with_streaming_response.list( + run_id="run_id", + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + output_item = await response.parse() + assert_matches_type(AsyncCursorPage[OutputItemListResponse], output_item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.runs.output_items.with_raw_response.list( + run_id="run_id", + eval_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await async_client.evals.runs.output_items.with_raw_response.list( + run_id="", + eval_id="eval_id", + ) diff --git a/tests/api_resources/evals/test_runs.py b/tests/api_resources/evals/test_runs.py new file mode 100644 index 0000000000..cefb1c82ff --- /dev/null +++ b/tests/api_resources/evals/test_runs.py @@ -0,0 +1,589 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.evals import ( + RunListResponse, + RunCancelResponse, + RunCreateResponse, + RunDeleteResponse, + RunRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestRuns: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + run = client.evals.runs.create( + eval_id="eval_id", + data_source={ + "source": { + "content": [{"item": {"foo": "bar"}}], + "type": "file_content", + }, + "type": "jsonl", + }, + ) + assert_matches_type(RunCreateResponse, run, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + run = client.evals.runs.create( + eval_id="eval_id", + data_source={ + "source": { + "content": [ + { + "item": {"foo": "bar"}, + "sample": {"foo": "bar"}, + } + ], + "type": "file_content", + }, + "type": "jsonl", + }, + metadata={"foo": "string"}, + name="name", + ) + assert_matches_type(RunCreateResponse, run, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.evals.runs.with_raw_response.create( + eval_id="eval_id", + data_source={ + "source": { + "content": [{"item": {"foo": "bar"}}], + "type": "file_content", + }, + "type": "jsonl", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(RunCreateResponse, run, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.evals.runs.with_streaming_response.create( + eval_id="eval_id", + data_source={ + "source": { + "content": [{"item": {"foo": "bar"}}], + "type": "file_content", + }, + "type": "jsonl", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(RunCreateResponse, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.runs.with_raw_response.create( + eval_id="", + data_source={ + "source": { + "content": [{"item": {"foo": "bar"}}], + "type": "file_content", + }, + "type": "jsonl", + }, + ) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + run = client.evals.runs.retrieve( + run_id="run_id", + eval_id="eval_id", + ) + assert_matches_type(RunRetrieveResponse, run, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.evals.runs.with_raw_response.retrieve( + run_id="run_id", + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(RunRetrieveResponse, run, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.evals.runs.with_streaming_response.retrieve( + run_id="run_id", + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(RunRetrieveResponse, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.runs.with_raw_response.retrieve( + run_id="run_id", + eval_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.evals.runs.with_raw_response.retrieve( + run_id="", + eval_id="eval_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + run = client.evals.runs.list( + eval_id="eval_id", + ) + assert_matches_type(SyncCursorPage[RunListResponse], run, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + run = client.evals.runs.list( + eval_id="eval_id", + after="after", + limit=0, + order="asc", + status="queued", + ) + assert_matches_type(SyncCursorPage[RunListResponse], run, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.evals.runs.with_raw_response.list( + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(SyncCursorPage[RunListResponse], run, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.evals.runs.with_streaming_response.list( + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(SyncCursorPage[RunListResponse], run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.runs.with_raw_response.list( + eval_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + run = client.evals.runs.delete( + run_id="run_id", + eval_id="eval_id", + ) + assert_matches_type(RunDeleteResponse, run, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.evals.runs.with_raw_response.delete( + run_id="run_id", + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(RunDeleteResponse, run, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.evals.runs.with_streaming_response.delete( + run_id="run_id", + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(RunDeleteResponse, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.runs.with_raw_response.delete( + run_id="run_id", + eval_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.evals.runs.with_raw_response.delete( + run_id="", + eval_id="eval_id", + ) + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + run = client.evals.runs.cancel( + run_id="run_id", + eval_id="eval_id", + ) + assert_matches_type(RunCancelResponse, run, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + response = client.evals.runs.with_raw_response.cancel( + run_id="run_id", + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(RunCancelResponse, run, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: OpenAI) -> None: + with client.evals.runs.with_streaming_response.cancel( + run_id="run_id", + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = response.parse() + assert_matches_type(RunCancelResponse, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.runs.with_raw_response.cancel( + run_id="run_id", + eval_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + client.evals.runs.with_raw_response.cancel( + run_id="", + eval_id="eval_id", + ) + + +class TestAsyncRuns: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + run = await async_client.evals.runs.create( + eval_id="eval_id", + data_source={ + "source": { + "content": [{"item": {"foo": "bar"}}], + "type": "file_content", + }, + "type": "jsonl", + }, + ) + assert_matches_type(RunCreateResponse, run, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + run = await async_client.evals.runs.create( + eval_id="eval_id", + data_source={ + "source": { + "content": [ + { + "item": {"foo": "bar"}, + "sample": {"foo": "bar"}, + } + ], + "type": "file_content", + }, + "type": "jsonl", + }, + metadata={"foo": "string"}, + name="name", + ) + assert_matches_type(RunCreateResponse, run, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.runs.with_raw_response.create( + eval_id="eval_id", + data_source={ + "source": { + "content": [{"item": {"foo": "bar"}}], + "type": "file_content", + }, + "type": "jsonl", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(RunCreateResponse, run, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.runs.with_streaming_response.create( + eval_id="eval_id", + data_source={ + "source": { + "content": [{"item": {"foo": "bar"}}], + "type": "file_content", + }, + "type": "jsonl", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(RunCreateResponse, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.runs.with_raw_response.create( + eval_id="", + data_source={ + "source": { + "content": [{"item": {"foo": "bar"}}], + "type": "file_content", + }, + "type": "jsonl", + }, + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + run = await async_client.evals.runs.retrieve( + run_id="run_id", + eval_id="eval_id", + ) + assert_matches_type(RunRetrieveResponse, run, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.runs.with_raw_response.retrieve( + run_id="run_id", + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(RunRetrieveResponse, run, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.runs.with_streaming_response.retrieve( + run_id="run_id", + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(RunRetrieveResponse, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.runs.with_raw_response.retrieve( + run_id="run_id", + eval_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await async_client.evals.runs.with_raw_response.retrieve( + run_id="", + eval_id="eval_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + run = await async_client.evals.runs.list( + eval_id="eval_id", + ) + assert_matches_type(AsyncCursorPage[RunListResponse], run, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + run = await async_client.evals.runs.list( + eval_id="eval_id", + after="after", + limit=0, + order="asc", + status="queued", + ) + assert_matches_type(AsyncCursorPage[RunListResponse], run, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.runs.with_raw_response.list( + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(AsyncCursorPage[RunListResponse], run, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.runs.with_streaming_response.list( + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(AsyncCursorPage[RunListResponse], run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.runs.with_raw_response.list( + eval_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + run = await async_client.evals.runs.delete( + run_id="run_id", + eval_id="eval_id", + ) + assert_matches_type(RunDeleteResponse, run, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.runs.with_raw_response.delete( + run_id="run_id", + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(RunDeleteResponse, run, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.runs.with_streaming_response.delete( + run_id="run_id", + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(RunDeleteResponse, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.runs.with_raw_response.delete( + run_id="run_id", + eval_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await async_client.evals.runs.with_raw_response.delete( + run_id="", + eval_id="eval_id", + ) + + @parametrize + async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: + run = await async_client.evals.runs.cancel( + run_id="run_id", + eval_id="eval_id", + ) + assert_matches_type(RunCancelResponse, run, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.runs.with_raw_response.cancel( + run_id="run_id", + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + run = response.parse() + assert_matches_type(RunCancelResponse, run, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.runs.with_streaming_response.cancel( + run_id="run_id", + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + run = await response.parse() + assert_matches_type(RunCancelResponse, run, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.runs.with_raw_response.cancel( + run_id="run_id", + eval_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `run_id` but received ''"): + await async_client.evals.runs.with_raw_response.cancel( + run_id="", + eval_id="eval_id", + ) diff --git a/tests/api_resources/fine_tuning/checkpoints/__init__.py b/tests/api_resources/fine_tuning/checkpoints/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/fine_tuning/checkpoints/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py new file mode 100644 index 0000000000..d25c784c33 --- /dev/null +++ b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py @@ -0,0 +1,297 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncPage, AsyncPage +from openai.types.fine_tuning.checkpoints import ( + PermissionCreateResponse, + PermissionDeleteResponse, + PermissionRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestPermissions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + permission = client.fine_tuning.checkpoints.permissions.create( + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + project_ids=["string"], + ) + assert_matches_type(SyncPage[PermissionCreateResponse], permission, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.fine_tuning.checkpoints.permissions.with_raw_response.create( + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + project_ids=["string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(SyncPage[PermissionCreateResponse], permission, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.fine_tuning.checkpoints.permissions.with_streaming_response.create( + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + project_ids=["string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = response.parse() + assert_matches_type(SyncPage[PermissionCreateResponse], permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" + ): + client.fine_tuning.checkpoints.permissions.with_raw_response.create( + fine_tuned_model_checkpoint="", + project_ids=["string"], + ) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + permission = client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: + permission = client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + after="after", + limit=0, + order="ascending", + project_id="project_id", + ) + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.fine_tuning.checkpoints.permissions.with_streaming_response.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = response.parse() + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" + ): + client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + fine_tuned_model_checkpoint="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + permission = client.fine_tuning.checkpoints.permissions.delete( + "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + ) + assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.fine_tuning.checkpoints.permissions.with_raw_response.delete( + "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.fine_tuning.checkpoints.permissions.with_streaming_response.delete( + "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = response.parse() + assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" + ): + client.fine_tuning.checkpoints.permissions.with_raw_response.delete( + "", + ) + + +class TestAsyncPermissions: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + permission = await async_client.fine_tuning.checkpoints.permissions.create( + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + project_ids=["string"], + ) + assert_matches_type(AsyncPage[PermissionCreateResponse], permission, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.checkpoints.permissions.with_raw_response.create( + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + project_ids=["string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(AsyncPage[PermissionCreateResponse], permission, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.checkpoints.permissions.with_streaming_response.create( + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + project_ids=["string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = await response.parse() + assert_matches_type(AsyncPage[PermissionCreateResponse], permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" + ): + await async_client.fine_tuning.checkpoints.permissions.with_raw_response.create( + fine_tuned_model_checkpoint="", + project_ids=["string"], + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + permission = await async_client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) -> None: + permission = await async_client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + after="after", + limit=0, + order="ascending", + project_id="project_id", + ) + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.checkpoints.permissions.with_streaming_response.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = await response.parse() + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" + ): + await async_client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + fine_tuned_model_checkpoint="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + permission = await async_client.fine_tuning.checkpoints.permissions.delete( + "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + ) + assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.checkpoints.permissions.with_raw_response.delete( + "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.checkpoints.permissions.with_streaming_response.delete( + "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = await response.parse() + assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" + ): + await async_client.fine_tuning.checkpoints.permissions.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/test_evals.py b/tests/api_resources/test_evals.py new file mode 100644 index 0000000000..33ba92cda5 --- /dev/null +++ b/tests/api_resources/test_evals.py @@ -0,0 +1,1701 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import ( + EvalListResponse, + EvalCreateResponse, + EvalDeleteResponse, + EvalUpdateResponse, + EvalRetrieveResponse, +) +from openai.pagination import SyncCursorPage, AsyncCursorPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestEvals: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + eval = client.evals.create( + data_source_config={ + "item_schema": { + "0": "bar", + "1": "bar", + "2": "bar", + "3": "bar", + "4": "bar", + "5": "bar", + "6": "bar", + "7": "bar", + "8": "bar", + "9": "bar", + "10": "bar", + "11": "bar", + "12": "bar", + "13": "bar", + "14": "bar", + "15": "bar", + "16": "bar", + "17": "bar", + "18": "bar", + "19": "bar", + "20": "bar", + "21": "bar", + "22": "bar", + "23": "bar", + "24": "bar", + "25": "bar", + "26": "bar", + "27": "bar", + "28": "bar", + "29": "bar", + "30": "bar", + "31": "bar", + "32": "bar", + "33": "bar", + "34": "bar", + "35": "bar", + "36": "bar", + "37": "bar", + "38": "bar", + "39": "bar", + "40": "bar", + "41": "bar", + "42": "bar", + "43": "bar", + "44": "bar", + "45": "bar", + "46": "bar", + "47": "bar", + "48": "bar", + "49": "bar", + "50": "bar", + "51": "bar", + "52": "bar", + "53": "bar", + "54": "bar", + "55": "bar", + "56": "bar", + "57": "bar", + "58": "bar", + "59": "bar", + "60": "bar", + "61": "bar", + "62": "bar", + "63": "bar", + "64": "bar", + "65": "bar", + "66": "bar", + "67": "bar", + "68": "bar", + "69": "bar", + "70": "bar", + "71": "bar", + "72": "bar", + "73": "bar", + "74": "bar", + "75": "bar", + "76": "bar", + "77": "bar", + "78": "bar", + "79": "bar", + "80": "bar", + "81": "bar", + "82": "bar", + "83": "bar", + "84": "bar", + "85": "bar", + "86": "bar", + "87": "bar", + "88": "bar", + "89": "bar", + "90": "bar", + "91": "bar", + "92": "bar", + "93": "bar", + "94": "bar", + "95": "bar", + "96": "bar", + "97": "bar", + "98": "bar", + "99": "bar", + "100": "bar", + "101": "bar", + "102": "bar", + "103": "bar", + "104": "bar", + "105": "bar", + "106": "bar", + "107": "bar", + "108": "bar", + "109": "bar", + "110": "bar", + "111": "bar", + "112": "bar", + "113": "bar", + "114": "bar", + "115": "bar", + "116": "bar", + "117": "bar", + "118": "bar", + "119": "bar", + "120": "bar", + "121": "bar", + "122": "bar", + "123": "bar", + "124": "bar", + "125": "bar", + "126": "bar", + "127": "bar", + "128": "bar", + "129": "bar", + "130": "bar", + "131": "bar", + "132": "bar", + "133": "bar", + "134": "bar", + "135": "bar", + "136": "bar", + "137": "bar", + "138": "bar", + "139": "bar", + }, + "type": "custom", + }, + testing_criteria=[ + { + "input": [ + { + "content": "content", + "role": "role", + } + ], + "labels": ["string"], + "model": "model", + "name": "name", + "passing_labels": ["string"], + "type": "label_model", + } + ], + ) + assert_matches_type(EvalCreateResponse, eval, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + eval = client.evals.create( + data_source_config={ + "item_schema": { + "0": "bar", + "1": "bar", + "2": "bar", + "3": "bar", + "4": "bar", + "5": "bar", + "6": "bar", + "7": "bar", + "8": "bar", + "9": "bar", + "10": "bar", + "11": "bar", + "12": "bar", + "13": "bar", + "14": "bar", + "15": "bar", + "16": "bar", + "17": "bar", + "18": "bar", + "19": "bar", + "20": "bar", + "21": "bar", + "22": "bar", + "23": "bar", + "24": "bar", + "25": "bar", + "26": "bar", + "27": "bar", + "28": "bar", + "29": "bar", + "30": "bar", + "31": "bar", + "32": "bar", + "33": "bar", + "34": "bar", + "35": "bar", + "36": "bar", + "37": "bar", + "38": "bar", + "39": "bar", + "40": "bar", + "41": "bar", + "42": "bar", + "43": "bar", + "44": "bar", + "45": "bar", + "46": "bar", + "47": "bar", + "48": "bar", + "49": "bar", + "50": "bar", + "51": "bar", + "52": "bar", + "53": "bar", + "54": "bar", + "55": "bar", + "56": "bar", + "57": "bar", + "58": "bar", + "59": "bar", + "60": "bar", + "61": "bar", + "62": "bar", + "63": "bar", + "64": "bar", + "65": "bar", + "66": "bar", + "67": "bar", + "68": "bar", + "69": "bar", + "70": "bar", + "71": "bar", + "72": "bar", + "73": "bar", + "74": "bar", + "75": "bar", + "76": "bar", + "77": "bar", + "78": "bar", + "79": "bar", + "80": "bar", + "81": "bar", + "82": "bar", + "83": "bar", + "84": "bar", + "85": "bar", + "86": "bar", + "87": "bar", + "88": "bar", + "89": "bar", + "90": "bar", + "91": "bar", + "92": "bar", + "93": "bar", + "94": "bar", + "95": "bar", + "96": "bar", + "97": "bar", + "98": "bar", + "99": "bar", + "100": "bar", + "101": "bar", + "102": "bar", + "103": "bar", + "104": "bar", + "105": "bar", + "106": "bar", + "107": "bar", + "108": "bar", + "109": "bar", + "110": "bar", + "111": "bar", + "112": "bar", + "113": "bar", + "114": "bar", + "115": "bar", + "116": "bar", + "117": "bar", + "118": "bar", + "119": "bar", + "120": "bar", + "121": "bar", + "122": "bar", + "123": "bar", + "124": "bar", + "125": "bar", + "126": "bar", + "127": "bar", + "128": "bar", + "129": "bar", + "130": "bar", + "131": "bar", + "132": "bar", + "133": "bar", + "134": "bar", + "135": "bar", + "136": "bar", + "137": "bar", + "138": "bar", + "139": "bar", + }, + "type": "custom", + "include_sample_schema": True, + }, + testing_criteria=[ + { + "input": [ + { + "content": "content", + "role": "role", + } + ], + "labels": ["string"], + "model": "model", + "name": "name", + "passing_labels": ["string"], + "type": "label_model", + } + ], + metadata={"foo": "string"}, + name="name", + share_with_openai=True, + ) + assert_matches_type(EvalCreateResponse, eval, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.evals.with_raw_response.create( + data_source_config={ + "item_schema": { + "0": "bar", + "1": "bar", + "2": "bar", + "3": "bar", + "4": "bar", + "5": "bar", + "6": "bar", + "7": "bar", + "8": "bar", + "9": "bar", + "10": "bar", + "11": "bar", + "12": "bar", + "13": "bar", + "14": "bar", + "15": "bar", + "16": "bar", + "17": "bar", + "18": "bar", + "19": "bar", + "20": "bar", + "21": "bar", + "22": "bar", + "23": "bar", + "24": "bar", + "25": "bar", + "26": "bar", + "27": "bar", + "28": "bar", + "29": "bar", + "30": "bar", + "31": "bar", + "32": "bar", + "33": "bar", + "34": "bar", + "35": "bar", + "36": "bar", + "37": "bar", + "38": "bar", + "39": "bar", + "40": "bar", + "41": "bar", + "42": "bar", + "43": "bar", + "44": "bar", + "45": "bar", + "46": "bar", + "47": "bar", + "48": "bar", + "49": "bar", + "50": "bar", + "51": "bar", + "52": "bar", + "53": "bar", + "54": "bar", + "55": "bar", + "56": "bar", + "57": "bar", + "58": "bar", + "59": "bar", + "60": "bar", + "61": "bar", + "62": "bar", + "63": "bar", + "64": "bar", + "65": "bar", + "66": "bar", + "67": "bar", + "68": "bar", + "69": "bar", + "70": "bar", + "71": "bar", + "72": "bar", + "73": "bar", + "74": "bar", + "75": "bar", + "76": "bar", + "77": "bar", + "78": "bar", + "79": "bar", + "80": "bar", + "81": "bar", + "82": "bar", + "83": "bar", + "84": "bar", + "85": "bar", + "86": "bar", + "87": "bar", + "88": "bar", + "89": "bar", + "90": "bar", + "91": "bar", + "92": "bar", + "93": "bar", + "94": "bar", + "95": "bar", + "96": "bar", + "97": "bar", + "98": "bar", + "99": "bar", + "100": "bar", + "101": "bar", + "102": "bar", + "103": "bar", + "104": "bar", + "105": "bar", + "106": "bar", + "107": "bar", + "108": "bar", + "109": "bar", + "110": "bar", + "111": "bar", + "112": "bar", + "113": "bar", + "114": "bar", + "115": "bar", + "116": "bar", + "117": "bar", + "118": "bar", + "119": "bar", + "120": "bar", + "121": "bar", + "122": "bar", + "123": "bar", + "124": "bar", + "125": "bar", + "126": "bar", + "127": "bar", + "128": "bar", + "129": "bar", + "130": "bar", + "131": "bar", + "132": "bar", + "133": "bar", + "134": "bar", + "135": "bar", + "136": "bar", + "137": "bar", + "138": "bar", + "139": "bar", + }, + "type": "custom", + }, + testing_criteria=[ + { + "input": [ + { + "content": "content", + "role": "role", + } + ], + "labels": ["string"], + "model": "model", + "name": "name", + "passing_labels": ["string"], + "type": "label_model", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(EvalCreateResponse, eval, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.evals.with_streaming_response.create( + data_source_config={ + "item_schema": { + "0": "bar", + "1": "bar", + "2": "bar", + "3": "bar", + "4": "bar", + "5": "bar", + "6": "bar", + "7": "bar", + "8": "bar", + "9": "bar", + "10": "bar", + "11": "bar", + "12": "bar", + "13": "bar", + "14": "bar", + "15": "bar", + "16": "bar", + "17": "bar", + "18": "bar", + "19": "bar", + "20": "bar", + "21": "bar", + "22": "bar", + "23": "bar", + "24": "bar", + "25": "bar", + "26": "bar", + "27": "bar", + "28": "bar", + "29": "bar", + "30": "bar", + "31": "bar", + "32": "bar", + "33": "bar", + "34": "bar", + "35": "bar", + "36": "bar", + "37": "bar", + "38": "bar", + "39": "bar", + "40": "bar", + "41": "bar", + "42": "bar", + "43": "bar", + "44": "bar", + "45": "bar", + "46": "bar", + "47": "bar", + "48": "bar", + "49": "bar", + "50": "bar", + "51": "bar", + "52": "bar", + "53": "bar", + "54": "bar", + "55": "bar", + "56": "bar", + "57": "bar", + "58": "bar", + "59": "bar", + "60": "bar", + "61": "bar", + "62": "bar", + "63": "bar", + "64": "bar", + "65": "bar", + "66": "bar", + "67": "bar", + "68": "bar", + "69": "bar", + "70": "bar", + "71": "bar", + "72": "bar", + "73": "bar", + "74": "bar", + "75": "bar", + "76": "bar", + "77": "bar", + "78": "bar", + "79": "bar", + "80": "bar", + "81": "bar", + "82": "bar", + "83": "bar", + "84": "bar", + "85": "bar", + "86": "bar", + "87": "bar", + "88": "bar", + "89": "bar", + "90": "bar", + "91": "bar", + "92": "bar", + "93": "bar", + "94": "bar", + "95": "bar", + "96": "bar", + "97": "bar", + "98": "bar", + "99": "bar", + "100": "bar", + "101": "bar", + "102": "bar", + "103": "bar", + "104": "bar", + "105": "bar", + "106": "bar", + "107": "bar", + "108": "bar", + "109": "bar", + "110": "bar", + "111": "bar", + "112": "bar", + "113": "bar", + "114": "bar", + "115": "bar", + "116": "bar", + "117": "bar", + "118": "bar", + "119": "bar", + "120": "bar", + "121": "bar", + "122": "bar", + "123": "bar", + "124": "bar", + "125": "bar", + "126": "bar", + "127": "bar", + "128": "bar", + "129": "bar", + "130": "bar", + "131": "bar", + "132": "bar", + "133": "bar", + "134": "bar", + "135": "bar", + "136": "bar", + "137": "bar", + "138": "bar", + "139": "bar", + }, + "type": "custom", + }, + testing_criteria=[ + { + "input": [ + { + "content": "content", + "role": "role", + } + ], + "labels": ["string"], + "model": "model", + "name": "name", + "passing_labels": ["string"], + "type": "label_model", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = response.parse() + assert_matches_type(EvalCreateResponse, eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + eval = client.evals.retrieve( + "eval_id", + ) + assert_matches_type(EvalRetrieveResponse, eval, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.evals.with_raw_response.retrieve( + "eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(EvalRetrieveResponse, eval, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.evals.with_streaming_response.retrieve( + "eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = response.parse() + assert_matches_type(EvalRetrieveResponse, eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + eval = client.evals.update( + eval_id="eval_id", + ) + assert_matches_type(EvalUpdateResponse, eval, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + eval = client.evals.update( + eval_id="eval_id", + metadata={"foo": "string"}, + name="name", + ) + assert_matches_type(EvalUpdateResponse, eval, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.evals.with_raw_response.update( + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(EvalUpdateResponse, eval, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.evals.with_streaming_response.update( + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = response.parse() + assert_matches_type(EvalUpdateResponse, eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.with_raw_response.update( + eval_id="", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + eval = client.evals.list() + assert_matches_type(SyncCursorPage[EvalListResponse], eval, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + eval = client.evals.list( + after="after", + limit=0, + order="asc", + order_by="created_at", + ) + assert_matches_type(SyncCursorPage[EvalListResponse], eval, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.evals.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(SyncCursorPage[EvalListResponse], eval, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.evals.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = response.parse() + assert_matches_type(SyncCursorPage[EvalListResponse], eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + eval = client.evals.delete( + "eval_id", + ) + assert_matches_type(EvalDeleteResponse, eval, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.evals.with_raw_response.delete( + "eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(EvalDeleteResponse, eval, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.evals.with_streaming_response.delete( + "eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = response.parse() + assert_matches_type(EvalDeleteResponse, eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + client.evals.with_raw_response.delete( + "", + ) + + +class TestAsyncEvals: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + eval = await async_client.evals.create( + data_source_config={ + "item_schema": { + "0": "bar", + "1": "bar", + "2": "bar", + "3": "bar", + "4": "bar", + "5": "bar", + "6": "bar", + "7": "bar", + "8": "bar", + "9": "bar", + "10": "bar", + "11": "bar", + "12": "bar", + "13": "bar", + "14": "bar", + "15": "bar", + "16": "bar", + "17": "bar", + "18": "bar", + "19": "bar", + "20": "bar", + "21": "bar", + "22": "bar", + "23": "bar", + "24": "bar", + "25": "bar", + "26": "bar", + "27": "bar", + "28": "bar", + "29": "bar", + "30": "bar", + "31": "bar", + "32": "bar", + "33": "bar", + "34": "bar", + "35": "bar", + "36": "bar", + "37": "bar", + "38": "bar", + "39": "bar", + "40": "bar", + "41": "bar", + "42": "bar", + "43": "bar", + "44": "bar", + "45": "bar", + "46": "bar", + "47": "bar", + "48": "bar", + "49": "bar", + "50": "bar", + "51": "bar", + "52": "bar", + "53": "bar", + "54": "bar", + "55": "bar", + "56": "bar", + "57": "bar", + "58": "bar", + "59": "bar", + "60": "bar", + "61": "bar", + "62": "bar", + "63": "bar", + "64": "bar", + "65": "bar", + "66": "bar", + "67": "bar", + "68": "bar", + "69": "bar", + "70": "bar", + "71": "bar", + "72": "bar", + "73": "bar", + "74": "bar", + "75": "bar", + "76": "bar", + "77": "bar", + "78": "bar", + "79": "bar", + "80": "bar", + "81": "bar", + "82": "bar", + "83": "bar", + "84": "bar", + "85": "bar", + "86": "bar", + "87": "bar", + "88": "bar", + "89": "bar", + "90": "bar", + "91": "bar", + "92": "bar", + "93": "bar", + "94": "bar", + "95": "bar", + "96": "bar", + "97": "bar", + "98": "bar", + "99": "bar", + "100": "bar", + "101": "bar", + "102": "bar", + "103": "bar", + "104": "bar", + "105": "bar", + "106": "bar", + "107": "bar", + "108": "bar", + "109": "bar", + "110": "bar", + "111": "bar", + "112": "bar", + "113": "bar", + "114": "bar", + "115": "bar", + "116": "bar", + "117": "bar", + "118": "bar", + "119": "bar", + "120": "bar", + "121": "bar", + "122": "bar", + "123": "bar", + "124": "bar", + "125": "bar", + "126": "bar", + "127": "bar", + "128": "bar", + "129": "bar", + "130": "bar", + "131": "bar", + "132": "bar", + "133": "bar", + "134": "bar", + "135": "bar", + "136": "bar", + "137": "bar", + "138": "bar", + "139": "bar", + }, + "type": "custom", + }, + testing_criteria=[ + { + "input": [ + { + "content": "content", + "role": "role", + } + ], + "labels": ["string"], + "model": "model", + "name": "name", + "passing_labels": ["string"], + "type": "label_model", + } + ], + ) + assert_matches_type(EvalCreateResponse, eval, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + eval = await async_client.evals.create( + data_source_config={ + "item_schema": { + "0": "bar", + "1": "bar", + "2": "bar", + "3": "bar", + "4": "bar", + "5": "bar", + "6": "bar", + "7": "bar", + "8": "bar", + "9": "bar", + "10": "bar", + "11": "bar", + "12": "bar", + "13": "bar", + "14": "bar", + "15": "bar", + "16": "bar", + "17": "bar", + "18": "bar", + "19": "bar", + "20": "bar", + "21": "bar", + "22": "bar", + "23": "bar", + "24": "bar", + "25": "bar", + "26": "bar", + "27": "bar", + "28": "bar", + "29": "bar", + "30": "bar", + "31": "bar", + "32": "bar", + "33": "bar", + "34": "bar", + "35": "bar", + "36": "bar", + "37": "bar", + "38": "bar", + "39": "bar", + "40": "bar", + "41": "bar", + "42": "bar", + "43": "bar", + "44": "bar", + "45": "bar", + "46": "bar", + "47": "bar", + "48": "bar", + "49": "bar", + "50": "bar", + "51": "bar", + "52": "bar", + "53": "bar", + "54": "bar", + "55": "bar", + "56": "bar", + "57": "bar", + "58": "bar", + "59": "bar", + "60": "bar", + "61": "bar", + "62": "bar", + "63": "bar", + "64": "bar", + "65": "bar", + "66": "bar", + "67": "bar", + "68": "bar", + "69": "bar", + "70": "bar", + "71": "bar", + "72": "bar", + "73": "bar", + "74": "bar", + "75": "bar", + "76": "bar", + "77": "bar", + "78": "bar", + "79": "bar", + "80": "bar", + "81": "bar", + "82": "bar", + "83": "bar", + "84": "bar", + "85": "bar", + "86": "bar", + "87": "bar", + "88": "bar", + "89": "bar", + "90": "bar", + "91": "bar", + "92": "bar", + "93": "bar", + "94": "bar", + "95": "bar", + "96": "bar", + "97": "bar", + "98": "bar", + "99": "bar", + "100": "bar", + "101": "bar", + "102": "bar", + "103": "bar", + "104": "bar", + "105": "bar", + "106": "bar", + "107": "bar", + "108": "bar", + "109": "bar", + "110": "bar", + "111": "bar", + "112": "bar", + "113": "bar", + "114": "bar", + "115": "bar", + "116": "bar", + "117": "bar", + "118": "bar", + "119": "bar", + "120": "bar", + "121": "bar", + "122": "bar", + "123": "bar", + "124": "bar", + "125": "bar", + "126": "bar", + "127": "bar", + "128": "bar", + "129": "bar", + "130": "bar", + "131": "bar", + "132": "bar", + "133": "bar", + "134": "bar", + "135": "bar", + "136": "bar", + "137": "bar", + "138": "bar", + "139": "bar", + }, + "type": "custom", + "include_sample_schema": True, + }, + testing_criteria=[ + { + "input": [ + { + "content": "content", + "role": "role", + } + ], + "labels": ["string"], + "model": "model", + "name": "name", + "passing_labels": ["string"], + "type": "label_model", + } + ], + metadata={"foo": "string"}, + name="name", + share_with_openai=True, + ) + assert_matches_type(EvalCreateResponse, eval, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.with_raw_response.create( + data_source_config={ + "item_schema": { + "0": "bar", + "1": "bar", + "2": "bar", + "3": "bar", + "4": "bar", + "5": "bar", + "6": "bar", + "7": "bar", + "8": "bar", + "9": "bar", + "10": "bar", + "11": "bar", + "12": "bar", + "13": "bar", + "14": "bar", + "15": "bar", + "16": "bar", + "17": "bar", + "18": "bar", + "19": "bar", + "20": "bar", + "21": "bar", + "22": "bar", + "23": "bar", + "24": "bar", + "25": "bar", + "26": "bar", + "27": "bar", + "28": "bar", + "29": "bar", + "30": "bar", + "31": "bar", + "32": "bar", + "33": "bar", + "34": "bar", + "35": "bar", + "36": "bar", + "37": "bar", + "38": "bar", + "39": "bar", + "40": "bar", + "41": "bar", + "42": "bar", + "43": "bar", + "44": "bar", + "45": "bar", + "46": "bar", + "47": "bar", + "48": "bar", + "49": "bar", + "50": "bar", + "51": "bar", + "52": "bar", + "53": "bar", + "54": "bar", + "55": "bar", + "56": "bar", + "57": "bar", + "58": "bar", + "59": "bar", + "60": "bar", + "61": "bar", + "62": "bar", + "63": "bar", + "64": "bar", + "65": "bar", + "66": "bar", + "67": "bar", + "68": "bar", + "69": "bar", + "70": "bar", + "71": "bar", + "72": "bar", + "73": "bar", + "74": "bar", + "75": "bar", + "76": "bar", + "77": "bar", + "78": "bar", + "79": "bar", + "80": "bar", + "81": "bar", + "82": "bar", + "83": "bar", + "84": "bar", + "85": "bar", + "86": "bar", + "87": "bar", + "88": "bar", + "89": "bar", + "90": "bar", + "91": "bar", + "92": "bar", + "93": "bar", + "94": "bar", + "95": "bar", + "96": "bar", + "97": "bar", + "98": "bar", + "99": "bar", + "100": "bar", + "101": "bar", + "102": "bar", + "103": "bar", + "104": "bar", + "105": "bar", + "106": "bar", + "107": "bar", + "108": "bar", + "109": "bar", + "110": "bar", + "111": "bar", + "112": "bar", + "113": "bar", + "114": "bar", + "115": "bar", + "116": "bar", + "117": "bar", + "118": "bar", + "119": "bar", + "120": "bar", + "121": "bar", + "122": "bar", + "123": "bar", + "124": "bar", + "125": "bar", + "126": "bar", + "127": "bar", + "128": "bar", + "129": "bar", + "130": "bar", + "131": "bar", + "132": "bar", + "133": "bar", + "134": "bar", + "135": "bar", + "136": "bar", + "137": "bar", + "138": "bar", + "139": "bar", + }, + "type": "custom", + }, + testing_criteria=[ + { + "input": [ + { + "content": "content", + "role": "role", + } + ], + "labels": ["string"], + "model": "model", + "name": "name", + "passing_labels": ["string"], + "type": "label_model", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(EvalCreateResponse, eval, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.with_streaming_response.create( + data_source_config={ + "item_schema": { + "0": "bar", + "1": "bar", + "2": "bar", + "3": "bar", + "4": "bar", + "5": "bar", + "6": "bar", + "7": "bar", + "8": "bar", + "9": "bar", + "10": "bar", + "11": "bar", + "12": "bar", + "13": "bar", + "14": "bar", + "15": "bar", + "16": "bar", + "17": "bar", + "18": "bar", + "19": "bar", + "20": "bar", + "21": "bar", + "22": "bar", + "23": "bar", + "24": "bar", + "25": "bar", + "26": "bar", + "27": "bar", + "28": "bar", + "29": "bar", + "30": "bar", + "31": "bar", + "32": "bar", + "33": "bar", + "34": "bar", + "35": "bar", + "36": "bar", + "37": "bar", + "38": "bar", + "39": "bar", + "40": "bar", + "41": "bar", + "42": "bar", + "43": "bar", + "44": "bar", + "45": "bar", + "46": "bar", + "47": "bar", + "48": "bar", + "49": "bar", + "50": "bar", + "51": "bar", + "52": "bar", + "53": "bar", + "54": "bar", + "55": "bar", + "56": "bar", + "57": "bar", + "58": "bar", + "59": "bar", + "60": "bar", + "61": "bar", + "62": "bar", + "63": "bar", + "64": "bar", + "65": "bar", + "66": "bar", + "67": "bar", + "68": "bar", + "69": "bar", + "70": "bar", + "71": "bar", + "72": "bar", + "73": "bar", + "74": "bar", + "75": "bar", + "76": "bar", + "77": "bar", + "78": "bar", + "79": "bar", + "80": "bar", + "81": "bar", + "82": "bar", + "83": "bar", + "84": "bar", + "85": "bar", + "86": "bar", + "87": "bar", + "88": "bar", + "89": "bar", + "90": "bar", + "91": "bar", + "92": "bar", + "93": "bar", + "94": "bar", + "95": "bar", + "96": "bar", + "97": "bar", + "98": "bar", + "99": "bar", + "100": "bar", + "101": "bar", + "102": "bar", + "103": "bar", + "104": "bar", + "105": "bar", + "106": "bar", + "107": "bar", + "108": "bar", + "109": "bar", + "110": "bar", + "111": "bar", + "112": "bar", + "113": "bar", + "114": "bar", + "115": "bar", + "116": "bar", + "117": "bar", + "118": "bar", + "119": "bar", + "120": "bar", + "121": "bar", + "122": "bar", + "123": "bar", + "124": "bar", + "125": "bar", + "126": "bar", + "127": "bar", + "128": "bar", + "129": "bar", + "130": "bar", + "131": "bar", + "132": "bar", + "133": "bar", + "134": "bar", + "135": "bar", + "136": "bar", + "137": "bar", + "138": "bar", + "139": "bar", + }, + "type": "custom", + }, + testing_criteria=[ + { + "input": [ + { + "content": "content", + "role": "role", + } + ], + "labels": ["string"], + "model": "model", + "name": "name", + "passing_labels": ["string"], + "type": "label_model", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = await response.parse() + assert_matches_type(EvalCreateResponse, eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + eval = await async_client.evals.retrieve( + "eval_id", + ) + assert_matches_type(EvalRetrieveResponse, eval, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.with_raw_response.retrieve( + "eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(EvalRetrieveResponse, eval, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.with_streaming_response.retrieve( + "eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = await response.parse() + assert_matches_type(EvalRetrieveResponse, eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + eval = await async_client.evals.update( + eval_id="eval_id", + ) + assert_matches_type(EvalUpdateResponse, eval, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + eval = await async_client.evals.update( + eval_id="eval_id", + metadata={"foo": "string"}, + name="name", + ) + assert_matches_type(EvalUpdateResponse, eval, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.with_raw_response.update( + eval_id="eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(EvalUpdateResponse, eval, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.with_streaming_response.update( + eval_id="eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = await response.parse() + assert_matches_type(EvalUpdateResponse, eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.with_raw_response.update( + eval_id="", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + eval = await async_client.evals.list() + assert_matches_type(AsyncCursorPage[EvalListResponse], eval, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + eval = await async_client.evals.list( + after="after", + limit=0, + order="asc", + order_by="created_at", + ) + assert_matches_type(AsyncCursorPage[EvalListResponse], eval, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(AsyncCursorPage[EvalListResponse], eval, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = await response.parse() + assert_matches_type(AsyncCursorPage[EvalListResponse], eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + eval = await async_client.evals.delete( + "eval_id", + ) + assert_matches_type(EvalDeleteResponse, eval, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.evals.with_raw_response.delete( + "eval_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + eval = response.parse() + assert_matches_type(EvalDeleteResponse, eval, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.evals.with_streaming_response.delete( + "eval_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + eval = await response.parse() + assert_matches_type(EvalDeleteResponse, eval, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `eval_id` but received ''"): + await async_client.evals.with_raw_response.delete( + "", + ) From 58163842137c006c37e833c7d08e02dc7415a59b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:44:37 +0000 Subject: [PATCH 864/914] chore(internal): fix examples (#2288) --- .stats.yml | 4 +- tests/api_resources/beta/test_threads.py | 8 +- tests/api_resources/beta/threads/test_runs.py | 8 +- tests/api_resources/test_evals.py | 1144 +---------------- tests/api_resources/test_images.py | 12 +- tests/api_resources/test_moderations.py | 4 +- 6 files changed, 26 insertions(+), 1154 deletions(-) diff --git a/.stats.yml b/.stats.yml index ebe07c1372..4a82ee242d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-472fe3036ea745365257fe870c0330917fb3153705c2826f49873cd631319b0a.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-32de3bc513663c5fac922c49be41c222b6ee8c0b841d8966bcdfa489d441daa3.yml openapi_spec_hash: ea86343b5e9858a74e85da8ab2c532f6 -config_hash: ef19d36c307306f14f2e1cd5c834a151 +config_hash: d6c61213488683418adb860a9ee1501b diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index ecf5b11102..9916d5bdc6 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -220,7 +220,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-4o", + model="string", parallel_tool_calls=True, response_format="auto", stream=False, @@ -309,7 +309,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-4o", + model="string", parallel_tool_calls=True, response_format="auto", temperature=1, @@ -584,7 +584,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-4o", + model="string", parallel_tool_calls=True, response_format="auto", stream=False, @@ -673,7 +673,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-4o", + model="string", parallel_tool_calls=True, response_format="auto", temperature=1, diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index d05ee96144..4230ccebe4 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -54,7 +54,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-4o", + model="string", parallel_tool_calls=True, reasoning_effort="low", response_format="auto", @@ -138,7 +138,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-4o", + model="string", parallel_tool_calls=True, reasoning_effort="low", response_format="auto", @@ -552,7 +552,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-4o", + model="string", parallel_tool_calls=True, reasoning_effort="low", response_format="auto", @@ -636,7 +636,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-4o", + model="string", parallel_tool_calls=True, reasoning_effort="low", response_format="auto", diff --git a/tests/api_resources/test_evals.py b/tests/api_resources/test_evals.py index 33ba92cda5..8d03513b32 100644 --- a/tests/api_resources/test_evals.py +++ b/tests/api_resources/test_evals.py @@ -28,148 +28,7 @@ class TestEvals: def test_method_create(self, client: OpenAI) -> None: eval = client.evals.create( data_source_config={ - "item_schema": { - "0": "bar", - "1": "bar", - "2": "bar", - "3": "bar", - "4": "bar", - "5": "bar", - "6": "bar", - "7": "bar", - "8": "bar", - "9": "bar", - "10": "bar", - "11": "bar", - "12": "bar", - "13": "bar", - "14": "bar", - "15": "bar", - "16": "bar", - "17": "bar", - "18": "bar", - "19": "bar", - "20": "bar", - "21": "bar", - "22": "bar", - "23": "bar", - "24": "bar", - "25": "bar", - "26": "bar", - "27": "bar", - "28": "bar", - "29": "bar", - "30": "bar", - "31": "bar", - "32": "bar", - "33": "bar", - "34": "bar", - "35": "bar", - "36": "bar", - "37": "bar", - "38": "bar", - "39": "bar", - "40": "bar", - "41": "bar", - "42": "bar", - "43": "bar", - "44": "bar", - "45": "bar", - "46": "bar", - "47": "bar", - "48": "bar", - "49": "bar", - "50": "bar", - "51": "bar", - "52": "bar", - "53": "bar", - "54": "bar", - "55": "bar", - "56": "bar", - "57": "bar", - "58": "bar", - "59": "bar", - "60": "bar", - "61": "bar", - "62": "bar", - "63": "bar", - "64": "bar", - "65": "bar", - "66": "bar", - "67": "bar", - "68": "bar", - "69": "bar", - "70": "bar", - "71": "bar", - "72": "bar", - "73": "bar", - "74": "bar", - "75": "bar", - "76": "bar", - "77": "bar", - "78": "bar", - "79": "bar", - "80": "bar", - "81": "bar", - "82": "bar", - "83": "bar", - "84": "bar", - "85": "bar", - "86": "bar", - "87": "bar", - "88": "bar", - "89": "bar", - "90": "bar", - "91": "bar", - "92": "bar", - "93": "bar", - "94": "bar", - "95": "bar", - "96": "bar", - "97": "bar", - "98": "bar", - "99": "bar", - "100": "bar", - "101": "bar", - "102": "bar", - "103": "bar", - "104": "bar", - "105": "bar", - "106": "bar", - "107": "bar", - "108": "bar", - "109": "bar", - "110": "bar", - "111": "bar", - "112": "bar", - "113": "bar", - "114": "bar", - "115": "bar", - "116": "bar", - "117": "bar", - "118": "bar", - "119": "bar", - "120": "bar", - "121": "bar", - "122": "bar", - "123": "bar", - "124": "bar", - "125": "bar", - "126": "bar", - "127": "bar", - "128": "bar", - "129": "bar", - "130": "bar", - "131": "bar", - "132": "bar", - "133": "bar", - "134": "bar", - "135": "bar", - "136": "bar", - "137": "bar", - "138": "bar", - "139": "bar", - }, + "item_schema": {"foo": "bar"}, "type": "custom", }, testing_criteria=[ @@ -194,148 +53,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: eval = client.evals.create( data_source_config={ - "item_schema": { - "0": "bar", - "1": "bar", - "2": "bar", - "3": "bar", - "4": "bar", - "5": "bar", - "6": "bar", - "7": "bar", - "8": "bar", - "9": "bar", - "10": "bar", - "11": "bar", - "12": "bar", - "13": "bar", - "14": "bar", - "15": "bar", - "16": "bar", - "17": "bar", - "18": "bar", - "19": "bar", - "20": "bar", - "21": "bar", - "22": "bar", - "23": "bar", - "24": "bar", - "25": "bar", - "26": "bar", - "27": "bar", - "28": "bar", - "29": "bar", - "30": "bar", - "31": "bar", - "32": "bar", - "33": "bar", - "34": "bar", - "35": "bar", - "36": "bar", - "37": "bar", - "38": "bar", - "39": "bar", - "40": "bar", - "41": "bar", - "42": "bar", - "43": "bar", - "44": "bar", - "45": "bar", - "46": "bar", - "47": "bar", - "48": "bar", - "49": "bar", - "50": "bar", - "51": "bar", - "52": "bar", - "53": "bar", - "54": "bar", - "55": "bar", - "56": "bar", - "57": "bar", - "58": "bar", - "59": "bar", - "60": "bar", - "61": "bar", - "62": "bar", - "63": "bar", - "64": "bar", - "65": "bar", - "66": "bar", - "67": "bar", - "68": "bar", - "69": "bar", - "70": "bar", - "71": "bar", - "72": "bar", - "73": "bar", - "74": "bar", - "75": "bar", - "76": "bar", - "77": "bar", - "78": "bar", - "79": "bar", - "80": "bar", - "81": "bar", - "82": "bar", - "83": "bar", - "84": "bar", - "85": "bar", - "86": "bar", - "87": "bar", - "88": "bar", - "89": "bar", - "90": "bar", - "91": "bar", - "92": "bar", - "93": "bar", - "94": "bar", - "95": "bar", - "96": "bar", - "97": "bar", - "98": "bar", - "99": "bar", - "100": "bar", - "101": "bar", - "102": "bar", - "103": "bar", - "104": "bar", - "105": "bar", - "106": "bar", - "107": "bar", - "108": "bar", - "109": "bar", - "110": "bar", - "111": "bar", - "112": "bar", - "113": "bar", - "114": "bar", - "115": "bar", - "116": "bar", - "117": "bar", - "118": "bar", - "119": "bar", - "120": "bar", - "121": "bar", - "122": "bar", - "123": "bar", - "124": "bar", - "125": "bar", - "126": "bar", - "127": "bar", - "128": "bar", - "129": "bar", - "130": "bar", - "131": "bar", - "132": "bar", - "133": "bar", - "134": "bar", - "135": "bar", - "136": "bar", - "137": "bar", - "138": "bar", - "139": "bar", - }, + "item_schema": {"foo": "bar"}, "type": "custom", "include_sample_schema": True, }, @@ -364,148 +82,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.evals.with_raw_response.create( data_source_config={ - "item_schema": { - "0": "bar", - "1": "bar", - "2": "bar", - "3": "bar", - "4": "bar", - "5": "bar", - "6": "bar", - "7": "bar", - "8": "bar", - "9": "bar", - "10": "bar", - "11": "bar", - "12": "bar", - "13": "bar", - "14": "bar", - "15": "bar", - "16": "bar", - "17": "bar", - "18": "bar", - "19": "bar", - "20": "bar", - "21": "bar", - "22": "bar", - "23": "bar", - "24": "bar", - "25": "bar", - "26": "bar", - "27": "bar", - "28": "bar", - "29": "bar", - "30": "bar", - "31": "bar", - "32": "bar", - "33": "bar", - "34": "bar", - "35": "bar", - "36": "bar", - "37": "bar", - "38": "bar", - "39": "bar", - "40": "bar", - "41": "bar", - "42": "bar", - "43": "bar", - "44": "bar", - "45": "bar", - "46": "bar", - "47": "bar", - "48": "bar", - "49": "bar", - "50": "bar", - "51": "bar", - "52": "bar", - "53": "bar", - "54": "bar", - "55": "bar", - "56": "bar", - "57": "bar", - "58": "bar", - "59": "bar", - "60": "bar", - "61": "bar", - "62": "bar", - "63": "bar", - "64": "bar", - "65": "bar", - "66": "bar", - "67": "bar", - "68": "bar", - "69": "bar", - "70": "bar", - "71": "bar", - "72": "bar", - "73": "bar", - "74": "bar", - "75": "bar", - "76": "bar", - "77": "bar", - "78": "bar", - "79": "bar", - "80": "bar", - "81": "bar", - "82": "bar", - "83": "bar", - "84": "bar", - "85": "bar", - "86": "bar", - "87": "bar", - "88": "bar", - "89": "bar", - "90": "bar", - "91": "bar", - "92": "bar", - "93": "bar", - "94": "bar", - "95": "bar", - "96": "bar", - "97": "bar", - "98": "bar", - "99": "bar", - "100": "bar", - "101": "bar", - "102": "bar", - "103": "bar", - "104": "bar", - "105": "bar", - "106": "bar", - "107": "bar", - "108": "bar", - "109": "bar", - "110": "bar", - "111": "bar", - "112": "bar", - "113": "bar", - "114": "bar", - "115": "bar", - "116": "bar", - "117": "bar", - "118": "bar", - "119": "bar", - "120": "bar", - "121": "bar", - "122": "bar", - "123": "bar", - "124": "bar", - "125": "bar", - "126": "bar", - "127": "bar", - "128": "bar", - "129": "bar", - "130": "bar", - "131": "bar", - "132": "bar", - "133": "bar", - "134": "bar", - "135": "bar", - "136": "bar", - "137": "bar", - "138": "bar", - "139": "bar", - }, + "item_schema": {"foo": "bar"}, "type": "custom", }, testing_criteria=[ @@ -534,148 +111,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: def test_streaming_response_create(self, client: OpenAI) -> None: with client.evals.with_streaming_response.create( data_source_config={ - "item_schema": { - "0": "bar", - "1": "bar", - "2": "bar", - "3": "bar", - "4": "bar", - "5": "bar", - "6": "bar", - "7": "bar", - "8": "bar", - "9": "bar", - "10": "bar", - "11": "bar", - "12": "bar", - "13": "bar", - "14": "bar", - "15": "bar", - "16": "bar", - "17": "bar", - "18": "bar", - "19": "bar", - "20": "bar", - "21": "bar", - "22": "bar", - "23": "bar", - "24": "bar", - "25": "bar", - "26": "bar", - "27": "bar", - "28": "bar", - "29": "bar", - "30": "bar", - "31": "bar", - "32": "bar", - "33": "bar", - "34": "bar", - "35": "bar", - "36": "bar", - "37": "bar", - "38": "bar", - "39": "bar", - "40": "bar", - "41": "bar", - "42": "bar", - "43": "bar", - "44": "bar", - "45": "bar", - "46": "bar", - "47": "bar", - "48": "bar", - "49": "bar", - "50": "bar", - "51": "bar", - "52": "bar", - "53": "bar", - "54": "bar", - "55": "bar", - "56": "bar", - "57": "bar", - "58": "bar", - "59": "bar", - "60": "bar", - "61": "bar", - "62": "bar", - "63": "bar", - "64": "bar", - "65": "bar", - "66": "bar", - "67": "bar", - "68": "bar", - "69": "bar", - "70": "bar", - "71": "bar", - "72": "bar", - "73": "bar", - "74": "bar", - "75": "bar", - "76": "bar", - "77": "bar", - "78": "bar", - "79": "bar", - "80": "bar", - "81": "bar", - "82": "bar", - "83": "bar", - "84": "bar", - "85": "bar", - "86": "bar", - "87": "bar", - "88": "bar", - "89": "bar", - "90": "bar", - "91": "bar", - "92": "bar", - "93": "bar", - "94": "bar", - "95": "bar", - "96": "bar", - "97": "bar", - "98": "bar", - "99": "bar", - "100": "bar", - "101": "bar", - "102": "bar", - "103": "bar", - "104": "bar", - "105": "bar", - "106": "bar", - "107": "bar", - "108": "bar", - "109": "bar", - "110": "bar", - "111": "bar", - "112": "bar", - "113": "bar", - "114": "bar", - "115": "bar", - "116": "bar", - "117": "bar", - "118": "bar", - "119": "bar", - "120": "bar", - "121": "bar", - "122": "bar", - "123": "bar", - "124": "bar", - "125": "bar", - "126": "bar", - "127": "bar", - "128": "bar", - "129": "bar", - "130": "bar", - "131": "bar", - "132": "bar", - "133": "bar", - "134": "bar", - "135": "bar", - "136": "bar", - "137": "bar", - "138": "bar", - "139": "bar", - }, + "item_schema": {"foo": "bar"}, "type": "custom", }, testing_criteria=[ @@ -868,148 +304,7 @@ class TestAsyncEvals: async def test_method_create(self, async_client: AsyncOpenAI) -> None: eval = await async_client.evals.create( data_source_config={ - "item_schema": { - "0": "bar", - "1": "bar", - "2": "bar", - "3": "bar", - "4": "bar", - "5": "bar", - "6": "bar", - "7": "bar", - "8": "bar", - "9": "bar", - "10": "bar", - "11": "bar", - "12": "bar", - "13": "bar", - "14": "bar", - "15": "bar", - "16": "bar", - "17": "bar", - "18": "bar", - "19": "bar", - "20": "bar", - "21": "bar", - "22": "bar", - "23": "bar", - "24": "bar", - "25": "bar", - "26": "bar", - "27": "bar", - "28": "bar", - "29": "bar", - "30": "bar", - "31": "bar", - "32": "bar", - "33": "bar", - "34": "bar", - "35": "bar", - "36": "bar", - "37": "bar", - "38": "bar", - "39": "bar", - "40": "bar", - "41": "bar", - "42": "bar", - "43": "bar", - "44": "bar", - "45": "bar", - "46": "bar", - "47": "bar", - "48": "bar", - "49": "bar", - "50": "bar", - "51": "bar", - "52": "bar", - "53": "bar", - "54": "bar", - "55": "bar", - "56": "bar", - "57": "bar", - "58": "bar", - "59": "bar", - "60": "bar", - "61": "bar", - "62": "bar", - "63": "bar", - "64": "bar", - "65": "bar", - "66": "bar", - "67": "bar", - "68": "bar", - "69": "bar", - "70": "bar", - "71": "bar", - "72": "bar", - "73": "bar", - "74": "bar", - "75": "bar", - "76": "bar", - "77": "bar", - "78": "bar", - "79": "bar", - "80": "bar", - "81": "bar", - "82": "bar", - "83": "bar", - "84": "bar", - "85": "bar", - "86": "bar", - "87": "bar", - "88": "bar", - "89": "bar", - "90": "bar", - "91": "bar", - "92": "bar", - "93": "bar", - "94": "bar", - "95": "bar", - "96": "bar", - "97": "bar", - "98": "bar", - "99": "bar", - "100": "bar", - "101": "bar", - "102": "bar", - "103": "bar", - "104": "bar", - "105": "bar", - "106": "bar", - "107": "bar", - "108": "bar", - "109": "bar", - "110": "bar", - "111": "bar", - "112": "bar", - "113": "bar", - "114": "bar", - "115": "bar", - "116": "bar", - "117": "bar", - "118": "bar", - "119": "bar", - "120": "bar", - "121": "bar", - "122": "bar", - "123": "bar", - "124": "bar", - "125": "bar", - "126": "bar", - "127": "bar", - "128": "bar", - "129": "bar", - "130": "bar", - "131": "bar", - "132": "bar", - "133": "bar", - "134": "bar", - "135": "bar", - "136": "bar", - "137": "bar", - "138": "bar", - "139": "bar", - }, + "item_schema": {"foo": "bar"}, "type": "custom", }, testing_criteria=[ @@ -1034,148 +329,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: eval = await async_client.evals.create( data_source_config={ - "item_schema": { - "0": "bar", - "1": "bar", - "2": "bar", - "3": "bar", - "4": "bar", - "5": "bar", - "6": "bar", - "7": "bar", - "8": "bar", - "9": "bar", - "10": "bar", - "11": "bar", - "12": "bar", - "13": "bar", - "14": "bar", - "15": "bar", - "16": "bar", - "17": "bar", - "18": "bar", - "19": "bar", - "20": "bar", - "21": "bar", - "22": "bar", - "23": "bar", - "24": "bar", - "25": "bar", - "26": "bar", - "27": "bar", - "28": "bar", - "29": "bar", - "30": "bar", - "31": "bar", - "32": "bar", - "33": "bar", - "34": "bar", - "35": "bar", - "36": "bar", - "37": "bar", - "38": "bar", - "39": "bar", - "40": "bar", - "41": "bar", - "42": "bar", - "43": "bar", - "44": "bar", - "45": "bar", - "46": "bar", - "47": "bar", - "48": "bar", - "49": "bar", - "50": "bar", - "51": "bar", - "52": "bar", - "53": "bar", - "54": "bar", - "55": "bar", - "56": "bar", - "57": "bar", - "58": "bar", - "59": "bar", - "60": "bar", - "61": "bar", - "62": "bar", - "63": "bar", - "64": "bar", - "65": "bar", - "66": "bar", - "67": "bar", - "68": "bar", - "69": "bar", - "70": "bar", - "71": "bar", - "72": "bar", - "73": "bar", - "74": "bar", - "75": "bar", - "76": "bar", - "77": "bar", - "78": "bar", - "79": "bar", - "80": "bar", - "81": "bar", - "82": "bar", - "83": "bar", - "84": "bar", - "85": "bar", - "86": "bar", - "87": "bar", - "88": "bar", - "89": "bar", - "90": "bar", - "91": "bar", - "92": "bar", - "93": "bar", - "94": "bar", - "95": "bar", - "96": "bar", - "97": "bar", - "98": "bar", - "99": "bar", - "100": "bar", - "101": "bar", - "102": "bar", - "103": "bar", - "104": "bar", - "105": "bar", - "106": "bar", - "107": "bar", - "108": "bar", - "109": "bar", - "110": "bar", - "111": "bar", - "112": "bar", - "113": "bar", - "114": "bar", - "115": "bar", - "116": "bar", - "117": "bar", - "118": "bar", - "119": "bar", - "120": "bar", - "121": "bar", - "122": "bar", - "123": "bar", - "124": "bar", - "125": "bar", - "126": "bar", - "127": "bar", - "128": "bar", - "129": "bar", - "130": "bar", - "131": "bar", - "132": "bar", - "133": "bar", - "134": "bar", - "135": "bar", - "136": "bar", - "137": "bar", - "138": "bar", - "139": "bar", - }, + "item_schema": {"foo": "bar"}, "type": "custom", "include_sample_schema": True, }, @@ -1204,148 +358,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.evals.with_raw_response.create( data_source_config={ - "item_schema": { - "0": "bar", - "1": "bar", - "2": "bar", - "3": "bar", - "4": "bar", - "5": "bar", - "6": "bar", - "7": "bar", - "8": "bar", - "9": "bar", - "10": "bar", - "11": "bar", - "12": "bar", - "13": "bar", - "14": "bar", - "15": "bar", - "16": "bar", - "17": "bar", - "18": "bar", - "19": "bar", - "20": "bar", - "21": "bar", - "22": "bar", - "23": "bar", - "24": "bar", - "25": "bar", - "26": "bar", - "27": "bar", - "28": "bar", - "29": "bar", - "30": "bar", - "31": "bar", - "32": "bar", - "33": "bar", - "34": "bar", - "35": "bar", - "36": "bar", - "37": "bar", - "38": "bar", - "39": "bar", - "40": "bar", - "41": "bar", - "42": "bar", - "43": "bar", - "44": "bar", - "45": "bar", - "46": "bar", - "47": "bar", - "48": "bar", - "49": "bar", - "50": "bar", - "51": "bar", - "52": "bar", - "53": "bar", - "54": "bar", - "55": "bar", - "56": "bar", - "57": "bar", - "58": "bar", - "59": "bar", - "60": "bar", - "61": "bar", - "62": "bar", - "63": "bar", - "64": "bar", - "65": "bar", - "66": "bar", - "67": "bar", - "68": "bar", - "69": "bar", - "70": "bar", - "71": "bar", - "72": "bar", - "73": "bar", - "74": "bar", - "75": "bar", - "76": "bar", - "77": "bar", - "78": "bar", - "79": "bar", - "80": "bar", - "81": "bar", - "82": "bar", - "83": "bar", - "84": "bar", - "85": "bar", - "86": "bar", - "87": "bar", - "88": "bar", - "89": "bar", - "90": "bar", - "91": "bar", - "92": "bar", - "93": "bar", - "94": "bar", - "95": "bar", - "96": "bar", - "97": "bar", - "98": "bar", - "99": "bar", - "100": "bar", - "101": "bar", - "102": "bar", - "103": "bar", - "104": "bar", - "105": "bar", - "106": "bar", - "107": "bar", - "108": "bar", - "109": "bar", - "110": "bar", - "111": "bar", - "112": "bar", - "113": "bar", - "114": "bar", - "115": "bar", - "116": "bar", - "117": "bar", - "118": "bar", - "119": "bar", - "120": "bar", - "121": "bar", - "122": "bar", - "123": "bar", - "124": "bar", - "125": "bar", - "126": "bar", - "127": "bar", - "128": "bar", - "129": "bar", - "130": "bar", - "131": "bar", - "132": "bar", - "133": "bar", - "134": "bar", - "135": "bar", - "136": "bar", - "137": "bar", - "138": "bar", - "139": "bar", - }, + "item_schema": {"foo": "bar"}, "type": "custom", }, testing_criteria=[ @@ -1374,148 +387,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.evals.with_streaming_response.create( data_source_config={ - "item_schema": { - "0": "bar", - "1": "bar", - "2": "bar", - "3": "bar", - "4": "bar", - "5": "bar", - "6": "bar", - "7": "bar", - "8": "bar", - "9": "bar", - "10": "bar", - "11": "bar", - "12": "bar", - "13": "bar", - "14": "bar", - "15": "bar", - "16": "bar", - "17": "bar", - "18": "bar", - "19": "bar", - "20": "bar", - "21": "bar", - "22": "bar", - "23": "bar", - "24": "bar", - "25": "bar", - "26": "bar", - "27": "bar", - "28": "bar", - "29": "bar", - "30": "bar", - "31": "bar", - "32": "bar", - "33": "bar", - "34": "bar", - "35": "bar", - "36": "bar", - "37": "bar", - "38": "bar", - "39": "bar", - "40": "bar", - "41": "bar", - "42": "bar", - "43": "bar", - "44": "bar", - "45": "bar", - "46": "bar", - "47": "bar", - "48": "bar", - "49": "bar", - "50": "bar", - "51": "bar", - "52": "bar", - "53": "bar", - "54": "bar", - "55": "bar", - "56": "bar", - "57": "bar", - "58": "bar", - "59": "bar", - "60": "bar", - "61": "bar", - "62": "bar", - "63": "bar", - "64": "bar", - "65": "bar", - "66": "bar", - "67": "bar", - "68": "bar", - "69": "bar", - "70": "bar", - "71": "bar", - "72": "bar", - "73": "bar", - "74": "bar", - "75": "bar", - "76": "bar", - "77": "bar", - "78": "bar", - "79": "bar", - "80": "bar", - "81": "bar", - "82": "bar", - "83": "bar", - "84": "bar", - "85": "bar", - "86": "bar", - "87": "bar", - "88": "bar", - "89": "bar", - "90": "bar", - "91": "bar", - "92": "bar", - "93": "bar", - "94": "bar", - "95": "bar", - "96": "bar", - "97": "bar", - "98": "bar", - "99": "bar", - "100": "bar", - "101": "bar", - "102": "bar", - "103": "bar", - "104": "bar", - "105": "bar", - "106": "bar", - "107": "bar", - "108": "bar", - "109": "bar", - "110": "bar", - "111": "bar", - "112": "bar", - "113": "bar", - "114": "bar", - "115": "bar", - "116": "bar", - "117": "bar", - "118": "bar", - "119": "bar", - "120": "bar", - "121": "bar", - "122": "bar", - "123": "bar", - "124": "bar", - "125": "bar", - "126": "bar", - "127": "bar", - "128": "bar", - "129": "bar", - "130": "bar", - "131": "bar", - "132": "bar", - "133": "bar", - "134": "bar", - "135": "bar", - "136": "bar", - "137": "bar", - "138": "bar", - "139": "bar", - }, + "item_schema": {"foo": "bar"}, "type": "custom", }, testing_criteria=[ diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 2e31f3354a..0a88f2ebcf 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -28,7 +28,7 @@ def test_method_create_variation(self, client: OpenAI) -> None: def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: image = client.images.create_variation( image=b"raw file contents", - model="dall-e-2", + model="string", n=1, response_format="url", size="1024x1024", @@ -74,7 +74,7 @@ def test_method_edit_with_all_params(self, client: OpenAI) -> None: image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", mask=b"raw file contents", - model="dall-e-2", + model="string", n=1, response_format="url", size="1024x1024", @@ -119,7 +119,7 @@ def test_method_generate(self, client: OpenAI) -> None: def test_method_generate_with_all_params(self, client: OpenAI) -> None: image = client.images.generate( prompt="A cute baby sea otter", - model="dall-e-3", + model="string", n=1, quality="standard", response_format="url", @@ -168,7 +168,7 @@ async def test_method_create_variation(self, async_client: AsyncOpenAI) -> None: async def test_method_create_variation_with_all_params(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.create_variation( image=b"raw file contents", - model="dall-e-2", + model="string", n=1, response_format="url", size="1024x1024", @@ -214,7 +214,7 @@ async def test_method_edit_with_all_params(self, async_client: AsyncOpenAI) -> N image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", mask=b"raw file contents", - model="dall-e-2", + model="string", n=1, response_format="url", size="1024x1024", @@ -259,7 +259,7 @@ async def test_method_generate(self, async_client: AsyncOpenAI) -> None: async def test_method_generate_with_all_params(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.generate( prompt="A cute baby sea otter", - model="dall-e-3", + model="string", n=1, quality="standard", response_format="url", diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index bbdeb63e49..6df6464110 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -28,7 +28,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: moderation = client.moderations.create( input="I want to kill them.", - model="omni-moderation-2024-09-26", + model="string", ) assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) @@ -71,7 +71,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: moderation = await async_client.moderations.create( input="I want to kill them.", - model="omni-moderation-2024-09-26", + model="string", ) assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) From 13ff6dd33e45d3c3c470d5673e81d3777b51aa5b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:46:29 +0000 Subject: [PATCH 865/914] chore(internal): skip broken test (#2289) --- .stats.yml | 2 +- .../fine_tuning/checkpoints/test_permissions.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 4a82ee242d..c39ce1186a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-32de3bc513663c5fac922c49be41c222b6ee8c0b841d8966bcdfa489d441daa3.yml openapi_spec_hash: ea86343b5e9858a74e85da8ab2c532f6 -config_hash: d6c61213488683418adb860a9ee1501b +config_hash: 43dc8df20ffec9d1503f91866cb2b7d9 diff --git a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py index d25c784c33..d40466919a 100644 --- a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py +++ b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py @@ -117,6 +117,7 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: fine_tuned_model_checkpoint="", ) + @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize def test_method_delete(self, client: OpenAI) -> None: permission = client.fine_tuning.checkpoints.permissions.delete( @@ -124,6 +125,7 @@ def test_method_delete(self, client: OpenAI) -> None: ) assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: response = client.fine_tuning.checkpoints.permissions.with_raw_response.delete( @@ -135,6 +137,7 @@ def test_raw_response_delete(self, client: OpenAI) -> None: permission = response.parse() assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize def test_streaming_response_delete(self, client: OpenAI) -> None: with client.fine_tuning.checkpoints.permissions.with_streaming_response.delete( @@ -148,6 +151,7 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize def test_path_params_delete(self, client: OpenAI) -> None: with pytest.raises( @@ -256,6 +260,7 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: fine_tuned_model_checkpoint="", ) + @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize async def test_method_delete(self, async_client: AsyncOpenAI) -> None: permission = await async_client.fine_tuning.checkpoints.permissions.delete( @@ -263,6 +268,7 @@ async def test_method_delete(self, async_client: AsyncOpenAI) -> None: ) assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: response = await async_client.fine_tuning.checkpoints.permissions.with_raw_response.delete( @@ -274,6 +280,7 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: permission = response.parse() assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) + @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: async with async_client.fine_tuning.checkpoints.permissions.with_streaming_response.delete( @@ -287,6 +294,7 @@ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> Non assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises( From 0c8343bb928b26d42feedd2a1a039e16ac58e744 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:47:02 +0000 Subject: [PATCH 866/914] release: 1.72.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c7704ce953..e6484623c0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.71.0" + ".": "1.72.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e8f2e22cb8..b02fae7e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 1.72.0 (2025-04-08) + +Full Changelog: [v1.71.0...v1.72.0](https://github.com/openai/openai-python/compare/v1.71.0...v1.72.0) + +### Features + +* **api:** Add evalapi to sdk ([#2287](https://github.com/openai/openai-python/issues/2287)) ([35262fc](https://github.com/openai/openai-python/commit/35262fcef6ccb7d1f75c9abdfdc68c3dcf87ef53)) + + +### Chores + +* **internal:** fix examples ([#2288](https://github.com/openai/openai-python/issues/2288)) ([39defd6](https://github.com/openai/openai-python/commit/39defd61e81ea0ec6b898be12e9fb7e621c0e532)) +* **internal:** skip broken test ([#2289](https://github.com/openai/openai-python/issues/2289)) ([e2c9bce](https://github.com/openai/openai-python/commit/e2c9bce1f59686ee053b495d06ea118b4a89e09e)) +* **internal:** slight transform perf improvement ([#2284](https://github.com/openai/openai-python/issues/2284)) ([746174f](https://github.com/openai/openai-python/commit/746174fae7a018ece5dab54fb0b5a15fcdd18f2f)) +* **tests:** improve enum examples ([#2286](https://github.com/openai/openai-python/issues/2286)) ([c9dd81c](https://github.com/openai/openai-python/commit/c9dd81ce0277e8b1f5db5e0a39c4c2bcd9004bcc)) + ## 1.71.0 (2025-04-07) Full Changelog: [v1.70.0...v1.71.0](https://github.com/openai/openai-python/compare/v1.70.0...v1.71.0) diff --git a/pyproject.toml b/pyproject.toml index 4583a5531f..29abf3ac4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.71.0" +version = "1.72.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 12e9d20bb1..e7c16742a2 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.71.0" # x-release-please-version +__version__ = "1.72.0" # x-release-please-version From ee272dd8dcfdb8cbe4faf6aa197fd03e24d978fa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 20:05:13 +0000 Subject: [PATCH 867/914] feat(api): manual updates --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index c39ce1186a..d4a4370a78 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-32de3bc513663c5fac922c49be41c222b6ee8c0b841d8966bcdfa489d441daa3.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-44b20fa9d24544217fe6bb48852037537030a1ad29b202936425110744fe66fb.yml openapi_spec_hash: ea86343b5e9858a74e85da8ab2c532f6 -config_hash: 43dc8df20ffec9d1503f91866cb2b7d9 +config_hash: 69e3afd56ccb0f0f822a7a9dc130fc99 From acf68ef3a57dfdae804072cc4f8abed32fd4f703 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 21:49:05 +0000 Subject: [PATCH 868/914] chore: slight wording improvement in README (#2291) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c52bffbb5f..f7e0eb6467 100644 --- a/README.md +++ b/README.md @@ -351,7 +351,7 @@ response = client.chat.responses.create( ## File uploads -Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. +Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. ```python from pathlib import Path From df5b323ea3963939d6a331c200116e688ce507e8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 09:26:34 +0000 Subject: [PATCH 869/914] chore: workaround build errors --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index d4a4370a78..9d8d07c6ac 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-44b20fa9d24544217fe6bb48852037537030a1ad29b202936425110744fe66fb.yml openapi_spec_hash: ea86343b5e9858a74e85da8ab2c532f6 -config_hash: 69e3afd56ccb0f0f822a7a9dc130fc99 +config_hash: 5ea32de61ff42fcf5e66cff8d9e247ea From 5020669996e299ee268db2f2f45f7b7cefc0ccd6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 16:31:40 +0000 Subject: [PATCH 870/914] chore(internal): expand CI branch coverage (#2295) --- .github/workflows/ci.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d2699cca8..bcd3e9d9d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,18 @@ name: CI on: push: - branches: - - main - pull_request: - branches: - - main - - next + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'preview-head/**' + - 'preview-base/**' + - 'preview/**' jobs: lint: name: lint runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 @@ -33,7 +33,6 @@ jobs: test: name: test runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 From c8833fc38f9634c43d3ca4091ecda6e3c4a07a56 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:08:33 +0000 Subject: [PATCH 871/914] chore(internal): reduce CI branch coverage --- .github/workflows/ci.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcd3e9d9d3..6f9cf84bb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,12 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'preview-head/**' - - 'preview-base/**' - - 'preview/**' + branches: + - main + pull_request: + branches: + - main + - next jobs: lint: From 284415c968c031c001ad833b4b955d99c6fbbc50 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 12:22:53 +0000 Subject: [PATCH 872/914] fix(perf): skip traversing types for NotGiven values --- src/openai/_utils/_transform.py | 11 +++++++++++ tests/test_transform.py | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 3ec620818c..3b2b8e009a 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -12,6 +12,7 @@ from ._utils import ( is_list, + is_given, is_mapping, is_iterable, ) @@ -258,6 +259,11 @@ def _transform_typeddict( result: dict[str, object] = {} annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): + if not is_given(value): + # we don't need to include `NotGiven` values here as they'll + # be stripped out before the request is sent anyway + continue + type_ = annotations.get(key) if type_ is None: # we do not have a type annotation for this field, leave it as is @@ -415,6 +421,11 @@ async def _async_transform_typeddict( result: dict[str, object] = {} annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): + if not is_given(value): + # we don't need to include `NotGiven` values here as they'll + # be stripped out before the request is sent anyway + continue + type_ = annotations.get(key) if type_ is None: # we do not have a type annotation for this field, leave it as is diff --git a/tests/test_transform.py b/tests/test_transform.py index cd584756d7..965f65f74f 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -8,7 +8,7 @@ import pytest -from openai._types import Base64FileInput +from openai._types import NOT_GIVEN, Base64FileInput from openai._utils import ( PropertyInfo, transform as _transform, @@ -444,3 +444,10 @@ async def test_transform_skipping(use_async: bool) -> None: # iterables of ints are converted to a list data = iter([1, 2, 3]) assert await transform(data, Iterable[int], use_async) == [1, 2, 3] + + +@parametrize +@pytest.mark.asyncio +async def test_strips_notgiven(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": NOT_GIVEN}, Foo1, use_async) == {} From 8086d61769128eeb249449eb12da9b6b3c46562e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 13:35:38 +0000 Subject: [PATCH 873/914] fix(perf): optimize some hot paths --- src/openai/_utils/_transform.py | 14 +++++++++++++- src/openai/_utils/_typing.py | 2 ++ src/openai/resources/audio/transcriptions.py | 14 ++++++++++++-- src/openai/resources/beta/threads/runs/runs.py | 12 ++++++++---- src/openai/resources/beta/threads/threads.py | 8 ++++++-- .../resources/chat/completions/completions.py | 8 ++++++-- src/openai/resources/completions.py | 8 ++++++-- src/openai/resources/responses/responses.py | 8 ++++++-- 8 files changed, 59 insertions(+), 15 deletions(-) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 3b2b8e009a..b0cc20a735 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -5,7 +5,7 @@ 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 +from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints import anyio import pydantic @@ -13,6 +13,7 @@ from ._utils import ( is_list, is_given, + lru_cache, is_mapping, is_iterable, ) @@ -109,6 +110,7 @@ class Params(TypedDict, total=False): return cast(_T, transformed) +@lru_cache(maxsize=8096) 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. @@ -433,3 +435,13 @@ async def _async_transform_typeddict( else: result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) return result + + +@lru_cache(maxsize=8096) +def get_type_hints( + obj: Any, + globalns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, + include_extras: bool = False, +) -> dict[str, Any]: + return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py index 278749b147..1958820f8d 100644 --- a/src/openai/_utils/_typing.py +++ b/src/openai/_utils/_typing.py @@ -13,6 +13,7 @@ get_origin, ) +from ._utils import lru_cache from .._types import InheritsGeneric from .._compat import is_union as _is_union @@ -66,6 +67,7 @@ def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: # Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +@lru_cache(maxsize=8096) 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])) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 2a77f91d69..7e62f70f60 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -321,7 +321,12 @@ def create( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( # type: ignore[return-value] "/audio/transcriptions", - body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams), + body=maybe_transform( + body, + transcription_create_params.TranscriptionCreateParamsStreaming + if stream + else transcription_create_params.TranscriptionCreateParamsNonStreaming, + ), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -616,7 +621,12 @@ async def create( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/audio/transcriptions", - body=await async_maybe_transform(body, transcription_create_params.TranscriptionCreateParams), + body=await async_maybe_transform( + body, + transcription_create_params.TranscriptionCreateParamsStreaming + if stream + else transcription_create_params.TranscriptionCreateParamsNonStreaming, + ), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index acb1c9b261..4d19010fea 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -587,7 +587,7 @@ def create( "top_p": top_p, "truncation_strategy": truncation_strategy, }, - run_create_params.RunCreateParams, + run_create_params.RunCreateParamsStreaming if stream else run_create_params.RunCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, @@ -1324,7 +1324,9 @@ def submit_tool_outputs( "tool_outputs": tool_outputs, "stream": stream, }, - run_submit_tool_outputs_params.RunSubmitToolOutputsParams, + run_submit_tool_outputs_params.RunSubmitToolOutputsParamsStreaming + if stream + else run_submit_tool_outputs_params.RunSubmitToolOutputsParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -1996,7 +1998,7 @@ async def create( "top_p": top_p, "truncation_strategy": truncation_strategy, }, - run_create_params.RunCreateParams, + run_create_params.RunCreateParamsStreaming if stream else run_create_params.RunCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, @@ -2732,7 +2734,9 @@ async def submit_tool_outputs( "tool_outputs": tool_outputs, "stream": stream, }, - run_submit_tool_outputs_params.RunSubmitToolOutputsParams, + run_submit_tool_outputs_params.RunSubmitToolOutputsParamsStreaming + if stream + else run_submit_tool_outputs_params.RunSubmitToolOutputsParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index d88559bdeb..c697be416d 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -717,7 +717,9 @@ def create_and_run( "top_p": top_p, "truncation_strategy": truncation_strategy, }, - thread_create_and_run_params.ThreadCreateAndRunParams, + thread_create_and_run_params.ThreadCreateAndRunParamsStreaming + if stream + else thread_create_and_run_params.ThreadCreateAndRunParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -1564,7 +1566,9 @@ async def create_and_run( "top_p": top_p, "truncation_strategy": truncation_strategy, }, - thread_create_and_run_params.ThreadCreateAndRunParams, + thread_create_and_run_params.ThreadCreateAndRunParamsStreaming + if stream + else thread_create_and_run_params.ThreadCreateAndRunParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index d28be012c9..f9e380cc72 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -947,7 +947,9 @@ def create( "user": user, "web_search_options": web_search_options, }, - completion_create_params.CompletionCreateParams, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -2033,7 +2035,9 @@ async def create( "user": user, "web_search_options": web_search_options, }, - completion_create_params.CompletionCreateParams, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 171f509352..592696f7da 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -559,7 +559,9 @@ def create( "top_p": top_p, "user": user, }, - completion_create_params.CompletionCreateParams, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -1101,7 +1103,9 @@ async def create( "top_p": top_p, "user": user, }, - completion_create_params.CompletionCreateParams, + completion_create_params.CompletionCreateParamsStreaming + if stream + else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 29ed3de42a..f8588178ed 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -623,7 +623,9 @@ def create( "truncation": truncation, "user": user, }, - response_create_params.ResponseCreateParams, + response_create_params.ResponseCreateParamsStreaming + if stream + else response_create_params.ResponseCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -1435,7 +1437,9 @@ async def create( "truncation": truncation, "user": user, }, - response_create_params.ResponseCreateParams, + response_create_params.ResponseCreateParamsStreaming + if stream + else response_create_params.ResponseCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout From ab091ca05349c594343dcb78ad5e7fd015d5804d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 12 Apr 2025 05:03:44 +0000 Subject: [PATCH 874/914] release: 1.73.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e6484623c0..c174a89798 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.72.0" + ".": "1.73.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b02fae7e87..7dffc39909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 1.73.0 (2025-04-12) + +Full Changelog: [v1.72.0...v1.73.0](https://github.com/openai/openai-python/compare/v1.72.0...v1.73.0) + +### Features + +* **api:** manual updates ([a3253dd](https://github.com/openai/openai-python/commit/a3253dd798c1eccd9810d4fc593e8c2a568bcf4f)) + + +### Bug Fixes + +* **perf:** optimize some hot paths ([f79d39f](https://github.com/openai/openai-python/commit/f79d39fbcaea8f366a9e48c06fb1696bab3e607d)) +* **perf:** skip traversing types for NotGiven values ([28d220d](https://github.com/openai/openai-python/commit/28d220de3b4a09d80450d0bcc9b347bbf68f81ec)) + + +### Chores + +* **internal:** expand CI branch coverage ([#2295](https://github.com/openai/openai-python/issues/2295)) ([0ae783b](https://github.com/openai/openai-python/commit/0ae783b99122975be521365de0b6d2bce46056c9)) +* **internal:** reduce CI branch coverage ([2fb7d42](https://github.com/openai/openai-python/commit/2fb7d425cda679a54aa3262090479fd747363bb4)) +* slight wording improvement in README ([#2291](https://github.com/openai/openai-python/issues/2291)) ([e020759](https://github.com/openai/openai-python/commit/e0207598d16a2a9cb3cb3a8e8e97fa9cfdccd5e8)) +* workaround build errors ([4e10c96](https://github.com/openai/openai-python/commit/4e10c96a483db28dedc2d8c2908765fb7317e049)) + ## 1.72.0 (2025-04-08) Full Changelog: [v1.71.0...v1.72.0](https://github.com/openai/openai-python/compare/v1.71.0...v1.72.0) diff --git a/pyproject.toml b/pyproject.toml index 29abf3ac4c..1126c96040 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.72.0" +version = "1.73.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e7c16742a2..bcc08e9c6d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.72.0" # x-release-please-version +__version__ = "1.73.0" # x-release-please-version From e3e791c85f0c508f530848025a7adeed71a8bbae Mon Sep 17 00:00:00 2001 From: Nikolai Pismennyi Date: Mon, 14 Apr 2025 12:04:43 +0300 Subject: [PATCH 875/914] fix(chat): skip azure async filter events (#2255) --- src/openai/lib/streaming/chat/_completions.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index 2146091354..f147696cca 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -113,6 +113,8 @@ def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: def __stream__(self) -> Iterator[ChatCompletionStreamEvent[ResponseFormatT]]: for sse_event in self._raw_stream: + if not _is_valid_chat_completion_chunk_weak(sse_event): + continue events_to_fire = self._state.handle_chunk(sse_event) for event in events_to_fire: yield event @@ -234,6 +236,8 @@ def current_completion_snapshot(self) -> ParsedChatCompletionSnapshot: async def __stream__(self) -> AsyncIterator[ChatCompletionStreamEvent[ResponseFormatT]]: async for sse_event in self._raw_stream: + if not _is_valid_chat_completion_chunk_weak(sse_event): + continue events_to_fire = self._state.handle_chunk(sse_event) for event in events_to_fire: yield event @@ -753,3 +757,12 @@ def _convert_initial_chunk_into_snapshot(chunk: ChatCompletionChunk) -> ParsedCh }, ), ) + + +def _is_valid_chat_completion_chunk_weak(sse_event: ChatCompletionChunk) -> bool: + # Although the _raw_stream is always supposed to contain only objects adhering to ChatCompletionChunk schema, + # this is broken by the Azure OpenAI in case of Asynchronous Filter enabled. + # An easy filter is to check for the "object" property: + # - should be "chat.completion.chunk" for a ChatCompletionChunk; + # - is an empty string for Asynchronous Filter events. + return sse_event.object == "chat.completion.chunk" # type: ignore # pylance reports this as a useless check From 1828fb6a71b12ba5deb7d57e7caa8584d2fe1aeb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:24:00 +0000 Subject: [PATCH 876/914] chore(internal): update pyright settings --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 1126c96040..27db20295f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -166,6 +166,7 @@ exclude = [ ] reportImplicitOverride = true +reportOverlappingOverload = false reportImportCycles = false reportPrivateUsage = false From 77ba4182e4e4a6f7e986762f6aef925ebb40f2f0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:30:49 +0000 Subject: [PATCH 877/914] chore(client): minor internal fixes --- src/openai/_base_client.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index f31e5af54b..1dd3a4772c 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -411,7 +411,8 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0 idempotency_header = self._idempotency_header if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: - headers[idempotency_header] = options.idempotency_key or self._idempotency_key() + options.idempotency_key = options.idempotency_key or self._idempotency_key() + headers[idempotency_header] = options.idempotency_key # Don't set these headers if they were already set or removed by the caller. We check # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. @@ -945,6 +946,10 @@ def _request( request = self._build_request(options, retries_taken=retries_taken) self._prepare_request(request) + if options.idempotency_key: + # ensure the idempotency key is reused between requests + input_options.idempotency_key = options.idempotency_key + kwargs: HttpxSendArgs = {} if self.custom_auth is not None: kwargs["auth"] = self.custom_auth @@ -1492,6 +1497,10 @@ async def _request( request = self._build_request(options, retries_taken=retries_taken) await self._prepare_request(request) + if options.idempotency_key: + # ensure the idempotency key is reused between requests + input_options.idempotency_key = options.idempotency_key + kwargs: HttpxSendArgs = {} if self.custom_auth is not None: kwargs["auth"] = self.custom_auth From 7d2b97d660882f68283cb2b54303660d1f73bec1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 16:40:51 +0000 Subject: [PATCH 878/914] feat(api): adding gpt-4.1 family of model IDs --- .stats.yml | 4 ++-- src/openai/resources/beta/assistants.py | 12 ++++++++++++ src/openai/types/beta/assistant_update_params.py | 6 ++++++ src/openai/types/shared/chat_model.py | 6 ++++++ src/openai/types/shared_params/chat_model.py | 6 ++++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9d8d07c6ac..b40485bd0a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-44b20fa9d24544217fe6bb48852037537030a1ad29b202936425110744fe66fb.yml -openapi_spec_hash: ea86343b5e9858a74e85da8ab2c532f6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a555f81249cb084f463dcefa4aba069f9341fdaf3dd6ac27d7f237fc90e8f488.yml +openapi_spec_hash: 8e590296cd1a54b9508510b0c7a2c45a config_hash: 5ea32de61ff42fcf5e66cff8d9e247ea diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 1c7cbf3737..43f6a7f135 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -223,6 +223,12 @@ def update( model: Union[ str, Literal[ + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", "o3-mini", "o3-mini-2025-01-31", "o1", @@ -666,6 +672,12 @@ async def update( model: Union[ str, Literal[ + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", "o3-mini", "o3-mini-2025-01-31", "o1", diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index d3ec7614fd..b28094a6a5 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -36,6 +36,12 @@ class AssistantUpdateParams(TypedDict, total=False): model: Union[ str, Literal[ + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", "o3-mini", "o3-mini-2025-01-31", "o1", diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index b19375725d..30878b4347 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -5,6 +5,12 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", "o3-mini", "o3-mini-2025-01-31", "o1", diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index ff81b07ac3..f606beb693 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -7,6 +7,12 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", "o3-mini", "o3-mini-2025-01-31", "o1", From 05810dd4088b6dbfc4194d7b0bea03eec236c83a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 16:41:56 +0000 Subject: [PATCH 879/914] release: 1.74.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c174a89798..71a38f2845 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.73.0" + ".": "1.74.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dffc39909..90af38d900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.74.0 (2025-04-14) + +Full Changelog: [v1.73.0...v1.74.0](https://github.com/openai/openai-python/compare/v1.73.0...v1.74.0) + +### Features + +* **api:** adding gpt-4.1 family of model IDs ([d4dae55](https://github.com/openai/openai-python/commit/d4dae5553ff3a2879b9ab79a6423661b212421f9)) + + +### Bug Fixes + +* **chat:** skip azure async filter events ([#2255](https://github.com/openai/openai-python/issues/2255)) ([fd3a38b](https://github.com/openai/openai-python/commit/fd3a38b1ed30af0a9f3302c1cfc6be6b352e65de)) + + +### Chores + +* **client:** minor internal fixes ([6071ae5](https://github.com/openai/openai-python/commit/6071ae5e8b4faa465afc8d07370737e66901900a)) +* **internal:** update pyright settings ([c8f8beb](https://github.com/openai/openai-python/commit/c8f8bebf852380a224701bc36826291d6387c53d)) + ## 1.73.0 (2025-04-12) Full Changelog: [v1.72.0...v1.73.0](https://github.com/openai/openai-python/compare/v1.72.0...v1.73.0) diff --git a/pyproject.toml b/pyproject.toml index 27db20295f..eb07cd5ba7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.73.0" +version = "1.74.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index bcc08e9c6d..b203ed859f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.73.0" # x-release-please-version +__version__ = "1.74.0" # x-release-please-version From 6d110a147f91cc5e9352ab1e823ed5eb4db06137 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 12:06:05 +0000 Subject: [PATCH 880/914] chore(internal): bump pyright version --- pyproject.toml | 2 +- requirements-dev.lock | 2 +- src/openai/_base_client.py | 6 +++++- src/openai/_models.py | 1 - src/openai/_utils/_typing.py | 2 +- tests/conftest.py | 2 +- tests/test_models.py | 2 +- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eb07cd5ba7..244dd2ecb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] managed = true # version pins are in requirements-dev.lock dev-dependencies = [ - "pyright>=1.1.359", + "pyright==1.1.399", "mypy", "respx", "pytest", diff --git a/requirements-dev.lock b/requirements-dev.lock index 11bb5c1b30..9875a2b860 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -126,7 +126,7 @@ pygments==2.18.0 # via rich pyjwt==2.8.0 # via msal -pyright==1.1.392.post0 +pyright==1.1.399 pytest==8.3.3 # via pytest-asyncio pytest-asyncio==0.24.0 diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 1dd3a4772c..d167c43763 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -100,7 +100,11 @@ _AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) if TYPE_CHECKING: - from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + from httpx._config import ( + DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] + ) + + HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG else: try: from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT diff --git a/src/openai/_models.py b/src/openai/_models.py index fc4f201e4e..9b1aeb30bf 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -20,7 +20,6 @@ ) import pydantic -import pydantic.generics from pydantic.fields import FieldInfo from ._types import ( diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py index 1958820f8d..1bac9542e2 100644 --- a/src/openai/_utils/_typing.py +++ b/src/openai/_utils/_typing.py @@ -110,7 +110,7 @@ class MyResponse(Foo[_T]): ``` """ cls = cast(object, get_origin(typ) or typ) - if cls in generic_bases: + if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] # we're given the class directly return extract_type_arg(typ, index) diff --git a/tests/conftest.py b/tests/conftest.py index fa82d39d86..8b01753e2f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,7 @@ from openai import OpenAI, AsyncOpenAI if TYPE_CHECKING: - from _pytest.fixtures import FixtureRequest + from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] pytest.register_assert_rewrite("tests.utils") diff --git a/tests/test_models.py b/tests/test_models.py index b9be1f3ea3..4b18940b49 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -832,7 +832,7 @@ class B(BaseModel): @pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") def test_type_alias_type() -> None: - Alias = TypeAliasType("Alias", str) + Alias = TypeAliasType("Alias", str) # pyright: ignore class Model(BaseModel): alias: Alias From ea638838b7aa5989516df3659a1be16825681fc6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 12:59:19 +0000 Subject: [PATCH 881/914] chore(internal): base client updates --- src/openai/_base_client.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index d167c43763..8b43a20699 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -121,6 +121,7 @@ class PageInfo: url: URL | NotGiven params: Query | NotGiven + json: Body | NotGiven @overload def __init__( @@ -136,19 +137,30 @@ def __init__( params: Query, ) -> None: ... + @overload + def __init__( + self, + *, + json: Body, + ) -> None: ... + def __init__( self, *, url: URL | NotGiven = NOT_GIVEN, + json: Body | NotGiven = NOT_GIVEN, params: Query | NotGiven = NOT_GIVEN, ) -> None: self.url = url + self.json = json self.params = params @override def __repr__(self) -> str: if self.url: return f"{self.__class__.__name__}(url={self.url})" + if self.json: + return f"{self.__class__.__name__}(json={self.json})" return f"{self.__class__.__name__}(params={self.params})" @@ -197,6 +209,19 @@ def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: options.url = str(url) return options + if not isinstance(info.json, NotGiven): + if not is_mapping(info.json): + raise TypeError("Pagination is only supported with mappings") + + if not options.json_data: + options.json_data = {**info.json} + else: + if not is_mapping(options.json_data): + raise TypeError("Pagination is only supported with mappings") + + options.json_data = {**options.json_data, **info.json} + return options + raise ValueError("Unexpected PageInfo state") From bf6dd816052d6ece746acc0339e5225d425dc1b3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 12:59:51 +0000 Subject: [PATCH 882/914] release: 1.74.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 71a38f2845..6603053537 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.74.0" + ".": "1.74.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 90af38d900..b03bbedb52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.74.1 (2025-04-16) + +Full Changelog: [v1.74.0...v1.74.1](https://github.com/openai/openai-python/compare/v1.74.0...v1.74.1) + +### Chores + +* **internal:** base client updates ([06303b5](https://github.com/openai/openai-python/commit/06303b501f8c17040c495971a4ee79ae340f6f4a)) +* **internal:** bump pyright version ([9fd1c77](https://github.com/openai/openai-python/commit/9fd1c778c3231616bf1331cb1daa86fdfca4cb7f)) + ## 1.74.0 (2025-04-14) Full Changelog: [v1.73.0...v1.74.0](https://github.com/openai/openai-python/compare/v1.73.0...v1.74.0) diff --git a/pyproject.toml b/pyproject.toml index 244dd2ecb1..e2cd25f69c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.74.0" +version = "1.74.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b203ed859f..5bbfee3232 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.74.0" # x-release-please-version +__version__ = "1.74.1" # x-release-please-version From c5ede36c6e2138e64f5194ecd5015ddc7cf50ec7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 16:42:28 +0000 Subject: [PATCH 883/914] feat(api): add o3 and o4-mini model IDs --- .stats.yml | 6 +- .../resources/chat/completions/completions.py | 82 +++++++---- src/openai/resources/completions.py | 24 +++- src/openai/resources/responses/responses.py | 130 +++++++++++++++++- src/openai/types/chat/chat_completion.py | 22 ++- .../types/chat/chat_completion_audio_param.py | 6 +- .../types/chat/chat_completion_chunk.py | 22 ++- .../types/chat/completion_create_params.py | 14 +- src/openai/types/completion_create_params.py | 5 +- src/openai/types/responses/response.py | 23 +++- .../types/responses/response_create_params.py | 23 +++- src/openai/types/shared/chat_model.py | 4 + src/openai/types/shared/reasoning.py | 15 +- src/openai/types/shared_params/chat_model.py | 4 + src/openai/types/shared_params/reasoning.py | 15 +- tests/api_resources/test_responses.py | 16 ++- 16 files changed, 342 insertions(+), 69 deletions(-) diff --git a/.stats.yml b/.stats.yml index b40485bd0a..848c5b5adb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a555f81249cb084f463dcefa4aba069f9341fdaf3dd6ac27d7f237fc90e8f488.yml -openapi_spec_hash: 8e590296cd1a54b9508510b0c7a2c45a -config_hash: 5ea32de61ff42fcf5e66cff8d9e247ea +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5633633cc38734869cf7d993f7b549bb8e4d10e0ec45381ec2cd91507cd8eb8f.yml +openapi_spec_hash: c855121b2b2324b99499c9244c21d24d +config_hash: d20837393b73efdb19cd08e04c1cc9a1 diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index f9e380cc72..d6214225d8 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -99,7 +99,7 @@ def create( reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, @@ -145,7 +145,7 @@ def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -201,7 +201,7 @@ def create( This value is now deprecated in favor of `max_completion_tokens`, and is not compatible with - [o1 series models](https://platform.openai.com/docs/guides/reasoning). + [o-series models](https://platform.openai.com/docs/guides/reasoning). metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and @@ -270,12 +270,17 @@ def create( latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in @@ -364,7 +369,7 @@ def create( reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -409,7 +414,7 @@ def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -474,7 +479,7 @@ def create( This value is now deprecated in favor of `max_completion_tokens`, and is not compatible with - [o1 series models](https://platform.openai.com/docs/guides/reasoning). + [o-series models](https://platform.openai.com/docs/guides/reasoning). metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and @@ -543,12 +548,17 @@ def create( latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in @@ -628,7 +638,7 @@ def create( reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -673,7 +683,7 @@ def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -738,7 +748,7 @@ def create( This value is now deprecated in favor of `max_completion_tokens`, and is not compatible with - [o1 series models](https://platform.openai.com/docs/guides/reasoning). + [o-series models](https://platform.openai.com/docs/guides/reasoning). metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and @@ -807,12 +817,17 @@ def create( latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in @@ -891,7 +906,7 @@ def create( reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -1187,7 +1202,7 @@ async def create( reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, @@ -1233,7 +1248,7 @@ async def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -1289,7 +1304,7 @@ async def create( This value is now deprecated in favor of `max_completion_tokens`, and is not compatible with - [o1 series models](https://platform.openai.com/docs/guides/reasoning). + [o-series models](https://platform.openai.com/docs/guides/reasoning). metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and @@ -1358,12 +1373,17 @@ async def create( latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in @@ -1452,7 +1472,7 @@ async def create( reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -1497,7 +1517,7 @@ async def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -1562,7 +1582,7 @@ async def create( This value is now deprecated in favor of `max_completion_tokens`, and is not compatible with - [o1 series models](https://platform.openai.com/docs/guides/reasoning). + [o-series models](https://platform.openai.com/docs/guides/reasoning). metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and @@ -1631,12 +1651,17 @@ async def create( latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in @@ -1716,7 +1741,7 @@ async def create( reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -1761,7 +1786,7 @@ async def create( [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio). - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -1826,7 +1851,7 @@ async def create( This value is now deprecated in favor of `max_completion_tokens`, and is not compatible with - [o1 series models](https://platform.openai.com/docs/guides/reasoning). + [o-series models](https://platform.openai.com/docs/guides/reasoning). metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and @@ -1895,12 +1920,17 @@ async def create( latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` utilized. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. store: Whether or not to store the output of this chat completion request for use in @@ -1979,7 +2009,7 @@ async def create( reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 592696f7da..aebf35d1f1 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -159,7 +159,9 @@ def create( Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. stream: Whether to stream back partial progress. If set, tokens will be sent as @@ -319,7 +321,9 @@ def create( Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -472,7 +476,9 @@ def create( Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -703,7 +709,9 @@ async def create( Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. stream: Whether to stream back partial progress. If set, tokens will be sent as @@ -863,7 +871,9 @@ async def create( Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -1016,7 +1026,9 @@ async def create( Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend. - stop: Up to 4 sequences where the API will stop generating further tokens. The + stop: Not supported with latest reasoning models `o3` and `o4-mini`. + + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. stream_options: Options for streaming response. Only set this when you set `stream: true`. diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index f8588178ed..f07b4d8c4a 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -89,6 +89,7 @@ def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -130,7 +131,7 @@ def create( - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - [Function calling](https://platform.openai.com/docs/guides/function-calling) - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -174,6 +175,24 @@ def create( Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + store: Whether to store the generated model response for later retrieval via API. stream: If set to true, the model response data will be streamed to the client as it is @@ -255,6 +274,7 @@ def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, @@ -295,7 +315,7 @@ def create( - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - [Function calling](https://platform.openai.com/docs/guides/function-calling) - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -346,6 +366,24 @@ def create( Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + store: Whether to store the generated model response for later retrieval via API. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -420,6 +458,7 @@ def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, @@ -460,7 +499,7 @@ def create( - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - [Function calling](https://platform.openai.com/docs/guides/function-calling) - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -511,6 +550,24 @@ def create( Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + store: Whether to store the generated model response for later retrieval via API. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -584,6 +641,7 @@ def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -613,6 +671,7 @@ def create( "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "reasoning": reasoning, + "service_tier": service_tier, "store": store, "stream": stream, "temperature": temperature, @@ -903,6 +962,7 @@ async def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -944,7 +1004,7 @@ async def create( - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - [Function calling](https://platform.openai.com/docs/guides/function-calling) - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -988,6 +1048,24 @@ async def create( Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + store: Whether to store the generated model response for later retrieval via API. stream: If set to true, the model response data will be streamed to the client as it is @@ -1069,6 +1147,7 @@ async def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, @@ -1109,7 +1188,7 @@ async def create( - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - [Function calling](https://platform.openai.com/docs/guides/function-calling) - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -1160,6 +1239,24 @@ async def create( Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + store: Whether to store the generated model response for later retrieval via API. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -1234,6 +1331,7 @@ async def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, @@ -1274,7 +1372,7 @@ async def create( - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) - [Function calling](https://platform.openai.com/docs/guides/function-calling) - model: Model ID used to generate the response, like `gpt-4o` or `o1`. OpenAI offers a + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare @@ -1325,6 +1423,24 @@ async def create( Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + service_tier: Specifies the latency tier to use for processing the request. This parameter is + relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + store: Whether to store the generated model response for later retrieval via API. temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will @@ -1398,6 +1514,7 @@ async def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1427,6 +1544,7 @@ async def create( "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "reasoning": reasoning, + "service_tier": service_tier, "store": store, "stream": stream, "temperature": temperature, diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index cb812a2702..3a235f89a5 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -59,8 +59,26 @@ class ChatCompletion(BaseModel): object: Literal["chat.completion"] """The object type, which is always `chat.completion`.""" - service_tier: Optional[Literal["scale", "default"]] = None - """The service tier used for processing the request.""" + service_tier: Optional[Literal["auto", "default", "flex"]] = None + """Specifies the latency tier to use for processing the request. + + This parameter is relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + """ system_fingerprint: Optional[str] = None """This fingerprint represents the backend configuration that the model runs with. diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index b902f2667f..25caada177 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -9,7 +9,7 @@ class ChatCompletionAudioParam(TypedDict, total=False): - format: Required[Literal["wav", "mp3", "flac", "opus", "pcm16"]] + format: Required[Literal["wav", "aac", "mp3", "flac", "opus", "pcm16"]] """Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. @@ -22,6 +22,6 @@ class ChatCompletionAudioParam(TypedDict, total=False): ] """The voice the model uses to respond. - Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, and - `shimmer`. + Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, + `onyx`, `sage`, and `shimmer`. """ diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 31b9cb5456..6fe996dd95 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -128,8 +128,26 @@ class ChatCompletionChunk(BaseModel): object: Literal["chat.completion.chunk"] """The object type, which is always `chat.completion.chunk`.""" - service_tier: Optional[Literal["scale", "default"]] = None - """The service tier used for processing the request.""" + service_tier: Optional[Literal["auto", "default", "flex"]] = None + """Specifies the latency tier to use for processing the request. + + This parameter is relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + """ system_fingerprint: Optional[str] = None """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 05103fba91..60d5f53cdd 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -45,7 +45,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ model: Required[Union[str, ChatModel]] - """Model ID used to generate the response, like `gpt-4o` or `o1`. + """Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the @@ -123,7 +123,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): This value is now deprecated in favor of `max_completion_tokens`, and is not compatible with - [o1 series models](https://platform.openai.com/docs/guides/reasoning). + [o-series models](https://platform.openai.com/docs/guides/reasoning). """ metadata: Optional[Metadata] @@ -208,7 +208,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): in the backend. """ - service_tier: Optional[Literal["auto", "default"]] + service_tier: Optional[Literal["auto", "default", "flex"]] """Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service: @@ -220,6 +220,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): latency guarentee. - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). - When not set, the default behavior is 'auto'. When this parameter is set, the response body will include the `service_tier` @@ -227,9 +230,10 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ stop: Union[Optional[str], List[str], None] - """Up to 4 sequences where the API will stop generating further tokens. + """Not supported with latest reasoning models `o3` and `o4-mini`. - The returned text will not contain the stop sequence. + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. """ store: Optional[bool] diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index fdb1680d26..6ae20cff83 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -120,9 +120,10 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ stop: Union[Optional[str], List[str], None] - """Up to 4 sequences where the API will stop generating further tokens. + """Not supported with latest reasoning models `o3` and `o4-mini`. - The returned text will not contain the stop sequence. + Up to 4 sequences where the API will stop generating further tokens. The + returned text will not contain the stop sequence. """ stream_options: Optional[ChatCompletionStreamOptionsParam] diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 8cd1e01144..254f7e204b 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -62,7 +62,7 @@ class Response(BaseModel): """ model: ResponsesModel - """Model ID used to generate the response, like `gpt-4o` or `o1`. + """Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the @@ -149,6 +149,27 @@ class Response(BaseModel): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ + service_tier: Optional[Literal["auto", "default", "flex"]] = None + """Specifies the latency tier to use for processing the request. + + This parameter is relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + """ + status: Optional[ResponseStatus] = None """The status of the response generation. diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index ed82e678e5..3c0a9d7b8a 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -38,7 +38,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): """ model: Required[ResponsesModel] - """Model ID used to generate the response, like `gpt-4o` or `o1`. + """Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the @@ -102,6 +102,27 @@ class ResponseCreateParamsBase(TypedDict, total=False): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ + service_tier: Optional[Literal["auto", "default", "flex"]] + """Specifies the latency tier to use for processing the request. + + This parameter is relevant for customers subscribed to the scale tier service: + + - If set to 'auto', and the Project is Scale tier enabled, the system will + utilize scale tier credits until they are exhausted. + - If set to 'auto', and the Project is not Scale tier enabled, the request will + be processed using the default service tier with a lower uptime SLA and no + latency guarentee. + - If set to 'default', the request will be processed using the default service + tier with a lower uptime SLA and no latency guarentee. + - If set to 'flex', the request will be processed with the Flex Processing + service tier. + [Learn more](https://platform.openai.com/docs/guides/flex-processing). + - When not set, the default behavior is 'auto'. + + When this parameter is set, the response body will include the `service_tier` + utilized. + """ + store: Optional[bool] """Whether to store the generated model response for later retrieval via API.""" diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index 30878b4347..4869cd325c 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -11,6 +11,10 @@ "gpt-4.1-2025-04-14", "gpt-4.1-mini-2025-04-14", "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", "o3-mini", "o3-mini-2025-01-31", "o1", diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 78a396d738..107aab2e4a 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -19,10 +19,17 @@ class Reasoning(BaseModel): result in faster responses and fewer tokens used on reasoning in a response. """ - generate_summary: Optional[Literal["concise", "detailed"]] = None - """**computer_use_preview only** + generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None + """**Deprecated:** use `summary` instead. A summary of the reasoning performed by the model. This can be useful for - debugging and understanding the model's reasoning process. One of `concise` or - `detailed`. + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] = None + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. """ diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index f606beb693..99e082fc11 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -13,6 +13,10 @@ "gpt-4.1-2025-04-14", "gpt-4.1-mini-2025-04-14", "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", "o3-mini", "o3-mini-2025-01-31", "o1", diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 2953b895c4..73e1a008df 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -20,10 +20,17 @@ class Reasoning(TypedDict, total=False): result in faster responses and fewer tokens used on reasoning in a response. """ - generate_summary: Optional[Literal["concise", "detailed"]] - """**computer_use_preview only** + generate_summary: Optional[Literal["auto", "concise", "detailed"]] + """**Deprecated:** use `summary` instead. A summary of the reasoning performed by the model. This can be useful for - debugging and understanding the model's reasoning process. One of `concise` or - `detailed`. + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. """ diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index e45a5becf3..3753af8fdb 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -38,8 +38,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: previous_response_id="previous_response_id", reasoning={ "effort": "low", - "generate_summary": "concise", + "generate_summary": "auto", + "summary": "auto", }, + service_tier="auto", store=True, stream=False, temperature=1, @@ -116,8 +118,10 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: previous_response_id="previous_response_id", reasoning={ "effort": "low", - "generate_summary": "concise", + "generate_summary": "auto", + "summary": "auto", }, + service_tier="auto", store=True, temperature=1, text={"format": {"type": "text"}}, @@ -280,8 +284,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn previous_response_id="previous_response_id", reasoning={ "effort": "low", - "generate_summary": "concise", + "generate_summary": "auto", + "summary": "auto", }, + service_tier="auto", store=True, stream=False, temperature=1, @@ -358,8 +364,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn previous_response_id="previous_response_id", reasoning={ "effort": "low", - "generate_summary": "concise", + "generate_summary": "auto", + "summary": "auto", }, + service_tier="auto", store=True, temperature=1, text={"format": {"type": "text"}}, From ed53107e10e6c86754866b48f8bd862659134ca8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 16:43:01 +0000 Subject: [PATCH 884/914] release: 1.75.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6603053537..cb464946f0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.74.1" + ".": "1.75.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b03bbedb52..fb077b91c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.75.0 (2025-04-16) + +Full Changelog: [v1.74.1...v1.75.0](https://github.com/openai/openai-python/compare/v1.74.1...v1.75.0) + +### Features + +* **api:** add o3 and o4-mini model IDs ([4bacbd5](https://github.com/openai/openai-python/commit/4bacbd5503137e266c127dc643ebae496cb4f158)) + ## 1.74.1 (2025-04-16) Full Changelog: [v1.74.0...v1.74.1](https://github.com/openai/openai-python/compare/v1.74.0...v1.74.1) diff --git a/pyproject.toml b/pyproject.toml index e2cd25f69c..b5648e9e51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.74.1" +version = "1.75.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5bbfee3232..8eab2d7416 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.74.1" # x-release-please-version +__version__ = "1.75.0" # x-release-please-version From 5d65ddfa34ed9c910f9a695e0a52f1458cc95b71 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 10:17:31 +0000 Subject: [PATCH 885/914] chore(internal): update models test --- tests/test_models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_models.py b/tests/test_models.py index 4b18940b49..440e17a08c 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -492,12 +492,15 @@ class Model(BaseModel): resource_id: Optional[str] = None m = Model.construct() + assert m.resource_id is None assert "resource_id" not in m.model_fields_set m = Model.construct(resource_id=None) + assert m.resource_id is None assert "resource_id" in m.model_fields_set m = Model.construct(resource_id="foo") + assert m.resource_id == "foo" assert "resource_id" in m.model_fields_set From 15902dc595a69ca37452d8bf9682ebf229d460d3 Mon Sep 17 00:00:00 2001 From: dogisgreat Date: Mon, 21 Apr 2025 14:23:33 -0400 Subject: [PATCH 886/914] chore: update completion parse signature --- src/openai/resources/beta/chat/completions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openai/resources/beta/chat/completions.py b/src/openai/resources/beta/chat/completions.py index 545a3f4087..80e015615f 100644 --- a/src/openai/resources/beta/chat/completions.py +++ b/src/openai/resources/beta/chat/completions.py @@ -81,7 +81,7 @@ def parse( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -228,7 +228,7 @@ def stream( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -360,7 +360,7 @@ async def parse( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -507,7 +507,7 @@ def stream( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, From 271d979a0893b7afa892d5109968c3fcfb6c13b4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 20:12:49 +0000 Subject: [PATCH 887/914] chore(ci): add timeout thresholds for CI jobs --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f9cf84bb4..d148b34a9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ on: jobs: lint: + timeout-minutes: 10 name: lint runs-on: ubuntu-latest steps: @@ -30,6 +31,7 @@ jobs: run: ./scripts/lint test: + timeout-minutes: 10 name: test runs-on: ubuntu-latest steps: @@ -50,6 +52,7 @@ jobs: run: ./scripts/test examples: + timeout-minutes: 10 name: examples runs-on: ubuntu-latest if: github.repository == 'openai/openai-python' From 1157528d952d02036058943f08fba99ef0d79d4f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 20:37:29 +0000 Subject: [PATCH 888/914] chore(internal): import reformatting --- src/openai/resources/audio/speech.py | 5 +---- src/openai/resources/audio/transcriptions.py | 8 +------- src/openai/resources/audio/translations.py | 7 +------ src/openai/resources/batches.py | 5 +---- src/openai/resources/beta/assistants.py | 5 +---- src/openai/resources/beta/realtime/sessions.py | 5 +---- .../resources/beta/realtime/transcription_sessions.py | 5 +---- src/openai/resources/beta/threads/messages.py | 5 +---- src/openai/resources/beta/threads/runs/steps.py | 5 +---- src/openai/resources/beta/threads/threads.py | 6 +----- src/openai/resources/chat/completions/completions.py | 6 +----- src/openai/resources/completions.py | 6 +----- src/openai/resources/evals/evals.py | 5 +---- src/openai/resources/evals/runs/runs.py | 5 +---- src/openai/resources/files.py | 7 +------ .../resources/fine_tuning/checkpoints/permissions.py | 5 +---- src/openai/resources/fine_tuning/jobs/jobs.py | 5 +---- src/openai/resources/images.py | 7 +------ src/openai/resources/moderations.py | 5 +---- src/openai/resources/responses/responses.py | 7 +------ src/openai/resources/uploads/parts.py | 7 +------ src/openai/resources/uploads/uploads.py | 5 +---- src/openai/resources/vector_stores/file_batches.py | 6 +----- src/openai/resources/vector_stores/files.py | 6 +----- src/openai/resources/vector_stores/vector_stores.py | 5 +---- 25 files changed, 25 insertions(+), 118 deletions(-) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 1ee53db9d5..fad18dcdf5 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -9,10 +9,7 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 7e62f70f60..0c7ebca7a6 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -11,13 +11,7 @@ from ... import _legacy_response from ...types import AudioResponseFormat from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import ( - extract_files, - required_args, - maybe_transform, - deepcopy_minimal, - async_maybe_transform, -) +from ..._utils import extract_files, required_args, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index f55dbd0ee5..28b577ce2e 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -10,12 +10,7 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import ( - extract_files, - maybe_transform, - deepcopy_minimal, - async_maybe_transform, -) +from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index b7a299be12..26ea498b31 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -10,10 +10,7 @@ from .. import _legacy_response from ..types import batch_list_params, batch_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 43f6a7f135..9059d93616 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -9,10 +9,7 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index 3e1c956fe4..3c0d4d47c1 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -9,10 +9,7 @@ from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/beta/realtime/transcription_sessions.py b/src/openai/resources/beta/realtime/transcription_sessions.py index 0917da71fa..dbcb1bb33b 100644 --- a/src/openai/resources/beta/realtime/transcription_sessions.py +++ b/src/openai/resources/beta/realtime/transcription_sessions.py @@ -9,10 +9,7 @@ from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index e3374aba37..3a8913ef16 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -9,10 +9,7 @@ from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 709c729d45..3d2148687b 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -9,10 +9,7 @@ from ..... import _legacy_response from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ....._utils import ( - maybe_transform, - async_maybe_transform, -) +from ....._utils import maybe_transform, async_maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index c697be416d..9c6954a9b3 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -18,11 +18,7 @@ AsyncMessagesWithStreamingResponse, ) from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from ...._utils import required_args, maybe_transform, async_maybe_transform from .runs.runs import ( Runs, AsyncRuns, diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index d6214225d8..0ab105a389 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -19,11 +19,7 @@ AsyncMessagesWithStreamingResponse, ) from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from ...._utils import required_args, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index aebf35d1f1..43b923b9b9 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -10,11 +10,7 @@ from .. import _legacy_response from ..types import completion_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from .._utils import required_args, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py index 24a0350cfb..30ac4bdf32 100644 --- a/src/openai/resources/evals/evals.py +++ b/src/openai/resources/evals/evals.py @@ -10,10 +10,7 @@ from ... import _legacy_response from ...types import eval_list_params, eval_create_params, eval_update_params from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from .runs.runs import ( Runs, diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py index 6df0b6d121..9c626d0903 100644 --- a/src/openai/resources/evals/runs/runs.py +++ b/src/openai/resources/evals/runs/runs.py @@ -9,10 +9,7 @@ from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 2eaa4a6401..179af870ba 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -12,12 +12,7 @@ from .. import _legacy_response from ..types import FilePurpose, file_list_params, file_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from .._utils import ( - extract_files, - maybe_transform, - deepcopy_minimal, - async_maybe_transform, -) +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py index beb7b099d3..b2bcb33020 100644 --- a/src/openai/resources/fine_tuning/checkpoints/permissions.py +++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py @@ -9,10 +9,7 @@ from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index bbeff60bc6..90619c8609 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -9,10 +9,7 @@ from .... import _legacy_response from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from .checkpoints import ( Checkpoints, diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 30473c14f7..e3398930e9 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -10,12 +10,7 @@ from .. import _legacy_response from ..types import image_edit_params, image_generate_params, image_create_variation_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from .._utils import ( - extract_files, - maybe_transform, - deepcopy_minimal, - async_maybe_transform, -) +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index a8f03142bc..f7a8b52c23 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -9,10 +9,7 @@ from .. import _legacy_response from ..types import moderation_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - maybe_transform, - async_maybe_transform, -) +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index f07b4d8c4a..4a0687f9f3 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -10,12 +10,7 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven -from ..._utils import ( - is_given, - required_args, - maybe_transform, - async_maybe_transform, -) +from ..._utils import is_given, required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py index 777469ac8e..a32f4eb1d2 100644 --- a/src/openai/resources/uploads/parts.py +++ b/src/openai/resources/uploads/parts.py @@ -8,12 +8,7 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import ( - extract_files, - maybe_transform, - deepcopy_minimal, - async_maybe_transform, -) +from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 9297dbc2c3..ecfcee4800 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -23,10 +23,7 @@ ) from ...types import FilePurpose, upload_create_params, upload_complete_params from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index 9b4b64d35e..4dd4430b71 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -13,11 +13,7 @@ from ... import _legacy_response from ...types import FileChunkingStrategyParam from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import ( - is_given, - maybe_transform, - async_maybe_transform, -) +from ..._utils import is_given, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py index 7d93798adf..f860384629 100644 --- a/src/openai/resources/vector_stores/files.py +++ b/src/openai/resources/vector_stores/files.py @@ -10,11 +10,7 @@ from ... import _legacy_response from ...types import FileChunkingStrategyParam from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from ..._utils import ( - is_given, - maybe_transform, - async_maybe_transform, -) +from ..._utils import is_given, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py index aaa6ed2757..9fc17b183b 100644 --- a/src/openai/resources/vector_stores/vector_stores.py +++ b/src/openai/resources/vector_stores/vector_stores.py @@ -24,10 +24,7 @@ vector_store_update_params, ) from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper From 1e7dea203321d1ca50d3607aaa33f29fbe534a91 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 22:01:33 +0000 Subject: [PATCH 889/914] chore(internal): fix list file params --- src/openai/_utils/_utils.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index d6734e6b8f..1e7d013b51 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -76,8 +76,16 @@ def _extract_items( 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 + + if is_list(obj): + files: list[tuple[str, FileTypes]] = [] + for entry in obj: + assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") + files.append((flattened_key + "[]", cast(FileTypes, entry))) + return files + + assert_is_file_content(obj, key=flattened_key) return [(flattened_key, cast(FileTypes, obj))] index += 1 From 7de6c5ce26a23ca450994c412d12e4244d2bb43c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 22:53:21 +0000 Subject: [PATCH 890/914] chore(internal): refactor retries to not use recursion --- src/openai/_base_client.py | 417 ++++++++++++++++--------------------- 1 file changed, 177 insertions(+), 240 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 8b43a20699..a0f9cce7d8 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -439,8 +439,7 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0 headers = httpx.Headers(headers_dict) idempotency_header = self._idempotency_header - if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: - options.idempotency_key = options.idempotency_key or self._idempotency_key() + if idempotency_header and options.idempotency_key and idempotency_header not in headers: headers[idempotency_header] = options.idempotency_key # Don't set these headers if they were already set or removed by the caller. We check @@ -905,7 +904,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: Literal[True], stream_cls: Type[_StreamT], @@ -916,7 +914,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: Literal[False] = False, ) -> ResponseT: ... @@ -926,7 +923,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: bool = False, stream_cls: Type[_StreamT] | None = None, @@ -936,126 +932,110 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: - if remaining_retries is not None: - retries_taken = options.get_max_retries(self.max_retries) - remaining_retries - else: - retries_taken = 0 - - return self._request( - cast_to=cast_to, - options=options, - stream=stream, - stream_cls=stream_cls, - retries_taken=retries_taken, - ) + cast_to = self._maybe_override_cast_to(cast_to, options) - def _request( - self, - *, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - retries_taken: int, - stream: bool, - stream_cls: type[_StreamT] | None, - ) -> ResponseT | _StreamT: # create a copy of the options we were given so that if the # options are mutated later & we then retry, the retries are # given the original options input_options = model_copy(options) - - cast_to = self._maybe_override_cast_to(cast_to, options) - options = self._prepare_options(options) - - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken - request = self._build_request(options, retries_taken=retries_taken) - self._prepare_request(request) - - if options.idempotency_key: + if input_options.idempotency_key is None and input_options.method.lower() != "get": # ensure the idempotency key is reused between requests - input_options.idempotency_key = options.idempotency_key + input_options.idempotency_key = self._idempotency_key() - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) - log.debug("Sending HTTP Request: %s %s", request.method, request.url) + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = self._prepare_options(options) - try: - response = self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + self._prepare_request(request) - if remaining_retries > 0: - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) + log.debug("Sending HTTP Request: %s %s", request.method, request.url) - if remaining_retries > 0: - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, + response = None + try: + response = self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + log.debug("request_id: %s", response.headers.get("x-request-id")) - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err - - log.debug( - 'HTTP Response: %s %s "%i %s" %s', - request.method, - request.url, - response.status_code, - response.reason_phrase, - response.headers, - ) - log.debug("request_id: %s", response.headers.get("x-request-id")) + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + err.response.close() + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if remaining_retries > 0 and self._should_retry(err.response): - err.response.close() - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - response_headers=err.response.headers, - stream=stream, - stream_cls=stream_cls, - ) + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - err.response.read() + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None - log.debug("Re-raising status error") - raise self._make_status_error_from_response(err.response) from None + break + assert response is not None, "could not resolve response (should never happen)" return self._process_response( cast_to=cast_to, options=options, @@ -1065,37 +1045,20 @@ def _request( retries_taken=retries_taken, ) - def _retry_request( - self, - options: FinalRequestOptions, - cast_to: Type[ResponseT], - *, - retries_taken: int, - response_headers: httpx.Headers | None, - stream: bool, - stream_cls: type[_StreamT] | None, - ) -> ResponseT | _StreamT: - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken if remaining_retries == 1: log.debug("1 retry left") else: log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) log.info("Retrying request to %s in %f seconds", options.url, timeout) - # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a - # different thread if necessary. time.sleep(timeout) - return self._request( - options=options, - cast_to=cast_to, - retries_taken=retries_taken + 1, - stream=stream, - stream_cls=stream_cls, - ) - def _process_response( self, *, @@ -1453,7 +1416,6 @@ async def request( options: FinalRequestOptions, *, stream: Literal[False] = False, - remaining_retries: Optional[int] = None, ) -> ResponseT: ... @overload @@ -1464,7 +1426,6 @@ async def request( *, stream: Literal[True], stream_cls: type[_AsyncStreamT], - remaining_retries: Optional[int] = None, ) -> _AsyncStreamT: ... @overload @@ -1475,7 +1436,6 @@ async def request( *, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, - remaining_retries: Optional[int] = None, ) -> ResponseT | _AsyncStreamT: ... async def request( @@ -1485,120 +1445,112 @@ async def request( *, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, - remaining_retries: Optional[int] = None, - ) -> ResponseT | _AsyncStreamT: - if remaining_retries is not None: - retries_taken = options.get_max_retries(self.max_retries) - remaining_retries - else: - retries_taken = 0 - - return await self._request( - cast_to=cast_to, - options=options, - stream=stream, - stream_cls=stream_cls, - retries_taken=retries_taken, - ) - - async def _request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: bool, - stream_cls: type[_AsyncStreamT] | None, - retries_taken: int, ) -> ResponseT | _AsyncStreamT: if self._platform is None: # `get_platform` can make blocking IO calls so we # execute it earlier while we are in an async context self._platform = await asyncify(get_platform)() + cast_to = self._maybe_override_cast_to(cast_to, options) + # create a copy of the options we were given so that if the # options are mutated later & we then retry, the retries are # given the original options input_options = model_copy(options) - - cast_to = self._maybe_override_cast_to(cast_to, options) - options = await self._prepare_options(options) - - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken - request = self._build_request(options, retries_taken=retries_taken) - await self._prepare_request(request) - - if options.idempotency_key: + if input_options.idempotency_key is None and input_options.method.lower() != "get": # ensure the idempotency key is reused between requests - input_options.idempotency_key = options.idempotency_key + input_options.idempotency_key = self._idempotency_key() - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) - try: - response = await self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = await self._prepare_options(options) - if remaining_retries > 0: - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + await self._prepare_request(request) - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth - if remaining_retries > 0: - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) + log.debug("Sending HTTP Request: %s %s", request.method, request.url) - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err + response = None + try: + response = await self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + log.debug("request_id: %s", response.headers.get("x-request-id")) - log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase - ) + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + await err.response.aclose() + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if remaining_retries > 0 and self._should_retry(err.response): - await err.response.aclose() - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - response_headers=err.response.headers, - stream=stream, - stream_cls=stream_cls, - ) + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - await err.response.aread() + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None - log.debug("Re-raising status error") - raise self._make_status_error_from_response(err.response) from None + break + assert response is not None, "could not resolve response (should never happen)" return await self._process_response( cast_to=cast_to, options=options, @@ -1608,35 +1560,20 @@ async def _request( retries_taken=retries_taken, ) - async def _retry_request( - self, - options: FinalRequestOptions, - cast_to: Type[ResponseT], - *, - retries_taken: int, - response_headers: httpx.Headers | None, - stream: bool, - stream_cls: type[_AsyncStreamT] | None, - ) -> ResponseT | _AsyncStreamT: - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + async def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken if remaining_retries == 1: log.debug("1 retry left") else: log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) log.info("Retrying request to %s in %f seconds", options.url, timeout) await anyio.sleep(timeout) - return await self._request( - options=options, - cast_to=cast_to, - retries_taken=retries_taken + 1, - stream=stream, - stream_cls=stream_cls, - ) - async def _process_response( self, *, From 6a2dfbb86c24edea66ecc57fc25e28c4d7d73616 Mon Sep 17 00:00:00 2001 From: Konnor-Young <97478325+Konnor-Young@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:24:20 -0600 Subject: [PATCH 891/914] fix(pydantic v1): more robust `ModelField.annotation` check (#2163) --------- Co-authored-by: Konnor Young Co-authored-by: Robert Craigie --- src/openai/_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 9b1aeb30bf..6b6f8e9294 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -651,8 +651,8 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, # Note: if one variant defines an alias then they all should discriminator_alias = field_info.alias - if field_info.annotation and is_literal_type(field_info.annotation): - for entry in get_args(field_info.annotation): + if (annotation := getattr(field_info, 'annotation', None)) and is_literal_type(annotation): + for entry in get_args(annotation): if isinstance(entry, str): mapping[entry] = variant From 8830a6c9234ebcc058f7661978b961c2bbdd2c93 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 12:00:43 +0000 Subject: [PATCH 892/914] fix(pydantic v1): more robust ModelField.annotation check --- src/openai/_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 6b6f8e9294..e2fce49250 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -651,7 +651,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, # Note: if one variant defines an alias then they all should discriminator_alias = field_info.alias - if (annotation := getattr(field_info, 'annotation', None)) and is_literal_type(annotation): + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): for entry in get_args(annotation): if isinstance(entry, str): mapping[entry] = variant From 1c1d144e428c60643f0fce52e3ffd9b681c9083c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:20:27 +0000 Subject: [PATCH 893/914] chore(internal): minor formatting changes --- src/openai/types/audio/transcription_word.py | 1 - src/openai/types/audio/translation.py | 1 - src/openai/types/batch_request_counts.py | 1 - src/openai/types/beta/assistant_tool_choice_function.py | 1 - src/openai/types/chat/chat_completion_audio.py | 1 - src/openai/types/chat/chat_completion_reasoning_effort.py | 1 - src/openai/types/chat/chat_completion_store_message.py | 1 - src/openai/types/chat_model.py | 1 - src/openai/types/eval_delete_response.py | 1 - src/openai/types/evals/eval_api_error.py | 1 - src/openai/types/fine_tuning/fine_tuning_job_integration.py | 1 - src/openai/types/model_deleted.py | 1 - src/openai/types/responses/response_function_tool_call_item.py | 1 - src/openai/types/responses/response_usage.py | 1 - src/openai/types/static_file_chunking_strategy.py | 1 - 15 files changed, 15 deletions(-) diff --git a/src/openai/types/audio/transcription_word.py b/src/openai/types/audio/transcription_word.py index 969da32509..2ce682f957 100644 --- a/src/openai/types/audio/transcription_word.py +++ b/src/openai/types/audio/transcription_word.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["TranscriptionWord"] diff --git a/src/openai/types/audio/translation.py b/src/openai/types/audio/translation.py index 7c0e905189..efc56f7f9b 100644 --- a/src/openai/types/audio/translation.py +++ b/src/openai/types/audio/translation.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["Translation"] diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py index 7e1d49fb88..068b071af1 100644 --- a/src/openai/types/batch_request_counts.py +++ b/src/openai/types/batch_request_counts.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["BatchRequestCounts"] diff --git a/src/openai/types/beta/assistant_tool_choice_function.py b/src/openai/types/beta/assistant_tool_choice_function.py index 0c896d8087..87f38310ca 100644 --- a/src/openai/types/beta/assistant_tool_choice_function.py +++ b/src/openai/types/beta/assistant_tool_choice_function.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["AssistantToolChoiceFunction"] diff --git a/src/openai/types/chat/chat_completion_audio.py b/src/openai/types/chat/chat_completion_audio.py index dd15508ebb..232d60563d 100644 --- a/src/openai/types/chat/chat_completion_audio.py +++ b/src/openai/types/chat/chat_completion_audio.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["ChatCompletionAudio"] diff --git a/src/openai/types/chat/chat_completion_reasoning_effort.py b/src/openai/types/chat/chat_completion_reasoning_effort.py index e4785c90bf..42a980c5b8 100644 --- a/src/openai/types/chat/chat_completion_reasoning_effort.py +++ b/src/openai/types/chat/chat_completion_reasoning_effort.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..shared.reasoning_effort import ReasoningEffort __all__ = ["ChatCompletionReasoningEffort"] diff --git a/src/openai/types/chat/chat_completion_store_message.py b/src/openai/types/chat/chat_completion_store_message.py index 95adc08af8..8dc093f7b8 100644 --- a/src/openai/types/chat/chat_completion_store_message.py +++ b/src/openai/types/chat/chat_completion_store_message.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .chat_completion_message import ChatCompletionMessage __all__ = ["ChatCompletionStoreMessage"] diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py index 9304d195d6..f3b0e310cc 100644 --- a/src/openai/types/chat_model.py +++ b/src/openai/types/chat_model.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .shared import chat_model __all__ = ["ChatModel"] diff --git a/src/openai/types/eval_delete_response.py b/src/openai/types/eval_delete_response.py index adb460ddbb..a27261e242 100644 --- a/src/openai/types/eval_delete_response.py +++ b/src/openai/types/eval_delete_response.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["EvalDeleteResponse"] diff --git a/src/openai/types/evals/eval_api_error.py b/src/openai/types/evals/eval_api_error.py index d67185e981..fe76871024 100644 --- a/src/openai/types/evals/eval_api_error.py +++ b/src/openai/types/evals/eval_api_error.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["EvalAPIError"] diff --git a/src/openai/types/fine_tuning/fine_tuning_job_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_integration.py index 9a66aa4f17..2af73fbffb 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_integration.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_integration.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject FineTuningJobIntegration = FineTuningJobWandbIntegrationObject diff --git a/src/openai/types/model_deleted.py b/src/openai/types/model_deleted.py index 7f81e1b380..e7601f74e4 100644 --- a/src/openai/types/model_deleted.py +++ b/src/openai/types/model_deleted.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["ModelDeleted"] diff --git a/src/openai/types/responses/response_function_tool_call_item.py b/src/openai/types/responses/response_function_tool_call_item.py index 25984f9451..762015a4b1 100644 --- a/src/openai/types/responses/response_function_tool_call_item.py +++ b/src/openai/types/responses/response_function_tool_call_item.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .response_function_tool_call import ResponseFunctionToolCall __all__ = ["ResponseFunctionToolCallItem"] diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py index 9ad36bd326..52b93ac578 100644 --- a/src/openai/types/responses/response_usage.py +++ b/src/openai/types/responses/response_usage.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["ResponseUsage", "InputTokensDetails", "OutputTokensDetails"] diff --git a/src/openai/types/static_file_chunking_strategy.py b/src/openai/types/static_file_chunking_strategy.py index 2813bc6630..cb842442c1 100644 --- a/src/openai/types/static_file_chunking_strategy.py +++ b/src/openai/types/static_file_chunking_strategy.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from .._models import BaseModel __all__ = ["StaticFileChunkingStrategy"] From 6321004aef37251116e7b79bcde71e404fce0300 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:29:55 +0000 Subject: [PATCH 894/914] feat(api): adding new image model support --- .stats.yml | 6 +- api.md | 6 +- .../resources/beta/realtime/realtime.py | 34 +++ src/openai/resources/beta/threads/threads.py | 17 +- src/openai/resources/evals/evals.py | 8 - src/openai/resources/evals/runs/runs.py | 8 +- .../fine_tuning/checkpoints/permissions.py | 14 +- src/openai/resources/images.py | 248 +++++++++++++----- .../beta/realtime/realtime_client_event.py | 17 +- .../realtime/realtime_client_event_param.py | 14 +- .../beta/realtime/realtime_server_event.py | 44 +++- .../beta/thread_create_and_run_params.py | 9 +- src/openai/types/eval_create_params.py | 166 ++++++++---- src/openai/types/eval_create_response.py | 96 ++++++- src/openai/types/eval_label_model_grader.py | 57 ++-- src/openai/types/eval_list_response.py | 96 ++++++- src/openai/types/eval_retrieve_response.py | 96 ++++++- .../types/eval_text_similarity_grader.py | 16 +- .../eval_text_similarity_grader_param.py | 16 +- src/openai/types/eval_update_response.py | 96 ++++++- ...create_eval_completions_run_data_source.py | 165 ++++++------ ..._eval_completions_run_data_source_param.py | 169 ++++++------ src/openai/types/evals/run_cancel_response.py | 218 ++++++++++++++- src/openai/types/evals/run_create_params.py | 222 +++++++++++++++- src/openai/types/evals/run_create_response.py | 218 ++++++++++++++- src/openai/types/evals/run_list_params.py | 2 +- src/openai/types/evals/run_list_response.py | 218 ++++++++++++++- .../types/evals/run_retrieve_response.py | 218 ++++++++++++++- src/openai/types/image.py | 18 +- .../types/image_create_variation_params.py | 5 +- src/openai/types/image_edit_params.py | 37 ++- src/openai/types/image_generate_params.py | 74 ++++-- src/openai/types/image_model.py | 2 +- src/openai/types/images_response.py | 33 ++- src/openai/types/responses/__init__.py | 13 + .../types/responses/easy_input_message.py | 26 ++ ...onse_reasoning_summary_part_added_event.py | 32 +++ ...ponse_reasoning_summary_part_done_event.py | 32 +++ ...onse_reasoning_summary_text_delta_event.py | 24 ++ ...ponse_reasoning_summary_text_done_event.py | 24 ++ .../types/responses/response_stream_event.py | 8 + .../checkpoints/test_permissions.py | 44 ++-- tests/api_resources/test_evals.py | 2 - tests/api_resources/test_images.py | 14 +- 44 files changed, 2367 insertions(+), 515 deletions(-) create mode 100644 src/openai/types/responses/easy_input_message.py create mode 100644 src/openai/types/responses/response_reasoning_summary_part_added_event.py create mode 100644 src/openai/types/responses/response_reasoning_summary_part_done_event.py create mode 100644 src/openai/types/responses/response_reasoning_summary_text_delta_event.py create mode 100644 src/openai/types/responses/response_reasoning_summary_text_done_event.py diff --git a/.stats.yml b/.stats.yml index 848c5b5adb..d92408173b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5633633cc38734869cf7d993f7b549bb8e4d10e0ec45381ec2cd91507cd8eb8f.yml -openapi_spec_hash: c855121b2b2324b99499c9244c21d24d -config_hash: d20837393b73efdb19cd08e04c1cc9a1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-8b68ae6b807dca92e914da1dd9e835a20f69b075e79102a264367fd7fddddb33.yml +openapi_spec_hash: b6ade5b1a6327339e6669e1134de2d03 +config_hash: b597cd9a31e9e5ec709e2eefb4c54122 diff --git a/api.md b/api.md index e06f55c2cc..d04c76960e 100644 --- a/api.md +++ b/api.md @@ -277,7 +277,7 @@ Methods: - client.fine_tuning.checkpoints.permissions.create(fine_tuned_model_checkpoint, \*\*params) -> SyncPage[PermissionCreateResponse] - client.fine_tuning.checkpoints.permissions.retrieve(fine_tuned_model_checkpoint, \*\*params) -> PermissionRetrieveResponse -- client.fine_tuning.checkpoints.permissions.delete(fine_tuned_model_checkpoint) -> PermissionDeleteResponse +- client.fine_tuning.checkpoints.permissions.delete(permission_id, \*, fine_tuned_model_checkpoint) -> PermissionDeleteResponse # VectorStores @@ -689,6 +689,10 @@ from openai.types.responses import ( ResponseOutputRefusal, ResponseOutputText, ResponseReasoningItem, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, ResponseRefusalDeltaEvent, ResponseRefusalDoneEvent, ResponseStatus, diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index 5cafce1322..d39db48e05 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -233,6 +233,7 @@ class AsyncRealtimeConnection: response: AsyncRealtimeResponseResource input_audio_buffer: AsyncRealtimeInputAudioBufferResource conversation: AsyncRealtimeConversationResource + output_audio_buffer: AsyncRealtimeOutputAudioBufferResource transcription_session: AsyncRealtimeTranscriptionSessionResource _connection: AsyncWebsocketConnection @@ -244,6 +245,7 @@ def __init__(self, connection: AsyncWebsocketConnection) -> None: self.response = AsyncRealtimeResponseResource(self) self.input_audio_buffer = AsyncRealtimeInputAudioBufferResource(self) self.conversation = AsyncRealtimeConversationResource(self) + self.output_audio_buffer = AsyncRealtimeOutputAudioBufferResource(self) self.transcription_session = AsyncRealtimeTranscriptionSessionResource(self) async def __aiter__(self) -> AsyncIterator[RealtimeServerEvent]: @@ -413,6 +415,7 @@ class RealtimeConnection: response: RealtimeResponseResource input_audio_buffer: RealtimeInputAudioBufferResource conversation: RealtimeConversationResource + output_audio_buffer: RealtimeOutputAudioBufferResource transcription_session: RealtimeTranscriptionSessionResource _connection: WebsocketConnection @@ -424,6 +427,7 @@ def __init__(self, connection: WebsocketConnection) -> None: self.response = RealtimeResponseResource(self) self.input_audio_buffer = RealtimeInputAudioBufferResource(self) self.conversation = RealtimeConversationResource(self) + self.output_audio_buffer = RealtimeOutputAudioBufferResource(self) self.transcription_session = RealtimeTranscriptionSessionResource(self) def __iter__(self) -> Iterator[RealtimeServerEvent]: @@ -808,6 +812,21 @@ def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> Non ) +class RealtimeOutputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """**WebRTC Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://platform.openai.com/docs/guides/realtime-model-capabilities#client-and-server-events-for-audio-in-webrtc). + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) + ) + + class RealtimeTranscriptionSessionResource(BaseRealtimeConnectionResource): def update( self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN @@ -1045,6 +1064,21 @@ async def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) ) +class AsyncRealtimeOutputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """**WebRTC Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://platform.openai.com/docs/guides/realtime-model-capabilities#client-and-server-events-for-audio-in-webrtc). + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) + ) + + class AsyncRealtimeTranscriptionSessionResource(BaseAsyncRealtimeConnectionResource): async def update( self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 9c6954a9b3..22dc5fe0ea 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -50,6 +50,7 @@ from ....types.shared.chat_model import ChatModel from ....types.beta.thread_deleted import ThreadDeleted from ....types.shared_params.metadata import Metadata +from ....types.beta.assistant_tool_param import AssistantToolParam from ....types.beta.assistant_stream_event import AssistantStreamEvent from ....types.beta.assistant_tool_choice_option_param import AssistantToolChoiceOptionParam from ....types.beta.assistant_response_format_option_param import AssistantResponseFormatOptionParam @@ -282,7 +283,7 @@ def create_and_run( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -415,7 +416,7 @@ def create_and_run( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -548,7 +549,7 @@ def create_and_run( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -681,7 +682,7 @@ def create_and_run( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1131,7 +1132,7 @@ async def create_and_run( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1264,7 +1265,7 @@ async def create_and_run( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1397,7 +1398,7 @@ async def create_and_run( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1530,7 +1531,7 @@ async def create_and_run( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py index 30ac4bdf32..c12562a86d 100644 --- a/src/openai/resources/evals/evals.py +++ b/src/openai/resources/evals/evals.py @@ -65,7 +65,6 @@ def create( testing_criteria: Iterable[eval_create_params.TestingCriterion], metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, - share_with_openai: bool | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -94,8 +93,6 @@ def create( name: The name of the evaluation. - share_with_openai: Indicates whether the evaluation is shared with OpenAI. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -112,7 +109,6 @@ def create( "testing_criteria": testing_criteria, "metadata": metadata, "name": name, - "share_with_openai": share_with_openai, }, eval_create_params.EvalCreateParams, ), @@ -328,7 +324,6 @@ async def create( testing_criteria: Iterable[eval_create_params.TestingCriterion], metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, - share_with_openai: bool | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -357,8 +352,6 @@ async def create( name: The name of the evaluation. - share_with_openai: Indicates whether the evaluation is shared with OpenAI. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -375,7 +368,6 @@ async def create( "testing_criteria": testing_criteria, "metadata": metadata, "name": name, - "share_with_openai": share_with_openai, }, eval_create_params.EvalCreateParams, ), diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py index 9c626d0903..d74c91e3c4 100644 --- a/src/openai/resources/evals/runs/runs.py +++ b/src/openai/resources/evals/runs/runs.py @@ -176,8 +176,8 @@ def list( order: Sort order for runs by timestamp. Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. - status: Filter runs by status. Use "queued" | "in_progress" | "failed" | "completed" | - "canceled". + status: Filter runs by status. One of `queued` | `in_progress` | `failed` | `completed` + | `canceled`. extra_headers: Send extra headers @@ -425,8 +425,8 @@ def list( order: Sort order for runs by timestamp. Use `asc` for ascending order or `desc` for descending order. Defaults to `asc`. - status: Filter runs by status. Use "queued" | "in_progress" | "failed" | "completed" | - "canceled". + status: Filter runs by status. One of `queued` | `in_progress` | `failed` | `completed` + | `canceled`. extra_headers: Send extra headers diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py index b2bcb33020..547e42ecac 100644 --- a/src/openai/resources/fine_tuning/checkpoints/permissions.py +++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py @@ -151,8 +151,9 @@ def retrieve( def delete( self, - fine_tuned_model_checkpoint: str, + permission_id: str, *, + fine_tuned_model_checkpoint: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -179,8 +180,10 @@ def delete( raise ValueError( f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" ) + if not permission_id: + raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}") return self._delete( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -316,8 +319,9 @@ async def retrieve( async def delete( self, - fine_tuned_model_checkpoint: str, + permission_id: str, *, + fine_tuned_model_checkpoint: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -344,8 +348,10 @@ async def delete( raise ValueError( f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" ) + if not permission_id: + raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}") return await self._delete( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index e3398930e9..e59d0ce35c 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Mapping, Optional, cast +from typing import List, Union, Mapping, Optional, cast from typing_extensions import Literal import httpx @@ -57,8 +57,9 @@ def create_variation( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: - """ - Creates a variation of a given image. + """Creates a variation of a given image. + + This endpoint only supports `dall-e-2`. Args: image: The image to use as the basis for the variation(s). Must be a valid PNG file, @@ -67,8 +68,7 @@ def create_variation( model: The model to use for image generation. Only `dall-e-2` is supported at this time. - n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only - `n=1` is supported. + n: The number of images to generate. Must be between 1 and 10. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been @@ -117,11 +117,12 @@ def create_variation( def edit( self, *, - image: FileTypes, + image: Union[FileTypes, List[FileTypes]], prompt: str, mask: FileTypes | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -132,31 +133,43 @@ def edit( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: - """ - Creates an edited or extended image given an original image and a prompt. + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint only supports `gpt-image-1` and `dall-e-2`. Args: - image: The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask - is not provided, image must have transparency, which will be used as the mask. + image: The image(s) to edit. Must be a supported image file or an array of images. For + `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 25MB. For `dall-e-2`, you can only provide one image, and it should be a square + `png` file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 - characters. + characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) - indicate where `image` should be edited. Must be a valid PNG file, less than + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` is supported at this - time. + model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are + supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` + is used. n: The number of images to generate. Must be between 1 and 10. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are + only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. + Defaults to `auto`. + response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. + generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` + will always return base64-encoded images. - size: The size of the generated images. Must be one of `256x256`, `512x512`, or - `1024x1024`. + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -177,12 +190,13 @@ def edit( "mask": mask, "model": model, "n": n, + "quality": quality, "response_format": response_format, "size": size, "user": user, } ) - files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["mask"]]) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["image", ""], ["mask"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- @@ -201,11 +215,18 @@ def generate( self, *, prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, - quality: Literal["standard", "hd"] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] | NotGiven = NOT_GIVEN, + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] + | NotGiven = NOT_GIVEN, style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -217,32 +238,60 @@ def generate( ) -> ImagesResponse: """ Creates an image given a prompt. + [Learn more](https://platform.openai.com/docs/guides/images). Args: - prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2` and 4000 characters for `dall-e-3`. + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters + for `dall-e-3`. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. - model: The model to use for image generation. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or + `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to + `gpt-image-1` is used. + + moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must + be either `low` for less restrictive filtering or `auto` (default value). n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. - quality: The quality of the image that will be generated. `hd` creates images with finer - details and greater consistency across the image. This param is only supported - for `dall-e-3`. + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. - response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. - size: The size of the generated images. Must be one of `256x256`, `512x512`, or - `1024x1024` for `dall-e-2`. Must be one of `1024x1024`, `1792x1024`, or - `1024x1792` for `dall-e-3` models. + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for + `gpt-image-1` which will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and + one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. - style: The style of the generated images. Must be one of `vivid` or `natural`. Vivid - causes the model to lean towards generating hyper-real and dramatic images. - Natural causes the model to produce more natural, less hyper-real looking - images. This param is only supported for `dall-e-3`. + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -261,8 +310,12 @@ def generate( body=maybe_transform( { "prompt": prompt, + "background": background, "model": model, + "moderation": moderation, "n": n, + "output_compression": output_compression, + "output_format": output_format, "quality": quality, "response_format": response_format, "size": size, @@ -314,8 +367,9 @@ async def create_variation( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: - """ - Creates a variation of a given image. + """Creates a variation of a given image. + + This endpoint only supports `dall-e-2`. Args: image: The image to use as the basis for the variation(s). Must be a valid PNG file, @@ -324,8 +378,7 @@ async def create_variation( model: The model to use for image generation. Only `dall-e-2` is supported at this time. - n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only - `n=1` is supported. + n: The number of images to generate. Must be between 1 and 10. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been @@ -374,11 +427,12 @@ async def create_variation( async def edit( self, *, - image: FileTypes, + image: Union[FileTypes, List[FileTypes]], prompt: str, mask: FileTypes | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -389,31 +443,43 @@ async def edit( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ImagesResponse: - """ - Creates an edited or extended image given an original image and a prompt. + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint only supports `gpt-image-1` and `dall-e-2`. Args: - image: The image to edit. Must be a valid PNG file, less than 4MB, and square. If mask - is not provided, image must have transparency, which will be used as the mask. + image: The image(s) to edit. Must be a supported image file or an array of images. For + `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 25MB. For `dall-e-2`, you can only provide one image, and it should be a square + `png` file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 - characters. + characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) - indicate where `image` should be edited. Must be a valid PNG file, less than + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` is supported at this - time. + model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are + supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` + is used. n: The number of images to generate. Must be between 1 and 10. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are + only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. + Defaults to `auto`. + response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. + generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` + will always return base64-encoded images. - size: The size of the generated images. Must be one of `256x256`, `512x512`, or - `1024x1024`. + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -434,12 +500,13 @@ async def edit( "mask": mask, "model": model, "n": n, + "quality": quality, "response_format": response_format, "size": size, "user": user, } ) - files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["mask"]]) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["image", ""], ["mask"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- @@ -458,11 +525,18 @@ async def generate( self, *, prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, - quality: Literal["standard", "hd"] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] | NotGiven = NOT_GIVEN, + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] + | NotGiven = NOT_GIVEN, style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -474,32 +548,60 @@ async def generate( ) -> ImagesResponse: """ Creates an image given a prompt. + [Learn more](https://platform.openai.com/docs/guides/images). Args: - prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2` and 4000 characters for `dall-e-3`. + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters + for `dall-e-3`. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. - model: The model to use for image generation. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or + `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to + `gpt-image-1` is used. + + moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must + be either `low` for less restrictive filtering or `auto` (default value). n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. - quality: The quality of the image that will be generated. `hd` creates images with finer - details and greater consistency across the image. This param is only supported - for `dall-e-3`. + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. - response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. - size: The size of the generated images. Must be one of `256x256`, `512x512`, or - `1024x1024` for `dall-e-2`. Must be one of `1024x1024`, `1792x1024`, or - `1024x1792` for `dall-e-3` models. + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for + `gpt-image-1` which will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and + one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. - style: The style of the generated images. Must be one of `vivid` or `natural`. Vivid - causes the model to lean towards generating hyper-real and dramatic images. - Natural causes the model to produce more natural, less hyper-real looking - images. This param is only supported for `dall-e-3`. + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -518,8 +620,12 @@ async def generate( body=await async_maybe_transform( { "prompt": prompt, + "background": background, "model": model, + "moderation": moderation, "n": n, + "output_compression": output_compression, + "output_format": output_format, "quality": quality, "response_format": response_format, "size": size, diff --git a/src/openai/types/beta/realtime/realtime_client_event.py b/src/openai/types/beta/realtime/realtime_client_event.py index f962a505cd..5f4858d688 100644 --- a/src/openai/types/beta/realtime/realtime_client_event.py +++ b/src/openai/types/beta/realtime/realtime_client_event.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union -from typing_extensions import Annotated, TypeAlias +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo +from ...._models import BaseModel from .session_update_event import SessionUpdateEvent from .response_cancel_event import ResponseCancelEvent from .response_create_event import ResponseCreateEvent @@ -16,7 +17,16 @@ from .conversation_item_retrieve_event import ConversationItemRetrieveEvent from .conversation_item_truncate_event import ConversationItemTruncateEvent -__all__ = ["RealtimeClientEvent"] +__all__ = ["RealtimeClientEvent", "OutputAudioBufferClear"] + + +class OutputAudioBufferClear(BaseModel): + type: Literal["output_audio_buffer.clear"] + """The event type, must be `output_audio_buffer.clear`.""" + + event_id: Optional[str] = None + """The unique ID of the client event used for error handling.""" + RealtimeClientEvent: TypeAlias = Annotated[ Union[ @@ -26,6 +36,7 @@ ConversationItemTruncateEvent, InputAudioBufferAppendEvent, InputAudioBufferClearEvent, + OutputAudioBufferClear, InputAudioBufferCommitEvent, ResponseCancelEvent, ResponseCreateEvent, diff --git a/src/openai/types/beta/realtime/realtime_client_event_param.py b/src/openai/types/beta/realtime/realtime_client_event_param.py index 6fdba4b87c..e7dfba241e 100644 --- a/src/openai/types/beta/realtime/realtime_client_event_param.py +++ b/src/openai/types/beta/realtime/realtime_client_event_param.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Union -from typing_extensions import TypeAlias +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .session_update_event_param import SessionUpdateEventParam from .response_cancel_event_param import ResponseCancelEventParam @@ -17,7 +17,16 @@ from .conversation_item_retrieve_event_param import ConversationItemRetrieveEventParam from .conversation_item_truncate_event_param import ConversationItemTruncateEventParam -__all__ = ["RealtimeClientEventParam"] +__all__ = ["RealtimeClientEventParam", "OutputAudioBufferClear"] + + +class OutputAudioBufferClear(TypedDict, total=False): + type: Required[Literal["output_audio_buffer.clear"]] + """The event type, must be `output_audio_buffer.clear`.""" + + event_id: str + """The unique ID of the client event used for error handling.""" + RealtimeClientEventParam: TypeAlias = Union[ ConversationItemCreateEventParam, @@ -26,6 +35,7 @@ ConversationItemTruncateEventParam, InputAudioBufferAppendEventParam, InputAudioBufferClearEventParam, + OutputAudioBufferClear, InputAudioBufferCommitEventParam, ResponseCancelEventParam, ResponseCreateEventParam, diff --git a/src/openai/types/beta/realtime/realtime_server_event.py b/src/openai/types/beta/realtime/realtime_server_event.py index ba1d324445..c12f5df977 100644 --- a/src/openai/types/beta/realtime/realtime_server_event.py +++ b/src/openai/types/beta/realtime/realtime_server_event.py @@ -39,7 +39,13 @@ ConversationItemInputAudioTranscriptionCompletedEvent, ) -__all__ = ["RealtimeServerEvent", "ConversationItemRetrieved"] +__all__ = [ + "RealtimeServerEvent", + "ConversationItemRetrieved", + "OutputAudioBufferStarted", + "OutputAudioBufferStopped", + "OutputAudioBufferCleared", +] class ConversationItemRetrieved(BaseModel): @@ -53,6 +59,39 @@ class ConversationItemRetrieved(BaseModel): """The event type, must be `conversation.item.retrieved`.""" +class OutputAudioBufferStarted(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.started"] + """The event type, must be `output_audio_buffer.started`.""" + + +class OutputAudioBufferStopped(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.stopped"] + """The event type, must be `output_audio_buffer.stopped`.""" + + +class OutputAudioBufferCleared(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.cleared"] + """The event type, must be `output_audio_buffer.cleared`.""" + + RealtimeServerEvent: TypeAlias = Annotated[ Union[ ConversationCreatedEvent, @@ -86,6 +125,9 @@ class ConversationItemRetrieved(BaseModel): SessionCreatedEvent, SessionUpdatedEvent, TranscriptionSessionUpdatedEvent, + OutputAudioBufferStarted, + OutputAudioBufferStopped, + OutputAudioBufferCleared, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 065c390f4e..d813710579 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -6,8 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..shared.chat_model import ChatModel -from .function_tool_param import FunctionToolParam -from .file_search_tool_param import FileSearchToolParam +from .assistant_tool_param import AssistantToolParam from ..shared_params.metadata import Metadata from .code_interpreter_tool_param import CodeInterpreterToolParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam @@ -32,7 +31,6 @@ "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch", - "Tool", "TruncationStrategy", "ThreadCreateAndRunParamsNonStreaming", "ThreadCreateAndRunParamsStreaming", @@ -153,7 +151,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): tool requires a list of vector store IDs. """ - tools: Optional[Iterable[Tool]] + tools: Optional[Iterable[AssistantToolParam]] """Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. @@ -360,9 +358,6 @@ class ToolResources(TypedDict, total=False): file_search: ToolResourcesFileSearch -Tool: TypeAlias = Union[CodeInterpreterToolParam, FileSearchToolParam, FunctionToolParam] - - class TruncationStrategy(TypedDict, total=False): type: Required[Literal["auto", "last_messages"]] """The truncation strategy to use for the thread. diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index 8b28e51a6b..03f44f2c8c 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -8,20 +8,25 @@ from .shared_params.metadata import Metadata from .eval_string_check_grader_param import EvalStringCheckGraderParam from .eval_text_similarity_grader_param import EvalTextSimilarityGraderParam +from .responses.response_input_text_param import ResponseInputTextParam __all__ = [ "EvalCreateParams", "DataSourceConfig", "DataSourceConfigCustom", - "DataSourceConfigStoredCompletions", + "DataSourceConfigLogs", "TestingCriterion", "TestingCriterionLabelModel", "TestingCriterionLabelModelInput", "TestingCriterionLabelModelInputSimpleInputMessage", - "TestingCriterionLabelModelInputInputMessage", - "TestingCriterionLabelModelInputInputMessageContent", - "TestingCriterionLabelModelInputOutputMessage", - "TestingCriterionLabelModelInputOutputMessageContent", + "TestingCriterionLabelModelInputEvalItem", + "TestingCriterionLabelModelInputEvalItemContent", + "TestingCriterionLabelModelInputEvalItemContentOutputText", + "TestingCriterionPython", + "TestingCriterionScoreModel", + "TestingCriterionScoreModelInput", + "TestingCriterionScoreModelInputContent", + "TestingCriterionScoreModelInputContentOutputText", ] @@ -45,37 +50,30 @@ class EvalCreateParams(TypedDict, total=False): name: str """The name of the evaluation.""" - share_with_openai: bool - """Indicates whether the evaluation is shared with OpenAI.""" - class DataSourceConfigCustom(TypedDict, total=False): item_schema: Required[Dict[str, object]] - """The json schema for the run data source items.""" + """The json schema for each row in the data source.""" type: Required[Literal["custom"]] """The type of data source. Always `custom`.""" include_sample_schema: bool - """Whether to include the sample schema in the data source.""" - - -class DataSourceConfigStoredCompletions(TypedDict, total=False): - type: Required[Literal["stored_completions"]] - """The type of data source. Always `stored_completions`.""" + """ + Whether the eval should expect you to populate the sample namespace (ie, by + generating responses off of your data source) + """ - metadata: Optional[Metadata] - """Set of 16 key-value pairs that can be attached to an object. - This can be useful for storing additional information about the object in a - structured format, and querying for objects via API or the dashboard. +class DataSourceConfigLogs(TypedDict, total=False): + type: Required[Literal["logs"]] + """The type of data source. Always `logs`.""" - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - """ + metadata: Dict[str, object] + """Metadata filters for the logs data source.""" -DataSourceConfig: TypeAlias = Union[DataSourceConfigCustom, DataSourceConfigStoredCompletions] +DataSourceConfig: TypeAlias = Union[DataSourceConfigCustom, DataSourceConfigLogs] class TestingCriterionLabelModelInputSimpleInputMessage(TypedDict, total=False): @@ -86,51 +84,44 @@ class TestingCriterionLabelModelInputSimpleInputMessage(TypedDict, total=False): """The role of the message (e.g. "system", "assistant", "user").""" -class TestingCriterionLabelModelInputInputMessageContent(TypedDict, total=False): +class TestingCriterionLabelModelInputEvalItemContentOutputText(TypedDict, total=False): text: Required[str] - """The text content.""" - - type: Required[Literal["input_text"]] - """The type of content, which is always `input_text`.""" - + """The text output from the model.""" -class TestingCriterionLabelModelInputInputMessage(TypedDict, total=False): - content: Required[TestingCriterionLabelModelInputInputMessageContent] - - role: Required[Literal["user", "system", "developer"]] - """The role of the message. One of `user`, `system`, or `developer`.""" - - type: Required[Literal["message"]] - """The type of item, which is always `message`.""" + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" -class TestingCriterionLabelModelInputOutputMessageContent(TypedDict, total=False): - text: Required[str] - """The text content.""" +TestingCriterionLabelModelInputEvalItemContent: TypeAlias = Union[ + str, ResponseInputTextParam, TestingCriterionLabelModelInputEvalItemContentOutputText +] - type: Required[Literal["output_text"]] - """The type of content, which is always `output_text`.""" +class TestingCriterionLabelModelInputEvalItem(TypedDict, total=False): + content: Required[TestingCriterionLabelModelInputEvalItemContent] + """Text inputs to the model - can contain template strings.""" -class TestingCriterionLabelModelInputOutputMessage(TypedDict, total=False): - content: Required[TestingCriterionLabelModelInputOutputMessageContent] + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. - role: Required[Literal["assistant"]] - """The role of the message. Must be `assistant` for output.""" + One of `user`, `assistant`, `system`, or `developer`. + """ - type: Required[Literal["message"]] - """The type of item, which is always `message`.""" + type: Literal["message"] + """The type of the message input. Always `message`.""" TestingCriterionLabelModelInput: TypeAlias = Union[ - TestingCriterionLabelModelInputSimpleInputMessage, - TestingCriterionLabelModelInputInputMessage, - TestingCriterionLabelModelInputOutputMessage, + TestingCriterionLabelModelInputSimpleInputMessage, TestingCriterionLabelModelInputEvalItem ] class TestingCriterionLabelModel(TypedDict, total=False): input: Required[Iterable[TestingCriterionLabelModelInput]] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ labels: Required[List[str]] """The labels to classify to each item in the evaluation.""" @@ -148,6 +139,77 @@ class TestingCriterionLabelModel(TypedDict, total=False): """The object type, which is always `label_model`.""" +class TestingCriterionPython(TypedDict, total=False): + name: Required[str] + """The name of the grader.""" + + source: Required[str] + """The source code of the python script.""" + + type: Required[Literal["python"]] + """The object type, which is always `python`.""" + + image_tag: str + """The image tag to use for the python script.""" + + pass_threshold: float + """The threshold for the score.""" + + +class TestingCriterionScoreModelInputContentOutputText(TypedDict, total=False): + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +TestingCriterionScoreModelInputContent: TypeAlias = Union[ + str, ResponseInputTextParam, TestingCriterionScoreModelInputContentOutputText +] + + +class TestingCriterionScoreModelInput(TypedDict, total=False): + content: Required[TestingCriterionScoreModelInputContent] + """Text inputs to the model - can contain template strings.""" + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +class TestingCriterionScoreModel(TypedDict, total=False): + input: Required[Iterable[TestingCriterionScoreModelInput]] + """The input text. This may include template strings.""" + + model: Required[str] + """The model to use for the evaluation.""" + + name: Required[str] + """The name of the grader.""" + + type: Required[Literal["score_model"]] + """The object type, which is always `score_model`.""" + + pass_threshold: float + """The threshold for the score.""" + + range: Iterable[float] + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: object + """The sampling parameters for the model.""" + + TestingCriterion: TypeAlias = Union[ - TestingCriterionLabelModel, EvalStringCheckGraderParam, EvalTextSimilarityGraderParam + TestingCriterionLabelModel, + EvalStringCheckGraderParam, + EvalTextSimilarityGraderParam, + TestingCriterionPython, + TestingCriterionScoreModel, ] diff --git a/src/openai/types/eval_create_response.py b/src/openai/types/eval_create_response.py index a1c2853a2a..6d77a81870 100644 --- a/src/openai/types/eval_create_response.py +++ b/src/openai/types/eval_create_response.py @@ -9,17 +9,106 @@ from .eval_label_model_grader import EvalLabelModelGrader from .eval_string_check_grader import EvalStringCheckGrader from .eval_text_similarity_grader import EvalTextSimilarityGrader +from .responses.response_input_text import ResponseInputText from .eval_custom_data_source_config import EvalCustomDataSourceConfig from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig -__all__ = ["EvalCreateResponse", "DataSourceConfig", "TestingCriterion"] +__all__ = [ + "EvalCreateResponse", + "DataSourceConfig", + "TestingCriterion", + "TestingCriterionPython", + "TestingCriterionScoreModel", + "TestingCriterionScoreModelInput", + "TestingCriterionScoreModelInputContent", + "TestingCriterionScoreModelInputContentOutputText", +] DataSourceConfig: TypeAlias = Annotated[ Union[EvalCustomDataSourceConfig, EvalStoredCompletionsDataSourceConfig], PropertyInfo(discriminator="type") ] + +class TestingCriterionPython(BaseModel): + __test__ = False + name: str + """The name of the grader.""" + + source: str + """The source code of the python script.""" + + type: Literal["python"] + """The object type, which is always `python`.""" + + image_tag: Optional[str] = None + """The image tag to use for the python script.""" + + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +class TestingCriterionScoreModelInputContentOutputText(BaseModel): + __test__ = False + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +TestingCriterionScoreModelInputContent: TypeAlias = Union[ + str, ResponseInputText, TestingCriterionScoreModelInputContentOutputText +] + + +class TestingCriterionScoreModelInput(BaseModel): + __test__ = False + content: TestingCriterionScoreModelInputContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +class TestingCriterionScoreModel(BaseModel): + __test__ = False + input: List[TestingCriterionScoreModelInput] + """The input text. This may include template strings.""" + + model: str + """The model to use for the evaluation.""" + + name: str + """The name of the grader.""" + + type: Literal["score_model"] + """The object type, which is always `score_model`.""" + + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + range: Optional[List[float]] = None + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: Optional[object] = None + """The sampling parameters for the model.""" + + TestingCriterion: TypeAlias = Annotated[ - Union[EvalLabelModelGrader, EvalStringCheckGrader, EvalTextSimilarityGrader], PropertyInfo(discriminator="type") + Union[ + EvalLabelModelGrader, + EvalStringCheckGrader, + EvalTextSimilarityGrader, + TestingCriterionPython, + TestingCriterionScoreModel, + ], + PropertyInfo(discriminator="type"), ] @@ -49,8 +138,5 @@ class EvalCreateResponse(BaseModel): object: Literal["eval"] """The object type.""" - share_with_openai: bool - """Indicates whether the evaluation is shared with OpenAI.""" - testing_criteria: List[TestingCriterion] """A list of testing criteria.""" diff --git a/src/openai/types/eval_label_model_grader.py b/src/openai/types/eval_label_model_grader.py index 826b116287..40e6bda140 100644 --- a/src/openai/types/eval_label_model_grader.py +++ b/src/openai/types/eval_label_model_grader.py @@ -1,58 +1,37 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union -from typing_extensions import Literal, Annotated, TypeAlias +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias -from .._utils import PropertyInfo from .._models import BaseModel +from .responses.response_input_text import ResponseInputText -__all__ = [ - "EvalLabelModelGrader", - "Input", - "InputInputMessage", - "InputInputMessageContent", - "InputAssistant", - "InputAssistantContent", -] +__all__ = ["EvalLabelModelGrader", "Input", "InputContent", "InputContentOutputText"] -class InputInputMessageContent(BaseModel): +class InputContentOutputText(BaseModel): text: str - """The text content.""" - - type: Literal["input_text"] - """The type of content, which is always `input_text`.""" - - -class InputInputMessage(BaseModel): - content: InputInputMessageContent - - role: Literal["user", "system", "developer"] - """The role of the message. One of `user`, `system`, or `developer`.""" - - type: Literal["message"] - """The type of item, which is always `message`.""" - - -class InputAssistantContent(BaseModel): - text: str - """The text content.""" + """The text output from the model.""" type: Literal["output_text"] - """The type of content, which is always `output_text`.""" + """The type of the output text. Always `output_text`.""" + +InputContent: TypeAlias = Union[str, ResponseInputText, InputContentOutputText] -class InputAssistant(BaseModel): - content: InputAssistantContent - role: Literal["assistant"] - """The role of the message. Must be `assistant` for output.""" +class Input(BaseModel): + content: InputContent + """Text inputs to the model - can contain template strings.""" - type: Literal["message"] - """The type of item, which is always `message`.""" + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + One of `user`, `assistant`, `system`, or `developer`. + """ -Input: TypeAlias = Annotated[Union[InputInputMessage, InputAssistant], PropertyInfo(discriminator="role")] + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" class EvalLabelModelGrader(BaseModel): diff --git a/src/openai/types/eval_list_response.py b/src/openai/types/eval_list_response.py index eb54569011..8c7e9c5588 100644 --- a/src/openai/types/eval_list_response.py +++ b/src/openai/types/eval_list_response.py @@ -9,17 +9,106 @@ from .eval_label_model_grader import EvalLabelModelGrader from .eval_string_check_grader import EvalStringCheckGrader from .eval_text_similarity_grader import EvalTextSimilarityGrader +from .responses.response_input_text import ResponseInputText from .eval_custom_data_source_config import EvalCustomDataSourceConfig from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig -__all__ = ["EvalListResponse", "DataSourceConfig", "TestingCriterion"] +__all__ = [ + "EvalListResponse", + "DataSourceConfig", + "TestingCriterion", + "TestingCriterionPython", + "TestingCriterionScoreModel", + "TestingCriterionScoreModelInput", + "TestingCriterionScoreModelInputContent", + "TestingCriterionScoreModelInputContentOutputText", +] DataSourceConfig: TypeAlias = Annotated[ Union[EvalCustomDataSourceConfig, EvalStoredCompletionsDataSourceConfig], PropertyInfo(discriminator="type") ] + +class TestingCriterionPython(BaseModel): + __test__ = False + name: str + """The name of the grader.""" + + source: str + """The source code of the python script.""" + + type: Literal["python"] + """The object type, which is always `python`.""" + + image_tag: Optional[str] = None + """The image tag to use for the python script.""" + + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +class TestingCriterionScoreModelInputContentOutputText(BaseModel): + __test__ = False + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +TestingCriterionScoreModelInputContent: TypeAlias = Union[ + str, ResponseInputText, TestingCriterionScoreModelInputContentOutputText +] + + +class TestingCriterionScoreModelInput(BaseModel): + __test__ = False + content: TestingCriterionScoreModelInputContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +class TestingCriterionScoreModel(BaseModel): + __test__ = False + input: List[TestingCriterionScoreModelInput] + """The input text. This may include template strings.""" + + model: str + """The model to use for the evaluation.""" + + name: str + """The name of the grader.""" + + type: Literal["score_model"] + """The object type, which is always `score_model`.""" + + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + range: Optional[List[float]] = None + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: Optional[object] = None + """The sampling parameters for the model.""" + + TestingCriterion: TypeAlias = Annotated[ - Union[EvalLabelModelGrader, EvalStringCheckGrader, EvalTextSimilarityGrader], PropertyInfo(discriminator="type") + Union[ + EvalLabelModelGrader, + EvalStringCheckGrader, + EvalTextSimilarityGrader, + TestingCriterionPython, + TestingCriterionScoreModel, + ], + PropertyInfo(discriminator="type"), ] @@ -49,8 +138,5 @@ class EvalListResponse(BaseModel): object: Literal["eval"] """The object type.""" - share_with_openai: bool - """Indicates whether the evaluation is shared with OpenAI.""" - testing_criteria: List[TestingCriterion] """A list of testing criteria.""" diff --git a/src/openai/types/eval_retrieve_response.py b/src/openai/types/eval_retrieve_response.py index 8f3bfdf902..625bae80f4 100644 --- a/src/openai/types/eval_retrieve_response.py +++ b/src/openai/types/eval_retrieve_response.py @@ -9,17 +9,106 @@ from .eval_label_model_grader import EvalLabelModelGrader from .eval_string_check_grader import EvalStringCheckGrader from .eval_text_similarity_grader import EvalTextSimilarityGrader +from .responses.response_input_text import ResponseInputText from .eval_custom_data_source_config import EvalCustomDataSourceConfig from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig -__all__ = ["EvalRetrieveResponse", "DataSourceConfig", "TestingCriterion"] +__all__ = [ + "EvalRetrieveResponse", + "DataSourceConfig", + "TestingCriterion", + "TestingCriterionPython", + "TestingCriterionScoreModel", + "TestingCriterionScoreModelInput", + "TestingCriterionScoreModelInputContent", + "TestingCriterionScoreModelInputContentOutputText", +] DataSourceConfig: TypeAlias = Annotated[ Union[EvalCustomDataSourceConfig, EvalStoredCompletionsDataSourceConfig], PropertyInfo(discriminator="type") ] + +class TestingCriterionPython(BaseModel): + __test__ = False + name: str + """The name of the grader.""" + + source: str + """The source code of the python script.""" + + type: Literal["python"] + """The object type, which is always `python`.""" + + image_tag: Optional[str] = None + """The image tag to use for the python script.""" + + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +class TestingCriterionScoreModelInputContentOutputText(BaseModel): + __test__ = False + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +TestingCriterionScoreModelInputContent: TypeAlias = Union[ + str, ResponseInputText, TestingCriterionScoreModelInputContentOutputText +] + + +class TestingCriterionScoreModelInput(BaseModel): + __test__ = False + content: TestingCriterionScoreModelInputContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +class TestingCriterionScoreModel(BaseModel): + __test__ = False + input: List[TestingCriterionScoreModelInput] + """The input text. This may include template strings.""" + + model: str + """The model to use for the evaluation.""" + + name: str + """The name of the grader.""" + + type: Literal["score_model"] + """The object type, which is always `score_model`.""" + + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + range: Optional[List[float]] = None + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: Optional[object] = None + """The sampling parameters for the model.""" + + TestingCriterion: TypeAlias = Annotated[ - Union[EvalLabelModelGrader, EvalStringCheckGrader, EvalTextSimilarityGrader], PropertyInfo(discriminator="type") + Union[ + EvalLabelModelGrader, + EvalStringCheckGrader, + EvalTextSimilarityGrader, + TestingCriterionPython, + TestingCriterionScoreModel, + ], + PropertyInfo(discriminator="type"), ] @@ -49,8 +138,5 @@ class EvalRetrieveResponse(BaseModel): object: Literal["eval"] """The object type.""" - share_with_openai: bool - """Indicates whether the evaluation is shared with OpenAI.""" - testing_criteria: List[TestingCriterion] """A list of testing criteria.""" diff --git a/src/openai/types/eval_text_similarity_grader.py b/src/openai/types/eval_text_similarity_grader.py index 7c6897a4a7..853c6d4fbf 100644 --- a/src/openai/types/eval_text_similarity_grader.py +++ b/src/openai/types/eval_text_similarity_grader.py @@ -10,22 +10,12 @@ class EvalTextSimilarityGrader(BaseModel): evaluation_metric: Literal[ - "fuzzy_match", - "bleu", - "gleu", - "meteor", - "rouge_1", - "rouge_2", - "rouge_3", - "rouge_4", - "rouge_5", - "rouge_l", - "cosine", + "fuzzy_match", "bleu", "gleu", "meteor", "rouge_1", "rouge_2", "rouge_3", "rouge_4", "rouge_5", "rouge_l" ] """The evaluation metric to use. - One of `cosine`, `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, - `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. + One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, + `rouge_4`, `rouge_5`, or `rouge_l`. """ input: str diff --git a/src/openai/types/eval_text_similarity_grader_param.py b/src/openai/types/eval_text_similarity_grader_param.py index 4bf5d586f3..f07cc29178 100644 --- a/src/openai/types/eval_text_similarity_grader_param.py +++ b/src/openai/types/eval_text_similarity_grader_param.py @@ -10,23 +10,13 @@ class EvalTextSimilarityGraderParam(TypedDict, total=False): evaluation_metric: Required[ Literal[ - "fuzzy_match", - "bleu", - "gleu", - "meteor", - "rouge_1", - "rouge_2", - "rouge_3", - "rouge_4", - "rouge_5", - "rouge_l", - "cosine", + "fuzzy_match", "bleu", "gleu", "meteor", "rouge_1", "rouge_2", "rouge_3", "rouge_4", "rouge_5", "rouge_l" ] ] """The evaluation metric to use. - One of `cosine`, `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, - `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. + One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, + `rouge_4`, `rouge_5`, or `rouge_l`. """ input: Required[str] diff --git a/src/openai/types/eval_update_response.py b/src/openai/types/eval_update_response.py index 728a291736..2c280977a1 100644 --- a/src/openai/types/eval_update_response.py +++ b/src/openai/types/eval_update_response.py @@ -9,17 +9,106 @@ from .eval_label_model_grader import EvalLabelModelGrader from .eval_string_check_grader import EvalStringCheckGrader from .eval_text_similarity_grader import EvalTextSimilarityGrader +from .responses.response_input_text import ResponseInputText from .eval_custom_data_source_config import EvalCustomDataSourceConfig from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig -__all__ = ["EvalUpdateResponse", "DataSourceConfig", "TestingCriterion"] +__all__ = [ + "EvalUpdateResponse", + "DataSourceConfig", + "TestingCriterion", + "TestingCriterionPython", + "TestingCriterionScoreModel", + "TestingCriterionScoreModelInput", + "TestingCriterionScoreModelInputContent", + "TestingCriterionScoreModelInputContentOutputText", +] DataSourceConfig: TypeAlias = Annotated[ Union[EvalCustomDataSourceConfig, EvalStoredCompletionsDataSourceConfig], PropertyInfo(discriminator="type") ] + +class TestingCriterionPython(BaseModel): + __test__ = False + name: str + """The name of the grader.""" + + source: str + """The source code of the python script.""" + + type: Literal["python"] + """The object type, which is always `python`.""" + + image_tag: Optional[str] = None + """The image tag to use for the python script.""" + + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + +class TestingCriterionScoreModelInputContentOutputText(BaseModel): + __test__ = False + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +TestingCriterionScoreModelInputContent: TypeAlias = Union[ + str, ResponseInputText, TestingCriterionScoreModelInputContentOutputText +] + + +class TestingCriterionScoreModelInput(BaseModel): + __test__ = False + content: TestingCriterionScoreModelInputContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +class TestingCriterionScoreModel(BaseModel): + __test__ = False + input: List[TestingCriterionScoreModelInput] + """The input text. This may include template strings.""" + + model: str + """The model to use for the evaluation.""" + + name: str + """The name of the grader.""" + + type: Literal["score_model"] + """The object type, which is always `score_model`.""" + + pass_threshold: Optional[float] = None + """The threshold for the score.""" + + range: Optional[List[float]] = None + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: Optional[object] = None + """The sampling parameters for the model.""" + + TestingCriterion: TypeAlias = Annotated[ - Union[EvalLabelModelGrader, EvalStringCheckGrader, EvalTextSimilarityGrader], PropertyInfo(discriminator="type") + Union[ + EvalLabelModelGrader, + EvalStringCheckGrader, + EvalTextSimilarityGrader, + TestingCriterionPython, + TestingCriterionScoreModel, + ], + PropertyInfo(discriminator="type"), ] @@ -49,8 +138,5 @@ class EvalUpdateResponse(BaseModel): object: Literal["eval"] """The object type.""" - share_with_openai: bool - """Indicates whether the evaluation is shared with OpenAI.""" - testing_criteria: List[TestingCriterion] """A list of testing criteria.""" diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index 07b88129e2..29c687b542 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -6,102 +6,27 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from ..shared.metadata import Metadata +from ..responses.easy_input_message import EasyInputMessage +from ..responses.response_input_text import ResponseInputText __all__ = [ "CreateEvalCompletionsRunDataSource", - "InputMessages", - "InputMessagesTemplate", - "InputMessagesTemplateTemplate", - "InputMessagesTemplateTemplateChatMessage", - "InputMessagesTemplateTemplateInputMessage", - "InputMessagesTemplateTemplateInputMessageContent", - "InputMessagesTemplateTemplateOutputMessage", - "InputMessagesTemplateTemplateOutputMessageContent", - "InputMessagesItemReference", "Source", "SourceFileContent", "SourceFileContentContent", "SourceFileID", "SourceStoredCompletions", + "InputMessages", + "InputMessagesTemplate", + "InputMessagesTemplateTemplate", + "InputMessagesTemplateTemplateMessage", + "InputMessagesTemplateTemplateMessageContent", + "InputMessagesTemplateTemplateMessageContentOutputText", + "InputMessagesItemReference", "SamplingParams", ] -class InputMessagesTemplateTemplateChatMessage(BaseModel): - content: str - """The content of the message.""" - - role: str - """The role of the message (e.g. "system", "assistant", "user").""" - - -class InputMessagesTemplateTemplateInputMessageContent(BaseModel): - text: str - """The text content.""" - - type: Literal["input_text"] - """The type of content, which is always `input_text`.""" - - -class InputMessagesTemplateTemplateInputMessage(BaseModel): - content: InputMessagesTemplateTemplateInputMessageContent - - role: Literal["user", "system", "developer"] - """The role of the message. One of `user`, `system`, or `developer`.""" - - type: Literal["message"] - """The type of item, which is always `message`.""" - - -class InputMessagesTemplateTemplateOutputMessageContent(BaseModel): - text: str - """The text content.""" - - type: Literal["output_text"] - """The type of content, which is always `output_text`.""" - - -class InputMessagesTemplateTemplateOutputMessage(BaseModel): - content: InputMessagesTemplateTemplateOutputMessageContent - - role: Literal["assistant"] - """The role of the message. Must be `assistant` for output.""" - - type: Literal["message"] - """The type of item, which is always `message`.""" - - -InputMessagesTemplateTemplate: TypeAlias = Union[ - InputMessagesTemplateTemplateChatMessage, - InputMessagesTemplateTemplateInputMessage, - InputMessagesTemplateTemplateOutputMessage, -] - - -class InputMessagesTemplate(BaseModel): - template: List[InputMessagesTemplateTemplate] - """A list of chat messages forming the prompt or context. - - May include variable references to the "item" namespace, ie {{item.name}}. - """ - - type: Literal["template"] - """The type of input messages. Always `template`.""" - - -class InputMessagesItemReference(BaseModel): - item_reference: str - """A reference to a variable in the "item" namespace. Ie, "item.name" """ - - type: Literal["item_reference"] - """The type of input messages. Always `item_reference`.""" - - -InputMessages: TypeAlias = Annotated[ - Union[InputMessagesTemplate, InputMessagesItemReference], PropertyInfo(discriminator="type") -] - - class SourceFileContentContent(BaseModel): item: Dict[str, object] @@ -125,6 +50,9 @@ class SourceFileID(BaseModel): class SourceStoredCompletions(BaseModel): + type: Literal["stored_completions"] + """The type of source. Always `stored_completions`.""" + created_after: Optional[int] = None """An optional Unix timestamp to filter items created after this time.""" @@ -147,15 +75,68 @@ class SourceStoredCompletions(BaseModel): model: Optional[str] = None """An optional model to filter by (e.g., 'gpt-4o').""" - type: Literal["stored_completions"] - """The type of source. Always `stored_completions`.""" - Source: TypeAlias = Annotated[ Union[SourceFileContent, SourceFileID, SourceStoredCompletions], PropertyInfo(discriminator="type") ] +class InputMessagesTemplateTemplateMessageContentOutputText(BaseModel): + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +InputMessagesTemplateTemplateMessageContent: TypeAlias = Union[ + str, ResponseInputText, InputMessagesTemplateTemplateMessageContentOutputText +] + + +class InputMessagesTemplateTemplateMessage(BaseModel): + content: InputMessagesTemplateTemplateMessageContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +InputMessagesTemplateTemplate: TypeAlias = Annotated[ + Union[EasyInputMessage, InputMessagesTemplateTemplateMessage], PropertyInfo(discriminator="type") +] + + +class InputMessagesTemplate(BaseModel): + template: List[InputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class InputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +InputMessages: TypeAlias = Annotated[ + Union[InputMessagesTemplate, InputMessagesItemReference], PropertyInfo(discriminator="type") +] + + class SamplingParams(BaseModel): max_completion_tokens: Optional[int] = None """The maximum number of tokens in the generated output.""" @@ -171,15 +152,15 @@ class SamplingParams(BaseModel): class CreateEvalCompletionsRunDataSource(BaseModel): - input_messages: InputMessages - - model: str - """The name of the model to use for generating completions (e.g. "o3-mini").""" - source: Source """A StoredCompletionsRunDataSource configuration describing a set of filters""" type: Literal["completions"] """The type of run data source. Always `completions`.""" + input_messages: Optional[InputMessages] = None + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + sampling_params: Optional[SamplingParams] = None diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index be4a6f1ec6..c53064ee27 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -6,100 +6,27 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..shared_params.metadata import Metadata +from ..responses.easy_input_message_param import EasyInputMessageParam +from ..responses.response_input_text_param import ResponseInputTextParam __all__ = [ "CreateEvalCompletionsRunDataSourceParam", - "InputMessages", - "InputMessagesTemplate", - "InputMessagesTemplateTemplate", - "InputMessagesTemplateTemplateChatMessage", - "InputMessagesTemplateTemplateInputMessage", - "InputMessagesTemplateTemplateInputMessageContent", - "InputMessagesTemplateTemplateOutputMessage", - "InputMessagesTemplateTemplateOutputMessageContent", - "InputMessagesItemReference", "Source", "SourceFileContent", "SourceFileContentContent", "SourceFileID", "SourceStoredCompletions", + "InputMessages", + "InputMessagesTemplate", + "InputMessagesTemplateTemplate", + "InputMessagesTemplateTemplateMessage", + "InputMessagesTemplateTemplateMessageContent", + "InputMessagesTemplateTemplateMessageContentOutputText", + "InputMessagesItemReference", "SamplingParams", ] -class InputMessagesTemplateTemplateChatMessage(TypedDict, total=False): - content: Required[str] - """The content of the message.""" - - role: Required[str] - """The role of the message (e.g. "system", "assistant", "user").""" - - -class InputMessagesTemplateTemplateInputMessageContent(TypedDict, total=False): - text: Required[str] - """The text content.""" - - type: Required[Literal["input_text"]] - """The type of content, which is always `input_text`.""" - - -class InputMessagesTemplateTemplateInputMessage(TypedDict, total=False): - content: Required[InputMessagesTemplateTemplateInputMessageContent] - - role: Required[Literal["user", "system", "developer"]] - """The role of the message. One of `user`, `system`, or `developer`.""" - - type: Required[Literal["message"]] - """The type of item, which is always `message`.""" - - -class InputMessagesTemplateTemplateOutputMessageContent(TypedDict, total=False): - text: Required[str] - """The text content.""" - - type: Required[Literal["output_text"]] - """The type of content, which is always `output_text`.""" - - -class InputMessagesTemplateTemplateOutputMessage(TypedDict, total=False): - content: Required[InputMessagesTemplateTemplateOutputMessageContent] - - role: Required[Literal["assistant"]] - """The role of the message. Must be `assistant` for output.""" - - type: Required[Literal["message"]] - """The type of item, which is always `message`.""" - - -InputMessagesTemplateTemplate: TypeAlias = Union[ - InputMessagesTemplateTemplateChatMessage, - InputMessagesTemplateTemplateInputMessage, - InputMessagesTemplateTemplateOutputMessage, -] - - -class InputMessagesTemplate(TypedDict, total=False): - template: Required[Iterable[InputMessagesTemplateTemplate]] - """A list of chat messages forming the prompt or context. - - May include variable references to the "item" namespace, ie {{item.name}}. - """ - - type: Required[Literal["template"]] - """The type of input messages. Always `template`.""" - - -class InputMessagesItemReference(TypedDict, total=False): - item_reference: Required[str] - """A reference to a variable in the "item" namespace. Ie, "item.name" """ - - type: Required[Literal["item_reference"]] - """The type of input messages. Always `item_reference`.""" - - -InputMessages: TypeAlias = Union[InputMessagesTemplate, InputMessagesItemReference] - - class SourceFileContentContent(TypedDict, total=False): item: Required[Dict[str, object]] @@ -123,16 +50,19 @@ class SourceFileID(TypedDict, total=False): class SourceStoredCompletions(TypedDict, total=False): - created_after: Required[Optional[int]] + type: Required[Literal["stored_completions"]] + """The type of source. Always `stored_completions`.""" + + created_after: Optional[int] """An optional Unix timestamp to filter items created after this time.""" - created_before: Required[Optional[int]] + created_before: Optional[int] """An optional Unix timestamp to filter items created before this time.""" - limit: Required[Optional[int]] + limit: Optional[int] """An optional maximum number of items to return.""" - metadata: Required[Optional[Metadata]] + metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a @@ -142,16 +72,65 @@ class SourceStoredCompletions(TypedDict, total=False): a maximum length of 512 characters. """ - model: Required[Optional[str]] + model: Optional[str] """An optional model to filter by (e.g., 'gpt-4o').""" - type: Required[Literal["stored_completions"]] - """The type of source. Always `stored_completions`.""" - Source: TypeAlias = Union[SourceFileContent, SourceFileID, SourceStoredCompletions] +class InputMessagesTemplateTemplateMessageContentOutputText(TypedDict, total=False): + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +InputMessagesTemplateTemplateMessageContent: TypeAlias = Union[ + str, ResponseInputTextParam, InputMessagesTemplateTemplateMessageContentOutputText +] + + +class InputMessagesTemplateTemplateMessage(TypedDict, total=False): + content: Required[InputMessagesTemplateTemplateMessageContent] + """Text inputs to the model - can contain template strings.""" + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +InputMessagesTemplateTemplate: TypeAlias = Union[EasyInputMessageParam, InputMessagesTemplateTemplateMessage] + + +class InputMessagesTemplate(TypedDict, total=False): + template: Required[Iterable[InputMessagesTemplateTemplate]] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Required[Literal["template"]] + """The type of input messages. Always `template`.""" + + +class InputMessagesItemReference(TypedDict, total=False): + item_reference: Required[str] + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Required[Literal["item_reference"]] + """The type of input messages. Always `item_reference`.""" + + +InputMessages: TypeAlias = Union[InputMessagesTemplate, InputMessagesItemReference] + + class SamplingParams(TypedDict, total=False): max_completion_tokens: int """The maximum number of tokens in the generated output.""" @@ -167,15 +146,15 @@ class SamplingParams(TypedDict, total=False): class CreateEvalCompletionsRunDataSourceParam(TypedDict, total=False): - input_messages: Required[InputMessages] - - model: Required[str] - """The name of the model to use for generating completions (e.g. "o3-mini").""" - source: Required[Source] """A StoredCompletionsRunDataSource configuration describing a set of filters""" type: Required[Literal["completions"]] """The type of run data source. Always `completions`.""" + input_messages: InputMessages + + model: str + """The name of the model to use for generating completions (e.g. "o3-mini").""" + sampling_params: SamplingParams diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 90e52241a6..eb6d689fc3 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from pydantic import Field as FieldInfo @@ -9,13 +9,225 @@ from ..._models import BaseModel from .eval_api_error import EvalAPIError from ..shared.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource -__all__ = ["RunCancelResponse", "DataSource", "PerModelUsage", "PerTestingCriteriaResult", "ResultCounts"] +__all__ = [ + "RunCancelResponse", + "DataSource", + "DataSourceCompletions", + "DataSourceCompletionsSource", + "DataSourceCompletionsSourceFileContent", + "DataSourceCompletionsSourceFileContentContent", + "DataSourceCompletionsSourceFileID", + "DataSourceCompletionsSourceResponses", + "DataSourceCompletionsInputMessages", + "DataSourceCompletionsInputMessagesTemplate", + "DataSourceCompletionsInputMessagesTemplateTemplate", + "DataSourceCompletionsInputMessagesTemplateTemplateChatMessage", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItem", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceCompletionsInputMessagesItemReference", + "DataSourceCompletionsSamplingParams", + "PerModelUsage", + "PerTestingCriteriaResult", + "ResultCounts", +] + + +class DataSourceCompletionsSourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class DataSourceCompletionsSourceFileContent(BaseModel): + content: List[DataSourceCompletionsSourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceCompletionsSourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceCompletionsSourceResponses(BaseModel): + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + allow_parallel_tool_calls: Optional[bool] = None + """Whether to allow parallel tool calls. + + This is a query parameter used to select responses. + """ + + created_after: Optional[int] = None + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] = None + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + has_tool_calls: Optional[bool] = None + """Whether the response has tool calls. + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] = None + """Optional search string for instructions. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] = None + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] = None + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] = None + """Optional reasoning effort parameter. + + This is a query parameter used to select responses. + """ + + temperature: Optional[float] = None + """Sampling temperature. This is a query parameter used to select responses.""" + + top_p: Optional[float] = None + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] = None + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceCompletionsSource: TypeAlias = Annotated[ + Union[ + DataSourceCompletionsSourceFileContent, DataSourceCompletionsSourceFileID, DataSourceCompletionsSourceResponses + ], + PropertyInfo(discriminator="type"), +] + + +class DataSourceCompletionsInputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, ResponseInputText, DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText +] + + +class DataSourceCompletionsInputMessagesTemplateTemplateEvalItem(BaseModel): + content: DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +DataSourceCompletionsInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceCompletionsInputMessagesTemplateTemplateChatMessage, + DataSourceCompletionsInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceCompletionsInputMessagesTemplate(BaseModel): + template: List[DataSourceCompletionsInputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class DataSourceCompletionsInputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +DataSourceCompletionsInputMessages: TypeAlias = Annotated[ + Union[DataSourceCompletionsInputMessagesTemplate, DataSourceCompletionsInputMessagesItemReference], + PropertyInfo(discriminator="type"), +] + + +class DataSourceCompletionsSamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceCompletions(BaseModel): + source: DataSourceCompletionsSource + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Literal["completions"] + """The type of run data source. Always `completions`.""" + + input_messages: Optional[DataSourceCompletionsInputMessages] = None + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[DataSourceCompletionsSamplingParams] = None + DataSource: TypeAlias = Annotated[ - Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource], PropertyInfo(discriminator="type") + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource, DataSourceCompletions], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index acf7b1b126..0c9720ea7a 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -2,14 +2,34 @@ from __future__ import annotations -from typing import Union, Optional -from typing_extensions import Required, TypeAlias, TypedDict +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..shared_params.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text_param import ResponseInputTextParam from .create_eval_jsonl_run_data_source_param import CreateEvalJSONLRunDataSourceParam from .create_eval_completions_run_data_source_param import CreateEvalCompletionsRunDataSourceParam -__all__ = ["RunCreateParams", "DataSource"] +__all__ = [ + "RunCreateParams", + "DataSource", + "DataSourceCreateEvalResponsesRunDataSource", + "DataSourceCreateEvalResponsesRunDataSourceSource", + "DataSourceCreateEvalResponsesRunDataSourceSourceFileContent", + "DataSourceCreateEvalResponsesRunDataSourceSourceFileContentContent", + "DataSourceCreateEvalResponsesRunDataSourceSourceFileID", + "DataSourceCreateEvalResponsesRunDataSourceSourceResponses", + "DataSourceCreateEvalResponsesRunDataSourceInputMessages", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplate", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplate", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateChatMessage", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference", + "DataSourceCreateEvalResponsesRunDataSourceSamplingParams", +] class RunCreateParams(TypedDict, total=False): @@ -30,4 +50,198 @@ class RunCreateParams(TypedDict, total=False): """The name of the run.""" -DataSource: TypeAlias = Union[CreateEvalJSONLRunDataSourceParam, CreateEvalCompletionsRunDataSourceParam] +class DataSourceCreateEvalResponsesRunDataSourceSourceFileContentContent(TypedDict, total=False): + item: Required[Dict[str, object]] + + sample: Dict[str, object] + + +class DataSourceCreateEvalResponsesRunDataSourceSourceFileContent(TypedDict, total=False): + content: Required[Iterable[DataSourceCreateEvalResponsesRunDataSourceSourceFileContentContent]] + """The content of the jsonl file.""" + + type: Required[Literal["file_content"]] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceCreateEvalResponsesRunDataSourceSourceFileID(TypedDict, total=False): + id: Required[str] + """The identifier of the file.""" + + type: Required[Literal["file_id"]] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total=False): + type: Required[Literal["responses"]] + """The type of run data source. Always `responses`.""" + + allow_parallel_tool_calls: Optional[bool] + """Whether to allow parallel tool calls. + + This is a query parameter used to select responses. + """ + + created_after: Optional[int] + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + has_tool_calls: Optional[bool] + """Whether the response has tool calls. + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] + """Optional search string for instructions. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] + """Optional reasoning effort parameter. + + This is a query parameter used to select responses. + """ + + temperature: Optional[float] + """Sampling temperature. This is a query parameter used to select responses.""" + + top_p: Optional[float] + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceCreateEvalResponsesRunDataSourceSource: TypeAlias = Union[ + DataSourceCreateEvalResponsesRunDataSourceSourceFileContent, + DataSourceCreateEvalResponsesRunDataSourceSourceFileID, + DataSourceCreateEvalResponsesRunDataSourceSourceResponses, +] + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateChatMessage(TypedDict, total=False): + content: Required[str] + """The content of the message.""" + + role: Required[str] + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText( + TypedDict, total=False +): + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, + ResponseInputTextParam, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText, +] + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem(TypedDict, total=False): + content: Required[DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent] + """Text inputs to the model - can contain template strings.""" + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateChatMessage, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplate(TypedDict, total=False): + template: Required[Iterable[DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplate]] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Required[Literal["template"]] + """The type of input messages. Always `template`.""" + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference(TypedDict, total=False): + item_reference: Required[str] + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Required[Literal["item_reference"]] + """The type of input messages. Always `item_reference`.""" + + +DataSourceCreateEvalResponsesRunDataSourceInputMessages: TypeAlias = Union[ + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplate, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference, +] + + +class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total=False): + max_completion_tokens: int + """The maximum number of tokens in the generated output.""" + + seed: int + """A seed value to initialize the randomness, during sampling.""" + + temperature: float + """A higher temperature increases randomness in the outputs.""" + + top_p: float + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceCreateEvalResponsesRunDataSource(TypedDict, total=False): + source: Required[DataSourceCreateEvalResponsesRunDataSourceSource] + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Required[Literal["completions"]] + """The type of run data source. Always `completions`.""" + + input_messages: DataSourceCreateEvalResponsesRunDataSourceInputMessages + + model: str + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: DataSourceCreateEvalResponsesRunDataSourceSamplingParams + + +DataSource: TypeAlias = Union[ + CreateEvalJSONLRunDataSourceParam, + CreateEvalCompletionsRunDataSourceParam, + DataSourceCreateEvalResponsesRunDataSource, +] diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index 14ca426427..459399511c 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from pydantic import Field as FieldInfo @@ -9,13 +9,225 @@ from ..._models import BaseModel from .eval_api_error import EvalAPIError from ..shared.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource -__all__ = ["RunCreateResponse", "DataSource", "PerModelUsage", "PerTestingCriteriaResult", "ResultCounts"] +__all__ = [ + "RunCreateResponse", + "DataSource", + "DataSourceCompletions", + "DataSourceCompletionsSource", + "DataSourceCompletionsSourceFileContent", + "DataSourceCompletionsSourceFileContentContent", + "DataSourceCompletionsSourceFileID", + "DataSourceCompletionsSourceResponses", + "DataSourceCompletionsInputMessages", + "DataSourceCompletionsInputMessagesTemplate", + "DataSourceCompletionsInputMessagesTemplateTemplate", + "DataSourceCompletionsInputMessagesTemplateTemplateChatMessage", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItem", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceCompletionsInputMessagesItemReference", + "DataSourceCompletionsSamplingParams", + "PerModelUsage", + "PerTestingCriteriaResult", + "ResultCounts", +] + + +class DataSourceCompletionsSourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class DataSourceCompletionsSourceFileContent(BaseModel): + content: List[DataSourceCompletionsSourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceCompletionsSourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceCompletionsSourceResponses(BaseModel): + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + allow_parallel_tool_calls: Optional[bool] = None + """Whether to allow parallel tool calls. + + This is a query parameter used to select responses. + """ + + created_after: Optional[int] = None + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] = None + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + has_tool_calls: Optional[bool] = None + """Whether the response has tool calls. + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] = None + """Optional search string for instructions. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] = None + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] = None + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] = None + """Optional reasoning effort parameter. + + This is a query parameter used to select responses. + """ + + temperature: Optional[float] = None + """Sampling temperature. This is a query parameter used to select responses.""" + + top_p: Optional[float] = None + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] = None + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceCompletionsSource: TypeAlias = Annotated[ + Union[ + DataSourceCompletionsSourceFileContent, DataSourceCompletionsSourceFileID, DataSourceCompletionsSourceResponses + ], + PropertyInfo(discriminator="type"), +] + + +class DataSourceCompletionsInputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, ResponseInputText, DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText +] + + +class DataSourceCompletionsInputMessagesTemplateTemplateEvalItem(BaseModel): + content: DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +DataSourceCompletionsInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceCompletionsInputMessagesTemplateTemplateChatMessage, + DataSourceCompletionsInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceCompletionsInputMessagesTemplate(BaseModel): + template: List[DataSourceCompletionsInputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class DataSourceCompletionsInputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +DataSourceCompletionsInputMessages: TypeAlias = Annotated[ + Union[DataSourceCompletionsInputMessagesTemplate, DataSourceCompletionsInputMessagesItemReference], + PropertyInfo(discriminator="type"), +] + + +class DataSourceCompletionsSamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceCompletions(BaseModel): + source: DataSourceCompletionsSource + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Literal["completions"] + """The type of run data source. Always `completions`.""" + + input_messages: Optional[DataSourceCompletionsInputMessages] = None + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[DataSourceCompletionsSamplingParams] = None + DataSource: TypeAlias = Annotated[ - Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource], PropertyInfo(discriminator="type") + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource, DataSourceCompletions], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/evals/run_list_params.py b/src/openai/types/evals/run_list_params.py index 6060eafb97..383b89d85c 100644 --- a/src/openai/types/evals/run_list_params.py +++ b/src/openai/types/evals/run_list_params.py @@ -23,5 +23,5 @@ class RunListParams(TypedDict, total=False): status: Literal["queued", "in_progress", "completed", "canceled", "failed"] """Filter runs by status. - Use "queued" | "in_progress" | "failed" | "completed" | "canceled". + One of `queued` | `in_progress` | `failed` | `completed` | `canceled`. """ diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index a1022f542f..278ceeabed 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from pydantic import Field as FieldInfo @@ -9,13 +9,225 @@ from ..._models import BaseModel from .eval_api_error import EvalAPIError from ..shared.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource -__all__ = ["RunListResponse", "DataSource", "PerModelUsage", "PerTestingCriteriaResult", "ResultCounts"] +__all__ = [ + "RunListResponse", + "DataSource", + "DataSourceCompletions", + "DataSourceCompletionsSource", + "DataSourceCompletionsSourceFileContent", + "DataSourceCompletionsSourceFileContentContent", + "DataSourceCompletionsSourceFileID", + "DataSourceCompletionsSourceResponses", + "DataSourceCompletionsInputMessages", + "DataSourceCompletionsInputMessagesTemplate", + "DataSourceCompletionsInputMessagesTemplateTemplate", + "DataSourceCompletionsInputMessagesTemplateTemplateChatMessage", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItem", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceCompletionsInputMessagesItemReference", + "DataSourceCompletionsSamplingParams", + "PerModelUsage", + "PerTestingCriteriaResult", + "ResultCounts", +] + + +class DataSourceCompletionsSourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class DataSourceCompletionsSourceFileContent(BaseModel): + content: List[DataSourceCompletionsSourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceCompletionsSourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceCompletionsSourceResponses(BaseModel): + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + allow_parallel_tool_calls: Optional[bool] = None + """Whether to allow parallel tool calls. + + This is a query parameter used to select responses. + """ + + created_after: Optional[int] = None + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] = None + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + has_tool_calls: Optional[bool] = None + """Whether the response has tool calls. + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] = None + """Optional search string for instructions. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] = None + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] = None + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] = None + """Optional reasoning effort parameter. + + This is a query parameter used to select responses. + """ + + temperature: Optional[float] = None + """Sampling temperature. This is a query parameter used to select responses.""" + + top_p: Optional[float] = None + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] = None + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceCompletionsSource: TypeAlias = Annotated[ + Union[ + DataSourceCompletionsSourceFileContent, DataSourceCompletionsSourceFileID, DataSourceCompletionsSourceResponses + ], + PropertyInfo(discriminator="type"), +] + + +class DataSourceCompletionsInputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, ResponseInputText, DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText +] + + +class DataSourceCompletionsInputMessagesTemplateTemplateEvalItem(BaseModel): + content: DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +DataSourceCompletionsInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceCompletionsInputMessagesTemplateTemplateChatMessage, + DataSourceCompletionsInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceCompletionsInputMessagesTemplate(BaseModel): + template: List[DataSourceCompletionsInputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class DataSourceCompletionsInputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +DataSourceCompletionsInputMessages: TypeAlias = Annotated[ + Union[DataSourceCompletionsInputMessagesTemplate, DataSourceCompletionsInputMessagesItemReference], + PropertyInfo(discriminator="type"), +] + + +class DataSourceCompletionsSamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceCompletions(BaseModel): + source: DataSourceCompletionsSource + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Literal["completions"] + """The type of run data source. Always `completions`.""" + + input_messages: Optional[DataSourceCompletionsInputMessages] = None + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[DataSourceCompletionsSamplingParams] = None + DataSource: TypeAlias = Annotated[ - Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource], PropertyInfo(discriminator="type") + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource, DataSourceCompletions], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index 461ed43dda..e142f31b14 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from pydantic import Field as FieldInfo @@ -9,13 +9,225 @@ from ..._models import BaseModel from .eval_api_error import EvalAPIError from ..shared.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort +from ..responses.response_input_text import ResponseInputText from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource -__all__ = ["RunRetrieveResponse", "DataSource", "PerModelUsage", "PerTestingCriteriaResult", "ResultCounts"] +__all__ = [ + "RunRetrieveResponse", + "DataSource", + "DataSourceCompletions", + "DataSourceCompletionsSource", + "DataSourceCompletionsSourceFileContent", + "DataSourceCompletionsSourceFileContentContent", + "DataSourceCompletionsSourceFileID", + "DataSourceCompletionsSourceResponses", + "DataSourceCompletionsInputMessages", + "DataSourceCompletionsInputMessagesTemplate", + "DataSourceCompletionsInputMessagesTemplateTemplate", + "DataSourceCompletionsInputMessagesTemplateTemplateChatMessage", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItem", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent", + "DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceCompletionsInputMessagesItemReference", + "DataSourceCompletionsSamplingParams", + "PerModelUsage", + "PerTestingCriteriaResult", + "ResultCounts", +] + + +class DataSourceCompletionsSourceFileContentContent(BaseModel): + item: Dict[str, object] + + sample: Optional[Dict[str, object]] = None + + +class DataSourceCompletionsSourceFileContent(BaseModel): + content: List[DataSourceCompletionsSourceFileContentContent] + """The content of the jsonl file.""" + + type: Literal["file_content"] + """The type of jsonl source. Always `file_content`.""" + + +class DataSourceCompletionsSourceFileID(BaseModel): + id: str + """The identifier of the file.""" + + type: Literal["file_id"] + """The type of jsonl source. Always `file_id`.""" + + +class DataSourceCompletionsSourceResponses(BaseModel): + type: Literal["responses"] + """The type of run data source. Always `responses`.""" + + allow_parallel_tool_calls: Optional[bool] = None + """Whether to allow parallel tool calls. + + This is a query parameter used to select responses. + """ + + created_after: Optional[int] = None + """Only include items created after this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + created_before: Optional[int] = None + """Only include items created before this timestamp (inclusive). + + This is a query parameter used to select responses. + """ + + has_tool_calls: Optional[bool] = None + """Whether the response has tool calls. + + This is a query parameter used to select responses. + """ + + instructions_search: Optional[str] = None + """Optional search string for instructions. + + This is a query parameter used to select responses. + """ + + metadata: Optional[object] = None + """Metadata filter for the responses. + + This is a query parameter used to select responses. + """ + + model: Optional[str] = None + """The name of the model to find responses for. + + This is a query parameter used to select responses. + """ + + reasoning_effort: Optional[ReasoningEffort] = None + """Optional reasoning effort parameter. + + This is a query parameter used to select responses. + """ + + temperature: Optional[float] = None + """Sampling temperature. This is a query parameter used to select responses.""" + + top_p: Optional[float] = None + """Nucleus sampling parameter. This is a query parameter used to select responses.""" + + users: Optional[List[str]] = None + """List of user identifiers. This is a query parameter used to select responses.""" + + +DataSourceCompletionsSource: TypeAlias = Annotated[ + Union[ + DataSourceCompletionsSourceFileContent, DataSourceCompletionsSourceFileID, DataSourceCompletionsSourceResponses + ], + PropertyInfo(discriminator="type"), +] + + +class DataSourceCompletionsInputMessagesTemplateTemplateChatMessage(BaseModel): + content: str + """The content of the message.""" + + role: str + """The role of the message (e.g. "system", "assistant", "user").""" + + +class DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ + str, ResponseInputText, DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContentOutputText +] + + +class DataSourceCompletionsInputMessagesTemplateTemplateEvalItem(BaseModel): + content: DataSourceCompletionsInputMessagesTemplateTemplateEvalItemContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +DataSourceCompletionsInputMessagesTemplateTemplate: TypeAlias = Union[ + DataSourceCompletionsInputMessagesTemplateTemplateChatMessage, + DataSourceCompletionsInputMessagesTemplateTemplateEvalItem, +] + + +class DataSourceCompletionsInputMessagesTemplate(BaseModel): + template: List[DataSourceCompletionsInputMessagesTemplateTemplate] + """A list of chat messages forming the prompt or context. + + May include variable references to the "item" namespace, ie {{item.name}}. + """ + + type: Literal["template"] + """The type of input messages. Always `template`.""" + + +class DataSourceCompletionsInputMessagesItemReference(BaseModel): + item_reference: str + """A reference to a variable in the "item" namespace. Ie, "item.name" """ + + type: Literal["item_reference"] + """The type of input messages. Always `item_reference`.""" + + +DataSourceCompletionsInputMessages: TypeAlias = Annotated[ + Union[DataSourceCompletionsInputMessagesTemplate, DataSourceCompletionsInputMessagesItemReference], + PropertyInfo(discriminator="type"), +] + + +class DataSourceCompletionsSamplingParams(BaseModel): + max_completion_tokens: Optional[int] = None + """The maximum number of tokens in the generated output.""" + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + +class DataSourceCompletions(BaseModel): + source: DataSourceCompletionsSource + """A EvalResponsesSource object describing a run data source configuration.""" + + type: Literal["completions"] + """The type of run data source. Always `completions`.""" + + input_messages: Optional[DataSourceCompletionsInputMessages] = None + + model: Optional[str] = None + """The name of the model to use for generating completions (e.g. "o3-mini").""" + + sampling_params: Optional[DataSourceCompletionsSamplingParams] = None + DataSource: TypeAlias = Annotated[ - Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource], PropertyInfo(discriminator="type") + Union[CreateEvalJSONLRunDataSource, CreateEvalCompletionsRunDataSource, DataSourceCompletions], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/image.py b/src/openai/types/image.py index f48aa2c702..ecaef3fd58 100644 --- a/src/openai/types/image.py +++ b/src/openai/types/image.py @@ -9,16 +9,18 @@ class Image(BaseModel): b64_json: Optional[str] = None - """ - The base64-encoded JSON of the generated image, if `response_format` is - `b64_json`. + """The base64-encoded JSON of the generated image. + + Default value for `gpt-image-1`, and only present if `response_format` is set to + `b64_json` for `dall-e-2` and `dall-e-3`. """ revised_prompt: Optional[str] = None - """ - The prompt that was used to generate the image, if there was any revision to the - prompt. - """ + """For `dall-e-3` only, the revised prompt that was used to generate the image.""" url: Optional[str] = None - """The URL of the generated image, if `response_format` is `url` (default).""" + """ + When using `dall-e-2` or `dall-e-3`, the URL of the generated image if + `response_format` is set to `url` (default value). Unsupported for + `gpt-image-1`. + """ diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py index d20f672912..d10b74b2c2 100644 --- a/src/openai/types/image_create_variation_params.py +++ b/src/openai/types/image_create_variation_params.py @@ -25,10 +25,7 @@ class ImageCreateVariationParams(TypedDict, total=False): """ n: Optional[int] - """The number of images to generate. - - Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. - """ + """The number of images to generate. Must be between 1 and 10.""" response_format: Optional[Literal["url", "b64_json"]] """The format in which the generated images are returned. diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 1cb10611f3..f01a12c1b0 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes @@ -12,46 +12,61 @@ class ImageEditParams(TypedDict, total=False): - image: Required[FileTypes] - """The image to edit. + image: Required[Union[FileTypes, List[FileTypes]]] + """The image(s) to edit. - Must be a valid PNG file, less than 4MB, and square. If mask is not provided, - image must have transparency, which will be used as the mask. + Must be a supported image file or an array of images. For `gpt-image-1`, each + image should be a `png`, `webp`, or `jpg` file less than 25MB. For `dall-e-2`, + you can only provide one image, and it should be a square `png` file less than + 4MB. """ prompt: Required[str] """A text description of the desired image(s). - The maximum length is 1000 characters. + The maximum length is 1000 characters for `dall-e-2`, and 32000 characters for + `gpt-image-1`. """ mask: FileTypes """An additional image whose fully transparent areas (e.g. - where alpha is zero) indicate where `image` should be edited. Must be a valid - PNG file, less than 4MB, and have the same dimensions as `image`. + where alpha is zero) indicate where `image` should be edited. If there are + multiple images provided, the mask will be applied on the first image. Must be a + valid PNG file, less than 4MB, and have the same dimensions as `image`. """ model: Union[str, ImageModel, None] """The model to use for image generation. - Only `dall-e-2` is supported at this time. + Only `dall-e-2` and `gpt-image-1` are supported. Defaults to `dall-e-2` unless a + parameter specific to `gpt-image-1` is used. """ n: Optional[int] """The number of images to generate. Must be between 1 and 10.""" + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] + """The quality of the image that will be generated. + + `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only + supports `standard` quality. Defaults to `auto`. + """ + response_format: Optional[Literal["url", "b64_json"]] """The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the - image has been generated. + image has been generated. This parameter is only supported for `dall-e-2`, as + `gpt-image-1` will always return base64-encoded images. """ size: Optional[Literal["256x256", "512x512", "1024x1024"]] """The size of the generated images. - Must be one of `256x256`, `512x512`, or `1024x1024`. + Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or + `auto` (default value) for `gpt-image-1`, and one of `256x256`, `512x512`, or + `1024x1024` for `dall-e-2`. """ user: str diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index c88c45f518..8fc10220dc 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -14,12 +14,33 @@ class ImageGenerateParams(TypedDict, total=False): prompt: Required[str] """A text description of the desired image(s). - The maximum length is 1000 characters for `dall-e-2` and 4000 characters for - `dall-e-3`. + The maximum length is 32000 characters for `gpt-image-1`, 1000 characters for + `dall-e-2` and 4000 characters for `dall-e-3`. + """ + + background: Optional[Literal["transparent", "opaque", "auto"]] + """Allows to set transparency for the background of the generated image(s). + + This parameter is only supported for `gpt-image-1`. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. """ model: Union[str, ImageModel, None] - """The model to use for image generation.""" + """The model to use for image generation. + + One of `dall-e-2`, `dall-e-3`, or `gpt-image-1`. Defaults to `dall-e-2` unless a + parameter specific to `gpt-image-1` is used. + """ + + moderation: Optional[Literal["low", "auto"]] + """Control the content-moderation level for images generated by `gpt-image-1`. + + Must be either `low` for less restrictive filtering or `auto` (default value). + """ n: Optional[int] """The number of images to generate. @@ -27,34 +48,57 @@ class ImageGenerateParams(TypedDict, total=False): Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. """ - quality: Literal["standard", "hd"] + output_compression: Optional[int] + """The compression level (0-100%) for the generated images. + + This parameter is only supported for `gpt-image-1` with the `webp` or `jpeg` + output formats, and defaults to 100. + """ + + output_format: Optional[Literal["png", "jpeg", "webp"]] + """The format in which the generated images are returned. + + This parameter is only supported for `gpt-image-1`. Must be one of `png`, + `jpeg`, or `webp`. + """ + + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] """The quality of the image that will be generated. - `hd` creates images with finer details and greater consistency across the image. - This param is only supported for `dall-e-3`. + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. """ response_format: Optional[Literal["url", "b64_json"]] - """The format in which the generated images are returned. + """The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the - image has been generated. + image has been generated. This parameter isn't supported for `gpt-image-1` which + will always return base64-encoded images. """ - size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] """The size of the generated images. - Must be one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. Must be one - of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3` models. + Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or + `auto` (default value) for `gpt-image-1`, one of `256x256`, `512x512`, or + `1024x1024` for `dall-e-2`, and one of `1024x1024`, `1792x1024`, or `1024x1792` + for `dall-e-3`. """ style: Optional[Literal["vivid", "natural"]] """The style of the generated images. - Must be one of `vivid` or `natural`. Vivid causes the model to lean towards - generating hyper-real and dramatic images. Natural causes the model to produce - more natural, less hyper-real looking images. This param is only supported for - `dall-e-3`. + This parameter is only supported for `dall-e-3`. Must be one of `vivid` or + `natural`. Vivid causes the model to lean towards generating hyper-real and + dramatic images. Natural causes the model to produce more natural, less + hyper-real looking images. """ user: str diff --git a/src/openai/types/image_model.py b/src/openai/types/image_model.py index 1672369bea..7fed69ed82 100644 --- a/src/openai/types/image_model.py +++ b/src/openai/types/image_model.py @@ -4,4 +4,4 @@ __all__ = ["ImageModel"] -ImageModel: TypeAlias = Literal["dall-e-2", "dall-e-3"] +ImageModel: TypeAlias = Literal["dall-e-2", "dall-e-3", "gpt-image-1"] diff --git a/src/openai/types/images_response.py b/src/openai/types/images_response.py index 7cee813184..df454afa4d 100644 --- a/src/openai/types/images_response.py +++ b/src/openai/types/images_response.py @@ -1,14 +1,41 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List +from typing import List, Optional from .image import Image from .._models import BaseModel -__all__ = ["ImagesResponse"] +__all__ = ["ImagesResponse", "Usage", "UsageInputTokensDetails"] + + +class UsageInputTokensDetails(BaseModel): + image_tokens: int + """The number of image tokens in the input prompt.""" + + text_tokens: int + """The number of text tokens in the input prompt.""" + + +class Usage(BaseModel): + input_tokens: int + """The number of tokens (images and text) in the input prompt.""" + + input_tokens_details: UsageInputTokensDetails + """The input tokens detailed information for the image generation.""" + + output_tokens: int + """The number of image tokens in the output image.""" + + total_tokens: int + """The total number of tokens (images and text) used for the image generation.""" class ImagesResponse(BaseModel): created: int + """The Unix timestamp (in seconds) of when the image was created.""" + + data: Optional[List[Image]] = None + """The list of generated images.""" - data: List[Image] + usage: Optional[Usage] = None + """For `gpt-image-1` only, the token usage information for the image generation.""" diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 4f07a3d097..22fd2a0802 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -22,6 +22,7 @@ from .web_search_tool import WebSearchTool as WebSearchTool from .file_search_tool import FileSearchTool as FileSearchTool from .tool_choice_types import ToolChoiceTypes as ToolChoiceTypes +from .easy_input_message import EasyInputMessage as EasyInputMessage from .response_item_list import ResponseItemList as ResponseItemList from .computer_tool_param import ComputerToolParam as ComputerToolParam from .function_tool_param import FunctionToolParam as FunctionToolParam @@ -117,6 +118,12 @@ from .response_input_message_content_list_param import ( ResponseInputMessageContentListParam as ResponseInputMessageContentListParam, ) +from .response_reasoning_summary_part_done_event import ( + ResponseReasoningSummaryPartDoneEvent as ResponseReasoningSummaryPartDoneEvent, +) +from .response_reasoning_summary_text_done_event import ( + ResponseReasoningSummaryTextDoneEvent as ResponseReasoningSummaryTextDoneEvent, +) from .response_web_search_call_in_progress_event import ( ResponseWebSearchCallInProgressEvent as ResponseWebSearchCallInProgressEvent, ) @@ -126,6 +133,12 @@ from .response_function_call_arguments_done_event import ( ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, ) +from .response_reasoning_summary_part_added_event import ( + ResponseReasoningSummaryPartAddedEvent as ResponseReasoningSummaryPartAddedEvent, +) +from .response_reasoning_summary_text_delta_event import ( + ResponseReasoningSummaryTextDeltaEvent as ResponseReasoningSummaryTextDeltaEvent, +) from .response_function_call_arguments_delta_event import ( ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, ) diff --git a/src/openai/types/responses/easy_input_message.py b/src/openai/types/responses/easy_input_message.py new file mode 100644 index 0000000000..4ed0194f9f --- /dev/null +++ b/src/openai/types/responses/easy_input_message.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_input_message_content_list import ResponseInputMessageContentList + +__all__ = ["EasyInputMessage"] + + +class EasyInputMessage(BaseModel): + content: Union[str, ResponseInputMessageContentList] + """ + Text, image, or audio input to the model, used to generate a response. Can also + contain previous assistant responses. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" diff --git a/src/openai/types/responses/response_reasoning_summary_part_added_event.py b/src/openai/types/responses/response_reasoning_summary_part_added_event.py new file mode 100644 index 0000000000..fd11520170 --- /dev/null +++ b/src/openai/types/responses/response_reasoning_summary_part_added_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningSummaryPartAddedEvent", "Part"] + + +class Part(BaseModel): + text: str + """The text of the summary part.""" + + type: Literal["summary_text"] + """The type of the summary part. Always `summary_text`.""" + + +class ResponseReasoningSummaryPartAddedEvent(BaseModel): + item_id: str + """The ID of the item this summary part is associated with.""" + + output_index: int + """The index of the output item this summary part is associated with.""" + + part: Part + """The summary part that was added.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_part.added"] + """The type of the event. Always `response.reasoning_summary_part.added`.""" diff --git a/src/openai/types/responses/response_reasoning_summary_part_done_event.py b/src/openai/types/responses/response_reasoning_summary_part_done_event.py new file mode 100644 index 0000000000..7f30189a49 --- /dev/null +++ b/src/openai/types/responses/response_reasoning_summary_part_done_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningSummaryPartDoneEvent", "Part"] + + +class Part(BaseModel): + text: str + """The text of the summary part.""" + + type: Literal["summary_text"] + """The type of the summary part. Always `summary_text`.""" + + +class ResponseReasoningSummaryPartDoneEvent(BaseModel): + item_id: str + """The ID of the item this summary part is associated with.""" + + output_index: int + """The index of the output item this summary part is associated with.""" + + part: Part + """The completed summary part.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_part.done"] + """The type of the event. Always `response.reasoning_summary_part.done`.""" diff --git a/src/openai/types/responses/response_reasoning_summary_text_delta_event.py b/src/openai/types/responses/response_reasoning_summary_text_delta_event.py new file mode 100644 index 0000000000..6d0cbd8265 --- /dev/null +++ b/src/openai/types/responses/response_reasoning_summary_text_delta_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningSummaryTextDeltaEvent"] + + +class ResponseReasoningSummaryTextDeltaEvent(BaseModel): + delta: str + """The text delta that was added to the summary.""" + + item_id: str + """The ID of the item this summary text delta is associated with.""" + + output_index: int + """The index of the output item this summary text delta is associated with.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_text.delta"] + """The type of the event. Always `response.reasoning_summary_text.delta`.""" diff --git a/src/openai/types/responses/response_reasoning_summary_text_done_event.py b/src/openai/types/responses/response_reasoning_summary_text_done_event.py new file mode 100644 index 0000000000..15b894c75b --- /dev/null +++ b/src/openai/types/responses/response_reasoning_summary_text_done_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningSummaryTextDoneEvent"] + + +class ResponseReasoningSummaryTextDoneEvent(BaseModel): + item_id: str + """The ID of the item this summary text is associated with.""" + + output_index: int + """The index of the output item this summary text is associated with.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + text: str + """The full text of the completed reasoning summary.""" + + type: Literal["response.reasoning_summary_text.done"] + """The type of the event. Always `response.reasoning_summary_text.done`.""" diff --git a/src/openai/types/responses/response_stream_event.py b/src/openai/types/responses/response_stream_event.py index 446863b175..07c18bd217 100644 --- a/src/openai/types/responses/response_stream_event.py +++ b/src/openai/types/responses/response_stream_event.py @@ -27,9 +27,13 @@ from .response_web_search_call_searching_event import ResponseWebSearchCallSearchingEvent from .response_file_search_call_completed_event import ResponseFileSearchCallCompletedEvent from .response_file_search_call_searching_event import ResponseFileSearchCallSearchingEvent +from .response_reasoning_summary_part_done_event import ResponseReasoningSummaryPartDoneEvent +from .response_reasoning_summary_text_done_event import ResponseReasoningSummaryTextDoneEvent from .response_web_search_call_in_progress_event import ResponseWebSearchCallInProgressEvent from .response_file_search_call_in_progress_event import ResponseFileSearchCallInProgressEvent from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .response_reasoning_summary_part_added_event import ResponseReasoningSummaryPartAddedEvent +from .response_reasoning_summary_text_delta_event import ResponseReasoningSummaryTextDeltaEvent from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent from .response_code_interpreter_call_code_done_event import ResponseCodeInterpreterCallCodeDoneEvent from .response_code_interpreter_call_completed_event import ResponseCodeInterpreterCallCompletedEvent @@ -65,6 +69,10 @@ ResponseIncompleteEvent, ResponseOutputItemAddedEvent, ResponseOutputItemDoneEvent, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, ResponseRefusalDeltaEvent, ResponseRefusalDoneEvent, ResponseTextAnnotationDeltaEvent, diff --git a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py index d40466919a..6aa0b867d9 100644 --- a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py +++ b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py @@ -117,19 +117,19 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: fine_tuned_model_checkpoint="", ) - @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize def test_method_delete(self, client: OpenAI) -> None: permission = client.fine_tuning.checkpoints.permissions.delete( - "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + permission_id="cp_zc4Q7MP6XxulcVzj4MZdwsAB", + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", ) assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) - @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: response = client.fine_tuning.checkpoints.permissions.with_raw_response.delete( - "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + permission_id="cp_zc4Q7MP6XxulcVzj4MZdwsAB", + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", ) assert response.is_closed is True @@ -137,11 +137,11 @@ def test_raw_response_delete(self, client: OpenAI) -> None: permission = response.parse() assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) - @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize def test_streaming_response_delete(self, client: OpenAI) -> None: with client.fine_tuning.checkpoints.permissions.with_streaming_response.delete( - "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + permission_id="cp_zc4Q7MP6XxulcVzj4MZdwsAB", + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -151,14 +151,20 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize def test_path_params_delete(self, client: OpenAI) -> None: with pytest.raises( ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" ): client.fine_tuning.checkpoints.permissions.with_raw_response.delete( - "", + permission_id="cp_zc4Q7MP6XxulcVzj4MZdwsAB", + fine_tuned_model_checkpoint="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `permission_id` but received ''"): + client.fine_tuning.checkpoints.permissions.with_raw_response.delete( + permission_id="", + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", ) @@ -260,19 +266,19 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: fine_tuned_model_checkpoint="", ) - @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize async def test_method_delete(self, async_client: AsyncOpenAI) -> None: permission = await async_client.fine_tuning.checkpoints.permissions.delete( - "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + permission_id="cp_zc4Q7MP6XxulcVzj4MZdwsAB", + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", ) assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) - @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: response = await async_client.fine_tuning.checkpoints.permissions.with_raw_response.delete( - "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + permission_id="cp_zc4Q7MP6XxulcVzj4MZdwsAB", + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", ) assert response.is_closed is True @@ -280,11 +286,11 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: permission = response.parse() assert_matches_type(PermissionDeleteResponse, permission, path=["response"]) - @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: async with async_client.fine_tuning.checkpoints.permissions.with_streaming_response.delete( - "ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", + permission_id="cp_zc4Q7MP6XxulcVzj4MZdwsAB", + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -294,12 +300,18 @@ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="OpenAPI spec is slightly incorrect") @parametrize async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises( ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" ): await async_client.fine_tuning.checkpoints.permissions.with_raw_response.delete( - "", + permission_id="cp_zc4Q7MP6XxulcVzj4MZdwsAB", + fine_tuned_model_checkpoint="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `permission_id` but received ''"): + await async_client.fine_tuning.checkpoints.permissions.with_raw_response.delete( + permission_id="", + fine_tuned_model_checkpoint="ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd", ) diff --git a/tests/api_resources/test_evals.py b/tests/api_resources/test_evals.py index 8d03513b32..4ae2c597dd 100644 --- a/tests/api_resources/test_evals.py +++ b/tests/api_resources/test_evals.py @@ -74,7 +74,6 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: ], metadata={"foo": "string"}, name="name", - share_with_openai=True, ) assert_matches_type(EvalCreateResponse, eval, path=["response"]) @@ -350,7 +349,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> ], metadata={"foo": "string"}, name="name", - share_with_openai=True, ) assert_matches_type(EvalCreateResponse, eval, path=["response"]) diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 0a88f2ebcf..7997e9f5a1 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -76,6 +76,7 @@ def test_method_edit_with_all_params(self, client: OpenAI) -> None: mask=b"raw file contents", model="string", n=1, + quality="high", response_format="url", size="1024x1024", user="user-1234", @@ -119,9 +120,13 @@ def test_method_generate(self, client: OpenAI) -> None: def test_method_generate_with_all_params(self, client: OpenAI) -> None: image = client.images.generate( prompt="A cute baby sea otter", + background="transparent", model="string", + moderation="low", n=1, - quality="standard", + output_compression=100, + output_format="png", + quality="medium", response_format="url", size="1024x1024", style="vivid", @@ -216,6 +221,7 @@ async def test_method_edit_with_all_params(self, async_client: AsyncOpenAI) -> N mask=b"raw file contents", model="string", n=1, + quality="high", response_format="url", size="1024x1024", user="user-1234", @@ -259,9 +265,13 @@ async def test_method_generate(self, async_client: AsyncOpenAI) -> None: async def test_method_generate_with_all_params(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.generate( prompt="A cute baby sea otter", + background="transparent", model="string", + moderation="low", n=1, - quality="standard", + output_compression=100, + output_format="png", + quality="medium", response_format="url", size="1024x1024", style="vivid", From 8e1a1cd60d990361b934f922fd7d176f2ae0a63c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:31:09 +0000 Subject: [PATCH 895/914] release: 1.76.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 25 +++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cb464946f0..df3aaa16a7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.75.0" + ".": "1.76.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index fb077b91c3..73d8f2bf6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 1.76.0 (2025-04-23) + +Full Changelog: [v1.75.0...v1.76.0](https://github.com/openai/openai-python/compare/v1.75.0...v1.76.0) + +### Features + +* **api:** adding new image model support ([74d7692](https://github.com/openai/openai-python/commit/74d7692e94c9dca96db8793809d75631c22dbb87)) + + +### Bug Fixes + +* **pydantic v1:** more robust `ModelField.annotation` check ([#2163](https://github.com/openai/openai-python/issues/2163)) ([7351b12](https://github.com/openai/openai-python/commit/7351b12bc981f56632b92342d9ef26f6fb28d540)) +* **pydantic v1:** more robust ModelField.annotation check ([eba7856](https://github.com/openai/openai-python/commit/eba7856db55afb8cb44376a0248587549f7bc65f)) + + +### Chores + +* **ci:** add timeout thresholds for CI jobs ([0997211](https://github.com/openai/openai-python/commit/09972119df5dd4c7c8db137c721364787e22d4c6)) +* **internal:** fix list file params ([da2113c](https://github.com/openai/openai-python/commit/da2113c60b50b4438459325fcd38d55df3f63d8e)) +* **internal:** import reformatting ([b425fb9](https://github.com/openai/openai-python/commit/b425fb906f62550c3669b09b9d8575f3d4d8496b)) +* **internal:** minor formatting changes ([aed1d76](https://github.com/openai/openai-python/commit/aed1d767898324cf90328db329e04e89a77579c3)) +* **internal:** refactor retries to not use recursion ([8cb8cfa](https://github.com/openai/openai-python/commit/8cb8cfab48a4fed70a756ce50036e7e56e1f9f87)) +* **internal:** update models test ([870ad4e](https://github.com/openai/openai-python/commit/870ad4ed3a284d75f44b825503750129284c7906)) +* update completion parse signature ([a44016c](https://github.com/openai/openai-python/commit/a44016c64cdefe404e97592808ed3c25411ab27b)) + ## 1.75.0 (2025-04-16) Full Changelog: [v1.74.1...v1.75.0](https://github.com/openai/openai-python/compare/v1.74.1...v1.75.0) diff --git a/pyproject.toml b/pyproject.toml index b5648e9e51..947e082f78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.75.0" +version = "1.76.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 8eab2d7416..ea6b974272 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.75.0" # x-release-please-version +__version__ = "1.76.0" # x-release-please-version From c1ceebbd62400d66291173763f546a8a98f201ad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 19:07:40 +0000 Subject: [PATCH 896/914] chore(ci): run on more branches and use depot runners --- .github/workflows/ci.yml | 18 +++++++++--------- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d148b34a9e..bbf8a2c65a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,18 @@ name: CI on: push: - branches: - - main - pull_request: - branches: - - main - - next + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' jobs: lint: timeout-minutes: 10 name: lint - runs-on: ubuntu-latest + runs-on: depot-ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -33,7 +33,7 @@ jobs: test: timeout-minutes: 10 name: test - runs-on: ubuntu-latest + runs-on: depot-ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -54,7 +54,7 @@ jobs: examples: timeout-minutes: 10 name: examples - runs-on: ubuntu-latest + runs-on: depot-ubuntu-24.04 if: github.repository == 'openai/openai-python' steps: diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 32bd6929e2..d669229973 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -7,7 +7,7 @@ on: jobs: publish: name: publish - runs-on: ubuntu-latest + runs-on: depot-ubuntu-24.04 environment: publish steps: diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index e078964a6f..be17b9c07f 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -8,7 +8,7 @@ on: jobs: release_doctor: name: release doctor - runs-on: ubuntu-latest + runs-on: depot-ubuntu-24.04 environment: publish if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') From c9cedd8a47290ff2c95c54c1528fbc7202f6b523 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:00:18 +0000 Subject: [PATCH 897/914] chore(ci): only use depot for staging repos --- .github/workflows/ci.yml | 6 +++--- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bbf8a2c65a..e1e21f3fae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: lint: timeout-minutes: 10 name: lint - runs-on: depot-ubuntu-24.04 + runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@v4 @@ -33,7 +33,7 @@ jobs: test: timeout-minutes: 10 name: test - runs-on: depot-ubuntu-24.04 + runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@v4 @@ -54,7 +54,7 @@ jobs: examples: timeout-minutes: 10 name: examples - runs-on: depot-ubuntu-24.04 + runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.repository == 'openai/openai-python' steps: diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index d669229973..32bd6929e2 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -7,7 +7,7 @@ on: jobs: publish: name: publish - runs-on: depot-ubuntu-24.04 + runs-on: ubuntu-latest environment: publish steps: diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index be17b9c07f..e078964a6f 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -8,7 +8,7 @@ on: jobs: release_doctor: name: release doctor - runs-on: depot-ubuntu-24.04 + runs-on: ubuntu-latest environment: publish if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') From 761be76cb7512de232b1892f8915cd022bee040a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 22:08:18 +0000 Subject: [PATCH 898/914] chore: broadly detect json family of content-type headers --- src/openai/_legacy_response.py | 2 +- src/openai/_response.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 8880e5f104..cfabaa2fc2 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -304,7 +304,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: # split is required to handle cases where additional information is included # in the response, e.g. application/json; charset=utf-8 content_type, *_ = response.headers.get("content-type", "*").split(";") - if content_type != "application/json": + if not content_type.endswith("json"): if is_basemodel(cast_to): try: data = response.json() diff --git a/src/openai/_response.py b/src/openai/_response.py index 95e94e6537..350da38dd4 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -237,7 +237,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: # split is required to handle cases where additional information is included # in the response, e.g. application/json; charset=utf-8 content_type, *_ = response.headers.get("content-type", "*").split(";") - if content_type != "application/json": + if not content_type.endswith("json"): if is_basemodel(cast_to): try: data = response.json() From b75f4093026265d7a3f0c38998a3360f03bf44f4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 05:03:55 +0000 Subject: [PATCH 899/914] release: 1.76.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 10 ++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index df3aaa16a7..0c3ec30cf9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.76.0" + ".": "1.76.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 73d8f2bf6e..1c5b507e43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.76.1 (2025-04-29) + +Full Changelog: [v1.76.0...v1.76.1](https://github.com/openai/openai-python/compare/v1.76.0...v1.76.1) + +### Chores + +* broadly detect json family of content-type headers ([b4b1b08](https://github.com/openai/openai-python/commit/b4b1b086b512eecc0ada7fc1efa45eb506982f13)) +* **ci:** only use depot for staging repos ([35312d8](https://github.com/openai/openai-python/commit/35312d80e6bbc1a61d06ad253af9a713b5ef040c)) +* **ci:** run on more branches and use depot runners ([a6a45d4](https://github.com/openai/openai-python/commit/a6a45d4af8a4d904b37573a9b223d56106b4887d)) + ## 1.76.0 (2025-04-23) Full Changelog: [v1.75.0...v1.76.0](https://github.com/openai/openai-python/compare/v1.75.0...v1.76.0) diff --git a/pyproject.toml b/pyproject.toml index 947e082f78..570e59ec67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.76.0" +version = "1.76.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ea6b974272..77a1b26c42 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.76.0" # x-release-please-version +__version__ = "1.76.1" # x-release-please-version From a6460677e956762d1b9cdb59cdc5e161cd5ea370 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 19:58:55 +0000 Subject: [PATCH 900/914] chore(api): API spec cleanup --- src/openai/lib/streaming/responses/_events.py | 8 ++++++++ src/openai/resources/beta/threads/threads.py | 16 ++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/openai/lib/streaming/responses/_events.py b/src/openai/lib/streaming/responses/_events.py index fe17edf649..0cdc5992ee 100644 --- a/src/openai/lib/streaming/responses/_events.py +++ b/src/openai/lib/streaming/responses/_events.py @@ -32,7 +32,11 @@ ResponseFileSearchCallSearchingEvent, ResponseWebSearchCallInProgressEvent, ResponseFileSearchCallInProgressEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDoneEvent, ResponseFunctionCallArgumentsDoneEvent, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryTextDeltaEvent, ResponseFunctionCallArgumentsDeltaEvent as RawResponseFunctionCallArgumentsDeltaEvent, ResponseCodeInterpreterCallCodeDoneEvent, ResponseCodeInterpreterCallCodeDeltaEvent, @@ -101,6 +105,10 @@ class ResponseCompletedEvent(RawResponseCompletedEvent, GenericModel, Generic[Te ResponseWebSearchCallCompletedEvent, ResponseWebSearchCallInProgressEvent, ResponseWebSearchCallSearchingEvent, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 22dc5fe0ea..13d8cb6411 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -741,7 +741,7 @@ def create_and_run_poll( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, @@ -797,7 +797,7 @@ def create_and_run_stream( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -826,7 +826,7 @@ def create_and_run_stream( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AssistantEventHandlerT, @@ -855,7 +855,7 @@ def create_and_run_stream( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AssistantEventHandlerT | None = None, @@ -1590,7 +1590,7 @@ async def create_and_run_poll( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, poll_interval_ms: int | NotGiven = NOT_GIVEN, @@ -1648,7 +1648,7 @@ def create_and_run_stream( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1677,7 +1677,7 @@ def create_and_run_stream( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AsyncAssistantEventHandlerT, @@ -1706,7 +1706,7 @@ def create_and_run_stream( thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[thread_create_and_run_params.Tool]] | NotGiven = NOT_GIVEN, + tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, event_handler: AsyncAssistantEventHandlerT | None = None, From fad098ffad7982a5150306a3d17f51ffef574f2e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 19:59:25 +0000 Subject: [PATCH 901/914] release: 1.76.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0c3ec30cf9..8bcd8a5b4f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.76.1" + ".": "1.76.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c5b507e43..bc85128f6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.76.2 (2025-04-29) + +Full Changelog: [v1.76.1...v1.76.2](https://github.com/openai/openai-python/compare/v1.76.1...v1.76.2) + +### Chores + +* **api:** API spec cleanup ([0a4d3e2](https://github.com/openai/openai-python/commit/0a4d3e2b495d22dd42ce1773b870554c64f9b3b2)) + ## 1.76.1 (2025-04-29) Full Changelog: [v1.76.0...v1.76.1](https://github.com/openai/openai-python/compare/v1.76.0...v1.76.1) diff --git a/pyproject.toml b/pyproject.toml index 570e59ec67..2c3c3eaf3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.76.1" +version = "1.76.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 77a1b26c42..ef1e3fe526 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.76.1" # x-release-please-version +__version__ = "1.76.2" # x-release-please-version From b3f0daf3dbc344998b09762615a59d80621d7921 Mon Sep 17 00:00:00 2001 From: Pon Pongwachirin <138108569+maesta7@users.noreply.github.com> Date: Wed, 30 Apr 2025 22:46:30 +0700 Subject: [PATCH 902/914] fix(parsing): handle whitespace only strings (#2007) * fix: add a check to handle empty or newline-only strings before calling `from_json` * style: adjust comment format for better readability Co-authored-by: Robert Craigie --------- Co-authored-by: SenorSpes <138108569+senorNox@users.noreply.github.com> Co-authored-by: Robert Craigie --- src/openai/lib/streaming/chat/_completions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index f147696cca..6177ffbed2 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -438,6 +438,8 @@ def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionS choice_snapshot.message.content and not choice_snapshot.message.refusal and is_given(self._rich_response_format) + # partial parsing fails on white-space + and choice_snapshot.message.content.strip() ): choice_snapshot.message.parsed = from_json( bytes(choice_snapshot.message.content, "utf-8"), From 4fc52529439c05ace100e05bf07f5e3d23abbc5b Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 30 Apr 2025 11:47:27 -0400 Subject: [PATCH 903/914] chore: only strip leading whitespace --- src/openai/lib/streaming/chat/_completions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index 6177ffbed2..a7b70c32d3 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -439,7 +439,7 @@ def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionS and not choice_snapshot.message.refusal and is_given(self._rich_response_format) # partial parsing fails on white-space - and choice_snapshot.message.content.strip() + and choice_snapshot.message.content.lstrip() ): choice_snapshot.message.parsed = from_json( bytes(choice_snapshot.message.content, "utf-8"), From b8a3720ed6157dff5100c9a36f8d51fe47a2994c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 19:09:24 +0000 Subject: [PATCH 904/914] feat(api): add image sizes, reasoning encryption --- .stats.yml | 6 +-- src/openai/resources/audio/speech.py | 4 +- src/openai/resources/images.py | 48 +++++++++++++++---- src/openai/resources/responses/responses.py | 30 ++++++++++++ .../types/audio/speech_create_params.py | 3 +- src/openai/types/image_edit_params.py | 24 +++++++--- src/openai/types/responses/computer_tool.py | 6 +-- .../types/responses/computer_tool_param.py | 6 +-- .../types/responses/file_search_tool.py | 12 ++--- .../types/responses/file_search_tool_param.py | 14 +++--- src/openai/types/responses/function_tool.py | 4 +- .../types/responses/function_tool_param.py | 4 +- .../types/responses/response_create_params.py | 5 ++ .../types/responses/response_includable.py | 5 +- .../responses/response_input_file_param.py | 3 +- .../types/responses/response_input_image.py | 2 +- .../responses/response_input_image_param.py | 2 +- .../responses/response_input_item_param.py | 18 +++---- .../types/responses/response_input_param.py | 18 +++---- .../responses/response_reasoning_item.py | 6 +++ .../response_reasoning_item_param.py | 8 +++- src/openai/types/responses/tool.py | 2 +- src/openai/types/responses/tool_param.py | 2 +- src/openai/types/responses/web_search_tool.py | 13 ++--- .../types/responses/web_search_tool_param.py | 21 ++++---- tests/api_resources/test_images.py | 2 + 26 files changed, 182 insertions(+), 86 deletions(-) diff --git a/.stats.yml b/.stats.yml index d92408173b..0c8278866d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-8b68ae6b807dca92e914da1dd9e835a20f69b075e79102a264367fd7fddddb33.yml -openapi_spec_hash: b6ade5b1a6327339e6669e1134de2d03 -config_hash: b597cd9a31e9e5ec709e2eefb4c54122 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0ee6b36cf3cc278cef4199a6aec5f7d530a6c1f17a74830037e96d50ca1edc50.yml +openapi_spec_hash: e8ec5f46bc0655b34f292422d58a60f6 +config_hash: d9b6b6e6bc85744663e300eebc482067 diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index fad18dcdf5..a195d7135e 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -85,7 +85,7 @@ def create( `wav`, and `pcm`. speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is - the default. + the default. Does not work with `gpt-4o-mini-tts`. extra_headers: Send extra headers @@ -176,7 +176,7 @@ async def create( `wav`, and `pcm`. speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is - the default. + the default. Does not work with `gpt-4o-mini-tts`. extra_headers: Send extra headers diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index e59d0ce35c..524bebacae 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -119,12 +119,14 @@ def edit( *, image: Union[FileTypes, List[FileTypes]], prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, mask: FileTypes | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -139,14 +141,25 @@ def edit( This endpoint only supports `gpt-image-1` and `dall-e-2`. Args: - image: The image(s) to edit. Must be a supported image file or an array of images. For - `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than - 25MB. For `dall-e-2`, you can only provide one image, and it should be a square - `png` file less than 4MB. + image: The image(s) to edit. Must be a supported image file or an array of images. + + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 25MB. You can provide up to 16 images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, the mask will be applied on the first image. Must be a valid PNG file, less than @@ -187,6 +200,7 @@ def edit( { "image": image, "prompt": prompt, + "background": background, "mask": mask, "model": model, "n": n, @@ -429,12 +443,14 @@ async def edit( *, image: Union[FileTypes, List[FileTypes]], prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, mask: FileTypes | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -449,14 +465,25 @@ async def edit( This endpoint only supports `gpt-image-1` and `dall-e-2`. Args: - image: The image(s) to edit. Must be a supported image file or an array of images. For - `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than - 25MB. For `dall-e-2`, you can only provide one image, and it should be a square - `png` file less than 4MB. + image: The image(s) to edit. Must be a supported image file or an array of images. + + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 25MB. You can provide up to 16 images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, the mask will be applied on the first image. Must be a valid PNG file, less than @@ -497,6 +524,7 @@ async def edit( { "image": image, "prompt": prompt, + "background": background, "mask": mask, "model": model, "n": n, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 4a0687f9f3..a905bc34b1 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -140,6 +140,11 @@ def create( - `message.input_image.image_url`: Include image urls from the input message. - `computer_call_output.output.image_url`: Include image urls from the computer call output. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). instructions: Inserts a system (or developer) message as the first item in the model's context. @@ -331,6 +336,11 @@ def create( - `message.input_image.image_url`: Include image urls from the input message. - `computer_call_output.output.image_url`: Include image urls from the computer call output. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). instructions: Inserts a system (or developer) message as the first item in the model's context. @@ -515,6 +525,11 @@ def create( - `message.input_image.image_url`: Include image urls from the input message. - `computer_call_output.output.image_url`: Include image urls from the computer call output. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). instructions: Inserts a system (or developer) message as the first item in the model's context. @@ -1013,6 +1028,11 @@ async def create( - `message.input_image.image_url`: Include image urls from the input message. - `computer_call_output.output.image_url`: Include image urls from the computer call output. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). instructions: Inserts a system (or developer) message as the first item in the model's context. @@ -1204,6 +1224,11 @@ async def create( - `message.input_image.image_url`: Include image urls from the input message. - `computer_call_output.output.image_url`: Include image urls from the computer call output. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). instructions: Inserts a system (or developer) message as the first item in the model's context. @@ -1388,6 +1413,11 @@ async def create( - `message.input_image.image_url`: Include image urls from the input message. - `computer_call_output.output.image_url`: Include image urls from the computer call output. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). instructions: Inserts a system (or developer) message as the first item in the model's context. diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index a4fc020532..905ca5c3a8 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -48,5 +48,6 @@ class SpeechCreateParams(TypedDict, total=False): speed: float """The speed of the generated audio. - Select a value from `0.25` to `4.0`. `1.0` is the default. + Select a value from `0.25` to `4.0`. `1.0` is the default. Does not work with + `gpt-4o-mini-tts`. """ diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index f01a12c1b0..6294e8ac19 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -13,12 +13,13 @@ class ImageEditParams(TypedDict, total=False): image: Required[Union[FileTypes, List[FileTypes]]] - """The image(s) to edit. + """The image(s) to edit. Must be a supported image file or an array of images. - Must be a supported image file or an array of images. For `gpt-image-1`, each - image should be a `png`, `webp`, or `jpg` file less than 25MB. For `dall-e-2`, - you can only provide one image, and it should be a square `png` file less than - 4MB. + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 25MB. You can provide up to 16 images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. """ prompt: Required[str] @@ -28,6 +29,17 @@ class ImageEditParams(TypedDict, total=False): `gpt-image-1`. """ + background: Optional[Literal["transparent", "opaque", "auto"]] + """Allows to set transparency for the background of the generated image(s). + + This parameter is only supported for `gpt-image-1`. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + """ + mask: FileTypes """An additional image whose fully transparent areas (e.g. @@ -61,7 +73,7 @@ class ImageEditParams(TypedDict, total=False): `gpt-image-1` will always return base64-encoded images. """ - size: Optional[Literal["256x256", "512x512", "1024x1024"]] + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] """The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or diff --git a/src/openai/types/responses/computer_tool.py b/src/openai/types/responses/computer_tool.py index dffb7af7b7..5b844f5bf4 100644 --- a/src/openai/types/responses/computer_tool.py +++ b/src/openai/types/responses/computer_tool.py @@ -8,13 +8,13 @@ class ComputerTool(BaseModel): - display_height: float + display_height: int """The height of the computer display.""" - display_width: float + display_width: int """The width of the computer display.""" - environment: Literal["mac", "windows", "ubuntu", "browser"] + environment: Literal["windows", "mac", "linux", "ubuntu", "browser"] """The type of computer environment to control.""" type: Literal["computer_use_preview"] diff --git a/src/openai/types/responses/computer_tool_param.py b/src/openai/types/responses/computer_tool_param.py index 6b1072ffd2..06a5c132ec 100644 --- a/src/openai/types/responses/computer_tool_param.py +++ b/src/openai/types/responses/computer_tool_param.py @@ -8,13 +8,13 @@ class ComputerToolParam(TypedDict, total=False): - display_height: Required[float] + display_height: Required[int] """The height of the computer display.""" - display_width: Required[float] + display_width: Required[int] """The width of the computer display.""" - environment: Required[Literal["mac", "windows", "ubuntu", "browser"]] + environment: Required[Literal["windows", "mac", "linux", "ubuntu", "browser"]] """The type of computer environment to control.""" type: Required[Literal["computer_use_preview"]] diff --git a/src/openai/types/responses/file_search_tool.py b/src/openai/types/responses/file_search_tool.py index 683fc533fe..dbdd8cffab 100644 --- a/src/openai/types/responses/file_search_tool.py +++ b/src/openai/types/responses/file_search_tool.py @@ -9,7 +9,7 @@ __all__ = ["FileSearchTool", "Filters", "RankingOptions"] -Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter] +Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter, None] class RankingOptions(BaseModel): @@ -17,10 +17,10 @@ class RankingOptions(BaseModel): """The ranker to use for the file search.""" score_threshold: Optional[float] = None - """ - The score threshold for the file search, a number between 0 and 1. Numbers - closer to 1 will attempt to return only the most relevant results, but may - return fewer results. + """The score threshold for the file search, a number between 0 and 1. + + Numbers closer to 1 will attempt to return only the most relevant results, but + may return fewer results. """ @@ -32,7 +32,7 @@ class FileSearchTool(BaseModel): """The IDs of the vector stores to search.""" filters: Optional[Filters] = None - """A filter to apply based on file attributes.""" + """A filter to apply.""" max_num_results: Optional[int] = None """The maximum number of results to return. diff --git a/src/openai/types/responses/file_search_tool_param.py b/src/openai/types/responses/file_search_tool_param.py index 2d6af8536b..2851fae460 100644 --- a/src/openai/types/responses/file_search_tool_param.py +++ b/src/openai/types/responses/file_search_tool_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..shared_params.compound_filter import CompoundFilter @@ -18,10 +18,10 @@ class RankingOptions(TypedDict, total=False): """The ranker to use for the file search.""" score_threshold: float - """ - The score threshold for the file search, a number between 0 and 1. Numbers - closer to 1 will attempt to return only the most relevant results, but may - return fewer results. + """The score threshold for the file search, a number between 0 and 1. + + Numbers closer to 1 will attempt to return only the most relevant results, but + may return fewer results. """ @@ -32,8 +32,8 @@ class FileSearchToolParam(TypedDict, total=False): vector_store_ids: Required[List[str]] """The IDs of the vector stores to search.""" - filters: Filters - """A filter to apply based on file attributes.""" + filters: Optional[Filters] + """A filter to apply.""" max_num_results: int """The maximum number of results to return. diff --git a/src/openai/types/responses/function_tool.py b/src/openai/types/responses/function_tool.py index 236a2c7c63..d881565356 100644 --- a/src/openai/types/responses/function_tool.py +++ b/src/openai/types/responses/function_tool.py @@ -12,10 +12,10 @@ class FunctionTool(BaseModel): name: str """The name of the function to call.""" - parameters: Dict[str, object] + parameters: Optional[Dict[str, object]] = None """A JSON schema object describing the parameters of the function.""" - strict: bool + strict: Optional[bool] = None """Whether to enforce strict parameter validation. Default `true`.""" type: Literal["function"] diff --git a/src/openai/types/responses/function_tool_param.py b/src/openai/types/responses/function_tool_param.py index 774a22e336..56bab36f47 100644 --- a/src/openai/types/responses/function_tool_param.py +++ b/src/openai/types/responses/function_tool_param.py @@ -12,10 +12,10 @@ class FunctionToolParam(TypedDict, total=False): name: Required[str] """The name of the function to call.""" - parameters: Required[Dict[str, object]] + parameters: Required[Optional[Dict[str, object]]] """A JSON schema object describing the parameters of the function.""" - strict: Required[bool] + strict: Required[Optional[bool]] """Whether to enforce strict parameter validation. Default `true`.""" type: Required[Literal["function"]] diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 3c0a9d7b8a..972d413926 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -56,6 +56,11 @@ class ResponseCreateParamsBase(TypedDict, total=False): - `message.input_image.image_url`: Include image urls from the input message. - `computer_call_output.output.image_url`: Include image urls from the computer call output. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). """ instructions: Optional[str] diff --git a/src/openai/types/responses/response_includable.py b/src/openai/types/responses/response_includable.py index 83489fa7f1..a01dddd71d 100644 --- a/src/openai/types/responses/response_includable.py +++ b/src/openai/types/responses/response_includable.py @@ -5,5 +5,8 @@ __all__ = ["ResponseIncludable"] ResponseIncludable: TypeAlias = Literal[ - "file_search_call.results", "message.input_image.image_url", "computer_call_output.output.image_url" + "file_search_call.results", + "message.input_image.image_url", + "computer_call_output.output.image_url", + "reasoning.encrypted_content", ] diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py index dc06a4ea2d..61ae46f0cb 100644 --- a/src/openai/types/responses/response_input_file_param.py +++ b/src/openai/types/responses/response_input_file_param.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ResponseInputFileParam"] @@ -14,7 +15,7 @@ class ResponseInputFileParam(TypedDict, total=False): file_data: str """The content of the file to be sent to the model.""" - file_id: str + file_id: Optional[str] """The ID of the file to be sent to the model.""" filename: str diff --git a/src/openai/types/responses/response_input_image.py b/src/openai/types/responses/response_input_image.py index d719f44e9b..f2d760b25e 100644 --- a/src/openai/types/responses/response_input_image.py +++ b/src/openai/types/responses/response_input_image.py @@ -9,7 +9,7 @@ class ResponseInputImage(BaseModel): - detail: Literal["high", "low", "auto"] + detail: Literal["low", "high", "auto"] """The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`. diff --git a/src/openai/types/responses/response_input_image_param.py b/src/openai/types/responses/response_input_image_param.py index 5dd4db2b5d..bc17e4f1c2 100644 --- a/src/openai/types/responses/response_input_image_param.py +++ b/src/openai/types/responses/response_input_image_param.py @@ -9,7 +9,7 @@ class ResponseInputImageParam(TypedDict, total=False): - detail: Required[Literal["high", "low", "auto"]] + detail: Required[Literal["low", "high", "auto"]] """The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`. diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 2505f7c0b5..290953a0ef 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Iterable +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .easy_input_message_param import EasyInputMessageParam @@ -50,10 +50,10 @@ class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): id: Required[str] """The ID of the pending safety check.""" - code: Required[str] + code: Optional[str] """The type of the pending safety check.""" - message: Required[str] + message: Optional[str] """Details about the pending safety check.""" @@ -67,16 +67,16 @@ class ComputerCallOutput(TypedDict, total=False): type: Required[Literal["computer_call_output"]] """The type of the computer tool call output. Always `computer_call_output`.""" - id: str + id: Optional[str] """The ID of the computer tool call output.""" - acknowledged_safety_checks: Iterable[ComputerCallOutputAcknowledgedSafetyCheck] + acknowledged_safety_checks: Optional[Iterable[ComputerCallOutputAcknowledgedSafetyCheck]] """ The safety checks reported by the API that have been acknowledged by the developer. """ - status: Literal["in_progress", "completed", "incomplete"] + status: Optional[Literal["in_progress", "completed", "incomplete"]] """The status of the message input. One of `in_progress`, `completed`, or `incomplete`. Populated when input items @@ -94,13 +94,13 @@ class FunctionCallOutput(TypedDict, total=False): type: Required[Literal["function_call_output"]] """The type of the function tool call output. Always `function_call_output`.""" - id: str + id: Optional[str] """The unique ID of the function tool call output. Populated when this item is returned via API. """ - status: Literal["in_progress", "completed", "incomplete"] + status: Optional[Literal["in_progress", "completed", "incomplete"]] """The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are @@ -112,7 +112,7 @@ class ItemReference(TypedDict, total=False): id: Required[str] """The ID of the item to reference.""" - type: Required[Literal["item_reference"]] + type: Optional[Literal["item_reference"]] """The type of item to reference. Always `item_reference`.""" diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 84a80eb7c2..b24182697a 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Iterable +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .easy_input_message_param import EasyInputMessageParam @@ -51,10 +51,10 @@ class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): id: Required[str] """The ID of the pending safety check.""" - code: Required[str] + code: Optional[str] """The type of the pending safety check.""" - message: Required[str] + message: Optional[str] """Details about the pending safety check.""" @@ -68,16 +68,16 @@ class ComputerCallOutput(TypedDict, total=False): type: Required[Literal["computer_call_output"]] """The type of the computer tool call output. Always `computer_call_output`.""" - id: str + id: Optional[str] """The ID of the computer tool call output.""" - acknowledged_safety_checks: Iterable[ComputerCallOutputAcknowledgedSafetyCheck] + acknowledged_safety_checks: Optional[Iterable[ComputerCallOutputAcknowledgedSafetyCheck]] """ The safety checks reported by the API that have been acknowledged by the developer. """ - status: Literal["in_progress", "completed", "incomplete"] + status: Optional[Literal["in_progress", "completed", "incomplete"]] """The status of the message input. One of `in_progress`, `completed`, or `incomplete`. Populated when input items @@ -95,13 +95,13 @@ class FunctionCallOutput(TypedDict, total=False): type: Required[Literal["function_call_output"]] """The type of the function tool call output. Always `function_call_output`.""" - id: str + id: Optional[str] """The unique ID of the function tool call output. Populated when this item is returned via API. """ - status: Literal["in_progress", "completed", "incomplete"] + status: Optional[Literal["in_progress", "completed", "incomplete"]] """The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are @@ -113,7 +113,7 @@ class ItemReference(TypedDict, total=False): id: Required[str] """The ID of the item to reference.""" - type: Required[Literal["item_reference"]] + type: Optional[Literal["item_reference"]] """The type of item to reference. Always `item_reference`.""" diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py index 57e5fbfe6d..f5da7802f8 100644 --- a/src/openai/types/responses/response_reasoning_item.py +++ b/src/openai/types/responses/response_reasoning_item.py @@ -28,6 +28,12 @@ class ResponseReasoningItem(BaseModel): type: Literal["reasoning"] """The type of the object. Always `reasoning`.""" + encrypted_content: Optional[str] = None + """ + The encrypted content of the reasoning item - populated when a response is + generated with `reasoning.encrypted_content` in the `include` parameter. + """ + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None """The status of the item. diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py index adb49d6402..2cfa5312ed 100644 --- a/src/openai/types/responses/response_reasoning_item_param.py +++ b/src/openai/types/responses/response_reasoning_item_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterable +from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ResponseReasoningItemParam", "Summary"] @@ -28,6 +28,12 @@ class ResponseReasoningItemParam(TypedDict, total=False): type: Required[Literal["reasoning"]] """The type of the object. Always `reasoning`.""" + encrypted_content: Optional[str] + """ + The encrypted content of the reasoning item - populated when a response is + generated with `reasoning.encrypted_content` in the `include` parameter. + """ + status: Literal["in_progress", "completed", "incomplete"] """The status of the item. diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index de5d5524d4..d96abdbe5a 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -12,5 +12,5 @@ __all__ = ["Tool"] Tool: TypeAlias = Annotated[ - Union[FileSearchTool, FunctionTool, ComputerTool, WebSearchTool], PropertyInfo(discriminator="type") + Union[FileSearchTool, FunctionTool, WebSearchTool, ComputerTool], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index be1cf82452..200c347005 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -13,6 +13,6 @@ __all__ = ["ToolParam"] -ToolParam: TypeAlias = Union[FileSearchToolParam, FunctionToolParam, ComputerToolParam, WebSearchToolParam] +ToolParam: TypeAlias = Union[FileSearchToolParam, FunctionToolParam, WebSearchToolParam, ComputerToolParam] ParseableToolParam: TypeAlias = Union[ToolParam, ChatCompletionToolParam] diff --git a/src/openai/types/responses/web_search_tool.py b/src/openai/types/responses/web_search_tool.py index bee270bf85..a6bf951145 100644 --- a/src/openai/types/responses/web_search_tool.py +++ b/src/openai/types/responses/web_search_tool.py @@ -33,16 +33,17 @@ class UserLocation(BaseModel): class WebSearchTool(BaseModel): type: Literal["web_search_preview", "web_search_preview_2025_03_11"] - """The type of the web search tool. One of: + """The type of the web search tool. - - `web_search_preview` - - `web_search_preview_2025_03_11` + One of `web_search_preview` or `web_search_preview_2025_03_11`. """ search_context_size: Optional[Literal["low", "medium", "high"]] = None - """ - High level guidance for the amount of context window space to use for the - search. One of `low`, `medium`, or `high`. `medium` is the default. + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. """ user_location: Optional[UserLocation] = None + """The user's location.""" diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py index 8ee36ffb47..d0335c01a3 100644 --- a/src/openai/types/responses/web_search_tool_param.py +++ b/src/openai/types/responses/web_search_tool_param.py @@ -12,19 +12,19 @@ class UserLocation(TypedDict, total=False): type: Required[Literal["approximate"]] """The type of location approximation. Always `approximate`.""" - city: str + city: Optional[str] """Free text input for the city of the user, e.g. `San Francisco`.""" - country: str + country: Optional[str] """ The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`. """ - region: str + region: Optional[str] """Free text input for the region of the user, e.g. `California`.""" - timezone: str + timezone: Optional[str] """ The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`. @@ -33,16 +33,17 @@ class UserLocation(TypedDict, total=False): class WebSearchToolParam(TypedDict, total=False): type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]] - """The type of the web search tool. One of: + """The type of the web search tool. - - `web_search_preview` - - `web_search_preview_2025_03_11` + One of `web_search_preview` or `web_search_preview_2025_03_11`. """ search_context_size: Literal["low", "medium", "high"] - """ - High level guidance for the amount of context window space to use for the - search. One of `low`, `medium`, or `high`. `medium` is the default. + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. """ user_location: Optional[UserLocation] + """The user's location.""" diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 7997e9f5a1..7c61453bc1 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -73,6 +73,7 @@ def test_method_edit_with_all_params(self, client: OpenAI) -> None: image = client.images.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", + background="transparent", mask=b"raw file contents", model="string", n=1, @@ -218,6 +219,7 @@ async def test_method_edit_with_all_params(self, async_client: AsyncOpenAI) -> N image = await async_client.images.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", + background="transparent", mask=b"raw file contents", model="string", n=1, From 67997a4ec1ebcdf8e740afb0d0b2e37897657bde Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 19:10:28 +0000 Subject: [PATCH 905/914] release: 1.77.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8bcd8a5b4f..33a65d75c4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.76.2" + ".": "1.77.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bc85128f6a..9097cdc65a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 1.77.0 (2025-05-02) + +Full Changelog: [v1.76.2...v1.77.0](https://github.com/openai/openai-python/compare/v1.76.2...v1.77.0) + +### Features + +* **api:** add image sizes, reasoning encryption ([473469a](https://github.com/openai/openai-python/commit/473469afa1a5f0a03f727bdcdadb9fd57872f9c5)) + + +### Bug Fixes + +* **parsing:** handle whitespace only strings ([#2007](https://github.com/openai/openai-python/issues/2007)) ([246bc5b](https://github.com/openai/openai-python/commit/246bc5b7559887840717667a0dad465caef66c3b)) + + +### Chores + +* only strip leading whitespace ([8467d66](https://github.com/openai/openai-python/commit/8467d666e0ddf1a9f81b8769a5c8a2fef1de20c1)) + ## 1.76.2 (2025-04-29) Full Changelog: [v1.76.1...v1.76.2](https://github.com/openai/openai-python/compare/v1.76.1...v1.76.2) diff --git a/pyproject.toml b/pyproject.toml index 2c3c3eaf3b..4b854b05e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.76.2" +version = "1.77.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ef1e3fe526..9d8ba015e1 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.76.2" # x-release-please-version +__version__ = "1.77.0" # x-release-please-version From 1356c89a1302a1f6c1f6d6d7e8398a741d4e7423 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 08:26:41 +0000 Subject: [PATCH 906/914] chore: use lazy imports for module level client --- src/openai/_module_client.py | 112 +++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index cf12f7a31e..dd601f9be9 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -1,113 +1,133 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from __future__ import annotations + +from typing import TYPE_CHECKING from typing_extensions import override -from . import resources, _load_client +if TYPE_CHECKING: + from .resources.files import Files + from .resources.images import Images + from .resources.models import Models + from .resources.batches import Batches + from .resources.beta.beta import Beta + from .resources.chat.chat import Chat + from .resources.embeddings import Embeddings + from .resources.audio.audio import Audio + from .resources.completions import Completions + from .resources.evals.evals import Evals + from .resources.moderations import Moderations + from .resources.uploads.uploads import Uploads + from .resources.responses.responses import Responses + from .resources.fine_tuning.fine_tuning import FineTuning + from .resources.vector_stores.vector_stores import VectorStores + +from . import _load_client from ._utils import LazyProxy -class ChatProxy(LazyProxy[resources.Chat]): +class ChatProxy(LazyProxy["Chat"]): @override - def __load__(self) -> resources.Chat: + def __load__(self) -> Chat: return _load_client().chat -class BetaProxy(LazyProxy[resources.Beta]): +class BetaProxy(LazyProxy["Beta"]): @override - def __load__(self) -> resources.Beta: + def __load__(self) -> Beta: return _load_client().beta -class FilesProxy(LazyProxy[resources.Files]): +class FilesProxy(LazyProxy["Files"]): @override - def __load__(self) -> resources.Files: + def __load__(self) -> Files: return _load_client().files -class AudioProxy(LazyProxy[resources.Audio]): +class AudioProxy(LazyProxy["Audio"]): @override - def __load__(self) -> resources.Audio: + def __load__(self) -> Audio: return _load_client().audio -class EvalsProxy(LazyProxy[resources.Evals]): +class EvalsProxy(LazyProxy["Evals"]): @override - def __load__(self) -> resources.Evals: + def __load__(self) -> Evals: return _load_client().evals -class ImagesProxy(LazyProxy[resources.Images]): +class ImagesProxy(LazyProxy["Images"]): @override - def __load__(self) -> resources.Images: + def __load__(self) -> Images: return _load_client().images -class ModelsProxy(LazyProxy[resources.Models]): +class ModelsProxy(LazyProxy["Models"]): @override - def __load__(self) -> resources.Models: + def __load__(self) -> Models: return _load_client().models -class BatchesProxy(LazyProxy[resources.Batches]): +class BatchesProxy(LazyProxy["Batches"]): @override - def __load__(self) -> resources.Batches: + def __load__(self) -> Batches: return _load_client().batches -class UploadsProxy(LazyProxy[resources.Uploads]): +class UploadsProxy(LazyProxy["Uploads"]): @override - def __load__(self) -> resources.Uploads: + def __load__(self) -> Uploads: return _load_client().uploads -class ResponsesProxy(LazyProxy[resources.Responses]): +class ResponsesProxy(LazyProxy["Responses"]): @override - def __load__(self) -> resources.Responses: + def __load__(self) -> Responses: return _load_client().responses -class EmbeddingsProxy(LazyProxy[resources.Embeddings]): +class EmbeddingsProxy(LazyProxy["Embeddings"]): @override - def __load__(self) -> resources.Embeddings: + def __load__(self) -> Embeddings: return _load_client().embeddings -class CompletionsProxy(LazyProxy[resources.Completions]): +class CompletionsProxy(LazyProxy["Completions"]): @override - def __load__(self) -> resources.Completions: + def __load__(self) -> Completions: return _load_client().completions -class ModerationsProxy(LazyProxy[resources.Moderations]): +class ModerationsProxy(LazyProxy["Moderations"]): @override - def __load__(self) -> resources.Moderations: + def __load__(self) -> Moderations: return _load_client().moderations -class FineTuningProxy(LazyProxy[resources.FineTuning]): +class FineTuningProxy(LazyProxy["FineTuning"]): @override - def __load__(self) -> resources.FineTuning: + def __load__(self) -> FineTuning: return _load_client().fine_tuning -class VectorStoresProxy(LazyProxy[resources.VectorStores]): +class VectorStoresProxy(LazyProxy["VectorStores"]): @override - def __load__(self) -> resources.VectorStores: + def __load__(self) -> VectorStores: return _load_client().vector_stores -chat: resources.Chat = ChatProxy().__as_proxied__() -beta: resources.Beta = BetaProxy().__as_proxied__() -files: resources.Files = FilesProxy().__as_proxied__() -audio: resources.Audio = AudioProxy().__as_proxied__() -evals: resources.Evals = EvalsProxy().__as_proxied__() -images: resources.Images = ImagesProxy().__as_proxied__() -models: resources.Models = ModelsProxy().__as_proxied__() -batches: resources.Batches = BatchesProxy().__as_proxied__() -uploads: resources.Uploads = UploadsProxy().__as_proxied__() -responses: resources.Responses = ResponsesProxy().__as_proxied__() -embeddings: resources.Embeddings = EmbeddingsProxy().__as_proxied__() -completions: resources.Completions = CompletionsProxy().__as_proxied__() -moderations: resources.Moderations = ModerationsProxy().__as_proxied__() -fine_tuning: resources.FineTuning = FineTuningProxy().__as_proxied__() -vector_stores: resources.VectorStores = VectorStoresProxy().__as_proxied__() +chat: Chat = ChatProxy().__as_proxied__() +beta: Beta = BetaProxy().__as_proxied__() +files: Files = FilesProxy().__as_proxied__() +audio: Audio = AudioProxy().__as_proxied__() +evals: Evals = EvalsProxy().__as_proxied__() +images: Images = ImagesProxy().__as_proxied__() +models: Models = ModelsProxy().__as_proxied__() +batches: Batches = BatchesProxy().__as_proxied__() +uploads: Uploads = UploadsProxy().__as_proxied__() +responses: Responses = ResponsesProxy().__as_proxied__() +embeddings: Embeddings = EmbeddingsProxy().__as_proxied__() +completions: Completions = CompletionsProxy().__as_proxied__() +moderations: Moderations = ModerationsProxy().__as_proxied__() +fine_tuning: FineTuning = FineTuningProxy().__as_proxied__() +vector_stores: VectorStores = VectorStoresProxy().__as_proxied__() From 917dade87f0243caf24d890a1ee3307ee89d145c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 13:15:15 +0000 Subject: [PATCH 907/914] chore: use lazy imports for resources --- src/openai/_client.py | 742 +++++++++++++++++++++++++------ src/openai/resources/__init__.py | 14 - 2 files changed, 602 insertions(+), 154 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index 3aca6cb124..b251ab0917 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Any, Union, Mapping +from typing import TYPE_CHECKING, Any, Union, Mapping from typing_extensions import Self, override import httpx @@ -24,8 +24,8 @@ is_mapping, get_async_library, ) +from ._compat import cached_property from ._version import __version__ -from .resources import files, images, models, batches, embeddings, completions, moderations from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError from ._base_client import ( @@ -33,37 +33,45 @@ SyncAPIClient, AsyncAPIClient, ) -from .resources.beta import beta -from .resources.chat import chat -from .resources.audio import audio -from .resources.evals import evals -from .resources.uploads import uploads -from .resources.responses import responses -from .resources.fine_tuning import fine_tuning -from .resources.vector_stores import vector_stores + +if TYPE_CHECKING: + from .resources import ( + beta, + chat, + audio, + evals, + files, + images, + models, + batches, + uploads, + responses, + embeddings, + completions, + fine_tuning, + moderations, + vector_stores, + ) + from .resources.files import Files, AsyncFiles + from .resources.images import Images, AsyncImages + from .resources.models import Models, AsyncModels + from .resources.batches import Batches, AsyncBatches + from .resources.beta.beta import Beta, AsyncBeta + from .resources.chat.chat import Chat, AsyncChat + from .resources.embeddings import Embeddings, AsyncEmbeddings + from .resources.audio.audio import Audio, AsyncAudio + from .resources.completions import Completions, AsyncCompletions + from .resources.evals.evals import Evals, AsyncEvals + from .resources.moderations import Moderations, AsyncModerations + from .resources.uploads.uploads import Uploads, AsyncUploads + from .resources.responses.responses import Responses, AsyncResponses + from .resources.fine_tuning.fine_tuning import FineTuning, AsyncFineTuning + from .resources.vector_stores.vector_stores import VectorStores, AsyncVectorStores __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "OpenAI", "AsyncOpenAI", "Client", "AsyncClient"] class OpenAI(SyncAPIClient): - completions: completions.Completions - chat: chat.Chat - embeddings: embeddings.Embeddings - files: files.Files - images: images.Images - audio: audio.Audio - moderations: moderations.Moderations - models: models.Models - fine_tuning: fine_tuning.FineTuning - vector_stores: vector_stores.VectorStores - beta: beta.Beta - batches: batches.Batches - uploads: uploads.Uploads - responses: responses.Responses - evals: evals.Evals - with_raw_response: OpenAIWithRawResponse - with_streaming_response: OpenAIWithStreamedResponse - # client options api_key: str organization: str | None @@ -146,23 +154,103 @@ def __init__( self._default_stream_cls = Stream - self.completions = completions.Completions(self) - self.chat = chat.Chat(self) - self.embeddings = embeddings.Embeddings(self) - self.files = files.Files(self) - self.images = images.Images(self) - self.audio = audio.Audio(self) - self.moderations = moderations.Moderations(self) - self.models = models.Models(self) - self.fine_tuning = fine_tuning.FineTuning(self) - self.vector_stores = vector_stores.VectorStores(self) - self.beta = beta.Beta(self) - self.batches = batches.Batches(self) - self.uploads = uploads.Uploads(self) - self.responses = responses.Responses(self) - self.evals = evals.Evals(self) - self.with_raw_response = OpenAIWithRawResponse(self) - self.with_streaming_response = OpenAIWithStreamedResponse(self) + @cached_property + def completions(self) -> Completions: + from .resources.completions import Completions + + return Completions(self) + + @cached_property + def chat(self) -> Chat: + from .resources.chat import Chat + + return Chat(self) + + @cached_property + def embeddings(self) -> Embeddings: + from .resources.embeddings import Embeddings + + return Embeddings(self) + + @cached_property + def files(self) -> Files: + from .resources.files import Files + + return Files(self) + + @cached_property + def images(self) -> Images: + from .resources.images import Images + + return Images(self) + + @cached_property + def audio(self) -> Audio: + from .resources.audio import Audio + + return Audio(self) + + @cached_property + def moderations(self) -> Moderations: + from .resources.moderations import Moderations + + return Moderations(self) + + @cached_property + def models(self) -> Models: + from .resources.models import Models + + return Models(self) + + @cached_property + def fine_tuning(self) -> FineTuning: + from .resources.fine_tuning import FineTuning + + return FineTuning(self) + + @cached_property + def vector_stores(self) -> VectorStores: + from .resources.vector_stores import VectorStores + + return VectorStores(self) + + @cached_property + def beta(self) -> Beta: + from .resources.beta import Beta + + return Beta(self) + + @cached_property + def batches(self) -> Batches: + from .resources.batches import Batches + + return Batches(self) + + @cached_property + def uploads(self) -> Uploads: + from .resources.uploads import Uploads + + return Uploads(self) + + @cached_property + def responses(self) -> Responses: + from .resources.responses import Responses + + return Responses(self) + + @cached_property + def evals(self) -> Evals: + from .resources.evals import Evals + + return Evals(self) + + @cached_property + def with_raw_response(self) -> OpenAIWithRawResponse: + return OpenAIWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OpenAIWithStreamedResponse: + return OpenAIWithStreamedResponse(self) @property @override @@ -279,24 +367,6 @@ def _make_status_error( class AsyncOpenAI(AsyncAPIClient): - completions: completions.AsyncCompletions - chat: chat.AsyncChat - embeddings: embeddings.AsyncEmbeddings - files: files.AsyncFiles - images: images.AsyncImages - audio: audio.AsyncAudio - moderations: moderations.AsyncModerations - models: models.AsyncModels - fine_tuning: fine_tuning.AsyncFineTuning - vector_stores: vector_stores.AsyncVectorStores - beta: beta.AsyncBeta - batches: batches.AsyncBatches - uploads: uploads.AsyncUploads - responses: responses.AsyncResponses - evals: evals.AsyncEvals - with_raw_response: AsyncOpenAIWithRawResponse - with_streaming_response: AsyncOpenAIWithStreamedResponse - # client options api_key: str organization: str | None @@ -379,23 +449,103 @@ def __init__( self._default_stream_cls = AsyncStream - self.completions = completions.AsyncCompletions(self) - self.chat = chat.AsyncChat(self) - self.embeddings = embeddings.AsyncEmbeddings(self) - self.files = files.AsyncFiles(self) - self.images = images.AsyncImages(self) - self.audio = audio.AsyncAudio(self) - self.moderations = moderations.AsyncModerations(self) - self.models = models.AsyncModels(self) - self.fine_tuning = fine_tuning.AsyncFineTuning(self) - self.vector_stores = vector_stores.AsyncVectorStores(self) - self.beta = beta.AsyncBeta(self) - self.batches = batches.AsyncBatches(self) - self.uploads = uploads.AsyncUploads(self) - self.responses = responses.AsyncResponses(self) - self.evals = evals.AsyncEvals(self) - self.with_raw_response = AsyncOpenAIWithRawResponse(self) - self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self) + @cached_property + def completions(self) -> AsyncCompletions: + from .resources.completions import AsyncCompletions + + return AsyncCompletions(self) + + @cached_property + def chat(self) -> AsyncChat: + from .resources.chat import AsyncChat + + return AsyncChat(self) + + @cached_property + def embeddings(self) -> AsyncEmbeddings: + from .resources.embeddings import AsyncEmbeddings + + return AsyncEmbeddings(self) + + @cached_property + def files(self) -> AsyncFiles: + from .resources.files import AsyncFiles + + return AsyncFiles(self) + + @cached_property + def images(self) -> AsyncImages: + from .resources.images import AsyncImages + + return AsyncImages(self) + + @cached_property + def audio(self) -> AsyncAudio: + from .resources.audio import AsyncAudio + + return AsyncAudio(self) + + @cached_property + def moderations(self) -> AsyncModerations: + from .resources.moderations import AsyncModerations + + return AsyncModerations(self) + + @cached_property + def models(self) -> AsyncModels: + from .resources.models import AsyncModels + + return AsyncModels(self) + + @cached_property + def fine_tuning(self) -> AsyncFineTuning: + from .resources.fine_tuning import AsyncFineTuning + + return AsyncFineTuning(self) + + @cached_property + def vector_stores(self) -> AsyncVectorStores: + from .resources.vector_stores import AsyncVectorStores + + return AsyncVectorStores(self) + + @cached_property + def beta(self) -> AsyncBeta: + from .resources.beta import AsyncBeta + + return AsyncBeta(self) + + @cached_property + def batches(self) -> AsyncBatches: + from .resources.batches import AsyncBatches + + return AsyncBatches(self) + + @cached_property + def uploads(self) -> AsyncUploads: + from .resources.uploads import AsyncUploads + + return AsyncUploads(self) + + @cached_property + def responses(self) -> AsyncResponses: + from .resources.responses import AsyncResponses + + return AsyncResponses(self) + + @cached_property + def evals(self) -> AsyncEvals: + from .resources.evals import AsyncEvals + + return AsyncEvals(self) + + @cached_property + def with_raw_response(self) -> AsyncOpenAIWithRawResponse: + return AsyncOpenAIWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOpenAIWithStreamedResponse: + return AsyncOpenAIWithStreamedResponse(self) @property @override @@ -512,79 +662,391 @@ def _make_status_error( class OpenAIWithRawResponse: + _client: OpenAI + def __init__(self, client: OpenAI) -> None: - self.completions = completions.CompletionsWithRawResponse(client.completions) - self.chat = chat.ChatWithRawResponse(client.chat) - self.embeddings = embeddings.EmbeddingsWithRawResponse(client.embeddings) - self.files = files.FilesWithRawResponse(client.files) - self.images = images.ImagesWithRawResponse(client.images) - self.audio = audio.AudioWithRawResponse(client.audio) - self.moderations = moderations.ModerationsWithRawResponse(client.moderations) - self.models = models.ModelsWithRawResponse(client.models) - self.fine_tuning = fine_tuning.FineTuningWithRawResponse(client.fine_tuning) - self.vector_stores = vector_stores.VectorStoresWithRawResponse(client.vector_stores) - self.beta = beta.BetaWithRawResponse(client.beta) - self.batches = batches.BatchesWithRawResponse(client.batches) - self.uploads = uploads.UploadsWithRawResponse(client.uploads) - self.responses = responses.ResponsesWithRawResponse(client.responses) - self.evals = evals.EvalsWithRawResponse(client.evals) + self._client = client + + @cached_property + def completions(self) -> completions.CompletionsWithRawResponse: + from .resources.completions import CompletionsWithRawResponse + + return CompletionsWithRawResponse(self._client.completions) + + @cached_property + def chat(self) -> chat.ChatWithRawResponse: + from .resources.chat import ChatWithRawResponse + + return ChatWithRawResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.EmbeddingsWithRawResponse: + from .resources.embeddings import EmbeddingsWithRawResponse + + return EmbeddingsWithRawResponse(self._client.embeddings) + + @cached_property + def files(self) -> files.FilesWithRawResponse: + from .resources.files import FilesWithRawResponse + + return FilesWithRawResponse(self._client.files) + + @cached_property + def images(self) -> images.ImagesWithRawResponse: + from .resources.images import ImagesWithRawResponse + + return ImagesWithRawResponse(self._client.images) + + @cached_property + def audio(self) -> audio.AudioWithRawResponse: + from .resources.audio import AudioWithRawResponse + + return AudioWithRawResponse(self._client.audio) + + @cached_property + def moderations(self) -> moderations.ModerationsWithRawResponse: + from .resources.moderations import ModerationsWithRawResponse + + return ModerationsWithRawResponse(self._client.moderations) + + @cached_property + def models(self) -> models.ModelsWithRawResponse: + from .resources.models import ModelsWithRawResponse + + return ModelsWithRawResponse(self._client.models) + + @cached_property + def fine_tuning(self) -> fine_tuning.FineTuningWithRawResponse: + from .resources.fine_tuning import FineTuningWithRawResponse + + return FineTuningWithRawResponse(self._client.fine_tuning) + + @cached_property + def vector_stores(self) -> vector_stores.VectorStoresWithRawResponse: + from .resources.vector_stores import VectorStoresWithRawResponse + + return VectorStoresWithRawResponse(self._client.vector_stores) + + @cached_property + def beta(self) -> beta.BetaWithRawResponse: + from .resources.beta import BetaWithRawResponse + + return BetaWithRawResponse(self._client.beta) + + @cached_property + def batches(self) -> batches.BatchesWithRawResponse: + from .resources.batches import BatchesWithRawResponse + + return BatchesWithRawResponse(self._client.batches) + + @cached_property + def uploads(self) -> uploads.UploadsWithRawResponse: + from .resources.uploads import UploadsWithRawResponse + + return UploadsWithRawResponse(self._client.uploads) + + @cached_property + def responses(self) -> responses.ResponsesWithRawResponse: + from .resources.responses import ResponsesWithRawResponse + + return ResponsesWithRawResponse(self._client.responses) + + @cached_property + def evals(self) -> evals.EvalsWithRawResponse: + from .resources.evals import EvalsWithRawResponse + + return EvalsWithRawResponse(self._client.evals) class AsyncOpenAIWithRawResponse: + _client: AsyncOpenAI + def __init__(self, client: AsyncOpenAI) -> None: - self.completions = completions.AsyncCompletionsWithRawResponse(client.completions) - self.chat = chat.AsyncChatWithRawResponse(client.chat) - self.embeddings = embeddings.AsyncEmbeddingsWithRawResponse(client.embeddings) - self.files = files.AsyncFilesWithRawResponse(client.files) - self.images = images.AsyncImagesWithRawResponse(client.images) - self.audio = audio.AsyncAudioWithRawResponse(client.audio) - self.moderations = moderations.AsyncModerationsWithRawResponse(client.moderations) - self.models = models.AsyncModelsWithRawResponse(client.models) - self.fine_tuning = fine_tuning.AsyncFineTuningWithRawResponse(client.fine_tuning) - self.vector_stores = vector_stores.AsyncVectorStoresWithRawResponse(client.vector_stores) - self.beta = beta.AsyncBetaWithRawResponse(client.beta) - self.batches = batches.AsyncBatchesWithRawResponse(client.batches) - self.uploads = uploads.AsyncUploadsWithRawResponse(client.uploads) - self.responses = responses.AsyncResponsesWithRawResponse(client.responses) - self.evals = evals.AsyncEvalsWithRawResponse(client.evals) + self._client = client + + @cached_property + def completions(self) -> completions.AsyncCompletionsWithRawResponse: + from .resources.completions import AsyncCompletionsWithRawResponse + + return AsyncCompletionsWithRawResponse(self._client.completions) + + @cached_property + def chat(self) -> chat.AsyncChatWithRawResponse: + from .resources.chat import AsyncChatWithRawResponse + + return AsyncChatWithRawResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.AsyncEmbeddingsWithRawResponse: + from .resources.embeddings import AsyncEmbeddingsWithRawResponse + + return AsyncEmbeddingsWithRawResponse(self._client.embeddings) + + @cached_property + def files(self) -> files.AsyncFilesWithRawResponse: + from .resources.files import AsyncFilesWithRawResponse + + return AsyncFilesWithRawResponse(self._client.files) + + @cached_property + def images(self) -> images.AsyncImagesWithRawResponse: + from .resources.images import AsyncImagesWithRawResponse + + return AsyncImagesWithRawResponse(self._client.images) + + @cached_property + def audio(self) -> audio.AsyncAudioWithRawResponse: + from .resources.audio import AsyncAudioWithRawResponse + + return AsyncAudioWithRawResponse(self._client.audio) + + @cached_property + def moderations(self) -> moderations.AsyncModerationsWithRawResponse: + from .resources.moderations import AsyncModerationsWithRawResponse + + return AsyncModerationsWithRawResponse(self._client.moderations) + + @cached_property + def models(self) -> models.AsyncModelsWithRawResponse: + from .resources.models import AsyncModelsWithRawResponse + + return AsyncModelsWithRawResponse(self._client.models) + + @cached_property + def fine_tuning(self) -> fine_tuning.AsyncFineTuningWithRawResponse: + from .resources.fine_tuning import AsyncFineTuningWithRawResponse + + return AsyncFineTuningWithRawResponse(self._client.fine_tuning) + + @cached_property + def vector_stores(self) -> vector_stores.AsyncVectorStoresWithRawResponse: + from .resources.vector_stores import AsyncVectorStoresWithRawResponse + + return AsyncVectorStoresWithRawResponse(self._client.vector_stores) + + @cached_property + def beta(self) -> beta.AsyncBetaWithRawResponse: + from .resources.beta import AsyncBetaWithRawResponse + + return AsyncBetaWithRawResponse(self._client.beta) + + @cached_property + def batches(self) -> batches.AsyncBatchesWithRawResponse: + from .resources.batches import AsyncBatchesWithRawResponse + + return AsyncBatchesWithRawResponse(self._client.batches) + + @cached_property + def uploads(self) -> uploads.AsyncUploadsWithRawResponse: + from .resources.uploads import AsyncUploadsWithRawResponse + + return AsyncUploadsWithRawResponse(self._client.uploads) + + @cached_property + def responses(self) -> responses.AsyncResponsesWithRawResponse: + from .resources.responses import AsyncResponsesWithRawResponse + + return AsyncResponsesWithRawResponse(self._client.responses) + + @cached_property + def evals(self) -> evals.AsyncEvalsWithRawResponse: + from .resources.evals import AsyncEvalsWithRawResponse + + return AsyncEvalsWithRawResponse(self._client.evals) class OpenAIWithStreamedResponse: + _client: OpenAI + def __init__(self, client: OpenAI) -> None: - self.completions = completions.CompletionsWithStreamingResponse(client.completions) - self.chat = chat.ChatWithStreamingResponse(client.chat) - self.embeddings = embeddings.EmbeddingsWithStreamingResponse(client.embeddings) - self.files = files.FilesWithStreamingResponse(client.files) - self.images = images.ImagesWithStreamingResponse(client.images) - self.audio = audio.AudioWithStreamingResponse(client.audio) - self.moderations = moderations.ModerationsWithStreamingResponse(client.moderations) - self.models = models.ModelsWithStreamingResponse(client.models) - self.fine_tuning = fine_tuning.FineTuningWithStreamingResponse(client.fine_tuning) - self.vector_stores = vector_stores.VectorStoresWithStreamingResponse(client.vector_stores) - self.beta = beta.BetaWithStreamingResponse(client.beta) - self.batches = batches.BatchesWithStreamingResponse(client.batches) - self.uploads = uploads.UploadsWithStreamingResponse(client.uploads) - self.responses = responses.ResponsesWithStreamingResponse(client.responses) - self.evals = evals.EvalsWithStreamingResponse(client.evals) + self._client = client + + @cached_property + def completions(self) -> completions.CompletionsWithStreamingResponse: + from .resources.completions import CompletionsWithStreamingResponse + + return CompletionsWithStreamingResponse(self._client.completions) + + @cached_property + def chat(self) -> chat.ChatWithStreamingResponse: + from .resources.chat import ChatWithStreamingResponse + + return ChatWithStreamingResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.EmbeddingsWithStreamingResponse: + from .resources.embeddings import EmbeddingsWithStreamingResponse + + return EmbeddingsWithStreamingResponse(self._client.embeddings) + + @cached_property + def files(self) -> files.FilesWithStreamingResponse: + from .resources.files import FilesWithStreamingResponse + + return FilesWithStreamingResponse(self._client.files) + + @cached_property + def images(self) -> images.ImagesWithStreamingResponse: + from .resources.images import ImagesWithStreamingResponse + + return ImagesWithStreamingResponse(self._client.images) + + @cached_property + def audio(self) -> audio.AudioWithStreamingResponse: + from .resources.audio import AudioWithStreamingResponse + + return AudioWithStreamingResponse(self._client.audio) + + @cached_property + def moderations(self) -> moderations.ModerationsWithStreamingResponse: + from .resources.moderations import ModerationsWithStreamingResponse + + return ModerationsWithStreamingResponse(self._client.moderations) + + @cached_property + def models(self) -> models.ModelsWithStreamingResponse: + from .resources.models import ModelsWithStreamingResponse + + return ModelsWithStreamingResponse(self._client.models) + + @cached_property + def fine_tuning(self) -> fine_tuning.FineTuningWithStreamingResponse: + from .resources.fine_tuning import FineTuningWithStreamingResponse + + return FineTuningWithStreamingResponse(self._client.fine_tuning) + + @cached_property + def vector_stores(self) -> vector_stores.VectorStoresWithStreamingResponse: + from .resources.vector_stores import VectorStoresWithStreamingResponse + + return VectorStoresWithStreamingResponse(self._client.vector_stores) + + @cached_property + def beta(self) -> beta.BetaWithStreamingResponse: + from .resources.beta import BetaWithStreamingResponse + + return BetaWithStreamingResponse(self._client.beta) + + @cached_property + def batches(self) -> batches.BatchesWithStreamingResponse: + from .resources.batches import BatchesWithStreamingResponse + + return BatchesWithStreamingResponse(self._client.batches) + + @cached_property + def uploads(self) -> uploads.UploadsWithStreamingResponse: + from .resources.uploads import UploadsWithStreamingResponse + + return UploadsWithStreamingResponse(self._client.uploads) + + @cached_property + def responses(self) -> responses.ResponsesWithStreamingResponse: + from .resources.responses import ResponsesWithStreamingResponse + + return ResponsesWithStreamingResponse(self._client.responses) + + @cached_property + def evals(self) -> evals.EvalsWithStreamingResponse: + from .resources.evals import EvalsWithStreamingResponse + + return EvalsWithStreamingResponse(self._client.evals) class AsyncOpenAIWithStreamedResponse: + _client: AsyncOpenAI + def __init__(self, client: AsyncOpenAI) -> None: - self.completions = completions.AsyncCompletionsWithStreamingResponse(client.completions) - self.chat = chat.AsyncChatWithStreamingResponse(client.chat) - self.embeddings = embeddings.AsyncEmbeddingsWithStreamingResponse(client.embeddings) - self.files = files.AsyncFilesWithStreamingResponse(client.files) - self.images = images.AsyncImagesWithStreamingResponse(client.images) - self.audio = audio.AsyncAudioWithStreamingResponse(client.audio) - self.moderations = moderations.AsyncModerationsWithStreamingResponse(client.moderations) - self.models = models.AsyncModelsWithStreamingResponse(client.models) - self.fine_tuning = fine_tuning.AsyncFineTuningWithStreamingResponse(client.fine_tuning) - self.vector_stores = vector_stores.AsyncVectorStoresWithStreamingResponse(client.vector_stores) - self.beta = beta.AsyncBetaWithStreamingResponse(client.beta) - self.batches = batches.AsyncBatchesWithStreamingResponse(client.batches) - self.uploads = uploads.AsyncUploadsWithStreamingResponse(client.uploads) - self.responses = responses.AsyncResponsesWithStreamingResponse(client.responses) - self.evals = evals.AsyncEvalsWithStreamingResponse(client.evals) + self._client = client + + @cached_property + def completions(self) -> completions.AsyncCompletionsWithStreamingResponse: + from .resources.completions import AsyncCompletionsWithStreamingResponse + + return AsyncCompletionsWithStreamingResponse(self._client.completions) + + @cached_property + def chat(self) -> chat.AsyncChatWithStreamingResponse: + from .resources.chat import AsyncChatWithStreamingResponse + + return AsyncChatWithStreamingResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.AsyncEmbeddingsWithStreamingResponse: + from .resources.embeddings import AsyncEmbeddingsWithStreamingResponse + + return AsyncEmbeddingsWithStreamingResponse(self._client.embeddings) + + @cached_property + def files(self) -> files.AsyncFilesWithStreamingResponse: + from .resources.files import AsyncFilesWithStreamingResponse + + return AsyncFilesWithStreamingResponse(self._client.files) + + @cached_property + def images(self) -> images.AsyncImagesWithStreamingResponse: + from .resources.images import AsyncImagesWithStreamingResponse + + return AsyncImagesWithStreamingResponse(self._client.images) + + @cached_property + def audio(self) -> audio.AsyncAudioWithStreamingResponse: + from .resources.audio import AsyncAudioWithStreamingResponse + + return AsyncAudioWithStreamingResponse(self._client.audio) + + @cached_property + def moderations(self) -> moderations.AsyncModerationsWithStreamingResponse: + from .resources.moderations import AsyncModerationsWithStreamingResponse + + return AsyncModerationsWithStreamingResponse(self._client.moderations) + + @cached_property + def models(self) -> models.AsyncModelsWithStreamingResponse: + from .resources.models import AsyncModelsWithStreamingResponse + + return AsyncModelsWithStreamingResponse(self._client.models) + + @cached_property + def fine_tuning(self) -> fine_tuning.AsyncFineTuningWithStreamingResponse: + from .resources.fine_tuning import AsyncFineTuningWithStreamingResponse + + return AsyncFineTuningWithStreamingResponse(self._client.fine_tuning) + + @cached_property + def vector_stores(self) -> vector_stores.AsyncVectorStoresWithStreamingResponse: + from .resources.vector_stores import AsyncVectorStoresWithStreamingResponse + + return AsyncVectorStoresWithStreamingResponse(self._client.vector_stores) + + @cached_property + def beta(self) -> beta.AsyncBetaWithStreamingResponse: + from .resources.beta import AsyncBetaWithStreamingResponse + + return AsyncBetaWithStreamingResponse(self._client.beta) + + @cached_property + def batches(self) -> batches.AsyncBatchesWithStreamingResponse: + from .resources.batches import AsyncBatchesWithStreamingResponse + + return AsyncBatchesWithStreamingResponse(self._client.batches) + + @cached_property + def uploads(self) -> uploads.AsyncUploadsWithStreamingResponse: + from .resources.uploads import AsyncUploadsWithStreamingResponse + + return AsyncUploadsWithStreamingResponse(self._client.uploads) + + @cached_property + def responses(self) -> responses.AsyncResponsesWithStreamingResponse: + from .resources.responses import AsyncResponsesWithStreamingResponse + + return AsyncResponsesWithStreamingResponse(self._client.responses) + + @cached_property + def evals(self) -> evals.AsyncEvalsWithStreamingResponse: + from .resources.evals import AsyncEvalsWithStreamingResponse + + return AsyncEvalsWithStreamingResponse(self._client.evals) Client = OpenAI diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index ab9cd73e81..8612dec797 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -72,14 +72,6 @@ UploadsWithStreamingResponse, AsyncUploadsWithStreamingResponse, ) -from .responses import ( - Responses, - AsyncResponses, - ResponsesWithRawResponse, - AsyncResponsesWithRawResponse, - ResponsesWithStreamingResponse, - AsyncResponsesWithStreamingResponse, -) from .embeddings import ( Embeddings, AsyncEmbeddings, @@ -200,12 +192,6 @@ "AsyncUploadsWithRawResponse", "UploadsWithStreamingResponse", "AsyncUploadsWithStreamingResponse", - "Responses", - "AsyncResponses", - "ResponsesWithRawResponse", - "AsyncResponsesWithRawResponse", - "ResponsesWithStreamingResponse", - "AsyncResponsesWithStreamingResponse", "Evals", "AsyncEvals", "EvalsWithRawResponse", From 08d67adfffad61802e1ac1b3f35795040182a12d Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 7 May 2025 18:50:47 +0100 Subject: [PATCH 908/914] fix: ignore errors in isinstance() calls on LazyProxy subclasses (#2343) Fix #2056 --- src/openai/_utils/_proxy.py | 5 ++++- tests/test_utils/test_proxy.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/openai/_utils/_proxy.py b/src/openai/_utils/_proxy.py index ffd883e9dd..0f239a33c6 100644 --- a/src/openai/_utils/_proxy.py +++ b/src/openai/_utils/_proxy.py @@ -46,7 +46,10 @@ def __dir__(self) -> Iterable[str]: @property # type: ignore @override def __class__(self) -> type: # pyright: ignore - proxied = self.__get_proxied__() + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) if issubclass(type(proxied), LazyProxy): return type(proxied) return proxied.__class__ diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py index aedd3731ee..19bedc7780 100644 --- a/tests/test_utils/test_proxy.py +++ b/tests/test_utils/test_proxy.py @@ -3,6 +3,7 @@ from typing_extensions import override from openai._utils import LazyProxy +from openai._extras._common import MissingDependencyError class RecursiveLazyProxy(LazyProxy[Any]): @@ -21,3 +22,14 @@ def test_recursive_proxy() -> None: assert dir(proxy) == [] assert type(proxy).__name__ == "RecursiveLazyProxy" assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" + + +def test_is_instance_with_missing_dependency_error() -> None: + class MissingDepsProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + raise MissingDependencyError("Mocking missing dependency") + + proxy = MissingDepsProxy() + assert not isinstance(proxy, dict) + assert isinstance(proxy, LazyProxy) From e241775593ce683a9888606645f0ecfa02fe6efc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 12:40:47 +0000 Subject: [PATCH 909/914] chore(internal): update proxy tests --- tests/test_utils/test_proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py index 19bedc7780..2b5ff19dab 100644 --- a/tests/test_utils/test_proxy.py +++ b/tests/test_utils/test_proxy.py @@ -24,7 +24,7 @@ def test_recursive_proxy() -> None: assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" -def test_is_instance_with_missing_dependency_error() -> None: +def test_isinstance_does_not_error() -> None: class MissingDepsProxy(LazyProxy[Any]): @override def __load__(self) -> Any: From a3b8e7724f505cb3f8dd9efff0f23301b6804bb6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 17:23:49 +0000 Subject: [PATCH 910/914] feat(api): Add reinforcement fine-tuning api support --- .stats.yml | 8 +- api.md | 52 +++- src/openai/resources/fine_tuning/__init__.py | 14 + .../resources/fine_tuning/alpha/__init__.py | 33 ++ .../resources/fine_tuning/alpha/alpha.py | 102 +++++++ .../resources/fine_tuning/alpha/graders.py | 272 +++++++++++++++++ .../resources/fine_tuning/fine_tuning.py | 32 ++ src/openai/resources/fine_tuning/jobs/jobs.py | 156 ++++++++++ src/openai/types/__init__.py | 5 - src/openai/types/eval_create_params.py | 91 ++---- src/openai/types/eval_create_response.py | 97 ++---- src/openai/types/eval_list_response.py | 97 ++---- src/openai/types/eval_retrieve_response.py | 97 ++---- src/openai/types/eval_update_response.py | 97 ++---- src/openai/types/fine_tuning/__init__.py | 12 + .../types/fine_tuning/alpha/__init__.py | 8 + .../fine_tuning/alpha/grader_run_params.py | 30 ++ .../fine_tuning/alpha/grader_run_response.py | 67 ++++ .../alpha/grader_validate_params.py | 24 ++ .../alpha/grader_validate_response.py | 20 ++ .../types/fine_tuning/dpo_hyperparameters.py | 36 +++ .../fine_tuning/dpo_hyperparameters_param.py | 36 +++ src/openai/types/fine_tuning/dpo_method.py | 13 + .../types/fine_tuning/dpo_method_param.py | 14 + .../types/fine_tuning/fine_tuning_job.py | 86 +----- .../types/fine_tuning/job_create_params.py | 87 +----- .../reinforcement_hyperparameters.py | 43 +++ .../reinforcement_hyperparameters_param.py | 43 +++ .../types/fine_tuning/reinforcement_method.py | 24 ++ .../fine_tuning/reinforcement_method_param.py | 27 ++ .../fine_tuning/supervised_hyperparameters.py | 29 ++ .../supervised_hyperparameters_param.py | 29 ++ .../types/fine_tuning/supervised_method.py | 13 + .../fine_tuning/supervised_method_param.py | 14 + src/openai/types/graders/__init__.py | 16 + .../label_model_grader.py} | 8 +- .../types/graders/label_model_grader_param.py | 54 ++++ src/openai/types/graders/multi_grader.py | 28 ++ .../types/graders/multi_grader_param.py | 31 ++ src/openai/types/graders/python_grader.py | 22 ++ .../types/graders/python_grader_param.py | 21 ++ .../types/graders/score_model_grader.py | 54 ++++ .../types/graders/score_model_grader_param.py | 55 ++++ .../string_check_grader.py} | 6 +- .../string_check_grader_param.py} | 4 +- .../text_similarity_grader.py} | 14 +- .../text_similarity_grader_param.py} | 11 +- .../fine_tuning/alpha/__init__.py | 1 + .../fine_tuning/alpha/test_graders.py | 289 ++++++++++++++++++ tests/api_resources/fine_tuning/test_jobs.py | 192 +++++++++++- 50 files changed, 2048 insertions(+), 566 deletions(-) create mode 100644 src/openai/resources/fine_tuning/alpha/__init__.py create mode 100644 src/openai/resources/fine_tuning/alpha/alpha.py create mode 100644 src/openai/resources/fine_tuning/alpha/graders.py create mode 100644 src/openai/types/fine_tuning/alpha/__init__.py create mode 100644 src/openai/types/fine_tuning/alpha/grader_run_params.py create mode 100644 src/openai/types/fine_tuning/alpha/grader_run_response.py create mode 100644 src/openai/types/fine_tuning/alpha/grader_validate_params.py create mode 100644 src/openai/types/fine_tuning/alpha/grader_validate_response.py create mode 100644 src/openai/types/fine_tuning/dpo_hyperparameters.py create mode 100644 src/openai/types/fine_tuning/dpo_hyperparameters_param.py create mode 100644 src/openai/types/fine_tuning/dpo_method.py create mode 100644 src/openai/types/fine_tuning/dpo_method_param.py create mode 100644 src/openai/types/fine_tuning/reinforcement_hyperparameters.py create mode 100644 src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py create mode 100644 src/openai/types/fine_tuning/reinforcement_method.py create mode 100644 src/openai/types/fine_tuning/reinforcement_method_param.py create mode 100644 src/openai/types/fine_tuning/supervised_hyperparameters.py create mode 100644 src/openai/types/fine_tuning/supervised_hyperparameters_param.py create mode 100644 src/openai/types/fine_tuning/supervised_method.py create mode 100644 src/openai/types/fine_tuning/supervised_method_param.py create mode 100644 src/openai/types/graders/__init__.py rename src/openai/types/{eval_label_model_grader.py => graders/label_model_grader.py} (85%) create mode 100644 src/openai/types/graders/label_model_grader_param.py create mode 100644 src/openai/types/graders/multi_grader.py create mode 100644 src/openai/types/graders/multi_grader_param.py create mode 100644 src/openai/types/graders/python_grader.py create mode 100644 src/openai/types/graders/python_grader_param.py create mode 100644 src/openai/types/graders/score_model_grader.py create mode 100644 src/openai/types/graders/score_model_grader_param.py rename src/openai/types/{eval_string_check_grader.py => graders/string_check_grader.py} (84%) rename src/openai/types/{eval_string_check_grader_param.py => graders/string_check_grader_param.py} (87%) rename src/openai/types/{eval_text_similarity_grader.py => graders/text_similarity_grader.py} (69%) rename src/openai/types/{eval_text_similarity_grader_param.py => graders/text_similarity_grader_param.py} (76%) create mode 100644 tests/api_resources/fine_tuning/alpha/__init__.py create mode 100644 tests/api_resources/fine_tuning/alpha/test_graders.py diff --git a/.stats.yml b/.stats.yml index 0c8278866d..5f1bee851b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 97 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0ee6b36cf3cc278cef4199a6aec5f7d530a6c1f17a74830037e96d50ca1edc50.yml -openapi_spec_hash: e8ec5f46bc0655b34f292422d58a60f6 -config_hash: d9b6b6e6bc85744663e300eebc482067 +configured_endpoints: 101 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-794a6ed3c3d3d77887564755168056af8a426b17cf1ec721e3a300503dc22a41.yml +openapi_spec_hash: 25a81c220713cd5b0bafc221d1dfa79a +config_hash: 0b768ed1b56c6d82816f0fa40dc4aaf5 diff --git a/api.md b/api.md index d04c76960e..496e5548b3 100644 --- a/api.md +++ b/api.md @@ -225,6 +225,21 @@ Methods: # FineTuning +## Methods + +Types: + +```python +from openai.types.fine_tuning import ( + DpoHyperparameters, + DpoMethod, + ReinforcementHyperparameters, + ReinforcementMethod, + SupervisedHyperparameters, + SupervisedMethod, +) +``` + ## Jobs Types: @@ -246,6 +261,8 @@ Methods: - client.fine_tuning.jobs.list(\*\*params) -> SyncCursorPage[FineTuningJob] - client.fine_tuning.jobs.cancel(fine_tuning_job_id) -> FineTuningJob - client.fine_tuning.jobs.list_events(fine_tuning_job_id, \*\*params) -> SyncCursorPage[FineTuningJobEvent] +- client.fine_tuning.jobs.pause(fine_tuning_job_id) -> FineTuningJob +- client.fine_tuning.jobs.resume(fine_tuning_job_id) -> FineTuningJob ### Checkpoints @@ -279,6 +296,38 @@ Methods: - client.fine_tuning.checkpoints.permissions.retrieve(fine_tuned_model_checkpoint, \*\*params) -> PermissionRetrieveResponse - client.fine_tuning.checkpoints.permissions.delete(permission_id, \*, fine_tuned_model_checkpoint) -> PermissionDeleteResponse +## Alpha + +### Graders + +Types: + +```python +from openai.types.fine_tuning.alpha import GraderRunResponse, GraderValidateResponse +``` + +Methods: + +- client.fine_tuning.alpha.graders.run(\*\*params) -> GraderRunResponse +- client.fine_tuning.alpha.graders.validate(\*\*params) -> GraderValidateResponse + +# Graders + +## GraderModels + +Types: + +```python +from openai.types.graders import ( + LabelModelGrader, + MultiGrader, + PythonGrader, + ScoreModelGrader, + StringCheckGrader, + TextSimilarityGrader, +) +``` + # VectorStores Types: @@ -738,10 +787,7 @@ Types: ```python from openai.types import ( EvalCustomDataSourceConfig, - EvalLabelModelGrader, EvalStoredCompletionsDataSourceConfig, - EvalStringCheckGrader, - EvalTextSimilarityGrader, EvalCreateResponse, EvalRetrieveResponse, EvalUpdateResponse, diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py index ed7db4f4e0..c76af83deb 100644 --- a/src/openai/resources/fine_tuning/__init__.py +++ b/src/openai/resources/fine_tuning/__init__.py @@ -8,6 +8,14 @@ JobsWithStreamingResponse, AsyncJobsWithStreamingResponse, ) +from .alpha import ( + Alpha, + AsyncAlpha, + AlphaWithRawResponse, + AsyncAlphaWithRawResponse, + AlphaWithStreamingResponse, + AsyncAlphaWithStreamingResponse, +) from .checkpoints import ( Checkpoints, AsyncCheckpoints, @@ -38,6 +46,12 @@ "AsyncCheckpointsWithRawResponse", "CheckpointsWithStreamingResponse", "AsyncCheckpointsWithStreamingResponse", + "Alpha", + "AsyncAlpha", + "AlphaWithRawResponse", + "AsyncAlphaWithRawResponse", + "AlphaWithStreamingResponse", + "AsyncAlphaWithStreamingResponse", "FineTuning", "AsyncFineTuning", "FineTuningWithRawResponse", diff --git a/src/openai/resources/fine_tuning/alpha/__init__.py b/src/openai/resources/fine_tuning/alpha/__init__.py new file mode 100644 index 0000000000..8bed8af4fd --- /dev/null +++ b/src/openai/resources/fine_tuning/alpha/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .alpha import ( + Alpha, + AsyncAlpha, + AlphaWithRawResponse, + AsyncAlphaWithRawResponse, + AlphaWithStreamingResponse, + AsyncAlphaWithStreamingResponse, +) +from .graders import ( + Graders, + AsyncGraders, + GradersWithRawResponse, + AsyncGradersWithRawResponse, + GradersWithStreamingResponse, + AsyncGradersWithStreamingResponse, +) + +__all__ = [ + "Graders", + "AsyncGraders", + "GradersWithRawResponse", + "AsyncGradersWithRawResponse", + "GradersWithStreamingResponse", + "AsyncGradersWithStreamingResponse", + "Alpha", + "AsyncAlpha", + "AlphaWithRawResponse", + "AsyncAlphaWithRawResponse", + "AlphaWithStreamingResponse", + "AsyncAlphaWithStreamingResponse", +] diff --git a/src/openai/resources/fine_tuning/alpha/alpha.py b/src/openai/resources/fine_tuning/alpha/alpha.py new file mode 100644 index 0000000000..54c05fab69 --- /dev/null +++ b/src/openai/resources/fine_tuning/alpha/alpha.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .graders import ( + Graders, + AsyncGraders, + GradersWithRawResponse, + AsyncGradersWithRawResponse, + GradersWithStreamingResponse, + AsyncGradersWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["Alpha", "AsyncAlpha"] + + +class Alpha(SyncAPIResource): + @cached_property + def graders(self) -> Graders: + return Graders(self._client) + + @cached_property + def with_raw_response(self) -> AlphaWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AlphaWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AlphaWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AlphaWithStreamingResponse(self) + + +class AsyncAlpha(AsyncAPIResource): + @cached_property + def graders(self) -> AsyncGraders: + return AsyncGraders(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAlphaWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAlphaWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAlphaWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAlphaWithStreamingResponse(self) + + +class AlphaWithRawResponse: + def __init__(self, alpha: Alpha) -> None: + self._alpha = alpha + + @cached_property + def graders(self) -> GradersWithRawResponse: + return GradersWithRawResponse(self._alpha.graders) + + +class AsyncAlphaWithRawResponse: + def __init__(self, alpha: AsyncAlpha) -> None: + self._alpha = alpha + + @cached_property + def graders(self) -> AsyncGradersWithRawResponse: + return AsyncGradersWithRawResponse(self._alpha.graders) + + +class AlphaWithStreamingResponse: + def __init__(self, alpha: Alpha) -> None: + self._alpha = alpha + + @cached_property + def graders(self) -> GradersWithStreamingResponse: + return GradersWithStreamingResponse(self._alpha.graders) + + +class AsyncAlphaWithStreamingResponse: + def __init__(self, alpha: AsyncAlpha) -> None: + self._alpha = alpha + + @cached_property + def graders(self) -> AsyncGradersWithStreamingResponse: + return AsyncGradersWithStreamingResponse(self._alpha.graders) diff --git a/src/openai/resources/fine_tuning/alpha/graders.py b/src/openai/resources/fine_tuning/alpha/graders.py new file mode 100644 index 0000000000..f27acdfd9c --- /dev/null +++ b/src/openai/resources/fine_tuning/alpha/graders.py @@ -0,0 +1,272 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.fine_tuning.alpha import grader_run_params, grader_validate_params +from ....types.fine_tuning.alpha.grader_run_response import GraderRunResponse +from ....types.fine_tuning.alpha.grader_validate_response import GraderValidateResponse + +__all__ = ["Graders", "AsyncGraders"] + + +class Graders(SyncAPIResource): + @cached_property + def with_raw_response(self) -> GradersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return GradersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GradersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return GradersWithStreamingResponse(self) + + def run( + self, + *, + grader: grader_run_params.Grader, + model_sample: str, + reference_answer: Union[str, Iterable[object], float, object], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> GraderRunResponse: + """ + Run a grader. + + Args: + grader: The grader used for the fine-tuning job. + + model_sample: The model sample to be evaluated. + + reference_answer: The reference answer for the evaluation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/fine_tuning/alpha/graders/run", + body=maybe_transform( + { + "grader": grader, + "model_sample": model_sample, + "reference_answer": reference_answer, + }, + grader_run_params.GraderRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GraderRunResponse, + ) + + def validate( + self, + *, + grader: grader_validate_params.Grader, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> GraderValidateResponse: + """ + Validate a grader. + + Args: + grader: The grader used for the fine-tuning job. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/fine_tuning/alpha/graders/validate", + body=maybe_transform({"grader": grader}, grader_validate_params.GraderValidateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GraderValidateResponse, + ) + + +class AsyncGraders(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncGradersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncGradersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGradersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncGradersWithStreamingResponse(self) + + async def run( + self, + *, + grader: grader_run_params.Grader, + model_sample: str, + reference_answer: Union[str, Iterable[object], float, object], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> GraderRunResponse: + """ + Run a grader. + + Args: + grader: The grader used for the fine-tuning job. + + model_sample: The model sample to be evaluated. + + reference_answer: The reference answer for the evaluation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/fine_tuning/alpha/graders/run", + body=await async_maybe_transform( + { + "grader": grader, + "model_sample": model_sample, + "reference_answer": reference_answer, + }, + grader_run_params.GraderRunParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GraderRunResponse, + ) + + async def validate( + self, + *, + grader: grader_validate_params.Grader, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> GraderValidateResponse: + """ + Validate a grader. + + Args: + grader: The grader used for the fine-tuning job. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/fine_tuning/alpha/graders/validate", + body=await async_maybe_transform({"grader": grader}, grader_validate_params.GraderValidateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GraderValidateResponse, + ) + + +class GradersWithRawResponse: + def __init__(self, graders: Graders) -> None: + self._graders = graders + + self.run = _legacy_response.to_raw_response_wrapper( + graders.run, + ) + self.validate = _legacy_response.to_raw_response_wrapper( + graders.validate, + ) + + +class AsyncGradersWithRawResponse: + def __init__(self, graders: AsyncGraders) -> None: + self._graders = graders + + self.run = _legacy_response.async_to_raw_response_wrapper( + graders.run, + ) + self.validate = _legacy_response.async_to_raw_response_wrapper( + graders.validate, + ) + + +class GradersWithStreamingResponse: + def __init__(self, graders: Graders) -> None: + self._graders = graders + + self.run = to_streamed_response_wrapper( + graders.run, + ) + self.validate = to_streamed_response_wrapper( + graders.validate, + ) + + +class AsyncGradersWithStreamingResponse: + def __init__(self, graders: AsyncGraders) -> None: + self._graders = graders + + self.run = async_to_streamed_response_wrapper( + graders.run, + ) + self.validate = async_to_streamed_response_wrapper( + graders.validate, + ) diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index 1388c8230c..25ae3e8cf4 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -12,6 +12,14 @@ AsyncJobsWithStreamingResponse, ) from ..._resource import SyncAPIResource, AsyncAPIResource +from .alpha.alpha import ( + Alpha, + AsyncAlpha, + AlphaWithRawResponse, + AsyncAlphaWithRawResponse, + AlphaWithStreamingResponse, + AsyncAlphaWithStreamingResponse, +) from .checkpoints.checkpoints import ( Checkpoints, AsyncCheckpoints, @@ -33,6 +41,10 @@ def jobs(self) -> Jobs: def checkpoints(self) -> Checkpoints: return Checkpoints(self._client) + @cached_property + def alpha(self) -> Alpha: + return Alpha(self._client) + @cached_property def with_raw_response(self) -> FineTuningWithRawResponse: """ @@ -62,6 +74,10 @@ def jobs(self) -> AsyncJobs: def checkpoints(self) -> AsyncCheckpoints: return AsyncCheckpoints(self._client) + @cached_property + def alpha(self) -> AsyncAlpha: + return AsyncAlpha(self._client) + @cached_property def with_raw_response(self) -> AsyncFineTuningWithRawResponse: """ @@ -94,6 +110,10 @@ def jobs(self) -> JobsWithRawResponse: def checkpoints(self) -> CheckpointsWithRawResponse: return CheckpointsWithRawResponse(self._fine_tuning.checkpoints) + @cached_property + def alpha(self) -> AlphaWithRawResponse: + return AlphaWithRawResponse(self._fine_tuning.alpha) + class AsyncFineTuningWithRawResponse: def __init__(self, fine_tuning: AsyncFineTuning) -> None: @@ -107,6 +127,10 @@ def jobs(self) -> AsyncJobsWithRawResponse: def checkpoints(self) -> AsyncCheckpointsWithRawResponse: return AsyncCheckpointsWithRawResponse(self._fine_tuning.checkpoints) + @cached_property + def alpha(self) -> AsyncAlphaWithRawResponse: + return AsyncAlphaWithRawResponse(self._fine_tuning.alpha) + class FineTuningWithStreamingResponse: def __init__(self, fine_tuning: FineTuning) -> None: @@ -120,6 +144,10 @@ def jobs(self) -> JobsWithStreamingResponse: def checkpoints(self) -> CheckpointsWithStreamingResponse: return CheckpointsWithStreamingResponse(self._fine_tuning.checkpoints) + @cached_property + def alpha(self) -> AlphaWithStreamingResponse: + return AlphaWithStreamingResponse(self._fine_tuning.alpha) + class AsyncFineTuningWithStreamingResponse: def __init__(self, fine_tuning: AsyncFineTuning) -> None: @@ -132,3 +160,7 @@ def jobs(self) -> AsyncJobsWithStreamingResponse: @cached_property def checkpoints(self) -> AsyncCheckpointsWithStreamingResponse: return AsyncCheckpointsWithStreamingResponse(self._fine_tuning.checkpoints) + + @cached_property + def alpha(self) -> AsyncAlphaWithStreamingResponse: + return AsyncAlphaWithStreamingResponse(self._fine_tuning.alpha) diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index 90619c8609..5cca219172 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -345,6 +345,72 @@ def list_events( model=FineTuningJobEvent, ) + def pause( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Pause a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._post( + f"/fine_tuning/jobs/{fine_tuning_job_id}/pause", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + + def resume( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Resume a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return self._post( + f"/fine_tuning/jobs/{fine_tuning_job_id}/resume", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + class AsyncJobs(AsyncAPIResource): @cached_property @@ -657,6 +723,72 @@ def list_events( model=FineTuningJobEvent, ) + async def pause( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Pause a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return await self._post( + f"/fine_tuning/jobs/{fine_tuning_job_id}/pause", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + + async def resume( + self, + fine_tuning_job_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> FineTuningJob: + """ + Resume a fine-tune job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuning_job_id: + raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") + return await self._post( + f"/fine_tuning/jobs/{fine_tuning_job_id}/resume", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FineTuningJob, + ) + class JobsWithRawResponse: def __init__(self, jobs: Jobs) -> None: @@ -677,6 +809,12 @@ def __init__(self, jobs: Jobs) -> None: self.list_events = _legacy_response.to_raw_response_wrapper( jobs.list_events, ) + self.pause = _legacy_response.to_raw_response_wrapper( + jobs.pause, + ) + self.resume = _legacy_response.to_raw_response_wrapper( + jobs.resume, + ) @cached_property def checkpoints(self) -> CheckpointsWithRawResponse: @@ -702,6 +840,12 @@ def __init__(self, jobs: AsyncJobs) -> None: self.list_events = _legacy_response.async_to_raw_response_wrapper( jobs.list_events, ) + self.pause = _legacy_response.async_to_raw_response_wrapper( + jobs.pause, + ) + self.resume = _legacy_response.async_to_raw_response_wrapper( + jobs.resume, + ) @cached_property def checkpoints(self) -> AsyncCheckpointsWithRawResponse: @@ -727,6 +871,12 @@ def __init__(self, jobs: Jobs) -> None: self.list_events = to_streamed_response_wrapper( jobs.list_events, ) + self.pause = to_streamed_response_wrapper( + jobs.pause, + ) + self.resume = to_streamed_response_wrapper( + jobs.resume, + ) @cached_property def checkpoints(self) -> CheckpointsWithStreamingResponse: @@ -752,6 +902,12 @@ def __init__(self, jobs: AsyncJobs) -> None: self.list_events = async_to_streamed_response_wrapper( jobs.list_events, ) + self.pause = async_to_streamed_response_wrapper( + jobs.pause, + ) + self.resume = async_to_streamed_response_wrapper( + jobs.resume, + ) @cached_property def checkpoints(self) -> AsyncCheckpointsWithStreamingResponse: diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 57c91811b9..bf5493fd62 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -61,9 +61,7 @@ from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy from .upload_complete_params import UploadCompleteParams as UploadCompleteParams from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams -from .eval_label_model_grader import EvalLabelModelGrader as EvalLabelModelGrader from .completion_create_params import CompletionCreateParams as CompletionCreateParams -from .eval_string_check_grader import EvalStringCheckGrader as EvalStringCheckGrader from .moderation_create_params import ModerationCreateParams as ModerationCreateParams from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse @@ -71,7 +69,6 @@ from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams from .vector_store_search_params import VectorStoreSearchParams as VectorStoreSearchParams from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams -from .eval_text_similarity_grader import EvalTextSimilarityGrader as EvalTextSimilarityGrader from .moderation_text_input_param import ModerationTextInputParam as ModerationTextInputParam from .file_chunking_strategy_param import FileChunkingStrategyParam as FileChunkingStrategyParam from .vector_store_search_response import VectorStoreSearchResponse as VectorStoreSearchResponse @@ -79,10 +76,8 @@ from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy from .eval_custom_data_source_config import EvalCustomDataSourceConfig as EvalCustomDataSourceConfig -from .eval_string_check_grader_param import EvalStringCheckGraderParam as EvalStringCheckGraderParam from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam as AutoFileChunkingStrategyParam -from .eval_text_similarity_grader_param import EvalTextSimilarityGraderParam as EvalTextSimilarityGraderParam from .moderation_multi_modal_input_param import ModerationMultiModalInputParam as ModerationMultiModalInputParam from .other_file_chunking_strategy_object import OtherFileChunkingStrategyObject as OtherFileChunkingStrategyObject from .static_file_chunking_strategy_param import StaticFileChunkingStrategyParam as StaticFileChunkingStrategyParam diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index 03f44f2c8c..66178287e4 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -6,15 +6,17 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from .shared_params.metadata import Metadata -from .eval_string_check_grader_param import EvalStringCheckGraderParam -from .eval_text_similarity_grader_param import EvalTextSimilarityGraderParam +from .graders.python_grader_param import PythonGraderParam +from .graders.score_model_grader_param import ScoreModelGraderParam +from .graders.string_check_grader_param import StringCheckGraderParam from .responses.response_input_text_param import ResponseInputTextParam +from .graders.text_similarity_grader_param import TextSimilarityGraderParam __all__ = [ "EvalCreateParams", "DataSourceConfig", "DataSourceConfigCustom", - "DataSourceConfigLogs", + "DataSourceConfigStoredCompletions", "TestingCriterion", "TestingCriterionLabelModel", "TestingCriterionLabelModelInput", @@ -22,11 +24,9 @@ "TestingCriterionLabelModelInputEvalItem", "TestingCriterionLabelModelInputEvalItemContent", "TestingCriterionLabelModelInputEvalItemContentOutputText", + "TestingCriterionTextSimilarity", "TestingCriterionPython", "TestingCriterionScoreModel", - "TestingCriterionScoreModelInput", - "TestingCriterionScoreModelInputContent", - "TestingCriterionScoreModelInputContentOutputText", ] @@ -65,15 +65,15 @@ class DataSourceConfigCustom(TypedDict, total=False): """ -class DataSourceConfigLogs(TypedDict, total=False): - type: Required[Literal["logs"]] - """The type of data source. Always `logs`.""" +class DataSourceConfigStoredCompletions(TypedDict, total=False): + type: Required[Literal["stored_completions"]] + """The type of data source. Always `stored_completions`.""" metadata: Dict[str, object] - """Metadata filters for the logs data source.""" + """Metadata filters for the stored completions data source.""" -DataSourceConfig: TypeAlias = Union[DataSourceConfigCustom, DataSourceConfigLogs] +DataSourceConfig: TypeAlias = Union[DataSourceConfigCustom, DataSourceConfigStoredCompletions] class TestingCriterionLabelModelInputSimpleInputMessage(TypedDict, total=False): @@ -139,77 +139,28 @@ class TestingCriterionLabelModel(TypedDict, total=False): """The object type, which is always `label_model`.""" -class TestingCriterionPython(TypedDict, total=False): - name: Required[str] - """The name of the grader.""" - - source: Required[str] - """The source code of the python script.""" - - type: Required[Literal["python"]] - """The object type, which is always `python`.""" +class TestingCriterionTextSimilarity(TextSimilarityGraderParam, total=False): + __test__ = False + pass_threshold: Required[float] + """The threshold for the score.""" - image_tag: str - """The image tag to use for the python script.""" +class TestingCriterionPython(PythonGraderParam, total=False): + __test__ = False pass_threshold: float """The threshold for the score.""" -class TestingCriterionScoreModelInputContentOutputText(TypedDict, total=False): - text: Required[str] - """The text output from the model.""" - - type: Required[Literal["output_text"]] - """The type of the output text. Always `output_text`.""" - - -TestingCriterionScoreModelInputContent: TypeAlias = Union[ - str, ResponseInputTextParam, TestingCriterionScoreModelInputContentOutputText -] - - -class TestingCriterionScoreModelInput(TypedDict, total=False): - content: Required[TestingCriterionScoreModelInputContent] - """Text inputs to the model - can contain template strings.""" - - role: Required[Literal["user", "assistant", "system", "developer"]] - """The role of the message input. - - One of `user`, `assistant`, `system`, or `developer`. - """ - - type: Literal["message"] - """The type of the message input. Always `message`.""" - - -class TestingCriterionScoreModel(TypedDict, total=False): - input: Required[Iterable[TestingCriterionScoreModelInput]] - """The input text. This may include template strings.""" - - model: Required[str] - """The model to use for the evaluation.""" - - name: Required[str] - """The name of the grader.""" - - type: Required[Literal["score_model"]] - """The object type, which is always `score_model`.""" - +class TestingCriterionScoreModel(ScoreModelGraderParam, total=False): + __test__ = False pass_threshold: float """The threshold for the score.""" - range: Iterable[float] - """The range of the score. Defaults to `[0, 1]`.""" - - sampling_params: object - """The sampling parameters for the model.""" - TestingCriterion: TypeAlias = Union[ TestingCriterionLabelModel, - EvalStringCheckGraderParam, - EvalTextSimilarityGraderParam, + StringCheckGraderParam, + TestingCriterionTextSimilarity, TestingCriterionPython, TestingCriterionScoreModel, ] diff --git a/src/openai/types/eval_create_response.py b/src/openai/types/eval_create_response.py index 6d77a81870..d5f158ad29 100644 --- a/src/openai/types/eval_create_response.py +++ b/src/openai/types/eval_create_response.py @@ -6,22 +6,21 @@ from .._utils import PropertyInfo from .._models import BaseModel from .shared.metadata import Metadata -from .eval_label_model_grader import EvalLabelModelGrader -from .eval_string_check_grader import EvalStringCheckGrader -from .eval_text_similarity_grader import EvalTextSimilarityGrader -from .responses.response_input_text import ResponseInputText +from .graders.python_grader import PythonGrader +from .graders.label_model_grader import LabelModelGrader +from .graders.score_model_grader import ScoreModelGrader +from .graders.string_check_grader import StringCheckGrader from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .graders.text_similarity_grader import TextSimilarityGrader from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig __all__ = [ "EvalCreateResponse", "DataSourceConfig", "TestingCriterion", - "TestingCriterionPython", - "TestingCriterionScoreModel", - "TestingCriterionScoreModelInput", - "TestingCriterionScoreModelInputContent", - "TestingCriterionScoreModelInputContentOutputText", + "TestingCriterionEvalGraderTextSimilarity", + "TestingCriterionEvalGraderPython", + "TestingCriterionEvalGraderScoreModel", ] DataSourceConfig: TypeAlias = Annotated[ @@ -29,86 +28,30 @@ ] -class TestingCriterionPython(BaseModel): +class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): __test__ = False - name: str - """The name of the grader.""" - - source: str - """The source code of the python script.""" - - type: Literal["python"] - """The object type, which is always `python`.""" - - image_tag: Optional[str] = None - """The image tag to use for the python script.""" - - pass_threshold: Optional[float] = None + pass_threshold: float """The threshold for the score.""" -class TestingCriterionScoreModelInputContentOutputText(BaseModel): - __test__ = False - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -TestingCriterionScoreModelInputContent: TypeAlias = Union[ - str, ResponseInputText, TestingCriterionScoreModelInputContentOutputText -] - - -class TestingCriterionScoreModelInput(BaseModel): +class TestingCriterionEvalGraderPython(PythonGrader): __test__ = False - content: TestingCriterionScoreModelInputContent - """Text inputs to the model - can contain template strings.""" - - role: Literal["user", "assistant", "system", "developer"] - """The role of the message input. - - One of `user`, `assistant`, `system`, or `developer`. - """ - - type: Optional[Literal["message"]] = None - """The type of the message input. Always `message`.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" -class TestingCriterionScoreModel(BaseModel): +class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): __test__ = False - input: List[TestingCriterionScoreModelInput] - """The input text. This may include template strings.""" - - model: str - """The model to use for the evaluation.""" - - name: str - """The name of the grader.""" - - type: Literal["score_model"] - """The object type, which is always `score_model`.""" - pass_threshold: Optional[float] = None """The threshold for the score.""" - range: Optional[List[float]] = None - """The range of the score. Defaults to `[0, 1]`.""" - - sampling_params: Optional[object] = None - """The sampling parameters for the model.""" - -TestingCriterion: TypeAlias = Annotated[ - Union[ - EvalLabelModelGrader, - EvalStringCheckGrader, - EvalTextSimilarityGrader, - TestingCriterionPython, - TestingCriterionScoreModel, - ], - PropertyInfo(discriminator="type"), +TestingCriterion: TypeAlias = Union[ + LabelModelGrader, + StringCheckGrader, + TestingCriterionEvalGraderTextSimilarity, + TestingCriterionEvalGraderPython, + TestingCriterionEvalGraderScoreModel, ] diff --git a/src/openai/types/eval_list_response.py b/src/openai/types/eval_list_response.py index 8c7e9c5588..b743f57f6a 100644 --- a/src/openai/types/eval_list_response.py +++ b/src/openai/types/eval_list_response.py @@ -6,22 +6,21 @@ from .._utils import PropertyInfo from .._models import BaseModel from .shared.metadata import Metadata -from .eval_label_model_grader import EvalLabelModelGrader -from .eval_string_check_grader import EvalStringCheckGrader -from .eval_text_similarity_grader import EvalTextSimilarityGrader -from .responses.response_input_text import ResponseInputText +from .graders.python_grader import PythonGrader +from .graders.label_model_grader import LabelModelGrader +from .graders.score_model_grader import ScoreModelGrader +from .graders.string_check_grader import StringCheckGrader from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .graders.text_similarity_grader import TextSimilarityGrader from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig __all__ = [ "EvalListResponse", "DataSourceConfig", "TestingCriterion", - "TestingCriterionPython", - "TestingCriterionScoreModel", - "TestingCriterionScoreModelInput", - "TestingCriterionScoreModelInputContent", - "TestingCriterionScoreModelInputContentOutputText", + "TestingCriterionEvalGraderTextSimilarity", + "TestingCriterionEvalGraderPython", + "TestingCriterionEvalGraderScoreModel", ] DataSourceConfig: TypeAlias = Annotated[ @@ -29,86 +28,30 @@ ] -class TestingCriterionPython(BaseModel): +class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): __test__ = False - name: str - """The name of the grader.""" - - source: str - """The source code of the python script.""" - - type: Literal["python"] - """The object type, which is always `python`.""" - - image_tag: Optional[str] = None - """The image tag to use for the python script.""" - - pass_threshold: Optional[float] = None + pass_threshold: float """The threshold for the score.""" -class TestingCriterionScoreModelInputContentOutputText(BaseModel): - __test__ = False - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -TestingCriterionScoreModelInputContent: TypeAlias = Union[ - str, ResponseInputText, TestingCriterionScoreModelInputContentOutputText -] - - -class TestingCriterionScoreModelInput(BaseModel): +class TestingCriterionEvalGraderPython(PythonGrader): __test__ = False - content: TestingCriterionScoreModelInputContent - """Text inputs to the model - can contain template strings.""" - - role: Literal["user", "assistant", "system", "developer"] - """The role of the message input. - - One of `user`, `assistant`, `system`, or `developer`. - """ - - type: Optional[Literal["message"]] = None - """The type of the message input. Always `message`.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" -class TestingCriterionScoreModel(BaseModel): +class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): __test__ = False - input: List[TestingCriterionScoreModelInput] - """The input text. This may include template strings.""" - - model: str - """The model to use for the evaluation.""" - - name: str - """The name of the grader.""" - - type: Literal["score_model"] - """The object type, which is always `score_model`.""" - pass_threshold: Optional[float] = None """The threshold for the score.""" - range: Optional[List[float]] = None - """The range of the score. Defaults to `[0, 1]`.""" - - sampling_params: Optional[object] = None - """The sampling parameters for the model.""" - -TestingCriterion: TypeAlias = Annotated[ - Union[ - EvalLabelModelGrader, - EvalStringCheckGrader, - EvalTextSimilarityGrader, - TestingCriterionPython, - TestingCriterionScoreModel, - ], - PropertyInfo(discriminator="type"), +TestingCriterion: TypeAlias = Union[ + LabelModelGrader, + StringCheckGrader, + TestingCriterionEvalGraderTextSimilarity, + TestingCriterionEvalGraderPython, + TestingCriterionEvalGraderScoreModel, ] diff --git a/src/openai/types/eval_retrieve_response.py b/src/openai/types/eval_retrieve_response.py index 625bae80f4..dabb20674e 100644 --- a/src/openai/types/eval_retrieve_response.py +++ b/src/openai/types/eval_retrieve_response.py @@ -6,22 +6,21 @@ from .._utils import PropertyInfo from .._models import BaseModel from .shared.metadata import Metadata -from .eval_label_model_grader import EvalLabelModelGrader -from .eval_string_check_grader import EvalStringCheckGrader -from .eval_text_similarity_grader import EvalTextSimilarityGrader -from .responses.response_input_text import ResponseInputText +from .graders.python_grader import PythonGrader +from .graders.label_model_grader import LabelModelGrader +from .graders.score_model_grader import ScoreModelGrader +from .graders.string_check_grader import StringCheckGrader from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .graders.text_similarity_grader import TextSimilarityGrader from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig __all__ = [ "EvalRetrieveResponse", "DataSourceConfig", "TestingCriterion", - "TestingCriterionPython", - "TestingCriterionScoreModel", - "TestingCriterionScoreModelInput", - "TestingCriterionScoreModelInputContent", - "TestingCriterionScoreModelInputContentOutputText", + "TestingCriterionEvalGraderTextSimilarity", + "TestingCriterionEvalGraderPython", + "TestingCriterionEvalGraderScoreModel", ] DataSourceConfig: TypeAlias = Annotated[ @@ -29,86 +28,30 @@ ] -class TestingCriterionPython(BaseModel): +class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): __test__ = False - name: str - """The name of the grader.""" - - source: str - """The source code of the python script.""" - - type: Literal["python"] - """The object type, which is always `python`.""" - - image_tag: Optional[str] = None - """The image tag to use for the python script.""" - - pass_threshold: Optional[float] = None + pass_threshold: float """The threshold for the score.""" -class TestingCriterionScoreModelInputContentOutputText(BaseModel): - __test__ = False - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -TestingCriterionScoreModelInputContent: TypeAlias = Union[ - str, ResponseInputText, TestingCriterionScoreModelInputContentOutputText -] - - -class TestingCriterionScoreModelInput(BaseModel): +class TestingCriterionEvalGraderPython(PythonGrader): __test__ = False - content: TestingCriterionScoreModelInputContent - """Text inputs to the model - can contain template strings.""" - - role: Literal["user", "assistant", "system", "developer"] - """The role of the message input. - - One of `user`, `assistant`, `system`, or `developer`. - """ - - type: Optional[Literal["message"]] = None - """The type of the message input. Always `message`.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" -class TestingCriterionScoreModel(BaseModel): +class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): __test__ = False - input: List[TestingCriterionScoreModelInput] - """The input text. This may include template strings.""" - - model: str - """The model to use for the evaluation.""" - - name: str - """The name of the grader.""" - - type: Literal["score_model"] - """The object type, which is always `score_model`.""" - pass_threshold: Optional[float] = None """The threshold for the score.""" - range: Optional[List[float]] = None - """The range of the score. Defaults to `[0, 1]`.""" - - sampling_params: Optional[object] = None - """The sampling parameters for the model.""" - -TestingCriterion: TypeAlias = Annotated[ - Union[ - EvalLabelModelGrader, - EvalStringCheckGrader, - EvalTextSimilarityGrader, - TestingCriterionPython, - TestingCriterionScoreModel, - ], - PropertyInfo(discriminator="type"), +TestingCriterion: TypeAlias = Union[ + LabelModelGrader, + StringCheckGrader, + TestingCriterionEvalGraderTextSimilarity, + TestingCriterionEvalGraderPython, + TestingCriterionEvalGraderScoreModel, ] diff --git a/src/openai/types/eval_update_response.py b/src/openai/types/eval_update_response.py index 2c280977a1..c5cb2622ea 100644 --- a/src/openai/types/eval_update_response.py +++ b/src/openai/types/eval_update_response.py @@ -6,22 +6,21 @@ from .._utils import PropertyInfo from .._models import BaseModel from .shared.metadata import Metadata -from .eval_label_model_grader import EvalLabelModelGrader -from .eval_string_check_grader import EvalStringCheckGrader -from .eval_text_similarity_grader import EvalTextSimilarityGrader -from .responses.response_input_text import ResponseInputText +from .graders.python_grader import PythonGrader +from .graders.label_model_grader import LabelModelGrader +from .graders.score_model_grader import ScoreModelGrader +from .graders.string_check_grader import StringCheckGrader from .eval_custom_data_source_config import EvalCustomDataSourceConfig +from .graders.text_similarity_grader import TextSimilarityGrader from .eval_stored_completions_data_source_config import EvalStoredCompletionsDataSourceConfig __all__ = [ "EvalUpdateResponse", "DataSourceConfig", "TestingCriterion", - "TestingCriterionPython", - "TestingCriterionScoreModel", - "TestingCriterionScoreModelInput", - "TestingCriterionScoreModelInputContent", - "TestingCriterionScoreModelInputContentOutputText", + "TestingCriterionEvalGraderTextSimilarity", + "TestingCriterionEvalGraderPython", + "TestingCriterionEvalGraderScoreModel", ] DataSourceConfig: TypeAlias = Annotated[ @@ -29,86 +28,30 @@ ] -class TestingCriterionPython(BaseModel): +class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): __test__ = False - name: str - """The name of the grader.""" - - source: str - """The source code of the python script.""" - - type: Literal["python"] - """The object type, which is always `python`.""" - - image_tag: Optional[str] = None - """The image tag to use for the python script.""" - - pass_threshold: Optional[float] = None + pass_threshold: float """The threshold for the score.""" -class TestingCriterionScoreModelInputContentOutputText(BaseModel): - __test__ = False - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -TestingCriterionScoreModelInputContent: TypeAlias = Union[ - str, ResponseInputText, TestingCriterionScoreModelInputContentOutputText -] - - -class TestingCriterionScoreModelInput(BaseModel): +class TestingCriterionEvalGraderPython(PythonGrader): __test__ = False - content: TestingCriterionScoreModelInputContent - """Text inputs to the model - can contain template strings.""" - - role: Literal["user", "assistant", "system", "developer"] - """The role of the message input. - - One of `user`, `assistant`, `system`, or `developer`. - """ - - type: Optional[Literal["message"]] = None - """The type of the message input. Always `message`.""" + pass_threshold: Optional[float] = None + """The threshold for the score.""" -class TestingCriterionScoreModel(BaseModel): +class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): __test__ = False - input: List[TestingCriterionScoreModelInput] - """The input text. This may include template strings.""" - - model: str - """The model to use for the evaluation.""" - - name: str - """The name of the grader.""" - - type: Literal["score_model"] - """The object type, which is always `score_model`.""" - pass_threshold: Optional[float] = None """The threshold for the score.""" - range: Optional[List[float]] = None - """The range of the score. Defaults to `[0, 1]`.""" - - sampling_params: Optional[object] = None - """The sampling parameters for the model.""" - -TestingCriterion: TypeAlias = Annotated[ - Union[ - EvalLabelModelGrader, - EvalStringCheckGrader, - EvalTextSimilarityGrader, - TestingCriterionPython, - TestingCriterionScoreModel, - ], - PropertyInfo(discriminator="type"), +TestingCriterion: TypeAlias = Union[ + LabelModelGrader, + StringCheckGrader, + TestingCriterionEvalGraderTextSimilarity, + TestingCriterionEvalGraderPython, + TestingCriterionEvalGraderScoreModel, ] diff --git a/src/openai/types/fine_tuning/__init__.py b/src/openai/types/fine_tuning/__init__.py index 92b81329b1..cc664eacea 100644 --- a/src/openai/types/fine_tuning/__init__.py +++ b/src/openai/types/fine_tuning/__init__.py @@ -2,13 +2,25 @@ from __future__ import annotations +from .dpo_method import DpoMethod as DpoMethod from .fine_tuning_job import FineTuningJob as FineTuningJob from .job_list_params import JobListParams as JobListParams +from .dpo_method_param import DpoMethodParam as DpoMethodParam from .job_create_params import JobCreateParams as JobCreateParams +from .supervised_method import SupervisedMethod as SupervisedMethod +from .dpo_hyperparameters import DpoHyperparameters as DpoHyperparameters +from .reinforcement_method import ReinforcementMethod as ReinforcementMethod from .fine_tuning_job_event import FineTuningJobEvent as FineTuningJobEvent from .job_list_events_params import JobListEventsParams as JobListEventsParams +from .supervised_method_param import SupervisedMethodParam as SupervisedMethodParam +from .dpo_hyperparameters_param import DpoHyperparametersParam as DpoHyperparametersParam +from .reinforcement_method_param import ReinforcementMethodParam as ReinforcementMethodParam +from .supervised_hyperparameters import SupervisedHyperparameters as SupervisedHyperparameters from .fine_tuning_job_integration import FineTuningJobIntegration as FineTuningJobIntegration +from .reinforcement_hyperparameters import ReinforcementHyperparameters as ReinforcementHyperparameters +from .supervised_hyperparameters_param import SupervisedHyperparametersParam as SupervisedHyperparametersParam from .fine_tuning_job_wandb_integration import FineTuningJobWandbIntegration as FineTuningJobWandbIntegration +from .reinforcement_hyperparameters_param import ReinforcementHyperparametersParam as ReinforcementHyperparametersParam from .fine_tuning_job_wandb_integration_object import ( FineTuningJobWandbIntegrationObject as FineTuningJobWandbIntegrationObject, ) diff --git a/src/openai/types/fine_tuning/alpha/__init__.py b/src/openai/types/fine_tuning/alpha/__init__.py new file mode 100644 index 0000000000..6394961b0b --- /dev/null +++ b/src/openai/types/fine_tuning/alpha/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .grader_run_params import GraderRunParams as GraderRunParams +from .grader_run_response import GraderRunResponse as GraderRunResponse +from .grader_validate_params import GraderValidateParams as GraderValidateParams +from .grader_validate_response import GraderValidateResponse as GraderValidateResponse diff --git a/src/openai/types/fine_tuning/alpha/grader_run_params.py b/src/openai/types/fine_tuning/alpha/grader_run_params.py new file mode 100644 index 0000000000..fa729f55ba --- /dev/null +++ b/src/openai/types/fine_tuning/alpha/grader_run_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Required, TypeAlias, TypedDict + +from ...graders.multi_grader_param import MultiGraderParam +from ...graders.python_grader_param import PythonGraderParam +from ...graders.score_model_grader_param import ScoreModelGraderParam +from ...graders.string_check_grader_param import StringCheckGraderParam +from ...graders.text_similarity_grader_param import TextSimilarityGraderParam + +__all__ = ["GraderRunParams", "Grader"] + + +class GraderRunParams(TypedDict, total=False): + grader: Required[Grader] + """The grader used for the fine-tuning job.""" + + model_sample: Required[str] + """The model sample to be evaluated.""" + + reference_answer: Required[Union[str, Iterable[object], float, object]] + """The reference answer for the evaluation.""" + + +Grader: TypeAlias = Union[ + StringCheckGraderParam, TextSimilarityGraderParam, PythonGraderParam, ScoreModelGraderParam, MultiGraderParam +] diff --git a/src/openai/types/fine_tuning/alpha/grader_run_response.py b/src/openai/types/fine_tuning/alpha/grader_run_response.py new file mode 100644 index 0000000000..8ef046d133 --- /dev/null +++ b/src/openai/types/fine_tuning/alpha/grader_run_response.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = ["GraderRunResponse", "Metadata", "MetadataErrors"] + + +class MetadataErrors(BaseModel): + formula_parse_error: bool + + invalid_variable_error: bool + + api_model_grader_parse_error: bool = FieldInfo(alias="model_grader_parse_error") + + api_model_grader_refusal_error: bool = FieldInfo(alias="model_grader_refusal_error") + + api_model_grader_server_error: bool = FieldInfo(alias="model_grader_server_error") + + api_model_grader_server_error_details: Optional[str] = FieldInfo( + alias="model_grader_server_error_details", default=None + ) + + other_error: bool + + python_grader_runtime_error: bool + + python_grader_runtime_error_details: Optional[str] = None + + python_grader_server_error: bool + + python_grader_server_error_type: Optional[str] = None + + sample_parse_error: bool + + truncated_observation_error: bool + + unresponsive_reward_error: bool + + +class Metadata(BaseModel): + errors: MetadataErrors + + execution_time: float + + name: str + + sampled_model_name: Optional[str] = None + + scores: Dict[str, object] + + token_usage: Optional[int] = None + + type: str + + +class GraderRunResponse(BaseModel): + metadata: Metadata + + api_model_grader_token_usage_per_model: Dict[str, object] = FieldInfo(alias="model_grader_token_usage_per_model") + + reward: float + + sub_rewards: Dict[str, object] diff --git a/src/openai/types/fine_tuning/alpha/grader_validate_params.py b/src/openai/types/fine_tuning/alpha/grader_validate_params.py new file mode 100644 index 0000000000..fe9eb44e32 --- /dev/null +++ b/src/openai/types/fine_tuning/alpha/grader_validate_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from ...graders.multi_grader_param import MultiGraderParam +from ...graders.python_grader_param import PythonGraderParam +from ...graders.score_model_grader_param import ScoreModelGraderParam +from ...graders.string_check_grader_param import StringCheckGraderParam +from ...graders.text_similarity_grader_param import TextSimilarityGraderParam + +__all__ = ["GraderValidateParams", "Grader"] + + +class GraderValidateParams(TypedDict, total=False): + grader: Required[Grader] + """The grader used for the fine-tuning job.""" + + +Grader: TypeAlias = Union[ + StringCheckGraderParam, TextSimilarityGraderParam, PythonGraderParam, ScoreModelGraderParam, MultiGraderParam +] diff --git a/src/openai/types/fine_tuning/alpha/grader_validate_response.py b/src/openai/types/fine_tuning/alpha/grader_validate_response.py new file mode 100644 index 0000000000..b373292d80 --- /dev/null +++ b/src/openai/types/fine_tuning/alpha/grader_validate_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import TypeAlias + +from ...._models import BaseModel +from ...graders.multi_grader import MultiGrader +from ...graders.python_grader import PythonGrader +from ...graders.score_model_grader import ScoreModelGrader +from ...graders.string_check_grader import StringCheckGrader +from ...graders.text_similarity_grader import TextSimilarityGrader + +__all__ = ["GraderValidateResponse", "Grader"] + +Grader: TypeAlias = Union[StringCheckGrader, TextSimilarityGrader, PythonGrader, ScoreModelGrader, MultiGrader] + + +class GraderValidateResponse(BaseModel): + grader: Optional[Grader] = None + """The grader used for the fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/dpo_hyperparameters.py b/src/openai/types/fine_tuning/dpo_hyperparameters.py new file mode 100644 index 0000000000..b0b3f0581b --- /dev/null +++ b/src/openai/types/fine_tuning/dpo_hyperparameters.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["DpoHyperparameters"] + + +class DpoHyperparameters(BaseModel): + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + beta: Union[Literal["auto"], float, None] = None + """The beta value for the DPO method. + + A higher beta value will increase the weight of the penalty between the policy + and reference model. + """ + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/src/openai/types/fine_tuning/dpo_hyperparameters_param.py b/src/openai/types/fine_tuning/dpo_hyperparameters_param.py new file mode 100644 index 0000000000..87c6ee80a5 --- /dev/null +++ b/src/openai/types/fine_tuning/dpo_hyperparameters_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +__all__ = ["DpoHyperparametersParam"] + + +class DpoHyperparametersParam(TypedDict, total=False): + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + beta: Union[Literal["auto"], float] + """The beta value for the DPO method. + + A higher beta value will increase the weight of the penalty between the policy + and reference model. + """ + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/src/openai/types/fine_tuning/dpo_method.py b/src/openai/types/fine_tuning/dpo_method.py new file mode 100644 index 0000000000..3e20f360dd --- /dev/null +++ b/src/openai/types/fine_tuning/dpo_method.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .dpo_hyperparameters import DpoHyperparameters + +__all__ = ["DpoMethod"] + + +class DpoMethod(BaseModel): + hyperparameters: Optional[DpoHyperparameters] = None + """The hyperparameters used for the DPO fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/dpo_method_param.py b/src/openai/types/fine_tuning/dpo_method_param.py new file mode 100644 index 0000000000..ce6b6510f6 --- /dev/null +++ b/src/openai/types/fine_tuning/dpo_method_param.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 TypedDict + +from .dpo_hyperparameters_param import DpoHyperparametersParam + +__all__ = ["DpoMethodParam"] + + +class DpoMethodParam(TypedDict, total=False): + hyperparameters: DpoHyperparametersParam + """The hyperparameters used for the DPO fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index c7fff2b7b1..f626fbba64 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -4,19 +4,13 @@ from typing_extensions import Literal from ..._models import BaseModel +from .dpo_method import DpoMethod from ..shared.metadata import Metadata +from .supervised_method import SupervisedMethod +from .reinforcement_method import ReinforcementMethod from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject -__all__ = [ - "FineTuningJob", - "Error", - "Hyperparameters", - "Method", - "MethodDpo", - "MethodDpoHyperparameters", - "MethodSupervised", - "MethodSupervisedHyperparameters", -] +__all__ = ["FineTuningJob", "Error", "Hyperparameters", "Method"] class Error(BaseModel): @@ -54,74 +48,18 @@ class Hyperparameters(BaseModel): """ -class MethodDpoHyperparameters(BaseModel): - batch_size: Union[Literal["auto"], int, None] = None - """Number of examples in each batch. - - A larger batch size means that model parameters are updated less frequently, but - with lower variance. - """ - - beta: Union[Literal["auto"], float, None] = None - """The beta value for the DPO method. - - A higher beta value will increase the weight of the penalty between the policy - and reference model. - """ - - learning_rate_multiplier: Union[Literal["auto"], float, None] = None - """Scaling factor for the learning rate. - - A smaller learning rate may be useful to avoid overfitting. - """ - - n_epochs: Union[Literal["auto"], int, None] = None - """The number of epochs to train the model for. - - An epoch refers to one full cycle through the training dataset. - """ - - -class MethodDpo(BaseModel): - hyperparameters: Optional[MethodDpoHyperparameters] = None - """The hyperparameters used for the fine-tuning job.""" - - -class MethodSupervisedHyperparameters(BaseModel): - batch_size: Union[Literal["auto"], int, None] = None - """Number of examples in each batch. - - A larger batch size means that model parameters are updated less frequently, but - with lower variance. - """ - - learning_rate_multiplier: Union[Literal["auto"], float, None] = None - """Scaling factor for the learning rate. - - A smaller learning rate may be useful to avoid overfitting. - """ - - n_epochs: Union[Literal["auto"], int, None] = None - """The number of epochs to train the model for. - - An epoch refers to one full cycle through the training dataset. - """ - - -class MethodSupervised(BaseModel): - hyperparameters: Optional[MethodSupervisedHyperparameters] = None - """The hyperparameters used for the fine-tuning job.""" - - class Method(BaseModel): - dpo: Optional[MethodDpo] = None + type: Literal["supervised", "dpo", "reinforcement"] + """The type of method. Is either `supervised`, `dpo`, or `reinforcement`.""" + + dpo: Optional[DpoMethod] = None """Configuration for the DPO fine-tuning method.""" - supervised: Optional[MethodSupervised] = None - """Configuration for the supervised fine-tuning method.""" + reinforcement: Optional[ReinforcementMethod] = None + """Configuration for the reinforcement fine-tuning method.""" - type: Optional[Literal["supervised", "dpo"]] = None - """The type of method. Is either `supervised` or `dpo`.""" + supervised: Optional[SupervisedMethod] = None + """Configuration for the supervised fine-tuning method.""" class FineTuningJob(BaseModel): diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index f4cf980b08..6b2f41cb71 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -5,19 +5,12 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from .dpo_method_param import DpoMethodParam from ..shared_params.metadata import Metadata +from .supervised_method_param import SupervisedMethodParam +from .reinforcement_method_param import ReinforcementMethodParam -__all__ = [ - "JobCreateParams", - "Hyperparameters", - "Integration", - "IntegrationWandb", - "Method", - "MethodDpo", - "MethodDpoHyperparameters", - "MethodSupervised", - "MethodSupervisedHyperparameters", -] +__all__ = ["JobCreateParams", "Hyperparameters", "Integration", "IntegrationWandb", "Method"] class JobCreateParams(TypedDict, total=False): @@ -166,71 +159,15 @@ class Integration(TypedDict, total=False): """ -class MethodDpoHyperparameters(TypedDict, total=False): - batch_size: Union[Literal["auto"], int] - """Number of examples in each batch. - - A larger batch size means that model parameters are updated less frequently, but - with lower variance. - """ - - beta: Union[Literal["auto"], float] - """The beta value for the DPO method. - - A higher beta value will increase the weight of the penalty between the policy - and reference model. - """ - - learning_rate_multiplier: Union[Literal["auto"], float] - """Scaling factor for the learning rate. - - A smaller learning rate may be useful to avoid overfitting. - """ - - n_epochs: Union[Literal["auto"], int] - """The number of epochs to train the model for. - - An epoch refers to one full cycle through the training dataset. - """ - - -class MethodDpo(TypedDict, total=False): - hyperparameters: MethodDpoHyperparameters - """The hyperparameters used for the fine-tuning job.""" - - -class MethodSupervisedHyperparameters(TypedDict, total=False): - batch_size: Union[Literal["auto"], int] - """Number of examples in each batch. - - A larger batch size means that model parameters are updated less frequently, but - with lower variance. - """ - - learning_rate_multiplier: Union[Literal["auto"], float] - """Scaling factor for the learning rate. - - A smaller learning rate may be useful to avoid overfitting. - """ - - n_epochs: Union[Literal["auto"], int] - """The number of epochs to train the model for. - - An epoch refers to one full cycle through the training dataset. - """ - - -class MethodSupervised(TypedDict, total=False): - hyperparameters: MethodSupervisedHyperparameters - """The hyperparameters used for the fine-tuning job.""" - - class Method(TypedDict, total=False): - dpo: MethodDpo + type: Required[Literal["supervised", "dpo", "reinforcement"]] + """The type of method. Is either `supervised`, `dpo`, or `reinforcement`.""" + + dpo: DpoMethodParam """Configuration for the DPO fine-tuning method.""" - supervised: MethodSupervised - """Configuration for the supervised fine-tuning method.""" + reinforcement: ReinforcementMethodParam + """Configuration for the reinforcement fine-tuning method.""" - type: Literal["supervised", "dpo"] - """The type of method. Is either `supervised` or `dpo`.""" + supervised: SupervisedMethodParam + """Configuration for the supervised fine-tuning method.""" diff --git a/src/openai/types/fine_tuning/reinforcement_hyperparameters.py b/src/openai/types/fine_tuning/reinforcement_hyperparameters.py new file mode 100644 index 0000000000..7c1762d38c --- /dev/null +++ b/src/openai/types/fine_tuning/reinforcement_hyperparameters.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ReinforcementHyperparameters"] + + +class ReinforcementHyperparameters(BaseModel): + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + compute_multiplier: Union[Literal["auto"], float, None] = None + """ + Multiplier on amount of compute used for exploring search space during training. + """ + + eval_interval: Union[Literal["auto"], int, None] = None + """The number of training steps between evaluation runs.""" + + eval_samples: Union[Literal["auto"], int, None] = None + """Number of evaluation samples to generate per training step.""" + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + reasoning_effort: Optional[Literal["default", "low", "medium", "high"]] = None + """Level of reasoning effort.""" diff --git a/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py b/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py new file mode 100644 index 0000000000..0cc12fcb17 --- /dev/null +++ b/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +__all__ = ["ReinforcementHyperparametersParam"] + + +class ReinforcementHyperparametersParam(TypedDict, total=False): + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + compute_multiplier: Union[Literal["auto"], float] + """ + Multiplier on amount of compute used for exploring search space during training. + """ + + eval_interval: Union[Literal["auto"], int] + """The number of training steps between evaluation runs.""" + + eval_samples: Union[Literal["auto"], int] + """Number of evaluation samples to generate per training step.""" + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ + + reasoning_effort: Literal["default", "low", "medium", "high"] + """Level of reasoning effort.""" diff --git a/src/openai/types/fine_tuning/reinforcement_method.py b/src/openai/types/fine_tuning/reinforcement_method.py new file mode 100644 index 0000000000..9b65c41033 --- /dev/null +++ b/src/openai/types/fine_tuning/reinforcement_method.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import TypeAlias + +from ..._models import BaseModel +from ..graders.multi_grader import MultiGrader +from ..graders.python_grader import PythonGrader +from ..graders.score_model_grader import ScoreModelGrader +from ..graders.string_check_grader import StringCheckGrader +from .reinforcement_hyperparameters import ReinforcementHyperparameters +from ..graders.text_similarity_grader import TextSimilarityGrader + +__all__ = ["ReinforcementMethod", "Grader"] + +Grader: TypeAlias = Union[StringCheckGrader, TextSimilarityGrader, PythonGrader, ScoreModelGrader, MultiGrader] + + +class ReinforcementMethod(BaseModel): + grader: Grader + """The grader used for the fine-tuning job.""" + + hyperparameters: Optional[ReinforcementHyperparameters] = None + """The hyperparameters used for the reinforcement fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/reinforcement_method_param.py b/src/openai/types/fine_tuning/reinforcement_method_param.py new file mode 100644 index 0000000000..00d5060536 --- /dev/null +++ b/src/openai/types/fine_tuning/reinforcement_method_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from ..graders.multi_grader_param import MultiGraderParam +from ..graders.python_grader_param import PythonGraderParam +from ..graders.score_model_grader_param import ScoreModelGraderParam +from ..graders.string_check_grader_param import StringCheckGraderParam +from .reinforcement_hyperparameters_param import ReinforcementHyperparametersParam +from ..graders.text_similarity_grader_param import TextSimilarityGraderParam + +__all__ = ["ReinforcementMethodParam", "Grader"] + +Grader: TypeAlias = Union[ + StringCheckGraderParam, TextSimilarityGraderParam, PythonGraderParam, ScoreModelGraderParam, MultiGraderParam +] + + +class ReinforcementMethodParam(TypedDict, total=False): + grader: Required[Grader] + """The grader used for the fine-tuning job.""" + + hyperparameters: ReinforcementHyperparametersParam + """The hyperparameters used for the reinforcement fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/supervised_hyperparameters.py b/src/openai/types/fine_tuning/supervised_hyperparameters.py new file mode 100644 index 0000000000..3955ecf437 --- /dev/null +++ b/src/openai/types/fine_tuning/supervised_hyperparameters.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SupervisedHyperparameters"] + + +class SupervisedHyperparameters(BaseModel): + batch_size: Union[Literal["auto"], int, None] = None + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float, None] = None + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int, None] = None + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/src/openai/types/fine_tuning/supervised_hyperparameters_param.py b/src/openai/types/fine_tuning/supervised_hyperparameters_param.py new file mode 100644 index 0000000000..bd37d9b239 --- /dev/null +++ b/src/openai/types/fine_tuning/supervised_hyperparameters_param.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +__all__ = ["SupervisedHyperparametersParam"] + + +class SupervisedHyperparametersParam(TypedDict, total=False): + batch_size: Union[Literal["auto"], int] + """Number of examples in each batch. + + A larger batch size means that model parameters are updated less frequently, but + with lower variance. + """ + + learning_rate_multiplier: Union[Literal["auto"], float] + """Scaling factor for the learning rate. + + A smaller learning rate may be useful to avoid overfitting. + """ + + n_epochs: Union[Literal["auto"], int] + """The number of epochs to train the model for. + + An epoch refers to one full cycle through the training dataset. + """ diff --git a/src/openai/types/fine_tuning/supervised_method.py b/src/openai/types/fine_tuning/supervised_method.py new file mode 100644 index 0000000000..3a32bf27a0 --- /dev/null +++ b/src/openai/types/fine_tuning/supervised_method.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .supervised_hyperparameters import SupervisedHyperparameters + +__all__ = ["SupervisedMethod"] + + +class SupervisedMethod(BaseModel): + hyperparameters: Optional[SupervisedHyperparameters] = None + """The hyperparameters used for the fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/supervised_method_param.py b/src/openai/types/fine_tuning/supervised_method_param.py new file mode 100644 index 0000000000..ba277853d7 --- /dev/null +++ b/src/openai/types/fine_tuning/supervised_method_param.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 TypedDict + +from .supervised_hyperparameters_param import SupervisedHyperparametersParam + +__all__ = ["SupervisedMethodParam"] + + +class SupervisedMethodParam(TypedDict, total=False): + hyperparameters: SupervisedHyperparametersParam + """The hyperparameters used for the fine-tuning job.""" diff --git a/src/openai/types/graders/__init__.py b/src/openai/types/graders/__init__.py new file mode 100644 index 0000000000..e0a909125e --- /dev/null +++ b/src/openai/types/graders/__init__.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .multi_grader import MultiGrader as MultiGrader +from .python_grader import PythonGrader as PythonGrader +from .label_model_grader import LabelModelGrader as LabelModelGrader +from .multi_grader_param import MultiGraderParam as MultiGraderParam +from .score_model_grader import ScoreModelGrader as ScoreModelGrader +from .python_grader_param import PythonGraderParam as PythonGraderParam +from .string_check_grader import StringCheckGrader as StringCheckGrader +from .text_similarity_grader import TextSimilarityGrader as TextSimilarityGrader +from .label_model_grader_param import LabelModelGraderParam as LabelModelGraderParam +from .score_model_grader_param import ScoreModelGraderParam as ScoreModelGraderParam +from .string_check_grader_param import StringCheckGraderParam as StringCheckGraderParam +from .text_similarity_grader_param import TextSimilarityGraderParam as TextSimilarityGraderParam diff --git a/src/openai/types/eval_label_model_grader.py b/src/openai/types/graders/label_model_grader.py similarity index 85% rename from src/openai/types/eval_label_model_grader.py rename to src/openai/types/graders/label_model_grader.py index 40e6bda140..d95ccc6df6 100644 --- a/src/openai/types/eval_label_model_grader.py +++ b/src/openai/types/graders/label_model_grader.py @@ -3,10 +3,10 @@ from typing import List, Union, Optional from typing_extensions import Literal, TypeAlias -from .._models import BaseModel -from .responses.response_input_text import ResponseInputText +from ..._models import BaseModel +from ..responses.response_input_text import ResponseInputText -__all__ = ["EvalLabelModelGrader", "Input", "InputContent", "InputContentOutputText"] +__all__ = ["LabelModelGrader", "Input", "InputContent", "InputContentOutputText"] class InputContentOutputText(BaseModel): @@ -34,7 +34,7 @@ class Input(BaseModel): """The type of the message input. Always `message`.""" -class EvalLabelModelGrader(BaseModel): +class LabelModelGrader(BaseModel): input: List[Input] labels: List[str] diff --git a/src/openai/types/graders/label_model_grader_param.py b/src/openai/types/graders/label_model_grader_param.py new file mode 100644 index 0000000000..76d01421ee --- /dev/null +++ b/src/openai/types/graders/label_model_grader_param.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..responses.response_input_text_param import ResponseInputTextParam + +__all__ = ["LabelModelGraderParam", "Input", "InputContent", "InputContentOutputText"] + + +class InputContentOutputText(TypedDict, total=False): + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +InputContent: TypeAlias = Union[str, ResponseInputTextParam, InputContentOutputText] + + +class Input(TypedDict, total=False): + content: Required[InputContent] + """Text inputs to the model - can contain template strings.""" + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +class LabelModelGraderParam(TypedDict, total=False): + input: Required[Iterable[Input]] + + labels: Required[List[str]] + """The labels to assign to each item in the evaluation.""" + + model: Required[str] + """The model to use for the evaluation. Must support structured outputs.""" + + name: Required[str] + """The name of the grader.""" + + passing_labels: Required[List[str]] + """The labels that indicate a passing result. Must be a subset of labels.""" + + type: Required[Literal["label_model"]] + """The object type, which is always `label_model`.""" diff --git a/src/openai/types/graders/multi_grader.py b/src/openai/types/graders/multi_grader.py new file mode 100644 index 0000000000..ee9b31d2b0 --- /dev/null +++ b/src/openai/types/graders/multi_grader.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Union +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .python_grader import PythonGrader +from .label_model_grader import LabelModelGrader +from .score_model_grader import ScoreModelGrader +from .string_check_grader import StringCheckGrader +from .text_similarity_grader import TextSimilarityGrader + +__all__ = ["MultiGrader", "Graders"] + +Graders: TypeAlias = Union[StringCheckGrader, TextSimilarityGrader, PythonGrader, ScoreModelGrader, LabelModelGrader] + + +class MultiGrader(BaseModel): + calculate_output: str + """A formula to calculate the output based on grader results.""" + + graders: Dict[str, Graders] + + name: str + """The name of the grader.""" + + type: Literal["multi"] + """The type of grader.""" diff --git a/src/openai/types/graders/multi_grader_param.py b/src/openai/types/graders/multi_grader_param.py new file mode 100644 index 0000000000..4dd1a48530 --- /dev/null +++ b/src/openai/types/graders/multi_grader_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .python_grader_param import PythonGraderParam +from .label_model_grader_param import LabelModelGraderParam +from .score_model_grader_param import ScoreModelGraderParam +from .string_check_grader_param import StringCheckGraderParam +from .text_similarity_grader_param import TextSimilarityGraderParam + +__all__ = ["MultiGraderParam", "Graders"] + +Graders: TypeAlias = Union[ + StringCheckGraderParam, TextSimilarityGraderParam, PythonGraderParam, ScoreModelGraderParam, LabelModelGraderParam +] + + +class MultiGraderParam(TypedDict, total=False): + calculate_output: Required[str] + """A formula to calculate the output based on grader results.""" + + graders: Required[Dict[str, Graders]] + + name: Required[str] + """The name of the grader.""" + + type: Required[Literal["multi"]] + """The type of grader.""" diff --git a/src/openai/types/graders/python_grader.py b/src/openai/types/graders/python_grader.py new file mode 100644 index 0000000000..faa10b1ef9 --- /dev/null +++ b/src/openai/types/graders/python_grader.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["PythonGrader"] + + +class PythonGrader(BaseModel): + name: str + """The name of the grader.""" + + source: str + """The source code of the python script.""" + + type: Literal["python"] + """The object type, which is always `python`.""" + + image_tag: Optional[str] = None + """The image tag to use for the python script.""" diff --git a/src/openai/types/graders/python_grader_param.py b/src/openai/types/graders/python_grader_param.py new file mode 100644 index 0000000000..efb923751e --- /dev/null +++ b/src/openai/types/graders/python_grader_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["PythonGraderParam"] + + +class PythonGraderParam(TypedDict, total=False): + name: Required[str] + """The name of the grader.""" + + source: Required[str] + """The source code of the python script.""" + + type: Required[Literal["python"]] + """The object type, which is always `python`.""" + + image_tag: str + """The image tag to use for the python script.""" diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py new file mode 100644 index 0000000000..1349f75a58 --- /dev/null +++ b/src/openai/types/graders/score_model_grader.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from ..responses.response_input_text import ResponseInputText + +__all__ = ["ScoreModelGrader", "Input", "InputContent", "InputContentOutputText"] + + +class InputContentOutputText(BaseModel): + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +InputContent: TypeAlias = Union[str, ResponseInputText, InputContentOutputText] + + +class Input(BaseModel): + content: InputContent + """Text inputs to the model - can contain template strings.""" + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" + + +class ScoreModelGrader(BaseModel): + input: List[Input] + """The input text. This may include template strings.""" + + model: str + """The model to use for the evaluation.""" + + name: str + """The name of the grader.""" + + type: Literal["score_model"] + """The object type, which is always `score_model`.""" + + range: Optional[List[float]] = None + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: Optional[object] = None + """The sampling parameters for the model.""" diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py new file mode 100644 index 0000000000..673f14e47d --- /dev/null +++ b/src/openai/types/graders/score_model_grader_param.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..responses.response_input_text_param import ResponseInputTextParam + +__all__ = ["ScoreModelGraderParam", "Input", "InputContent", "InputContentOutputText"] + + +class InputContentOutputText(TypedDict, total=False): + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +InputContent: TypeAlias = Union[str, ResponseInputTextParam, InputContentOutputText] + + +class Input(TypedDict, total=False): + content: Required[InputContent] + """Text inputs to the model - can contain template strings.""" + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" + + +class ScoreModelGraderParam(TypedDict, total=False): + input: Required[Iterable[Input]] + """The input text. This may include template strings.""" + + model: Required[str] + """The model to use for the evaluation.""" + + name: Required[str] + """The name of the grader.""" + + type: Required[Literal["score_model"]] + """The object type, which is always `score_model`.""" + + range: Iterable[float] + """The range of the score. Defaults to `[0, 1]`.""" + + sampling_params: object + """The sampling parameters for the model.""" diff --git a/src/openai/types/eval_string_check_grader.py b/src/openai/types/graders/string_check_grader.py similarity index 84% rename from src/openai/types/eval_string_check_grader.py rename to src/openai/types/graders/string_check_grader.py index 4dfc8035f9..3bf0b8c868 100644 --- a/src/openai/types/eval_string_check_grader.py +++ b/src/openai/types/graders/string_check_grader.py @@ -2,12 +2,12 @@ from typing_extensions import Literal -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EvalStringCheckGrader"] +__all__ = ["StringCheckGrader"] -class EvalStringCheckGrader(BaseModel): +class StringCheckGrader(BaseModel): input: str """The input text. This may include template strings.""" diff --git a/src/openai/types/eval_string_check_grader_param.py b/src/openai/types/graders/string_check_grader_param.py similarity index 87% rename from src/openai/types/eval_string_check_grader_param.py rename to src/openai/types/graders/string_check_grader_param.py index 3511329f8b..27b204cec0 100644 --- a/src/openai/types/eval_string_check_grader_param.py +++ b/src/openai/types/graders/string_check_grader_param.py @@ -4,10 +4,10 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["EvalStringCheckGraderParam"] +__all__ = ["StringCheckGraderParam"] -class EvalStringCheckGraderParam(TypedDict, total=False): +class StringCheckGraderParam(TypedDict, total=False): input: Required[str] """The input text. This may include template strings.""" diff --git a/src/openai/types/eval_text_similarity_grader.py b/src/openai/types/graders/text_similarity_grader.py similarity index 69% rename from src/openai/types/eval_text_similarity_grader.py rename to src/openai/types/graders/text_similarity_grader.py index 853c6d4fbf..738d317766 100644 --- a/src/openai/types/eval_text_similarity_grader.py +++ b/src/openai/types/graders/text_similarity_grader.py @@ -1,14 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional from typing_extensions import Literal -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EvalTextSimilarityGrader"] +__all__ = ["TextSimilarityGrader"] -class EvalTextSimilarityGrader(BaseModel): +class TextSimilarityGrader(BaseModel): evaluation_metric: Literal[ "fuzzy_match", "bleu", "gleu", "meteor", "rouge_1", "rouge_2", "rouge_3", "rouge_4", "rouge_5", "rouge_l" ] @@ -21,14 +20,11 @@ class EvalTextSimilarityGrader(BaseModel): input: str """The text being graded.""" - pass_threshold: float - """A float score where a value greater than or equal indicates a passing grade.""" + name: str + """The name of the grader.""" reference: str """The text being graded against.""" type: Literal["text_similarity"] """The type of grader.""" - - name: Optional[str] = None - """The name of the grader.""" diff --git a/src/openai/types/eval_text_similarity_grader_param.py b/src/openai/types/graders/text_similarity_grader_param.py similarity index 76% rename from src/openai/types/eval_text_similarity_grader_param.py rename to src/openai/types/graders/text_similarity_grader_param.py index f07cc29178..db14553217 100644 --- a/src/openai/types/eval_text_similarity_grader_param.py +++ b/src/openai/types/graders/text_similarity_grader_param.py @@ -4,10 +4,10 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["EvalTextSimilarityGraderParam"] +__all__ = ["TextSimilarityGraderParam"] -class EvalTextSimilarityGraderParam(TypedDict, total=False): +class TextSimilarityGraderParam(TypedDict, total=False): evaluation_metric: Required[ Literal[ "fuzzy_match", "bleu", "gleu", "meteor", "rouge_1", "rouge_2", "rouge_3", "rouge_4", "rouge_5", "rouge_l" @@ -22,14 +22,11 @@ class EvalTextSimilarityGraderParam(TypedDict, total=False): input: Required[str] """The text being graded.""" - pass_threshold: Required[float] - """A float score where a value greater than or equal indicates a passing grade.""" + name: Required[str] + """The name of the grader.""" reference: Required[str] """The text being graded against.""" type: Required[Literal["text_similarity"]] """The type of grader.""" - - name: str - """The name of the grader.""" diff --git a/tests/api_resources/fine_tuning/alpha/__init__.py b/tests/api_resources/fine_tuning/alpha/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/fine_tuning/alpha/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/fine_tuning/alpha/test_graders.py b/tests/api_resources/fine_tuning/alpha/test_graders.py new file mode 100644 index 0000000000..b144c78c74 --- /dev/null +++ b/tests/api_resources/fine_tuning/alpha/test_graders.py @@ -0,0 +1,289 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.fine_tuning.alpha import ( + GraderRunResponse, + GraderValidateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010/") + + +class TestGraders: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_run(self, client: OpenAI) -> None: + grader = client.fine_tuning.alpha.graders.run( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + model_sample="model_sample", + reference_answer="string", + ) + assert_matches_type(GraderRunResponse, grader, path=["response"]) + + @parametrize + def test_method_run_with_all_params(self, client: OpenAI) -> None: + grader = client.fine_tuning.alpha.graders.run( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + model_sample="model_sample", + reference_answer="string", + ) + assert_matches_type(GraderRunResponse, grader, path=["response"]) + + @parametrize + def test_raw_response_run(self, client: OpenAI) -> None: + response = client.fine_tuning.alpha.graders.with_raw_response.run( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + model_sample="model_sample", + reference_answer="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + grader = response.parse() + assert_matches_type(GraderRunResponse, grader, path=["response"]) + + @parametrize + def test_streaming_response_run(self, client: OpenAI) -> None: + with client.fine_tuning.alpha.graders.with_streaming_response.run( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + model_sample="model_sample", + reference_answer="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + grader = response.parse() + assert_matches_type(GraderRunResponse, grader, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_validate(self, client: OpenAI) -> None: + grader = client.fine_tuning.alpha.graders.validate( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + ) + assert_matches_type(GraderValidateResponse, grader, path=["response"]) + + @parametrize + def test_method_validate_with_all_params(self, client: OpenAI) -> None: + grader = client.fine_tuning.alpha.graders.validate( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + ) + assert_matches_type(GraderValidateResponse, grader, path=["response"]) + + @parametrize + def test_raw_response_validate(self, client: OpenAI) -> None: + response = client.fine_tuning.alpha.graders.with_raw_response.validate( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + grader = response.parse() + assert_matches_type(GraderValidateResponse, grader, path=["response"]) + + @parametrize + def test_streaming_response_validate(self, client: OpenAI) -> None: + with client.fine_tuning.alpha.graders.with_streaming_response.validate( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + grader = response.parse() + assert_matches_type(GraderValidateResponse, grader, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncGraders: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_run(self, async_client: AsyncOpenAI) -> None: + grader = await async_client.fine_tuning.alpha.graders.run( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + model_sample="model_sample", + reference_answer="string", + ) + assert_matches_type(GraderRunResponse, grader, path=["response"]) + + @parametrize + async def test_method_run_with_all_params(self, async_client: AsyncOpenAI) -> None: + grader = await async_client.fine_tuning.alpha.graders.run( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + model_sample="model_sample", + reference_answer="string", + ) + assert_matches_type(GraderRunResponse, grader, path=["response"]) + + @parametrize + async def test_raw_response_run(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.alpha.graders.with_raw_response.run( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + model_sample="model_sample", + reference_answer="string", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + grader = response.parse() + assert_matches_type(GraderRunResponse, grader, path=["response"]) + + @parametrize + async def test_streaming_response_run(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.alpha.graders.with_streaming_response.run( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + model_sample="model_sample", + reference_answer="string", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + grader = await response.parse() + assert_matches_type(GraderRunResponse, grader, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_validate(self, async_client: AsyncOpenAI) -> None: + grader = await async_client.fine_tuning.alpha.graders.validate( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + ) + assert_matches_type(GraderValidateResponse, grader, path=["response"]) + + @parametrize + async def test_method_validate_with_all_params(self, async_client: AsyncOpenAI) -> None: + grader = await async_client.fine_tuning.alpha.graders.validate( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + ) + assert_matches_type(GraderValidateResponse, grader, path=["response"]) + + @parametrize + async def test_raw_response_validate(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.alpha.graders.with_raw_response.validate( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + grader = response.parse() + assert_matches_type(GraderValidateResponse, grader, path=["response"]) + + @parametrize + async def test_streaming_response_validate(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.alpha.graders.with_streaming_response.validate( + grader={ + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + grader = await response.parse() + assert_matches_type(GraderValidateResponse, grader, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py index 75f72f9d09..4589f12846 100644 --- a/tests/api_resources/fine_tuning/test_jobs.py +++ b/tests/api_resources/fine_tuning/test_jobs.py @@ -52,6 +52,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: ], metadata={"foo": "string"}, method={ + "type": "supervised", "dpo": { "hyperparameters": { "batch_size": "auto", @@ -60,6 +61,24 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "n_epochs": "auto", } }, + "reinforcement": { + "grader": { + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + "hyperparameters": { + "batch_size": "auto", + "compute_multiplier": "auto", + "eval_interval": "auto", + "eval_samples": "auto", + "learning_rate_multiplier": "auto", + "n_epochs": "auto", + "reasoning_effort": "default", + }, + }, "supervised": { "hyperparameters": { "batch_size": "auto", @@ -67,7 +86,6 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "n_epochs": "auto", } }, - "type": "supervised", }, seed=42, suffix="x", @@ -258,6 +276,82 @@ def test_path_params_list_events(self, client: OpenAI) -> None: "", ) + @parametrize + def test_method_pause(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.pause( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_raw_response_pause(self, client: OpenAI) -> None: + response = client.fine_tuning.jobs.with_raw_response.pause( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_streaming_response_pause(self, client: OpenAI) -> None: + with client.fine_tuning.jobs.with_streaming_response.pause( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_pause(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + client.fine_tuning.jobs.with_raw_response.pause( + "", + ) + + @parametrize + def test_method_resume(self, client: OpenAI) -> None: + job = client.fine_tuning.jobs.resume( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_raw_response_resume(self, client: OpenAI) -> None: + response = client.fine_tuning.jobs.with_raw_response.resume( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + def test_streaming_response_resume(self, client: OpenAI) -> None: + with client.fine_tuning.jobs.with_streaming_response.resume( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_resume(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + client.fine_tuning.jobs.with_raw_response.resume( + "", + ) + class TestAsyncJobs: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -293,6 +387,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> ], metadata={"foo": "string"}, method={ + "type": "supervised", "dpo": { "hyperparameters": { "batch_size": "auto", @@ -301,6 +396,24 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "n_epochs": "auto", } }, + "reinforcement": { + "grader": { + "input": "input", + "name": "name", + "operation": "eq", + "reference": "reference", + "type": "string_check", + }, + "hyperparameters": { + "batch_size": "auto", + "compute_multiplier": "auto", + "eval_interval": "auto", + "eval_samples": "auto", + "learning_rate_multiplier": "auto", + "n_epochs": "auto", + "reasoning_effort": "default", + }, + }, "supervised": { "hyperparameters": { "batch_size": "auto", @@ -308,7 +421,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "n_epochs": "auto", } }, - "type": "supervised", }, seed=42, suffix="x", @@ -498,3 +610,79 @@ async def test_path_params_list_events(self, async_client: AsyncOpenAI) -> None: await async_client.fine_tuning.jobs.with_raw_response.list_events( "", ) + + @parametrize + async def test_method_pause(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.pause( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_raw_response_pause(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.jobs.with_raw_response.pause( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_streaming_response_pause(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.jobs.with_streaming_response.pause( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = await response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_pause(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + await async_client.fine_tuning.jobs.with_raw_response.pause( + "", + ) + + @parametrize + async def test_method_resume(self, async_client: AsyncOpenAI) -> None: + job = await async_client.fine_tuning.jobs.resume( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_raw_response_resume(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.jobs.with_raw_response.resume( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + job = response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + @parametrize + async def test_streaming_response_resume(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.jobs.with_streaming_response.resume( + "ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + job = await response.parse() + assert_matches_type(FineTuningJob, job, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_resume(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"): + await async_client.fine_tuning.jobs.with_raw_response.resume( + "", + ) From 01a69ab8cbed129e9edb84865893310ab52e59c7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 17:25:01 +0000 Subject: [PATCH 911/914] release: 1.78.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 33a65d75c4..21621582fa 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.77.0" + ".": "1.78.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9097cdc65a..8648497457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 1.78.0 (2025-05-08) + +Full Changelog: [v1.77.0...v1.78.0](https://github.com/openai/openai-python/compare/v1.77.0...v1.78.0) + +### Features + +* **api:** Add reinforcement fine-tuning api support ([bebe361](https://github.com/openai/openai-python/commit/bebe36104bd3062d09ab9bbfb4bacfc99e737cb2)) + + +### Bug Fixes + +* ignore errors in isinstance() calls on LazyProxy subclasses ([#2343](https://github.com/openai/openai-python/issues/2343)) ([52cbbdf](https://github.com/openai/openai-python/commit/52cbbdf2207567741f16d18f1ea1b0d13d667375)), closes [#2056](https://github.com/openai/openai-python/issues/2056) + + +### Chores + +* **internal:** update proxy tests ([b8e848d](https://github.com/openai/openai-python/commit/b8e848d5fb58472cbfa27fb3ed01efc25a05d944)) +* use lazy imports for module level client ([4d0f409](https://github.com/openai/openai-python/commit/4d0f409e79a18cce9855fe076f5a50e52b8bafd8)) +* use lazy imports for resources ([834813c](https://github.com/openai/openai-python/commit/834813c5cb1a84effc34e5eabed760393e1de806)) + ## 1.77.0 (2025-05-02) Full Changelog: [v1.76.2...v1.77.0](https://github.com/openai/openai-python/compare/v1.76.2...v1.77.0) diff --git a/pyproject.toml b/pyproject.toml index 4b854b05e5..3d5af260cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.77.0" +version = "1.78.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9d8ba015e1..495a094581 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.77.0" # x-release-please-version +__version__ = "1.78.0" # x-release-please-version From 21209abbf29113c8eedcd5e2645db31ab2bda61a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 14:38:23 +0000 Subject: [PATCH 912/914] fix(package): support direct resource imports --- src/openai/__init__.py | 4 ++++ src/openai/_utils/_resources_proxy.py | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/openai/_utils/_resources_proxy.py diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 9e97098bb0..6b21a9af23 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations import os as _os +import typing as _t from typing_extensions import override from . import types @@ -78,6 +79,9 @@ "DefaultAsyncHttpxClient", ] +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + from .lib import azure as _azure, pydantic_function_tool as pydantic_function_tool from .version import VERSION as VERSION from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI diff --git a/src/openai/_utils/_resources_proxy.py b/src/openai/_utils/_resources_proxy.py new file mode 100644 index 0000000000..e5b9ec7a37 --- /dev/null +++ b/src/openai/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `openai.resources` module. + + This is used so that we can lazily import `openai.resources` only when + needed *and* so that users can just import `openai` and reference `openai.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("openai.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() From 12a534987d0f3d238de5774749c326ee79bcfb00 Mon Sep 17 00:00:00 2001 From: David Meadows Date: Fri, 9 May 2025 11:15:26 -0400 Subject: [PATCH 913/914] fix(internal): fix linting due to broken __test__ annotation --- src/openai/types/eval_create_params.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index 66178287e4..3b712580a0 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -140,19 +140,16 @@ class TestingCriterionLabelModel(TypedDict, total=False): class TestingCriterionTextSimilarity(TextSimilarityGraderParam, total=False): - __test__ = False pass_threshold: Required[float] """The threshold for the score.""" class TestingCriterionPython(PythonGraderParam, total=False): - __test__ = False pass_threshold: float """The threshold for the score.""" class TestingCriterionScoreModel(ScoreModelGraderParam, total=False): - __test__ = False pass_threshold: float """The threshold for the score.""" From c097025779fc0bdc3389c047d4c060b5d7349f16 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 05:04:02 +0000 Subject: [PATCH 914/914] release: 1.78.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 21621582fa..f15af035f8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.78.0" + ".": "1.78.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8648497457..b153f3ef05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.78.1 (2025-05-12) + +Full Changelog: [v1.78.0...v1.78.1](https://github.com/openai/openai-python/compare/v1.78.0...v1.78.1) + +### Bug Fixes + +* **internal:** fix linting due to broken __test__ annotation ([5a7d7a0](https://github.com/openai/openai-python/commit/5a7d7a081138c6473bff44e60d439812ecb85cdf)) +* **package:** support direct resource imports ([2293fc0](https://github.com/openai/openai-python/commit/2293fc0dd23a9c756067cdc22b39c18448f35feb)) + ## 1.78.0 (2025-05-08) Full Changelog: [v1.77.0...v1.78.0](https://github.com/openai/openai-python/compare/v1.77.0...v1.78.0) diff --git a/pyproject.toml b/pyproject.toml index 3d5af260cf..71c86c38ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.78.0" +version = "1.78.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 495a094581..9b430dfa8b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.78.0" # x-release-please-version +__version__ = "1.78.1" # x-release-please-version